dsh-context 0.62.3 → 0.64.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
@@ -108,6 +108,7 @@ window.__ModuleLoader__.load({
108
108
  "insightsEntry.hide": "隐藏",
109
109
  "settings.gran": "趋势图 · 默认粒度",
110
110
  "settings.mode": "趋势图 · 默认展示方式",
111
+ "settings.durationCurve": "趋势图 · 耗时曲线",
111
112
  "settings.deltaBase": "上下文浏览器 · 对比基线",
112
113
  "settings.toolSort": "工具定义 · 默认排序",
113
114
  "settings.fileSort": "文件活动 · 默认排序",
@@ -117,9 +118,10 @@ window.__ModuleLoader__.load({
117
118
  "gran.total": "全量",
118
119
  "gran.delta": "增量",
119
120
  "gran.modeHint": "全量:累计构成;增量:相对上一条请求的变化量",
120
- "trend.focus": "仅展示 {cat}",
121
121
  "trend.adaptive": "自适应",
122
122
  "trend.adaptiveHint": "按当前可视范围内的柱子重新缩放高度,滚动时随之变化",
123
+ "trend.duration": "耗时",
124
+ "trend.durationTip": "在柱子上叠加每步的活跃耗时曲线(步骤开始至结束,含模型与工具调用,不含等待你决策或回答的时间),按图中右侧的四分位坐标轴读数;轮次粒度下为一轮各步耗时之和",
123
125
  "trend.dna": "DNA 模式",
124
126
  "trend.dnaTip": "把每根柱子画成该次请求的上下文指纹:按模型读取顺序逐条铺色,配合增量模式逐条展示每次请求的增减;悬停查看条目,点击让上下文浏览器定位到该条目,同一条目在所有柱子中亮出其存续变化",
125
127
  "trend.dnaItem": "{label}:{n}",
@@ -255,6 +257,7 @@ window.__ModuleLoader__.load({
255
257
  "tip.total": "合计 ≈ {n}",
256
258
  "tip.cat": "{cat} ≈ {n}",
257
259
  "tip.delta": "Δ {n}",
260
+ "tip.duration": "耗时 {n}",
258
261
  "ev.compaction": "压缩上下文(摘要替换 {n} 条消息)",
259
262
  "ev.prune": "剪枝工具输出",
260
263
  "ev.skill": "Skill 注入({name})",
@@ -354,7 +357,6 @@ window.__ModuleLoader__.load({
354
357
  "attach.sent": "发送",
355
358
  "attach.token": "Token",
356
359
  "attach.tokensTip": "按 DeepSeek 官方图片尺寸换算(单图上限 384 tokens)估算的 token 消耗",
357
- "ov.entry": "上下文洞察",
358
360
  "ov.title": "上下文洞察",
359
361
  "ov.unavailable": "会话列表暂不可用",
360
362
  "ov.range.label": "统计范围",
@@ -506,6 +508,7 @@ window.__ModuleLoader__.load({
506
508
  "insightsEntry.hide": "Hide",
507
509
  "settings.gran": "Trend Chart · Default granularity",
508
510
  "settings.mode": "Trend Chart · Default display",
511
+ "settings.durationCurve": "Trend Chart · Duration curve",
509
512
  "settings.deltaBase": "Context Browser · Diff baseline",
510
513
  "settings.toolSort": "Tool Schemas · Default sort",
511
514
  "settings.fileSort": "File Activity · Default sort",
@@ -515,9 +518,10 @@ window.__ModuleLoader__.load({
515
518
  "gran.total": "Total",
516
519
  "gran.delta": "Delta",
517
520
  "gran.modeHint": "Total: cumulative makeup; Delta: change vs the previous request",
518
- "trend.focus": "Showing {cat} only",
519
521
  "trend.adaptive": "Adaptive",
520
522
  "trend.adaptiveHint": "Rescale bar heights to the visible range, following the scroll",
523
+ "trend.duration": "Duration",
524
+ "trend.durationTip": "Overlay each step’s active time (step start to end — model and tool calls, excluding waits for your decisions or answers) as a curve over the bars, read off the quartile axis on the chart’s right; turn granularity sums a turn’s steps",
521
525
  "trend.dna": "DNA Mode",
522
526
  "trend.dnaTip": "Draw each bar as that request’s context fingerprint — one band per item in the order the model reads it; with Delta, each band shows that item’s change against the previous bar. Hover for the item, click to reveal it in the Context browser, and every bar lights the same item’s lifetime",
523
527
  "trend.dnaItem": "{label}: {n}",
@@ -653,6 +657,7 @@ window.__ModuleLoader__.load({
653
657
  "tip.total": "Total ≈ {n}",
654
658
  "tip.cat": "{cat} ≈ {n}",
655
659
  "tip.delta": "Δ {n}",
660
+ "tip.duration": "Duration {n}",
656
661
  "ev.compaction": "Context compacted (summary replaced {n} messages)",
657
662
  "ev.prune": "Tool output pruned",
658
663
  "ev.skill": "Skill injected ({name})",
@@ -752,7 +757,6 @@ window.__ModuleLoader__.load({
752
757
  "attach.sent": "Sent",
753
758
  "attach.token": "Token",
754
759
  "attach.tokensTip": "Estimated token cost via DeepSeek's official image-size conversion (384-token cap per image)",
755
- "ov.entry": "Context Insights",
756
760
  "ov.title": "Context Insights",
757
761
  "ov.unavailable": "The session list is unavailable",
758
762
  "ov.range.label": "Range",
@@ -841,11 +845,11 @@ window.__ModuleLoader__.load({
841
845
  * it to the Plugins page's package cards. The client bundle inlines the
842
846
  * file's markup at build time (the `?raw` channel in tsdown.config.ts) and
843
847
  * re-renders it at every requested size. The identity seats (tab chip,
844
- * command, sidebar registration, panel head) keep the sheet's fixed fills —
848
+ * command, sidebar registration) keep the sheet's fixed fills —
845
849
  * deliberately polychrome on both light and dark chrome — while the
846
- * sidebar-foot entry seat (overviewButton.tsx) opts into `mono`, trading
847
- * every fill for `currentColor` so the glyph sits quietly beside the
848
- * harness's own footer rows (Settings and friends).
850
+ * sidebar's panel-list seat (insightPage.ts) opts into `mono`, trading
851
+ * every fill for `currentColor` so the glyph follows the shell-owned row's
852
+ * own state colors, exactly as the shipped panel glyphs do.
849
853
  */
850
854
  /** Everything between the file's `<svg>` tags: the sheet's strokes in paint order, whitespace-folded. */
851
855
  const SHEET_MARKUP = icon_default.slice(icon_default.indexOf(">") + 1, icon_default.lastIndexOf("</svg>")).trim().replace(/>\s+</g, "><");
@@ -864,6 +868,19 @@ window.__ModuleLoader__.load({
864
868
  });
865
869
  }
866
870
  /**
871
+ * The sidebar panel-list glyph (`sidebar.panellist`): the emblem in `mono` at
872
+ * the size the shell asks for, so the shell-owned row's hover/active colors
873
+ * paint it like the shipped panel glyphs. The shell owns the row's label,
874
+ * and the row's selected state its own styling, so — as on the shipped
875
+ * glyphs — the owner props' `active` goes unread.
876
+ */
877
+ function InsightPanelIcon({ size = 18 }) {
878
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextIcon, {
879
+ size,
880
+ mono: true
881
+ });
882
+ }
883
+ /**
867
884
  * The tab chip's title seat (`sidebar.right.pane.tab.title`): the emblem before
868
885
  * the label, so the chip reads as the files chip does. The label comes from the
869
886
  * plugin's own bound translate — read at render, so the chip follows the active
@@ -2201,9 +2218,9 @@ window.__ModuleLoader__.load({
2201
2218
  * The no-stale-content guarantees: the store is per session and shared by
2202
2219
  * the tab and the modal; a refetch is single-flight with a trailing edge
2203
2220
  * (a rev bumped mid-flight re-reads after settle); responses race-safe by
2204
- * revision (latest wins, with the refold exception — a host that refolded
2205
- * restarts the revision, and a response matching the requested rev is
2206
- * accepted regardless of order); a transport failure keeps the last good
2221
+ * generation and revision (a host refold invalidates reads started before
2222
+ * its revisions restarted, and latest wins within a generation); a
2223
+ * transport failure keeps the last good
2207
2224
  * detail and backs off; an absent answer (the session left the live set)
2208
2225
  * stops the trailing until the head moves again. With no detail at all,
2209
2226
  * failure surfaces as a retryable note on the detail cards instead of an
@@ -2292,6 +2309,7 @@ window.__ModuleLoader__.load({
2292
2309
  targetRev = -1;
2293
2310
  /** The head rev seen last — a DECREASE means the host refolded (revisions restart). */
2294
2311
  lastHeadRev = -1;
2312
+ generation = 0;
2295
2313
  failed = false;
2296
2314
  inFlight = false;
2297
2315
  timer = null;
@@ -2315,6 +2333,7 @@ window.__ModuleLoader__.load({
2315
2333
  */
2316
2334
  request(rev) {
2317
2335
  if (rev < this.lastHeadRev) {
2336
+ this.generation++;
2318
2337
  this.acceptedRev = -1;
2319
2338
  this.wantedRev = -1;
2320
2339
  this.detail = null;
@@ -2352,9 +2371,11 @@ window.__ModuleLoader__.load({
2352
2371
  }
2353
2372
  this.inFlight = true;
2354
2373
  this.targetRev = this.wantedRev;
2374
+ const generation = this.generation;
2355
2375
  this.emit();
2356
2376
  try {
2357
2377
  const d = await this.fetcher();
2378
+ if (generation !== this.generation) return;
2358
2379
  if (d !== null) {
2359
2380
  if (d.rev >= this.acceptedRev || d.rev === this.targetRev) {
2360
2381
  this.detail = d;
@@ -2368,12 +2389,15 @@ window.__ModuleLoader__.load({
2368
2389
  this.failures++;
2369
2390
  }
2370
2391
  } catch {
2371
- this.failed = this.detail === null;
2372
- this.failures++;
2392
+ if (generation === this.generation) {
2393
+ this.failed = this.detail === null;
2394
+ this.failures++;
2395
+ }
2396
+ } finally {
2397
+ this.inFlight = false;
2398
+ if (this.wantedRev > this.acceptedRev) this.schedule();
2399
+ this.emit();
2373
2400
  }
2374
- this.inFlight = false;
2375
- if (this.wantedRev > this.acceptedRev) this.schedule();
2376
- this.emit();
2377
2401
  }
2378
2402
  emit() {
2379
2403
  const pending = this.timer !== null || this.inFlight;
@@ -3003,21 +3027,31 @@ window.__ModuleLoader__.load({
3003
3027
  */
3004
3028
  /**
3005
3029
  * Collapse per-step requests into one bar per turn — each turn is represented by its LAST step's record, tagged `stepCount` for the bar's
3006
- * column width; the log keeps one turn's requests consecutive, so a run of equal turns collapses to its final record.
3030
+ * column width; the log keeps one turn's requests consecutive, so a run of equal turns collapses to its final record. The duration
3031
+ * overlay's `activeMs` is the run's steps SUMMED (the turn's total step active time), absent only when no step of the run carried one.
3007
3032
  */
3008
3033
  function aggregateByTurn(requests) {
3009
3034
  const out = [];
3010
3035
  let runSteps = 0;
3036
+ let runActiveMs = 0;
3037
+ let runHasActiveMs = false;
3011
3038
  for (const req of requests) {
3012
3039
  const last = out.length > 0 ? out[out.length - 1] : null;
3013
3040
  if (last !== null && (last.turn ?? 0) === (req.turn ?? 0)) {
3014
3041
  runSteps++;
3042
+ if (req.activeMs !== void 0) {
3043
+ runActiveMs += req.activeMs;
3044
+ runHasActiveMs = true;
3045
+ }
3015
3046
  out[out.length - 1] = {
3016
3047
  ...req,
3017
- stepCount: runSteps
3048
+ stepCount: runSteps,
3049
+ ...runHasActiveMs ? { activeMs: runActiveMs } : {}
3018
3050
  };
3019
3051
  } else {
3020
3052
  runSteps = 1;
3053
+ runActiveMs = req.activeMs ?? 0;
3054
+ runHasActiveMs = req.activeMs !== void 0;
3021
3055
  out.push({
3022
3056
  ...req,
3023
3057
  stepCount: 1
@@ -3064,7 +3098,7 @@ window.__ModuleLoader__.load({
3064
3098
  return requests.length > 0 ? requests[0] : null;
3065
3099
  }
3066
3100
  function makeTrendChart(kit) {
3067
- const { t, fmt, eventLabel, eventAt, catLabel } = kit;
3101
+ const { t, fmt, fmtDuration, eventLabel, eventAt, catLabel } = kit;
3068
3102
  const CHART_H = 112;
3069
3103
  const fmtSigned = (v) => (v > 0 ? "+" : "") + fmt(v);
3070
3104
  const BAR_W = 14;
@@ -3335,6 +3369,7 @@ window.__ModuleLoader__.load({
3335
3369
  const dnaOn = dnaBands !== null;
3336
3370
  const delta = props.mode === "delta";
3337
3371
  const dnaDeltaOn = dnaOn && delta;
3372
+ const durationOn = props.durationCurve === true;
3338
3373
  const focus = !dnaOn && props.focusCat !== null && props.focusCat !== void 0 && CATS.some((c) => c.key === props.focusCat) ? props.focusCat : null;
3339
3374
  const [dnaHit, setDnaHit] = (0, react.useState)(null);
3340
3375
  const dnaDeltas = (0, react.useMemo)(() => dnaDeltaOn ? dnaBands.map((bands, i) => deltaBandsOf(bands, i > 0 ? dnaBands[i - 1] : null)) : null, [dnaDeltaOn, dnaBands]);
@@ -3359,12 +3394,15 @@ window.__ModuleLoader__.load({
3359
3394
  let total = 0;
3360
3395
  let up = 0;
3361
3396
  let down = 0;
3397
+ let activeMs = 0;
3362
3398
  const from = Math.max(0, Math.floor(sl / pitch));
3363
3399
  const to = Math.min(n - 1, Math.max(from, Math.floor((vr - 1) / pitch)));
3364
3400
  for (let i = from; i <= to; i++) {
3365
3401
  const col = i * pitch;
3366
3402
  if (col >= vr || col + BAR_W <= sl) continue;
3367
3403
  const req = requests[i];
3404
+ const m = req.activeMs ?? 0;
3405
+ if (m > activeMs) activeMs = m;
3368
3406
  if (dnaDeltaOn && dnaDeltas !== null) {
3369
3407
  let bu = 0;
3370
3408
  let bd = 0;
@@ -3384,10 +3422,11 @@ window.__ModuleLoader__.load({
3384
3422
  if (bd > down) down = bd;
3385
3423
  } else if (req.total > total) total = req.total;
3386
3424
  }
3387
- setVisMax((prev) => prev !== null && prev.total === total && prev.up === up && prev.down === down ? prev : {
3425
+ setVisMax((prev) => prev !== null && prev.total === total && prev.up === up && prev.down === down && prev.activeMs === activeMs ? prev : {
3388
3426
  total,
3389
3427
  up,
3390
- down
3428
+ down,
3429
+ activeMs
3391
3430
  });
3392
3431
  };
3393
3432
  let maxTotal = 1;
@@ -3421,6 +3460,34 @@ window.__ModuleLoader__.load({
3421
3460
  }
3422
3461
  } else maxTotal = Math.max(1, visMax.total);
3423
3462
  }
3463
+ let maxActiveMs = 0;
3464
+ if (durationOn) {
3465
+ for (const req of requests) {
3466
+ const m = req.activeMs ?? 0;
3467
+ if (m > maxActiveMs) maxActiveMs = m;
3468
+ }
3469
+ if (adaptive && visMax !== null) maxActiveMs = visMax.activeMs;
3470
+ }
3471
+ const durationRuns = [];
3472
+ const durationDots = [];
3473
+ if (durationOn) {
3474
+ const scale = CHART_H / Math.max(1, maxActiveMs);
3475
+ let run = [];
3476
+ const flushRun = () => {
3477
+ if (run.length >= 2) durationRuns.push(run.map((p) => `${p[0]},${p[1]}`).join(" "));
3478
+ else if (run.length === 1) durationDots.push(run[0]);
3479
+ run = [];
3480
+ };
3481
+ for (let i = 0; i < requests.length; i++) {
3482
+ const m = requests[i].activeMs;
3483
+ if (m === void 0) {
3484
+ flushRun();
3485
+ continue;
3486
+ }
3487
+ run.push([i * 16 + BAR_W / 2, Math.round((CHART_H - m * scale) * 100) / 100]);
3488
+ }
3489
+ flushRun();
3490
+ }
3424
3491
  const span = Math.max(1, maxUp + maxDown);
3425
3492
  const deltaScale = CHART_H / span;
3426
3493
  const upPx = Math.round(maxUp * deltaScale);
@@ -3468,6 +3535,8 @@ window.__ModuleLoader__.load({
3468
3535
  const lastGranRef = (0, react.useRef)(props.granularity);
3469
3536
  const lastSeqRef = (0, react.useRef)(0);
3470
3537
  const prevScrollWidthRef = (0, react.useRef)(0);
3538
+ const cwRef = (0, react.useRef)(0);
3539
+ const slRef = (0, react.useRef)(0);
3471
3540
  /**
3472
3541
  * Keep each turn label centered within its block's VISIBLE slice, then thin colliding labels: a label wider
3473
3542
  * than its block overflows it, so consecutive narrow turns would smear into each other — walking left→right
@@ -3538,6 +3607,8 @@ window.__ModuleLoader__.load({
3538
3607
  } else if (grew && el.scrollLeft + el.clientWidth >= widthBeforeAppend - 24) el.scrollLeft = el.scrollWidth;
3539
3608
  lastSeqRef.current = newestSeq;
3540
3609
  prevScrollWidthRef.current = el.scrollWidth;
3610
+ slRef.current = el.scrollLeft;
3611
+ cwRef.current = el.clientWidth;
3541
3612
  updateTurnLabels(el);
3542
3613
  syncTip(el);
3543
3614
  measureVisible(el);
@@ -3559,6 +3630,7 @@ window.__ModuleLoader__.load({
3559
3630
  if (el === null) return;
3560
3631
  if (typeof ResizeObserver !== "function") return;
3561
3632
  const observer = new ResizeObserver(() => {
3633
+ cwRef.current = el.clientWidth;
3562
3634
  measureRef.current(el);
3563
3635
  });
3564
3636
  observer.observe(el);
@@ -3624,35 +3696,40 @@ window.__ModuleLoader__.load({
3624
3696
  n: stepsOf(req.turn)
3625
3697
  });
3626
3698
  const band = hitBand ?? hoveredDelta;
3627
- if (band !== null) return [head, t("trend.dnaItem", {
3699
+ let metric;
3700
+ if (band !== null) metric = t("trend.dnaItem", {
3628
3701
  label: dnaBaseLabel(band, t, catLabel),
3629
3702
  n: dnaDeltaOn ? fmtSigned(band.tokens) : fmt(band.tokens)
3630
- })];
3631
- if (delta)
3703
+ });
3704
+ else if (delta)
3632
3705
  /* v8 ignore next 1 -- delta mode only receives records from
3633
3706
  deltaOf, which always assigns net; the fallback is defensive. */
3634
- return [head, t("tip.delta", { n: fmtSigned(req.net ?? 0) })];
3635
- return [head, focus !== null ? t("tip.cat", {
3707
+ metric = t("tip.delta", { n: fmtSigned(req.net ?? 0) });
3708
+ else metric = focus !== null ? t("tip.cat", {
3636
3709
  cat: catLabel(focus),
3637
3710
  n: fmt(req.total)
3638
- }) : t("tip.total", { n: fmt(req.total) })];
3711
+ }) : t("tip.total", { n: fmt(req.total) });
3712
+ const rows = [head, metric];
3713
+ if (durationOn && req.activeMs !== void 0) rows.push(t("tip.duration", { n: fmtDuration(req.activeMs) }));
3714
+ return rows;
3639
3715
  };
3640
3716
  const tipColRef = (0, react.useRef)(0);
3641
3717
  /**
3642
3718
  * Glue the hover tip to its bar's VISIBLE slice. The tip deliberately does NOT live inside the scrolling
3643
3719
  * content: an absolutely-positioned child of a scroller contributes to its scrollable overflow, so a wide
3644
3720
  * reply preview on a right-edge bar used to inflate scrollWidth on every hover and flap the horizontal
3645
- * scrollbar open/closed — jumping the whole card. Reads (offsetWidth/clientWidth) batch before the single
3646
- * style write; unchanged transforms write nothing.
3721
+ * scrollbar open/closed — jumping the whole card. The only layout read left is the tip's own width (its
3722
+ * subtree is the thing that just changed); the scroller's width/offset ride the cached refs, so this never
3723
+ * forces a document-scope layout flush on the hover path. Unchanged transforms write nothing.
3647
3724
  */
3648
3725
  const syncTip = (el) => {
3649
3726
  /* v8 ignore next 1 -- the scroll div renders unconditionally while mounted, so its parent exists. */
3650
3727
  const tip = (el.parentElement ?? document.body).querySelector(".lc-chart-tip");
3651
3728
  if (tip === null) return;
3652
3729
  const lw = tip.offsetWidth;
3653
- const cw = el.clientWidth;
3730
+ const cw = cwRef.current;
3654
3731
  const half = Math.min(lw / 2, cw / 2);
3655
- const cx = Math.min(Math.max(tipColRef.current - el.scrollLeft, half), cw - half);
3732
+ const cx = Math.min(Math.max(tipColRef.current - slRef.current, half), cw - half);
3656
3733
  const next = `translate(${Math.round(cx - lw / 2)}px, 0)`;
3657
3734
  if (tip.style.transform !== next) tip.style.transform = next;
3658
3735
  };
@@ -3665,134 +3742,175 @@ window.__ModuleLoader__.load({
3665
3742
  });
3666
3743
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3667
3744
  className: "lc-chartrow",
3668
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3669
- className: "lc-axis",
3670
- children: delta ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3671
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3672
- className: "lc-axis-top",
3673
- children: fmtSigned(maxUp)
3674
- }),
3675
- q3Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3676
- className: "lc-axis-q3",
3677
- children: fmtSigned(Math.round(maxUp - span / 4))
3678
- }) : null,
3679
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3680
- className: "lc-axis-mid",
3681
- style: { top: `${13 + upPx}px` },
3682
- children: "0"
3683
- }),
3684
- q1Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3685
- className: "lc-axis-q1",
3686
- children: fmtSigned(Math.round(maxUp - 3 * span / 4))
3687
- }) : null,
3688
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3689
- className: "lc-axis-bot",
3690
- children: fmtSigned(-maxDown)
3691
- })
3692
- ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3693
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3694
- className: "lc-axis-top",
3695
- children: fmt(maxTotal)
3696
- }),
3697
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3698
- className: "lc-axis-q3",
3699
- children: fmt(Math.round(maxTotal * 3 / 4))
3700
- }),
3701
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3702
- className: "lc-axis-mid",
3703
- children: fmt(Math.round(maxTotal / 2))
3704
- }),
3705
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3706
- className: "lc-axis-q1",
3707
- children: fmt(Math.round(maxTotal / 4))
3708
- }),
3709
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3710
- className: "lc-axis-bot",
3711
- children: "0"
3712
- })
3713
- ] })
3714
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3715
- className: "lc-chart-wrap",
3716
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3717
- className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
3718
- ref: scrollRef,
3719
- onScroll: (e) => {
3720
- updateTurnLabels(e.currentTarget);
3721
- syncTip(e.currentTarget);
3722
- measureVisible(e.currentTarget);
3723
- },
3745
+ children: [
3746
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3747
+ className: "lc-axis",
3748
+ children: delta ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3749
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3750
+ className: "lc-axis-top",
3751
+ children: fmtSigned(maxUp)
3752
+ }),
3753
+ q3Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3754
+ className: "lc-axis-q3",
3755
+ children: fmtSigned(Math.round(maxUp - span / 4))
3756
+ }) : null,
3757
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3758
+ className: "lc-axis-mid",
3759
+ style: { top: `${13 + upPx}px` },
3760
+ children: "0"
3761
+ }),
3762
+ q1Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3763
+ className: "lc-axis-q1",
3764
+ children: fmtSigned(Math.round(maxUp - 3 * span / 4))
3765
+ }) : null,
3766
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3767
+ className: "lc-axis-bot",
3768
+ children: fmtSigned(-maxDown)
3769
+ })
3770
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3771
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3772
+ className: "lc-axis-top",
3773
+ children: fmt(maxTotal)
3774
+ }),
3775
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3776
+ className: "lc-axis-q3",
3777
+ children: fmt(Math.round(maxTotal * 3 / 4))
3778
+ }),
3779
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3780
+ className: "lc-axis-mid",
3781
+ children: fmt(Math.round(maxTotal / 2))
3782
+ }),
3783
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3784
+ className: "lc-axis-q1",
3785
+ children: fmt(Math.round(maxTotal / 4))
3786
+ }),
3787
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3788
+ className: "lc-axis-bot",
3789
+ children: "0"
3790
+ })
3791
+ ] })
3792
+ }),
3793
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3794
+ className: "lc-chart-wrap",
3724
3795
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3725
- className: "lc-chart",
3726
- "data-catdim": props.hoverCat ?? void 0,
3727
- onMouseLeave: () => {
3728
- props.onHover(null);
3729
- setDnaHit(null);
3730
- },
3731
- children: [
3732
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-top" }),
3733
- !delta || q3Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-q3" }) : null,
3734
- !delta || q1Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-q1" }) : null,
3735
- !delta ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-mid" }) : null,
3736
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3737
- className: "lc-grid lc-grid-zero",
3738
- style: delta ? { top: `${18 + upPx}px` } : void 0
3739
- }),
3740
- requests.map((req, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartBar, {
3741
- req,
3742
- marker: markers[i],
3743
- selected: props.selectedSeq === req.seq,
3744
- hovered: props.hoveredSeq === req.seq,
3745
- inTurn: props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn,
3746
- flag: props.granularity === "step" && (i + 1) % STEP_FLAG_EVERY === 0 ? i + 1 : null,
3747
- maxTotal,
3748
- upPx: delta ? upPx : void 0,
3749
- downPx: delta ? downPx : void 0,
3750
- deltaScale: delta ? deltaScale : void 0,
3751
- enterIndex: i,
3752
- dna: dnaOn ? dnaBands[i] : null,
3753
- dnaDelta: dnaDeltaOn && dnaDeltas !== null ? dnaDeltas[i] : null,
3754
- dnaScale: dnaDeltaOn ? deltaScale : void 0,
3755
- zeroBottom: dnaDeltaOn ? downPx : void 0,
3756
- onDnaHit: setDnaHit,
3757
- onPickBand: props.onPickBand,
3758
- onSelect: props.onSelect,
3759
- onHover: props.onHover
3760
- }, `${req.seq}:${props.granularity}`)),
3761
- dnaOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DnaHighlights, { slices: dnaSlices }) : null
3762
- ]
3763
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3764
- className: "lc-turns",
3765
- style: labelFont !== "" ? { fontSize: labelFont } : void 0,
3766
- onMouseLeave: () => {
3767
- props.onHoverTurn(null);
3796
+ className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
3797
+ ref: scrollRef,
3798
+ onScroll: (e) => {
3799
+ slRef.current = e.currentTarget.scrollLeft;
3800
+ cwRef.current = e.currentTarget.clientWidth;
3801
+ updateTurnLabels(e.currentTarget);
3802
+ syncTip(e.currentTarget);
3803
+ measureVisible(e.currentTarget);
3768
3804
  },
3769
- children: groups.map((grp, gi) => {
3770
- const on = props.activeTurn === grp.turn;
3771
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3772
- className: "lc-turn" + (on ? " lc-turn-on" : ""),
3773
- style: {
3774
- width: `${turnWidths[gi]}px`,
3775
- background: TURN_FILLS[gi % TURN_FILLS.length]
3776
- },
3777
- title: `T${grp.turn}`,
3778
- onMouseEnter: () => {
3779
- props.onHoverTurn(grp.turn);
3780
- },
3781
- onClick: () => {
3782
- props.onPickTurn(grp.turn);
3783
- },
3784
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3785
- className: "lc-turn-label",
3786
- children: `${grp.turn}`
3787
- })
3788
- }, `turn-${gi}`);
3805
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3806
+ className: "lc-chart",
3807
+ "data-catdim": props.hoverCat ?? void 0,
3808
+ onMouseLeave: () => {
3809
+ props.onHover(null);
3810
+ setDnaHit(null);
3811
+ },
3812
+ children: [
3813
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-top" }),
3814
+ !delta || q3Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-q3" }) : null,
3815
+ !delta || q1Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-q1" }) : null,
3816
+ !delta ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-mid" }) : null,
3817
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3818
+ className: "lc-grid lc-grid-zero",
3819
+ style: delta ? { top: `${18 + upPx}px` } : void 0
3820
+ }),
3821
+ requests.map((req, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartBar, {
3822
+ req,
3823
+ marker: markers[i],
3824
+ selected: props.selectedSeq === req.seq,
3825
+ hovered: props.hoveredSeq === req.seq,
3826
+ inTurn: props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn,
3827
+ flag: props.granularity === "step" && (i + 1) % STEP_FLAG_EVERY === 0 ? i + 1 : null,
3828
+ maxTotal,
3829
+ upPx: delta ? upPx : void 0,
3830
+ downPx: delta ? downPx : void 0,
3831
+ deltaScale: delta ? deltaScale : void 0,
3832
+ enterIndex: i,
3833
+ dna: dnaOn ? dnaBands[i] : null,
3834
+ dnaDelta: dnaDeltaOn && dnaDeltas !== null ? dnaDeltas[i] : null,
3835
+ dnaScale: dnaDeltaOn ? deltaScale : void 0,
3836
+ zeroBottom: dnaDeltaOn ? downPx : void 0,
3837
+ onDnaHit: setDnaHit,
3838
+ onPickBand: props.onPickBand,
3839
+ onSelect: props.onSelect,
3840
+ onHover: props.onHover
3841
+ }, `${req.seq}:${props.granularity}`)),
3842
+ dnaOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DnaHighlights, { slices: dnaSlices }) : null,
3843
+ durationOn && (durationRuns.length > 0 || durationDots.length > 0) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
3844
+ className: "lc-duration",
3845
+ width: requests.length * 16 - BAR_GAP,
3846
+ height: CHART_H,
3847
+ "aria-hidden": "true",
3848
+ children: [durationRuns.map((pts, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: pts }, i)), durationDots.map((p, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
3849
+ cx: p[0],
3850
+ cy: p[1],
3851
+ r: 1.5
3852
+ }, i))]
3853
+ }) : null
3854
+ ]
3855
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3856
+ className: "lc-turns",
3857
+ style: labelFont !== "" ? { fontSize: labelFont } : void 0,
3858
+ onMouseLeave: () => {
3859
+ props.onHoverTurn(null);
3860
+ },
3861
+ children: groups.map((grp, gi) => {
3862
+ const on = props.activeTurn === grp.turn;
3863
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3864
+ className: "lc-turn" + (on ? " lc-turn-on" : ""),
3865
+ style: {
3866
+ width: `${turnWidths[gi]}px`,
3867
+ background: TURN_FILLS[gi % TURN_FILLS.length]
3868
+ },
3869
+ title: `T${grp.turn}`,
3870
+ onMouseEnter: () => {
3871
+ props.onHoverTurn(grp.turn);
3872
+ },
3873
+ onClick: () => {
3874
+ props.onPickTurn(grp.turn);
3875
+ },
3876
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3877
+ className: "lc-turn-label",
3878
+ children: `${grp.turn}`
3879
+ })
3880
+ }, `turn-${gi}`);
3881
+ })
3882
+ })]
3883
+ }), hoveredReq !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3884
+ className: "lc-chart-tip",
3885
+ children: tipRowsOf(hoveredReq).map((row, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row }, i))
3886
+ }) : null]
3887
+ }),
3888
+ durationOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3889
+ className: "lc-axis lc-axis-r",
3890
+ children: [
3891
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3892
+ className: "lc-axis-top",
3893
+ children: fmtDuration(maxActiveMs)
3894
+ }),
3895
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3896
+ className: "lc-axis-q3",
3897
+ children: fmtDuration(Math.round(maxActiveMs * 3 / 4))
3898
+ }),
3899
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3900
+ className: "lc-axis-mid",
3901
+ children: fmtDuration(Math.round(maxActiveMs / 2))
3902
+ }),
3903
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3904
+ className: "lc-axis-q1",
3905
+ children: fmtDuration(Math.round(maxActiveMs / 4))
3906
+ }),
3907
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3908
+ className: "lc-axis-bot",
3909
+ children: "0"
3789
3910
  })
