notload/dsh-session-toc

插件Plugin 原生Native ⭐ 2 MIT 会话与上下文Sessions & Context

- 常驻右侧:shell.overlay 浮动层,垂直居中靠右,默认 click-through,条目可交互。 - 每问一条:目录条目来自完整会话日志(host 端 sessionQuery.readSession),覆盖整段会话(含早期未加载进内存的消息)。 - 点击跳转:点目录项用 DSH 节点稳定 key(data-chat-anchor-key)精确定位并滚动;早期消息自动 loadOlder 补加载后定位;定位不可靠时降级为"仅高亮当前条目"。 - 可折叠:可收起成右侧一个窄条按钮,再点展开。

catalog 简介 / catalog descriptioncatalog description:为 DeepSeek Harness Web UI 每个会话页右侧加一个类似deepseek网页端的常驻、可折叠的目录栏:每条用户提问对应一个条目,点击即可滚动定位到对应消息并高亮当前条目。

项目介绍Project Overview

dsh-session-toc 是 DeepSeek Harness Web UI 的会话目录插件,在每个会话页右侧常驻一个可折叠目录栏,按用户提问逐条列出,点击精准滚动到对应消息并高亮当前条目。条目从 host 端完整会话日志读取,覆盖所有历史问题;支持明/暗主题切换、背景透明度调节、折叠收起,以及超长会话的虚拟列表渲染。适用于需要快速跳转、浏览长会话的场景。需注意:DSH 客户端配置管线暂未打通,minEntries 等参数目前以代码默认值生效。

dsh-session-toc is a DeepSeek Harness Web UI plugin that adds a persistent, collapsible table of contents on the right side of each session page, listing every user question. Clicking an entry scrolls the view to the matching message and highlights it. Entries are sourced from the full host-side session log, covering all historical questions, with auto/hide-on-few-entries, theme and opacity toggles, and virtualized rendering for long sessions. Use it for quick navigation in long conversations. Caveat: DSH's client config pipeline isn't wired through yet, so parameters like minEntries currently use code defaults.

或使用命令行安装(适合开发者)Or use CLI install (for developers)

命令行安装CLI Install

dsh plugin --profile web add dsh-session-toc

notload/dsh-session-toc 加入你的 DSH 配置(web profile)即可启用。

READMEREADME

dsh-session-toc

npm version npm monthly downloads license platform

右侧会话目录(Table of Contents)插件,为 DeepSeek Harness Web UI 的每个会话页加一个右侧中间常驻、可折叠的目录栏:把会话里的每个「用户提问」作为一条目录项列出,点一下尽量把会话滚动到对应消息,并高亮当前条目。

对应 DeepSeek 官网网页版里"右侧目录索引,点一下跳转到对应内容"的能力。

