dsh-context 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -4
- package/lib/client.js +1532 -258
- package/lib/index.d.ts +0 -8
- package/lib/index.js +14 -17
- package/package.json +8 -3
package/lib/client.js
CHANGED
|
@@ -39,8 +39,6 @@ window.__ModuleLoader__.load({
|
|
|
39
39
|
"plugin.hint": "The best DSH context plugin ⭐",
|
|
40
40
|
"plugin.name": "插件",
|
|
41
41
|
"plugin.github": "GitHub",
|
|
42
|
-
"tools.top": "工具定义 Top:",
|
|
43
|
-
"tools.more": "等 {n} 个",
|
|
44
42
|
"trend.title": "上下文趋势",
|
|
45
43
|
"gran.step": "步骤",
|
|
46
44
|
"gran.turn": "轮次",
|
|
@@ -48,6 +46,7 @@ window.__ModuleLoader__.load({
|
|
|
48
46
|
"settings.desc": "上下文面板的展示偏好",
|
|
49
47
|
"settings.gran": "趋势图默认粒度",
|
|
50
48
|
"settings.mode": "趋势图默认展示",
|
|
49
|
+
"settings.fileSort": "文件活动默认排序",
|
|
51
50
|
"settings.expand": "展开",
|
|
52
51
|
"settings.collapse": "收起",
|
|
53
52
|
"settings.readOnly": "当前环境的设置为只读",
|
|
@@ -67,10 +66,11 @@ window.__ModuleLoader__.load({
|
|
|
67
66
|
"brief.input": "输入",
|
|
68
67
|
"brief.reply": "回复",
|
|
69
68
|
"brief.more": "+{n}",
|
|
69
|
+
"brief.noInputs": "(无新增)",
|
|
70
70
|
"brief.locate": "在上下文浏览器中查看",
|
|
71
|
-
"brief.turnTip": "
|
|
72
|
-
"brief.inputTip": "
|
|
73
|
-
"brief.replyTip": "
|
|
71
|
+
"brief.turnTip": "这一轮开场时用户发送的消息;回合内的每一步都会回显同一条",
|
|
72
|
+
"brief.inputTip": "上一步回复之后、本次请求发出前新进入上下文的内容——通常是上一步所调工具的结果;每轮的第一步无新增",
|
|
73
|
+
"brief.replyTip": "这一步模型返回的内容(文本回复或工具调用);此步发起的调用,其结果会出现在下一柱的「输入」行",
|
|
74
74
|
"events.title": "上下文事件",
|
|
75
75
|
"events.empty": "暂无上下文事件(压缩、注入、模型切换会出现在这里)",
|
|
76
76
|
"events.at": "第 {t} 轮 · 第 {s} 步",
|
|
@@ -81,10 +81,43 @@ window.__ModuleLoader__.load({
|
|
|
81
81
|
"kind.prune": "剪枝",
|
|
82
82
|
"kind.model": "切换",
|
|
83
83
|
"kind.mode": "模式",
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
84
|
+
"files.title": "文件活动",
|
|
85
|
+
"files.scopeLatest": "截至最新 · 跟随趋势图的选择",
|
|
86
|
+
"files.kind.all": "全部",
|
|
87
|
+
"files.kind.read": "读取",
|
|
88
|
+
"files.kind.write": "写入",
|
|
89
|
+
"files.kind.search": "搜索",
|
|
90
|
+
"files.kind.image": "图片",
|
|
91
|
+
"files.chipTip": "{files} 个文件 · {ops} 次操作",
|
|
92
|
+
"files.search": "按路径过滤…",
|
|
93
|
+
"files.sort.count": "按次数",
|
|
94
|
+
"files.sort.latest": "按最新",
|
|
95
|
+
"files.sort.path": "按路径",
|
|
96
|
+
"files.sortTip": "文件排序:按操作次数(选中分类时按该分类的次数)、最近操作时间或路径",
|
|
97
|
+
"files.files": "{n} 个文件",
|
|
98
|
+
"files.deltaTip": "按 edit/write 调用参数估算的累计行数变化:+ 新增 / − 删除",
|
|
99
|
+
"files.empty": "该范围内没有文件读取、写入或搜索操作",
|
|
100
|
+
"files.noMatch": "没有匹配当前过滤条件的文件",
|
|
101
|
+
"files.locate": "在上下文浏览器中查看此次操作",
|
|
102
|
+
"files.errs": "{n} 次失败",
|
|
103
|
+
"files.form.image": "图片",
|
|
104
|
+
"files.form.dir": "目录",
|
|
105
|
+
"files.form.text": "文件",
|
|
106
|
+
"agents.title": "Agent 网络",
|
|
107
|
+
"agents.sub": "当前 Agent 与子 Agent 的实时上下文 · 点击节点跳转会话",
|
|
108
|
+
"agents.chip.count": "{n} 个 Agent",
|
|
109
|
+
"agents.chip.running": "{n} 个运行中",
|
|
110
|
+
"agents.chip.tokens": "上下文合计 {n}",
|
|
111
|
+
"agents.more": "另有 {n} 个未显示",
|
|
112
|
+
"agents.solo": "还没有子 Agent — 当 Agent 通过 subagent 工具委派任务时,协作网络会在这里展开",
|
|
113
|
+
"agents.self": "当前",
|
|
114
|
+
"agents.running": "运行中",
|
|
115
|
+
"agents.legend.free": "空闲窗口",
|
|
116
|
+
"agents.oneshot": "一次性",
|
|
117
|
+
"agents.continuable": "多轮",
|
|
118
|
+
"agents.requests": "{n} 次请求",
|
|
119
|
+
"agents.billed": "累计计费 {n}",
|
|
120
|
+
"agents.open": "点击打开会话 →",
|
|
88
121
|
"loading": "正在读取会话日志…",
|
|
89
122
|
"error": "上下文数据读取失败:",
|
|
90
123
|
"error.retry": "重试",
|
|
@@ -126,6 +159,8 @@ window.__ModuleLoader__.load({
|
|
|
126
159
|
"browser.noEpoch": "该步骤的头部内容(系统提示词 / 工具定义)不在保留范围内",
|
|
127
160
|
"browser.noContent": "完整内容不在当前加载的消息窗口内(在聊天页加载更早历史后可查看)",
|
|
128
161
|
"browser.loading": "正在从更早的会话历史加载完整内容…",
|
|
162
|
+
"browser.notInLog": "会话日志中未找到该条目的完整内容(可能已被压缩或清理)",
|
|
163
|
+
"browser.loadFailed": "内容加载失败,点击重试",
|
|
129
164
|
"browser.preview": "预览",
|
|
130
165
|
"tool.desc": "描述",
|
|
131
166
|
"tool.params": "参数",
|
|
@@ -194,8 +229,6 @@ window.__ModuleLoader__.load({
|
|
|
194
229
|
"plugin.hint": "The best DSH context plugin ⭐",
|
|
195
230
|
"plugin.name": "Plugin",
|
|
196
231
|
"plugin.github": "GitHub",
|
|
197
|
-
"tools.top": "Top Tool Schemas:",
|
|
198
|
-
"tools.more": "of {n}",
|
|
199
232
|
"trend.title": "Context Trend",
|
|
200
233
|
"gran.step": "Step",
|
|
201
234
|
"gran.turn": "Turn",
|
|
@@ -203,6 +236,7 @@ window.__ModuleLoader__.load({
|
|
|
203
236
|
"settings.desc": "Display preferences for the Context panel",
|
|
204
237
|
"settings.gran": "Default trend granularity",
|
|
205
238
|
"settings.mode": "Default trend display",
|
|
239
|
+
"settings.fileSort": "File Activity default sort",
|
|
206
240
|
"settings.expand": "Expand",
|
|
207
241
|
"settings.collapse": "Collapse",
|
|
208
242
|
"settings.readOnly": "Settings are read-only in this environment",
|
|
@@ -222,10 +256,11 @@ window.__ModuleLoader__.load({
|
|
|
222
256
|
"brief.input": "In",
|
|
223
257
|
"brief.reply": "Response",
|
|
224
258
|
"brief.more": "+{n}",
|
|
259
|
+
"brief.noInputs": "(nothing new)",
|
|
225
260
|
"brief.locate": "Reveal in Context Browser",
|
|
226
|
-
"brief.turnTip": "The user message that opened this turn",
|
|
227
|
-
"brief.inputTip": "What
|
|
228
|
-
"brief.replyTip": "What the model returned on this step (a text reply or tool calls)",
|
|
261
|
+
"brief.turnTip": "The user message that opened this turn — every step of the turn recalls it",
|
|
262
|
+
"brief.inputTip": "What entered after the previous reply and before this request went out — usually the previous tool-call results; a turn opener has nothing new",
|
|
263
|
+
"brief.replyTip": "What the model returned on this step (a text reply or tool calls) — results of calls made here appear in the In row of the next bar",
|
|
229
264
|
"events.title": "Context Events",
|
|
230
265
|
"events.empty": "No context events yet (compaction, injections, model switches appear here)",
|
|
231
266
|
"events.at": "Turn {t} · Step {s}",
|
|
@@ -236,10 +271,43 @@ window.__ModuleLoader__.load({
|
|
|
236
271
|
"kind.prune": "Prune",
|
|
237
272
|
"kind.model": "Switch",
|
|
238
273
|
"kind.mode": "Mode",
|
|
239
|
-
"
|
|
240
|
-
"
|
|
241
|
-
"
|
|
242
|
-
"
|
|
274
|
+
"files.title": "File Activity",
|
|
275
|
+
"files.scopeLatest": "Up to latest · follows the trend chart pick",
|
|
276
|
+
"files.kind.all": "All",
|
|
277
|
+
"files.kind.read": "Read",
|
|
278
|
+
"files.kind.write": "Written",
|
|
279
|
+
"files.kind.search": "Searched",
|
|
280
|
+
"files.kind.image": "Images",
|
|
281
|
+
"files.chipTip": "{files} files · {ops} ops",
|
|
282
|
+
"files.search": "Filter by path…",
|
|
283
|
+
"files.sort.count": "Most active",
|
|
284
|
+
"files.sort.latest": "Latest",
|
|
285
|
+
"files.sort.path": "By path",
|
|
286
|
+
"files.sortTip": "Sort files by operation counts (of the selected kind when a kind chip is active), most recent activity, or path",
|
|
287
|
+
"files.files": "{n} files",
|
|
288
|
+
"files.deltaTip": "Cumulative line change estimated from edit/write call arguments: + added / − removed",
|
|
289
|
+
"files.empty": "No file reads, writes, or searches in this range",
|
|
290
|
+
"files.noMatch": "No files match the current filters",
|
|
291
|
+
"files.locate": "Reveal this operation in the Context Browser",
|
|
292
|
+
"files.errs": "{n} failed",
|
|
293
|
+
"files.form.image": "Image",
|
|
294
|
+
"files.form.dir": "Directory",
|
|
295
|
+
"files.form.text": "File",
|
|
296
|
+
"agents.title": "Agent Network",
|
|
297
|
+
"agents.sub": "Live context of this agent and its subagents · click a node to open its session",
|
|
298
|
+
"agents.chip.count": "{n} agents",
|
|
299
|
+
"agents.chip.running": "{n} running",
|
|
300
|
+
"agents.chip.tokens": "{n} tokens in context",
|
|
301
|
+
"agents.more": "{n} more not shown",
|
|
302
|
+
"agents.solo": "No subagents yet — when the agent delegates work through the subagent tool, the network unfolds here",
|
|
303
|
+
"agents.self": "current",
|
|
304
|
+
"agents.running": "running",
|
|
305
|
+
"agents.legend.free": "free window",
|
|
306
|
+
"agents.oneshot": "one-shot",
|
|
307
|
+
"agents.continuable": "continuable",
|
|
308
|
+
"agents.requests": "{n} requests",
|
|
309
|
+
"agents.billed": "{n} billed",
|
|
310
|
+
"agents.open": "click to open →",
|
|
243
311
|
"loading": "Reading the session log…",
|
|
244
312
|
"error": "Failed to read context data: ",
|
|
245
313
|
"error.retry": "Retry",
|
|
@@ -281,6 +349,8 @@ window.__ModuleLoader__.load({
|
|
|
281
349
|
"browser.noEpoch": "The header content (System Prompt / Tool Schemas) of this step is outside retention",
|
|
282
350
|
"browser.noContent": "Full content is outside the loaded message window (load older history in Chat to view)",
|
|
283
351
|
"browser.loading": "Loading full content from older session history…",
|
|
352
|
+
"browser.notInLog": "This item is not in the session log anymore (it may have been compacted away or cleared)",
|
|
353
|
+
"browser.loadFailed": "Load failed — click to retry",
|
|
284
354
|
"browser.preview": "Preview",
|
|
285
355
|
"tool.desc": "Description",
|
|
286
356
|
"tool.params": "Parameters",
|
|
@@ -435,8 +505,7 @@ window.__ModuleLoader__.load({
|
|
|
435
505
|
"assistant",
|
|
436
506
|
"tool"
|
|
437
507
|
];
|
|
438
|
-
|
|
439
|
-
const CAT_COLORS = Object.fromEntries(CATS.map((c) => [c.key, c.color]));
|
|
508
|
+
Object.fromEntries(CATS.map((c) => [c.key, c.color]));
|
|
440
509
|
function partsOf(breakdown) {
|
|
441
510
|
return CATS.map((c) => {
|
|
442
511
|
return {
|
|
@@ -478,6 +547,9 @@ window.__ModuleLoader__.load({
|
|
|
478
547
|
return CATS.map((c) => ({
|
|
479
548
|
key: c.key,
|
|
480
549
|
color: c.color,
|
|
550
|
+
/* v8 ignore next 1 -- `shares` is initialized with system/tools and both
|
|
551
|
+
foldSurface arms assign every MESSAGE_CATS key, so each key is always
|
|
552
|
+
defined; the fallback is defensive. */
|
|
481
553
|
value: shares[c.key] ?? 0
|
|
482
554
|
}));
|
|
483
555
|
}
|
|
@@ -534,7 +606,7 @@ window.__ModuleLoader__.load({
|
|
|
534
606
|
* every field read below must re-prove itself (the no-white-screen
|
|
535
607
|
* guarantee), so no field may borrow the wire type before its check.
|
|
536
608
|
*/
|
|
537
|
-
function asRecord(value) {
|
|
609
|
+
function asRecord$1(value) {
|
|
538
610
|
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
539
611
|
return value;
|
|
540
612
|
}
|
|
@@ -563,7 +635,7 @@ window.__ModuleLoader__.load({
|
|
|
563
635
|
* during render and unmounting the conversation view.
|
|
564
636
|
*/
|
|
565
637
|
function timelineOf(value) {
|
|
566
|
-
const data = asRecord(value);
|
|
638
|
+
const data = asRecord$1(value);
|
|
567
639
|
if (data === null) return null;
|
|
568
640
|
const current = data.current;
|
|
569
641
|
if (current !== null && typeof current === "object" && [
|
|
@@ -574,7 +646,7 @@ window.__ModuleLoader__.load({
|
|
|
574
646
|
"assistant",
|
|
575
647
|
"tool",
|
|
576
648
|
"total"
|
|
577
|
-
].every((k) => typeof current[k] === "number") && Array.isArray(data.requests) && Array.isArray(data.events) && Array.isArray(data.nodes) && Array.isArray(data.archive)
|
|
649
|
+
].every((k) => typeof current[k] === "number") && Array.isArray(data.requests) && Array.isArray(data.events) && Array.isArray(data.nodes) && Array.isArray(data.archive)) return data;
|
|
578
650
|
const safeCurrent = current !== null && typeof current === "object" ? current : {};
|
|
579
651
|
const cost = typeof data.cost === "object" && data.cost !== null && !Array.isArray(data.cost) ? data.cost : void 0;
|
|
580
652
|
return {
|
|
@@ -591,7 +663,6 @@ window.__ModuleLoader__.load({
|
|
|
591
663
|
tool: numOf(safeCurrent.tool),
|
|
592
664
|
total: numOf(safeCurrent.total)
|
|
593
665
|
},
|
|
594
|
-
toolList: objectsOf(data.toolList),
|
|
595
666
|
requests: objectsOf(data.requests),
|
|
596
667
|
events: objectsOf(data.events),
|
|
597
668
|
nodes: objectsOf(data.nodes),
|
|
@@ -612,7 +683,7 @@ window.__ModuleLoader__.load({
|
|
|
612
683
|
* back to their derived anchor, so the UI degrades gracefully.
|
|
613
684
|
*/
|
|
614
685
|
function contextPressureOf(value) {
|
|
615
|
-
return asRecord(value);
|
|
686
|
+
return asRecord$1(value);
|
|
616
687
|
}
|
|
617
688
|
/**
|
|
618
689
|
* Narrow a delivered projection value to the official token-meter
|
|
@@ -622,7 +693,7 @@ window.__ModuleLoader__.load({
|
|
|
622
693
|
* own sums instead of mixing sources.
|
|
623
694
|
*/
|
|
624
695
|
function contextBreakdownOf(value) {
|
|
625
|
-
const data = asRecord(value);
|
|
696
|
+
const data = asRecord$1(value);
|
|
626
697
|
if (data === null) return null;
|
|
627
698
|
const { systemTokens, toolsTokens, messageTokens } = data;
|
|
628
699
|
if (typeof systemTokens !== "number" || !Number.isFinite(systemTokens)) return null;
|
|
@@ -641,7 +712,7 @@ window.__ModuleLoader__.load({
|
|
|
641
712
|
* usage yet) — callers drop the cache-hit cell to a dash.
|
|
642
713
|
*/
|
|
643
714
|
function tokenUsageOf(value) {
|
|
644
|
-
return asRecord(value);
|
|
715
|
+
return asRecord$1(value);
|
|
645
716
|
}
|
|
646
717
|
/**
|
|
647
718
|
* Narrow a delivered projection value to the plugin's `contextHeaders`
|
|
@@ -655,7 +726,7 @@ window.__ModuleLoader__.load({
|
|
|
655
726
|
* and the card falls back to its tokens-only note.
|
|
656
727
|
*/
|
|
657
728
|
function headersOf(value) {
|
|
658
|
-
const headers = asRecord(value);
|
|
729
|
+
const headers = asRecord$1(value);
|
|
659
730
|
if (headers === null || !Array.isArray(headers.headers)) return null;
|
|
660
731
|
for (const h of headers.headers) {
|
|
661
732
|
if (h === null || typeof h !== "object") return null;
|
|
@@ -666,6 +737,157 @@ window.__ModuleLoader__.load({
|
|
|
666
737
|
return headers;
|
|
667
738
|
}
|
|
668
739
|
//#endregion
|
|
740
|
+
//#region src/client/historyPage.ts
|
|
741
|
+
/** Narrow one served row to a validated durable event envelope, or null. */
|
|
742
|
+
function eventOf(entry) {
|
|
743
|
+
if (entry === null || typeof entry !== "object") return null;
|
|
744
|
+
const ev = entry.event;
|
|
745
|
+
if (ev === null || typeof ev !== "object") return null;
|
|
746
|
+
const e = ev;
|
|
747
|
+
if (typeof e.type !== "string" || typeof e.seq !== "number" || !Number.isFinite(e.seq)) return null;
|
|
748
|
+
const data = e.data !== null && typeof e.data === "object" ? e.data : {};
|
|
749
|
+
return {
|
|
750
|
+
type: e.type,
|
|
751
|
+
seq: e.seq,
|
|
752
|
+
data
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
/** All string texts of an event's message-content shape joined (compaction summaries). */
|
|
756
|
+
function textOf(blocks) {
|
|
757
|
+
if (!Array.isArray(blocks)) return null;
|
|
758
|
+
let out = "";
|
|
759
|
+
for (const b of blocks) {
|
|
760
|
+
const text = b !== null && typeof b === "object" ? b.text : void 0;
|
|
761
|
+
if (typeof text === "string") out += text;
|
|
762
|
+
}
|
|
763
|
+
return out.trim() === "" ? null : out;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* One assistant content block → the snapshot block vocabulary the browser
|
|
767
|
+
* already renders (`kind`: text/reasoning/image/tool-call); unmappable
|
|
768
|
+
* blocks pass through raw and degrade to the generic JSON section.
|
|
769
|
+
*/
|
|
770
|
+
function assistantBlockOf(block) {
|
|
771
|
+
const b = block !== null && typeof block === "object" ? block : null;
|
|
772
|
+
switch (b?.type) {
|
|
773
|
+
case "text":
|
|
774
|
+
case "reasoning": return {
|
|
775
|
+
kind: b.type,
|
|
776
|
+
...typeof b.text === "string" ? { text: b.text } : {}
|
|
777
|
+
};
|
|
778
|
+
case "image": return {
|
|
779
|
+
kind: "image",
|
|
780
|
+
...b.attachment !== void 0 ? { attachment: b.attachment } : {}
|
|
781
|
+
};
|
|
782
|
+
case "tool-call": return {
|
|
783
|
+
kind: "tool-call",
|
|
784
|
+
name: typeof b.name === "string" ? b.name : "?",
|
|
785
|
+
argsRaw: b.arguments
|
|
786
|
+
};
|
|
787
|
+
default: return block !== null && typeof block === "object" ? block : { value: block };
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Map one history page into joined conversation nodes keyed by their event
|
|
792
|
+
* seq — the display subset of the browser's join: user messages, assistant
|
|
793
|
+
* blocks, tool results paired with their in-page call head, and compaction
|
|
794
|
+
* checkpoints paired with their summary event. Everything else (headers,
|
|
795
|
+
* boundaries, chunks, bare calls) projects to nothing.
|
|
796
|
+
*/
|
|
797
|
+
function pageNodesOf(entries) {
|
|
798
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
799
|
+
const calls = /* @__PURE__ */ new Map();
|
|
800
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
801
|
+
for (const entry of entries) {
|
|
802
|
+
const ev = eventOf(entry);
|
|
803
|
+
if (ev === null) continue;
|
|
804
|
+
const { type, seq, data } = ev;
|
|
805
|
+
if (type === "tool/call") {
|
|
806
|
+
if (typeof data.callId === "string") calls.set(data.callId, {
|
|
807
|
+
name: typeof data.name === "string" ? data.name : "?",
|
|
808
|
+
argsRaw: typeof data.arguments === "string" ? data.arguments : ""
|
|
809
|
+
});
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (type === "compaction/summary") {
|
|
813
|
+
if (typeof data.compactionId === "string") {
|
|
814
|
+
const summary = textOf(data.summary);
|
|
815
|
+
if (summary !== null) summaries.set(data.compactionId, summary);
|
|
816
|
+
}
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
if (type === "user/message") {
|
|
820
|
+
const source = data.source !== null && typeof data.source === "object" ? data.source : null;
|
|
821
|
+
const compactionId = source !== null && source.kind === "plugin" && typeof source.compactionId === "string" ? source.compactionId : null;
|
|
822
|
+
if (compactionId !== null) {
|
|
823
|
+
nodes.set(seq, {
|
|
824
|
+
kind: "compaction",
|
|
825
|
+
seq,
|
|
826
|
+
summary: summaries.get(compactionId) ?? null
|
|
827
|
+
});
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
nodes.set(seq, {
|
|
831
|
+
kind: "user",
|
|
832
|
+
seq,
|
|
833
|
+
content: Array.isArray(data.content) ? data.content : []
|
|
834
|
+
});
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
if (type === "assistant/message") {
|
|
838
|
+
const message = data.message !== null && typeof data.message === "object" ? data.message : null;
|
|
839
|
+
const content = message !== null && Array.isArray(message.content) ? message.content : [];
|
|
840
|
+
nodes.set(seq, {
|
|
841
|
+
kind: "assistant",
|
|
842
|
+
seq,
|
|
843
|
+
blocks: content.map(assistantBlockOf)
|
|
844
|
+
});
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
if (type === "tool/result") {
|
|
848
|
+
const message = data.message !== null && typeof data.message === "object" ? data.message : null;
|
|
849
|
+
const source = message?.source !== null && typeof message?.source === "object" ? message.source : null;
|
|
850
|
+
const first = Array.isArray(message?.content) ? message.content[0] : void 0;
|
|
851
|
+
const block = first !== null && typeof first === "object" ? first : null;
|
|
852
|
+
const callId = typeof source?.callId === "string" ? source.callId : typeof block?.toolCallId === "string" ? block.toolCallId : null;
|
|
853
|
+
const call = callId !== null ? calls.get(callId) ?? null : null;
|
|
854
|
+
nodes.set(seq, {
|
|
855
|
+
kind: "tool-result",
|
|
856
|
+
seq,
|
|
857
|
+
call,
|
|
858
|
+
content: block !== null && Array.isArray(block.content) ? block.content : [],
|
|
859
|
+
isError: block?.isError === true || data.error === true
|
|
860
|
+
});
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return nodes;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Build the browser's per-session fetcher over the shared api client.
|
|
868
|
+
* Undefined when the connection face or the history verb is absent (older
|
|
869
|
+
* hosts) — the caller keeps its static preview-plus-hint degradation. Found
|
|
870
|
+
* nodes cache in the closure: one mount re-reading a row never re-fetches.
|
|
871
|
+
*/
|
|
872
|
+
function makeContentFetcher(ctx, sessionId) {
|
|
873
|
+
const sessions = ctx.get("connection")?.api?.sessions;
|
|
874
|
+
if (typeof sessions?.history !== "function") return void 0;
|
|
875
|
+
const cache = /* @__PURE__ */ new Map();
|
|
876
|
+
return async (seq) => {
|
|
877
|
+
const hit = cache.get(seq);
|
|
878
|
+
if (hit !== void 0) return hit;
|
|
879
|
+
const result = (await sessions.history({
|
|
880
|
+
sessionId,
|
|
881
|
+
beforeSeq: seq + 1
|
|
882
|
+
})).result;
|
|
883
|
+
if (result === null || typeof result !== "object" || result.ok !== true || result.value === null || typeof result.value !== "object") throw new Error("history rpc failed");
|
|
884
|
+
const events = result.value.events;
|
|
885
|
+
const node = pageNodesOf(Array.isArray(events) ? events : []).get(seq) ?? null;
|
|
886
|
+
if (node !== null) cache.set(seq, node);
|
|
887
|
+
return node;
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
//#endregion
|
|
669
891
|
//#region src/client/assemble.ts
|
|
670
892
|
/** The header epoch in force at `seq` (last logged before it), or the newest. */
|
|
671
893
|
function headerAt(headers, seq) {
|
|
@@ -767,24 +989,6 @@ window.__ModuleLoader__.load({
|
|
|
767
989
|
return t("node.nonText");
|
|
768
990
|
};
|
|
769
991
|
}
|
|
770
|
-
function makeNodeList(kit) {
|
|
771
|
-
const { t, fmt, fmtTime } = kit;
|
|
772
|
-
const nodeText = makeNodeText(kit);
|
|
773
|
-
return function NodeList(props) {
|
|
774
|
-
if (props.nodes.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("nodes.empty"));
|
|
775
|
-
const rows = props.nodes.slice().reverse();
|
|
776
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-nodes" }, props.dropped > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-nodes-more" }, t("nodes.more", { n: props.dropped })) : null, rows.map((n) => {
|
|
777
|
-
const text = nodeText(n);
|
|
778
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
779
|
-
key: n.seq,
|
|
780
|
-
className: "lc-node"
|
|
781
|
-
}, /* @__PURE__ */ React.createElement("i", { style: { background: CAT_COLORS[n.cat] || "#999" } }), /* @__PURE__ */ React.createElement("span", {
|
|
782
|
-
className: "lc-node-preview",
|
|
783
|
-
title: text
|
|
784
|
-
}, text), typeof n.time === "number" ? /* @__PURE__ */ React.createElement("span", { className: "lc-node-time" }, fmtTime(n.time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-node-tokens" }, fmt(n.tokens)));
|
|
785
|
-
}));
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
992
|
//#endregion
|
|
789
993
|
//#region src/client/format.ts
|
|
790
994
|
/** `fmt`: the k/M suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
|
|
@@ -865,6 +1069,8 @@ window.__ModuleLoader__.load({
|
|
|
865
1069
|
let bestWidth;
|
|
866
1070
|
if (gridW < 1) {
|
|
867
1071
|
let rows = floorDiv(budget - 2, 2);
|
|
1072
|
+
/* v8 ignore else -- budget enters at 381 and the tall solve always fits
|
|
1073
|
+
(never decrements), so rows is always odd here; parity-defensive. */
|
|
868
1074
|
if (rows % 2 === 1) rows -= 1;
|
|
869
1075
|
bestWidth = unit;
|
|
870
1076
|
bestHeight = rows * unit;
|
|
@@ -944,6 +1150,8 @@ window.__ModuleLoader__.load({
|
|
|
944
1150
|
}
|
|
945
1151
|
return null;
|
|
946
1152
|
} catch {
|
|
1153
|
+
/* v8 ignore next -- the only throw site is the clamped-away budget guard
|
|
1154
|
+
above; the catch keeps a hostile dimension from ever breaking the fold. */
|
|
947
1155
|
return null;
|
|
948
1156
|
}
|
|
949
1157
|
}
|
|
@@ -1189,18 +1397,22 @@ window.__ModuleLoader__.load({
|
|
|
1189
1397
|
*/
|
|
1190
1398
|
function Section(props) {
|
|
1191
1399
|
const right = props.actions !== void 0 || props.meta !== void 0;
|
|
1192
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", { className: props.labelClass }, props.label), right ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-right" }, props.meta
|
|
1400
|
+
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);
|
|
1401
|
+
}
|
|
1402
|
+
function lineCountOf(text) {
|
|
1403
|
+
return text.split(/\r\n|\r|\n/).length;
|
|
1193
1404
|
}
|
|
1194
1405
|
function TextSection(props) {
|
|
1195
1406
|
const { rich } = props;
|
|
1196
1407
|
const [mode, setMode] = rich.useRichMode();
|
|
1408
|
+
const lineCount = React.useMemo(() => lineCountOf(props.text), [props.text]);
|
|
1197
1409
|
return /* @__PURE__ */ React.createElement(Section, {
|
|
1198
1410
|
label: props.label,
|
|
1199
1411
|
actions: /* @__PURE__ */ React.createElement(rich.RichSwitch, {
|
|
1200
1412
|
mode,
|
|
1201
1413
|
onPick: setMode
|
|
1202
1414
|
}),
|
|
1203
|
-
meta: props.
|
|
1415
|
+
meta: /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-meta" }, props.lines(lineCount))
|
|
1204
1416
|
}, /* @__PURE__ */ React.createElement(rich.RichText, {
|
|
1205
1417
|
text: props.text,
|
|
1206
1418
|
mode
|
|
@@ -1238,7 +1450,8 @@ window.__ModuleLoader__.load({
|
|
|
1238
1450
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.description !== void 0 ? /* @__PURE__ */ React.createElement(TextSection, {
|
|
1239
1451
|
label: props.labels.desc,
|
|
1240
1452
|
text: props.description,
|
|
1241
|
-
rich
|
|
1453
|
+
rich,
|
|
1454
|
+
lines: props.lines
|
|
1242
1455
|
}) : null, params !== null && rows.length > 0 ? /* @__PURE__ */ React.createElement(Section, {
|
|
1243
1456
|
label: props.labels.title,
|
|
1244
1457
|
count: rows.length
|
|
@@ -1257,8 +1470,8 @@ window.__ModuleLoader__.load({
|
|
|
1257
1470
|
}
|
|
1258
1471
|
/**
|
|
1259
1472
|
* Both block vocabularies normalize here — raw durable blocks (`type`: text/reasoning/tool-call/tool-result/image) and snapshot assistant
|
|
1260
|
-
* blocks (`kind`: text/reasoning/tool-call/image, argsRaw). Consecutive images group into one grid;
|
|
1261
|
-
* switch + line count; nested tool-result blocks flatten into the same flow.
|
|
1473
|
+
* blocks (`kind`: text/reasoning/tool-call/image, argsRaw). Consecutive images group into one grid; every
|
|
1474
|
+
* rich text block carries the Raw/MD switch + line count; nested tool-result blocks flatten into the same flow.
|
|
1262
1475
|
*/
|
|
1263
1476
|
function BlocksBody(props) {
|
|
1264
1477
|
const { rich, img, labels } = props;
|
|
@@ -1294,7 +1507,7 @@ window.__ModuleLoader__.load({
|
|
|
1294
1507
|
label,
|
|
1295
1508
|
text: blk.text,
|
|
1296
1509
|
rich,
|
|
1297
|
-
|
|
1510
|
+
lines: labels.lines
|
|
1298
1511
|
}));
|
|
1299
1512
|
continue;
|
|
1300
1513
|
}
|
|
@@ -1412,6 +1625,8 @@ window.__ModuleLoader__.load({
|
|
|
1412
1625
|
}
|
|
1413
1626
|
function CallArgRow(props) {
|
|
1414
1627
|
const v = props.value;
|
|
1628
|
+
/* v8 ignore next 2 -- the only caller maps Object.keys of a JSON.parse'd
|
|
1629
|
+
object, which never holds undefined values; defensive. */
|
|
1415
1630
|
const text = typeof v === "string" ? v : v === void 0 ? "" : JSON.stringify(v);
|
|
1416
1631
|
return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-arg-row" }, /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-name" }, props.name), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-arg-val" }, text));
|
|
1417
1632
|
}
|
|
@@ -1422,7 +1637,8 @@ window.__ModuleLoader__.load({
|
|
|
1422
1637
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TextSection, {
|
|
1423
1638
|
label: labels.content,
|
|
1424
1639
|
text: node.text,
|
|
1425
|
-
rich
|
|
1640
|
+
rich,
|
|
1641
|
+
lines: labels.lines
|
|
1426
1642
|
}), /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
|
|
1427
1643
|
}
|
|
1428
1644
|
if (conv.kind === "assistant" && Array.isArray(conv.blocks)) return /* @__PURE__ */ React.createElement(BlocksBody, {
|
|
@@ -1452,7 +1668,8 @@ window.__ModuleLoader__.load({
|
|
|
1452
1668
|
if (conv.kind === "compaction") return typeof conv.summary === "string" && conv.summary !== "" ? /* @__PURE__ */ React.createElement(TextSection, {
|
|
1453
1669
|
label: labels.summary,
|
|
1454
1670
|
text: conv.summary,
|
|
1455
|
-
rich
|
|
1671
|
+
rich,
|
|
1672
|
+
lines: labels.lines
|
|
1456
1673
|
}) : /* @__PURE__ */ React.createElement(React.Fragment, null);
|
|
1457
1674
|
if (Array.isArray(conv.content)) return /* @__PURE__ */ React.createElement(BlocksBody, {
|
|
1458
1675
|
blocks: conv.content,
|
|
@@ -1483,52 +1700,51 @@ window.__ModuleLoader__.load({
|
|
|
1483
1700
|
const nodeText = makeNodeText(kit);
|
|
1484
1701
|
const rich = makeRichText(kit);
|
|
1485
1702
|
const ImageCard = makeImageCard(kit);
|
|
1486
|
-
const
|
|
1703
|
+
const lineLabel = (n) => t(n === 1 ? "block.line" : "block.lines", { n });
|
|
1487
1704
|
return function ContextBrowser(props) {
|
|
1488
1705
|
const { data, headers } = props;
|
|
1489
1706
|
const [sel, setSel] = React.useState("live");
|
|
1490
1707
|
const [openCat, setOpenCat] = React.useState(null);
|
|
1491
1708
|
const [openElem, setOpenElem] = React.useState(null);
|
|
1492
1709
|
const convNodes = typeof props.useSession === "function" ? props.useSession((s) => s.nodes) : void 0;
|
|
1710
|
+
const [fetched, setFetched] = React.useState(() => /* @__PURE__ */ new Map());
|
|
1493
1711
|
const bySeq = React.useMemo(() => {
|
|
1494
1712
|
const m = /* @__PURE__ */ new Map();
|
|
1495
1713
|
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
1714
|
+
for (const [seq, n] of fetched) if (!m.has(seq)) m.set(seq, n);
|
|
1496
1715
|
return m;
|
|
1497
|
-
}, [convNodes]);
|
|
1498
|
-
const hasMore = typeof props.useSession === "function" ? props.useSession((s) => s.hasMore === true) : false;
|
|
1499
|
-
const loadingOlder = typeof props.useSession === "function" ? props.useSession((s) => s.loadingOlder === true) : false;
|
|
1716
|
+
}, [convNodes, fetched]);
|
|
1500
1717
|
const openSeq = openElem !== null && openElem.startsWith("n") ? Number(openElem.slice(1)) : null;
|
|
1501
1718
|
const missingSeq = openSeq !== null && !bySeq.has(openSeq) ? openSeq : null;
|
|
1502
|
-
const
|
|
1503
|
-
const
|
|
1504
|
-
React.
|
|
1505
|
-
pagesRef.current = 0;
|
|
1506
|
-
setExhausted(false);
|
|
1507
|
-
}, [openElem]);
|
|
1508
|
-
const loadOlderHistory = props.loadOlderHistory;
|
|
1509
|
-
React.useEffect(() => {
|
|
1510
|
-
if (missingSeq === null || !hasMore || loadingOlder || exhausted) return;
|
|
1511
|
-
if (loadOlderHistory === void 0) return;
|
|
1512
|
-
if (pagesRef.current >= MAX_AUTO_PAGES) {
|
|
1513
|
-
setExhausted(true);
|
|
1514
|
-
return;
|
|
1515
|
-
}
|
|
1516
|
-
pagesRef.current += 1;
|
|
1517
|
-
loadOlderHistory();
|
|
1518
|
-
}, [
|
|
1519
|
-
missingSeq,
|
|
1520
|
-
hasMore,
|
|
1521
|
-
loadingOlder,
|
|
1522
|
-
exhausted,
|
|
1523
|
-
bySeq,
|
|
1524
|
-
loadOlderHistory
|
|
1525
|
-
]);
|
|
1719
|
+
const fetchContent = props.fetchContent;
|
|
1720
|
+
const [missState, setMissState] = React.useState("idle");
|
|
1721
|
+
const [retry, setRetry] = React.useState(0);
|
|
1526
1722
|
React.useEffect(() => {
|
|
1527
|
-
if (
|
|
1723
|
+
if (missingSeq === null || fetchContent === void 0) return;
|
|
1724
|
+
let live = true;
|
|
1725
|
+
setMissState("loading");
|
|
1726
|
+
fetchContent(missingSeq).then((node) => {
|
|
1727
|
+
if (!live) return;
|
|
1728
|
+
if (node === null) {
|
|
1729
|
+
setMissState("absent");
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
setFetched((prev) => {
|
|
1733
|
+
const next = new Map(prev);
|
|
1734
|
+
next.set(missingSeq, node);
|
|
1735
|
+
return next;
|
|
1736
|
+
});
|
|
1737
|
+
setMissState("idle");
|
|
1738
|
+
}, () => {
|
|
1739
|
+
if (live) setMissState("failed");
|
|
1740
|
+
});
|
|
1741
|
+
return () => {
|
|
1742
|
+
live = false;
|
|
1743
|
+
};
|
|
1528
1744
|
}, [
|
|
1529
|
-
hasMore,
|
|
1530
1745
|
missingSeq,
|
|
1531
|
-
|
|
1746
|
+
fetchContent,
|
|
1747
|
+
retry
|
|
1532
1748
|
]);
|
|
1533
1749
|
const pinSeq = props.pinSeq;
|
|
1534
1750
|
React.useEffect(() => {
|
|
@@ -1536,14 +1752,6 @@ window.__ModuleLoader__.load({
|
|
|
1536
1752
|
setOpenCat(null);
|
|
1537
1753
|
setOpenElem(null);
|
|
1538
1754
|
}, [pinSeq]);
|
|
1539
|
-
const toolFocus = props.toolFocus;
|
|
1540
|
-
React.useEffect(() => {
|
|
1541
|
-
if (toolFocus === null || toolFocus === void 0) return;
|
|
1542
|
-
setSel("live");
|
|
1543
|
-
setOpenCat("tools");
|
|
1544
|
-
setOpenElem(toolFocus.tool !== void 0 ? "tool:" + toolFocus.tool : null);
|
|
1545
|
-
if (props.onToolFocusHandled !== void 0) props.onToolFocusHandled();
|
|
1546
|
-
}, [toolFocus, props.onToolFocusHandled]);
|
|
1547
1755
|
const rootRef = React.useRef(null);
|
|
1548
1756
|
const focusScrollRef = React.useRef(false);
|
|
1549
1757
|
const nodeFocus = props.nodeFocus;
|
|
@@ -1560,7 +1768,13 @@ window.__ModuleLoader__.load({
|
|
|
1560
1768
|
focusScrollRef.current = false;
|
|
1561
1769
|
rootRef.current?.querySelector(".lc-br-elem-on")?.scrollIntoView({ block: "nearest" });
|
|
1562
1770
|
});
|
|
1563
|
-
const
|
|
1771
|
+
const missNote = fetchContent === void 0 ? t("browser.noContent") : missState === "loading" ? t("browser.loading") : missState === "absent" ? t("browser.notInLog") : missState === "failed" ? /* @__PURE__ */ React.createElement("button", {
|
|
1772
|
+
type: "button",
|
|
1773
|
+
className: "lc-br-retry",
|
|
1774
|
+
onClick: () => {
|
|
1775
|
+
setRetry((r) => r + 1);
|
|
1776
|
+
}
|
|
1777
|
+
}, t("browser.loadFailed")) : t("browser.noContent");
|
|
1564
1778
|
const requests = data.requests;
|
|
1565
1779
|
const hoverReq = props.previewSeq !== null && props.previewSeq !== void 0 ? requests.find((r) => r.seq === props.previewSeq) ?? null : null;
|
|
1566
1780
|
const req = hoverReq ?? (sel === "live" ? null : requests.find((r) => r.seq === sel) ?? null);
|
|
@@ -1583,8 +1797,13 @@ window.__ModuleLoader__.load({
|
|
|
1583
1797
|
const toolCount = (c) => countOf(view, byCat, c);
|
|
1584
1798
|
const toggleCat = (c) => {
|
|
1585
1799
|
if (!(toolCount(c) > 0 || (c === "system" || c === "tools") && view.header === null)) return;
|
|
1586
|
-
|
|
1587
|
-
|
|
1800
|
+
if (openCat === c) {
|
|
1801
|
+
setOpenCat(null);
|
|
1802
|
+
setOpenElem(null);
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
setOpenCat(c);
|
|
1806
|
+
setOpenElem(c === "system" && view.header?.system !== void 0 ? "sys" : null);
|
|
1588
1807
|
};
|
|
1589
1808
|
const toggleElem = (key) => {
|
|
1590
1809
|
setOpenElem(openElem === key ? null : key);
|
|
@@ -1613,11 +1832,14 @@ window.__ModuleLoader__.load({
|
|
|
1613
1832
|
if (c === "system") {
|
|
1614
1833
|
if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
|
|
1615
1834
|
const system = view.header.system;
|
|
1835
|
+
/* v8 ignore next 1 -- the category opens only when its count > 0
|
|
1836
|
+
(countOf ⟺ system defined); the undefined arm is defensive. */
|
|
1616
1837
|
if (system === void 0) return null;
|
|
1617
1838
|
return elemRow("sys", null, system.replace(/\s+/g, " ").trim().slice(0, 80), breakdown.system, void 0, /* @__PURE__ */ React.createElement(TextSection, {
|
|
1618
1839
|
label: catLabel("system"),
|
|
1619
1840
|
text: system,
|
|
1620
|
-
rich
|
|
1841
|
+
rich,
|
|
1842
|
+
lines: lineLabel
|
|
1621
1843
|
}));
|
|
1622
1844
|
}
|
|
1623
1845
|
if (c === "tools") {
|
|
@@ -1634,6 +1856,7 @@ window.__ModuleLoader__.load({
|
|
|
1634
1856
|
description: tool.description,
|
|
1635
1857
|
schema: tool.schema,
|
|
1636
1858
|
rich,
|
|
1859
|
+
lines: lineLabel,
|
|
1637
1860
|
labels
|
|
1638
1861
|
}));
|
|
1639
1862
|
});
|
|
@@ -1673,10 +1896,10 @@ window.__ModuleLoader__.load({
|
|
|
1673
1896
|
summary: t("block.summary"),
|
|
1674
1897
|
images: t("attach.images"),
|
|
1675
1898
|
other: t("attach.other"),
|
|
1676
|
-
lines:
|
|
1899
|
+
lines: lineLabel,
|
|
1677
1900
|
callState: (err, exit) => /* @__PURE__ */ React.createElement("span", { className: "lc-ts-call-state " + (err ? "lc-ts-call-err" : "lc-ts-call-ok") }, /* @__PURE__ */ React.createElement("i", null), err ? t("call.fail") + (exit !== null ? " · " + t("call.exit", { n: exit }) : "") : t("call.ok"))
|
|
1678
1901
|
},
|
|
1679
|
-
hint: conv === void 0
|
|
1902
|
+
hint: conv === void 0 ? missNote : t("browser.noContent")
|
|
1680
1903
|
}), rowErr);
|
|
1681
1904
|
});
|
|
1682
1905
|
};
|
|
@@ -1719,12 +1942,15 @@ window.__ModuleLoader__.load({
|
|
|
1719
1942
|
}, /* @__PURE__ */ React.createElement("button", {
|
|
1720
1943
|
type: "button",
|
|
1721
1944
|
className: "lc-br-cat-row" + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
|
|
1945
|
+
/* v8 ignore start -- the handlers exist only when linked,
|
|
1946
|
+
and linked already requires onHoverKey defined (above). */
|
|
1722
1947
|
onMouseEnter: linked ? () => {
|
|
1723
1948
|
if (props.onHoverKey !== void 0) props.onHoverKey(c.key);
|
|
1724
1949
|
} : void 0,
|
|
1725
1950
|
onMouseLeave: linked ? () => {
|
|
1726
1951
|
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
1727
1952
|
} : void 0,
|
|
1953
|
+
/* v8 ignore stop */
|
|
1728
1954
|
onClick: () => {
|
|
1729
1955
|
toggleCat(c.key);
|
|
1730
1956
|
}
|
|
@@ -1880,26 +2106,7 @@ window.__ModuleLoader__.load({
|
|
|
1880
2106
|
parts: head.parts,
|
|
1881
2107
|
hoverKey: props.hoverKey,
|
|
1882
2108
|
onHoverKey: props.onHoverKey
|
|
1883
|
-
})
|
|
1884
|
-
type: "button",
|
|
1885
|
-
className: "lc-tools-label",
|
|
1886
|
-
onClick: () => {
|
|
1887
|
-
if (props.onToolFocus !== void 0) props.onToolFocus({});
|
|
1888
|
-
}
|
|
1889
|
-
}, t("tools.top")), props.tools.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => /* @__PURE__ */ React.createElement("button", {
|
|
1890
|
-
key: tool.name,
|
|
1891
|
-
type: "button",
|
|
1892
|
-
className: "lc-tool-chip",
|
|
1893
|
-
onClick: () => {
|
|
1894
|
-
if (props.onToolFocus !== void 0) props.onToolFocus({ tool: tool.name });
|
|
1895
|
-
}
|
|
1896
|
-
}, tool.name + " " + fmt(tool.tokens))), props.tools.length > 5 ? /* @__PURE__ */ React.createElement("button", {
|
|
1897
|
-
type: "button",
|
|
1898
|
-
className: "lc-tools-more",
|
|
1899
|
-
onClick: () => {
|
|
1900
|
-
if (props.onToolFocus !== void 0) props.onToolFocus({});
|
|
1901
|
-
}
|
|
1902
|
-
}, t("tools.more", { n: props.tools.length })) : null) : null);
|
|
2109
|
+
}));
|
|
1903
2110
|
};
|
|
1904
2111
|
}
|
|
1905
2112
|
//#endregion
|
|
@@ -1943,7 +2150,7 @@ window.__ModuleLoader__.load({
|
|
|
1943
2150
|
const breakdown = typeof props.useProjection === "function" ? contextBreakdownOf(props.useProjection("contextBreakdown")) : null;
|
|
1944
2151
|
const headers = typeof props.useProjection === "function" ? headersOf(props.useProjection("contextHeaders")) : null;
|
|
1945
2152
|
const [hoverCat, setHoverCat] = React.useState(null);
|
|
1946
|
-
const
|
|
2153
|
+
const fetchContent = React.useMemo(() => sessionId !== "" ? makeContentFetcher(ctx, sessionId) : void 0, [ctx, sessionId]);
|
|
1947
2154
|
const close = React.useCallback(() => {
|
|
1948
2155
|
if (sessionId === "") return;
|
|
1949
2156
|
modalStoreOf(sessionId).set(false);
|
|
@@ -1986,20 +2193,14 @@ window.__ModuleLoader__.load({
|
|
|
1986
2193
|
head,
|
|
1987
2194
|
subtitle,
|
|
1988
2195
|
hoverKey: hoverCat,
|
|
1989
|
-
onHoverKey: setHoverCat
|
|
1990
|
-
tools: data.toolList,
|
|
1991
|
-
onToolFocus: setToolFocus
|
|
2196
|
+
onHoverKey: setHoverCat
|
|
1992
2197
|
}), /* @__PURE__ */ React.createElement(ContextBrowser, {
|
|
1993
2198
|
data,
|
|
1994
2199
|
headers,
|
|
1995
2200
|
useSession: props.useSession,
|
|
1996
|
-
|
|
2201
|
+
fetchContent,
|
|
1997
2202
|
hoverKey: hoverCat,
|
|
1998
|
-
onHoverKey: setHoverCat
|
|
1999
|
-
toolFocus,
|
|
2000
|
-
onToolFocusHandled: () => {
|
|
2001
|
-
setToolFocus(null);
|
|
2002
|
-
}
|
|
2203
|
+
onHoverKey: setHoverCat
|
|
2003
2204
|
}))));
|
|
2004
2205
|
}
|
|
2005
2206
|
return function ContextModal(props) {
|
|
@@ -2082,6 +2283,27 @@ window.__ModuleLoader__.load({
|
|
|
2082
2283
|
onPick: (id) => {
|
|
2083
2284
|
props.set?.("defaultTrendMode", id);
|
|
2084
2285
|
}
|
|
2286
|
+
}), /* @__PURE__ */ React.createElement(PrefRow, {
|
|
2287
|
+
label: t("settings.fileSort"),
|
|
2288
|
+
value: state.fileSort,
|
|
2289
|
+
disabled,
|
|
2290
|
+
options: [
|
|
2291
|
+
{
|
|
2292
|
+
id: "count",
|
|
2293
|
+
label: t("files.sort.count")
|
|
2294
|
+
},
|
|
2295
|
+
{
|
|
2296
|
+
id: "latest",
|
|
2297
|
+
label: t("files.sort.latest")
|
|
2298
|
+
},
|
|
2299
|
+
{
|
|
2300
|
+
id: "path",
|
|
2301
|
+
label: t("files.sort.path")
|
|
2302
|
+
}
|
|
2303
|
+
],
|
|
2304
|
+
onPick: (id) => {
|
|
2305
|
+
props.set?.("defaultFileSort", id);
|
|
2306
|
+
}
|
|
2085
2307
|
})) : null);
|
|
2086
2308
|
};
|
|
2087
2309
|
}
|
|
@@ -2092,7 +2314,8 @@ window.__ModuleLoader__.load({
|
|
|
2092
2314
|
const v = value;
|
|
2093
2315
|
return {
|
|
2094
2316
|
...v.defaultGranularity === "step" || v.defaultGranularity === "turn" ? { granularity: v.defaultGranularity } : {},
|
|
2095
|
-
...v.defaultTrendMode === "total" || v.defaultTrendMode === "delta" ? { mode: v.defaultTrendMode } : {}
|
|
2317
|
+
...v.defaultTrendMode === "total" || v.defaultTrendMode === "delta" ? { mode: v.defaultTrendMode } : {},
|
|
2318
|
+
...v.defaultFileSort === "count" || v.defaultFileSort === "latest" || v.defaultFileSort === "path" ? { fileSort: v.defaultFileSort } : {}
|
|
2096
2319
|
};
|
|
2097
2320
|
}
|
|
2098
2321
|
function createContextSettings() {
|
|
@@ -2100,12 +2323,13 @@ window.__ModuleLoader__.load({
|
|
|
2100
2323
|
status: "loading",
|
|
2101
2324
|
granularity: "step",
|
|
2102
2325
|
mode: "total",
|
|
2326
|
+
fileSort: "count",
|
|
2103
2327
|
writable: false
|
|
2104
2328
|
};
|
|
2105
2329
|
let scope;
|
|
2106
2330
|
const listeners = /* @__PURE__ */ new Set();
|
|
2107
2331
|
const publish = (next) => {
|
|
2108
|
-
if (next.status === state.status && next.granularity === state.granularity && next.mode === state.mode && next.writable === state.writable) return;
|
|
2332
|
+
if (next.status === state.status && next.granularity === state.granularity && next.mode === state.mode && next.fileSort === state.fileSort && next.writable === state.writable) return;
|
|
2109
2333
|
state = next;
|
|
2110
2334
|
for (const listener of listeners) listener();
|
|
2111
2335
|
};
|
|
@@ -2121,6 +2345,7 @@ window.__ModuleLoader__.load({
|
|
|
2121
2345
|
},
|
|
2122
2346
|
defaultGranularity: () => state.granularity,
|
|
2123
2347
|
defaultTrendMode: () => state.mode,
|
|
2348
|
+
defaultFileSort: () => state.fileSort,
|
|
2124
2349
|
attach(bound) {
|
|
2125
2350
|
scope = bound;
|
|
2126
2351
|
const sync = () => {
|
|
@@ -2130,6 +2355,7 @@ window.__ModuleLoader__.load({
|
|
|
2130
2355
|
status: snap.status === "ready" || snap.status === "unavailable" ? snap.status : "loading",
|
|
2131
2356
|
granularity: prefs.granularity ?? state.granularity,
|
|
2132
2357
|
mode: prefs.mode ?? state.mode,
|
|
2358
|
+
fileSort: prefs.fileSort ?? state.fileSort,
|
|
2133
2359
|
writable: snap.writable
|
|
2134
2360
|
});
|
|
2135
2361
|
};
|
|
@@ -2213,6 +2439,767 @@ window.__ModuleLoader__.load({
|
|
|
2213
2439
|
return m;
|
|
2214
2440
|
}
|
|
2215
2441
|
//#endregion
|
|
2442
|
+
//#region src/client/fileActivity.ts
|
|
2443
|
+
const KIND_BY_TOOL = {
|
|
2444
|
+
read: "read",
|
|
2445
|
+
read_image: "read",
|
|
2446
|
+
write: "write",
|
|
2447
|
+
edit: "write",
|
|
2448
|
+
grep: "search",
|
|
2449
|
+
glob: "search"
|
|
2450
|
+
};
|
|
2451
|
+
/** The file purpose of a tool, or null for non-file tools (bash, web_search…). */
|
|
2452
|
+
function kindOfTool(tool) {
|
|
2453
|
+
if (tool === void 0) return null;
|
|
2454
|
+
return KIND_BY_TOOL[tool] ?? null;
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* The operation's target path: the path-ish argument of read/write tools;
|
|
2458
|
+
* for searches the narrowing `path`, else the pattern itself (a pathless
|
|
2459
|
+
* grep/glob's target IS the pattern — the workspace-wide search text).
|
|
2460
|
+
*/
|
|
2461
|
+
function pathOfArgs(tool, args) {
|
|
2462
|
+
if (args === null) return null;
|
|
2463
|
+
if (tool === "grep" || tool === "glob") {
|
|
2464
|
+
const p = args.path;
|
|
2465
|
+
if (typeof p === "string" && p !== "") return p;
|
|
2466
|
+
const pattern = args.pattern;
|
|
2467
|
+
return typeof pattern === "string" && pattern !== "" ? pattern : null;
|
|
2468
|
+
}
|
|
2469
|
+
for (const k of [
|
|
2470
|
+
"file_path",
|
|
2471
|
+
"filePath",
|
|
2472
|
+
"path"
|
|
2473
|
+
]) {
|
|
2474
|
+
const v = args[k];
|
|
2475
|
+
if (typeof v === "string" && v !== "") return v;
|
|
2476
|
+
}
|
|
2477
|
+
return null;
|
|
2478
|
+
}
|
|
2479
|
+
/** Rendered line count: '' is 0, a trailing newline closes its own line. */
|
|
2480
|
+
function linesOf(s) {
|
|
2481
|
+
if (s === "") return 0;
|
|
2482
|
+
let n = 0;
|
|
2483
|
+
for (let i = 0; i < s.length; i++) if (s[i] === "\n") n++;
|
|
2484
|
+
return s.endsWith("\n") ? n : n + 1;
|
|
2485
|
+
}
|
|
2486
|
+
/**
|
|
2487
|
+
* The signed line footprint of one call: an edit removes its old string and
|
|
2488
|
+
* adds its new one; a write adds its content (the pre-existing body, if any,
|
|
2489
|
+
* is unknowable from the arguments — the estimate stays honest about that).
|
|
2490
|
+
* Callers reach here only with parsed args (a null parse yields no path).
|
|
2491
|
+
*/
|
|
2492
|
+
function deltaOf(tool, args) {
|
|
2493
|
+
if (tool === "edit") {
|
|
2494
|
+
const oldS = args.old_string;
|
|
2495
|
+
const newS = args.new_string;
|
|
2496
|
+
return {
|
|
2497
|
+
added: typeof newS === "string" ? linesOf(newS) : 0,
|
|
2498
|
+
removed: typeof oldS === "string" ? linesOf(oldS) : 0
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
if (tool === "write") {
|
|
2502
|
+
const content = args.content;
|
|
2503
|
+
return {
|
|
2504
|
+
added: typeof content === "string" ? linesOf(content) : 0,
|
|
2505
|
+
removed: 0
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
return {
|
|
2509
|
+
added: 0,
|
|
2510
|
+
removed: 0
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
const IMAGE_EXT = /\.(png|jpe?g|gif|webp|svg|bmp|ico|avif)$/i;
|
|
2514
|
+
/** The file's form — multimodal reads and image extensions scan apart; a trailing slash marks a directory target. */
|
|
2515
|
+
function formOf(tool, path) {
|
|
2516
|
+
if (tool === "read_image" || IMAGE_EXT.test(path)) return "image";
|
|
2517
|
+
if (path.endsWith("/")) return "dir";
|
|
2518
|
+
return "text";
|
|
2519
|
+
}
|
|
2520
|
+
/** Fold every served tool-result node into per-file activity. */
|
|
2521
|
+
function activityOf(nodes, convOf, before) {
|
|
2522
|
+
const totals = {
|
|
2523
|
+
read: {
|
|
2524
|
+
files: 0,
|
|
2525
|
+
ops: 0
|
|
2526
|
+
},
|
|
2527
|
+
write: {
|
|
2528
|
+
files: 0,
|
|
2529
|
+
ops: 0
|
|
2530
|
+
},
|
|
2531
|
+
search: {
|
|
2532
|
+
files: 0,
|
|
2533
|
+
ops: 0
|
|
2534
|
+
},
|
|
2535
|
+
image: {
|
|
2536
|
+
files: 0,
|
|
2537
|
+
ops: 0
|
|
2538
|
+
},
|
|
2539
|
+
added: 0,
|
|
2540
|
+
removed: 0
|
|
2541
|
+
};
|
|
2542
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
2543
|
+
for (const n of nodes) {
|
|
2544
|
+
if (n.cat !== "tool") continue;
|
|
2545
|
+
if (before !== null && n.seq >= before) continue;
|
|
2546
|
+
const kind = kindOfTool(n.tool);
|
|
2547
|
+
if (kind === null) continue;
|
|
2548
|
+
const tool = n.tool;
|
|
2549
|
+
const conv = convOf(n.seq);
|
|
2550
|
+
const args = parseCallArgs(conv?.call?.argsRaw);
|
|
2551
|
+
const path = pathOfArgs(tool, args);
|
|
2552
|
+
if (path === null || args === null) continue;
|
|
2553
|
+
totals[kind].ops++;
|
|
2554
|
+
const { added, removed } = deltaOf(tool, args);
|
|
2555
|
+
const detail = kind === "search" && typeof args.path === "string" && args.path !== "" && typeof args.pattern === "string" && args.pattern !== "" ? args.pattern : void 0;
|
|
2556
|
+
const op = {
|
|
2557
|
+
seq: n.seq,
|
|
2558
|
+
...n.gone !== void 0 ? { gone: n.gone } : {},
|
|
2559
|
+
kind,
|
|
2560
|
+
tool,
|
|
2561
|
+
...n.time !== void 0 ? { time: n.time } : {},
|
|
2562
|
+
err: n.err === true || conv.isError === true,
|
|
2563
|
+
added,
|
|
2564
|
+
removed,
|
|
2565
|
+
...detail !== void 0 ? { detail } : {}
|
|
2566
|
+
};
|
|
2567
|
+
let entry = byPath.get(path);
|
|
2568
|
+
if (entry === void 0) {
|
|
2569
|
+
entry = {
|
|
2570
|
+
path,
|
|
2571
|
+
form: formOf(tool, path),
|
|
2572
|
+
reads: 0,
|
|
2573
|
+
writes: 0,
|
|
2574
|
+
searches: 0,
|
|
2575
|
+
added: 0,
|
|
2576
|
+
removed: 0,
|
|
2577
|
+
errs: 0,
|
|
2578
|
+
ops: []
|
|
2579
|
+
};
|
|
2580
|
+
byPath.set(path, entry);
|
|
2581
|
+
}
|
|
2582
|
+
if (kind === "read") entry.reads++;
|
|
2583
|
+
else if (kind === "write") entry.writes++;
|
|
2584
|
+
else entry.searches++;
|
|
2585
|
+
entry.added += added;
|
|
2586
|
+
entry.removed += removed;
|
|
2587
|
+
if (op.err) entry.errs++;
|
|
2588
|
+
entry.ops.push(op);
|
|
2589
|
+
}
|
|
2590
|
+
const entries = [...byPath.values()];
|
|
2591
|
+
for (const e of entries) {
|
|
2592
|
+
e.ops.sort((a, b) => b.seq - a.seq);
|
|
2593
|
+
if (e.reads > 0) totals.read.files++;
|
|
2594
|
+
if (e.writes > 0) totals.write.files++;
|
|
2595
|
+
if (e.searches > 0) totals.search.files++;
|
|
2596
|
+
if (e.form === "image") {
|
|
2597
|
+
totals.image.files++;
|
|
2598
|
+
totals.image.ops += e.ops.length;
|
|
2599
|
+
}
|
|
2600
|
+
totals.added += e.added;
|
|
2601
|
+
totals.removed += e.removed;
|
|
2602
|
+
}
|
|
2603
|
+
entries.sort((a, b) => b.ops[0].seq - a.ops[0].seq);
|
|
2604
|
+
return {
|
|
2605
|
+
entries,
|
|
2606
|
+
totals
|
|
2607
|
+
};
|
|
2608
|
+
}
|
|
2609
|
+
/**
|
|
2610
|
+
* The browser step whose assembled surface SHOWS an op's result node: the
|
|
2611
|
+
* first request dispatched after it while it was still alive (that step's
|
|
2612
|
+
* brief is where the result landed). A live node no request has consumed
|
|
2613
|
+
* yet reveals on the live surface; an archived node no retained step still
|
|
2614
|
+
* contains is not viewable anywhere (null → the row stays inert).
|
|
2615
|
+
*/
|
|
2616
|
+
function locateStepOf(requests, seq, gone) {
|
|
2617
|
+
for (const r of requests) if (r.seq > seq && (gone === void 0 || gone > r.seq)) return r.seq;
|
|
2618
|
+
return gone === void 0 ? "live" : null;
|
|
2619
|
+
}
|
|
2620
|
+
const SLOT_MAX = 184;
|
|
2621
|
+
const SLOT_MIN = 112;
|
|
2622
|
+
const CAPTION_GUTTER = 8;
|
|
2623
|
+
const LEVEL_H = 154;
|
|
2624
|
+
const CELL_H = 90;
|
|
2625
|
+
const PAD_Y = 56;
|
|
2626
|
+
function asRecord(value) {
|
|
2627
|
+
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
2628
|
+
return value;
|
|
2629
|
+
}
|
|
2630
|
+
/** Narrow one list-row value; null when it is not a row at all. */
|
|
2631
|
+
function agentRowOf(value) {
|
|
2632
|
+
const rec = asRecord(value);
|
|
2633
|
+
if (rec === null) return null;
|
|
2634
|
+
const projections = asRecord(rec.projectionValues);
|
|
2635
|
+
return {
|
|
2636
|
+
...typeof rec.displayTitle === "string" ? { displayTitle: rec.displayTitle } : {},
|
|
2637
|
+
...typeof rec.title === "string" ? { title: rec.title } : {},
|
|
2638
|
+
...typeof rec.parentId === "string" ? { parentId: rec.parentId } : {},
|
|
2639
|
+
...typeof rec.origin === "string" ? { origin: rec.origin } : {},
|
|
2640
|
+
running: rec.running === true,
|
|
2641
|
+
completed: rec.completed === true,
|
|
2642
|
+
blank: rec.blank === true,
|
|
2643
|
+
updatedAt: numOf(rec.updatedAt),
|
|
2644
|
+
...projections !== null ? { projections } : {}
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
/** Narrow the subagent identity projection value (null = not a descriptor-backed subagent). */
|
|
2648
|
+
function agentIdentityOf(value) {
|
|
2649
|
+
const rec = asRecord(value);
|
|
2650
|
+
if (rec === null) return null;
|
|
2651
|
+
if (rec.mode !== "one-shot" && rec.mode !== "continuable") return null;
|
|
2652
|
+
return {
|
|
2653
|
+
mode: rec.mode,
|
|
2654
|
+
...typeof rec.label === "string" && rec.label !== "" ? { label: rec.label } : {}
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
/** Narrow the subagent timing projection value into a single duration (null = absent/malformed). */
|
|
2658
|
+
function agentDurationOf(value) {
|
|
2659
|
+
const rec = asRecord(value);
|
|
2660
|
+
if (rec === null) return null;
|
|
2661
|
+
const settled = numOf(rec.settledMs);
|
|
2662
|
+
const active = asRecord(rec.active);
|
|
2663
|
+
const total = settled + (active !== null ? Math.max(0, numOf(active.through) - numOf(active.since)) : 0);
|
|
2664
|
+
return total > 0 ? total : null;
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* Fold one row's projection values into render-ready stats. The composition
|
|
2668
|
+
* prefers the plugin's own `contextTimeline` (six buckets, provider-anchored
|
|
2669
|
+
* by token-meter's pressure/breakdown — the exact headlineOf derivation the
|
|
2670
|
+
* overview card shows); a pressure-only row (plugin host half absent for that
|
|
2671
|
+
* session) still yields an occupancy ring without slices.
|
|
2672
|
+
*/
|
|
2673
|
+
function agentStatsOf(values) {
|
|
2674
|
+
const timeline = timelineOf(values?.contextTimeline);
|
|
2675
|
+
const pressure = contextPressureOf(values?.contextPressure);
|
|
2676
|
+
const breakdown = contextBreakdownOf(values?.contextBreakdown);
|
|
2677
|
+
const usage = tokenUsageOf(values?.tokenUsage);
|
|
2678
|
+
let head = null;
|
|
2679
|
+
if (timeline !== null) head = headlineOf(timeline, pressure, breakdown);
|
|
2680
|
+
else if (pressure !== null) {
|
|
2681
|
+
const tokens = typeof pressure.projectedTokens === "number" ? pressure.projectedTokens : typeof pressure.pressureTokens === "number" ? pressure.pressureTokens : null;
|
|
2682
|
+
if (tokens !== null) {
|
|
2683
|
+
const window = typeof pressure.contextWindow === "number" && pressure.contextWindow > 0 ? pressure.contextWindow : void 0;
|
|
2684
|
+
head = {
|
|
2685
|
+
tokens,
|
|
2686
|
+
window,
|
|
2687
|
+
pct: window !== void 0 ? Math.min(100, Math.round(tokens / window * 100)) : null,
|
|
2688
|
+
parts: []
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
const billed = usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null;
|
|
2693
|
+
return {
|
|
2694
|
+
head,
|
|
2695
|
+
requests: timeline !== null ? timeline.requests.length : 0,
|
|
2696
|
+
billed,
|
|
2697
|
+
durationMs: agentDurationOf(values?.subagentTiming),
|
|
2698
|
+
identity: agentIdentityOf(values?.subagent)
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Build the current session's agent family from a session-list snapshot:
|
|
2703
|
+
* walk up `parentId` to the topmost known ancestor, then DFS its whole
|
|
2704
|
+
* subtree (blank placeholder rows excluded). Null when there is no anchor —
|
|
2705
|
+
* no current session, or a snapshot without a `byId` table (older harness).
|
|
2706
|
+
* The current session synthesizes a row when the list has not delivered it
|
|
2707
|
+
* yet, so the card can still show its live self stats.
|
|
2708
|
+
*/
|
|
2709
|
+
function agentForestOf(snapshot, currentId, self) {
|
|
2710
|
+
const byId = asRecord(asRecord(snapshot)?.byId);
|
|
2711
|
+
if (byId === null || currentId === void 0 || currentId === "") return null;
|
|
2712
|
+
const rows = /* @__PURE__ */ new Map();
|
|
2713
|
+
for (const key of Object.keys(byId)) {
|
|
2714
|
+
const row = agentRowOf(byId[key]);
|
|
2715
|
+
if (row !== null && (!row.blank || key === currentId)) rows.set(key, row);
|
|
2716
|
+
}
|
|
2717
|
+
if (!rows.has(currentId)) rows.set(currentId, {
|
|
2718
|
+
running: false,
|
|
2719
|
+
completed: false,
|
|
2720
|
+
blank: false,
|
|
2721
|
+
updatedAt: 0
|
|
2722
|
+
});
|
|
2723
|
+
let root = currentId;
|
|
2724
|
+
const chain = /* @__PURE__ */ new Set([currentId]);
|
|
2725
|
+
for (;;) {
|
|
2726
|
+
const parent = rows.get(root)?.parentId;
|
|
2727
|
+
if (parent === void 0 || !rows.has(parent) || chain.has(parent)) break;
|
|
2728
|
+
chain.add(parent);
|
|
2729
|
+
root = parent;
|
|
2730
|
+
}
|
|
2731
|
+
const rootRow = rows.get(root);
|
|
2732
|
+
/* v8 ignore next 2 -- root is currentId (inserted above) or a parent
|
|
2733
|
+
verified with rows.has, so its row always exists. */
|
|
2734
|
+
if (rootRow === void 0) return null;
|
|
2735
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
2736
|
+
for (const [id, row] of rows) {
|
|
2737
|
+
if (row.parentId === void 0 || !rows.has(row.parentId)) continue;
|
|
2738
|
+
const list = childrenOf.get(row.parentId) ?? [];
|
|
2739
|
+
list.push({
|
|
2740
|
+
id,
|
|
2741
|
+
row
|
|
2742
|
+
});
|
|
2743
|
+
childrenOf.set(row.parentId, list);
|
|
2744
|
+
}
|
|
2745
|
+
for (const kids of childrenOf.values()) kids.sort((a, b) => {
|
|
2746
|
+
const runDelta = Number(b.row.running) - Number(a.row.running);
|
|
2747
|
+
if (runDelta !== 0) return runDelta;
|
|
2748
|
+
const timeDelta = b.row.updatedAt - a.row.updatedAt;
|
|
2749
|
+
return timeDelta !== 0 ? timeDelta : a.id < b.id ? -1 : 1;
|
|
2750
|
+
});
|
|
2751
|
+
const measure = (id, seen) => {
|
|
2752
|
+
if (seen.has(id)) return 0;
|
|
2753
|
+
seen.add(id);
|
|
2754
|
+
let total = 1;
|
|
2755
|
+
for (const kid of childrenOf.get(id) ?? []) total += measure(kid.id, seen);
|
|
2756
|
+
return total;
|
|
2757
|
+
};
|
|
2758
|
+
const total = measure(root, /* @__PURE__ */ new Set());
|
|
2759
|
+
const nodes = [];
|
|
2760
|
+
const edges = [];
|
|
2761
|
+
const visit = (id, row, parentId, depth, seen, family) => {
|
|
2762
|
+
if (seen.has(id) || nodes.length >= 25) return;
|
|
2763
|
+
seen.add(id);
|
|
2764
|
+
const stats = agentStatsOf(row.projections);
|
|
2765
|
+
const identity = stats.identity;
|
|
2766
|
+
const node = {
|
|
2767
|
+
...stats,
|
|
2768
|
+
id,
|
|
2769
|
+
label: identity?.label ?? row.title ?? row.displayTitle ?? id,
|
|
2770
|
+
...parentId !== void 0 ? { parentId } : {},
|
|
2771
|
+
depth,
|
|
2772
|
+
family,
|
|
2773
|
+
isCurrent: id === currentId,
|
|
2774
|
+
running: row.running,
|
|
2775
|
+
completed: row.completed,
|
|
2776
|
+
subagent: row.origin === "subagent" || identity !== null
|
|
2777
|
+
};
|
|
2778
|
+
if (node.isCurrent && self !== void 0) {
|
|
2779
|
+
node.head = self.head ?? node.head;
|
|
2780
|
+
node.billed = self.billed ?? node.billed;
|
|
2781
|
+
node.requests = self.requests > 0 ? self.requests : node.requests;
|
|
2782
|
+
}
|
|
2783
|
+
nodes.push(node);
|
|
2784
|
+
if (parentId !== void 0) edges.push({
|
|
2785
|
+
from: parentId,
|
|
2786
|
+
to: id
|
|
2787
|
+
});
|
|
2788
|
+
(childrenOf.get(id) ?? []).forEach((kid, ki) => {
|
|
2789
|
+
visit(kid.id, kid.row, id, depth + 1, seen, depth === 0 ? ki : family);
|
|
2790
|
+
});
|
|
2791
|
+
};
|
|
2792
|
+
visit(root, rootRow, void 0, 0, /* @__PURE__ */ new Set(), -1);
|
|
2793
|
+
return {
|
|
2794
|
+
nodes,
|
|
2795
|
+
edges,
|
|
2796
|
+
overflow: Math.max(0, total - nodes.length),
|
|
2797
|
+
solo: nodes.length === 1
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* Tidy top-down tree layout: one row per depth level, siblings claim leaf
|
|
2802
|
+
* slots, parents center over their children. Fully responsive to the stage's
|
|
2803
|
+
* visible width: the slot pitch stretches up to SLOT_MAX and compresses down
|
|
2804
|
+
* to SLOT_MIN (captions wrap tighter); a level that still overflows wraps
|
|
2805
|
+
* into bands of at most a per-level node count derived from the stage width —
|
|
2806
|
+
* vertical room is cheaper than horizontal scrolling. Links exit a parent at
|
|
2807
|
+
* its cell bottom (below the caption zone) and enter the child at its top,
|
|
2808
|
+
* so a connector never crosses a label.
|
|
2809
|
+
*/
|
|
2810
|
+
function layoutForest(forest, stageWidth = 0) {
|
|
2811
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
2812
|
+
for (const n of forest.nodes) {
|
|
2813
|
+
if (n.parentId === void 0) continue;
|
|
2814
|
+
const kids = childrenOf.get(n.parentId) ?? [];
|
|
2815
|
+
kids.push(n);
|
|
2816
|
+
childrenOf.set(n.parentId, kids);
|
|
2817
|
+
}
|
|
2818
|
+
const slotOf = /* @__PURE__ */ new Map();
|
|
2819
|
+
let leafSlots = 0;
|
|
2820
|
+
const place = (node) => {
|
|
2821
|
+
const kids = childrenOf.get(node.id) ?? [];
|
|
2822
|
+
if (kids.length === 0) {
|
|
2823
|
+
const slot = leafSlots;
|
|
2824
|
+
leafSlots++;
|
|
2825
|
+
slotOf.set(node.id, slot);
|
|
2826
|
+
return slot;
|
|
2827
|
+
}
|
|
2828
|
+
let first = 0;
|
|
2829
|
+
let last = 0;
|
|
2830
|
+
kids.forEach((kid, index) => {
|
|
2831
|
+
const slot = place(kid);
|
|
2832
|
+
if (index === 0) first = slot;
|
|
2833
|
+
last = slot;
|
|
2834
|
+
});
|
|
2835
|
+
const slot = (first + last) / 2;
|
|
2836
|
+
slotOf.set(node.id, slot);
|
|
2837
|
+
return slot;
|
|
2838
|
+
};
|
|
2839
|
+
/* v8 ignore next 1 -- a forest always holds at least the (possibly
|
|
2840
|
+
synthesized) current node. */
|
|
2841
|
+
if (forest.nodes.length > 0) place(forest.nodes[0]);
|
|
2842
|
+
const perLevel = stageWidth > 0 ? Math.max(2, Math.floor(stageWidth / SLOT_MIN)) : 0;
|
|
2843
|
+
if (perLevel > 0 && leafSlots > perLevel) {
|
|
2844
|
+
const bandSlot = Math.min(SLOT_MAX, stageWidth / perLevel);
|
|
2845
|
+
const width = perLevel * bandSlot;
|
|
2846
|
+
const points = [];
|
|
2847
|
+
let row = 0;
|
|
2848
|
+
const emitBand = (nodes, depth) => {
|
|
2849
|
+
const inset = (width - nodes.length * bandSlot) / 2;
|
|
2850
|
+
nodes.forEach((node, i) => {
|
|
2851
|
+
points.push({
|
|
2852
|
+
id: node.id,
|
|
2853
|
+
x: inset + (i + .5) * bandSlot,
|
|
2854
|
+
y: PAD_Y + row * LEVEL_H,
|
|
2855
|
+
depth
|
|
2856
|
+
});
|
|
2857
|
+
});
|
|
2858
|
+
row++;
|
|
2859
|
+
let band = [];
|
|
2860
|
+
const flush = () => {
|
|
2861
|
+
if (band.length === 0) return;
|
|
2862
|
+
const packed = band;
|
|
2863
|
+
band = [];
|
|
2864
|
+
emitBand(packed, depth + 1);
|
|
2865
|
+
};
|
|
2866
|
+
for (const node of nodes) {
|
|
2867
|
+
const kids = childrenOf.get(node.id) ?? [];
|
|
2868
|
+
for (let start = 0; start < kids.length; start += perLevel) {
|
|
2869
|
+
const group = kids.slice(start, start + perLevel);
|
|
2870
|
+
if (band.length + group.length > perLevel) flush();
|
|
2871
|
+
band.push(...group);
|
|
2872
|
+
if (band.length === perLevel) flush();
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
flush();
|
|
2876
|
+
};
|
|
2877
|
+
/* v8 ignore next 1 -- a forest always holds at least the current node. */
|
|
2878
|
+
if (forest.nodes.length > 0) emitBand([forest.nodes[0]], 0);
|
|
2879
|
+
return {
|
|
2880
|
+
width,
|
|
2881
|
+
height: PAD_Y + (row - 1) * LEVEL_H + CELL_H + 28,
|
|
2882
|
+
captionW: bandSlot - CAPTION_GUTTER,
|
|
2883
|
+
points,
|
|
2884
|
+
links: linksOf(forest, points)
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
const slot = stageWidth > 0 && leafSlots > 1 ? Math.min(SLOT_MAX, stageWidth / leafSlots) : SLOT_MAX;
|
|
2888
|
+
const points = forest.nodes.map((node) => ({
|
|
2889
|
+
id: node.id,
|
|
2890
|
+
/* v8 ignore next 1 -- place() visits every node: the forest is exactly
|
|
2891
|
+
the root's subtree by construction. */
|
|
2892
|
+
x: (slotOf.get(node.id) ?? 0) * slot + slot / 2,
|
|
2893
|
+
y: PAD_Y + node.depth * LEVEL_H,
|
|
2894
|
+
depth: node.depth
|
|
2895
|
+
}));
|
|
2896
|
+
const maxDepth = points.reduce((max, p) => Math.max(max, p.depth), 0);
|
|
2897
|
+
return {
|
|
2898
|
+
width: leafSlots * slot,
|
|
2899
|
+
height: PAD_Y + maxDepth * LEVEL_H + CELL_H + 28,
|
|
2900
|
+
captionW: slot - CAPTION_GUTTER,
|
|
2901
|
+
points,
|
|
2902
|
+
links: linksOf(forest, points)
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
/**
|
|
2906
|
+
* Family hue by level-1 subtree index: the golden angle keeps consecutive
|
|
2907
|
+
* families maximally separated on the color wheel without a hand-tuned palette.
|
|
2908
|
+
*/
|
|
2909
|
+
function familyHue(index) {
|
|
2910
|
+
return `hsl(${Math.round(index * 137.508) % 360} 58% 52%)`;
|
|
2911
|
+
}
|
|
2912
|
+
/** Parent→child links: exit the parent's cell bottom, enter the child's top. */
|
|
2913
|
+
function linksOf(forest, points) {
|
|
2914
|
+
const pointOf = new Map(points.map((p) => [p.id, p]));
|
|
2915
|
+
const nodeOf = new Map(forest.nodes.map((n) => [n.id, n]));
|
|
2916
|
+
const runningIds = new Set(forest.nodes.filter((n) => n.running).map((n) => n.id));
|
|
2917
|
+
const links = [];
|
|
2918
|
+
for (const edge of forest.edges) {
|
|
2919
|
+
const from = pointOf.get(edge.from);
|
|
2920
|
+
const to = pointOf.get(edge.to);
|
|
2921
|
+
/* v8 ignore next 2 -- edges are emitted only for visited parent/child
|
|
2922
|
+
pairs, so both points always exist. */
|
|
2923
|
+
if (from === void 0 || to === void 0) continue;
|
|
2924
|
+
links.push({
|
|
2925
|
+
to: edge.to,
|
|
2926
|
+
running: runningIds.has(edge.to),
|
|
2927
|
+
/* v8 ignore next 1 -- edges only connect visited nodes. */
|
|
2928
|
+
color: familyHue(nodeOf.get(edge.to)?.family ?? 0),
|
|
2929
|
+
x1: from.x,
|
|
2930
|
+
y1: from.y + CELL_H,
|
|
2931
|
+
x2: to.x,
|
|
2932
|
+
y2: to.y - 26 - 10
|
|
2933
|
+
});
|
|
2934
|
+
}
|
|
2935
|
+
return links;
|
|
2936
|
+
}
|
|
2937
|
+
/**
|
|
2938
|
+
* One fused ring per agent — the exact semantics of the chat composer's own
|
|
2939
|
+
* context ring: the composition parts, scaled to the occupancy share of the
|
|
2940
|
+
* window, fill the circle, and a neutral remainder marks the free window.
|
|
2941
|
+
* With no known window the composition fills the whole circle; with no
|
|
2942
|
+
* composition (pressure-only rows) a single threshold-colored arc carries
|
|
2943
|
+
* the occupancy; a known window with zero occupancy draws the free outline.
|
|
2944
|
+
*/
|
|
2945
|
+
function ringSegments(parts, pct, radius, fallbackColor) {
|
|
2946
|
+
const circumference = 2 * Math.PI * radius;
|
|
2947
|
+
const occ = pct === null ? 1 : Math.min(100, Math.max(0, pct)) / 100;
|
|
2948
|
+
let total = 0;
|
|
2949
|
+
for (const p of parts) total += p.value > 0 ? p.value : 0;
|
|
2950
|
+
const segs = [];
|
|
2951
|
+
let offset = 0;
|
|
2952
|
+
if (total > 0) for (const p of parts) {
|
|
2953
|
+
if (p.value <= 0) continue;
|
|
2954
|
+
const len = circumference * (p.value / total) * occ;
|
|
2955
|
+
if (len <= 0) continue;
|
|
2956
|
+
segs.push({
|
|
2957
|
+
key: p.key,
|
|
2958
|
+
color: p.color,
|
|
2959
|
+
len,
|
|
2960
|
+
offset,
|
|
2961
|
+
free: false
|
|
2962
|
+
});
|
|
2963
|
+
offset += len;
|
|
2964
|
+
}
|
|
2965
|
+
else if (pct !== null && occ > 0) {
|
|
2966
|
+
segs.push({
|
|
2967
|
+
key: "fill",
|
|
2968
|
+
color: fallbackColor,
|
|
2969
|
+
len: circumference * occ,
|
|
2970
|
+
offset: 0,
|
|
2971
|
+
free: false
|
|
2972
|
+
});
|
|
2973
|
+
offset = circumference * occ;
|
|
2974
|
+
}
|
|
2975
|
+
if (pct !== null && offset < circumference) segs.push({
|
|
2976
|
+
key: "free",
|
|
2977
|
+
color: "",
|
|
2978
|
+
len: circumference - offset,
|
|
2979
|
+
offset,
|
|
2980
|
+
free: true
|
|
2981
|
+
});
|
|
2982
|
+
return segs;
|
|
2983
|
+
}
|
|
2984
|
+
/** Session-switch navigation, fail-soft: a stale row (list rebuilt between snapshot and click) loses its open() race and is ignored. */
|
|
2985
|
+
function openAgentSession(face, id) {
|
|
2986
|
+
if (face === null || typeof face.open !== "function") return;
|
|
2987
|
+
try {
|
|
2988
|
+
face.open(id);
|
|
2989
|
+
} catch {}
|
|
2990
|
+
}
|
|
2991
|
+
/** Narrow `ctx.get('sessions')` to the card's face (null = harness without the outward sessions service). */
|
|
2992
|
+
function sessionsFaceOf(ctx) {
|
|
2993
|
+
const rec = asRecord(ctx.get("sessions"));
|
|
2994
|
+
if (rec === null) return null;
|
|
2995
|
+
const list = asRecord(rec.list);
|
|
2996
|
+
if (list === null || typeof list.getSnapshot !== "function" || typeof list.subscribe !== "function") return null;
|
|
2997
|
+
return rec;
|
|
2998
|
+
}
|
|
2999
|
+
/** Compact duration: `42s`, `3m05s`, `1h07m` (shared by both locales). */
|
|
3000
|
+
function fmtDuration(ms) {
|
|
3001
|
+
if (!Number.isFinite(ms) || ms < 0) return "—";
|
|
3002
|
+
const s = Math.round(ms / 1e3);
|
|
3003
|
+
if (s < 60) return `${s}s`;
|
|
3004
|
+
const m = Math.floor(s / 60);
|
|
3005
|
+
if (m < 60) return `${m}m${String(s % 60).padStart(2, "0")}s`;
|
|
3006
|
+
return `${Math.floor(m / 60)}h${String(m % 60).padStart(2, "0")}m`;
|
|
3007
|
+
}
|
|
3008
|
+
//#endregion
|
|
3009
|
+
//#region src/client/components/agentGraph.tsx
|
|
3010
|
+
/** Caption box height under a node (3 wrapped label lines + the tokens line). */
|
|
3011
|
+
const CAPTION_H = 60;
|
|
3012
|
+
/** Fallback arc color for pressure-only nodes (no composition data), by fill ratio. */
|
|
3013
|
+
function ringColorOf(pct) {
|
|
3014
|
+
if (pct === null) return "var(--dsw-alias-border-l1)";
|
|
3015
|
+
if (pct >= 90) return "#ef4444";
|
|
3016
|
+
if (pct >= 70) return "#f59e0b";
|
|
3017
|
+
return "#22c55e";
|
|
3018
|
+
}
|
|
3019
|
+
function makeAgentGraph(ctx, kit) {
|
|
3020
|
+
const { t, fmt, catLabel } = kit;
|
|
3021
|
+
function AgentGraph(props) {
|
|
3022
|
+
const face = React.useMemo(() => sessionsFaceOf(ctx), []);
|
|
3023
|
+
const subscribe = React.useCallback((fn) => {
|
|
3024
|
+
if (face === null) return () => {};
|
|
3025
|
+
/* v8 ignore next 2 -- sessionsFaceOf returns a face only after proving list.subscribe. */
|
|
3026
|
+
if (face.list === void 0) return () => {};
|
|
3027
|
+
return face.list.subscribe(fn);
|
|
3028
|
+
}, [face]);
|
|
3029
|
+
const getSnapshot = React.useCallback(() => {
|
|
3030
|
+
if (face === null) return null;
|
|
3031
|
+
/* v8 ignore next 2 -- sessionsFaceOf proves list before returning the face. */
|
|
3032
|
+
if (face.list === void 0) return null;
|
|
3033
|
+
return face.list.getSnapshot();
|
|
3034
|
+
}, [face]);
|
|
3035
|
+
const snapshot = React.useSyncExternalStore(subscribe, getSnapshot);
|
|
3036
|
+
const sessionId = props.sessionId;
|
|
3037
|
+
const [hoverId, setHoverId] = React.useState(null);
|
|
3038
|
+
const stageRef = React.useRef(null);
|
|
3039
|
+
const [stageWidth, setStageWidth] = React.useState(0);
|
|
3040
|
+
React.useEffect(() => {
|
|
3041
|
+
/* v8 ignore start -- jsdom has neither ResizeObserver nor layout; tests exercise the natural-pitch fallback (stageWidth 0). */
|
|
3042
|
+
const el = stageRef.current;
|
|
3043
|
+
if (el === null || typeof ResizeObserver !== "function") return;
|
|
3044
|
+
setStageWidth(el.clientWidth);
|
|
3045
|
+
const observer = new ResizeObserver(() => {
|
|
3046
|
+
setStageWidth(el.clientWidth);
|
|
3047
|
+
});
|
|
3048
|
+
observer.observe(el);
|
|
3049
|
+
return () => {
|
|
3050
|
+
observer.disconnect();
|
|
3051
|
+
};
|
|
3052
|
+
/* v8 ignore stop */
|
|
3053
|
+
}, []);
|
|
3054
|
+
React.useEffect(() => {
|
|
3055
|
+
if (face === null || typeof sessionId !== "string" || sessionId === "") return;
|
|
3056
|
+
if (typeof face.refreshSubagents !== "function") return;
|
|
3057
|
+
face.refreshSubagents(sessionId).catch(() => {});
|
|
3058
|
+
}, [face, sessionId]);
|
|
3059
|
+
const built = React.useMemo(() => {
|
|
3060
|
+
const forest = agentForestOf(snapshot, sessionId, props.self);
|
|
3061
|
+
return forest !== null ? {
|
|
3062
|
+
forest,
|
|
3063
|
+
layout: layoutForest(forest, stageWidth)
|
|
3064
|
+
} : null;
|
|
3065
|
+
}, [
|
|
3066
|
+
snapshot,
|
|
3067
|
+
sessionId,
|
|
3068
|
+
props.self,
|
|
3069
|
+
stageWidth
|
|
3070
|
+
]);
|
|
3071
|
+
if (built === null) return null;
|
|
3072
|
+
const { forest, layout } = built;
|
|
3073
|
+
const byId = new Map(forest.nodes.map((n) => [n.id, n]));
|
|
3074
|
+
/* v8 ignore next 1 -- agentForestOf anchors the forest at the current
|
|
3075
|
+
session, so a current node always exists. */
|
|
3076
|
+
const current = forest.nodes.find((n) => n.isCurrent) ?? forest.nodes[0];
|
|
3077
|
+
const inspected = (hoverId !== null ? byId.get(hoverId) : void 0) ?? current;
|
|
3078
|
+
const runningCount = forest.nodes.filter((n) => n.running).length;
|
|
3079
|
+
let totalTokens = 0;
|
|
3080
|
+
for (const n of forest.nodes) totalTokens += n.head !== null ? n.head.tokens : 0;
|
|
3081
|
+
const open = (id) => {
|
|
3082
|
+
if (id === current.id) return;
|
|
3083
|
+
openAgentSession(face, id);
|
|
3084
|
+
};
|
|
3085
|
+
const keyOpen = (id) => (ev) => {
|
|
3086
|
+
if (ev.key !== "Enter" && ev.key !== " ") return;
|
|
3087
|
+
ev.preventDefault();
|
|
3088
|
+
open(id);
|
|
3089
|
+
};
|
|
3090
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-agents" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("agents.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("agents.sub"))), /* @__PURE__ */ React.createElement("div", { className: "lc-agents-chips" }, /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" }, t("agents.chip.count", { n: forest.nodes.length + forest.overflow })), /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" + (runningCount > 0 ? " lc-agents-chip-on" : "") }, t("agents.chip.running", { n: runningCount })), totalTokens > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" }, t("agents.chip.tokens", { n: fmt(totalTokens) })) : null, forest.overflow > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" }, t("agents.more", { n: forest.overflow })) : null), /* @__PURE__ */ React.createElement("div", {
|
|
3091
|
+
className: "lc-agents-stage",
|
|
3092
|
+
ref: stageRef
|
|
3093
|
+
}, /* @__PURE__ */ React.createElement("svg", {
|
|
3094
|
+
className: "lc-agents-svg",
|
|
3095
|
+
width: layout.width,
|
|
3096
|
+
height: layout.height,
|
|
3097
|
+
viewBox: `0 0 ${layout.width} ${layout.height}`
|
|
3098
|
+
}, layout.links.map((link) => {
|
|
3099
|
+
const d = `M ${link.x1} ${link.y1} L ${link.x2} ${link.y2}`;
|
|
3100
|
+
return /* @__PURE__ */ React.createElement("g", { key: link.to }, /* @__PURE__ */ React.createElement("path", {
|
|
3101
|
+
className: "lc-agents-link" + (link.running ? " lc-agents-link-live" : ""),
|
|
3102
|
+
d,
|
|
3103
|
+
stroke: link.color,
|
|
3104
|
+
fill: "none"
|
|
3105
|
+
}), link.running ? /* @__PURE__ */ React.createElement("path", {
|
|
3106
|
+
className: "lc-agents-flow",
|
|
3107
|
+
d,
|
|
3108
|
+
stroke: link.color,
|
|
3109
|
+
fill: "none"
|
|
3110
|
+
}) : null);
|
|
3111
|
+
}), forest.nodes.map((node) => {
|
|
3112
|
+
const point = layout.points.find((p) => p.id === node.id);
|
|
3113
|
+
/* v8 ignore next 2 -- layoutForest positions every forest node,
|
|
3114
|
+
so the lookup never misses. */
|
|
3115
|
+
if (point === void 0) return null;
|
|
3116
|
+
return /* @__PURE__ */ React.createElement(AgentNodeView, {
|
|
3117
|
+
key: node.id,
|
|
3118
|
+
node,
|
|
3119
|
+
x: point.x,
|
|
3120
|
+
y: point.y,
|
|
3121
|
+
captionW: layout.captionW,
|
|
3122
|
+
hovered: hoverId === node.id,
|
|
3123
|
+
onHover: setHoverId,
|
|
3124
|
+
onOpen: open,
|
|
3125
|
+
onKeyOpen: keyOpen(node.id),
|
|
3126
|
+
t,
|
|
3127
|
+
fmt
|
|
3128
|
+
});
|
|
3129
|
+
}))), forest.solo ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty lc-agents-solo" }, t("agents.solo")) : null, /* @__PURE__ */ React.createElement(Inspector, {
|
|
3130
|
+
node: inspected,
|
|
3131
|
+
t,
|
|
3132
|
+
fmt
|
|
3133
|
+
}), /* @__PURE__ */ React.createElement("div", { className: "lc-agents-legend" }, CATS.map((c) => /* @__PURE__ */ React.createElement("span", {
|
|
3134
|
+
key: c.key,
|
|
3135
|
+
className: "lc-agents-legend-item"
|
|
3136
|
+
}, /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }), catLabel(c.key))), /* @__PURE__ */ React.createElement("span", { className: "lc-agents-legend-item" }, /* @__PURE__ */ React.createElement("i", { className: "lc-agents-legend-free" }), t("agents.legend.free")), /* @__PURE__ */ React.createElement("span", { className: "lc-agents-legend-item" }, /* @__PURE__ */ React.createElement("i", { className: "lc-agents-legend-edge" }), t("agents.running"))));
|
|
3137
|
+
}
|
|
3138
|
+
return AgentGraph;
|
|
3139
|
+
}
|
|
3140
|
+
function AgentNodeView(props) {
|
|
3141
|
+
const { node, x, y, captionW } = props;
|
|
3142
|
+
const pct = node.head !== null ? node.head.pct : null;
|
|
3143
|
+
const ring = 2 * Math.PI * 20;
|
|
3144
|
+
const segs = node.head !== null ? ringSegments(node.head.parts, pct, 20, ringColorOf(pct)) : [];
|
|
3145
|
+
const cls = "lc-agent-node" + (node.isCurrent ? " lc-agent-self" : "") + (node.running ? " lc-agent-running" : "") + (node.completed && !node.running ? " lc-agent-done" : "") + (props.hovered ? " lc-agent-hover" : "") + (node.isCurrent ? "" : " lc-agent-clickable");
|
|
3146
|
+
return /* @__PURE__ */ React.createElement("g", {
|
|
3147
|
+
className: cls,
|
|
3148
|
+
transform: `translate(${x}, ${y})`,
|
|
3149
|
+
"data-agent": node.id,
|
|
3150
|
+
role: node.isCurrent ? "img" : "button",
|
|
3151
|
+
tabIndex: node.isCurrent ? void 0 : 0,
|
|
3152
|
+
onClick: () => {
|
|
3153
|
+
props.onOpen(node.id);
|
|
3154
|
+
},
|
|
3155
|
+
onKeyDown: props.onKeyOpen,
|
|
3156
|
+
onMouseEnter: () => {
|
|
3157
|
+
props.onHover(node.id);
|
|
3158
|
+
},
|
|
3159
|
+
onMouseLeave: () => {
|
|
3160
|
+
props.onHover(null);
|
|
3161
|
+
}
|
|
3162
|
+
}, /* @__PURE__ */ React.createElement("circle", {
|
|
3163
|
+
className: "lc-agent-halo",
|
|
3164
|
+
r: 35
|
|
3165
|
+
}), /* @__PURE__ */ React.createElement("circle", {
|
|
3166
|
+
className: "lc-agent-track",
|
|
3167
|
+
r: 26
|
|
3168
|
+
}), segs.map((seg) => /* @__PURE__ */ React.createElement("circle", {
|
|
3169
|
+
key: seg.key,
|
|
3170
|
+
className: "lc-agent-seg" + (seg.free ? " lc-agent-free" : ""),
|
|
3171
|
+
r: 20,
|
|
3172
|
+
stroke: seg.free ? void 0 : seg.color,
|
|
3173
|
+
strokeDasharray: `${seg.len} ${ring - seg.len}`,
|
|
3174
|
+
strokeDashoffset: -seg.offset,
|
|
3175
|
+
transform: "rotate(-90)"
|
|
3176
|
+
})), /* @__PURE__ */ React.createElement("text", {
|
|
3177
|
+
className: "lc-agent-pct",
|
|
3178
|
+
textAnchor: "middle",
|
|
3179
|
+
dy: "0.32em"
|
|
3180
|
+
}, pct !== null ? `${pct}%` : node.head !== null ? props.fmt(node.head.tokens) : "—"), /* @__PURE__ */ React.createElement("foreignObject", {
|
|
3181
|
+
x: -captionW / 2,
|
|
3182
|
+
y: 34,
|
|
3183
|
+
width: captionW,
|
|
3184
|
+
height: CAPTION_H
|
|
3185
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-agent-caption" }, /* @__PURE__ */ React.createElement("div", { className: "lc-agent-label" }, node.label, node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge lc-agent-self-badge" }, props.t("agents.self")) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-agent-tokens" }, node.head !== null ? props.fmt(node.head.tokens) : "—"))));
|
|
3186
|
+
}
|
|
3187
|
+
/** The detail strip mirroring the hovered (or current) node: identity, occupancy, activity, and the open hint. */
|
|
3188
|
+
function Inspector(props) {
|
|
3189
|
+
const { node, t, fmt } = props;
|
|
3190
|
+
const bits = [];
|
|
3191
|
+
if (node.head !== null) {
|
|
3192
|
+
const head = node.head;
|
|
3193
|
+
const window = head.window !== void 0 ? ` / ${fmt(head.window)}` : "";
|
|
3194
|
+
const pct = head.pct !== null ? ` · ${head.pct}%` : "";
|
|
3195
|
+
bits.push(`${fmt(head.tokens)}${window}${pct}`);
|
|
3196
|
+
}
|
|
3197
|
+
if (node.requests > 0) bits.push(t("agents.requests", { n: node.requests }));
|
|
3198
|
+
if (node.billed !== null && node.billed > 0) bits.push(t("agents.billed", { n: fmt(node.billed) }));
|
|
3199
|
+
if (node.durationMs !== null) bits.push(fmtDuration(node.durationMs));
|
|
3200
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-agents-inspector" }, /* @__PURE__ */ React.createElement("b", { className: "lc-agents-inspector-name" }, node.label), node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge" }, t("agents.self")) : null, node.running ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge lc-agents-badge-on" }, t("agents.running")) : null, node.identity !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge" }, t(node.identity.mode === "one-shot" ? "agents.oneshot" : "agents.continuable")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-agents-inspector-stats" }, bits.join(" · ")), !node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-inspector-open" }, t("agents.open")) : null);
|
|
3201
|
+
}
|
|
3202
|
+
//#endregion
|
|
2216
3203
|
//#region src/client/components/events.tsx
|
|
2217
3204
|
const EVENT_ICONS = {
|
|
2218
3205
|
compaction: "✂",
|
|
@@ -2274,6 +3261,14 @@ window.__ModuleLoader__.load({
|
|
|
2274
3261
|
eventAt
|
|
2275
3262
|
};
|
|
2276
3263
|
}
|
|
3264
|
+
/**
|
|
3265
|
+
* Set the native `title` on every label whose text overflows its box (the styled tips cover cards, but a plain
|
|
3266
|
+
* ellipsis row still needs the native fallback); reads (scrollWidth/clientWidth) and writes (title) stay separate
|
|
3267
|
+
* from layout-affecting work, and callers gate WHEN this runs so it never becomes a per-render forced layout.
|
|
3268
|
+
*/
|
|
3269
|
+
function syncTitles(root) {
|
|
3270
|
+
for (const el of root.querySelectorAll(".lc-event-label")) el.title = el.scrollWidth > el.clientWidth ? el.textContent || "" : "";
|
|
3271
|
+
}
|
|
2277
3272
|
function makeEventList(kit) {
|
|
2278
3273
|
const { t, fmt, fmtTime, eventLabel, eventAt } = kit;
|
|
2279
3274
|
return function EventList(props) {
|
|
@@ -2281,46 +3276,214 @@ window.__ModuleLoader__.load({
|
|
|
2281
3276
|
React.useLayoutEffect(() => {
|
|
2282
3277
|
const root = rootRef.current;
|
|
2283
3278
|
if (!root) return;
|
|
2284
|
-
|
|
2285
|
-
|
|
3279
|
+
syncTitles(root);
|
|
3280
|
+
}, [props.events]);
|
|
3281
|
+
React.useEffect(() => {
|
|
3282
|
+
const onResize = () => {
|
|
3283
|
+
const root = rootRef.current;
|
|
3284
|
+
if (root !== null) syncTitles(root);
|
|
2286
3285
|
};
|
|
2287
|
-
|
|
2288
|
-
window.addEventListener("resize", sync);
|
|
3286
|
+
window.addEventListener("resize", onResize);
|
|
2289
3287
|
return () => {
|
|
2290
|
-
window.removeEventListener("resize",
|
|
3288
|
+
window.removeEventListener("resize", onResize);
|
|
2291
3289
|
};
|
|
2292
|
-
});
|
|
3290
|
+
}, []);
|
|
2293
3291
|
if (props.events.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
|
|
2294
3292
|
const sorted = props.events.slice().reverse();
|
|
2295
3293
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2296
3294
|
className: "lc-events",
|
|
2297
3295
|
ref: rootRef
|
|
2298
|
-
}, sorted.map((ev
|
|
3296
|
+
}, sorted.map((ev) => {
|
|
2299
3297
|
const label = eventLabel(ev);
|
|
2300
3298
|
const at = eventAt(ev);
|
|
2301
3299
|
const glyph = ev.kind === "inject" ? /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, null) : ev.kind === "model" ? /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, null) : EVENT_ICONS[ev.kind] || "•";
|
|
2302
3300
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2303
|
-
key:
|
|
3301
|
+
key: ev.seq,
|
|
2304
3302
|
className: "lc-event"
|
|
2305
3303
|
}, /* @__PURE__ */ React.createElement("span", { className: "lc-event-icon lc-event-" + ev.kind }, glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-kind lc-kind-" + ev.kind }, t("kind." + ev.kind)), /* @__PURE__ */ React.createElement("span", { className: "lc-event-label" }, label), at !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-at" }, at) : null, ev.tokens ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-tokens" + (ev.kind === "inject" ? " lc-up" : " lc-down") }, (ev.kind === "inject" ? "+" : "−") + fmt(ev.tokens)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-event-time" }, fmtTime(ev.time)));
|
|
2306
3304
|
}));
|
|
2307
3305
|
};
|
|
2308
3306
|
}
|
|
3307
|
+
//#endregion
|
|
3308
|
+
//#region src/client/components/fileCard.tsx
|
|
3309
|
+
function makeFileCard(kit, settings) {
|
|
3310
|
+
const { t, fmt, fmtTime } = kit;
|
|
3311
|
+
function formGlyph(form) {
|
|
3312
|
+
return form === "image" ? "🖼" : form === "dir" ? "📁" : "📄";
|
|
3313
|
+
}
|
|
3314
|
+
function matches(e, f) {
|
|
3315
|
+
if (f === "all") return true;
|
|
3316
|
+
if (f === "image") return e.form === "image";
|
|
3317
|
+
if (f === "read") return e.reads > 0;
|
|
3318
|
+
if (f === "write") return e.writes > 0;
|
|
3319
|
+
return e.searches > 0;
|
|
3320
|
+
}
|
|
3321
|
+
/** Signed line pair, harness diff semantics: growth on the success token, shrinkage on the error token. */
|
|
3322
|
+
function DeltaPair(props) {
|
|
3323
|
+
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);
|
|
3324
|
+
}
|
|
3325
|
+
return React.memo(function FileCard(props) {
|
|
3326
|
+
const { activity } = props;
|
|
3327
|
+
const [filter, setFilter] = React.useState("all");
|
|
3328
|
+
const [sort, setSort] = React.useState(() => settings.defaultFileSort());
|
|
3329
|
+
const [query, setQuery] = React.useState("");
|
|
3330
|
+
const [openPath, setOpenPath] = React.useState(null);
|
|
3331
|
+
const q = query.trim().toLowerCase();
|
|
3332
|
+
const countOf = (e) => filter === "read" ? e.reads : filter === "write" ? e.writes : filter === "search" ? e.searches : e.ops.length;
|
|
3333
|
+
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);
|
|
3334
|
+
const chips = [
|
|
3335
|
+
{
|
|
3336
|
+
key: "all",
|
|
3337
|
+
files: activity.entries.length,
|
|
3338
|
+
ops: activity.totals.read.ops + activity.totals.write.ops + activity.totals.search.ops
|
|
3339
|
+
},
|
|
3340
|
+
{
|
|
3341
|
+
key: "read",
|
|
3342
|
+
files: activity.totals.read.files,
|
|
3343
|
+
ops: activity.totals.read.ops
|
|
3344
|
+
},
|
|
3345
|
+
{
|
|
3346
|
+
key: "write",
|
|
3347
|
+
files: activity.totals.write.files,
|
|
3348
|
+
ops: activity.totals.write.ops
|
|
3349
|
+
},
|
|
3350
|
+
{
|
|
3351
|
+
key: "search",
|
|
3352
|
+
files: activity.totals.search.files,
|
|
3353
|
+
ops: activity.totals.search.ops
|
|
3354
|
+
},
|
|
3355
|
+
{
|
|
3356
|
+
key: "image",
|
|
3357
|
+
files: activity.totals.image.files,
|
|
3358
|
+
ops: activity.totals.image.ops
|
|
3359
|
+
}
|
|
3360
|
+
];
|
|
3361
|
+
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, {
|
|
3362
|
+
added: op.added,
|
|
3363
|
+
removed: op.removed
|
|
3364
|
+
}) : null, op.err ? /* @__PURE__ */ React.createElement("span", {
|
|
3365
|
+
className: "lc-br-err-dot",
|
|
3366
|
+
title: t("node.failed")
|
|
3367
|
+
}) : null);
|
|
3368
|
+
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", {
|
|
3369
|
+
key: c.key,
|
|
3370
|
+
type: "button",
|
|
3371
|
+
className: "lc-gran-btn" + (filter === c.key ? " lc-gran-on" : "") + (c.key === "read" || c.key === "write" || c.key === "search" ? " lc-fa-chip-" + c.key : ""),
|
|
3372
|
+
title: t("files.chipTip", {
|
|
3373
|
+
files: c.files,
|
|
3374
|
+
ops: c.ops
|
|
3375
|
+
}),
|
|
3376
|
+
onClick: () => {
|
|
3377
|
+
setFilter((cur) => cur === c.key ? "all" : c.key);
|
|
3378
|
+
}
|
|
3379
|
+
}, t("files.kind." + c.key), /* @__PURE__ */ React.createElement("b", { className: "lc-fa-n" }, fmt(c.ops))))), /* @__PURE__ */ React.createElement("input", {
|
|
3380
|
+
className: "lc-fa-search",
|
|
3381
|
+
value: query,
|
|
3382
|
+
placeholder: t("files.search"),
|
|
3383
|
+
onChange: (ev) => {
|
|
3384
|
+
setQuery(ev.target.value);
|
|
3385
|
+
}
|
|
3386
|
+
})), /* @__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, {
|
|
3387
|
+
added: activity.totals.added,
|
|
3388
|
+
removed: activity.totals.removed
|
|
3389
|
+
}), /* @__PURE__ */ React.createElement("span", {
|
|
3390
|
+
className: "lc-fa-meta-tip",
|
|
3391
|
+
role: "tooltip"
|
|
3392
|
+
}, t("files.deltaTip"))) : null, /* @__PURE__ */ React.createElement("span", {
|
|
3393
|
+
className: "lc-gran lc-fa-sort",
|
|
3394
|
+
role: "group",
|
|
3395
|
+
title: t("files.sortTip")
|
|
3396
|
+
}, [
|
|
3397
|
+
"count",
|
|
3398
|
+
"latest",
|
|
3399
|
+
"path"
|
|
3400
|
+
].map((k) => /* @__PURE__ */ React.createElement("button", {
|
|
3401
|
+
key: k,
|
|
3402
|
+
type: "button",
|
|
3403
|
+
className: "lc-gran-btn" + (sort === k ? " lc-gran-on" : ""),
|
|
3404
|
+
onClick: () => {
|
|
3405
|
+
setSort(k);
|
|
3406
|
+
}
|
|
3407
|
+
}, 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) => {
|
|
3408
|
+
const open = openPath === e.path;
|
|
3409
|
+
const trimmed = e.path.endsWith("/") ? e.path.slice(0, -1) : e.path;
|
|
3410
|
+
const slash = trimmed.lastIndexOf("/");
|
|
3411
|
+
const dir = slash >= 0 ? trimmed.slice(0, slash + 1) : "";
|
|
3412
|
+
const base = slash >= 0 ? trimmed.slice(slash + 1) : trimmed;
|
|
3413
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
3414
|
+
key: e.path,
|
|
3415
|
+
className: "lc-fa-item" + (open ? " lc-fa-item-on" : "")
|
|
3416
|
+
}, /* @__PURE__ */ React.createElement("button", {
|
|
3417
|
+
type: "button",
|
|
3418
|
+
className: "lc-fa-row",
|
|
3419
|
+
title: e.path,
|
|
3420
|
+
onClick: () => {
|
|
3421
|
+
setOpenPath(open ? null : e.path);
|
|
3422
|
+
}
|
|
3423
|
+
}, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "▸"), /* @__PURE__ */ React.createElement("span", {
|
|
3424
|
+
className: "lc-fa-form",
|
|
3425
|
+
title: t("files.form." + e.form)
|
|
3426
|
+
}, 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", {
|
|
3427
|
+
className: "lc-fa-badge lc-fa-b-read",
|
|
3428
|
+
title: t("files.kind.read")
|
|
3429
|
+
}, /* @__PURE__ */ React.createElement("i", null), fmt(e.reads)) : null, e.writes > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
3430
|
+
className: "lc-fa-badge lc-fa-b-write",
|
|
3431
|
+
title: t("files.kind.write")
|
|
3432
|
+
}, /* @__PURE__ */ React.createElement("i", null), fmt(e.writes)) : null, e.searches > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
3433
|
+
className: "lc-fa-badge lc-fa-b-search",
|
|
3434
|
+
title: t("files.kind.search")
|
|
3435
|
+
}, /* @__PURE__ */ React.createElement("i", null), fmt(e.searches)) : null, e.added + e.removed > 0 ? /* @__PURE__ */ React.createElement(DeltaPair, {
|
|
3436
|
+
added: e.added,
|
|
3437
|
+
removed: e.removed
|
|
3438
|
+
}) : null, e.errs > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
3439
|
+
className: "lc-br-err-dot",
|
|
3440
|
+
title: t("files.errs", { n: e.errs })
|
|
3441
|
+
}) : 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.map((op) => {
|
|
3442
|
+
const onLocate = props.onLocate;
|
|
3443
|
+
return onLocate !== void 0 ? /* @__PURE__ */ React.createElement("button", {
|
|
3444
|
+
key: op.seq,
|
|
3445
|
+
type: "button",
|
|
3446
|
+
className: "lc-fa-op lc-fa-op-link",
|
|
3447
|
+
title: t("files.locate"),
|
|
3448
|
+
onClick: () => {
|
|
3449
|
+
onLocate(op);
|
|
3450
|
+
}
|
|
3451
|
+
}, opLine(op)) : /* @__PURE__ */ React.createElement("div", {
|
|
3452
|
+
key: op.seq,
|
|
3453
|
+
className: "lc-fa-op"
|
|
3454
|
+
}, opLine(op));
|
|
3455
|
+
})) : null);
|
|
3456
|
+
}))));
|
|
3457
|
+
});
|
|
3458
|
+
}
|
|
2309
3459
|
const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
|
|
2310
3460
|
const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
|
|
2311
3461
|
//#endregion
|
|
2312
3462
|
//#region src/client/latestVersion.ts
|
|
2313
3463
|
/**
|
|
2314
|
-
* One live check for the Plugin-info card: lazy with 1h TTL;
|
|
2315
|
-
*
|
|
3464
|
+
* One live check for the Plugin-info card: lazy with 1h TTL; registries
|
|
3465
|
+
* are tried strictly in order — official npm first, npmmirror only as a
|
|
3466
|
+
* fallback — and one-at-a-time, never in parallel. Each source's failure
|
|
3467
|
+
* (non-ok response, missing/non-string version, network error) advances
|
|
3468
|
+
* to the next one; only an all-source failure resolves to null so the
|
|
3469
|
+
* card silently keeps its static version.
|
|
2316
3470
|
*/
|
|
2317
|
-
const
|
|
3471
|
+
const REGISTRY_HOSTS = ["https://registry.npmjs.org", "https://registry.npmmirror.com"];
|
|
2318
3472
|
const TTL_MS = 36e5;
|
|
2319
3473
|
let cached = null;
|
|
3474
|
+
function readVersion(host) {
|
|
3475
|
+
return fetch(host + "/dsh-context/latest").then((res) => res.ok ? res.json() : null).then((body) => body !== null && typeof body.version === "string" ? body.version : null).catch(() => null);
|
|
3476
|
+
}
|
|
2320
3477
|
function fetchLatestVersion() {
|
|
2321
3478
|
if (!cached || Date.now() - cached.at >= TTL_MS) cached = {
|
|
2322
3479
|
at: Date.now(),
|
|
2323
|
-
promise:
|
|
3480
|
+
promise: (async () => {
|
|
3481
|
+
for (const host of REGISTRY_HOSTS) {
|
|
3482
|
+
const v = await readVersion(host);
|
|
3483
|
+
if (v !== null) return v;
|
|
3484
|
+
}
|
|
3485
|
+
return null;
|
|
3486
|
+
})()
|
|
2324
3487
|
};
|
|
2325
3488
|
return cached.promise;
|
|
2326
3489
|
}
|
|
@@ -2349,7 +3512,7 @@ window.__ModuleLoader__.load({
|
|
|
2349
3512
|
return function PluginInfo() {
|
|
2350
3513
|
const [latest, setLatest] = React.useState(null);
|
|
2351
3514
|
React.useEffect(() => {
|
|
2352
|
-
if ("0.
|
|
3515
|
+
if ("0.34.0".includes("-dev")) return;
|
|
2353
3516
|
let on = true;
|
|
2354
3517
|
fetchLatestVersion().then((v) => {
|
|
2355
3518
|
if (on && v) setLatest(v);
|
|
@@ -2358,8 +3521,8 @@ window.__ModuleLoader__.load({
|
|
|
2358
3521
|
on = false;
|
|
2359
3522
|
};
|
|
2360
3523
|
}, []);
|
|
2361
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
2362
|
-
const nameValue = ["dsh-context (v0.
|
|
3524
|
+
const update = latest !== null && isNewerVersion(latest, "0.34.0") ? latest : null;
|
|
3525
|
+
const nameValue = ["dsh-context (v0.34.0)"];
|
|
2363
3526
|
if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
|
|
2364
3527
|
key: "update",
|
|
2365
3528
|
className: "lc-pi-update"
|
|
@@ -2374,26 +3537,29 @@ window.__ModuleLoader__.load({
|
|
|
2374
3537
|
/**
|
|
2375
3538
|
* One brief row: a fixed-width kind tag plus one glanceable line. The tag carries a styled, instant explanation bubble (the
|
|
2376
3539
|
* `.lc-stat-tip` pattern); the content span keeps the native title (preview + locate hint), so the two never stack.
|
|
2377
|
-
* Clickable when the browser linkage is wired.
|
|
3540
|
+
* Clickable when the browser linkage is wired AND the row carries a node (the always-present In row's empty state stays inert).
|
|
2378
3541
|
*/
|
|
2379
3542
|
function BriefRow(props) {
|
|
2380
|
-
const cls = "lc-brief-row" + (props.onLocate !== void 0 ? " lc-brief-row-link" : "");
|
|
2381
3543
|
const inner = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-brief-tag" }, props.tag, /* @__PURE__ */ React.createElement("span", {
|
|
2382
3544
|
className: "lc-brief-tip",
|
|
2383
3545
|
role: "tooltip"
|
|
2384
3546
|
}, props.tagTip)), props.children);
|
|
2385
|
-
if (props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className:
|
|
3547
|
+
if (props.node === void 0 || props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-brief-row" }, inner);
|
|
3548
|
+
const node = props.node;
|
|
3549
|
+
const onLocate = props.onLocate;
|
|
2386
3550
|
const locate = () => {
|
|
2387
|
-
|
|
3551
|
+
onLocate(node, props.isResponse);
|
|
2388
3552
|
};
|
|
2389
3553
|
return /* @__PURE__ */ React.createElement("button", {
|
|
2390
3554
|
type: "button",
|
|
2391
|
-
className:
|
|
3555
|
+
className: "lc-brief-row lc-brief-row-link",
|
|
2392
3556
|
onClick: locate
|
|
2393
3557
|
}, inner);
|
|
2394
3558
|
}
|
|
2395
3559
|
/** One-line identity of a surface node: text preview, call breadcrumb, tool name, or a localized placeholder. */
|
|
2396
3560
|
function nodeLine(n, conv) {
|
|
3561
|
+
/* v8 ignore if -- chipParts returns for tool cats upstream; only
|
|
3562
|
+
inject-with-skill and drift nodes ever reach nodeLine. Defensive. */
|
|
2397
3563
|
if (n.cat === "tool") return callSummaryOf(conv) ?? n.tool ?? t("node.toolResult");
|
|
2398
3564
|
if (n.text !== void 0 && n.text !== "") return n.text;
|
|
2399
3565
|
if (n.skill !== void 0) return t("node.skillTag", { name: n.skill });
|
|
@@ -2401,13 +3567,71 @@ window.__ModuleLoader__.load({
|
|
|
2401
3567
|
const summary = blockSummaryOf(conv);
|
|
2402
3568
|
return n.calls.join(" › ") + (summary !== null ? " · " + summary : "");
|
|
2403
3569
|
}
|
|
3570
|
+
/* v8 ignore if -- chipParts returns for assistant cats upstream. Defensive. */
|
|
2404
3571
|
if (n.cat === "assistant") return t("node.empty");
|
|
3572
|
+
/* v8 ignore if -- inject nodes arrive only WITH a skill (the text/skill
|
|
3573
|
+
arms above return first); inject-without-skill never reaches nodeLine. */
|
|
2405
3574
|
if (n.cat === "inject") return t("form." + (n.form ?? "context"));
|
|
2406
3575
|
return t("node.nonText");
|
|
2407
3576
|
}
|
|
3577
|
+
/**
|
|
3578
|
+
* One line's anatomy — a compact FACT tag plus the preview text, mirroring the Context browser's element rows so a
|
|
3579
|
+
* brief line reads exactly like the browser row its click reveals: tool results tag the tool name (skill results the
|
|
3580
|
+
* skill name), assistant replies tag the call breadcrumb, injections tag the form, user messages tag image attachments.
|
|
3581
|
+
*/
|
|
3582
|
+
function chipParts(n, conv) {
|
|
3583
|
+
if (n.cat === "tool") {
|
|
3584
|
+
const summary = callSummaryOf(conv);
|
|
3585
|
+
if (n.skill !== void 0) return {
|
|
3586
|
+
tag: t("node.skillTag", { name: n.skill }),
|
|
3587
|
+
text: summary ?? ""
|
|
3588
|
+
};
|
|
3589
|
+
if (n.tool !== void 0) return {
|
|
3590
|
+
tag: n.tool,
|
|
3591
|
+
text: summary ?? ""
|
|
3592
|
+
};
|
|
3593
|
+
return {
|
|
3594
|
+
tag: null,
|
|
3595
|
+
text: summary ?? t("node.toolResult")
|
|
3596
|
+
};
|
|
3597
|
+
}
|
|
3598
|
+
if (n.cat === "assistant") {
|
|
3599
|
+
const names = n.calls !== void 0 && n.calls.length > 0 ? n.calls : callNamesOf(conv);
|
|
3600
|
+
const own = n.text !== void 0 && n.text !== "" ? n.text : blockSummaryOf(conv) ?? "";
|
|
3601
|
+
return {
|
|
3602
|
+
tag: names.length > 0 ? names.join(" › ") : null,
|
|
3603
|
+
text: own !== "" ? own : names.length > 0 ? "" : t("node.empty")
|
|
3604
|
+
};
|
|
3605
|
+
}
|
|
3606
|
+
if (n.cat === "inject" && n.skill === void 0) {
|
|
3607
|
+
const text = n.text !== void 0 && n.text !== "" ? n.form === "snapshot" ? t("node.snapshot") + n.text : n.text : "";
|
|
3608
|
+
return {
|
|
3609
|
+
tag: t("form." + (n.form ?? "context")),
|
|
3610
|
+
text
|
|
3611
|
+
};
|
|
3612
|
+
}
|
|
3613
|
+
if (n.cat === "user") {
|
|
3614
|
+
const imgs = n.imgs ?? 0;
|
|
3615
|
+
return {
|
|
3616
|
+
tag: imgs > 0 ? t("attach.image") + (imgs > 1 ? " ×" + String(imgs) : "") : null,
|
|
3617
|
+
text: n.text ?? ""
|
|
3618
|
+
};
|
|
3619
|
+
}
|
|
3620
|
+
return {
|
|
3621
|
+
tag: null,
|
|
3622
|
+
text: nodeLine(n, conv)
|
|
3623
|
+
};
|
|
3624
|
+
}
|
|
3625
|
+
/** The native-title line for a fact+text pair: 'tag · text', degrading to whichever half exists. */
|
|
3626
|
+
function factTitle(tag, text) {
|
|
3627
|
+
return tag !== null ? text !== "" ? tag + " · " + text : tag : text;
|
|
3628
|
+
}
|
|
3629
|
+
/**
|
|
3630
|
+
* The brief's container ALWAYS renders — a fixed three-row lane (`.lc-brief`'s min-height) so scrubbing the chart never
|
|
3631
|
+
* changes the panel's height; unknown or empty steps just leave parts of the lane blank instead of collapsing it.
|
|
3632
|
+
*/
|
|
2408
3633
|
function BriefSection(props) {
|
|
2409
|
-
const { opener, inputs, response } = props.brief;
|
|
2410
|
-
if (opener === void 0 && inputs.length === 0 && response === void 0) return null;
|
|
3634
|
+
const { opener, inputs, response } = props.brief ?? { inputs: [] };
|
|
2411
3635
|
const convOf = props.convOf ?? (() => void 0);
|
|
2412
3636
|
const hint = props.onLocate !== void 0 ? " — " + t("brief.locate") : "";
|
|
2413
3637
|
const locateChip = props.onLocate === void 0 ? void 0 : (n) => (e) => {
|
|
@@ -2415,54 +3639,52 @@ window.__ModuleLoader__.load({
|
|
|
2415
3639
|
props.onLocate?.(n, false);
|
|
2416
3640
|
};
|
|
2417
3641
|
const MAX_CHIPS = 3;
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
3642
|
+
/**
|
|
3643
|
+
* The ONE content unit of the brief — inputs and the reply share the same chip anatomy (error dot, fact tag,
|
|
3644
|
+
* preview text). Input chips are compact and individually clickable (each locates its own node); the reply is a
|
|
3645
|
+
* single chip grown to the row's width, left inert because the row button already locates it.
|
|
3646
|
+
*/
|
|
3647
|
+
const nodeChip = (n, onClick, grow = false) => {
|
|
3648
|
+
const { tag, text } = chipParts(n, convOf(n.seq));
|
|
3649
|
+
return /* @__PURE__ */ React.createElement("span", {
|
|
3650
|
+
key: n.seq,
|
|
3651
|
+
className: "lc-brief-chip" + (grow ? " lc-brief-chip-grow" : "") + (onClick !== void 0 ? " lc-brief-chip-link" : ""),
|
|
3652
|
+
title: factTitle(tag, text) + hint,
|
|
3653
|
+
onClick
|
|
3654
|
+
}, 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);
|
|
3655
|
+
};
|
|
3656
|
+
const openerParts = opener !== void 0 ? chipParts(opener, convOf(opener.seq)) : null;
|
|
3657
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-brief" }, opener !== void 0 && openerParts !== null ? /* @__PURE__ */ React.createElement(BriefRow, {
|
|
2430
3658
|
tag: t("brief.turn"),
|
|
2431
3659
|
tagTip: t("brief.turnTip"),
|
|
2432
3660
|
node: opener,
|
|
2433
3661
|
isResponse: false,
|
|
2434
3662
|
onLocate: props.onLocate
|
|
2435
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
3663
|
+
}, openerParts.tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-fact" }, openerParts.tag) : null, openerParts.text !== "" ? /* @__PURE__ */ React.createElement("span", {
|
|
2436
3664
|
className: "lc-brief-text",
|
|
2437
|
-
title:
|
|
2438
|
-
},
|
|
3665
|
+
title: factTitle(openerParts.tag, openerParts.text) + hint
|
|
3666
|
+
}, openerParts.text) : null) : null, /* @__PURE__ */ React.createElement(BriefRow, {
|
|
2439
3667
|
tag: t("brief.input"),
|
|
2440
3668
|
tagTip: t("brief.inputTip"),
|
|
2441
3669
|
node: inputs[0],
|
|
2442
3670
|
isResponse: false,
|
|
2443
3671
|
onLocate: props.onLocate
|
|
2444
|
-
}, inputs.slice(0, MAX_CHIPS).map((n) => /* @__PURE__ */ React.createElement("span", {
|
|
2445
|
-
key: n.seq,
|
|
2446
|
-
className: "lc-brief-chip" + (props.onLocate !== void 0 ? " lc-brief-chip-link" : ""),
|
|
2447
|
-
title: nodeLine(n, convOf(n.seq)) + hint,
|
|
2448
|
-
onClick: locateChip !== void 0 ? locateChip(n) : void 0
|
|
2449
|
-
}, n.err === true ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-err-dot" }) : null, nodeLine(n, convOf(n.seq)))), inputs.length > MAX_CHIPS ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-more" }, t("brief.more", { n: inputs.length - MAX_CHIPS })) : null) : null, response !== void 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
|
|
3672
|
+
}, inputs.length > 0 ? /* @__PURE__ */ React.createElement(React.Fragment, null, 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) : /* @__PURE__ */ React.createElement("span", { className: "lc-brief-empty" }, t("brief.noInputs"))), response !== void 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
|
|
2450
3673
|
tag: t("brief.reply"),
|
|
2451
3674
|
tagTip: t("brief.replyTip"),
|
|
2452
3675
|
node: response,
|
|
2453
3676
|
isResponse: true,
|
|
2454
3677
|
onLocate: props.onLocate
|
|
2455
|
-
},
|
|
2456
|
-
className: "lc-brief-text",
|
|
2457
|
-
title: replyText + hint
|
|
2458
|
-
}, replyArrow ? "→ " : "", replyText)) : null);
|
|
3678
|
+
}, nodeChip(response, void 0, true)) : null);
|
|
2459
3679
|
}
|
|
2460
|
-
return function RequestDetail(props) {
|
|
3680
|
+
return React.memo(function RequestDetail(props) {
|
|
2461
3681
|
const req = props.request;
|
|
2462
3682
|
if (!req) return null;
|
|
2463
3683
|
const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
|
|
2464
3684
|
const head = isTurn ? t("detail.turn", {
|
|
2465
3685
|
t: req.turn ?? 0,
|
|
3686
|
+
/* v8 ignore next -- isTurn already guarantees stepCount !== undefined;
|
|
3687
|
+
the fallback is defensive. */
|
|
2466
3688
|
n: req.stepCount ?? 0
|
|
2467
3689
|
}) : t("detail.step", {
|
|
2468
3690
|
t: req.turn ?? 0,
|
|
@@ -2487,11 +3709,11 @@ window.__ModuleLoader__.load({
|
|
|
2487
3709
|
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", {
|
|
2488
3710
|
className: "lc-detail-marker",
|
|
2489
3711
|
title: eventLabel(marker)
|
|
2490
|
-
}, "✂ " + 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),
|
|
3712
|
+
}, "✂ " + 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(BriefSection, {
|
|
2491
3713
|
brief: props.brief,
|
|
2492
3714
|
convOf: props.convOf,
|
|
2493
3715
|
onLocate: props.onLocate
|
|
2494
|
-
})
|
|
3716
|
+
}), /* @__PURE__ */ React.createElement(StackedBar, {
|
|
2495
3717
|
parts,
|
|
2496
3718
|
height: 10
|
|
2497
3719
|
}), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c, i) => {
|
|
@@ -2514,7 +3736,7 @@ window.__ModuleLoader__.load({
|
|
|
2514
3736
|
}
|
|
2515
3737
|
})), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" + (v > 0 ? " lc-detail-num-up" : v < 0 ? " lc-detail-num-down" : "") }, (v > 0 ? "+" : "") + fmt(v)) : /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" }, "≈" + fmt(v)), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-pct" }, !delta && req.total > 0 ? `${Math.round(v / req.total * 100)}%` : ""));
|
|
2516
3738
|
})));
|
|
2517
|
-
};
|
|
3739
|
+
});
|
|
2518
3740
|
}
|
|
2519
3741
|
//#endregion
|
|
2520
3742
|
//#region src/client/cost.ts
|
|
@@ -2844,25 +4066,8 @@ window.__ModuleLoader__.load({
|
|
|
2844
4066
|
const scrollRef = React.useRef(null);
|
|
2845
4067
|
const scrolledOnce = React.useRef(false);
|
|
2846
4068
|
const lastGranRef = React.useRef(props.granularity);
|
|
2847
|
-
const
|
|
2848
|
-
|
|
2849
|
-
right: false
|
|
2850
|
-
});
|
|
2851
|
-
const edgesRef = React.useRef(edges);
|
|
2852
|
-
const updateEdges = (el) => {
|
|
2853
|
-
const left = el.scrollLeft > 4;
|
|
2854
|
-
const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 4;
|
|
2855
|
-
const prev = edgesRef.current;
|
|
2856
|
-
if (prev.left === left && prev.right === right) return;
|
|
2857
|
-
edgesRef.current = {
|
|
2858
|
-
left,
|
|
2859
|
-
right
|
|
2860
|
-
};
|
|
2861
|
-
setEdges({
|
|
2862
|
-
left,
|
|
2863
|
-
right
|
|
2864
|
-
});
|
|
2865
|
-
};
|
|
4069
|
+
const lastSeqRef = React.useRef(0);
|
|
4070
|
+
const prevScrollWidthRef = React.useRef(0);
|
|
2866
4071
|
/**
|
|
2867
4072
|
* Keep each turn label centered within its block's VISIBLE slice so it never scrolls out while any part of the block is on screen
|
|
2868
4073
|
* (narrower-than-label blocks stay put); reads (offsetWidth) batch before writes (transform) to avoid layout thrash — out-of-view
|
|
@@ -2894,7 +4099,12 @@ window.__ModuleLoader__.load({
|
|
|
2894
4099
|
};
|
|
2895
4100
|
React.useLayoutEffect(() => {
|
|
2896
4101
|
const el = scrollRef.current;
|
|
4102
|
+
/* v8 ignore next 1 -- the scroll div renders unconditionally and React
|
|
4103
|
+
attaches refs before layout effects run; el is never null here. */
|
|
2897
4104
|
if (el === null) return;
|
|
4105
|
+
const newestSeq = requests.length === 0 ? 0 : requests[requests.length - 1].seq;
|
|
4106
|
+
const grew = newestSeq !== lastSeqRef.current;
|
|
4107
|
+
const widthBeforeAppend = prevScrollWidthRef.current;
|
|
2898
4108
|
if (props.granularity !== lastGranRef.current) {
|
|
2899
4109
|
lastGranRef.current = props.granularity;
|
|
2900
4110
|
scrolledOnce.current = false;
|
|
@@ -2906,14 +4116,19 @@ window.__ModuleLoader__.load({
|
|
|
2906
4116
|
el.scrollLeft = Math.max(0, gi * 16 + BAR_W / 2 - el.clientWidth / 2);
|
|
2907
4117
|
}
|
|
2908
4118
|
props.onFocusTurnHandled();
|
|
2909
|
-
}
|
|
2910
|
-
if (!scrolledOnce.current) {
|
|
4119
|
+
} else if (!scrolledOnce.current) {
|
|
2911
4120
|
scrolledOnce.current = true;
|
|
2912
4121
|
el.scrollLeft = el.scrollWidth;
|
|
2913
|
-
} else if (el.scrollLeft + el.clientWidth >=
|
|
2914
|
-
|
|
4122
|
+
} else if (grew && el.scrollLeft + el.clientWidth >= widthBeforeAppend - 24) el.scrollLeft = el.scrollWidth;
|
|
4123
|
+
lastSeqRef.current = newestSeq;
|
|
4124
|
+
prevScrollWidthRef.current = el.scrollWidth;
|
|
2915
4125
|
updateTurnLabels(el);
|
|
2916
|
-
|
|
4126
|
+
syncTip(el);
|
|
4127
|
+
}, [
|
|
4128
|
+
props.granularity,
|
|
4129
|
+
props.focusTurn,
|
|
4130
|
+
requests
|
|
4131
|
+
]);
|
|
2917
4132
|
const tipOf = (req) => {
|
|
2918
4133
|
const head = req.stepCount !== void 0 && req.stepCount > 1 ? t("tip.turn", {
|
|
2919
4134
|
t: req.turn ?? 0,
|
|
@@ -2925,6 +4140,8 @@ window.__ModuleLoader__.load({
|
|
|
2925
4140
|
const reply = props.replyTips?.get(req.seq);
|
|
2926
4141
|
const tail = reply !== void 0 ? " · “" + (reply.length > 48 ? reply.slice(0, 48) + "…" : reply) + "”" : "";
|
|
2927
4142
|
if (delta) {
|
|
4143
|
+
/* v8 ignore next 1 -- delta mode only receives records from
|
|
4144
|
+
deltaOf, which always assigns net; the fallback is defensive. */
|
|
2928
4145
|
const n = req.net ?? 0;
|
|
2929
4146
|
return head + " · " + fmtTime(req.time) + " · " + t("tip.delta", { n: (n > 0 ? "+" : "") + fmt(n) }) + tail;
|
|
2930
4147
|
}
|
|
@@ -2932,17 +4149,43 @@ window.__ModuleLoader__.load({
|
|
|
2932
4149
|
};
|
|
2933
4150
|
const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
|
|
2934
4151
|
const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
|
|
4152
|
+
const tipColRef = React.useRef(0);
|
|
4153
|
+
/**
|
|
4154
|
+
* Glue the hover tip to its bar's VISIBLE slice. The tip deliberately does NOT live inside the scrolling
|
|
4155
|
+
* content: an absolutely-positioned child of a scroller contributes to its scrollable overflow, so a wide
|
|
4156
|
+
* reply preview on a right-edge bar used to inflate scrollWidth on every hover and flap the horizontal
|
|
4157
|
+
* scrollbar open/closed — jumping the whole card. Reads (offsetWidth/clientWidth) batch before the single
|
|
4158
|
+
* style write; unchanged transforms write nothing.
|
|
4159
|
+
*/
|
|
4160
|
+
const syncTip = (el) => {
|
|
4161
|
+
/* v8 ignore next 1 -- the scroll div renders unconditionally while mounted, so its parent exists. */
|
|
4162
|
+
const tip = (el.parentElement ?? document.body).querySelector(".lc-chart-tip");
|
|
4163
|
+
if (tip === null) return;
|
|
4164
|
+
const lw = tip.offsetWidth;
|
|
4165
|
+
const cw = el.clientWidth;
|
|
4166
|
+
const half = Math.min(lw / 2, cw / 2);
|
|
4167
|
+
const cx = Math.min(Math.max(tipColRef.current - el.scrollLeft, half), cw - half);
|
|
4168
|
+
const next = `translate(${Math.round(cx - lw / 2)}px, 0)`;
|
|
4169
|
+
if (tip.style.transform !== next) tip.style.transform = next;
|
|
4170
|
+
};
|
|
4171
|
+
React.useLayoutEffect(() => {
|
|
4172
|
+
/* v8 ignore next 1 -- the scroll div renders unconditionally and React attaches refs before
|
|
4173
|
+
layout effects run; el is never null here. */
|
|
4174
|
+
if (scrollRef.current === null) return;
|
|
4175
|
+
tipColRef.current = hoveredIdx >= 0 ? hoveredIdx * 16 + BAR_W / 2 : 0;
|
|
4176
|
+
syncTip(scrollRef.current);
|
|
4177
|
+
});
|
|
2935
4178
|
return /* @__PURE__ */ React.createElement("div", { className: "lc-chartrow" }, /* @__PURE__ */ React.createElement("div", { className: "lc-axis" }, delta ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, (maxUp > 0 ? "+" : "") + fmt(maxUp)), /* @__PURE__ */ React.createElement("span", {
|
|
2936
4179
|
className: "lc-axis-mid",
|
|
2937
4180
|
style: { top: `${13 + upPx}px` }
|
|
2938
|
-
}, "0"), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, (maxDown > 0 ? "-" : "") + fmt(maxDown))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, fmt(maxTotal)), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-mid" }, fmt(Math.round(maxTotal / 2))), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, "0"))), /* @__PURE__ */ React.createElement("div", {
|
|
4181
|
+
}, "0"), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, (maxDown > 0 ? "-" : "") + fmt(maxDown))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, fmt(maxTotal)), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-mid" }, fmt(Math.round(maxTotal / 2))), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, "0"))), /* @__PURE__ */ React.createElement("div", { className: "lc-chart-wrap" }, /* @__PURE__ */ React.createElement("div", {
|
|
2939
4182
|
className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
|
|
2940
4183
|
ref: scrollRef,
|
|
2941
4184
|
onScroll: (e) => {
|
|
2942
|
-
updateEdges(e.currentTarget);
|
|
2943
4185
|
updateTurnLabels(e.currentTarget);
|
|
4186
|
+
syncTip(e.currentTarget);
|
|
2944
4187
|
}
|
|
2945
|
-
},
|
|
4188
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
2946
4189
|
className: "lc-chart",
|
|
2947
4190
|
onMouseLeave: () => {
|
|
2948
4191
|
props.onHover(null);
|
|
@@ -2963,10 +4206,7 @@ window.__ModuleLoader__.load({
|
|
|
2963
4206
|
deltaScale: delta ? deltaScale : void 0,
|
|
2964
4207
|
onSelect: props.onSelect,
|
|
2965
4208
|
onHover: props.onHover
|
|
2966
|
-
}))),
|
|
2967
|
-
className: "lc-chart-tip",
|
|
2968
|
-
style: { left: `${hoveredIdx * 16 + BAR_W / 2}px` }
|
|
2969
|
-
}, tipOf(hoveredReq)) : null, /* @__PURE__ */ React.createElement("div", {
|
|
4209
|
+
}))), /* @__PURE__ */ React.createElement("div", {
|
|
2970
4210
|
className: "lc-turns",
|
|
2971
4211
|
onMouseLeave: () => {
|
|
2972
4212
|
props.onHoverTurn(null);
|
|
@@ -2988,7 +4228,7 @@ window.__ModuleLoader__.load({
|
|
|
2988
4228
|
props.onPickTurn(grp.turn);
|
|
2989
4229
|
}
|
|
2990
4230
|
}, /* @__PURE__ */ React.createElement("span", { className: "lc-turn-label" }, `T${grp.turn}`));
|
|
2991
|
-
}))),
|
|
4231
|
+
}))), hoveredReq !== null ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-tip" }, tipOf(hoveredReq)) : null));
|
|
2992
4232
|
};
|
|
2993
4233
|
}
|
|
2994
4234
|
//#endregion
|
|
@@ -3008,10 +4248,11 @@ window.__ModuleLoader__.load({
|
|
|
3008
4248
|
const TrendChart = makeTrendChart(kit);
|
|
3009
4249
|
const RequestDetail = makeRequestDetail(kit, StackedBar);
|
|
3010
4250
|
const EventList = makeEventList(kit);
|
|
3011
|
-
const
|
|
4251
|
+
const FileCard = makeFileCard(kit, settings);
|
|
3012
4252
|
const StatsBoard = makeStatsBoard(kit);
|
|
3013
4253
|
const PluginInfo = makePluginInfo(kit);
|
|
3014
4254
|
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
4255
|
+
const AgentGraph = makeAgentGraph(ctx, kit);
|
|
3015
4256
|
const ErrorBoundary = makeErrorBoundary(t);
|
|
3016
4257
|
function ContextViewBody(props) {
|
|
3017
4258
|
const sessionId = props.sessionId;
|
|
@@ -3035,10 +4276,6 @@ window.__ModuleLoader__.load({
|
|
|
3035
4276
|
return p.length === 1 ? [...EVENT_KINDS] : p.filter((x) => x !== k);
|
|
3036
4277
|
});
|
|
3037
4278
|
};
|
|
3038
|
-
const [toolFocus, setToolFocus] = React.useState(null);
|
|
3039
|
-
const clearToolFocus = React.useCallback(() => {
|
|
3040
|
-
setToolFocus(null);
|
|
3041
|
-
}, []);
|
|
3042
4279
|
const [nodeFocus, setNodeFocus] = React.useState(null);
|
|
3043
4280
|
const clearNodeFocus = React.useCallback(() => {
|
|
3044
4281
|
setNodeFocus(null);
|
|
@@ -3049,6 +4286,7 @@ window.__ModuleLoader__.load({
|
|
|
3049
4286
|
if (conversation === void 0 || typeof conversation.resolveImage !== "function") return void 0;
|
|
3050
4287
|
return (attachment) => conversation.resolveImage(sessionId, attachment);
|
|
3051
4288
|
}, [sessionId]);
|
|
4289
|
+
const fetchContent = React.useMemo(() => typeof sessionId === "string" && sessionId !== "" ? makeContentFetcher(ctx, sessionId) : void 0, [ctx, sessionId]);
|
|
3052
4290
|
const rootRef = React.useRef(null);
|
|
3053
4291
|
const scrollerRef = React.useRef(null);
|
|
3054
4292
|
const restoredRef = React.useRef(null);
|
|
@@ -3056,6 +4294,8 @@ window.__ModuleLoader__.load({
|
|
|
3056
4294
|
if (typeof sessionId !== "string" || sessionId === "" || data === null) return;
|
|
3057
4295
|
if (restoredRef.current === sessionId) return;
|
|
3058
4296
|
restoredRef.current = sessionId;
|
|
4297
|
+
/* v8 ignore next 3 -- a layout effect body only runs while mounted and
|
|
4298
|
+
both render paths attach rootRef, so the null arm cannot fire. */
|
|
3059
4299
|
const scroller = rootRef.current !== null ? rootRef.current.closest("[data-conversation-scroll]") : null;
|
|
3060
4300
|
if (scroller === null) return;
|
|
3061
4301
|
scrollerRef.current = scroller;
|
|
@@ -3072,7 +4312,6 @@ window.__ModuleLoader__.load({
|
|
|
3072
4312
|
const requests = data ? data.requests : [];
|
|
3073
4313
|
const events = data ? data.events : [];
|
|
3074
4314
|
const shownEvents = pickedKinds.length === EVENT_KINDS.length ? events : events.filter((e) => pickedKinds.includes(e.kind));
|
|
3075
|
-
const nodes = data ? data.nodes : [];
|
|
3076
4315
|
const displayRequests = React.useMemo(() => granularity === "turn" ? aggregateByTurn(requests) : requests, [requests, granularity]);
|
|
3077
4316
|
const markers = React.useMemo(() => attachMarkers(displayRequests, events), [displayRequests, events]);
|
|
3078
4317
|
const briefList = React.useMemo(() => data ? briefNodes(data) : [], [data]);
|
|
@@ -3083,14 +4322,6 @@ window.__ModuleLoader__.load({
|
|
|
3083
4322
|
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
3084
4323
|
return m;
|
|
3085
4324
|
}, [convNodes]);
|
|
3086
|
-
if (!data) return /* @__PURE__ */ React.createElement("div", {
|
|
3087
|
-
className: "lc-root",
|
|
3088
|
-
ref: rootRef
|
|
3089
|
-
}, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
|
|
3090
|
-
const markerOf = (req) => {
|
|
3091
|
-
const i = displayRequests.indexOf(req);
|
|
3092
|
-
return i >= 0 ? markers[i] : void 0;
|
|
3093
|
-
};
|
|
3094
4325
|
let pinnedIdx = -1;
|
|
3095
4326
|
for (let i = 0; i < displayRequests.length; i++) if (displayRequests[i].seq === selectedSeq) pinnedIdx = i;
|
|
3096
4327
|
const pinnedReq = pinnedIdx >= 0 ? displayRequests[pinnedIdx] : null;
|
|
@@ -3104,9 +4335,35 @@ window.__ModuleLoader__.load({
|
|
|
3104
4335
|
if (activeIdx < 0) activeIdx = pinnedIdx;
|
|
3105
4336
|
if (activeIdx < 0 && displayRequests.length > 0) activeIdx = displayRequests.length - 1;
|
|
3106
4337
|
const activeReq = activeIdx >= 0 ? displayRequests[activeIdx] : null;
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
4338
|
+
let filesBefore = null;
|
|
4339
|
+
if (activeReq !== null) {
|
|
4340
|
+
const ri = requests.findIndex((r) => r.seq === activeReq.seq);
|
|
4341
|
+
filesBefore = ri + 1 < requests.length ? requests[ri + 1].seq : null;
|
|
4342
|
+
}
|
|
4343
|
+
const brief = React.useMemo(() => activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null, [
|
|
4344
|
+
activeReq,
|
|
4345
|
+
briefList,
|
|
4346
|
+
displayRequests,
|
|
4347
|
+
activeIdx
|
|
4348
|
+
]);
|
|
4349
|
+
const convOf = React.useCallback((seq) => bySeq.get(seq), [bySeq]);
|
|
4350
|
+
const fileActivity = React.useMemo(() => activityOf(briefList, convOf, filesBefore), [
|
|
4351
|
+
briefList,
|
|
4352
|
+
convOf,
|
|
4353
|
+
filesBefore
|
|
4354
|
+
]);
|
|
4355
|
+
const locateFileOp = React.useCallback((op) => {
|
|
4356
|
+
const step = locateStepOf(requests, op.seq, op.gone);
|
|
4357
|
+
if (step === null) return;
|
|
4358
|
+
setNodeFocus({
|
|
4359
|
+
step,
|
|
4360
|
+
seq: op.seq,
|
|
4361
|
+
cat: "tool"
|
|
4362
|
+
});
|
|
4363
|
+
}, [requests]);
|
|
4364
|
+
const locateNode = React.useCallback((node, isResponse) => {
|
|
4365
|
+
/* v8 ignore next 1 -- locateNode is only wired to brief rows, and
|
|
4366
|
+
brief !== null guarantees activeReq !== null in the same closure. */
|
|
3110
4367
|
if (activeReq === null) return;
|
|
3111
4368
|
const next = isResponse && activeIdx + 1 < displayRequests.length ? displayRequests[activeIdx + 1] : null;
|
|
3112
4369
|
const step = isResponse ? next !== null ? next.seq : "live" : activeReq.seq;
|
|
@@ -3115,7 +4372,30 @@ window.__ModuleLoader__.load({
|
|
|
3115
4372
|
seq: node.seq,
|
|
3116
4373
|
cat: node.cat
|
|
3117
4374
|
});
|
|
4375
|
+
}, [
|
|
4376
|
+
activeReq,
|
|
4377
|
+
activeIdx,
|
|
4378
|
+
displayRequests
|
|
4379
|
+
]);
|
|
4380
|
+
if (!data) return /* @__PURE__ */ React.createElement("div", {
|
|
4381
|
+
className: "lc-root",
|
|
4382
|
+
ref: rootRef
|
|
4383
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
|
|
4384
|
+
const markerOf = (req) => {
|
|
4385
|
+
const i = displayRequests.indexOf(req);
|
|
4386
|
+
/* v8 ignore next 1 -- the only caller passes displayRequests[activeIdx],
|
|
4387
|
+
an element of the very array indexOf scans. */
|
|
4388
|
+
return i >= 0 ? markers[i] : void 0;
|
|
3118
4389
|
};
|
|
4390
|
+
const head = headlineOf(data, pressure, breakdown);
|
|
4391
|
+
let fileScope = t("files.scopeLatest");
|
|
4392
|
+
if (activeReq !== null && filesBefore !== null) fileScope = activeReq.stepCount !== void 0 && activeReq.stepCount > 1 ? t("detail.turn", {
|
|
4393
|
+
t: activeReq.turn ?? 0,
|
|
4394
|
+
n: activeReq.stepCount
|
|
4395
|
+
}) : t("detail.step", {
|
|
4396
|
+
t: activeReq.turn ?? 0,
|
|
4397
|
+
s: activeReq.step ?? 0
|
|
4398
|
+
});
|
|
3119
4399
|
let activeTurn = hoverTurn;
|
|
3120
4400
|
if (activeTurn === null && hoveredSeq !== null) {
|
|
3121
4401
|
for (const req of displayRequests) if (req.seq === hoveredSeq) {
|
|
@@ -3123,9 +4403,9 @@ window.__ModuleLoader__.load({
|
|
|
3123
4403
|
break;
|
|
3124
4404
|
}
|
|
3125
4405
|
}
|
|
3126
|
-
const head = headlineOf(data, pressure, breakdown);
|
|
3127
4406
|
const localeSvc = ctx.get("locale");
|
|
3128
4407
|
const activeLocale = localeSvc !== void 0 && typeof localeSvc.getLocale === "function" ? localeSvc.getLocale().active : "en";
|
|
4408
|
+
const subtitle = (data.model ?? "") + (data.provider ? " · " + data.provider : "");
|
|
3129
4409
|
return /* @__PURE__ */ React.createElement("div", {
|
|
3130
4410
|
className: "lc-root",
|
|
3131
4411
|
ref: rootRef
|
|
@@ -3139,11 +4419,9 @@ window.__ModuleLoader__.load({
|
|
|
3139
4419
|
locale: activeLocale
|
|
3140
4420
|
}), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement(CurrentComposition, {
|
|
3141
4421
|
head,
|
|
3142
|
-
subtitle
|
|
4422
|
+
subtitle,
|
|
3143
4423
|
hoverKey: hoverCat,
|
|
3144
|
-
onHoverKey: setHoverCat
|
|
3145
|
-
tools: data.toolList,
|
|
3146
|
-
onToolFocus: setToolFocus
|
|
4424
|
+
onHoverKey: setHoverCat
|
|
3147
4425
|
}), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("trend.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-trend-ctl" }, /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement("button", {
|
|
3148
4426
|
className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
|
|
3149
4427
|
onClick: () => {
|
|
@@ -3191,6 +4469,9 @@ window.__ModuleLoader__.load({
|
|
|
3191
4469
|
}), /* @__PURE__ */ React.createElement(RequestDetail, {
|
|
3192
4470
|
request: activeReq,
|
|
3193
4471
|
prev: trendMode === "delta" && activeIdx >= 0 ? activeIdx > 0 ? displayRequests[activeIdx - 1] : null : void 0,
|
|
4472
|
+
/* v8 ignore next 1 -- RequestDetail renders only when
|
|
4473
|
+
displayRequests.length > 0, which forces activeReq
|
|
4474
|
+
non-null via the activeIdx fallback above. */
|
|
3194
4475
|
marker: activeReq !== null ? markerOf(activeReq) : void 0,
|
|
3195
4476
|
brief,
|
|
3196
4477
|
convOf,
|
|
@@ -3199,13 +4480,11 @@ window.__ModuleLoader__.load({
|
|
|
3199
4480
|
data,
|
|
3200
4481
|
headers,
|
|
3201
4482
|
useSession: props.useSession,
|
|
3202
|
-
|
|
4483
|
+
fetchContent,
|
|
3203
4484
|
previewSeq: hoveredSeq,
|
|
3204
4485
|
pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
|
|
3205
4486
|
hoverKey: hoverCat,
|
|
3206
4487
|
onHoverKey: setHoverCat,
|
|
3207
|
-
toolFocus,
|
|
3208
|
-
onToolFocusHandled: clearToolFocus,
|
|
3209
4488
|
nodeFocus,
|
|
3210
4489
|
onNodeFocusHandled: clearNodeFocus,
|
|
3211
4490
|
loadImage
|
|
@@ -3215,10 +4494,18 @@ window.__ModuleLoader__.load({
|
|
|
3215
4494
|
onClick: () => {
|
|
3216
4495
|
toggleKind(k);
|
|
3217
4496
|
}
|
|
3218
|
-
}, t("kind." + k))))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement(
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
4497
|
+
}, t("kind." + k))))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement(FileCard, {
|
|
4498
|
+
activity: fileActivity,
|
|
4499
|
+
scope: fileScope,
|
|
4500
|
+
onLocate: locateFileOp
|
|
4501
|
+
})), /* @__PURE__ */ React.createElement(AgentGraph, {
|
|
4502
|
+
sessionId: typeof sessionId === "string" ? sessionId : void 0,
|
|
4503
|
+
self: {
|
|
4504
|
+
head,
|
|
4505
|
+
billed: usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null,
|
|
4506
|
+
requests: requests.length
|
|
4507
|
+
}
|
|
4508
|
+
}), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
|
|
3222
4509
|
}
|
|
3223
4510
|
return function ContextView(props) {
|
|
3224
4511
|
return h(ErrorBoundary, null, h(ContextViewBody, props));
|
|
@@ -3239,7 +4526,7 @@ window.__ModuleLoader__.load({
|
|
|
3239
4526
|
}
|
|
3240
4527
|
//#endregion
|
|
3241
4528
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles.css.mjs
|
|
3242
|
-
const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i,.lc-node i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-tools{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;display:flex}.lc-tools-label,.lc-tools-more{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;padding:0;font-family:inherit;font-size:12px}.lc-tools-label:hover,.lc-tools-more:hover{text-decoration:underline}.lc-tool-chip{background:var(--dsw-alias-bg-layer-2);font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:0;border-radius:4px;padding:1px 7px;font-size:12px}.lc-tool-chip:hover{text-decoration:underline}.lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;scrollbar-gutter:stable;flex:1;min-width:0;padding-bottom:8px;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px 8px;margin-bottom:10px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary);font-size:13px}.lc-detail-time{font-variant-numeric:tabular-nums;white-space:nowrap;margin-left:auto;font-size:12px}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-metric{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 14%, transparent);border-radius:6px;padding:2px 8px;font-size:12px;font-weight:600}.lc-detail-metric-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-detail-metric-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-brief{flex-direction:column;gap:2px;margin:-4px 0 10px;display:flex}.lc-brief-row{min-width:0;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;margin-left:-6px;margin-right:-6px;padding:3px 6px;font-family:inherit;font-size:12px;line-height:1.5;transition:background-color .12s;display:flex}.lc-brief-row-link:hover{background:var(--dsw-alias-interactive-bg-hover)}button.lc-brief-row-link{cursor:pointer}.lc-brief-tag{box-sizing:border-box;text-align:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:56px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;position:relative}.lc-brief-tip{z-index:6;white-space:normal;text-align:left;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:240px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-brief-tag:hover .lc-brief-tip{opacity:1}.lc-brief-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-chip{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:220px;color:var(--dsw-alias-label-primary);border-radius:5px;align-items:center;gap:5px;padding:0 7px;font-size:12px;transition:background-color .12s;display:inline-flex;overflow:hidden}.lc-brief-chip-link{cursor:pointer}.lc-brief-chip-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-brief-more{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;position:relative;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-bar-zero{border-left:1px solid #80808073;position:absolute;top:0;bottom:0;left:50%}.lc-bar-fill-up,.lc-bar-fill-down{height:100%;display:block;position:absolute;top:0}.lc-bar-fill-up{border-radius:0 3px 3px 0;left:50%}.lc-bar-fill-down{border-radius:3px 0 0 3px;right:50%}.lc-detail-num{text-align:right;font-variant-numeric:tabular-nums;width:52px}.lc-detail-num-up{color:var(--dsw-alias-state-success-primary)}.lc-detail-num-down{color:var(--dsw-alias-state-error-primary)}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-nodes{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-node{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-node-preview{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);flex:1;overflow:hidden}.lc-node-time{color:var(--dsw-alias-label-secondary);text-align:right;min-width:54px;font-size:12px}.lc-node-tokens{color:var(--dsw-alias-label-secondary)}.lc-nodes-more{color:var(--dsw-alias-label-secondary);padding:3px 0}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
|
|
4529
|
+
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{flex-wrap:wrap;align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-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-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-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;left:0;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:repeat(auto-fit,minmax(200px,1fr));gap:4px 24px;margin-top:10px;display:grid}.lc-brief{flex-direction:column;gap:2px;min-height:76px;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-empty{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-tertiary);flex:1;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;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{flex-wrap:wrap;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);flex-wrap:wrap;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{word-break:break-all;flex:1;min-width:0}.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-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{min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-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:minmax(0,140px) minmax(0,90px) 56px minmax(0,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);overflow-wrap:anywhere;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}.lc-agents-chips{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.lc-agents-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;border-radius:999px;padding:2px 8px;font-size:11px}.lc-agents-chip-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-stage{margin:0 -4px;padding:0 4px;overflow-x:auto}.lc-agents-svg{max-width:none;margin:0 auto;display:block}.lc-agents-link{stroke-width:1.5px;stroke-opacity:.45}.lc-agents-link-live{stroke-opacity:.85}.lc-agents-flow{stroke-width:2px;stroke-linecap:round;stroke-dasharray:2 7;animation:.9s linear infinite lc-agent-flow}@keyframes lc-agent-flow{to{stroke-dashoffset:-9px}}.lc-agent-node{outline:none}.lc-agent-clickable{cursor:pointer}.lc-agent-halo{fill:#0000;transition:fill .12s}.lc-agent-hover .lc-agent-halo,.lc-agent-node:focus-visible .lc-agent-halo{fill:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-agent-self .lc-agent-halo{fill:var(--dsw-alias-bg-layer-2)}.lc-agent-running .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent);animation:1.8s ease-in-out infinite lc-agent-glow}.lc-agent-done .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 7%, transparent)}@keyframes lc-agent-glow{0%,to{opacity:1}50%{opacity:.4}}.lc-agent-track{fill:var(--dsw-alias-bg-layer-1);stroke:var(--dsw-alias-border-l1);stroke-width:1.5px}.lc-agent-seg{fill:none;stroke-width:9px;transition:stroke-dasharray .3s,stroke-dashoffset .3s}.lc-agent-free{stroke:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agent-pct{fill:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;pointer-events:none;font-size:11px;font-weight:600}.lc-agent-caption{text-align:center;pointer-events:none;width:100%;line-height:1.25}.lc-agent-label{color:var(--dsw-alias-label-primary);overflow-wrap:break-word;-webkit-line-clamp:3;-webkit-box-orient:vertical;font-size:11px;display:-webkit-box;overflow:hidden}.lc-agent-tokens{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:10px}.lc-agents-solo{padding:4px 0 10px}.lc-agents-inspector{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;flex-wrap:wrap;align-items:baseline;gap:4px 8px;min-width:0;margin-top:10px;padding:7px 10px;font-size:12px;display:flex}.lc-agents-inspector-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}.lc-agents-badge{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 6px;font-size:10px}.lc-agent-self-badge{vertical-align:1px;margin-left:5px}.lc-agents-badge-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-inspector-stats{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.lc-agents-inspector-open{color:var(--dsw-alias-brand-primary,#3b82f6);flex:none;margin-left:auto}.lc-agents-legend{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:4px 14px;margin-top:8px;font-size:11px;display:flex}.lc-agents-legend-item{align-items:center;gap:5px;display:inline-flex}.lc-agents-legend-item i{border-radius:2px;width:8px;height:8px}.lc-agents-legend-free{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agents-legend-edge{border-top:2px dashed var(--dsw-alias-label-secondary);border-radius:0!important;width:14px!important;height:0!important}";
|
|
3243
4530
|
const tagId = "dsh-context/styles.css";
|
|
3244
4531
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
3245
4532
|
const tag = document.createElement("style");
|
|
@@ -3284,19 +4571,6 @@ window.__ModuleLoader__.load({
|
|
|
3284
4571
|
const kit = makeViewKit(t);
|
|
3285
4572
|
const settings = createContextSettings();
|
|
3286
4573
|
const ContextView = makeContextView(ctx, kit, settings);
|
|
3287
|
-
ctx.effect(() => {
|
|
3288
|
-
const noop = () => {};
|
|
3289
|
-
const sessions = ctx.get("sessions");
|
|
3290
|
-
if (sessions === void 0 || typeof sessions.provide !== "function") return noop;
|
|
3291
|
-
try {
|
|
3292
|
-
return sessions.provide({
|
|
3293
|
-
props: ["loadOlderHistory"],
|
|
3294
|
-
resolve: (binding) => ({ props: { loadOlderHistory: () => binding.session.loadOlder() } })
|
|
3295
|
-
});
|
|
3296
|
-
} catch {
|
|
3297
|
-
return noop;
|
|
3298
|
-
}
|
|
3299
|
-
}, "dsh-context: loadOlderHistory prop");
|
|
3300
4574
|
ctx.slots.inject("conversation.view", () => {
|
|
3301
4575
|
return ctx.slots.register({
|
|
3302
4576
|
name: "conversation.view",
|
|
@@ -3314,7 +4588,7 @@ window.__ModuleLoader__.load({
|
|
|
3314
4588
|
id: "context-modal",
|
|
3315
4589
|
order: 10,
|
|
3316
4590
|
locale: NS,
|
|
3317
|
-
inject: (sessionId) => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
|
|
4591
|
+
inject: (sessionId = "") => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
|
|
3318
4592
|
}, (props) => h(ContextModal, props));
|
|
3319
4593
|
});
|
|
3320
4594
|
ctx.inject(["settingsScope"], (raw) => {
|