3790
- })]
3791
- }), hoveredReq !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3792
- className: "lc-chart-tip",
3793
- children: tipRowsOf(hoveredReq).map((row, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row }, i))
3794
- }) : null]
3795
- })]
3911
+ ]
3912
+ }) : null
3913
+ ]
3796
3914
  });
3797
3915
  };
3798
3916
  }
@@ -3937,10 +4055,17 @@ window.__ModuleLoader__.load({
3937
4055
  const scaled = Math.trunc(reads / billed * 100 * factor + 1e-9);
3938
4056
  return `${Math.floor(scaled / factor)}.${String(scaled % factor).padStart(decimals, "0")}`;
3939
4057
  }
4058
+ /** One shared formatter: building an Intl formatter per call costs ~50x the format itself (issue #116). */
4059
+ const TIME_FMT = new Intl.DateTimeFormat("en-GB", {
4060
+ hour12: false,
4061
+ hour: "2-digit",
4062
+ minute: "2-digit",
4063
+ second: "2-digit"
4064
+ });
3940
4065
  function fmtTime(t) {
3941
4066
  const d = new Date(t);
3942
4067
  if (isNaN(d.getTime())) return "—";
3943
- return d.toLocaleTimeString("en-GB", { hour12: false });
4068
+ return TIME_FMT.format(d);
3944
4069
  }
3945
4070
  /**
3946
4071
  * Share of a whole as a compact leading percentage for the slice rows:
@@ -5930,71 +6055,6 @@ window.__ModuleLoader__.load({
5930
6055
  };
5931
6056
  }
5932
6057
  //#endregion
5933
- //#region src/client/overviewStore.ts
5934
- function createStore() {
5935
- let open = false;
5936
- const listeners = /* @__PURE__ */ new Set();
5937
- return {
5938
- subscribe: (listener) => {
5939
- listeners.add(listener);
5940
- return () => {
5941
- listeners.delete(listener);
5942
- };
5943
- },
5944
- getSnapshot: () => open,
5945
- set: (next) => {
5946
- if (next === open) return;
5947
- open = next;
5948
- for (const listener of listeners) listener();
5949
- }
5950
- };
5951
- }
5952
- const overviewStore = createStore();
5953
- //#endregion
5954
- //#region src/client/components/overviewButton.tsx
5955
- /**
5956
- * The Context Dashboard's sidebar entry: a footer action stacked directly
5957
- * above Settings (the harness's own foot layout: footer actions, then the
5958
- * settings row), mirroring the Settings trigger's geometry in both column
5959
- * widths. The button carries the plugin emblem — in the mono seat, so the
5960
- * glyph matches the harness's own footer rows rather than shouting over
5961
- * them — and, on the wide column, its label. Clicking opens the overview
5962
- * overlay through the shared module store (overviewStore.ts).
5963
- *
5964
- * The per-user `insightsEntry` preference takes the entry down (renders
5965
- * null) without unregistering the seat. The subscription fails open: an
5966
- * absent settings store, an unserved namespace, or a value the plugin
5967
- * cannot understand all leave the entry visible.
5968
- */
5969
- /** Stable subscription faces for the settings-less degrade (no re-subscribes). */
5970
- const subscribeNever = () => () => {};
5971
- const entryShown = () => "show";
5972
- function makeOverviewButton(kit, settings) {
5973
- const { t } = kit;
5974
- const subscribeEntry = settings === void 0 ? subscribeNever : (listener) => settings.store.subscribe(listener);
5975
- const getEntry = settings === void 0 ? entryShown : () => settings.insightsEntry();
5976
- return function OverviewButton(props) {
5977
- if ((0, react.useSyncExternalStore)(subscribeEntry, getEntry) === "hide") return null;
5978
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
5979
- type: "button",
5980
- className: props.wide === true ? "lc-ov-entry" : "lc-ov-entry lc-ov-entry-rail",
5981
- title: t("ov.entry"),
5982
- "aria-label": t("ov.entry"),
5983
- onClick: () => {
5984
- overviewStore.set(true);
5985
- },
5986
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextIcon, {
5987
- size: props.wide === true ? 16 : 18,
5988
- className: "lc-ov-entry-icon",
5989
- mono: true
5990
- }), props.wide === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5991
- className: "lc-ov-entry-label",
5992
- children: t("ov.entry")
5993
- })]
5994
- });
5995
- };
5996
- }
5997
- //#endregion
5998
6058
  //#region src/shared/providers.ts
