Passer au contenu principal
X

dsh-open-at-last-input

xinming-chenniang/dsh-open-at-last-input

Lands the chat viewport on your most recent own message when you open a finished session, instead of the bottom of the conversation; running sessions still follow the bottom. Client-only: it reads the viewport and never touches files, credentials, subprocesses, or the network.

Installer

dsh plugin --profile web add github:xinming-chenniang/dsh-open-at-last-input

README

dsh-open-at-last-input

English — When you open a settled DeepSeek Harness session, land the chat viewport on your own last message instead of the bottom. Client-only; no files, credentials, subprocesses, or network.

打开已结束的 DSH 会话时,视口落在你最近一条本人发言处,而不是页面最底部;正在运行的会话仍跟到底。

  • 纯客户端:不碰文件、凭据、子进程、网络;宿主半部只有空入口。
  • 安装:npm 已发布;桌面端 设置 → 插件 → 添加插件 输入 dsh-open-at-last-input, CLI 用 dsh plugin --profile <profile> add dsh-open-at-last-input。
  • 失效边界:DOM 契约或 sessions.list 形状变化时静默不生效,但不会把界面带崩。

背景与出处

  • 出处:2026-10-20 用户反馈「切换到一个已完成的会话时,窗口默认停在最低端;我需要从我的最近一条发言那里看」。首版做成 asar 补丁(本机工具,未随包发布,2026-10-21 已验收通过);2026-10-21 因「要发布给别人用」改做成客户端插件(本包)。
  • 是什么:一个纯客户端 DSH 插件。打开已结束的会话(首次进入或从别的会话切过来)时,把聊天视口落到你最近一条本人发言(user / steering 行)的顶部,上方留 24px;正在运行的会话仍跟到底部。
  • 状态:唯一实现(2026-10-21 22:01 起)。asar 补丁已于 22:01 回滚(原厂 182,794,869 B / SHA256 47874618BF5B…),现在这个落点只有插件在做。此前 2026-10-21 已确认它独立生效:调试日志(window.__DSH_OPEN_LAST_INPUT_DEBUG__ = true)里出现 切换/打开会话 <sid> → 到位 <sid>,证明是插件自己找行、对齐、稳定住的。日志里可能先出现一行 等不到本人发言行,那是会话刚切换、行还没挂载时第一次 tick 的落空,60ms 后重试即到位(重试路径在起作用,不是故障)。
  • 处置:保留。失效条件很窄:DOM 契约([data-conversation-scroll] / [data-chat-flow-kind])或 sessions.list 快照形状变了 → 插件静默不生效,但不会把界面带崩(所有读取都在 try 里)。
  • 关联:asar 补丁版(同一需求的第一实现,仅本机保留,未随包发布)· dsh-button(同形态的客户端插件样板,sessions.list 的用法就是从它抄的)· ~/.dsh/profiles/<profile>/package.json(注册点)。

为什么改成插件

asar 补丁能跑,但三个代价让它不适合发布:锚点绑死 0.2.1-rc.2 的 client.js、只支持 Windows(apply.ps1)、每次升级都要重打。插件不碰二进制,可装可卸。

能做成插件的依据(都来自 0.2.1-rc.2 的源码,不是猜的):

