数式をpreviewするUserScript
数式記法 (scrapbox)を記法むき出し状態のときにreal-timeでpreviewするUserScript
/forum-jp/texのリアルタイムプレビューが欲しいが開発動機
数式を見たまま編集することができる
使い方
次のcodeを自分のページのscript.jsに書く
code:js
import('/api/code/programming-notes/数式をpreviewするUserScript/script.js');
もしくは以下の実行結果を自分のページのscript.jsから読み込めるように自分のprojectに書き込んでおく
UserScriptの危険性を考慮して自分のprojectからimportしたいけど、コードを何ファイルもコピペするのが面倒な人向け
code:sh
deno run -A --unstable https://scrapbox.io/api/code/takker/UserScriptをbundleするDeno_script/build.ts "https://scrapbox.io/api/code/programming-notes/数式をpreviewするUserScript/script.js" --bundle --minify --charset=utf8 --outfile=script.min.js
features
[$ ]内でTeXを打つと、どんな数式になるかpreviewしてくれるよ
こんな感じで$ E=mc^2
https://gyazo.com/52ef50b5ef74eb17cbaf365571af1d80
数式にミスが有ると、どこにミスが有るのか教えてくれるよ
{aligned}とかもちゃんと表示できるよ
$ \begin{aligned}\int\sin\theta\mathrm{d}\theta=&\int\mathrm{d}(-\cos\theta)\\=&-\cos\theta+C\quad.\mathrm{for}\exist C\in\Bbb{R}\end{aligned}
https://gyazo.com/bd426c7ce9600b2eefe6fbd808f3b6f0
mobileの場合は、長押しすると出てくる
実装
popupに数式を出してみるに微調節を加えただけ
お借りしたもの
mizdra/scrapbox-userscript-icon-suggestion
これをベースにpopup-container@0.1.0を実装しました
2021-08-05 08:06:06 fix ✅mathml版KaTeXが表示されてしまう
2021-07-16 19:35:12 update to popup-container@0.1.1
2021-05-26 13:53:44 .formulaの監視部分をmemo化した
2021-05-25 15:53:28 custom elementをやめてElement.shadowRootにReact appをmountする
dependencies
htm@3.0.4/preact
use-KaTeX
preact@10.5.13
popup-container@0.1.2
useMutationObserver
use-cursor-observer
custom-throttle
code:script.js
import {html, render} from '../htm@3.0.4%2Fpreact/script.js';
import {useKaTeX} from '../use-KaTeX/script.js';
import {useState} from '../preact@10.5.13/hooks.js';
import {tag} from '../popup-container@0.1.2/script.js';
import {useCursorObserver} from '../use-cursor-observer/script.js';
const App = () => {
const {ref, error, setFormula} = useKaTeX(''); // 数式rendering用hook
const open, setOpen = useState(false); // popupの開閉
const cursor, setCursor = useState({
top: 0,
left: 0,
}); // cursorの位置
// .formula内にcursorが来たらpreviewを開始する
useCursorObserver(({
cursorRect: {left},
parentRect: {top: editorTop, left: editorLeft},
elements
}) => {
const formulaDOM = elements.find(element => element.classList.contains('formula'));
if (!formulaDOM) {
setOpen(false);
return;
}
setOpen(true);
setFormula(formulaDOM.textContent.slice(3, -1));
// popupを出すy座標は、$ の上端に合わせる
const {top: formulaTop} = formulaDOM.getBoundingClientRect();
setCursor({
top: formulaTop - editorTop,
left: left - editorLeft,
});
});
return html`
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.12.0/katex.min.css" />
<style>
.error {color:#fd7373; }
.katex-display {
display: inline-block !important;
margin: 0 !important;
text-align: inherit !important;
color: #eee;
}
</style>
<${tag} cursor-top="${cursor.top}" cursor-left="${cursor.left}" open="${open}">
${error && html<span class="error">${error}</span>}
<span class="katex-display" ref="${ref}" />
</${tag}>
`;
};
<div>のElement.shadowRootにReact appをmountする
code:script.js
const app = document.createElement('div');
app.dataset.userscriptName= 'katex-previewer';
document.getElementById('editor').append(app);
app.attachShadow({mode: 'open'});
render(html<${App} />, app.shadowRoot);
Deno.icon