dsh-context 0.32.0 → 0.33.1

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
@@ -39,8 +39,6 @@ window.__ModuleLoader__.load({
39
39
  "plugin.hint": "The best DSH context plugin ⭐",
40
40
  "plugin.name": "插件",
41
41
  "plugin.github": "GitHub",
42
- "tools.top": "工具定义 Top:",
43
- "tools.more": "等 {n} 个",
44
42
  "trend.title": "上下文趋势",
45
43
  "gran.step": "步骤",
46
44
  "gran.turn": "轮次",
@@ -48,6 +46,7 @@ window.__ModuleLoader__.load({
48
46
  "settings.desc": "上下文面板的展示偏好",
49
47
  "settings.gran": "趋势图默认粒度",
50
48
  "settings.mode": "趋势图默认展示",
49
+ "settings.fileSort": "文件活动默认排序",
51
50
  "settings.expand": "展开",
52
51
  "settings.collapse": "收起",
53
52
  "settings.readOnly": "当前环境的设置为只读",
@@ -81,10 +80,30 @@ window.__ModuleLoader__.load({
81
80
  "kind.prune": "剪枝",
82
81
  "kind.model": "切换",
83
82
  "kind.mode": "模式",
84
- "nodes.title": "消息构成",
85
- "nodes.hint": "当前模型可见的消息,最新在前",
86
- "nodes.more": "… 更早的 {n} 条消息已省略",
87
- "nodes.empty": "当前没有模型可见的消息",
83
+ "files.title": "文件活动",
84
+ "files.scopeLatest": "截至最新 · 跟随趋势图的选择",
85
+ "files.kind.all": "全部",
86
+ "files.kind.read": "读取",
87
+ "files.kind.write": "写入",
88
+ "files.kind.search": "搜索",
89
+ "files.kind.image": "图片",
90
+ "files.chipTip": "{files} 个文件 · {ops} 次操作",
91
+ "files.search": "按路径过滤…",
92
+ "files.sort.count": "按次数",
93
+ "files.sort.latest": "按最新",
94
+ "files.sort.path": "按路径",
95
+ "files.sortTip": "文件排序:按操作次数(选中分类时按该分类的次数)、最近操作时间或路径",
96
+ "files.files": "{n} 个文件",
97
+ "files.deltaTip": "按 edit/write 调用参数估算的累计行数变化:+ 新增 / − 删除",
98
+ "files.unresolved": "… 另有 {n} 次文件操作的路径超出保留窗口",
99
+ "files.empty": "该范围内没有文件读取、写入或搜索操作",
100
+ "files.noMatch": "没有匹配当前过滤条件的文件",
101
+ "files.locate": "在上下文浏览器中查看此次操作",
102
+ "files.moreOps": "+{n} 次更早操作",
103
+ "files.errs": "{n} 次失败",
104
+ "files.form.image": "图片",
105
+ "files.form.dir": "目录",
106
+ "files.form.text": "文件",
88
107
  "loading": "正在读取会话日志…",
89
108
  "error": "上下文数据读取失败:",
90
109
  "error.retry": "重试",
@@ -126,6 +145,8 @@ window.__ModuleLoader__.load({
126
145
  "browser.noEpoch": "该步骤的头部内容(系统提示词 / 工具定义)不在保留范围内",
127
146
  "browser.noContent": "完整内容不在当前加载的消息窗口内(在聊天页加载更早历史后可查看)",
128
147
  "browser.loading": "正在从更早的会话历史加载完整内容…",
148
+ "browser.notInLog": "会话日志中未找到该条目的完整内容(可能已被压缩或清理)",
149
+ "browser.loadFailed": "内容加载失败,点击重试",
129
150
  "browser.preview": "预览",
130
151
  "tool.desc": "描述",
131
152
  "tool.params": "参数",
@@ -194,8 +215,6 @@ window.__ModuleLoader__.load({
194
215
  "plugin.hint": "The best DSH context plugin ⭐",
195
216
  "plugin.name": "Plugin",
196
217
  "plugin.github": "GitHub",
197
- "tools.top": "Top Tool Schemas:",
198
- "tools.more": "of {n}",
199
218
  "trend.title": "Context Trend",
200
219
  "gran.step": "Step",
201
220
  "gran.turn": "Turn",
@@ -203,6 +222,7 @@ window.__ModuleLoader__.load({
203
222
  "settings.desc": "Display preferences for the Context panel",
204
223
  "settings.gran": "Default trend granularity",
205
224
  "settings.mode": "Default trend display",
225
+ "settings.fileSort": "File Activity default sort",
206
226
  "settings.expand": "Expand",
207
227
  "settings.collapse": "Collapse",
208
228
  "settings.readOnly": "Settings are read-only in this environment",
@@ -236,10 +256,30 @@ window.__ModuleLoader__.load({
236
256
  "kind.prune": "Prune",
237
257
  "kind.model": "Switch",
238
258
  "kind.mode": "Mode",
239
- "nodes.title": "Messages",
240
- "nodes.hint": "currently model-visible, newest first",
241
- "nodes.more": "… {n} earlier messages omitted",
242
- "nodes.empty": "No model-visible messages right now",
259
+ "files.title": "File Activity",
260
+ "files.scopeLatest": "Up to latest · follows the trend chart pick",
261
+ "files.kind.all": "All",
262
+ "files.kind.read": "Read",
263
+ "files.kind.write": "Written",
264
+ "files.kind.search": "Searched",
265
+ "files.kind.image": "Images",
266
+ "files.chipTip": "{files} files · {ops} ops",
267
+ "files.search": "Filter by path…",
268
+ "files.sort.count": "Most active",
269
+ "files.sort.latest": "Latest",
270
+ "files.sort.path": "By path",
271
+ "files.sortTip": "Sort files by operation counts (of the selected kind when a kind chip is active), most recent activity, or path",
272
+ "files.files": "{n} files",
273
+ "files.deltaTip": "Cumulative line change estimated from edit/write call arguments: + added / − removed",
274
+ "files.unresolved": "… {n} more file ops have paths outside the retained window",
275
+ "files.empty": "No file reads, writes, or searches in this range",
276
+ "files.noMatch": "No files match the current filters",
277
+ "files.locate": "Reveal this operation in the Context Browser",
278
+ "files.moreOps": "+{n} earlier ops",
279
+ "files.errs": "{n} failed",
280
+ "files.form.image": "Image",
281
+ "files.form.dir": "Directory",
282
+ "files.form.text": "File",
243
283
  "loading": "Reading the session log…",
244
284
  "error": "Failed to read context data: ",
245
285
  "error.retry": "Retry",
@@ -281,6 +321,8 @@ window.__ModuleLoader__.load({
281
321
  "browser.noEpoch": "The header content (System Prompt / Tool Schemas) of this step is outside retention",
282
322
  "browser.noContent": "Full content is outside the loaded message window (load older history in Chat to view)",
283
323
  "browser.loading": "Loading full content from older session history…",
324
+ "browser.notInLog": "This item is not in the session log anymore (it may have been compacted away or cleared)",
325
+ "browser.loadFailed": "Load failed — click to retry",
284
326
  "browser.preview": "Preview",
285
327
  "tool.desc": "Description",
286
328
  "tool.params": "Parameters",
@@ -435,8 +477,7 @@ window.__ModuleLoader__.load({
435
477
  "assistant",
436
478
  "tool"
437
479
  ];
438
- /** Category key → chart color, built once from CATS. */
439
- const CAT_COLORS = Object.fromEntries(CATS.map((c) => [c.key, c.color]));
480
+ Object.fromEntries(CATS.map((c) => [c.key, c.color]));
440
481
  function partsOf(breakdown) {
441
482
  return CATS.map((c) => {
442
483
  return {
@@ -478,6 +519,9 @@ window.__ModuleLoader__.load({
478
519
  return CATS.map((c) => ({
479
520
  key: c.key,
480
521
  color: c.color,
522
+ /* v8 ignore next 1 -- `shares` is initialized with system/tools and both
523
+ foldSurface arms assign every MESSAGE_CATS key, so each key is always
524
+ defined; the fallback is defensive. */
481
525
  value: shares[c.key] ?? 0
482
526
  }));
483
527
  }
@@ -574,7 +618,7 @@ window.__ModuleLoader__.load({
574
618
  "assistant",
575
619
  "tool",
576
620
  "total"
577
- ].every((k) => typeof current[k] === "number") && Array.isArray(data.requests) && Array.isArray(data.events) && Array.isArray(data.nodes) && Array.isArray(data.archive) && Array.isArray(data.toolList)) return data;
621
+ ].every((k) => typeof current[k] === "number") && Array.isArray(data.requests) && Array.isArray(data.events) && Array.isArray(data.nodes) && Array.isArray(data.archive)) return data;
578
622
  const safeCurrent = current !== null && typeof current === "object" ? current : {};
579
623
  const cost = typeof data.cost === "object" && data.cost !== null && !Array.isArray(data.cost) ? data.cost : void 0;
580
624
  return {
@@ -591,7 +635,6 @@ window.__ModuleLoader__.load({
591
635
  tool: numOf(safeCurrent.tool),
592
636
  total: numOf(safeCurrent.total)
593
637
  },
594
- toolList: objectsOf(data.toolList),
595
638
  requests: objectsOf(data.requests),
596
639
  events: objectsOf(data.events),
597
640
  nodes: objectsOf(data.nodes),
@@ -666,6 +709,157 @@ window.__ModuleLoader__.load({
666
709
  return headers;
667
710
  }
668
711
  //#endregion
712
+ //#region src/client/historyPage.ts
713
+ /** Narrow one served row to a validated durable event envelope, or null. */
714
+ function eventOf(entry) {
715
+ if (entry === null || typeof entry !== "object") return null;
716
+ const ev = entry.event;
717
+ if (ev === null || typeof ev !== "object") return null;
718
+ const e = ev;
719
+ if (typeof e.type !== "string" || typeof e.seq !== "number" || !Number.isFinite(e.seq)) return null;
720
+ const data = e.data !== null && typeof e.data === "object" ? e.data : {};
721
+ return {
722
+ type: e.type,
723
+ seq: e.seq,
724
+ data
725
+ };
726
+ }
727
+ /** All string texts of an event's message-content shape joined (compaction summaries). */
728
+ function textOf(blocks) {
729
+ if (!Array.isArray(blocks)) return null;
730
+ let out = "";
731
+ for (const b of blocks) {
732
+ const text = b !== null && typeof b === "object" ? b.text : void 0;
733
+ if (typeof text === "string") out += text;
734
+ }
735
+ return out.trim() === "" ? null : out;
736
+ }
737
+ /**
738
+ * One assistant content block → the snapshot block vocabulary the browser
739
+ * already renders (`kind`: text/reasoning/image/tool-call); unmappable
740
+ * blocks pass through raw and degrade to the generic JSON section.
741
+ */
742
+ function assistantBlockOf(block) {
743
+ const b = block !== null && typeof block === "object" ? block : null;
744
+ switch (b?.type) {
745
+ case "text":
746
+ case "reasoning": return {
747
+ kind: b.type,
748
+ ...typeof b.text === "string" ? { text: b.text } : {}
749
+ };
750
+ case "image": return {
751
+ kind: "image",
752
+ ...b.attachment !== void 0 ? { attachment: b.attachment } : {}
753
+ };
754
+ case "tool-call": return {
755
+ kind: "tool-call",
756
+ name: typeof b.name === "string" ? b.name : "?",
757
+ argsRaw: b.arguments
758
+ };
759
+ default: return block !== null && typeof block === "object" ? block : { value: block };
760
+ }
761
+ }
762
+ /**
763
+ * Map one history page into joined conversation nodes keyed by their event
764
+ * seq — the display subset of the browser's join: user messages, assistant
765
+ * blocks, tool results paired with their in-page call head, and compaction
766
+ * checkpoints paired with their summary event. Everything else (headers,
767
+ * boundaries, chunks, bare calls) projects to nothing.
768
+ */
769
+ function pageNodesOf(entries) {
770
+ const nodes = /* @__PURE__ */ new Map();
771
+ const calls = /* @__PURE__ */ new Map();
772
+ const summaries = /* @__PURE__ */ new Map();
773
+ for (const entry of entries) {
774
+ const ev = eventOf(entry);
775
+ if (ev === null) continue;
776
+ const { type, seq, data } = ev;
777
+ if (type === "tool/call") {
778
+ if (typeof data.callId === "string") calls.set(data.callId, {
779
+ name: typeof data.name === "string" ? data.name : "?",
780
+ argsRaw: typeof data.arguments === "string" ? data.arguments : ""
781
+ });
782
+ continue;
783
+ }
784
+ if (type === "compaction/summary") {
785
+ if (typeof data.compactionId === "string") {
786
+ const summary = textOf(data.summary);
787
+ if (summary !== null) summaries.set(data.compactionId, summary);
788
+ }
789
+ continue;
790
+ }
791
+ if (type === "user/message") {
792
+ const source = data.source !== null && typeof data.source === "object" ? data.source : null;
793
+ const compactionId = source !== null && source.kind === "plugin" && typeof source.compactionId === "string" ? source.compactionId : null;
794
+ if (compactionId !== null) {
795
+ nodes.set(seq, {
796
+ kind: "compaction",
797
+ seq,
798
+ summary: summaries.get(compactionId) ?? null
799
+ });
800
+ continue;
801
+ }
802
+ nodes.set(seq, {
803
+ kind: "user",
804
+ seq,
805
+ content: Array.isArray(data.content) ? data.content : []
806
+ });
807
+ continue;
808
+ }
809
+ if (type === "assistant/message") {
810
+ const message = data.message !== null && typeof data.message === "object" ? data.message : null;
811
+ const content = message !== null && Array.isArray(message.content) ? message.content : [];
812
+ nodes.set(seq, {
813
+ kind: "assistant",
814
+ seq,
815
+ blocks: content.map(assistantBlockOf)
816
+ });
817
+ continue;
818
+ }
819
+ if (type === "tool/result") {
820
+ const message = data.message !== null && typeof data.message === "object" ? data.message : null;
821
+ const source = message?.source !== null && typeof message?.source === "object" ? message.source : null;
822
+ const first = Array.isArray(message?.content) ? message.content[0] : void 0;
823
+ const block = first !== null && typeof first === "object" ? first : null;
824
+ const callId = typeof source?.callId === "string" ? source.callId : typeof block?.toolCallId === "string" ? block.toolCallId : null;
825
+ const call = callId !== null ? calls.get(callId) ?? null : null;
826
+ nodes.set(seq, {
827
+ kind: "tool-result",
828
+ seq,
829
+ call,
830
+ content: block !== null && Array.isArray(block.content) ? block.content : [],
831
+ isError: block?.isError === true || data.error === true
832
+ });
833
+ continue;
834
+ }
835
+ }
836
+ return nodes;
837
+ }
838
+ /**
839
+ * Build the browser's per-session fetcher over the shared api client.
840
+ * Undefined when the connection face or the history verb is absent (older
841
+ * hosts) — the caller keeps its static preview-plus-hint degradation. Found
842
+ * nodes cache in the closure: one mount re-reading a row never re-fetches.
843
+ */
844
+ function makeContentFetcher(ctx, sessionId) {
845
+ const sessions = ctx.get("connection")?.api?.sessions;
846
+ if (typeof sessions?.history !== "function") return void 0;
847
+ const cache = /* @__PURE__ */ new Map();
848
+ return async (seq) => {
849
+ const hit = cache.get(seq);
850
+ if (hit !== void 0) return hit;
851
+ const result = (await sessions.history({
852
+ sessionId,
853
+ beforeSeq: seq + 1
854
+ })).result;
855
+ if (result === null || typeof result !== "object" || result.ok !== true || result.value === null || typeof result.value !== "object") throw new Error("history rpc failed");
856
+ const events = result.value.events;
857
+ const node = pageNodesOf(Array.isArray(events) ? events : []).get(seq) ?? null;
858
+ if (node !== null) cache.set(seq, node);
859
+ return node;
860
+ };
861
+ }
862
+ //#endregion
669
863
  //#region src/client/assemble.ts
670
864
  /** The header epoch in force at `seq` (last logged before it), or the newest. */
671
865
  function headerAt(headers, seq) {
@@ -767,24 +961,6 @@ window.__ModuleLoader__.load({
767
961
  return t("node.nonText");
768
962
  };
769
963
  }
770
- function makeNodeList(kit) {
771
- const { t, fmt, fmtTime } = kit;
772
- const nodeText = makeNodeText(kit);
773
- return function NodeList(props) {
774
- if (props.nodes.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("nodes.empty"));
775
- const rows = props.nodes.slice().reverse();
776
- return /* @__PURE__ */ React.createElement("div", { className: "lc-nodes" }, props.dropped > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-nodes-more" }, t("nodes.more", { n: props.dropped })) : null, rows.map((n) => {
777
- const text = nodeText(n);
778
- return /* @__PURE__ */ React.createElement("div", {
779
- key: n.seq,
780
- className: "lc-node"
781
- }, /* @__PURE__ */ React.createElement("i", { style: { background: CAT_COLORS[n.cat] || "#999" } }), /* @__PURE__ */ React.createElement("span", {
782
- className: "lc-node-preview",
783
- title: text
784
- }, text), typeof n.time === "number" ? /* @__PURE__ */ React.createElement("span", { className: "lc-node-time" }, fmtTime(n.time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-node-tokens" }, fmt(n.tokens)));
785
- }));
786
- };
787
- }
788
964
  //#endregion
789
965
  //#region src/client/format.ts
790
966
  /** `fmt`: the k/M suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
@@ -865,6 +1041,8 @@ window.__ModuleLoader__.load({
865
1041
  let bestWidth;
866
1042
  if (gridW < 1) {
867
1043
  let rows = floorDiv(budget - 2, 2);
1044
+ /* v8 ignore else -- budget enters at 381 and the tall solve always fits
1045
+ (never decrements), so rows is always odd here; parity-defensive. */
868
1046
  if (rows % 2 === 1) rows -= 1;
869
1047
  bestWidth = unit;
870
1048
  bestHeight = rows * unit;
@@ -944,6 +1122,8 @@ window.__ModuleLoader__.load({
944
1122
  }
945
1123
  return null;
946
1124
  } catch {
1125
+ /* v8 ignore next -- the only throw site is the clamped-away budget guard
1126
+ above; the catch keeps a hostile dimension from ever breaking the fold. */
947
1127
  return null;
948
1128
  }
949
1129
  }
@@ -1189,18 +1369,22 @@ window.__ModuleLoader__.load({
1189
1369
  */
1190
1370
  function Section(props) {
1191
1371
  const right = props.actions !== void 0 || props.meta !== void 0;
1192
- return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", { className: props.labelClass }, props.label), right ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-right" }, props.meta ?? null, props.actions ?? null) : null, props.count !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-count" }, props.count) : null), props.children);
1372
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", { className: props.labelClass }, props.label), right ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-right" }, props.meta, props.actions) : null, props.count !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-count" }, props.count) : null), props.children);
1373
+ }
1374
+ function lineCountOf(text) {
1375
+ return text.split(/\r\n|\r|\n/).length;
1193
1376
  }
1194
1377
  function TextSection(props) {
1195
1378
  const { rich } = props;
1196
1379
  const [mode, setMode] = rich.useRichMode();
1380
+ const lineCount = React.useMemo(() => lineCountOf(props.text), [props.text]);
1197
1381
  return /* @__PURE__ */ React.createElement(Section, {
1198
1382
  label: props.label,
1199
1383
  actions: /* @__PURE__ */ React.createElement(rich.RichSwitch, {
1200
1384
  mode,
1201
1385
  onPick: setMode
1202
1386
  }),
1203
- meta: props.meta
1387
+ meta: /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-meta" }, props.lines(lineCount))
1204
1388
  }, /* @__PURE__ */ React.createElement(rich.RichText, {
1205
1389
  text: props.text,
1206
1390
  mode
@@ -1238,7 +1422,8 @@ window.__ModuleLoader__.load({
1238
1422
  return /* @__PURE__ */ React.createElement(React.Fragment, null, props.description !== void 0 ? /* @__PURE__ */ React.createElement(TextSection, {
1239
1423
  label: props.labels.desc,
1240
1424
  text: props.description,
1241
- rich
1425
+ rich,
1426
+ lines: props.lines
1242
1427
  }) : null, params !== null && rows.length > 0 ? /* @__PURE__ */ React.createElement(Section, {
1243
1428
  label: props.labels.title,
1244
1429
  count: rows.length
@@ -1257,8 +1442,8 @@ window.__ModuleLoader__.load({
1257
1442
  }
1258
1443
  /**
1259
1444
  * Both block vocabularies normalize here — raw durable blocks (`type`: text/reasoning/tool-call/tool-result/image) and snapshot assistant
1260
- * blocks (`kind`: text/reasoning/tool-call/image, argsRaw). Consecutive images group into one grid; tool-result payloads carry the Raw/MD
1261
- * switch + line count; nested tool-result blocks flatten into the same flow.
1445
+ * blocks (`kind`: text/reasoning/tool-call/image, argsRaw). Consecutive images group into one grid; every
1446
+ * rich text block carries the Raw/MD switch + line count; nested tool-result blocks flatten into the same flow.
1262
1447
  */
1263
1448
  function BlocksBody(props) {
1264
1449
  const { rich, img, labels } = props;
@@ -1294,7 +1479,7 @@ window.__ModuleLoader__.load({
1294
1479
  label,
1295
1480
  text: blk.text,
1296
1481
  rich,
1297
- meta: props.textLabel === labels.result ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-meta" }, labels.lines(blk.text.split("\n").length)) : void 0
1482
+ lines: labels.lines
1298
1483
  }));
1299
1484
  continue;
1300
1485
  }
@@ -1412,6 +1597,8 @@ window.__ModuleLoader__.load({
1412
1597
  }
1413
1598
  function CallArgRow(props) {
1414
1599
  const v = props.value;
1600
+ /* v8 ignore next 2 -- the only caller maps Object.keys of a JSON.parse'd
1601
+ object, which never holds undefined values; defensive. */
1415
1602
  const text = typeof v === "string" ? v : v === void 0 ? "" : JSON.stringify(v);
1416
1603
  return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-arg-row" }, /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-name" }, props.name), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-arg-val" }, text));
1417
1604
  }
@@ -1422,7 +1609,8 @@ window.__ModuleLoader__.load({
1422
1609
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TextSection, {
1423
1610
  label: labels.content,
1424
1611
  text: node.text,
1425
- rich
1612
+ rich,
1613
+ lines: labels.lines
1426
1614
  }), /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
1427
1615
  }
1428
1616
  if (conv.kind === "assistant" && Array.isArray(conv.blocks)) return /* @__PURE__ */ React.createElement(BlocksBody, {
@@ -1452,7 +1640,8 @@ window.__ModuleLoader__.load({
1452
1640
  if (conv.kind === "compaction") return typeof conv.summary === "string" && conv.summary !== "" ? /* @__PURE__ */ React.createElement(TextSection, {
1453
1641
  label: labels.summary,
1454
1642
  text: conv.summary,
1455
- rich
1643
+ rich,
1644
+ lines: labels.lines
1456
1645
  }) : /* @__PURE__ */ React.createElement(React.Fragment, null);
1457
1646
  if (Array.isArray(conv.content)) return /* @__PURE__ */ React.createElement(BlocksBody, {
1458
1647
  blocks: conv.content,
@@ -1483,52 +1672,51 @@ window.__ModuleLoader__.load({
1483
1672
  const nodeText = makeNodeText(kit);
1484
1673
  const rich = makeRichText(kit);
1485
1674
  const ImageCard = makeImageCard(kit);
1486
- const MAX_AUTO_PAGES = 20;
1675
+ const lineLabel = (n) => t(n === 1 ? "block.line" : "block.lines", { n });
1487
1676
  return function ContextBrowser(props) {
1488
1677
  const { data, headers } = props;
1489
1678
  const [sel, setSel] = React.useState("live");
1490
1679
  const [openCat, setOpenCat] = React.useState(null);
1491
1680
  const [openElem, setOpenElem] = React.useState(null);
1492
1681
  const convNodes = typeof props.useSession === "function" ? props.useSession((s) => s.nodes) : void 0;
1682
+ const [fetched, setFetched] = React.useState(() => /* @__PURE__ */ new Map());
1493
1683
  const bySeq = React.useMemo(() => {
1494
1684
  const m = /* @__PURE__ */ new Map();
1495
1685
  for (const n of convNodes ?? []) m.set(n.seq, n);
1686
+ for (const [seq, n] of fetched) if (!m.has(seq)) m.set(seq, n);
1496
1687
  return m;
1497
- }, [convNodes]);
1498
- const hasMore = typeof props.useSession === "function" ? props.useSession((s) => s.hasMore === true) : false;
1499
- const loadingOlder = typeof props.useSession === "function" ? props.useSession((s) => s.loadingOlder === true) : false;
1688
+ }, [convNodes, fetched]);
1500
1689
  const openSeq = openElem !== null && openElem.startsWith("n") ? Number(openElem.slice(1)) : null;
1501
1690
  const missingSeq = openSeq !== null && !bySeq.has(openSeq) ? openSeq : null;
1502
- const [exhausted, setExhausted] = React.useState(false);
1503
- const pagesRef = React.useRef(0);
1504
- React.useEffect(() => {
1505
- pagesRef.current = 0;
1506
- setExhausted(false);
1507
- }, [openElem]);
1508
- const loadOlderHistory = props.loadOlderHistory;
1509
- React.useEffect(() => {
1510
- if (missingSeq === null || !hasMore || loadingOlder || exhausted) return;
1511
- if (loadOlderHistory === void 0) return;
1512
- if (pagesRef.current >= MAX_AUTO_PAGES) {
1513
- setExhausted(true);
1514
- return;
1515
- }
1516
- pagesRef.current += 1;
1517
- loadOlderHistory();
1518
- }, [
1519
- missingSeq,
1520
- hasMore,
1521
- loadingOlder,
1522
- exhausted,
1523
- bySeq,
1524
- loadOlderHistory
1525
- ]);
1691
+ const fetchContent = props.fetchContent;
1692
+ const [missState, setMissState] = React.useState("idle");
1693
+ const [retry, setRetry] = React.useState(0);
1526
1694
  React.useEffect(() => {
1527
- if (!hasMore && missingSeq !== null && !exhausted) setExhausted(true);
1695
+ if (missingSeq === null || fetchContent === void 0) return;
1696
+ let live = true;
1697
+ setMissState("loading");
1698
+ fetchContent(missingSeq).then((node) => {
1699
+ if (!live) return;
1700
+ if (node === null) {
1701
+ setMissState("absent");
1702
+ return;
1703
+ }
1704
+ setFetched((prev) => {
1705
+ const next = new Map(prev);
1706
+ next.set(missingSeq, node);
1707
+ return next;
1708
+ });
1709
+ setMissState("idle");
1710
+ }, () => {
1711
+ if (live) setMissState("failed");
1712
+ });
1713
+ return () => {
1714
+ live = false;
1715
+ };
1528
1716
  }, [
1529
- hasMore,
1530
1717
  missingSeq,
1531
- exhausted
1718
+ fetchContent,
1719
+ retry
1532
1720
  ]);
1533
1721
  const pinSeq = props.pinSeq;
1534
1722
  React.useEffect(() => {
@@ -1536,14 +1724,6 @@ window.__ModuleLoader__.load({
1536
1724
  setOpenCat(null);
1537
1725
  setOpenElem(null);
1538
1726
  }, [pinSeq]);
1539
- const toolFocus = props.toolFocus;
1540
- React.useEffect(() => {
1541
- if (toolFocus === null || toolFocus === void 0) return;
1542
- setSel("live");
1543
- setOpenCat("tools");
1544
- setOpenElem(toolFocus.tool !== void 0 ? "tool:" + toolFocus.tool : null);
1545
- if (props.onToolFocusHandled !== void 0) props.onToolFocusHandled();
1546
- }, [toolFocus, props.onToolFocusHandled]);
1547
1727
  const rootRef = React.useRef(null);
1548
1728
  const focusScrollRef = React.useRef(false);
1549
1729
  const nodeFocus = props.nodeFocus;
@@ -1560,7 +1740,13 @@ window.__ModuleLoader__.load({
1560
1740
  focusScrollRef.current = false;
1561
1741
  rootRef.current?.querySelector(".lc-br-elem-on")?.scrollIntoView({ block: "nearest" });
1562
1742
  });
1563
- const awaiting = missingSeq !== null && !exhausted && loadOlderHistory !== void 0 && hasMore;
1743
+ const missNote = fetchContent === void 0 ? t("browser.noContent") : missState === "loading" ? t("browser.loading") : missState === "absent" ? t("browser.notInLog") : missState === "failed" ? /* @__PURE__ */ React.createElement("button", {
1744
+ type: "button",
1745
+ className: "lc-br-retry",
1746
+ onClick: () => {
1747
+ setRetry((r) => r + 1);
1748
+ }
1749
+ }, t("browser.loadFailed")) : t("browser.noContent");
1564
1750
  const requests = data.requests;
1565
1751
  const hoverReq = props.previewSeq !== null && props.previewSeq !== void 0 ? requests.find((r) => r.seq === props.previewSeq) ?? null : null;
1566
1752
  const req = hoverReq ?? (sel === "live" ? null : requests.find((r) => r.seq === sel) ?? null);
@@ -1583,8 +1769,13 @@ window.__ModuleLoader__.load({
1583
1769
  const toolCount = (c) => countOf(view, byCat, c);
1584
1770
  const toggleCat = (c) => {
1585
1771
  if (!(toolCount(c) > 0 || (c === "system" || c === "tools") && view.header === null)) return;
1586
- setOpenCat(openCat === c ? null : c);
1587
- setOpenElem(null);
1772
+ if (openCat === c) {
1773
+ setOpenCat(null);
1774
+ setOpenElem(null);
1775
+ return;
1776
+ }
1777
+ setOpenCat(c);
1778
+ setOpenElem(c === "system" && view.header?.system !== void 0 ? "sys" : null);
1588
1779
  };
1589
1780
  const toggleElem = (key) => {
1590
1781
  setOpenElem(openElem === key ? null : key);
@@ -1613,11 +1804,14 @@ window.__ModuleLoader__.load({
1613
1804
  if (c === "system") {
1614
1805
  if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
1615
1806
  const system = view.header.system;
1807
+ /* v8 ignore next 1 -- the category opens only when its count > 0
1808
+ (countOf ⟺ system defined); the undefined arm is defensive. */
1616
1809
  if (system === void 0) return null;
1617
1810
  return elemRow("sys", null, system.replace(/\s+/g, " ").trim().slice(0, 80), breakdown.system, void 0, /* @__PURE__ */ React.createElement(TextSection, {
1618
1811
  label: catLabel("system"),
1619
1812
  text: system,
1620
- rich
1813
+ rich,
1814
+ lines: lineLabel
1621
1815
  }));
1622
1816
  }
1623
1817
  if (c === "tools") {
@@ -1634,6 +1828,7 @@ window.__ModuleLoader__.load({
1634
1828
  description: tool.description,
1635
1829
  schema: tool.schema,
1636
1830
  rich,
1831
+ lines: lineLabel,
1637
1832
  labels
1638
1833
  }));
1639
1834
  });
@@ -1673,10 +1868,10 @@ window.__ModuleLoader__.load({
1673
1868
  summary: t("block.summary"),
1674
1869
  images: t("attach.images"),
1675
1870
  other: t("attach.other"),
1676
- lines: (n) => t(n === 1 ? "block.line" : "block.lines", { n }),
1871
+ lines: lineLabel,
1677
1872
  callState: (err, exit) => /* @__PURE__ */ React.createElement("span", { className: "lc-ts-call-state " + (err ? "lc-ts-call-err" : "lc-ts-call-ok") }, /* @__PURE__ */ React.createElement("i", null), err ? t("call.fail") + (exit !== null ? " · " + t("call.exit", { n: exit }) : "") : t("call.ok"))
1678
1873
  },
1679
- hint: conv === void 0 && awaiting ? t("browser.loading") : t("browser.noContent")
1874
+ hint: conv === void 0 ? missNote : t("browser.noContent")
1680
1875
  }), rowErr);
1681
1876
  });
1682
1877
  };
@@ -1719,12 +1914,15 @@ window.__ModuleLoader__.load({
1719
1914
  }, /* @__PURE__ */ React.createElement("button", {
1720
1915
  type: "button",
1721
1916
  className: "lc-br-cat-row" + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
1917
+ /* v8 ignore start -- the handlers exist only when linked,
1918
+ and linked already requires onHoverKey defined (above). */
1722
1919
  onMouseEnter: linked ? () => {
1723
1920
  if (props.onHoverKey !== void 0) props.onHoverKey(c.key);
1724
1921
  } : void 0,
1725
1922
  onMouseLeave: linked ? () => {
1726
1923
  if (props.onHoverKey !== void 0) props.onHoverKey(null);
1727
1924
  } : void 0,
1925
+ /* v8 ignore stop */
1728
1926
  onClick: () => {
1729
1927
  toggleCat(c.key);
1730
1928
  }
@@ -1880,26 +2078,7 @@ window.__ModuleLoader__.load({
1880
2078
  parts: head.parts,
1881
2079
  hoverKey: props.hoverKey,
1882
2080
  onHoverKey: props.onHoverKey
1883
- }), props.tools !== void 0 && props.tools.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-tools" }, /* @__PURE__ */ React.createElement("button", {
1884
- type: "button",
1885
- className: "lc-tools-label",
1886
- onClick: () => {
1887
- if (props.onToolFocus !== void 0) props.onToolFocus({});
1888
- }
1889
- }, t("tools.top")), props.tools.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => /* @__PURE__ */ React.createElement("button", {
1890
- key: tool.name,
1891
- type: "button",
1892
- className: "lc-tool-chip",
1893
- onClick: () => {
1894
- if (props.onToolFocus !== void 0) props.onToolFocus({ tool: tool.name });
1895
- }
1896
- }, tool.name + " " + fmt(tool.tokens))), props.tools.length > 5 ? /* @__PURE__ */ React.createElement("button", {
1897
- type: "button",
1898
- className: "lc-tools-more",
1899
- onClick: () => {
1900
- if (props.onToolFocus !== void 0) props.onToolFocus({});
1901
- }
1902
- }, t("tools.more", { n: props.tools.length })) : null) : null);
2081
+ }));
1903
2082
  };
1904
2083
  }
1905
2084
  //#endregion
@@ -1943,7 +2122,7 @@ window.__ModuleLoader__.load({
1943
2122
  const breakdown = typeof props.useProjection === "function" ? contextBreakdownOf(props.useProjection("contextBreakdown")) : null;
1944
2123
  const headers = typeof props.useProjection === "function" ? headersOf(props.useProjection("contextHeaders")) : null;
1945
2124
  const [hoverCat, setHoverCat] = React.useState(null);
1946
- const [toolFocus, setToolFocus] = React.useState(null);
2125
+ const fetchContent = React.useMemo(() => sessionId !== "" ? makeContentFetcher(ctx, sessionId) : void 0, [ctx, sessionId]);
1947
2126
  const close = React.useCallback(() => {
1948
2127
  if (sessionId === "") return;
1949
2128
  modalStoreOf(sessionId).set(false);
@@ -1986,20 +2165,14 @@ window.__ModuleLoader__.load({
1986
2165
  head,
1987
2166
  subtitle,
1988
2167
  hoverKey: hoverCat,
1989
- onHoverKey: setHoverCat,
1990
- tools: data.toolList,
1991
- onToolFocus: setToolFocus
2168
+ onHoverKey: setHoverCat
1992
2169
  }), /* @__PURE__ */ React.createElement(ContextBrowser, {
1993
2170
  data,
1994
2171
  headers,
1995
2172
  useSession: props.useSession,
1996
- loadOlderHistory: props.loadOlderHistory,
2173
+ fetchContent,
1997
2174
  hoverKey: hoverCat,
1998
- onHoverKey: setHoverCat,
1999
- toolFocus,
2000
- onToolFocusHandled: () => {
2001
- setToolFocus(null);
2002
- }
2175
+ onHoverKey: setHoverCat
2003
2176
  }))));
2004
2177
  }
2005
2178
  return function ContextModal(props) {
@@ -2082,6 +2255,27 @@ window.__ModuleLoader__.load({
2082
2255
  onPick: (id) => {
2083
2256
  props.set?.("defaultTrendMode", id);
2084
2257
  }
2258
+ }), /* @__PURE__ */ React.createElement(PrefRow, {
2259
+ label: t("settings.fileSort"),
2260
+ value: state.fileSort,
2261
+ disabled,
2262
+ options: [
2263
+ {
2264
+ id: "count",
2265
+ label: t("files.sort.count")
2266
+ },
2267
+ {
2268
+ id: "latest",
2269
+ label: t("files.sort.latest")
2270
+ },
2271
+ {
2272
+ id: "path",
2273
+ label: t("files.sort.path")
2274
+ }
2275
+ ],
2276
+ onPick: (id) => {
2277
+ props.set?.("defaultFileSort", id);
2278
+ }
2085
2279
  })) : null);
2086
2280
  };
2087
2281
  }
@@ -2092,7 +2286,8 @@ window.__ModuleLoader__.load({
2092
2286
  const v = value;
2093
2287
  return {
2094
2288
  ...v.defaultGranularity === "step" || v.defaultGranularity === "turn" ? { granularity: v.defaultGranularity } : {},
2095
- ...v.defaultTrendMode === "total" || v.defaultTrendMode === "delta" ? { mode: v.defaultTrendMode } : {}
2289
+ ...v.defaultTrendMode === "total" || v.defaultTrendMode === "delta" ? { mode: v.defaultTrendMode } : {},
2290
+ ...v.defaultFileSort === "count" || v.defaultFileSort === "latest" || v.defaultFileSort === "path" ? { fileSort: v.defaultFileSort } : {}
2096
2291
  };
2097
2292
  }
2098
2293
  function createContextSettings() {
@@ -2100,12 +2295,13 @@ window.__ModuleLoader__.load({
2100
2295
  status: "loading",
2101
2296
  granularity: "step",
2102
2297
  mode: "total",
2298
+ fileSort: "count",
2103
2299
  writable: false
2104
2300
  };
2105
2301
  let scope;
2106
2302
  const listeners = /* @__PURE__ */ new Set();
2107
2303
  const publish = (next) => {
2108
- if (next.status === state.status && next.granularity === state.granularity && next.mode === state.mode && next.writable === state.writable) return;
2304
+ if (next.status === state.status && next.granularity === state.granularity && next.mode === state.mode && next.fileSort === state.fileSort && next.writable === state.writable) return;
2109
2305
  state = next;
2110
2306
  for (const listener of listeners) listener();
2111
2307
  };
@@ -2121,6 +2317,7 @@ window.__ModuleLoader__.load({
2121
2317
  },
2122
2318
  defaultGranularity: () => state.granularity,
2123
2319
  defaultTrendMode: () => state.mode,
2320
+ defaultFileSort: () => state.fileSort,
2124
2321
  attach(bound) {
2125
2322
  scope = bound;
2126
2323
  const sync = () => {
@@ -2130,6 +2327,7 @@ window.__ModuleLoader__.load({
2130
2327
  status: snap.status === "ready" || snap.status === "unavailable" ? snap.status : "loading",
2131
2328
  granularity: prefs.granularity ?? state.granularity,
2132
2329
  mode: prefs.mode ?? state.mode,
2330
+ fileSort: prefs.fileSort ?? state.fileSort,
2133
2331
  writable: snap.writable
2134
2332
  });
2135
2333
  };
@@ -2213,6 +2411,190 @@ window.__ModuleLoader__.load({
2213
2411
  return m;
2214
2412
  }
2215
2413
  //#endregion
2414
+ //#region src/client/fileActivity.ts
2415
+ const KIND_BY_TOOL = {
2416
+ read: "read",
2417
+ read_image: "read",
2418
+ write: "write",
2419
+ edit: "write",
2420
+ grep: "search",
2421
+ glob: "search"
2422
+ };
2423
+ /** The file purpose of a tool, or null for non-file tools (bash, web_search…). */
2424
+ function kindOfTool(tool) {
2425
+ if (tool === void 0) return null;
2426
+ return KIND_BY_TOOL[tool] ?? null;
2427
+ }
2428
+ /**
2429
+ * The operation's target path: the path-ish argument of read/write tools;
2430
+ * for searches the narrowing `path`, else the pattern itself (a pathless
2431
+ * grep/glob's target IS the pattern — the workspace-wide search text).
2432
+ */
2433
+ function pathOfArgs(tool, args) {
2434
+ if (args === null) return null;
2435
+ if (tool === "grep" || tool === "glob") {
2436
+ const p = args.path;
2437
+ if (typeof p === "string" && p !== "") return p;
2438
+ const pattern = args.pattern;
2439
+ return typeof pattern === "string" && pattern !== "" ? pattern : null;
2440
+ }
2441
+ for (const k of [
2442
+ "file_path",
2443
+ "filePath",
2444
+ "path"
2445
+ ]) {
2446
+ const v = args[k];
2447
+ if (typeof v === "string" && v !== "") return v;
2448
+ }
2449
+ return null;
2450
+ }
2451
+ /** Rendered line count: '' is 0, a trailing newline closes its own line. */
2452
+ function linesOf(s) {
2453
+ if (s === "") return 0;
2454
+ let n = 0;
2455
+ for (let i = 0; i < s.length; i++) if (s[i] === "\n") n++;
2456
+ return s.endsWith("\n") ? n : n + 1;
2457
+ }
2458
+ /**
2459
+ * The signed line footprint of one call: an edit removes its old string and
2460
+ * adds its new one; a write adds its content (the pre-existing body, if any,
2461
+ * is unknowable from the arguments — the estimate stays honest about that).
2462
+ * Callers reach here only with parsed args (a null parse yields no path).
2463
+ */
2464
+ function deltaOf(tool, args) {
2465
+ if (tool === "edit") {
2466
+ const oldS = args.old_string;
2467
+ const newS = args.new_string;
2468
+ return {
2469
+ added: typeof newS === "string" ? linesOf(newS) : 0,
2470
+ removed: typeof oldS === "string" ? linesOf(oldS) : 0
2471
+ };
2472
+ }
2473
+ if (tool === "write") {
2474
+ const content = args.content;
2475
+ return {
2476
+ added: typeof content === "string" ? linesOf(content) : 0,
2477
+ removed: 0
2478
+ };
2479
+ }
2480
+ return {
2481
+ added: 0,
2482
+ removed: 0
2483
+ };
2484
+ }
2485
+ const IMAGE_EXT = /\.(png|jpe?g|gif|webp|svg|bmp|ico|avif)$/i;
2486
+ /** The file's form — multimodal reads and image extensions scan apart; a trailing slash marks a directory target. */
2487
+ function formOf(tool, path) {
2488
+ if (tool === "read_image" || IMAGE_EXT.test(path)) return "image";
2489
+ if (path.endsWith("/")) return "dir";
2490
+ return "text";
2491
+ }
2492
+ /** Fold every served tool-result node into per-file activity. */
2493
+ function activityOf(nodes, convOf, before) {
2494
+ const totals = {
2495
+ read: {
2496
+ files: 0,
2497
+ ops: 0
2498
+ },
2499
+ write: {
2500
+ files: 0,
2501
+ ops: 0
2502
+ },
2503
+ search: {
2504
+ files: 0,
2505
+ ops: 0
2506
+ },
2507
+ image: {
2508
+ files: 0,
2509
+ ops: 0
2510
+ },
2511
+ added: 0,
2512
+ removed: 0
2513
+ };
2514
+ const byPath = /* @__PURE__ */ new Map();
2515
+ let unresolved = 0;
2516
+ for (const n of nodes) {
2517
+ if (n.cat !== "tool") continue;
2518
+ if (before !== null && n.seq >= before) continue;
2519
+ const kind = kindOfTool(n.tool);
2520
+ if (kind === null) continue;
2521
+ const tool = n.tool;
2522
+ totals[kind].ops++;
2523
+ const conv = convOf(n.seq);
2524
+ const args = parseCallArgs(conv?.call?.argsRaw);
2525
+ const path = pathOfArgs(tool, args);
2526
+ if (args === null || path === null) {
2527
+ unresolved++;
2528
+ continue;
2529
+ }
2530
+ const { added, removed } = deltaOf(tool, args);
2531
+ const detail = kind === "search" && typeof args.path === "string" && args.path !== "" && typeof args.pattern === "string" && args.pattern !== "" ? args.pattern : void 0;
2532
+ const op = {
2533
+ seq: n.seq,
2534
+ ...n.gone !== void 0 ? { gone: n.gone } : {},
2535
+ kind,
2536
+ tool,
2537
+ ...n.time !== void 0 ? { time: n.time } : {},
2538
+ err: n.err === true || conv.isError === true,
2539
+ added,
2540
+ removed,
2541
+ ...detail !== void 0 ? { detail } : {}
2542
+ };
2543
+ let entry = byPath.get(path);
2544
+ if (entry === void 0) {
2545
+ entry = {
2546
+ path,
2547
+ form: formOf(tool, path),
2548
+ reads: 0,
2549
+ writes: 0,
2550
+ searches: 0,
2551
+ added: 0,
2552
+ removed: 0,
2553
+ errs: 0,
2554
+ ops: []
2555
+ };
2556
+ byPath.set(path, entry);
2557
+ }
2558
+ if (kind === "read") entry.reads++;
2559
+ else if (kind === "write") entry.writes++;
2560
+ else entry.searches++;
2561
+ entry.added += added;
2562
+ entry.removed += removed;
2563
+ if (op.err) entry.errs++;
2564
+ entry.ops.push(op);
2565
+ }
2566
+ const entries = [...byPath.values()];
2567
+ for (const e of entries) {
2568
+ e.ops.sort((a, b) => b.seq - a.seq);
2569
+ if (e.reads > 0) totals.read.files++;
2570
+ if (e.writes > 0) totals.write.files++;
2571
+ if (e.searches > 0) totals.search.files++;
2572
+ if (e.form === "image") {
2573
+ totals.image.files++;
2574
+ totals.image.ops += e.ops.length;
2575
+ }
2576
+ totals.added += e.added;
2577
+ totals.removed += e.removed;
2578
+ }
2579
+ entries.sort((a, b) => b.ops[0].seq - a.ops[0].seq);
2580
+ return {
2581
+ entries,
2582
+ totals,
2583
+ unresolved
2584
+ };
2585
+ }
2586
+ /**
2587
+ * The browser step whose assembled surface SHOWS an op's result node: the
2588
+ * first request dispatched after it while it was still alive (that step's
2589
+ * brief is where the result landed). A live node no request has consumed
2590
+ * yet reveals on the live surface; an archived node no retained step still
2591
+ * contains is not viewable anywhere (null → the row stays inert).
2592
+ */
2593
+ function locateStepOf(requests, seq, gone) {
2594
+ for (const r of requests) if (r.seq > seq && (gone === void 0 || gone > r.seq)) return r.seq;
2595
+ return gone === void 0 ? "live" : null;
2596
+ }
2597
+ //#endregion
2216
2598
  //#region src/client/components/events.tsx
2217
2599
  const EVENT_ICONS = {
2218
2600
  compaction: "✂",
@@ -2306,6 +2688,160 @@ window.__ModuleLoader__.load({
2306
2688
  }));
2307
2689
  };
2308
2690
  }
2691
+ //#endregion
2692
+ //#region src/client/components/fileCard.tsx
2693
+ /** An expanded file lists at most this many ops before a "+n earlier" line. */
2694
+ const MAX_OPS = 8;
2695
+ function makeFileCard(kit, settings) {
2696
+ const { t, fmt, fmtTime } = kit;
2697
+ function formGlyph(form) {
2698
+ return form === "image" ? "🖼" : form === "dir" ? "📁" : "📄";
2699
+ }
2700
+ function matches(e, f) {
2701
+ if (f === "all") return true;
2702
+ if (f === "image") return e.form === "image";
2703
+ if (f === "read") return e.reads > 0;
2704
+ if (f === "write") return e.writes > 0;
2705
+ return e.searches > 0;
2706
+ }
2707
+ /** Signed line pair, harness diff semantics: growth on the success token, shrinkage on the error token. */
2708
+ function DeltaPair(props) {
2709
+ return /* @__PURE__ */ React.createElement("span", { className: "lc-fa-delta" }, props.added > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-up" }, "+" + fmt(props.added)) : null, props.removed > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-down" }, "−" + fmt(props.removed)) : null);
2710
+ }
2711
+ return function FileCard(props) {
2712
+ const { activity } = props;
2713
+ const [filter, setFilter] = React.useState("all");
2714
+ const [sort, setSort] = React.useState(() => settings.defaultFileSort());
2715
+ const [query, setQuery] = React.useState("");
2716
+ const [openPath, setOpenPath] = React.useState(null);
2717
+ const q = query.trim().toLowerCase();
2718
+ const countOf = (e) => filter === "read" ? e.reads : filter === "write" ? e.writes : filter === "search" ? e.searches : e.ops.length;
2719
+ const shown = activity.entries.filter((e) => matches(e, filter) && (q === "" || e.path.toLowerCase().includes(q))).sort((a, b) => sort === "count" ? countOf(b) - countOf(a) || b.ops[0].seq - a.ops[0].seq : sort === "latest" ? b.ops[0].seq - a.ops[0].seq : a.path < b.path ? -1 : 1);
2720
+ const chips = [
2721
+ {
2722
+ key: "all",
2723
+ files: activity.entries.length,
2724
+ ops: activity.totals.read.ops + activity.totals.write.ops + activity.totals.search.ops
2725
+ },
2726
+ {
2727
+ key: "read",
2728
+ files: activity.totals.read.files,
2729
+ ops: activity.totals.read.ops
2730
+ },
2731
+ {
2732
+ key: "write",
2733
+ files: activity.totals.write.files,
2734
+ ops: activity.totals.write.ops
2735
+ },
2736
+ {
2737
+ key: "search",
2738
+ files: activity.totals.search.files,
2739
+ ops: activity.totals.search.ops
2740
+ },
2741
+ {
2742
+ key: "image",
2743
+ files: activity.totals.image.files,
2744
+ ops: activity.totals.image.ops
2745
+ }
2746
+ ];
2747
+ const opLine = (op) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-time" }, op.time !== void 0 ? fmtTime(op.time) : "—"), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-tool" }, op.tool), op.detail !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-detail" }, op.detail) : null, op.added + op.removed > 0 ? /* @__PURE__ */ React.createElement(DeltaPair, {
2748
+ added: op.added,
2749
+ removed: op.removed
2750
+ }) : null, op.err ? /* @__PURE__ */ React.createElement("span", {
2751
+ className: "lc-br-err-dot",
2752
+ title: t("node.failed")
2753
+ }) : null);
2754
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("files.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, props.scope)), activity.entries.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("files.empty")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "lc-fa-ctl" }, /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, chips.map((c) => /* @__PURE__ */ React.createElement("button", {
2755
+ key: c.key,
2756
+ type: "button",
2757
+ className: "lc-gran-btn" + (filter === c.key ? " lc-gran-on" : "") + (c.key === "read" || c.key === "write" || c.key === "search" ? " lc-fa-chip-" + c.key : ""),
2758
+ title: t("files.chipTip", {
2759
+ files: c.files,
2760
+ ops: c.ops
2761
+ }),
2762
+ onClick: () => {
2763
+ setFilter((cur) => cur === c.key ? "all" : c.key);
2764
+ }
2765
+ }, t("files.kind." + c.key), /* @__PURE__ */ React.createElement("b", { className: "lc-fa-n" }, fmt(c.ops))))), /* @__PURE__ */ React.createElement("input", {
2766
+ className: "lc-fa-search",
2767
+ value: query,
2768
+ placeholder: t("files.search"),
2769
+ onChange: (ev) => {
2770
+ setQuery(ev.target.value);
2771
+ }
2772
+ })), /* @__PURE__ */ React.createElement("div", { className: "lc-fa-meta" }, /* @__PURE__ */ React.createElement("span", null, t("files.files", { n: activity.entries.length })), activity.totals.added + activity.totals.removed > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-meta-delta" }, /* @__PURE__ */ React.createElement(DeltaPair, {
2773
+ added: activity.totals.added,
2774
+ removed: activity.totals.removed
2775
+ }), /* @__PURE__ */ React.createElement("span", {
2776
+ className: "lc-fa-meta-tip",
2777
+ role: "tooltip"
2778
+ }, t("files.deltaTip"))) : null, /* @__PURE__ */ React.createElement("span", {
2779
+ className: "lc-gran lc-fa-sort",
2780
+ role: "group",
2781
+ title: t("files.sortTip")
2782
+ }, [
2783
+ "count",
2784
+ "latest",
2785
+ "path"
2786
+ ].map((k) => /* @__PURE__ */ React.createElement("button", {
2787
+ key: k,
2788
+ type: "button",
2789
+ className: "lc-gran-btn" + (sort === k ? " lc-gran-on" : ""),
2790
+ onClick: () => {
2791
+ setSort(k);
2792
+ }
2793
+ }, t("files.sort." + k))))), shown.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("files.noMatch")) : /* @__PURE__ */ React.createElement("div", { className: "lc-fa-list" }, shown.map((e) => {
2794
+ const open = openPath === e.path;
2795
+ const trimmed = e.path.endsWith("/") ? e.path.slice(0, -1) : e.path;
2796
+ const slash = trimmed.lastIndexOf("/");
2797
+ const dir = slash >= 0 ? trimmed.slice(0, slash + 1) : "";
2798
+ const base = slash >= 0 ? trimmed.slice(slash + 1) : trimmed;
2799
+ return /* @__PURE__ */ React.createElement("div", {
2800
+ key: e.path,
2801
+ className: "lc-fa-item" + (open ? " lc-fa-item-on" : "")
2802
+ }, /* @__PURE__ */ React.createElement("button", {
2803
+ type: "button",
2804
+ className: "lc-fa-row",
2805
+ title: e.path,
2806
+ onClick: () => {
2807
+ setOpenPath(open ? null : e.path);
2808
+ }
2809
+ }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "▸"), /* @__PURE__ */ React.createElement("span", {
2810
+ className: "lc-fa-form",
2811
+ title: t("files.form." + e.form)
2812
+ }, formGlyph(e.form)), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-path" }, dir !== "" ? /* @__PURE__ */ React.createElement("em", null, dir) : null, /* @__PURE__ */ React.createElement("b", null, base)), e.reads > 0 ? /* @__PURE__ */ React.createElement("span", {
2813
+ className: "lc-fa-badge lc-fa-b-read",
2814
+ title: t("files.kind.read")
2815
+ }, /* @__PURE__ */ React.createElement("i", null), fmt(e.reads)) : null, e.writes > 0 ? /* @__PURE__ */ React.createElement("span", {
2816
+ className: "lc-fa-badge lc-fa-b-write",
2817
+ title: t("files.kind.write")
2818
+ }, /* @__PURE__ */ React.createElement("i", null), fmt(e.writes)) : null, e.searches > 0 ? /* @__PURE__ */ React.createElement("span", {
2819
+ className: "lc-fa-badge lc-fa-b-search",
2820
+ title: t("files.kind.search")
2821
+ }, /* @__PURE__ */ React.createElement("i", null), fmt(e.searches)) : null, e.added + e.removed > 0 ? /* @__PURE__ */ React.createElement(DeltaPair, {
2822
+ added: e.added,
2823
+ removed: e.removed
2824
+ }) : null, e.errs > 0 ? /* @__PURE__ */ React.createElement("span", {
2825
+ className: "lc-br-err-dot",
2826
+ title: t("files.errs", { n: e.errs })
2827
+ }) : null, e.ops[0].time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-time" }, fmtTime(e.ops[0].time)) : null), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-ops" }, e.ops.slice(0, MAX_OPS).map((op) => {
2828
+ const onLocate = props.onLocate;
2829
+ return onLocate !== void 0 ? /* @__PURE__ */ React.createElement("button", {
2830
+ key: op.seq,
2831
+ type: "button",
2832
+ className: "lc-fa-op lc-fa-op-link",
2833
+ title: t("files.locate"),
2834
+ onClick: () => {
2835
+ onLocate(op);
2836
+ }
2837
+ }, opLine(op)) : /* @__PURE__ */ React.createElement("div", {
2838
+ key: op.seq,
2839
+ className: "lc-fa-op"
2840
+ }, opLine(op));
2841
+ }), e.ops.length > MAX_OPS ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-more" }, t("files.moreOps", { n: e.ops.length - MAX_OPS })) : null) : null);
2842
+ }))), activity.unresolved > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-note" }, t("files.unresolved", { n: activity.unresolved })) : null);
2843
+ };
2844
+ }
2309
2845
  const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
2310
2846
  const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
2311
2847
  //#endregion
@@ -2349,7 +2885,7 @@ window.__ModuleLoader__.load({
2349
2885
  return function PluginInfo() {
2350
2886
  const [latest, setLatest] = React.useState(null);
2351
2887
  React.useEffect(() => {
2352
- if ("0.32.0".includes("-dev")) return;
2888
+ if ("0.33.1".includes("-dev")) return;
2353
2889
  let on = true;
2354
2890
  fetchLatestVersion().then((v) => {
2355
2891
  if (on && v) setLatest(v);
@@ -2358,8 +2894,8 @@ window.__ModuleLoader__.load({
2358
2894
  on = false;
2359
2895
  };
2360
2896
  }, []);
2361
- const update = latest !== null && isNewerVersion(latest, "0.32.0") ? latest : null;
2362
- const nameValue = ["dsh-context (v0.32.0)"];
2897
+ const update = latest !== null && isNewerVersion(latest, "0.33.1") ? latest : null;
2898
+ const nameValue = ["dsh-context (v0.33.1)"];
2363
2899
  if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
2364
2900
  key: "update",
2365
2901
  className: "lc-pi-update"
@@ -2394,6 +2930,8 @@ window.__ModuleLoader__.load({
2394
2930
  }
2395
2931
  /** One-line identity of a surface node: text preview, call breadcrumb, tool name, or a localized placeholder. */
2396
2932
  function nodeLine(n, conv) {
2933
+ /* v8 ignore if -- chipParts returns for tool cats upstream; only
2934
+ inject-with-skill and drift nodes ever reach nodeLine. Defensive. */
2397
2935
  if (n.cat === "tool") return callSummaryOf(conv) ?? n.tool ?? t("node.toolResult");
2398
2936
  if (n.text !== void 0 && n.text !== "") return n.text;
2399
2937
  if (n.skill !== void 0) return t("node.skillTag", { name: n.skill });
@@ -2401,10 +2939,65 @@ window.__ModuleLoader__.load({
2401
2939
  const summary = blockSummaryOf(conv);
2402
2940
  return n.calls.join(" › ") + (summary !== null ? " · " + summary : "");
2403
2941
  }
2942
+ /* v8 ignore if -- chipParts returns for assistant cats upstream. Defensive. */
2404
2943
  if (n.cat === "assistant") return t("node.empty");
2944
+ /* v8 ignore if -- inject nodes arrive only WITH a skill (the text/skill
2945
+ arms above return first); inject-without-skill never reaches nodeLine. */
2405
2946
  if (n.cat === "inject") return t("form." + (n.form ?? "context"));
2406
2947
  return t("node.nonText");
2407
2948
  }
2949
+ /**
2950
+ * One line's anatomy — a compact FACT tag plus the preview text, mirroring the Context browser's element rows so a
2951
+ * brief line reads exactly like the browser row its click reveals: tool results tag the tool name (skill results the
2952
+ * skill name), assistant replies tag the call breadcrumb, injections tag the form, user messages tag image attachments.
2953
+ */
2954
+ function chipParts(n, conv) {
2955
+ if (n.cat === "tool") {
2956
+ const summary = callSummaryOf(conv);
2957
+ if (n.skill !== void 0) return {
2958
+ tag: t("node.skillTag", { name: n.skill }),
2959
+ text: summary ?? ""
2960
+ };
2961
+ if (n.tool !== void 0) return {
2962
+ tag: n.tool,
2963
+ text: summary ?? ""
2964
+ };
2965
+ return {
2966
+ tag: null,
2967
+ text: summary ?? t("node.toolResult")
2968
+ };
2969
+ }
2970
+ if (n.cat === "assistant") {
2971
+ const names = n.calls !== void 0 && n.calls.length > 0 ? n.calls : callNamesOf(conv);
2972
+ const own = n.text !== void 0 && n.text !== "" ? n.text : blockSummaryOf(conv) ?? "";
2973
+ return {
2974
+ tag: names.length > 0 ? names.join(" › ") : null,
2975
+ text: own !== "" ? own : names.length > 0 ? "" : t("node.empty")
2976
+ };
2977
+ }
2978
+ if (n.cat === "inject" && n.skill === void 0) {
2979
+ const text = n.text !== void 0 && n.text !== "" ? n.form === "snapshot" ? t("node.snapshot") + n.text : n.text : "";
2980
+ return {
2981
+ tag: t("form." + (n.form ?? "context")),
2982
+ text
2983
+ };
2984
+ }
2985
+ if (n.cat === "user") {
2986
+ const imgs = n.imgs ?? 0;
2987
+ return {
2988
+ tag: imgs > 0 ? t("attach.image") + (imgs > 1 ? " ×" + String(imgs) : "") : null,
2989
+ text: n.text ?? ""
2990
+ };
2991
+ }
2992
+ return {
2993
+ tag: null,
2994
+ text: nodeLine(n, conv)
2995
+ };
2996
+ }
2997
+ /** The native-title line for a fact+text pair: 'tag · text', degrading to whichever half exists. */
2998
+ function factTitle(tag, text) {
2999
+ return tag !== null ? text !== "" ? tag + " · " + text : tag : text;
3000
+ }
2408
3001
  function BriefSection(props) {
2409
3002
  const { opener, inputs, response } = props.brief;
2410
3003
  if (opener === void 0 && inputs.length === 0 && response === void 0) return null;
@@ -2415,47 +3008,43 @@ window.__ModuleLoader__.load({
2415
3008
  props.onLocate?.(n, false);
2416
3009
  };
2417
3010
  const MAX_CHIPS = 3;
2418
- let replyText = "";
2419
- let replyArrow = false;
2420
- if (response !== void 0) {
2421
- const conv = convOf(response.seq);
2422
- replyText = nodeLine(response, conv);
2423
- if (response.calls !== void 0 && response.calls.length > 0) replyArrow = true;
2424
- else {
2425
- const joined = callNamesOf(conv);
2426
- if (joined.length > 0) replyText += " → " + joined.join(" › ");
2427
- }
2428
- }
2429
- return /* @__PURE__ */ React.createElement("div", { className: "lc-brief" }, opener !== void 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
3011
+ /**
3012
+ * The ONE content unit of the brief — inputs and the reply share the same chip anatomy (error dot, fact tag,
3013
+ * preview text). Input chips are compact and individually clickable (each locates its own node); the reply is a
3014
+ * single chip grown to the row's width, left inert because the row button already locates it.
3015
+ */
3016
+ const nodeChip = (n, onClick, grow = false) => {
3017
+ const { tag, text } = chipParts(n, convOf(n.seq));
3018
+ return /* @__PURE__ */ React.createElement("span", {
3019
+ key: n.seq,
3020
+ className: "lc-brief-chip" + (grow ? " lc-brief-chip-grow" : "") + (onClick !== void 0 ? " lc-brief-chip-link" : ""),
3021
+ title: factTitle(tag, text) + hint,
3022
+ onClick
3023
+ }, n.err === true ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-err-dot" }) : null, tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-chip-tag" }, tag) : null, text !== "" ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-chip-text" }, text) : null);
3024
+ };
3025
+ const openerParts = opener !== void 0 ? chipParts(opener, convOf(opener.seq)) : null;
3026
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-brief" }, opener !== void 0 && openerParts !== null ? /* @__PURE__ */ React.createElement(BriefRow, {
2430
3027
  tag: t("brief.turn"),
2431
3028
  tagTip: t("brief.turnTip"),
2432
3029
  node: opener,
2433
3030
  isResponse: false,
2434
3031
  onLocate: props.onLocate
2435
- }, /* @__PURE__ */ React.createElement("span", {
3032
+ }, openerParts.tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-fact" }, openerParts.tag) : null, openerParts.text !== "" ? /* @__PURE__ */ React.createElement("span", {
2436
3033
  className: "lc-brief-text",
2437
- title: nodeLine(opener, convOf(opener.seq)) + hint
2438
- }, nodeLine(opener, convOf(opener.seq)))) : null, inputs.length > 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
3034
+ title: factTitle(openerParts.tag, openerParts.text) + hint
3035
+ }, openerParts.text) : null) : null, inputs.length > 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
2439
3036
  tag: t("brief.input"),
2440
3037
  tagTip: t("brief.inputTip"),
2441
3038
  node: inputs[0],
2442
3039
  isResponse: false,
2443
3040
  onLocate: props.onLocate
2444
- }, inputs.slice(0, MAX_CHIPS).map((n) => /* @__PURE__ */ React.createElement("span", {
2445
- key: n.seq,
2446
- className: "lc-brief-chip" + (props.onLocate !== void 0 ? " lc-brief-chip-link" : ""),
2447
- title: nodeLine(n, convOf(n.seq)) + hint,
2448
- onClick: locateChip !== void 0 ? locateChip(n) : void 0
2449
- }, n.err === true ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-err-dot" }) : null, nodeLine(n, convOf(n.seq)))), inputs.length > MAX_CHIPS ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-more" }, t("brief.more", { n: inputs.length - MAX_CHIPS })) : null) : null, response !== void 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
3041
+ }, inputs.slice(0, MAX_CHIPS).map((n) => nodeChip(n, locateChip?.(n))), inputs.length > MAX_CHIPS ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-more" }, t("brief.more", { n: inputs.length - MAX_CHIPS })) : null) : null, response !== void 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
2450
3042
  tag: t("brief.reply"),
2451
3043
  tagTip: t("brief.replyTip"),
2452
3044
  node: response,
2453
3045
  isResponse: true,
2454
3046
  onLocate: props.onLocate
2455
- }, /* @__PURE__ */ React.createElement("span", {
2456
- className: "lc-brief-text",
2457
- title: replyText + hint
2458
- }, replyArrow ? "→ " : "", replyText)) : null);
3047
+ }, nodeChip(response, void 0, true)) : null);
2459
3048
  }
2460
3049
  return function RequestDetail(props) {
2461
3050
  const req = props.request;
@@ -2463,6 +3052,8 @@ window.__ModuleLoader__.load({
2463
3052
  const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
2464
3053
  const head = isTurn ? t("detail.turn", {
2465
3054
  t: req.turn ?? 0,
3055
+ /* v8 ignore next -- isTurn already guarantees stepCount !== undefined;
3056
+ the fallback is defensive. */
2466
3057
  n: req.stepCount ?? 0
2467
3058
  }) : t("detail.step", {
2468
3059
  t: req.turn ?? 0,
@@ -2894,6 +3485,8 @@ window.__ModuleLoader__.load({
2894
3485
  };
2895
3486
  React.useLayoutEffect(() => {
2896
3487
  const el = scrollRef.current;
3488
+ /* v8 ignore next 1 -- the scroll div renders unconditionally and React
3489
+ attaches refs before layout effects run; el is never null here. */
2897
3490
  if (el === null) return;
2898
3491
  if (props.granularity !== lastGranRef.current) {
2899
3492
  lastGranRef.current = props.granularity;
@@ -2925,6 +3518,8 @@ window.__ModuleLoader__.load({
2925
3518
  const reply = props.replyTips?.get(req.seq);
2926
3519
  const tail = reply !== void 0 ? " · “" + (reply.length > 48 ? reply.slice(0, 48) + "…" : reply) + "”" : "";
2927
3520
  if (delta) {
3521
+ /* v8 ignore next 1 -- delta mode only receives records from
3522
+ deltaOf, which always assigns net; the fallback is defensive. */
2928
3523
  const n = req.net ?? 0;
2929
3524
  return head + " · " + fmtTime(req.time) + " · " + t("tip.delta", { n: (n > 0 ? "+" : "") + fmt(n) }) + tail;
2930
3525
  }
@@ -3008,7 +3603,7 @@ window.__ModuleLoader__.load({
3008
3603
  const TrendChart = makeTrendChart(kit);
3009
3604
  const RequestDetail = makeRequestDetail(kit, StackedBar);
3010
3605
  const EventList = makeEventList(kit);
3011
- const NodeList = makeNodeList(kit);
3606
+ const FileCard = makeFileCard(kit, settings);
3012
3607
  const StatsBoard = makeStatsBoard(kit);
3013
3608
  const PluginInfo = makePluginInfo(kit);
3014
3609
  const ContextBrowser = makeContextBrowser(kit, StackedBar);
@@ -3035,10 +3630,6 @@ window.__ModuleLoader__.load({
3035
3630
  return p.length === 1 ? [...EVENT_KINDS] : p.filter((x) => x !== k);
3036
3631
  });
3037
3632
  };
3038
- const [toolFocus, setToolFocus] = React.useState(null);
3039
- const clearToolFocus = React.useCallback(() => {
3040
- setToolFocus(null);
3041
- }, []);
3042
3633
  const [nodeFocus, setNodeFocus] = React.useState(null);
3043
3634
  const clearNodeFocus = React.useCallback(() => {
3044
3635
  setNodeFocus(null);
@@ -3049,6 +3640,7 @@ window.__ModuleLoader__.load({
3049
3640
  if (conversation === void 0 || typeof conversation.resolveImage !== "function") return void 0;
3050
3641
  return (attachment) => conversation.resolveImage(sessionId, attachment);
3051
3642
  }, [sessionId]);
3643
+ const fetchContent = React.useMemo(() => typeof sessionId === "string" && sessionId !== "" ? makeContentFetcher(ctx, sessionId) : void 0, [ctx, sessionId]);
3052
3644
  const rootRef = React.useRef(null);
3053
3645
  const scrollerRef = React.useRef(null);
3054
3646
  const restoredRef = React.useRef(null);
@@ -3056,6 +3648,8 @@ window.__ModuleLoader__.load({
3056
3648
  if (typeof sessionId !== "string" || sessionId === "" || data === null) return;
3057
3649
  if (restoredRef.current === sessionId) return;
3058
3650
  restoredRef.current = sessionId;
3651
+ /* v8 ignore next 3 -- a layout effect body only runs while mounted and
3652
+ both render paths attach rootRef, so the null arm cannot fire. */
3059
3653
  const scroller = rootRef.current !== null ? rootRef.current.closest("[data-conversation-scroll]") : null;
3060
3654
  if (scroller === null) return;
3061
3655
  scrollerRef.current = scroller;
@@ -3072,7 +3666,6 @@ window.__ModuleLoader__.load({
3072
3666
  const requests = data ? data.requests : [];
3073
3667
  const events = data ? data.events : [];
3074
3668
  const shownEvents = pickedKinds.length === EVENT_KINDS.length ? events : events.filter((e) => pickedKinds.includes(e.kind));
3075
- const nodes = data ? data.nodes : [];
3076
3669
  const displayRequests = React.useMemo(() => granularity === "turn" ? aggregateByTurn(requests) : requests, [requests, granularity]);
3077
3670
  const markers = React.useMemo(() => attachMarkers(displayRequests, events), [displayRequests, events]);
3078
3671
  const briefList = React.useMemo(() => data ? briefNodes(data) : [], [data]);
@@ -3089,6 +3682,8 @@ window.__ModuleLoader__.load({
3089
3682
  }, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
3090
3683
  const markerOf = (req) => {
3091
3684
  const i = displayRequests.indexOf(req);
3685
+ /* v8 ignore next 1 -- the only caller passes displayRequests[activeIdx],
3686
+ an element of the very array indexOf scans. */
3092
3687
  return i >= 0 ? markers[i] : void 0;
3093
3688
  };
3094
3689
  let pinnedIdx = -1;
@@ -3106,7 +3701,32 @@ window.__ModuleLoader__.load({
3106
3701
  const activeReq = activeIdx >= 0 ? displayRequests[activeIdx] : null;
3107
3702
  const brief = activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null;
3108
3703
  const convOf = (seq) => bySeq.get(seq);
3704
+ let filesBefore = null;
3705
+ if (activeReq !== null) {
3706
+ const ri = requests.findIndex((r) => r.seq === activeReq.seq);
3707
+ filesBefore = ri + 1 < requests.length ? requests[ri + 1].seq : null;
3708
+ }
3709
+ let fileScope = t("files.scopeLatest");
3710
+ if (activeReq !== null && filesBefore !== null) fileScope = activeReq.stepCount !== void 0 && activeReq.stepCount > 1 ? t("detail.turn", {
3711
+ t: activeReq.turn ?? 0,
3712
+ n: activeReq.stepCount
3713
+ }) : t("detail.step", {
3714
+ t: activeReq.turn ?? 0,
3715
+ s: activeReq.step ?? 0
3716
+ });
3717
+ const fileActivity = activityOf(briefList, convOf, filesBefore);
3718
+ const locateFileOp = (op) => {
3719
+ const step = locateStepOf(requests, op.seq, op.gone);
3720
+ if (step === null) return;
3721
+ setNodeFocus({
3722
+ step,
3723
+ seq: op.seq,
3724
+ cat: "tool"
3725
+ });
3726
+ };
3109
3727
  const locateNode = (node, isResponse) => {
3728
+ /* v8 ignore next 1 -- locateNode is only wired to brief rows, and
3729
+ brief !== null guarantees activeReq !== null in the same closure. */
3110
3730
  if (activeReq === null) return;
3111
3731
  const next = isResponse && activeIdx + 1 < displayRequests.length ? displayRequests[activeIdx + 1] : null;
3112
3732
  const step = isResponse ? next !== null ? next.seq : "live" : activeReq.seq;
@@ -3141,9 +3761,7 @@ window.__ModuleLoader__.load({
3141
3761
  head,
3142
3762
  subtitle: (data.model ? data.model : "") + (data.provider ? " · " + data.provider : ""),
3143
3763
  hoverKey: hoverCat,
3144
- onHoverKey: setHoverCat,
3145
- tools: data.toolList,
3146
- onToolFocus: setToolFocus
3764
+ onHoverKey: setHoverCat
3147
3765
  }), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("trend.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-trend-ctl" }, /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement("button", {
3148
3766
  className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
3149
3767
  onClick: () => {
@@ -3191,6 +3809,9 @@ window.__ModuleLoader__.load({
3191
3809
  }), /* @__PURE__ */ React.createElement(RequestDetail, {
3192
3810
  request: activeReq,
3193
3811
  prev: trendMode === "delta" && activeIdx >= 0 ? activeIdx > 0 ? displayRequests[activeIdx - 1] : null : void 0,
3812
+ /* v8 ignore next 1 -- RequestDetail renders only when
3813
+ displayRequests.length > 0, which forces activeReq
3814
+ non-null via the activeIdx fallback above. */
3194
3815
  marker: activeReq !== null ? markerOf(activeReq) : void 0,
3195
3816
  brief,
3196
3817
  convOf,
@@ -3199,13 +3820,11 @@ window.__ModuleLoader__.load({
3199
3820
  data,
3200
3821
  headers,
3201
3822
  useSession: props.useSession,
3202
- loadOlderHistory: props.loadOlderHistory,
3823
+ fetchContent,
3203
3824
  previewSeq: hoveredSeq,
3204
3825
  pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
3205
3826
  hoverKey: hoverCat,
3206
3827
  onHoverKey: setHoverCat,
3207
- toolFocus,
3208
- onToolFocusHandled: clearToolFocus,
3209
3828
  nodeFocus,
3210
3829
  onNodeFocusHandled: clearNodeFocus,
3211
3830
  loadImage
@@ -3215,10 +3834,11 @@ window.__ModuleLoader__.load({
3215
3834
  onClick: () => {
3216
3835
  toggleKind(k);
3217
3836
  }
3218
- }, t("kind." + k))))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("nodes.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("nodes.hint"))), /* @__PURE__ */ React.createElement(NodeList, {
3219
- nodes,
3220
- dropped: data.droppedNodes || 0
3221
- }))), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
3837
+ }, t("kind." + k))))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement(FileCard, {
3838
+ activity: fileActivity,
3839
+ scope: fileScope,
3840
+ onLocate: locateFileOp
3841
+ })), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
3222
3842
  }
3223
3843
  return function ContextView(props) {
3224
3844
  return h(ErrorBoundary, null, h(ContextViewBody, props));
@@ -3239,7 +3859,7 @@ window.__ModuleLoader__.load({
3239
3859
  }
3240
3860
  //#endregion
3241
3861
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles.css.mjs
3242
- const css = ".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-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);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;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-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i,.lc-node i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-tools{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;display:flex}.lc-tools-label,.lc-tools-more{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;padding:0;font-family:inherit;font-size:12px}.lc-tools-label:hover,.lc-tools-more:hover{text-decoration:underline}.lc-tool-chip{background:var(--dsw-alias-bg-layer-2);font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:0;border-radius:4px;padding:1px 7px;font-size:12px}.lc-tool-chip:hover{text-decoration:underline}.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-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;scrollbar-gutter:stable;flex:1;min-width:0;padding-bottom:8px;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.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-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;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;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.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-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}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px 8px;margin-bottom:10px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary);font-size:13px}.lc-detail-time{font-variant-numeric:tabular-nums;white-space:nowrap;margin-left:auto;font-size:12px}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-metric{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 14%, transparent);border-radius:6px;padding:2px 8px;font-size:12px;font-weight:600}.lc-detail-metric-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-detail-metric-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-brief{flex-direction:column;gap:2px;margin:-4px 0 10px;display:flex}.lc-brief-row{min-width:0;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;margin-left:-6px;margin-right:-6px;padding:3px 6px;font-family:inherit;font-size:12px;line-height:1.5;transition:background-color .12s;display:flex}.lc-brief-row-link:hover{background:var(--dsw-alias-interactive-bg-hover)}button.lc-brief-row-link{cursor:pointer}.lc-brief-tag{box-sizing:border-box;text-align:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:56px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;position:relative}.lc-brief-tip{z-index:6;white-space:normal;text-align:left;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:240px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-brief-tag:hover .lc-brief-tip{opacity:1}.lc-brief-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-chip{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:220px;color:var(--dsw-alias-label-primary);border-radius:5px;align-items:center;gap:5px;padding:0 7px;font-size:12px;transition:background-color .12s;display:inline-flex;overflow:hidden}.lc-brief-chip-link{cursor:pointer}.lc-brief-chip-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-brief-more{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;position:relative;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-bar-zero{border-left:1px solid #80808073;position:absolute;top:0;bottom:0;left:50%}.lc-bar-fill-up,.lc-bar-fill-down{height:100%;display:block;position:absolute;top:0}.lc-bar-fill-up{border-radius:0 3px 3px 0;left:50%}.lc-bar-fill-down{border-radius:3px 0 0 3px;right:50%}.lc-detail-num{text-align:right;font-variant-numeric:tabular-nums;width:52px}.lc-detail-num-up{color:var(--dsw-alias-state-success-primary)}.lc-detail-num-down{color:var(--dsw-alias-state-error-primary)}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-nodes{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-node{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-node-preview{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);flex:1;overflow:hidden}.lc-node-time{color:var(--dsw-alias-label-secondary);text-align:right;min-width:54px;font-size:12px}.lc-node-tokens{color:var(--dsw-alias-label-secondary)}.lc-nodes-more{color:var(--dsw-alias-label-secondary);padding:3px 0}.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{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-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;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}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.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-l1);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-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);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;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;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{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;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;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.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;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;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{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;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.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)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
3862
+ const css = ".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-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);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;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-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.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-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;scrollbar-gutter:stable;flex:1;min-width:0;padding-bottom:8px;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.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-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;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;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.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-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}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px 8px;margin-bottom:10px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary);font-size:13px}.lc-detail-time{font-variant-numeric:tabular-nums;white-space:nowrap;margin-left:auto;font-size:12px}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-metric{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 14%, transparent);border-radius:6px;padding:2px 8px;font-size:12px;font-weight:600}.lc-detail-metric-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-detail-metric-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-brief{flex-direction:column;gap:2px;margin:-4px 0 10px;display:flex}.lc-brief-row{min-width:0;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;margin-left:-6px;margin-right:-6px;padding:3px 6px;font-family:inherit;font-size:12px;line-height:1.5;transition:background-color .12s;display:flex}.lc-brief-row-link:hover{background:var(--dsw-alias-interactive-bg-hover)}button.lc-brief-row-link{cursor:pointer}.lc-brief-tag{box-sizing:border-box;text-align:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:56px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;position:relative}.lc-brief-tip{z-index:6;white-space:normal;text-align:left;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:240px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-brief-tag:hover .lc-brief-tip{opacity:1}.lc-brief-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-fact{box-sizing:border-box;text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:160px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:0;max-width:260px;color:var(--dsw-alias-label-primary);border-radius:5px;align-items:center;gap:5px;padding:0 7px;font-size:12px;transition:background-color .12s;display:inline-flex;overflow:hidden}.lc-brief-chip-grow{flex:1;max-width:none}.lc-brief-chip-tag{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);max-width:120px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-chip-link{cursor:pointer}.lc-brief-chip-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-brief-more{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;position:relative;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-bar-zero{border-left:1px solid #80808073;position:absolute;top:0;bottom:0;left:50%}.lc-bar-fill-up,.lc-bar-fill-down{height:100%;display:block;position:absolute;top:0}.lc-bar-fill-up{border-radius:0 3px 3px 0;left:50%}.lc-bar-fill-down{border-radius:3px 0 0 3px;right:50%}.lc-detail-num{text-align:right;font-variant-numeric:tabular-nums;width:52px}.lc-detail-num-up{color:var(--dsw-alias-state-success-primary)}.lc-detail-num-down{color:var(--dsw-alias-state-error-primary)}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-fa-ctl{align-items:center;gap:8px;margin-bottom:8px;display:flex}.lc-fa-ctl .lc-gran{margin-left:0}.lc-fa-n{opacity:.75;margin-left:4px;font-weight:700}.lc-fa-search{min-width:80px;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;outline:none;flex:1;padding:3px 8px;font-size:12px}.lc-fa-search:focus{border-color:var(--dsw-alias-label-dimmed)}.lc-fa-meta{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;margin-bottom:6px;font-size:12px;display:flex}.lc-fa-meta .lc-fa-sort{flex:none}.lc-fa-meta-delta{position:relative}.lc-fa-meta-tip{z-index:6;white-space:normal;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:260px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-fa-meta-delta:hover .lc-fa-meta-tip{opacity:1}.lc-fa-item{border:1px solid #0000;border-radius:8px;transition:background-color .12s,border-color .12s}.lc-fa-item-on{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l1)}.lc-fa-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:7px;align-items:center;gap:7px;padding:6px 8px;font-size:12px;transition:background-color .12s;display:flex}.lc-fa-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-form{text-align:center;opacity:.85;flex:none;width:16px;font-size:11px}.lc-fa-path{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-fa-path em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-fa-badge{font-variant-numeric:tabular-nums;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:1px 7px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-badge i{background:currentColor;border-radius:50%;width:5px;height:5px;display:inline-block}.lc-fa-b-read{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent)}.lc-fa-b-write{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent)}.lc-fa-b-search{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent)}.lc-fa-chip-read,.lc-fa-chip-read:hover{color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write,.lc-fa-chip-write:hover{color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search,.lc-fa-chip-search:hover{color:var(--dsw-alias-state-warn-primary)}.lc-fa-chip-read.lc-gran-on,.lc-fa-chip-read.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write.lc-gran-on,.lc-fa-chip-write.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search.lc-gran-on,.lc-fa-chip-search.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);color:var(--dsw-alias-state-warn-primary)}.lc-fa-delta{font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;gap:5px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-up{color:var(--dsw-alias-state-success-primary)}.lc-fa-down{color:var(--dsw-alias-state-error-primary)}.lc-fa-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.lc-fa-ops{border-left:2px solid var(--dsw-alias-border-l1);flex-direction:column;gap:1px;margin:0 8px 8px 15px;padding:2px 0 2px 10px;display:flex}.lc-fa-op{min-width:0;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;background:0 0;border:0;border-radius:5px;align-items:center;gap:8px;padding:3px 6px;font-size:11px;transition:background-color .12s;display:flex}.lc-fa-op-link{cursor:pointer}.lc-fa-op-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-op-time{width:58px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none}.lc-fa-op-tool{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:0 6px;font-size:11px}.lc-fa-op-detail{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;overflow:hidden}.lc-fa-more{color:var(--dsw-alias-label-secondary);padding:2px 6px;font-size:11px}.lc-fa-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.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{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-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;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}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.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-l1);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;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-l1);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;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;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{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;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;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.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;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;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{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;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.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)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
3243
3863
  const tagId = "dsh-context/styles.css";
3244
3864
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
3245
3865
  const tag = document.createElement("style");
@@ -3284,19 +3904,6 @@ window.__ModuleLoader__.load({
3284
3904
  const kit = makeViewKit(t);
3285
3905
  const settings = createContextSettings();
3286
3906
  const ContextView = makeContextView(ctx, kit, settings);
3287
- ctx.effect(() => {
3288
- const noop = () => {};
3289
- const sessions = ctx.get("sessions");
3290
- if (sessions === void 0 || typeof sessions.provide !== "function") return noop;
3291
- try {
3292
- return sessions.provide({
3293
- props: ["loadOlderHistory"],
3294
- resolve: (binding) => ({ props: { loadOlderHistory: () => binding.session.loadOlder() } })
3295
- });
3296
- } catch {
3297
- return noop;
3298
- }
3299
- }, "dsh-context: loadOlderHistory prop");
3300
3907
  ctx.slots.inject("conversation.view", () => {
3301
3908
  return ctx.slots.register({
3302
3909
  name: "conversation.view",
@@ -3314,7 +3921,7 @@ window.__ModuleLoader__.load({
3314
3921
  id: "context-modal",
3315
3922
  order: 10,
3316
3923
  locale: NS,
3317
- inject: (sessionId) => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
3924
+ inject: (sessionId = "") => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
3318
3925
  }, (props) => h(ContextModal, props));
3319
3926
  });
3320
3927
  ctx.inject(["settingsScope"], (raw) => {