DeepSeek Harness 插件

AliceLJY/dsh-thumb

Star 数 ★ 1 分类 UI 增强 收录于 2026-08-27

网页版界面的手机外壳:侧栏改为覆盖式抽屉、点会话自动收起,设置面板改全屏单列,正文排版按手机阅读距离重调。

安装

# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)

dsh plugin --profile web add github:AliceLJY/dsh-thumb

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。

README

给 DeepSeek Harness(dsh)网页版做的手机外壳。侧栏从「挤压聊天区」改成「覆盖式抽屉」,点会话自动收起,设置面板改全屏单列,正文排版按手机的阅读距离重调。桌面端一点不碰。

English

个人自用工具,按原样发布。 是照着一个人的手机、一个 dsh 版本(0.1.0-rc.6)写的,不承诺维护、不承诺响应 issue。dsh 升级后失效的话,改 src/client.js 顶部 LOCATORS 那四条定位器就行,是分钟级的事。

为什么需要它

dsh 的网页界面在手机上首屏是正常的——侧栏自动收成 56px 图标条,输入框完整。问题全在侧栏展开之后,而那正是手机上切会话的必经路径。

实测(iPhone 14 Pro 视口 393×660,dsh 0.1.0-rc.6,装本插件之前):

场景 改造前 改造后
收起态(图标条) 正常 不介入,原样
侧栏展开 侧栏占 71% 屏宽,挤压聊天区到 113px 抽屉覆盖,聊天区 393px 满宽
点会话之后 侧栏不收,一直卡在 113px 窄缝 自动收起
关闭抽屉 只能手动点收起按钮 点遮罩任意处即可
设置面板 800px 两列硬塞,英文逐词换行,选择器切出屏幕 全屏单列,文字正常换行,选择器回到屏内
设置面板被切元素 7 处 1 处
正文总高(三轮会话) 610px,按桌面阅读距离排的 490px,少五分之一

根因不是 bug,是上游的显式契约。@deepseek-ai/dsh-client-ui-layout 的列求解器注释写得很清楚:

The sidebar never concedes: its rendered width is always the drag preference (or the collapsed rail), and center absorbs any remaining deficit as the last resort.

配合 SIDEBAR_DEFAULT = 280,在 393px 视口上算出来就是 393 − 280 = 113。让步链是「先缩详情栏 → 再关详情栏 → 最后中间栏吃掉全部亏空」。在窄的桌面窗口上这很合理(还剩几百 px),在手机上就不行了。

装 / 卸 / 临时关

# 装
dsh plugin --profile web add github:AliceLJY/dsh-thumb
# 确认 ~/.dsh/profiles/web/package.json 的 dsh.profile.bundles 里有 "dsh-thumb",然后重启服务

# 卸
dsh plugin --profile web remove dsh-thumb
# 同样确认 bundles 数组里已移除,再重启

没有 npm 包:dsh plugin add 把参数直接交给 pnpm,所以 github: 规格能直接装仓库。冷存储下实测 8.9 秒,dsh 会自己把它追加进 dsh.profile.bundles。

想在本地改着用?同一条命令换成路径即可:

dsh plugin --profile web add link:/绝对路径/dsh-thumb

⚠️ 卸载/回滚要两件一起做:package.json 和 node_modules 里的 link。只还原 package.json 的话,下次 pnpm add 会看到 link 还在、判定 "Already up to date",不写任何东西也不报错——看起来装上了,实际没有。

临时关闭,不用重启服务:

  • 地址后加 ?thumb=0
  • 或控制台 localStorage.setItem('dsh-thumb','0') 后刷新

它跟 GitHub 上那些同类插件的区别

同期社区有十几个 dsh 移动端项目,做同一件事的插件的通行写法是在 CSS 里硬编码 dsh 的 class(pI_x6G_sidebarCol、Md3f7G_scroll 这种)。那些 class 是 CSS Modules 构建时生成的,dsh 每次重新构建就全变,插件随之静默失效——样式不生效、页面还在、控制台不报错,你只会觉得"今天怎么又难用了"。