5999
6059
  /**
6000
6060
  * The provider-id seam between dsh request envelopes and the models.dev
@@ -7340,14 +7400,15 @@ window.__ModuleLoader__.load({
7340
7400
  //#region src/client/components/balanceCapsule.tsx
7341
7401
  /**
7342
7402
  * BalanceCapsule — the DeepSeek open-platform balance pill in the Context
7343
- * Dashboard's header (the plugin's one account-level figure, beside the
7403
+ * Insights page's heading (the plugin's one account-level figure, beside the
7344
7404
  * session-level ones). Clicking it opens the platform's usage console in a
7345
- * new tab. Mounted only by the dashboard header; it paints the figure the
7405
+ * new tab. Mounted only by the page heading; it paints the figure the
7346
7406
  * previous open remembered while client/balance.ts revalidates it in the
7347
7407
  * background, and renders NOTHING when nothing is remembered and the route
7348
7408
  * answers nothing — the pill exists only when there is a figure to show. The
7349
- * non-zero parts of the breakdown (topped-up / granted) ride the harness
7350
- * Tooltip primitive — immediate on hover, where a native `title` lags — in
7409
+ * non-zero parts of the breakdown (topped-up / granted) ride the page's
7410
+ * portaled hover tip (hoverTip.tsx, off the pill's `data-lc-tip`) —
7411
+ * immediate on hover, where a native `title` lags — in
7351
7412
  * the entry's own currency (the pill itself carries the total); the entry
7352
7413
  * follows the active locale (zh → CNY) with the account's first currency as
7353
7414
  * the fallback.
@@ -7371,9 +7432,13 @@ window.__ModuleLoader__.load({
7371
7432
  if (entry === null) return null;
7372
7433
  const money = (amount) => symbolOf(entry.currency) + amount.toFixed(2);
7373
7434
  const tipLines = [...entry.toppedUp > 0 ? [t("balance.tip.toppedUp") + ": " + money(entry.toppedUp)] : [], ...entry.granted > 0 ? [t("balance.tip.granted") + ": " + money(entry.granted)] : []];
7374
- const pill = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
7435
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
7375
7436
  className: "lc-ov-balance",
7376
7437
  "aria-label": tipLines.length > 0 ? tipLines.join("\n") : void 0,
7438
+ ...tipLines.length > 0 ? {
7439
+ "data-lc-tip": tipLines.join("\n"),
7440
+ "data-lc-tip-side": "bottom"
7441
+ } : {},
7377
7442
  href: USAGE_URL,
7378
7443
  target: "_blank",
7379
7444
  rel: "noreferrer",
@@ -7385,11 +7450,6 @@ window.__ModuleLoader__.load({
7385
7450
  children: money(entry.total)
7386
7451
  })]
7387
7452
  });
7388
- return tipLines.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
7389
- label: tipLines.join("\n"),
7390
- side: "bottom",
7391
- children: pill
7392
- }) : pill;
7393
7453
  };
7394
7454
  }
7395
7455
  //#endregion
@@ -7450,24 +7510,6 @@ window.__ModuleLoader__.load({
7450
7510
  //#endregion
7451
7511
  //#region src/client/components/heatmap.tsx
7452
7512
  /**
7453
- * The Context Dashboard's activity heatmap: a GitHub-style contribution grid
7454
- * (weeks as columns, Sunday-first weekdays as rows, a month label over each
7455
- * column where a month begins — the window's opening column always labeled —
7456
- * and a Less→More key for the depth steps) over the merged daily
7457
- * ledger (overview.ts). Cell depth is the selected metric's share of the
7458
- * window's maximum — distinct active sessions or settlement steps; a session
7459
- * can spread its steps thinly across many days while another day stacks a
7460
- * run, so each metric scales on its OWN maximum — in four steps; a day with
7461
- * data is a button whose click
7462
- * pins the session list to that day (click again to release). Cells tip
7463
- * through the harness's own Tooltip primitive (instant on hover; native
7464
- * `title` lags a second behind), the bubble carrying the date, the day's
7465
- * active sessions, and its steps. The grid is computed from the injected
7466
- * `today` key, so
7467
- * the layout is deterministic in tests and follows the browser's local
7468
- * calendar at runtime.
7469
- */
7470
- /**
7471
7513
  * Lay out the grid: `weeks` columns ending at today's week, each seven
7472
7514
  * Sunday-first cells (row 0 = Sunday). Returns null when `today` (or its
7473
7515
  * week math) fails — a corrupt injected key degrades the card to its empty
@@ -7578,25 +7620,21 @@ window.__ModuleLoader__.load({
7578
7620
  "aria-hidden": "true"
7579
7621
  }, cell.key);
7580
7622
  const level = cell.entry === void 0 ? 0 : levelOf(metricValueOf(cell.entry), max);
7581
- if (cell.entry === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
7582
- label: cell.key,
7583
- side: "top",
7584
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "lc-heat-cell lc-heat-0" })
7623
+ if (cell.entry === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7624
+ className: "lc-heat-cell lc-heat-0",
7625
+ "data-lc-tip": cell.key
7585
7626
  }, cell.key);
7586
7627
  const picked = props.selected === cell.key;
7587
7628
  const label = `${cell.key}\n${t("ov.heat.sessions", { n: cell.entry.sessions })}\n${t("ov.heat.steps", { n: cell.entry.requests })}`;
7588
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
7589
- label,
7590
- side: "top",
7591
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7592
- type: "button",
7593
- className: `lc-heat-cell lc-heat-${String(level)}${picked ? " lc-heat-on" : ""}`,
7594
- "aria-label": label,
7595
- "aria-pressed": picked,
7596
- onClick: () => {
7597
- if (props.onSelect !== void 0) props.onSelect(picked ? null : cell.key);
7598
- }
7599
- })
7629
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7630
+ type: "button",
7631
+ className: `lc-heat-cell lc-heat-${String(level)}${picked ? " lc-heat-on" : ""}`,
7632
+ "data-lc-tip": label,
7633
+ "aria-label": label,
7634
+ "aria-pressed": picked,
7635
+ onClick: () => {
7636
+ if (props.onSelect !== void 0) props.onSelect(picked ? null : cell.key);
7637
+ }
7600
7638
  }, cell.key);
7601
7639
  })]
7602
7640
  }, wi);
@@ -7724,6 +7762,156 @@ window.__ModuleLoader__.load({
7724
7762
  };
7725
7763
  }
7726
7764
  //#endregion
7765
+ //#region src/client/components/hoverTip.tsx
7766
+ /**
7767
+ * The dashboard's hover tips — one fixed bubble, portaled to <body>, driven
7768
+ * by event delegation off the page root.
7769
+ *
7770
+ * Why not the harness Tooltip: the Insights page is the `lc-ov` query
7771
+ * container, and inline-size containment makes the page the containing block
7772
+ * for FIXED descendants — an in-tree fixed bubble (the harness Tooltip's
7773
+ * unportaled spelling) would paint sidebar-width away from its anchor, while
7774
+ * the primitive's `portal` prop exists only on 0.1.7+. The plugin already
7775
+ * portals its own fixed overlays for exactly this trap (the image lightbox,
7776
+ * base.css), so the page's tips follow that precedent and stay correct on
7777
+ * every supported line.
7778
+ *
7779
+ * Anchors carry `data-lc-tip` (the label) and optional `data-lc-tip-side`
7780
+ * ('top' default, 'bottom'); the zone delegates `mouseover`/`mouseout` and
7781
+ * focus/blur capture, so anchors stay plain elements — no per-cell wrapper
7782
+ * components. One bubble at a time: it re-anchors on anchor switch, hides on
7783
+ * anchor leave, on any zone scroll, on a viewport resize (every anchor just
7784
+ * moved), and on unmount; while shown it sets the anchor's
7785
+ * `aria-describedby`.
7786
+ *
7787
+ * @module dsh-context/client/components/hoverTip
7788
+ */
7789
+ /** The bubble's identity for the anchor's `aria-describedby` (one live bubble at a time). */
7790
+ const TIP_ID = "lc-hovertip-bubble";
7791
+ /** Anchor-to-bubble distance and the viewport margin the bubble clamps inside, in px. */
7792
+ const GAP = 6;
7793
+ const VIEWPORT_MARGIN = 8;
7794
+ /** The nearest tip anchor of an event target, if any. */
7795
+ function anchorOf(target) {
7796
+ return target instanceof Element ? target.closest("[data-lc-tip]") : null;
7797
+ }
7798
+ function tipOf(anchor) {
7799
+ const label = anchor.getAttribute("data-lc-tip");
7800
+ if (label === null || label === "") return null;
7801
+ const r = anchor.getBoundingClientRect();
7802
+ return {
7803
+ anchor,
7804
+ label,
7805
+ side: anchor.getAttribute("data-lc-tip-side") === "bottom" ? "bottom" : "top",
7806
+ rect: {
7807
+ left: r.left,
7808
+ top: r.top,
7809
+ width: r.width,
7810
+ height: r.height,
7811
+ bottom: r.bottom
7812
+ }
7813
+ };
7814
+ }
7815
+ /**
7816
+ * The zone hook: stable delegated handlers plus the portaled bubble. The
7817
+ * bubble renders hidden until the layout effect measures it, so it never
7818
+ * flashes at an unmeasured spot.
7819
+ */
7820
+ function useHoverTip() {
7821
+ const [tip, setTip] = (0, react.useState)(null);
7822
+ const [measured, setMeasured] = (0, react.useState)(null);
7823
+ const bubbleRef = (0, react.useRef)(null);
7824
+ const show = (0, react.useCallback)((anchor) => {
7825
+ setTip((current) => {
7826
+ if (current?.anchor === anchor) return current;
7827
+ current?.anchor.removeAttribute("aria-describedby");
7828
+ const next = tipOf(anchor);
7829
+ if (next === null) return null;
7830
+ anchor.setAttribute("aria-describedby", TIP_ID);
7831
+ return next;
7832
+ });
7833
+ }, []);
7834
+ const hide = (0, react.useCallback)(() => {
7835
+ setTip((current) => {
7836
+ current?.anchor.removeAttribute("aria-describedby");
7837
+ return null;
7838
+ });
7839
+ }, []);
7840
+ const onMouseOver = (0, react.useCallback)((ev) => {
7841
+ const anchor = anchorOf(ev.target);
7842
+ if (anchor !== null) show(anchor);
7843
+ }, [show]);
7844
+ const onMouseOut = (0, react.useCallback)((ev) => {
7845
+ if (anchorOf(ev.target) !== null && anchorOf(ev.relatedTarget) === null) hide();
7846
+ }, [hide]);
7847
+ const onFocusCapture = (0, react.useCallback)((ev) => {
7848
+ const anchor = anchorOf(ev.target);
7849
+ if (anchor !== null) show(anchor);
7850
+ }, [show]);
7851
+ const onBlurCapture = (0, react.useCallback)((ev) => {
7852
+ if (anchorOf(ev.target) !== null && anchorOf(ev.relatedTarget) === null) hide();
7853
+ }, [hide]);
7854
+ const onScrollCapture = (0, react.useCallback)(() => {
7855
+ hide();
7856
+ }, [hide]);
7857
+ (0, react.useEffect)(() => {
7858
+ if (tip === null) return void 0;
7859
+ const retract = () => {
7860
+ hide();
7861
+ };
7862
+ window.addEventListener("resize", retract);
7863
+ return () => {
7864
+ window.removeEventListener("resize", retract);
7865
+ };
7866
+ }, [tip, hide]);
7867
+ (0, react.useLayoutEffect)(() => {
7868
+ if (tip === null || measured?.tip === tip) return;
7869
+ const el = bubbleRef.current;
7870
+ /* v8 ignore next -- the portal commits its ref before layout effects run,
7871
+ * so a null ref here is unreachable; kept as a guard so a future portal
7872
+ * change cannot crash the page. */
7873
+ if (el === null) return;
7874
+ const w = el.offsetWidth;
7875
+ const h = el.offsetHeight;
7876
+ const { rect } = tip;
7877
+ const left = Math.max(VIEWPORT_MARGIN, Math.min(rect.left + rect.width / 2 - w / 2, window.innerWidth - w - VIEWPORT_MARGIN));
7878
+ let top = tip.side === "top" ? rect.top - h - GAP : rect.bottom + GAP;
7879
+ if (tip.side === "top" && top < VIEWPORT_MARGIN) top = rect.bottom + GAP;
7880
+ else if (tip.side === "bottom" && top + h > window.innerHeight - VIEWPORT_MARGIN) top = rect.top - h - GAP;
7881
+ setMeasured({
7882
+ tip,
7883
+ left,
7884
+ top: Math.max(VIEWPORT_MARGIN, top)
7885
+ });
7886
+ }, [tip, measured]);
7887
+ const shown = measured !== null && measured.tip === tip ? measured : null;
7888
+ return {
7889
+ zoneProps: {
7890
+ onMouseOver,
7891
+ onMouseOut,
7892
+ onFocusCapture,
7893
+ onBlurCapture,
7894
+ onScrollCapture
7895
+ },
7896
+ bubble: tip === null ? null : (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7897
+ ref: bubbleRef,
7898
+ id: TIP_ID,
7899
+ role: "tooltip",
7900
+ className: "lc-tip lc-hovertip",
7901
+ style: shown === null ? {
7902
+ left: 0,
7903
+ top: 0,
7904
+ visibility: "hidden"
7905
+ } : {
7906
+ left: shown.left,
7907
+ top: shown.top,
7908
+ opacity: 1
7909
+ },
7910
+ children: tip.label
7911
+ }), document.body)
7912
+ };
7913
+ }
7914
+ //#endregion
7727
7915
  //#region src/client/components/sliceList.tsx
