把聊天里已写完的 mermaid 代码块渲染成 SVG 图
28.1/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/11 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add XiaoBinGan/dsh-mermaid-fence该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · chat
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 42 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🟢 扫描:未发现敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 可切换查看源码
- 代码块兜底
在 dsh Web GUI 聊天里把已定稿的 ```mermaid 代码块渲染成 SVG 图——输出加固、可切换源码,还有代码块兜底,源码永不丢失。
README
由 DeepSeek 最新模型翻译生成dsh-mermaid-fence
把 dsh Web GUI 聊天里的已定格的围栏会变成图。还在流式输出的围栏不会 —— 它会保持普通代码块,直到这一轮结束。这样,写了一半的图不会闪烁,不会每来一个分片就重新解析,也不会因为模型还没写完就报解析错误。
先把定位说清楚。 社区里已经有两个插件做这件事,而且都比这个插件做得多:AKS1st/dsh-mermaid 和 timedomain1/dsh-mermaid-renderer。想要懒加载、视口驱动加载、缩放、全屏查看器,或者报错回环,就装它们 —— 见与已有两个插件的区别。这个包只做一件它们不做的事:在 mermaid 渲染完成后加固它输出的 SVG,删掉 securityLevel: 'strict' 仍会放行的可联网元素。装之前先读那一节;加固是真的,但只是部分加固,那一节会讲清楚到底做到哪一步。
中文摘要
这是一个 dsh Web GUI 插件,把已定格的 mermaid 围栏渲染为 SVG 图;流式输出中的围栏保持为代码块。
先说清楚定位:社区里已经有更成熟的两个同类插件 —— AKS1st/dsh-mermaid(懒加载、视口驱动、全屏缩放、报错回环)和 timedomain1/dsh-mermaid-renderer(图/源码切换、缩放、独立查看器)。想要这些功能,请优先用它们。
展开完整文档(README 剩余 1 节)
dsh-mermaid-fence
把 dsh Web GUI 聊天里的已定格的围栏会变成图。还在流式输出的围栏不会 —— 它会保持普通代码块,直到这一轮结束。这样,写了一半的图不会闪烁,不会每来一个分片就重新解析,也不会因为模型还没写完就报解析错误。
先把定位说清楚。 社区里已经有两个插件做这件事,而且都比这个插件做得多:AKS1st/dsh-mermaid 和 timedomain1/dsh-mermaid-renderer。想要懒加载、视口驱动加载、缩放、全屏查看器,或者报错回环,就装它们 —— 见与已有两个插件的区别。这个包只做一件它们不做的事:在 mermaid 渲染完成后加固它输出的 SVG,删掉 securityLevel: 'strict' 仍会放行的可联网元素。装之前先读那一节;加固是真的,但只是部分加固,那一节会讲清楚到底做到哪一步。
中文摘要
这是一个 dsh Web GUI 插件,把已定格的 mermaid 围栏渲染为 SVG 图;流式输出中的围栏保持为代码块。
先说清楚定位:社区里已经有更成熟的两个同类插件 —— AKS1st/dsh-mermaid(懒加载、视口驱动、全屏缩放、报错回环)和 timedomain1/dsh-mermaid-renderer(图/源码切换、缩放、独立查看器)。想要这些功能,请优先用它们。
本插件唯一多出来的一步:在 mermaid 渲染完成后再加固一遍 SVG,删掉 securityLevel: 'strict' 仍会保留的可联网标签(img/iframe/object/embed/image/audio/video/source/track)、剥离 on 属性、丢弃非片段且非 data:image 的 href/src。但这不能阻止 mermaid 自己在渲染阶段发起的那次请求(已在 Chromium 实测),详见英文部分。
mermaid
graph TD
A[Start] --> B{Choice}
B -->|yes| C[Done]
B -->|no| D[Stop]
| 围栏状态 | 你会看到什么 |
| 已定格,源码有效 | SVG 图,外加一个开关,可以显示原始源码;宿主自带的复制按钮仍然可用 |
| 已定格,源码无效 | 原始代码块,外加一条可读的解析错误 |
| 仍在流式输出 | 原始代码块,原样不动 |
| 在非图内容上声明 mermaid | 原始代码块(mermaid 会拒绝它,并显示错误) |
这个插件是社区包,不是上游改动。DeepSeek Harness 不接受外部 pull request;它的 CONTRIBUTING.md 指出,发布途径是 dsh-plugin GitHub 话题,这个包就是这么分发的。这个仓库里没有任何东西去钩住内部渲染器的接缝 —— 它重写渲染后的对话 DOM,这既让它能作为独立插件安装,也是它主要的脆弱之处,见已知限制。
安装
不在 npm 上。公共 registry 对 dsh-mermaid-fence 返回 404,所以从 git 安装。已验证:这条命令会安装这个包,并准确解出下面六个文件,不需要构建步骤(client.js 已提交):
npm install github:XiaoBinGan/dsh-mermaid-fence
在 dsh 配置档里,同一个 specifier 会走宿主自己的安装器,把 bundle 加进配置档的 layer 列表:
dsh plugin --profile web add github:XiaoBinGan/dsh-mermaid-fence
(dsh plugin 这种写法是 git 安装 bundle 的官方途径;本 README 里那些数字所在机器上没有执行它,因为那台机器没装 dsh 命令行。执行的是 npm install 那种写法,结果就是下面引用的六文件目录树。)
安装后的目录树就是:
LICENSE README.md client.js cordis.patch.yml index.js package.json
client.js 是预构建好的浏览器产物——安装过程不跑构建步骤。package.json 声明了 dsh.bundle.patch: ./cordis.patch.yml,配置档列出这个包时,它就把包插进宿主的 bundle:
- insert:
- id: dsh-mermaid-fence
name: 'dsh-mermaid-fence'
行为
只处理已定稿的内容
宿主的 AssistantMarkdown 在 token 陆续到达时,会给消息根节点加上 data-streaming,等这一轮定稿后再把这个属性去掉。插件只扫描没有 [data-streaming] 祖先的围栏。不做任何推测性解析,流式中的围栏与宿主渲染出来的结果逐字节一致。
安全策略很严格,且不可配置
securityLevel: 'strict' 硬编码在 src/render.js 里。它不是选项,也不能通过配置放宽,因为围栏内容是模型输出——不可信输入。公开接口里没有 securityLevel 键,没有 htmlLabels 开关,也没有调色板:themeVariables 在渲染器内部从宿主自己的 CSS token 推导,渲染器只导出 render 和 refreshTheme 两个方法。
加固去掉了什么,没去掉什么——实测
securityLevel: 'strict' 是必要条件,但不充分,所以 mermaid 返回 SVG 后,还要过一遍 src/harden.js。这一遍会移除 script、iframe、object、embed、img、image、audio、video、source、track 这些元素,剥掉所有 on 属性,并丢弃任何 href/xlink:href/src/srcset/data/poster/action/formaction 值——只要它不是同文档片段(#id)或 data:image/ URI。
在真实 Chromium 里针对 mermaid 11.17.2 实测(下面这些数字都来自 Verification performed 里记录的运行):
| 实测项 | 结果 |
| 标签 在 strict 下 mermaid 产出的 SVG | ——处理函数被剥掉,元素及其 URL 都保留 |
| 换成插件自己的配置(flowchart.htmlLabels: false)后同样测试 | 没变化: 依然从 mermaid 里活下来 |
| 过完 harden.js 之后 | 不再有 img/image 元素;图里的 rect/path/text 图元都还在 |
| 在 strict 下,click C "https://example.com/tip" 指令会让 mermaid 输出 (没有 href);加固处理会丢掉这个属性,让锚点保持惰性 |
直说这个限制。 加固在 mermaid.render() 完成之后才运行,而 mermaid 构建输出时会把标记插入到活动文档里。所以在 Chromium 中, 这样的标签会在渲染调用期间、harden.js 还没看到字符串之前就发出真正的跨源请求——实测结果是:渲染期间一次请求,插入 mermaid 原始标记时再一次;而加固路径则是渲染期间两次、插入时零次。所以这一轮处理只是把元素从留在页面里的内容中移除(DOM 里不会留下活动的 元素,同一份标记重新渲染也不会再次抓取),但它拦不住 mermaid 自己那次瞬时抓取。唯一能拦住的办法,是一开始就拒绝渲染不受信任的标签;securityLevel: 'strict' 配上 htmlLabels: false 做不到这一点。
失败可见,绝不空白
如果 mermaid 拒绝这段源码,插件会原样保留宿主的代码块,并追加一条 role="status" 提示,写明 mermaid 自己报的错误。源码不会丢失,也不会有异常逃进宿主的控制台。失败的围栏会被标记,后续扫描不会重试。
主题绑定
颜色取自宿主自己的 --dsw- 自定义属性(通过 getComputedStyle 从 <body> 上读取),所以图表在两种主题下都清晰可读,插件不必自己维护一套配色。深色模式通过 <body> 上的 data-ds-dark-theme 属性检测。该属性一翻转,插件就重新读取 token,并重新渲染发生变化的那棵子树里已挂载的图表。
源码访问
每张图表都保留宿主的横幅——这样宿主自己的复制按钮仍然可用——并加一个开关,用来显示原始围栏文本。
销毁
插件创建的一切都在 apply 内通过 ctx.effect 注册,每次注册都返回自己的清理逻辑:MutationObserver(断开连接)、防抖定时器(清除)、arm(销毁,释放所有引用)以及注入的 (移除)。销毁由一个测试验证:在跑完清理之后挂载一个新的围栏,断言它没有被渲染。
已知限制
- 包体积。 client.js 有 3,461,602 字节(3.3 MiB / 3.5 MB 十进制;gzip 后 962,140 字节),因为 mermaid 被内联了。见为什么 mermaid 要内联。
- DOM 后处理,不是渲染器 arm。 插件改写渲染后的对话 DOM,而不是挂到宿主的 renderCode() 接缝上。这正是它能作为独立社区插件安装的原因,但也意味着图表是在 React 渲染之后才插入的,而且宿主将来若改动代码块 DOM(.md-code-block、[data-code-block-banner] 和 [data-code-block-content] 契约),就需要更新这个插件。
- 语言检测读取横幅文本。 宿主把 code.className 留空,只把围栏语言放进横幅的 infostring,所以插件读的就是它;对于未声明语言的围栏,回退到取第一个关键字。未声明语言的围栏,如果第一个关键字不在识别集合里,就不渲染。
- jsdom 无法对 SVG 做布局。 单元测试对 getBBox、getScreenCTM 和 getComputedTextLength 打了桩,所以它们只能证明渲染管线跑得通、产出的结构符合预期——不能证明几何。几何和净化在真实 Chromium 里验证(见下文)。
- 一个围栏一张图,没有交互。 点击处理器和脚本化图表按设计禁用(securityLevel: 'strict')。
与已有的两个插件有何不同
两者都是 MIT 协议,渲染工作都比这个包做得多。哪个合你用,就用哪个。
| | AKS1st/dsh-mermaid | timedomain1/dsh-mermaid-renderer | 本包 |
| mermaid 加载 | 懒加载,走宿主自己的路由 | 首次画图时懒加载 | 内联在 client.js 里(3.5 MB) |
| 何时渲染 | 视口驱动,一次排队一个 | 扫描时 | 稳定后 |
| 缩放 / 全屏 | 全屏浮层、滚轮缩放、拖拽平移 | 卡片缩放 0.25–4x、独立查看器、在新标签页打开 | 无 |
| 源码视图 | 横幅复制按钮 | 图表/源码切换 + 复制 | 源码切换,保留宿主的复制按钮 |
| 渲染失败 UX | 错误条,带复制报告和「发送给 AI」 | 重试提示 | 一行提示,写明 mermaid 的报错 |
| 语言覆盖 | infostring mermaid | mermaid / mmd,外加关键字检测 | infostring mermaid/mmd,外加关键字检测 |
| mermaid 返回之后 | 插入 mermaid 的 SVG | 插入 mermaid 的 SVG | 先加固 SVG(见上文,包括那条限制) |
所以:想要打磨好、懒加载的查看器,还带错误报告闭环,就用 AKS1st/dsh-mermaid;想要每张卡片独立缩放和独立查看器,就用 timedomain1/dsh-mermaid-renderer。只有当你就是冲着渲染后的加固这一步来的,才装这个——而且先读那条限制,因为它并没有堵住 mermaid 自己在渲染期间发起的请求。
为什么把 mermaid 内联
宿主的模块表把 dsh.client 这一行解析到包的 ./client 导出。mermaid 不是平台模块,所以不能来自 require(...)。
加载器确实通过 require.async 支持包内动态分块,src/browser-entry.js 也用了动态 import('mermaid')——但 esbuild 的 iife 格式没有代码分割,所以那次 import 变成单个输出文件里一个懒求值的分块,而不是单独的文件。单独的分块 URL 由宿主的 bundle roster 拼出来,而一个独立的社区插件没有 roster 行,所以真正拆分构建在安装时无法解析。
因此,内联是唯一无需宿主侧集成就能安装的机制——这也是这个包主要的实际成本。保留动态 import,是因为它仍会推迟 mermaid 的模块主体——以及约 3 MB 的图表定义——直到插件应用时才加载。
已执行的验证
下面每条命令都在产出这份 README 的机器上、于本包目录内运行。计数和大小均复制自这些运行的真实输出,不是估算。环境:Node v24.7.0、npm 11.5.1、mermaid 11.17.2(node_modules/mermaid/package.json)、Playwright 的 Chromium。
| 命令 | 结果 |
| npm test | 26 tests, 25 pass, 0 fail, 1 skipped |
| npm run test:browser (real Chromium) | 2 pass |
| npm run check (node --check client.js && node --check index.js) | exit 0 |
| wc -c client.js | 3,461,602 bytes |
| gzip -c client.js \| wc -c | 962,140 bytes |
| npm pack --dry-run | 6 files, 967.8 kB packed / 3.5 MB unpacked (the tarball includes this README, so the packed figure moves by well under a kilobyte whenever the README is edited) |
| npm install github:XiaoBinGan/dsh-mermaid-fence (clean dir) | exit 0, 1 package, the six files above, client.js byte-identical (sha256 match) |
跳过的测试是 a label containing HTML is escaped rather than executed:mermaid 的 HTML 标签清理器在 jsdom 下永远不会结束(没有布局,也没有真正的 HTML 解析器完成),所以这条断言被移到了真实浏览器测试套件,而不是用桩来伪造。
在真实 Chromium 中验证(tests/browser.test.mjs,通过 HTTP 驱动 example/harness.html)。测试夹具会报告自己的测量值,所以下面是某次运行的真实数字,而不只是通过/失败:
- 已完成的围栏会生成带非零布局盒的 SVG(在测试夹具中为 250 × 335)、4 个节点组、3 条边,以及一条具有真实 getTotalLength() 几何值(46)的边路径;
- 流式围栏生成零个图表;
- 无效围栏生成零个图表和一条错误提示,引用 mermaid 自己的消息(No diagram type detected matching given configuration…);
- 宿主的 --dsw- token 被读取,图表文字颜色解析为真实值(rgb(27, 28, 31));
- 宿主的复制按钮仍可访问,源码切换正常工作;
- 任何情况下都没有未捕获的页面错误;
- 恶意标签()不会产生 、不会产生 、不会产生 on*= 属性,也不会弹出对话框。
上文记录的加固限制是单独测量的,用一个一次性的 Chromium 脚本(两个服务器:页面源和一个统计命中次数的「攻击者」源)。在 mermaid 11.17.2 下、使用插件的精确配置,标签 <img src="http://<attacker>/x.gif"> 产生的结果是:原始路径——在 mermaid.render() 期间有 1 次跨源请求,插入 mermaid 的 SVG 时 1 次,再插入时又 1 次再说一次,DOM 里还剩 2 个 元素;加固路径下——渲染期间 2 个,两次插入时都是 0,最后剩 0 个 元素。渲染期间那次短暂的请求来自 mermaid,不是这个插件,这个插件拦不住。
自己打开测试台
npx serve . # or any static server
open http://localhost:3000/example/harness.html
测试台会把真正的 client.js 挂到一份手写的围栏 DOM 上(一个已定型、一个流式、一个非法、一个恶意),还带主题开关,方便检查浅色和深色。
开发
npm install
npm run build # regenerate client.js with esbuild
npm test # jsdom suite
npm run test:browser # real Chromium (needs: npx playwright install chromium)
目录结构
client.js built browser artifact (committed; mermaid inlined)
index.js Host face (no services, no listeners)
cordis.patch.yml bundle insert
src/
browser-entry.js entry point of the built artifact
client.js the Cordis surface: apply, effects, observer
arm.js the DOM walk: settled gate, mount, fallback, dispose
render.js the renderer; hardcoded strict security
detect.js language/source recognition
theme.js host token → mermaid themeVariables
harden.js post-render SVG hardening (removes fetch/execute vectors)
styles.js token-driven stylesheet
i18n.js UI strings
example/harness.html browser harness
tests/ node:test suites over real DOM and real mermaid
许可证
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
XiaoBinGan/dsh-mermaid-fence
把聊天里已写完的 mermaid 代码块渲染成 SVG 图
相关插件 · 对话 / 记忆
上下文中枢oceanbase/powercontext
上下文跟着工作走 —— 跨会话、跨智能体、跨人。
★ 1,489已汉化DSH
73.1
智能体记忆库plastic-labs/honcho
Honcho 是记忆基础设施,用来构建有状态的智能体。这些智能体能理解不断变化的人、智能体、群组、项目和想法。
★ 7,918已汉化DSH
71.7
长程智能体控制面loopx-project/loopx
给 Agent 一个目标,让工作持续向前。
★ 6,358已汉化DSH
71.3
智能体上下文数据库volcengine/OpenViking
给 AI 智能体用的上下文数据库
★ 41,453已汉化DSH
71.3
长效机器人团队Fei2411/DS-Bot
Download DS Bot for Windows 10/11 64-bit / 下载 Windows…
★ 5,293已汉化DSH
71.2
可携带记忆层EverMind-AI/EverOS
为什么选择 EverOS
★ 13,797已汉化DSH
71.1
这是你的仓库?认领条目