Chuyển đến nội dung chính

dsh-question-index

Đã xác minh

dsh-question-index · v0.1.0 · MIT · Giao diện web

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

Cài đặt

dsh plugin add dsh-question-index

Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.

Mã nguồn

Phát hành lên npm mà không có repository công khai. Hãy kiểm tra nội dung package trước khi cài.

Thẻ

Tác giả

Readme

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