dsh-conversation-navigator 0.1.2 → 0.1.3

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.
Files changed (2) hide show
  1. package/lib/client.js +58 -4
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -127,6 +127,7 @@ window.__ModuleLoader__.load({
127
127
  flex: none; color: var(--dsw-alias-label-secondary, #888); font-size: 10px; opacity: .8;
128
128
  }
129
129
  .cnvnav-empty { padding: 18px 10px; text-align: center; color: var(--dsw-alias-label-secondary, #888); }
130
+ .cnvnav-topbar { display: flex; gap: 6px; margin: 6px 6px 0; }
130
131
  .cnvnav-footer { display: flex; gap: 6px; margin: 0 6px 6px; }
131
132
  .cnvnav-bottom {
132
133
  flex: 1; padding: 7px; border-radius: 8px;
@@ -135,12 +136,15 @@ window.__ModuleLoader__.load({
135
136
  color: var(--dsw-alias-label-primary, #222); cursor: pointer; font-size: 12px;
136
137
  }
137
138
  .cnvnav-bottom:hover { border-color: var(--dsw-alias-brand-primary, #4f46e5); color: var(--dsw-alias-brand-primary, #4f46e5); }
139
+ .cnvnav-bottom:disabled { opacity: .45; cursor: default; border-color: var(--dsw-alias-border-l1, rgba(128,128,128,.35)); color: var(--dsw-alias-label-secondary, #888); }
140
+ .cnvnav-bottom:disabled:hover { border-color: var(--dsw-alias-border-l1, rgba(128,128,128,.35)); color: var(--dsw-alias-label-secondary, #888); }
138
141
  `;
139
142
 
140
143
  function apply(ctx) {
141
144
  const slots = ctx.get("slots");
142
145
  if (slots === undefined) return;
143
146
  const timer = ctx.get("timer");
147
+ const sessions = ctx.get("sessions");
144
148
 
145
149
  /* own stylesheet with fiber-scoped cleanup */
146
150
  const styleEl = document.createElement("style");
@@ -150,7 +154,7 @@ window.__ModuleLoader__.load({
150
154
  ctx.effect(() => () => styleEl.remove());
151
155
 
152
156
  /* ---------- shared store (toggle button + overlay panel) ---------- */
153
- let state = { open: true, sessionId: null, outline: null, activeKey: null, expanded: {} };
157
+ let state = { open: true, sessionId: null, outline: null, activeKey: null, expanded: {}, hasMore: false, loadingOlder: false, loadingAll: false };
154
158
  const listeners = new Set();
155
159
  function setState(patch) {
156
160
  state = Object.assign({}, state, patch);
@@ -163,6 +167,38 @@ window.__ModuleLoader__.load({
163
167
  return snap;
164
168
  }
165
169
 
170
+ /* ---------- history paging ---------- */
171
+ let loadingAll = false;
172
+ function loadOlderPage() {
173
+ if (sessions === undefined) return;
174
+ const sid = state.sessionId;
175
+ if (sid === null) return;
176
+ const binding = sessions.binding(sid);
177
+ if (binding === undefined) return;
178
+ binding.session.loadOlder().catch((err) => console.error(err));
179
+ }
180
+ async function loadAllPages() {
181
+ if (sessions === undefined || loadingAll) return;
182
+ const sid = state.sessionId;
183
+ if (sid === null) return;
184
+ const binding = sessions.binding(sid);
185
+ if (binding === undefined) return;
186
+ loadingAll = true;
187
+ setState({ loadingAll: true });
188
+ try {
189
+ let guard = 0;
190
+ while (binding.session.getSnapshot().hasMore && guard < 500) {
191
+ await binding.session.loadOlder();
192
+ guard += 1;
193
+ }
194
+ } catch (err) {
195
+ console.error(err);
196
+ } finally {
197
+ loadingAll = false;
198
+ setState({ loadingAll: false });
199
+ }
200
+ }
201
+
166
202
  /* ---------- outline derivation from ConversationSnapshot ---------- */
167
203
  const KIND_META = {
168
204
  "user": { label: "你", cls: "traj-user" },
@@ -382,9 +418,11 @@ window.__ModuleLoader__.load({
382
418
  function ToggleButton(props) {
383
419
  const snap = useStore();
384
420
  const outline = props.useSession(selectOutline);
421
+ const hasMore = props.useSession((s) => s.hasMore);
422
+ const loadingOlder = props.useSession((s) => s.loadingOlder);
385
423
  React.useEffect(() => {
386
- setState({ sessionId: props.sessionId, outline: outline });
387
- }, [outline, props.sessionId]);
424
+ setState({ sessionId: props.sessionId, outline: outline, hasMore: hasMore, loadingOlder: loadingOlder });
425
+ }, [outline, props.sessionId, hasMore, loadingOlder]);
388
426
  React.useEffect(() => {
389
427
  if (state.sessionId !== null && state.sessionId !== props.sessionId) {
390
428
  lastActive = null;
@@ -542,6 +580,22 @@ window.__ModuleLoader__.load({
542
580
  onClick: () => setState({ open: false }),
543
581
  }, "×"),
544
582
  ),
583
+ React.createElement("div", { className: "cnvnav-topbar" },
584
+ React.createElement("button", {
585
+ type: "button",
586
+ className: "cnvnav-bottom",
587
+ disabled: !snap.hasMore || snap.loadingOlder,
588
+ title: "加载更早的一批轮次",
589
+ onClick: () => loadOlderPage(),
590
+ }, "△ 加载更早"),
591
+ React.createElement("button", {
592
+ type: "button",
593
+ className: "cnvnav-bottom",
594
+ disabled: !snap.hasMore || snap.loadingOlder || snap.loadingAll,
595
+ title: "加载全部历史轮次",
596
+ onClick: () => loadAllPages(),
597
+ }, "▴ 加载全部"),
598
+ ),
545
599
  React.createElement("div", {
546
600
  ref: (el) => { listEl = el; },
547
601
  className: "cnvnav-list",
@@ -583,7 +637,7 @@ window.__ModuleLoader__.load({
583
637
  ));
584
638
  }
585
639
 
586
- const inject = ["slots"];
640
+ const inject = ["slots", "sessions"];
587
641
  exports.apply = apply;
588
642
  exports.inject = inject;
589
643
  return module.exports;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-conversation-navigator",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Codex 风格的 DSH(DeepSeek Harness)会话导航插件:按轮折叠的对话大纲面板,支持节点点击跳转、滚动位置高亮与轨迹视图同款配色。",
5
5
  "keywords": [
6
6
  "dsh",