前言¶
在 DeepSeek Harness(DSH)Web GUI 里读长回答时,若一边读一边拖窗口边缘,段落会重新换行,眼睛所在的那一行往往就「跑」了。这不是滚动条误触:对话列宽度会随窗格变化,浏览器仍用像素保存 scrollTop,换行后同一数值不再指向同一段文字。DSH 自带的 resize 处理会跟尾或重探当前轮次,但不会把读者原先的阅读锚点拉回来。
下面介绍社区客户端插件 freeze-content-on-window-resize(维护者 nnnanann)。它在窗口缩放时锁定对话阅读列宽度,并在缩放或缩放后尽量保持阅读位置,减少长文重排带来的跳读。
这是什么¶
freeze-content-on-window-resize 是面向 DSH Web GUI 的客户端插件:锁定正在阅读的对话列宽度,并在窗口 resize 时补偿阅读锚点,使长回答不再因窗宽变化而整段重排。
- 维护者:nnnanann
- 分类:客户端(
dsh.client.platform: web) - 版本:1.0.0(见仓库
package.json) - 许可证:MIT
- 目录页:SkillHub 插件页
- 源码:GitHub — nnnanann/Deepseek-harness-freeze-content-on-window-resize
DSH 生态强调「一切皆插件」;SkillHub 等社区目录独立于 DeepSeek / 幻方,并非官方应用商店。安装前建议自行查看源码与许可证——插件随当前 DSH 进程在本地 Web GUI 中运行,权限与宿主一致。
背景:为何会「读丢行」¶
在 DSH 0.2.0-rc.2(win32)上验证过的行为大致如下(插件依赖 DSH 稳定 data-* 属性,而非哈希 class,较新版本通常仍可工作):
对话列在滚动容器内,内容宽度由窗格与 ResizeObserver 共同决定。未在本地存过拖拽宽度时,宽度大致为 clamp(680px, 0.64 × pane, 920px),在约 1063–1438px 窗格范围内会随窗口逐步变化,每一步 resize 都可能重排。若曾拖拽过宽度,则使用 localStorage["dsh.conversation.contentWidth"] 中的偏好,直到窗格放不下才再缩小。
与此同时 scrollTop 仍是旧像素位置,重排后视觉锚点偏移。README 中给出过测量示例:强制变窄约 140px 时,阅读行可偏移约 2417px;启用锚点补偿后可回到 0px 偏移(方法见仓库 docs/self-test.md)。
核心功能¶
| 机制 | 效果 |
|---|---|
| 锁定内容宽度 | 对已挂载的阅读列用同一像素宽度固定 width / min-width / max-width(!important),换行不再随窗口变化;窗变窄时出现横向滚动而非重排。 |
| 保持阅读位置 | 每列维护 live anchor(阅读行上的 data-chat-anchor-key 及其相对视口顶部的偏移);resize 或 zoom 后跨两帧动画再加一次延迟 pass 重新对齐。 |
补充说明(与 README 一致):
- 视图处于「跟尾」状态(
[data-chat-following-tail])时,锚点补偿会退让,由 DSH 自带跟尾逻辑负责;宽度锁定仍生效。 - 侧栏内嵌的对话 transcript 仍用面板自身宽度,不受本插件对主阅读列的处理影响。
- 锁定后,加宽窗口不会再自动加宽阅读列(与 stock DSH 在
clamp未饱和时会变宽不同)。这是刻意取舍:宽度一次选定后保持不变;要改宽度需用面板 Width 或 DSH 自带拖拽手柄。
依赖与环境:
- 需要带 Web GUI 的 DSH(桌面窗口或
dsh web)。 - 无额外 npm 依赖、无构建步骤;客户端入口为已提交的
client/client.js(window.__ModuleLoader__.load手写 bundle)。
安装与启用¶
profile 名对应 ~/.dsh/profiles/ 下目录;文档示例使用 desktop,请换成你自己的 profile。
推荐方式(无需手动 clone):
dsh plugin --profile desktop add github:nnnanann/Deepseek-harness-freeze-content-on-window-resize
dsh plugin add 会把参数转给 pnpm,pnpm 支持的 spec 均可尝试。若当前 DSH 构建不接受 github: spec,可从本地 checkout 使用 link:(README 亦给出 git clone + link: 示例,此处不展开 clone 步骤)。
安装后会写入 profile 的 package.json、追加 dsh.profile.bundles 中的 freeze-content-on-window-resize 并完成安装。已打开的 GUI 页面可热加载,一般无需重启 DSH。
卸载:
dsh plugin --profile desktop remove freeze-content-on-window-resize
典型用法¶
界面控件¶
窗口右下角有一个约 26px、半透明为主的锁定按钮(fixed 于窗口,侧栏打开时可能叠在侧栏上;可在面板中隐藏)。点击打开设置面板,主要项如下:
| 面板项 | 含义 |
|---|---|
| Lock content width / 锁定内容宽度 | 总开关;关闭则恢复 DSH 默认行为。 |
| Width / 宽度 | 数字或预设 620 / 680 / 748 / 820 / 920;首次运行会采用 DSH 当前内容宽度,避免安装瞬间跳变。 |
| Keep reading position / 保持阅读位置 | 锚点补偿开关。 |
| Show this control / 显示此控件 | 隐藏右下角按钮。 |
| Reset / 恢复默认 | 恢复默认设置。 |
面板文案随 DSH UI 语言;插件自带中英文词典,其他语言时回退中文。开关下方灰字说明权衡:锁定后文本不再因窗宽重排,窗变窄则横向滚动。
快捷键¶
文本输入框聚焦时快捷键不生效:
| 快捷键 | 作用 |
|---|---|
Alt+Shift+L |
切换宽度锁定 |
Alt+Shift+W |
循环预设宽度 |
DSH 自带的列宽拖拽(cursor: col-resize)仍可用:按下拖拽会释放锁定,松开指针后以拖拽宽度作为新的锁定宽度。
配置存储¶
设置保存在页面 localStorage 的 freeze-content-on-window-resize:config;width: 0 表示「采用 DSH 自身宽度」,首次运行会解析并写入实际值。示例:
{ "lockWidth": true, "width": 748, "keepPosition": true, "showControl": true }
控制台诊断¶
在 DevTools 控制台:
__fcwr.status()
__fcwr.realign()
__fcwr.dispose()
启动时控制台会输出一行当前语言日志,例如锁定列宽或 width lock off。另有 freeze-content-on-window-resize:boot stamp 记录 revision、列宽与 anchor 等,便于排查。
适用场景与注意¶
适合: 经常在 DSH Web GUI 中阅读长回答、需要分屏或频繁调整窗口大小、且希望换行稳定、少丢阅读位置的开发者或日常用户。
注意:
- 锁定宽度后,单纯拉大窗口不会自动变宽阅读列;需 consciously 调整 Width 或拖拽 DSH 手柄。
- 窄窗下可能出现横向滚动,这是锁定换行的直接结果。
- 插件仅在客户端 Web 层生效,不修改 DSH 服务端;安装与更新请使用官方 README 中的
dsh plugin命令,并核对 MIT 许可与源码。 - 开发验证基线为 DSH desktop 0.2.0-rc.2;其他版本若 DOM 或属性有变,需自行验证。
实现要点(简述)¶
插件不依赖哈希 class,主要使用 DSH 稳定属性,例如 [data-chat-flow](列)、[data-conversation-scroll](外层滚动)、[data-composer-seat]、[data-chat-anchor-key]、[data-chat-following-tail]。宽度变量仅回写到同时持有列与相关上下文的元素,避免误伤侧栏 transcript。
结尾¶
freeze-content-on-window-resize 用「锁列宽 + 锚点补偿」应对 DSH 对话区在 resize 时的重排与 scroll 错位,适合长文阅读场景。安装与配置以仓库 README 为准。
- 目录页:https://www.skillhub.cn/plugins/nnnanann/Deepseek-harness-freeze-content-on-window-resize
- GitHub:https://github.com/nnnanann/Deepseek-harness-freeze-content-on-window-resize