dsh-context 0.46.1 → 0.48.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
@@ -31,15 +31,18 @@ window.__ModuleLoader__.load({
31
31
  "stats.toolCalls": "工具调用",
32
32
  "stats.images": "图片",
33
33
  "stats.cost": "预估费用",
34
- "stats.costTip": "按 DeepSeek 官方刊例价估算整个会话的累计费用:输入区分缓存命中/未命中,输出含思考;按请求时间区分高峰(北京时间工作日 9:00–12:00、14:00–18:00)与空闲时段(半价,周末全天均为空闲时段)。适用于 deepseek-v4-flash / deepseek-v4-pro(不限 provider),价格写死在代码中,仅供参考。",
34
+ "stats.costTip": "按 DeepSeek 官方刊例价估算整个会话的累计费用:输入区分缓存命中/未命中,输出含思考;按请求时间区分高峰(北京时间工作日 9:00–12:00、14:00–18:00)与空闲时段(半价,周末全天均为空闲时段)。适用于 deepseek-v4.1-flash / deepseek-v4-pro(不限 provider;V4.1 Pro 上线前,V4 Pro 请求将被路由并按 V4.1 Flash 单价计费),价格写死在代码中,仅供参考。",
35
35
  "stats.costPriceHead": "每百万 tokens 价格(高峰 | 空闲半价):",
36
36
  "stats.costHit": "命中",
37
37
  "stats.costMiss": "未命中",
38
38
  "stats.costOut": "输出",
39
39
  "timing.title": "耗时统计",
40
- "timing.total": "总活跃时长",
40
+ "timing.total": "活跃时长",
41
41
  "timing.ttft": "模型等待",
42
42
  "timing.gen": "模型生成",
43
+ "timing.reasoning": "模型思考",
44
+ "timing.text": "正文输出",
45
+ "timing.toolArgs": "工具参数",
43
46
  "timing.tools": "工具执行",
44
47
  "timing.other": "其他开销",
45
48
  "timing.callTimes": "{n}次",
@@ -317,7 +320,7 @@ window.__ModuleLoader__.load({
317
320
  "stats.toolCalls": "Tool Calls",
318
321
  "stats.images": "Images",
319
322
  "stats.cost": "Cost",
320
- "stats.costTip": "Rough cumulative cost of the whole session at DeepSeek’s list prices: input split by cache hit/miss, output includes reasoning; each request is priced at the peak (Beijing Time weekdays 09:00–12:00, 14:00–18:00) or half-price off-peak rate by its time — weekends bill at off-peak all day. Applies to deepseek-v4-flash / deepseek-v4-pro on any provider; prices are hardcoded, for reference only.",
323
+ "stats.costTip": "Rough cumulative cost of the whole session at DeepSeek’s list prices: input split by cache hit/miss, output includes reasoning; each request is priced at the peak (Beijing Time weekdays 09:00–12:00, 14:00–18:00) or half-price off-peak rate by its time — weekends bill at off-peak all day. Applies to deepseek-v4.1-flash / deepseek-v4-pro on any provider (until V4.1 Pro ships, Pro requests are routed to and billed at V4.1 Flash rates); prices are hardcoded, for reference only.",
321
324
  "stats.costPriceHead": "Per-1M-token rates (peak | off-peak at half price):",
322
325
  "stats.costHit": "hit",
323
326
  "stats.costMiss": "miss",
@@ -326,6 +329,9 @@ window.__ModuleLoader__.load({
326
329
  "timing.total": "Active Time",
327
330
  "timing.ttft": "TTFT",
328
331
  "timing.gen": "LLM Gen",
332
+ "timing.reasoning": "Thinking",
333
+ "timing.text": "Answer",
334
+ "timing.toolArgs": "Tool args",
329
335
  "timing.tools": "Tool runs",
330
336
  "timing.other": "Overhead",
331
337
  "timing.callTimes": "{n} calls",
@@ -954,7 +960,7 @@ window.__ModuleLoader__.load({
954
960
  "assistant",
955
961
  "tool",
956
962
  "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;
963
+ ].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
964
  const safeCurrent = current !== null && typeof current === "object" ? current : {};
959
965
  const cost = typeof data.cost === "object" && data.cost !== null && !Array.isArray(data.cost) ? data.cost : void 0;
960
966
  const timing = timingOf(data.timing);
@@ -988,6 +994,7 @@ window.__ModuleLoader__.load({
988
994
  ...typeof data.detailRev === "number" && Number.isFinite(data.detailRev) ? { detailRev: data.detailRev } : {},
989
995
  ...cost !== void 0 ? { cost } : {},
990
996
  ...timing !== null ? { timing } : {},
997
+ ...data.systems !== void 0 ? { systems: systemsOf(data.systems) } : {},
991
998
  ...typeof data.surfaceFloor === "number" ? { surfaceFloor: data.surfaceFloor } : {},
992
999
  ...typeof data.archiveFloor === "number" ? { archiveFloor: data.archiveFloor } : {},
993
1000
  ...data.fileOps !== void 0 ? { fileOps: objectsOf(data.fileOps) } : {},
@@ -995,6 +1002,45 @@ window.__ModuleLoader__.load({
995
1002
  };
996
1003
  }
997
1004
  /**
1005
+ * The live system-prompt nodes, re-proved per entry and sorted by seq: an
1006
+ * entry missing a finite seq/time/tokens drops out (the browser then falls
1007
+ * back to the header epoch), so a hostile collection can never produce a NaN
1008
+ * prompt figure or an unfetchable seq. Absent or empty stays absent.
1009
+ */
1010
+ function systemsOf(value) {
1011
+ const list = objectsOf(value);
1012
+ const out = [];
1013
+ for (const entry of list) {
1014
+ const { seq, time, tokens } = entry;
1015
+ if (typeof seq !== "number" || !Number.isFinite(seq)) continue;
1016
+ if (typeof time !== "number" || !Number.isFinite(time)) continue;
1017
+ if (typeof tokens !== "number" || !Number.isFinite(tokens)) continue;
1018
+ out.push({
1019
+ seq,
1020
+ time,
1021
+ tokens
1022
+ });
1023
+ }
1024
+ return out.sort((a, b) => a.seq - b.seq);
1025
+ }
1026
+ /**
1027
+ * The fast path's check for the live system-prompt nodes: every entry must
1028
+ * carry the three finite numbers the browser reads — `seq` for the per-step
1029
+ * resolution, `time` for the DNA band, `tokens` for its width. A primitive
1030
+ * entry, or one whose fields are not numbers, sends the payload down the
1031
+ * sanitizing slow path (`systemsOf` drops it) instead of leaking `undefined`
1032
+ * into the bar math. An absent list is fine.
1033
+ */
1034
+ function systemsFastOk(value) {
1035
+ if (value === void 0) return true;
1036
+ if (!Array.isArray(value)) return false;
1037
+ return value.every((entry) => {
1038
+ if (entry === null || typeof entry !== "object") return false;
1039
+ const { seq, time, tokens } = entry;
1040
+ return typeof seq === "number" && Number.isFinite(seq) && typeof time === "number" && Number.isFinite(time) && typeof tokens === "number" && Number.isFinite(tokens);
1041
+ });
1042
+ }
1043
+ /**
998
1044
  * The split head's count figures, re-proved field by field: a present-but-
999
1045
  * partial record zeroes its unreadable fields (the stats board's no-NaN
1000
1046
  * guarantee), an absent or non-record value stays absent (legacy generation
@@ -1094,6 +1140,14 @@ window.__ModuleLoader__.load({
1094
1140
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
1095
1141
  }
1096
1142
  /**
1143
+ * The OPTIONAL timing scalars (the generation split): a real non-negative
1144
+ * number passes, anything else — including absence — reads as undefined so the
1145
+ * field stays absent on the narrowed value (see `timingOf`).
1146
+ */
1147
+ function optMsNumOf(value) {
1148
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1149
+ }
1150
+ /**
1097
1151
  * Cheap whole-value check for the pass-through path of `timelineOf`: absent
1098
1152
  * timing passes; present timing must already be well-formed (every scalar
1099
1153
  * numeric, every per-name row shaped) — anything else sends the payload down
@@ -1111,6 +1165,14 @@ window.__ModuleLoader__.load({
1111
1165
  "toolsMs",
1112
1166
  "toolCalls"
1113
1167
  ]) if (typeof t[k] !== "number") return false;
1168
+ for (const k of [
1169
+ "reasoningMs",
1170
+ "textMs",
1171
+ "toolArgMs"
1172
+ ]) {
1173
+ const v = t[k];
1174
+ if (v !== void 0 && (typeof v !== "number" || !Number.isFinite(v) || v < 0)) return false;
1175
+ }
1114
1176
  const tools = t.tools;
1115
1177
  if (tools === null || typeof tools !== "object" || Array.isArray(tools)) return false;
1116
1178
  for (const k in tools) {
@@ -1145,7 +1207,7 @@ window.__ModuleLoader__.load({
1145
1207
  ms
1146
1208
  };
1147
1209
  }
1148
- return {
1210
+ const totals = {
1149
1211
  wallMs: msNumOf(data.wallMs),
1150
1212
  ttftMs: msNumOf(data.ttftMs),
1151
1213
  genMs: msNumOf(data.genMs),
@@ -1154,6 +1216,13 @@ window.__ModuleLoader__.load({
1154
1216
  toolCalls: msNumOf(data.toolCalls),
1155
1217
  tools
1156
1218
  };
1219
+ const reasoning = optMsNumOf(data.reasoningMs);
1220
+ if (reasoning !== void 0) totals.reasoningMs = reasoning;
1221
+ const textMs = optMsNumOf(data.textMs);
1222
+ if (textMs !== void 0) totals.textMs = textMs;
1223
+ const toolArgMs = optMsNumOf(data.toolArgMs);
1224
+ if (toolArgMs !== void 0) totals.toolArgMs = toolArgMs;
1225
+ return totals;
1157
1226
  }
1158
1227
  /**
1159
1228
  * Narrow a delivered projection value to the plugin's `contextHeaders`
@@ -1323,6 +1392,25 @@ window.__ModuleLoader__.load({
1323
1392
  return out.trim() === "" ? null : out;
1324
1393
  }
1325
1394
  /**
1395
+ * The system prompt's exact rendered text — every text block joined with NO
1396
+ * normalization: a whitespace-only prompt is still the prompt the model
1397
+ * received (the host prices it as text), so it must render rather than read
1398
+ * as absent. Null when the content carries no text block at all.
1399
+ */
1400
+ function systemTextOf(blocks) {
1401
+ if (!Array.isArray(blocks)) return null;
1402
+ let out = "";
1403
+ let seen = false;
1404
+ for (const b of blocks) {
1405
+ const text = b !== null && typeof b === "object" ? b.text : void 0;
1406
+ if (typeof text === "string") {
1407
+ out += text;
1408
+ seen = true;
1409
+ }
1410
+ }
1411
+ return seen ? out : null;
1412
+ }
1413
+ /**
1326
1414
  * One assistant content block → the snapshot block vocabulary the browser
1327
1415
  * already renders (`kind`: text/reasoning/image/tool-call); unmappable
1328
1416
  * blocks pass through raw and degrade to the generic JSON section.
@@ -1548,13 +1636,23 @@ window.__ModuleLoader__.load({
1548
1636
  };
1549
1637
  }
1550
1638
  /**
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.
1639
+ * Map one raw durable event into the epoch content the browser renders. A
1640
+ * `request/header` yields the full system prompt text (V0/V2 envelope) plus
1641
+ * each tool's producer description and raw schema; a V3 `system/message`
1642
+ * yields the prompt text alone (its tools live in the request header). Both
1643
+ * mirror the host fold's per-entry guards a null or primitive tool entry
1644
+ * degrades to an unnamed row instead of throwing the read. Null when the
1645
+ * envelope carries neither.
1556
1646
  */
1557
- function headerContentOf(data) {
1647
+ function headerContentOf(event) {
1648
+ const { type, data } = event;
1649
+ if (type === "system/message") {
1650
+ const system = systemTextOf((data.message !== null && typeof data.message === "object" ? data.message : null)?.content);
1651
+ return system === null ? null : {
1652
+ system,
1653
+ tools: []
1654
+ };
1655
+ }
1558
1656
  const rawHeader = data.header !== null && typeof data.header === "object" ? data.header : null;
1559
1657
  if (rawHeader === null) return null;
1560
1658
  const toolsRaw = Array.isArray(rawHeader.tools) ? rawHeader.tools : [];
@@ -1573,15 +1671,15 @@ window.__ModuleLoader__.load({
1573
1671
  };
1574
1672
  }
1575
1673
  /**
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.
1674
+ * The on-demand CONTENT fetch for the browser's System and Tools sections —
1675
+ * the lazy counterpart of the node fetcher above. One seq-anchored history
1676
+ * read off the requested seq returns the page holding that event (non-message
1677
+ * events ride the page verbatim); a `request/header` maps to the epoch's
1678
+ * tools (and its V0/V2 system text), a `system/message` to a V3 prompt's
1679
+ * text. Landed content caches per seq (history is immutable), and OLDER
1680
+ * epochs sharing the page cache for free stepping back through epochs walks
1681
+ * the same pages. Undefined when no history face exists — the browser keeps a
1682
+ * metadata-only degradation instead.
1585
1683
  */
1586
1684
  function makeHeaderFetcher(sessionId) {
1587
1685
  const read = pageReaderOf(sessionId);
@@ -1594,8 +1692,8 @@ window.__ModuleLoader__.load({
1594
1692
  let picked = null;
1595
1693
  for (const entry of rows) {
1596
1694
  const ev = eventOf(entry);
1597
- if (ev === null || ev.type !== "request/header") continue;
1598
- const content = headerContentOf(ev.data);
1695
+ if (ev === null || ev.type !== "request/header" && ev.type !== "system/message") continue;
1696
+ const content = headerContentOf(ev);
1599
1697
  if (content === null) continue;
1600
1698
  cache.set(ev.seq, content);
1601
1699
  if (ev.seq === seq) picked = content;
@@ -1872,6 +1970,30 @@ window.__ModuleLoader__.load({
1872
1970
  for (let i = headers.headers.length - 1; i >= 0; i--) if (headers.headers[i].seq < seq) return headers.headers[i];
1873
1971
  return null;
1874
1972
  }
1973
+ /**
1974
+ * The system prompt in force at `seq` (null = none) — the LAST live system
1975
+ * node at or before it carrying tokens, which is exactly the host fold's
1976
+ * "last nonempty surviving system" rule and therefore agrees with the
1977
+ * per-step `system` figure the fold recorded. Rows folded before `systems`
1978
+ * existed carry none; the header epoch's envelope figure stands in for them
1979
+ * (the pre-V3 wire shape).
1980
+ */
1981
+ function systemAt(data, header, seq) {
1982
+ const systems = data.systems;
1983
+ if (systems !== void 0 && systems.length > 0) {
1984
+ for (let i = systems.length - 1; i >= 0; i--) {
1985
+ const node = systems[i];
1986
+ if (node.tokens <= 0) continue;
1987
+ if (seq === null || node.seq < seq) return node;
1988
+ }
1989
+ return null;
1990
+ }
1991
+ return header !== null && header.systemTokens !== void 0 ? {
1992
+ seq: header.seq,
1993
+ time: header.time,
1994
+ tokens: header.systemTokens
1995
+ } : null;
1996
+ }
1875
1997
  function assemble(data, headers, seq) {
1876
1998
  const live = seq === null;
1877
1999
  let nodes;
@@ -1888,9 +2010,11 @@ window.__ModuleLoader__.load({
1888
2010
  if (live || data.surfaceFloor !== void 0 && seq > data.surfaceFloor) missingLive = data.droppedNodes;
1889
2011
  }
1890
2012
  const approximate = !live && data.archiveFloor !== void 0 && seq < data.archiveFloor;
2013
+ const header = headerAt(headers, seq);
1891
2014
  return {
1892
2015
  live,
1893
- header: headerAt(headers, seq),
2016
+ header,
2017
+ system: systemAt(data, header, seq),
1894
2018
  nodes,
1895
2019
  missingLive,
1896
2020
  approximate
@@ -1900,19 +2024,17 @@ window.__ModuleLoader__.load({
1900
2024
  //#region src/client/dna.ts
1901
2025
  function dnaOf(view) {
1902
2026
  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
- }
2027
+ if (view.system !== null) items.push({
2028
+ key: "sys",
2029
+ cat: "system",
2030
+ tokens: view.system.tokens,
2031
+ time: view.system.time
2032
+ });
2033
+ if (view.header !== null) for (const tool of view.header.tools) items.push({
2034
+ key: "tool:" + tool.name,
2035
+ cat: "tools",
2036
+ tokens: tool.tokens
2037
+ });
1916
2038
  for (const n of view.nodes) items.push({
1917
2039
  key: "n" + String(n.seq),
1918
2040
  cat: n.cat,
@@ -2358,22 +2480,20 @@ window.__ModuleLoader__.load({
2358
2480
  return `${pct.toFixed(1)}%`;
2359
2481
  }
2360
2482
  /**
2361
- * Whole-session durations for the timing card, in the locale's units: raw ms
2362
- * under a second, one-decimal seconds under a minute, then m/s and h/m.
2483
+ * Whole-session durations for the timing card, locale-free compact units: raw
2484
+ * ms under a second, one-decimal seconds under a minute, then m/s and h/m.
2363
2485
  * Non-finite or non-positive input shows the dash (callers render their empty
2364
2486
  * state anyway).
2365
2487
  */
2366
- function fmtDuration(ms, lang) {
2488
+ function fmtDuration(ms) {
2367
2489
  if (!Number.isFinite(ms) || ms <= 0) return "—";
2368
2490
  if (ms < 1e3) return `${Math.round(ms)}ms`;
2369
- const zh = lang === "zh";
2370
- if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}${zh ? "秒" : "s"}`;
2491
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2371
2492
  const totalSec = Math.floor(ms / 1e3);
2372
2493
  const m = Math.floor(totalSec / 60);
2373
2494
  const s = totalSec % 60;
2374
- if (ms < 36e5) return zh ? `${m}分${s}秒` : `${m}m ${s}s`;
2375
- const h = Math.floor(m / 60);
2376
- return zh ? `${h}时${m % 60}分` : `${h}h ${m % 60}m`;
2495
+ if (ms < 36e5) return `${m}m${s}s`;
2496
+ return `${Math.floor(m / 60)}h${m % 60}m`;
2377
2497
  }
2378
2498
  //#endregion
2379
2499
  //#region src/shared/imageTokens.ts
@@ -3217,7 +3337,7 @@ window.__ModuleLoader__.load({
3217
3337
  return m;
3218
3338
  }
3219
3339
  function countOf(asm, byCat, c) {
3220
- if (c === "system") return asm.header !== null && asm.header.systemTokens !== void 0 ? 1 : 0;
3340
+ if (c === "system") return asm.system !== null ? 1 : 0;
3221
3341
  if (c === "tools") return asm.header !== null ? asm.header.tools.length : 0;
3222
3342
  return byCat[c]?.length ?? 0;
3223
3343
  }
@@ -3302,7 +3422,8 @@ window.__ModuleLoader__.load({
3302
3422
  const view = assemble(data, headers, seq);
3303
3423
  const fetchHeader = props.fetchHeader;
3304
3424
  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");
3425
+ const systemSeq = view.system !== null ? view.system.seq : null;
3426
+ const epoch = useFetchOnMiss(openCat === "system" ? systemSeq : openCat === "tools" ? headerSeq : null, fetchHeader, "dsh-context: header content fetch failed");
3306
3427
  const headerContent = epoch.values;
3307
3428
  const headerNote = fetchMissNote(t, fetchHeader, epoch.state, epoch.retry, "browser.headerMetaOnly");
3308
3429
  const breakdown = req !== null ? req : data.current;
@@ -3348,7 +3469,7 @@ window.__ModuleLoader__.load({
3348
3469
  };
3349
3470
  const toolCount = (c) => countOf(view, byCat, c);
3350
3471
  const singleKeyOf = (c) => {
3351
- if (c === "system") return view.header?.systemTokens !== void 0 ? "sys" : null;
3472
+ if (c === "system") return view.system !== null ? "sys" : null;
3352
3473
  if (c === "tools") {
3353
3474
  const tools = view.header?.tools;
3354
3475
  return tools !== void 0 && tools.length === 1 ? "tool:" + tools[0].name : null;
@@ -3418,11 +3539,12 @@ window.__ModuleLoader__.load({
3418
3539
  };
3419
3540
  const catBody = (c) => {
3420
3541
  if (c === "system") {
3421
- if (view.header === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3542
+ const sys = view.system;
3543
+ if (sys === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3422
3544
  className: "lc-br-note",
3423
3545
  children: t(headers === null ? "browser.noHeader" : "browser.noEpoch")
3424
3546
  });
3425
- const content = headerContent.get(view.header.seq);
3547
+ const content = headerContent.get(sys.seq);
3426
3548
  if (content === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3427
3549
  className: "lc-br-note",
3428
3550
  children: headerNote
@@ -3827,7 +3949,7 @@ window.__ModuleLoader__.load({
3827
3949
  className: "lc-stacked-seg" + (on ? " lc-stacked-seg-on" : "") + (pickKey !== void 0 ? " lc-stacked-seg-pick" : ""),
3828
3950
  style: {
3829
3951
  width: `${widths[i]}%`,
3830
- background: p.color
3952
+ backgroundColor: p.color
3831
3953
  },
3832
3954
  onMouseEnter: () => {
3833
3955
  if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
@@ -5565,9 +5687,9 @@ window.__ModuleLoader__.load({
5565
5687
  }
5566
5688
  /**
5567
5689
  * Compact duration: `42s`, `3m05s`, `1h07m` (shared by both locales).
5568
- * Deliberately distinct from format.ts's locale-aware `fmtDuration` (the
5569
- * timing card's `12.3s` / `4分0秒`): the inspector's caption column needs
5570
- * whole-second, fixed-width, locale-free text.
5690
+ * Deliberately distinct from format.ts's `fmtDuration` (the timing card's
5691
+ * `12.3s` / `3m25s`): the inspector's caption column needs whole-second,
5692
+ * fixed-width text.
5571
5693
  */
5572
5694
  function fmtDurationCompact(ms) {
5573
5695
  if (!Number.isFinite(ms) || ms < 0) return "—";
@@ -6482,7 +6604,7 @@ window.__ModuleLoader__.load({
6482
6604
  return function PluginInfo() {
6483
6605
  const [latest, setLatest] = (0, react.useState)(null);
6484
6606
  (0, react.useEffect)(() => {
6485
- if ("0.46.1".includes("-dev")) return;
6607
+ if ("0.48.0".includes("-dev")) return;
6486
6608
  let on = true;
6487
6609
  fetchLatestVersion().then((v) => {
6488
6610
  if (on && v) setLatest(v);
@@ -6491,8 +6613,8 @@ window.__ModuleLoader__.load({
6491
6613
  on = false;
6492
6614
  };
6493
6615
  }, []);
6494
- const update = latest !== null && isNewerVersion(latest, "0.46.1") ? latest : null;
6495
- const nameText = "dsh-context (v0.46.1)";
6616
+ const update = latest !== null && isNewerVersion(latest, "0.48.0") ? latest : null;
6617
+ const nameText = "dsh-context (v0.48.0)";
6496
6618
  const nameValue = [nameText];
6497
6619
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6498
6620
  className: "lc-pi-update",
@@ -6949,61 +7071,49 @@ window.__ModuleLoader__.load({
6949
7071
  }
6950
7072
  //#endregion
6951
7073
  //#region src/client/cost.ts
6952
- const PRICES = {
7074
+ /** The Flash-series rates (peak = 2× off-peak), per currency. */
7075
+ const FLASH_RATES = {
6953
7076
  usd: {
6954
- flash: {
6955
- peak: {
6956
- hit: .014,
6957
- miss: .44,
6958
- out: 1.32
6959
- },
6960
- off: {
6961
- hit: .007,
6962
- miss: .22,
6963
- out: .66
6964
- }
7077
+ peak: {
7078
+ hit: .006,
7079
+ miss: .3,
7080
+ out: 1.2
6965
7081
  },
6966
- pro: {
6967
- peak: {
6968
- hit: .044,
6969
- miss: 1.32,
6970
- out: 3.96
6971
- },
6972
- off: {
6973
- hit: .022,
6974
- miss: .66,
6975
- out: 1.98
6976
- }
7082
+ off: {
7083
+ hit: .003,
7084
+ miss: .15,
7085
+ out: .6
6977
7086
  }
6978
7087
  },
6979
7088
  cny: {
6980
- flash: {
6981
- peak: {
6982
- hit: .1,
6983
- miss: 3,
6984
- out: 9
6985
- },
6986
- off: {
6987
- hit: .05,
6988
- miss: 1.5,
6989
- out: 4.5
6990
- }
7089
+ peak: {
7090
+ hit: .04,
7091
+ miss: 2,
7092
+ out: 8
6991
7093
  },
6992
- pro: {
6993
- peak: {
6994
- hit: .3,
6995
- miss: 9,
6996
- out: 27
6997
- },
6998
- off: {
6999
- hit: .15,
7000
- miss: 4.5,
7001
- out: 13.5
7002
- }
7094
+ off: {
7095
+ hit: .02,
7096
+ miss: 1,
7097
+ out: 4
7003
7098
  }
7004
7099
  }
7005
7100
  };
7006
7101
  /**
7102
+ * Pro rides the Flash triple: between the V4.1 Flash launch and the V4.1 Pro
7103
+ * release, DeepSeek routes every Pro request to V4.1 Flash and bills it at
7104
+ * the Flash rates — point `pro` at the new table once V4.1 Pro is priced.
7105
+ */
7106
+ const PRICES = {
7107
+ usd: {
7108
+ flash: FLASH_RATES.usd,
7109
+ pro: FLASH_RATES.usd
7110
+ },
7111
+ cny: {
7112
+ flash: FLASH_RATES.cny,
7113
+ pro: FLASH_RATES.cny
7114
+ }
7115
+ };
7116
+ /**
7007
7117
  * Price the session's cumulative billed-token totals. Cache reads bill at
7008
7118
  * the hit rate; uncached input AND cache writes bill at the miss rate;
7009
7119
  * output (reasoning included) bills at the out rate. Null when nothing was
@@ -7038,7 +7148,7 @@ window.__ModuleLoader__.load({
7038
7148
  */
7039
7149
  function sessionPrices(currency) {
7040
7150
  return ["flash", "pro"].map((id) => ({
7041
- family: id === "flash" ? "deepseek-v4-flash" : "deepseek-v4-pro",
7151
+ family: id === "flash" ? "deepseek-v4.1-flash" : "deepseek-v4-pro",
7042
7152
  peak: PRICES[currency][id].peak,
7043
7153
  off: PRICES[currency][id].off
7044
7154
  }));
@@ -7233,90 +7343,139 @@ window.__ModuleLoader__.load({
7233
7343
  * token, the wait) and LLM generation (first token → assistant message) —
7234
7344
  * and the tool-execution slice, with the residue as overhead; a call whose
7235
7345
  * stream recorded no token delta (legacy or aborted) stays unattributed and
7236
- * lands in the residue. The slice rows lead with the true duration and
7237
- * qualify it with the call count on the secondary line. The donut and the
7238
- * rows sit side by side so the head row stays half-height. Parallel tool
7239
- * calls each count, so the tools figure can overlap the ring clamps it
7240
- * into the post-model window while the row numbers stay true.
7346
+ * lands in the residue. When the host folded the generation split (see
7347
+ * TimingTotals), the generation slice expands into what was being decoded:
7348
+ * thinking, answer text, and tool-call arguments. The slice rows lead with
7349
+ * the true duration and qualify it with the call count on the secondary line
7350
+ * except the decode slices, which count BLOCKS (zero to many per call, so a
7351
+ * call count would be a false tally) and therefore carry no qualifier. A slice
7352
+ * that never happened (zero time) is omitted entirely — the ring already skips
7353
+ * its arc, and a zero row would otherwise borrow the session's call count and
7354
+ * read as "no time, many calls". The donut and the rows sit side by side so
7355
+ * the head row stays half-height. Parallel tool calls each count, so the tools
7356
+ * figure can overlap — the ring clamps it into the post-model window while the
7357
+ * row numbers stay true.
7241
7358
  */
7359
+ const COLOR = {
7360
+ ttft: "#3b82f6",
7361
+ reasoning: "#8b5cf6",
7362
+ text: "#ec4899",
7363
+ toolarg: "#f59e0b",
7364
+ tools: "#14b8a6",
7365
+ other: "#94a3b8"
7366
+ };
7242
7367
  function makeStatsTiming(kit, Donut) {
7243
7368
  const { t, fmt, fmtDuration, fmtShare } = kit;
7244
7369
  const SliceList = makeSliceList(kit);
7245
7370
  return function StatsTiming(props) {
7246
- const lang = props.locale === "zh" ? "zh" : "en";
7247
7371
  const [hoverKey, setHoverKey] = (0, react.useState)(null);
7248
7372
  const timing = props.timing;
7249
7373
  const wall = timing !== null && Number.isFinite(timing.wallMs) && timing.wallMs > 0 ? timing.wallMs : 0;
7250
7374
  let segments = [];
7251
7375
  let rows = [];
7252
7376
  if (timing !== null && (wall > 0 || timing.calls > 0 || timing.toolCalls > 0)) {
7253
- const ttft = Math.min(timing.ttftMs, wall);
7254
- const gen = Math.max(0, Math.min(timing.genMs, wall - ttft));
7255
- const toolRing = Math.max(0, Math.min(timing.toolsMs, wall - ttft - gen));
7256
- const other = Math.max(0, wall - ttft - gen - toolRing);
7377
+ const callTimes = t("timing.callTimes", { n: fmt(timing.calls) });
7378
+ const modelSlices = [{
7379
+ key: "ttft",
7380
+ color: COLOR.ttft,
7381
+ label: t("timing.ttft"),
7382
+ ms: timing.ttftMs,
7383
+ times: callTimes
7384
+ }];
7385
+ const buckets = [
7386
+ [
7387
+ "reasoning",
7388
+ COLOR.reasoning,
7389
+ t("timing.reasoning"),
7390
+ timing.reasoningMs ?? 0
7391
+ ],
7392
+ [
7393
+ "text",
7394
+ COLOR.text,
7395
+ t("timing.text"),
7396
+ timing.textMs ?? 0
7397
+ ],
7398
+ [
7399
+ "toolarg",
7400
+ COLOR.toolarg,
7401
+ t("timing.toolArgs"),
7402
+ timing.toolArgMs ?? 0
7403
+ ]
7404
+ ];
7405
+ if (buckets.some(([, , , ms]) => ms > 0)) for (const [key, color, label, ms] of buckets) modelSlices.push({
7406
+ key,
7407
+ color,
7408
+ label,
7409
+ ms
7410
+ });
7411
+ else modelSlices.push({
7412
+ key: "gen",
7413
+ color: COLOR.reasoning,
7414
+ label: t("timing.gen"),
7415
+ ms: timing.genMs
7416
+ });
7417
+ const ttftRing = Math.min(timing.ttftMs, wall);
7418
+ const genRing = Math.max(0, Math.min(timing.genMs, wall - ttftRing));
7419
+ const modelRings = [ttftRing];
7420
+ let used = ttftRing;
7421
+ const genEnd = ttftRing + genRing;
7422
+ for (const slice of modelSlices.slice(1)) {
7423
+ const take = Math.max(0, Math.min(slice.ms, genEnd - used));
7424
+ modelRings.push(take);
7425
+ used += take;
7426
+ }
7427
+ const toolRing = Math.max(0, Math.min(timing.toolsMs, wall - ttftRing - genRing));
7428
+ const other = Math.max(0, wall - ttftRing - genRing - toolRing);
7257
7429
  const share = (ms) => wall > 0 ? ms / wall : 0;
7258
7430
  const countOf = (ms, times) => {
7259
- const dur = fmtDuration(ms, lang);
7431
+ const dur = fmtDuration(ms);
7260
7432
  if (times === void 0) return dur;
7261
7433
  return ms > 0 ? `${dur} · ${times}` : times;
7262
7434
  };
7263
- const callTimes = t("timing.callTimes", { n: fmt(timing.calls) });
7435
+ const modelSegments = modelSlices.map((slice, index) => ({
7436
+ key: slice.key,
7437
+ color: slice.color,
7438
+ value: share(modelRings[index])
7439
+ }));
7440
+ const toolSlice = {
7441
+ key: "tools",
7442
+ color: COLOR.tools,
7443
+ label: t("timing.tools"),
7444
+ ms: timing.toolsMs,
7445
+ times: t("timing.toolTimes", { n: fmt(timing.toolCalls) })
7446
+ };
7447
+ const otherSlice = {
7448
+ key: "other",
7449
+ color: COLOR.other,
7450
+ label: t("timing.other"),
7451
+ ms: other
7452
+ };
7264
7453
  segments = [
7265
- {
7266
- key: "ttft",
7267
- color: "#3b82f6",
7268
- value: share(ttft)
7269
- },
7270
- {
7271
- key: "gen",
7272
- color: "#8b5cf6",
7273
- value: share(gen)
7274
- },
7454
+ ...modelSegments,
7275
7455
  {
7276
7456
  key: "tools",
7277
- color: "#14b8a6",
7457
+ color: COLOR.tools,
7278
7458
  value: share(toolRing)
7279
7459
  },
7280
7460
  {
7281
7461
  key: "other",
7282
- color: "#94a3b8",
7462
+ color: COLOR.other,
7283
7463
  value: share(other)
7284
7464
  }
7285
7465
  ];
7466
+ const toRow = (slice) => ({
7467
+ key: slice.key,
7468
+ color: slice.color,
7469
+ label: slice.label,
7470
+ dim: slice.ms === 0,
7471
+ pct: fmtShare(slice.ms, wall),
7472
+ count: countOf(slice.ms, slice.times)
7473
+ });
7286
7474
  rows = [
7287
- {
7288
- key: "ttft",
7289
- color: "#3b82f6",
7290
- label: t("timing.ttft"),
7291
- dim: timing.ttftMs === 0,
7292
- pct: fmtShare(timing.ttftMs, wall),
7293
- count: countOf(timing.ttftMs, callTimes)
7294
- },
7295
- {
7296
- key: "gen",
7297
- color: "#8b5cf6",
7298
- label: t("timing.gen"),
7299
- dim: timing.genMs === 0,
7300
- pct: fmtShare(timing.genMs, wall),
7301
- count: countOf(timing.genMs, callTimes)
7302
- },
7303
- {
7304
- key: "tools",
7305
- color: "#14b8a6",
7306
- label: t("timing.tools"),
7307
- dim: timing.toolsMs === 0,
7308
- pct: fmtShare(timing.toolsMs, wall),
7309
- count: countOf(timing.toolsMs, t("timing.toolTimes", { n: fmt(timing.toolCalls) }))
7310
- },
7311
- {
7312
- key: "other",
7313
- color: "#94a3b8",
7314
- label: t("timing.other"),
7315
- dim: other === 0,
7316
- pct: fmtShare(other, wall),
7317
- count: countOf(other)
7318
- }
7319
- ];
7475
+ ...modelSlices.map(toRow),
7476
+ toRow(toolSlice),
7477
+ toRow(otherSlice)
7478
+ ].filter((row) => !row.dim);
7320
7479
  }
7321
7480
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7322
7481
  className: "lc-card lc-col-stats lc-col-donut",
@@ -7334,7 +7493,7 @@ window.__ModuleLoader__.load({
7334
7493
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Donut, {
7335
7494
  segments,
7336
7495
  size: 96,
7337
- centerTop: wall > 0 ? fmtDuration(wall, lang) : "—",
7496
+ centerTop: wall > 0 ? fmtDuration(wall) : "—",
7338
7497
  centerSub: t("timing.total"),
7339
7498
  hoverKey,
7340
7499
  onHoverKey: setHoverKey
@@ -8067,6 +8226,7 @@ window.__ModuleLoader__.load({
8067
8226
  const ErrorBoundary = makeErrorBoundary(t);
8068
8227
  function ContextViewBody(props) {
8069
8228
  const sessionId = props.sessionId;
8229
+ const inSidebar = props.host === "sidebar";
8070
8230
  const source = useTimelineSource(ctx, props);
8071
8231
  const data = source.data;
8072
8232
  const pressure = projectionOf(props, "contextPressure", contextPressureOf);
@@ -8261,6 +8421,132 @@ window.__ModuleLoader__.load({
8261
8421
  const activeLocale = localeSvc !== void 0 && typeof localeSvc.getLocale === "function" ? localeSvc.getLocale().active : "en";
8262
8422
  const subtitle = (data.model ?? "") + (data.provider ? " · " + data.provider : "");
8263
8423
  const gate = unsupportedOf(data.unsupported);
8424
+ const compositionCard = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CurrentComposition, {
8425
+ head,
8426
+ subtitle,
8427
+ hoverKey: hoverCat,
8428
+ onHoverKey: setHoverCat
8429
+ });
8430
+ const trendCard = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8431
+ className: "lc-card",
8432
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8433
+ className: "lc-card-title",
8434
+ children: [
8435
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8436
+ className: "lc-card-title-text",
8437
+ children: t("trend.title")
8438
+ }),
8439
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8440
+ className: "lc-gran lc-trend-adaptive",
8441
+ role: "group",
8442
+ title: t("trend.adaptiveHint"),
8443
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8444
+ type: "button",
8445
+ className: "lc-gran-btn" + (adaptive ? " lc-gran-on" : ""),
8446
+ onClick: () => {
8447
+ setAdaptive((on) => !on);
8448
+ },
8449
+ children: t("trend.adaptive")
8450
+ })
8451
+ }),
8452
+ focusCat !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8453
+ className: "lc-card-sub",
8454
+ children: t("trend.focus", { cat: kit.catLabel(focusCat) })
8455
+ }) : null,
8456
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8457
+ className: "lc-trend-ctl",
8458
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8459
+ className: "lc-gran",
8460
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8461
+ className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
8462
+ onClick: () => {
8463
+ setGranularity("step");
8464
+ },
8465
+ children: t("gran.step")
8466
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8467
+ className: "lc-gran-btn" + (granularity === "turn" ? " lc-gran-on" : ""),
8468
+ onClick: () => {
8469
+ setGranularity("turn");
8470
+ },
8471
+ children: t("gran.turn")
8472
+ })]
8473
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8474
+ className: "lc-gran",
8475
+ title: t("gran.modeHint"),
8476
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8477
+ className: "lc-gran-btn" + (trendMode === "total" ? " lc-gran-on" : ""),
8478
+ onClick: () => {
8479
+ setTrendMode("total");
8480
+ },
8481
+ children: t("gran.total")
8482
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8483
+ className: "lc-gran-btn" + (trendMode === "delta" ? " lc-gran-on" : ""),
8484
+ onClick: () => {
8485
+ setTrendMode("delta");
8486
+ },
8487
+ children: t("gran.delta")
8488
+ })]
8489
+ })]
8490
+ })
8491
+ ]
8492
+ }), displayRequests.length === 0 ? detailReady ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8493
+ className: "lc-empty",
8494
+ children: t("trend.empty")
8495
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailNote, {
8496
+ state: source.detailState === "failed" ? "failed" : "loading",
8497
+ onRetry: source.retryDetail
8498
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrendChart, {
8499
+ requests: displayRequests,
8500
+ markers,
8501
+ selectedSeq: pinnedReq ? pinnedReq.seq : null,
8502
+ hoveredSeq,
8503
+ activeTurn,
8504
+ granularity,
8505
+ mode: trendMode,
8506
+ focusTurn,
8507
+ hoverCat: trendHoverCat,
8508
+ focusCat,
8509
+ adaptive,
8510
+ onSelect: setSelectedSeq,
8511
+ onHover: setHoveredSeq,
8512
+ onHoverTurn: setHoverTurn,
8513
+ onPickTurn: (turn) => {
8514
+ setGranularity("turn");
8515
+ setFocusTurn(turn);
8516
+ },
8517
+ onFocusTurnHandled: () => {
8518
+ setFocusTurn(null);
8519
+ }
8520
+ }, sessionId), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RequestDetail, {
8521
+ request: activeReq,
8522
+ prev: trendMode === "delta" && activeIdx >= 0 ? activeIdx > 0 ? displayRequests[activeIdx - 1] : null : void 0,
8523
+ /* v8 ignore next 1 -- RequestDetail renders only when
8524
+ displayRequests.length > 0, which forces activeReq
8525
+ non-null via the activeIdx fallback above. */
8526
+ marker: activeReq !== null ? markerOf(activeReq) : void 0,
8527
+ brief,
8528
+ convOf,
8529
+ onLocate: locateNode,
8530
+ hoverKey: trendHoverCat
8531
+ })] })]
8532
+ });
8533
+ const browserCard = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextBrowser, {
8534
+ data,
8535
+ headers,
8536
+ convNodes,
8537
+ fetchContent,
8538
+ fetchHeader,
8539
+ previewSeq: hoveredSeq,
8540
+ pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
8541
+ hoverKey: hoverCat,
8542
+ onHoverKey: setHoverCat,
8543
+ onOpenCat: setFocusCat,
8544
+ nodeFocus,
8545
+ onNodeFocusHandled: clearNodeFocus,
8546
+ loadImage,
8547
+ detailState: source.detailState,
8548
+ onDetailRetry: source.retryDetail
8549
+ });
8264
8550
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8265
8551
  className: "lc-root",
8266
8552
  ref: rootRef,
@@ -8268,7 +8554,7 @@ window.__ModuleLoader__.load({
8268
8554
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8269
8555
  className: "lc-cols lc-head",
8270
8556
  children: [
8271
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsContext, {
8557
+ inSidebar ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsContext, {
8272
8558
  counts: data.counts ?? countsOfRecords(requests, events),
8273
8559
  toolCalls: data.toolCalls,
8274
8560
  images: data.images,
@@ -8276,144 +8562,18 @@ window.__ModuleLoader__.load({
8276
8562
  locale: activeLocale
8277
8563
  }),
8278
8564
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTokens, { usage }),
8279
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTiming, {
8280
- timing: data.timing ?? null,
8281
- locale: activeLocale
8282
- }),
8283
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginInfo, {})
8565
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTiming, { timing: data.timing ?? null }),
8566
+ inSidebar ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginInfo, {})
8284
8567
  ]
8285
8568
  }),
8286
8569
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8287
- className: "lc-cols",
8570
+ className: "lc-cols lc-cols-main",
8288
8571
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8289
8572
  className: "lc-col",
8290
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CurrentComposition, {
8291
- head,
8292
- subtitle,
8293
- hoverKey: hoverCat,
8294
- onHoverKey: setHoverCat
8295
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8296
- className: "lc-card",
8297
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8298
- className: "lc-card-title",
8299
- children: [
8300
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8301
- className: "lc-card-title-text",
8302
- children: t("trend.title")
8303
- }),
8304
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8305
- className: "lc-gran lc-trend-adaptive",
8306
- role: "group",
8307
- title: t("trend.adaptiveHint"),
8308
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8309
- type: "button",
8310
- className: "lc-gran-btn" + (adaptive ? " lc-gran-on" : ""),
8311
- onClick: () => {
8312
- setAdaptive((on) => !on);
8313
- },
8314
- children: t("trend.adaptive")
8315
- })
8316
- }),
8317
- focusCat !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8318
- className: "lc-card-sub",
8319
- children: t("trend.focus", { cat: kit.catLabel(focusCat) })
8320
- }) : null,
8321
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8322
- className: "lc-trend-ctl",
8323
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8324
- className: "lc-gran",
8325
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8326
- className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
8327
- onClick: () => {
8328
- setGranularity("step");
8329
- },
8330
- children: t("gran.step")
8331
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8332
- className: "lc-gran-btn" + (granularity === "turn" ? " lc-gran-on" : ""),
8333
- onClick: () => {
8334
- setGranularity("turn");
8335
- },
8336
- children: t("gran.turn")
8337
- })]
8338
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8339
- className: "lc-gran",
8340
- title: t("gran.modeHint"),
8341
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8342
- className: "lc-gran-btn" + (trendMode === "total" ? " lc-gran-on" : ""),
8343
- onClick: () => {
8344
- setTrendMode("total");
8345
- },
8346
- children: t("gran.total")
8347
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8348
- className: "lc-gran-btn" + (trendMode === "delta" ? " lc-gran-on" : ""),
8349
- onClick: () => {
8350
- setTrendMode("delta");
8351
- },
8352
- children: t("gran.delta")
8353
- })]
8354
- })]
8355
- })
8356
- ]
8357
- }), displayRequests.length === 0 ? detailReady ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8358
- className: "lc-empty",
8359
- children: t("trend.empty")
8360
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailNote, {
8361
- state: source.detailState === "failed" ? "failed" : "loading",
8362
- onRetry: source.retryDetail
8363
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrendChart, {
8364
- requests: displayRequests,
8365
- markers,
8366
- selectedSeq: pinnedReq ? pinnedReq.seq : null,
8367
- hoveredSeq,
8368
- activeTurn,
8369
- granularity,
8370
- mode: trendMode,
8371
- focusTurn,
8372
- hoverCat: trendHoverCat,
8373
- focusCat,
8374
- adaptive,
8375
- onSelect: setSelectedSeq,
8376
- onHover: setHoveredSeq,
8377
- onHoverTurn: setHoverTurn,
8378
- onPickTurn: (turn) => {
8379
- setGranularity("turn");
8380
- setFocusTurn(turn);
8381
- },
8382
- onFocusTurnHandled: () => {
8383
- setFocusTurn(null);
8384
- }
8385
- }, sessionId), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RequestDetail, {
8386
- request: activeReq,
8387
- prev: trendMode === "delta" && activeIdx >= 0 ? activeIdx > 0 ? displayRequests[activeIdx - 1] : null : void 0,
8388
- /* v8 ignore next 1 -- RequestDetail renders only when
8389
- displayRequests.length > 0, which forces activeReq
8390
- non-null via the activeIdx fallback above. */
8391
- marker: activeReq !== null ? markerOf(activeReq) : void 0,
8392
- brief,
8393
- convOf,
8394
- onLocate: locateNode,
8395
- hoverKey: trendHoverCat
8396
- })] })]
8397
- })]
8573
+ children: [compositionCard, trendCard]
8398
8574
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8399
8575
  className: "lc-col lc-col-browser",
8400
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextBrowser, {
8401
- data,
8402
- headers,
8403
- convNodes,
8404
- fetchContent,
8405
- fetchHeader,
8406
- previewSeq: hoveredSeq,
8407
- pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
8408
- hoverKey: hoverCat,
8409
- onHoverKey: setHoverCat,
8410
- onOpenCat: setFocusCat,
8411
- nodeFocus,
8412
- onNodeFocusHandled: clearNodeFocus,
8413
- loadImage,
8414
- detailState: source.detailState,
8415
- onDetailRetry: source.retryDetail
8416
- })
8576
+ children: browserCard
8417
8577
  })]
8418
8578
  }),
8419
8579
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -8560,6 +8720,99 @@ window.__ModuleLoader__.load({
8560
8720
  };
8561
8721
  }
8562
8722
  //#endregion
8723
+ //#region src/client/sidebar.ts
8724
+ /**
8725
+ * The right Sidebar's Context tab (dsh 0.1.5-alpha.2+).
8726
+ *
8727
+ * The tab reuses the Context conversation-view component VERBATIM: the
8728
+ * `sidebar.right.pane.tab` seat is session-scoped and delivers the same
8729
+ * framework standard kit (`sessionId`, `useProjection`, `useChat`, the locale
8730
+ * `t` seat) the `conversation.view` seat does, so the panel and the tab are
8731
+ * one component with one data path. The tab type contributes a guide entry, so
8732
+ * the sidebar's guide page offers "Context" and picking it opens the panel —
8733
+ * the product's own path, exactly as the shipped Files type does: a capsule of
8734
+ * glyph + title.
8735
+ *
8736
+ * OPTIONAL BY CONTRACT. `ctx.sidebarRightTabs` and the seat ship only on the
8737
+ * 0.1.5 line (0.1.5-alpha.2+ supported); the registration therefore rides a
8738
+ * DEFERRED inject (the plugin's hard injects stay `slots` + `locale`), so on
8739
+ * every older supported line the callback never fires, the plugin fiber never
8740
+ * pends, and nothing is registered. The registry is re-proved structurally and
8741
+ * the whole registration is guarded: a foreign or hostile registry (a throwing
8742
+ * `register`, a taken id/kind) leaves the sidebar without the tab instead of
8743
+ * taking the browser down.
8744
+ *
8745
+ * @module dsh-context/client/sidebar
8746
+ */
8747
+ /** The tab type's identity in the sidebar's tab system (also its body-seat key). */
8748
+ const SIDEBAR_CONTEXT_ID = "dsh-context";
8749
+ /**
8750
+ * The kind `openTab` names. Namespaced rather than the bare `context`: the
8751
+ * registry THROWS when a kind collides with another registration in a
8752
+ * non-coexisting band, and a foreign plugin may well own `context`.
8753
+ */
8754
+ const SIDEBAR_CONTEXT_KIND = "dsh-context";
8755
+ /** The guide capsule's position: after the shipped Files entry (order 10). */
8756
+ const GUIDE_ORDER = 20;
8757
+ /**
8758
+ * The guide capsule's glyph, read defensively: the icon is OPTIONAL in the
8759
+ * entry, and a primitives module that does not serve it — or whose namespace
8760
+ * THROWS on the read (an interop/mock shape does exactly that) — must cost the
8761
+ * glyph, never the whole tab.
8762
+ * @returns the icon component, or undefined to register the tab without one.
8763
+ */
8764
+ function guideIcon() {
8765
+ try {
8766
+ return _deepseek_ai_dsh_client_ui_primitives.IconContextInjectionOutline16;
8767
+ } catch {
8768
+ return;
8769
+ }
8770
+ }
8771
+ /**
8772
+ * Register the Context tab type and its body on the right Sidebar, if — and
8773
+ * only if — this harness serves the sidebar tab registry.
8774
+ * @param ctx - client root context carrying `slots` and the locale service.
8775
+ * @param view - the Context view component factory result (the same one the
8776
+ * conversation tab mounts).
8777
+ * @param t - the plugin-namespace translate; the label thunks read the active
8778
+ * locale at call time, so a language switch relabels the guide entry.
8779
+ * @param ns - the plugin's locale namespace, put on the body registration so
8780
+ * the framework synthesizes the `t` seat for the panel too.
8781
+ */
8782
+ function watchSidebarContextTab(ctx, view, t, ns) {
8783
+ ctx.inject(["sidebarRightTabs"], (raw) => {
8784
+ const injected = raw;
8785
+ try {
8786
+ const tabs = injected.sidebarRightTabs;
8787
+ if (tabs === void 0 || typeof tabs.register !== "function") return;
8788
+ const disposeType = tabs.register({
8789
+ id: SIDEBAR_CONTEXT_ID,
8790
+ kind: SIDEBAR_CONTEXT_KIND,
8791
+ title: () => t("tab"),
8792
+ guide: [{
8793
+ order: GUIDE_ORDER,
8794
+ title: () => t("tab"),
8795
+ icon: guideIcon()
8796
+ }]
8797
+ });
8798
+ const disposeBody = injected.slots.inject("sidebar.right.pane.tab", () => injected.slots.register({
8799
+ name: "sidebar.right.pane.tab",
8800
+ key: SIDEBAR_CONTEXT_ID,
8801
+ locale: ns
8802
+ }, (props) => view({
8803
+ ...props,
8804
+ host: "sidebar"
8805
+ })));
8806
+ return () => {
8807
+ disposeType();
8808
+ if (typeof disposeBody === "function") disposeBody();
8809
+ };
8810
+ } catch {
8811
+ return;
8812
+ }
8813
+ });
8814
+ }
8815
+ //#endregion
8563
8816
  //#region src/client/viewkit.ts
8564
8817
  function makeViewKit(t) {
8565
8818
  const { eventLabel, eventAt } = makeEventText(t);
@@ -8576,7 +8829,7 @@ window.__ModuleLoader__.load({
8576
8829
  }
8577
8830
  //#endregion
8578
8831
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/base.css.mjs
8579
- 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-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(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}";
8832
+ 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:12px;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}";
8580
8833
  const tagId$13 = "dsh-context/base.css";
8581
8834
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
8582
8835
  const tag = document.createElement("style");
@@ -8631,7 +8884,7 @@ window.__ModuleLoader__.load({
8631
8884
  }
8632
8885
  //#endregion
8633
8886
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/trendChart.css.mjs
8634
- 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}";
8887
+ 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-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=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}";
8635
8888
  const tagId$8 = "dsh-context/trendChart.css";
8636
8889
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$8) + "]") === null) {
8637
8890
  const tag = document.createElement("style");
@@ -8686,7 +8939,7 @@ window.__ModuleLoader__.load({
8686
8939
  }
8687
8940
  //#endregion
8688
8941
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/browser.css.mjs
8689
- const css$3 = ".lc-br-dna-ctl{flex:none;margin-left:0}.lc-br-bar-dna .lc-stacked-seg{box-shadow:inset -1px 0 0 var(--dsw-alias-bg-layer-1)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-retry:hover{filter:brightness(1.15)}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{width:8px;height:8px;transition:box-shadow var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;flex:none;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:flex}.lc-br-chev:before{content:\"\";width:5px;height:5px;transition:transform var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-bottom:1px solid;border-right:1px solid;transform:rotate(-45deg)}.lc-br-chev-on:before{transform:rotate(45deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-tool-plugin{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);border-color:#0000;border-radius:6px;font-size:12px}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}";
8942
+ const css$3 = ".lc-br-dna-ctl{flex:none;margin-left:0}.lc-br-bar-dna .lc-stacked-seg{background-image:linear-gradient(to right, transparent calc(100% - min(1px, 25%)), var(--dsw-alias-bg-layer-1) 0)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-retry:hover{filter:brightness(1.15)}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{width:8px;height:8px;transition:box-shadow var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;flex:none;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:flex}.lc-br-chev:before{content:\"\";width:5px;height:5px;transition:transform var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-bottom:1px solid;border-right:1px solid;transform:rotate(-45deg)}.lc-br-chev-on:before{transform:rotate(45deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-tool-plugin{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);border-color:#0000;border-radius:6px;font-size:12px}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}";
8690
8943
  const tagId$3 = "dsh-context/browser.css";
8691
8944
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
8692
8945
  const tag = document.createElement("style");
@@ -8775,6 +9028,7 @@ window.__ModuleLoader__.load({
8775
9028
  label: () => t("tab")
8776
9029
  }, (props) => (0, react.createElement)(ContextView, props));
8777
9030
  });
9031
+ watchSidebarContextTab(ctx, ContextView, t, NS);
8778
9032
  const ContextJump = makeContextJumpButton(kit);
8779
9033
  ctx.slots.inject("conversation.chat.assistant-actions", () => {
8780
9034
  return ctx.slots.register({