数式編集中に数式内にカーソルを表示するUserScript
texで長い数式を書いていると、カーソルがどこなのかが分かりにくくなる
このUserScriptで、カーソルの場所に赤い縦棒が生えて、ちょっと見やすくなります
https://gyazo.com/9636e4a43fe0b47ffa4ce76e3e829283
code:tex
\begin{pmatrix}
1 & 2 & 3 \\
4 & 5 & 6 \\
7 & 8 & 9
\end{pmatrix}
数式ブロックのみサポートしてます
仕組み
katexを自前で表示
コードブロックのカーソル位置に|を追加したテキストを作る
この原理上、たまに変な表示になるときもある 無視しよう
既存のpreviewの上に被せる形で表示する
このUserScript由来の表記である事がわかるように背景色をつけてます
code:script.js
const katexCursor = "katex-cursor";
function render() {
const lines = getCodeBlockLinesOnCursor();
const line = lines?.0;
if (!line) return;
const { id } = line;
if (line.codeBlock.lang !== "tex") return;
const content = extractCodeBlockTextWithCursorChar(
lines,
String.raw\textcolor{red}{|}
//String.raw\CURSOR
);
const elementId = id + "-katex-cursor";
try {
const s = katex.renderToString(content, {
throwOnError: true,
displayMode: true,
//macros: { "\\CURSOR": String.raw\textcolor{red}{|} },
});
if (s.match("katex-error")) return;
document.getElementById(elementId)?.remove();
const div = document.createElement("div");
div.id = elementId;
div.classList.add(katexCursor, 'formula');
div.style.position = "absolute";
div.style.bottom = 0;
div.style.background = "lightyellow";
const span = document.createElement("span");
div.style.marginLeft = `calc(${
((line.codeBlock?.indent ?? 0) - 1) * 1.5
}em + 6px)`;
span.innerHTML = s;
div.appendChild(span);
putOnPreview("L" + id, div);
} catch (e) {}
}
function putOnPreview(id, elm) {
const elm = document.querySelector(
#${id} ~ .line:has(.code-block-formula-preview)
);
if (elm.querySelector(".error")) return;
elm.appendChild(elm);
}
function cleanup() {
$(. + katexCursor).remove()
}
async function loadScript(src) {
const script = document.createElement("script");
script.src = src;
const promise = new Promise((resolve) => {
script.addEventListener("load", resolve);
});
document.body.appendChild(script);
return promise;
}
async function initialize() {
await loadScript(
"https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.12.0/katex.min.js"
);
cosense.on("layout:changed", ()=>{
cleanup()
if(cosense.Layout === 'page') {
observeCursor()
}
});
cosense.on("page:changed", () => {
cleanup();
render();
});
observeCursor()
}
function observeCursor() {
new MutationObserver(() => {
cleanup();
render();
}).observe(document.querySelector(".cursor"), { attributes: true });
}
initialize();
function getCodeBlockLinesOnCursor() {
const cursorLineId = $(".cursor-line").attr("id")?.slice(1);
if (!cursorLineId) return;
const lines = cosense.Page.lines;
let codeBlockLines = [];
let found = false;
for (const line of lines) {
if (!line.codeBlock) continue;
if (line.id === cursorLineId) found = true;
if (line?.codeBlock?.start) {
codeBlockLines = [];
continue;
}
if (line?.codeBlock) {
codeBlockLines.push(line);
}
if (line?.codeBlock?.end) {
if (found) return codeBlockLines;
}
}
}
function extractCodeBlockTextWithCursorChar(lines, char='|') {
const cursorLineId = $(".cursor-line").attr("id").slice(1);
const cursorX = cosense.Page.cursor.char
const textLines = lines.map((line) => {
const text = line.text.slice(line.codeBlock.indent);
const x = cursorX - line.codeBlock.indent;
if (x < 0) return text;
if (textx - 1 === "\\") return text // カーソル前が\ならやめといたほうが良さげ
if (line.id === cursorLineId) {
return text.slice(0, x) + char + text.slice(x);
}
return text;
});
return textLines.join("\n");
}
/cockscomb/Mermaid
とても参考になりました
面倒なのでやらない可能性があるが、next
parseすれば、カーソルのある位置の箱?みたいなのがわかるかも
カーソルじゃなくて、該当箇所のrectを光らせたほうがいいかもしれない
カーソルの現在位置(x軸)を取れるapiが欲しい