dsh-context 0.13.0 → 0.14.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.
package/lib/client.js CHANGED
@@ -16,10 +16,9 @@ var DICT_ZH = {
16
16
  "cat.assistant": "\u52A9\u624B\u56DE\u590D",
17
17
  "cat.tool": "\u5DE5\u5177\u7ED3\u679C",
18
18
  "overview.title": "\u5F53\u524D\u6784\u6210",
19
- "overview.ofWindow": "tokens\uFF08\u7EA6 {p}%\uFF09",
20
19
  "overview.estimate": "tokens\uFF08\u4F30\u7B97\uFF09",
21
20
  "overview.free": "\u5269\u4F59\u7A97\u53E3",
22
- "overview.splitEst": "\xB7 \u5206\u7C7B\u6309\u4F30\u7B97\u6BD4\u4F8B\u5206\u914D",
21
+ "overview.used": "\u4E0A\u4E0B\u6587\u5DF2\u7528",
23
22
  "overview.ofUsed": "\u5360\u5DF2\u7528\u4E0A\u4E0B\u6587",
24
23
  "stats.title": "\u4E0A\u4E0B\u6587\u7EDF\u8BA1",
25
24
  "stats.hint": "\u7EDF\u8BA1\u5F53\u524D\u4FDD\u7559\u7684\u5386\u53F2\u7A97\u53E3\uFF08\u4E0E\u8D8B\u52BF\u56FE\u4E00\u81F4\uFF09",
@@ -103,11 +102,10 @@ var DICT_EN = {
103
102
  "cat.inject": "Injected",
104
103
  "cat.assistant": "Assistant",
105
104
  "cat.tool": "Tool results",
106
- "overview.title": "Current composition",
107
- "overview.ofWindow": "tokens (~{p}%)",
105
+ "overview.title": "Current Context",
108
106
  "overview.estimate": "tokens (estimated)",
109
107
  "overview.free": "Free window",
110
- "overview.splitEst": "\xB7 category split is estimated",
108
+ "overview.used": "of context used",
111
109
  "overview.ofUsed": "of used context",
112
110
  "stats.title": "Context stats",
113
111
  "stats.hint": "Over the retained history window (same as the History chart)",
@@ -122,10 +120,10 @@ var DICT_EN = {
122
120
  "plugin.github": "GitHub",
123
121
  "tools.top": "Top tool schemas:",
124
122
  "tools.more": "of {n}",
125
- "trend.title": "History",
123
+ "trend.title": "Context History",
126
124
  "gran.step": "Step",
127
125
  "gran.turn": "Turn",
128
- "trend.hint": "hover a bar for details (click to pin), \u2702 marks compaction/prune; scroll sideways to see earlier; Step/Turn switches granularity",
126
+ "trend.hint": "Hover to view details, \u2702 marks compaction/prune, Step/Turn switches granularity",
129
127
  "trend.empty": "Send a message and each model request\u2019s context makeup shows up here",
130
128
  "detail.step": "Turn {t} \xB7 Step {s}",
131
129
  "detail.turn": "Turn {t} \xB7 {n} steps",
@@ -171,7 +169,7 @@ var DICT_EN = {
171
169
  "node.snapshot": "snapshot: ",
172
170
  "cmd.desc": "View context makeup and how it evolves",
173
171
  "cmd.close": "Close",
174
- "browser.title": "Context browser",
172
+ "browser.title": "Context Browser",
175
173
  "browser.live": "Live (next request)",
176
174
  "browser.liveNow": "Live \xB7 next request",
177
175
  "browser.items": "{n} items",
@@ -286,23 +284,19 @@ function headlineOf(data, pressure = null) {
286
284
  const tokens = occupancyTokens ?? current.total;
287
285
  const pct = window2 !== void 0 && window2 > 0 ? Math.min(100, Math.round(tokens / window2 * 100)) : null;
288
286
  const parts = anchoredParts(partsOf(current), occupancyTokens !== null && tokens > 0 ? tokens : null);
289
- return { tokens, window: window2, pct, parts, estimated: occupancyTokens === null };
287
+ return { tokens, window: window2, pct, parts };
290
288
  }
291
289
 
292
290
  // 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
- function contextPressureOf(value) {
291
+ function asRecord(value) {
298
292
  if (value === null || value === void 0 || typeof value !== "object") return null;
299
293
  return value;
300
294
  }
295
+ var timelineOf = (value) => asRecord(value);
296
+ var contextPressureOf = (value) => asRecord(value);
301
297
  function headersOf(value) {
302
- if (value === null || value === void 0 || typeof value !== "object") return null;
303
- const headers = value.headers;
304
- if (!Array.isArray(headers)) return null;
305
- return value;
298
+ const headers = asRecord(value);
299
+ return headers !== null && Array.isArray(headers.headers) ? headers : null;
306
300
  }
307
301
 
308
302
  // src/client/react.ts
@@ -311,15 +305,15 @@ var h = React.createElement;
311
305
 
312
306
  // src/client/components/requestDetail.tsx
313
307
  function makeRequestDetail(kit, StackedBar) {
314
- const { t, tr, fmt: fmt3, fmtTime: fmtTime2, catLabel, eventLabel, eventAt } = kit;
308
+ const { t, fmt: fmt3, fmtTime: fmtTime2, catLabel, eventLabel, eventAt } = kit;
315
309
  return function RequestDetail(props) {
316
310
  const req = props.request;
317
311
  if (!req) return null;
318
312
  const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
319
- 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 });
313
+ const head = isTurn ? t("detail.turn", { t: req.turn ?? 0, n: req.stepCount ?? 0 }) : t("detail.step", { t: req.turn ?? 0, s: req.step ?? 0 });
320
314
  const marker = props.marker ?? null;
321
315
  const markerAt = marker !== null ? eventAt(marker) : null;
322
- 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) => {
316
+ 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, t("detail.estTotal", { n: fmt3(req.total) })), req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt3(req.prompt) })) : null, req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", null, t("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) => {
323
317
  const v = req[c.key] || 0;
324
318
  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) + "%" : ""));
