dsh-context 0.8.0 → 0.9.0

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 (3) hide show
  1. package/lib/client.js +226 -439
  2. package/lib/index.js +269 -210
  3. package/package.json +9 -2
package/lib/client.js CHANGED
@@ -20,6 +20,7 @@ var DICT_ZH = {
20
20
  "overview.estimate": "tokens\uFF08\u4F30\u7B97\uFF09",
21
21
  "overview.free": "\u5269\u4F59\u7A97\u53E3",
22
22
  "overview.splitEst": "\xB7 \u5206\u7C7B\u6309\u4F30\u7B97\u6BD4\u4F8B\u5206\u914D",
23
+ "overview.ofUsed": "\u5360\u5DF2\u7528\u4E0A\u4E0B\u6587",
23
24
  "stats.title": "\u4E0A\u4E0B\u6587\u7EDF\u8BA1",
24
25
  "stats.hint": "\u7EDF\u8BA1\u5F53\u524D\u4FDD\u7559\u7684\u5386\u53F2\u7A97\u53E3\uFF08\u4E0E\u8D8B\u52BF\u56FE\u4E00\u81F4\uFF09",
25
26
  "stats.turns": "\u8F6E\u6B21",
@@ -90,6 +91,7 @@ var DICT_EN = {
90
91
  "overview.estimate": "tokens (estimated)",
91
92
  "overview.free": "Free window",
92
93
  "overview.splitEst": "\xB7 category split is estimated",
94
+ "overview.ofUsed": "of used context",
93
95
  "stats.title": "Context stats",
94
96
  "stats.hint": "Over the retained history window (same as the History chart)",
95
97
  "stats.turns": "Turns",
@@ -167,11 +169,25 @@ var STYLES = [
167
169
  ".lc-overview-num b { font-size: 20px; }",
168
170
  ".lc-overview-num span { color: var(--dsw-alias-label-secondary); }",
169
171
  ".lc-stacked-wrap { position: relative; width: 100%; }",
170
- ".lc-stacked { display: flex; width: 100%; border-radius: 5px; overflow: hidden; background: rgba(128,128,128,0.18); }",
172
+ ".lc-stacked { display: flex; width: 100%; border-radius: 5px; overflow: hidden; background: rgba(128,128,128,0.18); position: relative; }",
173
+ // Hover reference frame around the OCCUPIED region of the composition bar:
174
+ // dashes from the left edge to the used/window boundary, so the legend's
175
+ // "share of used" percentages visibly map to the boxed part (the free track
176
+ // sits outside it). pointer-events: none keeps hover on the segments/free.
177
+ // Deliberately high-contrast (label-primary) and thick so the frame reads at
178
+ // a glance, and the other parts dim underneath it (`.lc-stacked-dim`).
179
+ ".lc-occupied-box { position: absolute; top: 0; bottom: 0; left: 0; border: 2px dashed var(--dsw-alias-label-primary); border-radius: 5px; box-sizing: border-box; pointer-events: none; opacity: 1; box-shadow: 0 0 0 1px var(--dsw-alias-bg-layer-2); }",
171
180
  ".lc-bar-tip { position: absolute; bottom: calc(100% + 6px); transform: translateX(-50%); z-index: 5; white-space: nowrap; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; padding: 3px 8px; font-size: 12px; color: var(--dsw-alias-label-primary); box-shadow: 0 2px 8px rgba(0,0,0,0.18); pointer-events: none; }",
172
181
  ".lc-stacked > div { height: 100%; }",
173
182
  ".lc-stacked-seg-on { filter: brightness(1.18); }",
174
183
  ".lc-stacked-free-on { box-shadow: inset 0 0 0 1px var(--dsw-alias-label-secondary); border-radius: 3px; }",
184
+ // Hover focus: everything except the hovered part (segment, legend chip, or
185
+ // free track) recedes, so the composition highlight and the occupied-region
186
+ // frame read clearly. The selected segment/free keeps full opacity.
187
+ ".lc-stacked-dim .lc-stacked-seg { opacity: 0.35; }",
188
+ ".lc-stacked-dim .lc-stacked-seg-on { opacity: 1; }",
189
+ ".lc-stacked-dim .lc-stacked-free { opacity: 0.35; }",
190
+ ".lc-stacked-dim .lc-stacked-free-on { opacity: 1; }",
175
191
  ".lc-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; }",
176
192
  ".lc-chip { display: inline-flex; align-items: center; gap: 5px; color: var(--dsw-alias-label-primary); }",
177
193
  ".lc-chip i, .lc-detail-row i, .lc-node i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; }",
@@ -250,20 +266,6 @@ var STYLES = [
250
266
  ".lc-foot { color: var(--dsw-alias-label-secondary); font-size: 12px; margin-top: 4px; }"
251
267
  ].join("\n");
252
268
 
253
- // src/client/cache.ts
254
- var MAX_CACHED_SESSIONS = 10;
255
- var sessionCache = /* @__PURE__ */ new Map();
256
- function cacheGet(sessionId) {
257
- return sessionCache.get(sessionId);
258
- }
259
- function cachePut(sessionId, snapshot) {
260
- sessionCache.set(sessionId, snapshot);
261
- if (sessionCache.size > MAX_CACHED_SESSIONS) {
262
- const oldest = sessionCache.keys().next().value;
263
- if (oldest !== void 0) sessionCache.delete(oldest);
264
- }
265
- }
266
-
267
269
  // src/client/categories.ts
268
270
  var CATS = [
269
271
  { key: "system", color: "#6366f1" },
@@ -287,6 +289,15 @@ function anchoredParts(parts, target) {
287
289
  return parts.map((p) => ({ ...p, value: Math.round(p.value * scale) }));
288
290
  }
289
291
 
292
+ // src/client/services.ts
293
+ function timelineOf(value) {
294
+ if (value === null || value === void 0 || typeof value !== "object") return null;
295
+ return value;
296
+ }
297
+
298
+ // src/client/components/events.tsx
299
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
300
+
290
301
  // src/client/format.ts
291
302
  function fmt(n) {
292
303
  if (n === void 0 || n === null || isNaN(n)) return "\u2014";
@@ -306,7 +317,7 @@ function fmtTime(t) {
306
317
  var React = require("react");
307
318
  var h = React.createElement;
308
319
 
309
- // src/client/components/events.ts
320
+ // src/client/components/events.tsx
310
321
  var EVENT_ICONS = { compaction: "\u2702", prune: "\u2702", inject: "\uFF0B", model: "\u21C4" };
311
322
  function makeEventText(t, tr) {
312
323
  function eventLabel(ev) {
@@ -342,34 +353,19 @@ function makeEventList(kit) {
342
353
  const { t, fmt: fmt3, fmtTime: fmtTime2, eventLabel, eventAt } = kit;
343
354
  return function EventList(props) {
344
355
  if (props.events.length === 0) {
345
- return h("div", { className: "lc-empty" }, t("events.empty"));
356
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
346
357
  }
347
358
  const sorted = props.events.slice().reverse();
348
- return h(
349
- "div",
350
- { className: "lc-events" },
351
- sorted.map((ev, i) => {
352
- const label = eventLabel(ev);
353
- const at = eventAt(ev);
354
- return h(
355
- "div",
356
- { key: ev.seq + "-" + i, className: "lc-event" },
357
- h("span", { className: "lc-event-icon lc-event-" + ev.kind }, EVENT_ICONS[ev.kind] || "\u2022"),
358
- h("span", { className: "lc-event-label", title: label }, label),
359
- at !== null ? h("span", { className: "lc-event-at" }, at) : null,
360
- ev.tokens ? h(
361
- "span",
362
- { className: "lc-event-tokens" + (ev.kind === "inject" ? " lc-up" : " lc-down") },
363
- (ev.kind === "inject" ? "+" : "\u2212") + fmt3(ev.tokens)
364
- ) : null,
365
- h("span", { className: "lc-event-time" }, fmtTime2(ev.time))
366
- );
367
- })
368
- );
359
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-events" }, sorted.map((ev, i) => {
360
+ const label = eventLabel(ev);
361
+ const at = eventAt(ev);
362
+ const glyph = ev.kind === "inject" ? /* @__PURE__ */ React.createElement(import_dsh_client_ui_primitives.IconPlusOutline16, null) : ev.kind === "model" ? /* @__PURE__ */ React.createElement(import_dsh_client_ui_primitives.IconBranchOutline16, null) : EVENT_ICONS[ev.kind] || "\u2022";
363
+ return /* @__PURE__ */ React.createElement("div", { key: ev.seq + "-" + i, className: "lc-event" }, /* @__PURE__ */ React.createElement("span", { className: "lc-event-icon lc-event-" + ev.kind }, glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-event-label", title: label }, label), at !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-at" }, at) : null, ev.tokens ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-tokens" + (ev.kind === "inject" ? " lc-up" : " lc-down") }, (ev.kind === "inject" ? "+" : "\u2212") + fmt3(ev.tokens)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-event-time" }, fmtTime2(ev.time)));
364
+ }));
369
365
  };
370
366
  }
371
367
 
372
- // src/client/components/nodes.ts
368
+ // src/client/components/nodes.tsx
373
369
  function makeNodeList(kit) {
374
370
  const { t, tr, fmt: fmt3, fmtTime: fmtTime2 } = kit;
375
371
  function nodeText(n) {
@@ -385,32 +381,19 @@ function makeNodeList(kit) {
385
381
  }
386
382
  return function NodeList(props) {
387
383
  if (props.nodes.length === 0) {
388
- return h("div", { className: "lc-empty" }, t("nodes.empty"));
384
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("nodes.empty"));
389
385
  }
390
386
  const catColor = {};
391
387
  for (const c of CATS) catColor[c.key] = c.color;
392
388
  const rows = props.nodes.slice().reverse();
393
- return h(
394
- "div",
395
- { className: "lc-nodes" },
396
- props.dropped > 0 ? h("div", { className: "lc-nodes-more" }, tr("nodes.more", { n: props.dropped })) : null,
397
- rows.map((n) => {
398
- const text = nodeText(n);
399
- return h(
400
- "div",
401
- { key: n.seq, className: "lc-node" },
402
- h("i", { style: { background: catColor[n.cat] || "#999" } }),
403
- h("span", { className: "lc-node-preview", title: text }, text),
404
- // Timestamp when the host event carried one.
405
- typeof n.time === "number" ? h("span", { className: "lc-node-time" }, fmtTime2(n.time)) : null,
406
- h("span", { className: "lc-node-tokens" }, fmt3(n.tokens))
407
- );
408
- })
409
- );
389
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-nodes" }, props.dropped > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-nodes-more" }, tr("nodes.more", { n: props.dropped })) : null, rows.map((n) => {
390
+ const text = nodeText(n);
391
+ return /* @__PURE__ */ React.createElement("div", { key: n.seq, className: "lc-node" }, /* @__PURE__ */ React.createElement("i", { style: { background: catColor[n.cat] || "#999" } }), /* @__PURE__ */ React.createElement("span", { className: "lc-node-preview", title: text }, text), typeof n.time === "number" ? /* @__PURE__ */ React.createElement("span", { className: "lc-node-time" }, fmtTime2(n.time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-node-tokens" }, fmt3(n.tokens)));
392
+ }));
410
393
  };
411
394
  }
412
395
 
413
- // src/client/components/requestDetail.ts
396
+ // src/client/components/requestDetail.tsx
414
397
  function makeRequestDetail(kit, StackedBar) {
415
398
  const { t, tr, fmt: fmt3, fmtTime: fmtTime2, catLabel, eventLabel, eventAt } = kit;
416
399
  return function RequestDetail(props) {
@@ -420,46 +403,14 @@ function makeRequestDetail(kit, StackedBar) {
420
403
  const head = isTurn ? tr("detail.turn", { t: req.turn ?? 0, n: req.stepCount ?? 0 }) : tr("detail.step", { t: req.turn ?? 0, s: req.step ?? 0 });
421
404
  const marker = props.marker ?? null;
422
405
  const markerAt = marker !== null ? eventAt(marker) : null;
423
- return h(
424
- "div",
425
- { className: "lc-detail" },
426
- h(
427
- "div",
428
- { className: "lc-detail-head" },
429
- h("b", null, head),
430
- marker !== null && markerAt !== null ? h("span", { className: "lc-detail-marker", title: eventLabel(marker) }, "\u2702 " + markerAt) : null,
431
- isTurn ? h("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null,
432
- h("span", null, fmtTime2(req.time)),
433
- h("span", null, tr("detail.estTotal", { n: fmt3(req.total) })),
434
- req.prompt !== void 0 ? h("span", { className: "lc-actual" }, tr("detail.actual", { n: fmt3(req.prompt) })) : null,
435
- req.output !== void 0 ? h("span", null, tr("detail.output", { n: fmt3(req.output) })) : null
436
- ),
437
- h(StackedBar, { parts: partsOf(req), height: 10 }),
438
- h(
439
- "div",
440
- { className: "lc-detail-rows" },
441
- CATS.map((c) => {
442
- const v = req[c.key] || 0;
443
- return h(
444
- "div",
445
- { key: c.key, className: "lc-detail-row" },
446
- h("i", { style: { background: c.color } }),
447
- h("span", { className: "lc-detail-label" }, catLabel(c.key)),
448
- h(
449
- "span",
450
- { className: "lc-bar-track" },
451
- h("span", { className: "lc-bar-fill", style: { width: (req.total > 0 ? v / req.total * 100 : 0) + "%", background: c.color } })
452
- ),
453
- h("span", { className: "lc-detail-num" }, "\u2248" + fmt3(v)),
454
- h("span", { className: "lc-detail-pct" }, req.total > 0 ? Math.round(v / req.total * 100) + "%" : "")
455
- );
456
- })
457
- )
458
- );
406
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-detail" }, /* @__PURE__ */ React.createElement("div", { className: "lc-detail-head" }, /* @__PURE__ */ React.createElement("b", null, head), marker !== null && markerAt !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-marker", title: eventLabel(marker) }, "\u2702 " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, /* @__PURE__ */ React.createElement("span", null, fmtTime2(req.time)), /* @__PURE__ */ React.createElement("span", null, tr("detail.estTotal", { n: fmt3(req.total) })), req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, tr("detail.actual", { n: fmt3(req.prompt) })) : null, req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", null, tr("detail.output", { n: fmt3(req.output) })) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: partsOf(req), height: 10 }), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c) => {
407
+ const v = req[c.key] || 0;
408
+ return /* @__PURE__ */ React.createElement("div", { key: c.key, className: "lc-detail-row" }, /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-label" }, catLabel(c.key)), /* @__PURE__ */ React.createElement("span", { className: "lc-bar-track" }, /* @__PURE__ */ React.createElement("span", { className: "lc-bar-fill", style: { width: (req.total > 0 ? v / req.total * 100 : 0) + "%", background: c.color } })), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" }, "\u2248" + fmt3(v)), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-pct" }, req.total > 0 ? Math.round(v / req.total * 100) + "%" : ""));
409
+ })));
459
410
  };
