dsh-context 0.46.0 → 0.47.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
@@ -10,6 +10,7 @@ window.__ModuleLoader__.load({
10
10
  //#region src/client/i18n.ts
11
11
  const DICT_ZH = {
12
12
  "tab": "上下文",
13
+ "sidebar.guideDescription": "查看这个会话的上下文构成、每步变化与消息列表。",
13
14
  "cat.system": "系统提示词",
14
15
  "cat.tools": "工具定义",
15
16
  "cat.user": "用户消息",
@@ -72,8 +73,9 @@ window.__ModuleLoader__.load({
72
73
  "gran.total": "全量",
73
74
  "gran.delta": "增量",
74
75
  "gran.modeHint": "全量:累计构成;增量:相对上一条请求的变化量",
75
- "trend.hint": "✂ 表示压缩/剪枝,步骤/轮次 切换粒度",
76
76
  "trend.focus": "仅展示 {cat}",
77
+ "trend.adaptive": "自适应",
78
+ "trend.adaptiveHint": "按当前可视范围内的柱子重新缩放高度,滚动时随之变化",
77
79
  "trend.empty": "发起一轮对话后,这里会展示每次模型请求的上下文构成",
78
80
  "detail.step": "第 {t} 轮 · 第 {s} 步",
79
81
  "detail.turn": "第 {t} 轮 · 共 {n} 步",
@@ -89,7 +91,6 @@ window.__ModuleLoader__.load({
89
91
  "brief.noInputs": "(无新增)",
90
92
  "brief.locate": "在上下文浏览器中查看",
91
93
  "jump.title": "在上下文标签页中查看此轮",
92
- "jump.statsLine": "在上下文标签页中查看上下文构成",
93
94
  "brief.turnTip": "这一轮开场时用户发送的消息;回合内的每一步都会回显同一条",
94
95
  "brief.inputTip": "上一步回复之后、本次请求发出前新进入上下文的内容——通常是上一步所调工具的结果;每轮的第一步无新增",
95
96
  "brief.replyTip": "这一步模型返回的内容(文本回复或工具调用);此步发起的调用,其结果会出现在下一柱的「输入」行",
@@ -296,6 +297,7 @@ window.__ModuleLoader__.load({
296
297
  };
297
298
  const DICT_EN = {
298
299
  "tab": "Context",
300
+ "sidebar.guideDescription": "Inspect this session's context composition, per-step history, and messages.",
299
301
  "cat.system": "System Prompt",
300
302
  "cat.tools": "Tool Schemas",
301
303
  "cat.user": "User Messages",
@@ -358,8 +360,9 @@ window.__ModuleLoader__.load({
358
360
  "gran.total": "Total",
359
361
  "gran.delta": "Delta",
360
362
  "gran.modeHint": "Total: cumulative makeup; Delta: change vs the previous request",
361
- "trend.hint": "✂ marks compaction/prune, Step/Turn switches granularity",
362
363
  "trend.focus": "Showing {cat} only",
364
+ "trend.adaptive": "Adaptive",
365
+ "trend.adaptiveHint": "Rescale bar heights to the visible range, following the scroll",
363
366
  "trend.empty": "Send a message and each model request’s context makeup shows up here",
364
367
  "detail.step": "Turn {t} · Step {s}",
365
368
  "detail.turn": "Turn {t} · {n} steps",
@@ -375,7 +378,6 @@ window.__ModuleLoader__.load({
375
378
  "brief.noInputs": "(nothing new)",
376
379
  "brief.locate": "Reveal in Context Browser",
377
380
  "jump.title": "View this turn in the Context tab",
378
- "jump.statsLine": "View the context composition in the Context tab",
379
381
  "brief.turnTip": "The user message that opened this turn — every step of the turn recalls it",
380
382
  "brief.inputTip": "What entered after the previous reply and before this request went out — usually the previous tool-call results; a turn opener has nothing new",
381
383
  "brief.replyTip": "What the model returned on this step (a text reply or tool calls) — results of calls made here appear in the In row of the next bar",
@@ -954,7 +956,7 @@ window.__ModuleLoader__.load({
954
956
  "assistant",
955
957
  "tool",
956
958
  "total"
957
- ].every((k) => typeof current[k] === "number") && recordsOnly(data.requests) && recordsOnly(data.events) && recordsOnly(data.nodes) && recordsOnly(data.archive) && timingFastOk(data.timing)) return data;
959
+ ].every((k) => typeof current[k] === "number") && recordsOnly(data.requests) && recordsOnly(data.events) && recordsOnly(data.nodes) && recordsOnly(data.archive) && systemsFastOk(data.systems) && timingFastOk(data.timing)) return data;
958
960
  const safeCurrent = current !== null && typeof current === "object" ? current : {};
959
961
  const cost = typeof data.cost === "object" && data.cost !== null && !Array.isArray(data.cost) ? data.cost : void 0;
960
962
  const timing = timingOf(data.timing);
@@ -988,6 +990,7 @@ window.__ModuleLoader__.load({
988
990
  ...typeof data.detailRev === "number" && Number.isFinite(data.detailRev) ? { detailRev: data.detailRev } : {},
989
991
  ...cost !== void 0 ? { cost } : {},
990
992
  ...timing !== null ? { timing } : {},
993
+ ...data.systems !== void 0 ? { systems: systemsOf(data.systems) } : {},
991
994
  ...typeof data.surfaceFloor === "number" ? { surfaceFloor: data.surfaceFloor } : {},
992
995
  ...typeof data.archiveFloor === "number" ? { archiveFloor: data.archiveFloor } : {},
993
996
  ...data.fileOps !== void 0 ? { fileOps: objectsOf(data.fileOps) } : {},
@@ -995,6 +998,45 @@ window.__ModuleLoader__.load({
995
998
  };
996
999
  }
997
1000
  /**
1001
+ * The live system-prompt nodes, re-proved per entry and sorted by seq: an
1002
+ * entry missing a finite seq/time/tokens drops out (the browser then falls
1003
+ * back to the header epoch), so a hostile collection can never produce a NaN
1004
+ * prompt figure or an unfetchable seq. Absent or empty stays absent.
1005
+ */
1006
+ function systemsOf(value) {
1007
+ const list = objectsOf(value);
1008
+ const out = [];
1009
+ for (const entry of list) {
1010
+ const { seq, time, tokens } = entry;
1011
+ if (typeof seq !== "number" || !Number.isFinite(seq)) continue;
1012
+ if (typeof time !== "number" || !Number.isFinite(time)) continue;
1013
+ if (typeof tokens !== "number" || !Number.isFinite(tokens)) continue;
1014
+ out.push({
1015
+ seq,
1016
+ time,
1017
+ tokens
1018
+ });
1019
+ }
1020
+ return out.sort((a, b) => a.seq - b.seq);
1021
+ }
1022
+ /**
1023
+ * The fast path's check for the live system-prompt nodes: every entry must
1024
+ * carry the three finite numbers the browser reads — `seq` for the per-step
1025
+ * resolution, `time` for the DNA band, `tokens` for its width. A primitive
1026
+ * entry, or one whose fields are not numbers, sends the payload down the
1027
+ * sanitizing slow path (`systemsOf` drops it) instead of leaking `undefined`
1028
+ * into the bar math. An absent list is fine.
1029
+ */
1030
+ function systemsFastOk(value) {
1031
+ if (value === void 0) return true;
1032
+ if (!Array.isArray(value)) return false;
1033
+ return value.every((entry) => {
1034
+ if (entry === null || typeof entry !== "object") return false;
1035
+ const { seq, time, tokens } = entry;
1036
+ return typeof seq === "number" && Number.isFinite(seq) && typeof time === "number" && Number.isFinite(time) && typeof tokens === "number" && Number.isFinite(tokens);
1037
+ });
1038
+ }
1039
+ /**
998
1040
  * The split head's count figures, re-proved field by field: a present-but-
999
1041
  * partial record zeroes its unreadable fields (the stats board's no-NaN
1000
1042
  * guarantee), an absent or non-record value stays absent (legacy generation
@@ -1323,6 +1365,25 @@ window.__ModuleLoader__.load({
1323
1365
  return out.trim() === "" ? null : out;
1324
1366
  }
1325
1367
  /**
1368
+ * The system prompt's exact rendered text — every text block joined with NO
1369
+ * normalization: a whitespace-only prompt is still the prompt the model
1370
+ * received (the host prices it as text), so it must render rather than read
1371
+ * as absent. Null when the content carries no text block at all.
1372
+ */
1373
+ function systemTextOf(blocks) {
1374
+ if (!Array.isArray(blocks)) return null;
1375
+ let out = "";
1376
+ let seen = false;
1377
+ for (const b of blocks) {
1378
+ const text = b !== null && typeof b === "object" ? b.text : void 0;
1379
+ if (typeof text === "string") {
1380
+ out += text;
1381
+ seen = true;
1382
+ }
1383
+ }
1384
+ return seen ? out : null;
1385
+ }
1386
+ /**
1326
1387
  * One assistant content block → the snapshot block vocabulary the browser
1327
1388
  * already renders (`kind`: text/reasoning/image/tool-call); unmappable
1328
1389
  * blocks pass through raw and degrade to the generic JSON section.
@@ -1548,13 +1609,23 @@ window.__ModuleLoader__.load({
1548
1609
  };
1549
1610
  }
1550
1611
  /**
1551
- * Map one raw `request/header` event into the epoch content the browser
1552
- * renders the full system prompt text plus each tool's producer
1553
- * description and raw schema, mirroring the host fold's per-entry guards
1554
- * (a null or primitive tool entry degrades to an unnamed row instead of
1555
- * throwing the read). Null when the envelope carries no usable header.
1612
+ * Map one raw durable event into the epoch content the browser renders. A
1613
+ * `request/header` yields the full system prompt text (V0/V2 envelope) plus
1614
+ * each tool's producer description and raw schema; a V3 `system/message`
1615
+ * yields the prompt text alone (its tools live in the request header). Both
1616
+ * mirror the host fold's per-entry guards a null or primitive tool entry
1617
+ * degrades to an unnamed row instead of throwing the read. Null when the
1618
+ * envelope carries neither.
1556
1619
  */
1557
- function headerContentOf(data) {
1620
+ function headerContentOf(event) {
1621
+ const { type, data } = event;
1622
+ if (type === "system/message") {
1623
+ const system = systemTextOf((data.message !== null && typeof data.message === "object" ? data.message : null)?.content);
1624
+ return system === null ? null : {
1625
+ system,
1626
+ tools: []
1627
+ };
1628
+ }
1558
1629
  const rawHeader = data.header !== null && typeof data.header === "object" ? data.header : null;
1559
1630
  if (rawHeader === null) return null;
1560
1631
  const toolsRaw = Array.isArray(rawHeader.tools) ? rawHeader.tools : [];
@@ -1573,15 +1644,15 @@ window.__ModuleLoader__.load({
1573
1644
  };
1574
1645
  }
1575
1646
  /**
1576
- * The on-demand CONTENT fetch for `contextHeaders` epochs the lazy
1577
- * counterpart of the node fetcher above. One seq-anchored history read off
1578
- * the epoch's `seq` returns the page holding that epoch's `request/header`
1579
- * event (non-message events ride the page verbatim); the raw header is
1580
- * mapped client-side into the renderable content. Epochs cache per session
1581
- * (history is immutable), and OLDER epochs sharing the page cache for free —
1582
- * stepping back through epochs walks the same pages. Undefined when no
1583
- * history face exists — the browser keeps a metadata-only degradation
1584
- * instead.
1647
+ * The on-demand CONTENT fetch for the browser's System and Tools sections —
1648
+ * the lazy counterpart of the node fetcher above. One seq-anchored history
1649
+ * read off the requested seq returns the page holding that event (non-message
1650
+ * events ride the page verbatim); a `request/header` maps to the epoch's
1651
+ * tools (and its V0/V2 system text), a `system/message` to a V3 prompt's
1652
+ * text. Landed content caches per seq (history is immutable), and OLDER
1653
+ * epochs sharing the page cache for free stepping back through epochs walks
1654
+ * the same pages. Undefined when no history face exists — the browser keeps a
1655
+ * metadata-only degradation instead.
1585
1656
  */
1586
1657
  function makeHeaderFetcher(sessionId) {
1587
1658
  const read = pageReaderOf(sessionId);
@@ -1594,8 +1665,8 @@ window.__ModuleLoader__.load({
1594
1665
  let picked = null;
1595
1666
  for (const entry of rows) {
1596
1667
  const ev = eventOf(entry);
1597
- if (ev === null || ev.type !== "request/header") continue;
1598
- const content = headerContentOf(ev.data);
1668
+ if (ev === null || ev.type !== "request/header" && ev.type !== "system/message") continue;
1669
+ const content = headerContentOf(ev);
1599
1670
  if (content === null) continue;
1600
1671
  cache.set(ev.seq, content);
1601
1672
  if (ev.seq === seq) picked = content;
@@ -1872,6 +1943,30 @@ window.__ModuleLoader__.load({
1872
1943
  for (let i = headers.headers.length - 1; i >= 0; i--) if (headers.headers[i].seq < seq) return headers.headers[i];
1873
1944
  return null;
1874
1945
  }
1946
+ /**
1947
+ * The system prompt in force at `seq` (null = none) — the LAST live system
1948
+ * node at or before it carrying tokens, which is exactly the host fold's
1949
+ * "last nonempty surviving system" rule and therefore agrees with the
1950
+ * per-step `system` figure the fold recorded. Rows folded before `systems`
1951
+ * existed carry none; the header epoch's envelope figure stands in for them
1952
+ * (the pre-V3 wire shape).
1953
+ */
1954
+ function systemAt(data, header, seq) {
1955
+ const systems = data.systems;
1956
+ if (systems !== void 0 && systems.length > 0) {
1957
+ for (let i = systems.length - 1; i >= 0; i--) {
1958
+ const node = systems[i];
1959
+ if (node.tokens <= 0) continue;
1960
+ if (seq === null || node.seq < seq) return node;
1961
+ }
1962
+ return null;
1963
+ }
1964
+ return header !== null && header.systemTokens !== void 0 ? {
1965
+ seq: header.seq,
1966
+ time: header.time,
1967
+ tokens: header.systemTokens
1968
+ } : null;
1969
+ }
1875
1970
  function assemble(data, headers, seq) {
1876
1971
  const live = seq === null;
1877
1972
  let nodes;
@@ -1888,9 +1983,11 @@ window.__ModuleLoader__.load({
1888
1983
  if (live || data.surfaceFloor !== void 0 && seq > data.surfaceFloor) missingLive = data.droppedNodes;
1889
1984
  }
1890
1985
  const approximate = !live && data.archiveFloor !== void 0 && seq < data.archiveFloor;
1986
+ const header = headerAt(headers, seq);
1891
1987
  return {
1892
1988
  live,
1893
- header: headerAt(headers, seq),
1989
+ header,
1990
+ system: systemAt(data, header, seq),
1894
1991
  nodes,
1895
1992
  missingLive,
1896
1993
  approximate
@@ -1900,19 +1997,17 @@ window.__ModuleLoader__.load({
1900
1997
  //#region src/client/dna.ts
1901
1998
  function dnaOf(view) {
1902
1999
  const items = [];
1903
- if (view.header !== null) {
1904
- if (view.header.systemTokens !== void 0) items.push({
1905
- key: "sys",
1906
- cat: "system",
1907
- tokens: view.header.systemTokens,
1908
- time: view.header.time
1909
- });
1910
- for (const tool of view.header.tools) items.push({
1911
- key: "tool:" + tool.name,
1912
- cat: "tools",
1913
- tokens: tool.tokens
1914
- });
1915
- }
2000
+ if (view.system !== null) items.push({
2001
+ key: "sys",
2002
+ cat: "system",
2003
+ tokens: view.system.tokens,
2004
+ time: view.system.time
2005
+ });
2006
+ if (view.header !== null) for (const tool of view.header.tools) items.push({
2007
+ key: "tool:" + tool.name,
2008
+ cat: "tools",
2009
+ tokens: tool.tokens
2010
+ });
1916
2011
  for (const n of view.nodes) items.push({
1917
2012
  key: "n" + String(n.seq),
1918
2013
  cat: n.cat,
@@ -3217,7 +3312,7 @@ window.__ModuleLoader__.load({
3217
3312
  return m;
3218
3313
  }
3219
3314
  function countOf(asm, byCat, c) {
3220
- if (c === "system") return asm.header !== null && asm.header.systemTokens !== void 0 ? 1 : 0;
3315
+ if (c === "system") return asm.system !== null ? 1 : 0;
3221
3316
  if (c === "tools") return asm.header !== null ? asm.header.tools.length : 0;
3222
3317
  return byCat[c]?.length ?? 0;
3223
3318
  }
@@ -3302,7 +3397,8 @@ window.__ModuleLoader__.load({
3302
3397
  const view = assemble(data, headers, seq);
3303
3398
  const fetchHeader = props.fetchHeader;
3304
3399
  const headerSeq = view.header !== null ? view.header.seq : null;
3305
- const epoch = useFetchOnMiss(headerSeq !== null && (openCat === "system" || openCat === "tools") ? headerSeq : null, fetchHeader, "dsh-context: header content fetch failed");
3400
+ const systemSeq = view.system !== null ? view.system.seq : null;
3401
+ const epoch = useFetchOnMiss(openCat === "system" ? systemSeq : openCat === "tools" ? headerSeq : null, fetchHeader, "dsh-context: header content fetch failed");
3306
3402
  const headerContent = epoch.values;
3307
3403
  const headerNote = fetchMissNote(t, fetchHeader, epoch.state, epoch.retry, "browser.headerMetaOnly");
3308
3404
  const breakdown = req !== null ? req : data.current;
@@ -3336,6 +3432,7 @@ window.__ModuleLoader__.load({
3336
3432
  label: dnaLabel(it),
3337
3433
  group: it.cat
3338
3434
  })) ?? null;
3435
+ const liveDnaKey = dnaKey !== null && dnaByKey.has(dnaKey) ? dnaKey : null;
3339
3436
  const pickDna = (key) => {
3340
3437
  const it = dnaByKey.get(key);
3341
3438
  /* v8 ignore next 1 -- the bar only reports keys of the parts it was handed; defensive. */
@@ -3347,7 +3444,7 @@ window.__ModuleLoader__.load({
3347
3444
  };
3348
3445
  const toolCount = (c) => countOf(view, byCat, c);
3349
3446
  const singleKeyOf = (c) => {
3350
- if (c === "system") return view.header?.systemTokens !== void 0 ? "sys" : null;
3447
+ if (c === "system") return view.system !== null ? "sys" : null;
3351
3448
  if (c === "tools") {
3352
3449
  const tools = view.header?.tools;
3353
3450
  return tools !== void 0 && tools.length === 1 ? "tool:" + tools[0].name : null;
@@ -3417,11 +3514,12 @@ window.__ModuleLoader__.load({
3417
3514
  };
3418
3515
  const catBody = (c) => {
3419
3516
  if (c === "system") {
3420
- if (view.header === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3517
+ const sys = view.system;
3518
+ if (sys === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3421
3519
  className: "lc-br-note",
3422
3520
  children: t(headers === null ? "browser.noHeader" : "browser.noEpoch")
3423
3521
  });
3424
- const content = headerContent.get(view.header.seq);
3522
+ const content = headerContent.get(sys.seq);
3425
3523
  if (content === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3426
3524
  className: "lc-br-note",
3427
3525
  children: headerNote
@@ -3638,7 +3736,7 @@ window.__ModuleLoader__.load({
3638
3736
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StackedBar, {
3639
3737
  parts: dnaParts ?? parts,
3640
3738
  height: 10,
3641
- hoverKey: dna ? dnaKey ?? linkKey ?? pinKey : linkKey ?? pinKey,
3739
+ hoverKey: dna ? liveDnaKey ?? linkKey ?? pinKey : linkKey ?? pinKey,
3642
3740
  onHoverKey: dna ? setDnaKey : linked ? props.onHoverKey : void 0,
3643
3741
  tip: dna,
3644
3742
  onPickKey: dna ? pickDna : void 0,
@@ -5158,6 +5256,35 @@ window.__ModuleLoader__.load({
5158
5256
  for (const r of requests) if (r.seq > seq && (gone === void 0 || gone > r.seq)) return r.seq;
5159
5257
  return gone === void 0 ? "live" : null;
5160
5258
  }
5259
+ //#endregion
5260
+ //#region src/client/overscroll.ts
5261
+ /**
5262
+ * Stop a horizontal scroller's overscroll from reaching the browser's history navigation — the trackpad
5263
+ * swipe-back (left edge) and swipe-forward (right edge) gestures. The spec answer is `overscroll-behavior-x:
5264
+ * contain`, the harness's own idiom for its horizontal scrollers and what the sheets beside this module's
5265
+ * callers already set; it covers Chromium and Firefox, but WebKit still navigates on horizontal overscroll with
5266
+ * the property set (bug 240183), where a canceled wheel is the only lever. Without either, one swipe past the
5267
+ * edge of the trend chart or the agent graph leaves the whole app for another page in the tab's history.
5268
+ */
5269
+ /**
5270
+ * Cancel the horizontal-dominant wheel gestures a scroller cannot consume — the ones the browser would
5271
+ * otherwise read as a history swipe. Vertical-dominant gestures are left alone: they belong to the page's own
5272
+ * scrolling, and the scrollers this guards only overflow horizontally.
5273
+ * @param el - the horizontal scroll container (the listener must be non-passive, so it cannot ride React's passive wheel seat).
5274
+ * @returns the disposer removing the listener.
5275
+ */
5276
+ function containHorizontalOverscroll(el) {
5277
+ const onWheel = (e) => {
5278
+ if (Math.abs(e.deltaX) <= Math.abs(e.deltaY)) return;
5279
+ const atStart = e.deltaX < 0 && el.scrollLeft <= 0;
5280
+ const atEnd = e.deltaX > 0 && el.scrollLeft + el.clientWidth >= el.scrollWidth - 1;
5281
+ if (atStart || atEnd) e.preventDefault();
5282
+ };
5283
+ el.addEventListener("wheel", onWheel, { passive: false });
5284
+ return () => {
5285
+ el.removeEventListener("wheel", onWheel);
5286
+ };
5287
+ }
5161
5288
  const SLOT_MAX = 184;
5162
5289
  const SLOT_MIN = 112;
5163
5290
  const CAPTION_GUTTER = 8;
@@ -5605,6 +5732,13 @@ window.__ModuleLoader__.load({
5605
5732
  };
5606
5733
  /* v8 ignore stop */
5607
5734
  }, []);
5735
+ (0, react.useEffect)(() => {
5736
+ const el = stageRef.current;
5737
+ /* v8 ignore next 1 -- the stage renders whenever the card does, and React
5738
+ attaches refs before effects run; el is never null here. */
5739
+ if (el === null) return;
5740
+ return containHorizontalOverscroll(el);
5741
+ }, []);
5608
5742
  (0, react.useEffect)(() => {
5609
5743
  if (face === null || typeof sessionId !== "string" || sessionId === "") return;
5610
5744
  if (typeof face.refreshSubagents !== "function") return;
@@ -6445,7 +6579,7 @@ window.__ModuleLoader__.load({
6445
6579
  return function PluginInfo() {
6446
6580
  const [latest, setLatest] = (0, react.useState)(null);
6447
6581
  (0, react.useEffect)(() => {
6448
- if ("0.46.0".includes("-dev")) return;
6582
+ if ("0.47.0".includes("-dev")) return;
6449
6583
  let on = true;
6450
6584
  fetchLatestVersion().then((v) => {
6451
6585
  if (on && v) setLatest(v);
@@ -6454,8 +6588,8 @@ window.__ModuleLoader__.load({
6454
6588
  on = false;
6455
6589
  };
6456
6590
  }, []);
6457
- const update = latest !== null && isNewerVersion(latest, "0.46.0") ? latest : null;
6458
- const nameText = "dsh-context (v0.46.0)";
6591
+ const update = latest !== null && isNewerVersion(latest, "0.47.0") ? latest : null;
6592
+ const nameText = "dsh-context (v0.47.0)";
6459
6593
  const nameValue = [nameText];
6460
6594
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6461
6595
  className: "lc-pi-update",
@@ -7576,6 +7710,42 @@ window.__ModuleLoader__.load({
7576
7710
  focus
7577
7711
  ]);
7578
7712
  const markers = props.markers;
7713
+ const adaptive = props.adaptive === true;
7714
+ const [visMax, setVisMax] = (0, react.useState)(null);
7715
+ const measureVisible = (el) => {
7716
+ if (!adaptive) return;
7717
+ const n = requests.length;
7718
+ if (n === 0 || el.clientWidth <= 0) return;
7719
+ const pitch = 16;
7720
+ const sl = el.scrollLeft;
7721
+ const vr = sl + el.clientWidth;
7722
+ let total = 0;
7723
+ let up = 0;
7724
+ let down = 0;
7725
+ const from = Math.max(0, Math.floor(sl / pitch));
7726
+ const to = Math.min(n - 1, Math.max(from, Math.floor((vr - 1) / pitch)));
7727
+ for (let i = from; i <= to; i++) {
7728
+ const col = i * pitch;
7729
+ if (col >= vr || col + BAR_W <= sl) continue;
7730
+ const req = requests[i];
7731
+ if (delta) {
7732
+ let bu = 0;
7733
+ let bd = 0;
7734
+ for (const c of CATS) {
7735
+ const d = req[c.key] || 0;
7736
+ if (d > 0) bu += d;
7737
+ else bd -= d;
7738
+ }
7739
+ if (bu > up) up = bu;
7740
+ if (bd > down) down = bd;
7741
+ } else if (req.total > total) total = req.total;
7742
+ }
7743
+ setVisMax((prev) => prev !== null && prev.total === total && prev.up === up && prev.down === down ? prev : {
7744
+ total,
7745
+ up,
7746
+ down
7747
+ });
7748
+ };
7579
7749
  let maxTotal = 1;
7580
7750
  let maxUp = 0;
7581
7751
  let maxDown = 0;
@@ -7591,6 +7761,14 @@ window.__ModuleLoader__.load({
7591
7761
  if (down > maxDown) maxDown = down;
7592
7762
  }
7593
7763
  else for (const req of requests) if (req.total > maxTotal) maxTotal = req.total;
7764
+ if (adaptive && visMax !== null) {
7765
+ if (delta) {
7766
+ if (visMax.up + visMax.down > 0) {
7767
+ maxUp = visMax.up;
7768
+ maxDown = visMax.down;
7769
+ }
7770
+ } else maxTotal = Math.max(1, visMax.total);
7771
+ }
7594
7772
  const span = Math.max(1, maxUp + maxDown);
7595
7773
  const deltaScale = CHART_H / span;
7596
7774
  const upPx = Math.round(maxUp * deltaScale);
@@ -7710,11 +7888,38 @@ window.__ModuleLoader__.load({
7710
7888
  prevScrollWidthRef.current = el.scrollWidth;
7711
7889
  updateTurnLabels(el);
7712
7890
  syncTip(el);
7891
+ measureVisible(el);
7713
7892
  }, [
7714
7893
  props.granularity,
7715
7894
  props.focusTurn,
7716
- requests
7895
+ requests,
7896
+ adaptive
7717
7897
  ]);
7898
+ const measureRef = (0, react.useRef)(measureVisible);
7899
+ (0, react.useLayoutEffect)(() => {
7900
+ measureRef.current = measureVisible;
7901
+ });
7902
+ (0, react.useLayoutEffect)(() => {
7903
+ const el = scrollRef.current;
7904
+ /* v8 ignore next 1 -- the scroll div renders unconditionally and React
7905
+ attaches refs before layout effects run; el is never null here. */
7906
+ if (el === null) return;
7907
+ if (typeof ResizeObserver !== "function") return;
7908
+ const observer = new ResizeObserver(() => {
7909
+ measureRef.current(el);
7910
+ });
7911
+ observer.observe(el);
7912
+ return () => {
7913
+ observer.disconnect();
7914
+ };
7915
+ }, []);
7916
+ (0, react.useLayoutEffect)(() => {
7917
+ const el = scrollRef.current;
7918
+ /* v8 ignore next 1 -- the scroll div renders unconditionally and React
7919
+ attaches refs before layout effects run; el is never null here. */
7920
+ if (el === null) return;
7921
+ return containHorizontalOverscroll(el);
7922
+ }, []);
7718
7923
  const tipRowsOf = (req) => {
7719
7924
  const n = req.stepCount ?? 1;
7720
7925
  const head = props.granularity === "turn" ? n > 1 ? t("tip.turn", {
@@ -7819,6 +8024,7 @@ window.__ModuleLoader__.load({
7819
8024
  onScroll: (e) => {
7820
8025
  updateTurnLabels(e.currentTarget);
7821
8026
  syncTip(e.currentTarget);
8027
+ measureVisible(e.currentTarget);
7822
8028
  },
7823
8029
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7824
8030
  className: "lc-chart",
@@ -7958,6 +8164,7 @@ window.__ModuleLoader__.load({
7958
8164
  const ErrorBoundary = makeErrorBoundary(t);
7959
8165
  function ContextViewBody(props) {
7960
8166
  const sessionId = props.sessionId;
8167
+ const inSidebar = props.host === "sidebar";
7961
8168
  const source = useTimelineSource(ctx, props);
7962
8169
  const data = source.data;
7963
8170
  const pressure = projectionOf(props, "contextPressure", contextPressureOf);
@@ -7969,6 +8176,7 @@ window.__ModuleLoader__.load({
7969
8176
  const [hoverTurn, setHoverTurn] = (0, react.useState)(null);
7970
8177
  const [granularity, setGranularity] = (0, react.useState)(() => settings.defaultGranularity());
7971
8178
  const [trendMode, setTrendMode] = (0, react.useState)(() => settings.defaultTrendMode());
8179
+ const [adaptive, setAdaptive] = (0, react.useState)(false);
7972
8180
  const [focusTurn, setFocusTurn] = (0, react.useState)(null);
7973
8181
  const [jumpSeq, setJumpSeq] = (0, react.useState)(null);
7974
8182
  const [hoverCat, setHoverCat] = (0, react.useState)(null);
@@ -8151,6 +8359,132 @@ window.__ModuleLoader__.load({
8151
8359
  const activeLocale = localeSvc !== void 0 && typeof localeSvc.getLocale === "function" ? localeSvc.getLocale().active : "en";
8152
8360
  const subtitle = (data.model ?? "") + (data.provider ? " · " + data.provider : "");
8153
8361
  const gate = unsupportedOf(data.unsupported);
8362
+ const compositionCard = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CurrentComposition, {
8363
+ head,
8364
+ subtitle,
8365
+ hoverKey: hoverCat,
8366
+ onHoverKey: setHoverCat
8367
+ });
8368
+ const trendCard = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8369
+ className: "lc-card",
8370
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8371
+ className: "lc-card-title",
8372
+ children: [
8373
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8374
+ className: "lc-card-title-text",
8375
+ children: t("trend.title")
8376
+ }),
8377
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8378
+ className: "lc-gran lc-trend-adaptive",
8379
+ role: "group",
8380
+ title: t("trend.adaptiveHint"),
8381
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8382
+ type: "button",
8383
+ className: "lc-gran-btn" + (adaptive ? " lc-gran-on" : ""),
8384
+ onClick: () => {
8385
+ setAdaptive((on) => !on);
8386
+ },
8387
+ children: t("trend.adaptive")
8388
+ })
8389
+ }),
8390
+ focusCat !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8391
+ className: "lc-card-sub",
8392
+ children: t("trend.focus", { cat: kit.catLabel(focusCat) })
8393
+ }) : null,
8394
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8395
+ className: "lc-trend-ctl",
8396
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8397
+ className: "lc-gran",
8398
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8399
+ className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
8400
+ onClick: () => {
8401
+ setGranularity("step");
8402
+ },
8403
+ children: t("gran.step")
8404
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8405
+ className: "lc-gran-btn" + (granularity === "turn" ? " lc-gran-on" : ""),
8406
+ onClick: () => {
8407
+ setGranularity("turn");
8408
+ },
8409
+ children: t("gran.turn")
8410
+ })]
8411
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8412
+ className: "lc-gran",
8413
+ title: t("gran.modeHint"),
8414
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8415
+ className: "lc-gran-btn" + (trendMode === "total" ? " lc-gran-on" : ""),
8416
+ onClick: () => {
8417
+ setTrendMode("total");
8418
+ },
8419
+ children: t("gran.total")
8420
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8421
+ className: "lc-gran-btn" + (trendMode === "delta" ? " lc-gran-on" : ""),
8422
+ onClick: () => {
8423
+ setTrendMode("delta");
8424
+ },
8425
+ children: t("gran.delta")
8426
+ })]
8427
+ })]
8428
+ })
8429
+ ]
8430
+ }), displayRequests.length === 0 ? detailReady ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8431
+ className: "lc-empty",
8432
+ children: t("trend.empty")
8433
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailNote, {
8434
+ state: source.detailState === "failed" ? "failed" : "loading",
8435
+ onRetry: source.retryDetail
8436
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrendChart, {
8437
+ requests: displayRequests,
8438
+ markers,
8439
+ selectedSeq: pinnedReq ? pinnedReq.seq : null,
8440
+ hoveredSeq,
8441
+ activeTurn,
8442
+ granularity,
8443
+ mode: trendMode,
8444
+ focusTurn,
8445
+ hoverCat: trendHoverCat,
8446
+ focusCat,
8447
+ adaptive,
8448
+ onSelect: setSelectedSeq,
8449
+ onHover: setHoveredSeq,
8450
+ onHoverTurn: setHoverTurn,
8451
+ onPickTurn: (turn) => {
8452
+ setGranularity("turn");
8453
+ setFocusTurn(turn);
8454
+ },
8455
+ onFocusTurnHandled: () => {
8456
+ setFocusTurn(null);
8457
+ }
8458
+ }, sessionId), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RequestDetail, {
8459
+ request: activeReq,
8460
+ prev: trendMode === "delta" && activeIdx >= 0 ? activeIdx > 0 ? displayRequests[activeIdx - 1] : null : void 0,
8461
+ /* v8 ignore next 1 -- RequestDetail renders only when
8462
+ displayRequests.length > 0, which forces activeReq
8463
+ non-null via the activeIdx fallback above. */
8464
+ marker: activeReq !== null ? markerOf(activeReq) : void 0,
8465
+ brief,
8466
+ convOf,
8467
+ onLocate: locateNode,
8468
+ hoverKey: trendHoverCat
8469
+ })] })]
8470
+ });
8471
+ const browserCard = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextBrowser, {
8472
+ data,
8473
+ headers,
8474
+ convNodes,
8475
+ fetchContent,
8476
+ fetchHeader,
8477
+ previewSeq: hoveredSeq,
8478
+ pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
8479
+ hoverKey: hoverCat,
8480
+ onHoverKey: setHoverCat,
8481
+ onOpenCat: setFocusCat,
8482
+ nodeFocus,
8483
+ onNodeFocusHandled: clearNodeFocus,
8484
+ loadImage,
8485
+ detailState: source.detailState,
8486
+ onDetailRetry: source.retryDetail
8487
+ });
8154
8488
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8155
8489
  className: "lc-root",
8156
8490
  ref: rootRef,
@@ -8158,7 +8492,7 @@ window.__ModuleLoader__.load({
8158
8492
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8159
8493
  className: "lc-cols lc-head",
8160
8494
  children: [
8161
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsContext, {
8495
+ inSidebar ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsContext, {
8162
8496
  counts: data.counts ?? countsOfRecords(requests, events),
8163
8497
  toolCalls: data.toolCalls,
8164
8498
  images: data.images,
@@ -8170,127 +8504,31 @@ window.__ModuleLoader__.load({
8170
8504
  timing: data.timing ?? null,
8171
8505
  locale: activeLocale
8172
8506
  }),
8173
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginInfo, {})
8507
+ inSidebar ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginInfo, {})
8174
8508
  ]
8175
8509
  }),
8176
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8177
- className: "lc-cols",
8178
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8510
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8511
+ className: "lc-cols lc-cols-main",
8512
+ children: inSidebar ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
8513
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8514
+ className: "lc-col",
8515
+ children: compositionCard
8516
+ }),
8517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8518
+ className: "lc-col lc-col-browser",
8519
+ children: browserCard
8520
+ }),
8521
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8522
+ className: "lc-col",
8523
+ children: trendCard
8524
+ })
8525
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8179
8526
  className: "lc-col",
8180
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CurrentComposition, {
8181
- head,
8182
- subtitle,
8183
- hoverKey: hoverCat,
8184
- onHoverKey: setHoverCat
8185
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8186
- className: "lc-card",
8187
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8188
- className: "lc-card-title",
8189
- children: [
8190
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8191
- className: "lc-card-title-text",
8192
- children: t("trend.title")
8193
- }),
8194
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8195
- className: "lc-card-sub",
8196
- children: focusCat !== null ? t("trend.focus", { cat: kit.catLabel(focusCat) }) : t("trend.hint")
8197
- }),
8198
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8199
- className: "lc-trend-ctl",
8200
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8201
- className: "lc-gran",
8202
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8203
- className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
8204
- onClick: () => {
8205
- setGranularity("step");
8206
- },
8207
- children: t("gran.step")
8208
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8209
- className: "lc-gran-btn" + (granularity === "turn" ? " lc-gran-on" : ""),
8210
- onClick: () => {
8211
- setGranularity("turn");
8212
- },
8213
- children: t("gran.turn")
8214
- })]
8215
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8216
- className: "lc-gran",
8217
- title: t("gran.modeHint"),
8218
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8219
- className: "lc-gran-btn" + (trendMode === "total" ? " lc-gran-on" : ""),
8220
- onClick: () => {
8221
- setTrendMode("total");
8222
- },
8223
- children: t("gran.total")
8224
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8225
- className: "lc-gran-btn" + (trendMode === "delta" ? " lc-gran-on" : ""),
8226
- onClick: () => {
8227
- setTrendMode("delta");
8228
- },
8229
- children: t("gran.delta")
8230
- })]
8231
- })]
8232
- })
8233
- ]
8234
- }), displayRequests.length === 0 ? detailReady ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8235
- className: "lc-empty",
8236
- children: t("trend.empty")
8237
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailNote, {
8238
- state: source.detailState === "failed" ? "failed" : "loading",
8239
- onRetry: source.retryDetail
8240
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrendChart, {
8241
- requests: displayRequests,
8242
- markers,
8243
- selectedSeq: pinnedReq ? pinnedReq.seq : null,
8244
- hoveredSeq,
8245
- activeTurn,
8246
- granularity,
8247
- mode: trendMode,
8248
- focusTurn,
8249
- hoverCat: trendHoverCat,
8250
- focusCat,
8251
- onSelect: setSelectedSeq,
8252
- onHover: setHoveredSeq,
8253
- onHoverTurn: setHoverTurn,
8254
- onPickTurn: (turn) => {
8255
- setGranularity("turn");
8256
- setFocusTurn(turn);
8257
- },
8258
- onFocusTurnHandled: () => {
8259
- setFocusTurn(null);
8260
- }
8261
- }, sessionId), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RequestDetail, {
8262
- request: activeReq,
8263
- prev: trendMode === "delta" && activeIdx >= 0 ? activeIdx > 0 ? displayRequests[activeIdx - 1] : null : void 0,
8264
- /* v8 ignore next 1 -- RequestDetail renders only when
8265
- displayRequests.length > 0, which forces activeReq
8266
- non-null via the activeIdx fallback above. */
8267
- marker: activeReq !== null ? markerOf(activeReq) : void 0,
8268
- brief,
8269
- convOf,
8270
- onLocate: locateNode,
8271
- hoverKey: trendHoverCat
8272
- })] })]
8273
- })]
8527
+ children: [compositionCard, trendCard]
8274
8528
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8275
8529
  className: "lc-col lc-col-browser",
8276
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextBrowser, {
8277
- data,
8278
- headers,
8279
- convNodes,
8280
- fetchContent,
8281
- fetchHeader,
8282
- previewSeq: hoveredSeq,
8283
- pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
8284
- hoverKey: hoverCat,
8285
- onHoverKey: setHoverCat,
8286
- onOpenCat: setFocusCat,
8287
- nodeFocus,
8288
- onNodeFocusHandled: clearNodeFocus,
8289
- loadImage,
8290
- detailState: source.detailState,
8291
- onDetailRetry: source.retryDetail
8292
- })
8293
- })]
8530
+ children: browserCard
8531
+ })] })
8294
8532
  }),
