オリジナルのpikchrはCで実装されており、そのまま利用するにはD2のようにchild_processと一時ファイルで対応することになる。
幸いなことにpikchrをwasmにするのはそれほど難しくなく、既にwasm化されたパッケージが存在する。
この記事では以下のパッケージを利用する。
npm i pikchr-wasm
VitePressはmarkdown-itを利用しているため、markdown-itのプラグインとして実装して対応すればよい。
プラグインといってもそれほど大掛かりなものではなく一つのファイルで済む。
.vitepress/
配下にmarkdown-pikchr.ts
というファイル名で作成して、その中身は以下のように実装する。説明のため例外処理は省略している。
.vitepress/markdown-pikchr.ts
import pikchr from 'pikchr-wasm'
export default async () => {
await pikchr.loadWASM()
return (md: any) => {
const fence = md.renderer.rules.fence.bind(md.renderer.rules)
md.renderer.rules.fence = (tokens: any, idx: any, options: any, env: any, slf: any) => {
const token = tokens[idx]
const tokenInfo = token.info.toLowerCase()
if (tokenInfo === "pikchr") {
const code = token.content
const svg = pikchr.render(code)
return `<div class="pikchr-diagram">${svg}</div>`
}
return fence(tokens, idx, options, env, slf)
}
}
}
pikchr.loadWASM
を実行するために全体をasyncにしておく必要がある。それ以外は特に難しいところはなく、ほぼ決まりごとのようなものである。型がなにも活かせてないのでjsにしてもよいと思う。
次にconfig.mts
を修正する。中身は以下の通り。
注意点としてはmarkdown-pikchrをasyncにしたため、こちらの定義も全体をasyncにしておく必要がある。
.vitepress/config.mts
import { defineConfig } from 'vitepress'
import mdpikchr from './markdown-pikchr'
export default async () => {
const pikchr = await mdpikchr()
return defineConfig({
themeConfig: {
...
},
markdown: {
config: (md) => {
md.use(pikchr)
}
}
})
}
たったこれだけで終わり。
pikchr-examples.md
# Pikchr Example
```pikchr
circle "DISK"
arrow "character" "defns" right 150%
CPU: box "CPU" "(16-bit mini)"
arrow <- from top of CPU up "input " rjust
move right from CPU.e
CRT: " CRT" ljust
line from CRT - 0,0.075 up 0.15 \
then right 0.5 \
then right 0.5 up 0.25 \
then down 0.5+0.15 \
then left 0.5 up 0.25 \
then left 0.5
arrow from CPU.e right until even with previous.start
Paper: CRT + 1.05,0.75
arrow <- from Paper down 1.5
" ... paper" ljust at end of last arrow + 0, 0.25
circle rad 0.05 at Paper + (-0.055, -0.25)
circle rad 0.05 at Paper + (0.055, -0.25)
" rollers" ljust at Paper + (0.1, -0.25)
```
...(snip)...