7728
7916
  /**
7729
7917
  * The stats cards' slice rows: the donut's legend with numbers, two tiers
@@ -7834,26 +8022,6 @@ window.__ModuleLoader__.load({
7834
8022
  }
7835
8023
  //#endregion
7836
8024
  //#region src/client/components/overviewUsage.tsx
7837
- /**
7838
- * The Context Insights panel's first-row usage chart — the RIGHT half of the
7839
- * KPI row. Seven day columns ending today, each pairing two bars priced off
7840
- * the merged daily ledger (overview.ts aggregateDays): billed TOKENS (the
7841
- * heatmap's own blue) and the day's estimated COST (green). The fee rides
7842
- * the same per-day pricing records the host fold books at each turn's
7843
- * initiation, priced by the same book and estimator as the KPI band's cost
7844
- * cell — never a whole session's spend dumped on the day it was last
7845
- * active. The two series scale on their OWN window maxima (a $2 week must
7846
- * not flatten under a 169M-token week), so each bar reads as a share of its
7847
- * series' peak; the tooltip carries the exact figures. The window is fixed
7848
- * to the last 7 days — the range selector above scopes the KPI figures, not
7849
- * this calendar.
7850
- *
7851
- * The plot is framed like a chart: hairline gridlines at 0/25/50/75/100% of
7852
- * each series' own peak, a solid baseline at 0, and a tick rail on each
7853
- * side — TOKENS on the left, COST on the right, computed from the same
7854
- * maxima the bars scale to. The cost rail reads '—' until some day actually
7855
- * prices.
7856
- */
7857
8025
  /** The chart's window: today and the six days before it, oldest first. */
