dsh-context 0.33.1 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -2
- package/lib/client.js +786 -119
- 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,28 @@ 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.moreOps": "+{n} 次更早操作",
|
|
103
102
|
"files.errs": "{n} 次失败",
|
|
104
103
|
"files.form.image": "图片",
|
|
105
104
|
"files.form.dir": "目录",
|
|
106
105
|
"files.form.text": "文件",
|
|
106
|
+
"agents.title": "Agent 网络",
|
|
107
|
+
"agents.sub": "当前 Agent 与子 Agent 的实时上下文 · 点击节点跳转会话",
|
|
108
|
+
"agents.chip.count": "{n} 个 Agent",
|
|
109
|
+
"agents.chip.running": "{n} 个运行中",
|
|
110
|
+
"agents.chip.tokens": "上下文合计 {n}",
|
|
111
|
+
"agents.more": "另有 {n} 个未显示",
|
|
112
|
+
"agents.solo": "还没有子 Agent — 当 Agent 通过 subagent 工具委派任务时,协作网络会在这里展开",
|
|
113
|
+
"agents.self": "当前",
|
|
114
|
+
"agents.running": "运行中",
|
|
115
|
+
"agents.legend.free": "空闲窗口",
|
|
116
|
+
"agents.oneshot": "一次性",
|
|
117
|
+
"agents.continuable": "多轮",
|
|
118
|
+
"agents.requests": "{n} 次请求",
|
|
119
|
+
"agents.billed": "累计计费 {n}",
|
|
120
|
+
"agents.open": "点击打开会话 →",
|
|
107
121
|
"loading": "正在读取会话日志…",
|
|
108
122
|
"error": "上下文数据读取失败:",
|
|
109
123
|
"error.retry": "重试",
|
|
@@ -242,10 +256,11 @@ window.__ModuleLoader__.load({
|
|
|
242
256
|
"brief.input": "In",
|
|
243
257
|
"brief.reply": "Response",
|
|
244
258
|
"brief.more": "+{n}",
|
|
259
|
+
"brief.noInputs": "(nothing new)",
|
|
245
260
|
"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)",
|
|
261
|
+
"brief.turnTip": "The user message that opened this turn — every step of the turn recalls it",
|
|
262
|
+
"brief.inputTip": "What entered after the previous reply and before this request went out — usually the previous tool-call results; a turn opener has nothing new",
|
|
263
|
+
"brief.replyTip": "What the model returned on this step (a text reply or tool calls) — results of calls made here appear in the In row of the next bar",
|
|
249
264
|
"events.title": "Context Events",
|
|
250
265
|
"events.empty": "No context events yet (compaction, injections, model switches appear here)",
|
|
251
266
|
"events.at": "Turn {t} · Step {s}",
|
|
@@ -271,15 +286,28 @@ window.__ModuleLoader__.load({
|
|
|
271
286
|
"files.sortTip": "Sort files by operation counts (of the selected kind when a kind chip is active), most recent activity, or path",
|
|
272
287
|
"files.files": "{n} files",
|
|
273
288
|
"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
289
|
"files.empty": "No file reads, writes, or searches in this range",
|
|
276
290
|
"files.noMatch": "No files match the current filters",
|
|
277
291
|
"files.locate": "Reveal this operation in the Context Browser",
|
|
278
|
-
"files.moreOps": "+{n} earlier ops",
|
|
279
292
|
"files.errs": "{n} failed",
|
|
280
293
|
"files.form.image": "Image",
|
|
281
294
|
"files.form.dir": "Directory",
|
|
282
295
|
"files.form.text": "File",
|
|
296
|
+
"agents.title": "Agent Network",
|
|
297
|
+
"agents.sub": "Live context of this agent and its subagents · click a node to open its session",
|
|
298
|
+
"agents.chip.count": "{n} agents",
|
|
299
|
+
"agents.chip.running": "{n} running",
|
|
300
|
+
"agents.chip.tokens": "{n} tokens in context",
|
|
301
|
+
"agents.more": "{n} more not shown",
|
|
302
|
+
"agents.solo": "No subagents yet — when the agent delegates work through the subagent tool, the network unfolds here",
|
|
303
|
+
"agents.self": "current",
|
|
304
|
+
"agents.running": "running",
|
|
305
|
+
"agents.legend.free": "free window",
|
|
306
|
+
"agents.oneshot": "one-shot",
|
|
307
|
+
"agents.continuable": "continuable",
|
|
308
|
+
"agents.requests": "{n} requests",
|
|
309
|
+
"agents.billed": "{n} billed",
|
|
310
|
+
"agents.open": "click to open →",
|
|
283
311
|
"loading": "Reading the session log…",
|
|
284
312
|
"error": "Failed to read context data: ",
|
|
285
313
|
"error.retry": "Retry",
|
|
@@ -578,7 +606,7 @@ window.__ModuleLoader__.load({
|
|
|
578
606
|
* every field read below must re-prove itself (the no-white-screen
|
|
579
607
|
* guarantee), so no field may borrow the wire type before its check.
|
|
580
608
|
*/
|
|
581
|
-
function asRecord(value) {
|
|
609
|
+
function asRecord$1(value) {
|
|
582
610
|
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
583
611
|
return value;
|
|
584
612
|
}
|
|
@@ -607,7 +635,7 @@ window.__ModuleLoader__.load({
|
|
|
607
635
|
* during render and unmounting the conversation view.
|
|
608
636
|
*/
|
|
609
637
|
function timelineOf(value) {
|
|
610
|
-
const data = asRecord(value);
|
|
638
|
+
const data = asRecord$1(value);
|
|
611
639
|
if (data === null) return null;
|
|
612
640
|
const current = data.current;
|
|
613
641
|
if (current !== null && typeof current === "object" && [
|
|
@@ -655,7 +683,7 @@ window.__ModuleLoader__.load({
|
|
|
655
683
|
* back to their derived anchor, so the UI degrades gracefully.
|
|
656
684
|
*/
|
|
657
685
|
function contextPressureOf(value) {
|
|
658
|
-
return asRecord(value);
|
|
686
|
+
return asRecord$1(value);
|
|
659
687
|
}
|
|
660
688
|
/**
|
|
661
689
|
* Narrow a delivered projection value to the official token-meter
|
|
@@ -665,7 +693,7 @@ window.__ModuleLoader__.load({
|
|
|
665
693
|
* own sums instead of mixing sources.
|
|
666
694
|
*/
|
|
667
695
|
function contextBreakdownOf(value) {
|
|
668
|
-
const data = asRecord(value);
|
|
696
|
+
const data = asRecord$1(value);
|
|
669
697
|
if (data === null) return null;
|
|
670
698
|
const { systemTokens, toolsTokens, messageTokens } = data;
|
|
671
699
|
if (typeof systemTokens !== "number" || !Number.isFinite(systemTokens)) return null;
|
|
@@ -684,7 +712,7 @@ window.__ModuleLoader__.load({
|
|
|
684
712
|
* usage yet) — callers drop the cache-hit cell to a dash.
|
|
685
713
|
*/
|
|
686
714
|
function tokenUsageOf(value) {
|
|
687
|
-
return asRecord(value);
|
|
715
|
+
return asRecord$1(value);
|
|
688
716
|
}
|
|
689
717
|
/**
|
|
690
718
|
* Narrow a delivered projection value to the plugin's `contextHeaders`
|
|
@@ -698,7 +726,7 @@ window.__ModuleLoader__.load({
|
|
|
698
726
|
* and the card falls back to its tokens-only note.
|
|
699
727
|
*/
|
|
700
728
|
function headersOf(value) {
|
|
701
|
-
const headers = asRecord(value);
|
|
729
|
+
const headers = asRecord$1(value);
|
|
702
730
|
if (headers === null || !Array.isArray(headers.headers)) return null;
|
|
703
731
|
for (const h of headers.headers) {
|
|
704
732
|
if (h === null || typeof h !== "object") return null;
|
|
@@ -2512,21 +2540,17 @@ window.__ModuleLoader__.load({
|
|
|
2512
2540
|
removed: 0
|
|
2513
2541
|
};
|
|
2514
2542
|
const byPath = /* @__PURE__ */ new Map();
|
|
2515
|
-
let unresolved = 0;
|
|
2516
2543
|
for (const n of nodes) {
|
|
2517
2544
|
if (n.cat !== "tool") continue;
|
|
2518
2545
|
if (before !== null && n.seq >= before) continue;
|
|
2519
2546
|
const kind = kindOfTool(n.tool);
|
|
2520
2547
|
if (kind === null) continue;
|
|
2521
2548
|
const tool = n.tool;
|
|
2522
|
-
totals[kind].ops++;
|
|
2523
2549
|
const conv = convOf(n.seq);
|
|
2524
2550
|
const args = parseCallArgs(conv?.call?.argsRaw);
|
|
2525
2551
|
const path = pathOfArgs(tool, args);
|
|
2526
|
-
if (
|
|
2527
|
-
|
|
2528
|
-
continue;
|
|
2529
|
-
}
|
|
2552
|
+
if (path === null || args === null) continue;
|
|
2553
|
+
totals[kind].ops++;
|
|
2530
2554
|
const { added, removed } = deltaOf(tool, args);
|
|
2531
2555
|
const detail = kind === "search" && typeof args.path === "string" && args.path !== "" && typeof args.pattern === "string" && args.pattern !== "" ? args.pattern : void 0;
|
|
2532
2556
|
const op = {
|
|
@@ -2579,8 +2603,7 @@ window.__ModuleLoader__.load({
|
|
|
2579
2603
|
entries.sort((a, b) => b.ops[0].seq - a.ops[0].seq);
|
|
2580
2604
|
return {
|
|
2581
2605
|
entries,
|
|
2582
|
-
totals
|
|
2583
|
-
unresolved
|
|
2606
|
+
totals
|
|
2584
2607
|
};
|
|
2585
2608
|
}
|
|
2586
2609
|
/**
|
|
@@ -2594,6 +2617,588 @@ window.__ModuleLoader__.load({
|
|
|
2594
2617
|
for (const r of requests) if (r.seq > seq && (gone === void 0 || gone > r.seq)) return r.seq;
|
|
2595
2618
|
return gone === void 0 ? "live" : null;
|
|
2596
2619
|
}
|
|
2620
|
+
const SLOT_MAX = 184;
|
|
2621
|
+
const SLOT_MIN = 112;
|
|
2622
|
+
const CAPTION_GUTTER = 8;
|
|
2623
|
+
const LEVEL_H = 154;
|
|
2624
|
+
const CELL_H = 90;
|
|
2625
|
+
const PAD_Y = 56;
|
|
2626
|
+
function asRecord(value) {
|
|
2627
|
+
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
2628
|
+
return value;
|
|
2629
|
+
}
|
|
2630
|
+
/** Narrow one list-row value; null when it is not a row at all. */
|
|
2631
|
+
function agentRowOf(value) {
|
|
2632
|
+
const rec = asRecord(value);
|
|
2633
|
+
if (rec === null) return null;
|
|
2634
|
+
const projections = asRecord(rec.projectionValues);
|
|
2635
|
+
return {
|
|
2636
|
+
...typeof rec.displayTitle === "string" ? { displayTitle: rec.displayTitle } : {},
|
|
2637
|
+
...typeof rec.title === "string" ? { title: rec.title } : {},
|
|
2638
|
+
...typeof rec.parentId === "string" ? { parentId: rec.parentId } : {},
|
|
2639
|
+
...typeof rec.origin === "string" ? { origin: rec.origin } : {},
|
|
2640
|
+
running: rec.running === true,
|
|
2641
|
+
completed: rec.completed === true,
|
|
2642
|
+
blank: rec.blank === true,
|
|
2643
|
+
updatedAt: numOf(rec.updatedAt),
|
|
2644
|
+
...projections !== null ? { projections } : {}
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
/** Narrow the subagent identity projection value (null = not a descriptor-backed subagent). */
|
|
2648
|
+
function agentIdentityOf(value) {
|
|
2649
|
+
const rec = asRecord(value);
|
|
2650
|
+
if (rec === null) return null;
|
|
2651
|
+
if (rec.mode !== "one-shot" && rec.mode !== "continuable") return null;
|
|
2652
|
+
return {
|
|
2653
|
+
mode: rec.mode,
|
|
2654
|
+
...typeof rec.label === "string" && rec.label !== "" ? { label: rec.label } : {}
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
/** Narrow the subagent timing projection value into a single duration (null = absent/malformed). */
|
|
2658
|
+
function agentDurationOf(value) {
|
|
2659
|
+
const rec = asRecord(value);
|
|
2660
|
+
if (rec === null) return null;
|
|
2661
|
+
const settled = numOf(rec.settledMs);
|
|
2662
|
+
const active = asRecord(rec.active);
|
|
2663
|
+
const total = settled + (active !== null ? Math.max(0, numOf(active.through) - numOf(active.since)) : 0);
|
|
2664
|
+
return total > 0 ? total : null;
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* Fold one row's projection values into render-ready stats. The composition
|
|
2668
|
+
* prefers the plugin's own `contextTimeline` (six buckets, provider-anchored
|
|
2669
|
+
* by token-meter's pressure/breakdown — the exact headlineOf derivation the
|
|
2670
|
+
* overview card shows); a pressure-only row (plugin host half absent for that
|
|
2671
|
+
* session) still yields an occupancy ring without slices.
|
|
2672
|
+
*/
|
|
2673
|
+
function agentStatsOf(values) {
|
|
2674
|
+
const timeline = timelineOf(values?.contextTimeline);
|
|
2675
|
+
const pressure = contextPressureOf(values?.contextPressure);
|
|
2676
|
+
const breakdown = contextBreakdownOf(values?.contextBreakdown);
|
|
2677
|
+
const usage = tokenUsageOf(values?.tokenUsage);
|
|
2678
|
+
let head = null;
|
|
2679
|
+
if (timeline !== null) head = headlineOf(timeline, pressure, breakdown);
|
|
2680
|
+
else if (pressure !== null) {
|
|
2681
|
+
const tokens = typeof pressure.projectedTokens === "number" ? pressure.projectedTokens : typeof pressure.pressureTokens === "number" ? pressure.pressureTokens : null;
|
|
2682
|
+
if (tokens !== null) {
|
|
2683
|
+
const window = typeof pressure.contextWindow === "number" && pressure.contextWindow > 0 ? pressure.contextWindow : void 0;
|
|
2684
|
+
head = {
|
|
2685
|
+
tokens,
|
|
2686
|
+
window,
|
|
2687
|
+
pct: window !== void 0 ? Math.min(100, Math.round(tokens / window * 100)) : null,
|
|
2688
|
+
parts: []
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
const billed = usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null;
|
|
2693
|
+
return {
|
|
2694
|
+
head,
|
|
2695
|
+
requests: timeline !== null ? timeline.requests.length : 0,
|
|
2696
|
+
billed,
|
|
2697
|
+
durationMs: agentDurationOf(values?.subagentTiming),
|
|
2698
|
+
identity: agentIdentityOf(values?.subagent)
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Build the current session's agent family from a session-list snapshot:
|
|
2703
|
+
* walk up `parentId` to the topmost known ancestor, then DFS its whole
|
|
2704
|
+
* subtree (blank placeholder rows excluded). Null when there is no anchor —
|
|
2705
|
+
* no current session, or a snapshot without a `byId` table (older harness).
|
|
2706
|
+
* The current session synthesizes a row when the list has not delivered it
|
|
2707
|
+
* yet, so the card can still show its live self stats.
|
|
2708
|
+
*/
|
|
2709
|
+
function agentForestOf(snapshot, currentId, self) {
|
|
2710
|
+
const byId = asRecord(asRecord(snapshot)?.byId);
|
|
2711
|
+
if (byId === null || currentId === void 0 || currentId === "") return null;
|
|
2712
|
+
const rows = /* @__PURE__ */ new Map();
|
|
2713
|
+
for (const key of Object.keys(byId)) {
|
|
2714
|
+
const row = agentRowOf(byId[key]);
|
|
2715
|
+
if (row !== null && (!row.blank || key === currentId)) rows.set(key, row);
|
|
2716
|
+
}
|
|
2717
|
+
if (!rows.has(currentId)) rows.set(currentId, {
|
|
2718
|
+
running: false,
|
|
2719
|
+
completed: false,
|
|
2720
|
+
blank: false,
|
|
2721
|
+
updatedAt: 0
|
|
2722
|
+
});
|
|
2723
|
+
let root = currentId;
|
|
2724
|
+
const chain = /* @__PURE__ */ new Set([currentId]);
|
|
2725
|
+
for (;;) {
|
|
2726
|
+
const parent = rows.get(root)?.parentId;
|
|
2727
|
+
if (parent === void 0 || !rows.has(parent) || chain.has(parent)) break;
|
|
2728
|
+
chain.add(parent);
|
|
2729
|
+
root = parent;
|
|
2730
|
+
}
|
|
2731
|
+
const rootRow = rows.get(root);
|
|
2732
|
+
/* v8 ignore next 2 -- root is currentId (inserted above) or a parent
|
|
2733
|
+
verified with rows.has, so its row always exists. */
|
|
2734
|
+
if (rootRow === void 0) return null;
|
|
2735
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
2736
|
+
for (const [id, row] of rows) {
|
|
2737
|
+
if (row.parentId === void 0 || !rows.has(row.parentId)) continue;
|
|
2738
|
+
const list = childrenOf.get(row.parentId) ?? [];
|
|
2739
|
+
list.push({
|
|
2740
|
+
id,
|
|
2741
|
+
row
|
|
2742
|
+
});
|
|
2743
|
+
childrenOf.set(row.parentId, list);
|
|
2744
|
+
}
|
|
2745
|
+
for (const kids of childrenOf.values()) kids.sort((a, b) => {
|
|
2746
|
+
const runDelta = Number(b.row.running) - Number(a.row.running);
|
|
2747
|
+
if (runDelta !== 0) return runDelta;
|
|
2748
|
+
const timeDelta = b.row.updatedAt - a.row.updatedAt;
|
|
2749
|
+
return timeDelta !== 0 ? timeDelta : a.id < b.id ? -1 : 1;
|
|
2750
|
+
});
|
|
2751
|
+
const measure = (id, seen) => {
|
|
2752
|
+
if (seen.has(id)) return 0;
|
|
2753
|
+
seen.add(id);
|
|
2754
|
+
let total = 1;
|
|
2755
|
+
for (const kid of childrenOf.get(id) ?? []) total += measure(kid.id, seen);
|
|
2756
|
+
return total;
|
|
2757
|
+
};
|
|
2758
|
+
const total = measure(root, /* @__PURE__ */ new Set());
|
|
2759
|
+
const nodes = [];
|
|
2760
|
+
const edges = [];
|
|
2761
|
+
const visit = (id, row, parentId, depth, seen, family) => {
|
|
2762
|
+
if (seen.has(id) || nodes.length >= 25) return;
|
|
2763
|
+
seen.add(id);
|
|
2764
|
+
const stats = agentStatsOf(row.projections);
|
|
2765
|
+
const identity = stats.identity;
|
|
2766
|
+
const node = {
|
|
2767
|
+
...stats,
|
|
2768
|
+
id,
|
|
2769
|
+
label: identity?.label ?? row.title ?? row.displayTitle ?? id,
|
|
2770
|
+
...parentId !== void 0 ? { parentId } : {},
|
|
2771
|
+
depth,
|
|
2772
|
+
family,
|
|
2773
|
+
isCurrent: id === currentId,
|
|
2774
|
+
running: row.running,
|
|
2775
|
+
completed: row.completed,
|
|
2776
|
+
subagent: row.origin === "subagent" || identity !== null
|
|
2777
|
+
};
|
|
2778
|
+
if (node.isCurrent && self !== void 0) {
|
|
2779
|
+
node.head = self.head ?? node.head;
|
|
2780
|
+
node.billed = self.billed ?? node.billed;
|
|
2781
|
+
node.requests = self.requests > 0 ? self.requests : node.requests;
|
|
2782
|
+
}
|
|
2783
|
+
nodes.push(node);
|
|
2784
|
+
if (parentId !== void 0) edges.push({
|
|
2785
|
+
from: parentId,
|
|
2786
|
+
to: id
|
|
2787
|
+
});
|
|
2788
|
+
(childrenOf.get(id) ?? []).forEach((kid, ki) => {
|
|
2789
|
+
visit(kid.id, kid.row, id, depth + 1, seen, depth === 0 ? ki : family);
|
|
2790
|
+
});
|
|
2791
|
+
};
|
|
2792
|
+
visit(root, rootRow, void 0, 0, /* @__PURE__ */ new Set(), -1);
|
|
2793
|
+
return {
|
|
2794
|
+
nodes,
|
|
2795
|
+
edges,
|
|
2796
|
+
overflow: Math.max(0, total - nodes.length),
|
|
2797
|
+
solo: nodes.length === 1
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* Tidy top-down tree layout: one row per depth level, siblings claim leaf
|
|
2802
|
+
* slots, parents center over their children. Fully responsive to the stage's
|
|
2803
|
+
* visible width: the slot pitch stretches up to SLOT_MAX and compresses down
|
|
2804
|
+
* to SLOT_MIN (captions wrap tighter); a level that still overflows wraps
|
|
2805
|
+
* into bands of at most a per-level node count derived from the stage width —
|
|
2806
|
+
* vertical room is cheaper than horizontal scrolling. Links exit a parent at
|
|
2807
|
+
* its cell bottom (below the caption zone) and enter the child at its top,
|
|
2808
|
+
* so a connector never crosses a label.
|
|
2809
|
+
*/
|
|
2810
|
+
function layoutForest(forest, stageWidth = 0) {
|
|
2811
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
2812
|
+
for (const n of forest.nodes) {
|
|
2813
|
+
if (n.parentId === void 0) continue;
|
|
2814
|
+
const kids = childrenOf.get(n.parentId) ?? [];
|
|
2815
|
+
kids.push(n);
|
|
2816
|
+
childrenOf.set(n.parentId, kids);
|
|
2817
|
+
}
|
|
2818
|
+
const slotOf = /* @__PURE__ */ new Map();
|
|
2819
|
+
let leafSlots = 0;
|
|
2820
|
+
const place = (node) => {
|
|
2821
|
+
const kids = childrenOf.get(node.id) ?? [];
|
|
2822
|
+
if (kids.length === 0) {
|
|
2823
|
+
const slot = leafSlots;
|
|
2824
|
+
leafSlots++;
|
|
2825
|
+
slotOf.set(node.id, slot);
|
|
2826
|
+
return slot;
|
|
2827
|
+
}
|
|
2828
|
+
let first = 0;
|
|
2829
|
+
let last = 0;
|
|
2830
|
+
kids.forEach((kid, index) => {
|
|
2831
|
+
const slot = place(kid);
|
|
2832
|
+
if (index === 0) first = slot;
|
|
2833
|
+
last = slot;
|
|
2834
|
+
});
|
|
2835
|
+
const slot = (first + last) / 2;
|
|
2836
|
+
slotOf.set(node.id, slot);
|
|
2837
|
+
return slot;
|
|
2838
|
+
};
|
|
2839
|
+
/* v8 ignore next 1 -- a forest always holds at least the (possibly
|
|
2840
|
+
synthesized) current node. */
|
|
2841
|
+
if (forest.nodes.length > 0) place(forest.nodes[0]);
|
|
2842
|
+
const perLevel = stageWidth > 0 ? Math.max(2, Math.floor(stageWidth / SLOT_MIN)) : 0;
|
|
2843
|
+
if (perLevel > 0 && leafSlots > perLevel) {
|
|
2844
|
+
const bandSlot = Math.min(SLOT_MAX, stageWidth / perLevel);
|
|
2845
|
+
const width = perLevel * bandSlot;
|
|
2846
|
+
const points = [];
|
|
2847
|
+
let row = 0;
|
|
2848
|
+
const emitBand = (nodes, depth) => {
|
|
2849
|
+
const inset = (width - nodes.length * bandSlot) / 2;
|
|
2850
|
+
nodes.forEach((node, i) => {
|
|
2851
|
+
points.push({
|
|
2852
|
+
id: node.id,
|
|
2853
|
+
x: inset + (i + .5) * bandSlot,
|
|
2854
|
+
y: PAD_Y + row * LEVEL_H,
|
|
2855
|
+
depth
|
|
2856
|
+
});
|
|
2857
|
+
});
|
|
2858
|
+
row++;
|
|
2859
|
+
let band = [];
|
|
2860
|
+
const flush = () => {
|
|
2861
|
+
if (band.length === 0) return;
|
|
2862
|
+
const packed = band;
|
|
2863
|
+
band = [];
|
|
2864
|
+
emitBand(packed, depth + 1);
|
|
2865
|
+
};
|
|
2866
|
+
for (const node of nodes) {
|
|
2867
|
+
const kids = childrenOf.get(node.id) ?? [];
|
|
2868
|
+
for (let start = 0; start < kids.length; start += perLevel) {
|
|
2869
|
+
const group = kids.slice(start, start + perLevel);
|
|
2870
|
+
if (band.length + group.length > perLevel) flush();
|
|
2871
|
+
band.push(...group);
|
|
2872
|
+
if (band.length === perLevel) flush();
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
flush();
|
|
2876
|
+
};
|
|
2877
|
+
/* v8 ignore next 1 -- a forest always holds at least the current node. */
|
|
2878
|
+
if (forest.nodes.length > 0) emitBand([forest.nodes[0]], 0);
|
|
2879
|
+
return {
|
|
2880
|
+
width,
|
|
2881
|
+
height: PAD_Y + (row - 1) * LEVEL_H + CELL_H + 28,
|
|
2882
|
+
captionW: bandSlot - CAPTION_GUTTER,
|
|
2883
|
+
points,
|
|
2884
|
+
links: linksOf(forest, points)
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
const slot = stageWidth > 0 && leafSlots > 1 ? Math.min(SLOT_MAX, stageWidth / leafSlots) : SLOT_MAX;
|
|
2888
|
+
const points = forest.nodes.map((node) => ({
|
|
2889
|
+
id: node.id,
|
|
2890
|
+
/* v8 ignore next 1 -- place() visits every node: the forest is exactly
|
|
2891
|
+
the root's subtree by construction. */
|
|
2892
|
+
x: (slotOf.get(node.id) ?? 0) * slot + slot / 2,
|
|
2893
|
+
y: PAD_Y + node.depth * LEVEL_H,
|
|
2894
|
+
depth: node.depth
|
|
2895
|
+
}));
|
|
2896
|
+
const maxDepth = points.reduce((max, p) => Math.max(max, p.depth), 0);
|
|
2897
|
+
return {
|
|
2898
|
+
width: leafSlots * slot,
|
|
2899
|
+
height: PAD_Y + maxDepth * LEVEL_H + CELL_H + 28,
|
|
2900
|
+
captionW: slot - CAPTION_GUTTER,
|
|
2901
|
+
points,
|
|
2902
|
+
links: linksOf(forest, points)
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
/**
|
|
2906
|
+
* Family hue by level-1 subtree index: the golden angle keeps consecutive
|
|
2907
|
+
* families maximally separated on the color wheel without a hand-tuned palette.
|
|
2908
|
+
*/
|
|
2909
|
+
function familyHue(index) {
|
|
2910
|
+
return `hsl(${Math.round(index * 137.508) % 360} 58% 52%)`;
|
|
2911
|
+
}
|
|
2912
|
+
/** Parent→child links: exit the parent's cell bottom, enter the child's top. */
|
|
2913
|
+
function linksOf(forest, points) {
|
|
2914
|
+
const pointOf = new Map(points.map((p) => [p.id, p]));
|
|
2915
|
+
const nodeOf = new Map(forest.nodes.map((n) => [n.id, n]));
|
|
2916
|
+
const runningIds = new Set(forest.nodes.filter((n) => n.running).map((n) => n.id));
|
|
2917
|
+
const links = [];
|
|
2918
|
+
for (const edge of forest.edges) {
|
|
2919
|
+
const from = pointOf.get(edge.from);
|
|
2920
|
+
const to = pointOf.get(edge.to);
|
|
2921
|
+
/* v8 ignore next 2 -- edges are emitted only for visited parent/child
|
|
2922
|
+
pairs, so both points always exist. */
|
|
2923
|
+
if (from === void 0 || to === void 0) continue;
|
|
2924
|
+
links.push({
|
|
2925
|
+
to: edge.to,
|
|
2926
|
+
running: runningIds.has(edge.to),
|
|
2927
|
+
/* v8 ignore next 1 -- edges only connect visited nodes. */
|
|
2928
|
+
color: familyHue(nodeOf.get(edge.to)?.family ?? 0),
|
|
2929
|
+
x1: from.x,
|
|
2930
|
+
y1: from.y + CELL_H,
|
|
2931
|
+
x2: to.x,
|
|
2932
|
+
y2: to.y - 26 - 10
|
|
2933
|
+
});
|
|
2934
|
+
}
|
|
2935
|
+
return links;
|
|
2936
|
+
}
|
|
2937
|
+
/**
|
|
2938
|
+
* One fused ring per agent — the exact semantics of the chat composer's own
|
|
2939
|
+
* context ring: the composition parts, scaled to the occupancy share of the
|
|
2940
|
+
* window, fill the circle, and a neutral remainder marks the free window.
|
|
2941
|
+
* With no known window the composition fills the whole circle; with no
|
|
2942
|
+
* composition (pressure-only rows) a single threshold-colored arc carries
|
|
2943
|
+
* the occupancy; a known window with zero occupancy draws the free outline.
|
|
2944
|
+
*/
|
|
2945
|
+
function ringSegments(parts, pct, radius, fallbackColor) {
|
|
2946
|
+
const circumference = 2 * Math.PI * radius;
|
|
2947
|
+
const occ = pct === null ? 1 : Math.min(100, Math.max(0, pct)) / 100;
|
|
2948
|
+
let total = 0;
|
|
2949
|
+
for (const p of parts) total += p.value > 0 ? p.value : 0;
|
|
2950
|
+
const segs = [];
|
|
2951
|
+
let offset = 0;
|
|
2952
|
+
if (total > 0) for (const p of parts) {
|
|
2953
|
+
if (p.value <= 0) continue;
|
|
2954
|
+
const len = circumference * (p.value / total) * occ;
|
|
2955
|
+
if (len <= 0) continue;
|
|
2956
|
+
segs.push({
|
|
2957
|
+
key: p.key,
|
|
2958
|
+
color: p.color,
|
|
2959
|
+
len,
|
|
2960
|
+
offset,
|
|
2961
|
+
free: false
|
|
2962
|
+
});
|
|
2963
|
+
offset += len;
|
|
2964
|
+
}
|
|
2965
|
+
else if (pct !== null && occ > 0) {
|
|
2966
|
+
segs.push({
|
|
2967
|
+
key: "fill",
|
|
2968
|
+
color: fallbackColor,
|
|
2969
|
+
len: circumference * occ,
|
|
2970
|
+
offset: 0,
|
|
2971
|
+
free: false
|
|
2972
|
+
});
|
|
2973
|
+
offset = circumference * occ;
|
|
2974
|
+
}
|
|
2975
|
+
if (pct !== null && offset < circumference) segs.push({
|
|
2976
|
+
key: "free",
|
|
2977
|
+
color: "",
|
|
2978
|
+
len: circumference - offset,
|
|
2979
|
+
offset,
|
|
2980
|
+
free: true
|
|
2981
|
+
});
|
|
2982
|
+
return segs;
|
|
2983
|
+
}
|
|
2984
|
+
/** Session-switch navigation, fail-soft: a stale row (list rebuilt between snapshot and click) loses its open() race and is ignored. */
|
|
2985
|
+
function openAgentSession(face, id) {
|
|
2986
|
+
if (face === null || typeof face.open !== "function") return;
|
|
2987
|
+
try {
|
|
2988
|
+
face.open(id);
|
|
2989
|
+
} catch {}
|
|
2990
|
+
}
|
|
2991
|
+
/** Narrow `ctx.get('sessions')` to the card's face (null = harness without the outward sessions service). */
|
|
2992
|
+
function sessionsFaceOf(ctx) {
|
|
2993
|
+
const rec = asRecord(ctx.get("sessions"));
|
|
2994
|
+
if (rec === null) return null;
|
|
2995
|
+
const list = asRecord(rec.list);
|
|
2996
|
+
if (list === null || typeof list.getSnapshot !== "function" || typeof list.subscribe !== "function") return null;
|
|
2997
|
+
return rec;
|
|
2998
|
+
}
|
|
2999
|
+
/** Compact duration: `42s`, `3m05s`, `1h07m` (shared by both locales). */
|
|
3000
|
+
function fmtDuration(ms) {
|
|
3001
|
+
if (!Number.isFinite(ms) || ms < 0) return "—";
|
|
3002
|
+
const s = Math.round(ms / 1e3);
|
|
3003
|
+
if (s < 60) return `${s}s`;
|
|
3004
|
+
const m = Math.floor(s / 60);
|
|
3005
|
+
if (m < 60) return `${m}m${String(s % 60).padStart(2, "0")}s`;
|
|
3006
|
+
return `${Math.floor(m / 60)}h${String(m % 60).padStart(2, "0")}m`;
|
|
3007
|
+
}
|
|
3008
|
+
//#endregion
|
|
3009
|
+
//#region src/client/components/agentGraph.tsx
|
|
3010
|
+
/** Caption box height under a node (3 wrapped label lines + the tokens line). */
|
|
3011
|
+
const CAPTION_H = 60;
|
|
3012
|
+
/** Fallback arc color for pressure-only nodes (no composition data), by fill ratio. */
|
|
3013
|
+
function ringColorOf(pct) {
|
|
3014
|
+
if (pct === null) return "var(--dsw-alias-border-l1)";
|
|
3015
|
+
if (pct >= 90) return "#ef4444";
|
|
3016
|
+
if (pct >= 70) return "#f59e0b";
|
|
3017
|
+
return "#22c55e";
|
|
3018
|
+
}
|
|
3019
|
+
function makeAgentGraph(ctx, kit) {
|
|
3020
|
+
const { t, fmt, catLabel } = kit;
|
|
3021
|
+
function AgentGraph(props) {
|
|
3022
|
+
const face = React.useMemo(() => sessionsFaceOf(ctx), []);
|
|
3023
|
+
const subscribe = React.useCallback((fn) => {
|
|
3024
|
+
if (face === null) return () => {};
|
|
3025
|
+
/* v8 ignore next 2 -- sessionsFaceOf returns a face only after proving list.subscribe. */
|
|
3026
|
+
if (face.list === void 0) return () => {};
|
|
3027
|
+
return face.list.subscribe(fn);
|
|
3028
|
+
}, [face]);
|
|
3029
|
+
const getSnapshot = React.useCallback(() => {
|
|
3030
|
+
if (face === null) return null;
|
|
3031
|
+
/* v8 ignore next 2 -- sessionsFaceOf proves list before returning the face. */
|
|
3032
|
+
if (face.list === void 0) return null;
|
|
3033
|
+
return face.list.getSnapshot();
|
|
3034
|
+
}, [face]);
|
|
3035
|
+
const snapshot = React.useSyncExternalStore(subscribe, getSnapshot);
|
|
3036
|
+
const sessionId = props.sessionId;
|
|
3037
|
+
const [hoverId, setHoverId] = React.useState(null);
|
|
3038
|
+
const stageRef = React.useRef(null);
|
|
3039
|
+
const [stageWidth, setStageWidth] = React.useState(0);
|
|
3040
|
+
React.useEffect(() => {
|
|
3041
|
+
/* v8 ignore start -- jsdom has neither ResizeObserver nor layout; tests exercise the natural-pitch fallback (stageWidth 0). */
|
|
3042
|
+
const el = stageRef.current;
|
|
3043
|
+
if (el === null || typeof ResizeObserver !== "function") return;
|
|
3044
|
+
setStageWidth(el.clientWidth);
|
|
3045
|
+
const observer = new ResizeObserver(() => {
|
|
3046
|
+
setStageWidth(el.clientWidth);
|
|
3047
|
+
});
|
|
3048
|
+
observer.observe(el);
|
|
3049
|
+
return () => {
|
|
3050
|
+
observer.disconnect();
|
|
3051
|
+
};
|
|
3052
|
+
/* v8 ignore stop */
|
|
3053
|
+
}, []);
|
|
3054
|
+
React.useEffect(() => {
|
|
3055
|
+
if (face === null || typeof sessionId !== "string" || sessionId === "") return;
|
|
3056
|
+
if (typeof face.refreshSubagents !== "function") return;
|
|
3057
|
+
face.refreshSubagents(sessionId).catch(() => {});
|
|
3058
|
+
}, [face, sessionId]);
|
|
3059
|
+
const built = React.useMemo(() => {
|
|
3060
|
+
const forest = agentForestOf(snapshot, sessionId, props.self);
|
|
3061
|
+
return forest !== null ? {
|
|
3062
|
+
forest,
|
|
3063
|
+
layout: layoutForest(forest, stageWidth)
|
|
3064
|
+
} : null;
|
|
3065
|
+
}, [
|
|
3066
|
+
snapshot,
|
|
3067
|
+
sessionId,
|
|
3068
|
+
props.self,
|
|
3069
|
+
stageWidth
|
|
3070
|
+
]);
|
|
3071
|
+
if (built === null) return null;
|
|
3072
|
+
const { forest, layout } = built;
|
|
3073
|
+
const byId = new Map(forest.nodes.map((n) => [n.id, n]));
|
|
3074
|
+
/* v8 ignore next 1 -- agentForestOf anchors the forest at the current
|
|
3075
|
+
session, so a current node always exists. */
|
|
3076
|
+
const current = forest.nodes.find((n) => n.isCurrent) ?? forest.nodes[0];
|
|
3077
|
+
const inspected = (hoverId !== null ? byId.get(hoverId) : void 0) ?? current;
|
|
3078
|
+
const runningCount = forest.nodes.filter((n) => n.running).length;
|
|
3079
|
+
let totalTokens = 0;
|
|
3080
|
+
for (const n of forest.nodes) totalTokens += n.head !== null ? n.head.tokens : 0;
|
|
3081
|
+
const open = (id) => {
|
|
3082
|
+
if (id === current.id) return;
|
|
3083
|
+
openAgentSession(face, id);
|
|
3084
|
+
};
|
|
3085
|
+
const keyOpen = (id) => (ev) => {
|
|
3086
|
+
if (ev.key !== "Enter" && ev.key !== " ") return;
|
|
3087
|
+
ev.preventDefault();
|
|
3088
|
+
open(id);
|
|
3089
|
+
};
|
|
3090
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-agents" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("agents.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("agents.sub"))), /* @__PURE__ */ React.createElement("div", { className: "lc-agents-chips" }, /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" }, t("agents.chip.count", { n: forest.nodes.length + forest.overflow })), /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" + (runningCount > 0 ? " lc-agents-chip-on" : "") }, t("agents.chip.running", { n: runningCount })), totalTokens > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" }, t("agents.chip.tokens", { n: fmt(totalTokens) })) : null, forest.overflow > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" }, t("agents.more", { n: forest.overflow })) : null), /* @__PURE__ */ React.createElement("div", {
|
|
3091
|
+
className: "lc-agents-stage",
|
|
3092
|
+
ref: stageRef
|
|
3093
|
+
}, /* @__PURE__ */ React.createElement("svg", {
|
|
3094
|
+
className: "lc-agents-svg",
|
|
3095
|
+
width: layout.width,
|
|
3096
|
+
height: layout.height,
|
|
3097
|
+
viewBox: `0 0 ${layout.width} ${layout.height}`
|
|
3098
|
+
}, layout.links.map((link) => {
|
|
3099
|
+
const d = `M ${link.x1} ${link.y1} L ${link.x2} ${link.y2}`;
|
|
3100
|
+
return /* @__PURE__ */ React.createElement("g", { key: link.to }, /* @__PURE__ */ React.createElement("path", {
|
|
3101
|
+
className: "lc-agents-link" + (link.running ? " lc-agents-link-live" : ""),
|
|
3102
|
+
d,
|
|
3103
|
+
stroke: link.color,
|
|
3104
|
+
fill: "none"
|
|
3105
|
+
}), link.running ? /* @__PURE__ */ React.createElement("path", {
|
|
3106
|
+
className: "lc-agents-flow",
|
|
3107
|
+
d,
|
|
3108
|
+
stroke: link.color,
|
|
3109
|
+
fill: "none"
|
|
3110
|
+
}) : null);
|
|
3111
|
+
}), forest.nodes.map((node) => {
|
|
3112
|
+
const point = layout.points.find((p) => p.id === node.id);
|
|
3113
|
+
/* v8 ignore next 2 -- layoutForest positions every forest node,
|
|
3114
|
+
so the lookup never misses. */
|
|
3115
|
+
if (point === void 0) return null;
|
|
3116
|
+
return /* @__PURE__ */ React.createElement(AgentNodeView, {
|
|
3117
|
+
key: node.id,
|
|
3118
|
+
node,
|
|
3119
|
+
x: point.x,
|
|
3120
|
+
y: point.y,
|
|
3121
|
+
captionW: layout.captionW,
|
|
3122
|
+
hovered: hoverId === node.id,
|
|
3123
|
+
onHover: setHoverId,
|
|
3124
|
+
onOpen: open,
|
|
3125
|
+
onKeyOpen: keyOpen(node.id),
|
|
3126
|
+
t,
|
|
3127
|
+
fmt
|
|
3128
|
+
});
|
|
3129
|
+
}))), forest.solo ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty lc-agents-solo" }, t("agents.solo")) : null, /* @__PURE__ */ React.createElement(Inspector, {
|
|
3130
|
+
node: inspected,
|
|
3131
|
+
t,
|
|
3132
|
+
fmt
|
|
3133
|
+
}), /* @__PURE__ */ React.createElement("div", { className: "lc-agents-legend" }, CATS.map((c) => /* @__PURE__ */ React.createElement("span", {
|
|
3134
|
+
key: c.key,
|
|
3135
|
+
className: "lc-agents-legend-item"
|
|
3136
|
+
}, /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }), catLabel(c.key))), /* @__PURE__ */ React.createElement("span", { className: "lc-agents-legend-item" }, /* @__PURE__ */ React.createElement("i", { className: "lc-agents-legend-free" }), t("agents.legend.free")), /* @__PURE__ */ React.createElement("span", { className: "lc-agents-legend-item" }, /* @__PURE__ */ React.createElement("i", { className: "lc-agents-legend-edge" }), t("agents.running"))));
|
|
3137
|
+
}
|
|
3138
|
+
return AgentGraph;
|
|
3139
|
+
}
|
|
3140
|
+
function AgentNodeView(props) {
|
|
3141
|
+
const { node, x, y, captionW } = props;
|
|
3142
|
+
const pct = node.head !== null ? node.head.pct : null;
|
|
3143
|
+
const ring = 2 * Math.PI * 20;
|
|
3144
|
+
const segs = node.head !== null ? ringSegments(node.head.parts, pct, 20, ringColorOf(pct)) : [];
|
|
3145
|
+
const cls = "lc-agent-node" + (node.isCurrent ? " lc-agent-self" : "") + (node.running ? " lc-agent-running" : "") + (node.completed && !node.running ? " lc-agent-done" : "") + (props.hovered ? " lc-agent-hover" : "") + (node.isCurrent ? "" : " lc-agent-clickable");
|
|
3146
|
+
return /* @__PURE__ */ React.createElement("g", {
|
|
3147
|
+
className: cls,
|
|
3148
|
+
transform: `translate(${x}, ${y})`,
|
|
3149
|
+
"data-agent": node.id,
|
|
3150
|
+
role: node.isCurrent ? "img" : "button",
|
|
3151
|
+
tabIndex: node.isCurrent ? void 0 : 0,
|
|
3152
|
+
onClick: () => {
|
|
3153
|
+
props.onOpen(node.id);
|
|
3154
|
+
},
|
|
3155
|
+
onKeyDown: props.onKeyOpen,
|
|
3156
|
+
onMouseEnter: () => {
|
|
3157
|
+
props.onHover(node.id);
|
|
3158
|
+
},
|
|
3159
|
+
onMouseLeave: () => {
|
|
3160
|
+
props.onHover(null);
|
|
3161
|
+
}
|
|
3162
|
+
}, /* @__PURE__ */ React.createElement("circle", {
|
|
3163
|
+
className: "lc-agent-halo",
|
|
3164
|
+
r: 35
|
|
3165
|
+
}), /* @__PURE__ */ React.createElement("circle", {
|
|
3166
|
+
className: "lc-agent-track",
|
|
3167
|
+
r: 26
|
|
3168
|
+
}), segs.map((seg) => /* @__PURE__ */ React.createElement("circle", {
|
|
3169
|
+
key: seg.key,
|
|
3170
|
+
className: "lc-agent-seg" + (seg.free ? " lc-agent-free" : ""),
|
|
3171
|
+
r: 20,
|
|
3172
|
+
stroke: seg.free ? void 0 : seg.color,
|
|
3173
|
+
strokeDasharray: `${seg.len} ${ring - seg.len}`,
|
|
3174
|
+
strokeDashoffset: -seg.offset,
|
|
3175
|
+
transform: "rotate(-90)"
|
|
3176
|
+
})), /* @__PURE__ */ React.createElement("text", {
|
|
3177
|
+
className: "lc-agent-pct",
|
|
3178
|
+
textAnchor: "middle",
|
|
3179
|
+
dy: "0.32em"
|
|
3180
|
+
}, pct !== null ? `${pct}%` : node.head !== null ? props.fmt(node.head.tokens) : "—"), /* @__PURE__ */ React.createElement("foreignObject", {
|
|
3181
|
+
x: -captionW / 2,
|
|
3182
|
+
y: 34,
|
|
3183
|
+
width: captionW,
|
|
3184
|
+
height: CAPTION_H
|
|
3185
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-agent-caption" }, /* @__PURE__ */ React.createElement("div", { className: "lc-agent-label" }, node.label, node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge lc-agent-self-badge" }, props.t("agents.self")) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-agent-tokens" }, node.head !== null ? props.fmt(node.head.tokens) : "—"))));
|
|
3186
|
+
}
|
|
3187
|
+
/** The detail strip mirroring the hovered (or current) node: identity, occupancy, activity, and the open hint. */
|
|
3188
|
+
function Inspector(props) {
|
|
3189
|
+
const { node, t, fmt } = props;
|
|
3190
|
+
const bits = [];
|
|
3191
|
+
if (node.head !== null) {
|
|
3192
|
+
const head = node.head;
|
|
3193
|
+
const window = head.window !== void 0 ? ` / ${fmt(head.window)}` : "";
|
|
3194
|
+
const pct = head.pct !== null ? ` · ${head.pct}%` : "";
|
|
3195
|
+
bits.push(`${fmt(head.tokens)}${window}${pct}`);
|
|
3196
|
+
}
|
|
3197
|
+
if (node.requests > 0) bits.push(t("agents.requests", { n: node.requests }));
|
|
3198
|
+
if (node.billed !== null && node.billed > 0) bits.push(t("agents.billed", { n: fmt(node.billed) }));
|
|
3199
|
+
if (node.durationMs !== null) bits.push(fmtDuration(node.durationMs));
|
|
3200
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-agents-inspector" }, /* @__PURE__ */ React.createElement("b", { className: "lc-agents-inspector-name" }, node.label), node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge" }, t("agents.self")) : null, node.running ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge lc-agents-badge-on" }, t("agents.running")) : null, node.identity !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge" }, t(node.identity.mode === "one-shot" ? "agents.oneshot" : "agents.continuable")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-agents-inspector-stats" }, bits.join(" · ")), !node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-inspector-open" }, t("agents.open")) : null);
|
|
3201
|
+
}
|
|
2597
3202
|
//#endregion
|
|
2598
3203
|
//#region src/client/components/events.tsx
|
|
2599
3204
|
const EVENT_ICONS = {
|
|
@@ -2656,6 +3261,14 @@ window.__ModuleLoader__.load({
|
|
|
2656
3261
|
eventAt
|
|
2657
3262
|
};
|
|
2658
3263
|
}
|
|
3264
|
+
/**
|
|
3265
|
+
* Set the native `title` on every label whose text overflows its box (the styled tips cover cards, but a plain
|
|
3266
|
+
* ellipsis row still needs the native fallback); reads (scrollWidth/clientWidth) and writes (title) stay separate
|
|
3267
|
+
* from layout-affecting work, and callers gate WHEN this runs so it never becomes a per-render forced layout.
|
|
3268
|
+
*/
|
|
3269
|
+
function syncTitles(root) {
|
|
3270
|
+
for (const el of root.querySelectorAll(".lc-event-label")) el.title = el.scrollWidth > el.clientWidth ? el.textContent || "" : "";
|
|
3271
|
+
}
|
|
2659
3272
|
function makeEventList(kit) {
|
|
2660
3273
|
const { t, fmt, fmtTime, eventLabel, eventAt } = kit;
|
|
2661
3274
|
return function EventList(props) {
|
|
@@ -2663,26 +3276,29 @@ window.__ModuleLoader__.load({
|
|
|
2663
3276
|
React.useLayoutEffect(() => {
|
|
2664
3277
|
const root = rootRef.current;
|
|
2665
3278
|
if (!root) return;
|
|
2666
|
-
|
|
2667
|
-
|
|
3279
|
+
syncTitles(root);
|
|
3280
|
+
}, [props.events]);
|
|
3281
|
+
React.useEffect(() => {
|
|
3282
|
+
const onResize = () => {
|
|
3283
|
+
const root = rootRef.current;
|
|
3284
|
+
if (root !== null) syncTitles(root);
|
|
2668
3285
|
};
|
|
2669
|
-
|
|
2670
|
-
window.addEventListener("resize", sync);
|
|
3286
|
+
window.addEventListener("resize", onResize);
|
|
2671
3287
|
return () => {
|
|
2672
|
-
window.removeEventListener("resize",
|
|
3288
|
+
window.removeEventListener("resize", onResize);
|
|
2673
3289
|
};
|
|
2674
|
-
});
|
|
3290
|
+
}, []);
|
|
2675
3291
|
if (props.events.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
|
|
2676
3292
|
const sorted = props.events.slice().reverse();
|
|
2677
3293
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2678
3294
|
className: "lc-events",
|
|
2679
3295
|
ref: rootRef
|
|
2680
|
-
}, sorted.map((ev
|
|
3296
|
+
}, sorted.map((ev) => {
|
|
2681
3297
|
const label = eventLabel(ev);
|
|
2682
3298
|
const at = eventAt(ev);
|
|
2683
3299
|
const glyph = ev.kind === "inject" ? /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, null) : ev.kind === "model" ? /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, null) : EVENT_ICONS[ev.kind] || "•";
|
|
2684
3300
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2685
|
-
key:
|
|
3301
|
+
key: ev.seq,
|
|
2686
3302
|
className: "lc-event"
|
|
2687
3303
|
}, /* @__PURE__ */ React.createElement("span", { className: "lc-event-icon lc-event-" + ev.kind }, glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-kind lc-kind-" + ev.kind }, t("kind." + ev.kind)), /* @__PURE__ */ React.createElement("span", { className: "lc-event-label" }, label), at !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-at" }, at) : null, ev.tokens ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-tokens" + (ev.kind === "inject" ? " lc-up" : " lc-down") }, (ev.kind === "inject" ? "+" : "−") + fmt(ev.tokens)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-event-time" }, fmtTime(ev.time)));
|
|
2688
3304
|
}));
|
|
@@ -2690,8 +3306,6 @@ window.__ModuleLoader__.load({
|
|
|
2690
3306
|
}
|
|
2691
3307
|
//#endregion
|
|
2692
3308
|
//#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
3309
|
function makeFileCard(kit, settings) {
|
|
2696
3310
|
const { t, fmt, fmtTime } = kit;
|
|
2697
3311
|
function formGlyph(form) {
|
|
@@ -2708,7 +3322,7 @@ window.__ModuleLoader__.load({
|
|
|
2708
3322
|
function DeltaPair(props) {
|
|
2709
3323
|
return /* @__PURE__ */ React.createElement("span", { className: "lc-fa-delta" }, props.added > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-up" }, "+" + fmt(props.added)) : null, props.removed > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-down" }, "−" + fmt(props.removed)) : null);
|
|
2710
3324
|
}
|
|
2711
|
-
return function FileCard(props) {
|
|
3325
|
+
return React.memo(function FileCard(props) {
|
|
2712
3326
|
const { activity } = props;
|
|
2713
3327
|
const [filter, setFilter] = React.useState("all");
|
|
2714
3328
|
const [sort, setSort] = React.useState(() => settings.defaultFileSort());
|
|
@@ -2824,7 +3438,7 @@ window.__ModuleLoader__.load({
|
|
|
2824
3438
|
}) : null, e.errs > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
2825
3439
|
className: "lc-br-err-dot",
|
|
2826
3440
|
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.
|
|
3441
|
+
}) : null, e.ops[0].time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-time" }, fmtTime(e.ops[0].time)) : null), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-ops" }, e.ops.map((op) => {
|
|
2828
3442
|
const onLocate = props.onLocate;
|
|
2829
3443
|
return onLocate !== void 0 ? /* @__PURE__ */ React.createElement("button", {
|
|
2830
3444
|
key: op.seq,
|
|
@@ -2838,25 +3452,38 @@ window.__ModuleLoader__.load({
|
|
|
2838
3452
|
key: op.seq,
|
|
2839
3453
|
className: "lc-fa-op"
|
|
2840
3454
|
}, opLine(op));
|
|
2841
|
-
})
|
|
2842
|
-
})))
|
|
2843
|
-
};
|
|
3455
|
+
})) : null);
|
|
3456
|
+
}))));
|
|
3457
|
+
});
|
|
2844
3458
|
}
|
|
2845
3459
|
const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
|
|
2846
3460
|
const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
|
|
2847
3461
|
//#endregion
|
|
2848
3462
|
//#region src/client/latestVersion.ts
|
|
2849
3463
|
/**
|
|
2850
|
-
* One live check for the Plugin-info card: lazy with 1h TTL;
|
|
2851
|
-
*
|
|
3464
|
+
* One live check for the Plugin-info card: lazy with 1h TTL; registries
|
|
3465
|
+
* are tried strictly in order — official npm first, npmmirror only as a
|
|
3466
|
+
* fallback — and one-at-a-time, never in parallel. Each source's failure
|
|
3467
|
+
* (non-ok response, missing/non-string version, network error) advances
|
|
3468
|
+
* to the next one; only an all-source failure resolves to null so the
|
|
3469
|
+
* card silently keeps its static version.
|
|
2852
3470
|
*/
|
|
2853
|
-
const
|
|
3471
|
+
const REGISTRY_HOSTS = ["https://registry.npmjs.org", "https://registry.npmmirror.com"];
|
|
2854
3472
|
const TTL_MS = 36e5;
|
|
2855
3473
|
let cached = null;
|
|
3474
|
+
function readVersion(host) {
|
|
3475
|
+
return fetch(host + "/dsh-context/latest").then((res) => res.ok ? res.json() : null).then((body) => body !== null && typeof body.version === "string" ? body.version : null).catch(() => null);
|
|
3476
|
+
}
|
|
2856
3477
|
function fetchLatestVersion() {
|
|
2857
3478
|
if (!cached || Date.now() - cached.at >= TTL_MS) cached = {
|
|
2858
3479
|
at: Date.now(),
|
|
2859
|
-
promise:
|
|
3480
|
+
promise: (async () => {
|
|
3481
|
+
for (const host of REGISTRY_HOSTS) {
|
|
3482
|
+
const v = await readVersion(host);
|
|
3483
|
+
if (v !== null) return v;
|
|
3484
|
+
}
|
|
3485
|
+
return null;
|
|
3486
|
+
})()
|
|
2860
3487
|
};
|
|
2861
3488
|
return cached.promise;
|
|
2862
3489
|
}
|
|
@@ -2885,7 +3512,7 @@ window.__ModuleLoader__.load({
|
|
|
2885
3512
|
return function PluginInfo() {
|
|
2886
3513
|
const [latest, setLatest] = React.useState(null);
|
|
2887
3514
|
React.useEffect(() => {
|
|
2888
|
-
if ("0.
|
|
3515
|
+
if ("0.34.0".includes("-dev")) return;
|
|
2889
3516
|
let on = true;
|
|
2890
3517
|
fetchLatestVersion().then((v) => {
|
|
2891
3518
|
if (on && v) setLatest(v);
|
|
@@ -2894,8 +3521,8 @@ window.__ModuleLoader__.load({
|
|
|
2894
3521
|
on = false;
|
|
2895
3522
|
};
|
|
2896
3523
|
}, []);
|
|
2897
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
2898
|
-
const nameValue = ["dsh-context (v0.
|
|
3524
|
+
const update = latest !== null && isNewerVersion(latest, "0.34.0") ? latest : null;
|
|
3525
|
+
const nameValue = ["dsh-context (v0.34.0)"];
|
|
2899
3526
|
if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
|
|
2900
3527
|
key: "update",
|
|
2901
3528
|
className: "lc-pi-update"
|
|
@@ -2910,21 +3537,22 @@ window.__ModuleLoader__.load({
|
|
|
2910
3537
|
/**
|
|
2911
3538
|
* One brief row: a fixed-width kind tag plus one glanceable line. The tag carries a styled, instant explanation bubble (the
|
|
2912
3539
|
* `.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.
|
|
3540
|
+
* Clickable when the browser linkage is wired AND the row carries a node (the always-present In row's empty state stays inert).
|
|
2914
3541
|
*/
|
|
2915
3542
|
function BriefRow(props) {
|
|
2916
|
-
const cls = "lc-brief-row" + (props.onLocate !== void 0 ? " lc-brief-row-link" : "");
|
|
2917
3543
|
const inner = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-brief-tag" }, props.tag, /* @__PURE__ */ React.createElement("span", {
|
|
2918
3544
|
className: "lc-brief-tip",
|
|
2919
3545
|
role: "tooltip"
|
|
2920
3546
|
}, props.tagTip)), props.children);
|
|
2921
|
-
if (props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className:
|
|
3547
|
+
if (props.node === void 0 || props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-brief-row" }, inner);
|
|
3548
|
+
const node = props.node;
|
|
3549
|
+
const onLocate = props.onLocate;
|
|
2922
3550
|
const locate = () => {
|
|
2923
|
-
|
|
3551
|
+
onLocate(node, props.isResponse);
|
|
2924
3552
|
};
|
|
2925
3553
|
return /* @__PURE__ */ React.createElement("button", {
|
|
2926
3554
|
type: "button",
|
|
2927
|
-
className:
|
|
3555
|
+
className: "lc-brief-row lc-brief-row-link",
|
|
2928
3556
|
onClick: locate
|
|
2929
3557
|
}, inner);
|
|
2930
3558
|
}
|
|
@@ -2998,9 +3626,12 @@ window.__ModuleLoader__.load({
|
|
|
2998
3626
|
function factTitle(tag, text) {
|
|
2999
3627
|
return tag !== null ? text !== "" ? tag + " · " + text : tag : text;
|
|
3000
3628
|
}
|
|
3629
|
+
/**
|
|
3630
|
+
* The brief's container ALWAYS renders — a fixed three-row lane (`.lc-brief`'s min-height) so scrubbing the chart never
|
|
3631
|
+
* changes the panel's height; unknown or empty steps just leave parts of the lane blank instead of collapsing it.
|
|
3632
|
+
*/
|
|
3001
3633
|
function BriefSection(props) {
|
|
3002
|
-
const { opener, inputs, response } = props.brief;
|
|
3003
|
-
if (opener === void 0 && inputs.length === 0 && response === void 0) return null;
|
|
3634
|
+
const { opener, inputs, response } = props.brief ?? { inputs: [] };
|
|
3004
3635
|
const convOf = props.convOf ?? (() => void 0);
|
|
3005
3636
|
const hint = props.onLocate !== void 0 ? " — " + t("brief.locate") : "";
|
|
3006
3637
|
const locateChip = props.onLocate === void 0 ? void 0 : (n) => (e) => {
|
|
@@ -3032,13 +3663,13 @@ window.__ModuleLoader__.load({
|
|
|
3032
3663
|
}, openerParts.tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-fact" }, openerParts.tag) : null, openerParts.text !== "" ? /* @__PURE__ */ React.createElement("span", {
|
|
3033
3664
|
className: "lc-brief-text",
|
|
3034
3665
|
title: factTitle(openerParts.tag, openerParts.text) + hint
|
|
3035
|
-
}, openerParts.text) : null) : null,
|
|
3666
|
+
}, openerParts.text) : null) : null, /* @__PURE__ */ React.createElement(BriefRow, {
|
|
3036
3667
|
tag: t("brief.input"),
|
|
3037
3668
|
tagTip: t("brief.inputTip"),
|
|
3038
3669
|
node: inputs[0],
|
|
3039
3670
|
isResponse: false,
|
|
3040
3671
|
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) :
|
|
3672
|
+
}, inputs.length > 0 ? /* @__PURE__ */ React.createElement(React.Fragment, null, inputs.slice(0, MAX_CHIPS).map((n) => nodeChip(n, locateChip?.(n))), inputs.length > MAX_CHIPS ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-more" }, t("brief.more", { n: inputs.length - MAX_CHIPS })) : null) : /* @__PURE__ */ React.createElement("span", { className: "lc-brief-empty" }, t("brief.noInputs"))), response !== void 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
|
|
3042
3673
|
tag: t("brief.reply"),
|
|
3043
3674
|
tagTip: t("brief.replyTip"),
|
|
3044
3675
|
node: response,
|
|
@@ -3046,7 +3677,7 @@ window.__ModuleLoader__.load({
|
|
|
3046
3677
|
onLocate: props.onLocate
|
|
3047
3678
|
}, nodeChip(response, void 0, true)) : null);
|
|
3048
3679
|
}
|
|
3049
|
-
return function RequestDetail(props) {
|
|
3680
|
+
return React.memo(function RequestDetail(props) {
|
|
3050
3681
|
const req = props.request;
|
|
3051
3682
|
if (!req) return null;
|
|
3052
3683
|
const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
|
|
@@ -3078,11 +3709,11 @@ window.__ModuleLoader__.load({
|
|
|
3078
3709
|
return /* @__PURE__ */ React.createElement("div", { className: "lc-detail" }, /* @__PURE__ */ React.createElement("div", { className: "lc-detail-head" }, /* @__PURE__ */ React.createElement("b", null, head), marker !== null && markerAt !== null ? /* @__PURE__ */ React.createElement("span", {
|
|
3079
3710
|
className: "lc-detail-marker",
|
|
3080
3711
|
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),
|
|
3712
|
+
}, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("gran.delta")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-detail-time" }, fmtTime(req.time)), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" + (net > 0 ? " lc-detail-metric-up" : net < 0 ? " lc-detail-metric-down" : "") }, t("tip.delta", { n: (net > 0 ? "+" : "") + fmt(net) })) : null, !delta && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.actual", { n: fmt(req.prompt) })) : null, !delta && req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.output", { n: fmt(req.output) })) : null, !delta && req.prompt !== void 0 && req.cacheRead !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.cache", { n: cacheHitPercent$1(req.cacheRead, req.prompt) ?? "—" })) : null), /* @__PURE__ */ React.createElement(BriefSection, {
|
|
3082
3713
|
brief: props.brief,
|
|
3083
3714
|
convOf: props.convOf,
|
|
3084
3715
|
onLocate: props.onLocate
|
|
3085
|
-
})
|
|
3716
|
+
}), /* @__PURE__ */ React.createElement(StackedBar, {
|
|
3086
3717
|
parts,
|
|
3087
3718
|
height: 10
|
|
3088
3719
|
}), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c, i) => {
|
|
@@ -3105,7 +3736,7 @@ window.__ModuleLoader__.load({
|
|
|
3105
3736
|
}
|
|
3106
3737
|
})), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" + (v > 0 ? " lc-detail-num-up" : v < 0 ? " lc-detail-num-down" : "") }, (v > 0 ? "+" : "") + fmt(v)) : /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" }, "≈" + fmt(v)), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-pct" }, !delta && req.total > 0 ? `${Math.round(v / req.total * 100)}%` : ""));
|
|
3107
3738
|
})));
|
|
3108
|
-
};
|
|
3739
|
+
});
|
|
3109
3740
|
}
|
|
3110
3741
|
//#endregion
|
|
3111
3742
|
//#region src/client/cost.ts
|
|
@@ -3435,25 +4066,8 @@ window.__ModuleLoader__.load({
|
|
|
3435
4066
|
const scrollRef = React.useRef(null);
|
|
3436
4067
|
const scrolledOnce = React.useRef(false);
|
|
3437
4068
|
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
|
-
};
|
|
4069
|
+
const lastSeqRef = React.useRef(0);
|
|
4070
|
+
const prevScrollWidthRef = React.useRef(0);
|
|
3457
4071
|
/**
|
|
3458
4072
|
* Keep each turn label centered within its block's VISIBLE slice so it never scrolls out while any part of the block is on screen
|
|
3459
4073
|
* (narrower-than-label blocks stay put); reads (offsetWidth) batch before writes (transform) to avoid layout thrash — out-of-view
|
|
@@ -3488,6 +4102,9 @@ window.__ModuleLoader__.load({
|
|
|
3488
4102
|
/* v8 ignore next 1 -- the scroll div renders unconditionally and React
|
|
3489
4103
|
attaches refs before layout effects run; el is never null here. */
|
|
3490
4104
|
if (el === null) return;
|
|
4105
|
+
const newestSeq = requests.length === 0 ? 0 : requests[requests.length - 1].seq;
|
|
4106
|
+
const grew = newestSeq !== lastSeqRef.current;
|
|
4107
|
+
const widthBeforeAppend = prevScrollWidthRef.current;
|
|
3491
4108
|
if (props.granularity !== lastGranRef.current) {
|
|
3492
4109
|
lastGranRef.current = props.granularity;
|
|
3493
4110
|
scrolledOnce.current = false;
|
|
@@ -3499,14 +4116,19 @@ window.__ModuleLoader__.load({
|
|
|
3499
4116
|
el.scrollLeft = Math.max(0, gi * 16 + BAR_W / 2 - el.clientWidth / 2);
|
|
3500
4117
|
}
|
|
3501
4118
|
props.onFocusTurnHandled();
|
|
3502
|
-
}
|
|
3503
|
-
if (!scrolledOnce.current) {
|
|
4119
|
+
} else if (!scrolledOnce.current) {
|
|
3504
4120
|
scrolledOnce.current = true;
|
|
3505
4121
|
el.scrollLeft = el.scrollWidth;
|
|
3506
|
-
} else if (el.scrollLeft + el.clientWidth >=
|
|
3507
|
-
|
|
4122
|
+
} else if (grew && el.scrollLeft + el.clientWidth >= widthBeforeAppend - 24) el.scrollLeft = el.scrollWidth;
|
|
4123
|
+
lastSeqRef.current = newestSeq;
|
|
4124
|
+
prevScrollWidthRef.current = el.scrollWidth;
|
|
3508
4125
|
updateTurnLabels(el);
|
|
3509
|
-
|
|
4126
|
+
syncTip(el);
|
|
4127
|
+
}, [
|
|
4128
|
+
props.granularity,
|
|
4129
|
+
props.focusTurn,
|
|
4130
|
+
requests
|
|
4131
|
+
]);
|
|
3510
4132
|
const tipOf = (req) => {
|
|
3511
4133
|
const head = req.stepCount !== void 0 && req.stepCount > 1 ? t("tip.turn", {
|
|
3512
4134
|
t: req.turn ?? 0,
|
|
@@ -3527,17 +4149,43 @@ window.__ModuleLoader__.load({
|
|
|
3527
4149
|
};
|
|
3528
4150
|
const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
|
|
3529
4151
|
const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
|
|
4152
|
+
const tipColRef = React.useRef(0);
|
|
4153
|
+
/**
|
|
4154
|
+
* Glue the hover tip to its bar's VISIBLE slice. The tip deliberately does NOT live inside the scrolling
|
|
4155
|
+
* content: an absolutely-positioned child of a scroller contributes to its scrollable overflow, so a wide
|
|
4156
|
+
* reply preview on a right-edge bar used to inflate scrollWidth on every hover and flap the horizontal
|
|
4157
|
+
* scrollbar open/closed — jumping the whole card. Reads (offsetWidth/clientWidth) batch before the single
|
|
4158
|
+
* style write; unchanged transforms write nothing.
|
|
4159
|
+
*/
|
|
4160
|
+
const syncTip = (el) => {
|
|
4161
|
+
/* v8 ignore next 1 -- the scroll div renders unconditionally while mounted, so its parent exists. */
|
|
4162
|
+
const tip = (el.parentElement ?? document.body).querySelector(".lc-chart-tip");
|
|
4163
|
+
if (tip === null) return;
|
|
4164
|
+
const lw = tip.offsetWidth;
|
|
4165
|
+
const cw = el.clientWidth;
|
|
4166
|
+
const half = Math.min(lw / 2, cw / 2);
|
|
4167
|
+
const cx = Math.min(Math.max(tipColRef.current - el.scrollLeft, half), cw - half);
|
|
4168
|
+
const next = `translate(${Math.round(cx - lw / 2)}px, 0)`;
|
|
4169
|
+
if (tip.style.transform !== next) tip.style.transform = next;
|
|
4170
|
+
};
|
|
4171
|
+
React.useLayoutEffect(() => {
|
|
4172
|
+
/* v8 ignore next 1 -- the scroll div renders unconditionally and React attaches refs before
|
|
4173
|
+
layout effects run; el is never null here. */
|
|
4174
|
+
if (scrollRef.current === null) return;
|
|
4175
|
+
tipColRef.current = hoveredIdx >= 0 ? hoveredIdx * 16 + BAR_W / 2 : 0;
|
|
4176
|
+
syncTip(scrollRef.current);
|
|
4177
|
+
});
|
|
3530
4178
|
return /* @__PURE__ */ React.createElement("div", { className: "lc-chartrow" }, /* @__PURE__ */ React.createElement("div", { className: "lc-axis" }, delta ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, (maxUp > 0 ? "+" : "") + fmt(maxUp)), /* @__PURE__ */ React.createElement("span", {
|
|
3531
4179
|
className: "lc-axis-mid",
|
|
3532
4180
|
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", {
|
|
4181
|
+
}, "0"), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, (maxDown > 0 ? "-" : "") + fmt(maxDown))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, fmt(maxTotal)), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-mid" }, fmt(Math.round(maxTotal / 2))), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, "0"))), /* @__PURE__ */ React.createElement("div", { className: "lc-chart-wrap" }, /* @__PURE__ */ React.createElement("div", {
|
|
3534
4182
|
className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
|
|
3535
4183
|
ref: scrollRef,
|
|
3536
4184
|
onScroll: (e) => {
|
|
3537
|
-
updateEdges(e.currentTarget);
|
|
3538
4185
|
updateTurnLabels(e.currentTarget);
|
|
4186
|
+
syncTip(e.currentTarget);
|
|
3539
4187
|
}
|
|
3540
|
-
},
|
|
4188
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
3541
4189
|
className: "lc-chart",
|
|
3542
4190
|
onMouseLeave: () => {
|
|
3543
4191
|
props.onHover(null);
|
|
@@ -3558,10 +4206,7 @@ window.__ModuleLoader__.load({
|
|
|
3558
4206
|
deltaScale: delta ? deltaScale : void 0,
|
|
3559
4207
|
onSelect: props.onSelect,
|
|
3560
4208
|
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", {
|
|
4209
|
+
}))), /* @__PURE__ */ React.createElement("div", {
|
|
3565
4210
|
className: "lc-turns",
|
|
3566
4211
|
onMouseLeave: () => {
|
|
3567
4212
|
props.onHoverTurn(null);
|
|
@@ -3583,7 +4228,7 @@ window.__ModuleLoader__.load({
|
|
|
3583
4228
|
props.onPickTurn(grp.turn);
|
|
3584
4229
|
}
|
|
3585
4230
|
}, /* @__PURE__ */ React.createElement("span", { className: "lc-turn-label" }, `T${grp.turn}`));
|
|
3586
|
-
}))),
|
|
4231
|
+
}))), hoveredReq !== null ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-tip" }, tipOf(hoveredReq)) : null));
|
|
3587
4232
|
};
|
|
3588
4233
|
}
|
|
3589
4234
|
//#endregion
|
|
@@ -3607,6 +4252,7 @@ window.__ModuleLoader__.load({
|
|
|
3607
4252
|
const StatsBoard = makeStatsBoard(kit);
|
|
3608
4253
|
const PluginInfo = makePluginInfo(kit);
|
|
3609
4254
|
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
4255
|
+
const AgentGraph = makeAgentGraph(ctx, kit);
|
|
3610
4256
|
const ErrorBoundary = makeErrorBoundary(t);
|
|
3611
4257
|
function ContextViewBody(props) {
|
|
3612
4258
|
const sessionId = props.sessionId;
|
|
@@ -3676,16 +4322,6 @@ window.__ModuleLoader__.load({
|
|
|
3676
4322
|
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
3677
4323
|
return m;
|
|
3678
4324
|
}, [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
4325
|
let pinnedIdx = -1;
|
|
3690
4326
|
for (let i = 0; i < displayRequests.length; i++) if (displayRequests[i].seq === selectedSeq) pinnedIdx = i;
|
|
3691
4327
|
const pinnedReq = pinnedIdx >= 0 ? displayRequests[pinnedIdx] : null;
|
|
@@ -3699,23 +4335,24 @@ window.__ModuleLoader__.load({
|
|
|
3699
4335
|
if (activeIdx < 0) activeIdx = pinnedIdx;
|
|
3700
4336
|
if (activeIdx < 0 && displayRequests.length > 0) activeIdx = displayRequests.length - 1;
|
|
3701
4337
|
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
4338
|
let filesBefore = null;
|
|
3705
4339
|
if (activeReq !== null) {
|
|
3706
4340
|
const ri = requests.findIndex((r) => r.seq === activeReq.seq);
|
|
3707
4341
|
filesBefore = ri + 1 < requests.length ? requests[ri + 1].seq : null;
|
|
3708
4342
|
}
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
4343
|
+
const brief = React.useMemo(() => activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null, [
|
|
4344
|
+
activeReq,
|
|
4345
|
+
briefList,
|
|
4346
|
+
displayRequests,
|
|
4347
|
+
activeIdx
|
|
4348
|
+
]);
|
|
4349
|
+
const convOf = React.useCallback((seq) => bySeq.get(seq), [bySeq]);
|
|
4350
|
+
const fileActivity = React.useMemo(() => activityOf(briefList, convOf, filesBefore), [
|
|
4351
|
+
briefList,
|
|
4352
|
+
convOf,
|
|
4353
|
+
filesBefore
|
|
4354
|
+
]);
|
|
4355
|
+
const locateFileOp = React.useCallback((op) => {
|
|
3719
4356
|
const step = locateStepOf(requests, op.seq, op.gone);
|
|
3720
4357
|
if (step === null) return;
|
|
3721
4358
|
setNodeFocus({
|
|
@@ -3723,8 +4360,8 @@ window.__ModuleLoader__.load({
|
|
|
3723
4360
|
seq: op.seq,
|
|
3724
4361
|
cat: "tool"
|
|
3725
4362
|
});
|
|
3726
|
-
};
|
|
3727
|
-
const locateNode = (node, isResponse) => {
|
|
4363
|
+
}, [requests]);
|
|
4364
|
+
const locateNode = React.useCallback((node, isResponse) => {
|
|
3728
4365
|
/* v8 ignore next 1 -- locateNode is only wired to brief rows, and
|
|
3729
4366
|
brief !== null guarantees activeReq !== null in the same closure. */
|
|
3730
4367
|
if (activeReq === null) return;
|
|
@@ -3735,7 +4372,30 @@ window.__ModuleLoader__.load({
|
|
|
3735
4372
|
seq: node.seq,
|
|
3736
4373
|
cat: node.cat
|
|
3737
4374
|
});
|
|
4375
|
+
}, [
|
|
4376
|
+
activeReq,
|
|
4377
|
+
activeIdx,
|
|
4378
|
+
displayRequests
|
|
4379
|
+
]);
|
|
4380
|
+
if (!data) return /* @__PURE__ */ React.createElement("div", {
|
|
4381
|
+
className: "lc-root",
|
|
4382
|
+
ref: rootRef
|
|
4383
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
|
|
4384
|
+
const markerOf = (req) => {
|
|
4385
|
+
const i = displayRequests.indexOf(req);
|
|
4386
|
+
/* v8 ignore next 1 -- the only caller passes displayRequests[activeIdx],
|
|
4387
|
+
an element of the very array indexOf scans. */
|
|
4388
|
+
return i >= 0 ? markers[i] : void 0;
|
|
3738
4389
|
};
|
|
4390
|
+
const head = headlineOf(data, pressure, breakdown);
|
|
4391
|
+
let fileScope = t("files.scopeLatest");
|
|
4392
|
+
if (activeReq !== null && filesBefore !== null) fileScope = activeReq.stepCount !== void 0 && activeReq.stepCount > 1 ? t("detail.turn", {
|
|
4393
|
+
t: activeReq.turn ?? 0,
|
|
4394
|
+
n: activeReq.stepCount
|
|
4395
|
+
}) : t("detail.step", {
|
|
4396
|
+
t: activeReq.turn ?? 0,
|
|
4397
|
+
s: activeReq.step ?? 0
|
|
4398
|
+
});
|
|
3739
4399
|
let activeTurn = hoverTurn;
|
|
3740
4400
|
if (activeTurn === null && hoveredSeq !== null) {
|
|
3741
4401
|
for (const req of displayRequests) if (req.seq === hoveredSeq) {
|
|
@@ -3743,9 +4403,9 @@ window.__ModuleLoader__.load({
|
|
|
3743
4403
|
break;
|
|
3744
4404
|
}
|
|
3745
4405
|
}
|
|
3746
|
-
const head = headlineOf(data, pressure, breakdown);
|
|
3747
4406
|
const localeSvc = ctx.get("locale");
|
|
3748
4407
|
const activeLocale = localeSvc !== void 0 && typeof localeSvc.getLocale === "function" ? localeSvc.getLocale().active : "en";
|
|
4408
|
+
const subtitle = (data.model ?? "") + (data.provider ? " · " + data.provider : "");
|
|
3749
4409
|
return /* @__PURE__ */ React.createElement("div", {
|
|
3750
4410
|
className: "lc-root",
|
|
3751
4411
|
ref: rootRef
|
|
@@ -3759,7 +4419,7 @@ window.__ModuleLoader__.load({
|
|
|
3759
4419
|
locale: activeLocale
|
|
3760
4420
|
}), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement(CurrentComposition, {
|
|
3761
4421
|
head,
|
|
3762
|
-
subtitle
|
|
4422
|
+
subtitle,
|
|
3763
4423
|
hoverKey: hoverCat,
|
|
3764
4424
|
onHoverKey: setHoverCat
|
|
3765
4425
|
}), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("trend.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-trend-ctl" }, /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement("button", {
|
|
@@ -3838,7 +4498,14 @@ window.__ModuleLoader__.load({
|
|
|
3838
4498
|
activity: fileActivity,
|
|
3839
4499
|
scope: fileScope,
|
|
3840
4500
|
onLocate: locateFileOp
|
|
3841
|
-
})), /* @__PURE__ */ React.createElement(
|
|
4501
|
+
})), /* @__PURE__ */ React.createElement(AgentGraph, {
|
|
4502
|
+
sessionId: typeof sessionId === "string" ? sessionId : void 0,
|
|
4503
|
+
self: {
|
|
4504
|
+
head,
|
|
4505
|
+
billed: usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null,
|
|
4506
|
+
requests: requests.length
|
|
4507
|
+
}
|
|
4508
|
+
}), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
|
|
3842
4509
|
}
|
|
3843
4510
|
return function ContextView(props) {
|
|
3844
4511
|
return h(ErrorBoundary, null, h(ContextViewBody, props));
|
|
@@ -3859,7 +4526,7 @@ window.__ModuleLoader__.load({
|
|
|
3859
4526
|
}
|
|
3860
4527
|
//#endregion
|
|
3861
4528
|
//#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}";
|
|
4529
|
+
const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{flex-wrap:wrap;align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;left:0;box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px 8px;margin-bottom:10px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary);font-size:13px}.lc-detail-time{font-variant-numeric:tabular-nums;white-space:nowrap;margin-left:auto;font-size:12px}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-metric{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 14%, transparent);border-radius:6px;padding:2px 8px;font-size:12px;font-weight:600}.lc-detail-metric-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-detail-metric-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-detail-rows{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:4px 24px;margin-top:10px;display:grid}.lc-brief{flex-direction:column;gap:2px;min-height:76px;margin:-4px 0 10px;display:flex}.lc-brief-row{min-width:0;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;margin-left:-6px;margin-right:-6px;padding:3px 6px;font-family:inherit;font-size:12px;line-height:1.5;transition:background-color .12s;display:flex}.lc-brief-row-link:hover{background:var(--dsw-alias-interactive-bg-hover)}button.lc-brief-row-link{cursor:pointer}.lc-brief-tag{box-sizing:border-box;text-align:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:56px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;position:relative}.lc-brief-tip{z-index:6;white-space:normal;text-align:left;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:240px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-brief-tag:hover .lc-brief-tip{opacity:1}.lc-brief-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-empty{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-tertiary);flex:1;overflow:hidden}.lc-brief-fact{box-sizing:border-box;text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:160px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:0;max-width:260px;color:var(--dsw-alias-label-primary);border-radius:5px;align-items:center;gap:5px;padding:0 7px;font-size:12px;transition:background-color .12s;display:inline-flex;overflow:hidden}.lc-brief-chip-grow{flex:1;max-width:none}.lc-brief-chip-tag{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);max-width:120px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-chip-link{cursor:pointer}.lc-brief-chip-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-brief-more{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;position:relative;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-bar-zero{border-left:1px solid #80808073;position:absolute;top:0;bottom:0;left:50%}.lc-bar-fill-up,.lc-bar-fill-down{height:100%;display:block;position:absolute;top:0}.lc-bar-fill-up{border-radius:0 3px 3px 0;left:50%}.lc-bar-fill-down{border-radius:3px 0 0 3px;right:50%}.lc-detail-num{text-align:right;font-variant-numeric:tabular-nums;width:52px}.lc-detail-num-up{color:var(--dsw-alias-state-success-primary)}.lc-detail-num-down{color:var(--dsw-alias-state-error-primary)}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-fa-ctl{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px;display:flex}.lc-fa-ctl .lc-gran{margin-left:0}.lc-fa-n{opacity:.75;margin-left:4px;font-weight:700}.lc-fa-search{min-width:80px;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;outline:none;flex:1;padding:3px 8px;font-size:12px}.lc-fa-search:focus{border-color:var(--dsw-alias-label-dimmed)}.lc-fa-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:6px;font-size:12px;display:flex}.lc-fa-meta .lc-fa-sort{flex:none}.lc-fa-meta-delta{position:relative}.lc-fa-meta-tip{z-index:6;white-space:normal;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:260px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-fa-meta-delta:hover .lc-fa-meta-tip{opacity:1}.lc-fa-item{border:1px solid #0000;border-radius:8px;transition:background-color .12s,border-color .12s}.lc-fa-item-on{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l1)}.lc-fa-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:7px;align-items:center;gap:7px;padding:6px 8px;font-size:12px;transition:background-color .12s;display:flex}.lc-fa-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-form{text-align:center;opacity:.85;flex:none;width:16px;font-size:11px}.lc-fa-path{word-break:break-all;flex:1;min-width:0}.lc-fa-path em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-fa-badge{font-variant-numeric:tabular-nums;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:1px 7px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-badge i{background:currentColor;border-radius:50%;width:5px;height:5px;display:inline-block}.lc-fa-b-read{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent)}.lc-fa-b-write{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent)}.lc-fa-b-search{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent)}.lc-fa-chip-read,.lc-fa-chip-read:hover{color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write,.lc-fa-chip-write:hover{color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search,.lc-fa-chip-search:hover{color:var(--dsw-alias-state-warn-primary)}.lc-fa-chip-read.lc-gran-on,.lc-fa-chip-read.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write.lc-gran-on,.lc-fa-chip-write.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search.lc-gran-on,.lc-fa-chip-search.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);color:var(--dsw-alias-state-warn-primary)}.lc-fa-delta{font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;gap:5px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-up{color:var(--dsw-alias-state-success-primary)}.lc-fa-down{color:var(--dsw-alias-state-error-primary)}.lc-fa-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.lc-fa-ops{border-left:2px solid var(--dsw-alias-border-l1);flex-direction:column;gap:1px;margin:0 8px 8px 15px;padding:2px 0 2px 10px;display:flex}.lc-fa-op{min-width:0;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;background:0 0;border:0;border-radius:5px;align-items:center;gap:8px;padding:3px 6px;font-size:11px;transition:background-color .12s;display:flex}.lc-fa-op-link{cursor:pointer}.lc-fa-op-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-op-time{width:58px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none}.lc-fa-op-tool{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:0 6px;font-size:11px}.lc-fa-op-detail{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;overflow:hidden}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-retry:hover{filter:brightness(1.15)}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,140px) minmax(0,90px) 56px minmax(0,1fr);align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}.lc-agents-chips{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.lc-agents-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;border-radius:999px;padding:2px 8px;font-size:11px}.lc-agents-chip-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-stage{margin:0 -4px;padding:0 4px;overflow-x:auto}.lc-agents-svg{max-width:none;margin:0 auto;display:block}.lc-agents-link{stroke-width:1.5px;stroke-opacity:.45}.lc-agents-link-live{stroke-opacity:.85}.lc-agents-flow{stroke-width:2px;stroke-linecap:round;stroke-dasharray:2 7;animation:.9s linear infinite lc-agent-flow}@keyframes lc-agent-flow{to{stroke-dashoffset:-9px}}.lc-agent-node{outline:none}.lc-agent-clickable{cursor:pointer}.lc-agent-halo{fill:#0000;transition:fill .12s}.lc-agent-hover .lc-agent-halo,.lc-agent-node:focus-visible .lc-agent-halo{fill:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-agent-self .lc-agent-halo{fill:var(--dsw-alias-bg-layer-2)}.lc-agent-running .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent);animation:1.8s ease-in-out infinite lc-agent-glow}.lc-agent-done .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 7%, transparent)}@keyframes lc-agent-glow{0%,to{opacity:1}50%{opacity:.4}}.lc-agent-track{fill:var(--dsw-alias-bg-layer-1);stroke:var(--dsw-alias-border-l1);stroke-width:1.5px}.lc-agent-seg{fill:none;stroke-width:9px;transition:stroke-dasharray .3s,stroke-dashoffset .3s}.lc-agent-free{stroke:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agent-pct{fill:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;pointer-events:none;font-size:11px;font-weight:600}.lc-agent-caption{text-align:center;pointer-events:none;width:100%;line-height:1.25}.lc-agent-label{color:var(--dsw-alias-label-primary);overflow-wrap:break-word;-webkit-line-clamp:3;-webkit-box-orient:vertical;font-size:11px;display:-webkit-box;overflow:hidden}.lc-agent-tokens{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:10px}.lc-agents-solo{padding:4px 0 10px}.lc-agents-inspector{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;flex-wrap:wrap;align-items:baseline;gap:4px 8px;min-width:0;margin-top:10px;padding:7px 10px;font-size:12px;display:flex}.lc-agents-inspector-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}.lc-agents-badge{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 6px;font-size:10px}.lc-agent-self-badge{vertical-align:1px;margin-left:5px}.lc-agents-badge-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-inspector-stats{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.lc-agents-inspector-open{color:var(--dsw-alias-brand-primary,#3b82f6);flex:none;margin-left:auto}.lc-agents-legend{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:4px 14px;margin-top:8px;font-size:11px;display:flex}.lc-agents-legend-item{align-items:center;gap:5px;display:inline-flex}.lc-agents-legend-item i{border-radius:2px;width:8px;height:8px}.lc-agents-legend-free{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agents-legend-edge{border-top:2px dashed var(--dsw-alias-label-secondary);border-radius:0!important;width:14px!important;height:0!important}";
|
|
3863
4530
|
const tagId = "dsh-context/styles.css";
|
|
3864
4531
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
3865
4532
|
const tag = document.createElement("style");
|