460
411
  }
461
412
 
462
- // src/client/components/statsBoard.ts
413
+ // src/client/components/statsBoard.tsx
463
414
  function makeStatsBoard(kit) {
464
415
  const { t, tr, fmt: fmt3 } = kit;
465
416
  return function StatsBoard(props) {
@@ -483,50 +434,26 @@ function makeStatsBoard(kit) {
483
434
  } else if (ev.kind === "inject") injects++;
484
435
  else if (ev.kind === "model") switches++;
485
436
  }
486
- const cell = (label, value, sub) => h(
487
- "div",
488
- { className: "lc-stat" },
489
- h("span", { className: "lc-stat-label" }, label),
490
- h("b", { className: "lc-stat-value" }, value),
491
- sub !== void 0 ? h("span", { className: "lc-stat-sub" }, sub) : null
492
- );
493
- return h(
494
- "div",
495
- { className: "lc-card" },
496
- h(
497
- "div",
498
- { className: "lc-card-title" },
499
- t("stats.title"),
500
- h("span", { className: "lc-card-sub" }, t("stats.hint"))
501
- ),
502
- h(
503
- "div",
504
- { className: "lc-stats" },
505
- cell(t("stats.turns"), fmt3(turns.size)),
506
- cell(t("stats.steps"), fmt3(steps)),
507
- cell(
508
- t("stats.recycled"),
509
- recycled > 0 ? "\u2212" + fmt3(recycled) : "0",
510
- tr("stats.recycleSub", { c: compactions, p: prunes })
511
- ),
512
- cell(t("stats.injects"), fmt3(injects)),
513
- cell(t("stats.switches"), fmt3(switches)),
514
- cell(t("stats.est"), "\u2248 " + fmt3(est)),
515
- cell(t("stats.actualPrompt"), fmt3(prompt)),
516
- cell(t("stats.output"), fmt3(output))
517
- )
518
- );
437
+ const cell = (label, value, sub) => /* @__PURE__ */ React.createElement("div", { className: "lc-stat" }, /* @__PURE__ */ React.createElement("span", { className: "lc-stat-label" }, label), /* @__PURE__ */ React.createElement("b", { className: "lc-stat-value" }, value), sub !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-stat-sub" }, sub) : null);
438
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("stats.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("stats.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-stats" }, cell(t("stats.turns"), fmt3(turns.size)), cell(t("stats.steps"), fmt3(steps)), cell(
439
+ t("stats.recycled"),
440
+ recycled > 0 ? "\u2212" + fmt3(recycled) : "0",
441
+ tr("stats.recycleSub", { c: compactions, p: prunes })
442
+ ), cell(t("stats.injects"), fmt3(injects)), cell(t("stats.switches"), fmt3(switches)), cell(t("stats.est"), "\u2248 " + fmt3(est)), cell(t("stats.actualPrompt"), fmt3(prompt)), cell(t("stats.output"), fmt3(output))));
519
443
  };