7858
8026
  function usageWindowOf(today) {
7859
8027
  const columns = [];
@@ -7934,21 +8102,18 @@ window.__ModuleLoader__.load({
7934
8102
  const tokens = c.entry?.tokens ?? 0;
7935
8103
  const cost = c.entry?.cost ?? null;
7936
8104
  const label = `${c.key}\n${t("ov.usage.tokens")} ${fmt(tokens)}\n${t("ov.usage.costTip")} ${cost === null ? "—" : formatCost(cost, props.currency)}`;
7937
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
7938
- label,
7939
- side: "top",
7940
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7941
- className: "lc-ov-usage-col",
7942
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7943
- className: "lc-ov-usage-bars",
7944
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7945
- className: "lc-ov-usage-bar lc-ov-usage-tokens",
7946
- style: { height: heightOf(tokens, maxTokens) }
7947
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7948
- className: "lc-ov-usage-bar lc-ov-usage-cost",
7949
- style: { height: cost === null ? "0%" : heightOf(cost, maxCost) }
7950
- })]
7951
- })
8105
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8106
+ className: "lc-ov-usage-col",
8107
+ "data-lc-tip": label,
8108
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8109
+ className: "lc-ov-usage-bars",
8110
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8111
+ className: "lc-ov-usage-bar lc-ov-usage-tokens",
8112
+ style: { height: heightOf(tokens, maxTokens) }
8113
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8114
+ className: "lc-ov-usage-bar lc-ov-usage-cost",
8115
+ style: { height: cost === null ? "0%" : heightOf(cost, maxCost) }
8116
+ })]
7952
8117
  })
7953
8118
  }, c.key);
7954
8119
  })
@@ -8440,27 +8605,37 @@ window.__ModuleLoader__.load({
8440
8605
  //#endregion
8441
8606
  //#region src/client/components/overviewPanel.tsx
8442
8607
  /**
8443
- * The Context Dashboard panel — the cross-session insight surface opened
8444
- * from the sidebar foot (overviewButton.tsx). Rendered from the frame-wide
8445
- * `shell.overlay` slot behind the module store's flag; data rides the
8446
- * root-scope `useSessions` standard kit (every list row's host-cached
8447
- * projection values), so the panel draws every session's insight without
8448
- * opening one log.
8608
+ * The Context Insights page — the plugin's first-level panel, opened from
8609
+ * the sidebar's panel list (insightPage.ts), a sibling of the shipped
8610
+ * Plugins and Automation tasks pages. Registered on the layout's keyed
8611
+ * `main` slot, so the shell's center column mounts it when the panel is
8612
+ * selected and unmounts it on leave — the page's mount IS its open, and its
8613
+ * data rides the root-scope `useSessions`/`useWorkspaces` standard kit
8614
+ * (every list row's host-cached projection values), so the page draws every
8615
+ * session's insight without opening one log.
8616
+ *
8617
+ * The chrome mirrors the shipped first-level pages: one scroll region whose
8618
+ * centered content column (the Plugins page's 960px frame) opens with the
8619
+ * page heading — the title beside the range group (and the DeepSeek balance
8620
+ * capsule when it serves) — and the page element is the `lc-ov` query
8621
+ * container, so every fold keys off the pane's own width. Hover tips portal
8622
+ * to <body> (hoverTip.tsx) because the container would otherwise capture
8623
+ * their fixed positioning.
8449
8624
  *
8450
- * The panel's first row is a 1:1 column pair: the KPI metrics band (the
8625
+ * The body's first row is a 1:1 column pair: the KPI metrics band (the
8451
8626
  * range's six figures — sessions, billed tokens, cost, cache hit, tool
8452
8627
  * calls, active time — three per row) beside the last-7-days usage chart
8453
- * (each day's billed tokens and estimated cost as a bar pair, folded off
8454
- * the merged daily ledger). Below it, the aggregate stats row folds the
8455
- * same range's
8456
- * sessions into the two donut cards the per-session Context tab opens with —
8457
- * Token Stats (the composition-split billed volume) and Timing Stats (the
8458
- * summed totals). The body is then a 3:7 column pair: the insight column (the
8459
- * activity heatmap, then the preferences entry row) beside the session column
8460
- * (search, group chips, and the card grid); the heatmap keeps its own fixed
8461
- * 8-week window and PINs the list to a picked day (the panel's drill-down
8462
- * gesture). A session card click jumps to that session through the harness's
8463
- * own selection verb (openSessionVia) and closes the panel.
8628
+ * (each day's billed tokens and estimated cost as a bar pair, folded off the
8629
+ * merged daily ledger). Below it, the aggregate stats row folds the same
8630
+ * range's sessions into the two donut cards the per-session Context tab
8631
+ * opens with — Token Stats (the composition-split billed volume) and Timing
8632
+ * Stats (the summed totals). The body is then a 3:7 column pair: the
8633
+ * insight column (the activity heatmap, then the preferences entry row)
8634
+ * beside the session column (search, group chips, and the card grid); the
8635
+ * heatmap keeps its own fixed 8-week window and PINs the list to a picked
8636
+ * day (the page's drill-down gesture). A session card click jumps to that
8637
+ * session through the harness's own selection verb (openSessionVia), whose
8638
+ * navigation returns the center column to the conversation.
8464
8639
  */
8465
8640
  const RANGES = [
8466
8641
  "24h",
@@ -8491,7 +8666,6 @@ window.__ModuleLoader__.load({
8491
8666
  return (typeof locale.getLocale === "function" ? locale.getLocale().active : "en") === "zh" ? "cny" : "usd";
8492
8667
  }
8493
8668
  function OverviewBody(props) {
8494
- const open = (0, react.useSyncExternalStore)(overviewStore.subscribe, overviewStore.getSnapshot);
8495
8669
  const { book } = useModelPrices();
8496
8670
  const snapshot = sessionsSnapshotOf(props);
8497
8671
  const wsSnapshot = workspacesSnapshotOf(props);
@@ -8502,18 +8676,13 @@ window.__ModuleLoader__.load({
8502
8676
  const [sort, setSort] = (0, react.useState)("recent");
8503
8677
  const [metric, setMetric] = (0, react.useState)("steps");
8504
8678
  const [page, setPage] = (0, react.useState)(0);
8505
- const close = () => {
8506
- overviewStore.set(false);
8507
- };
8508
- useEscapeClose(open, close);
8679
+ const { zoneProps, bubble } = useHoverTip();
8509
8680
  const rows = (0, react.useMemo)(() => rowsOfSnapshot(snapshot, wsSnapshot), [snapshot, wsSnapshot]);
8510
8681
  const groups = (0, react.useMemo)(() => sessionGroupsOf(wsSnapshot), [wsSnapshot]);
8511
8682
  (0, react.useEffect)(() => {
8512
- if (open) {
8513
- requestActivityBackfill();
8514
- refreshSessions(ctx);
8515
- }
8516
- }, [open]);
8683
+ requestActivityBackfill();
8684
+ refreshSessions(ctx);
8685
+ }, []);
8517
8686
  (0, react.useEffect)(() => {
8518
8687
  setPage(0);
8519
8688
  }, [
@@ -8523,7 +8692,6 @@ window.__ModuleLoader__.load({
8523
8692
  group,
8524
8693
  sort
8525
8694
  ]);
8526
- if (!open) return null;
8527
8695
  const currency = activeCurrency();
8528
8696
  const now = Date.now();
8529
8697
  const allRows = rows ?? [];
@@ -8548,55 +8716,41 @@ window.__ModuleLoader__.load({
8548
8716
  const days = aggregateDays(allRows, book, currency);
8549
8717
  const openOne = (id) => {
8550
8718
  openSessionVia(ctx, id);
8551
- overviewStore.set(false);
8552
8719
  };
8553
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8554
- className: "lc-ov-backdrop",
8555
- onClick: close,
8556
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8557
- className: "lc-ov-card",
8558
- onClick: (ev) => {
8559
- ev.stopPropagation();
8560
- },
8561
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8562
- className: "lc-ov-head",
8563
- children: [
8564
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextIcon, {
8565
- size: 18,
8566
- className: "lc-ov-head-icon"
8567
- }),
8568
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8569
- className: "lc-ov-title",
8570
- children: t("ov.title")
8571
- }),
8572
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BalanceCapsule, {}),
8573
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8574
- className: "lc-gran lc-ov-range",
8575
- role: "group",
8576
- "aria-label": t("ov.range.label"),
8577
- children: RANGES.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8578
- type: "button",
8579
- className: "lc-gran-btn" + (range === r ? " lc-gran-on" : ""),
8580
- onClick: () => {
8581
- setRange(r);
8582
- },
8583
- children: t("ov.range." + r)
8584
- }, r))
8585
- }),
8586
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8587
- type: "button",
8588
- className: "lc-modal-close hover:text-(--dsw-alias-label-primary) hover:bg-(--dsw-alias-bg-layer-2)",
8589
- "aria-label": t("cmd.close"),
8590
- onClick: close,
8591
- children: "×"
8592
- })
8593
- ]
8594
- }), rows === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8595
- className: "lc-empty",
8596
- children: t("ov.unavailable")
8597
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8598
- className: "lc-ov-scroll",
8599
- children: [
8720
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8721
+ className: "lc-ov-page",
8722
+ "aria-label": t("ov.title"),
8723
+ ...zoneProps,
8724
+ children: [bubble, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8725
+ className: "lc-ov-pagescroll",
8726
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8727
+ className: "lc-ov-pagecontent",
8728
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8729
+ className: "lc-ov-pagehead",
8730
+ children: [
8731
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
8732
+ className: "lc-ov-pagetitle",
8733
+ children: t("ov.title")
8734
+ }),
8735
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BalanceCapsule, {}),
8736
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8737
+ className: "lc-gran lc-ov-range",
8738
+ role: "group",
8739
+ "aria-label": t("ov.range.label"),
8740
+ children: RANGES.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8741
+ type: "button",
8742
+ className: "lc-gran-btn" + (range === r ? " lc-gran-on" : ""),
8743
+ onClick: () => {
8744
+ setRange(r);
8745
+ },
8746
+ children: t("ov.range." + r)
8747
+ }, r))
8748
+ })
8749
+ ]
8750
+ }), rows === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8751
+ className: "lc-empty",
8752
+ children: t("ov.unavailable")
8753
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
8600
8754
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8601
8755
  className: "lc-ov-first",