325
319
  })));
@@ -328,7 +322,7 @@ function makeRequestDetail(kit, StackedBar) {
328
322
 
329
323
  // src/client/components/stackedBar.tsx
330
324
  function makeStackedBar(kit) {
331
- const { t, tr, fmt: fmt3, catLabel } = kit;
325
+ const { t, fmt: fmt3, catLabel } = kit;
332
326
  return function StackedBar(props) {
333
327
  let total = 0;
334
328
  for (const p of props.parts) total += p.value;
@@ -353,7 +347,7 @@ function makeStackedBar(kit) {
353
347
  tip = {
354
348
  // "(pct%)" is a share of the OCCUPIED total — the dashed box
355
349
  // that appears on hover frames exactly this reference region.
356
- text: catLabel(p.key) + " \u2248" + fmt3(p.value) + " (" + Math.round(p.value / total * 100) + "%) " + tr("overview.ofUsed"),
350
+ text: catLabel(p.key) + " \u2248" + fmt3(p.value) + " (" + Math.round(p.value / total * 100) + "%) " + t("overview.ofUsed"),
357
351
  leftPct: Math.max(12, Math.min(acc + pct / 2, 88))
358
352
  };
359
353
  break;
@@ -397,12 +391,25 @@ function makeStackedBar(kit) {
397
391
  }
398
392
  }
399
393
  ) : null,
400
- showBox ? /* @__PURE__ */ React.createElement("div", { className: "lc-occupied-box", style: { width: usedPct + "%" } }) : null
401
- ), tip ? /* @__PURE__ */ React.createElement("div", { className: "lc-bar-tip", style: { left: tip.leftPct + "%" } }, tip.text) : null);
394
+ /* @__PURE__ */ React.createElement(
395
+ "div",
396
+ {
397
+ className: "lc-occupied-box" + (showBox ? " lc-occupied-box-on" : ""),
398
+ style: { width: usedPct + "%" }
399
+ }
400
+ )
401
+ ), props.tip !== false ? /* @__PURE__ */ React.createElement(
402
+ "div",
403
+ {
404
+ className: "lc-bar-tip" + (tip ? " lc-bar-tip-on" : ""),
405
+ style: { left: tip ? tip.leftPct + "%" : "50%" }
406
+ },
407
+ tip ? tip.text : ""
408
+ ) : null);
402
409
  };
403
410
  }