520
444
  }
521
445
 
522
- // src/client/components/stackedBar.ts
446
+ // src/client/components/stackedBar.tsx
523
447
  function makeStackedBar(kit) {
524
- const { t, fmt: fmt3, catLabel } = kit;
448
+ const { t, tr, fmt: fmt3, catLabel } = kit;
525
449
  return function StackedBar(props) {
526
450
  let total = 0;
527
451
  for (const p of props.parts) total += p.value;
528
452
  const scale = props.max !== void 0 && props.max > total ? props.max : total;
529
453
  const free = props.max !== void 0 && props.max > total ? props.max - total : 0;
454
+ const usedPct = scale > 0 ? total / scale * 100 : 0;
455
+ const hovering = props.hoverKey !== null && props.hoverKey !== void 0;
456
+ const showBox = free > 0 && hovering;
530
457
  let tip = null;
531
458
  if (props.hoverKey !== null && props.hoverKey !== void 0) {
532
459
  if (props.hoverKey === "free" && free > 0) {
@@ -541,7 +468,9 @@ function makeStackedBar(kit) {
541
468
  const pct = scale > 0 ? p.value / scale * 100 : 0;
542
469
  if (p.key === props.hoverKey && p.value > 0) {
543
470
  tip = {
544
- text: catLabel(p.key) + " \u2248" + fmt3(p.value) + " (" + Math.round(p.value / total * 100) + "%)",
471
+ // "(pct%)" is a share of the OCCUPIED total the dashed box
472
+ // that appears on hover frames exactly this reference region.
473
+ text: catLabel(p.key) + " \u2248" + fmt3(p.value) + " (" + Math.round(p.value / total * 100) + "%) " + tr("overview.ofUsed"),
545
474
  leftPct: Math.max(12, Math.min(acc + pct / 2, 88))
546
475
  };
547
476
  break;
@@ -550,75 +479,74 @@ function makeStackedBar(kit) {
550
479
  }
551
480
  }
552
481
  }
553
- return h(
482
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-stacked-wrap" }, /* @__PURE__ */ React.createElement(
554
483
  "div",
555
- { className: "lc-stacked-wrap" },
556
- h(
557
- "div",
558
- {
559
- className: "lc-stacked",
560
- style: { height: (props.height || 14) + "px" },
561
- onMouseLeave: () => {
562
- if (props.onHoverKey !== void 0) props.onHoverKey(null);
563
- }
564
- },
565
- total > 0 ? props.parts.map((p) => {
566
- if (!p.value) return null;
567
- const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
568
- return h("div", {
484
+ {
485
+ className: "lc-stacked" + (hovering ? " lc-stacked-dim" : ""),
486
+ style: { height: (props.height || 14) + "px" },
487
+ onMouseLeave: () => {
488
+ if (props.onHoverKey !== void 0) props.onHoverKey(null);
489
+ }
490
+ },
491
+ total > 0 ? props.parts.map((p) => {
492
+ if (!p.value) return null;
493
+ const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
494
+ return /* @__PURE__ */ React.createElement(
495
+ "div",
496
+ {
569
497
  key: p.key,
570
498
  className: "lc-stacked-seg" + (on ? " lc-stacked-seg-on" : ""),
571
499
  style: { width: p.value / scale * 100 + "%", background: p.color },
572
500
  onMouseEnter: () => {
573
501
  if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
574
502
  }
575
- });
576
- }) : null,
577
- free > 0 ? h("div", {
503
+ }
504
+ );
505
+ }) : null,
506
+ free > 0 ? /* @__PURE__ */ React.createElement(
507
+ "div",
508
+ {
578
509
  key: "free",
579
510
  className: "lc-stacked-free" + (props.hoverKey === "free" ? " lc-stacked-free-on" : ""),
580
511
  style: { width: free / scale * 100 + "%" },
581
512
  onMouseEnter: () => {
582
513
  if (props.onHoverKey !== void 0) props.onHoverKey("free");
583
514
  }
584
- }) : null
585
- ),
586
- tip ? h("div", { className: "lc-bar-tip", style: { left: tip.leftPct + "%" } }, tip.text) : null
587
- );
515
+ }
516
+ ) : null,
517
+ showBox ? /* @__PURE__ */ React.createElement("div", { className: "lc-occupied-box", style: { width: usedPct + "%" } }) : null
518
+ ), tip ? /* @__PURE__ */ React.createElement("div", { className: "lc-bar-tip", style: { left: tip.leftPct + "%" } }, tip.text) : null);
588
519
  };
589
520
  }
