dsh-chat-fold
折叠/展开AI回复的Think、Read、Edit 等操作内容,默认只展示重要信息
- Stars
- 0
- Language
- JavaScript
- Created
- Sep 5, 2026
- Updated
- Sep 5, 2026
Introduction
dsh-chat-fold — DeepSeek Harness 对话过程折叠插件
给 DeepSeek Harness Web 界面(dsh web)的对话窗口增加**「展开 / 折叠」控制**:
折叠 = 彻底隐藏全部 AI 思考过程(Think) 与工具执行过程(Bash / Read / Edit 等 tool-call),
AI 回复正文与用户输入始终全文显示、永不折叠;点「展开全部」可把过程行显示为 5 行预览。
纯浏览器端实现,零后端、零 API key。
当前版本:v0.1.8(变更记录见 CHANGELOG.md)
功能一览
- 右上角单个自切换按钮 —— 会话标题栏右上角(
conversation.session.header.actions槽位,与 Agent 预设、任务状态等按钮同一行)放一个按钮,和每条回复上的单按钮 交互一致:- 折叠态显示 「▾ 展开全部」:把 Think / Bash / Read / Edit 等过程行暴露出来, 每行默认半展开显示前 5 行内容/摘要(点击某行展开完整详情); AI 回复与用户输入全文始终可见;新流式到达 / 滚动加载内容按当前模式自动处理;
- 再点变成 「▴ 折叠全部」:彻底隐藏全部过程行(纯思考步骤整条消失);
- 模式状态持久化到
localStorage(dsh-chat-fold:mode),刷新 / 重开会话后保持。
- 两级展开:
- 展开(暴露行,5 行预览):全局「展开全部」或单条「展开回复」都让过程行可见, 默认显示前 5 行内容(Think / Read / Edit 摘要)+ 右下「展开详情 ▾」提示;
- 展开详情:点某一条 Think / Read / Edit 的预览区,才展开它的完整详细内容; 点「折叠全部 / 收起回复」或折叠一轮后再展开,会回到 5 行预览。
- 每条 AI 回复一个同款单按钮 —— 在每条已完成回复的操作条上(与「🔊 朗读」等 按钮同一行)有 「展开回复 / 收起回复」:只展开/收起这条消息自己的过程行 (5 行预览,作用域覆盖全局折叠、只限本条);回复正文与用户输入不受影响、始终全文。 全局与单条互相独立,工具栏一键复位全部。
- 输入内容重点突出 —— 用户输入气泡始终完整显示,并带有蓝色高亮描边; 折叠模式下描边更强,让「输入内容」成为折叠态下的视觉锚点,一眼定位每一轮提问。 插件不折叠、不隐藏任何用户输入。
文件结构
dsh-chat-fold/ # ← git 仓库根
├── package.json # dsh.client 声明(platform: web → 浏览器端 bundle)
├── lib/
│ ├── index.js # Node 半身:空插件(让 loader 能创建 fiber)
│ └── client.js # 浏览器端 bundle(全部功能所在,手写、零构建)
├── test/
│ └── smoke.test.mjs # 冒烟测试:模块表 + 假 DOM 适配器(44 项断言)
├── README.md
└── CHANGELOG.md
安装(一次性接线)
若本机尚未完成接线,按以下步骤操作;
webprofile 已接线时会直接加载本仓库。
-
把插件软链进 web profile 的
node_modules(使dsh-chat-fold可被 Node 与 client-modules 从 profile 目录解析):mkdir -p ~/.dsh/profiles/web/node_modules ln -sfn /Users/robinddu/Desktop/workspace/robinddu/dsh-chat-fold \ ~/.dsh/profiles/web/node_modules/dsh-chat-fold -
在
~/.dsh/profiles/web/cordis.patch.yml追加:# dsh-chat-fold: 对话过程折叠插件(浏览器端;右上角 折叠/展开) - insert: - id: chat-fold name: 'dsh-chat-fold' config: defaultMode: 'folded' # 初始模式:folded=折叠思考/工具/AI回复 | expanded=全部展开 userEmphasis: 'auto' # 用户输入高亮:auto | strong | none replyFoldMinChars: 120 # 短于此字符数的 AI 回复不折叠,直接展示全文 debug: false -
重启
dsh web(首次安装需重启以重新扫描 client 插件清单),浏览器硬刷新 (Cmd+Shift+R)。之后代码改动只需硬刷新页面即可生效。
为什么是
dsh-chat-fold(包名)而不是绝对路径:client-modules 会用条目名解析<name>/package.json并据此扫描dsh.client声明、服务/plugins/<id>/client.js; loader 的 Node 侧 import 也从 profile 目录解析包名。软链一次接线、两处生效。 浏览器端 bundle 只依赖react(平台种子模块),无需 node_modules 软链到 harness。
使用
- 打开任意会话,在对话窗口右上角看到一个按钮:折叠态显示 「▾ 展开全部」。
- 点 「展开全部」:所有 Think / Bash / Read / Edit 过程行暴露出来, 默认半展开显示前 5 行内容/摘要,AI 回复显示全文;按钮翻转为 「▴ 折叠全部」。
- 想看完整细节:点那条 Think / Read / Edit 的预览区 → 展开该行完整详细内容; 想全部看细节也可以逐条点开。
- 点 「折叠全部」:过程行完全隐藏(纯思考步骤整条消失),界面只剩提问与 回复;AI 回复与用户输入始终全文显示;用户输入气泡带更强高亮。
- 单条回复:点该回复操作条上的 「展开回复 / 收起回复」:只展开/收起本条 消息自己的过程行(5 行预览,只限本条、全局不变);回复正文不受影响。 全局按钮可一键复位所有单条状态。
工作原理 / 技术要点
- 完全依赖会话 DOM 上的稳定语义属性,不碰 hashed CSS class:
- 消息流容器:
[data-conversation-scroll] - 每条消息:
[data-chat-flow-kind](user/assistant-step/tool-call…) - AI 思考块:
[data-variant="think"]内的 Disclosure 行 ([data-disclosure-row][role="button"][aria-expanded],与内置「Think」行同源) - 工具执行卡片:tool-call 消息内的
[data-expandable][role="button"][aria-expanded] - 展开 = 暴露行并半展开为 5 行预览(移除隐藏 class / 打
dsh-fold-scope-opendsh-fold-expose-half裁剪);完整详情由用户点击预览区展开(capture 拦截防误收起)
- 消息流容器:
- 折叠引擎(
createCore)职责:collectTargets收集当前已渲染的全部可折叠目标(含展开态);scan:按作用域归位(隐藏/暴露行、scope-open、5 行半展开预览), 预览只自动打开到 5 行,不自动展开全文、不干预用户已展开的完整详情;applyAll(open):一键作用于当前所有目标(按钮语义),忽略用户临时开关;- AI 回复正文永不折叠:引擎不对回复正文做任何裁剪/隐藏,折叠与展开只作用于
思考(Think)与工具执行(tool-call)过程行;
findReplyWrapper仅用于定位 “某条最终回复”以便作用域归属与单按钮挂载; - 每条回复的单按钮:注册进
conversation.chat.assistant-actions官方槽位 (与复制 / 🔊 朗读同一条操作条),从按钮 DOM 上溯到 turn-tail 再取其前一个 assistant-step 定位「本条回复」,toggleReplyForFlow只改这条的 override 与 class, 与全局模式解耦;按钮经回复 revision store 订阅刷新「展开回复 / 收起回复」状态; - 彻底折叠隐藏:折叠态下由插件 CSS 直接隐藏过程行 ——
html[data-dsh-fold-mode="folded"] [data-variant="think"](思考行)与[data-chat-flow-kind="tool-call"](Bash/Read/Edit 工具卡)display:none; 纯思考步骤(只有 Think、无正文)由isThinkOnlyStep识别后打 classdsh-chat-fold-step-hidden整条隐藏,避免留空行; - 用户输入气泡标记:结构性探测(圆角 ≥ 14px、非
display: contents、非透明的div)找到气泡并加上自有 classdsh-chat-fold-user-bubble,由插件自己的 CSS 描边, 不依赖任何 hashed class。
- MutationObserver 监听 body 子树(childList + 关键属性),变更后 200ms 防抖扫描, 因此流式输出、翻页虚拟化加载的块都能被自动接管。
- 模式状态经
subscribeMode/publishMode模块级 store 管理,React 槽位组件用useSyncExternalStore订阅,按钮按下态与引擎状态始终一致。 - 本目录是插件的唯一可信源(source of truth),已用 git 管理。
开发 / 测试
npm test # 冒烟测试(Node,44 项断言:识别/归位/彻底隐藏/过程行预览/单按钮/作用域展开/纯思考步骤/气泡标记)
若需手工验证 DOM 行为(点击 Disclosure 是否驱动 React 展开等),可参考仓库
docs/ 之外的开发会话记录;核心假设已内置在冒烟测试与注释中。
已知边界
- 折叠 = 彻底隐藏思考过程(Think)与工具执行过程(Bash / Read / Edit 等 tool-call, 含纯思考步骤);AI 回复正文与用户输入永不折叠、始终全文显示; 「展开」模式把过程行显示为 5 行预览,点某行看完整详情。
- 过程行 5 行预览采用 CSS 裁剪(max-height ≈164px + 渐隐),展示的是该行开头内容 而非语义摘要;如需真正的「要点摘要」,需接入 LLM 或文档解析,超出纯前端插件范围。
- 若上游 Harness 改变消息流 DOM 语义属性 / 消息容器结构,插件需要跟随升级;所有
依赖点集中在
collectTargets/findUserBubble/findReplyWrapper与少量常量里, 改动面很小。
卸载
- 从
~/.dsh/profiles/web/cordis.patch.yml删除对应insert段; - 删除软链
~/.dsh/profiles/web/node_modules/dsh-chat-fold; - 重启
dsh web;残留的 localStorage 键dsh-chat-fold:mode无副作用,可不管。
License
MIT