8602
8756
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -8764,7 +8918,6 @@ window.__ModuleLoader__.load({
8764
8918
  className: "lc-ov-settings",
8765
8919
  onClick: () => {
8766
8920
  openPluginSettings();
8767
- close();
8768
8921
  },
8769
8922
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8770
8923
  className: "lc-ov-settings-label",
@@ -8897,9 +9050,9 @@ window.__ModuleLoader__.load({
8897
9050
  ]
8898
9051
  })]
8899
9052
  })
8900
- ]
8901
- })]
8902
- })
9053
+ ] })]
9054
+ })
9055
+ })]
8903
9056
  });
8904
9057
  }
8905
9058
  return function OverviewPanel(props) {
@@ -8915,7 +9068,7 @@ window.__ModuleLoader__.load({
8915
9068
  //#endregion
8916
9069
  //#region src/client/components/settingsCard.tsx
8917
9070
  /**
8918
- * The dsh-context preference cards — two seats over the same seven rows. The
9071
+ * The dsh-context preference cards — two seats over the same eight rows. The
8919
9072
  * settings-section card (`settings.plugin.item`, the older harness lines)
8920
9073
  * renders a collapsible list item in Settings → Plugins → Plugin
8921
9074
  * configuration; the Plugins-page card (`plugins.bundle.config`, the
@@ -9034,6 +9187,21 @@ window.__ModuleLoader__.load({
9034
9187
  set?.("defaultTrendMode", id);
9035
9188
  }
9036
9189
  }),
9190
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
9191
+ label: t("settings.durationCurve"),
9192
+ value: state.durationCurve,
9193
+ disabled,
9194
+ options: [{
9195
+ id: "show",
9196
+ label: t("insightsEntry.show")
9197
+ }, {
9198
+ id: "hide",
9199
+ label: t("insightsEntry.hide")
9200
+ }],
9201
+ onPick: (id) => {
9202
+ set?.("defaultDurationCurve", id);
9203
+ }
9204
+ }),
9037
9205
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
9038
9206
  label: t("settings.deltaBase"),
9039
9207
  value: state.deltaBase,
@@ -9171,6 +9339,97 @@ window.__ModuleLoader__.load({
9171
9339
  };
9172
9340
  }
9173
9341
  //#endregion
9342
+ //#region src/client/insightPage.ts
9343
+ /**
9344
+ * The Context Insights page — the plugin's first-level panel, a sibling of
9345
+ * the shipped Plugins and Automation tasks pages.
9346
+ *
9347
+ * Two registrations, one identity:
9348
+ * - the full page itself on the layout's root keyed `main` slot, under the
9349
+ * panel key — the shell's center column renders it whenever the panel is
9350
+ * selected (keyed entryKey switching mounts it on entry and unmounts it
9351
+ * on leave, so the page's mount IS its open);
9352
+ * - the sidebar entry on the root list `sidebar.panellist` under the same
9353
+ * id — the shell owns the row (button, label, active state, and the
9354
+ * click that selects the panel), the plugin contributes only the glyph
9355
+ * and the localized label thunk.
9356
+ *
9357
+ * Both seats ship on every supported line (the `main` conversation panel and
9358
+ * the panellist contract are present since 0.1.5-rc.1 — the compat matrix
9359
+ * pins both spellings per baseline), so the registrations ride plain
9360
+ * `slots.inject` declaration injects, like the conversation tab's.
9361
+ *
9362
+ * The per-user `insightsEntry` preference gates the pair: 'show' mounts both
9363
+ * registrations, 'hide' unwinds them (a hidden entry leaves the page no
9364
+ * opener). The watcher mirrors placement.ts's preference-glued mounting;
9365
+ * each mount owns its disposers so a preference flip takes down exactly what
9366
+ * it drops.
9367
+ *
9368
+ * @module dsh-context/client/insightPage
9369
+ */
9370
+ /**
9371
+ * The id shared by the sidebar entry and the `main` panel it opens (the
9372
+ * layout's MainPanelId domain) — namespaced, like the right-Sidebar kind, so
9373
+ * no shipped or foreign panel collides with it.
9374
+ */
9375
+ const INSIGHT_PANEL_ID = "dsh-context";
9376
+ /**
9377
+ * Mount the page and its sidebar entry while the preference shows them.
9378
+ * @param ctx - client root context carrying `slots` and the locale service.
9379
+ * @param settings - the preferences store; the gate re-reads on every publish.
9380
+ * @param page - the Insights page component (the root-scope standard kit
9381
+ * arrives on its props, as on every root seat).
9382
+ * @param t - the plugin-namespace translate; the label thunk reads it at call
9383
+ * time, so a language switch relabels the sidebar row.
9384
+ * @param ns - the plugin's locale namespace, put on both registrations so the
9385
+ * framework synthesizes the `t` seat for the page too.
9386
+ * @returns the watcher's disposer (unsubscribes and unwinds any live mount).
9387
+ */
9388
+ function watchInsightPage(ctx, settings, page, t, ns) {
9389
+ let mounted = false;
9390
+ const disposers = [];
9391
+ const own = (result) => {
9392
+ if (typeof result === "function") disposers.push(result);
9393
+ };
9394
+ const mount = () => {
9395
+ own(ctx.slots.inject("main", () => ctx.slots.register({
9396
+ name: "main",
9397
+ key: INSIGHT_PANEL_ID,
9398
+ locale: ns
9399
+ }, page)));
9400
+ own(ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
9401
+ name: "sidebar.panellist",
9402
+ id: INSIGHT_PANEL_ID,
9403
+ order: 20,
9404
+ label: () => t("ov.title"),
9405
+ locale: ns
9406
+ }, (props) => (0, react.createElement)(InsightPanelIcon, { size: typeof props.size === "number" ? props.size : void 0 }))));
9407
+ };
9408
+ const unmount = () => {
9409
+ while (disposers.length > 0) disposers.pop()?.();
9410
+ };
9411
+ const applyEntry = (entry) => {
9412
+ if (entry === "show" && !mounted) {
9413
+ mounted = true;
9414
+ mount();
9415
+ } else if (entry === "hide" && mounted) {
9416
+ mounted = false;
9417
+ unmount();
9418
+ }
9419
+ };
9420
+ applyEntry(settings.insightsEntry());
9421
+ const unsubscribe = settings.store.subscribe(() => {
9422
+ applyEntry(settings.insightsEntry());
9423
+ });
9424
+ return () => {
9425
+ unsubscribe();
9426
+ if (mounted) {
9427
+ mounted = false;
9428
+ unmount();
9429
+ }
9430
+ };
9431
+ }
9432
+ //#endregion
9174
9433
  //#region src/client/settings.ts
9175
9434
  function prefsOf(value) {
9176
9435
  if (value === null || typeof value !== "object") return {};
@@ -9182,7 +9441,8 @@ window.__ModuleLoader__.load({
9182
9441
  ...v.defaultDeltaBase === "step" || v.defaultDeltaBase === "turn" ? { deltaBase: v.defaultDeltaBase } : {},
9183
9442
  ...v.defaultToolSort === "size" || v.defaultToolSort === "count" || v.defaultToolSort === "name" ? { toolSort: v.defaultToolSort } : {},
9184
9443
  ...v.defaultFileSort === "count" || v.defaultFileSort === "latest" || v.defaultFileSort === "path" ? { fileSort: v.defaultFileSort } : {},
9185
- ...v.insightsEntry === "show" || v.insightsEntry === "hide" ? { insightsEntry: v.insightsEntry } : {}
9444
+ ...v.insightsEntry === "show" || v.insightsEntry === "hide" ? { insightsEntry: v.insightsEntry } : {},
9445
+ ...v.defaultDurationCurve === "show" || v.defaultDurationCurve === "hide" ? { durationCurve: v.defaultDurationCurve } : {}
9186
9446
  };
9187
9447
  }
9188
9448
  function createContextSettings() {
@@ -9195,12 +9455,13 @@ window.__ModuleLoader__.load({
9195
9455
  toolSort: "count",
9196
9456
  fileSort: "count",
9197
9457
  insightsEntry: "show",
9458
+ durationCurve: "show",
9198
9459
  writable: false
9199
9460
  };
9200
9461
  let scope;
9201
9462
  const listeners = /* @__PURE__ */ new Set();
9202
9463
  const publish = (next) => {
9203
- if (next.status === state.status && next.placement === state.placement && next.granularity === state.granularity && next.mode === state.mode && next.deltaBase === state.deltaBase && next.toolSort === state.toolSort && next.fileSort === state.fileSort && next.insightsEntry === state.insightsEntry && next.writable === state.writable) return;
9464
+ if (next.status === state.status && next.placement === state.placement && next.granularity === state.granularity && next.mode === state.mode && next.deltaBase === state.deltaBase && next.toolSort === state.toolSort && next.fileSort === state.fileSort && next.insightsEntry === state.insightsEntry && next.durationCurve === state.durationCurve && next.writable === state.writable) return;
9204
9465
  state = next;
9205
9466
  for (const listener of listeners) listener();
9206
9467
  };
@@ -9217,6 +9478,7 @@ window.__ModuleLoader__.load({
9217
9478
  toolSort: prefs.toolSort ?? state.toolSort,
9218
9479
  fileSort: prefs.fileSort ?? state.fileSort,
9219
9480
  insightsEntry: prefs.insightsEntry ?? (raw?.insightsEntry === void 0 ? state.insightsEntry : "show"),
9481
+ durationCurve: prefs.durationCurve ?? (raw?.defaultDurationCurve === void 0 ? state.durationCurve : "show"),
9220
9482
  writable: snap.writable
9221
9483
  });
9222
9484
  return {
@@ -9241,6 +9503,7 @@ window.__ModuleLoader__.load({
9241
9503
  defaultToolSort: () => state.toolSort,
9242
9504
  defaultFileSort: () => state.fileSort,
9243
9505
  insightsEntry: () => state.insightsEntry,
9506
+ defaultDurationCurve: () => state.durationCurve,
9244
9507
  attach(bound) {
9245
9508
  scope = bound;
9246
9509
  sync(bound);
@@ -11404,7 +11667,7 @@ window.__ModuleLoader__.load({
11404
11667
  return function PluginInfo() {
11405
11668
  const [latest, setLatest] = (0, react.useState)(null);
11406
11669
  (0, react.useEffect)(() => {
11407
- if ("0.62.3".includes("-dev")) return;
11670
+ if ("0.64.0".includes("-dev")) return;
11408
11671
  let on = true;
11409
11672
  fetchLatestVersion().then((v) => {
11410
11673
  if (on && v) setLatest(v);
@@ -11413,8 +11676,8 @@ window.__ModuleLoader__.load({
11413
11676
  on = false;
11414
11677
  };
11415
11678
  }, []);
11416
- const update = latest !== null && isNewerVersion(latest, "0.62.3") ? latest : null;
11417
- const nameText = "dsh-context (v0.62.3)";
11679
+ const update = latest !== null && isNewerVersion(latest, "0.64.0") ? latest : null;
11680
+ const nameText = "dsh-context (v0.64.0)";
11418
11681
  const nameValue = [nameText];
11419
11682
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11420
11683
  className: "lc-pi-update",
@@ -12379,6 +12642,7 @@ window.__ModuleLoader__.load({
12379
12642
  const [trendMode, setTrendMode] = (0, react.useState)(() => settings.defaultTrendMode());
12380
12643
  const [dna, setDna] = (0, react.useState)(false);
12381
12644
  const [adaptive, setAdaptive] = (0, react.useState)(false);
12645
+ const [durationCurve, setDurationCurve] = (0, react.useState)(() => settings.defaultDurationCurve() === "show");
12382
12646
  const [focusTurn, setFocusTurn] = (0, react.useState)(null);
12383
12647
  const [jumpSeq, setJumpSeq] = (0, react.useState)(null);
12384
12648
  const [hoverCat, setHoverCat] = (0, react.useState)(null);
@@ -12619,36 +12883,39 @@ window.__ModuleLoader__.load({
12619
12883
  className: "lc-card-title-text",
12620
12884
  children: t("trend.title")
12621
12885
  }),
12622
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12623
- className: "lc-gran lc-trend-dna",
12624
- role: "group",
12625
- title: t("trend.dnaTip"),
12626
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12627
- type: "button",
12628
- className: "lc-gran-btn" + (dna ? " lc-gran-on" : ""),
12629
- onClick: () => {
12630
- setDna((v) => !v);
12631
- },
12632
- children: t("trend.dna")
12633
- })
12634
- }),
12635
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12636
- className: "lc-gran lc-trend-adaptive",
12886
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
12887
+ className: "lc-gran lc-trend-mods",
12637
12888
  role: "group",
12638
- title: t("trend.adaptiveHint"),
12639
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12640
- type: "button",
12641
- className: "lc-gran-btn" + (adaptive ? " lc-gran-on" : ""),
12642
- onClick: () => {
12643
- setAdaptive((on) => !on);
12644
- },
12645
- children: t("trend.adaptive")
12646
- })
12889
+ children: [
12890
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12891
+ type: "button",
12892
+ className: "lc-gran-btn" + (dna ? " lc-gran-on" : ""),
12893
+ title: t("trend.dnaTip"),
12894
+ onClick: () => {
12895
+ setDna((v) => !v);
12896
+ },
12897
+ children: t("trend.dna")
12898
+ }),
12899
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12900
+ type: "button",
12901
+ className: "lc-gran-btn" + (adaptive ? " lc-gran-on" : ""),
12902
+ title: t("trend.adaptiveHint"),
12903
+ onClick: () => {
12904
+ setAdaptive((on) => !on);
12905
+ },
12906
+ children: t("trend.adaptive")
12907
+ }),
12908
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
12909
+ type: "button",
12910
+ className: "lc-gran-btn" + (durationCurve ? " lc-gran-on" : ""),
12911
+ title: t("trend.durationTip"),
12912
+ onClick: () => {
12913
+ setDurationCurve((on) => !on);
12914
+ },
12915
+ children: t("trend.duration")
12916
+ })
12917
+ ]
12647
12918
  }),
12648
- focusCat !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12649
- className: "lc-card-sub",
12650
- children: t("trend.focus", { cat: kit.catLabel(focusCat) })
12651
- }) : null,
12652
12919
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12653
12920
  className: "lc-trend-ctl",
