Markdown をその場で試せる /preview を付けた #
ヘッダー右の preview リンクから /preview に行ける。textarea に書いた Markdown が、すぐ右にこのサイトの記事と同じ形で描かれる。保存はしない(issue #71)。この記事自体を貼り付ければ、見出しのアンカーもアラートも表もそのまま出る。
欲しかった理由 #
tinymarkdown とブログの renderer がどこまで出来ているかを、ビルドやデプロイをせずに確認したかった。issue を立てたのはスマホからだった。LLM に生成させた Markdown を仮当てして、どういう見え方になるか試す用途が最初から頭にあった。スマホだとクリップボードの制限で長文を貼るのが難しいので、ファイル入力で .md をそのまま読み込めるようにもしてある。
描画はサイト自身にやらせた #
ロマンを目指して、記事を描くのと同じ renderer をブラウザーに持っていった。site パッケージが記事用に持っているレンダリング(front matter からタイトルを取る、見出しに id と permalink、alert、```svg、コピーボタン)を丸ごと呼べる関数 render(src, files) として export して、wasm-gc で preview.wasm にした(use-js-builtin-string、experiments/browser-render の spike と同じ載せ方)。
最初は Wasm を instantiate できなかったとき用の JS ビルド(.mjs)も作っていたのだけど、Wasm の動作確認が取れたので丸ごと捨てた。「ロマンより動く機能」の免罪符で残すより、1 経路だけを面倒見る方が良い。
リンクは posts/preview.md を書いているつもりで解決 #
プレビュー内の相対リンクは、「あなたが posts/preview.md という記事を書いている」つもりで解決する。../TODO.md ならこのサイトの /docs/ のページへ、存在しないパスは GitHub の blob URL へ飛ぶ。飛び先を検証するため、ページにはサイトが持っているファイル一覧を仕込んである。
副産物と落とし穴 #
code-copy.jsはページロード時だけpreを飾っていたので、プレビューで挿入されるpreにコピーボタンが付かなかった。MutationObserverで後から足されたノードも飾るように直した。- Devin Review が「ファイル名に
</script>があれば<script type="text/plain">を抜けて HTML を差し込める」と指摘した。ファイル一覧は<script>の生テキストに置くのをやめて、属性として書けば必ずエスケープされるdata-wasm/data-filesに移した。 - ```svg ブロックは生 HTML を通す仕様なので、他人が用意した Markdown を貼ると被害者のブラウザーでスクリプトが動きうる。記事と同じ見え方が目的なのでここは変えず、サニタイズは Future work に記録した。
確認 #
いつも通り make check と make compare(Go サーバーと Workers 用 wasm のバイト一致、今回から /preview も比較対象)。最後に実際の Chrome で Wasm が instantiate されるところまで見た。ページのファイル入力の横には小さく rendered by wasm と出るので、どの経路で動いているかも分かる。