dsh-context 0.31.1 → 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
@@ -13,7 +13,7 @@ window.__ModuleLoader__.load({
13
13
  "cat.inject": "注入内容",
14
14
  "cat.assistant": "助手消息",
15
15
  "cat.tool": "工具结果",
16
- "overview.title": "当前构成",
16
+ "overview.title": "当前上下文",
17
17
  "overview.estimate": "tokens(估算)",
18
18
  "overview.free": "剩余窗口",
19
19
  "overview.used": "上下文已用",
@@ -39,15 +39,14 @@ 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
- "trend.title": "历史趋势",
42
+ "trend.title": "上下文趋势",
45
43
  "gran.step": "步骤",
46
44
  "gran.turn": "轮次",
47
45
  "settings.title": "上下文",
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": "当前环境的设置为只读",
@@ -63,6 +62,14 @@ window.__ModuleLoader__.load({
63
62
  "detail.actual": "实际 prompt {n}",
64
63
  "detail.output": "输出 {n}",
65
64
  "detail.cache": "缓存 {n}%",
65
+ "brief.turn": "本轮",
66
+ "brief.input": "输入",
67
+ "brief.reply": "回复",
68
+ "brief.more": "+{n}",
69
+ "brief.locate": "在上下文浏览器中查看",
70
+ "brief.turnTip": "这一轮开场时用户发送的消息",
71
+ "brief.inputTip": "这一步新进入上下文的内容(通常是上一步工具调用的结果)",
72
+ "brief.replyTip": "这一步模型返回的内容(文本回复或工具调用)",
66
73
  "events.title": "上下文事件",
67
74
  "events.empty": "暂无上下文事件(压缩、注入、模型切换会出现在这里)",
68
75
  "events.at": "第 {t} 轮 · 第 {s} 步",
@@ -73,10 +80,30 @@ window.__ModuleLoader__.load({
73
80
  "kind.prune": "剪枝",
74
81
  "kind.model": "切换",
75
82
  "kind.mode": "模式",
76
- "nodes.title": "消息构成",
77
- "nodes.hint": "当前模型可见的消息,最新在前",
78
- "nodes.more": "… 更早的 {n} 条消息已省略",
79
- "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": "文件",
80
107
  "loading": "正在读取会话日志…",
81
108
  "error": "上下文数据读取失败:",
82
109
  "error.retry": "重试",
@@ -118,6 +145,8 @@ window.__ModuleLoader__.load({
118
145
  "browser.noEpoch": "该步骤的头部内容(系统提示词 / 工具定义)不在保留范围内",
119
146
  "browser.noContent": "完整内容不在当前加载的消息窗口内(在聊天页加载更早历史后可查看)",
120
147
  "browser.loading": "正在从更早的会话历史加载完整内容…",
148
+ "browser.notInLog": "会话日志中未找到该条目的完整内容(可能已被压缩或清理)",
149
+ "browser.loadFailed": "内容加载失败,点击重试",
121
150
  "browser.preview": "预览",
122
151
  "tool.desc": "描述",
123
152
  "tool.params": "参数",
@@ -186,15 +215,14 @@ window.__ModuleLoader__.load({
186
215
  "plugin.hint": "The best DSH context plugin ⭐",
187
216
  "plugin.name": "Plugin",
188
217
  "plugin.github": "GitHub",
189
- "tools.top": "Top Tool Schemas:",
190
- "tools.more": "of {n}",
191
- "trend.title": "Context History",
218
+ "trend.title": "Context Trend",
192
219
  "gran.step": "Step",
193
220
  "gran.turn": "Turn",
194
221
  "settings.title": "Context",
195
222
  "settings.desc": "Display preferences for the Context panel",
196
223
  "settings.gran": "Default trend granularity",
197
224
  "settings.mode": "Default trend display",
225
+ "settings.fileSort": "File Activity default sort",
198
226
  "settings.expand": "Expand",
199
227
  "settings.collapse": "Collapse",
200
228
  "settings.readOnly": "Settings are read-only in this environment",
@@ -210,6 +238,14 @@ window.__ModuleLoader__.load({
210
238
  "detail.actual": "Actual Prompt {n}",
211
239
  "detail.output": "Output {n}",
212
240
  "detail.cache": "Cache {n}%",
241
+ "brief.turn": "User",
242
+ "brief.input": "In",
243
+ "brief.reply": "Response",
244
+ "brief.more": "+{n}",
245
+ "brief.locate": "Reveal in Context Browser",
246
+ "brief.turnTip": "The user message that opened this turn",
247
+ "brief.inputTip": "What newly entered the context for this step (usually results of the previous tool calls)",
248
+ "brief.replyTip": "What the model returned on this step (a text reply or tool calls)",
213
249
  "events.title": "Context Events",
214
250
  "events.empty": "No context events yet (compaction, injections, model switches appear here)",
215
251
  "events.at": "Turn {t} · Step {s}",
@@ -220,10 +256,30 @@ window.__ModuleLoader__.load({
220
256
  "kind.prune": "Prune",
221
257
  "kind.model": "Switch",
222
258
  "kind.mode": "Mode",
223
- "nodes.title": "Messages",
224
- "nodes.hint": "currently model-visible, newest first",
225
- "nodes.more": "… {n} earlier messages omitted",
226
- "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",
227
283
  "loading": "Reading the session log…",
228
284
  "error": "Failed to read context data: ",
229
285
  "error.retry": "Retry",
@@ -265,6 +321,8 @@ window.__ModuleLoader__.load({
265
321
  "browser.noEpoch": "The header content (System Prompt / Tool Schemas) of this step is outside retention",
266
322
  "browser.noContent": "Full content is outside the loaded message window (load older history in Chat to view)",
267
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",
268
326
  "browser.preview": "Preview",
269
327
  "tool.desc": "Description",
270
328
  "tool.params": "Parameters",
@@ -419,8 +477,7 @@ window.__ModuleLoader__.load({
419
477
  "assistant",
420
478
  "tool"
421
479
  ];
422
- /** Category key → chart color, built once from CATS. */
423
- const CAT_COLORS = Object.fromEntries(CATS.map((c) => [c.key, c.color]));
480
+ Object.fromEntries(CATS.map((c) => [c.key, c.color]));
424
481
  function partsOf(breakdown) {
425
482
  return CATS.map((c) => {
426
483
  return {
@@ -462,6 +519,9 @@ window.__ModuleLoader__.load({
462
519
  return CATS.map((c) => ({
463
520
  key: c.key,
464
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. */
465
525
  value: shares[c.key] ?? 0
466
526
  }));
467
527
  }
@@ -558,7 +618,7 @@ window.__ModuleLoader__.load({
558
618
  "assistant",
559
619
  "tool",
560
620
  "total"
561
- ].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;
562
622
  const safeCurrent = current !== null && typeof current === "object" ? current : {};
563
623
  const cost = typeof data.cost === "object" && data.cost !== null && !Array.isArray(data.cost) ? data.cost : void 0;
564
624
  return {
@@ -575,7 +635,6 @@ window.__ModuleLoader__.load({
575
635
  tool: numOf(safeCurrent.tool),
576
636
  total: numOf(safeCurrent.total)
577
637
  },
578
- toolList: objectsOf(data.toolList),
579
638
  requests: objectsOf(data.requests),
580
639
  events: objectsOf(data.events),
581
640
  nodes: objectsOf(data.nodes),
@@ -650,6 +709,157 @@ window.__ModuleLoader__.load({
650
709
  return headers;
651
710
  }
652
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
653
863
  //#region src/client/assemble.ts
654
864
  /** The header epoch in force at `seq` (last logged before it), or the newest. */
655
865
  function headerAt(headers, seq) {
@@ -688,6 +898,56 @@ window.__ModuleLoader__.load({
688
898
  const h = React.createElement;
689
899
  const ReactDOM = require("react-dom");
690
900
  //#endregion
901
+ //#region src/client/callSummary.ts
902
+ function parseCallArgs(raw) {
903
+ if (typeof raw !== "string" || raw === "") return null;
904
+ try {
905
+ const parsed = JSON.parse(raw);
906
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
907
+ } catch {
908
+ return null;
909
+ }
910
+ }
911
+ function summaryInArgs(args) {
912
+ if (args === null) return null;
913
+ for (const k of [
914
+ "description",
915
+ "file_path",
916
+ "path",
917
+ "filePath"
918
+ ]) {
919
+ const v = args[k];
920
+ if (typeof v === "string" && v !== "") return v;
921
+ }
922
+ return null;
923
+ }
924
+ function callSummaryOf(conv) {
925
+ return summaryInArgs(parseCallArgs(conv?.call?.argsRaw));
926
+ }
927
+ function blockSummaryOf(conv) {
928
+ if (conv === void 0 || !Array.isArray(conv.blocks)) return null;
929
+ for (const b of conv.blocks) {
930
+ const blk = b !== null && typeof b === "object" ? b : null;
931
+ if (blk === null || blk.kind !== "tool-call") continue;
932
+ const s = summaryInArgs(parseCallArgs(blk.argsRaw));
933
+ if (s !== null) return s;
934
+ }
935
+ return null;
936
+ }
937
+ /**
938
+ * All tool-call names in an assistant conversation node, in order. The fold's surface node keeps `calls` only for TEXT-LESS replies,
939
+ * so a reply carrying both text and calls recovers its call breadcrumb here through the conversation join.
940
+ */
941
+ function callNamesOf(conv) {
942
+ if (conv === void 0 || !Array.isArray(conv.blocks)) return [];
943
+ const names = [];
944
+ for (const b of conv.blocks) {
945
+ const blk = b !== null && typeof b === "object" ? b : null;
946
+ if (blk !== null && blk.kind === "tool-call" && typeof blk.name === "string") names.push(blk.name);
947
+ }
948
+ return names;
949
+ }
950
+ //#endregion
691
951
  //#region src/client/components/nodes.tsx
692
952
  function makeNodeText(kit) {
693
953
  const { t } = kit;
@@ -701,24 +961,6 @@ window.__ModuleLoader__.load({
701
961
  return t("node.nonText");
702
962
  };
703
963
  }
704
- function makeNodeList(kit) {
705
- const { t, fmt, fmtTime } = kit;
706
- const nodeText = makeNodeText(kit);
707
- return function NodeList(props) {
708
- if (props.nodes.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("nodes.empty"));
709
- const rows = props.nodes.slice().reverse();
710
- 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) => {
711
- const text = nodeText(n);
712
- return /* @__PURE__ */ React.createElement("div", {
713
- key: n.seq,
714
- className: "lc-node"
715
- }, /* @__PURE__ */ React.createElement("i", { style: { background: CAT_COLORS[n.cat] || "#999" } }), /* @__PURE__ */ React.createElement("span", {
716
- className: "lc-node-preview",
717
- title: text
718
- }, 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)));
719
- }));
720
- };
721
- }
722
964
  //#endregion
723
965
  //#region src/client/format.ts
724
966
  /** `fmt`: the k/M suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
@@ -799,6 +1041,8 @@ window.__ModuleLoader__.load({
799
1041
  let bestWidth;
800
1042
  if (gridW < 1) {
801
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. */
802
1046
  if (rows % 2 === 1) rows -= 1;
803
1047
  bestWidth = unit;
804
1048
  bestHeight = rows * unit;
@@ -878,6 +1122,8 @@ window.__ModuleLoader__.load({
878
1122
  }
879
1123
  return null;
880
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. */
881
1127
  return null;
882
1128
  }
883
1129
  }
@@ -1123,18 +1369,22 @@ window.__ModuleLoader__.load({
1123
1369
  */
1124
1370
  function Section(props) {
1125
1371
  const right = props.actions !== void 0 || props.meta !== void 0;
1126
- 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;
1127
1376
  }
1128
1377
  function TextSection(props) {
1129
1378
  const { rich } = props;
1130
1379
  const [mode, setMode] = rich.useRichMode();
1380
+ const lineCount = React.useMemo(() => lineCountOf(props.text), [props.text]);
1131
1381
  return /* @__PURE__ */ React.createElement(Section, {
1132
1382
  label: props.label,
1133
1383
  actions: /* @__PURE__ */ React.createElement(rich.RichSwitch, {
1134
1384
  mode,
1135
1385
  onPick: setMode
1136
1386
  }),
1137
- meta: props.meta
1387
+ meta: /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-meta" }, props.lines(lineCount))
1138
1388
  }, /* @__PURE__ */ React.createElement(rich.RichText, {
1139
1389
  text: props.text,
1140
1390
  mode
@@ -1172,7 +1422,8 @@ window.__ModuleLoader__.load({
1172
1422
  return /* @__PURE__ */ React.createElement(React.Fragment, null, props.description !== void 0 ? /* @__PURE__ */ React.createElement(TextSection, {
1173
1423
  label: props.labels.desc,
1174
1424
  text: props.description,
1175
- rich
1425
+ rich,
1426
+ lines: props.lines
1176
1427
  }) : null, params !== null && rows.length > 0 ? /* @__PURE__ */ React.createElement(Section, {
1177
1428
  label: props.labels.title,
1178
1429
  count: rows.length
@@ -1191,8 +1442,8 @@ window.__ModuleLoader__.load({
1191
1442
  }
1192
1443
  /**
1193
1444
  * Both block vocabularies normalize here — raw durable blocks (`type`: text/reasoning/tool-call/tool-result/image) and snapshot assistant
1194
- * blocks (`kind`: text/reasoning/tool-call/image, argsRaw). Consecutive images group into one grid; tool-result payloads carry the Raw/MD
1195
- * 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.
1196
1447
  */
1197
1448
  function BlocksBody(props) {
1198
1449
  const { rich, img, labels } = props;
@@ -1228,7 +1479,7 @@ window.__ModuleLoader__.load({
1228
1479
  label,
1229
1480
  text: blk.text,
1230
1481
  rich,
1231
- 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
1232
1483
  }));
1233
1484
  continue;
1234
1485
  }
@@ -1261,35 +1512,6 @@ window.__ModuleLoader__.load({
1261
1512
  flushImages();
1262
1513
  return /* @__PURE__ */ React.createElement(React.Fragment, null, out);
1263
1514
  }
1264
- function parseArgs(raw) {
1265
- if (typeof raw !== "string" || raw === "") return null;
1266
- try {
1267
- const parsed = JSON.parse(raw);
1268
- return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
1269
- } catch {
1270
- return null;
1271
- }
1272
- }
1273
- /**
1274
- * A call's preview: bash-style `description`, or the target path (`file_path`/`path`/`filePath`) for edit/read/write tools; null when the
1275
- * arguments hold neither.
1276
- */
1277
- function summaryIn(args) {
1278
- if (args === null) return null;
1279
- for (const k of [
1280
- "description",
1281
- "file_path",
1282
- "path",
1283
- "filePath"
1284
- ]) {
1285
- const v = args[k];
1286
- if (typeof v === "string" && v !== "") return v;
1287
- }
1288
- return null;
1289
- }
1290
- function callSummaryOf(conv) {
1291
- return summaryIn(parseArgs(conv?.call?.argsRaw));
1292
- }
1293
1515
  /**
1294
1516
  * The trailing status markers dsh shell tools append at the END of a result's text while `isError` stays false
1295
1517
  * (the status is result data, per tool-bash's render: "non-zero exits are reported, not errored"):
@@ -1361,18 +1583,8 @@ window.__ModuleLoader__.load({
1361
1583
  exit: tail.exit
1362
1584
  };
1363
1585
  }
1364
- function blockSummaryOf(conv) {
1365
- if (conv === void 0 || !Array.isArray(conv.blocks)) return null;
1366
- for (const b of conv.blocks) {
1367
- const blk = b !== null && typeof b === "object" ? b : null;
1368
- if (blk === null || blk.kind !== "tool-call") continue;
1369
- const s = summaryIn(parseArgs(blk.argsRaw));
1370
- if (s !== null) return s;
1371
- }
1372
- return null;
1373
- }
1374
1586
  function ToolCallCard(props) {
1375
- const args = React.useMemo(() => parseArgs(props.argsRaw), [props.argsRaw]);
1587
+ const args = React.useMemo(() => parseCallArgs(props.argsRaw), [props.argsRaw]);
1376
1588
  return /* @__PURE__ */ React.createElement(Section, {
1377
1589
  label: (props.arrow ?? "→") + " " + props.name,
1378
1590
  labelClass: "lc-ts-call-name",
@@ -1385,6 +1597,8 @@ window.__ModuleLoader__.load({
1385
1597
  }
1386
1598
  function CallArgRow(props) {
1387
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. */
1388
1602
  const text = typeof v === "string" ? v : v === void 0 ? "" : JSON.stringify(v);
1389
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));
1390
1604
  }
@@ -1395,7 +1609,8 @@ window.__ModuleLoader__.load({
1395
1609
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TextSection, {
1396
1610
  label: labels.content,
1397
1611
  text: node.text,
1398
- rich
1612
+ rich,
1613
+ lines: labels.lines
1399
1614
  }), /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
1400
1615
  }
1401
1616
  if (conv.kind === "assistant" && Array.isArray(conv.blocks)) return /* @__PURE__ */ React.createElement(BlocksBody, {
@@ -1425,7 +1640,8 @@ window.__ModuleLoader__.load({
1425
1640
  if (conv.kind === "compaction") return typeof conv.summary === "string" && conv.summary !== "" ? /* @__PURE__ */ React.createElement(TextSection, {
1426
1641
  label: labels.summary,
1427
1642
  text: conv.summary,
1428
- rich
1643
+ rich,
1644
+ lines: labels.lines
1429
1645
  }) : /* @__PURE__ */ React.createElement(React.Fragment, null);
1430
1646
  if (Array.isArray(conv.content)) return /* @__PURE__ */ React.createElement(BlocksBody, {
1431
1647
  blocks: conv.content,
@@ -1456,52 +1672,51 @@ window.__ModuleLoader__.load({
1456
1672
  const nodeText = makeNodeText(kit);
1457
1673
  const rich = makeRichText(kit);
1458
1674
  const ImageCard = makeImageCard(kit);
1459
- const MAX_AUTO_PAGES = 20;
1675
+ const lineLabel = (n) => t(n === 1 ? "block.line" : "block.lines", { n });
1460
1676
  return function ContextBrowser(props) {
1461
1677
  const { data, headers } = props;
1462
1678
  const [sel, setSel] = React.useState("live");
1463
1679
  const [openCat, setOpenCat] = React.useState(null);
1464
1680
  const [openElem, setOpenElem] = React.useState(null);
1465
1681
  const convNodes = typeof props.useSession === "function" ? props.useSession((s) => s.nodes) : void 0;
1682
+ const [fetched, setFetched] = React.useState(() => /* @__PURE__ */ new Map());
1466
1683
  const bySeq = React.useMemo(() => {
1467
1684
  const m = /* @__PURE__ */ new Map();
1468
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);
1469
1687
  return m;
1470
- }, [convNodes]);
1471
- const hasMore = typeof props.useSession === "function" ? props.useSession((s) => s.hasMore === true) : false;
1472
- const loadingOlder = typeof props.useSession === "function" ? props.useSession((s) => s.loadingOlder === true) : false;
1688
+ }, [convNodes, fetched]);
1473
1689
  const openSeq = openElem !== null && openElem.startsWith("n") ? Number(openElem.slice(1)) : null;
1474
1690
  const missingSeq = openSeq !== null && !bySeq.has(openSeq) ? openSeq : null;
1475
- const [exhausted, setExhausted] = React.useState(false);
1476
- const pagesRef = React.useRef(0);
1477
- React.useEffect(() => {
1478
- pagesRef.current = 0;
1479
- setExhausted(false);
1480
- }, [openElem]);
1481
- const loadOlderHistory = props.loadOlderHistory;
1482
- React.useEffect(() => {
1483
- if (missingSeq === null || !hasMore || loadingOlder || exhausted) return;
1484
- if (loadOlderHistory === void 0) return;
1485
- if (pagesRef.current >= MAX_AUTO_PAGES) {
1486
- setExhausted(true);
1487
- return;
1488
- }
1489
- pagesRef.current += 1;
1490
- loadOlderHistory();
1491
- }, [
1492
- missingSeq,
1493
- hasMore,
1494
- loadingOlder,
1495
- exhausted,
1496
- bySeq,
1497
- loadOlderHistory
1498
- ]);
1691
+ const fetchContent = props.fetchContent;
1692
+ const [missState, setMissState] = React.useState("idle");
1693
+ const [retry, setRetry] = React.useState(0);
1499
1694
  React.useEffect(() => {
1500
- 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
+ };
1501
1716
  }, [
1502
- hasMore,
1503
1717
  missingSeq,
1504
- exhausted
1718
+ fetchContent,
1719
+ retry
1505
1720
  ]);
1506
1721
  const pinSeq = props.pinSeq;
1507
1722
  React.useEffect(() => {
@@ -1509,15 +1724,29 @@ window.__ModuleLoader__.load({
1509
1724
  setOpenCat(null);
1510
1725
  setOpenElem(null);
1511
1726
  }, [pinSeq]);
1512
- const toolFocus = props.toolFocus;
1727
+ const rootRef = React.useRef(null);
1728
+ const focusScrollRef = React.useRef(false);
1729
+ const nodeFocus = props.nodeFocus;
1513
1730
  React.useEffect(() => {
1514
- if (toolFocus === null || toolFocus === void 0) return;
1515
- setSel("live");
1516
- setOpenCat("tools");
1517
- setOpenElem(toolFocus.tool !== void 0 ? "tool:" + toolFocus.tool : null);
1518
- if (props.onToolFocusHandled !== void 0) props.onToolFocusHandled();
1519
- }, [toolFocus, props.onToolFocusHandled]);
1520
- const awaiting = missingSeq !== null && !exhausted && loadOlderHistory !== void 0 && hasMore;
1731
+ if (nodeFocus === null || nodeFocus === void 0) return;
1732
+ setSel(nodeFocus.step);
1733
+ setOpenCat(nodeFocus.cat);
1734
+ setOpenElem("n" + String(nodeFocus.seq));
1735
+ focusScrollRef.current = true;
1736
+ if (props.onNodeFocusHandled !== void 0) props.onNodeFocusHandled();
1737
+ }, [nodeFocus, props.onNodeFocusHandled]);
1738
+ React.useLayoutEffect(() => {
1739
+ if (!focusScrollRef.current) return;
1740
+ focusScrollRef.current = false;
1741
+ rootRef.current?.querySelector(".lc-br-elem-on")?.scrollIntoView({ block: "nearest" });
1742
+ });
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");
1521
1750
  const requests = data.requests;
1522
1751
  const hoverReq = props.previewSeq !== null && props.previewSeq !== void 0 ? requests.find((r) => r.seq === props.previewSeq) ?? null : null;
1523
1752
  const req = hoverReq ?? (sel === "live" ? null : requests.find((r) => r.seq === sel) ?? null);
@@ -1540,8 +1769,13 @@ window.__ModuleLoader__.load({
1540
1769
  const toolCount = (c) => countOf(view, byCat, c);
1541
1770
  const toggleCat = (c) => {
1542
1771
  if (!(toolCount(c) > 0 || (c === "system" || c === "tools") && view.header === null)) return;
1543
- setOpenCat(openCat === c ? null : c);
1544
- 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);
1545
1779
  };
1546
1780
  const toggleElem = (key) => {
1547
1781
  setOpenElem(openElem === key ? null : key);
@@ -1570,11 +1804,14 @@ window.__ModuleLoader__.load({
1570
1804
  if (c === "system") {
1571
1805
  if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
1572
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. */
1573
1809
  if (system === void 0) return null;
1574
1810
  return elemRow("sys", null, system.replace(/\s+/g, " ").trim().slice(0, 80), breakdown.system, void 0, /* @__PURE__ */ React.createElement(TextSection, {
1575
1811
  label: catLabel("system"),
1576
1812
  text: system,
1577
- rich
1813
+ rich,
1814
+ lines: lineLabel
1578
1815
  }));
1579
1816
  }
1580
1817
  if (c === "tools") {
@@ -1591,6 +1828,7 @@ window.__ModuleLoader__.load({
1591
1828
  description: tool.description,
1592
1829
  schema: tool.schema,
1593
1830
  rich,
1831
+ lines: lineLabel,
1594
1832
  labels
1595
1833
  }));
1596
1834
  });
@@ -1630,14 +1868,17 @@ window.__ModuleLoader__.load({
1630
1868
  summary: t("block.summary"),
1631
1869
  images: t("attach.images"),
1632
1870
  other: t("attach.other"),
1633
- lines: (n) => t(n === 1 ? "block.line" : "block.lines", { n }),
1871
+ lines: lineLabel,
1634
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"))
1635
1873
  },
1636
- hint: conv === void 0 && awaiting ? t("browser.loading") : t("browser.noContent")
1874
+ hint: conv === void 0 ? missNote : t("browser.noContent")
1637
1875
  }), rowErr);
1638
1876
  });
1639
1877
  };
1640
- return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("browser.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-br-hint" }, t("browser.deltaHint")), /* @__PURE__ */ React.createElement("select", {
1878
+ return /* @__PURE__ */ React.createElement("div", {
1879
+ className: "lc-card",
1880
+ ref: rootRef
1881
+ }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("browser.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-br-hint" }, t("browser.deltaHint")), /* @__PURE__ */ React.createElement("select", {
1641
1882
  className: "lc-br-pick",
1642
1883
  value: seq === null ? "live" : String(seq),
1643
1884
  onChange: (e) => {
@@ -1673,12 +1914,15 @@ window.__ModuleLoader__.load({
1673
1914
  }, /* @__PURE__ */ React.createElement("button", {
1674
1915
  type: "button",
1675
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). */
1676
1919
  onMouseEnter: linked ? () => {
1677
1920
  if (props.onHoverKey !== void 0) props.onHoverKey(c.key);
1678
1921
  } : void 0,
1679
1922
  onMouseLeave: linked ? () => {
1680
1923
  if (props.onHoverKey !== void 0) props.onHoverKey(null);
1681
1924
  } : void 0,
1925
+ /* v8 ignore stop */
1682
1926
  onClick: () => {
1683
1927
  toggleCat(c.key);
1684
1928
  }
@@ -1834,26 +2078,7 @@ window.__ModuleLoader__.load({
1834
2078
  parts: head.parts,
1835
2079
  hoverKey: props.hoverKey,
1836
2080
  onHoverKey: props.onHoverKey
1837
- }), props.tools !== void 0 && props.tools.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-tools" }, /* @__PURE__ */ React.createElement("button", {
1838
- type: "button",
1839
- className: "lc-tools-label",
1840
- onClick: () => {
1841
- if (props.onToolFocus !== void 0) props.onToolFocus({});
1842
- }
1843
- }, t("tools.top")), props.tools.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => /* @__PURE__ */ React.createElement("button", {
1844
- key: tool.name,
1845
- type: "button",
1846
- className: "lc-tool-chip",
1847
- onClick: () => {
1848
- if (props.onToolFocus !== void 0) props.onToolFocus({ tool: tool.name });
1849
- }
1850
- }, tool.name + " " + fmt(tool.tokens))), props.tools.length > 5 ? /* @__PURE__ */ React.createElement("button", {
1851
- type: "button",
1852
- className: "lc-tools-more",
1853
- onClick: () => {
1854
- if (props.onToolFocus !== void 0) props.onToolFocus({});
1855
- }
1856
- }, t("tools.more", { n: props.tools.length })) : null) : null);
2081
+ }));
1857
2082
  };
1858
2083
  }
1859
2084
  //#endregion
@@ -1897,7 +2122,7 @@ window.__ModuleLoader__.load({
1897
2122
  const breakdown = typeof props.useProjection === "function" ? contextBreakdownOf(props.useProjection("contextBreakdown")) : null;
1898
2123
  const headers = typeof props.useProjection === "function" ? headersOf(props.useProjection("contextHeaders")) : null;
1899
2124
  const [hoverCat, setHoverCat] = React.useState(null);
1900
- const [toolFocus, setToolFocus] = React.useState(null);
2125
+ const fetchContent = React.useMemo(() => sessionId !== "" ? makeContentFetcher(ctx, sessionId) : void 0, [ctx, sessionId]);
1901
2126
  const close = React.useCallback(() => {
1902
2127
  if (sessionId === "") return;
1903
2128
  modalStoreOf(sessionId).set(false);
@@ -1940,20 +2165,14 @@ window.__ModuleLoader__.load({
1940
2165
  head,
1941
2166
  subtitle,
1942
2167
  hoverKey: hoverCat,
1943
- onHoverKey: setHoverCat,
1944
- tools: data.toolList,
1945
- onToolFocus: setToolFocus
2168
+ onHoverKey: setHoverCat
1946
2169
  }), /* @__PURE__ */ React.createElement(ContextBrowser, {
1947
2170
  data,
1948
2171
  headers,
1949
2172
  useSession: props.useSession,
1950
- loadOlderHistory: props.loadOlderHistory,
2173
+ fetchContent,
1951
2174
  hoverKey: hoverCat,
1952
- onHoverKey: setHoverCat,
1953
- toolFocus,
1954
- onToolFocusHandled: () => {
1955
- setToolFocus(null);
1956
- }
2175
+ onHoverKey: setHoverCat
1957
2176
  }))));
1958
2177
  }
1959
2178
  return function ContextModal(props) {
@@ -2036,6 +2255,27 @@ window.__ModuleLoader__.load({
2036
2255
  onPick: (id) => {
2037
2256
  props.set?.("defaultTrendMode", id);
2038
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
+ }
2039
2279
  })) : null);
2040
2280
  };
2041
2281
  }
@@ -2046,7 +2286,8 @@ window.__ModuleLoader__.load({
2046
2286
  const v = value;
2047
2287
  return {
2048
2288
  ...v.defaultGranularity === "step" || v.defaultGranularity === "turn" ? { granularity: v.defaultGranularity } : {},
2049
- ...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 } : {}
2050
2291
  };
2051
2292
  }
2052
2293
  function createContextSettings() {
@@ -2054,12 +2295,13 @@ window.__ModuleLoader__.load({
2054
2295
  status: "loading",
2055
2296
  granularity: "step",
2056
2297
  mode: "total",
2298
+ fileSort: "count",
2057
2299
  writable: false
2058
2300
  };
2059
2301
  let scope;
2060
2302
  const listeners = /* @__PURE__ */ new Set();
2061
2303
  const publish = (next) => {
2062
- 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;
2063
2305
  state = next;
2064
2306
  for (const listener of listeners) listener();
2065
2307
  };
@@ -2075,6 +2317,7 @@ window.__ModuleLoader__.load({
2075
2317
  },
2076
2318
  defaultGranularity: () => state.granularity,
2077
2319
  defaultTrendMode: () => state.mode,
2320
+ defaultFileSort: () => state.fileSort,
2078
2321
  attach(bound) {
2079
2322
  scope = bound;
2080
2323
  const sync = () => {
@@ -2084,6 +2327,7 @@ window.__ModuleLoader__.load({
2084
2327
  status: snap.status === "ready" || snap.status === "unavailable" ? snap.status : "loading",
2085
2328
  granularity: prefs.granularity ?? state.granularity,
2086
2329
  mode: prefs.mode ?? state.mode,
2330
+ fileSort: prefs.fileSort ?? state.fileSort,
2087
2331
  writable: snap.writable
2088
2332
  });
2089
2333
  };
@@ -2100,6 +2344,257 @@ window.__ModuleLoader__.load({
2100
2344
  };
2101
2345
  }
2102
2346
  //#endregion
2347
+ //#region src/client/brief.ts
2348
+ /** Every served node (live tail + removed archive copies), seq-sorted. */
2349
+ function briefNodes(data) {
2350
+ return [...data.nodes, ...data.archive].sort((a, b) => a.seq - b.seq);
2351
+ }
2352
+ /** First index whose node seq is >= `seq` (lower bound over the sorted list). */
2353
+ function lowerBound(nodes, seq) {
2354
+ let lo = 0;
2355
+ let hi = nodes.length;
2356
+ while (lo < hi) {
2357
+ const mid = lo + hi >> 1;
2358
+ if (nodes[mid].seq < seq) lo = mid + 1;
2359
+ else hi = mid;
2360
+ }
2361
+ return lo;
2362
+ }
2363
+ /**
2364
+ * Derive the brief for `requests[idx]` (the DISPLAY list — step records or
2365
+ * turn aggregates; a turn aggregate is always a turn start, so its inputs row
2366
+ * stays hidden and the opener/response carry the narrative).
2367
+ */
2368
+ function briefOf(nodes, requests, idx) {
2369
+ if (idx < 0 || idx >= requests.length) return null;
2370
+ const req = requests[idx];
2371
+ const ri = lowerBound(nodes, req.seq);
2372
+ const hit = ri < nodes.length && nodes[ri].seq === req.seq ? nodes[ri] : void 0;
2373
+ const response = hit !== void 0 && hit.cat === "assistant" ? hit : void 0;
2374
+ const turnStart = idx === 0 || (requests[idx - 1].turn ?? 0) !== (req.turn ?? 0);
2375
+ let firstIdx = idx;
2376
+ while (firstIdx > 0 && (requests[firstIdx - 1].turn ?? 0) === (req.turn ?? 0)) firstIdx--;
2377
+ const upper = requests[firstIdx].seq;
2378
+ const lower = firstIdx > 0 ? requests[firstIdx - 1].seq : -1;
2379
+ let opener;
2380
+ for (let i = lowerBound(nodes, upper) - 1; i >= 0; i--) {
2381
+ const n = nodes[i];
2382
+ if (n.seq <= lower) break;
2383
+ if (n.cat === "user") {
2384
+ opener = n;
2385
+ break;
2386
+ }
2387
+ }
2388
+ const inputs = [];
2389
+ if (!turnStart) {
2390
+ const prevSeq = requests[idx - 1].seq;
2391
+ for (let i = lowerBound(nodes, prevSeq + 1); i < nodes.length && nodes[i].seq < req.seq; i++) inputs.push(nodes[i]);
2392
+ }
2393
+ return {
2394
+ opener,
2395
+ inputs,
2396
+ response
2397
+ };
2398
+ }
2399
+ /**
2400
+ * seq → one-line reply preview for the chart tooltip: the response node's
2401
+ * text, else its tool-call breadcrumb. Absent for usage-only replies and
2402
+ * steps outside retention.
2403
+ */
2404
+ function replyTipsOf(nodes) {
2405
+ const m = /* @__PURE__ */ new Map();
2406
+ for (const n of nodes) {
2407
+ if (n.cat !== "assistant") continue;
2408
+ const s = n.text ?? (n.calls !== void 0 ? n.calls.join(" › ") : "");
2409
+ if (s !== "") m.set(n.seq, s);
2410
+ }
2411
+ return m;
2412
+ }
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
2103
2598
  //#region src/client/components/events.tsx
2104
2599
  const EVENT_ICONS = {
2105
2600
  compaction: "✂",
@@ -2193,6 +2688,160 @@ window.__ModuleLoader__.load({
2193
2688
  }));
2194
2689
  };
2195
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
+ }
2196
2845
  const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
2197
2846
  const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
2198
2847
  //#endregion
@@ -2236,7 +2885,7 @@ window.__ModuleLoader__.load({
2236
2885
  return function PluginInfo() {
2237
2886
  const [latest, setLatest] = React.useState(null);
2238
2887
  React.useEffect(() => {
2239
- if ("0.31.1".includes("-dev")) return;
2888
+ if ("0.33.1".includes("-dev")) return;
2240
2889
  let on = true;
2241
2890
  fetchLatestVersion().then((v) => {
2242
2891
  if (on && v) setLatest(v);
@@ -2245,8 +2894,8 @@ window.__ModuleLoader__.load({
2245
2894
  on = false;
2246
2895
  };
2247
2896
  }, []);
2248
- const update = latest !== null && isNewerVersion(latest, "0.31.1") ? latest : null;
2249
- const nameValue = ["dsh-context (v0.31.1)"];
2897
+ const update = latest !== null && isNewerVersion(latest, "0.33.1") ? latest : null;
2898
+ const nameValue = ["dsh-context (v0.33.1)"];
2250
2899
  if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
2251
2900
  key: "update",
2252
2901
  className: "lc-pi-update"
@@ -2258,12 +2907,153 @@ window.__ModuleLoader__.load({
2258
2907
  //#region src/client/components/requestDetail.tsx
2259
2908
  function makeRequestDetail(kit, StackedBar) {
2260
2909
  const { t, fmt, fmtTime, catLabel, eventLabel, eventAt } = kit;
2910
+ /**
2911
+ * One brief row: a fixed-width kind tag plus one glanceable line. The tag carries a styled, instant explanation bubble (the
2912
+ * `.lc-stat-tip` pattern); the content span keeps the native title (preview + locate hint), so the two never stack.
2913
+ * Clickable when the browser linkage is wired.
2914
+ */
2915
+ function BriefRow(props) {
2916
+ const cls = "lc-brief-row" + (props.onLocate !== void 0 ? " lc-brief-row-link" : "");
2917
+ const inner = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-brief-tag" }, props.tag, /* @__PURE__ */ React.createElement("span", {
2918
+ className: "lc-brief-tip",
2919
+ role: "tooltip"
2920
+ }, props.tagTip)), props.children);
2921
+ if (props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className: cls }, inner);
2922
+ const locate = () => {
2923
+ props.onLocate?.(props.node, props.isResponse);
2924
+ };
2925
+ return /* @__PURE__ */ React.createElement("button", {
2926
+ type: "button",
2927
+ className: cls,
2928
+ onClick: locate
2929
+ }, inner);
2930
+ }
2931
+ /** One-line identity of a surface node: text preview, call breadcrumb, tool name, or a localized placeholder. */
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. */
2935
+ if (n.cat === "tool") return callSummaryOf(conv) ?? n.tool ?? t("node.toolResult");
2936
+ if (n.text !== void 0 && n.text !== "") return n.text;
2937
+ if (n.skill !== void 0) return t("node.skillTag", { name: n.skill });
2938
+ if (n.calls !== void 0 && n.calls.length > 0) {
2939
+ const summary = blockSummaryOf(conv);
2940
+ return n.calls.join(" › ") + (summary !== null ? " · " + summary : "");
2941
+ }
2942
+ /* v8 ignore if -- chipParts returns for assistant cats upstream. Defensive. */
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. */
2946
+ if (n.cat === "inject") return t("form." + (n.form ?? "context"));
2947
+ return t("node.nonText");
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
+ }
3001
+ function BriefSection(props) {
3002
+ const { opener, inputs, response } = props.brief;
3003
+ if (opener === void 0 && inputs.length === 0 && response === void 0) return null;
3004
+ const convOf = props.convOf ?? (() => void 0);
3005
+ const hint = props.onLocate !== void 0 ? " — " + t("brief.locate") : "";
3006
+ const locateChip = props.onLocate === void 0 ? void 0 : (n) => (e) => {
3007
+ e?.stopPropagation();
3008
+ props.onLocate?.(n, false);
3009
+ };
3010
+ const MAX_CHIPS = 3;
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, {
3027
+ tag: t("brief.turn"),
3028
+ tagTip: t("brief.turnTip"),
3029
+ node: opener,
3030
+ isResponse: false,
3031
+ onLocate: props.onLocate
3032
+ }, openerParts.tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-fact" }, openerParts.tag) : null, openerParts.text !== "" ? /* @__PURE__ */ React.createElement("span", {
3033
+ className: "lc-brief-text",
3034
+ title: factTitle(openerParts.tag, openerParts.text) + hint
3035
+ }, openerParts.text) : null) : null, inputs.length > 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
3036
+ tag: t("brief.input"),
3037
+ tagTip: t("brief.inputTip"),
3038
+ node: inputs[0],
3039
+ isResponse: false,
3040
+ onLocate: props.onLocate
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, {
3042
+ tag: t("brief.reply"),
3043
+ tagTip: t("brief.replyTip"),
3044
+ node: response,
3045
+ isResponse: true,
3046
+ onLocate: props.onLocate
3047
+ }, nodeChip(response, void 0, true)) : null);
3048
+ }
2261
3049
  return function RequestDetail(props) {
2262
3050
  const req = props.request;
2263
3051
  if (!req) return null;
2264
3052
  const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
2265
3053
  const head = isTurn ? t("detail.turn", {
2266
3054
  t: req.turn ?? 0,
3055
+ /* v8 ignore next -- isTurn already guarantees stepCount !== undefined;
3056
+ the fallback is defensive. */
2267
3057
  n: req.stepCount ?? 0
2268
3058
  }) : t("detail.step", {
2269
3059
  t: req.turn ?? 0,
@@ -2288,7 +3078,11 @@ window.__ModuleLoader__.load({
2288
3078
  return /* @__PURE__ */ React.createElement("div", { className: "lc-detail" }, /* @__PURE__ */ React.createElement("div", { className: "lc-detail-head" }, /* @__PURE__ */ React.createElement("b", null, head), marker !== null && markerAt !== null ? /* @__PURE__ */ React.createElement("span", {
2289
3079
  className: "lc-detail-marker",
2290
3080
  title: eventLabel(marker)
2291
- }, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("gran.delta")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-detail-time" }, fmtTime(req.time)), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" + (net > 0 ? " lc-detail-metric-up" : net < 0 ? " lc-detail-metric-down" : "") }, t("tip.delta", { n: (net > 0 ? "+" : "") + fmt(net) })) : null, !delta && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.actual", { n: fmt(req.prompt) })) : null, !delta && req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.output", { n: fmt(req.output) })) : null, !delta && req.prompt !== void 0 && req.cacheRead !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.cache", { n: cacheHitPercent$1(req.cacheRead, req.prompt) ?? "—" })) : null), /* @__PURE__ */ React.createElement(StackedBar, {
3081
+ }, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("gran.delta")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-detail-time" }, fmtTime(req.time)), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" + (net > 0 ? " lc-detail-metric-up" : net < 0 ? " lc-detail-metric-down" : "") }, t("tip.delta", { n: (net > 0 ? "+" : "") + fmt(net) })) : null, !delta && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.actual", { n: fmt(req.prompt) })) : null, !delta && req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.output", { n: fmt(req.output) })) : null, !delta && req.prompt !== void 0 && req.cacheRead !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.cache", { n: cacheHitPercent$1(req.cacheRead, req.prompt) ?? "—" })) : null), props.brief !== null && props.brief !== void 0 ? /* @__PURE__ */ React.createElement(BriefSection, {
3082
+ brief: props.brief,
3083
+ convOf: props.convOf,
3084
+ onLocate: props.onLocate
3085
+ }) : null, /* @__PURE__ */ React.createElement(StackedBar, {
2292
3086
  parts,
2293
3087
  height: 10
2294
3088
  }), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c, i) => {
@@ -2691,6 +3485,8 @@ window.__ModuleLoader__.load({
2691
3485
  };
2692
3486
  React.useLayoutEffect(() => {
2693
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. */
2694
3490
  if (el === null) return;
2695
3491
  if (props.granularity !== lastGranRef.current) {
2696
3492
  lastGranRef.current = props.granularity;
@@ -2719,11 +3515,15 @@ window.__ModuleLoader__.load({
2719
3515
  t: req.turn ?? 0,
2720
3516
  s: req.step ?? 0
2721
3517
  });
3518
+ const reply = props.replyTips?.get(req.seq);
3519
+ const tail = reply !== void 0 ? " · “" + (reply.length > 48 ? reply.slice(0, 48) + "…" : reply) + "”" : "";
2722
3520
  if (delta) {
3521
+ /* v8 ignore next 1 -- delta mode only receives records from
3522
+ deltaOf, which always assigns net; the fallback is defensive. */
2723
3523
  const n = req.net ?? 0;
2724
- return head + " · " + fmtTime(req.time) + " · " + t("tip.delta", { n: (n > 0 ? "+" : "") + fmt(n) });
3524
+ return head + " · " + fmtTime(req.time) + " · " + t("tip.delta", { n: (n > 0 ? "+" : "") + fmt(n) }) + tail;
2725
3525
  }
2726
- return head + " · " + fmtTime(req.time) + " · " + t("tip.total", { n: fmt(req.total) }) + (req.prompt !== void 0 ? " · " + t("tip.actual", { n: fmt(req.prompt) }) : "");
3526
+ return head + " · " + fmtTime(req.time) + " · " + t("tip.total", { n: fmt(req.total) }) + (req.prompt !== void 0 ? " · " + t("tip.actual", { n: fmt(req.prompt) }) : "") + tail;
2727
3527
  };
2728
3528
  const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
2729
3529
  const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
@@ -2803,7 +3603,7 @@ window.__ModuleLoader__.load({
2803
3603
  const TrendChart = makeTrendChart(kit);
2804
3604
  const RequestDetail = makeRequestDetail(kit, StackedBar);
2805
3605
  const EventList = makeEventList(kit);
2806
- const NodeList = makeNodeList(kit);
3606
+ const FileCard = makeFileCard(kit, settings);
2807
3607
  const StatsBoard = makeStatsBoard(kit);
2808
3608
  const PluginInfo = makePluginInfo(kit);
2809
3609
  const ContextBrowser = makeContextBrowser(kit, StackedBar);
@@ -2830,9 +3630,9 @@ window.__ModuleLoader__.load({
2830
3630
  return p.length === 1 ? [...EVENT_KINDS] : p.filter((x) => x !== k);
2831
3631
  });
2832
3632
  };
2833
- const [toolFocus, setToolFocus] = React.useState(null);
2834
- const clearToolFocus = React.useCallback(() => {
2835
- setToolFocus(null);
3633
+ const [nodeFocus, setNodeFocus] = React.useState(null);
3634
+ const clearNodeFocus = React.useCallback(() => {
3635
+ setNodeFocus(null);
2836
3636
  }, []);
2837
3637
  const loadImage = React.useMemo(() => {
2838
3638
  if (typeof sessionId !== "string" || sessionId === "") return void 0;
@@ -2840,6 +3640,7 @@ window.__ModuleLoader__.load({
2840
3640
  if (conversation === void 0 || typeof conversation.resolveImage !== "function") return void 0;
2841
3641
  return (attachment) => conversation.resolveImage(sessionId, attachment);
2842
3642
  }, [sessionId]);
3643
+ const fetchContent = React.useMemo(() => typeof sessionId === "string" && sessionId !== "" ? makeContentFetcher(ctx, sessionId) : void 0, [ctx, sessionId]);
2843
3644
  const rootRef = React.useRef(null);
2844
3645
  const scrollerRef = React.useRef(null);
2845
3646
  const restoredRef = React.useRef(null);
@@ -2847,6 +3648,8 @@ window.__ModuleLoader__.load({
2847
3648
  if (typeof sessionId !== "string" || sessionId === "" || data === null) return;
2848
3649
  if (restoredRef.current === sessionId) return;
2849
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. */
2850
3653
  const scroller = rootRef.current !== null ? rootRef.current.closest("[data-conversation-scroll]") : null;
2851
3654
  if (scroller === null) return;
2852
3655
  scrollerRef.current = scroller;
@@ -2863,15 +3666,24 @@ window.__ModuleLoader__.load({
2863
3666
  const requests = data ? data.requests : [];
2864
3667
  const events = data ? data.events : [];
2865
3668
  const shownEvents = pickedKinds.length === EVENT_KINDS.length ? events : events.filter((e) => pickedKinds.includes(e.kind));
2866
- const nodes = data ? data.nodes : [];
2867
3669
  const displayRequests = React.useMemo(() => granularity === "turn" ? aggregateByTurn(requests) : requests, [requests, granularity]);
2868
3670
  const markers = React.useMemo(() => attachMarkers(displayRequests, events), [displayRequests, events]);
3671
+ const briefList = React.useMemo(() => data ? briefNodes(data) : [], [data]);
3672
+ const replyTips = React.useMemo(() => replyTipsOf(briefList), [briefList]);
3673
+ const convNodes = typeof props.useSession === "function" ? props.useSession((s) => s.nodes) : void 0;
3674
+ const bySeq = React.useMemo(() => {
3675
+ const m = /* @__PURE__ */ new Map();
3676
+ for (const n of convNodes ?? []) m.set(n.seq, n);
3677
+ return m;
3678
+ }, [convNodes]);
2869
3679
  if (!data) return /* @__PURE__ */ React.createElement("div", {
2870
3680
  className: "lc-root",
2871
3681
  ref: rootRef
2872
3682
  }, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
2873
3683
  const markerOf = (req) => {
2874
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. */
2875
3687
  return i >= 0 ? markers[i] : void 0;
2876
3688
  };
2877
3689
  let pinnedIdx = -1;
@@ -2887,6 +3699,43 @@ window.__ModuleLoader__.load({
2887
3699
  if (activeIdx < 0) activeIdx = pinnedIdx;
2888
3700
  if (activeIdx < 0 && displayRequests.length > 0) activeIdx = displayRequests.length - 1;
2889
3701
  const activeReq = activeIdx >= 0 ? displayRequests[activeIdx] : null;
3702
+ const brief = activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null;
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
+ };
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. */
3730
+ if (activeReq === null) return;
3731
+ const next = isResponse && activeIdx + 1 < displayRequests.length ? displayRequests[activeIdx + 1] : null;
3732
+ const step = isResponse ? next !== null ? next.seq : "live" : activeReq.seq;
3733
+ setNodeFocus({
3734
+ step,
3735
+ seq: node.seq,
3736
+ cat: node.cat
3737
+ });
3738
+ };
2890
3739
  let activeTurn = hoverTurn;
2891
3740
  if (activeTurn === null && hoveredSeq !== null) {
2892
3741
  for (const req of displayRequests) if (req.seq === hoveredSeq) {
@@ -2912,9 +3761,7 @@ window.__ModuleLoader__.load({
2912
3761
  head,
2913
3762
  subtitle: (data.model ? data.model : "") + (data.provider ? " · " + data.provider : ""),
2914
3763
  hoverKey: hoverCat,
2915
- onHoverKey: setHoverCat,
2916
- tools: data.toolList,
2917
- onToolFocus: setToolFocus
3764
+ onHoverKey: setHoverCat
2918
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", {
2919
3766
  className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
2920
3767
  onClick: () => {
@@ -2957,22 +3804,29 @@ window.__ModuleLoader__.load({
2957
3804
  },
2958
3805
  onFocusTurnHandled: () => {
2959
3806
  setFocusTurn(null);
2960
- }
3807
+ },
3808
+ replyTips
2961
3809
  }), /* @__PURE__ */ React.createElement(RequestDetail, {
2962
3810
  request: activeReq,
2963
3811
  prev: trendMode === "delta" && activeIdx >= 0 ? activeIdx > 0 ? displayRequests[activeIdx - 1] : null : void 0,
2964
- marker: activeReq !== null ? markerOf(activeReq) : 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. */
3815
+ marker: activeReq !== null ? markerOf(activeReq) : void 0,
3816
+ brief,
3817
+ convOf,
3818
+ onLocate: locateNode
2965
3819
  })))), /* @__PURE__ */ React.createElement("div", { className: "lc-col lc-col-browser" }, /* @__PURE__ */ React.createElement(ContextBrowser, {
2966
3820
  data,
2967
3821
  headers,
2968
3822
  useSession: props.useSession,
2969
- loadOlderHistory: props.loadOlderHistory,
3823
+ fetchContent,
2970
3824
  previewSeq: hoveredSeq,
2971
3825
  pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
2972
3826
  hoverKey: hoverCat,
2973
3827
  onHoverKey: setHoverCat,
2974
- toolFocus,
2975
- onToolFocusHandled: clearToolFocus,
3828
+ nodeFocus,
3829
+ onNodeFocusHandled: clearNodeFocus,
2976
3830
  loadImage
2977
3831
  }))), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("events.title")), /* @__PURE__ */ React.createElement("div", { className: "lc-kinds" }, EVENT_KINDS.map((k) => /* @__PURE__ */ React.createElement("button", {
2978
3832
  key: k,
@@ -2980,10 +3834,11 @@ window.__ModuleLoader__.load({
2980
3834
  onClick: () => {
2981
3835
  toggleKind(k);
2982
3836
  }
2983
- }, 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, {
2984
- nodes,
2985
- dropped: data.droppedNodes || 0
2986
- }))), /* @__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")));
2987
3842
  }
2988
3843
  return function ContextView(props) {
2989
3844
  return h(ErrorBoundary, null, h(ContextViewBody, props));
@@ -3004,7 +3859,7 @@ window.__ModuleLoader__.load({
3004
3859
  }
3005
3860
  //#endregion
3006
3861
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles.css.mjs
3007
- 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-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}";
3008
3863
  const tagId = "dsh-context/styles.css";
3009
3864
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
3010
3865
  const tag = document.createElement("style");
@@ -3049,19 +3904,6 @@ window.__ModuleLoader__.load({
3049
3904
  const kit = makeViewKit(t);
3050
3905
  const settings = createContextSettings();
3051
3906
  const ContextView = makeContextView(ctx, kit, settings);
3052
- ctx.effect(() => {
3053
- const noop = () => {};
3054
- const sessions = ctx.get("sessions");
3055
- if (sessions === void 0 || typeof sessions.provide !== "function") return noop;
3056
- try {
3057
- return sessions.provide({
3058
- props: ["loadOlderHistory"],
3059
- resolve: (binding) => ({ props: { loadOlderHistory: () => binding.session.loadOlder() } })
3060
- });
3061
- } catch {
3062
- return noop;
3063
- }
3064
- }, "dsh-context: loadOlderHistory prop");
3065
3907
  ctx.slots.inject("conversation.view", () => {
3066
3908
  return ctx.slots.register({
3067
3909
  name: "conversation.view",
@@ -3079,7 +3921,7 @@ window.__ModuleLoader__.load({
3079
3921
  id: "context-modal",
3080
3922
  order: 10,
3081
3923
  locale: NS,
3082
- inject: (sessionId) => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
3924
+ inject: (sessionId = "") => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
3083
3925
  }, (props) => h(ContextModal, props));
3084
3926
  });
3085
3927
  ctx.inject(["settingsScope"], (raw) => {