12654
12921
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -12703,6 +12970,7 @@ window.__ModuleLoader__.load({
12703
12970
  hoverCat: trendHoverCat,
12704
12971
  focusCat,
12705
12972
  adaptive,
12973
+ durationCurve,
12706
12974
  dna: dnaBands,
12707
12975
  onPickBand: revealBand,
12708
12976
  onSelect: setSelectedSeq,
@@ -13005,7 +13273,7 @@ window.__ModuleLoader__.load({
13005
13273
  }
13006
13274
  //#endregion
13007
13275
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/base.css.mjs
13008
- const css$14 = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}[data-sidebar-right-open] .lc-root,[data-sidebar-right-float-host] .lc-root{padding:12px}.lc-title-icon{flex:none}.lc-title-label{padding-right:30px}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-root .lc-card{box-sizing:border-box;margin-bottom:7px;padding:7px 8px;container:lc-card/inline-size}.lc-root .lc-cols{gap:7px;margin-bottom:7px}.lc-card-title{flex-wrap:wrap;align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl,.lc-heat-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran,.lc-heat-ctl .lc-gran,.lc-heat-ctl .lc-card-sub{margin-left:0}.lc-trend-dna,.lc-trend-adaptive{flex:none;margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-btn:disabled{opacity:.4;cursor:not-allowed}.lc-gran-btn:disabled:hover{color:var(--dsw-alias-label-secondary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-kind-n{font-variant-numeric:tabular-nums;opacity:.65;margin-left:4px}.lc-gran-on .lc-kind-n{opacity:.85}.lc-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3,0 2px 8px #0000002e);pointer-events:none;opacity:0;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:6px 10px;font-size:12px;position:absolute}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;height:320px;display:flex;overflow-y:auto}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:has(.lc-stats){flex-direction:column;display:flex}.lc-head .lc-stats{flex:1;align-content:stretch}.lc-head .lc-stat{justify-content:center}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:4px 14px;font-size:12px}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])),[data-conversation-scroll]:has(.lc-root,.lc-modal-backdrop)~[data-width-handle]{display:none}";
13276
+ const css$14 = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}[data-sidebar-right-open] .lc-root,[data-sidebar-right-float-host] .lc-root{padding:12px}.lc-title-icon{flex:none}.lc-title-label{padding-right:30px}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-root .lc-card{box-sizing:border-box;margin-bottom:7px;padding:7px 8px;container:lc-card/inline-size}.lc-root .lc-cols{gap:7px;margin-bottom:7px}.lc-card-title{flex-wrap:wrap;align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl,.lc-heat-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran,.lc-heat-ctl .lc-gran,.lc-heat-ctl .lc-card-sub{margin-left:0}.lc-trend-mods{flex:none;margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-btn:disabled{opacity:.4;cursor:not-allowed}.lc-gran-btn:disabled:hover{color:var(--dsw-alias-label-secondary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-kind-n{font-variant-numeric:tabular-nums;opacity:.65;margin-left:4px}.lc-gran-on .lc-kind-n{opacity:.85}.lc-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3,0 2px 8px #0000002e);pointer-events:none;opacity:0;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:6px 10px;font-size:12px;position:absolute}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;height:320px;display:flex;overflow-y:auto}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:has(.lc-stats){flex-direction:column;display:flex}.lc-head .lc-stats{flex:1;align-content:stretch}.lc-head .lc-stat{justify-content:center}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:4px 14px;font-size:12px}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])),[data-conversation-scroll]:has(.lc-root,.lc-modal-backdrop)~[data-width-handle]{display:none}";
13009
13277
  const tagId$14 = "dsh-context/base.css";
13010
13278
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$14) + "]") === null) {
13011
13279
  const tag = document.createElement("style");
@@ -13016,7 +13284,7 @@ window.__ModuleLoader__.load({
13016
13284
  }
13017
13285
  //#endregion
13018
13286
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/stats.css.mjs
13019
- const css$13 = ".lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}a.lc-stat{color:inherit;cursor:pointer;text-decoration:none}a.lc-stat:hover{border-color:var(--dsw-alias-label-secondary)}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-timing-tps{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);font-variant-numeric:tabular-nums;cursor:default;border-radius:999px;flex:none;margin-left:auto;padding:1px 8px;font-size:12px;line-height:18px}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-band{justify-content:space-between;gap:2rem;display:flex}.lc-stat-tip-band i{font-style:normal}.lc-stat-tip-by{color:var(--dsw-alias-label-secondary);margin-top:3px;display:block}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:color-mix(in srgb, var(--color-neutral-500) 18%, transparent)}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:4px 20px;min-width:0;display:grid;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-hint{text-decoration:none}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-labelicon{vertical-align:-2px;margin-right:4px;display:inline-block}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-tstrip{margin-top:10px}.lc-tstrip-wrap{position:relative}.lc-tstrip-bar{background:color-mix(in srgb, var(--color-neutral-500) 18%, transparent);border-radius:4px;width:100%;height:12px;position:relative;overflow:hidden}.lc-tstrip-seg{transform-origin:0;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);position:absolute;top:0}.lc-tstrip-seg-on{filter:brightness(1.18)}.lc-tstrip-dim .lc-tstrip-seg{opacity:.35}.lc-tstrip-dim .lc-tstrip-seg-on{opacity:1}.lc-tstrip-axis{height:13px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;margin-top:5px;font-size:10px;line-height:13px;position:relative}.lc-tstrip-tick{white-space:nowrap;position:absolute;transform:translate(-50%)}.lc-tstrip-tick:before{content:\"\";background:var(--dsw-alias-border-l1);width:1px;height:3px;position:absolute;top:-4px;left:50%}.lc-tstrip-tick-first{transform:none}.lc-tstrip-tick-first:before{left:0}.lc-tstrip-tick-last{transform:translate(-100%)}.lc-tstrip-tick-last:before{left:auto;right:0}";
13287
+ const css$13 = ".lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}a.lc-stat{color:inherit;cursor:pointer;text-decoration:none}a.lc-stat:hover{border-color:var(--dsw-alias-label-secondary)}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-timing-tps{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);font-variant-numeric:tabular-nums;cursor:default;border-radius:999px;flex:none;margin-left:auto;padding:1px 8px;font-size:12px;line-height:18px}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-band{justify-content:space-between;gap:2rem;display:flex}.lc-stat-tip-band i{font-style:normal}.lc-stat-tip-by{color:var(--dsw-alias-label-secondary);margin-top:3px;display:block}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:color-mix(in srgb, var(--color-neutral-500) 18%, transparent)}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{grid-template-columns:repeat(auto-fill,minmax(min(150px,100%),1fr));gap:4px 8px;min-width:0;display:grid;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:30px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-hint{text-decoration:none}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-labelicon{vertical-align:-2px;margin-right:4px;display:inline-block}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-tstrip{margin-top:10px}.lc-tstrip-wrap{position:relative}.lc-tstrip-bar{background:color-mix(in srgb, var(--color-neutral-500) 18%, transparent);border-radius:4px;width:100%;height:12px;position:relative;overflow:hidden}.lc-tstrip-seg{transform-origin:0;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);position:absolute;top:0}.lc-tstrip-seg-on{filter:brightness(1.18)}.lc-tstrip-dim .lc-tstrip-seg{opacity:.35}.lc-tstrip-dim .lc-tstrip-seg-on{opacity:1}.lc-tstrip-axis{height:13px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;margin-top:5px;font-size:10px;line-height:13px;position:relative}.lc-tstrip-tick{white-space:nowrap;position:absolute;transform:translate(-50%)}.lc-tstrip-tick:before{content:\"\";background:var(--dsw-alias-border-l1);width:1px;height:3px;position:absolute;top:-4px;left:50%}.lc-tstrip-tick-first{transform:none}.lc-tstrip-tick-first:before{left:0}.lc-tstrip-tick-last{transform:translate(-100%)}.lc-tstrip-tick-last:before{left:auto;right:0}";
13020
13288
  const tagId$13 = "dsh-context/stats.css";
13021
13289
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
13022
13290
  const tag = document.createElement("style");
@@ -13060,7 +13328,7 @@ window.__ModuleLoader__.load({
13060
13328
  }
13061
13329
  //#endregion
13062
13330
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/trendChart.css.mjs
13063
- const css$9 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding:18px 2px 0;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:color-mix(in srgb, var(--color-neutral-500) 50%, transparent);top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-chart-tip span+span{color:var(--dsw-alias-label-secondary)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:color-mix(in srgb, var(--color-neutral-500) 14%, transparent)}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-dna{border-radius:2px;width:100%}.lc-bar-stack,.lc-bar-up,.lc-bar-down,.lc-bar-dna{transform-origin:50% 100%}.lc-bar-down{transform-origin:50% 0}.lc-bar-stack>div:first-child{border-radius:0 0 2px 2px}.lc-bar-stack>div:last-child{border-radius:2px 2px 0 0}.lc-bar-stack>div:only-child{border-radius:2px}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-up>div:last-child{border-radius:2px 2px 0 0}.lc-bar-down>div:last-child{border-radius:0 0 2px 2px}.lc-cat-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=skill] .lc-cat-seg[data-cat=skill],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1;filter:brightness(1.18)}.lc-dna-hl{z-index:1;pointer-events:none;align-items:stretch;gap:2px;display:flex;position:absolute;inset:18px 2px 0}.lc-dna-cell{flex:none;width:14px;position:relative}.lc-dna-slice{box-sizing:border-box;border:1px solid var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 26%, transparent);border-radius:1px;width:100%;position:absolute;left:0}.lc-bar-marker{z-index:2;color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-step-flag{z-index:2;background:color-mix(in srgb, var(--color-neutral-500) 45%, transparent);pointer-events:none;width:1px;position:absolute;top:-17px;bottom:0;right:-1px}.lc-step-flag-label{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--color-neutral-500) 45%, transparent);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:3px;padding:1px 3px;font-size:9px;font-weight:600;line-height:1;position:absolute;top:0;right:0}.lc-turns{box-sizing:border-box;gap:2px;width:max-content;min-width:100%;margin-top:4px;padding:0 2px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}";
13331
+ const css$9 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-r{flex:none;width:34px}.lc-axis-r span{left:0;right:auto}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding:18px 2px 0;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:color-mix(in srgb, var(--color-neutral-500) 50%, transparent);top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-chart-tip span+span{color:var(--dsw-alias-label-secondary)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:color-mix(in srgb, var(--color-neutral-500) 14%, transparent)}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-dna{border-radius:2px;width:100%}.lc-bar-stack,.lc-bar-up,.lc-bar-down,.lc-bar-dna{transform-origin:50% 100%}.lc-bar-down{transform-origin:50% 0}.lc-bar-stack>div:first-child{border-radius:0 0 2px 2px}.lc-bar-stack>div:last-child{border-radius:2px 2px 0 0}.lc-bar-stack>div:only-child{border-radius:2px}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-up>div:last-child{border-radius:2px 2px 0 0}.lc-bar-down>div:last-child{border-radius:0 0 2px 2px}.lc-cat-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=skill] .lc-cat-seg[data-cat=skill],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1}.lc-dna-hl{z-index:1;pointer-events:none;align-items:stretch;gap:2px;display:flex;position:absolute;inset:18px 2px 0}.lc-dna-cell{flex:none;width:14px;position:relative}.lc-dna-slice{box-sizing:border-box;border:1px solid var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 26%, transparent);border-radius:1px;width:100%;position:absolute;left:0}.lc-duration{z-index:1;pointer-events:none;position:absolute;top:18px;left:2px;overflow:visible}.lc-duration polyline,.lc-duration circle{fill:none;stroke:var(--dsw-alias-brand-primary);stroke-width:1.5px;stroke-linejoin:round;stroke-linecap:round}.lc-duration circle{fill:var(--dsw-alias-brand-primary)}.lc-chart-dim .lc-duration{opacity:.35}.lc-bar-marker{z-index:2;color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-step-flag{z-index:2;background:color-mix(in srgb, var(--color-neutral-500) 45%, transparent);pointer-events:none;width:1px;position:absolute;top:-17px;bottom:0;right:-1px}.lc-step-flag-label{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--color-neutral-500) 45%, transparent);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:3px;padding:1px 3px;font-size:9px;font-weight:600;line-height:1;position:absolute;top:0;right:0}.lc-turns{box-sizing:border-box;gap:2px;width:max-content;min-width:100%;margin-top:4px;padding:0 2px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}";
13064
13332
  const tagId$9 = "dsh-context/trendChart.css";
13065
13333
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$9) + "]") === null) {
13066
13334
  const tag = document.createElement("style");
@@ -13159,7 +13427,7 @@ window.__ModuleLoader__.load({
13159
13427
  }
13160
13428
  //#endregion
13161
13429
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/overview.css.mjs
13162
- const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);height:calc(100% - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:inherit;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex}.lc-ov-balance:hover .lc-ov-balance-value{text-decoration:underline}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-scroll{scrollbar-gutter:stable;flex:auto;min-height:0;padding-right:8px;overflow-y:auto}.lc-ov-first{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-kpis{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;display:grid}.lc-ov-kpi{align-items:center;padding:8px}.lc-ov-kpi .lc-stat-value{font-size:20px}@container lc-ov (width<=932px){.lc-ov-first{grid-template-columns:minmax(0,1fr)}}@container lc-ov (width<=640px){.lc-ov-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}}.lc-ov-usage{box-sizing:border-box;flex-direction:column;flex:auto;min-width:0;margin-bottom:0;display:flex}.lc-ov-usage .lc-card-title{margin-bottom:4px}.lc-ov-usage-plot{flex:auto;align-items:stretch;gap:6px;min-height:0;display:flex}.lc-ov-usage-rail{width:42px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:9px;line-height:1.2;position:relative}.lc-ov-usage-rail-l .lc-ov-usage-tick{right:0}.lc-ov-usage-rail-r .lc-ov-usage-tick{left:0}.lc-ov-usage-tick{white-space:nowrap;position:absolute;transform:translateY(-50%)}.lc-ov-usage-grid{flex:auto;min-width:0;min-height:56px;display:flex;position:relative}.lc-ov-usage-line{background:color-mix(in srgb, var(--dsw-alias-border-l2) 70%, transparent);height:1px;position:absolute;left:0;right:0}.lc-ov-usage-line-0{background:var(--dsw-alias-border-l2)}.lc-ov-usage-cols{flex:auto;align-items:stretch;gap:6px;min-width:0;display:flex;position:relative}.lc-ov-usage-col{flex-direction:column;flex:1 1 0;min-width:0;display:flex}.lc-ov-usage-bars{flex:auto;justify-content:center;align-items:flex-end;gap:3px;width:100%;display:flex}.lc-ov-usage-bar{border-radius:3px 3px 0 0;flex:none;width:10px}.lc-ov-usage-tokens{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-ov-usage-cost{background:color-mix(in srgb, var(--color-green-500) 72%, transparent)}.lc-ov-usage-days{flex:none;gap:6px;padding-top:3px;display:flex}.lc-ov-usage-day{text-align:center;min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;flex:1 1 0;font-size:9px;line-height:1.2}.lc-ov-usage-key{flex:none;justify-content:flex-end;align-items:center;gap:5px;padding-top:5px;display:flex}.lc-ov-usage-swatch{border-radius:2px;flex:none;width:10px;height:8px}.lc-ov-usage-key-label{color:var(--dsw-alias-label-secondary);font-size:9px}.lc-ov-stats{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-stats>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}@container lc-ov (width<=700px){.lc-ov-stats{grid-template-columns:minmax(0,1fr)}}.lc-ov-body{grid-template-columns:minmax(0,3fr) minmax(0,7fr);align-items:start;gap:10px;display:grid}.lc-ov-left{flex-direction:column;gap:10px;min-width:0;display:flex}.lc-ov-right{min-width:0}@container lc-ov (width<=932px){.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-heat-card{flex-direction:column;display:flex}.lc-ov-settings{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex:none;align-items:center;gap:8px;padding:9px 12px;font-size:12px;display:flex}.lc-ov-settings:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.lc-ov-settings-label{white-space:nowrap;flex:none;align-items:center;gap:6px;display:inline-flex}.lc-ov-settings-hint{text-align:right;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-heat-wrap{flex-direction:column;flex:1;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:8px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:18px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{height:20px;color:var(--dsw-alias-label-secondary);text-align:center;white-space:nowrap;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:18px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-heat-legend{justify-content:flex-end;align-items:center;gap:3px;padding-top:6px;display:flex}.lc-heat-key{color:var(--dsw-alias-label-secondary);font-size:9px}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
13430
+ const css = ".lc-ov-page{box-sizing:border-box;background:var(--dsw-alias-bg-base);width:100%;min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);flex-direction:column;font-size:13px;display:flex;overflow:hidden;container:lc-ov/inline-size}.lc-ov-pagescroll{scrollbar-gutter:stable;flex:auto;min-height:0;overflow-y:auto}.lc-ov-pagecontent{max-width:960px;margin:0 auto;padding:0 clamp(24px,4vw,48px) 48px}.lc-ov-pagehead{flex-wrap:wrap;align-items:center;gap:16px;margin-bottom:24px;padding-top:28px;display:flex}[data-platform=darwin] .lc-ov-pagehead{padding-top:calc(28px + var(--dsh-frame-top-clearance,0px))}.lc-ov-pagetitle{flex:auto;min-width:0;margin:0;font-size:20px;font-weight:500;line-height:28px}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:inherit;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex}.lc-ov-balance:hover .lc-ov-balance-value{text-decoration:underline}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-hovertip{z-index:300;white-space:pre-line;max-width:min(320px,50vw);position:fixed}.lc-ov-first{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-kpis{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;display:grid}.lc-ov-kpi{align-items:center;padding:8px}.lc-ov-kpi .lc-stat-value{font-size:20px}@container lc-ov (width<=932px){.lc-ov-first{grid-template-columns:minmax(0,1fr)}}@container lc-ov (width<=640px){.lc-ov-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}}.lc-ov-usage{box-sizing:border-box;flex-direction:column;flex:auto;min-width:0;margin-bottom:0;display:flex}.lc-ov-usage .lc-card-title{margin-bottom:4px}.lc-ov-usage-plot{flex:auto;align-items:stretch;gap:6px;min-height:0;display:flex}.lc-ov-usage-rail{width:42px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:9px;line-height:1.2;position:relative}.lc-ov-usage-rail-l .lc-ov-usage-tick{right:0}.lc-ov-usage-rail-r .lc-ov-usage-tick{left:0}.lc-ov-usage-tick{white-space:nowrap;position:absolute;transform:translateY(-50%)}.lc-ov-usage-grid{flex:auto;min-width:0;min-height:56px;display:flex;position:relative}.lc-ov-usage-line{background:color-mix(in srgb, var(--dsw-alias-border-l2) 70%, transparent);height:1px;position:absolute;left:0;right:0}.lc-ov-usage-line-0{background:var(--dsw-alias-border-l2)}.lc-ov-usage-cols{flex:auto;align-items:stretch;gap:6px;min-width:0;display:flex;position:relative}.lc-ov-usage-col{flex-direction:column;flex:1 1 0;min-width:0;display:flex}.lc-ov-usage-bars{flex:auto;justify-content:center;align-items:flex-end;gap:3px;width:100%;display:flex}.lc-ov-usage-bar{border-radius:3px 3px 0 0;flex:none;width:10px}.lc-ov-usage-tokens{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-ov-usage-cost{background:color-mix(in srgb, var(--color-green-500) 72%, transparent)}.lc-ov-usage-days{flex:none;gap:6px;padding-top:3px;display:flex}.lc-ov-usage-day{text-align:center;min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;flex:1 1 0;font-size:9px;line-height:1.2}.lc-ov-usage-key{flex:none;justify-content:flex-end;align-items:center;gap:5px;padding-top:5px;display:flex}.lc-ov-usage-swatch{border-radius:2px;flex:none;width:10px;height:8px}.lc-ov-usage-key-label{color:var(--dsw-alias-label-secondary);font-size:9px}.lc-ov-stats{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-stats>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}@container lc-ov (width<=700px){.lc-ov-stats{grid-template-columns:minmax(0,1fr)}}.lc-ov-body{grid-template-columns:minmax(0,3fr) minmax(0,7fr);align-items:start;gap:10px;display:grid}.lc-ov-left{flex-direction:column;gap:10px;min-width:0;display:flex}.lc-ov-right{min-width:0}@container lc-ov (width<=932px){.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-heat-card{flex-direction:column;display:flex}.lc-ov-settings{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex:none;align-items:center;gap:8px;padding:9px 12px;font-size:12px;display:flex}.lc-ov-settings:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.lc-ov-settings-label{white-space:nowrap;flex:none;align-items:center;gap:6px;display:inline-flex}.lc-ov-settings-hint{text-align:right;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-heat-wrap{flex-direction:column;flex:1;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:8px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:18px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{height:20px;color:var(--dsw-alias-label-secondary);text-align:center;white-space:nowrap;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:18px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-heat-legend{justify-content:flex-end;align-items:center;gap:3px;padding-top:6px;display:flex}.lc-heat-key{color:var(--dsw-alias-label-secondary);font-size:9px}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
13163
13431
  const tagId = "dsh-context/overview.css";
13164
13432
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
13165
13433
  const tag = document.createElement("style");
@@ -13238,24 +13506,8 @@ window.__ModuleLoader__.load({
13238
13506
  inject: (sessionId = "") => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
13239
13507
  }, (props) => (0, react.createElement)(ContextModal, props));
13240
13508
  });
13241
- const OverviewButton = makeOverviewButton(kit, settings);
13242
- ctx.slots.inject("sidebar.footer.action", () => {
13243
- return ctx.slots.register({
13244
- name: "sidebar.footer.action",
13245
- id: "context-overview",
13246
- order: 10,
13247
- locale: NS
13248
- }, (props) => (0, react.createElement)(OverviewButton, props));
13249
- });
13250
13509
  const OverviewPanel = makeOverviewPanel(ctx, kit);
13251
- ctx.slots.inject("shell.overlay", () => {
13252
- return ctx.slots.register({
13253
- name: "shell.overlay",
13254
- id: "context-overview",
13255
- order: 10,
13256
- locale: NS
13257
- }, (props) => (0, react.createElement)(OverviewPanel, props));
13258
- });
13510
+ ctx.effect(() => watchInsightPage(ctx, settings, (props) => (0, react.createElement)(OverviewPanel, props), t, NS), "dsh-context: insight page");
13259
13511
  /** The injected face both preference cards ride: the settings store as the
13260
13512
  * framework's hooks-compartment `useContextSettings` seat, plus the set verb. */
13261
13513
  const cardFace = () => ({