dsh-progress-caption
A one-line caption above the composer that tells you what the AI is doing right now: the step number, the action (e.g. running npm test), and whether it is still going, done, or failed with a short plain-language reason. Inferred locally from tool calls and the thinking text; no model calls, no netw
Install
dsh plugin --profile web add dsh-progress-caption
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-progress-caption 思维链进度字幕

在 DSH 的输入框上方加一行小字,回答三个问题:现在第几步、在干什么、成没成。
给新手用:不用点开思考过程,也能看懂 AI 卡在哪一步。
第 3 步 · 正在执行命令 npm test · 琢磨中 第 2 步 · 正在读取配置文件 · 酝酿中
外观是一条圆角胶囊:DeepSeek 蓝的淡底 + 蓝点 + 蓝色步骤号,成功转绿、失败转红。
主文案始终是一整句(动作 + 具体命令/文件名),不会再被挤成两个字。
免责声明不再占着这一行——只留在鼠标悬停的提示(title)里。
怎么装
推荐:在 DSH 的插件市场里装。 打开插件市场(发现页)搜 dsh-progress-caption,点安装。
市场会从 npm 拉预构建的包,不用编译、不用装依赖,装完就能用。
命令行装也可以,让 agent 调 plugin_manager → install_bundle,target 填插件目录。
包也发在 npm 上:
npm i dsh-progress-caption
不过要注意:只用 npm i 不会自动注册成 DSH 插件,它只是把代码下载下来。想让 DSH 加载它,还是得走插件市场或 install_bundle。
手动装(从源码):
git clone https://github.com/wanted09527/dsh-progress-caption.git
然后在插件管理器里用克隆下来的目录安装。装完不需要重启:新 bundle 会通过热加载生效;替换已装包的代码才需要重启。
不做什么
- 不调模型、不发任何外部网络请求、不做翻译。
- 不改 DSH 的任何数据:只读界面已经推过来的快照;设置只存在浏览器本地
localStorage。 - 不做历史归档、没有独立的设置页(设置就在字幕条自己的小面板里)、没有侧栏面板(留给第二版)。
说人话:三层「它到底在干嘛」
全部靠本机规则和数据推断,不联网、不调模型。按准确度排序,上一层说得出来就不用下一层:
层 · 数据来源 · 例子
① 工具事实 · 正在跑/刚跑完的工具 + argsRaw · 正在执行命令 npm test、正在读取 client.js、跑完命令 npm test
② 规则表 · 思考文本末尾 400 字(17 条规则) · 正在理解你的需求、正在定位问题、正在对比方案、正在评估风险
③ 阶段话术 · 本回合已完成工具的类型序列 · 看完了 3 个文件、刚改完 client.js、命令跑完了
④ 空档事实 · 刚跑完的那条工具 + 它花了多久 · npm test 跑完了 · 1.2s;这一轮还没跑过工具就交给俏皮词
⑤ 俏皮词 · 112 个词轮换 · 前面都说不出话时,酝酿中 / 琢磨中 / 翻箱倒柜 / 摸鱼中 顶上
工具动词分组(不再是「正在执行 读取文件」这种机翻腔):
工具 · 运行中 · 完成
bash / pwsh · 正在执行命令 · 跑完命令
read / read_image · 正在读取 · 读完了
glob / grep / search_context · 正在搜索 · 搜完了
write · 正在写入 · 写好了
edit · 正在编辑 · 改好了
web_search / web_fetch · 正在搜网络资料 · 查完资料
subagent / subagent_fork / send_message · 派了小助手 · 小助手回来了
compress / mnemon_* · 正在翻记忆 · 翻到相关记录
present · 正在整理交付 · 整理好了
todo_write · 正在列计划 · 计划列好了
job_output / job_kill / list_agents · 正在等后台任务 · 后台任务有结果了
ask_user_question · 正在等你回话 · 你回话了
skill · 正在调技能 · 技能调完了
对象取自 argsRaw,按下面的先后顺序取第一个有的:command / cmd、path / file_path / files /
notebook_path(这四个都只留文件名)、description、pattern、query;以上都取不到时,用该组类
自带的兜底名词把动词补成一句完整的话(如「正在读取文件」「正在搜索代码」),不留「正在读取 」这种
半句,也绝不猜;太长会截断成 …,不会把字幕条撑爆。
分组是按真实会话里 6783 次工具调用的name分布补的:ops_bash1580 次、pwsh1465、
read845、edit696 之外,read_image330、mnemon_*392、compress92、present50
这些以前全掉进 other,只能显示「正在执行 read_image」这种把工具名糊上去的句子。
节奏:为什么字幕不逐字跟着思维链
v4.1-flash 这类模型思考极快,字是一串串来的。如果字幕逐字跟,会出现一秒跳三次的闪烁。这里的策略是:
机制 · 值 · 作用
常驻不闪没 · — · 思考中/跑工具/写回答,字幕一直在;整轮结束才收成齿轮。两步之间 partial 会短暂为空,这时说「刚才那件事 + 花了多久」(如 npm test 跑完了 · 1.2s),最长容忍 20 秒空档(TURN_GAP_MS),不会因此收起来;这一轮还没跑过工具就只显示俏皮词,不写「正在准备下一步」这类空话。步号整轮常驻:partial 空档时沿用上一个非空步号,跨轮才清空
事实优先 · — · 有工具在跑就用工具信息(准确),思考文本只作兜底
标签稳定窗口 · 520ms,快思考时 800ms · 候选文案要稳住这么久才真正换上去。前 3 步(SETTLE_STEPS)连淡入都不播,纯硬切换——那几步状态翻得最勤,淡入叠起来就是「闪」
最短停留 · 2600ms · 状态结束(开始写回答/工具结束)后,最后一条至少再挂 2.6 秒,想得再快也来得及读完
快思考判定 · 每秒新增 40 字 · 超过就把切换压到 800ms 一次,字再快也不会闪
计时刷新 · 无 · 条上那个每秒跳的耗时已整条移除,组件不再为它每秒重渲染(只剩俏皮词每 2.8 秒换一次)
同一个动作,换着说法说
字幕最容易犯的毛病是单调:每次读文件都说「正在读取配置文件」,看十次就麻木了。这里做了两层:
1. 规则库带变体:17 条规则每条配 3 种说法,按「第几回合 + 第几步」确定性轮换
(翻一下配置文件 / 看看配置里怎么写的 / 对一下配置项)。不随机跳,同一个动作在同一回合里不会来回变。
2. 副行挑一句有信息量的思考:把「思考原话」开关打开,主文案下面多一行模型自己的思考。
不是照抄第一句,而是从这段思考里挑一句——优先带判断/权衡/卡点的(「因为…」「问题是…」
「卡在…」「风险是…」),其次带具体东西的(路径、文件名、数字、命令)。
第一句几乎总是「Now let me…/让我先看看」这种过场话,所以会被跳过,句首的口语垫词也会被剥掉。
全程只截断、不改写、不润色,所以不会猜错;一段里实在挑不出一句像样的话,这行就不显示。
第 6 步 看看配置里怎么写的
真正的问题是这条字幕没有固定宽度,每次换词都会把左边的字挤走 ◠ [盘算中]
关着就只有主文案,界面更紧凑。
(早期版本这里叫「行数 1 / 2 / 3」,但主文案是拼好的整句,几乎不折行,开到 2、3 看不出区别,
那三项实际只起到「要不要显示副行」这一个开关的作用,所以后来换成了这条说人话的开关。)
思考期间俏皮词一直轮换(每 2.8 秒换一个):有事实文案时它挂在最右端当陪衬,
前两层都说不出话时它才顶上当主文案——就是 Claude Code 等待时闪 Brewing / Pondering / Noodling 的那种效果。
模型开始写回答时显示「正在组织回答」,一样常驻不消失。
觉得还是太快/太慢就改client.js顶部的MIN_SHOW_MS(默认 2600)和OK_LINGER_MS(成功后停留,默认 5000)。
回合小结
每个回合结束时,在对话流末尾那条汇总行前面加一行:
本回合 · 3 次工具 · 2 成 1 败 · 用时 12.4s
只数事实(工具结果条数、成败、耗时,用 seq 卡住回合边界),没有工具调用就完全不显示。
这样滚动回看历史时,能一眼看出那一回合干了多少活。
小动画与俏皮词
两种动画(设置面板里选):
选项 · 表现
安静(默认) · 左侧三颗小圆点接力跳,周期 1.1s——一跳一落地,主文案完全静止
扫光 · 同一组点跳得快一档(0.9s)+ 一道光从左往右横扫整条字幕(不局限于第一行,整框一起扫),光带宽 = 整框宽,只动 transform,不推挤布局
老存档里如果还留着 anim: 'off',读设置时会被折回「安静」——.pgc-anim-off
那套全静止样式仍留在 CSS 里当兜底,只是面板上不再提供这个选项。
为什么默认是「安静」:思考时文字本来就在飞快地长,字幕条再跟着流光、再跟着跳,两个动的东西
叠在一起就是「一直在闪」。默认档只让左侧三颗点慢慢跳,主文案是静止的。
成功 / 失败时点继续跳、只换成绿 / 红(不冻住,理由见下文)。
三颗点仿的是 Claude 主题插件 dsh-claude-style 的 await loader(见
node_modules/dsh-claude-style/lib/client.js 的 .dsh-claude-chat-wait-dot 与
@keyframes dsh-claude-wait-dot):
- 容器
.pgc-loader写死 18×14,三颗 4px 的点绝对定位在left: 0 / 7px / 14px; - 同一条
@keyframes pgc-dot-bounce(0%贴地压扁scale(1.7,.25),40%起跳收回比例,
100% 到最高点 -8px)+ infinite alternate——一条动画既去又回,不用写两组关键帧;
- 三颗的负延迟分别是
-1 周期/-3/5 周期/-2/5 周期:任何一帧都有一颗在最高点、
一颗在半空、一颗刚落地,读起来是「接力跳」而不是三颗一起蹦;
- 只动
transform,容器总宽固定,所以跳起来也不会推动右侧文案。
状态标记:经典三点 / Claude 三态
左边那组点有两种,设置里用开关切换:关着是经典三点(上面讲的那种,一直原地接力跳),
打开是Claude 三态,它会每 6 秒(SHAPE_MS)换一种形态,三种轮着来:
形态 · 长什么样 · 来源
bounce 弹跳 · 三颗原地接力跳,周期 .5s,起跳前先压扁 · Claude 的 @keyframes dsh-claude-wait-dot
line 斜线 · 三颗沿一条斜线往返,中途缩到 55%,周期 1.25s · @keyframes dsh-claude-wait-dot-line
orbit 绕圈 · 四颗沿一个椭圆轨道转,相位各差 1/4,周期 2.4s 匀速 · @keyframes dsh-claude-wait-dot-orbit-1..4
实现上只有一套 DOM:<span class="pgc-loader" data-pgc-shape="bounce|line|orbit|plain">
里常驻四颗 .pgc-dot,形态名放在 data-pgc-shape 上,由 CSS 属性选择器决定挂哪条动画。
四颗是因为绕圈要四颗,弹跳 / 斜线只用前三颗,多的那颗由
.pgc-loader:not([data-pgc-shape="orbit"]) .pgc-dot:nth-child(4) { display: none } 藏掉
——切换形态时结构不变、不重排。
参数全部抄自 dsh-claude-style(--dsh-claude-distance-travel: 8px、
period-dot-bounce: .5s、period-dot-line: 1.25s、period-dot-orbit: 2.4s、
scale-dot-mid: .55),只是把变量名换成 --pgc-*。绕圈的四条轨道做了简化(每条 8 个采样点
而不是原版的 17 个),椭圆尺寸与相位保持一致。想关掉它就把那个开关关回去(经典三点)。
**耗时的位置换成了一个
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::3,最后更新 2026-10-24