dsh-conversation-navigator 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/lib/client.js +12 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
# DSH 会话导航插件(dsh-conversation-navigator)
|
|
2
2
|
|
|
3
|
-
Codex 风格的 **DeepSeek Harness(DSH)Web
|
|
3
|
+
Codex 风格的 **DeepSeek Harness(DSH)Web 端会话导航面板**:在对话页**右侧**悬浮展示按轮折叠的对话大纲,点击任意节点平滑跳转,滚动对话时实时高亮当前阅读位置,步次徽标配色与内置"轨迹"视图统一。
|
|
4
4
|
|
|
5
|
-
纯浏览器插件(无宿主行为)、纯 JavaScript
|
|
5
|
+
纯浏览器插件(无宿主行为)、纯 JavaScript、零构建、零 npm 依赖(按钮/图标复用 DSH 内核 seed 的官方 primitives)。
|
|
6
6
|
|
|
7
7
|
## 功能
|
|
8
8
|
|
|
9
9
|
- **按轮折叠大纲**:默认只显示"第 N 轮 + 你的问题"列表,长对话一目了然
|
|
10
10
|
- **展开/折叠步次**:点击轮次行右侧的 `▸ N` / `▾` 展开或折叠该轮的步次明细(助手回复、工具调用、命令、压缩点等)
|
|
11
11
|
- **点击定位**:点击轮次主体或步次条目,平滑滚动跳转到对话中对应位置(不改变折叠状态)
|
|
12
|
+
- **加载更早 / 加载全部**:面板顶部两个按钮——「加载更早」向后翻一页、「加载全部」一键把所有历史轮次载入导航,之后可任意跳转(页面默认仍懒加载,只有点按钮才补载)
|
|
12
13
|
- **位置跟踪**:手动滚动对话时,面板自动高亮并跟随当前正在阅读的轮次
|
|
14
|
+
- **右侧定位**:面板锚定视口右侧,收起/展开左侧边栏时纹丝不动
|
|
13
15
|
- **回到最新 / 全部折叠**:面板底部两个快捷按钮
|
|
14
|
-
- **轨迹配色**:用户/插话 = 业务蓝、上下文 = 成功绿、助手 = 紫罗兰、工具 = 琥珀、压缩 = 中性灰(
|
|
16
|
+
- **轨迹配色**:用户/插话 = 业务蓝、上下文 = 成功绿、助手 = 紫罗兰、工具 = 琥珀、压缩 = 中性灰(取自 `dsh-client-ui-trajectory` 的 kindTag 主题 token,自动适配明暗主题)
|
|
17
|
+
- **DSH 原生按钮与图标**:所有操作按钮复用官方 `Button` 组件与 `ic_ds_*` 图标集,与 DSH 界面完全同款
|
|
15
18
|
- 切换工作区/会话自动跟随并重建大纲
|
|
16
19
|
|
|
17
20
|
## 安装
|
|
@@ -27,7 +30,7 @@ dsh plugin --profile web add github:gjj-star/dsh-conversation-navigato
|
|
|
27
30
|
|
|
28
31
|
# 方式三:本地 tarball
|
|
29
32
|
pnpm pack
|
|
30
|
-
dsh plugin --profile web add ./dsh-conversation-navigator
|
|
33
|
+
dsh plugin --profile web add ./dsh-conversation-navigator-<version>.tgz
|
|
31
34
|
```
|
|
32
35
|
|
|
33
36
|
`dsh plugin` 在 profile 目录内转发给 pnpm,因此需要 **pnpm 在 PATH 上**;安装会自动把本包追加进 profile 的 `dsh.profile.bundles`,其自带的 `cordis.patch.yml` 层负责插入插件行。重启 `dsh web` 后生效,面板默认展开。
|
|
@@ -40,15 +43,16 @@ dsh plugin --profile web add ./dsh-conversation-navigator-0.1.0.tgz
|
|
|
40
43
|
|
|
41
44
|
## 工作原理
|
|
42
45
|
|
|
43
|
-
- 注册槽位:`conversation.session.header.utilities`(
|
|
46
|
+
- 注册槽位:`conversation.session.header.utilities`(标题栏「导航」开关)+ `shell.overlay`(浮动面板)
|
|
44
47
|
- 数据来源:会话级标准 props 的 `useSession`(选择 `ConversationSnapshot.chat` 的稳定渲染顺序 `order` + `ChatNodeStore`),按 `node.location` 的 turn 分组
|
|
48
|
+
- 历史补载:通过 `sessions` 服务的 `binding(sessionId).session.loadOlder()` 分页向后加载,「加载全部」循环至 `hasMore=false`
|
|
45
49
|
- 跳转定位:复用 DSH 聊天视图自身的稳定 DOM 锚点 `[data-chat-anchor-key]`(与产品内部 paging/scroll 定位同源),`scrollIntoView` 平滑滚动
|
|
46
50
|
- 位置跟踪:捕获 `[data-conversation-scroll]` 滚动容器的 scroll 事件(节流 120ms),计算视口顶部首个可见节点
|
|
47
|
-
-
|
|
51
|
+
- 样式:按钮/图标复用 `@deepseek-ai/dsh-client-ui-primitives`;面板容器自建 `<style>` 注入,颜色使用 `--dsw-*` 主题 token;插件卸载时随 fiber 清理
|
|
48
52
|
|
|
49
53
|
## 兼容性说明
|
|
50
54
|
|
|
51
|
-
- 目标平台:DSH Web 端(`dsh.client.platform: web`),依赖内核 seed 的 `react`
|
|
55
|
+
- 目标平台:DSH Web 端(`dsh.client.platform: web`),依赖内核 seed 的 `react`、`slots`、`sessions` 服务与 `@deepseek-ai/dsh-client-ui-primitives`,以及 `dsh-client-runtime`、`dsh-client-ui-conversation` 提供的标准能力(`dsh.client.inject` 已声明 runtime 与 conversation)
|
|
52
56
|
- **版本敏感点**:`[data-chat-anchor-key]` / `[data-conversation-scroll]` 是当前 DSH 聊天视图的 DOM 锚点约定,DSH 升级后若锚点变化,只需调整 `lib/client.js` 中 `findAnchor` / `computeActiveKey` 两个函数
|
|
53
57
|
- 未声明 `timer` 硬依赖:客户端的 timer 服务存在则用于节流,不存在时自动退化为未节流模式
|
|
54
58
|
|
package/lib/client.js
CHANGED
|
@@ -5,7 +5,7 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
const React = require("react");
|
|
8
|
-
const { Button } = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
8
|
+
const { Button, IconPanelLeftOutline16, IconChevronUpOutline14, IconRefreshOutline14, IconChevronDownOutline14, IconTriangleRightFill14, IconCloseOutline16 } = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
9
|
|
|
10
10
|
/* ---------- own stylesheet (static packages have no styles builtin) ---------- */
|
|
11
11
|
const CSS = `
|
|
@@ -372,17 +372,6 @@ window.__ModuleLoader__.load({
|
|
|
372
372
|
const groupEls = {};
|
|
373
373
|
|
|
374
374
|
/* ---------- header toggle button ---------- */
|
|
375
|
-
function ToggleIcon() {
|
|
376
|
-
return React.createElement("svg", {
|
|
377
|
-
viewBox: "0 0 16 16", width: 14, height: 14, fill: "none",
|
|
378
|
-
stroke: "currentColor", strokeWidth: 1.4, "aria-hidden": true,
|
|
379
|
-
},
|
|
380
|
-
React.createElement("circle", { cx: 3, cy: 3.5, r: 1.1, fill: "currentColor", stroke: "none" }),
|
|
381
|
-
React.createElement("path", { d: "M6.2 3.5h7.8M6.2 8h7.8M6.2 12.5h7.8", strokeLinecap: "round" }),
|
|
382
|
-
React.createElement("circle", { cx: 3, cy: 8, r: 1.1, fill: "currentColor", stroke: "none" }),
|
|
383
|
-
React.createElement("circle", { cx: 3, cy: 12.5, r: 1.1, fill: "currentColor", stroke: "none" }),
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
375
|
function ToggleButton(props) {
|
|
387
376
|
const snap = useStore();
|
|
388
377
|
const outline = props.useSession(selectOutline);
|
|
@@ -400,7 +389,7 @@ window.__ModuleLoader__.load({
|
|
|
400
389
|
return React.createElement(Button, {
|
|
401
390
|
variant: snap.open ? "primary" : "ghost",
|
|
402
391
|
size: "sm",
|
|
403
|
-
icon: React.createElement(
|
|
392
|
+
icon: React.createElement(IconPanelLeftOutline16),
|
|
404
393
|
"aria-pressed": snap.open,
|
|
405
394
|
title: snap.open ? "收起会话导航" : "打开会话导航",
|
|
406
395
|
onClick: () => setState({ open: !snap.open }),
|
|
@@ -546,27 +535,30 @@ window.__ModuleLoader__.load({
|
|
|
546
535
|
variant: "ghost",
|
|
547
536
|
size: "sm",
|
|
548
537
|
className: "cnvnav-close",
|
|
538
|
+
icon: React.createElement(IconCloseOutline16, { size: 14 }),
|
|
549
539
|
title: "收起",
|
|
550
540
|
onClick: () => setState({ open: false }),
|
|
551
|
-
}
|
|
541
|
+
}),
|
|
552
542
|
),
|
|
553
543
|
React.createElement("div", { className: "cnvnav-topbar" },
|
|
554
544
|
React.createElement(Button, {
|
|
555
545
|
variant: "ghost",
|
|
556
546
|
size: "sm",
|
|
557
547
|
className: "cnvnav-bar-btn",
|
|
548
|
+
icon: React.createElement(IconChevronUpOutline14),
|
|
558
549
|
disabled: !snap.hasMore || snap.loadingOlder,
|
|
559
550
|
title: "加载更早的一批轮次",
|
|
560
551
|
onClick: () => loadOlderPage(),
|
|
561
|
-
}, "
|
|
552
|
+
}, "加载更早"),
|
|
562
553
|
React.createElement(Button, {
|
|
563
554
|
variant: "ghost",
|
|
564
555
|
size: "sm",
|
|
565
556
|
className: "cnvnav-bar-btn",
|
|
557
|
+
icon: React.createElement(IconRefreshOutline14),
|
|
566
558
|
disabled: !snap.hasMore || snap.loadingOlder || snap.loadingAll,
|
|
567
559
|
title: "加载全部历史轮次",
|
|
568
560
|
onClick: () => loadAllPages(),
|
|
569
|
-
}, "
|
|
561
|
+
}, "加载全部"),
|
|
570
562
|
),
|
|
571
563
|
React.createElement("div", {
|
|
572
564
|
ref: (el) => { listEl = el; },
|
|
@@ -581,6 +573,7 @@ window.__ModuleLoader__.load({
|
|
|
581
573
|
variant: "ghost",
|
|
582
574
|
size: "sm",
|
|
583
575
|
className: "cnvnav-bar-btn",
|
|
576
|
+
icon: React.createElement(IconChevronDownOutline14),
|
|
584
577
|
onClick: () => {
|
|
585
578
|
const gs = snap.outline !== null ? snap.outline.groups : [];
|
|
586
579
|
if (gs.length === 0) return;
|
|
@@ -588,14 +581,15 @@ window.__ModuleLoader__.load({
|
|
|
588
581
|
const lastItem = lastGroup.items[lastGroup.items.length - 1];
|
|
589
582
|
if (lastItem !== undefined) jumpTo(lastItem.key);
|
|
590
583
|
},
|
|
591
|
-
}, "
|
|
584
|
+
}, "回到最新"),
|
|
592
585
|
React.createElement(Button, {
|
|
593
586
|
variant: "ghost",
|
|
594
587
|
size: "sm",
|
|
595
588
|
className: "cnvnav-bar-btn",
|
|
589
|
+
icon: React.createElement(IconTriangleRightFill14),
|
|
596
590
|
title: "折叠所有已展开的轮次",
|
|
597
591
|
onClick: () => setState({ expanded: {} }),
|
|
598
|
-
}, "
|
|
592
|
+
}, "全部折叠"),
|
|
599
593
|
),
|
|
600
594
|
);
|
|
601
595
|
}
|