404
411
  function makeLegend(kit) {
405
- const { tr, fmt: fmt3, catLabel } = kit;
412
+ const { t, fmt: fmt3, catLabel } = kit;
406
413
  return function Legend(props) {
407
414
  let total = 0;
408
415
  for (const p of props.parts) total += p.value;
@@ -413,7 +420,7 @@ function makeLegend(kit) {
413
420
  {
414
421
  key: p.key,
415
422
  className: "lc-chip" + (on ? " lc-chip-on" : ""),
416
- title: tr("overview.ofUsed"),
423
+ title: t("overview.ofUsed"),
417
424
  onMouseEnter: () => {
418
425
  if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
419
426
  },
@@ -459,7 +466,7 @@ function attachMarkers(requests, events) {
459
466
  return markers;
460
467
  }
461
468
  function makeTrendChart(kit) {
462
- const { t, tr, fmt: fmt3, fmtTime: fmtTime2, catLabel, eventLabel, eventAt } = kit;
469
+ const { t, fmt: fmt3, fmtTime: fmtTime2, catLabel, eventLabel, eventAt } = kit;
463
470
  const CHART_H = 112;
464
471
  const BAR_W = 14;
465
472
  const BAR_GAP = 2;
@@ -513,8 +520,8 @@ function makeTrendChart(kit) {
513
520
  updateEdges(el);
514
521
  });
515
522
  const tipOf = (req) => {
516
- const head = req.stepCount !== void 0 && req.stepCount > 1 ? tr("tip.turn", { t: req.turn ?? 0, n: req.stepCount }) : tr("tip.step", { t: req.turn ?? 0, s: req.step ?? 0 });
517
- return head + " \xB7 " + fmtTime2(req.time) + " \xB7 " + tr("tip.total", { n: fmt3(req.total) }) + (req.prompt !== void 0 ? " \xB7 " + tr("tip.actual", { n: fmt3(req.prompt) }) : "");
523
+ const head = req.stepCount !== void 0 && req.stepCount > 1 ? t("tip.turn", { t: req.turn ?? 0, n: req.stepCount }) : t("tip.step", { t: req.turn ?? 0, s: req.step ?? 0 });
524
+ return head + " \xB7 " + fmtTime2(req.time) + " \xB7 " + t("tip.total", { n: fmt3(req.total) }) + (req.prompt !== void 0 ? " \xB7 " + t("tip.actual", { n: fmt3(req.prompt) }) : "");
518
525
  };
519
526
  const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
520
527
  const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
@@ -610,7 +617,7 @@ function makeTrendChart(kit) {
610
617
  // src/client/components/contextModal.tsx
611
618
  var TREND_TURNS = 10;
612
619
  function makeContextModal(ctx, kit) {
613
- const { t, tr, fmt: fmt3 } = kit;
620
+ const { t, fmt: fmt3 } = kit;
614
621
  const sessions = ctx.get("sessions");
615
622
  const StackedBar = makeStackedBar(kit);
616
623
  const Legend = makeLegend(kit);
@@ -667,7 +674,7 @@ function makeContextModal(ctx, kit) {
667
674
  const head = data !== null ? headlineOf(data, pressure) : null;
668
675
  return /* @__PURE__ */ React.createElement("div", { className: "lc-modal-backdrop", onClick: close }, /* @__PURE__ */ React.createElement("div", { className: "lc-modal-card", onClick: (ev) => {
669
676
  ev.stopPropagation();
670
- } }, /* @__PURE__ */ React.createElement("div", { className: "lc-modal-head" }, /* @__PURE__ */ React.createElement("span", { className: "lc-modal-title" }, t("tab")), data !== null && (data.model || data.provider) ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : "")) : null, /* @__PURE__ */ React.createElement("button", { className: "lc-modal-close", "aria-label": t("cmd.close"), onClick: close }, "\xD7")), data === null || head === null ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt3(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt3(head.window) + " " + tr("overview.ofWindow", { p: head.pct ?? 0 }) : " " + t("overview.estimate")), !head.estimated ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("overview.splitEst")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: head.parts, height: 16, max: head.window, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts: head.parts, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement("div", { className: "lc-card-title lc-modal-trend" }, t("trend.title")), turns.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("trend.empty")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
677
+ } }, /* @__PURE__ */ React.createElement("div", { className: "lc-modal-head" }, /* @__PURE__ */ React.createElement("span", { className: "lc-modal-title" }, t("tab")), data !== null && (data.model || data.provider) ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : "")) : null, /* @__PURE__ */ React.createElement("button", { className: "lc-modal-close", "aria-label": t("cmd.close"), onClick: close }, "\xD7")), data === null || head === null ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt3(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt3(head.window) + " tokens" : " " + t("overview.estimate")), head.pct !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-overview-pct" }, /* @__PURE__ */ React.createElement("b", null, head.pct + "%"), t("overview.used")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: head.parts, height: 16, max: head.window, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts: head.parts, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement("div", { className: "lc-card-title lc-modal-trend" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("trend.title"))), turns.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("trend.empty")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
671
678
  TrendChart,
672
679
  {
673
680
  requests: turns,
@@ -690,6 +697,7 @@ var STYLES = [
690
697
  ".lc-root { padding: 16px 20px 32px; overflow-y: auto; height: 100%; box-sizing: border-box; color: var(--dsw-alias-label-primary); font-size: 13px; }",
691
698
  ".lc-card { background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l1); border-radius: 10px; padding: 14px 16px; margin-bottom: 14px; }",
692
699
  ".lc-card-title { font-weight: 600; margin-bottom: 10px; display: flex; align-items: baseline; gap: 8px; }",
700
+ ".lc-card-title-text { flex: none; white-space: nowrap; }",
693
701
  ".lc-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: 8px; }",
694
702
  ".lc-stat { background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }",
695
703
  ".lc-stat-label { color: var(--dsw-alias-label-secondary); font-size: 11px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }",
@@ -700,9 +708,13 @@ var STYLES = [
700
708
  ".lc-gran-btn { border: 0; background: transparent; color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 1; padding: 3px 8px; border-radius: 5px; cursor: pointer; font-family: inherit; }",
701
709
  ".lc-gran-btn:hover { color: var(--dsw-alias-label-primary); }",
702
710
  ".lc-gran-on, .lc-gran-on:hover { background: var(--dsw-alias-button-primary-fill); color: var(--dsw-alias-label-primary-foreground); }",
703
- ".lc-overview-num { margin-bottom: 8px; }",
704
- ".lc-overview-num b { font-size: 20px; }",
711
+ ".lc-overview-num { display: flex; align-items: baseline; gap: 6px; margin-bottom: 8px; }",
712
+ ".lc-overview-num > b { font-size: 20px; }",
705
713
  ".lc-overview-num span { color: var(--dsw-alias-label-secondary); }",
714
+ // The used-percentage figure is the emphasis of the line: pushed to the
715
+ // right edge, large and primary-colored, with a small "used" caption.
716
+ ".lc-overview-pct { margin-left: auto; font-size: 11px; }",
717
+ ".lc-overview-pct b { font-size: 20px; color: var(--dsw-alias-label-primary); margin-right: 4px; }",
706
718
  ".lc-stacked-wrap { position: relative; width: 100%; }",
707
719
  ".lc-stacked { display: flex; width: 100%; border-radius: 5px; overflow: hidden; background: rgba(128,128,128,0.18); position: relative; }",
708
720
  // Hover reference frame around the OCCUPIED region of the composition bar:
@@ -711,9 +723,16 @@ var STYLES = [
711
723
  // sits outside it). pointer-events: none keeps hover on the segments/free.
712
724
  // Deliberately high-contrast (label-primary) and thick so the frame reads at
713
725
  // a glance, and the other parts dim underneath it (`.lc-stacked-dim`).
714
- ".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); }",
715
- ".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; }",
726
+ ".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: 0; box-shadow: 0 0 0 1px var(--dsw-alias-bg-layer-2); transition: opacity 120ms ease; }",
727
+ ".lc-occupied-box-on { opacity: 1; }",
728
+ ".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; opacity: 0; transition: opacity 120ms ease; }",
729
+ ".lc-bar-tip-on { opacity: 1; }",
716
730
  ".lc-stacked > div { height: 100%; }",
731
+ // Hover eases: the shared composition hover (dim + segment brighten, on the
732
+ // overview or the mirrored browser bar) glides in and out — same 120ms
733
+ // rhythm as the rest of the view.
734
+ ".lc-stacked-seg { transition: filter 120ms ease, opacity 120ms ease; }",
735
+ ".lc-stacked-free { transition: box-shadow 120ms ease, opacity 120ms ease; }",
717
736
  ".lc-stacked-seg-on { filter: brightness(1.18); }",
718
737
  ".lc-stacked-free-on { box-shadow: inset 0 0 0 1px var(--dsw-alias-label-secondary); border-radius: 3px; }",
719
738
  // Hover focus: everything except the hovered part (segment, legend chip, or
@@ -724,10 +743,14 @@ var STYLES = [
724
743
  ".lc-stacked-dim .lc-stacked-free { opacity: 0.35; }",
725
744
  ".lc-stacked-dim .lc-stacked-free-on { opacity: 1; }",
726
745
  ".lc-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; }",
727
- ".lc-chip { display: inline-flex; align-items: center; gap: 5px; color: var(--dsw-alias-label-primary); }",
746
+ ".lc-chip { display: inline-flex; align-items: center; gap: 5px; color: var(--dsw-alias-label-primary); padding: 1px 6px; border-radius: 6px; transition: background-color 120ms ease; }",
728
747
  ".lc-chip i, .lc-detail-row i, .lc-node i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; }",
748
+ ".lc-chip i { transition: box-shadow 120ms ease; }",
729
749
  ".lc-chip em { font-style: normal; color: var(--dsw-alias-label-secondary); }",
730
- ".lc-chip-on { font-weight: 600; }",
750
+ // The selected chip glows with the shared hover tint (smoothly, like the
751
+ // browser rows) instead of snapping its weight; the dot ring on top marks
752
+ // the exact segment.
753
+ ".lc-chip-on { font-weight: 600; background: var(--dsw-alias-interactive-bg-hover); }",
731
754
  ".lc-chip-on i { box-shadow: 0 0 0 1px var(--dsw-alias-brand-primary); }",
732
755
  ".lc-tools { margin-top: 10px; color: var(--dsw-alias-label-secondary); display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }",
733
756
  ".lc-tool-chip { background: var(--dsw-alias-bg-layer-2); border-radius: 4px; padding: 1px 7px; font-size: 12px; color: var(--dsw-alias-label-primary); }",
@@ -745,7 +768,7 @@ var STYLES = [
745
768
  ".lc-grid { position: absolute; left: 0; right: 0; border-top: 1px dashed var(--dsw-alias-border-l1); pointer-events: none; }",
746
769
  ".lc-grid-top { top: 18px; }",
747
770
  ".lc-grid-mid { top: 74px; }",
748
- ".lc-bar { position: relative; width: 14px; flex: none; height: 100%; display: flex; align-items: flex-end; cursor: pointer; border-radius: 2px; transition: opacity 120ms ease; }",
771
+ ".lc-bar { position: relative; width: 14px; flex: none; height: 100%; display: flex; align-items: flex-end; cursor: pointer; border-radius: 2px; transition: opacity 120ms ease, background-color 120ms ease; }",
749
772
  // Turn-aware dimming: while a turn is focused, bars OUTSIDE the active
750
773
  // turn fade to 35% and the whole current turn stays fully opaque.
751
774
  ".lc-chart-dim .lc-bar { opacity: 0.35; }",
@@ -856,9 +879,14 @@ var STYLES = [
856
879
  ".lc-br-cats { margin-top: 10px; display: flex; flex-direction: column; gap: 4px; }",
857
880
  ".lc-br-cat { border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; background: var(--dsw-alias-bg-layer-2); overflow: hidden; }",
858
881
  ".lc-br-cat-empty { opacity: 0.55; }",
859
- ".lc-br-cat-row { display: flex; align-items: center; gap: 8px; width: 100%; border: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; padding: 7px 10px; cursor: pointer; text-align: left; }",
860
- ".lc-br-cat-row:hover { background: var(--dsw-alias-bg-layer-1); }",
861
- ".lc-br-cat-row i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; flex: none; }",
882
+ ".lc-br-cat-row { display: flex; align-items: center; gap: 8px; width: 100%; border: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; padding: 7px 10px; cursor: pointer; text-align: left; transition: background-color 120ms ease; }",
883
+ ".lc-br-cat-row:hover { background: var(--dsw-alias-interactive-bg-hover); }",
884
+ // Current-composition hover echo: the category row lit by the shared hover
885
+ // link (hovering the Current Composition card's bar/legend) looks hovered
886
+ // too — same tint as the physical hover, plus a ring on its color dot.
887
+ ".lc-br-cat-on { background: var(--dsw-alias-interactive-bg-hover); }",
888
+ ".lc-br-cat-on i { box-shadow: 0 0 0 1px var(--dsw-alias-brand-primary); }",
889
+ ".lc-br-cat-row i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; flex: none; transition: box-shadow 120ms ease; }",
862
890
  ".lc-br-cat-label { font-weight: 600; }",
863
891
  ".lc-br-cat-count { color: var(--dsw-alias-label-secondary); font-size: 12px; flex: 1; }",
864
892
  ".lc-br-chev { flex: none; width: 12px; color: var(--dsw-alias-label-secondary); transition: transform 120ms ease; }",
@@ -867,9 +895,9 @@ var STYLES = [
867
895
  ".lc-br-pct { flex: none; width: 36px; text-align: right; color: var(--dsw-alias-label-secondary); font-size: 12px; }",
868
896
  ".lc-br-body { border-top: 1px solid var(--dsw-alias-border-l1); padding: 4px 6px; display: flex; flex-direction: column; gap: 2px; }",
869
897
  ".lc-br-elem { border-radius: 6px; }",
870
- ".lc-br-elem-on { background: var(--dsw-alias-bg-layer-1); }",
871
- ".lc-br-elem-row { display: flex; align-items: center; gap: 8px; width: 100%; border: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; padding: 5px 6px; cursor: pointer; text-align: left; border-radius: 6px; }",
872
- ".lc-br-elem-row:hover { background: var(--dsw-alias-bg-layer-1); }",
898
+ ".lc-br-elem-on { background: var(--dsw-alias-interactive-bg-active); }",
899
+ ".lc-br-elem-row { display: flex; align-items: center; gap: 8px; width: 100%; border: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; padding: 5px 6px; cursor: pointer; text-align: left; border-radius: 6px; transition: background-color 120ms ease; }",
900
+ ".lc-br-elem-row:hover { background: var(--dsw-alias-interactive-bg-hover); }",
873
901
  ".lc-br-tag { flex: none; font-size: 11px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l1); border-radius: 4px; padding: 0 6px; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
874
902
  ".lc-br-preview { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
875
903
  ".lc-br-time { flex: none; color: var(--dsw-alias-label-secondary); font-size: 11px; }",
@@ -930,7 +958,7 @@ function makeNodeText(kit) {
930
958
  };
931
959
  }
932
960
  function makeNodeList(kit) {
933
- const { t, tr, fmt: fmt3, fmtTime: fmtTime2 } = kit;
961
+ const { t, fmt: fmt3, fmtTime: fmtTime2 } = kit;
934
962
  const nodeText = makeNodeText(kit);
935
963
  return function NodeList(props) {
936
964
  if (props.nodes.length === 0) {
@@ -939,7 +967,7 @@ function makeNodeList(kit) {
939
967
  const catColor = {};
940
968
  for (const c of CATS) catColor[c.key] = c.color;
941
969
  const rows = props.nodes.slice().reverse();
942
- 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) => {
970
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-nodes" }, props.dropped > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-nodes-more" }, t("nodes.more", { n: props.dropped })) : null, rows.map((n) => {
943
971
  const text = nodeText(n);
944
972
  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)));
945
973
  }));
@@ -991,7 +1019,7 @@ function NodeContent(props) {
991
1019
  return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
992
1020
  }
993
1021
  function makeContextBrowser(kit, StackedBar) {
994
- const { t, tr, fmt: fmt3, fmtTime: fmtTime2, catLabel } = kit;
1022
+ const { t, fmt: fmt3, fmtTime: fmtTime2, catLabel } = kit;
995
1023
  const nodeText = makeNodeText(kit);
996
1024
  const catColor = {};
997
1025
  for (const c of CATS) catColor[c.key] = c.color;
@@ -1032,11 +1060,19 @@ function makeContextBrowser(kit, StackedBar) {
1032
1060
  React.useEffect(() => {
1033
1061
  if (!hasMore && missingSeq !== null && !exhausted) setExhausted(true);
1034
1062
  }, [hasMore, missingSeq, exhausted]);
1063
+ const pinSeq = props.pinSeq;
1064
+ React.useEffect(() => {
1065
+ setSel(pinSeq === null || pinSeq === void 0 ? "live" : pinSeq);
1066
+ setOpenCat(null);
1067
+ setOpenElem(null);
1068
+ }, [pinSeq]);
1035
1069
  const awaiting = missingSeq !== null && !exhausted && loadOlderHistory !== void 0 && hasMore;
1036
1070
  const requests = data.requests || [];
1037
1071
  const hoverReq = props.previewSeq !== null && props.previewSeq !== void 0 ? requests.find((r) => r.seq === props.previewSeq) ?? null : null;
1038
1072
  const req = hoverReq ?? (sel === "live" ? null : requests.find((r) => r.seq === sel) ?? null);
1039
1073
  const seq = req !== null ? req.seq : null;
1074
+ const linked = req === null && props.onHoverKey !== void 0;
1075
+ const linkKey = linked && props.hoverKey !== null && props.hoverKey !== "free" ? props.hoverKey : null;
1040
1076
  const view = assemble(data, headers, seq);
1041
1077
  const breakdown = req !== null ? req : data.current;
1042
1078
  const parts = partsOf(breakdown);
@@ -1124,7 +1160,7 @@ function makeContextBrowser(kit, StackedBar) {
1124
1160
  );
1125
1161
  });
1126
1162
  };
1127
- return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("browser.title"), /* @__PURE__ */ React.createElement(
1163
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("browser.title")), /* @__PURE__ */ React.createElement(
1128
1164
  "select",
1129
1165
  {
1130
1166
  className: "lc-br-pick",
@@ -1134,15 +1170,43 @@ function makeContextBrowser(kit, StackedBar) {
1134
1170
  }
1135
1171
  },
1136
1172
  /* @__PURE__ */ React.createElement("option", { value: "live" }, t("browser.live")),
1137
- requests.slice().reverse().map((r) => /* @__PURE__ */ React.createElement("option", { key: r.seq, value: String(r.seq) }, tr("detail.step", { t: r.turn ?? 0, s: r.step ?? 0 }) + " \xB7 " + fmtTime2(r.time)))
1138
- )), /* @__PURE__ */ React.createElement("div", { className: "lc-br-meta" }, /* @__PURE__ */ React.createElement("b", null, req !== null ? tr("detail.step", { t: req.turn ?? 0, s: req.step ?? 0 }) : t("browser.liveNow")), req !== null ? /* @__PURE__ */ React.createElement("span", null, fmtTime2(req.time)) : null, hoverReq !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("browser.preview")) : null, /* @__PURE__ */ React.createElement("span", null, tr("detail.estTotal", { n: fmt3(total) })), req !== null && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, tr("detail.actual", { n: fmt3(req.prompt) })) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts, height: 10 }), view.missingLive > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, tr("browser.missingLive", { n: view.missingLive })) : null, view.approximate ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.approx")) : null, /* @__PURE__ */ React.createElement("div", { className: "lc-br-cats" }, CATS.map((c) => {
1173
+ requests.slice().reverse().map((r) => /* @__PURE__ */ React.createElement("option", { key: r.seq, value: String(r.seq) }, t("detail.step", { t: r.turn ?? 0, s: r.step ?? 0 }) + " \xB7 " + fmtTime2(r.time)))
1174
+ )), /* @__PURE__ */ React.createElement("div", { className: "lc-br-meta" }, /* @__PURE__ */ React.createElement("b", null, req !== null ? t("detail.step", { t: req.turn ?? 0, s: req.step ?? 0 }) : t("browser.liveNow")), req !== null ? /* @__PURE__ */ React.createElement("span", null, fmtTime2(req.time)) : null, hoverReq !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("browser.preview")) : null, /* @__PURE__ */ React.createElement("span", null, t("detail.estTotal", { n: fmt3(total) })), req !== null && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt3(req.prompt) })) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-br-bar" }, /* @__PURE__ */ React.createElement(
1175
+ StackedBar,
1176
+ {
1177
+ parts,
1178
+ height: 10,
1179
+ hoverKey: linked ? linkKey : void 0,
1180
+ onHoverKey: linked ? props.onHoverKey : void 0,
1181
+ tip: false
1182
+ }
1183
+ )), view.missingLive > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.missingLive", { n: view.missingLive })) : null, view.approximate ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.approx")) : null, /* @__PURE__ */ React.createElement("div", { className: "lc-br-cats" }, CATS.map((c) => {
1139
1184
  const count = toolCount(c.key);
1140
1185
  const v = breakdown[c.key] || 0;
1141
1186
  const openable = count > 0 || (c.key === "system" || c.key === "tools") && view.header === null;
1142
1187
  const open = openCat === c.key && openable;
1143
- return /* @__PURE__ */ React.createElement("div", { key: c.key, className: "lc-br-cat" + (openable ? "" : " lc-br-cat-empty") }, /* @__PURE__ */ React.createElement("button", { type: "button", className: "lc-br-cat-row", onClick: () => {
1144
- toggleCat(c.key);
1145
- } }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "\u25B8"), /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }), /* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-label" }, catLabel(c.key)), /* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-count" }, tr("browser.items", { n: count })), /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "\u2248" + fmt3(v)), /* @__PURE__ */ React.createElement("span", { className: "lc-br-pct" }, total > 0 ? Math.round(v / total * 100) + "%" : "")), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-body" }, catBody(c.key)) : null);
1188
+ return /* @__PURE__ */ React.createElement("div", { key: c.key, className: "lc-br-cat" + (openable ? "" : " lc-br-cat-empty") }, /* @__PURE__ */ React.createElement(
1189
+ "button",
1190
+ {
1191
+ type: "button",
1192
+ className: "lc-br-cat-row" + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
1193
+ onMouseEnter: linked ? () => {
1194
+ if (props.onHoverKey !== void 0) props.onHoverKey(c.key);
1195
+ } : void 0,
1196
+ onMouseLeave: linked ? () => {
1197
+ if (props.onHoverKey !== void 0) props.onHoverKey(null);
1198
+ } : void 0,
1199
+ onClick: () => {
1200
+ toggleCat(c.key);
1201
+ }
1202
+ },
1203
+ /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "\u25B8"),
1204
+ /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }),
1205
+ /* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-label" }, catLabel(c.key)),
1206
+ /* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-count" }, t("browser.items", { n: count })),
1207
+ /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "\u2248" + fmt3(v)),
1208
+ /* @__PURE__ */ React.createElement("span", { className: "lc-br-pct" }, total > 0 ? Math.round(v / total * 100) + "%" : "")
1209
+ ), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-body" }, catBody(c.key)) : null);
1146
1210
  })));
