DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Turn Fold — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins

@winteries/dsh-turn-fold

Turn Fold

Enhanced collapsible sections: automatically collapses tool calls and Think segments into step sections, then collapses the entire turn at the end into a turn section with real-time metrics (duration/first token/tokens/tok/s/cache hits), leaving only the final summary text. The leading icon is custo

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add @winteries/dsh-turn-fold@0.5.3
READMECompatibilityVersions

Description

Enhanced collapsible sections: automatically collapses tool calls and Think segments into step sections, then collapses the entire turn at the end into a turn section with real-time metrics (duration/first token/tokens/tok/s/cache hits), leaving only the final summary text. The leading icon is customizable, with built-in dynamic playing cards (a looping suit card animation while running, a collapsed deck, and an expanded fan). Provides an official collapse method settings row and supports both new and old versions of DSH. No changes to DSH source code.

Compatibility and provenance

Turn Fold is published as @winteries/dsh-turn-fold and currently resolves to version 0.5.3. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
npm
Registry updated
9/18/2026

Versions

0.5.3stable
9/18/2026
0.5.2stable
9/17/2026
0.5.1stable
9/8/2026
Show 2 more versionsCollapse versions
0.5.0stable
9/8/2026
0.4.0stable
8/29/2026

Related plugins

Loading related plugins…

Latest
0.5.3
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
1.1 MB
Files
16
Surface
web
License
MIT
Source
npm
GitHub
★ 11
Weekly downloads
0
Last push
9/18/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in ui-customization.

Web App@deepseek-ai/dsh-web-appThe dsh browser-surface bundle: the web patch layer over dsh-base plus the runtime glue plugin (frontend dist serving, web-surface prompt, bash runtime variables, URL line)Experimental Agent Team Web Profile@deepseek-ai/dsh-experimental-agent-team-web-profileExperimental Web profile layer for Agent Teams Remote and UI pluginsRemote Web Ui@linxin666/dsh-remote-web-uiScan-to-pair remote access for the dsh web GUI that shares one official interface: a QR beside the settings button pairs phones and PCs into the same Web GUI (a portrait-touch adaptation layer for phones, full desktop on PCs) through one-time tokens and rClient Ui Task Board@linxin666/dsh-client-ui-task-boardHost-authoritative task board for the DSH Web GUI with real session execution, Host cron scheduling, and optional cross-platform idle-sleep protection; mounted without DSH source changes.

README

dsh-turn-fold

简体中文(默认) | English

受够了几十条工具调用占满屏幕? 也眼馋隔壁 Codex 的自动折叠? 那这个插件就是为你准备的。

DeepSeek Harness(DSH)纯插件,只负责折叠:

  1. 步骤分组自动折叠:两个 text 之间的所有工具调用和 Think(含纯 Think 段)收成一个步骤折叠栏,默认折叠;运行中步骤折叠栏动态显示「正在运行 图标 工具名 · 描述 / 正在思考 图标 Think · 内容」(文字带 shimmer 光泽动效),下一个 text 出现后按工具类型分组显示详细标题(如「运行了pwsh」「读取了client.js」「编辑了index.js [ +12 -3 ]」),纯 Think 段闭合后显示「思考了N次」。
  2. 运行中回合折叠栏:发消息即出现(0 秒占位,不等第一个 response),折叠栏实时显示耗时/首字/消耗token/tok/s/缓存命中率/待折叠步数,最右侧右对齐显示「第x轮」;折叠栏与内容之间有分隔线。
  3. 整回合折叠:一轮回复完成后自动收成一个回合折叠栏(默认收起),最终总结只显示正文。
  4. 手动展开/收起:点击折叠栏切换。

不修改任何 @deepseek-ai/dsh-* 源码。

功能一:步骤分组自动折叠