样式表里不放任何宿主 class 名(源码注释里出现过两次,是作为"不该硬编码成什么样"的反例)。做法是运行时按语义后缀(sidebarCol 这半截来自上游源码的变量名,不随构建变)定位一次,给节点打上自己的 data-thumb 属性,样式表只认这些属性。上游要是真改了变量名,坏的是一个定位器,在一个地方、能直接查出来;不会变成满地样式无声失灵。

行为上也尽量走官方接口:关抽屉调的是 ctx.layout.toggleSidebar()(上游 ILayout 的公开方法,ui-sidebar 自己也用它),不是去模拟点击某个按钮。

作用域

布局类规则只在两个条件同时成立时生效:视口 ≤1023px(对齐上游的 SIDEBAR_AUTO_COLLAPSE = 1024,避免两套断点打架)且侧栏被手动展开了。56px 图标条那个状态完全不碰——它本来就是好的。

平板尺寸下抽屉化还值不值?

这份文件早先的版本猜测「到了平板尺寸抽屉化未必还是改善」,并建议把断点降到 767px。实测下来,那个猜测是错的。

真正该看的是点完一个会话之后的状态——因为切换会话必须走这条路。原生行为是侧栏赖着不走,正文停在 宽度 − 280;本插件会自动收起它,留下 56px 图标条,正文是 宽度 − 56:

视口宽度 原生 装了本插件 收益
393px 113px 337px +198%
480px 200px 424px +112%
604px 324px 548px +69%
768px 488px 712px +46%
900px 620px 844px +36%
1023px 743px 967px +30%

这张表可以用 node test/measure-widths.mjs 重新生成;那个文件里也写清了为什么量的是「最后剩给你多宽」而不是「抽屉盖住了多少」——后者正是第一次量错的地方。

收益随宽度递减,这符合预期——但在整个支持区间里从未反转。降断点等于在它本该起作用的尺寸上,关掉一个仍值 30–46% 的东西。1023px 这个断点保持不变。

正文密度规则是唯一有意的例外:它在 ≤1023px、抽屉关着的状态下生效,因为读正文本来就是抽屉关着的时候。实测那个会话从 610px 压到 490px,其中 60px 只来自消息之间的间距(16px → 10px),这一项没有任何交互代价。正文字号 16px/28px → 14px/21px,操作按钮 28px → 24px;按钮本来就低于 iOS 建议的 44px 触摸目标,所以最后这项是个权衡,样式表顶部的 --thumb-hit 可以调回去。

桌面端已验证零变化:侧栏 280px、中间栏 1160px、position: static、无遮罩、设置面板仍是 800px 两列。

已知限制

  • hover tooltip 仍可能溢出右边缘(悬停工作区行时那个黑色卡片)。有意不修:它的 class 语义(_card / _copyable)太泛,稳妥的修法得在每帧扫描所有 position: fixed 浮层再逐个夹回视口,误伤面不明,而它只是视觉瑕疵、不挡任何操作。真要修的话,正确入口是上游给 tooltip 加触屏判断,不是从外面兜。
  • 实测过 393、480、560、604、640、700、768、900、1023px 以及桌面 1440×900,数据见作用域一节。手机尺寸是每天在用的,其余只是量过一次,没有长期使用。
  • 设置面板的导航项仍是竖排,没有变成横向滚动条——那条 flex-direction: row 打在了一个并非真正承载这些项的容器上,结果是面板顶部多占一点垂直空间。有意不追:面板已经从「没法用」变成「能用」,再去精确定位那一层属于打磨不属于修复。
  • AI 回复下面那排操作图标仍是 28px,只有用户消息那侧压到了 24px。它套在另一层 flex 容器里,height、align-self、min-height 三样都推不动它,而扫遍样式表也找不到任何一条给这些 class 设过高度的规则——也就是说撑住它的东西不在「按 class 名找规则」这个探测范围内。有意不追:三轮会话上只值 12px,约占正文总高的 2%。
  • 上游改了列布局的实现方式就得跟着改。定位器在 src/client.js 顶部的 LOCATORS,四条,改起来是分钟级的事。

验证

node test/smoke.mjs        # 21 项断言,需要一个跑着的 dsh

脚本在手机视口和 1440×900 各驱动一遍真实 dsh:定位器打标、抽屉打开时聊天区是否占满宽度、四个密度数值、作用域是否越界、平板宽度、关闭开关,以及桌面回归(断言桌面正文仍是 16px)。其中一条不是查属性而是端到端的——同一段会话加载两次、一次带 ?thumb=0,要求本插件那次的正文至少矮 15%。

到目前为止它接住了两个真回归,两个都值得记下来。

关闭开关,第一次跑就红了。?thumb=0 一直以来只关掉了 React 组件,在密度规则落地之前这就够了——那时每条规则都挂在一个只有活组件才会设置的抽屉属性上。密度规则是抽屉关着也生效的,样式表本身就成了承重件,开关够不到它。已在 ensureStyle / stampFrame 里直接检查开关修掉。

作用域越界,而这个测试没接住,是从手机上报回来的。中间栏挂的不只是对话,还有轨迹视图,而轨迹页的工具栏是一排文字按钮。[class*="_actions"] > button 把它们统统压成了 24px 见方,Duration / Turns / Calls 叠成一团没法读。现在每条密度规则都走 FLOW 作用域——它靠"这一列里装着消息条目"来认对话列,而不是靠 class 名。真正的教训不是选择器写宽了,而是一套只打开过一个 tab 的测试,会在隔壁视图坏掉的时候一路绿灯。所以补了那条越界断言,并且把 bug 放回去验过它确实会红、还会点名那三个按钮。

复现那个的注意事项:手机宽度下 Playwright 点不动轨迹 tab。先在桌面尺寸点开,再把视口缩小——同一个已挂载的视图、窄屏布局,中间不发生跳转。

跑出来的截图没有随仓库发布——上面有真实的工作区名和会话标题。下面三个坑才是复现时真正需要的东西。

复现时会踩到的三个坑,一并记在这:

  1. ESM 不读 NODE_PATH —— 全局装的 playwright 要用绝对路径引入,而且它是 CommonJS,得走 default import。
  2. Chrome 会吃系统代理,ts.net 地址直接超时 —— 走 http://127.0.0.1:3080 加 --no-proxy-server。
  3. waitUntil: 'networkidle' 永远等不到 —— dsh 有常驻实时连接,用 domcontentloaded 加固定等待。

开发笔记

两个坑记在这里,因为都属于"看起来在别处、实际在自己身上"那类:

插件不激活、整页白屏。 package.json 的 dsh.client.inject 和 client.js 导出的 inject 同名同形但语义不同:前者是包名(模块加载顺序),后者是 cordis 服务名(['slots', 'layout'])。把后者写成包名,插件会一直 pending,外壳报 web boot: 1 entry did not activate,而整个页面渲染不出来。参照 @deepseek-ai/dsh-client-ui-sidebar 的 inject 值就对了。

永远关不掉的抽屉。 判断抽屉是否展开,一开始读的是侧栏的渲染宽度(>56px 即展开)——而抽屉 CSS 恰恰把侧栏钉在 320px。判据被自己的副作用污染,于是它永远为真,收起按钮、遮罩、自动收起全部"失灵",看起来像 ctx.layout.toggleSidebar() 在窄屏下不工作(它其实一直是好的)。现在读的是 AppFrame 写在 frame 上的 inline grid-template-columns 第一列——那是上游的意图,本插件不碰。规矩:不要用一个自己会覆盖的量去做判据。

License

MIT

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →

社区评论

评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。