dsh-context 0.33.1 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -2
- package/lib/client.js +1407 -173
- package/lib/index.js +171 -143
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -66,10 +66,11 @@ window.__ModuleLoader__.load({
|
|
|
66
66
|
"brief.input": "输入",
|
|
67
67
|
"brief.reply": "回复",
|
|
68
68
|
"brief.more": "+{n}",
|
|
69
|
+
"brief.noInputs": "(无新增)",
|
|
69
70
|
"brief.locate": "在上下文浏览器中查看",
|
|
70
|
-
"brief.turnTip": "
|
|
71
|
-
"brief.inputTip": "
|
|
72
|
-
"brief.replyTip": "
|
|
71
|
+
"brief.turnTip": "这一轮开场时用户发送的消息;回合内的每一步都会回显同一条",
|
|
72
|
+
"brief.inputTip": "上一步回复之后、本次请求发出前新进入上下文的内容——通常是上一步所调工具的结果;每轮的第一步无新增",
|
|
73
|
+
"brief.replyTip": "这一步模型返回的内容(文本回复或工具调用);此步发起的调用,其结果会出现在下一柱的「输入」行",
|
|
73
74
|
"events.title": "上下文事件",
|
|
74
75
|
"events.empty": "暂无上下文事件(压缩、注入、模型切换会出现在这里)",
|
|
75
76
|
"events.at": "第 {t} 轮 · 第 {s} 步",
|
|
@@ -95,15 +96,57 @@ window.__ModuleLoader__.load({
|
|
|
95
96
|
"files.sortTip": "文件排序:按操作次数(选中分类时按该分类的次数)、最近操作时间或路径",
|
|
96
97
|
"files.files": "{n} 个文件",
|
|
97
98
|
"files.deltaTip": "按 edit/write 调用参数估算的累计行数变化:+ 新增 / − 删除",
|
|
98
|
-
"files.unresolved": "… 另有 {n} 次文件操作的路径超出保留窗口",
|
|
99
99
|
"files.empty": "该范围内没有文件读取、写入或搜索操作",
|
|
100
100
|
"files.noMatch": "没有匹配当前过滤条件的文件",
|
|
101
101
|
"files.locate": "在上下文浏览器中查看此次操作",
|
|
102
|
-
"files.
|
|
102
|
+
"files.hits": "{n} 处命中",
|
|
103
103
|
"files.errs": "{n} 次失败",
|
|
104
104
|
"files.form.image": "图片",
|
|
105
105
|
"files.form.dir": "目录",
|
|
106
106
|
"files.form.text": "文件",
|
|
107
|
+
"files.glyph.root": "工作区根",
|
|
108
|
+
"files.glyph.tests": "测试",
|
|
109
|
+
"files.glyph.docs": "文档目录",
|
|
110
|
+
"files.glyph.deps": "依赖",
|
|
111
|
+
"files.glyph.build": "构建产物",
|
|
112
|
+
"files.glyph.scripts": "脚本",
|
|
113
|
+
"files.glyph.config": "配置",
|
|
114
|
+
"files.glyph.assets": "静态资源",
|
|
115
|
+
"files.glyph.hidden": "元数据目录",
|
|
116
|
+
"files.glyph.lock": "锁文件",
|
|
117
|
+
"files.glyph.docker": "Docker",
|
|
118
|
+
"files.glyph.ignore": "忽略规则",
|
|
119
|
+
"files.glyph.license": "许可证",
|
|
120
|
+
"files.glyph.python": "Python",
|
|
121
|
+
"files.glyph.notebook": "Notebook",
|
|
122
|
+
"files.glyph.shell": "Shell",
|
|
123
|
+
"files.glyph.web": "网页",
|
|
124
|
+
"files.glyph.style": "样式",
|
|
125
|
+
"files.glyph.database": "数据库",
|
|
126
|
+
"files.glyph.code": "代码",
|
|
127
|
+
"files.glyph.data": "数据清单",
|
|
128
|
+
"files.glyph.markdown": "标记文档",
|
|
129
|
+
"files.glyph.log": "日志",
|
|
130
|
+
"files.glyph.sheet": "表格",
|
|
131
|
+
"files.glyph.document": "文档",
|
|
132
|
+
"files.glyph.archive": "压缩包",
|
|
133
|
+
"files.glyph.font": "字体",
|
|
134
|
+
"files.glyph.media": "媒体",
|
|
135
|
+
"agents.title": "Agent 网络",
|
|
136
|
+
"agents.sub": "当前 Agent 与子 Agent 的实时上下文 · 点击节点跳转会话",
|
|
137
|
+
"agents.chip.count": "{n} 个 Agent",
|
|
138
|
+
"agents.chip.running": "{n} 个运行中",
|
|
139
|
+
"agents.chip.tokens": "上下文合计 {n}",
|
|
140
|
+
"agents.more": "另有 {n} 个未显示",
|
|
141
|
+
"agents.solo": "还没有子 Agent — 当 Agent 通过 subagent 工具委派任务时,协作网络会在这里展开",
|
|
142
|
+
"agents.self": "当前",
|
|
143
|
+
"agents.running": "运行中",
|
|
144
|
+
"agents.legend.free": "空闲窗口",
|
|
145
|
+
"agents.oneshot": "一次性",
|
|
146
|
+
"agents.continuable": "多轮",
|
|
147
|
+
"agents.requests": "{n} 次请求",
|
|
148
|
+
"agents.billed": "累计计费 {n}",
|
|
149
|
+
"agents.open": "点击打开会话 →",
|
|
107
150
|
"loading": "正在读取会话日志…",
|
|
108
151
|
"error": "上下文数据读取失败:",
|
|
109
152
|
"error.retry": "重试",
|
|
@@ -242,10 +285,11 @@ window.__ModuleLoader__.load({
|
|
|
242
285
|
"brief.input": "In",
|
|
243
286
|
"brief.reply": "Response",
|
|
244
287
|
"brief.more": "+{n}",
|
|
288
|
+
"brief.noInputs": "(nothing new)",
|
|
245
289
|
"brief.locate": "Reveal in Context Browser",
|
|
246
|
-
"brief.turnTip": "The user message that opened this turn",
|
|
247
|
-
"brief.inputTip": "What
|
|
248
|
-
"brief.replyTip": "What the model returned on this step (a text reply or tool calls)",
|
|
290
|
+
"brief.turnTip": "The user message that opened this turn — every step of the turn recalls it",
|
|
291
|
+
"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",
|
|
292
|
+
"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",
|
|
249
293
|
"events.title": "Context Events",
|
|
250
294
|
"events.empty": "No context events yet (compaction, injections, model switches appear here)",
|
|
251
295
|
"events.at": "Turn {t} · Step {s}",
|
|
@@ -271,15 +315,57 @@ window.__ModuleLoader__.load({
|
|
|
271
315
|
"files.sortTip": "Sort files by operation counts (of the selected kind when a kind chip is active), most recent activity, or path",
|
|
272
316
|
"files.files": "{n} files",
|
|
273
317
|
"files.deltaTip": "Cumulative line change estimated from edit/write call arguments: + added / − removed",
|
|
274
|
-
"files.unresolved": "… {n} more file ops have paths outside the retained window",
|
|
275
318
|
"files.empty": "No file reads, writes, or searches in this range",
|
|
276
319
|
"files.noMatch": "No files match the current filters",
|
|
277
320
|
"files.locate": "Reveal this operation in the Context Browser",
|
|
278
|
-
"files.
|
|
321
|
+
"files.hits": "{n} hits",
|
|
279
322
|
"files.errs": "{n} failed",
|
|
280
323
|
"files.form.image": "Image",
|
|
281
324
|
"files.form.dir": "Directory",
|
|
282
325
|
"files.form.text": "File",
|
|
326
|
+
"files.glyph.root": "Workspace root",
|
|
327
|
+
"files.glyph.tests": "Tests",
|
|
328
|
+
"files.glyph.docs": "Docs",
|
|
329
|
+
"files.glyph.deps": "Dependencies",
|
|
330
|
+
"files.glyph.build": "Build output",
|
|
331
|
+
"files.glyph.scripts": "Scripts",
|
|
332
|
+
"files.glyph.config": "Config",
|
|
333
|
+
"files.glyph.assets": "Assets",
|
|
334
|
+
"files.glyph.hidden": "Metadata directory",
|
|
335
|
+
"files.glyph.lock": "Lockfile",
|
|
336
|
+
"files.glyph.docker": "Docker",
|
|
337
|
+
"files.glyph.ignore": "Ignore rules",
|
|
338
|
+
"files.glyph.license": "License",
|
|
339
|
+
"files.glyph.python": "Python",
|
|
340
|
+
"files.glyph.notebook": "Notebook",
|
|
341
|
+
"files.glyph.shell": "Shell",
|
|
342
|
+
"files.glyph.web": "Web page",
|
|
343
|
+
"files.glyph.style": "Styles",
|
|
344
|
+
"files.glyph.database": "Database",
|
|
345
|
+
"files.glyph.code": "Code",
|
|
346
|
+
"files.glyph.data": "Data / manifest",
|
|
347
|
+
"files.glyph.markdown": "Markup document",
|
|
348
|
+
"files.glyph.log": "Log",
|
|
349
|
+
"files.glyph.sheet": "Spreadsheet",
|
|
350
|
+
"files.glyph.document": "Document",
|
|
351
|
+
"files.glyph.archive": "Archive",
|
|
352
|
+
"files.glyph.font": "Font",
|
|
353
|
+
"files.glyph.media": "Media",
|
|
354
|
+
"agents.title": "Agent Network",
|
|
355
|
+
"agents.sub": "Live context of this agent and its subagents · click a node to open its session",
|
|
356
|
+
"agents.chip.count": "{n} agents",
|
|
357
|
+
"agents.chip.running": "{n} running",
|
|
358
|
+
"agents.chip.tokens": "{n} tokens in context",
|
|
359
|
+
"agents.more": "{n} more not shown",
|
|
360
|
+
"agents.solo": "No subagents yet — when the agent delegates work through the subagent tool, the network unfolds here",
|
|
361
|
+
"agents.self": "current",
|
|
362
|
+
"agents.running": "running",
|
|
363
|
+
"agents.legend.free": "free window",
|
|
364
|
+
"agents.oneshot": "one-shot",
|
|
365
|
+
"agents.continuable": "continuable",
|
|
366
|
+
"agents.requests": "{n} requests",
|
|
367
|
+
"agents.billed": "{n} billed",
|
|
368
|
+
"agents.open": "click to open →",
|
|
283
369
|
"loading": "Reading the session log…",
|
|
284
370
|
"error": "Failed to read context data: ",
|
|
285
371
|
"error.retry": "Retry",
|
|
@@ -578,7 +664,7 @@ window.__ModuleLoader__.load({
|
|
|
578
664
|
* every field read below must re-prove itself (the no-white-screen
|
|
579
665
|
* guarantee), so no field may borrow the wire type before its check.
|
|
580
666
|
*/
|
|
581
|
-
function asRecord(value) {
|
|
667
|
+
function asRecord$1(value) {
|
|
582
668
|
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
583
669
|
return value;
|
|
584
670
|
}
|
|
@@ -607,7 +693,7 @@ window.__ModuleLoader__.load({
|
|
|
607
693
|
* during render and unmounting the conversation view.
|
|
608
694
|
*/
|
|
609
695
|
function timelineOf(value) {
|
|
610
|
-
const data = asRecord(value);
|
|
696
|
+
const data = asRecord$1(value);
|
|
611
697
|
if (data === null) return null;
|
|
612
698
|
const current = data.current;
|
|
613
699
|
if (current !== null && typeof current === "object" && [
|
|
@@ -655,7 +741,7 @@ window.__ModuleLoader__.load({
|
|
|
655
741
|
* back to their derived anchor, so the UI degrades gracefully.
|
|
656
742
|
*/
|
|
657
743
|
function contextPressureOf(value) {
|
|
658
|
-
return asRecord(value);
|
|
744
|
+
return asRecord$1(value);
|
|
659
745
|
}
|
|
660
746
|
/**
|
|
661
747
|
* Narrow a delivered projection value to the official token-meter
|
|
@@ -665,7 +751,7 @@ window.__ModuleLoader__.load({
|
|
|
665
751
|
* own sums instead of mixing sources.
|
|
666
752
|
*/
|
|
667
753
|
function contextBreakdownOf(value) {
|
|
668
|
-
const data = asRecord(value);
|
|
754
|
+
const data = asRecord$1(value);
|
|
669
755
|
if (data === null) return null;
|
|
670
756
|
const { systemTokens, toolsTokens, messageTokens } = data;
|
|
671
757
|
if (typeof systemTokens !== "number" || !Number.isFinite(systemTokens)) return null;
|
|
@@ -684,7 +770,7 @@ window.__ModuleLoader__.load({
|
|
|
684
770
|
* usage yet) — callers drop the cache-hit cell to a dash.
|
|
685
771
|
*/
|
|
686
772
|
function tokenUsageOf(value) {
|
|
687
|
-
return asRecord(value);
|
|
773
|
+
return asRecord$1(value);
|
|
688
774
|
}
|
|
689
775
|
/**
|
|
690
776
|
* Narrow a delivered projection value to the plugin's `contextHeaders`
|
|
@@ -698,7 +784,7 @@ window.__ModuleLoader__.load({
|
|
|
698
784
|
* and the card falls back to its tokens-only note.
|
|
699
785
|
*/
|
|
700
786
|
function headersOf(value) {
|
|
701
|
-
const headers = asRecord(value);
|
|
787
|
+
const headers = asRecord$1(value);
|
|
702
788
|
if (headers === null || !Array.isArray(headers.headers)) return null;
|
|
703
789
|
for (const h of headers.headers) {
|
|
704
790
|
if (h === null || typeof h !== "object") return null;
|
|
@@ -2426,6 +2512,17 @@ window.__ModuleLoader__.load({
|
|
|
2426
2512
|
return KIND_BY_TOOL[tool] ?? null;
|
|
2427
2513
|
}
|
|
2428
2514
|
/**
|
|
2515
|
+
* The file purpose of one executed call. Like {@link kindOfTool} except for
|
|
2516
|
+
* the one file tool whose purpose follows its arguments: `str_replace_editor`
|
|
2517
|
+
* reads on `view` and writes on every other command (create / str_replace /
|
|
2518
|
+
* insert — an unknown command writes too; the call failed and the row keeps
|
|
2519
|
+
* its error flag).
|
|
2520
|
+
*/
|
|
2521
|
+
function kindOfCall(tool, args) {
|
|
2522
|
+
if (tool === "str_replace_editor") return args !== null && args.command === "view" ? "read" : "write";
|
|
2523
|
+
return kindOfTool(tool);
|
|
2524
|
+
}
|
|
2525
|
+
/**
|
|
2429
2526
|
* The operation's target path: the path-ish argument of read/write tools;
|
|
2430
2527
|
* for searches the narrowing `path`, else the pattern itself (a pathless
|
|
2431
2528
|
* grep/glob's target IS the pattern — the workspace-wide search text).
|
|
@@ -2455,27 +2552,27 @@ window.__ModuleLoader__.load({
|
|
|
2455
2552
|
for (let i = 0; i < s.length; i++) if (s[i] === "\n") n++;
|
|
2456
2553
|
return s.endsWith("\n") ? n : n + 1;
|
|
2457
2554
|
}
|
|
2555
|
+
/** The added/removed pair of one content-bearing argument set, or zeros. */
|
|
2556
|
+
function pairOf(added, removed) {
|
|
2557
|
+
return {
|
|
2558
|
+
added: typeof added === "string" ? linesOf(added) : 0,
|
|
2559
|
+
removed: typeof removed === "string" ? linesOf(removed) : 0
|
|
2560
|
+
};
|
|
2561
|
+
}
|
|
2458
2562
|
/**
|
|
2459
2563
|
* The signed line footprint of one call: an edit removes its old string and
|
|
2460
2564
|
* adds its new one; a write adds its content (the pre-existing body, if any,
|
|
2461
|
-
* is unknowable from the arguments — the estimate stays honest about that)
|
|
2462
|
-
*
|
|
2565
|
+
* is unknowable from the arguments — the estimate stays honest about that);
|
|
2566
|
+
* `str_replace_editor` splits the same shapes across its commands. Callers
|
|
2567
|
+
* reach here only with parsed args (a null parse yields no path).
|
|
2463
2568
|
*/
|
|
2464
2569
|
function deltaOf(tool, args) {
|
|
2465
|
-
if (tool === "edit")
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
return
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
};
|
|
2472
|
-
}
|
|
2473
|
-
if (tool === "write") {
|
|
2474
|
-
const content = args.content;
|
|
2475
|
-
return {
|
|
2476
|
-
added: typeof content === "string" ? linesOf(content) : 0,
|
|
2477
|
-
removed: 0
|
|
2478
|
-
};
|
|
2570
|
+
if (tool === "edit") return pairOf(args.new_string, args.old_string);
|
|
2571
|
+
if (tool === "write") return pairOf(args.content, void 0);
|
|
2572
|
+
if (tool === "str_replace_editor") {
|
|
2573
|
+
if (args.command === "str_replace") return pairOf(args.new_str, args.old_str);
|
|
2574
|
+
if (args.command === "insert") return pairOf(args.new_str, void 0);
|
|
2575
|
+
if (args.command === "create") return pairOf(args.file_text, void 0);
|
|
2479
2576
|
}
|
|
2480
2577
|
return {
|
|
2481
2578
|
added: 0,
|
|
@@ -2489,6 +2586,470 @@ window.__ModuleLoader__.load({
|
|
|
2489
2586
|
if (path.endsWith("/")) return "dir";
|
|
2490
2587
|
return "text";
|
|
2491
2588
|
}
|
|
2589
|
+
/** Directory buckets, by last path segment (lowercased, explicit plurals). Checked in order, then hidden dirs, then the plain folder. */
|
|
2590
|
+
const DIR_BUCKETS = [
|
|
2591
|
+
[
|
|
2592
|
+
[
|
|
2593
|
+
"test",
|
|
2594
|
+
"tests",
|
|
2595
|
+
"__tests__",
|
|
2596
|
+
"spec",
|
|
2597
|
+
"specs",
|
|
2598
|
+
"e2e"
|
|
2599
|
+
],
|
|
2600
|
+
"🧪",
|
|
2601
|
+
"files.glyph.tests"
|
|
2602
|
+
],
|
|
2603
|
+
[
|
|
2604
|
+
[
|
|
2605
|
+
"doc",
|
|
2606
|
+
"docs",
|
|
2607
|
+
"documentation"
|
|
2608
|
+
],
|
|
2609
|
+
"📚",
|
|
2610
|
+
"files.glyph.docs"
|
|
2611
|
+
],
|
|
2612
|
+
[
|
|
2613
|
+
[
|
|
2614
|
+
"node_modules",
|
|
2615
|
+
"vendor",
|
|
2616
|
+
"third_party",
|
|
2617
|
+
"third-party",
|
|
2618
|
+
"packages"
|
|
2619
|
+
],
|
|
2620
|
+
"📦",
|
|
2621
|
+
"files.glyph.deps"
|
|
2622
|
+
],
|
|
2623
|
+
[
|
|
2624
|
+
[
|
|
2625
|
+
"dist",
|
|
2626
|
+
"build",
|
|
2627
|
+
"out",
|
|
2628
|
+
"target",
|
|
2629
|
+
"release",
|
|
2630
|
+
"debug",
|
|
2631
|
+
"coverage",
|
|
2632
|
+
"artifacts"
|
|
2633
|
+
],
|
|
2634
|
+
"🏗️",
|
|
2635
|
+
"files.glyph.build"
|
|
2636
|
+
],
|
|
2637
|
+
[
|
|
2638
|
+
[
|
|
2639
|
+
"scripts",
|
|
2640
|
+
"tools",
|
|
2641
|
+
"bin"
|
|
2642
|
+
],
|
|
2643
|
+
"🛠️",
|
|
2644
|
+
"files.glyph.scripts"
|
|
2645
|
+
],
|
|
2646
|
+
[
|
|
2647
|
+
[
|
|
2648
|
+
"config",
|
|
2649
|
+
"configs",
|
|
2650
|
+
"settings",
|
|
2651
|
+
".config"
|
|
2652
|
+
],
|
|
2653
|
+
"⚙️",
|
|
2654
|
+
"files.glyph.config"
|
|
2655
|
+
],
|
|
2656
|
+
[
|
|
2657
|
+
[
|
|
2658
|
+
"assets",
|
|
2659
|
+
"static",
|
|
2660
|
+
"public",
|
|
2661
|
+
"images",
|
|
2662
|
+
"fonts",
|
|
2663
|
+
"icons",
|
|
2664
|
+
"media"
|
|
2665
|
+
],
|
|
2666
|
+
"🎨",
|
|
2667
|
+
"files.glyph.assets"
|
|
2668
|
+
]
|
|
2669
|
+
];
|
|
2670
|
+
/** Lockfile base names that do not end in `.lock`. */
|
|
2671
|
+
const LOCK_NAMES = [
|
|
2672
|
+
"package-lock.json",
|
|
2673
|
+
"pnpm-lock.yaml",
|
|
2674
|
+
"npm-shrinkwrap.json"
|
|
2675
|
+
];
|
|
2676
|
+
const MAKE_NAMES = [
|
|
2677
|
+
"makefile",
|
|
2678
|
+
"justfile",
|
|
2679
|
+
"cmakelists.txt"
|
|
2680
|
+
];
|
|
2681
|
+
/** A test file by name: a standalone or delimited `test`, an inline `.test.`/`.spec.`, or their underscore forms. */
|
|
2682
|
+
const TEST_NAME = /(^|[^a-z0-9])test([^a-z0-9]|$)|\.(test|spec)\./;
|
|
2683
|
+
/** Extension buckets, most specific first; the last row catches every programming language. */
|
|
2684
|
+
const EXT_BUCKETS = [
|
|
2685
|
+
[
|
|
2686
|
+
[
|
|
2687
|
+
"py",
|
|
2688
|
+
"pyi",
|
|
2689
|
+
"pyw"
|
|
2690
|
+
],
|
|
2691
|
+
"🐍",
|
|
2692
|
+
"files.glyph.python"
|
|
2693
|
+
],
|
|
2694
|
+
[
|
|
2695
|
+
["ipynb"],
|
|
2696
|
+
"📓",
|
|
2697
|
+
"files.glyph.notebook"
|
|
2698
|
+
],
|
|
2699
|
+
[
|
|
2700
|
+
[
|
|
2701
|
+
"sh",
|
|
2702
|
+
"bash",
|
|
2703
|
+
"zsh",
|
|
2704
|
+
"fish",
|
|
2705
|
+
"ps1"
|
|
2706
|
+
],
|
|
2707
|
+
"🐚",
|
|
2708
|
+
"files.glyph.shell"
|
|
2709
|
+
],
|
|
2710
|
+
[
|
|
2711
|
+
[
|
|
2712
|
+
"html",
|
|
2713
|
+
"htm",
|
|
2714
|
+
"xhtml"
|
|
2715
|
+
],
|
|
2716
|
+
"🌐",
|
|
2717
|
+
"files.glyph.web"
|
|
2718
|
+
],
|
|
2719
|
+
[
|
|
2720
|
+
[
|
|
2721
|
+
"css",
|
|
2722
|
+
"scss",
|
|
2723
|
+
"sass",
|
|
2724
|
+
"less",
|
|
2725
|
+
"styl"
|
|
2726
|
+
],
|
|
2727
|
+
"🎨",
|
|
2728
|
+
"files.glyph.style"
|
|
2729
|
+
],
|
|
2730
|
+
[
|
|
2731
|
+
["sql"],
|
|
2732
|
+
"🗃️",
|
|
2733
|
+
"files.glyph.database"
|
|
2734
|
+
],
|
|
2735
|
+
[
|
|
2736
|
+
[
|
|
2737
|
+
"yaml",
|
|
2738
|
+
"yml",
|
|
2739
|
+
"toml",
|
|
2740
|
+
"ini",
|
|
2741
|
+
"conf",
|
|
2742
|
+
"cfg",
|
|
2743
|
+
"properties",
|
|
2744
|
+
"env"
|
|
2745
|
+
],
|
|
2746
|
+
"⚙️",
|
|
2747
|
+
"files.glyph.config"
|
|
2748
|
+
],
|
|
2749
|
+
[
|
|
2750
|
+
[
|
|
2751
|
+
"json",
|
|
2752
|
+
"jsonc",
|
|
2753
|
+
"json5",
|
|
2754
|
+
"jsonl",
|
|
2755
|
+
"ndjson",
|
|
2756
|
+
"xml"
|
|
2757
|
+
],
|
|
2758
|
+
"🧾",
|
|
2759
|
+
"files.glyph.data"
|
|
2760
|
+
],
|
|
2761
|
+
[
|
|
2762
|
+
[
|
|
2763
|
+
"md",
|
|
2764
|
+
"mdx",
|
|
2765
|
+
"markdown",
|
|
2766
|
+
"rst",
|
|
2767
|
+
"adoc",
|
|
2768
|
+
"org"
|
|
2769
|
+
],
|
|
2770
|
+
"📝",
|
|
2771
|
+
"files.glyph.markdown"
|
|
2772
|
+
],
|
|
2773
|
+
[
|
|
2774
|
+
["log"],
|
|
2775
|
+
"📜",
|
|
2776
|
+
"files.glyph.log"
|
|
2777
|
+
],
|
|
2778
|
+
[
|
|
2779
|
+
[
|
|
2780
|
+
"csv",
|
|
2781
|
+
"tsv",
|
|
2782
|
+
"xls",
|
|
2783
|
+
"xlsx",
|
|
2784
|
+
"ods"
|
|
2785
|
+
],
|
|
2786
|
+
"📊",
|
|
2787
|
+
"files.glyph.sheet"
|
|
2788
|
+
],
|
|
2789
|
+
[
|
|
2790
|
+
[
|
|
2791
|
+
"pdf",
|
|
2792
|
+
"doc",
|
|
2793
|
+
"docx",
|
|
2794
|
+
"odt",
|
|
2795
|
+
"rtf"
|
|
2796
|
+
],
|
|
2797
|
+
"📕",
|
|
2798
|
+
"files.glyph.document"
|
|
2799
|
+
],
|
|
2800
|
+
[
|
|
2801
|
+
[
|
|
2802
|
+
"zip",
|
|
2803
|
+
"gz",
|
|
2804
|
+
"tgz",
|
|
2805
|
+
"tar",
|
|
2806
|
+
"bz2",
|
|
2807
|
+
"xz",
|
|
2808
|
+
"7z",
|
|
2809
|
+
"rar",
|
|
2810
|
+
"jar"
|
|
2811
|
+
],
|
|
2812
|
+
"🗜️",
|
|
2813
|
+
"files.glyph.archive"
|
|
2814
|
+
],
|
|
2815
|
+
[
|
|
2816
|
+
[
|
|
2817
|
+
"ttf",
|
|
2818
|
+
"otf",
|
|
2819
|
+
"woff",
|
|
2820
|
+
"woff2",
|
|
2821
|
+
"eot"
|
|
2822
|
+
],
|
|
2823
|
+
"🔤",
|
|
2824
|
+
"files.glyph.font"
|
|
2825
|
+
],
|
|
2826
|
+
[
|
|
2827
|
+
[
|
|
2828
|
+
"mp4",
|
|
2829
|
+
"mov",
|
|
2830
|
+
"mkv",
|
|
2831
|
+
"webm",
|
|
2832
|
+
"mp3",
|
|
2833
|
+
"wav",
|
|
2834
|
+
"flac",
|
|
2835
|
+
"ogg"
|
|
2836
|
+
],
|
|
2837
|
+
"🎬",
|
|
2838
|
+
"files.glyph.media"
|
|
2839
|
+
],
|
|
2840
|
+
[
|
|
2841
|
+
[
|
|
2842
|
+
"ts",
|
|
2843
|
+
"tsx",
|
|
2844
|
+
"js",
|
|
2845
|
+
"jsx",
|
|
2846
|
+
"mjs",
|
|
2847
|
+
"cjs",
|
|
2848
|
+
"go",
|
|
2849
|
+
"rs",
|
|
2850
|
+
"java",
|
|
2851
|
+
"kt",
|
|
2852
|
+
"kts",
|
|
2853
|
+
"swift",
|
|
2854
|
+
"rb",
|
|
2855
|
+
"php",
|
|
2856
|
+
"c",
|
|
2857
|
+
"h",
|
|
2858
|
+
"cpp",
|
|
2859
|
+
"cc",
|
|
2860
|
+
"cxx",
|
|
2861
|
+
"hpp",
|
|
2862
|
+
"cs",
|
|
2863
|
+
"scala",
|
|
2864
|
+
"lua",
|
|
2865
|
+
"dart",
|
|
2866
|
+
"vue",
|
|
2867
|
+
"svelte"
|
|
2868
|
+
],
|
|
2869
|
+
"🧩",
|
|
2870
|
+
"files.glyph.code"
|
|
2871
|
+
]
|
|
2872
|
+
];
|
|
2873
|
+
function dirGlyph(base) {
|
|
2874
|
+
if (base === "" || base === ".") return {
|
|
2875
|
+
glyph: "🏠",
|
|
2876
|
+
tip: "files.glyph.root"
|
|
2877
|
+
};
|
|
2878
|
+
const name = base.toLowerCase();
|
|
2879
|
+
for (const [names, glyph, tip] of DIR_BUCKETS) if (names.includes(name)) return {
|
|
2880
|
+
glyph,
|
|
2881
|
+
tip
|
|
2882
|
+
};
|
|
2883
|
+
if (name.startsWith(".")) return {
|
|
2884
|
+
glyph: "🗄️",
|
|
2885
|
+
tip: "files.glyph.hidden"
|
|
2886
|
+
};
|
|
2887
|
+
return {
|
|
2888
|
+
glyph: "📁",
|
|
2889
|
+
tip: "files.form.dir"
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
function fileGlyph(base) {
|
|
2893
|
+
const name = base.toLowerCase();
|
|
2894
|
+
const dot = name.lastIndexOf(".");
|
|
2895
|
+
const ext = dot > 0 ? name.slice(dot + 1) : "";
|
|
2896
|
+
if (name.endsWith(".lock") || LOCK_NAMES.includes(name)) return {
|
|
2897
|
+
glyph: "🔒",
|
|
2898
|
+
tip: "files.glyph.lock"
|
|
2899
|
+
};
|
|
2900
|
+
if (TEST_NAME.test(name)) return {
|
|
2901
|
+
glyph: "🧪",
|
|
2902
|
+
tip: "files.glyph.tests"
|
|
2903
|
+
};
|
|
2904
|
+
if (name === "dockerfile" || name.startsWith("dockerfile.")) return {
|
|
2905
|
+
glyph: "🐳",
|
|
2906
|
+
tip: "files.glyph.docker"
|
|
2907
|
+
};
|
|
2908
|
+
if (MAKE_NAMES.includes(name)) return {
|
|
2909
|
+
glyph: "🛠️",
|
|
2910
|
+
tip: "files.glyph.scripts"
|
|
2911
|
+
};
|
|
2912
|
+
if (name === ".gitignore" || name === ".gitattributes") return {
|
|
2913
|
+
glyph: "🚫",
|
|
2914
|
+
tip: "files.glyph.ignore"
|
|
2915
|
+
};
|
|
2916
|
+
if (name === "license" || name.startsWith("license.") || name === "copying") return {
|
|
2917
|
+
glyph: "⚖️",
|
|
2918
|
+
tip: "files.glyph.license"
|
|
2919
|
+
};
|
|
2920
|
+
if (name === ".env" || name.startsWith(".env.")) return {
|
|
2921
|
+
glyph: "⚙️",
|
|
2922
|
+
tip: "files.glyph.config"
|
|
2923
|
+
};
|
|
2924
|
+
if (ext !== "") {
|
|
2925
|
+
for (const [exts, glyph, tip] of EXT_BUCKETS) if (exts.includes(ext)) return {
|
|
2926
|
+
glyph,
|
|
2927
|
+
tip
|
|
2928
|
+
};
|
|
2929
|
+
}
|
|
2930
|
+
return {
|
|
2931
|
+
glyph: "📄",
|
|
2932
|
+
tip: "files.form.text"
|
|
2933
|
+
};
|
|
2934
|
+
}
|
|
2935
|
+
/** The row icon for one file entry: form first (image/dir), then the file-name tables. */
|
|
2936
|
+
function glyphOf(path, form) {
|
|
2937
|
+
if (form === "image") return {
|
|
2938
|
+
glyph: "🖼",
|
|
2939
|
+
tip: "files.form.image"
|
|
2940
|
+
};
|
|
2941
|
+
const trimmed = path.endsWith("/") ? path.slice(0, -1) : path;
|
|
2942
|
+
const base = trimmed.slice(trimmed.lastIndexOf("/") + 1);
|
|
2943
|
+
return form === "dir" ? dirGlyph(base) : fileGlyph(base);
|
|
2944
|
+
}
|
|
2945
|
+
/**
|
|
2946
|
+
* A search op's detail: the pattern, with the include filter appended when
|
|
2947
|
+
* one narrowed the call. A patternless (malformed) search has no detail.
|
|
2948
|
+
*/
|
|
2949
|
+
function searchDetailOf(args) {
|
|
2950
|
+
const pattern = args?.pattern;
|
|
2951
|
+
if (typeof pattern !== "string" || pattern === "") return void 0;
|
|
2952
|
+
const include = args?.include;
|
|
2953
|
+
return typeof include === "string" && include !== "" ? `${pattern} (${include})` : pattern;
|
|
2954
|
+
}
|
|
2955
|
+
/**
|
|
2956
|
+
* The files a search demonstrably reached, read off the result's bounded
|
|
2957
|
+
* presentation meta (grep groups matched lines by file; glob lists paths).
|
|
2958
|
+
* Only the COMPLETE list attributes: a capped search (`truncated`) names a
|
|
2959
|
+
* partial file set, and a malformed meta names none — both fall back to the
|
|
2960
|
+
* call's own target. Each entry carries the reported match count.
|
|
2961
|
+
*/
|
|
2962
|
+
function searchFilesOf(meta) {
|
|
2963
|
+
if (meta === null || typeof meta !== "object") return null;
|
|
2964
|
+
const m = meta;
|
|
2965
|
+
if (m.truncated !== false) return null;
|
|
2966
|
+
const files = [];
|
|
2967
|
+
if (m.shape === "matches" && Array.isArray(m.files)) for (const f of m.files) {
|
|
2968
|
+
if (f === null || typeof f !== "object") continue;
|
|
2969
|
+
const group = f;
|
|
2970
|
+
if (typeof group.path === "string" && group.path !== "" && Array.isArray(group.matches)) files.push({
|
|
2971
|
+
path: group.path,
|
|
2972
|
+
hits: group.matches.length
|
|
2973
|
+
});
|
|
2974
|
+
}
|
|
2975
|
+
else if (m.shape === "paths" && Array.isArray(m.paths)) {
|
|
2976
|
+
for (const p of m.paths) if (typeof p === "string" && p !== "") files.push({
|
|
2977
|
+
path: p,
|
|
2978
|
+
hits: 0
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
return files.length > 0 ? files : null;
|
|
2982
|
+
}
|
|
2983
|
+
/**
|
|
2984
|
+
* Narrow one block of a conversation node's `subCalls` tree to a settled
|
|
2985
|
+
* nested call, or null. The join is defensive — a running call has no result
|
|
2986
|
+
* kind yet, and any malformed block is dropped, never thrown. (Null and
|
|
2987
|
+
* non-object blocks never reach here: the folding loop pre-filters them.)
|
|
2988
|
+
*/
|
|
2989
|
+
function subCallOf(block) {
|
|
2990
|
+
const b = block;
|
|
2991
|
+
if (b.kind !== "tool-result") return null;
|
|
2992
|
+
if (b.call === null || typeof b.call !== "object") return null;
|
|
2993
|
+
const call = b.call;
|
|
2994
|
+
if (typeof call.name !== "string" || typeof call.argsRaw !== "string") return null;
|
|
2995
|
+
if (typeof b.seq !== "number" || !Number.isFinite(b.seq)) return null;
|
|
2996
|
+
return {
|
|
2997
|
+
name: call.name,
|
|
2998
|
+
argsRaw: call.argsRaw,
|
|
2999
|
+
seq: b.seq,
|
|
3000
|
+
...typeof b.time === "number" ? { time: b.time } : {},
|
|
3001
|
+
...b.isError === true ? { err: true } : { err: false },
|
|
3002
|
+
...Array.isArray(b.subCalls) ? { subCalls: b.subCalls } : {}
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
/** The run_code call's model-authored description — the nested ops' "why". */
|
|
3006
|
+
function programOf(conv) {
|
|
3007
|
+
const description = parseCallArgs(conv?.call?.argsRaw)?.description;
|
|
3008
|
+
return typeof description === "string" && description !== "" ? description : void 0;
|
|
3009
|
+
}
|
|
3010
|
+
/**
|
|
3011
|
+
* Depth guard for nested Code-Mode trees. The SDK bindings exclude `run_code`
|
|
3012
|
+
* itself, so a real tree is one level deep; the cap only bounds defensive
|
|
3013
|
+
* re-entry over a malformed join.
|
|
3014
|
+
*/
|
|
3015
|
+
const SUBCALL_MAX_DEPTH = 8;
|
|
3016
|
+
/**
|
|
3017
|
+
* Fold one nested Code-Mode tree: every settled sub-dispatch whose arguments
|
|
3018
|
+
* resolve to a file target books one op, attributed to the nested tool and
|
|
3019
|
+
* located on the parent run_code result. `seen` holds the already-visited
|
|
3020
|
+
* blocks, so a malformed (cyclic) join cannot loop.
|
|
3021
|
+
*/
|
|
3022
|
+
function foldSubCalls(blocks, parent, program, add, seen, depth) {
|
|
3023
|
+
if (depth > SUBCALL_MAX_DEPTH) return;
|
|
3024
|
+
for (const block of blocks) {
|
|
3025
|
+
if (block === null || typeof block !== "object" || seen.has(block)) continue;
|
|
3026
|
+
seen.add(block);
|
|
3027
|
+
const sub = subCallOf(block);
|
|
3028
|
+
if (sub !== null) {
|
|
3029
|
+
const args = parseCallArgs(sub.argsRaw);
|
|
3030
|
+
const kind = args !== null ? kindOfCall(sub.name, args) : null;
|
|
3031
|
+
const path = kind !== null ? pathOfArgs(sub.name, args) : null;
|
|
3032
|
+
if (args !== null && kind !== null && path !== null) {
|
|
3033
|
+
const { added, removed } = deltaOf(sub.name, args);
|
|
3034
|
+
const detail = kind === "search" && typeof args.path === "string" && args.path !== "" ? searchDetailOf(args) : void 0;
|
|
3035
|
+
add({
|
|
3036
|
+
seq: sub.seq,
|
|
3037
|
+
...parent.gone !== void 0 ? { gone: parent.gone } : {},
|
|
3038
|
+
kind,
|
|
3039
|
+
tool: sub.name,
|
|
3040
|
+
...sub.time !== void 0 ? { time: sub.time } : {},
|
|
3041
|
+
err: sub.err,
|
|
3042
|
+
added,
|
|
3043
|
+
removed,
|
|
3044
|
+
...detail !== void 0 ? { detail } : {},
|
|
3045
|
+
parent: parent.seq,
|
|
3046
|
+
...program !== void 0 ? { program } : {}
|
|
3047
|
+
}, path);
|
|
3048
|
+
}
|
|
3049
|
+
if (sub.subCalls !== void 0) foldSubCalls(sub.subCalls, parent, program, add, seen, depth + 1);
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
2492
3053
|
/** Fold every served tool-result node into per-file activity. */
|
|
2493
3054
|
function activityOf(nodes, convOf, before) {
|
|
2494
3055
|
const totals = {
|
|
@@ -2512,39 +3073,14 @@ window.__ModuleLoader__.load({
|
|
|
2512
3073
|
removed: 0
|
|
2513
3074
|
};
|
|
2514
3075
|
const byPath = /* @__PURE__ */ new Map();
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
if (before !== null && n.seq >= before) continue;
|
|
2519
|
-
const kind = kindOfTool(n.tool);
|
|
2520
|
-
if (kind === null) continue;
|
|
2521
|
-
const tool = n.tool;
|
|
2522
|
-
totals[kind].ops++;
|
|
2523
|
-
const conv = convOf(n.seq);
|
|
2524
|
-
const args = parseCallArgs(conv?.call?.argsRaw);
|
|
2525
|
-
const path = pathOfArgs(tool, args);
|
|
2526
|
-
if (args === null || path === null) {
|
|
2527
|
-
unresolved++;
|
|
2528
|
-
continue;
|
|
2529
|
-
}
|
|
2530
|
-
const { added, removed } = deltaOf(tool, args);
|
|
2531
|
-
const detail = kind === "search" && typeof args.path === "string" && args.path !== "" && typeof args.pattern === "string" && args.pattern !== "" ? args.pattern : void 0;
|
|
2532
|
-
const op = {
|
|
2533
|
-
seq: n.seq,
|
|
2534
|
-
...n.gone !== void 0 ? { gone: n.gone } : {},
|
|
2535
|
-
kind,
|
|
2536
|
-
tool,
|
|
2537
|
-
...n.time !== void 0 ? { time: n.time } : {},
|
|
2538
|
-
err: n.err === true || conv.isError === true,
|
|
2539
|
-
added,
|
|
2540
|
-
removed,
|
|
2541
|
-
...detail !== void 0 ? { detail } : {}
|
|
2542
|
-
};
|
|
3076
|
+
/** Book one executed op under its file; the totals and the entry move together. */
|
|
3077
|
+
const add = (op, path) => {
|
|
3078
|
+
totals[op.kind].ops++;
|
|
2543
3079
|
let entry = byPath.get(path);
|
|
2544
3080
|
if (entry === void 0) {
|
|
2545
3081
|
entry = {
|
|
2546
3082
|
path,
|
|
2547
|
-
form: formOf(tool, path),
|
|
3083
|
+
form: formOf(op.tool, path),
|
|
2548
3084
|
reads: 0,
|
|
2549
3085
|
writes: 0,
|
|
2550
3086
|
searches: 0,
|
|
@@ -2555,13 +3091,63 @@ window.__ModuleLoader__.load({
|
|
|
2555
3091
|
};
|
|
2556
3092
|
byPath.set(path, entry);
|
|
2557
3093
|
}
|
|
2558
|
-
if (kind === "read") entry.reads++;
|
|
2559
|
-
else if (kind === "write") entry.writes++;
|
|
3094
|
+
if (op.kind === "read") entry.reads++;
|
|
3095
|
+
else if (op.kind === "write") entry.writes++;
|
|
2560
3096
|
else entry.searches++;
|
|
2561
|
-
entry.added += added;
|
|
2562
|
-
entry.removed += removed;
|
|
3097
|
+
entry.added += op.added;
|
|
3098
|
+
entry.removed += op.removed;
|
|
2563
3099
|
if (op.err) entry.errs++;
|
|
2564
3100
|
entry.ops.push(op);
|
|
3101
|
+
};
|
|
3102
|
+
for (const n of nodes) {
|
|
3103
|
+
if (n.cat !== "tool") continue;
|
|
3104
|
+
if (before !== null && n.seq >= before) continue;
|
|
3105
|
+
const tool = n.tool;
|
|
3106
|
+
if (tool === void 0) continue;
|
|
3107
|
+
try {
|
|
3108
|
+
const conv = convOf(n.seq);
|
|
3109
|
+
const staticKind = kindOfTool(tool);
|
|
3110
|
+
const args = staticKind !== null || tool === "str_replace_editor" ? parseCallArgs(conv?.call?.argsRaw) : null;
|
|
3111
|
+
const kind = staticKind ?? kindOfCall(tool, args);
|
|
3112
|
+
if (kind !== null) {
|
|
3113
|
+
const err = n.err === true || conv?.isError === true;
|
|
3114
|
+
const files = kind === "search" ? searchFilesOf(conv?.meta) : null;
|
|
3115
|
+
if (files !== null) {
|
|
3116
|
+
const detail = searchDetailOf(args);
|
|
3117
|
+
for (const f of files) add({
|
|
3118
|
+
seq: n.seq,
|
|
3119
|
+
...n.gone !== void 0 ? { gone: n.gone } : {},
|
|
3120
|
+
kind,
|
|
3121
|
+
tool,
|
|
3122
|
+
...n.time !== void 0 ? { time: n.time } : {},
|
|
3123
|
+
err,
|
|
3124
|
+
added: 0,
|
|
3125
|
+
removed: 0,
|
|
3126
|
+
...detail !== void 0 ? { detail } : {},
|
|
3127
|
+
...f.hits > 0 ? { hits: f.hits } : {}
|
|
3128
|
+
}, f.path);
|
|
3129
|
+
} else if (args !== null) {
|
|
3130
|
+
const path = pathOfArgs(tool, args);
|
|
3131
|
+
if (path !== null) {
|
|
3132
|
+
const { added, removed } = deltaOf(tool, args);
|
|
3133
|
+
const detail = kind === "search" && typeof args.path === "string" && args.path !== "" ? searchDetailOf(args) : void 0;
|
|
3134
|
+
add({
|
|
3135
|
+
seq: n.seq,
|
|
3136
|
+
...n.gone !== void 0 ? { gone: n.gone } : {},
|
|
3137
|
+
kind,
|
|
3138
|
+
tool,
|
|
3139
|
+
...n.time !== void 0 ? { time: n.time } : {},
|
|
3140
|
+
err,
|
|
3141
|
+
added,
|
|
3142
|
+
removed,
|
|
3143
|
+
...detail !== void 0 ? { detail } : {}
|
|
3144
|
+
}, path);
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
const subCalls = conv?.subCalls;
|
|
3149
|
+
if (subCalls !== void 0 && subCalls.length > 0) foldSubCalls(subCalls, n, programOf(conv), add, /* @__PURE__ */ new Set(), 1);
|
|
3150
|
+
} catch {}
|
|
2565
3151
|
}
|
|
2566
3152
|
const entries = [...byPath.values()];
|
|
2567
3153
|
for (const e of entries) {
|
|
@@ -2579,8 +3165,7 @@ window.__ModuleLoader__.load({
|
|
|
2579
3165
|
entries.sort((a, b) => b.ops[0].seq - a.ops[0].seq);
|
|
2580
3166
|
return {
|
|
2581
3167
|
entries,
|
|
2582
|
-
totals
|
|
2583
|
-
unresolved
|
|
3168
|
+
totals
|
|
2584
3169
|
};
|
|
2585
3170
|
}
|
|
2586
3171
|
/**
|
|
@@ -2594,6 +3179,588 @@ window.__ModuleLoader__.load({
|
|
|
2594
3179
|
for (const r of requests) if (r.seq > seq && (gone === void 0 || gone > r.seq)) return r.seq;
|
|
2595
3180
|
return gone === void 0 ? "live" : null;
|
|
2596
3181
|
}
|
|
3182
|
+
const SLOT_MAX = 184;
|
|
3183
|
+
const SLOT_MIN = 112;
|
|
3184
|
+
const CAPTION_GUTTER = 8;
|
|
3185
|
+
const LEVEL_H = 154;
|
|
3186
|
+
const CELL_H = 90;
|
|
3187
|
+
const PAD_Y = 56;
|
|
3188
|
+
function asRecord(value) {
|
|
3189
|
+
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
3190
|
+
return value;
|
|
3191
|
+
}
|
|
3192
|
+
/** Narrow one list-row value; null when it is not a row at all. */
|
|
3193
|
+
function agentRowOf(value) {
|
|
3194
|
+
const rec = asRecord(value);
|
|
3195
|
+
if (rec === null) return null;
|
|
3196
|
+
const projections = asRecord(rec.projectionValues);
|
|
3197
|
+
return {
|
|
3198
|
+
...typeof rec.displayTitle === "string" ? { displayTitle: rec.displayTitle } : {},
|
|
3199
|
+
...typeof rec.title === "string" ? { title: rec.title } : {},
|
|
3200
|
+
...typeof rec.parentId === "string" ? { parentId: rec.parentId } : {},
|
|
3201
|
+
...typeof rec.origin === "string" ? { origin: rec.origin } : {},
|
|
3202
|
+
running: rec.running === true,
|
|
3203
|
+
completed: rec.completed === true,
|
|
3204
|
+
blank: rec.blank === true,
|
|
3205
|
+
updatedAt: numOf(rec.updatedAt),
|
|
3206
|
+
...projections !== null ? { projections } : {}
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
/** Narrow the subagent identity projection value (null = not a descriptor-backed subagent). */
|
|
3210
|
+
function agentIdentityOf(value) {
|
|
3211
|
+
const rec = asRecord(value);
|
|
3212
|
+
if (rec === null) return null;
|
|
3213
|
+
if (rec.mode !== "one-shot" && rec.mode !== "continuable") return null;
|
|
3214
|
+
return {
|
|
3215
|
+
mode: rec.mode,
|
|
3216
|
+
...typeof rec.label === "string" && rec.label !== "" ? { label: rec.label } : {}
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
/** Narrow the subagent timing projection value into a single duration (null = absent/malformed). */
|
|
3220
|
+
function agentDurationOf(value) {
|
|
3221
|
+
const rec = asRecord(value);
|
|
3222
|
+
if (rec === null) return null;
|
|
3223
|
+
const settled = numOf(rec.settledMs);
|
|
3224
|
+
const active = asRecord(rec.active);
|
|
3225
|
+
const total = settled + (active !== null ? Math.max(0, numOf(active.through) - numOf(active.since)) : 0);
|
|
3226
|
+
return total > 0 ? total : null;
|
|
3227
|
+
}
|
|
3228
|
+
/**
|
|
3229
|
+
* Fold one row's projection values into render-ready stats. The composition
|
|
3230
|
+
* prefers the plugin's own `contextTimeline` (six buckets, provider-anchored
|
|
3231
|
+
* by token-meter's pressure/breakdown — the exact headlineOf derivation the
|
|
3232
|
+
* overview card shows); a pressure-only row (plugin host half absent for that
|
|
3233
|
+
* session) still yields an occupancy ring without slices.
|
|
3234
|
+
*/
|
|
3235
|
+
function agentStatsOf(values) {
|
|
3236
|
+
const timeline = timelineOf(values?.contextTimeline);
|
|
3237
|
+
const pressure = contextPressureOf(values?.contextPressure);
|
|
3238
|
+
const breakdown = contextBreakdownOf(values?.contextBreakdown);
|
|
3239
|
+
const usage = tokenUsageOf(values?.tokenUsage);
|
|
3240
|
+
let head = null;
|
|
3241
|
+
if (timeline !== null) head = headlineOf(timeline, pressure, breakdown);
|
|
3242
|
+
else if (pressure !== null) {
|
|
3243
|
+
const tokens = typeof pressure.projectedTokens === "number" ? pressure.projectedTokens : typeof pressure.pressureTokens === "number" ? pressure.pressureTokens : null;
|
|
3244
|
+
if (tokens !== null) {
|
|
3245
|
+
const window = typeof pressure.contextWindow === "number" && pressure.contextWindow > 0 ? pressure.contextWindow : void 0;
|
|
3246
|
+
head = {
|
|
3247
|
+
tokens,
|
|
3248
|
+
window,
|
|
3249
|
+
pct: window !== void 0 ? Math.min(100, Math.round(tokens / window * 100)) : null,
|
|
3250
|
+
parts: []
|
|
3251
|
+
};
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
const billed = usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null;
|
|
3255
|
+
return {
|
|
3256
|
+
head,
|
|
3257
|
+
requests: timeline !== null ? timeline.requests.length : 0,
|
|
3258
|
+
billed,
|
|
3259
|
+
durationMs: agentDurationOf(values?.subagentTiming),
|
|
3260
|
+
identity: agentIdentityOf(values?.subagent)
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
/**
|
|
3264
|
+
* Build the current session's agent family from a session-list snapshot:
|
|
3265
|
+
* walk up `parentId` to the topmost known ancestor, then DFS its whole
|
|
3266
|
+
* subtree (blank placeholder rows excluded). Null when there is no anchor —
|
|
3267
|
+
* no current session, or a snapshot without a `byId` table (older harness).
|
|
3268
|
+
* The current session synthesizes a row when the list has not delivered it
|
|
3269
|
+
* yet, so the card can still show its live self stats.
|
|
3270
|
+
*/
|
|
3271
|
+
function agentForestOf(snapshot, currentId, self) {
|
|
3272
|
+
const byId = asRecord(asRecord(snapshot)?.byId);
|
|
3273
|
+
if (byId === null || currentId === void 0 || currentId === "") return null;
|
|
3274
|
+
const rows = /* @__PURE__ */ new Map();
|
|
3275
|
+
for (const key of Object.keys(byId)) {
|
|
3276
|
+
const row = agentRowOf(byId[key]);
|
|
3277
|
+
if (row !== null && (!row.blank || key === currentId)) rows.set(key, row);
|
|
3278
|
+
}
|
|
3279
|
+
if (!rows.has(currentId)) rows.set(currentId, {
|
|
3280
|
+
running: false,
|
|
3281
|
+
completed: false,
|
|
3282
|
+
blank: false,
|
|
3283
|
+
updatedAt: 0
|
|
3284
|
+
});
|
|
3285
|
+
let root = currentId;
|
|
3286
|
+
const chain = /* @__PURE__ */ new Set([currentId]);
|
|
3287
|
+
for (;;) {
|
|
3288
|
+
const parent = rows.get(root)?.parentId;
|
|
3289
|
+
if (parent === void 0 || !rows.has(parent) || chain.has(parent)) break;
|
|
3290
|
+
chain.add(parent);
|
|
3291
|
+
root = parent;
|
|
3292
|
+
}
|
|
3293
|
+
const rootRow = rows.get(root);
|
|
3294
|
+
/* v8 ignore next 2 -- root is currentId (inserted above) or a parent
|
|
3295
|
+
verified with rows.has, so its row always exists. */
|
|
3296
|
+
if (rootRow === void 0) return null;
|
|
3297
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
3298
|
+
for (const [id, row] of rows) {
|
|
3299
|
+
if (row.parentId === void 0 || !rows.has(row.parentId)) continue;
|
|
3300
|
+
const list = childrenOf.get(row.parentId) ?? [];
|
|
3301
|
+
list.push({
|
|
3302
|
+
id,
|
|
3303
|
+
row
|
|
3304
|
+
});
|
|
3305
|
+
childrenOf.set(row.parentId, list);
|
|
3306
|
+
}
|
|
3307
|
+
for (const kids of childrenOf.values()) kids.sort((a, b) => {
|
|
3308
|
+
const runDelta = Number(b.row.running) - Number(a.row.running);
|
|
3309
|
+
if (runDelta !== 0) return runDelta;
|
|
3310
|
+
const timeDelta = b.row.updatedAt - a.row.updatedAt;
|
|
3311
|
+
return timeDelta !== 0 ? timeDelta : a.id < b.id ? -1 : 1;
|
|
3312
|
+
});
|
|
3313
|
+
const measure = (id, seen) => {
|
|
3314
|
+
if (seen.has(id)) return 0;
|
|
3315
|
+
seen.add(id);
|
|
3316
|
+
let total = 1;
|
|
3317
|
+
for (const kid of childrenOf.get(id) ?? []) total += measure(kid.id, seen);
|
|
3318
|
+
return total;
|
|
3319
|
+
};
|
|
3320
|
+
const total = measure(root, /* @__PURE__ */ new Set());
|
|
3321
|
+
const nodes = [];
|
|
3322
|
+
const edges = [];
|
|
3323
|
+
const visit = (id, row, parentId, depth, seen, family) => {
|
|
3324
|
+
if (seen.has(id) || nodes.length >= 25) return;
|
|
3325
|
+
seen.add(id);
|
|
3326
|
+
const stats = agentStatsOf(row.projections);
|
|
3327
|
+
const identity = stats.identity;
|
|
3328
|
+
const node = {
|
|
3329
|
+
...stats,
|
|
3330
|
+
id,
|
|
3331
|
+
label: identity?.label ?? row.title ?? row.displayTitle ?? id,
|
|
3332
|
+
...parentId !== void 0 ? { parentId } : {},
|
|
3333
|
+
depth,
|
|
3334
|
+
family,
|
|
3335
|
+
isCurrent: id === currentId,
|
|
3336
|
+
running: row.running,
|
|
3337
|
+
completed: row.completed,
|
|
3338
|
+
subagent: row.origin === "subagent" || identity !== null
|
|
3339
|
+
};
|
|
3340
|
+
if (node.isCurrent && self !== void 0) {
|
|
3341
|
+
node.head = self.head ?? node.head;
|
|
3342
|
+
node.billed = self.billed ?? node.billed;
|
|
3343
|
+
node.requests = self.requests > 0 ? self.requests : node.requests;
|
|
3344
|
+
}
|
|
3345
|
+
nodes.push(node);
|
|
3346
|
+
if (parentId !== void 0) edges.push({
|
|
3347
|
+
from: parentId,
|
|
3348
|
+
to: id
|
|
3349
|
+
});
|
|
3350
|
+
(childrenOf.get(id) ?? []).forEach((kid, ki) => {
|
|
3351
|
+
visit(kid.id, kid.row, id, depth + 1, seen, depth === 0 ? ki : family);
|
|
3352
|
+
});
|
|
3353
|
+
};
|
|
3354
|
+
visit(root, rootRow, void 0, 0, /* @__PURE__ */ new Set(), -1);
|
|
3355
|
+
return {
|
|
3356
|
+
nodes,
|
|
3357
|
+
edges,
|
|
3358
|
+
overflow: Math.max(0, total - nodes.length),
|
|
3359
|
+
solo: nodes.length === 1
|
|
3360
|
+
};
|
|
3361
|
+
}
|
|
3362
|
+
/**
|
|
3363
|
+
* Tidy top-down tree layout: one row per depth level, siblings claim leaf
|
|
3364
|
+
* slots, parents center over their children. Fully responsive to the stage's
|
|
3365
|
+
* visible width: the slot pitch stretches up to SLOT_MAX and compresses down
|
|
3366
|
+
* to SLOT_MIN (captions wrap tighter); a level that still overflows wraps
|
|
3367
|
+
* into bands of at most a per-level node count derived from the stage width —
|
|
3368
|
+
* vertical room is cheaper than horizontal scrolling. Links exit a parent at
|
|
3369
|
+
* its cell bottom (below the caption zone) and enter the child at its top,
|
|
3370
|
+
* so a connector never crosses a label.
|
|
3371
|
+
*/
|
|
3372
|
+
function layoutForest(forest, stageWidth = 0) {
|
|
3373
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
3374
|
+
for (const n of forest.nodes) {
|
|
3375
|
+
if (n.parentId === void 0) continue;
|
|
3376
|
+
const kids = childrenOf.get(n.parentId) ?? [];
|
|
3377
|
+
kids.push(n);
|
|
3378
|
+
childrenOf.set(n.parentId, kids);
|
|
3379
|
+
}
|
|
3380
|
+
const slotOf = /* @__PURE__ */ new Map();
|
|
3381
|
+
let leafSlots = 0;
|
|
3382
|
+
const place = (node) => {
|
|
3383
|
+
const kids = childrenOf.get(node.id) ?? [];
|
|
3384
|
+
if (kids.length === 0) {
|
|
3385
|
+
const slot = leafSlots;
|
|
3386
|
+
leafSlots++;
|
|
3387
|
+
slotOf.set(node.id, slot);
|
|
3388
|
+
return slot;
|
|
3389
|
+
}
|
|
3390
|
+
let first = 0;
|
|
3391
|
+
let last = 0;
|
|
3392
|
+
kids.forEach((kid, index) => {
|
|
3393
|
+
const slot = place(kid);
|
|
3394
|
+
if (index === 0) first = slot;
|
|
3395
|
+
last = slot;
|
|
3396
|
+
});
|
|
3397
|
+
const slot = (first + last) / 2;
|
|
3398
|
+
slotOf.set(node.id, slot);
|
|
3399
|
+
return slot;
|
|
3400
|
+
};
|
|
3401
|
+
/* v8 ignore next 1 -- a forest always holds at least the (possibly
|
|
3402
|
+
synthesized) current node. */
|
|
3403
|
+
if (forest.nodes.length > 0) place(forest.nodes[0]);
|
|
3404
|
+
const perLevel = stageWidth > 0 ? Math.max(2, Math.floor(stageWidth / SLOT_MIN)) : 0;
|
|
3405
|
+
if (perLevel > 0 && leafSlots > perLevel) {
|
|
3406
|
+
const bandSlot = Math.min(SLOT_MAX, stageWidth / perLevel);
|
|
3407
|
+
const width = perLevel * bandSlot;
|
|
3408
|
+
const points = [];
|
|
3409
|
+
let row = 0;
|
|
3410
|
+
const emitBand = (nodes, depth) => {
|
|
3411
|
+
const inset = (width - nodes.length * bandSlot) / 2;
|
|
3412
|
+
nodes.forEach((node, i) => {
|
|
3413
|
+
points.push({
|
|
3414
|
+
id: node.id,
|
|
3415
|
+
x: inset + (i + .5) * bandSlot,
|
|
3416
|
+
y: PAD_Y + row * LEVEL_H,
|
|
3417
|
+
depth
|
|
3418
|
+
});
|
|
3419
|
+
});
|
|
3420
|
+
row++;
|
|
3421
|
+
let band = [];
|
|
3422
|
+
const flush = () => {
|
|
3423
|
+
if (band.length === 0) return;
|
|
3424
|
+
const packed = band;
|
|
3425
|
+
band = [];
|
|
3426
|
+
emitBand(packed, depth + 1);
|
|
3427
|
+
};
|
|
3428
|
+
for (const node of nodes) {
|
|
3429
|
+
const kids = childrenOf.get(node.id) ?? [];
|
|
3430
|
+
for (let start = 0; start < kids.length; start += perLevel) {
|
|
3431
|
+
const group = kids.slice(start, start + perLevel);
|
|
3432
|
+
if (band.length + group.length > perLevel) flush();
|
|
3433
|
+
band.push(...group);
|
|
3434
|
+
if (band.length === perLevel) flush();
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
flush();
|
|
3438
|
+
};
|
|
3439
|
+
/* v8 ignore next 1 -- a forest always holds at least the current node. */
|
|
3440
|
+
if (forest.nodes.length > 0) emitBand([forest.nodes[0]], 0);
|
|
3441
|
+
return {
|
|
3442
|
+
width,
|
|
3443
|
+
height: PAD_Y + (row - 1) * LEVEL_H + CELL_H + 28,
|
|
3444
|
+
captionW: bandSlot - CAPTION_GUTTER,
|
|
3445
|
+
points,
|
|
3446
|
+
links: linksOf(forest, points)
|
|
3447
|
+
};
|
|
3448
|
+
}
|
|
3449
|
+
const slot = stageWidth > 0 && leafSlots > 1 ? Math.min(SLOT_MAX, stageWidth / leafSlots) : SLOT_MAX;
|
|
3450
|
+
const points = forest.nodes.map((node) => ({
|
|
3451
|
+
id: node.id,
|
|
3452
|
+
/* v8 ignore next 1 -- place() visits every node: the forest is exactly
|
|
3453
|
+
the root's subtree by construction. */
|
|
3454
|
+
x: (slotOf.get(node.id) ?? 0) * slot + slot / 2,
|
|
3455
|
+
y: PAD_Y + node.depth * LEVEL_H,
|
|
3456
|
+
depth: node.depth
|
|
3457
|
+
}));
|
|
3458
|
+
const maxDepth = points.reduce((max, p) => Math.max(max, p.depth), 0);
|
|
3459
|
+
return {
|
|
3460
|
+
width: leafSlots * slot,
|
|
3461
|
+
height: PAD_Y + maxDepth * LEVEL_H + CELL_H + 28,
|
|
3462
|
+
captionW: slot - CAPTION_GUTTER,
|
|
3463
|
+
points,
|
|
3464
|
+
links: linksOf(forest, points)
|
|
3465
|
+
};
|
|
3466
|
+
}
|
|
3467
|
+
/**
|
|
3468
|
+
* Family hue by level-1 subtree index: the golden angle keeps consecutive
|
|
3469
|
+
* families maximally separated on the color wheel without a hand-tuned palette.
|
|
3470
|
+
*/
|
|
3471
|
+
function familyHue(index) {
|
|
3472
|
+
return `hsl(${Math.round(index * 137.508) % 360} 58% 52%)`;
|
|
3473
|
+
}
|
|
3474
|
+
/** Parent→child links: exit the parent's cell bottom, enter the child's top. */
|
|
3475
|
+
function linksOf(forest, points) {
|
|
3476
|
+
const pointOf = new Map(points.map((p) => [p.id, p]));
|
|
3477
|
+
const nodeOf = new Map(forest.nodes.map((n) => [n.id, n]));
|
|
3478
|
+
const runningIds = new Set(forest.nodes.filter((n) => n.running).map((n) => n.id));
|
|
3479
|
+
const links = [];
|
|
3480
|
+
for (const edge of forest.edges) {
|
|
3481
|
+
const from = pointOf.get(edge.from);
|
|
3482
|
+
const to = pointOf.get(edge.to);
|
|
3483
|
+
/* v8 ignore next 2 -- edges are emitted only for visited parent/child
|
|
3484
|
+
pairs, so both points always exist. */
|
|
3485
|
+
if (from === void 0 || to === void 0) continue;
|
|
3486
|
+
links.push({
|
|
3487
|
+
to: edge.to,
|
|
3488
|
+
running: runningIds.has(edge.to),
|
|
3489
|
+
/* v8 ignore next 1 -- edges only connect visited nodes. */
|
|
3490
|
+
color: familyHue(nodeOf.get(edge.to)?.family ?? 0),
|
|
3491
|
+
x1: from.x,
|
|
3492
|
+
y1: from.y + CELL_H,
|
|
3493
|
+
x2: to.x,
|
|
3494
|
+
y2: to.y - 26 - 10
|
|
3495
|
+
});
|
|
3496
|
+
}
|
|
3497
|
+
return links;
|
|
3498
|
+
}
|
|
3499
|
+
/**
|
|
3500
|
+
* One fused ring per agent — the exact semantics of the chat composer's own
|
|
3501
|
+
* context ring: the composition parts, scaled to the occupancy share of the
|
|
3502
|
+
* window, fill the circle, and a neutral remainder marks the free window.
|
|
3503
|
+
* With no known window the composition fills the whole circle; with no
|
|
3504
|
+
* composition (pressure-only rows) a single threshold-colored arc carries
|
|
3505
|
+
* the occupancy; a known window with zero occupancy draws the free outline.
|
|
3506
|
+
*/
|
|
3507
|
+
function ringSegments(parts, pct, radius, fallbackColor) {
|
|
3508
|
+
const circumference = 2 * Math.PI * radius;
|
|
3509
|
+
const occ = pct === null ? 1 : Math.min(100, Math.max(0, pct)) / 100;
|
|
3510
|
+
let total = 0;
|
|
3511
|
+
for (const p of parts) total += p.value > 0 ? p.value : 0;
|
|
3512
|
+
const segs = [];
|
|
3513
|
+
let offset = 0;
|
|
3514
|
+
if (total > 0) for (const p of parts) {
|
|
3515
|
+
if (p.value <= 0) continue;
|
|
3516
|
+
const len = circumference * (p.value / total) * occ;
|
|
3517
|
+
if (len <= 0) continue;
|
|
3518
|
+
segs.push({
|
|
3519
|
+
key: p.key,
|
|
3520
|
+
color: p.color,
|
|
3521
|
+
len,
|
|
3522
|
+
offset,
|
|
3523
|
+
free: false
|
|
3524
|
+
});
|
|
3525
|
+
offset += len;
|
|
3526
|
+
}
|
|
3527
|
+
else if (pct !== null && occ > 0) {
|
|
3528
|
+
segs.push({
|
|
3529
|
+
key: "fill",
|
|
3530
|
+
color: fallbackColor,
|
|
3531
|
+
len: circumference * occ,
|
|
3532
|
+
offset: 0,
|
|
3533
|
+
free: false
|
|
3534
|
+
});
|
|
3535
|
+
offset = circumference * occ;
|
|
3536
|
+
}
|
|
3537
|
+
if (pct !== null && offset < circumference) segs.push({
|
|
3538
|
+
key: "free",
|
|
3539
|
+
color: "",
|
|
3540
|
+
len: circumference - offset,
|
|
3541
|
+
offset,
|
|
3542
|
+
free: true
|
|
3543
|
+
});
|
|
3544
|
+
return segs;
|
|
3545
|
+
}
|
|
3546
|
+
/** Session-switch navigation, fail-soft: a stale row (list rebuilt between snapshot and click) loses its open() race and is ignored. */
|
|
3547
|
+
function openAgentSession(face, id) {
|
|
3548
|
+
if (face === null || typeof face.open !== "function") return;
|
|
3549
|
+
try {
|
|
3550
|
+
face.open(id);
|
|
3551
|
+
} catch {}
|
|
3552
|
+
}
|
|
3553
|
+
/** Narrow `ctx.get('sessions')` to the card's face (null = harness without the outward sessions service). */
|
|
3554
|
+
function sessionsFaceOf(ctx) {
|
|
3555
|
+
const rec = asRecord(ctx.get("sessions"));
|
|
3556
|
+
if (rec === null) return null;
|
|
3557
|
+
const list = asRecord(rec.list);
|
|
3558
|
+
if (list === null || typeof list.getSnapshot !== "function" || typeof list.subscribe !== "function") return null;
|
|
3559
|
+
return rec;
|
|
3560
|
+
}
|
|
3561
|
+
/** Compact duration: `42s`, `3m05s`, `1h07m` (shared by both locales). */
|
|
3562
|
+
function fmtDuration(ms) {
|
|
3563
|
+
if (!Number.isFinite(ms) || ms < 0) return "—";
|
|
3564
|
+
const s = Math.round(ms / 1e3);
|
|
3565
|
+
if (s < 60) return `${s}s`;
|
|
3566
|
+
const m = Math.floor(s / 60);
|
|
3567
|
+
if (m < 60) return `${m}m${String(s % 60).padStart(2, "0")}s`;
|
|
3568
|
+
return `${Math.floor(m / 60)}h${String(m % 60).padStart(2, "0")}m`;
|
|
3569
|
+
}
|
|
3570
|
+
//#endregion
|
|
3571
|
+
//#region src/client/components/agentGraph.tsx
|
|
3572
|
+
/** Caption box height under a node (3 wrapped label lines + the tokens line). */
|
|
3573
|
+
const CAPTION_H = 60;
|
|
3574
|
+
/** Fallback arc color for pressure-only nodes (no composition data), by fill ratio. */
|
|
3575
|
+
function ringColorOf(pct) {
|
|
3576
|
+
if (pct === null) return "var(--dsw-alias-border-l1)";
|
|
3577
|
+
if (pct >= 90) return "#ef4444";
|
|
3578
|
+
if (pct >= 70) return "#f59e0b";
|
|
3579
|
+
return "#22c55e";
|
|
3580
|
+
}
|
|
3581
|
+
function makeAgentGraph(ctx, kit) {
|
|
3582
|
+
const { t, fmt, catLabel } = kit;
|
|
3583
|
+
function AgentGraph(props) {
|
|
3584
|
+
const face = React.useMemo(() => sessionsFaceOf(ctx), []);
|
|
3585
|
+
const subscribe = React.useCallback((fn) => {
|
|
3586
|
+
if (face === null) return () => {};
|
|
3587
|
+
/* v8 ignore next 2 -- sessionsFaceOf returns a face only after proving list.subscribe. */
|
|
3588
|
+
if (face.list === void 0) return () => {};
|
|
3589
|
+
return face.list.subscribe(fn);
|
|
3590
|
+
}, [face]);
|
|
3591
|
+
const getSnapshot = React.useCallback(() => {
|
|
3592
|
+
if (face === null) return null;
|
|
3593
|
+
/* v8 ignore next 2 -- sessionsFaceOf proves list before returning the face. */
|
|
3594
|
+
if (face.list === void 0) return null;
|
|
3595
|
+
return face.list.getSnapshot();
|
|
3596
|
+
}, [face]);
|
|
3597
|
+
const snapshot = React.useSyncExternalStore(subscribe, getSnapshot);
|
|
3598
|
+
const sessionId = props.sessionId;
|
|
3599
|
+
const [hoverId, setHoverId] = React.useState(null);
|
|
3600
|
+
const stageRef = React.useRef(null);
|
|
3601
|
+
const [stageWidth, setStageWidth] = React.useState(0);
|
|
3602
|
+
React.useEffect(() => {
|
|
3603
|
+
/* v8 ignore start -- jsdom has neither ResizeObserver nor layout; tests exercise the natural-pitch fallback (stageWidth 0). */
|
|
3604
|
+
const el = stageRef.current;
|
|
3605
|
+
if (el === null || typeof ResizeObserver !== "function") return;
|
|
3606
|
+
setStageWidth(el.clientWidth);
|
|
3607
|
+
const observer = new ResizeObserver(() => {
|
|
3608
|
+
setStageWidth(el.clientWidth);
|
|
3609
|
+
});
|
|
3610
|
+
observer.observe(el);
|
|
3611
|
+
return () => {
|
|
3612
|
+
observer.disconnect();
|
|
3613
|
+
};
|
|
3614
|
+
/* v8 ignore stop */
|
|
3615
|
+
}, []);
|
|
3616
|
+
React.useEffect(() => {
|
|
3617
|
+
if (face === null || typeof sessionId !== "string" || sessionId === "") return;
|
|
3618
|
+
if (typeof face.refreshSubagents !== "function") return;
|
|
3619
|
+
face.refreshSubagents(sessionId).catch(() => {});
|
|
3620
|
+
}, [face, sessionId]);
|
|
3621
|
+
const built = React.useMemo(() => {
|
|
3622
|
+
const forest = agentForestOf(snapshot, sessionId, props.self);
|
|
3623
|
+
return forest !== null ? {
|
|
3624
|
+
forest,
|
|
3625
|
+
layout: layoutForest(forest, stageWidth)
|
|
3626
|
+
} : null;
|
|
3627
|
+
}, [
|
|
3628
|
+
snapshot,
|
|
3629
|
+
sessionId,
|
|
3630
|
+
props.self,
|
|
3631
|
+
stageWidth
|
|
3632
|
+
]);
|
|
3633
|
+
if (built === null) return null;
|
|
3634
|
+
const { forest, layout } = built;
|
|
3635
|
+
const byId = new Map(forest.nodes.map((n) => [n.id, n]));
|
|
3636
|
+
/* v8 ignore next 1 -- agentForestOf anchors the forest at the current
|
|
3637
|
+
session, so a current node always exists. */
|
|
3638
|
+
const current = forest.nodes.find((n) => n.isCurrent) ?? forest.nodes[0];
|
|
3639
|
+
const inspected = (hoverId !== null ? byId.get(hoverId) : void 0) ?? current;
|
|
3640
|
+
const runningCount = forest.nodes.filter((n) => n.running).length;
|
|
3641
|
+
let totalTokens = 0;
|
|
3642
|
+
for (const n of forest.nodes) totalTokens += n.head !== null ? n.head.tokens : 0;
|
|
3643
|
+
const open = (id) => {
|
|
3644
|
+
if (id === current.id) return;
|
|
3645
|
+
openAgentSession(face, id);
|
|
3646
|
+
};
|
|
3647
|
+
const keyOpen = (id) => (ev) => {
|
|
3648
|
+
if (ev.key !== "Enter" && ev.key !== " ") return;
|
|
3649
|
+
ev.preventDefault();
|
|
3650
|
+
open(id);
|
|
3651
|
+
};
|
|
3652
|
+
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", {
|
|
3653
|
+
className: "lc-agents-stage",
|
|
3654
|
+
ref: stageRef
|
|
3655
|
+
}, /* @__PURE__ */ React.createElement("svg", {
|
|
3656
|
+
className: "lc-agents-svg",
|
|
3657
|
+
width: layout.width,
|
|
3658
|
+
height: layout.height,
|
|
3659
|
+
viewBox: `0 0 ${layout.width} ${layout.height}`
|
|
3660
|
+
}, layout.links.map((link) => {
|
|
3661
|
+
const d = `M ${link.x1} ${link.y1} L ${link.x2} ${link.y2}`;
|
|
3662
|
+
return /* @__PURE__ */ React.createElement("g", { key: link.to }, /* @__PURE__ */ React.createElement("path", {
|
|
3663
|
+
className: "lc-agents-link" + (link.running ? " lc-agents-link-live" : ""),
|
|
3664
|
+
d,
|
|
3665
|
+
stroke: link.color,
|
|
3666
|
+
fill: "none"
|
|
3667
|
+
}), link.running ? /* @__PURE__ */ React.createElement("path", {
|
|
3668
|
+
className: "lc-agents-flow",
|
|
3669
|
+
d,
|
|
3670
|
+
stroke: link.color,
|
|
3671
|
+
fill: "none"
|
|
3672
|
+
}) : null);
|
|
3673
|
+
}), forest.nodes.map((node) => {
|
|
3674
|
+
const point = layout.points.find((p) => p.id === node.id);
|
|
3675
|
+
/* v8 ignore next 2 -- layoutForest positions every forest node,
|
|
3676
|
+
so the lookup never misses. */
|
|
3677
|
+
if (point === void 0) return null;
|
|
3678
|
+
return /* @__PURE__ */ React.createElement(AgentNodeView, {
|
|
3679
|
+
key: node.id,
|
|
3680
|
+
node,
|
|
3681
|
+
x: point.x,
|
|
3682
|
+
y: point.y,
|
|
3683
|
+
captionW: layout.captionW,
|
|
3684
|
+
hovered: hoverId === node.id,
|
|
3685
|
+
onHover: setHoverId,
|
|
3686
|
+
onOpen: open,
|
|
3687
|
+
onKeyOpen: keyOpen(node.id),
|
|
3688
|
+
t,
|
|
3689
|
+
fmt
|
|
3690
|
+
});
|
|
3691
|
+
}))), forest.solo ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty lc-agents-solo" }, t("agents.solo")) : null, /* @__PURE__ */ React.createElement(Inspector, {
|
|
3692
|
+
node: inspected,
|
|
3693
|
+
t,
|
|
3694
|
+
fmt
|
|
3695
|
+
}), /* @__PURE__ */ React.createElement("div", { className: "lc-agents-legend" }, CATS.map((c) => /* @__PURE__ */ React.createElement("span", {
|
|
3696
|
+
key: c.key,
|
|
3697
|
+
className: "lc-agents-legend-item"
|
|
3698
|
+
}, /* @__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"))));
|
|
3699
|
+
}
|
|
3700
|
+
return AgentGraph;
|
|
3701
|
+
}
|
|
3702
|
+
function AgentNodeView(props) {
|
|
3703
|
+
const { node, x, y, captionW } = props;
|
|
3704
|
+
const pct = node.head !== null ? node.head.pct : null;
|
|
3705
|
+
const ring = 2 * Math.PI * 20;
|
|
3706
|
+
const segs = node.head !== null ? ringSegments(node.head.parts, pct, 20, ringColorOf(pct)) : [];
|
|
3707
|
+
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");
|
|
3708
|
+
return /* @__PURE__ */ React.createElement("g", {
|
|
3709
|
+
className: cls,
|
|
3710
|
+
transform: `translate(${x}, ${y})`,
|
|
3711
|
+
"data-agent": node.id,
|
|
3712
|
+
role: node.isCurrent ? "img" : "button",
|
|
3713
|
+
tabIndex: node.isCurrent ? void 0 : 0,
|
|
3714
|
+
onClick: () => {
|
|
3715
|
+
props.onOpen(node.id);
|
|
3716
|
+
},
|
|
3717
|
+
onKeyDown: props.onKeyOpen,
|
|
3718
|
+
onMouseEnter: () => {
|
|
3719
|
+
props.onHover(node.id);
|
|
3720
|
+
},
|
|
3721
|
+
onMouseLeave: () => {
|
|
3722
|
+
props.onHover(null);
|
|
3723
|
+
}
|
|
3724
|
+
}, /* @__PURE__ */ React.createElement("circle", {
|
|
3725
|
+
className: "lc-agent-halo",
|
|
3726
|
+
r: 35
|
|
3727
|
+
}), /* @__PURE__ */ React.createElement("circle", {
|
|
3728
|
+
className: "lc-agent-track",
|
|
3729
|
+
r: 26
|
|
3730
|
+
}), segs.map((seg) => /* @__PURE__ */ React.createElement("circle", {
|
|
3731
|
+
key: seg.key,
|
|
3732
|
+
className: "lc-agent-seg" + (seg.free ? " lc-agent-free" : ""),
|
|
3733
|
+
r: 20,
|
|
3734
|
+
stroke: seg.free ? void 0 : seg.color,
|
|
3735
|
+
strokeDasharray: `${seg.len} ${ring - seg.len}`,
|
|
3736
|
+
strokeDashoffset: -seg.offset,
|
|
3737
|
+
transform: "rotate(-90)"
|
|
3738
|
+
})), /* @__PURE__ */ React.createElement("text", {
|
|
3739
|
+
className: "lc-agent-pct",
|
|
3740
|
+
textAnchor: "middle",
|
|
3741
|
+
dy: "0.32em"
|
|
3742
|
+
}, pct !== null ? `${pct}%` : node.head !== null ? props.fmt(node.head.tokens) : "—"), /* @__PURE__ */ React.createElement("foreignObject", {
|
|
3743
|
+
x: -captionW / 2,
|
|
3744
|
+
y: 34,
|
|
3745
|
+
width: captionW,
|
|
3746
|
+
height: CAPTION_H
|
|
3747
|
+
}, /* @__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) : "—"))));
|
|
3748
|
+
}
|
|
3749
|
+
/** The detail strip mirroring the hovered (or current) node: identity, occupancy, activity, and the open hint. */
|
|
3750
|
+
function Inspector(props) {
|
|
3751
|
+
const { node, t, fmt } = props;
|
|
3752
|
+
const bits = [];
|
|
3753
|
+
if (node.head !== null) {
|
|
3754
|
+
const head = node.head;
|
|
3755
|
+
const window = head.window !== void 0 ? ` / ${fmt(head.window)}` : "";
|
|
3756
|
+
const pct = head.pct !== null ? ` · ${head.pct}%` : "";
|
|
3757
|
+
bits.push(`${fmt(head.tokens)}${window}${pct}`);
|
|
3758
|
+
}
|
|
3759
|
+
if (node.requests > 0) bits.push(t("agents.requests", { n: node.requests }));
|
|
3760
|
+
if (node.billed !== null && node.billed > 0) bits.push(t("agents.billed", { n: fmt(node.billed) }));
|
|
3761
|
+
if (node.durationMs !== null) bits.push(fmtDuration(node.durationMs));
|
|
3762
|
+
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);
|
|
3763
|
+
}
|
|
2597
3764
|
//#endregion
|
|
2598
3765
|
//#region src/client/components/events.tsx
|
|
2599
3766
|
const EVENT_ICONS = {
|
|
@@ -2656,6 +3823,14 @@ window.__ModuleLoader__.load({
|
|
|
2656
3823
|
eventAt
|
|
2657
3824
|
};
|
|
2658
3825
|
}
|
|
3826
|
+
/**
|
|
3827
|
+
* Set the native `title` on every label whose text overflows its box (the styled tips cover cards, but a plain
|
|
3828
|
+
* ellipsis row still needs the native fallback); reads (scrollWidth/clientWidth) and writes (title) stay separate
|
|
3829
|
+
* from layout-affecting work, and callers gate WHEN this runs so it never becomes a per-render forced layout.
|
|
3830
|
+
*/
|
|
3831
|
+
function syncTitles(root) {
|
|
3832
|
+
for (const el of root.querySelectorAll(".lc-event-label")) el.title = el.scrollWidth > el.clientWidth ? el.textContent || "" : "";
|
|
3833
|
+
}
|
|
2659
3834
|
function makeEventList(kit) {
|
|
2660
3835
|
const { t, fmt, fmtTime, eventLabel, eventAt } = kit;
|
|
2661
3836
|
return function EventList(props) {
|
|
@@ -2663,26 +3838,29 @@ window.__ModuleLoader__.load({
|
|
|
2663
3838
|
React.useLayoutEffect(() => {
|
|
2664
3839
|
const root = rootRef.current;
|
|
2665
3840
|
if (!root) return;
|
|
2666
|
-
|
|
2667
|
-
|
|
3841
|
+
syncTitles(root);
|
|
3842
|
+
}, [props.events]);
|
|
3843
|
+
React.useEffect(() => {
|
|
3844
|
+
const onResize = () => {
|
|
3845
|
+
const root = rootRef.current;
|
|
3846
|
+
if (root !== null) syncTitles(root);
|
|
2668
3847
|
};
|
|
2669
|
-
|
|
2670
|
-
window.addEventListener("resize", sync);
|
|
3848
|
+
window.addEventListener("resize", onResize);
|
|
2671
3849
|
return () => {
|
|
2672
|
-
window.removeEventListener("resize",
|
|
3850
|
+
window.removeEventListener("resize", onResize);
|
|
2673
3851
|
};
|
|
2674
|
-
});
|
|
3852
|
+
}, []);
|
|
2675
3853
|
if (props.events.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
|
|
2676
3854
|
const sorted = props.events.slice().reverse();
|
|
2677
3855
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2678
3856
|
className: "lc-events",
|
|
2679
3857
|
ref: rootRef
|
|
2680
|
-
}, sorted.map((ev
|
|
3858
|
+
}, sorted.map((ev) => {
|
|
2681
3859
|
const label = eventLabel(ev);
|
|
2682
3860
|
const at = eventAt(ev);
|
|
2683
3861
|
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] || "•";
|
|
2684
3862
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2685
|
-
key:
|
|
3863
|
+
key: ev.seq,
|
|
2686
3864
|
className: "lc-event"
|
|
2687
3865
|
}, /* @__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)));
|
|
2688
3866
|
}));
|
|
@@ -2690,13 +3868,8 @@ window.__ModuleLoader__.load({
|
|
|
2690
3868
|
}
|
|
2691
3869
|
//#endregion
|
|
2692
3870
|
//#region src/client/components/fileCard.tsx
|
|
2693
|
-
/** An expanded file lists at most this many ops before a "+n earlier" line. */
|
|
2694
|
-
const MAX_OPS = 8;
|
|
2695
3871
|
function makeFileCard(kit, settings) {
|
|
2696
3872
|
const { t, fmt, fmtTime } = kit;
|
|
2697
|
-
function formGlyph(form) {
|
|
2698
|
-
return form === "image" ? "🖼" : form === "dir" ? "📁" : "📄";
|
|
2699
|
-
}
|
|
2700
3873
|
function matches(e, f) {
|
|
2701
3874
|
if (f === "all") return true;
|
|
2702
3875
|
if (f === "image") return e.form === "image";
|
|
@@ -2708,7 +3881,7 @@ window.__ModuleLoader__.load({
|
|
|
2708
3881
|
function DeltaPair(props) {
|
|
2709
3882
|
return /* @__PURE__ */ React.createElement("span", { className: "lc-fa-delta" }, props.added > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-up" }, "+" + fmt(props.added)) : null, props.removed > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-down" }, "−" + fmt(props.removed)) : null);
|
|
2710
3883
|
}
|
|
2711
|
-
return function FileCard(props) {
|
|
3884
|
+
return React.memo(function FileCard(props) {
|
|
2712
3885
|
const { activity } = props;
|
|
2713
3886
|
const [filter, setFilter] = React.useState("all");
|
|
2714
3887
|
const [sort, setSort] = React.useState(() => settings.defaultFileSort());
|
|
@@ -2744,7 +3917,7 @@ window.__ModuleLoader__.load({
|
|
|
2744
3917
|
ops: activity.totals.image.ops
|
|
2745
3918
|
}
|
|
2746
3919
|
];
|
|
2747
|
-
const opLine = (op) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-time" }, op.time !== void 0 ? fmtTime(op.time) : "—"), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-tool" }, op.tool), op.detail !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-detail" }, op.detail) : null, op.added + op.removed > 0 ? /* @__PURE__ */ React.createElement(DeltaPair, {
|
|
3920
|
+
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.hits !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-detail" }, t("files.hits", { n: fmt(op.hits) })) : null, op.detail === void 0 && op.hits === void 0 && op.program !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-detail" }, op.program) : null, op.added + op.removed > 0 ? /* @__PURE__ */ React.createElement(DeltaPair, {
|
|
2748
3921
|
added: op.added,
|
|
2749
3922
|
removed: op.removed
|
|
2750
3923
|
}) : null, op.err ? /* @__PURE__ */ React.createElement("span", {
|
|
@@ -2796,6 +3969,7 @@ window.__ModuleLoader__.load({
|
|
|
2796
3969
|
const slash = trimmed.lastIndexOf("/");
|
|
2797
3970
|
const dir = slash >= 0 ? trimmed.slice(0, slash + 1) : "";
|
|
2798
3971
|
const base = slash >= 0 ? trimmed.slice(slash + 1) : trimmed;
|
|
3972
|
+
const glyph = glyphOf(e.path, e.form);
|
|
2799
3973
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2800
3974
|
key: e.path,
|
|
2801
3975
|
className: "lc-fa-item" + (open ? " lc-fa-item-on" : "")
|
|
@@ -2808,8 +3982,8 @@ window.__ModuleLoader__.load({
|
|
|
2808
3982
|
}
|
|
2809
3983
|
}, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "▸"), /* @__PURE__ */ React.createElement("span", {
|
|
2810
3984
|
className: "lc-fa-form",
|
|
2811
|
-
title: t(
|
|
2812
|
-
},
|
|
3985
|
+
title: t(glyph.tip)
|
|
3986
|
+
}, glyph.glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-path" }, dir !== "" ? /* @__PURE__ */ React.createElement("em", null, dir) : null, /* @__PURE__ */ React.createElement("b", null, base)), e.reads > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
2813
3987
|
className: "lc-fa-badge lc-fa-b-read",
|
|
2814
3988
|
title: t("files.kind.read")
|
|
2815
3989
|
}, /* @__PURE__ */ React.createElement("i", null), fmt(e.reads)) : null, e.writes > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
@@ -2824,10 +3998,11 @@ window.__ModuleLoader__.load({
|
|
|
2824
3998
|
}) : null, e.errs > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
2825
3999
|
className: "lc-br-err-dot",
|
|
2826
4000
|
title: t("files.errs", { n: e.errs })
|
|
2827
|
-
}) : null, e.ops[0].time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-time" }, fmtTime(e.ops[0].time)) : null), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-ops" }, e.ops.
|
|
4001
|
+
}) : 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, i) => {
|
|
2828
4002
|
const onLocate = props.onLocate;
|
|
4003
|
+
const key = `${op.seq}:${i}`;
|
|
2829
4004
|
return onLocate !== void 0 ? /* @__PURE__ */ React.createElement("button", {
|
|
2830
|
-
key
|
|
4005
|
+
key,
|
|
2831
4006
|
type: "button",
|
|
2832
4007
|
className: "lc-fa-op lc-fa-op-link",
|
|
2833
4008
|
title: t("files.locate"),
|
|
@@ -2835,28 +4010,41 @@ window.__ModuleLoader__.load({
|
|
|
2835
4010
|
onLocate(op);
|
|
2836
4011
|
}
|
|
2837
4012
|
}, opLine(op)) : /* @__PURE__ */ React.createElement("div", {
|
|
2838
|
-
key
|
|
4013
|
+
key,
|
|
2839
4014
|
className: "lc-fa-op"
|
|
2840
4015
|
}, opLine(op));
|
|
2841
|
-
})
|
|
2842
|
-
})))
|
|
2843
|
-
};
|
|
4016
|
+
})) : null);
|
|
4017
|
+
}))));
|
|
4018
|
+
});
|
|
2844
4019
|
}
|
|
2845
4020
|
const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
|
|
2846
4021
|
const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
|
|
2847
4022
|
//#endregion
|
|
2848
4023
|
//#region src/client/latestVersion.ts
|
|
2849
4024
|
/**
|
|
2850
|
-
* One live check for the Plugin-info card: lazy with 1h TTL;
|
|
2851
|
-
*
|
|
4025
|
+
* One live check for the Plugin-info card: lazy with 1h TTL; registries
|
|
4026
|
+
* are tried strictly in order — official npm first, npmmirror only as a
|
|
4027
|
+
* fallback — and one-at-a-time, never in parallel. Each source's failure
|
|
4028
|
+
* (non-ok response, missing/non-string version, network error) advances
|
|
4029
|
+
* to the next one; only an all-source failure resolves to null so the
|
|
4030
|
+
* card silently keeps its static version.
|
|
2852
4031
|
*/
|
|
2853
|
-
const
|
|
4032
|
+
const REGISTRY_HOSTS = ["https://registry.npmjs.org", "https://registry.npmmirror.com"];
|
|
2854
4033
|
const TTL_MS = 36e5;
|
|
2855
4034
|
let cached = null;
|
|
4035
|
+
function readVersion(host) {
|
|
4036
|
+
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);
|
|
4037
|
+
}
|
|
2856
4038
|
function fetchLatestVersion() {
|
|
2857
4039
|
if (!cached || Date.now() - cached.at >= TTL_MS) cached = {
|
|
2858
4040
|
at: Date.now(),
|
|
2859
|
-
promise:
|
|
4041
|
+
promise: (async () => {
|
|
4042
|
+
for (const host of REGISTRY_HOSTS) {
|
|
4043
|
+
const v = await readVersion(host);
|
|
4044
|
+
if (v !== null) return v;
|
|
4045
|
+
}
|
|
4046
|
+
return null;
|
|
4047
|
+
})()
|
|
2860
4048
|
};
|
|
2861
4049
|
return cached.promise;
|
|
2862
4050
|
}
|
|
@@ -2885,7 +4073,7 @@ window.__ModuleLoader__.load({
|
|
|
2885
4073
|
return function PluginInfo() {
|
|
2886
4074
|
const [latest, setLatest] = React.useState(null);
|
|
2887
4075
|
React.useEffect(() => {
|
|
2888
|
-
if ("0.
|
|
4076
|
+
if ("0.34.1".includes("-dev")) return;
|
|
2889
4077
|
let on = true;
|
|
2890
4078
|
fetchLatestVersion().then((v) => {
|
|
2891
4079
|
if (on && v) setLatest(v);
|
|
@@ -2894,13 +4082,18 @@ window.__ModuleLoader__.load({
|
|
|
2894
4082
|
on = false;
|
|
2895
4083
|
};
|
|
2896
4084
|
}, []);
|
|
2897
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
2898
|
-
const nameValue = ["dsh-context (v0.
|
|
4085
|
+
const update = latest !== null && isNewerVersion(latest, "0.34.1") ? latest : null;
|
|
4086
|
+
const nameValue = ["dsh-context (v0.34.1)"];
|
|
2899
4087
|
if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
|
|
2900
4088
|
key: "update",
|
|
2901
4089
|
className: "lc-pi-update"
|
|
2902
4090
|
}, "↑ v" + update));
|
|
2903
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("plugin.title")), /* @__PURE__ */ React.createElement("
|
|
4091
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("plugin.title")), /* @__PURE__ */ React.createElement("a", {
|
|
4092
|
+
className: "lc-card-sub lc-pi-hint",
|
|
4093
|
+
href: PLUGIN_REPO,
|
|
4094
|
+
target: "_blank",
|
|
4095
|
+
rel: "noreferrer"
|
|
4096
|
+
}, t("plugin.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-pi-grid" }, row(t("plugin.name"), nameValue, PLUGIN_REPO + "/releases"), row(t("plugin.github"), PLUGIN_REPO_SHORT, PLUGIN_REPO)));
|
|
2904
4097
|
};
|
|
2905
4098
|
}
|
|
2906
4099
|
//#endregion
|
|
@@ -2910,21 +4103,22 @@ window.__ModuleLoader__.load({
|
|
|
2910
4103
|
/**
|
|
2911
4104
|
* One brief row: a fixed-width kind tag plus one glanceable line. The tag carries a styled, instant explanation bubble (the
|
|
2912
4105
|
* `.lc-stat-tip` pattern); the content span keeps the native title (preview + locate hint), so the two never stack.
|
|
2913
|
-
* Clickable when the browser linkage is wired.
|
|
4106
|
+
* Clickable when the browser linkage is wired AND the row carries a node (the always-present In row's empty state stays inert).
|
|
2914
4107
|
*/
|
|
2915
4108
|
function BriefRow(props) {
|
|
2916
|
-
const cls = "lc-brief-row" + (props.onLocate !== void 0 ? " lc-brief-row-link" : "");
|
|
2917
4109
|
const inner = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-brief-tag" }, props.tag, /* @__PURE__ */ React.createElement("span", {
|
|
2918
4110
|
className: "lc-brief-tip",
|
|
2919
4111
|
role: "tooltip"
|
|
2920
4112
|
}, props.tagTip)), props.children);
|
|
2921
|
-
if (props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className:
|
|
4113
|
+
if (props.node === void 0 || props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-brief-row" }, inner);
|
|
4114
|
+
const node = props.node;
|
|
4115
|
+
const onLocate = props.onLocate;
|
|
2922
4116
|
const locate = () => {
|
|
2923
|
-
|
|
4117
|
+
onLocate(node, props.isResponse);
|
|
2924
4118
|
};
|
|
2925
4119
|
return /* @__PURE__ */ React.createElement("button", {
|
|
2926
4120
|
type: "button",
|
|
2927
|
-
className:
|
|
4121
|
+
className: "lc-brief-row lc-brief-row-link",
|
|
2928
4122
|
onClick: locate
|
|
2929
4123
|
}, inner);
|
|
2930
4124
|
}
|
|
@@ -2998,9 +4192,12 @@ window.__ModuleLoader__.load({
|
|
|
2998
4192
|
function factTitle(tag, text) {
|
|
2999
4193
|
return tag !== null ? text !== "" ? tag + " · " + text : tag : text;
|
|
3000
4194
|
}
|
|
4195
|
+
/**
|
|
4196
|
+
* The brief's container ALWAYS renders — a fixed three-row lane (`.lc-brief`'s min-height) so scrubbing the chart never
|
|
4197
|
+
* changes the panel's height; unknown or empty steps just leave parts of the lane blank instead of collapsing it.
|
|
4198
|
+
*/
|
|
3001
4199
|
function BriefSection(props) {
|
|
3002
|
-
const { opener, inputs, response } = props.brief;
|
|
3003
|
-
if (opener === void 0 && inputs.length === 0 && response === void 0) return null;
|
|
4200
|
+
const { opener, inputs, response } = props.brief ?? { inputs: [] };
|
|
3004
4201
|
const convOf = props.convOf ?? (() => void 0);
|
|
3005
4202
|
const hint = props.onLocate !== void 0 ? " — " + t("brief.locate") : "";
|
|
3006
4203
|
const locateChip = props.onLocate === void 0 ? void 0 : (n) => (e) => {
|
|
@@ -3032,13 +4229,13 @@ window.__ModuleLoader__.load({
|
|
|
3032
4229
|
}, openerParts.tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-fact" }, openerParts.tag) : null, openerParts.text !== "" ? /* @__PURE__ */ React.createElement("span", {
|
|
3033
4230
|
className: "lc-brief-text",
|
|
3034
4231
|
title: factTitle(openerParts.tag, openerParts.text) + hint
|
|
3035
|
-
}, openerParts.text) : null) : null,
|
|
4232
|
+
}, openerParts.text) : null) : null, /* @__PURE__ */ React.createElement(BriefRow, {
|
|
3036
4233
|
tag: t("brief.input"),
|
|
3037
4234
|
tagTip: t("brief.inputTip"),
|
|
3038
4235
|
node: inputs[0],
|
|
3039
4236
|
isResponse: false,
|
|
3040
4237
|
onLocate: props.onLocate
|
|
3041
|
-
}, inputs.slice(0, MAX_CHIPS).map((n) => nodeChip(n, locateChip?.(n))), inputs.length > MAX_CHIPS ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-more" }, t("brief.more", { n: inputs.length - MAX_CHIPS })) : null) :
|
|
4238
|
+
}, 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, {
|
|
3042
4239
|
tag: t("brief.reply"),
|
|
3043
4240
|
tagTip: t("brief.replyTip"),
|
|
3044
4241
|
node: response,
|
|
@@ -3046,7 +4243,7 @@ window.__ModuleLoader__.load({
|
|
|
3046
4243
|
onLocate: props.onLocate
|
|
3047
4244
|
}, nodeChip(response, void 0, true)) : null);
|
|
3048
4245
|
}
|
|
3049
|
-
return function RequestDetail(props) {
|
|
4246
|
+
return React.memo(function RequestDetail(props) {
|
|
3050
4247
|
const req = props.request;
|
|
3051
4248
|
if (!req) return null;
|
|
3052
4249
|
const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
|
|
@@ -3078,11 +4275,11 @@ window.__ModuleLoader__.load({
|
|
|
3078
4275
|
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", {
|
|
3079
4276
|
className: "lc-detail-marker",
|
|
3080
4277
|
title: eventLabel(marker)
|
|
3081
|
-
}, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("gran.delta")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-detail-time" }, fmtTime(req.time)), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" + (net > 0 ? " lc-detail-metric-up" : net < 0 ? " lc-detail-metric-down" : "") }, t("tip.delta", { n: (net > 0 ? "+" : "") + fmt(net) })) : null, !delta && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.actual", { n: fmt(req.prompt) })) : null, !delta && req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.output", { n: fmt(req.output) })) : null, !delta && req.prompt !== void 0 && req.cacheRead !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.cache", { n: cacheHitPercent$1(req.cacheRead, req.prompt) ?? "—" })) : null),
|
|
4278
|
+
}, "✂ " + 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, {
|
|
3082
4279
|
brief: props.brief,
|
|
3083
4280
|
convOf: props.convOf,
|
|
3084
4281
|
onLocate: props.onLocate
|
|
3085
|
-
})
|
|
4282
|
+
}), /* @__PURE__ */ React.createElement(StackedBar, {
|
|
3086
4283
|
parts,
|
|
3087
4284
|
height: 10
|
|
3088
4285
|
}), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c, i) => {
|
|
@@ -3105,7 +4302,7 @@ window.__ModuleLoader__.load({
|
|
|
3105
4302
|
}
|
|
3106
4303
|
})), 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)}%` : ""));
|
|
3107
4304
|
})));
|
|
3108
|
-
};
|
|
4305
|
+
});
|
|
3109
4306
|
}
|
|
3110
4307
|
//#endregion
|
|
3111
4308
|
//#region src/client/cost.ts
|
|
@@ -3435,25 +4632,8 @@ window.__ModuleLoader__.load({
|
|
|
3435
4632
|
const scrollRef = React.useRef(null);
|
|
3436
4633
|
const scrolledOnce = React.useRef(false);
|
|
3437
4634
|
const lastGranRef = React.useRef(props.granularity);
|
|
3438
|
-
const
|
|
3439
|
-
|
|
3440
|
-
right: false
|
|
3441
|
-
});
|
|
3442
|
-
const edgesRef = React.useRef(edges);
|
|
3443
|
-
const updateEdges = (el) => {
|
|
3444
|
-
const left = el.scrollLeft > 4;
|
|
3445
|
-
const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 4;
|
|
3446
|
-
const prev = edgesRef.current;
|
|
3447
|
-
if (prev.left === left && prev.right === right) return;
|
|
3448
|
-
edgesRef.current = {
|
|
3449
|
-
left,
|
|
3450
|
-
right
|
|
3451
|
-
};
|
|
3452
|
-
setEdges({
|
|
3453
|
-
left,
|
|
3454
|
-
right
|
|
3455
|
-
});
|
|
3456
|
-
};
|
|
4635
|
+
const lastSeqRef = React.useRef(0);
|
|
4636
|
+
const prevScrollWidthRef = React.useRef(0);
|
|
3457
4637
|
/**
|
|
3458
4638
|
* 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
|
|
3459
4639
|
* (narrower-than-label blocks stay put); reads (offsetWidth) batch before writes (transform) to avoid layout thrash — out-of-view
|
|
@@ -3488,6 +4668,9 @@ window.__ModuleLoader__.load({
|
|
|
3488
4668
|
/* v8 ignore next 1 -- the scroll div renders unconditionally and React
|
|
3489
4669
|
attaches refs before layout effects run; el is never null here. */
|
|
3490
4670
|
if (el === null) return;
|
|
4671
|
+
const newestSeq = requests.length === 0 ? 0 : requests[requests.length - 1].seq;
|
|
4672
|
+
const grew = newestSeq !== lastSeqRef.current;
|
|
4673
|
+
const widthBeforeAppend = prevScrollWidthRef.current;
|
|
3491
4674
|
if (props.granularity !== lastGranRef.current) {
|
|
3492
4675
|
lastGranRef.current = props.granularity;
|
|
3493
4676
|
scrolledOnce.current = false;
|
|
@@ -3499,14 +4682,19 @@ window.__ModuleLoader__.load({
|
|
|
3499
4682
|
el.scrollLeft = Math.max(0, gi * 16 + BAR_W / 2 - el.clientWidth / 2);
|
|
3500
4683
|
}
|
|
3501
4684
|
props.onFocusTurnHandled();
|
|
3502
|
-
}
|
|
3503
|
-
if (!scrolledOnce.current) {
|
|
4685
|
+
} else if (!scrolledOnce.current) {
|
|
3504
4686
|
scrolledOnce.current = true;
|
|
3505
4687
|
el.scrollLeft = el.scrollWidth;
|
|
3506
|
-
} else if (el.scrollLeft + el.clientWidth >=
|
|
3507
|
-
|
|
4688
|
+
} else if (grew && el.scrollLeft + el.clientWidth >= widthBeforeAppend - 24) el.scrollLeft = el.scrollWidth;
|
|
4689
|
+
lastSeqRef.current = newestSeq;
|
|
4690
|
+
prevScrollWidthRef.current = el.scrollWidth;
|
|
3508
4691
|
updateTurnLabels(el);
|
|
3509
|
-
|
|
4692
|
+
syncTip(el);
|
|
4693
|
+
}, [
|
|
4694
|
+
props.granularity,
|
|
4695
|
+
props.focusTurn,
|
|
4696
|
+
requests
|
|
4697
|
+
]);
|
|
3510
4698
|
const tipOf = (req) => {
|
|
3511
4699
|
const head = req.stepCount !== void 0 && req.stepCount > 1 ? t("tip.turn", {
|
|
3512
4700
|
t: req.turn ?? 0,
|
|
@@ -3527,17 +4715,43 @@ window.__ModuleLoader__.load({
|
|
|
3527
4715
|
};
|
|
3528
4716
|
const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
|
|
3529
4717
|
const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
|
|
4718
|
+
const tipColRef = React.useRef(0);
|
|
4719
|
+
/**
|
|
4720
|
+
* Glue the hover tip to its bar's VISIBLE slice. The tip deliberately does NOT live inside the scrolling
|
|
4721
|
+
* content: an absolutely-positioned child of a scroller contributes to its scrollable overflow, so a wide
|
|
4722
|
+
* reply preview on a right-edge bar used to inflate scrollWidth on every hover and flap the horizontal
|
|
4723
|
+
* scrollbar open/closed — jumping the whole card. Reads (offsetWidth/clientWidth) batch before the single
|
|
4724
|
+
* style write; unchanged transforms write nothing.
|
|
4725
|
+
*/
|
|
4726
|
+
const syncTip = (el) => {
|
|
4727
|
+
/* v8 ignore next 1 -- the scroll div renders unconditionally while mounted, so its parent exists. */
|
|
4728
|
+
const tip = (el.parentElement ?? document.body).querySelector(".lc-chart-tip");
|
|
4729
|
+
if (tip === null) return;
|
|
4730
|
+
const lw = tip.offsetWidth;
|
|
4731
|
+
const cw = el.clientWidth;
|
|
4732
|
+
const half = Math.min(lw / 2, cw / 2);
|
|
4733
|
+
const cx = Math.min(Math.max(tipColRef.current - el.scrollLeft, half), cw - half);
|
|
4734
|
+
const next = `translate(${Math.round(cx - lw / 2)}px, 0)`;
|
|
4735
|
+
if (tip.style.transform !== next) tip.style.transform = next;
|
|
4736
|
+
};
|
|
4737
|
+
React.useLayoutEffect(() => {
|
|
4738
|
+
/* v8 ignore next 1 -- the scroll div renders unconditionally and React attaches refs before
|
|
4739
|
+
layout effects run; el is never null here. */
|
|
4740
|
+
if (scrollRef.current === null) return;
|
|
4741
|
+
tipColRef.current = hoveredIdx >= 0 ? hoveredIdx * 16 + BAR_W / 2 : 0;
|
|
4742
|
+
syncTip(scrollRef.current);
|
|
4743
|
+
});
|
|
3530
4744
|
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", {
|
|
3531
4745
|
className: "lc-axis-mid",
|
|
3532
4746
|
style: { top: `${13 + upPx}px` }
|
|
3533
|
-
}, "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", {
|
|
4747
|
+
}, "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", {
|
|
3534
4748
|
className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
|
|
3535
4749
|
ref: scrollRef,
|
|
3536
4750
|
onScroll: (e) => {
|
|
3537
|
-
updateEdges(e.currentTarget);
|
|
3538
4751
|
updateTurnLabels(e.currentTarget);
|
|
4752
|
+
syncTip(e.currentTarget);
|
|
3539
4753
|
}
|
|
3540
|
-
},
|
|
4754
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
3541
4755
|
className: "lc-chart",
|
|
3542
4756
|
onMouseLeave: () => {
|
|
3543
4757
|
props.onHover(null);
|
|
@@ -3558,10 +4772,7 @@ window.__ModuleLoader__.load({
|
|
|
3558
4772
|
deltaScale: delta ? deltaScale : void 0,
|
|
3559
4773
|
onSelect: props.onSelect,
|
|
3560
4774
|
onHover: props.onHover
|
|
3561
|
-
}))),
|
|
3562
|
-
className: "lc-chart-tip",
|
|
3563
|
-
style: { left: `${hoveredIdx * 16 + BAR_W / 2}px` }
|
|
3564
|
-
}, tipOf(hoveredReq)) : null, /* @__PURE__ */ React.createElement("div", {
|
|
4775
|
+
}))), /* @__PURE__ */ React.createElement("div", {
|
|
3565
4776
|
className: "lc-turns",
|
|
3566
4777
|
onMouseLeave: () => {
|
|
3567
4778
|
props.onHoverTurn(null);
|
|
@@ -3583,7 +4794,7 @@ window.__ModuleLoader__.load({
|
|
|
3583
4794
|
props.onPickTurn(grp.turn);
|
|
3584
4795
|
}
|
|
3585
4796
|
}, /* @__PURE__ */ React.createElement("span", { className: "lc-turn-label" }, `T${grp.turn}`));
|
|
3586
|
-
}))),
|
|
4797
|
+
}))), hoveredReq !== null ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-tip" }, tipOf(hoveredReq)) : null));
|
|
3587
4798
|
};
|
|
3588
4799
|
}
|
|
3589
4800
|
//#endregion
|
|
@@ -3607,6 +4818,7 @@ window.__ModuleLoader__.load({
|
|
|
3607
4818
|
const StatsBoard = makeStatsBoard(kit);
|
|
3608
4819
|
const PluginInfo = makePluginInfo(kit);
|
|
3609
4820
|
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
4821
|
+
const AgentGraph = makeAgentGraph(ctx, kit);
|
|
3610
4822
|
const ErrorBoundary = makeErrorBoundary(t);
|
|
3611
4823
|
function ContextViewBody(props) {
|
|
3612
4824
|
const sessionId = props.sessionId;
|
|
@@ -3676,16 +4888,6 @@ window.__ModuleLoader__.load({
|
|
|
3676
4888
|
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
3677
4889
|
return m;
|
|
3678
4890
|
}, [convNodes]);
|
|
3679
|
-
if (!data) return /* @__PURE__ */ React.createElement("div", {
|
|
3680
|
-
className: "lc-root",
|
|
3681
|
-
ref: rootRef
|
|
3682
|
-
}, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
|
|
3683
|
-
const markerOf = (req) => {
|
|
3684
|
-
const i = displayRequests.indexOf(req);
|
|
3685
|
-
/* v8 ignore next 1 -- the only caller passes displayRequests[activeIdx],
|
|
3686
|
-
an element of the very array indexOf scans. */
|
|
3687
|
-
return i >= 0 ? markers[i] : void 0;
|
|
3688
|
-
};
|
|
3689
4891
|
let pinnedIdx = -1;
|
|
3690
4892
|
for (let i = 0; i < displayRequests.length; i++) if (displayRequests[i].seq === selectedSeq) pinnedIdx = i;
|
|
3691
4893
|
const pinnedReq = pinnedIdx >= 0 ? displayRequests[pinnedIdx] : null;
|
|
@@ -3699,32 +4901,34 @@ window.__ModuleLoader__.load({
|
|
|
3699
4901
|
if (activeIdx < 0) activeIdx = pinnedIdx;
|
|
3700
4902
|
if (activeIdx < 0 && displayRequests.length > 0) activeIdx = displayRequests.length - 1;
|
|
3701
4903
|
const activeReq = activeIdx >= 0 ? displayRequests[activeIdx] : null;
|
|
3702
|
-
const brief = activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null;
|
|
3703
|
-
const convOf = (seq) => bySeq.get(seq);
|
|
3704
4904
|
let filesBefore = null;
|
|
3705
4905
|
if (activeReq !== null) {
|
|
3706
4906
|
const ri = requests.findIndex((r) => r.seq === activeReq.seq);
|
|
3707
4907
|
filesBefore = ri + 1 < requests.length ? requests[ri + 1].seq : null;
|
|
3708
4908
|
}
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
4909
|
+
const brief = React.useMemo(() => activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null, [
|
|
4910
|
+
activeReq,
|
|
4911
|
+
briefList,
|
|
4912
|
+
displayRequests,
|
|
4913
|
+
activeIdx
|
|
4914
|
+
]);
|
|
4915
|
+
const convOf = React.useCallback((seq) => bySeq.get(seq), [bySeq]);
|
|
4916
|
+
const fileActivity = React.useMemo(() => activityOf(briefList, convOf, filesBefore), [
|
|
4917
|
+
briefList,
|
|
4918
|
+
convOf,
|
|
4919
|
+
filesBefore
|
|
4920
|
+
]);
|
|
4921
|
+
const locateFileOp = React.useCallback((op) => {
|
|
4922
|
+
const seq = op.parent ?? op.seq;
|
|
4923
|
+
const step = locateStepOf(requests, seq, op.gone);
|
|
3720
4924
|
if (step === null) return;
|
|
3721
4925
|
setNodeFocus({
|
|
3722
4926
|
step,
|
|
3723
|
-
seq
|
|
4927
|
+
seq,
|
|
3724
4928
|
cat: "tool"
|
|
3725
4929
|
});
|
|
3726
|
-
};
|
|
3727
|
-
const locateNode = (node, isResponse) => {
|
|
4930
|
+
}, [requests]);
|
|
4931
|
+
const locateNode = React.useCallback((node, isResponse) => {
|
|
3728
4932
|
/* v8 ignore next 1 -- locateNode is only wired to brief rows, and
|
|
3729
4933
|
brief !== null guarantees activeReq !== null in the same closure. */
|
|
3730
4934
|
if (activeReq === null) return;
|
|
@@ -3735,7 +4939,30 @@ window.__ModuleLoader__.load({
|
|
|
3735
4939
|
seq: node.seq,
|
|
3736
4940
|
cat: node.cat
|
|
3737
4941
|
});
|
|
4942
|
+
}, [
|
|
4943
|
+
activeReq,
|
|
4944
|
+
activeIdx,
|
|
4945
|
+
displayRequests
|
|
4946
|
+
]);
|
|
4947
|
+
if (!data) return /* @__PURE__ */ React.createElement("div", {
|
|
4948
|
+
className: "lc-root",
|
|
4949
|
+
ref: rootRef
|
|
4950
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
|
|
4951
|
+
const markerOf = (req) => {
|
|
4952
|
+
const i = displayRequests.indexOf(req);
|
|
4953
|
+
/* v8 ignore next 1 -- the only caller passes displayRequests[activeIdx],
|
|
4954
|
+
an element of the very array indexOf scans. */
|
|
4955
|
+
return i >= 0 ? markers[i] : void 0;
|
|
3738
4956
|
};
|
|
4957
|
+
const head = headlineOf(data, pressure, breakdown);
|
|
4958
|
+
let fileScope = t("files.scopeLatest");
|
|
4959
|
+
if (activeReq !== null && filesBefore !== null) fileScope = activeReq.stepCount !== void 0 && activeReq.stepCount > 1 ? t("detail.turn", {
|
|
4960
|
+
t: activeReq.turn ?? 0,
|
|
4961
|
+
n: activeReq.stepCount
|
|
4962
|
+
}) : t("detail.step", {
|
|
4963
|
+
t: activeReq.turn ?? 0,
|
|
4964
|
+
s: activeReq.step ?? 0
|
|
4965
|
+
});
|
|
3739
4966
|
let activeTurn = hoverTurn;
|
|
3740
4967
|
if (activeTurn === null && hoveredSeq !== null) {
|
|
3741
4968
|
for (const req of displayRequests) if (req.seq === hoveredSeq) {
|
|
@@ -3743,9 +4970,9 @@ window.__ModuleLoader__.load({
|
|
|
3743
4970
|
break;
|
|
3744
4971
|
}
|
|
3745
4972
|
}
|
|
3746
|
-
const head = headlineOf(data, pressure, breakdown);
|
|
3747
4973
|
const localeSvc = ctx.get("locale");
|
|
3748
4974
|
const activeLocale = localeSvc !== void 0 && typeof localeSvc.getLocale === "function" ? localeSvc.getLocale().active : "en";
|
|
4975
|
+
const subtitle = (data.model ?? "") + (data.provider ? " · " + data.provider : "");
|
|
3749
4976
|
return /* @__PURE__ */ React.createElement("div", {
|
|
3750
4977
|
className: "lc-root",
|
|
3751
4978
|
ref: rootRef
|
|
@@ -3759,7 +4986,7 @@ window.__ModuleLoader__.load({
|
|
|
3759
4986
|
locale: activeLocale
|
|
3760
4987
|
}), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement(CurrentComposition, {
|
|
3761
4988
|
head,
|
|
3762
|
-
subtitle
|
|
4989
|
+
subtitle,
|
|
3763
4990
|
hoverKey: hoverCat,
|
|
3764
4991
|
onHoverKey: setHoverCat
|
|
3765
4992
|
}), /* @__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", {
|
|
@@ -3838,7 +5065,14 @@ window.__ModuleLoader__.load({
|
|
|
3838
5065
|
activity: fileActivity,
|
|
3839
5066
|
scope: fileScope,
|
|
3840
5067
|
onLocate: locateFileOp
|
|
3841
|
-
})), /* @__PURE__ */ React.createElement(
|
|
5068
|
+
})), /* @__PURE__ */ React.createElement(AgentGraph, {
|
|
5069
|
+
sessionId: typeof sessionId === "string" ? sessionId : void 0,
|
|
5070
|
+
self: {
|
|
5071
|
+
head,
|
|
5072
|
+
billed: usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null,
|
|
5073
|
+
requests: requests.length
|
|
5074
|
+
}
|
|
5075
|
+
}), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
|
|
3842
5076
|
}
|
|
3843
5077
|
return function ContextView(props) {
|
|
3844
5078
|
return h(ErrorBoundary, null, h(ContextViewBody, props));
|
|
@@ -3859,7 +5093,7 @@ window.__ModuleLoader__.load({
|
|
|
3859
5093
|
}
|
|
3860
5094
|
//#endregion
|
|
3861
5095
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles.css.mjs
|
|
3862
|
-
const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;scrollbar-gutter:stable;flex:1;min-width:0;padding-bottom:8px;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px 8px;margin-bottom:10px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary);font-size:13px}.lc-detail-time{font-variant-numeric:tabular-nums;white-space:nowrap;margin-left:auto;font-size:12px}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-metric{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 14%, transparent);border-radius:6px;padding:2px 8px;font-size:12px;font-weight:600}.lc-detail-metric-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-detail-metric-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-brief{flex-direction:column;gap:2px;margin:-4px 0 10px;display:flex}.lc-brief-row{min-width:0;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;margin-left:-6px;margin-right:-6px;padding:3px 6px;font-family:inherit;font-size:12px;line-height:1.5;transition:background-color .12s;display:flex}.lc-brief-row-link:hover{background:var(--dsw-alias-interactive-bg-hover)}button.lc-brief-row-link{cursor:pointer}.lc-brief-tag{box-sizing:border-box;text-align:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:56px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;position:relative}.lc-brief-tip{z-index:6;white-space:normal;text-align:left;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:240px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-brief-tag:hover .lc-brief-tip{opacity:1}.lc-brief-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-fact{box-sizing:border-box;text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:160px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:0;max-width:260px;color:var(--dsw-alias-label-primary);border-radius:5px;align-items:center;gap:5px;padding:0 7px;font-size:12px;transition:background-color .12s;display:inline-flex;overflow:hidden}.lc-brief-chip-grow{flex:1;max-width:none}.lc-brief-chip-tag{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);max-width:120px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-chip-link{cursor:pointer}.lc-brief-chip-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-brief-more{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;position:relative;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-bar-zero{border-left:1px solid #80808073;position:absolute;top:0;bottom:0;left:50%}.lc-bar-fill-up,.lc-bar-fill-down{height:100%;display:block;position:absolute;top:0}.lc-bar-fill-up{border-radius:0 3px 3px 0;left:50%}.lc-bar-fill-down{border-radius:3px 0 0 3px;right:50%}.lc-detail-num{text-align:right;font-variant-numeric:tabular-nums;width:52px}.lc-detail-num-up{color:var(--dsw-alias-state-success-primary)}.lc-detail-num-down{color:var(--dsw-alias-state-error-primary)}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-fa-ctl{align-items:center;gap:8px;margin-bottom:8px;display:flex}.lc-fa-ctl .lc-gran{margin-left:0}.lc-fa-n{opacity:.75;margin-left:4px;font-weight:700}.lc-fa-search{min-width:80px;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;outline:none;flex:1;padding:3px 8px;font-size:12px}.lc-fa-search:focus{border-color:var(--dsw-alias-label-dimmed)}.lc-fa-meta{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;margin-bottom:6px;font-size:12px;display:flex}.lc-fa-meta .lc-fa-sort{flex:none}.lc-fa-meta-delta{position:relative}.lc-fa-meta-tip{z-index:6;white-space:normal;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:260px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-fa-meta-delta:hover .lc-fa-meta-tip{opacity:1}.lc-fa-item{border:1px solid #0000;border-radius:8px;transition:background-color .12s,border-color .12s}.lc-fa-item-on{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l1)}.lc-fa-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:7px;align-items:center;gap:7px;padding:6px 8px;font-size:12px;transition:background-color .12s;display:flex}.lc-fa-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-form{text-align:center;opacity:.85;flex:none;width:16px;font-size:11px}.lc-fa-path{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-fa-path em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-fa-badge{font-variant-numeric:tabular-nums;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:1px 7px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-badge i{background:currentColor;border-radius:50%;width:5px;height:5px;display:inline-block}.lc-fa-b-read{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent)}.lc-fa-b-write{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent)}.lc-fa-b-search{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent)}.lc-fa-chip-read,.lc-fa-chip-read:hover{color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write,.lc-fa-chip-write:hover{color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search,.lc-fa-chip-search:hover{color:var(--dsw-alias-state-warn-primary)}.lc-fa-chip-read.lc-gran-on,.lc-fa-chip-read.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write.lc-gran-on,.lc-fa-chip-write.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search.lc-gran-on,.lc-fa-chip-search.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);color:var(--dsw-alias-state-warn-primary)}.lc-fa-delta{font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;gap:5px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-up{color:var(--dsw-alias-state-success-primary)}.lc-fa-down{color:var(--dsw-alias-state-error-primary)}.lc-fa-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.lc-fa-ops{border-left:2px solid var(--dsw-alias-border-l1);flex-direction:column;gap:1px;margin:0 8px 8px 15px;padding:2px 0 2px 10px;display:flex}.lc-fa-op{min-width:0;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;background:0 0;border:0;border-radius:5px;align-items:center;gap:8px;padding:3px 6px;font-size:11px;transition:background-color .12s;display:flex}.lc-fa-op-link{cursor:pointer}.lc-fa-op-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-op-time{width:58px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none}.lc-fa-op-tool{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:0 6px;font-size:11px}.lc-fa-op-detail{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;overflow:hidden}.lc-fa-more{color:var(--dsw-alias-label-secondary);padding:2px 6px;font-size:11px}.lc-fa-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-retry:hover{filter:brightness(1.15)}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
|
|
5096
|
+
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-hint{text-decoration:none}.lc-pi-hint:hover{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}";
|
|
3863
5097
|
const tagId = "dsh-context/styles.css";
|
|
3864
5098
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
3865
5099
|
const tag = document.createElement("style");
|