text:先看仓库状态和改动规模:                        ← text 直接显示
┌────────────────────────────────────────────────────┐
│ › 正在运行 ⬢ Pwsh · Commit 1: core +tests          │  ← 运行中:图标 + 工具名 + 参数摘要
└────────────────────────────────────────────────────┘
text:……                                             ← 下一个 text 出现
┌────────────────────────────────────────────────────┐
│ › 编辑了client.js [ +7 -7 ] 运行了2条命令           │  ← 段闭合:按工具类型分组显示
└────────────────────────────────────────────────────┘
  • 段 = 两个 text 之间的内容:连续的工具调用与 Think 混排成一段(Think 不再打断分组), 含 text 的消息是段边界;text 正文始终在步骤折叠栏下方直接显示(官方渲染,唯一一份, 不参与折叠——DSH 把 think 和 text 放在同一节点,think 部分收进段、text 部分留在段外)。
  • 默认折叠:步骤折叠栏始终默认收起(运行中也不例外)——运行中只显示 text 和步骤折叠栏行, 工具卡片/Think 内容点击步骤折叠栏才展开。
  • 运行中动态标题:段未闭合(下一个 text 还没出现)时,步骤折叠栏显示段内最后一个节点—— 工具调用显示「正在运行 图标 工具名 · 参数摘要」(工具图标复用官方 VARIANT_ICONS 映射, 如 Pwsh → API 图标、Read → 浏览图标、Grep → 搜索图标), Think 显示「正在思考 图标 Think · 最新一行」(前缀 + 官方 Think 图标 + 摘要,摘要取 最新一行、横向自动滚动跟随末尾,内容随流式逐字推进);运行中标题文字带shimmer 光泽 扫过动画(灰色基调 + 高光流动,节奏为流动 1.8s + 停顿 2s,亮/暗主题各自配色)。
  • 段闭合标题(按工具类型分组):下一个 text 出现后,按段内工具类型分组显示—— 仅命令:运行了pwsh(单次显示工具名)/ 运行了3条命令(多次显示次数); 仅读取:读取了client.js(同一文件显示文件名)/ 读取了2份文件(多文件显示数量); 仅编辑:编辑了index.js [ +12 -3 ](单文件附加行数变更,从官方 diffs 数据读取)/ 编辑了3份文件;搜索:搜索了2次; 混合时按「读取 → 编辑 → 搜索 → 命令」排序且命令始终在最后, 如 读取了client.js 编辑了App.tsx 运行了2条命令;纯 Think 段(段内无工具)闭合后显示 「思考了N次」(N = 段内 Think 次数)。
  • 手动可展开/收起:点击步骤折叠栏切换;手动选择会覆盖自动规则。
  • 失败命令标红:组内已有命令执行失败(工具结果 isError,含中断)时,折叠栏文字变红, 并在标题后追加失败数——仅单条工具调用失败显示「 —— 执行失败」(无条数), 多条工具调用时 1 条失败也显示「 —— 1条执行失败」、多条显示「 —— y条执行失败」。 失败统计涵盖段内所有工具类型(read/edit/search/命令都算)。

效果示意

段闭合标题:按工具类型汇总 + 编辑行数统计([ +11 -11 ],悬停括号 +N 变绿 / -N 变红):

段折叠栏:编辑了 client.js [ +11 -11 ]

标题里的文件名可点击复制完整路径:悬停变 DeepSeek 蓝 + 白色下划实线:

文件名悬停

功能二:运行中回合折叠栏 + 整回合折叠成一个回合折叠栏