8295
8533
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8296
8534
  className: "lc-cols",
@@ -8436,122 +8674,97 @@ window.__ModuleLoader__.load({
8436
8674
  };
8437
8675
  }
8438
8676
  //#endregion
8439
- //#region src/client/components/statsJump.tsx
8440
- /**
8441
- * The chat page's stats line the harness's own StatsLine entry on the
8442
- * `conversation.composer.dock` seat — becomes a jump affordance: hovering
8443
- * underlines it, a click (or Enter/Space) opens the Context tab. The plugin
8444
- * wires this from a sibling dock entry that renders no UI of its own: an
8445
- * invisible anchor mounts inside the slot's `[data-slot]` wrapper and spots
8446
- * the stats row beside it, and a MutationObserver re-spots it on dock churn
8447
- * (the row mounts only once the first stats exist, tooltip bubbles come and
8448
- * go beside it, and switching conversations remounts the pair together).
8677
+ //#region src/client/sidebar.ts
8678
+ /**
8679
+ * The right Sidebar's Context tab (dsh 0.1.5-alpha.1+).
8449
8680
  *
8450
- * EVERY step degrades silently: a host without the dock seat never mounts
8451
- * the entry; a dock without exactly one spotless candidate row (another
8452
- * plugin's entry, a crash face, an unexpected layout) decorates nothing; a
8453
- * row that throws mid-wiring stays unwired; a drag-selection click is
8454
- * ignored. The affordance is simply absent nothing here can take the chat
8455
- * page down.
8456
- */
8457
- /** The plugin's invisible dock anchor, and the class it brands the stats row with. */
8458
- const ANCHOR_CLASS = "lc-stats-jump";
8459
- const ROW_CLASS = "lc-stats-jump-row";
8460
- /**
8461
- * The stats row beside `anchor`: the single element child that is neither
8462
- * the anchor nor a tooltip bubble. Children are untrusted input — one that
8463
- * throws on attribute access is skipped whole, and two live candidates at
8464
- * once (another plugin's dock entry, a crash face) answer null so nothing
8465
- * is ever decorated on a guess.
8466
- */
8467
- function statsRowOf(anchor) {
8468
- const parent = anchor.parentElement;
8469
- if (parent === null) return null;
8470
- let row = null;
8471
- for (const child of parent.children) {
8472
- try {
8473
- if (child === anchor || child.getAttribute("role") === "tooltip") continue;
8474
- } catch {
8475
- continue;
8476
- }
8477
- if (row !== null) return null;
8478
- row = child;
8681
+ * The tab reuses the Context conversation-view component VERBATIM: the
8682
+ * `sidebar.right.pane.tab` seat is session-scoped and delivers the same
8683
+ * framework standard kit (`sessionId`, `useProjection`, `useChat`, the locale
8684
+ * `t` seat) the `conversation.view` seat does, so the panel and the tab are
8685
+ * one component with one data path. The tab type contributes a guide entry, so
8686
+ * the sidebar's guide page offers "Context" and picking it opens the panel —
8687
+ * the product's own path, exactly as the shipped Files type does.
8688
+ *
8689
+ * OPTIONAL BY CONTRACT. `ctx.sidebarRightTabs` and the seat exist only from
8690
+ * dsh 0.1.5-alpha.1; the registration therefore rides a DEFERRED inject (the
8691
+ * plugin's hard injects stay `slots` + `locale`), so on every older supported
8692
+ * line the callback never fires, the plugin fiber never pends, and nothing is
8693
+ * registered. The registry is re-proved structurally and the whole
8694
+ * registration is guarded: a foreign or hostile registry (a throwing
8695
+ * `register`, a taken id/kind) leaves the sidebar without the tab instead of
8696
+ * taking the browser down.
8697
+ *
8698
+ * @module dsh-context/client/sidebar
8699
+ */
8700
+ /** The tab type's identity in the sidebar's tab system (also its body-seat key). */
8701
+ const SIDEBAR_CONTEXT_ID = "dsh-context";
8702
+ /**
8703
+ * The kind `openTab` names. Namespaced rather than the bare `context`: the
8704
+ * registry THROWS when a kind collides with another registration in a
8705
+ * non-coexisting band, and a foreign plugin may well own `context`.
8706
+ */
8707
+ const SIDEBAR_CONTEXT_KIND = "dsh-context";
8708
+ /** The guide box's position: after the shipped Files entry (order 10). */
8709
+ const GUIDE_ORDER = 20;
8710
+ /**
8711
+ * The guide box's glyph, read defensively: the icon is OPTIONAL in the entry,
8712
+ * and a primitives module that does not serve it — or whose namespace THROWS
8713
+ * on the read (an interop/mock shape does exactly that) — must cost the glyph,
8714
+ * never the whole tab.
8715
+ * @returns the icon component, or undefined to register the tab without one.
8716
+ */
8717
+ function guideIcon() {
8718
+ try {
8719
+ return _deepseek_ai_dsh_client_ui_primitives.IconContextInjectionOutline16;
8720
+ } catch {
8721
+ return;
8479
8722
  }
8480
- return row;
8481
8723
  }
8482
8724
  /**
8483
- * Wire the dock beside `anchor`: decorate today's stats row and re-spot it
8484
- * on dock churn. Returns the teardown that disconnects the observer and
8485
- * strips every mark the wiring left on the row.
8725
+ * Register the Context tab type and its body on the right Sidebar, if — and
8726
+ * only if this harness serves the sidebar tab registry.
8727
+ * @param ctx - client root context carrying `slots` and the locale service.
8728
+ * @param view - the Context view component factory result (the same one the
8729
+ * conversation tab mounts).
8730
+ * @param t - the plugin-namespace translate; the label thunks read the active
8731
+ * locale at call time, so a language switch relabels the guide entry.
8732
+ * @param ns - the plugin's locale namespace, put on the body registration so
8733
+ * the framework synthesizes the `t` seat for the panel too.
8486
8734
  */
8487
- function wireDock(anchor, label, activate) {
8488
- if (anchor === null) return () => {};
8489
- let wired = null;
8490
- const unwire = () => {
8491
- wired?.off();
8492
- wired = null;
8493
- };
8494
- const scan = () => {
8735
+ function watchSidebarContextTab(ctx, view, t, ns) {
8736
+ ctx.inject(["sidebarRightTabs"], (raw) => {
8737
+ const injected = raw;
8495
8738
  try {
8496
- const row = statsRowOf(anchor);
8497
- if (wired?.row === row) return;
8498
- unwire();
8499
- if (row === null || !(row instanceof HTMLElement)) return;
8500
- row.classList.add(ROW_CLASS);
8501
- row.setAttribute("role", "button");
8502
- row.setAttribute("tabindex", "0");
8503
- row.setAttribute("aria-label", label);
8504
- const onKey = (e) => {
8505
- if (e.key !== "Enter" && e.key !== " ") return;
8506
- e.preventDefault();
8507
- activate();
8508
- };
8509
- const onClick = () => {
8510
- const selection = anchor.ownerDocument.getSelection();
8511
- if (selection !== null && !selection.isCollapsed) return;
8512
- activate();
8513
- };
8514
- row.addEventListener("click", onClick);
8515
- row.addEventListener("keydown", onKey);
8516
- wired = {
8517
- row,
8518
- off() {
8519
- row.removeEventListener("click", onClick);
8520
- row.removeEventListener("keydown", onKey);
8521
- row.classList.remove(ROW_CLASS);
8522
- row.removeAttribute("role");
8523
- row.removeAttribute("tabindex");
8524
- row.removeAttribute("aria-label");
8525
- }
8739
+ const tabs = injected.sidebarRightTabs;
8740
+ if (tabs === void 0 || typeof tabs.register !== "function") return;
8741
+ const disposeType = tabs.register({
8742
+ id: SIDEBAR_CONTEXT_ID,
8743
+ kind: SIDEBAR_CONTEXT_KIND,
8744
+ title: () => t("tab"),
8745
+ guide: [{
8746
+ order: GUIDE_ORDER,
8747
+ title: () => t("tab"),
8748
+ description: () => t("sidebar.guideDescription"),
8749
+ icon: guideIcon()
8750
+ }]
8751
+ });
8752
+ const disposeBody = injected.slots.inject("sidebar.right.pane.tab", () => injected.slots.register({
8753
+ name: "sidebar.right.pane.tab",
8754
+ key: SIDEBAR_CONTEXT_ID,
8755
+ locale: ns
8756
+ }, (props) => view({
8757
+ ...props,
8758
+ host: "sidebar"
8759
+ })));
8760
+ return () => {
8761
+ disposeType();
8762
+ if (typeof disposeBody === "function") disposeBody();
8526
8763
  };
8527
- } catch {}
8528
- };
8529
- scan();
8530
- const parent = anchor.parentElement;
8531
- if (parent === null) return unwire;
8532
- const observer = new MutationObserver(scan);
8533
- observer.observe(parent, { childList: true });
8534
- return () => {
8535
- observer.disconnect();
8536
- unwire();
8537
- };
8538
- }
8539
- /** The dock entry component: the invisible anchor plus the wiring around it. */
8540
- function makeStatsJump(kit) {
8541
- const { t } = kit;
8542
- return function StatsJump() {
8543
- const anchorRef = (0, react.useRef)(null);
8544
- const label = t("jump.statsLine");
8545
- (0, react.useEffect)(() => wireDock(anchorRef.current, label, () => {
8546
- activateContextTab(t("tab"));
8547
- }), [label]);
8548
- return (0, react.createElement)("span", {
8549
- ref: anchorRef,
8550
- className: ANCHOR_CLASS,
8551
- hidden: true,
8552
- "aria-hidden": "true"
8553
- });
8554
- };
8764
+ } catch {
8765
+ return;
8766
+ }
8767
+ });
8555
8768
  }
8556
8769
  //#endregion
8557
8770
  //#region src/client/viewkit.ts
@@ -8570,7 +8783,7 @@ window.__ModuleLoader__.load({
8570
8783
  }
8571
8784
  //#endregion
8572
8785
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/base.css.mjs
8573
- const css$13 = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.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{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{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{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-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.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-col{flex:1;min-width:min(280px,100%)}.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{container:lc-head-row/inline-size}.lc-head>.lc-card{flex:1 1 0;min-width:0}@container lc-head-row (width<=1045px){.lc-head>.lc-card{flex-basis:40%}}@container lc-head-row (width<=480px){.lc-head>.lc-card{flex-basis:100%}}.lc-head>.lc-card:first-child{flex-direction:column;display:flex}.lc-head>.lc-card:first-child .lc-stats{flex:1;align-content:stretch}.lc-head>.lc-card:first-child .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-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.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}";
8786
+ const css$13 = ".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{height:auto;padding:0;overflow:visible}.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{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{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.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-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.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-col{flex:1;min-width:min(360px,100%)}.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{container:lc-head-row/inline-size}.lc-head>.lc-card{flex:1 1 0;min-width:0}@container lc-head-row (width<=1045px){.lc-head>.lc-card{flex-basis:40%}}@container lc-head-row (width<=480px){.lc-head>.lc-card{flex-basis:100%}}.lc-head>.lc-card:first-child{flex-direction:column;display:flex}.lc-head>.lc-card:first-child .lc-stats{flex:1;align-content:stretch}.lc-head>.lc-card:first-child .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-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.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}";
8574
8787
  const tagId$13 = "dsh-context/base.css";
8575
8788
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
8576
8789
  const tag = document.createElement("style");
@@ -8592,7 +8805,7 @@ window.__ModuleLoader__.load({
8592
8805
  }
8593
8806
  //#endregion
8594
8807
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/jump.css.mjs
8595
- const css$11 = ".lc-jump{width:calc(28px + var(--dsh-content-font-delta,0px));height:calc(28px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);cursor:pointer;transition: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);background:0 0;border:0;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.lc-jump svg{width:calc(16px + var(--dsh-content-font-delta,0px));height:calc(16px + var(--dsh-content-font-delta,0px))}.lc-jump:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.lc-stats-jump{display:none}.lc-stats-jump-row{cursor:pointer}.lc-stats-jump-row:hover,.lc-stats-jump-row:focus-visible{text-decoration:underline}";
8808
+ const css$11 = ".lc-jump{width:calc(28px + var(--dsh-content-font-delta,0px));height:calc(28px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);cursor:pointer;transition: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);background:0 0;border:0;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.lc-jump svg{width:calc(16px + var(--dsh-content-font-delta,0px));height:calc(16px + var(--dsh-content-font-delta,0px))}.lc-jump:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}";
8596
8809
  const tagId$11 = "dsh-context/jump.css";
8597
8810
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$11) + "]") === null) {
8598
8811
  const tag = document.createElement("style");
@@ -8625,7 +8838,7 @@ window.__ModuleLoader__.load({
8625
8838
  }
8626
8839
  //#endregion
8627
8840
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/trendChart.css.mjs
8628
- const css$8 = ".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{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-top:18px;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:#80808080;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-bar:hover{background:var(--dsw-alias-bg-layer-2)}.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:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.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-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=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-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;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}";
8841
+ const css$8 = ".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-top:18px;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:#80808080;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-bar:hover{background:var(--dsw-alias-bg-layer-2)}.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:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.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-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=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-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;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}";
8629
8842
  const tagId$8 = "dsh-context/trendChart.css";
8630
8843
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$8) + "]") === null) {
8631
8844
  const tag = document.createElement("style");
@@ -8713,7 +8926,7 @@ window.__ModuleLoader__.load({
8713
8926
  }
8714
8927
  //#endregion
8715
8928
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/agentGraph.css.mjs
8716
- const css = ".lc-agents-chips{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.lc-agents-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;border-radius:999px;padding:2px 8px;font-size:11px}.lc-agents-chip-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-stage{margin:0 -4px;padding:0 4px;overflow-x:auto}.lc-agents-svg{max-width:none;margin:0 auto;display:block}.lc-agents-link{stroke-width:1.5px;stroke-opacity:.45}.lc-agents-link-live{stroke-opacity:.85}.lc-agents-flow{stroke-width:2px;stroke-linecap:round;stroke-dasharray:2 7;animation:.9s linear infinite lc-agent-flow}@keyframes lc-agent-flow{to{stroke-dashoffset:-9px}}.lc-agent-node{outline:none}.lc-agent-clickable{cursor:pointer}.lc-agent-halo{fill:#0000;transition:fill var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-agent-hover .lc-agent-halo,.lc-agent-node:focus-visible .lc-agent-halo{fill:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-agent-self .lc-agent-halo{fill:var(--dsw-alias-bg-layer-2)}.lc-agent-running .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent);animation:1.8s ease-in-out infinite lc-agent-glow}.lc-agent-done .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 7%, transparent)}@keyframes lc-agent-glow{0%,to{opacity:1}50%{opacity:.4}}.lc-agent-track{fill:var(--dsw-alias-bg-layer-1);stroke:var(--dsw-alias-border-l1);stroke-width:1.5px}.lc-agent-seg{fill:none;stroke-width:9px;transition:stroke-dasharray .3s,stroke-dashoffset .3s}.lc-agent-free{stroke:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agent-pct{fill:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;pointer-events:none;font-size:11px;font-weight:600}.lc-agent-caption{text-align:center;pointer-events:none;width:100%;line-height:1.25}.lc-agent-label{color:var(--dsw-alias-label-primary);overflow-wrap:break-word;-webkit-line-clamp:3;-webkit-box-orient:vertical;font-size:11px;display:-webkit-box;overflow:hidden}.lc-agent-tokens{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:10px}.lc-agents-solo{padding:4px 0 10px}.lc-agents-inspector{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;flex-wrap:wrap;align-items:baseline;gap:4px 8px;min-width:0;margin-top:10px;padding:7px 10px;font-size:12px;display:flex}.lc-agents-inspector-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}.lc-agents-badge{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 6px;font-size:10px}.lc-agent-self-badge{vertical-align:1px;margin-left:5px}.lc-agents-badge-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-inspector-stats{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.lc-agents-inspector-open{color:var(--dsw-alias-brand-primary,#3b82f6);flex:none;margin-left:auto}.lc-agents-legend{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:4px 14px;margin-top:8px;font-size:11px;display:flex}.lc-agents-legend-item{align-items:center;gap:5px;display:inline-flex}.lc-agents-legend-item i{border-radius:2px;width:8px;height:8px}.lc-agents-legend-free{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agents-legend-edge{border-top:2px dashed var(--dsw-alias-label-secondary);border-radius:0!important;width:14px!important;height:0!important}";
8929
+ const css = ".lc-agents-chips{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.lc-agents-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;border-radius:999px;padding:2px 8px;font-size:11px}.lc-agents-chip-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-stage{overscroll-behavior-x:contain;margin:0 -4px;padding:0 4px;overflow-x:auto}.lc-agents-svg{max-width:none;margin:0 auto;display:block}.lc-agents-link{stroke-width:1.5px;stroke-opacity:.45}.lc-agents-link-live{stroke-opacity:.85}.lc-agents-flow{stroke-width:2px;stroke-linecap:round;stroke-dasharray:2 7;animation:.9s linear infinite lc-agent-flow}@keyframes lc-agent-flow{to{stroke-dashoffset:-9px}}.lc-agent-node{outline:none}.lc-agent-clickable{cursor:pointer}.lc-agent-halo{fill:#0000;transition:fill var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-agent-hover .lc-agent-halo,.lc-agent-node:focus-visible .lc-agent-halo{fill:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-agent-self .lc-agent-halo{fill:var(--dsw-alias-bg-layer-2)}.lc-agent-running .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent);animation:1.8s ease-in-out infinite lc-agent-glow}.lc-agent-done .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 7%, transparent)}@keyframes lc-agent-glow{0%,to{opacity:1}50%{opacity:.4}}.lc-agent-track{fill:var(--dsw-alias-bg-layer-1);stroke:var(--dsw-alias-border-l1);stroke-width:1.5px}.lc-agent-seg{fill:none;stroke-width:9px;transition:stroke-dasharray .3s,stroke-dashoffset .3s}.lc-agent-free{stroke:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agent-pct{fill:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;pointer-events:none;font-size:11px;font-weight:600}.lc-agent-caption{text-align:center;pointer-events:none;width:100%;line-height:1.25}.lc-agent-label{color:var(--dsw-alias-label-primary);overflow-wrap:break-word;-webkit-line-clamp:3;-webkit-box-orient:vertical;font-size:11px;display:-webkit-box;overflow:hidden}.lc-agent-tokens{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:10px}.lc-agents-solo{padding:4px 0 10px}.lc-agents-inspector{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;flex-wrap:wrap;align-items:baseline;gap:4px 8px;min-width:0;margin-top:10px;padding:7px 10px;font-size:12px;display:flex}.lc-agents-inspector-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}.lc-agents-badge{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 6px;font-size:10px}.lc-agent-self-badge{vertical-align:1px;margin-left:5px}.lc-agents-badge-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-inspector-stats{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.lc-agents-inspector-open{color:var(--dsw-alias-brand-primary,#3b82f6);flex:none;margin-left:auto}.lc-agents-legend{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:4px 14px;margin-top:8px;font-size:11px;display:flex}.lc-agents-legend-item{align-items:center;gap:5px;display:inline-flex}.lc-agents-legend-item i{border-radius:2px;width:8px;height:8px}.lc-agents-legend-free{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agents-legend-edge{border-top:2px dashed var(--dsw-alias-label-secondary);border-radius:0!important;width:14px!important;height:0!important}";
8717
8930
  const tagId = "dsh-context/agentGraph.css";
8718
8931
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
8719
8932
  const tag = document.createElement("style");
@@ -8769,6 +8982,7 @@ window.__ModuleLoader__.load({
8769
8982
  label: () => t("tab")
8770
8983
  }, (props) => (0, react.createElement)(ContextView, props));
8771
8984
  });
8985
+ watchSidebarContextTab(ctx, ContextView, t, NS);
8772
8986
  const ContextJump = makeContextJumpButton(kit);
8773
8987
  ctx.slots.inject("conversation.chat.assistant-actions", () => {
8774
8988
  return ctx.slots.register({
@@ -8778,15 +8992,6 @@ window.__ModuleLoader__.load({
8778
8992
  locale: NS
8779
8993
  }, (props) => (0, react.createElement)(ContextJump, props));
8780
8994
  });
8781
- const StatsJump = makeStatsJump(kit);
8782
- ctx.slots.inject("conversation.composer.dock", () => {
8783
- return ctx.slots.register({
8784
- name: "conversation.composer.dock",
8785
- id: "stats-jump",
8786
- order: 10,
8787
- locale: NS
8788
- }, (props) => (0, react.createElement)(StatsJump, props));
8789
- });
8790
8995
  registerContextCommand(ctx, kit);
8791
8996
  const ContextModal = makeContextModal(ctx, kit);
8792
8997
  ctx.slots.inject("conversation.input.overlay", () => {