跳到主要内容

dsh-question-index

已验证

dsh-question-index · v0.1.0 · MIT · Web 界面

DeepSeek Harness 会话内提问索引:右侧圆角横线时间线,悬停查看提问、点击跳转。In-session question index rail for the DeepSeek Harness Web UI.

安装

dsh plugin add dsh-question-index

dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南

源码

发布到 npm 但没有公开仓库。安装前请检查包内容。

标签

作者

说明文档

dsh-question-index

DeepSeek Harness 会话内提问索引:右侧一条圆角横线时间线,每个用户提问一条短横线,悬停展开全部提问面板,点击跳转到对应消息。

In-session question index for the DeepSeek Harness Web UI: a floating right-edge timeline of short rounded lines — one per user question — with a hover-expanded all-questions panel and click-to-jump.

功能

  • 横线时间线:右侧悬浮一条圆角横线时间线,与右边缘保持距离、上下居中;默认白色短横线,当前阅读位置的提问为蓝色。横线等距分布:提问多就密集、少就稀疏,与回复长度无关。
  • 悬停展开全部提问:鼠标悬停到时间线上,弹出白底圆角面板,列出会话全部提问(内容预览 + 提问时间,无轮次),点击任意一条跳转。
  • 点击跳转:点击横线或面板条目,平滑滚动到会话中对应的提问并短暂高亮;跳转后面板保持打开(鼠标未离开时间线)。目标提问尚未加载到会话窗口时,自动按需加载更早历史再跳转。
  • 当前位置高亮:滚动会话时,当前阅读位置附近的提问横线高亮为蓝色。
  • 长会话不卡:索引由会话事件增量构建(追加一条提问 O(1),流式输出期间零重扫),"全部提问"来自宿主侧轻量路由(不加载整个会话内容),面板列表窗口化渲染,千轮对话也流畅。

安装

要求 Node.js >= 24、DeepSeek Harness 0.1.1-rc.2 及以上。

# 从 npm 安装(推荐)
dsh plugin --profile web add dsh-question-index

# 或从 GitHub 源码安装
dsh plugin --profile web add "github:deadz000/dsh-question-index#main"

# 启动 Web UI
dsh web   # → http://127.0.0.1:3080

从 GitHub 安装时,pnpm >= 10 默认禁止运行 git 依赖的 prepare 构建脚本,首次 add 会失败并打印一个 allowBuilds 键;把它复制到 profile 的 pnpm-workspace.yaml~/.dsh/profiles/web/pnpm-workspace.yaml)后重新 add 即可。

安装后重启 dsh web 服务(或重启 dsh --profile web),刷新页面。本地 file: 安装是拷贝不是链接:改代码后需要 pnpm run build + dsh plugin --profile web remove dsh-question-index && dsh plugin --profile web add file:... 刷新拷贝,再重启服务。

使用方法

打开任意一个多轮对话,右侧出现悬浮的横线时间线:

  1. 把鼠标移上去 → 弹出「全部提问」面板,看到这个会话里所有提问(含尚未加载进会话窗口的更早提问)。
  2. 点击任意横线或面板条目 → 跳到那条提问,消息短暂高亮;面板保持打开。
  3. 上下滚动会话 → 当前提问的横线高亮为蓝色。

工作原理

dsh-question-index 完全跑在 DeepSeek Harness 官方的扩展点上,不修改任何 harness 源码:

组件 扩展点 职责
node half 路由 webServer.register(自定义 HTTP 路由 /dsh-question-index/<sessionId> 从宿主内存中的完整会话日志提取全部提问元数据(collectQuestions),浏览器一次 fetch 拿到,不加载整个会话内容
questionIndexDefinition ctx.conversationEvents(ConversationNodeDefinition) 每个用户提问一个 Context:只匹配 user/message(人类来源)事件,状态即条目
QuestionIndexBuilder ctx.conversationViews(自定义 view target question-index 增量累积实时尾部的有序提问快照,只接收变化的提问节点
QuestionIndexOverlay shell.overlay(root 作用域) 全框架浮动层挂载点,通过 SessionProvider 桥接会话作用域
QuestionIndexRail question-index.rail(会话作用域,自声明子槽) 横线时间线 + 悬停面板 + 跳转(未加载目标时按需 loadOlder)+ 当前位置高亮

浏览器侧将「宿主全量列表」与「实时尾部索引」按消息 id 合并(mergeQuestionEntries):宿主的完整列表一次到位,新提问继续增量追加。

为什么长会话不卡

与「每次渲染都从完整会话快照重新推导提问列表」的做法不同,本项目遵循引擎的增量契约:

  • 追加路径 O(1):每条 user/message 事件只做一次类型检查匹配;assistant/chunk(流式输出)根本不匹配,模型打字期间我们零工作
  • "全部提问"不加载会话:宿主侧只扫描内存日志提取提问元数据(每条约几十字节),浏览器渲染的是紧凑列表,不会把整个会话历史拉进页面 —— 第一次进入长会话也不会卡。
  • 禁止全量扫描:Definition 和渲染路径从不遍历事件窗口、Context 集合或聊天节点列表。
  • 快照引用稳定:没有新提问时快照对象不变,组件零重渲染成本。
  • 位置是纯数学:横线等距分布(position.ts),不做 DOM 测量;当前位置高亮是 O(1) 序号映射 + rAF 节流。
  • 面板窗口化:全部提问面板只渲染可视区间的条目(QuestionIndexPanel),千条提问也只渲染几十行 DOM。

开发

与 DeepSeek Harness checkout 同级放置(仅开发期 typecheck 需要;运行时从 npm 安装依赖):

~/git/deepseek-harness   # harness 源码(可选,仅类型对照)
~/git/dsh-question-index # 本仓库
pnpm install
pnpm run typecheck   # 类型检查
pnpm test            # vitest 单测(逻辑、构建器、宿主提取、合并、组件)
pnpm run build       # tsdown:lib/index.js + lib/client.js
pnpm run watch       # 开发期监听重建

本地安装测试:

pnpm run build
dsh plugin --profile web add file:D:/path/to/dsh-question-index
# 改代码后刷新拷贝:remove 再 add(见上),然后重启 dsh web

已知限制

  • 跳转未加载的提问需按需加载:点击一个还没加载进会话窗口的更早提问时,会逐页加载更早历史直到目标出现(有页数上限),这是 DSH 的分页机制决定的;首次进入会话本身不受影响。
  • 横线等距分布:位置与提问总数等距,不反映消息的实际像素高度,长消息段落会有少量视觉偏差;当前位置高亮同样按等距近似。
  • 时间线覆盖原生滚动条区域:横线悬浮在滚动条上方,横线处无法直接拖动原生滚动条(滚轮滚动不受影响)。
  • 单会话列假设:时间线定位到页面上唯一的会话滚动容器([data-conversation-scroll]);多列布局暂不支持。
  • 宿主路由无鉴权/dsh-question-index/<sessionId>/api 同信任域(默认仅回环地址),不额外做鉴权。

License

MIT