[用户消息]
[▸ 耗时5分12秒 · 首字1.2s · 消耗12345token · 34tok/s · 缓存命中80.00% · 待折叠6步        第13轮]  ← 回复开始即出现的回合折叠栏
─────────────────────────────────────────────          ← 分隔线
[Think / 工具调用逐条加载…]                             ← 运行中默认折叠成步骤折叠栏
[最终总结正文]                                           ← 无 Think 行,只有正文
[耗时 · token 脚注]                                      ← 官方 turn-tail
  • 发消息即出现回合折叠栏(0 秒占位):用户发送消息后立即出现回合折叠栏(耗时从 0 开始计时), 不等第一个 response——占位栏渲染在 user 消息正下方(官方「状态描述」行 "Deep diving..." 之上,与正式回合折叠栏同位置、交接无位移),第一条中间节点到达后 占位消失、正式回合折叠栏接替显示;
  • 指标实时更新:回合折叠栏中的耗时秒数每秒走动(从回合 turn/start 起计时), "消耗token"按随机间隔(默认 125~250ms)刷新且持续增长,tok/s 按已输出 token / 已耗时实时估算, 缓存命中率显示两位小数(如 80.00%),**首字(TTFT)**在第一个请求完成 (step settle)后即显示官方值(assistant-step 的 finalNode.timing: firstTokenTime - stepStartTime),回合结束后切换为官方持久化聚合值 (turn-tail 携带的 ttftMs,来自事件日志,刷新页面不丢);仅当首个请求仍在流式时 用渲染时刻近似(回合启动到首个 assistant-step 渲染); 回合结束后全部指标切换为官方权威值(turn-tail 的 tok/s、turn/end 的精确耗时); 消耗token 亦切换为 turn-tail 携带的官方 tokenUsage 精确值(见下);
  • 回合折叠栏最右侧右对齐显示"第x轮"(如 第13轮 / Turn 13,英文随 DSH 语言切换);
  • "消耗token"持续增长动画:真实 usage 只在每个请求完成时到达,两次之间数字会 停住——运行中在真实基线之上叠加纯展示用的动画偏移,偏移按实际 tick 次数推进 (+1/+11 交替:个位每 tick +1、十位每 2 tick +1、更高位随进位自然走动), tick 间隔 = liveTickMs × 随机数(liveTickJitter ~ 1,默认 125~250ms), 数字跳动节奏不规律,更像真实生成速率而不是节拍器;真实 usage 到达时只把基线校 正为真实值,偏移继续累计、数字只增不减。偏移封顶:上限 = max(500, 真实基线 × 10%)(CONFIG.liveTokenAnimMaxRatio / liveTokenAnimMaxFloor,比例与下限都置 0 即关闭增长)——长时间工具执行不会把虚构数字堆到万级、失真不可信,真实值到达时 上限随基线一起抬高。基准间隔和抖动分别通过 CONFIG.liveTickMs 和 CONFIG.liveTickJitter 调整;
  • 消耗token 与官方统计同口径:回合结束后优先采用官方 turn-tail 携带的 tokenUsage(官方 deriveTurnTokenUsage 在持久化事件日志上折叠全部计费 attempt 的精确值,含被重试的请求;缓存命中率分母同为 prompt 侧总量 totalTokens - outputTokens); 节点累加路径(运行中基线、无 tokenUsage 时的回退)通过 nodes.values() 补采 visibility:hidden 的 assistant-step——纯 tool-call 的中间步骤(无可见 reasoning/text) 官方以隐藏节点结算、不进 locations.getTurn,只遍历可见节点会漏计(实测案例: 官方统计 175,844 vs 修复前 117,301,差值恰为一个隐藏步骤的 58,543);
  • 滚轮式数字动画:运行中数值变化时,每一位数字独立"滚动"到新值(里程表/滚轮效果, 回弹缓动;动画时长按变化频率自适应:token 个位这类快速变化用略短于刷新周期的短动画 保证每拍完整走完,耗时秒数等慢速变化用 350ms 回弹滚动)——数字拆成逐位视窗、内部 竖排 0-9,视觉上像计数滚筒;完整文案另有 sr-only 副本,读屏/无障碍不受影响, 系统开启「减少动态效果」时自动退化为静态数字;
  • 折叠栏下方常驻分隔线:回合折叠栏文字下方始终有一条 1px 水平细线 (颜色取官方 --dsw-alias-line-secondary token,随主题明暗自动适配), 收起/展开都显示,展开时同时充当折叠栏与内容的视觉分界;
  • 一轮回复完成(输出最终总结、回合结束)后,回合折叠栏自动收起,本回合内所有 Think、 工具调用和上下文注入收进回合折叠栏,只保留最终总结消息和官方耗时/token 脚注可见; (手动展开过的回合保持展开状态)
  • 无工具调用也折叠:回合内只有上下文注入 / Think、没有任何工具调用时,同样收成 一个回合折叠栏(折叠栏显示耗时/token 指标,不显示命令数);

效果示意

回合进行中/手动展开:回合折叠栏实时显示耗时、首字、token、tok/s、缓存命中率、 已折叠步数(右端对齐轮次),过程内容按步骤分组折叠、工具卡片与 Think 行原样可读:

回合展开态

回合结束后(或手动收起):整回合收进回合折叠栏,只保留最终总结正文与用量脚注:

整回合折叠

