TikZ(精确数学图形)— 已上线
补上 mermaid 补不了的缺口:精确的数学图形——几何作图、交换图、带坐标/标签的方框、函数曲线。一个 ```tikz 代码块渲成 SVG,reader 上已经在跑。
选 TikZ 的决定性理由是引擎对称。 node-tikzjax 跟 Obsidian 的 TikZJax 插件是同一个引擎,于是 owner 在 Obsidian 里画,StandMeet 渲出一模一样的图 —— 跟 katex / mermaid 是同一个论证。(权衡后否掉的:quiver、Mafs、function-plot、JSXGraph、Penrose —— 需求是静态的精确图形。)
它在服务端渲染,不在浏览器
最初的设想是浏览器端 WASM,载荷懒加载、或者放在某个设置项后面。落地的不是那样,而这次反转本身就是设计。 TeX 引擎跑在服务端(/render-tikz,Node runtime 的路由,用例在 lib/render/tikz);客户端的 TikZBlock 把源码 POST 上去,收回 SVG。重 WASM 一个字节都不到访客那儿。
真正的代价在别处:serverExternalPackages + outputFileTracingIncludes,因为 TeX 的运行时资产(core.dump.gz、tex.wasm.gz、tex_files.tar.gz)是从文件系统读的,不是 import 进来的,Next 的依赖追踪带不进 standalone 构建 —— 少了它们,那条路由 ENOENT 然后挂住。
引擎不可重入 —— 这条是承重的
node-tikzjax 驱动的是一个进程内单例 WASM TeX 引擎。两次渲染重叠就会互相踩,双方一起抛 TeX engine render failed。而 reader 页面上有几张图就同时发几个 POST,于是一篇有四张图的笔记会随机丢掉其中几张 —— 读者在本该是图的地方看到一整段 LaTeX,而且哪几张失败每次刷新都不一样。
线上量到的:同一份源码单发返 200,并发两个时两个都 422,0.6 秒就回 —— 离 25 秒的超时差得远,所以它从来不是"慢"的问题。
因此渲染排队:同一时刻只有一个进引擎。计时从真正开跑那一刻起,不含排队等待,否则一页图一多,排在后面的还没轮到就被判超时。队列是每进程一条;多副本各串各的,够用 —— 单例本来就是进程内的。
字体必须自托管
SVG 里的文字是 <text> + font-family: cmr10 / cmsy10 / …,而那些字符是 TeX 字体的槽位,不是 Unicode:$\to$ 在 cmsy10 里落在 0x21,也就是 !。字体不加载就退回系统字体,箭头当场变成惊叹号,字距也按错的度量排,词会被拆开(stochastic → sto chastic)。
包里 embedFontCss 默认 false,默认的 fontCssUrl 还指着 jsDelivr。对一个自托管产品这两条都是错的:离线的实例取不到,而且每张图都会替 owner 的读者向第三方发一次请求。字体由本实例发(scripts/copy-tikz-fonts.mjs 跟着 build 把它们搬进 public/)。
失败不是读者的问题
渲染失败以前会把 LaTeX 源码印进页面。mermaid 早就定过这个问题 —— owner 看得见诊断,访客什么都看不见,而失败照样记日志 —— TikZ 这边当初就是没跟上。loading 现在也是占位而不是源码,这在排队之后更要紧:多图页面上最后一张要等几秒,而那几秒不能是一整墙 LaTeX。