Web 端会话大纲面板:用户提问为根节点(序号、提问时间、该轮工具数与正文量),助手回复的 Markdown 标题按层级挂在其下;带层级滑块、搜索范围、收藏、复制与导出 Markdown、节级跳转、键盘导航、右侧密度刻度条与按会话记忆的阅读位置。纯只读 UI:不接触提示词与工具结果,不向提供方发起请求,只写自己的 localStorage 键。
安装
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:huangfuren/dsh-conversation
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
中文 | English
Web 端「会话大纲」插件:在页面右上角贡献一个浮动面板,把当前会话组织成一棵大纲树 —— 用户提问是根节点(带序号、提问时间、该轮工具数与正文量),助手回复里的 Markdown 标题按其层级挂到最近的提问下。点击任意一行跳到正文并短暂高亮。纯 UI 插件,不接触任何提示词、消息、工具结果,也不向提供方发起请求。
Project status: 1.x。已在 dsh 0.1.5-rc.2 +
webprofile 上验证,并适配 dsh 0.2.x(>=0.2.0-rc.1 <1.0.0);更早的 dsh 版本与其它 profile 未认证 —— 插件遇到不认识的宿主 API 会降级为"面板不出现"而不是抛错,但那条路径不在测试矩阵内。
版本脉络:1.1.0 合并了原 dsh-outline 插件的全部能力并把包名从 dsh-client-hqst 改为 dsh-conversation;1.2.0 起补齐了大量导航能力(三档宽度 / 侧边刻度条 / 搜索范围 / 导出 / 节级跳转 / 快捷键 / 阅读位置记忆),其中侧边刻度条已于 1.3.1 停用,1.5.5 起其代码就地注释保留并逐段标注原因(无行为变更)。
效果
- 常驻态(默认):浮动面板(
dsh-conv-panel),标题「历史提问 · 大纲」。- 三档宽度:紧凑 320px → 加宽 480px → 全屏 720px 整高,标题栏按钮循环切换; 换档时按面板位置决定朝哪边生长 —— 贴左向右长、贴右向左长、拖到中间则两侧对称,居中时若某一侧 空间不够,差额全部转给另一侧;贴边时靠墙那条边始终不动,所以加宽不会顶出窗口、缩窄会退回原位置 (边距 ≤ 32px 视为贴边,含默认停靠的 16px);
- 左右停靠:标题栏的停靠按钮在靠左 / 靠右之间切换,切换后回到默认位置;
- 标题栏是拖拽把手(4px 阈值,拖动后位置持久化);
- 工具行:展开/收起全部、只看收藏、复制大纲(Markdown 进剪贴板)、导出为 .md 文件、搜索框(命中片段高亮);
- 搜索范围:点击搜索框左侧的「全部 / 问题 / 标题」按钮循环切换,只匹配提问或只匹配标题;
- 层级滑块:0 = 仅显示提问,1~6 = 展开到对应标题层级,圆点 tooltip 显示各层级数量;
- 背景不透明度:面板底部滑杆可在 40%~100% 之间调面板背景的不透明度(默认 100%,与旧版观感一致)。只降底色、不降文字;背景模糊由它反向派生 —— 越透明,背后的字越透得出来、模糊半径也越大(40% 时约 3.6px,100% 时为 0),这是设计使然而非渲染故障。点右侧百分比回到完全不透明,双击滑杆同样可复位。用于面板挡住正文时把内容透出来;
- 列表行:折叠箭头 /
Q1序号徽标 / 文本 / 提问时间 / 该轮「N 工具」「N 字」统计 / 收藏星;当前阅读位置的行自动浅底高亮并滚入可视区; - 底部:回到顶部 / 回到底部。
- 收起态:面板收起后,唯一的入口是会话标题栏上的「大纲」按钮(
order: 100,位于原生按钮旁)。 1.3.1 起停用了原本的右侧刻度条(dsh-conv-rail)与右上角胶囊(dsh-conv-fab)两个入口 —— 它们与标题栏按钮重复,且刻度条会压住自带同类导航的插件。1.5.5 起这段代码已就地注释保留: 每段都写明失效原因(渲染点在哪个提交移除)、保留原因、恢复方式与停用日期, 在client.js里搜「停用保留」即可定位;既不删除、也不参与构建,将来要重启时去掉注释标记即可。 - 跳转粒度:单击任意行 → 跳到该节点;双击任意行 → 跳到"本节末尾"(下一个同级或更高级节点,已是最后一节则滚到底部)。
- 键盘:面板聚焦时
Alt+↓ / Alt+↑跳到下一个 / 上一个节点(只在面板内监听,不抢宿主按键)。 - 阅读位置记忆:每个会话记录上次看到的大纲节点,切回来时面板直接落在那里。
- 空态:会话里还没有提问/标题时显示「暂无大纲内容」。
- 首屏占位:刷新后宿主会话/chat 数据尚未就绪时,先在面板位置显示占位「正在读取会话…」,会话绑定就绪后才挂载面板;若绑定先到、chat 快照稍后写入,面板可能先渲染一次空态,下一次快照更新即补上;宿主超过 2 秒仍未给出会话绑定就照常挂载并展示空态。
- 标题栏按钮自适应(icon-only):右栏展开、标题栏变窄时,标题栏里的「大纲」按钮自动收成纯图标(与宿主原生按钮同机制:同一容器查询
@container (width<=540px)、同一容器.wSkVaW_titleRow),按钮真实收窄且aria-label/title保留、可点击、可键盘聚焦;右栏收起后自动还原。旧版宿主无该容器 ⇒ 保持宽态(降级安全)。 - 状态持久化:
pinned/ 拖拽位置 / 视图档位 / 面板背景不透明度 / 默认展开层级 / 每会话收藏 / 每会话阅读位置,全部走localStorage(键前缀dsh-conversation:);1.3.1 停用的刻度条开关字段自 1.5.5 起不再读取(写入逻辑已注释掉),旧localStorage里的该键不会被清除、只是被忽略,恢复那段代码即可继续复用旧值。
实现方式
| 半 | 文件 | 职责 |
|---|---|---|
| Node 半 | index.js |
空 apply(),仅为让插件在宿主 cordis.yml / Loader 中可见;无任何宿主侧行为 |
| 客户端半 | client.js |
实际 UI,经 exports["./client"] + package.json 的 dsh.client 声明被 Web 宿主发现 |
客户端半行为:
- 通过
inject: ["slots", "sessions"]取两个必需服务;只有slots.register本身缺失才打警告并禁用面板。apply()同时把sessions/uiSession服务抓进插件私有的 host bridge。 - 在
conversation.session.header.actions槽位注册dsh-conversation(order: 100),把store/host合并在槽位 props 的最后 —— 宿主传空 props、或第三方 header 传另一套形状,都覆盖不掉它们;槽位声明晚到时有「注入 / 声明订阅 / 退避重试」三层通道(见「兼容性 / 注册通道三层兜底」)。 - 数据有两条路:slot props 的
useChat/useSession/useSessions(旧宿主 / 第三方 header 注入)优先;它们缺失时读uiSession.current的hooks.chat(ChatSnapshot)与hooks.session(hasMore/loadOlder)。两条路都经conversationNodesOf归一 +Array.isArray兜底;都拿不到就是空态。sessions.binding(id)仍用于旧回退与 loadOlder。 buildOutlineItems把快照摊平成大纲项:user/steering → level 0(同时统计该轮工具数与正文字数);assistant 的 text block 按 ATX 标题(^#{1,6}\s+,跳过围栏代码块内的#)→ level 1~6;流式中的partial标题实时出现并带呼吸动画;tool-call / context 等非正文节点只计数、不进大纲。OutlineManager持有层级、搜索(含范围)、收藏状态并与 localStorage 同步;折树 / 折叠恢复 / 可见行压平 / 定位揭示都是纯函数。- 点击定位:先用宿主内部锚点属性
[data-chat-anchor-key]→[data-message-key]→[data-node-key],命不中再按data-chat-flow-kind顺序匹配 + 文本校验;始终找不到就提示「当前节点暂不可定位」,绝不静默错位滚动。 - 「本节末尾」跳转由
nextSectionItem在先序扁平顺序里找下一个同级或更高级节点,没有则回退到滚到底部。 - 导出走内存里的 Blob +
URL.createObjectURL(不请求宿主、不落盘到工作区),内容与「复制大纲」同源(outlineToMarkdown)。 - 样式全部使用
dsw-alias-*/dsw-shadow-*主题令牌,无硬编码色值。
兼容性
- 快照契约双支持:新结构
snapshot.nodes[]+snapshot.partial;旧结构snapshot.chat.{order, nodes(Map)}+node.data.content。结构不符时返回空列表而不是抛错。 - 槽位数据源加固:
conversation.session.header.actions的 owner props 可能为空(dsh 0.2.0-rc.2 即以renderSlot(..., {})渲染),第三方 header(如dsh-better-sidebar)接管 header 区时 props 形状也可能不同。面板优先用 slot props 注入的 hook,缺失时改读uiSession.current的hooks.chat/hooks.session;两条路都拿不到就渲染空态,不会让槽位崩溃。 - 宿主 API 降级:
slots/sessions/uiSession的形状、store 的getSnapshot/subscribe都逐层探测并吞掉异常;任一改名或缺失只让本面板降级为空态,不会拖垮同槽位的其它插件。 - 一次性兼容性诊断:首次拿到可识别的快照(0 行也记录)时用一条
console.info记录实际结构(legacy.nodes/nodes/chat.order…)与行数;结构无法识别时console.warn列出实际键名。首次渲染出大纲时探测 DOM 契约(data-conversation-scroll/data-chat-flow-kind/ 锚点属性),缺失或改名时console.warn说明已降级为「按文本顺序匹配」。两类日志每次页面加载最多各一条,不改变任何容错行为。 - 注册通道三层兜底:槽位声明可能晚于插件装载,首次
slots.register会抛slot "…" is not declared(0.1.5-rc.2 / 0.2.0-rc.1 / 0.2.1-rc.5 三版都会这样抛)。因此按宿主实际能力选通道:①slots.inject存在 → 沿用宿主契约;② 否则slots.subscribeDeclaration存在 → 订阅声明事件后注册,槽位塌缩重建也能自动恢复;③ 都没有 → 立即尝试 + 700ms 退避重试(≤12 次)。inject消失不再导致面板整块消失。重复注册由declarationEpoch把关,避免同 id 触发already has an entry。 - 样式能力探测:背景透明度依赖 CSS
color-mix(Chromium 111+)。宿主不支持时该行不渲染、面板保持完全不透明,不会留下「拖了没反应」的死控件。 - 文案双语:自带 zh/en 字典,按
navigator.language回落;宿主若注入了可用的t则优先使用。
设计说明与来源
本插件的功能取舍参考了 DSH 生态内同类插件(会话大纲 / 提问导航 / minimap 类)公开的功能描述与交互形态 —— 同类能力(大纲树、右缘导航条、搜索、导出、阅读位置跟随等)属于通用交互设计,不受版权保护。代码为本仓库独立实现,未复制、未改编任何第三方源码,也不依赖任何第三方运行时包(唯一 peer 依赖是宿主提供的 react)。
依赖与许可
- peerDependencies:
react ^18.2.0(由宿主提供,无第三方运行时依赖) - license:MIT
兼容矩阵
| 组件 | 支持基线 |
|---|---|
| Node.js | >= 22.19 |
| DSH | >= 0.1.5-rc.2 < 1.0.0(槽位名、slots.inject / register 契约在 0.1.5-rc.2、0.2.0-rc.1、0.2.1-rc.5 上核对一致;实机验证:0.2.1-rc.5 桌面端) |
| react | ^18.2.0(宿主提供) |
| 界面 | web(dsh.client.platform) |
| 渲染引擎 | Chromium 111+(仅「背景透明度」需要;0.2.1-rc.5 实测 Electron 44.7.0 / Chromium 152) |
超出上表的组合未认证。
安装
优先用不可变 tag:
dsh plugin --profile web add github:huangfuren/dsh-conversation#v1.5.6
跟踪开发分支(需要尚未发布的最新提交时用这个):
dsh plugin --profile web add github:huangfuren/dsh-conversation
或指向本地 checkout:
dsh plugin --profile web add file:/absolute/path/to/dsh-conversation
package.json 已声明 dsh.bundle.patch(./cordis.patch.yml),由 dsh plugin add 自行维护组合。
若想手工插到 profile 的 cordis.patch.yml,等价写法是:
- insert:
- id: conversation
name: dsh-conversation
开发与测试
npm run check # node --check 两个入口
npm test # node --test
npm run verify # check + test(已绑定 prepack,测试不过打不出包)
client.js 不是 ES 模块:dsh 客户端模块加载器按 window.__ModuleLoader__.load({ id, factory }) 加载它,
factory 里的 require 只能解析宿主提供的外部模块(本插件只用到 react),相对路径依赖在真实加载器里
解析不了 —— 所以客户端保持自包含单文件(dsh-grafana 的 client.js 约 67 KB,同样如此)。
test/client.test.js 用最小加载器桩把它 load 进来,断言它导出的纯函数 internals;
test/host-service.test.js 驱动 apply() 注册出来的槽位组件,锁定「宿主 slot props 为空时从
uiSession / sessions 服务取快照」以及服务缺失 / 恶意 props 的降级路径。
test/diagnostics.test.js 锁定 DOM 契约探测矩阵、快照形状矩阵,以及两类一次性日志「每次页面加载最多一条」的约定。
已知限制
- 点击定位依赖宿主的 DOM 契约(
data-chat-anchor-key/data-message-key/data-node-key或data-chat-flow-kind);若未来整体改属性,需在locateByKey/collectUserRows里追加新候选——插件会在首次渲染出大纲时探测一次,缺失时打一条console.warn(见「兼容性 / 一次性兼容性诊断」)。 - 服务路径跟随宿主「主视图」绑定的会话(
uiSession.current):该槽位的 owner props 不带sessionId,所以同页子会话(subagent)的 header 上,面板展示的仍是主会话大纲。若宿主将来在 props 里给sessionId,会优先使用它。 - 面板为 fixed 定位,与其它右上角悬浮插件共存时可能重叠,需各自错开
top。 - 「N 工具 / N 字」统计口径是快照里该轮之后的 tool 节点数与 assistant 正文字符数,宿主若改变节点 kind 命名会退化为不显示(不会报错)。
- 导出文件名固定为
dsh-conversation-outline.md,不做命名配置。
链接
同类插件
zhu1090093659/dsh-web#packages/dsh-task-board★ 10124
侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。
zhu1090093659/dsh-web#packages/dsh-web-all★ 10124
DSH Web UI 插件与皮肤合集:任务看板、git 图、右侧面板、远程移动端 UI、桌宠、实时 token 统计与皮肤中心。
ccch1mneyyy/dsh-TUI★ 7006
Claude Code 风格全屏终端 UI:像素鲸鱼顶栏、实时工作状态行、思考流式展开。
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 6922
右下角常驻的小鲸鱼挂件:余额、今日已用与每轮对话消耗(含峰谷价),余额预警与今日预算的泡泡内容都可编辑;泡泡点击序列模块化自定义,支持并列加权 A/B、随机台词与随机图片;内置 30+ 厂商模板(OpenAI / OpenRouter / Kimi / 硅基流动 / 方舟 / 智谱 / MiniMax 等),按模型查余额与订阅额度;另有任务结束音效、导入音频、自定义角色与资源管理。数据全在本机,无遥测。
omdsh-dev/DSH-better-sidebar★ 4933
侧边栏完整工作台:内置文件渲染编辑、终端、Git 与子代理,支持三方插件注册新 Tab。
Devin-AXIS/deepseek-design#deepseek-idesign★ 1723
可视化设计工作室,支持网站、App 原型、海报、信息卡、报告和杂志的模板创建、元素编辑、选区级 AI 草稿衔接与导出。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。