590
521
  function makeLegend(kit) {
591
- const { fmt: fmt3, catLabel } = kit;
522
+ const { tr, fmt: fmt3, catLabel } = kit;
592
523
  return function Legend(props) {
593
524
  let total = 0;
594
525
  for (const p of props.parts) total += p.value;
595
- return h(
596
- "div",
597
- { className: "lc-legend" },
598
- props.parts.map((p) => {
599
- const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
600
- return h(
601
- "span",
602
- {
603
- key: p.key,
604
- className: "lc-chip" + (on ? " lc-chip-on" : ""),
605
- onMouseEnter: () => {
606
- if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
607
- },
608
- onMouseLeave: () => {
609
- if (props.onHoverKey !== void 0) props.onHoverKey(null);
610
- }
526
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-legend" }, props.parts.map((p) => {
527
+ const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
528
+ return /* @__PURE__ */ React.createElement(
529
+ "span",
530
+ {
531
+ key: p.key,
532
+ className: "lc-chip" + (on ? " lc-chip-on" : ""),
533
+ title: tr("overview.ofUsed"),
534
+ onMouseEnter: () => {
535
+ if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
611
536
  },
612
- h("i", { style: { background: p.color } }),
613
- catLabel(p.key) + " \u2248" + fmt3(p.value),
614
- total > 0 ? h("em", null, Math.round(p.value / total * 100) + "%") : null
615
- );
616
- })
617
- );
537
+ onMouseLeave: () => {
538
+ if (props.onHoverKey !== void 0) props.onHoverKey(null);
539
+ }
540
+ },
541
+ /* @__PURE__ */ React.createElement("i", { style: { background: p.color } }),
542
+ catLabel(p.key) + " \u2248" + fmt3(p.value),
543
+ total > 0 ? /* @__PURE__ */ React.createElement("em", null, Math.round(p.value / total * 100) + "%") : null
544
+ );
545
+ }));
618
546
  };