1147
1211
  };
1148
1212
  }
@@ -1158,22 +1222,18 @@ function fmt(n) {
1158
1222
  return String(Math.round(n));
1159
1223
  }
1160
1224
  function fmtTime(t) {
1161
- const d = new Date(t);
1162
- function p(x) {
1163
- return (x < 10 ? "0" : "") + x;
1164
- }
1165
- return p(d.getHours()) + ":" + p(d.getMinutes()) + ":" + p(d.getSeconds());
1225
+ return new Date(t).toLocaleTimeString("en-GB", { hour12: false });
1166
1226
  }
1167
1227
 
1168
1228
  // src/client/components/events.tsx
1169
1229
  var EVENT_ICONS = { compaction: "\u2702", prune: "\u2702", inject: "\uFF0B", model: "\u21C4" };
1170
- function makeEventText(t, tr) {
1230
+ function makeEventText(t) {
1171
1231
  function eventLabel(ev) {
1172
- if (ev.kind === "compaction") return tr("ev.compaction", { n: ev.count || 0 });
1232
+ if (ev.kind === "compaction") return t("ev.compaction", { n: ev.count || 0 });
1173
1233
  if (ev.kind === "prune") return t("ev.prune");
1174
- if (ev.kind === "model") return tr("ev.model", { a: ev.from || "?", b: ev.to || "?" });
1234
+ if (ev.kind === "model") return t("ev.model", { a: ev.from || "?", b: ev.to || "?" });
1175
1235
  if (ev.kind === "inject") {
1176
- if (ev.sub === "skill") return tr("ev.skill", { name: ev.name || "?" });
1236
+ if (ev.sub === "skill") return t("ev.skill", { name: ev.name || "?" });
1177
1237
  const base = t("form." + (ev.form || "context"));
1178
1238
  return ev.name ? base + " \xB7 " + ev.name : base;
1179
1239
  }
@@ -1228,7 +1288,7 @@ function makeEventList(kit) {
1228
1288
 
1229
1289
  // src/client/meta.ts
1230
1290
  var PLUGIN_NAME = "dsh-context";
1231
- var PLUGIN_VERSION = true ? "0.13.0" : "0.0.0-dev";
1291
+ var PLUGIN_VERSION = true ? "0.14.0" : "0.0.0-dev";
1232
1292
  var PLUGIN_REPO = true ? "https://github.com/bowenliang123/dsh-context" : "https://github.com/bowenliang123/dsh-context";
1233
1293
  var PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
1234
1294
 
@@ -1276,7 +1336,7 @@ function makePluginInfo(kit) {
1276
1336
  const update = latest !== null && isNewerVersion(latest, PLUGIN_VERSION) ? latest : null;
1277
1337
  const nameValue = [PLUGIN_NAME + " (v" + PLUGIN_VERSION + ")"];
1278
1338
  if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", { key: "update", className: "lc-pi-update" }, "\u2191 v" + update));
1279
- return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("plugin.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("plugin.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-pi-grid" }, row(t("plugin.name"), nameValue, PLUGIN_REPO + "/releases"), row(t("plugin.github"), PLUGIN_REPO_SHORT, PLUGIN_REPO)));
1339
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("plugin.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("plugin.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-pi-grid" }, row(t("plugin.name"), nameValue, PLUGIN_REPO + "/releases"), row(t("plugin.github"), PLUGIN_REPO_SHORT, PLUGIN_REPO)));
1280
1340
  };
1281
1341
  }
1282
1342
 
@@ -1296,7 +1356,7 @@ function makeStatsBoard(kit) {
1296
1356
  else if (ev.kind === "inject") injects++;
1297
1357
  }
1298
1358
  const cell = (label, value) => /* @__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));
1299
- return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col lc-col-stats" }, /* @__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(t("stats.injects"), fmt3(injects)), cell(t("stats.compactions"), fmt3(compactions)), cell(t("stats.prunes"), fmt3(prunes))));
1359
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col lc-col-stats" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, 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(t("stats.injects"), fmt3(injects)), cell(t("stats.compactions"), fmt3(compactions)), cell(t("stats.prunes"), fmt3(prunes))));
1300
1360
  };
1301
1361
  }
1302
1362
 
@@ -1304,7 +1364,7 @@ function makeStatsBoard(kit) {
1304
1364
  var viewScroll = /* @__PURE__ */ new Map();
1305
1365
  var EVENT_KINDS = ["inject", "compaction", "prune", "model"];
1306
1366
  function makeContextView(ctx, kit) {
1307
- const { t, tr, fmt: fmt3, fmtTime: fmtTime2, catLabel } = kit;
1367
+ const { t, fmt: fmt3, fmtTime: fmtTime2, catLabel } = kit;
1308
1368
  const StackedBar = makeStackedBar(kit);
1309
1369
  const Legend = makeLegend(kit);
1310
1370
  const TrendChart = makeTrendChart(kit);
@@ -1388,9 +1448,9 @@ function makeContextView(ctx, kit) {
1388
1448
  }
1389
1449
  }
1390
1450
  const head = headlineOf(data, pressure);
1391
- return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement("div", { className: "lc-cols lc-head" }, /* @__PURE__ */ React.createElement(StatsBoard, { requests, events }), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__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(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt3(head.window) + " " + tr("overview.ofWindow", { p: head.pct ?? 0 }) : " " + t("overview.estimate")), !head.estimated ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("overview.splitEst")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: head.parts, height: 16, max: head.window, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts: head.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) => {
1451
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement("div", { className: "lc-cols lc-head" }, /* @__PURE__ */ React.createElement(StatsBoard, { requests, events }), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, 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(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt3(head.window) + " tokens" : " " + t("overview.estimate")), head.pct !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-overview-pct" }, /* @__PURE__ */ React.createElement("b", null, head.pct + "%"), t("overview.used")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: head.parts, height: 16, max: head.window, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts: head.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) => {
1392
1452
  return /* @__PURE__ */ React.createElement("span", { key: tool.name, className: "lc-tool-chip" }, tool.name + " " + fmt3(tool.tokens));
1393
- }), 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(
1453
+ }), data.toolList.length > 5 ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, " " + t("tools.more", { n: data.toolList.length })) : null) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("trend.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement(
1394
1454
  "button",
1395
1455
  {
1396
1456
  className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
@@ -1429,9 +1489,12 @@ function makeContextView(ctx, kit) {
1429
1489
  headers,
1430
1490
  useSession: props.useSession,
1431
1491
  loadOlderHistory: props.loadOlderHistory,
1432
- previewSeq: hoveredSeq
1492
+ previewSeq: hoveredSeq,
1493
+ pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
1494
+ hoverKey: hoverCat,
1495
+ onHoverKey: setHoverCat
1433
1496
  }
1434
- ))), /* @__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("div", { className: "lc-kinds" }, EVENT_KINDS.map((k) => /* @__PURE__ */ React.createElement(
1497
+ ))), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("events.title")), /* @__PURE__ */ React.createElement("div", { className: "lc-kinds" }, EVENT_KINDS.map((k) => /* @__PURE__ */ React.createElement(
1435
1498
  "button",
1436
1499
  {
1437
1500
  key: k,
@@ -1441,16 +1504,15 @@ function makeContextView(ctx, kit) {
1441
1504
  }
1442
1505
  },
1443
1506
  t("kind." + k)
1444
- )))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__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")));
1507
+ )))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, 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")));
1445
1508
  };
1446
1509
  }
1447
1510
 
1448
1511
  // src/client/viewkit.ts
1449
1512
  function makeViewKit(t) {
1450
- const { eventLabel, eventAt } = makeEventText(t, t);
1513
+ const { eventLabel, eventAt } = makeEventText(t);
1451
1514
  return {
1452
1515
  t,
1453
- tr: t,
1454
1516
  fmt,
1455
1517
  fmtTime,
1456
1518
  catLabel: (key) => t("cat." + key),
package/lib/index.js CHANGED
@@ -18,14 +18,7 @@ var Config = z.preprocess(
18
18
  }).strict()
19
19
  );
20
20
  function resolveBounds(config) {
21
- const parsed = Config.parse(config ?? {});
22
- return {
23
- maxRequestSteps: parsed.maxRequestSteps,
24
- maxKeptTurns: parsed.maxKeptTurns,
25
- maxEvents: parsed.maxEvents,
26
- maxNodes: parsed.maxNodes,
27
- maxArchiveNodes: parsed.maxArchiveNodes
28
- };
21
+ return Config.parse(config ?? {});
29
22
  }
30
23
 
31
24
  // src/host/headers.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {