Skip to content

About

DeepSeek Harness 可视化插件:svg/html/mermaid 代码块渲染为图表卡片(宿主端 mermaid、深色主题配色、节点文字自适应、灯箱、保存导出)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

vizcb-codeblock-visualizer

version license ds desktop mermaid present hide install

DeepSeek Harness 可视化插件:把模型回答中的 svg` / html / ````mermaid 代码块渲染为消息底部的内嵌图表卡片;并把 ````present-files` 指令渲染为工作区文件预览面板(HTML/图片/文本 + 文件卡片)。mermaid 为宿主端渲染(mermaid + svgdom,运行在独立 worker_threads,不污染宿主进程),运行时零网络外联(无 CDN、无 iframe 脚本);依赖由安装时自动拉取(仓库不内置 node_modules)。

效果预览

vizcb-codeblock-visualizer 效果

功能

  • SVG 卡片:宿主端 DOMPurify 消毒(剥 script / 事件属性 / javascript: 链接)+ 客户端宽松校验(与渲染路径一致),轻微 XML 瑕疵也能正常渲染
  • HTML 卡片:空 sandbox iframe(默认禁脚本;htmlAllowScripts 可开 iframe 内脚本)
  • Mermaid 卡片:宿主端渲染(mermaid + svgdom → SVG,独立 worker_threads 隔离),24 种方言别名(flowchart/graph/sequenceDiagram…);深色主题下文字/连线/箭头按宿主色板提亮(文字 #E5E7EB、连线 #4F8CFF),节点文字超出时自动扩宽矩形 + viewBox 自适应;同一图的并发请求合并、结果内存缓存
  • 图注标题:自动提取代码块上方的标题行
  • 交互:复制源码 / 全屏灯箱放大(Esc、点背景、按钮关闭)/ 保存导出(原生对话框自选位置与格式:PNG / SVG;HTML 导出源文件)
  • 失败可见化:未渲染时显示原因通知条;mermaid 渲染错误显示具体原因
  • 可预览文件(present-files):模型在回复末尾输出 ````present-files块列出最终成品文件 → 第一行自动打开预览面板(HTML 走sandbox="allow-scripts"` iframe,图片大图,css/js/文本源码),其余排成文件卡片点击切换;mtime 轮询:文件被改写后预览自动整体刷新;卡片支持复制完整路径、在系统文件管理器中显示(宿主 `explorer /select`);路径先 resolve + 包含性校验(防 `../` 越界),经随机 token 前缀暴露,单文件超 `previewMaxBytes` 拒绝;用户手动关闭预览后本次会话不再自动弹开;`hideSourceBlocks` 隐藏渲染过的源码围栏,只留渲染结果(源码仍可复制;渲染失败/被禁用的图不会被误藏,源码保留)
  • 呈现层(v1.7.0):源码围栏与渲染卡片不同框——检测到成对闭合围栏的同一轮提交里隐藏源码并顶出占位骨架,成品淡入出现;卡片按语言预留占位高度,减少跳位;prefers-reduced-motion 下无动画
  • 工程化:配置化(8 块/条、64KB/块、重试阶梯、画布底色、mermaid 深色模式、预览上限等)、per-seq 缓存(可重试失败不缓存,重试真实重取)、递增退避重试(400/800/2000ms 阶梯,默认 6s 放弃并还原源码)、请求体限制 + 每会话限流 + mermaid 全局限流、/vizcb/debug 自检、启动日志 [vizcb] mounted vX、node --test 24 用例 + GitHub Actions CI

安装

前提:目标 profile 为标准 DSH web 应用栈(含 dsh-web-app 的 turnTail 槽位,以及 systemPrompt / webServer / sessionQuery 服务)。桌面版 2.0.4 已验证;独立 dsh web(web profile)v1.4.1 实测可用。

方式 A —— 一键安装脚本(推荐)

# 1. 获取源码(git clone 本仓库 / 下载 zip)
# 2. 在仓库根目录运行(会自动复制插件 + 写入 bundles + 缺依赖时 npm install):
powershell -ExecutionPolicy Bypass -File install-vizcb.ps1
# 其他 profile:
powershell -ExecutionPolicy Bypass -File install-vizcb.ps1 -ProfileName web
# 3. 重启 DeepSeek Harness

方式 B —— 手动复制

  1. 把 vizcb-codeblock-visualizer 目录复制到目标 profile 的 node_modules/
  2. 在 profile 的 package.json 的 dsh.profile.bundles 追加 "vizcb-codeblock-visualizer"
  3. (若未带 node_modules)在插件目录执行 npm install
  4. 重启

方式 C —— 作为 npm/git 依赖安装

在目标 profile 的 package.json 中把插件加入依赖并注册 bundle,然后执行 pnpm install(桌面版自带 pnpm,或直接用 dsh plugin 命令转发):

// ~/.dsh/profiles/<name>/package.json
{
  "dependencies": {
    "vizcb-codeblock-visualizer": "github:Reseezhang/vizcb-codeblock-visualizer"
  },
  "dsh": {
    "profile": {
      "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "vizcb-codeblock-visualizer"]
    }
  }
}

或者用 dsh CLI 一行安装依赖(随后仍需把 "vizcb-codeblock-visualizer" 追加进上述 bundles 数组):

dsh plugin --profile <name> add github:Reseezhang/vizcb-codeblock-visualizer

依赖(mermaid/jsdom/dompurify/svgdom)由 npm/pnpm 从 registry 自动拉取,仓库不内置 node_modules。

配置

默认值见 lib/index.js 的 DEFAULTS。在 profile 级 cordis.patch.yml 按 id 覆盖:

- id: vizcb-codeblock-visualizer
  config:
    maxBlocks: 8              # 每条消息最多渲染块数
    maxBlockChars: 65536      # 单块最大字符数
    retryDelaysMs: [400, 800, 2000]  # 空结果重试的递增退避阶梯(第 N 次失败取第 N 项,末位兜底)
    retryGiveUpMs: 6000       # 放弃等待并还原源码的时限(ms);超时后仍按末位间隔探测
    retryDelayMs: 2000        # 【兼容旧配置】显式配置它即按固定间隔重试,退避阶梯失效
    minSvgHeight: 120         # SVG 无 viewBox 时的兜底高度
    mermaidEnabled: true      # 是否渲染 mermaid
    mermaidTextColor: "#E5E7EB"  # mermaid 深色主题文字/标签颜色(对齐宿主色板)
    mermaidLineColor: "#4F8CFF"  # mermaid 连线/箭头/生命线颜色
    canvasBg: "#0F172A"       # 卡片图区 / HTML / PNG 导出的画布底色
    mermaidDark: null         # mermaid 主题:null=自动检测(data-theme/prefers-color-scheme)、true/false=强制
    htmlAllowScripts: false   # HTML iframe 是否允许脚本(安全权衡,默认关)
    debugLog: false           # 请求级调试日志(开启写 os.tmpdir()/vizcb-debug.log)
    mermaidRateMax: 30        # mermaid 渲染全局限流(滑动窗口内最大次数)
    previewEnabled: true      # 是否启用 present-files 文件预览
    workspaceRoot: ""         # 工作区根目录;留空 = sandboxPolicy.workspaceRoot → 进程 cwd
    previewMaxBytes: 5242880  # 单文件预览上限(超过提示用浏览器打开)
    previewPollMs: 3000       # 文件变化轮询间隔(ms)
    maxFiles: 12              # 一次展示最多卡片数
    hideSourceBlocks: true    # 隐藏本插件渲染过的源码围栏(svg/html/mermaid/present-files),只留渲染结果

卸载 / 回滚

删掉 bundles 条目 + 删除插件目录 + 重启。

文件结构

vizcb-codeblock-visualizer/
├── package.json        # dsh.bundle.patch + dsh.client 声明 + files 白名单
├── cordis.patch.yml    # 挂载行 + 配置说明
├── README.md
├── DEVELOPMENT.md      # 完整开发历程与踩坑记录
├── LICENSE             # MIT
├── install-vizcb.ps1   # 一键安装脚本(按 files 白名单复制)
├── test/
│   └── index.test.mjs  # node --test 冒烟测试
├── .github/workflows/ci.yml
└── lib/
    ├── index.js        # Host:提示词 section + read-turn / mermaid.svg / p/<token> 预览 / reveal / debug 路由 + worker 管理(不碰 jsdom)
    ├── client.js       # Client:turnTail 渲染 + 灯箱 + 保存导出 + 预览面板(__ModuleLoader__ 格式)
    ├── mermaid-worker.mjs # Worker:mermaid + svgdom 隔离渲染 + 渲染收尾(增强/自适应/消毒)
    ├── dom-worker.mjs  # Worker:```svg 块的宿主端消毒(与 mermaid 渲染分线程,互不排队)
    ├── svg-dom.mjs     # jsdom + DOMPurify + 节点自适应 + 配色增强(只允许被 worker import)
    └── label-metrics.mjs # 纯文本度量(零依赖,主线程与 worker 共用)

版本历史

  • 1.7.5 修「胶囊/菱形里的文字压出图形」(v1.7.4 的遗留):后处理只认 rect.label-container,而 ([..]) 胶囊是 <g class="label-container"><path>、{..} 菱形是 <polygon class="label-container">,两者被直接跳过 —— mermaid 在 svgdom 下按「单行」算图形尺寸,两行标签就从底部(实测下溢 4.5px)和两侧(实测左右各溢 4~5px)压出去。现在:① 从 d / points 反推图形包围盒(jsdom 没有布局引擎,只能几何推算)② 矩形仍直接改宽高 ③ 路径/多边形按需非等比缩放(上限 1.6×),超出部分压字号兜住,并对描边加 vector-effect="non-scaling-stroke" 免得边框跟着变粗 ④ 行盒一律改成绝对像素并居中到图形中心。实测三个图形四边均为负值(文字完全在框内),state/class/ER 仍不受影响。POLISH_VERSION 递增到 2 让老缓存失效

  • 1.7.4 mermaid 版式优化(纯增量,不动渲染引擎):多行标签此前在 SVG 里每行字号字重完全一致,自动生成的图因此显得「平」;而 svgdom 下字体测量不准,mermaid 按「换行前」的行数算子块居中与框高,实渲染折成更多行后行与行会叠在一起、末行压出方框。现在在 worker 里同一趟 jsdom 解析中做版式后处理:① 节点标签首行 → 主标题(14px/500)、其余行 → 副标题(11.5px/0.72)② 按真实行数用像素重算行盒,并把整块文字在框内垂直居中(只补高不居中会让文字贴住框顶)③ 节点方框补 rx=6 圆角 ④ 边标签去灰底 ⑤ 连线统一 1.4px。渲染参数同时补 CJK 字体栈(Microsoft YaHei/苹方)与 14px 字号、curve: basis、间距 45/55、padding 10。只对 flowchart 生效(state/class/ER 的 g.node 结构不同,按固定行盒重排会把标题顶到框上沿);mermaidPolish: false 会把渲染参数、配色增强、后处理整包退回改动前行为,可直接做 A/B 回滚(逐字节等价已验证)。缓存 key 补上配色与 POLISH_VERSION,避免「改了配置旧图不变」。

  • 1.7.3 修「源码围栏一直不消失」(hideSourceBlocks 在 DSH 0.2.0-rc.2 上从未生效过——三处宿主假设都不成立,任一处单独就足以让功能失效):① 语言标签类名是 CSS Modules 哈希名:旧代码查 [data-code-block-banner] .infostring,而真机是 _infostring_7gxqk_45,永远匹配不到(实测映射见 DEVELOPMENT 教训 14)② 即使选中横幅,横幅也不显示语言:宿主 CodeToolbar 的标签是 isLang(lang) ? lang : codeLabel,isLang 基于 shiki 语法集,svg/mermaid/present-files 都不在其中,只显示通用标签「代码块」(js 能显示正因为它在集合里)—— 语言在真机 DOM 上根本读不到 ③ turnTail 槽位与助手消息节点是兄弟(各自是 div[data-chat-turn] flowItem),旧实现「向上找最近的含代码祖先」会越过消息停在会话滚动列:fences 混进其它回合的块 → 与「本轮解析出的块」按序号对账必然错位(targets 恒空),命中数还会撞上 ≤24 守卫直接放弃(真机一个 turn 有 116 个流节点)。现在:语言标签按 [class*="infostring"], [class*="language"] 子串匹配;读不到语言时按正文特征识别(^<svg/mermaid 关键字…);确认阶段改用宿主解析出的 code 做归一化内容对账(与顺序、语言标签无关,唯一命中才认,多命中要求语言一致,否则宁可少藏);作用域按 data-chat-turn 收窄到本轮。顺带修掉一个反向 bug:原「正文正则扫 ```」兜底会把讲解 mermaid 语法的普通代码块误判成图表块并藏掉用户代码。另:客户端改动不需要重启(dsh-client-hmr 每 500ms 轮询每个客户端 bundle 的 mtime/ctime/size,变更后经 /plugins/events SSE 推送、浏览器热重载该模块)

  • 1.7.2 修「宿主重载后卡片仍然只是代码块」:新版宿主的 list 类插槽强制要求 options.id(chain 要 select、keyed 要 key),conversation.chat.turnTail 正是 list 类,缺 id 时 ctx.slots.register 直接抛 Uncaught Error: list slot "conversation.chat.turnTail" requires options.id,客户端组件从未挂载(症状极具迷惑性:宿主侧一切正常、/vizcb/debug 200、提示词规范也注入了,但 /vizcb/read-turn 永远收不到请求)。注册项补 id: "vizcb-codeblock-visualizer"(旧宿主多传一个字段无害,向后兼容),客户端版本徽标常量同步对齐到 1.7.2

  • 1.7.1 修「插件在 DSH 0.2.0-rc.2 上 failed to import」:jsdom 是 CJS 包,宿主主线程解析它时会让 DSH 的模块解析路由器抛 TypeError: createRequire.resolve.paths is not a function(ResolutionRouter.routeScoped),插件的整次 import 因此失败(宿主只留一句 failed to import,装法/启动方式/版本都无关,1.6.10 与 1.7.0 同样中招;改 await import("jsdom") 也无效)。现在主线程与 jsdom/DOMPurify 彻底隔离:新模块 lib/svg-dom.mjs(jsdom + DOMPurify + 节点自适应 + 配色增强)只被 worker 线程 import,lib/dom-worker.mjs 专做 ````svg 块的消毒,lib/mermaid-worker.mjs` 渲染完就地收尾(增强 → 自适应 → 消毒),`sanitizeSvgText` 改为 async(失败仍 fail-closed 返回空);`estimateLabelWidth` 抽到零依赖的 `lib/label-metrics.mjs` 供主线程与 worker 共用。另加回归防线:单测断言 `lib/index.js` 不得出现 jsdom/dompurify

  • 1.7.0 呈现层("绘制过程"不再外露):占位骨架 + 淡入替代"源码围栏 → 空白 → 硬切 → 跳位"四段式。① 新增 VizSkeleton(骨架条 + 按语言预留高度:svg/mermaid 320、html 420、present-files 480)② 源码隐藏时机从"卡片就绪"提前到检测到成对闭合围栏(分别数开/闭标记,流式中间态不会被误判),隐藏与骨架落在同一轮提交,不存在"卡片没出来源码已被藏"的空白窗口 ③ 宿主始终读不到块时还原源码(retryGiveUpMs,默认 6s)——这是与 v1.6.2 那次回退的关键区别 ④ 卡片 180ms 淡入,prefers-reduced-motion 下关闭 ⑤ 重试从固定 2s 改为递增退避阶梯。同时修掉一个长期失效点:源码围栏定位此前依赖 code.language-mermaid,而宿主渲染器的 CodeBlock 除空围栏外从不输出 language-* 类,导致隐藏逻辑一直空手而归;现在按真实 DOM(div.md-code-block + 横幅 infostring + 源码首行围栏标记)定位,并与宿主解析结果按序号对账,对不上就放弃隐藏

  • 1.6.9 展示详情弹窗(WorkBuddy vizcb-detail-spec 落地,升级现有灯箱):新增「渲染 | 源码」tab(同会话记住上次选择);渲染 tab 支持滚轮缩放(0.2x–8x)+ 拖拽平移;源码 tab 以原始文本(textContent 直出,非消毒产物)等宽显示、横向滚动、超长(>200KB/2000 行)截断提示且下载仍是完整内容;底部操作栏:复制源码 / 下载文件(按类型命名 .svg/.html/.mmd)/ 在新窗口打开(svg/html);ESC/×/遮罩三种关闭 + 遮罩拖动后不误关 + 焦点管理;卡片按钮「放大」改「详情」

  • 1.6.8 提示词规则:输出位置——代码块必须紧跟在引入它的那句话之后输出,禁止把所有代码块堆积在回答末尾;每个代码块之前用一句话说明这张图展示什么,之后继续正文讲解;多个图时图文交替分布(解决"图来得晚"与"图和上下文脱节",观感提升明显)

  • 1.6.7 修复"助手消息的图不渲染":DSH 把消息里的代码块按结构化存储({type:code, language, text},文本无围栏标记),提取时只认 .text 原文导致围栏丢失、检测不到块 → extractEventText 现在会识别图表语言的结构化代码块并还原 ```<lang> 围栏(含 collectText 兜底路径),纯文本带围栏的旧格式不受影响;新增 2 个单测覆盖两种存储形态

  • 1.6.6 提示词规则补全:timeline 图 period(时间)内禁止冒号(: 是时间与事件分隔符,10:33 解析失败),用 10点33分/10时33分 或日期格式,描述写在冒号后(已用渲染器验证:10:33 与 10:33 启动 均 FAIL,10点33分 启动 OK)

  • 1.6.5 提示词规则补全:subgraph 标题含空格/括号/中文等字符时用双引号包裹(subgraph "本地 PC 日志(规格)"),标题内禁止 [ ] { } 等语法字符,节点必须另起一行不能与 subgraph 同行(修"subgraph 标题带节点 → Lexical error")

  • 1.6.3 提示词规则:mermaid 节点/边标签含 |、引号、<、> 等特殊字符时用双引号包裹整个标签(F["A | B"]),| 是连接线标签定界符裸写会解析失败;<br> 用于标签内换行

  • 1.6.2 评审修订:移除 CSS 全局隐藏兜底(pre:has(...) 会吞掉渲染失败的图——卡片没出来源码已被藏,且选择器作用于整个页面的历史消息)——隐藏完全交给 JS 按"本条消息实际解析出的块"精确处理,并加容器查找三重守卫(深度上限 / 不越过 body / 命中数上限,异常即放弃隐藏);sanitizeSvgText 消毒失败改为失败即拒绝(返回空,不再放行未消毒原文,非字符串结果同样拒绝);mermaid GET 兼容通道补 maxBlockChars 上限(与 POST 一致);worker 的 DOMPurify 绑定补注释说明(防御性保留,非笔误)

  • 1.6.1 只留渲染结果:hideSourceBlocks(默认开)隐藏本插件渲染过的源码围栏(svg/html/mermaid 及其方言别名 + present-files),仅当宿主确认解析出对应块时按解析语言精确隐藏(含流式渲染期间的 MutationObserver 补隐藏);源码仍可通过卡片"复制"获取,普通代码块(js/py 等)不受影响

  • 1.6.0 可预览文件(WorkBuddy html-preview-spec 落地):模型输出 ````present-files指令 → 宿主校验路径(resolve + 工作区包含性)→ 同源/vizcb/p//` 提供预览(随机 token + 越界防护 + 大小上限 + no-store)→ 客户端预览面板 + 文件卡片(点击切换 / 复制路径 / 打开所在目录 / mtime 轮询自动刷新 / 关闭后不再自动弹开);iframe 仅 `sandbox="allow-scripts"`;提示词新增"可预览文件展示规范"(含"不复述内容"规则)

  • 1.5.0 代码评审落地:mermaid 渲染移入独立 worker_threads(不再污染宿主 globalThis);SVG/mermaid 输出宿主端 DOMPurify 消毒后才返回客户端;mermaid 渲染全局限流 + 同 key 在途去重 + 串行化;readTurn 重试缓存修复(可重试失败不再入缓存);调试日志改配置项 debugLog(默认关);injectBootGlobals 转义 </script>;主题适配(canvasBg 配置化 + mermaid dark 自动检测/可强制);灯箱与卡片同套 SVG 校验;node --test 用例 + GitHub Actions CI;install-vizcb.ps1 按 files 白名单复制(排除 .git/test)

  • 安装脚本/文档:install-vizcb.ps1 加 UTF-8 BOM(无 BOM 时中文注释在 Windows PowerShell 5.1 下被按 ANSI 解析导致脚本解析崩溃);README 补充独立 dsh web(web profile)实测可用

  • 1.0.0 动态插件移植为 bundle(路由 fetch)

  • 1.1.0 加固:SVG 校验 / 图注标题 / 复制缩放 / mermaid / 配置化 / 缓存 / 自检 / 限流 / 网格 / i18n

  • 1.2.0 全屏灯箱放大

  • 1.2.1 mermaid 方言别名 + 普通代码块静默

  • 1.3.0 mermaid 改宿主端渲染(弃 CDN/iframe)

  • 1.3.1 卡片版本徽标 + fetch+inline

  • 1.3.2 请求级调试日志

  • 1.3.3 箭头可见性增强

  • 1.3.4 SVG 校验对齐渲染路径

  • 1.4.0 保存导出(PNG/SVG/HTML)

  • 1.4.1 修复深色主题渲染(dark 标志丢失导致浅色主题);mermaid 配色对齐宿主色板(文字 #E5E7EB、连线/箭头 #4F8CFF);节点文字自适应(自动扩宽矩形 + viewBox)

完整踩坑历程见 DEVELOPMENT.md。

About

DeepSeek Harness 可视化插件:svg/html/mermaid 代码块渲染为图表卡片(宿主端 mermaid、深色主题配色、节点文字自适应、灯箱、保存导出)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages