前言¶
DSH(DeepSeek Harness)的理念是「一切皆插件」,客户端 UI 也走同一套路:内置能力只做必要的骨架,长尾需求靠插件补。这就带来一个具体痛点:用户往 Web 对话里贴大段日志或大文本时,一条消息就能占满整个会话视口,前面的上下文全被顶走。DSH 自带的 Turn Process Folding 只折叠过程行(tool-call / thinking),不折叠 user 消息正文,所以这块一直是空缺。
下面介绍一个填这个空缺的客户端插件:constraint21 维护的 dsh-long-message-fold。它把超长的用户消息折叠成固定高度(默认 360px,约 16 行),底部渐隐,并提供「展开全文 / 收起」切换;短消息一个像素都不变,装上即生效。
这是什么¶
一句话定位:一个纯客户端(Web 平台)UI 插件,遮罩 shipped 的 PlainUserText 渲染器,把超长 user 消息正文折叠成固定预览高度。
- 维护者:constraint21
- 分类:客户端(Client)
- 许可证:MIT(© 2026 constraint21)
- 包名/版本:
@local/dsh-long-message-fold,0.1.0,package.json里private: true(刻意不发 npm,只走 git 或本地目录安装) - 目录页:https://www.skillhub.cn/plugins/constraint21/dsh-long-message-fold
- 仓库:https://github.com/constraint21/dsh-long-message-fold
核心功能¶
1、超长用户消息自动折叠成固定高度(默认 MAX_HEIGHT = 360px,约 16 行),底部用 mask-image 渐隐。渐隐不猜背景色,因此在任意主题下都正确。
2、折叠态下给出 pill 状操作按钮:「展开全文 · N 行」展开并显示总行数;「收起」回到折叠态。
3、拖动窗口宽度或侧栏变宽后,原本溢出的消息会自动恢复成不折叠状态,不需要手动操作。
4、短消息和未溢出的文本片段保持原样。未溢出的渲染路径返回与 shipped UserTextRun 等价的 <span style="font-size:inherit;line-height:inherit">,流式布局与原来一致。
5、展开状态有内存级记忆(模块级 Map,键为节点起止偏移 + 文本长度 + FNV-1a 哈希,最多 400 条),虚拟列表卸载再挂载同一行时状态不丢;不写 localStorage,刷新即复位。
6、没有开关,装上即生效。
关于「算不算超长」:插件不看文本长度,而是实测溢出。它通过 useLayoutEffect + ResizeObserver 测量:
- 内联态取
getBoundingClientRect().height,能反映折行后的真实高度; - 已成块态取
scrollHeight,折叠与展开两态下判定一致,不会来回抖动。
这个做法是必需的:conversation.user.text.node 这个 key 在 QueueDock 预览和 submission echo 中也会被调用(ellipsis: true,父级是 white-space: nowrap),nowrap 下高度恒定为 1 行,因此靠长度判会误伤。ResizeObserver 挂到祖先链上最近一个有盒子的节点(bubble),所以侧栏或详情面板宽度变化时能双向重算。
安装¶
插件走 DSH 的 Plugin Manager 安装,三种方式任选。
第一种,让 agent 装(README 标注为推荐):
安装插件 github:constraint21/dsh-long-message-fold
对 DSH 说出这句话即可,agent 会调用 plugin_manager → install_bundle,审批通过后生效。等价的显式调用方式是:
plugin_manager → install_bundle → target: github:constraint21/dsh-long-message-fold
第二种,本地目录:先把源码取到本地,然后把 install_bundle 的 target 指向该目录的绝对路径。
第三种,手动(不推荐):把包放进 profile 的 node_modules,并在 cordis.patch.yml 里 insert 一个 long-message-fold 条目。手工修改 profile 的 package.json 或 patch 容易与 Plugin Manager 的状态打架,能用前两种就别用这个。
安装后要刷新 Web 页面。插件是纯客户端 UI,不重启 Host 也可以,但页面资源需要重新加载。
卸载时在 Plugin Manager 里移除 @local/dsh-long-message-fold(或执行 plugin_manager action: remove_bundle),刷新页面后 user 消息立即恢复原样。插件状态只存在内存里,无残留。
典型用法¶
装完刷新页面后直接使用即可,没有配置项需要打开。
1、往对话里贴一段超过 360px 的用户消息(例如日志或长文)。
2、消息体现为折叠态:正文最高 360px、底部渐隐,下方有「展开全文 · N 行」按钮。
3、点击按钮展开完整内容,按钮文字变为「收起」;再次点击回到折叠态。
4、把窗口或侧栏拉宽,让消息不再溢出,它会自行恢复成不折叠。
调参方式¶
参数是 client.js 顶部的四个常量,改完重新加载页面即可,不需要重新安装:
| 常量 | 默认 | 含义 |
|---|---|---|
MAX_HEIGHT |
360 |
折叠后正文高度(px,约 16 行) |
FADE_HEIGHT |
48 |
底部渐隐高度 |
REMEMBER_LIMIT |
400 |
展开状态记忆条数 |
OBSERVE_MIN_CHARS |
120 |
短于此长度的文本不挂 ResizeObserver |
实现要点(给要看源码的人)¶
插件采用同 key 注册的方式遮盖 shipped 渲染器:为 slot conversation.user.text.node 的 key 'text' 注册自己的渲染器,外层气泡、附件、各类控件全部保留。该 slot 在 catalog 里声明为 replaceRisk: "shadows-shipped-ui";shipped 的 PlainUserText 以 priority: 100 注册,动态安装的插件条目由 runner 的 allocatePriority() 分到更小的数值,而数值更小者获胜,所以同 key 注册即取代,无需自己传 priority。
README 记录了几个被否决的替代方案,理由是它们要求重写整行或整块(pending / echo / 时间 / 复制控件、userStack / 附件行 / bubble / JsonBlock),或者违反「不写组件外 DOM、不读别人 DOM」的插件纪律。
实现上的约束:文案走 Client locale service(ctx.locale.register(NS, { zh, en }),slot 注册带 locale: NS 以拿到框架注入的 t);配色只用 --dsw-alias-* 主题 token;不 require 任何 Harness Client 包。
兼容性方面:插件依赖 slot conversation.user.text.node 与 Client 包 @deepseek-ai/dsh-client-ui-conversation(见 package.json 的 dsh.client.inject),slot 或 shipped 实现大改时可能失效。退化是安全的:若上游把 'text' key 改名或不再有 occupant,框架会用 fallback 渲染原始文本,消息显示不受影响,只是不再折叠。插件只在 Web 平台注册(dsh.client.platform = "web"),Host 半的 index.js 是空实现。
适用场景与注意¶
适合:经常在 DSH Web 里贴长日志、长文本,且愿意用插件补这块空白的 DSH 用户和智能体开发者;也适合想看「如何在客户端用 slot 同 key 覆盖 shipped 渲染器」这个做法的插件作者。
注意几点:
1、插件以当前 DSH 进程权限运行。安装前应打开源码检查一遍,并确认许可证(MIT)是否符合自身合规要求。
2、该仓库目前 star 数为 0,属于很早期的个人项目。功能描述和参数以 README 为准,缺少大规模使用反馈的迹象,建议当作小工具自用并在自己的 DSH 环境里验证。
3、它只处理 user 消息正文,不处理 assistant 输出、tool-call 或 thinking 的过程行。
4、package.json 标了 private: true,所以不能通过 npm install 安装,只能走 Plugin Manager 的 git 或本地目录方式。
5、目录页(skillhub.cn)是独立的社区站点,与 DeepSeek / 幻方无官方从属关系,不是一个官方应用商店。
结尾¶
这个插件的价值很窄也很明确:把「贴大段日志把上下文顶走」这件事用折叠 + 渐隐 + 一键展开解决掉,同时不改变短消息和流式布局。安装一条命令、刷新页面即生效,不写 localStorage、卸载无残留。目录页在 https://www.skillhub.cn/plugins/constraint21/dsh-long-message-fold,源码与 README 在 https://github.com/constraint21/dsh-long-message-fold。