特性

  • 常驻右侧shell.overlay 浮动层,垂直居中靠右,默认 click-through,条目可交互。
  • 每问一条:目录条目来自完整会话日志(host 端 sessionQuery.readSession),覆盖整段会话(含早期未加载进内存的消息)。
  • 点击跳转:点目录项用 DSH 节点稳定 key(data-chat-anchor-key)精确定位并滚动;早期消息自动 loadOlder 补加载后定位;定位不可靠时降级为"仅高亮当前条目"。
  • 可折叠:可收起成右侧一个窄条按钮,再点展开。
  • 样式自切换 + 主题跟随data-dsh-toc-theme):目录栏明/暗皮肤三档auto(默认,跟随 DSH 全局主题,读不到则退化系统 prefers-color-scheme)/ light / dark,通过卡片顶部齿轮(设置抽屉)一键切换;并可调节背景不透明度(只影响背景、文字始终不透明)。选择持久化到 localStoragedsh-session-toc.theme / dsh-session-toc.bgAlpha),跨会话、刷新保留,独立于 DSH 全局主题。
  • 自动隐藏:当前会话的用户提问不足阈值(默认 3 条)时目录栏不显示,避免噪音。
  • 长会话友好:目录列表超过 200 条时启用固定行高虚拟列表,只渲染可见窗口 ± 缓冲,避免长会话撑大 DOM 内存;条目数据全量保留,点击定位不受影响。
  • 切换不卡顿:宿主侧做「进程内 LRU 缓存 + 落盘索引」双层降频,切换会话不再反复触发全量 readSession(磁盘读 + 深拷贝 + 全量 replay);persisted 会话按 revision 失效、live 会话按消息序号增量失效。
  • 重启秒开:落盘索引($DSH_HOME/storages/session-toc/)跨进程/重启保留,重启后切换会话也能秒开。
  • 触发性更新(issue 本轮):监听会话快照,新提问 ~1.2s 内自动进入目录(本地即时插入 + 后台全量矫正),不再等切换/手动重载。
  • 美术细节(issue#9):悬浮圆角化(卡片四角 12px + 右侧 8px 不贴屏、dock 独立圆钮、active 左侧强调条、折叠窄条圆角);设置抽屉化(控制条只留齿轮,popover 收纳主题/透明度/重载,ESC 关闭);可拖拽定位将在下一版本(v0.1.10)加入。
  • 零侵入:不改动任何 @deepseek-ai/* 内置包,仅以 bundle 插件方式挂载。

安装

需要本机已安装 pnpm,且 DSH 的 web profile 存在(如 ~/.dsh/profiles/web)。

方式一:从 GitHub 克隆后以 link 方式安装(推荐)

git clone https://github.com/notload/dsh-session-toc.git
cd dsh-session-toc
dsh plugin --profile web add link:$(pwd)

如果 $(pwd) 在你的 shell 里不生效,直接写完整的绝对路径即可,例如 Windows: dsh plugin --profile web add link:C:\Users\<你的用户名>\dsh-session-toc

方式二:安装已发布版本(若已发布到 npm)

dsh plugin --profile web add dsh-session-toc

安装后会写入 profile 的 package.jsondependencies,并自动进入 dsh.profile.bundles 层栈(dsh plugin 会自动 reconcile)。

确认进入层栈:

dsh plugin --profile web list

然后重启 dsh web,浏览器访问同一 URL,即可在会话页右侧看到目录栏。

配置

目录参数通过 cordis.patch.yml 传给 host 半侧(applyconfig)。注意:DSH 客户端配置管线当前尚未打通,浏览器半侧收到的 config 是空对象,因此以下参数目前以代码默认值为准(与 dsh-pet 同样的限制)。

配置项(对照 lib/client.js 的默认值):

key 默认值 说明
minEntries 1 当前会话用户提问少于该值时隐藏目录栏
maxChars 48 单条目录文案的最大字符数(超出加省略号)
collapsed false 初始是否折叠

当前这些值在 client 里不可被用户覆盖。若需要可配置,需等 DSH 打通客户端配置管线,或改为硬编码默认值。

行为与限制

目录条目来源

  • 条目来自完整会话日志:浏览器侧请求 host 端 /session-toc/questions?sessionId=…,host 用 ctx.sessionQuery.readSession 读取整个会话日志并提取全部 user/message 事件,再返回给浏览器生成目录。
  • 覆盖整段会话:不依赖浏览器"已加载到内存"的节点。DSH 会话视图是按需加载的(有"加载更早"按钮,早期消息默认不在内存),但目录仍能列出全部历史提问。
  • 图片/文件配文字时用文字:如果一条用户消息带有文字说明,目录文案直接用文字(忽略图片/文件名);只有纯图片/文件、无任何文字时,才用文件名代替(🖼 文件名,无文件名用媒体类型兜底)。这样就不会出现"文字说明 + 文件名"混杂的目录条目。
  • 排除上下文注入:只保留 source.kind === 'user' 的事件,session-referenceworkspace 等注入式上下文不会进目录。
  • 不受 DSH 上下文压缩影响:目录始终由 host 读取磁盘上的完整会话日志readSession / 落盘索引)生成;DSH 上下文压缩(thresholdRatio/retainRatio 的早期消息摘要化)只改变主会话的"记忆",不动日志压缩后目录条目完整无损。

加载失败降级(长会话 / 格式不兼容)

  • host 读取失败(如会话日志含本 harness 不认识的 background-agents/* 新事件类型,报 SessionFormatUnsupportedError)时,返回结构化错误码(SESSION_FORMAT_UNSUPPORTED / SESSION_READ_FAILED)。
  • 前端收到失败后不再静默清空:会降级为"浏览器已加载节点"的目录(部分可用),并在目录里显示"目录加载失败(已显示部分已加载内容)+ 重试"提示条。
  • host 对读取结果做「进程内 LRU 缓存 + 落盘索引」双层降频:
    • 进程内 LRU 缓存(上限 64 会话 + 8MB 总字节预算)命中直接返回已序列化 JSON;长对话多个大缓存共存时按字节预算再多淘汰,兜住进程堆内存;
    • 落盘索引($DSH_HOME/storages/session-toc/<id>.json)跨进程/重启保留,仅对 persisted 会话生效;
    • 失效信号:persisted 会话按 revisionsessionPersistence 的 stat 级变更 token),live 会话按消息序号(lastSeq)零 IO 增量校验。
    • 这样切换会话 / 重启后都不再反复触发 readSession(磁盘读 + 多次深拷贝 + 全量 replay 校验);失败结果不缓存。
  • 安全:路由不携带 CORS 头(由浏览器同源策略拦截跨源读取);并校验请求携带浏览器来源信号(sec-fetch-site 或同源 Origin),curl/脚本/局域网主机等裸请求一律 403;sessionId 还需属于当前 host 可见会话(live 或 persisted),否则拒绝;错误响应不回显内部错误详情。

滚动定位

  • DSH 会话视图的每个 chat 节点在 DOM 上有稳定属性 data-chat-anchor-key(= 节点的 engine 稳定 key)。本插件优先用它来精确定位:点击条目时 document.querySelector('[data-chat-anchor-key="<key>"]') 找到节点并 scrollIntoView({block:'center'})。这比文本/图片匹配可靠得多,图片消息也能准确定位(图片节点同样有该属性)。
  • 节点 key = conversationContextKey("input-message", userMessageId) = "13:input-message" + id,其中 id 是 host 从会话日志取出的用户消息 id。
  • 早期消息自动补加载(渐进、不卡顿):DSH 会话是虚拟列表(每页约 50 条),早期消息默认不在 DOM。点击条目时若目标已加载则立即定位(秒回);若需补加载,插件先把视图滚到当前已加载的最早边界给即时反馈,再逐页调用 session.loadOlder()每页之间用 requestAnimationFrame 让出主线程(不再用 requestIdleCallback 排队叠加,避免浏览器忙碌时越卡);guard 按目标距离估算(estimateLoadOlderPages,封顶 40 页)。浏览器 console 会打印 [dsh-session-toc] jump {targetSeq,pages,ms} 供量化。
  • 回退:节点始终未找到(如会话正在运行、loadOlder 不可用)或拿不到 id 时,降级为文本匹配;仍失败则仅高亮条目。

开发与测试

npm test   # 等价于 node --test --test-isolation=none --expose-gc(全量)
  • 内存/泄漏探针的判据依赖强制 GChost-memory / memory-leak-probe 用「双 GC + 线性回归/中位数」区分真泄漏与低 GC 频率机器的堆未回收。测试文件自带兜底不带 --expose-gc 直接跑(如 node --test test/memory-leak-probe.test.js)会自动以强制 GC 重跑自身,任何跑法下判据都有效,不会误报红灯。
  • memory-leak-probe 除回归判据外,还有一条「FinalizationRegistry 正面证明」用例:LRU 淘汰的对象必须可被 GC 回收,直接证明缓存无引用残留。
  • 本机跑法注意:Windows 下 test runner 需 --test-isolation=none(否则 spawn EPERM)。

目录结构

dsh-session-toc
├── package.json          # bundle + client 声明
├── cordis.patch.yml      # 挂载声明(insert 行)
├── lib
│   ├── index.js          # host 半侧占位插件
│   ├── client.js         # 浏览器半侧:右侧目录栏 UI + 滚动定位
│   └── types
│       ├── index.d.ts    # host 侧类型
│       └── client
│           └── index.d.ts # 浏览器侧类型
└── README.md

License

MIT © notload


如果这个插件对你有帮助,欢迎点个 ⭐ Star 支持一下~

上一个 Prev dsh-vsceditor 下一个 Next dsh-ENHANCED