dsh-conversation-navigator 0.1.2 → 0.1.4
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/lib/client.js +77 -49
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -5,26 +5,10 @@ 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
9
|
|
|
9
10
|
/* ---------- own stylesheet (static packages have no styles builtin) ---------- */
|
|
10
11
|
const CSS = `
|
|
11
|
-
.cnvnav-toggle {
|
|
12
|
-
display: inline-flex; align-items: center; gap: 6px;
|
|
13
|
-
height: 28px; padding: 0 10px; border-radius: 8px;
|
|
14
|
-
border: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35));
|
|
15
|
-
background: transparent;
|
|
16
|
-
color: var(--dsw-alias-label-secondary, #666);
|
|
17
|
-
font-size: 12px; line-height: 1; cursor: pointer;
|
|
18
|
-
}
|
|
19
|
-
.cnvnav-toggle:hover {
|
|
20
|
-
background: var(--dsw-alias-bg-layer-2, rgba(128,128,128,.12));
|
|
21
|
-
color: var(--dsw-alias-label-primary, #222);
|
|
22
|
-
}
|
|
23
|
-
.cnvnav-toggle-on {
|
|
24
|
-
color: var(--dsw-alias-brand-primary, #4f46e5);
|
|
25
|
-
border-color: var(--dsw-alias-brand-primary, #4f46e5);
|
|
26
|
-
}
|
|
27
|
-
.cnvnav-toggle svg { display: block; }
|
|
28
12
|
.cnvnav-panel {
|
|
29
13
|
position: fixed; z-index: 9999; width: 288px;
|
|
30
14
|
display: flex; flex-direction: column;
|
|
@@ -45,15 +29,7 @@ window.__ModuleLoader__.load({
|
|
|
45
29
|
}
|
|
46
30
|
.cnvnav-title { font-weight: 600; font-size: 13px; }
|
|
47
31
|
.cnvnav-count { color: var(--dsw-alias-label-secondary, #888); font-size: 11px; }
|
|
48
|
-
.cnvnav-close {
|
|
49
|
-
margin-left: auto; border: none; background: transparent;
|
|
50
|
-
color: var(--dsw-alias-label-secondary, #888); cursor: pointer;
|
|
51
|
-
font-size: 16px; line-height: 1; padding: 2px 7px; border-radius: 6px;
|
|
52
|
-
}
|
|
53
|
-
.cnvnav-close:hover {
|
|
54
|
-
background: var(--dsw-alias-bg-layer-2, rgba(128,128,128,.12));
|
|
55
|
-
color: var(--dsw-alias-label-primary, #222);
|
|
56
|
-
}
|
|
32
|
+
.cnvnav-close { margin-left: auto; }
|
|
57
33
|
.cnvnav-list {
|
|
58
34
|
overflow-y: auto; padding: 6px; min-height: 0;
|
|
59
35
|
display: flex; flex-direction: column; gap: 2px;
|
|
@@ -127,20 +103,16 @@ window.__ModuleLoader__.load({
|
|
|
127
103
|
flex: none; color: var(--dsw-alias-label-secondary, #888); font-size: 10px; opacity: .8;
|
|
128
104
|
}
|
|
129
105
|
.cnvnav-empty { padding: 18px 10px; text-align: center; color: var(--dsw-alias-label-secondary, #888); }
|
|
106
|
+
.cnvnav-topbar { display: flex; gap: 6px; margin: 6px 6px 0; }
|
|
130
107
|
.cnvnav-footer { display: flex; gap: 6px; margin: 0 6px 6px; }
|
|
131
|
-
.cnvnav-
|
|
132
|
-
flex: 1; padding: 7px; border-radius: 8px;
|
|
133
|
-
border: 1px solid var(--dsw-alias-border-l1, rgba(128,128,128,.35));
|
|
134
|
-
background: var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06));
|
|
135
|
-
color: var(--dsw-alias-label-primary, #222); cursor: pointer; font-size: 12px;
|
|
136
|
-
}
|
|
137
|
-
.cnvnav-bottom:hover { border-color: var(--dsw-alias-brand-primary, #4f46e5); color: var(--dsw-alias-brand-primary, #4f46e5); }
|
|
108
|
+
.cnvnav-bar-btn { flex: 1; }
|
|
138
109
|
`;
|
|
139
110
|
|
|
140
111
|
function apply(ctx) {
|
|
141
112
|
const slots = ctx.get("slots");
|
|
142
113
|
if (slots === undefined) return;
|
|
143
114
|
const timer = ctx.get("timer");
|
|
115
|
+
const sessions = ctx.get("sessions");
|
|
144
116
|
|
|
145
117
|
/* own stylesheet with fiber-scoped cleanup */
|
|
146
118
|
const styleEl = document.createElement("style");
|
|
@@ -150,7 +122,7 @@ window.__ModuleLoader__.load({
|
|
|
150
122
|
ctx.effect(() => () => styleEl.remove());
|
|
151
123
|
|
|
152
124
|
/* ---------- shared store (toggle button + overlay panel) ---------- */
|
|
153
|
-
let state = { open: true, sessionId: null, outline: null, activeKey: null, expanded: {} };
|
|
125
|
+
let state = { open: true, sessionId: null, outline: null, activeKey: null, expanded: {}, hasMore: false, loadingOlder: false, loadingAll: false };
|
|
154
126
|
const listeners = new Set();
|
|
155
127
|
function setState(patch) {
|
|
156
128
|
state = Object.assign({}, state, patch);
|
|
@@ -163,6 +135,38 @@ window.__ModuleLoader__.load({
|
|
|
163
135
|
return snap;
|
|
164
136
|
}
|
|
165
137
|
|
|
138
|
+
/* ---------- history paging ---------- */
|
|
139
|
+
let loadingAll = false;
|
|
140
|
+
function loadOlderPage() {
|
|
141
|
+
if (sessions === undefined) return;
|
|
142
|
+
const sid = state.sessionId;
|
|
143
|
+
if (sid === null) return;
|
|
144
|
+
const binding = sessions.binding(sid);
|
|
145
|
+
if (binding === undefined) return;
|
|
146
|
+
binding.session.loadOlder().catch((err) => console.error(err));
|
|
147
|
+
}
|
|
148
|
+
async function loadAllPages() {
|
|
149
|
+
if (sessions === undefined || loadingAll) return;
|
|
150
|
+
const sid = state.sessionId;
|
|
151
|
+
if (sid === null) return;
|
|
152
|
+
const binding = sessions.binding(sid);
|
|
153
|
+
if (binding === undefined) return;
|
|
154
|
+
loadingAll = true;
|
|
155
|
+
setState({ loadingAll: true });
|
|
156
|
+
try {
|
|
157
|
+
let guard = 0;
|
|
158
|
+
while (binding.session.getSnapshot().hasMore && guard < 500) {
|
|
159
|
+
await binding.session.loadOlder();
|
|
160
|
+
guard += 1;
|
|
161
|
+
}
|
|
162
|
+
} catch (err) {
|
|
163
|
+
console.error(err);
|
|
164
|
+
} finally {
|
|
165
|
+
loadingAll = false;
|
|
166
|
+
setState({ loadingAll: false });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
166
170
|
/* ---------- outline derivation from ConversationSnapshot ---------- */
|
|
167
171
|
const KIND_META = {
|
|
168
172
|
"user": { label: "你", cls: "traj-user" },
|
|
@@ -382,22 +386,25 @@ window.__ModuleLoader__.load({
|
|
|
382
386
|
function ToggleButton(props) {
|
|
383
387
|
const snap = useStore();
|
|
384
388
|
const outline = props.useSession(selectOutline);
|
|
389
|
+
const hasMore = props.useSession((s) => s.hasMore);
|
|
390
|
+
const loadingOlder = props.useSession((s) => s.loadingOlder);
|
|
385
391
|
React.useEffect(() => {
|
|
386
|
-
setState({ sessionId: props.sessionId, outline: outline });
|
|
387
|
-
}, [outline, props.sessionId]);
|
|
392
|
+
setState({ sessionId: props.sessionId, outline: outline, hasMore: hasMore, loadingOlder: loadingOlder });
|
|
393
|
+
}, [outline, props.sessionId, hasMore, loadingOlder]);
|
|
388
394
|
React.useEffect(() => {
|
|
389
395
|
if (state.sessionId !== null && state.sessionId !== props.sessionId) {
|
|
390
396
|
lastActive = null;
|
|
391
397
|
setState({ activeKey: null, expanded: {} });
|
|
392
398
|
}
|
|
393
399
|
}, [props.sessionId]);
|
|
394
|
-
return React.createElement(
|
|
395
|
-
|
|
396
|
-
|
|
400
|
+
return React.createElement(Button, {
|
|
401
|
+
variant: snap.open ? "primary" : "ghost",
|
|
402
|
+
size: "sm",
|
|
403
|
+
icon: React.createElement(ToggleIcon),
|
|
397
404
|
"aria-pressed": snap.open,
|
|
398
405
|
title: snap.open ? "收起会话导航" : "打开会话导航",
|
|
399
406
|
onClick: () => setState({ open: !snap.open }),
|
|
400
|
-
},
|
|
407
|
+
}, "导航");
|
|
401
408
|
}
|
|
402
409
|
|
|
403
410
|
/* ---------- floating navigator panel ---------- */
|
|
@@ -535,13 +542,32 @@ window.__ModuleLoader__.load({
|
|
|
535
542
|
React.createElement("div", { className: "cnvnav-head" },
|
|
536
543
|
React.createElement("span", { className: "cnvnav-title" }, "会话导航"),
|
|
537
544
|
React.createElement("span", { className: "cnvnav-count" }, String(count) + " 条"),
|
|
538
|
-
React.createElement(
|
|
539
|
-
|
|
545
|
+
React.createElement(Button, {
|
|
546
|
+
variant: "ghost",
|
|
547
|
+
size: "sm",
|
|
540
548
|
className: "cnvnav-close",
|
|
541
549
|
title: "收起",
|
|
542
550
|
onClick: () => setState({ open: false }),
|
|
543
551
|
}, "×"),
|
|
544
552
|
),
|
|
553
|
+
React.createElement("div", { className: "cnvnav-topbar" },
|
|
554
|
+
React.createElement(Button, {
|
|
555
|
+
variant: "ghost",
|
|
556
|
+
size: "sm",
|
|
557
|
+
className: "cnvnav-bar-btn",
|
|
558
|
+
disabled: !snap.hasMore || snap.loadingOlder,
|
|
559
|
+
title: "加载更早的一批轮次",
|
|
560
|
+
onClick: () => loadOlderPage(),
|
|
561
|
+
}, "△ 加载更早"),
|
|
562
|
+
React.createElement(Button, {
|
|
563
|
+
variant: "ghost",
|
|
564
|
+
size: "sm",
|
|
565
|
+
className: "cnvnav-bar-btn",
|
|
566
|
+
disabled: !snap.hasMore || snap.loadingOlder || snap.loadingAll,
|
|
567
|
+
title: "加载全部历史轮次",
|
|
568
|
+
onClick: () => loadAllPages(),
|
|
569
|
+
}, "▴ 加载全部"),
|
|
570
|
+
),
|
|
545
571
|
React.createElement("div", {
|
|
546
572
|
ref: (el) => { listEl = el; },
|
|
547
573
|
className: "cnvnav-list",
|
|
@@ -551,9 +577,10 @@ window.__ModuleLoader__.load({
|
|
|
551
577
|
: groupNodes,
|
|
552
578
|
),
|
|
553
579
|
React.createElement("div", { className: "cnvnav-footer" },
|
|
554
|
-
React.createElement(
|
|
555
|
-
|
|
556
|
-
|
|
580
|
+
React.createElement(Button, {
|
|
581
|
+
variant: "ghost",
|
|
582
|
+
size: "sm",
|
|
583
|
+
className: "cnvnav-bar-btn",
|
|
557
584
|
onClick: () => {
|
|
558
585
|
const gs = snap.outline !== null ? snap.outline.groups : [];
|
|
559
586
|
if (gs.length === 0) return;
|
|
@@ -562,9 +589,10 @@ window.__ModuleLoader__.load({
|
|
|
562
589
|
if (lastItem !== undefined) jumpTo(lastItem.key);
|
|
563
590
|
},
|
|
564
591
|
}, "↓ 回到最新"),
|
|
565
|
-
React.createElement(
|
|
566
|
-
|
|
567
|
-
|
|
592
|
+
React.createElement(Button, {
|
|
593
|
+
variant: "ghost",
|
|
594
|
+
size: "sm",
|
|
595
|
+
className: "cnvnav-bar-btn",
|
|
568
596
|
title: "折叠所有已展开的轮次",
|
|
569
597
|
onClick: () => setState({ expanded: {} }),
|
|
570
598
|
}, "▼ 全部折叠"),
|
|
@@ -583,7 +611,7 @@ window.__ModuleLoader__.load({
|
|
|
583
611
|
));
|
|
584
612
|
}
|
|
585
613
|
|
|
586
|
-
const inject = ["slots"];
|
|
614
|
+
const inject = ["slots", "sessions"];
|
|
587
615
|
exports.apply = apply;
|
|
588
616
|
exports.inject = inject;
|
|
589
617
|
return module.exports;
|