dsh-auto-expand-thinking
DSH Web Chat 里的「思考」块默认是合上的:只看得到一行标题或首句,正文要手点才出来。 这个插件把折叠的思考块自动展开——任务跑到哪一步、模型在想什么,当场可见。
- 宿主:DeepSeek Harness 0.2.1-rc.2(本机实测版本),profile
desktop - 形态:宿主半 + 浏览器半(
dsh.client.platform = "web"),零运行时依赖,零外部 import - 全部代码:
lib/client.js(约 400 行,纯 DOM + 一个 MutationObserver)+lib/index.js(日志壳)
30 秒装上
git clone https://github.com/basaltfold/dsh-auto-expand-thinking
cd dsh-auto-expand-thinking
pwsh -File .\install.ps1
(也可以用 dshmarket / 插件页按本仓库地址装:dsh.install 已声明 tarball / zip / git 三种源。)
然后重启 DeepSeek Harness(插件是启动期组合的,重启后才加载)。
卸载(带备份自动回滚):
pwsh -File .\uninstall.ps1
# 或直接还原某次备份:
pwsh -File .\uninstall.ps1 -Restore 20261026-123732
只支持 Windows 的
install.ps1是"落地到 profile"这一步;手动装法见下面「装了什么」, 三步在任何平台上都能手做(建立 node_modules 联接 / 加 file: 依赖 / 加 bundles 项)。
控制
| 动作 | 效果 |
|---|---|
Ctrl+Alt+T |
循环三档:总是展开 → 仅运行中展开 → 关闭(页面底部有 1.6s 提示) |
__dshAutoExpandThinking.stats() |
当前档位 / 命中的点击策略 / 已展开数 / 放弃数 |
__dshAutoExpandThinking.setMode('running') |
直接切档 |
__dshAutoExpandThinking.debug(true) |
打开详细日志 |
档位写在本页 localStorage:
| 键 | 含义 |
|---|---|
dsh-auto-expand-thinking:mode |
always(默认)/ running / off |
dsh-auto-expand-thinking:strategy |
自动探测到并记住的点击策略 |
dsh-auto-expand-thinking:debug |
1 打开日志 |
三档语义:
- 总是展开(默认):所有思考块都展开;手动合上会被再次展开。
- 仅运行中展开:只展开正在流式输出的那条;已经结束的留给你自己决定——不想被"抢鼠标"就用这档。
- 关闭:完全不动,等同临时禁用。
为什么不能用 CSS 解决
DSH 的 ReasoningRow 只在「展开」状态才挂载正文节点:
const content = expanded
? jsx("div", { className: thinkBody, children: renderSlot("conversation.chat.reasoning.body", { text, running }) })
: undefined;
折叠时正文根本不在 DOM 里,并且根节点被固定成 height: calc(24px + …)。
所以 details[open]、max-height: none、[data-expanded] { height: auto } 这类样式 hack 拿到的都是空壳——没有节点可显示。
第三方插件也够不到那个开合状态:useDisclosure 由 Chat 节点的 slot 注入(conversation.chat.node 的 useDisclosure prop),reset 源不是可注入的服务,conversation.chat.reasoning.body 这个 slot 又只在已展开时才被调用。
结论:只能像用户那样去点它自己的开合控件。本插件做的就是这件事,并且每次点完都回读状态确认成功。
DOM 契约与点击策略
从本机已构建产物里还原(ReasoningRow 在 @deepseek-ai/dsh-client-ui-chat/lib/client.js;DisclosureRow 在 @deepseek-ai/dsh-web-frontend/dist/assets/index-*.js):
<div data-variant="think" data-state="ok|running" [data-expanded]> ← 思考行根(本插件扫描的目标)
<div data-open> ← DisclosureRow 根
<div data-disclosure-header> ← 标题包装层,没有 onClick
<div data-disclosure-row role="button" onClick> ← 真正的开合控件
{open && 正文}
要点:onClick 在 [data-disclosure-row] 上,不在 [data-disclosure-header] 上——点包装层不会展开。
点击目标不写死,按阶梯试:
[data-disclosure-row](当前发布版的真实控件)button, [role="button"][data-disclosure-header]- 首个子元素
- 行本身
每次点击后回读 data-expanded,成功才计数并把策略写进 localStorage;失败就试下一个。
所以发布版改了属性名时是"降级 + 自愈",不是崩掉。
其它工程细节:
- 一次一帧最多展开 6 行,长会话回放/滚动不卡;
- 没有任何 client rects 的行(被整轮折叠隐藏)直接跳过;
- 同一行连续 3 次打不开就放弃并计入
giveUp,不会死循环; MutationObserver跟随新增行与data-expanded/data-state变化,另有 2s 兜底扫描(虚拟列表回收、HMR 重挂都能自愈);apply(ctx)用ctx.effect注册清理,插件停用即断开 observer / 定时器 / 快捷键,并摘掉页面上的__dshAutoExpandThinking。
验证
cd <插件目录>
node --check lib/client.js
node test/client.test.mjs # 13 项:策略阶梯、三档语义、放弃上限、快捷键、dispose
node test/install.test.mjs # 6 项:清单契约 + 已安装时的 profile 接线
测试把 lib/client.js 放进 node:vm,用按真实 DOM 形状写的 stub 驱动引擎,不需要浏览器。
install.test.mjs 里"清单契约"一节逐条对照 @deepseek-ai/dsh-client-modules 宿主半实际执行的校验(dsh.client.platform、exports["./client"]、bundle 自注册 id、自包含性)。
装了什么
install.ps1 只动三处,都先备份(后缀 .aetbak-<时间戳>):
profile/node_modules/dsh-auto-expand-thinking→ 本目录(junction,不是拷贝,改代码即改插件)profile/package.json:dependencies加file:依赖;dsh.profile.bundles追加一项profile/cordis.patch.yml:追加 insert 兜底行(bundle 层没生效时用)
已知限制
- 只处理思考行(
data-variant="think"),不动工具调用、系统提示、上下文注入那些折叠块。 - 属性名是发布版的内部契约,不是公开 API。DSH 换版本后属性若变化,插件按阶梯降级;仍然不行就静默停止展开(
stats().giveUp能看到),不影响页面其它功能。 - 本机没有可供我操作的浏览器实例,自动化测试只覆盖到 DOM 契约与安装契约;页面上的最终效果需要你重启后目视确认一次。
许可
MIT
No comments yet. Be the first to write one.