619
547
  }
620
548
 
621
- // src/client/components/trendChart.ts
549
+ // src/client/components/trendChart.tsx
622
550
  function aggregateByTurn(requests) {
623
551
  const out = [];
624
552
  let runSteps = 0;
@@ -707,121 +635,96 @@ function makeTrendChart(kit) {
707
635
  };
708
636
  const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
709
637
  const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
710
- return h(
638
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-chartrow" }, /* @__PURE__ */ React.createElement("div", { className: "lc-axis" }, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, fmt3(maxTotal)), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-mid" }, fmt3(Math.round(maxTotal / 2))), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, "0")), /* @__PURE__ */ React.createElement(
711
639
  "div",
712
- { className: "lc-chartrow" },
713
- h(
714
- "div",
715
- { className: "lc-axis" },
716
- h("span", { className: "lc-axis-top" }, fmt3(maxTotal)),
717
- h("span", { className: "lc-axis-mid" }, fmt3(Math.round(maxTotal / 2))),
718
- h("span", { className: "lc-axis-bot" }, "0")
719
- ),
720
- h(
640
+ {
641
+ className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
642
+ ref: scrollRef,
643
+ onScroll: (e) => {
644
+ updateEdges(e.currentTarget);
645
+ }
646
+ },
647
+ edges.left ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-l" }) : null,
648
+ /* @__PURE__ */ React.createElement(
721
649
  "div",
722
650
  {
723
- // Turn-aware dim scope: while a turn is focused (bar or strip hover),
724
- // bars and strip blocks OUTSIDE the active turn fade to 35%.
725
- className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
726
- ref: scrollRef,
727
- onScroll: (e) => {
728
- updateEdges(e.currentTarget);
651
+ className: "lc-chart",
652
+ onMouseLeave: () => {
653
+ props.onHover(null);
729
654
  }
730
655
  },
731
- // Edge fades: visible whenever more history sits beyond the viewport,
732
- // so the horizontal scroll affordance is obvious.
733
- edges.left ? h("div", { className: "lc-chart-fade lc-chart-fade-l" }) : null,
734
- // Hovering a bar previews it in the detail below; leaving the plot
735
- // clears the preview (a pinned selection, if any, takes over again).
736
- h(
737
- "div",
738
- {
739
- className: "lc-chart",
740
- onMouseLeave: () => {
741
- props.onHover(null);
742
- }
743
- },
744
- h("div", { className: "lc-grid lc-grid-top" }),
745
- h("div", { className: "lc-grid lc-grid-mid" }),
746
- requests.map((req, i) => {
747
- const marker = markers[i];
748
- const markerAt = marker !== void 0 ? eventAt(marker) : null;
749
- const selected = props.selectedSeq === req.seq;
750
- const hovered = props.hoveredSeq === req.seq;
751
- const inTurn = props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn;
752
- return h(
753
- "div",
754
- {
755
- key: req.seq,
756
- // Uniform column width in BOTH granularities: turn aggregates
757
- // keep the same fixed width as step bars.
758
- className: "lc-bar" + (selected ? " lc-bar-selected" : "") + (hovered ? " lc-bar-hovered" : "") + (inTurn ? " lc-bar-in-turn" : ""),
759
- style: { width: BAR_W + "px" },
760
- onClick: () => {
761
- props.onSelect(selected ? null : req.seq);
762
- },
763
- onMouseEnter: () => {
764
- props.onHover(req.seq);
765
- }
656
+ /* @__PURE__ */ React.createElement("div", { className: "lc-grid lc-grid-top" }),
657
+ /* @__PURE__ */ React.createElement("div", { className: "lc-grid lc-grid-mid" }),
658
+ requests.map((req, i) => {
659
+ const marker = markers[i];
660
+ const markerAt = marker !== void 0 ? eventAt(marker) : null;
661
+ const selected = props.selectedSeq === req.seq;
662
+ const hovered = props.hoveredSeq === req.seq;
663
+ const inTurn = props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn;
664
+ return /* @__PURE__ */ React.createElement(
665
+ "div",
666
+ {
667
+ key: req.seq,
668
+ className: "lc-bar" + (selected ? " lc-bar-selected" : "") + (hovered ? " lc-bar-hovered" : "") + (inTurn ? " lc-bar-in-turn" : ""),
669
+ style: { width: BAR_W + "px" },
670
+ onClick: () => {
671
+ props.onSelect(selected ? null : req.seq);
766
672
  },
767
- // The tooltip names the event AND where it happened: the
768
- // gap between the request before and the request after.
769
- marker !== void 0 ? h("span", {
673
+ onMouseEnter: () => {
674
+ props.onHover(req.seq);
675
+ }
676
+ },
677
+ marker !== void 0 ? /* @__PURE__ */ React.createElement(
678
+ "span",
679
+ {
770
680
  className: "lc-bar-marker",
771
681
  title: "\u2702 " + (markerAt !== null ? markerAt + " \u2014 " : "") + eventLabel(marker)
772
- }, "\u2702") : null,
773
- h(
774
- "div",
775
- { className: "lc-bar-stack" },
776
- CATS.map((c) => {
777
- const v = (req[c.key] || 0) * anchorOf(req);
778
- if (!v) return null;
779
- return h("div", { key: c.key, style: { height: Math.max(1, Math.round(v / maxTotal * CHART_H)) + "px", background: c.color } });
780
- })
781
- )
782
- );
783
- })
784
- ),
785
- // Instant hover tooltip, glued to its bar's column (it lives in the
786
- // scrolling content, so it follows the bar while the chart scrolls).
787
- hoveredReq !== null ? h("div", {
788
- className: "lc-chart-tip",
789
- style: { left: hoveredIdx * (BAR_W + BAR_GAP) + BAR_W / 2 + "px" }
790
- }, tipOf(hoveredReq)) : null,
791
- // Turn strip: one COLOR BLOCK per turn, spanning exactly its bars'
792
- // columns, so the partition reads at a glance and lines up with the
793
- // steps above. Hovering a block highlights that turn's bars in the
794
- // chart (and hovering a bar highlights its block — the active turn
795
- // is shared hover-only state).
796
- h(
797
- "div",
798
- { className: "lc-turns", onMouseLeave: () => {
799
- props.onHoverTurn(null);
800
- } },
801
- groups.map((grp, gi) => {
802
- const on = props.activeTurn === grp.turn;
803
- const blockW = grp.agg ? BAR_W : grp.span * (BAR_W + BAR_GAP) - BAR_GAP;
804
- return h("span", {
805
- key: "turn-" + gi,
806
- className: "lc-turn" + (on ? " lc-turn-on" : ""),
807
- style: {
808
- width: blockW + "px",
809
- background: TURN_COLORS[gi % TURN_COLORS.length]
810
682
  },
811
- title: "T" + grp.turn,
812
- onMouseEnter: () => {
813
- props.onHoverTurn(grp.turn);
814
- }
815
- }, "T" + grp.turn);
816
- })
817
- )
683
+ "\u2702"
684
+ ) : null,
685
+ /* @__PURE__ */ React.createElement("div", { className: "lc-bar-stack" }, CATS.map((c) => {
686
+ const v = (req[c.key] || 0) * anchorOf(req);
687
+ if (!v) return null;
688
+ return /* @__PURE__ */ React.createElement("div", { key: c.key, style: { height: Math.max(1, Math.round(v / maxTotal * CHART_H)) + "px", background: c.color } });
689
+ }))
690
+ );
691
+ })
818
692
  ),
819
- edges.right ? h("div", { className: "lc-chart-fade lc-chart-fade-r" }) : null
820
- );
693
+ hoveredReq !== null ? /* @__PURE__ */ React.createElement(
694
+ "div",
695
+ {
696
+ className: "lc-chart-tip",
697
+ style: { left: hoveredIdx * (BAR_W + BAR_GAP) + BAR_W / 2 + "px" }
698
+ },
699
+ tipOf(hoveredReq)
700
+ ) : null,
701
+ /* @__PURE__ */ React.createElement("div", { className: "lc-turns", onMouseLeave: () => {
702
+ props.onHoverTurn(null);
703
+ } }, groups.map((grp, gi) => {
704
+ const on = props.activeTurn === grp.turn;
705
+ const blockW = grp.agg ? BAR_W : grp.span * (BAR_W + BAR_GAP) - BAR_GAP;
706
+ return /* @__PURE__ */ React.createElement(
707
+ "span",
708
+ {
709
+ key: "turn-" + gi,
710
+ className: "lc-turn" + (on ? " lc-turn-on" : ""),
711
+ style: {
712
+ width: blockW + "px",
713
+ background: TURN_COLORS[gi % TURN_COLORS.length]
714
+ },
715
+ title: "T" + grp.turn,
716
+ onMouseEnter: () => {
717
+ props.onHoverTurn(grp.turn);
718
+ }
719
+ },
720
+ "T" + grp.turn
721
+ );
722
+ }))
723
+ ), edges.right ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-r" }) : null);
821
724
  };
822
725
  }
823
726
 
824
- // src/client/components/contextView.ts
727
+ // src/client/components/contextView.tsx
825
728
  var viewScroll = /* @__PURE__ */ new Map();
826
729
  function makeContextView(ctx, kit) {
827
730
  const { t, tr, fmt: fmt3, fmtTime: fmtTime2, catLabel } = kit;
@@ -834,19 +737,13 @@ function makeContextView(ctx, kit) {
834
737
  const StatsBoard = makeStatsBoard(kit);
835
738
  return function ContextView(props) {
836
739
  const sessionId = props.sessionId;
837
- const initial = typeof sessionId === "string" && sessionId !== "" ? cacheGet(sessionId) ?? null : null;
838
- const [data, setData] = React.useState(initial);
839
- const [error, setError] = React.useState(null);
740
+ const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
840
741
  const [selectedSeq, setSelectedSeq] = React.useState(null);
841
742
  const [hoveredSeq, setHoveredSeq] = React.useState(null);
842
743
  const [hoverTurn, setHoverTurn] = React.useState(null);
843
744
  const [tick, setTick] = React.useState(0);
844
745
  const [granularity, setGranularity] = React.useState("step");
845
746
  const [hoverCat, setHoverCat] = React.useState(null);
846
- const dataRef = React.useRef(initial);
847
- React.useEffect(() => {
848
- dataRef.current = data;
849
- }, [data]);
850
747
  const rootRef = React.useRef(null);
851
748
  const scrollerRef = React.useRef(null);
852
749
  const restoredRef = React.useRef(null);
@@ -867,51 +764,14 @@ function makeContextView(ctx, kit) {
867
764
  viewScroll.set(sessionId, scroller.scrollTop);
868
765
  };
869
766
  }, [sessionId]);
870
- React.useEffect(() => {
871
- if (typeof sessionId !== "string" || sessionId === "") return void 0;
872
- let alive = true;
873
- const load = () => {
874
- ctx.connection.rpc.call("/dsh-context", "snapshot", { sessionId }).then((res) => {
875
- if (!alive) return;
876
- if (res && res.ok) {
877
- const snap = res.value;
878
- cachePut(sessionId, snap);
879
- setData(snap);
880
- setError(null);
881
- } else if (dataRef.current === null) {
882
- setError(res && res.error ? String(res.error.message || res.error.code) : "failed");
883
- }
884
- }, (err) => {
885
- if (alive && dataRef.current === null) {
886
- setError(String(err instanceof Error ? err.message : err));
887
- }
888
- });
889
- };
890
- load();
891
- const timerId = setInterval(() => {
892
- if (document.visibilityState !== "hidden") load();
893
- }, 2e3);
894
- const onVisible = () => {
895
- if (document.visibilityState === "visible") load();
896
- };
897
- document.addEventListener("visibilitychange", onVisible);
898
- return () => {
899
- alive = false;
900
- clearInterval(timerId);
901
- document.removeEventListener("visibilitychange", onVisible);
902
- };
903
- }, [sessionId]);
904
767
  React.useEffect(() => {
905
768
  const localeSvc = ctx.get("locale");
906
769
  if (!localeSvc) return void 0;
907
770
  return localeSvc.subscribe(() => setTick((x) => x + 1));
908
771
  }, []);
909
772
  void tick;
910
- if (error) {
911
- return h("div", { className: "lc-root", ref: rootRef }, h("div", { className: "lc-empty" }, t("error") + error));
912
- }
913
773
  if (!data) {
914
- return h("div", { className: "lc-root", ref: rootRef }, h("div", { className: "lc-empty" }, t("loading")));
774
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
915
775
  }
916
776
  const current = data.current;
917
777
  const requests = data.requests || [];
@@ -947,116 +807,41 @@ function makeContextView(ctx, kit) {
947
807
  const headline = occupancyTokens ?? current.total;
948
808
  const headlinePct = occupancyWindow ? Math.min(100, Math.round(headline / occupancyWindow * 100)) : null;
949
809
  const parts = anchoredParts(partsOf(current), occupancyTokens !== null && headline > 0 ? headline : null);
950
- return h(
951
- "div",
952
- { className: "lc-root", ref: rootRef },
953
- // ---- session context stats (over the retained window) ----
954
- h(StatsBoard, { requests, events }),
955
- // ---- overview ----
956
- h(
957
- "div",
958
- { className: "lc-card" },
959
- h(
960
- "div",
961
- { className: "lc-card-title" },
962
- t("overview.title"),
963
- h(
964
- "span",
965
- { className: "lc-card-sub" },
966
- (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : "")
967
- )
968
- ),
969
- h(
970
- "div",
971
- { className: "lc-overview-num" },
972
- h("b", null, fmt3(headline)),
973
- h("span", null, occupancyWindow ? " / " + fmt3(occupancyWindow) + " " + tr("overview.ofWindow", { p: headlinePct ?? 0 }) : " " + t("overview.estimate")),
974
- occupancyTokens !== null ? h("span", { className: "lc-card-sub" }, t("overview.splitEst")) : null
975
- ),
976
- h(StackedBar, { parts, height: 16, max: occupancyWindow, hoverKey: hoverCat, onHoverKey: setHoverCat }),
977
- h(Legend, { parts, hoverKey: hoverCat, onHoverKey: setHoverCat }),
978
- data.toolList && data.toolList.length > 0 ? h(
979
- "div",
980
- { className: "lc-tools" },
981
- t("tools.top"),
982
- data.toolList.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => {
983
- return h("span", { key: tool.name, className: "lc-tool-chip" }, tool.name + " " + fmt3(tool.tokens));
984
- }),
985
- data.toolList.length > 5 ? h("span", { className: "lc-card-sub" }, " " + tr("tools.more", { n: data.toolList.length })) : null
986
- ) : null
987
- ),
988
- // ---- trend ----
989
- h(
990
- "div",
991
- { className: "lc-card" },
992
- h(
993
- "div",
994
- { className: "lc-card-title" },
995
- t("trend.title"),
996
- h("span", { className: "lc-card-sub" }, t("trend.hint")),
997
- h(
998
- "div",
999
- { className: "lc-gran" },
1000
- h("button", {
1001
- className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
1002
- onClick: () => {
1003
- setGranularity("step");
1004
- }
1005
- }, t("gran.step")),
1006
- h("button", {
1007
- className: "lc-gran-btn" + (granularity === "turn" ? " lc-gran-on" : ""),
1008
- onClick: () => {
1009
- setGranularity("turn");
1010
- }
1011
- }, t("gran.turn"))
1012
- )
1013
- ),
1014
- displayRequests.length === 0 ? h("div", { className: "lc-empty" }, t("trend.empty")) : h(
1015
- "div",
1016
- null,
1017
- h(TrendChart, {
1018
- // Remount per session: switching sessions re-anchors the chart
1019
- // at the newest bars instead of inheriting stale scroll state.
1020
- key: sessionId,
1021
- // The host caps the log at 160 requests; render them ALL so
1022
- // earlier turns/steps stay reachable via horizontal scroll.
1023
- requests: displayRequests,
1024
- markers,
1025
- selectedSeq: pinnedReq ? pinnedReq.seq : null,
1026
- hoveredSeq,
1027
- activeTurn,
1028
- granularity,
1029
- onSelect: setSelectedSeq,
1030
- onHover: setHoveredSeq,
1031
- onHoverTurn: setHoverTurn
1032
- }),
1033
- h(RequestDetail, { request: activeReq, marker: activeReq !== null ? markerOf(activeReq) : void 0 })
1034
- )
1035
- ),
1036
- // ---- events + messages ----
1037
- h(
1038
- "div",
1039
- { className: "lc-cols" },
1040
- h(
1041
- "div",
1042
- { className: "lc-card lc-col" },
1043
- h("div", { className: "lc-card-title" }, t("events.title")),
1044
- h(EventList, { events })
1045
- ),
1046
- h(
1047
- "div",
1048
- { className: "lc-card lc-col" },
1049
- h(
1050
- "div",
1051
- { className: "lc-card-title" },
1052
- t("nodes.title"),
1053
- h("span", { className: "lc-card-sub" }, t("nodes.hint"))
1054
- ),
1055
- h(NodeList, { nodes, dropped: data.droppedNodes || 0 })
1056
- )
1057
- ),
1058
- h("div", { className: "lc-foot" }, t("footer"))
1059
- );
810
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement(StatsBoard, { requests, events }), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("overview.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : ""))), /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt3(headline)), /* @__PURE__ */ React.createElement("span", null, occupancyWindow ? " / " + fmt3(occupancyWindow) + " " + tr("overview.ofWindow", { p: headlinePct ?? 0 }) : " " + t("overview.estimate")), occupancyTokens !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("overview.splitEst")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts, height: 16, max: occupancyWindow, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts, hoverKey: hoverCat, onHoverKey: setHoverCat }), data.toolList && data.toolList.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-tools" }, t("tools.top"), data.toolList.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => {
811
+ return /* @__PURE__ */ React.createElement("span", { key: tool.name, className: "lc-tool-chip" }, tool.name + " " + fmt3(tool.tokens));
812
+ }), data.toolList.length > 5 ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, " " + tr("tools.more", { n: data.toolList.length })) : null) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("trend.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement(
813
+ "button",
814
+ {
815
+ className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
816
+ onClick: () => {
817
+ setGranularity("step");
818
+ }
819
+ },
820
+ t("gran.step")
821
+ ), /* @__PURE__ */ React.createElement(
822
+ "button",
823
+ {
824
+ className: "lc-gran-btn" + (granularity === "turn" ? " lc-gran-on" : ""),
825
+ onClick: () => {
826
+ setGranularity("turn");
827
+ }
828
+ },
829
+ t("gran.turn")
830
+ ))), displayRequests.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("trend.empty")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
831
+ TrendChart,
832
+ {
833
+ key: sessionId,
834
+ requests: displayRequests,
835
+ markers,
836
+ selectedSeq: pinnedReq ? pinnedReq.seq : null,
837
+ hoveredSeq,
838
+ activeTurn,
839
+ granularity,
840
+ onSelect: setSelectedSeq,
841
+ onHover: setHoveredSeq,
842
+ onHoverTurn: setHoverTurn
843
+ }
844
+ ), /* @__PURE__ */ React.createElement(RequestDetail, { request: activeReq, marker: activeReq !== null ? markerOf(activeReq) : void 0 }))), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("events.title")), /* @__PURE__ */ React.createElement(EventList, { events })), /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("nodes.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("nodes.hint"))), /* @__PURE__ */ React.createElement(NodeList, { nodes, dropped: data.droppedNodes || 0 }))), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
1060
845
  };