组件样式与行距

  • 折叠栏即官方样式:折叠栏直接复用官方 DisclosureRow 原语(@deepseek-ai/dsh-client-ui-primitives) 渲染——24px 行高、16px 前导、官方 14px chevron(收起右向 / 展开下向)、14px/24px 标题, 与 Think / 工具卡片的折叠行逐像素一致;
  • 回合折叠栏分隔线:折叠栏下方常驻一条 1px 水平细线(.ccg-turn-divider,颜色按 var(--dsw-alias-line-secondary, var(--dsw-alias-border-l1, #d1d5db)) 链式回退—— 截至目前(0.1.1 ~ 0.1.5-rc.1)DSH 均未定义 --dsw-alias-line-secondary,实际生效的 是两版共有的 --dsw-alias-border-l1,随主题明暗自动适配),收起/展开都显示, 上下留白 4px / 8px;
  • 紧凑行距:折叠组只占一行(24px);被折叠的成员节点整行 display:none,不会残留空行, 行距与官方消息完全一致(column 的 16px 节奏),折叠再多也不会越空越大;
  • 过渡动画:展开时内容从 0 高度平滑展开到真实高度(grid 轨道 0fr→1fr 过渡 + 淡入,280ms, 起始帧用 useLayoutEffect 同步提交保证过渡稳定播放);收起时播放收缩动画(280ms)后卸载内容; 系统开启「减少动态效果」时自动禁用动画;回合运行中(直播模式)内容高度自适应, 不裁切不断增长的流式内容;
  • 运行中标题 shimmer 动效:步骤折叠栏运行中标题(「正在运行…」「正在思考…」)的文字带 shimmer 光泽扫过动画——渐变背景 + background-clip: text + 背景位移动画,整行一个渐变 统一流动(高光节奏:流动 1.8s + 停顿 2s);暗色/亮色主题各有配色,图标不受影响;
  • 滚轮数字:运行中回合折叠栏的数字(耗时/首字/token/tok/s/缓存命中)按数位拆成 1ch 宽的 滚动视窗,数值变化时逐位滚动(350ms 回弹缓动);回合结束后回退纯文本;
  • 多语言:界面文案跟随 DSH 界面语言实时切换(读取 document.documentElement.lang, 简体中文 / 英语),浏览器语言仅作回退;
  • 无障碍:折叠栏带 aria-label / aria-expanded,键盘可操作(Enter / Space 切换)。

安装

方式一(推荐):从 npm 安装

本插件已发布到 npm registry:@winteries/dsh-turn-fold (旧包名 dsh-turn-fold 仍会随每次发布同步更新,供已安装旧包的用户持续获取更新;新安装请使用 @winteries/dsh-turn-fold。)

# 官方命令(推荐)
dsh plugin --profile web add @winteries/dsh-turn-fold

# 或从 GitHub 源码安装
dsh plugin --profile web add github:Winter-And-You-Gone/dsh-turn-fold

dsh plugin 会将包加入 profile 的 pnpm 依赖并自动追加到组合包层(dsh.profile.bundles),无需手动改任何文件。验证方式:

dsh --profile web --dump-config    # 确认输出中能看到 "@winteries/dsh-turn-fold" 层

然后完全退出 DSH 进程并重启。

方式二:手工 install.ps1

# 把插件目录放到你已有的插件目录,然后:
.\install.ps1 -PluginSource "<你的插件目录>"
# 例如:.\install.ps1 -PluginSource "C:\dsh-plugins\dsh-turn-fold"
# 不传参数时默认用脚本自身所在目录作为插件源

脚本会:

  1. 在 ~/.dsh/profiles/node_modules/@winteries/dsh-turn-fold 建 Junction 指向插件目录;
  2. 在 ~/.dsh/profiles/web/cordis.patch.yml 追加一行 - insert: 注册;
  3. 校验 require.resolve 可解析。

然后完全退出 DSH 进程并重启。

卸载

# 官方方式:同时移除依赖和插件层
dsh plugin --profile web remove @winteries/dsh-turn-fold

手工方式(曾用 install.ps1 安装时):

Remove-Item "$env:DSH_HOME\profiles\node_modules\@winteries\dsh-turn-fold" -Force   # 删 Junction
# 手动删掉 cordis.patch.yml 里对应的 insert 块

测试

npm install        # 首次:安装 jsdom / react / react-dom(devDependencies)
npm test           # node --test 运行 tests/ 下的全部测试
npm run check      # 语法检查 client.js / index.js

测试套件(tests/)直接加载真实 client.js(经 __ModuleLoader__ 注入 + __test 导出,无复制粘贴漂移),分六层(7 个文件):

文件覆盖
unit.logic.test.mjs纯函数:computeGroup 步骤分组、computeTurnFold 整回合折叠、computeTurnMetrics / turnHeaderLabel 指标文案、turnNumber 定位;含历史 verify-fix 的全部场景与真实会话数据(TURN13)
unit.render.test.mjsReact 渲染:初始折叠 → 点击回合折叠栏展开 → 再收起 的完整交互;委托渲染内置组件时官方 inject 面 hook 的逐名透传(useConnectionGeneration / useHostInfo,由 chatNodeEntryInject 探测合并);条目注册契约(inject 声明)
unit.css.test.mjsCSS :has() 隐藏规则在真实 DOM 上的生效(含"展开→收起"往返)
regression.test.mjs历史 bug 回归:节点对象替换(Bug1)、inject 缺失崩溃/abdicate(Bug2)、无工具调用回合折叠(v0.2.3)、折叠作用域不越过用户消息(v0.2.2)、步骤分组手动展开/收起
unit.gear.test.mjs / unit.settings-row.test.mjs齿轮字段弹窗与 shadow 官方 transcript-view 的设置行(界面标题为官方原文「对话显示」,选项 Normal / Compact / Turn-Fold)
unit.compat.test.mjsDSH 双版本兼容:0.1.1 useSession(.chat + 顶层 turnEnds/turnTimings) 与 0.1.2 useChat + chat.legacy 两条快照路径、折叠模式切换 hooks 顺序回归、官方 diffs 读取链(meta.diffs / resultView / callView)

在 Windows 沙箱等无法 spawn 子进程的环境下需要 --test-isolation=none(已在 npm test 中内置);普通 Linux/macOS CI 同样可用该参数(Node ≥ 22.9)。

折叠图标(扑克牌)

步骤/回合折叠栏的前导图标默认为动态扑克牌(回合折叠栏右侧 ⚙ 齿轮 → 弹窗里的 「折叠图标」选择器可切回官方 chevron):

  • 完成态:收起为牌堆(段内工具 ≤3 用 3 张、>3 用 5 张),点击展开变扇形;
  • 牌面池:♠ ♥ ♦ ♣ + DeepSeek 鲸鱼 Logo 五选一,每个折叠栏按 leaderKey 随机记忆(重渲染不变);
  • 运行态:步骤栏播放五牌面轮换动画、回合栏播放对角线轴翻牌(四花色循环、 Logo 背面),均为 SVG 原生动画;
  • 遮挡:luminance mask 按上层牌变换动态挖空下层覆盖区,牌身透明(壁纸/ 透明背景下正确);
  • 设置预览:4 个静态形态每秒轮换牌面(相位错开,同一时刻 4 种不同牌面)+ 两个运行态动画预览;
  • 数据源:icons/default.json(花色路径、卡牌几何、扇形/牌堆变换表、动画 模板),改完 npm run sync:icons 注入、npm run icons:check 校验。

设置弹窗(回合折叠栏字段显隐 + 折叠图标选择;预览项悬浮 2x 放大):

设置弹窗

自定义图标(Agent Skill)

想改折叠栏图标的用户不用手动操作——本插件随包注册了一个 agent skill dsh-turn-fold-customize-icons(host 半边 index.js 通过 ctx.skills 注册, DSH 0.1.2+ 装配了 @deepseek-ai/dsh-skill 时自动生效)。对 AI 助手说"帮我把 扑克牌图标改成××样式",助手会自动加载该 skill,得到完整自定义流程:

  • 数据源:icons/default.json(唯一数据源,含花色路径、牌堆/扇形几何、动画)
  • 改完同步:npm run sync:icons 注入 client.js → npm run icons:check 校验
  • 快速预览:写 localStorage['dsh-turn-fold:icons'] 可免改代码覆盖
  • 避坑指南:该环境特有的 SVG 渲染坑(fill var 属性不生效、defs fill 覆盖不掉、 clip-rule 无效、transform-origin 不可靠等)

skill 正文在 assets/dsh-turn-fold-customize-icons.md,随 npm 包 files 一起发布。

CI 与发布

GitHub Actions 会在每次 PR / push 到 main 时自动运行语法检查、npm test 全套测试和 npm pack --dry-run 打包预检;推送 v* tag 时自动发布到 npm(OIDC Trusted Publishing, 无需长期 token)并创建 GitHub Release。一次性配置(把 npm 包绑定到本仓库的 release workflow):

npx npm@^11.15.0 trust github @winteries/dsh-turn-fold \
  --repo Winter-And-You-Gone/dsh-turn-fold \
  --file release.yml \
  --allow-publish

也可以改为在 npmjs.com 网站账户设置里配置 Trusted Publishing。

之后每次发版只需两步:

npm version patch    # 或 minor / major:bump 版本并自动打 v* tag
git push --follow-tags

提示:npm version 要求工作区干净,先把待发布的改动提交;tag 名必须与 package.json 的 version 一致(workflow 会校验,不一致即失败)。

工作原理(为什么不用改源码)

  • DSH 会话 UI 是 Cordis 插件 + Slot 插槽系统拼出来的;聊天流每个块经 conversation.chat.node(keyed slot)按类型分发渲染器。
  • Slot 注册器官方支持 不同 priority 覆盖(register at a different priority to shadow it, lowest renders)。 本插件用 priority: -1 覆盖内置的 tool-call / assistant-step / context 渲染器; user 格(0 秒占位条)注册在 -2(顺序无关下限,绝不占 -1)——dsh-easyrewrite 硬编码 -1,本插件若先加载占了 -1、它后注册就会撞车抛错(真机事故:bundle 顺序 turn-fold 在 easyrewrite 前);固定 -2 后无论谁先加载都不冲突(easyrewrite 永远 -1、本插件永远 -2 或更低,注册表层面零碰撞)。仅当 -2 也被第三方占用(极罕见) 才继续下探到最低占用位 -1。并把第三方条目(如 dsh-easyrewrite 的撤回/重编辑气泡) 的组件链式委托渲染(整包 props 转发、其 inject 面的扁平 props 并入注入面)—— 占位条与 user 消息专用插件共存、功能互不丢失。⚠️ 其他想占用 user 格的插件请避开 -2 或使用探测式优先级:本插件从不主动撞已注册者(探测到更低占用即下探),但 后注册且硬编码同优先级者会自撞(slot 模型固有,责任在硬编码者)。
  • 注册冲突自动让位:注册前探测同 key/id 的 priority: -1 是否已被占用 (ctx.slots.entries),被占则自动让位到第一个不冲突的值(官方 0 恒预留,绝不 落回官方档)并打 console.warn——本插件后加载时不再与先占者冲突。 conversation.chat.node 三格(tool-call/assistant-step/context)与 settings.general.item 的 transcript-view 行都走该逻辑;user 格例外(占位条必须 渲染在 user 消息正下方,让位即弃权,且不能用探测-1 方案)——固定 -2 下限并链式 委托共存。
  • 注册异常软降级(绝不带崩 DSH):slots 注入回调若让异常外泄,延迟执行路径 (目标 slot 声明晚于插件加载时,回调跑在官方声明者的调用栈里 / 声明订阅里 uncaught re-throw)会打断官方 UI 激活、web 整页无法启动。因此本插件所有 slot 注册(chat.node 四格、设置行)统一走一个注册管道:inject 声明等待 与回调内 register 各自兜异常(return undefined 即"无可清理资源",官方 cachedSlotInject 对 falsy 返回无害),单个条目注册失败仅跳过该条目,console.warn 留排查线索并弹一次中性措辞的降级 Toast 告知用户(不指涉冲突方——旧版宿主未声明 slot 的版本缺口也走同一条降级路径);宿主半边的 skill 注册同样双层防护。DSH 启动 不受本插件任何注册异常影响。
  • 展开时通过 ctx.slots.entries('conversation.chat.node') 取到内置组件引用做委托渲染, 工具卡片/Think 行/上下文注入的内容与样式与内置完全一致。
  • 整回合折叠通过会话快照的 turnEnds(turn/end 事件驱动)判定回合完成,配合 chat.locations.getTurn() 计算折叠栏/成员/最终消息,再以 CSS :has() 隐藏成员 flowItem。 回合运行中由 turnTimings(turn/start 事件给出 startTime)判定回合已开始, 回合折叠栏即出现:耗时用随机间隔时钟(每 CONFIG.liveTickMs × 0.51,默认 125250ms) 补 Date.now() 实时走动,"消耗token"在真实值之上叠加每 tick +1/+11 交替的动画 偏移持续增长(真实 usage 到达时校正基线),全部指标在 turn/end 后切换为权威值。
  • 消耗token 口径(对齐官方统计):回合结束后优先取 turn-tail 携带的官方 (官方 在持久化事件日志上折叠全部计费 attempt: = 精确 prompt+output、含被重试请求,缓存命中率分母 = prompt 侧总量); 节点累加路径(运行中基线、无 回退)在 之外用 (返回 visible+hidden 全部已物化节点,旧版缺方法自动跳过)按节点 引用去重补采本回合隐藏的 assistant-step——纯 tool-call 步骤官方以 结算、不进 order/locations,只遍历 getTurn 会漏计其 usage。

注意事项

  • 兼容 DSH 0.1.1-rc.2 ~ 0.1.6-alpha.1(会话快照契约差异由插件内适配层消化、官方 渲染 hook 面自动跟随,见工作原理;0.1.5-rc.1 与 0.1.6-alpha.1 上均已逐条核对槽位/ 快照/设置行/节点数据契约)。DSH 升级若改变上述槽位契约或内置组件 props,本插件可能 需要随版本小改(属插件维护,非改源码)。
  • 宿主要求已声明:package.json 的 engines.dsh = >=0.1.1-rc.2 <=0.1.6-alpha.1 —— 插件市场(dshmarket)读 npm latest manifest 的这个字段,在插件卡片上显示 DSH >=0.1.1-rc.2 <=0.1.6-alpha.1,并在更新前拦下确定不满足的版本(undeclared/未知 一律放行;DSH 本体不读该字段,不影响加载)。区间是闭区间、锁到已核验的宿主版本: 每次 DSH 升级后重新核对契约,再抬上限并随新版本发布。
  • 折叠栏文案在 client.js 顶部 CONFIG 可调。
  • 耦合点清单(DSH 升级时对照排查;任一失效均优雅降级——回退内置渲染 / 文案兜底 + console.warn 提示,不会白屏):
    • 会话快照字段:0.1.1 走 useSession 快照的 s.chat.order / nodes / locations、 locations.getTurn()、顶层 turnEnds / turnTimings、chat.timeline.turns; 0.1.2 快照拆分后改走框架注入的 useChat(扁平 ChatSnapshot),turnEnds / turnTimings 在 chat.legacy(适配层自动选择,见工作原理)——用于段/回合分组、 结束判定、耗时与状态标签;
    • 节点数据结构:tool-call 的 data.root(call.name / argsRaw;diffs 按版本在 root.meta.diffs(0.1.2)或 resultView / callView 视图(0.1.1))、 assistant-step 的 blocks(reasoning / text)与 usage、turn-tail 的 tokensPerSecond 与 tokenUsage(用于折叠栏文案、think 摘要、token/缓存命中指标; tokenUsage 为 0.1.2+ 官方每回合精确统计,缺失时回退节点累加)、 ChatNodeStore.values()(隐藏 assistant-step 补采;缺失时自动跳过);
    • CSS 选择器:[data-chat-flow-kind]、[data-variant="think"](隐藏折叠成员 flowItem 与最终总结的 Think 行);
    • Slot 系统:conversation.chat.node 内置条目(priority: 0)、 slotsService.entriesOfSlot()(委托渲染与 tool.call.toolview 子视图分发);
    • Locale 命名空间:条目的 locale: 声明决定注入的 t 词典,且同一个 slot 上官方 条目混用两种命名空间——tool-call(ui-tool 注册)声明 'conversation'(工具标题词 tool.title.read=读取 等),assistant-step/context/user(ui-chat 注册)声明 'chat'(message.think=思考 等);旧版全在 'conversation'。插件注册时按条目 key 对应复制同 key 官方条目的声明(,无对应时 试查后回退 ),转发给官方组件的 一律过 兜底(查不到 key 时用 内嵌的官方词典合并本——chat + conversation + common 共 282 词条——做 插值兜底,不再裸显 / / 等任何原始 key)。
  • 回合折叠栏显示本轮指标:耗时x时x分x秒(不足 1 小时只显示分秒,不足 1 分钟只显示秒), 首字x.xs,消耗xxx token,xxx tok/s,缓存命中xx.xx%,待折叠/已折叠N步(>0 时才显示; 运行中为「待折叠N步」,回合结束后为「已折叠N步」)」;某几项缺失时自动省略, 全部缺失才回退为「运行了 N 条命令」;字段之间用 · 分隔,右侧附「第x轮」;
  • 点击回合折叠栏展开/收起整轮内容;重新打开历史会话时,已完成的回合同样保持整回合折叠;
  • 折叠作用域不越过用户消息:回合折叠栏只折叠「用户消息之后、agent 回复之间」的内容。 锚定在用户消息上方的上下文行(如审批策略变更通知)不属于本回合输出区间, 始终保持原样可见,绝不参与折叠,也不会被当作折叠栏锚点——避免回合折叠栏「跨过」用户消息 去折叠其上方的内容;
  • 最终总结只显示正文:回合结束后,最终总结消息内部自带的 Think 行也一并隐藏;
  • 状态标签:非正常结束的回合(用户停止 / 中断)在回合折叠栏前置状态文本, 如「已停止 | 耗时5分12秒…」,正常完成不显示额外标签;
  • 单条也分组:两个 text 之间只有 1 条命令(或 1 个 Think)时同样套步骤折叠栏, 运行中显示「正在运行 图标 工具名 · …」、text 出现后显示「运行了pwsh」; 回合结束整回合折叠时它收进回合折叠栏,展开回合折叠栏后步骤折叠栏行可见。
  • tokenUsage
    deriveTurnTokenUsage
    totalTokens
    tokenUsage
    locations.getTurn()
    nodes.values()
    visibility:hidden
  • 会话快照双版本读取层:DSH 0.1.1 与 0.1.2 的快照契约不同——0.1.2 把快照拆分成 useSession(会话级状态)与 useChat(chat 数据),turnEnds/turnTimings 收进 chat.legacy。组件统一经 useChatSnapshotData 适配:有 useChat(0.1.2+)就读 useChat 快照本体,否则从 useSession(s).chat 取;turnEnds/turnTimings 优先读 chat.legacy、顶层兼容字段兜底。所有 hooks 无条件调用(数据计算与订阅和"是否接管 折叠"解耦),折叠模式切换(接管 ↔ 委托内置)不改变 hook 数量,条目不会崩。
  • 0 秒占位(user 消息正下方):GroupedUserView 注册 conversation.chat.node 的 user key,优先级固定 -2(顺序无关下限,绝不占 -1——与 easyrewrite 硬编码 -1 零碰撞,本插件先加载也不会让它后注册撞车;-2 被第三方占用时才继续下探), 在「会话运行中且该 user 是最后一条消息」时于 user 消息正下方渲染占位回合 折叠栏(耗时从运行中回合的 startTime 计时),第一条中间节点到达后自动交接给正式 回合折叠栏(占位栏补 16px 上间距与官方 flow gap 对齐,交接无位移)。第三方 user 条目(dsh-easyrewrite)的组件链式委托渲染、整包 props 转发;chat.node 是核心 slot 恒声明,无需 try/catch 兜底。位置说明:占位栏在聊天流列内、官方 TurnStatus ("Deep diving..." 状态描述行)之上——2026-08-30 至 0.5.x 曾挂输入区 dock,会跑到 状态描述行下面(输入框左上角),位置错误,故恢复 user 格方案。
  • 首字(TTFT)三来源(官方优先):① step settle 后即实时读取官方值—— assistant-step 节点的 data.finalNode.timing(官方在 assistant/message 事件后写入 { stepStartTime, firstTokenTime, completedTime }),取回合内 step 号最小者(第一个 请求)的 firstTokenTime - stepStartTime(与官方 deriveTurnMetrics 同款语义); ② 回合结束后优先用 turn-tail 携带的聚合 ttftMs(同值、来自持久化事件日志、 刷新页面不丢);③ 仅当无任何 step 完成(首个请求仍在流式)时回退渲染时刻近似 (Date.now() - turnTimings.startTime,误差约一帧渲染延迟,幂等记录、回合内只记一次)。
  • 段闭合标题缓存:段闭合后标题不再变化,按 leaderKey + 节点 keys + 语言 + 工具指纹 (名称/isError/argsRaw 长度,不解析内容)记忆,避免每次渲染重复解析 argsRaw; 工具行数变更优先读取官方 diffs 数据(oldText/newText 块行数;0.1.2 在结算 metadata root.meta.diffs、0.1.1 在 wire 视图 root.resultView.diffs / root.callView.diffs), 无 diffs 时才回退解析 argsRaw(单次解析同时提取路径与行数)。
  • 会话切换清理:segmentLabelCache(段闭合标题缓存,每段一条字符串、长会话可达数百 KB)、 liveTokenCache(每回合 1-2 条)与手动展开状态(overrides / turnOverrides)在切换 会话时清理——手动状态回到自动规则(已结束回合默认收起);ttftCache 保留(每回合一个 数字,量级可忽略)。切换回原会话仅"已结束回合回到默认收起 + 段标题重新计算一次"。
  • 多语言跟随:文案读取 document.documentElement.lang(DSH 切换界面语言时由 dsh-client-locale 设置),随 DSH 语言实时切换,浏览器语言仅作回退。
  • detectChatLocale
    ctx.locale
    'conversation'
    t
    wrapLocaleT
    {占位符}
    "message.think"
    "message.contextInjection"
    "tool.title.read"