十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

gstack diagram-render:用单文件 HTML 页面实现完全离线的 Mermaid/Excalidraw 图表渲染

gstack diagram-render:用单文件 HTML 页面实现完全离线的 Mermaid/Excalidraw 图表渲染 gstack diagram-render用单文件 HTML 页面实现完全离线的 Mermaid/Excalidraw 图表渲染【免费下载链接】gstackUse Garry Tans exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstack本篇技术指南解析 gstack 中lib/diagram-render模块的设计与实现它是make-pdf与/diagram两个技能共用的离线图表渲染引擎把 mermaid、Excalidraw 导出工具与官方 mermaid→Excalidraw 转换器全部打包进一个自包含 HTML 页面由 browse daemon 以load-html加载、通过browse js驱动、以js --out取回二进制结果。读完本文你将掌握该模块的 Page API8 个window.__*函数的调用契约、渲染安全约束securityLevel strict、htmlLabels false、字体栈锁定、确定性构建流程与防篡改的 drift 测试机制。一、模块定位为什么需要一个离线渲染页gstack 的 文档目录说明 将其定位为 Use Garry Tans exact Claude Code setup其中make-pdf负责把 Markdown 生成 PDF/diagram负责生成图表文件。二者都依赖 mermaid 渲染而 mermaid 依赖浏览器环境运行。diagram-render 模块给出的方案是构建产物只有一个自包含 HTML 页面dist/diagram-render.html约 9MB内置 mermaid、excalidraw 导出工具与官方 mermaid→excalidraw 转换器browse daemon 通过load-html把该页面加载进一个标签页调用方用browse js驱动页面内的window.__*函数再用js --out把 data URL 解码为磁盘上的字节构建产物是提交进仓库的eng-review D2 决策安装时和渲染时均零网络依赖./setup中没有 npm 供应链暴露面漂移测试 drift 测试 会在dist/被手工编辑、或与BUILD_INFO.json不同步时让 CI 失败。从源码结构看该页面的消费者主要是 make-pdf 的图表预处理器以及 diagram 技能的 e2e 测试 和 make-pdf 的多个 e2e 门如make-pdf/test/e2e/diagram-gate.test.ts。二、Page API页面暴露的 window 函数README 完整列出了页面的 API 契约入口源码 与之一一对应。调用方必须遵守以下约定函数入 → 出__renderMermaid(id, text)mermaid 文本 → SVG 字符串。id每个 fence 必须唯一mermaid-fence-n——它命名空间化了内部所有 SVG id。__mermaidToExcalidraw(text)mermaid 文本 →.excalidraw场景 JSONflowchart 完整支持其他类型在上游退化。__excalidrawToSvg(sceneJson)场景 JSON → SVG 字符串内嵌 Excalifont离线。__rasterize(svg, targetWidthPx)SVG → PNG data URL。调用方自行负责 DPI 换算targetWidthPx 版面物理宽度 (in) × 300。在 tainted canvas 上抛错。__downscaleRaster(dataUri, targetWidthPx, mime)栅格 data URI → 缩放到targetWidthPx的 data URImime 不变。make-pdf 用它把超尺寸照片归一化到印刷分辨率。__mountForScreenshot(svg, px)防 taint 兜底把 SVG 挂到#raster-stage上供browse screenshot --selector截图。__probeImage(src)data URI/URL →{width, height}JSON。__bundleInfo{ name, deps }—— 构建时烧录的固定依赖版本。就绪探测轮询#status文本直到变为ready或用browse wait #done。页面错误累积在window.__errors。源码中几个值得注意的实现细节entry.ts渲染 id 白名单校验__renderMermaid对id做正则校验/^[A-Za-z][\w-]*$/防止非法字符注入 mermaid 内部 id 命名空间。mermaid 会把 id 烘焙进每个内部 SVG id因此同一文档内内联两张图不会在 gradient/marker 上发生 id 碰撞。DPI 上限__rasterize与__downscaleRaster共享MAX_TARGET_PX 10_000的硬上限越界直接抛错——bundle 永不猜测视口DPI 换算完全由调用方负责。tainted canvas 兜底链__rasterize走canvas.toDataURL一旦 canvas 被污染即抛错此时调用方切换到__mountForScreenshot把 SVG 挂进 DOM返回mounted:px标记串真正的产物是截图本身而非返回值再用browse screenshot --selector #raster-stage完成栅格化。字体栈锁定PRINT_SANS字体栈与 make-pdf 的 print-css 完全一致Helvetica / Liberation Sans / Arial 日文 Hiragino Noto Sans CJK JP Microsoft YaHei 三套 Emoji 字体保证 mermaid 在渲染页内的文本测量与最终打印文档的排版逐像素一致。Excalidraw 资源占位window.EXCALIDRAW_ASSET_PATH被设为一个绝对但不存在的https://gstack-render.localhost/excalidraw-assets/——页面天生离线exportToSvg会直接内嵌 bundle 中自带的 Excalifont 字形不会发起网络请求。三、渲染安全契约eng-review D3entry.ts 头部注释 与初始化代码明确了渲染契约mermaid.initialize({ startOnLoad: false, securityLevel: strict, // 无点击回调、无 HTML 标签注入 theme: neutral, fontFamily: PRINT_SANS, htmlLabels: false, // foreignObject 标签会污染 canvas 并破坏内联 flowchart: { htmlLabels: false }, });securityLevel: strict是第一道防线该标签页内不存在点击回调也不存在 HTML 标签注入make-pdf 的 sanitizer 是下游第二道防线。htmlLabels: false是双重要求foreignObject 标签既会污染 canvas阻断toDataURL栅格化又会在 SVG 被内联进另一份文档时失效。页面生命周期同样记录在 entry.ts 注释中load-html加载 dist 副本 → 轮询#status ready→ N 次__renderMermaid/__excalidrawToSvg/__rasterize→ orchestrator 在 finally 中关闭标签页若渲染出错调用方在下一个 fence 前重新加载页面重置契约不残留被污染的 mermaid 全局状态eng-review D6.2。四、确定性构建bun run build 与 BUILD_INFO.json构建脚本 用Bun.build把 src/entry.ts 打成浏览器目标、minify 后的单文件内联模块并写入两个文件dist/diagram-render.html完整页面dist/BUILD_INFO.json记录{ name, sha256, srcSha256, bytes, bunVersion, deps }。当前提交的产物为 9,645,479 字节约 9.2MBbunVersion 1.3.13依赖 pin 见 dist/BUILD_INFO.json。构建脚本注释中记录了三个不可简化的页面装配要点来自 spike 阶段的经验内联脚本必须是typemodule——mermaid bundle 内含import.metaclassic script 会直接抛错minified JS 中的/scri序列必须转义为\/scri否则内联script会被提前终止Unexpected end of input需要一个绝对 URL 的base href页面运行在about:blankpage.setContent相对 URL 构造会抛错。构建脚本写入的是base hrefhttps://gstack-render.localhost/。此外脚本还埋了一个__BUNDLE_INFO_DEPS__define构建时替换为 package.json 中的精确 pin 映射——这正是__bundleInfo的数据来源dependencies: { excalidraw/excalidraw: 0.18.0, excalidraw/mermaid-to-excalidraw: 1.1.2, mermaid: 11.12.2, react: 18.3.1, react-dom: 18.3.1 }版本全部精确 pin不带^构建脚本头注释也明确要求升级依赖时编辑 package.json 中的精确 pinbun install后重建并将 src、dist、BUILD_INFO.json 一起提交。第三方许可证清单见 THIRD-PARTY-LICENSES.md五个依赖包均为 MITExcalidraw 自带的 Excalifont 等字体为 SIL Open Font License 1.1升级 pin 时必须同步复核许可证字段并更新该表。五、更新流程README 原样继承# 1. edit the exact pin in package.json cd lib/diagram-render bun install # 2. rebuild (deterministic; build twice → same sha) bun run build # 3. commit package.json bun.lock dist/ together注意确定性是有前提的minifier 输出只在同一 bun 版本内保证可复现这也是 drift 测试中深检层级跳过条件的由来见下节。六、漂移测试三层防篡改守卫drift 测试 是dist 已提交策略的守门人分为三层Tier 1始终运行50msdist/diagram-render.html的 sha256 必须精确等于BUILD_INFO.json记录的sha256且字节数等于bytesBUILD_INFO.deps必须与package.json的dependencies完全相等。捕获手工编辑 dist 的文件、以及升了 pin 忘了重建的提交。Tier 1.5无需 node_modulessrcSha256是src/entry.ts与scripts/build.ts两个源文件内容串联后的 sha256。构建脚本在写 BUILD_INFO 时同步计算它见 build.ts 的注释让改了 src 忘了 rebuild这种漂移在不做完整重建、不装依赖的情况下也能被抓到。字体栈守卫print-css 组合进 body 字体栈的每个家族名Helvetica、Liberation Sans、Arial、Hiragino Kaku Gothic ProN、Noto Sans CJK JP、Microsoft YaHei、Apple Color Emoji、Segoe UI Emoji、Noto Color Emoji都必须出现在 entry.ts 的PRINT_SANS字面量中——mermaid 用这些字体测量文本打印文档用 print-css 排版漂移直接导致标签文字溢出eng-review D3。页面不变量dist 必须含script typemodule、base hrefhttps://gstack-render.localhost/、window.__errors []且整个文档中/script闭合标签恰好出现 2 次head 错误捕获器一次 模块脚本一次——任何多余的/script都意味着转义失败。Tier 2深度重建仅 CI/安装后重新执行bun run scripts/build.ts并比较 sha。跳过条件有两个该目录下node_modules不存在fresh clone 未安装或本地 bun 版本与 BUILD_INFO 记录的不同。测试中还断言了错误捕获基础设施的存在window.__errors []与构建脚本写入 head 的onerror/unhandledrejection钩子对应——页面运行时的任何未处理异常都会被累积调用方可随时读取排查。七、小结这套方案的工程取舍从仓库实际内容看diagram-render 的价值不在功能本身mermaid 渲染是标准能力而在三条工程约束的组合零网络产物提交进仓库安装与渲染全程离线./setup无 npm 供应链暴露面确定性精确 pin sha256 记录 同 bun 版本内可复现构建任何漂移手改 dist、忘重建、字体栈漂移、pin 与 BUILD_INFO 不一致都会被 CI 拦截安全分层页面内 mermaid strict 模式 htmlLabels false页面外 make-pdf sanitizer 兜底canvas taint 场景有 DOM 截图回退路径栅格化有 10,000px 硬上限。若需深入阅读建议依次查看READMEAPI 契约与更新流程、src/entry.ts渲染契约与全部 window 函数实现、scripts/build.ts页面装配要点与 BUILD_INFO 生成、test/diagram-render-drift.test.ts三层漂移守卫以及消费侧的 make-pdf/src/diagram-prepass.ts。【免费下载链接】gstackUse Garry Tans exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表