1061
846
  }
1062
847
 
@@ -1075,11 +860,12 @@ function makeViewKit(t) {
1075
860
  }
1076
861
 
1077
862
  // src/client/index.ts
863
+ var NS = "dsh-context";
1078
864
  function apply(ctx) {
1079
865
  ctx.effect(() => {
1080
- return ctx.locale.register("dsh-context", { zh: DICT_ZH, en: DICT_EN });
866
+ return ctx.locale.register(NS, { zh: DICT_ZH, en: DICT_EN });
1081
867
  }, "dsh-context: dictionaries");
1082
- const t = ctx.locale.bind("dsh-context");
868
+ const t = ctx.locale.bind(NS);
1083
869
  ctx.effect(() => {
1084
870
  const tag = document.createElement("style");
1085
871
  tag.setAttribute("data-plugin", "dsh-context");
@@ -1092,15 +878,16 @@ function apply(ctx) {
1092
878
  const ContextView = makeContextView(ctx, makeViewKit(t));
1093
879
  ctx.slots.inject("conversation.view", () => {
1094
880
  return ctx.slots.register(
1095
- // order 20 renders right of Chat (0) and Trajectory (10).
1096
- { name: "conversation.view", id: "context", order: 20, label: () => t("tab") },
881
+ // order 20 renders right of Chat (0) and Trajectory (10); the locale
882
+ // namespace put the framework `t` seat on the component's props too.
883
+ { name: "conversation.view", id: "context", order: 20, locale: NS, label: () => t("tab") },
1097
884
  (props) => h(ContextView, props)
1098
885
  );
1099
886
  });
1100
887
  }
1101
888
  module.exports = {
1102
889
  name: "dsh-context",
1103
- inject: ["connection", "slots", "locale"],
890
+ inject: ["slots", "locale"],
1104
891
  apply
1105
892
  };
1106
893