需要的能力依据
知道「当前是哪个会话」sessions.list.getSnapshot() → { current, byId }(dsh-closeout-button 已在用)
拿到滚动容器ChatViewport.attach() 里 list.closest("[data-conversation-scroll]") ?? list
找到「本人发言」行原生 scrollToLastInput 用的同一个选择器:`[data-chat-flow-kind="user"
不和原生抢原生的落点只在首次挂载跑一次(content.current.opened),切会话时不滚动 —— 空出来的这段正是我们可以补的
用户一插手就收手原生把 wheel/touch/keydown 当「读取意图」;插件只在自己那 ~800ms 的落点窗口里监听,触发即放弃

行为

场景打开后的落点
已结束会话,之前没读过 / 上次停在底部最近一条本人发言的顶部 ← 本插件
已结束会话,上次停在中间本插件会把它拉到最近一条本人发言(原生「记住中间位置」的语义在这里让位;若是插件的目标,就按插件来)
正在运行的会话跟到底部(插件读到 running 就跳过)
没有本人发言的会话保持在原位,不动作

安装

npm 已发布 dsh-open-at-last-input:

dsh plugin --profile <profile> add dsh-open-at-last-input

也可以用 GitHub 源 dsh plugin --profile <profile> add github:xinming-chenniang/dsh-open-at-last-input, 或 Release 资产(文件名形如 dsh-open-at-last-input-<版本>.tgz)。

在插件页「发现」里安装(可选)

DSH 插件页的「发现」标签页支持自定义索引。添加下面这个索引源即可看到本插件:

https://raw.githubusercontent.com/xinming-chenniang/dsh-open-at-last-input/main/market/index.json

索引本身在 market/index.json,格式与更新方式见 market/README.md。

装 / 卸

  • 装:dsh plugin --profile <profile> add dsh-open-at-last-input,或桌面端插件管理器里添加包名,然后重启 DSH。
  • 卸:插件管理器里 remove,然后重启 DSH。
  • 改源码调试:dsh plugin --profile <profile> add link:<本仓库路径>,改源码即生效,不必重新安装。

怎么确认它活着

重启应用后按 Ctrl+Shift+I 打开控制台,应看到:

[dsh-open-at-last-input] 已启用:打开已结束会话时落在你最近一条发言处

想看每次落点的调试日志,先在控制台执行一次:

window.__DSH_OPEN_LAST_INPUT_DEBUG__ = true

之后切会话会打印 切换/打开会话 <sid>、到位 <sid> 或 用户插手,放弃本轮 <sid>。

关掉它 / 和「位置记忆」类插件的关系

  • 关掉:控制台执行 localStorage.setItem('dsh-open-at-last-input', 'off'),刷新页面生效;删掉这个键即恢复。关掉后插件不再有任何动作。
  • 和位置记忆类插件语义相反:dsh-chat-answer-anchor 这类插件把你留在上次停的位置、不让新回复拽走;本插件是在打开会话时把你拉到最近一条本人发言。两个都装会互相抢滚动条,建议只留一个。
  • 和消息导航条(右侧刻度、大纲面板)不冲突:那类插件负责「我想跳到某条」,本插件负责「一打开就在我该看的地方」。

兼容性

DSH 版本结果
0.2.1-rc.2已测:client entry active;npm test 11/11;滚动行为未在其它版本复验
0.2.2-alpha.2仅源码核对(未真机复验):inject: ["sessions"]、list.getSnapshot().byId、retainedBy.mainView、row.running 仍在;快照已无 current 字段,插件走 byId 兜底
其他版本未测;package.json 只声明 engines.dsh >= 0.2.0-rc.2

已知边界(没做,别当已做)

  • 运行中会话不处理:靠 byId[sid].running(或 busy / state / status)判断;宿主字段名若变,最坏是「运行中也被拉到你上一条发言」,不会崩。
  • 懒加载引起的位移:切换会话后持续跟踪 60ms × 25 步(≈1.5s);窗口内每步只比较位置、没变化时不写 scrollTop。1.5s 之后才撑开的内容仍会偏。
  • 首次启动就打开会话:会同样触发一次落点,这是有意的(重开应用时你也想从自己的发言接着看)。

2026-10-21 修复:开关右侧栏也会定位

现象:点开一个会话后马上打开右侧栏,聊天区会再跳一次到最近那条发言。

根因:落点有一个约 600–840ms 的跟踪窗口。窗口期内开/关右栏会改变聊天区尺寸,行的 getBoundingClientRect() 随之变化,跟踪器把这个变化当成「内容还没稳定」而重新对齐 —— 看起来就是「打开右栏也会定位」。

三处修复(都在 lib/client.js):

  1. 视口尺寸变了就放弃本轮:记下落点开始时的 scroller.clientWidth/clientHeight,后续 tick 一旦发现尺寸变化,直接 detach()。面板开合/拖分隔条/缩放窗口都属于「用户在看别的东西」,不该被拉回。
  2. 多滚动容器取最宽的那个:右栏/抽屉里也可能挂一个窄的会话视图;窄于 MIN_SCROLLER_WIDTH(320px)的不认作主视图,避免把预览面板当成聊天区去滚。
  3. 跟踪窗口收窄:MAX_STEPS 14 → 10(≈600ms),SETTLE_PX 1 → 2(吸收亚像素抖动),减少与用户操作的窗口重叠。

判据:改完后「点会话 → 立刻开右栏」不再跳;懒加载把位置顶偏时仍会在 600ms 内校正回来(两者不冲突:前者改的是 clientWidth/Height,后者只改 scrollHeight)。

Plugins associés