中文EN

@yfwu2020/dsh-reply-visual

docsnpmMIT

在 DSH Web 界面每条定稿回复下加一个「图解」按钮:模型产出单文件 HTML(内联 SVG),右侧栏以沙箱 iframe 渲染;带图解/源码/原文三个视图、命中磁盘缓存即回放(不再调用模型)的会话内历史列表、重画与另存 .html。

下载量 / Downloads
583

安装

dsh plugin --profile web add @yfwu2020/dsh-reply-visual

沙箱测试

这个插件还没测过。提交测试 →

README

dsh-reply-visual · 回复可视化(图解)

在 DSH Web 界面里,把一条 AI 回复变成一页看得懂的图解:
每条定稿回复下面多一个「图解」按钮 → 点一下,右侧栏打开「图解」页——
模型直接产出单文件 HTML(内联 SVG),在沙箱 iframe 里渲染。

![release](https://github.com/yfwu2020/dsh-reply-visual/releases)

![npm](https://www.npmjs.com/package/@yfwu2020/dsh-reply-visual)

![license](./package.json)

![DSH plugin](https://github.com/yfwu2020/dsh-reply-visual)


它解决什么问题

长回复难读。一段三千字的分析里,真正的结构(三个层次、两条分支、一个取舍)是空间关系,

而 Markdown 只能把它摊成一条竖线——你得在脑子里重新拼一遍。

这个插件把那条回复交给模型,让模型画一页:摘要卡、流程图、时间线、对照矩阵、结构树、

示意图……画成什么样完全由模型决定。插件只做两件事:把回复交给模型,和给一个沙箱。

它和"让模型输出 Mermaid"的区别在于:没有图表 DSL、没有内置渲染器、没有图型白名单。

模型直接写一页 HTML + 内联 SVG,所以能画的东西只受表达能力限制,不受图库限制。

长什么样

页头(模型/档位/耗时/字节)· 三段切换「图解 / 源码 / 原文」·

「历史」「重画」「字号」「复制源码」「保存 .html」。

<img src="assets/panel-light.png" alt="图解页:工具栏 + 模型画出的整页图解" width="460">

<table>

<tr>

<td><img src="assets/panel-dark.png" alt="深色主题" width="330"></td>

<td><img src="assets/history.png" alt="历史下拉:按对话顺序排、可筛选、可跨会话" width="330"></td>

</tr>

<tr>

<td align="center"><b>深浅两套配色</b>(页面自己带一套,跟随系统)</td>

<td align="center"><b>历史</b>:本会话画过的页,按对话顺序排,点了直接回放</td>

</tr>

</table>

三个视图各管一件事:

视图 · 内容

图解 · 流式期间是进度卡(已画多少字 / 秒表),整页到齐后换成模型画的页面

源码 · 模型产出的 HTML 原文(可复制)。页面不完整时这是唯一出口

原文 · 这条回复本身,用 DSH 自带的 MarkdownText 渲染——不调模型、不截断,永远可用

「原文」是兜底,也是「图看不懂时回去看原话」的去处。

等待期:20~70 秒不是空白

生成一页要 20~70 秒(长回复实测 57s / 64s)。这段时间侧栏分三段,各管一段、不重复表达:

<table>

<tr>

<td><img src="assets/reading.png" alt="理解中:一道匀速向右游走的声波" width="330"></td>

<td><img src="assets/waiting.png" alt="绘制中:线稿按字数逐行长出来,末尾浮出橙色光标" width="330"></td>

</tr>

<tr>

<td align="center"><b>① 理解中</b>(还没有产出)<br>声波——只表达"在活动",不确定态不假装有进度</td>

<td align="center"><b>② 绘制中 → ③ 收尾</b><br>线稿按字数生长;画完后末尾浮出闪烁光标</td>

</tr>

</table>

状态行给真实数字(已画字数 + 秒表),动画只负责"还在长"——两者分工。

不显示百分比:总量未知,报百分比就是编的。

历史与翻页

同一会话里画过的页都能回去,三个入口殊途同归:

入口 · 定位到

每条 AI 回复下面的「图解」 · 就是你点的那一条

会话头部的「图解」图标 · 本会话上次画的那一条

工具栏的「历史 N ▾」 · 本会话任意一条(下拉列表)

列表按对话顺序排(最早的一轮在最上面,和聊天记录同向),不是按"什么时候画的"——

重画一条旧回复只会刷新它的时间,位置不动。位置的依据是会话日志里那条

assistant/message 的序号(seq,会话内单调递增),它天然等于"这段对话的第几步";

定位不到的旧条目退回按生成时间排,并统一排在最后。

  • 本会话:一条平铺的列表,不分组(只有一段对话,给分组头是噪音)。行里给时刻、

相对时间、字节数,以及第几轮。

  • 全部会话:按会话分组,会话之间"最近活跃的在前",组内仍是对话正序;分组头是

会话标题(host 从会话日志折出来的,取不到就显示会话号前 8 位),右侧给

"几条 · 最近什么时候画的",当前会话标「本会话」。

筛选框、↑↓ 选择、↵ 打开、Esc 关闭、「全部会话」开关都在。

「页面自己的标题优先」——显示的是产出 HTML 里的 <title>/<h1>/<h2>,

不是回复首行那句话片段。

翻页贴在页面右缘(▲ N/M ▼),鼠标悬停才显形:序列和下拉同一个顺序

(对话正序),到头循环,只有 ≥2 条时才出现。切页不闪骨架屏(保留当前页,只转一个小圈)。

回放走 /result,token 命中即返回——一次模型都不调。

安装

# 从 npm(推荐)
dsh plugin --profile web add @yfwu2020/dsh-reply-visual

# 或从 GitHub Release 的 tgz(同一个产物,不走 npm)
dsh plugin --profile web add /path/to/yfwu2020-dsh-reply-visual-0.2.0.tgz

# 或从本地目录(开发用:改完不用重装,只重建)
dsh plugin --profile web add link:/path/to/dsh-reply-visual

它在 DSH 里落成三处:

位置 · 内容

~/.dsh/profiles/web/package.json · "@yfwu2020/dsh-reply-visual": "<版本或 link:路径>"

~/.dsh/profiles/web/node_modules/@yfwu2020/dsh-reply-visual · 软链 → 插件目录

profile 的 dsh.profile.bundles · 包名(按包名引用,与路径无关)

⚠️ 装完要重建进程:host 半是新的 bundle,改完需要重启 dsh web(或走你现有的热重载通道),
之后硬刷新浏览器(Cmd/Ctrl+Shift+R)。client 半之后的改动会被 client-hmr 就地替换,不用刷新。

要求:DSH 运行时 >=0.1.7-rc(见 package.json 的 peerDependencies)。

0.1.7-rc 起 createSystemMessage 去掉了插件署名参数,更旧的运行时上行为会不一致。

另外 npm 上这些包的 latest 还停在更旧的 0.0.1-rc.x,缺 WebServer 等 API,装上会起不来。

配置项

改 profile 的 cordis.patch.yml 里 id: reply-visual 那一行的 config:。

模型与生成

键 · 默认 · 说明

provider / model · 空 · 固定模型路由;留空 = 跟随「设置 → 默认模型」

reasoningEffort · low · 画图靠长输出、不靠深推理;要更讲究可调 high

temperature · 0.4 · 画图要一点自由度;别调到 1 以上

maxTokens · 0 · 单页输出上限。默认 0 = 不传这个参数,交给模型/供应商自己的上限(早先默认 8000 会把页面切在 SVG 中间)

timeoutMs · 180000 · 单次生成超时

maxSourceChars · 24000 · 原文上限,超出取头 60% + 尾 40%(结论在尾部,不能砍尾)

页面渲染与手感

键 · 默认 · 说明

allowScripts · true · iframe 是否放脚本;false = sandbox="" 纯静态

wheelSmooth · true · 滚轮走"跟手 + 惯性"(触摸手感);false = 中间段落交回原生滚轮,只在边界做橡皮筋

wheelSmoothFactor · 0.22 · 惯性逼近系数(0.02~0.8);越小越"拖沓",越大越"利落"

elastic · true · 橡皮筋总开关(prefers-reduced-motion 下自动关)

elasticMax · 140 · 橡皮筋最大可拉距离(像素);0 = 关闭

elasticResist · 0.5 · 橡皮筋阻尼(0.05~1,越小越"沉");曲线非线性,越拉越沉

elasticStiffness · 0.18 · 回弹刚度(0.005~0.3,越小越软);配 elasticDamping 0.52 时不过冲

elasticDamping · 0.52 · 回弹阻尼(0.4~0.98);调高会重新开始过冲、弹两下

elasticTop · false · 顶端是否也做橡皮筋(默认关:到顶继续拉没有过拉效果)

elasticMaxHoldMs · 10000 · 橡皮筋硬上限(0 = 不限):到点强制回弹,继续拉伸不重置

elasticGestureGapMs · 120 · 两次滚轮/触摸之间超过它就当作"新的一次操作" → 立刻弹回

bottomGap · 0 · 页面底部留白像素(默认关):让最后一行避开右下角胶囊;观感是死空白,所以改用 elastic 解决

maxPageBytes · 2000000 · 「页面偏大」的提示阈值(不是拒绝线;只有 >16MB 才拒绝渲染)

缓存

键 · 默认 · 说明

resultCacheTtlMs · 0 · 回放的时间判据:默认 0 = 不限时(文件在就回放);设正数才会到点强制重新生成

maxCacheEntries · 200 · 条数上限:result/ 与 prepared/ 各自保留最新这么多

cacheMaxBytes · 33554432 · 缓存总字节预算(32MB):超了从最老的开始删

cacheMaxAgeMs · 0 · 按龄清理(0 = 不限龄)

maxRequestsPerMinute · 20 · 本地限流

自定义这两份规范

键 · 默认 · 说明

skillPath · 包内 skills/visual-page/SKILL.md · 可视化规范(做什么 + 环境是什么样)

designPath · 包内 skills/page-design/SKILL.md · 审美参考(怎么做得好看);填 off = 不注入,留空 = 用包内那份

缓存会一直涨吗

不会无界增长——生成成功后自动清扫,启动后 5 秒也会扫一次。四道闸门:

闸门 · 默认 · 清什么

按条数 · 200 · result/ 与 prepared/ 各自只留最新 200 份

按总字节 · 32MB · 整个缓存超预算时从最老的删起

按龄 · 关 · 想自动过期就设 cacheMaxAgeMs(比如 7 天)

…(truncated)

以上内容来自 npm 上的 README,我们只做格式化,不改内容。

版本

npm 上共发布:3,最后更新 2026-09-28

链接

GitHub · Homepage · npm

同类插件