dsh-context 0.31.1 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -11
- package/lib/client.js +1042 -200
- package/lib/index.d.ts +0 -8
- package/lib/index.js +14 -17
- package/package.json +8 -3
package/lib/client.js
CHANGED
|
@@ -13,7 +13,7 @@ window.__ModuleLoader__.load({
|
|
|
13
13
|
"cat.inject": "注入内容",
|
|
14
14
|
"cat.assistant": "助手消息",
|
|
15
15
|
"cat.tool": "工具结果",
|
|
16
|
-
"overview.title": "
|
|
16
|
+
"overview.title": "当前上下文",
|
|
17
17
|
"overview.estimate": "tokens(估算)",
|
|
18
18
|
"overview.free": "剩余窗口",
|
|
19
19
|
"overview.used": "上下文已用",
|
|
@@ -39,15 +39,14 @@ window.__ModuleLoader__.load({
|
|
|
39
39
|
"plugin.hint": "The best DSH context plugin ⭐",
|
|
40
40
|
"plugin.name": "插件",
|
|
41
41
|
"plugin.github": "GitHub",
|
|
42
|
-
"
|
|
43
|
-
"tools.more": "等 {n} 个",
|
|
44
|
-
"trend.title": "历史趋势",
|
|
42
|
+
"trend.title": "上下文趋势",
|
|
45
43
|
"gran.step": "步骤",
|
|
46
44
|
"gran.turn": "轮次",
|
|
47
45
|
"settings.title": "上下文",
|
|
48
46
|
"settings.desc": "上下文面板的展示偏好",
|
|
49
47
|
"settings.gran": "趋势图默认粒度",
|
|
50
48
|
"settings.mode": "趋势图默认展示",
|
|
49
|
+
"settings.fileSort": "文件活动默认排序",
|
|
51
50
|
"settings.expand": "展开",
|
|
52
51
|
"settings.collapse": "收起",
|
|
53
52
|
"settings.readOnly": "当前环境的设置为只读",
|
|
@@ -63,6 +62,14 @@ window.__ModuleLoader__.load({
|
|
|
63
62
|
"detail.actual": "实际 prompt {n}",
|
|
64
63
|
"detail.output": "输出 {n}",
|
|
65
64
|
"detail.cache": "缓存 {n}%",
|
|
65
|
+
"brief.turn": "本轮",
|
|
66
|
+
"brief.input": "输入",
|
|
67
|
+
"brief.reply": "回复",
|
|
68
|
+
"brief.more": "+{n}",
|
|
69
|
+
"brief.locate": "在上下文浏览器中查看",
|
|
70
|
+
"brief.turnTip": "这一轮开场时用户发送的消息",
|
|
71
|
+
"brief.inputTip": "这一步新进入上下文的内容(通常是上一步工具调用的结果)",
|
|
72
|
+
"brief.replyTip": "这一步模型返回的内容(文本回复或工具调用)",
|
|
66
73
|
"events.title": "上下文事件",
|
|
67
74
|
"events.empty": "暂无上下文事件(压缩、注入、模型切换会出现在这里)",
|
|
68
75
|
"events.at": "第 {t} 轮 · 第 {s} 步",
|
|
@@ -73,10 +80,30 @@ window.__ModuleLoader__.load({
|
|
|
73
80
|
"kind.prune": "剪枝",
|
|
74
81
|
"kind.model": "切换",
|
|
75
82
|
"kind.mode": "模式",
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
83
|
+
"files.title": "文件活动",
|
|
84
|
+
"files.scopeLatest": "截至最新 · 跟随趋势图的选择",
|
|
85
|
+
"files.kind.all": "全部",
|
|
86
|
+
"files.kind.read": "读取",
|
|
87
|
+
"files.kind.write": "写入",
|
|
88
|
+
"files.kind.search": "搜索",
|
|
89
|
+
"files.kind.image": "图片",
|
|
90
|
+
"files.chipTip": "{files} 个文件 · {ops} 次操作",
|
|
91
|
+
"files.search": "按路径过滤…",
|
|
92
|
+
"files.sort.count": "按次数",
|
|
93
|
+
"files.sort.latest": "按最新",
|
|
94
|
+
"files.sort.path": "按路径",
|
|
95
|
+
"files.sortTip": "文件排序:按操作次数(选中分类时按该分类的次数)、最近操作时间或路径",
|
|
96
|
+
"files.files": "{n} 个文件",
|
|
97
|
+
"files.deltaTip": "按 edit/write 调用参数估算的累计行数变化:+ 新增 / − 删除",
|
|
98
|
+
"files.unresolved": "… 另有 {n} 次文件操作的路径超出保留窗口",
|
|
99
|
+
"files.empty": "该范围内没有文件读取、写入或搜索操作",
|
|
100
|
+
"files.noMatch": "没有匹配当前过滤条件的文件",
|
|
101
|
+
"files.locate": "在上下文浏览器中查看此次操作",
|
|
102
|
+
"files.moreOps": "+{n} 次更早操作",
|
|
103
|
+
"files.errs": "{n} 次失败",
|
|
104
|
+
"files.form.image": "图片",
|
|
105
|
+
"files.form.dir": "目录",
|
|
106
|
+
"files.form.text": "文件",
|
|
80
107
|
"loading": "正在读取会话日志…",
|
|
81
108
|
"error": "上下文数据读取失败:",
|
|
82
109
|
"error.retry": "重试",
|
|
@@ -118,6 +145,8 @@ window.__ModuleLoader__.load({
|
|
|
118
145
|
"browser.noEpoch": "该步骤的头部内容(系统提示词 / 工具定义)不在保留范围内",
|
|
119
146
|
"browser.noContent": "完整内容不在当前加载的消息窗口内(在聊天页加载更早历史后可查看)",
|
|
120
147
|
"browser.loading": "正在从更早的会话历史加载完整内容…",
|
|
148
|
+
"browser.notInLog": "会话日志中未找到该条目的完整内容(可能已被压缩或清理)",
|
|
149
|
+
"browser.loadFailed": "内容加载失败,点击重试",
|
|
121
150
|
"browser.preview": "预览",
|
|
122
151
|
"tool.desc": "描述",
|
|
123
152
|
"tool.params": "参数",
|
|
@@ -186,15 +215,14 @@ window.__ModuleLoader__.load({
|
|
|
186
215
|
"plugin.hint": "The best DSH context plugin ⭐",
|
|
187
216
|
"plugin.name": "Plugin",
|
|
188
217
|
"plugin.github": "GitHub",
|
|
189
|
-
"
|
|
190
|
-
"tools.more": "of {n}",
|
|
191
|
-
"trend.title": "Context History",
|
|
218
|
+
"trend.title": "Context Trend",
|
|
192
219
|
"gran.step": "Step",
|
|
193
220
|
"gran.turn": "Turn",
|
|
194
221
|
"settings.title": "Context",
|
|
195
222
|
"settings.desc": "Display preferences for the Context panel",
|
|
196
223
|
"settings.gran": "Default trend granularity",
|
|
197
224
|
"settings.mode": "Default trend display",
|
|
225
|
+
"settings.fileSort": "File Activity default sort",
|
|
198
226
|
"settings.expand": "Expand",
|
|
199
227
|
"settings.collapse": "Collapse",
|
|
200
228
|
"settings.readOnly": "Settings are read-only in this environment",
|
|
@@ -210,6 +238,14 @@ window.__ModuleLoader__.load({
|
|
|
210
238
|
"detail.actual": "Actual Prompt {n}",
|
|
211
239
|
"detail.output": "Output {n}",
|
|
212
240
|
"detail.cache": "Cache {n}%",
|
|
241
|
+
"brief.turn": "User",
|
|
242
|
+
"brief.input": "In",
|
|
243
|
+
"brief.reply": "Response",
|
|
244
|
+
"brief.more": "+{n}",
|
|
245
|
+
"brief.locate": "Reveal in Context Browser",
|
|
246
|
+
"brief.turnTip": "The user message that opened this turn",
|
|
247
|
+
"brief.inputTip": "What newly entered the context for this step (usually results of the previous tool calls)",
|
|
248
|
+
"brief.replyTip": "What the model returned on this step (a text reply or tool calls)",
|
|
213
249
|
"events.title": "Context Events",
|
|
214
250
|
"events.empty": "No context events yet (compaction, injections, model switches appear here)",
|
|
215
251
|
"events.at": "Turn {t} · Step {s}",
|
|
@@ -220,10 +256,30 @@ window.__ModuleLoader__.load({
|
|
|
220
256
|
"kind.prune": "Prune",
|
|
221
257
|
"kind.model": "Switch",
|
|
222
258
|
"kind.mode": "Mode",
|
|
223
|
-
"
|
|
224
|
-
"
|
|
225
|
-
"
|
|
226
|
-
"
|
|
259
|
+
"files.title": "File Activity",
|
|
260
|
+
"files.scopeLatest": "Up to latest · follows the trend chart pick",
|
|
261
|
+
"files.kind.all": "All",
|
|
262
|
+
"files.kind.read": "Read",
|
|
263
|
+
"files.kind.write": "Written",
|
|
264
|
+
"files.kind.search": "Searched",
|
|
265
|
+
"files.kind.image": "Images",
|
|
266
|
+
"files.chipTip": "{files} files · {ops} ops",
|
|
267
|
+
"files.search": "Filter by path…",
|
|
268
|
+
"files.sort.count": "Most active",
|
|
269
|
+
"files.sort.latest": "Latest",
|
|
270
|
+
"files.sort.path": "By path",
|
|
271
|
+
"files.sortTip": "Sort files by operation counts (of the selected kind when a kind chip is active), most recent activity, or path",
|
|
272
|
+
"files.files": "{n} files",
|
|
273
|
+
"files.deltaTip": "Cumulative line change estimated from edit/write call arguments: + added / − removed",
|
|
274
|
+
"files.unresolved": "… {n} more file ops have paths outside the retained window",
|
|
275
|
+
"files.empty": "No file reads, writes, or searches in this range",
|
|
276
|
+
"files.noMatch": "No files match the current filters",
|
|
277
|
+
"files.locate": "Reveal this operation in the Context Browser",
|
|
278
|
+
"files.moreOps": "+{n} earlier ops",
|
|
279
|
+
"files.errs": "{n} failed",
|
|
280
|
+
"files.form.image": "Image",
|
|
281
|
+
"files.form.dir": "Directory",
|
|
282
|
+
"files.form.text": "File",
|
|
227
283
|
"loading": "Reading the session log…",
|
|
228
284
|
"error": "Failed to read context data: ",
|
|
229
285
|
"error.retry": "Retry",
|
|
@@ -265,6 +321,8 @@ window.__ModuleLoader__.load({
|
|
|
265
321
|
"browser.noEpoch": "The header content (System Prompt / Tool Schemas) of this step is outside retention",
|
|
266
322
|
"browser.noContent": "Full content is outside the loaded message window (load older history in Chat to view)",
|
|
267
323
|
"browser.loading": "Loading full content from older session history…",
|
|
324
|
+
"browser.notInLog": "This item is not in the session log anymore (it may have been compacted away or cleared)",
|
|
325
|
+
"browser.loadFailed": "Load failed — click to retry",
|
|
268
326
|
"browser.preview": "Preview",
|
|
269
327
|
"tool.desc": "Description",
|
|
270
328
|
"tool.params": "Parameters",
|
|
@@ -419,8 +477,7 @@ window.__ModuleLoader__.load({
|
|
|
419
477
|
"assistant",
|
|
420
478
|
"tool"
|
|
421
479
|
];
|
|
422
|
-
|
|
423
|
-
const CAT_COLORS = Object.fromEntries(CATS.map((c) => [c.key, c.color]));
|
|
480
|
+
Object.fromEntries(CATS.map((c) => [c.key, c.color]));
|
|
424
481
|
function partsOf(breakdown) {
|
|
425
482
|
return CATS.map((c) => {
|
|
426
483
|
return {
|
|
@@ -462,6 +519,9 @@ window.__ModuleLoader__.load({
|
|
|
462
519
|
return CATS.map((c) => ({
|
|
463
520
|
key: c.key,
|
|
464
521
|
color: c.color,
|
|
522
|
+
/* v8 ignore next 1 -- `shares` is initialized with system/tools and both
|
|
523
|
+
foldSurface arms assign every MESSAGE_CATS key, so each key is always
|
|
524
|
+
defined; the fallback is defensive. */
|
|
465
525
|
value: shares[c.key] ?? 0
|
|
466
526
|
}));
|
|
467
527
|
}
|
|
@@ -558,7 +618,7 @@ window.__ModuleLoader__.load({
|
|
|
558
618
|
"assistant",
|
|
559
619
|
"tool",
|
|
560
620
|
"total"
|
|
561
|
-
].every((k) => typeof current[k] === "number") && Array.isArray(data.requests) && Array.isArray(data.events) && Array.isArray(data.nodes) && Array.isArray(data.archive)
|
|
621
|
+
].every((k) => typeof current[k] === "number") && Array.isArray(data.requests) && Array.isArray(data.events) && Array.isArray(data.nodes) && Array.isArray(data.archive)) return data;
|
|
562
622
|
const safeCurrent = current !== null && typeof current === "object" ? current : {};
|
|
563
623
|
const cost = typeof data.cost === "object" && data.cost !== null && !Array.isArray(data.cost) ? data.cost : void 0;
|
|
564
624
|
return {
|
|
@@ -575,7 +635,6 @@ window.__ModuleLoader__.load({
|
|
|
575
635
|
tool: numOf(safeCurrent.tool),
|
|
576
636
|
total: numOf(safeCurrent.total)
|
|
577
637
|
},
|
|
578
|
-
toolList: objectsOf(data.toolList),
|
|
579
638
|
requests: objectsOf(data.requests),
|
|
580
639
|
events: objectsOf(data.events),
|
|
581
640
|
nodes: objectsOf(data.nodes),
|
|
@@ -650,6 +709,157 @@ window.__ModuleLoader__.load({
|
|
|
650
709
|
return headers;
|
|
651
710
|
}
|
|
652
711
|
//#endregion
|
|
712
|
+
//#region src/client/historyPage.ts
|
|
713
|
+
/** Narrow one served row to a validated durable event envelope, or null. */
|
|
714
|
+
function eventOf(entry) {
|
|
715
|
+
if (entry === null || typeof entry !== "object") return null;
|
|
716
|
+
const ev = entry.event;
|
|
717
|
+
if (ev === null || typeof ev !== "object") return null;
|
|
718
|
+
const e = ev;
|
|
719
|
+
if (typeof e.type !== "string" || typeof e.seq !== "number" || !Number.isFinite(e.seq)) return null;
|
|
720
|
+
const data = e.data !== null && typeof e.data === "object" ? e.data : {};
|
|
721
|
+
return {
|
|
722
|
+
type: e.type,
|
|
723
|
+
seq: e.seq,
|
|
724
|
+
data
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
/** All string texts of an event's message-content shape joined (compaction summaries). */
|
|
728
|
+
function textOf(blocks) {
|
|
729
|
+
if (!Array.isArray(blocks)) return null;
|
|
730
|
+
let out = "";
|
|
731
|
+
for (const b of blocks) {
|
|
732
|
+
const text = b !== null && typeof b === "object" ? b.text : void 0;
|
|
733
|
+
if (typeof text === "string") out += text;
|
|
734
|
+
}
|
|
735
|
+
return out.trim() === "" ? null : out;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* One assistant content block → the snapshot block vocabulary the browser
|
|
739
|
+
* already renders (`kind`: text/reasoning/image/tool-call); unmappable
|
|
740
|
+
* blocks pass through raw and degrade to the generic JSON section.
|
|
741
|
+
*/
|
|
742
|
+
function assistantBlockOf(block) {
|
|
743
|
+
const b = block !== null && typeof block === "object" ? block : null;
|
|
744
|
+
switch (b?.type) {
|
|
745
|
+
case "text":
|
|
746
|
+
case "reasoning": return {
|
|
747
|
+
kind: b.type,
|
|
748
|
+
...typeof b.text === "string" ? { text: b.text } : {}
|
|
749
|
+
};
|
|
750
|
+
case "image": return {
|
|
751
|
+
kind: "image",
|
|
752
|
+
...b.attachment !== void 0 ? { attachment: b.attachment } : {}
|
|
753
|
+
};
|
|
754
|
+
case "tool-call": return {
|
|
755
|
+
kind: "tool-call",
|
|
756
|
+
name: typeof b.name === "string" ? b.name : "?",
|
|
757
|
+
argsRaw: b.arguments
|
|
758
|
+
};
|
|
759
|
+
default: return block !== null && typeof block === "object" ? block : { value: block };
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Map one history page into joined conversation nodes keyed by their event
|
|
764
|
+
* seq — the display subset of the browser's join: user messages, assistant
|
|
765
|
+
* blocks, tool results paired with their in-page call head, and compaction
|
|
766
|
+
* checkpoints paired with their summary event. Everything else (headers,
|
|
767
|
+
* boundaries, chunks, bare calls) projects to nothing.
|
|
768
|
+
*/
|
|
769
|
+
function pageNodesOf(entries) {
|
|
770
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
771
|
+
const calls = /* @__PURE__ */ new Map();
|
|
772
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
773
|
+
for (const entry of entries) {
|
|
774
|
+
const ev = eventOf(entry);
|
|
775
|
+
if (ev === null) continue;
|
|
776
|
+
const { type, seq, data } = ev;
|
|
777
|
+
if (type === "tool/call") {
|
|
778
|
+
if (typeof data.callId === "string") calls.set(data.callId, {
|
|
779
|
+
name: typeof data.name === "string" ? data.name : "?",
|
|
780
|
+
argsRaw: typeof data.arguments === "string" ? data.arguments : ""
|
|
781
|
+
});
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
if (type === "compaction/summary") {
|
|
785
|
+
if (typeof data.compactionId === "string") {
|
|
786
|
+
const summary = textOf(data.summary);
|
|
787
|
+
if (summary !== null) summaries.set(data.compactionId, summary);
|
|
788
|
+
}
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
if (type === "user/message") {
|
|
792
|
+
const source = data.source !== null && typeof data.source === "object" ? data.source : null;
|
|
793
|
+
const compactionId = source !== null && source.kind === "plugin" && typeof source.compactionId === "string" ? source.compactionId : null;
|
|
794
|
+
if (compactionId !== null) {
|
|
795
|
+
nodes.set(seq, {
|
|
796
|
+
kind: "compaction",
|
|
797
|
+
seq,
|
|
798
|
+
summary: summaries.get(compactionId) ?? null
|
|
799
|
+
});
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
nodes.set(seq, {
|
|
803
|
+
kind: "user",
|
|
804
|
+
seq,
|
|
805
|
+
content: Array.isArray(data.content) ? data.content : []
|
|
806
|
+
});
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
if (type === "assistant/message") {
|
|
810
|
+
const message = data.message !== null && typeof data.message === "object" ? data.message : null;
|
|
811
|
+
const content = message !== null && Array.isArray(message.content) ? message.content : [];
|
|
812
|
+
nodes.set(seq, {
|
|
813
|
+
kind: "assistant",
|
|
814
|
+
seq,
|
|
815
|
+
blocks: content.map(assistantBlockOf)
|
|
816
|
+
});
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
if (type === "tool/result") {
|
|
820
|
+
const message = data.message !== null && typeof data.message === "object" ? data.message : null;
|
|
821
|
+
const source = message?.source !== null && typeof message?.source === "object" ? message.source : null;
|
|
822
|
+
const first = Array.isArray(message?.content) ? message.content[0] : void 0;
|
|
823
|
+
const block = first !== null && typeof first === "object" ? first : null;
|
|
824
|
+
const callId = typeof source?.callId === "string" ? source.callId : typeof block?.toolCallId === "string" ? block.toolCallId : null;
|
|
825
|
+
const call = callId !== null ? calls.get(callId) ?? null : null;
|
|
826
|
+
nodes.set(seq, {
|
|
827
|
+
kind: "tool-result",
|
|
828
|
+
seq,
|
|
829
|
+
call,
|
|
830
|
+
content: block !== null && Array.isArray(block.content) ? block.content : [],
|
|
831
|
+
isError: block?.isError === true || data.error === true
|
|
832
|
+
});
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return nodes;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Build the browser's per-session fetcher over the shared api client.
|
|
840
|
+
* Undefined when the connection face or the history verb is absent (older
|
|
841
|
+
* hosts) — the caller keeps its static preview-plus-hint degradation. Found
|
|
842
|
+
* nodes cache in the closure: one mount re-reading a row never re-fetches.
|
|
843
|
+
*/
|
|
844
|
+
function makeContentFetcher(ctx, sessionId) {
|
|
845
|
+
const sessions = ctx.get("connection")?.api?.sessions;
|
|
846
|
+
if (typeof sessions?.history !== "function") return void 0;
|
|
847
|
+
const cache = /* @__PURE__ */ new Map();
|
|
848
|
+
return async (seq) => {
|
|
849
|
+
const hit = cache.get(seq);
|
|
850
|
+
if (hit !== void 0) return hit;
|
|
851
|
+
const result = (await sessions.history({
|
|
852
|
+
sessionId,
|
|
853
|
+
beforeSeq: seq + 1
|
|
854
|
+
})).result;
|
|
855
|
+
if (result === null || typeof result !== "object" || result.ok !== true || result.value === null || typeof result.value !== "object") throw new Error("history rpc failed");
|
|
856
|
+
const events = result.value.events;
|
|
857
|
+
const node = pageNodesOf(Array.isArray(events) ? events : []).get(seq) ?? null;
|
|
858
|
+
if (node !== null) cache.set(seq, node);
|
|
859
|
+
return node;
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
//#endregion
|
|
653
863
|
//#region src/client/assemble.ts
|
|
654
864
|
/** The header epoch in force at `seq` (last logged before it), or the newest. */
|
|
655
865
|
function headerAt(headers, seq) {
|
|
@@ -688,6 +898,56 @@ window.__ModuleLoader__.load({
|
|
|
688
898
|
const h = React.createElement;
|
|
689
899
|
const ReactDOM = require("react-dom");
|
|
690
900
|
//#endregion
|
|
901
|
+
//#region src/client/callSummary.ts
|
|
902
|
+
function parseCallArgs(raw) {
|
|
903
|
+
if (typeof raw !== "string" || raw === "") return null;
|
|
904
|
+
try {
|
|
905
|
+
const parsed = JSON.parse(raw);
|
|
906
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
907
|
+
} catch {
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
function summaryInArgs(args) {
|
|
912
|
+
if (args === null) return null;
|
|
913
|
+
for (const k of [
|
|
914
|
+
"description",
|
|
915
|
+
"file_path",
|
|
916
|
+
"path",
|
|
917
|
+
"filePath"
|
|
918
|
+
]) {
|
|
919
|
+
const v = args[k];
|
|
920
|
+
if (typeof v === "string" && v !== "") return v;
|
|
921
|
+
}
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
function callSummaryOf(conv) {
|
|
925
|
+
return summaryInArgs(parseCallArgs(conv?.call?.argsRaw));
|
|
926
|
+
}
|
|
927
|
+
function blockSummaryOf(conv) {
|
|
928
|
+
if (conv === void 0 || !Array.isArray(conv.blocks)) return null;
|
|
929
|
+
for (const b of conv.blocks) {
|
|
930
|
+
const blk = b !== null && typeof b === "object" ? b : null;
|
|
931
|
+
if (blk === null || blk.kind !== "tool-call") continue;
|
|
932
|
+
const s = summaryInArgs(parseCallArgs(blk.argsRaw));
|
|
933
|
+
if (s !== null) return s;
|
|
934
|
+
}
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* All tool-call names in an assistant conversation node, in order. The fold's surface node keeps `calls` only for TEXT-LESS replies,
|
|
939
|
+
* so a reply carrying both text and calls recovers its call breadcrumb here through the conversation join.
|
|
940
|
+
*/
|
|
941
|
+
function callNamesOf(conv) {
|
|
942
|
+
if (conv === void 0 || !Array.isArray(conv.blocks)) return [];
|
|
943
|
+
const names = [];
|
|
944
|
+
for (const b of conv.blocks) {
|
|
945
|
+
const blk = b !== null && typeof b === "object" ? b : null;
|
|
946
|
+
if (blk !== null && blk.kind === "tool-call" && typeof blk.name === "string") names.push(blk.name);
|
|
947
|
+
}
|
|
948
|
+
return names;
|
|
949
|
+
}
|
|
950
|
+
//#endregion
|
|
691
951
|
//#region src/client/components/nodes.tsx
|
|
692
952
|
function makeNodeText(kit) {
|
|
693
953
|
const { t } = kit;
|
|
@@ -701,24 +961,6 @@ window.__ModuleLoader__.load({
|
|
|
701
961
|
return t("node.nonText");
|
|
702
962
|
};
|
|
703
963
|
}
|
|
704
|
-
function makeNodeList(kit) {
|
|
705
|
-
const { t, fmt, fmtTime } = kit;
|
|
706
|
-
const nodeText = makeNodeText(kit);
|
|
707
|
-
return function NodeList(props) {
|
|
708
|
-
if (props.nodes.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("nodes.empty"));
|
|
709
|
-
const rows = props.nodes.slice().reverse();
|
|
710
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-nodes" }, props.dropped > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-nodes-more" }, t("nodes.more", { n: props.dropped })) : null, rows.map((n) => {
|
|
711
|
-
const text = nodeText(n);
|
|
712
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
713
|
-
key: n.seq,
|
|
714
|
-
className: "lc-node"
|
|
715
|
-
}, /* @__PURE__ */ React.createElement("i", { style: { background: CAT_COLORS[n.cat] || "#999" } }), /* @__PURE__ */ React.createElement("span", {
|
|
716
|
-
className: "lc-node-preview",
|
|
717
|
-
title: text
|
|
718
|
-
}, text), typeof n.time === "number" ? /* @__PURE__ */ React.createElement("span", { className: "lc-node-time" }, fmtTime(n.time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-node-tokens" }, fmt(n.tokens)));
|
|
719
|
-
}));
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
964
|
//#endregion
|
|
723
965
|
//#region src/client/format.ts
|
|
724
966
|
/** `fmt`: the k/M suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
|
|
@@ -799,6 +1041,8 @@ window.__ModuleLoader__.load({
|
|
|
799
1041
|
let bestWidth;
|
|
800
1042
|
if (gridW < 1) {
|
|
801
1043
|
let rows = floorDiv(budget - 2, 2);
|
|
1044
|
+
/* v8 ignore else -- budget enters at 381 and the tall solve always fits
|
|
1045
|
+
(never decrements), so rows is always odd here; parity-defensive. */
|
|
802
1046
|
if (rows % 2 === 1) rows -= 1;
|
|
803
1047
|
bestWidth = unit;
|
|
804
1048
|
bestHeight = rows * unit;
|
|
@@ -878,6 +1122,8 @@ window.__ModuleLoader__.load({
|
|
|
878
1122
|
}
|
|
879
1123
|
return null;
|
|
880
1124
|
} catch {
|
|
1125
|
+
/* v8 ignore next -- the only throw site is the clamped-away budget guard
|
|
1126
|
+
above; the catch keeps a hostile dimension from ever breaking the fold. */
|
|
881
1127
|
return null;
|
|
882
1128
|
}
|
|
883
1129
|
}
|
|
@@ -1123,18 +1369,22 @@ window.__ModuleLoader__.load({
|
|
|
1123
1369
|
*/
|
|
1124
1370
|
function Section(props) {
|
|
1125
1371
|
const right = props.actions !== void 0 || props.meta !== void 0;
|
|
1126
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", { className: props.labelClass }, props.label), right ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-right" }, props.meta
|
|
1372
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", { className: props.labelClass }, props.label), right ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-right" }, props.meta, props.actions) : null, props.count !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-count" }, props.count) : null), props.children);
|
|
1373
|
+
}
|
|
1374
|
+
function lineCountOf(text) {
|
|
1375
|
+
return text.split(/\r\n|\r|\n/).length;
|
|
1127
1376
|
}
|
|
1128
1377
|
function TextSection(props) {
|
|
1129
1378
|
const { rich } = props;
|
|
1130
1379
|
const [mode, setMode] = rich.useRichMode();
|
|
1380
|
+
const lineCount = React.useMemo(() => lineCountOf(props.text), [props.text]);
|
|
1131
1381
|
return /* @__PURE__ */ React.createElement(Section, {
|
|
1132
1382
|
label: props.label,
|
|
1133
1383
|
actions: /* @__PURE__ */ React.createElement(rich.RichSwitch, {
|
|
1134
1384
|
mode,
|
|
1135
1385
|
onPick: setMode
|
|
1136
1386
|
}),
|
|
1137
|
-
meta: props.
|
|
1387
|
+
meta: /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-meta" }, props.lines(lineCount))
|
|
1138
1388
|
}, /* @__PURE__ */ React.createElement(rich.RichText, {
|
|
1139
1389
|
text: props.text,
|
|
1140
1390
|
mode
|
|
@@ -1172,7 +1422,8 @@ window.__ModuleLoader__.load({
|
|
|
1172
1422
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.description !== void 0 ? /* @__PURE__ */ React.createElement(TextSection, {
|
|
1173
1423
|
label: props.labels.desc,
|
|
1174
1424
|
text: props.description,
|
|
1175
|
-
rich
|
|
1425
|
+
rich,
|
|
1426
|
+
lines: props.lines
|
|
1176
1427
|
}) : null, params !== null && rows.length > 0 ? /* @__PURE__ */ React.createElement(Section, {
|
|
1177
1428
|
label: props.labels.title,
|
|
1178
1429
|
count: rows.length
|
|
@@ -1191,8 +1442,8 @@ window.__ModuleLoader__.load({
|
|
|
1191
1442
|
}
|
|
1192
1443
|
/**
|
|
1193
1444
|
* Both block vocabularies normalize here — raw durable blocks (`type`: text/reasoning/tool-call/tool-result/image) and snapshot assistant
|
|
1194
|
-
* blocks (`kind`: text/reasoning/tool-call/image, argsRaw). Consecutive images group into one grid;
|
|
1195
|
-
* switch + line count; nested tool-result blocks flatten into the same flow.
|
|
1445
|
+
* blocks (`kind`: text/reasoning/tool-call/image, argsRaw). Consecutive images group into one grid; every
|
|
1446
|
+
* rich text block carries the Raw/MD switch + line count; nested tool-result blocks flatten into the same flow.
|
|
1196
1447
|
*/
|
|
1197
1448
|
function BlocksBody(props) {
|
|
1198
1449
|
const { rich, img, labels } = props;
|
|
@@ -1228,7 +1479,7 @@ window.__ModuleLoader__.load({
|
|
|
1228
1479
|
label,
|
|
1229
1480
|
text: blk.text,
|
|
1230
1481
|
rich,
|
|
1231
|
-
|
|
1482
|
+
lines: labels.lines
|
|
1232
1483
|
}));
|
|
1233
1484
|
continue;
|
|
1234
1485
|
}
|
|
@@ -1261,35 +1512,6 @@ window.__ModuleLoader__.load({
|
|
|
1261
1512
|
flushImages();
|
|
1262
1513
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, out);
|
|
1263
1514
|
}
|
|
1264
|
-
function parseArgs(raw) {
|
|
1265
|
-
if (typeof raw !== "string" || raw === "") return null;
|
|
1266
|
-
try {
|
|
1267
|
-
const parsed = JSON.parse(raw);
|
|
1268
|
-
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
1269
|
-
} catch {
|
|
1270
|
-
return null;
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
/**
|
|
1274
|
-
* A call's preview: bash-style `description`, or the target path (`file_path`/`path`/`filePath`) for edit/read/write tools; null when the
|
|
1275
|
-
* arguments hold neither.
|
|
1276
|
-
*/
|
|
1277
|
-
function summaryIn(args) {
|
|
1278
|
-
if (args === null) return null;
|
|
1279
|
-
for (const k of [
|
|
1280
|
-
"description",
|
|
1281
|
-
"file_path",
|
|
1282
|
-
"path",
|
|
1283
|
-
"filePath"
|
|
1284
|
-
]) {
|
|
1285
|
-
const v = args[k];
|
|
1286
|
-
if (typeof v === "string" && v !== "") return v;
|
|
1287
|
-
}
|
|
1288
|
-
return null;
|
|
1289
|
-
}
|
|
1290
|
-
function callSummaryOf(conv) {
|
|
1291
|
-
return summaryIn(parseArgs(conv?.call?.argsRaw));
|
|
1292
|
-
}
|
|
1293
1515
|
/**
|
|
1294
1516
|
* The trailing status markers dsh shell tools append at the END of a result's text while `isError` stays false
|
|
1295
1517
|
* (the status is result data, per tool-bash's render: "non-zero exits are reported, not errored"):
|
|
@@ -1361,18 +1583,8 @@ window.__ModuleLoader__.load({
|
|
|
1361
1583
|
exit: tail.exit
|
|
1362
1584
|
};
|
|
1363
1585
|
}
|
|
1364
|
-
function blockSummaryOf(conv) {
|
|
1365
|
-
if (conv === void 0 || !Array.isArray(conv.blocks)) return null;
|
|
1366
|
-
for (const b of conv.blocks) {
|
|
1367
|
-
const blk = b !== null && typeof b === "object" ? b : null;
|
|
1368
|
-
if (blk === null || blk.kind !== "tool-call") continue;
|
|
1369
|
-
const s = summaryIn(parseArgs(blk.argsRaw));
|
|
1370
|
-
if (s !== null) return s;
|
|
1371
|
-
}
|
|
1372
|
-
return null;
|
|
1373
|
-
}
|
|
1374
1586
|
function ToolCallCard(props) {
|
|
1375
|
-
const args = React.useMemo(() =>
|
|
1587
|
+
const args = React.useMemo(() => parseCallArgs(props.argsRaw), [props.argsRaw]);
|
|
1376
1588
|
return /* @__PURE__ */ React.createElement(Section, {
|
|
1377
1589
|
label: (props.arrow ?? "→") + " " + props.name,
|
|
1378
1590
|
labelClass: "lc-ts-call-name",
|
|
@@ -1385,6 +1597,8 @@ window.__ModuleLoader__.load({
|
|
|
1385
1597
|
}
|
|
1386
1598
|
function CallArgRow(props) {
|
|
1387
1599
|
const v = props.value;
|
|
1600
|
+
/* v8 ignore next 2 -- the only caller maps Object.keys of a JSON.parse'd
|
|
1601
|
+
object, which never holds undefined values; defensive. */
|
|
1388
1602
|
const text = typeof v === "string" ? v : v === void 0 ? "" : JSON.stringify(v);
|
|
1389
1603
|
return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-arg-row" }, /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-name" }, props.name), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-arg-val" }, text));
|
|
1390
1604
|
}
|
|
@@ -1395,7 +1609,8 @@ window.__ModuleLoader__.load({
|
|
|
1395
1609
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TextSection, {
|
|
1396
1610
|
label: labels.content,
|
|
1397
1611
|
text: node.text,
|
|
1398
|
-
rich
|
|
1612
|
+
rich,
|
|
1613
|
+
lines: labels.lines
|
|
1399
1614
|
}), /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
|
|
1400
1615
|
}
|
|
1401
1616
|
if (conv.kind === "assistant" && Array.isArray(conv.blocks)) return /* @__PURE__ */ React.createElement(BlocksBody, {
|
|
@@ -1425,7 +1640,8 @@ window.__ModuleLoader__.load({
|
|
|
1425
1640
|
if (conv.kind === "compaction") return typeof conv.summary === "string" && conv.summary !== "" ? /* @__PURE__ */ React.createElement(TextSection, {
|
|
1426
1641
|
label: labels.summary,
|
|
1427
1642
|
text: conv.summary,
|
|
1428
|
-
rich
|
|
1643
|
+
rich,
|
|
1644
|
+
lines: labels.lines
|
|
1429
1645
|
}) : /* @__PURE__ */ React.createElement(React.Fragment, null);
|
|
1430
1646
|
if (Array.isArray(conv.content)) return /* @__PURE__ */ React.createElement(BlocksBody, {
|
|
1431
1647
|
blocks: conv.content,
|
|
@@ -1456,52 +1672,51 @@ window.__ModuleLoader__.load({
|
|
|
1456
1672
|
const nodeText = makeNodeText(kit);
|
|
1457
1673
|
const rich = makeRichText(kit);
|
|
1458
1674
|
const ImageCard = makeImageCard(kit);
|
|
1459
|
-
const
|
|
1675
|
+
const lineLabel = (n) => t(n === 1 ? "block.line" : "block.lines", { n });
|
|
1460
1676
|
return function ContextBrowser(props) {
|
|
1461
1677
|
const { data, headers } = props;
|
|
1462
1678
|
const [sel, setSel] = React.useState("live");
|
|
1463
1679
|
const [openCat, setOpenCat] = React.useState(null);
|
|
1464
1680
|
const [openElem, setOpenElem] = React.useState(null);
|
|
1465
1681
|
const convNodes = typeof props.useSession === "function" ? props.useSession((s) => s.nodes) : void 0;
|
|
1682
|
+
const [fetched, setFetched] = React.useState(() => /* @__PURE__ */ new Map());
|
|
1466
1683
|
const bySeq = React.useMemo(() => {
|
|
1467
1684
|
const m = /* @__PURE__ */ new Map();
|
|
1468
1685
|
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
1686
|
+
for (const [seq, n] of fetched) if (!m.has(seq)) m.set(seq, n);
|
|
1469
1687
|
return m;
|
|
1470
|
-
}, [convNodes]);
|
|
1471
|
-
const hasMore = typeof props.useSession === "function" ? props.useSession((s) => s.hasMore === true) : false;
|
|
1472
|
-
const loadingOlder = typeof props.useSession === "function" ? props.useSession((s) => s.loadingOlder === true) : false;
|
|
1688
|
+
}, [convNodes, fetched]);
|
|
1473
1689
|
const openSeq = openElem !== null && openElem.startsWith("n") ? Number(openElem.slice(1)) : null;
|
|
1474
1690
|
const missingSeq = openSeq !== null && !bySeq.has(openSeq) ? openSeq : null;
|
|
1475
|
-
const
|
|
1476
|
-
const
|
|
1477
|
-
React.
|
|
1478
|
-
pagesRef.current = 0;
|
|
1479
|
-
setExhausted(false);
|
|
1480
|
-
}, [openElem]);
|
|
1481
|
-
const loadOlderHistory = props.loadOlderHistory;
|
|
1482
|
-
React.useEffect(() => {
|
|
1483
|
-
if (missingSeq === null || !hasMore || loadingOlder || exhausted) return;
|
|
1484
|
-
if (loadOlderHistory === void 0) return;
|
|
1485
|
-
if (pagesRef.current >= MAX_AUTO_PAGES) {
|
|
1486
|
-
setExhausted(true);
|
|
1487
|
-
return;
|
|
1488
|
-
}
|
|
1489
|
-
pagesRef.current += 1;
|
|
1490
|
-
loadOlderHistory();
|
|
1491
|
-
}, [
|
|
1492
|
-
missingSeq,
|
|
1493
|
-
hasMore,
|
|
1494
|
-
loadingOlder,
|
|
1495
|
-
exhausted,
|
|
1496
|
-
bySeq,
|
|
1497
|
-
loadOlderHistory
|
|
1498
|
-
]);
|
|
1691
|
+
const fetchContent = props.fetchContent;
|
|
1692
|
+
const [missState, setMissState] = React.useState("idle");
|
|
1693
|
+
const [retry, setRetry] = React.useState(0);
|
|
1499
1694
|
React.useEffect(() => {
|
|
1500
|
-
if (
|
|
1695
|
+
if (missingSeq === null || fetchContent === void 0) return;
|
|
1696
|
+
let live = true;
|
|
1697
|
+
setMissState("loading");
|
|
1698
|
+
fetchContent(missingSeq).then((node) => {
|
|
1699
|
+
if (!live) return;
|
|
1700
|
+
if (node === null) {
|
|
1701
|
+
setMissState("absent");
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
setFetched((prev) => {
|
|
1705
|
+
const next = new Map(prev);
|
|
1706
|
+
next.set(missingSeq, node);
|
|
1707
|
+
return next;
|
|
1708
|
+
});
|
|
1709
|
+
setMissState("idle");
|
|
1710
|
+
}, () => {
|
|
1711
|
+
if (live) setMissState("failed");
|
|
1712
|
+
});
|
|
1713
|
+
return () => {
|
|
1714
|
+
live = false;
|
|
1715
|
+
};
|
|
1501
1716
|
}, [
|
|
1502
|
-
hasMore,
|
|
1503
1717
|
missingSeq,
|
|
1504
|
-
|
|
1718
|
+
fetchContent,
|
|
1719
|
+
retry
|
|
1505
1720
|
]);
|
|
1506
1721
|
const pinSeq = props.pinSeq;
|
|
1507
1722
|
React.useEffect(() => {
|
|
@@ -1509,15 +1724,29 @@ window.__ModuleLoader__.load({
|
|
|
1509
1724
|
setOpenCat(null);
|
|
1510
1725
|
setOpenElem(null);
|
|
1511
1726
|
}, [pinSeq]);
|
|
1512
|
-
const
|
|
1727
|
+
const rootRef = React.useRef(null);
|
|
1728
|
+
const focusScrollRef = React.useRef(false);
|
|
1729
|
+
const nodeFocus = props.nodeFocus;
|
|
1513
1730
|
React.useEffect(() => {
|
|
1514
|
-
if (
|
|
1515
|
-
setSel(
|
|
1516
|
-
setOpenCat(
|
|
1517
|
-
setOpenElem(
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1731
|
+
if (nodeFocus === null || nodeFocus === void 0) return;
|
|
1732
|
+
setSel(nodeFocus.step);
|
|
1733
|
+
setOpenCat(nodeFocus.cat);
|
|
1734
|
+
setOpenElem("n" + String(nodeFocus.seq));
|
|
1735
|
+
focusScrollRef.current = true;
|
|
1736
|
+
if (props.onNodeFocusHandled !== void 0) props.onNodeFocusHandled();
|
|
1737
|
+
}, [nodeFocus, props.onNodeFocusHandled]);
|
|
1738
|
+
React.useLayoutEffect(() => {
|
|
1739
|
+
if (!focusScrollRef.current) return;
|
|
1740
|
+
focusScrollRef.current = false;
|
|
1741
|
+
rootRef.current?.querySelector(".lc-br-elem-on")?.scrollIntoView({ block: "nearest" });
|
|
1742
|
+
});
|
|
1743
|
+
const missNote = fetchContent === void 0 ? t("browser.noContent") : missState === "loading" ? t("browser.loading") : missState === "absent" ? t("browser.notInLog") : missState === "failed" ? /* @__PURE__ */ React.createElement("button", {
|
|
1744
|
+
type: "button",
|
|
1745
|
+
className: "lc-br-retry",
|
|
1746
|
+
onClick: () => {
|
|
1747
|
+
setRetry((r) => r + 1);
|
|
1748
|
+
}
|
|
1749
|
+
}, t("browser.loadFailed")) : t("browser.noContent");
|
|
1521
1750
|
const requests = data.requests;
|
|
1522
1751
|
const hoverReq = props.previewSeq !== null && props.previewSeq !== void 0 ? requests.find((r) => r.seq === props.previewSeq) ?? null : null;
|
|
1523
1752
|
const req = hoverReq ?? (sel === "live" ? null : requests.find((r) => r.seq === sel) ?? null);
|
|
@@ -1540,8 +1769,13 @@ window.__ModuleLoader__.load({
|
|
|
1540
1769
|
const toolCount = (c) => countOf(view, byCat, c);
|
|
1541
1770
|
const toggleCat = (c) => {
|
|
1542
1771
|
if (!(toolCount(c) > 0 || (c === "system" || c === "tools") && view.header === null)) return;
|
|
1543
|
-
|
|
1544
|
-
|
|
1772
|
+
if (openCat === c) {
|
|
1773
|
+
setOpenCat(null);
|
|
1774
|
+
setOpenElem(null);
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1777
|
+
setOpenCat(c);
|
|
1778
|
+
setOpenElem(c === "system" && view.header?.system !== void 0 ? "sys" : null);
|
|
1545
1779
|
};
|
|
1546
1780
|
const toggleElem = (key) => {
|
|
1547
1781
|
setOpenElem(openElem === key ? null : key);
|
|
@@ -1570,11 +1804,14 @@ window.__ModuleLoader__.load({
|
|
|
1570
1804
|
if (c === "system") {
|
|
1571
1805
|
if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
|
|
1572
1806
|
const system = view.header.system;
|
|
1807
|
+
/* v8 ignore next 1 -- the category opens only when its count > 0
|
|
1808
|
+
(countOf ⟺ system defined); the undefined arm is defensive. */
|
|
1573
1809
|
if (system === void 0) return null;
|
|
1574
1810
|
return elemRow("sys", null, system.replace(/\s+/g, " ").trim().slice(0, 80), breakdown.system, void 0, /* @__PURE__ */ React.createElement(TextSection, {
|
|
1575
1811
|
label: catLabel("system"),
|
|
1576
1812
|
text: system,
|
|
1577
|
-
rich
|
|
1813
|
+
rich,
|
|
1814
|
+
lines: lineLabel
|
|
1578
1815
|
}));
|
|
1579
1816
|
}
|
|
1580
1817
|
if (c === "tools") {
|
|
@@ -1591,6 +1828,7 @@ window.__ModuleLoader__.load({
|
|
|
1591
1828
|
description: tool.description,
|
|
1592
1829
|
schema: tool.schema,
|
|
1593
1830
|
rich,
|
|
1831
|
+
lines: lineLabel,
|
|
1594
1832
|
labels
|
|
1595
1833
|
}));
|
|
1596
1834
|
});
|
|
@@ -1630,14 +1868,17 @@ window.__ModuleLoader__.load({
|
|
|
1630
1868
|
summary: t("block.summary"),
|
|
1631
1869
|
images: t("attach.images"),
|
|
1632
1870
|
other: t("attach.other"),
|
|
1633
|
-
lines:
|
|
1871
|
+
lines: lineLabel,
|
|
1634
1872
|
callState: (err, exit) => /* @__PURE__ */ React.createElement("span", { className: "lc-ts-call-state " + (err ? "lc-ts-call-err" : "lc-ts-call-ok") }, /* @__PURE__ */ React.createElement("i", null), err ? t("call.fail") + (exit !== null ? " · " + t("call.exit", { n: exit }) : "") : t("call.ok"))
|
|
1635
1873
|
},
|
|
1636
|
-
hint: conv === void 0
|
|
1874
|
+
hint: conv === void 0 ? missNote : t("browser.noContent")
|
|
1637
1875
|
}), rowErr);
|
|
1638
1876
|
});
|
|
1639
1877
|
};
|
|
1640
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
1878
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1879
|
+
className: "lc-card",
|
|
1880
|
+
ref: rootRef
|
|
1881
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("browser.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-br-hint" }, t("browser.deltaHint")), /* @__PURE__ */ React.createElement("select", {
|
|
1641
1882
|
className: "lc-br-pick",
|
|
1642
1883
|
value: seq === null ? "live" : String(seq),
|
|
1643
1884
|
onChange: (e) => {
|
|
@@ -1673,12 +1914,15 @@ window.__ModuleLoader__.load({
|
|
|
1673
1914
|
}, /* @__PURE__ */ React.createElement("button", {
|
|
1674
1915
|
type: "button",
|
|
1675
1916
|
className: "lc-br-cat-row" + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
|
|
1917
|
+
/* v8 ignore start -- the handlers exist only when linked,
|
|
1918
|
+
and linked already requires onHoverKey defined (above). */
|
|
1676
1919
|
onMouseEnter: linked ? () => {
|
|
1677
1920
|
if (props.onHoverKey !== void 0) props.onHoverKey(c.key);
|
|
1678
1921
|
} : void 0,
|
|
1679
1922
|
onMouseLeave: linked ? () => {
|
|
1680
1923
|
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
1681
1924
|
} : void 0,
|
|
1925
|
+
/* v8 ignore stop */
|
|
1682
1926
|
onClick: () => {
|
|
1683
1927
|
toggleCat(c.key);
|
|
1684
1928
|
}
|
|
@@ -1834,26 +2078,7 @@ window.__ModuleLoader__.load({
|
|
|
1834
2078
|
parts: head.parts,
|
|
1835
2079
|
hoverKey: props.hoverKey,
|
|
1836
2080
|
onHoverKey: props.onHoverKey
|
|
1837
|
-
})
|
|
1838
|
-
type: "button",
|
|
1839
|
-
className: "lc-tools-label",
|
|
1840
|
-
onClick: () => {
|
|
1841
|
-
if (props.onToolFocus !== void 0) props.onToolFocus({});
|
|
1842
|
-
}
|
|
1843
|
-
}, t("tools.top")), props.tools.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => /* @__PURE__ */ React.createElement("button", {
|
|
1844
|
-
key: tool.name,
|
|
1845
|
-
type: "button",
|
|
1846
|
-
className: "lc-tool-chip",
|
|
1847
|
-
onClick: () => {
|
|
1848
|
-
if (props.onToolFocus !== void 0) props.onToolFocus({ tool: tool.name });
|
|
1849
|
-
}
|
|
1850
|
-
}, tool.name + " " + fmt(tool.tokens))), props.tools.length > 5 ? /* @__PURE__ */ React.createElement("button", {
|
|
1851
|
-
type: "button",
|
|
1852
|
-
className: "lc-tools-more",
|
|
1853
|
-
onClick: () => {
|
|
1854
|
-
if (props.onToolFocus !== void 0) props.onToolFocus({});
|
|
1855
|
-
}
|
|
1856
|
-
}, t("tools.more", { n: props.tools.length })) : null) : null);
|
|
2081
|
+
}));
|
|
1857
2082
|
};
|
|
1858
2083
|
}
|
|
1859
2084
|
//#endregion
|
|
@@ -1897,7 +2122,7 @@ window.__ModuleLoader__.load({
|
|
|
1897
2122
|
const breakdown = typeof props.useProjection === "function" ? contextBreakdownOf(props.useProjection("contextBreakdown")) : null;
|
|
1898
2123
|
const headers = typeof props.useProjection === "function" ? headersOf(props.useProjection("contextHeaders")) : null;
|
|
1899
2124
|
const [hoverCat, setHoverCat] = React.useState(null);
|
|
1900
|
-
const
|
|
2125
|
+
const fetchContent = React.useMemo(() => sessionId !== "" ? makeContentFetcher(ctx, sessionId) : void 0, [ctx, sessionId]);
|
|
1901
2126
|
const close = React.useCallback(() => {
|
|
1902
2127
|
if (sessionId === "") return;
|
|
1903
2128
|
modalStoreOf(sessionId).set(false);
|
|
@@ -1940,20 +2165,14 @@ window.__ModuleLoader__.load({
|
|
|
1940
2165
|
head,
|
|
1941
2166
|
subtitle,
|
|
1942
2167
|
hoverKey: hoverCat,
|
|
1943
|
-
onHoverKey: setHoverCat
|
|
1944
|
-
tools: data.toolList,
|
|
1945
|
-
onToolFocus: setToolFocus
|
|
2168
|
+
onHoverKey: setHoverCat
|
|
1946
2169
|
}), /* @__PURE__ */ React.createElement(ContextBrowser, {
|
|
1947
2170
|
data,
|
|
1948
2171
|
headers,
|
|
1949
2172
|
useSession: props.useSession,
|
|
1950
|
-
|
|
2173
|
+
fetchContent,
|
|
1951
2174
|
hoverKey: hoverCat,
|
|
1952
|
-
onHoverKey: setHoverCat
|
|
1953
|
-
toolFocus,
|
|
1954
|
-
onToolFocusHandled: () => {
|
|
1955
|
-
setToolFocus(null);
|
|
1956
|
-
}
|
|
2175
|
+
onHoverKey: setHoverCat
|
|
1957
2176
|
}))));
|
|
1958
2177
|
}
|
|
1959
2178
|
return function ContextModal(props) {
|
|
@@ -2036,6 +2255,27 @@ window.__ModuleLoader__.load({
|
|
|
2036
2255
|
onPick: (id) => {
|
|
2037
2256
|
props.set?.("defaultTrendMode", id);
|
|
2038
2257
|
}
|
|
2258
|
+
}), /* @__PURE__ */ React.createElement(PrefRow, {
|
|
2259
|
+
label: t("settings.fileSort"),
|
|
2260
|
+
value: state.fileSort,
|
|
2261
|
+
disabled,
|
|
2262
|
+
options: [
|
|
2263
|
+
{
|
|
2264
|
+
id: "count",
|
|
2265
|
+
label: t("files.sort.count")
|
|
2266
|
+
},
|
|
2267
|
+
{
|
|
2268
|
+
id: "latest",
|
|
2269
|
+
label: t("files.sort.latest")
|
|
2270
|
+
},
|
|
2271
|
+
{
|
|
2272
|
+
id: "path",
|
|
2273
|
+
label: t("files.sort.path")
|
|
2274
|
+
}
|
|
2275
|
+
],
|
|
2276
|
+
onPick: (id) => {
|
|
2277
|
+
props.set?.("defaultFileSort", id);
|
|
2278
|
+
}
|
|
2039
2279
|
})) : null);
|
|
2040
2280
|
};
|
|
2041
2281
|
}
|
|
@@ -2046,7 +2286,8 @@ window.__ModuleLoader__.load({
|
|
|
2046
2286
|
const v = value;
|
|
2047
2287
|
return {
|
|
2048
2288
|
...v.defaultGranularity === "step" || v.defaultGranularity === "turn" ? { granularity: v.defaultGranularity } : {},
|
|
2049
|
-
...v.defaultTrendMode === "total" || v.defaultTrendMode === "delta" ? { mode: v.defaultTrendMode } : {}
|
|
2289
|
+
...v.defaultTrendMode === "total" || v.defaultTrendMode === "delta" ? { mode: v.defaultTrendMode } : {},
|
|
2290
|
+
...v.defaultFileSort === "count" || v.defaultFileSort === "latest" || v.defaultFileSort === "path" ? { fileSort: v.defaultFileSort } : {}
|
|
2050
2291
|
};
|
|
2051
2292
|
}
|
|
2052
2293
|
function createContextSettings() {
|
|
@@ -2054,12 +2295,13 @@ window.__ModuleLoader__.load({
|
|
|
2054
2295
|
status: "loading",
|
|
2055
2296
|
granularity: "step",
|
|
2056
2297
|
mode: "total",
|
|
2298
|
+
fileSort: "count",
|
|
2057
2299
|
writable: false
|
|
2058
2300
|
};
|
|
2059
2301
|
let scope;
|
|
2060
2302
|
const listeners = /* @__PURE__ */ new Set();
|
|
2061
2303
|
const publish = (next) => {
|
|
2062
|
-
if (next.status === state.status && next.granularity === state.granularity && next.mode === state.mode && next.writable === state.writable) return;
|
|
2304
|
+
if (next.status === state.status && next.granularity === state.granularity && next.mode === state.mode && next.fileSort === state.fileSort && next.writable === state.writable) return;
|
|
2063
2305
|
state = next;
|
|
2064
2306
|
for (const listener of listeners) listener();
|
|
2065
2307
|
};
|
|
@@ -2075,6 +2317,7 @@ window.__ModuleLoader__.load({
|
|
|
2075
2317
|
},
|
|
2076
2318
|
defaultGranularity: () => state.granularity,
|
|
2077
2319
|
defaultTrendMode: () => state.mode,
|
|
2320
|
+
defaultFileSort: () => state.fileSort,
|
|
2078
2321
|
attach(bound) {
|
|
2079
2322
|
scope = bound;
|
|
2080
2323
|
const sync = () => {
|
|
@@ -2084,6 +2327,7 @@ window.__ModuleLoader__.load({
|
|
|
2084
2327
|
status: snap.status === "ready" || snap.status === "unavailable" ? snap.status : "loading",
|
|
2085
2328
|
granularity: prefs.granularity ?? state.granularity,
|
|
2086
2329
|
mode: prefs.mode ?? state.mode,
|
|
2330
|
+
fileSort: prefs.fileSort ?? state.fileSort,
|
|
2087
2331
|
writable: snap.writable
|
|
2088
2332
|
});
|
|
2089
2333
|
};
|
|
@@ -2100,6 +2344,257 @@ window.__ModuleLoader__.load({
|
|
|
2100
2344
|
};
|
|
2101
2345
|
}
|
|
2102
2346
|
//#endregion
|
|
2347
|
+
//#region src/client/brief.ts
|
|
2348
|
+
/** Every served node (live tail + removed archive copies), seq-sorted. */
|
|
2349
|
+
function briefNodes(data) {
|
|
2350
|
+
return [...data.nodes, ...data.archive].sort((a, b) => a.seq - b.seq);
|
|
2351
|
+
}
|
|
2352
|
+
/** First index whose node seq is >= `seq` (lower bound over the sorted list). */
|
|
2353
|
+
function lowerBound(nodes, seq) {
|
|
2354
|
+
let lo = 0;
|
|
2355
|
+
let hi = nodes.length;
|
|
2356
|
+
while (lo < hi) {
|
|
2357
|
+
const mid = lo + hi >> 1;
|
|
2358
|
+
if (nodes[mid].seq < seq) lo = mid + 1;
|
|
2359
|
+
else hi = mid;
|
|
2360
|
+
}
|
|
2361
|
+
return lo;
|
|
2362
|
+
}
|
|
2363
|
+
/**
|
|
2364
|
+
* Derive the brief for `requests[idx]` (the DISPLAY list — step records or
|
|
2365
|
+
* turn aggregates; a turn aggregate is always a turn start, so its inputs row
|
|
2366
|
+
* stays hidden and the opener/response carry the narrative).
|
|
2367
|
+
*/
|
|
2368
|
+
function briefOf(nodes, requests, idx) {
|
|
2369
|
+
if (idx < 0 || idx >= requests.length) return null;
|
|
2370
|
+
const req = requests[idx];
|
|
2371
|
+
const ri = lowerBound(nodes, req.seq);
|
|
2372
|
+
const hit = ri < nodes.length && nodes[ri].seq === req.seq ? nodes[ri] : void 0;
|
|
2373
|
+
const response = hit !== void 0 && hit.cat === "assistant" ? hit : void 0;
|
|
2374
|
+
const turnStart = idx === 0 || (requests[idx - 1].turn ?? 0) !== (req.turn ?? 0);
|
|
2375
|
+
let firstIdx = idx;
|
|
2376
|
+
while (firstIdx > 0 && (requests[firstIdx - 1].turn ?? 0) === (req.turn ?? 0)) firstIdx--;
|
|
2377
|
+
const upper = requests[firstIdx].seq;
|
|
2378
|
+
const lower = firstIdx > 0 ? requests[firstIdx - 1].seq : -1;
|
|
2379
|
+
let opener;
|
|
2380
|
+
for (let i = lowerBound(nodes, upper) - 1; i >= 0; i--) {
|
|
2381
|
+
const n = nodes[i];
|
|
2382
|
+
if (n.seq <= lower) break;
|
|
2383
|
+
if (n.cat === "user") {
|
|
2384
|
+
opener = n;
|
|
2385
|
+
break;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
const inputs = [];
|
|
2389
|
+
if (!turnStart) {
|
|
2390
|
+
const prevSeq = requests[idx - 1].seq;
|
|
2391
|
+
for (let i = lowerBound(nodes, prevSeq + 1); i < nodes.length && nodes[i].seq < req.seq; i++) inputs.push(nodes[i]);
|
|
2392
|
+
}
|
|
2393
|
+
return {
|
|
2394
|
+
opener,
|
|
2395
|
+
inputs,
|
|
2396
|
+
response
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* seq → one-line reply preview for the chart tooltip: the response node's
|
|
2401
|
+
* text, else its tool-call breadcrumb. Absent for usage-only replies and
|
|
2402
|
+
* steps outside retention.
|
|
2403
|
+
*/
|
|
2404
|
+
function replyTipsOf(nodes) {
|
|
2405
|
+
const m = /* @__PURE__ */ new Map();
|
|
2406
|
+
for (const n of nodes) {
|
|
2407
|
+
if (n.cat !== "assistant") continue;
|
|
2408
|
+
const s = n.text ?? (n.calls !== void 0 ? n.calls.join(" › ") : "");
|
|
2409
|
+
if (s !== "") m.set(n.seq, s);
|
|
2410
|
+
}
|
|
2411
|
+
return m;
|
|
2412
|
+
}
|
|
2413
|
+
//#endregion
|
|
2414
|
+
//#region src/client/fileActivity.ts
|
|
2415
|
+
const KIND_BY_TOOL = {
|
|
2416
|
+
read: "read",
|
|
2417
|
+
read_image: "read",
|
|
2418
|
+
write: "write",
|
|
2419
|
+
edit: "write",
|
|
2420
|
+
grep: "search",
|
|
2421
|
+
glob: "search"
|
|
2422
|
+
};
|
|
2423
|
+
/** The file purpose of a tool, or null for non-file tools (bash, web_search…). */
|
|
2424
|
+
function kindOfTool(tool) {
|
|
2425
|
+
if (tool === void 0) return null;
|
|
2426
|
+
return KIND_BY_TOOL[tool] ?? null;
|
|
2427
|
+
}
|
|
2428
|
+
/**
|
|
2429
|
+
* The operation's target path: the path-ish argument of read/write tools;
|
|
2430
|
+
* for searches the narrowing `path`, else the pattern itself (a pathless
|
|
2431
|
+
* grep/glob's target IS the pattern — the workspace-wide search text).
|
|
2432
|
+
*/
|
|
2433
|
+
function pathOfArgs(tool, args) {
|
|
2434
|
+
if (args === null) return null;
|
|
2435
|
+
if (tool === "grep" || tool === "glob") {
|
|
2436
|
+
const p = args.path;
|
|
2437
|
+
if (typeof p === "string" && p !== "") return p;
|
|
2438
|
+
const pattern = args.pattern;
|
|
2439
|
+
return typeof pattern === "string" && pattern !== "" ? pattern : null;
|
|
2440
|
+
}
|
|
2441
|
+
for (const k of [
|
|
2442
|
+
"file_path",
|
|
2443
|
+
"filePath",
|
|
2444
|
+
"path"
|
|
2445
|
+
]) {
|
|
2446
|
+
const v = args[k];
|
|
2447
|
+
if (typeof v === "string" && v !== "") return v;
|
|
2448
|
+
}
|
|
2449
|
+
return null;
|
|
2450
|
+
}
|
|
2451
|
+
/** Rendered line count: '' is 0, a trailing newline closes its own line. */
|
|
2452
|
+
function linesOf(s) {
|
|
2453
|
+
if (s === "") return 0;
|
|
2454
|
+
let n = 0;
|
|
2455
|
+
for (let i = 0; i < s.length; i++) if (s[i] === "\n") n++;
|
|
2456
|
+
return s.endsWith("\n") ? n : n + 1;
|
|
2457
|
+
}
|
|
2458
|
+
/**
|
|
2459
|
+
* The signed line footprint of one call: an edit removes its old string and
|
|
2460
|
+
* adds its new one; a write adds its content (the pre-existing body, if any,
|
|
2461
|
+
* is unknowable from the arguments — the estimate stays honest about that).
|
|
2462
|
+
* Callers reach here only with parsed args (a null parse yields no path).
|
|
2463
|
+
*/
|
|
2464
|
+
function deltaOf(tool, args) {
|
|
2465
|
+
if (tool === "edit") {
|
|
2466
|
+
const oldS = args.old_string;
|
|
2467
|
+
const newS = args.new_string;
|
|
2468
|
+
return {
|
|
2469
|
+
added: typeof newS === "string" ? linesOf(newS) : 0,
|
|
2470
|
+
removed: typeof oldS === "string" ? linesOf(oldS) : 0
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
if (tool === "write") {
|
|
2474
|
+
const content = args.content;
|
|
2475
|
+
return {
|
|
2476
|
+
added: typeof content === "string" ? linesOf(content) : 0,
|
|
2477
|
+
removed: 0
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
return {
|
|
2481
|
+
added: 0,
|
|
2482
|
+
removed: 0
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
const IMAGE_EXT = /\.(png|jpe?g|gif|webp|svg|bmp|ico|avif)$/i;
|
|
2486
|
+
/** The file's form — multimodal reads and image extensions scan apart; a trailing slash marks a directory target. */
|
|
2487
|
+
function formOf(tool, path) {
|
|
2488
|
+
if (tool === "read_image" || IMAGE_EXT.test(path)) return "image";
|
|
2489
|
+
if (path.endsWith("/")) return "dir";
|
|
2490
|
+
return "text";
|
|
2491
|
+
}
|
|
2492
|
+
/** Fold every served tool-result node into per-file activity. */
|
|
2493
|
+
function activityOf(nodes, convOf, before) {
|
|
2494
|
+
const totals = {
|
|
2495
|
+
read: {
|
|
2496
|
+
files: 0,
|
|
2497
|
+
ops: 0
|
|
2498
|
+
},
|
|
2499
|
+
write: {
|
|
2500
|
+
files: 0,
|
|
2501
|
+
ops: 0
|
|
2502
|
+
},
|
|
2503
|
+
search: {
|
|
2504
|
+
files: 0,
|
|
2505
|
+
ops: 0
|
|
2506
|
+
},
|
|
2507
|
+
image: {
|
|
2508
|
+
files: 0,
|
|
2509
|
+
ops: 0
|
|
2510
|
+
},
|
|
2511
|
+
added: 0,
|
|
2512
|
+
removed: 0
|
|
2513
|
+
};
|
|
2514
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
2515
|
+
let unresolved = 0;
|
|
2516
|
+
for (const n of nodes) {
|
|
2517
|
+
if (n.cat !== "tool") continue;
|
|
2518
|
+
if (before !== null && n.seq >= before) continue;
|
|
2519
|
+
const kind = kindOfTool(n.tool);
|
|
2520
|
+
if (kind === null) continue;
|
|
2521
|
+
const tool = n.tool;
|
|
2522
|
+
totals[kind].ops++;
|
|
2523
|
+
const conv = convOf(n.seq);
|
|
2524
|
+
const args = parseCallArgs(conv?.call?.argsRaw);
|
|
2525
|
+
const path = pathOfArgs(tool, args);
|
|
2526
|
+
if (args === null || path === null) {
|
|
2527
|
+
unresolved++;
|
|
2528
|
+
continue;
|
|
2529
|
+
}
|
|
2530
|
+
const { added, removed } = deltaOf(tool, args);
|
|
2531
|
+
const detail = kind === "search" && typeof args.path === "string" && args.path !== "" && typeof args.pattern === "string" && args.pattern !== "" ? args.pattern : void 0;
|
|
2532
|
+
const op = {
|
|
2533
|
+
seq: n.seq,
|
|
2534
|
+
...n.gone !== void 0 ? { gone: n.gone } : {},
|
|
2535
|
+
kind,
|
|
2536
|
+
tool,
|
|
2537
|
+
...n.time !== void 0 ? { time: n.time } : {},
|
|
2538
|
+
err: n.err === true || conv.isError === true,
|
|
2539
|
+
added,
|
|
2540
|
+
removed,
|
|
2541
|
+
...detail !== void 0 ? { detail } : {}
|
|
2542
|
+
};
|
|
2543
|
+
let entry = byPath.get(path);
|
|
2544
|
+
if (entry === void 0) {
|
|
2545
|
+
entry = {
|
|
2546
|
+
path,
|
|
2547
|
+
form: formOf(tool, path),
|
|
2548
|
+
reads: 0,
|
|
2549
|
+
writes: 0,
|
|
2550
|
+
searches: 0,
|
|
2551
|
+
added: 0,
|
|
2552
|
+
removed: 0,
|
|
2553
|
+
errs: 0,
|
|
2554
|
+
ops: []
|
|
2555
|
+
};
|
|
2556
|
+
byPath.set(path, entry);
|
|
2557
|
+
}
|
|
2558
|
+
if (kind === "read") entry.reads++;
|
|
2559
|
+
else if (kind === "write") entry.writes++;
|
|
2560
|
+
else entry.searches++;
|
|
2561
|
+
entry.added += added;
|
|
2562
|
+
entry.removed += removed;
|
|
2563
|
+
if (op.err) entry.errs++;
|
|
2564
|
+
entry.ops.push(op);
|
|
2565
|
+
}
|
|
2566
|
+
const entries = [...byPath.values()];
|
|
2567
|
+
for (const e of entries) {
|
|
2568
|
+
e.ops.sort((a, b) => b.seq - a.seq);
|
|
2569
|
+
if (e.reads > 0) totals.read.files++;
|
|
2570
|
+
if (e.writes > 0) totals.write.files++;
|
|
2571
|
+
if (e.searches > 0) totals.search.files++;
|
|
2572
|
+
if (e.form === "image") {
|
|
2573
|
+
totals.image.files++;
|
|
2574
|
+
totals.image.ops += e.ops.length;
|
|
2575
|
+
}
|
|
2576
|
+
totals.added += e.added;
|
|
2577
|
+
totals.removed += e.removed;
|
|
2578
|
+
}
|
|
2579
|
+
entries.sort((a, b) => b.ops[0].seq - a.ops[0].seq);
|
|
2580
|
+
return {
|
|
2581
|
+
entries,
|
|
2582
|
+
totals,
|
|
2583
|
+
unresolved
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
/**
|
|
2587
|
+
* The browser step whose assembled surface SHOWS an op's result node: the
|
|
2588
|
+
* first request dispatched after it while it was still alive (that step's
|
|
2589
|
+
* brief is where the result landed). A live node no request has consumed
|
|
2590
|
+
* yet reveals on the live surface; an archived node no retained step still
|
|
2591
|
+
* contains is not viewable anywhere (null → the row stays inert).
|
|
2592
|
+
*/
|
|
2593
|
+
function locateStepOf(requests, seq, gone) {
|
|
2594
|
+
for (const r of requests) if (r.seq > seq && (gone === void 0 || gone > r.seq)) return r.seq;
|
|
2595
|
+
return gone === void 0 ? "live" : null;
|
|
2596
|
+
}
|
|
2597
|
+
//#endregion
|
|
2103
2598
|
//#region src/client/components/events.tsx
|
|
2104
2599
|
const EVENT_ICONS = {
|
|
2105
2600
|
compaction: "✂",
|
|
@@ -2193,6 +2688,160 @@ window.__ModuleLoader__.load({
|
|
|
2193
2688
|
}));
|
|
2194
2689
|
};
|
|
2195
2690
|
}
|
|
2691
|
+
//#endregion
|
|
2692
|
+
//#region src/client/components/fileCard.tsx
|
|
2693
|
+
/** An expanded file lists at most this many ops before a "+n earlier" line. */
|
|
2694
|
+
const MAX_OPS = 8;
|
|
2695
|
+
function makeFileCard(kit, settings) {
|
|
2696
|
+
const { t, fmt, fmtTime } = kit;
|
|
2697
|
+
function formGlyph(form) {
|
|
2698
|
+
return form === "image" ? "🖼" : form === "dir" ? "📁" : "📄";
|
|
2699
|
+
}
|
|
2700
|
+
function matches(e, f) {
|
|
2701
|
+
if (f === "all") return true;
|
|
2702
|
+
if (f === "image") return e.form === "image";
|
|
2703
|
+
if (f === "read") return e.reads > 0;
|
|
2704
|
+
if (f === "write") return e.writes > 0;
|
|
2705
|
+
return e.searches > 0;
|
|
2706
|
+
}
|
|
2707
|
+
/** Signed line pair, harness diff semantics: growth on the success token, shrinkage on the error token. */
|
|
2708
|
+
function DeltaPair(props) {
|
|
2709
|
+
return /* @__PURE__ */ React.createElement("span", { className: "lc-fa-delta" }, props.added > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-up" }, "+" + fmt(props.added)) : null, props.removed > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-down" }, "−" + fmt(props.removed)) : null);
|
|
2710
|
+
}
|
|
2711
|
+
return function FileCard(props) {
|
|
2712
|
+
const { activity } = props;
|
|
2713
|
+
const [filter, setFilter] = React.useState("all");
|
|
2714
|
+
const [sort, setSort] = React.useState(() => settings.defaultFileSort());
|
|
2715
|
+
const [query, setQuery] = React.useState("");
|
|
2716
|
+
const [openPath, setOpenPath] = React.useState(null);
|
|
2717
|
+
const q = query.trim().toLowerCase();
|
|
2718
|
+
const countOf = (e) => filter === "read" ? e.reads : filter === "write" ? e.writes : filter === "search" ? e.searches : e.ops.length;
|
|
2719
|
+
const shown = activity.entries.filter((e) => matches(e, filter) && (q === "" || e.path.toLowerCase().includes(q))).sort((a, b) => sort === "count" ? countOf(b) - countOf(a) || b.ops[0].seq - a.ops[0].seq : sort === "latest" ? b.ops[0].seq - a.ops[0].seq : a.path < b.path ? -1 : 1);
|
|
2720
|
+
const chips = [
|
|
2721
|
+
{
|
|
2722
|
+
key: "all",
|
|
2723
|
+
files: activity.entries.length,
|
|
2724
|
+
ops: activity.totals.read.ops + activity.totals.write.ops + activity.totals.search.ops
|
|
2725
|
+
},
|
|
2726
|
+
{
|
|
2727
|
+
key: "read",
|
|
2728
|
+
files: activity.totals.read.files,
|
|
2729
|
+
ops: activity.totals.read.ops
|
|
2730
|
+
},
|
|
2731
|
+
{
|
|
2732
|
+
key: "write",
|
|
2733
|
+
files: activity.totals.write.files,
|
|
2734
|
+
ops: activity.totals.write.ops
|
|
2735
|
+
},
|
|
2736
|
+
{
|
|
2737
|
+
key: "search",
|
|
2738
|
+
files: activity.totals.search.files,
|
|
2739
|
+
ops: activity.totals.search.ops
|
|
2740
|
+
},
|
|
2741
|
+
{
|
|
2742
|
+
key: "image",
|
|
2743
|
+
files: activity.totals.image.files,
|
|
2744
|
+
ops: activity.totals.image.ops
|
|
2745
|
+
}
|
|
2746
|
+
];
|
|
2747
|
+
const opLine = (op) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-time" }, op.time !== void 0 ? fmtTime(op.time) : "—"), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-tool" }, op.tool), op.detail !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-detail" }, op.detail) : null, op.added + op.removed > 0 ? /* @__PURE__ */ React.createElement(DeltaPair, {
|
|
2748
|
+
added: op.added,
|
|
2749
|
+
removed: op.removed
|
|
2750
|
+
}) : null, op.err ? /* @__PURE__ */ React.createElement("span", {
|
|
2751
|
+
className: "lc-br-err-dot",
|
|
2752
|
+
title: t("node.failed")
|
|
2753
|
+
}) : null);
|
|
2754
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("files.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, props.scope)), activity.entries.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("files.empty")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "lc-fa-ctl" }, /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, chips.map((c) => /* @__PURE__ */ React.createElement("button", {
|
|
2755
|
+
key: c.key,
|
|
2756
|
+
type: "button",
|
|
2757
|
+
className: "lc-gran-btn" + (filter === c.key ? " lc-gran-on" : "") + (c.key === "read" || c.key === "write" || c.key === "search" ? " lc-fa-chip-" + c.key : ""),
|
|
2758
|
+
title: t("files.chipTip", {
|
|
2759
|
+
files: c.files,
|
|
2760
|
+
ops: c.ops
|
|
2761
|
+
}),
|
|
2762
|
+
onClick: () => {
|
|
2763
|
+
setFilter((cur) => cur === c.key ? "all" : c.key);
|
|
2764
|
+
}
|
|
2765
|
+
}, t("files.kind." + c.key), /* @__PURE__ */ React.createElement("b", { className: "lc-fa-n" }, fmt(c.ops))))), /* @__PURE__ */ React.createElement("input", {
|
|
2766
|
+
className: "lc-fa-search",
|
|
2767
|
+
value: query,
|
|
2768
|
+
placeholder: t("files.search"),
|
|
2769
|
+
onChange: (ev) => {
|
|
2770
|
+
setQuery(ev.target.value);
|
|
2771
|
+
}
|
|
2772
|
+
})), /* @__PURE__ */ React.createElement("div", { className: "lc-fa-meta" }, /* @__PURE__ */ React.createElement("span", null, t("files.files", { n: activity.entries.length })), activity.totals.added + activity.totals.removed > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-meta-delta" }, /* @__PURE__ */ React.createElement(DeltaPair, {
|
|
2773
|
+
added: activity.totals.added,
|
|
2774
|
+
removed: activity.totals.removed
|
|
2775
|
+
}), /* @__PURE__ */ React.createElement("span", {
|
|
2776
|
+
className: "lc-fa-meta-tip",
|
|
2777
|
+
role: "tooltip"
|
|
2778
|
+
}, t("files.deltaTip"))) : null, /* @__PURE__ */ React.createElement("span", {
|
|
2779
|
+
className: "lc-gran lc-fa-sort",
|
|
2780
|
+
role: "group",
|
|
2781
|
+
title: t("files.sortTip")
|
|
2782
|
+
}, [
|
|
2783
|
+
"count",
|
|
2784
|
+
"latest",
|
|
2785
|
+
"path"
|
|
2786
|
+
].map((k) => /* @__PURE__ */ React.createElement("button", {
|
|
2787
|
+
key: k,
|
|
2788
|
+
type: "button",
|
|
2789
|
+
className: "lc-gran-btn" + (sort === k ? " lc-gran-on" : ""),
|
|
2790
|
+
onClick: () => {
|
|
2791
|
+
setSort(k);
|
|
2792
|
+
}
|
|
2793
|
+
}, t("files.sort." + k))))), shown.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("files.noMatch")) : /* @__PURE__ */ React.createElement("div", { className: "lc-fa-list" }, shown.map((e) => {
|
|
2794
|
+
const open = openPath === e.path;
|
|
2795
|
+
const trimmed = e.path.endsWith("/") ? e.path.slice(0, -1) : e.path;
|
|
2796
|
+
const slash = trimmed.lastIndexOf("/");
|
|
2797
|
+
const dir = slash >= 0 ? trimmed.slice(0, slash + 1) : "";
|
|
2798
|
+
const base = slash >= 0 ? trimmed.slice(slash + 1) : trimmed;
|
|
2799
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
2800
|
+
key: e.path,
|
|
2801
|
+
className: "lc-fa-item" + (open ? " lc-fa-item-on" : "")
|
|
2802
|
+
}, /* @__PURE__ */ React.createElement("button", {
|
|
2803
|
+
type: "button",
|
|
2804
|
+
className: "lc-fa-row",
|
|
2805
|
+
title: e.path,
|
|
2806
|
+
onClick: () => {
|
|
2807
|
+
setOpenPath(open ? null : e.path);
|
|
2808
|
+
}
|
|
2809
|
+
}, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "▸"), /* @__PURE__ */ React.createElement("span", {
|
|
2810
|
+
className: "lc-fa-form",
|
|
2811
|
+
title: t("files.form." + e.form)
|
|
2812
|
+
}, formGlyph(e.form)), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-path" }, dir !== "" ? /* @__PURE__ */ React.createElement("em", null, dir) : null, /* @__PURE__ */ React.createElement("b", null, base)), e.reads > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
2813
|
+
className: "lc-fa-badge lc-fa-b-read",
|
|
2814
|
+
title: t("files.kind.read")
|
|
2815
|
+
}, /* @__PURE__ */ React.createElement("i", null), fmt(e.reads)) : null, e.writes > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
2816
|
+
className: "lc-fa-badge lc-fa-b-write",
|
|
2817
|
+
title: t("files.kind.write")
|
|
2818
|
+
}, /* @__PURE__ */ React.createElement("i", null), fmt(e.writes)) : null, e.searches > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
2819
|
+
className: "lc-fa-badge lc-fa-b-search",
|
|
2820
|
+
title: t("files.kind.search")
|
|
2821
|
+
}, /* @__PURE__ */ React.createElement("i", null), fmt(e.searches)) : null, e.added + e.removed > 0 ? /* @__PURE__ */ React.createElement(DeltaPair, {
|
|
2822
|
+
added: e.added,
|
|
2823
|
+
removed: e.removed
|
|
2824
|
+
}) : null, e.errs > 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
2825
|
+
className: "lc-br-err-dot",
|
|
2826
|
+
title: t("files.errs", { n: e.errs })
|
|
2827
|
+
}) : null, e.ops[0].time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-time" }, fmtTime(e.ops[0].time)) : null), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-ops" }, e.ops.slice(0, MAX_OPS).map((op) => {
|
|
2828
|
+
const onLocate = props.onLocate;
|
|
2829
|
+
return onLocate !== void 0 ? /* @__PURE__ */ React.createElement("button", {
|
|
2830
|
+
key: op.seq,
|
|
2831
|
+
type: "button",
|
|
2832
|
+
className: "lc-fa-op lc-fa-op-link",
|
|
2833
|
+
title: t("files.locate"),
|
|
2834
|
+
onClick: () => {
|
|
2835
|
+
onLocate(op);
|
|
2836
|
+
}
|
|
2837
|
+
}, opLine(op)) : /* @__PURE__ */ React.createElement("div", {
|
|
2838
|
+
key: op.seq,
|
|
2839
|
+
className: "lc-fa-op"
|
|
2840
|
+
}, opLine(op));
|
|
2841
|
+
}), e.ops.length > MAX_OPS ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-more" }, t("files.moreOps", { n: e.ops.length - MAX_OPS })) : null) : null);
|
|
2842
|
+
}))), activity.unresolved > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-note" }, t("files.unresolved", { n: activity.unresolved })) : null);
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2196
2845
|
const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
|
|
2197
2846
|
const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
|
|
2198
2847
|
//#endregion
|
|
@@ -2236,7 +2885,7 @@ window.__ModuleLoader__.load({
|
|
|
2236
2885
|
return function PluginInfo() {
|
|
2237
2886
|
const [latest, setLatest] = React.useState(null);
|
|
2238
2887
|
React.useEffect(() => {
|
|
2239
|
-
if ("0.
|
|
2888
|
+
if ("0.33.1".includes("-dev")) return;
|
|
2240
2889
|
let on = true;
|
|
2241
2890
|
fetchLatestVersion().then((v) => {
|
|
2242
2891
|
if (on && v) setLatest(v);
|
|
@@ -2245,8 +2894,8 @@ window.__ModuleLoader__.load({
|
|
|
2245
2894
|
on = false;
|
|
2246
2895
|
};
|
|
2247
2896
|
}, []);
|
|
2248
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
2249
|
-
const nameValue = ["dsh-context (v0.
|
|
2897
|
+
const update = latest !== null && isNewerVersion(latest, "0.33.1") ? latest : null;
|
|
2898
|
+
const nameValue = ["dsh-context (v0.33.1)"];
|
|
2250
2899
|
if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
|
|
2251
2900
|
key: "update",
|
|
2252
2901
|
className: "lc-pi-update"
|
|
@@ -2258,12 +2907,153 @@ window.__ModuleLoader__.load({
|
|
|
2258
2907
|
//#region src/client/components/requestDetail.tsx
|
|
2259
2908
|
function makeRequestDetail(kit, StackedBar) {
|
|
2260
2909
|
const { t, fmt, fmtTime, catLabel, eventLabel, eventAt } = kit;
|
|
2910
|
+
/**
|
|
2911
|
+
* One brief row: a fixed-width kind tag plus one glanceable line. The tag carries a styled, instant explanation bubble (the
|
|
2912
|
+
* `.lc-stat-tip` pattern); the content span keeps the native title (preview + locate hint), so the two never stack.
|
|
2913
|
+
* Clickable when the browser linkage is wired.
|
|
2914
|
+
*/
|
|
2915
|
+
function BriefRow(props) {
|
|
2916
|
+
const cls = "lc-brief-row" + (props.onLocate !== void 0 ? " lc-brief-row-link" : "");
|
|
2917
|
+
const inner = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-brief-tag" }, props.tag, /* @__PURE__ */ React.createElement("span", {
|
|
2918
|
+
className: "lc-brief-tip",
|
|
2919
|
+
role: "tooltip"
|
|
2920
|
+
}, props.tagTip)), props.children);
|
|
2921
|
+
if (props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className: cls }, inner);
|
|
2922
|
+
const locate = () => {
|
|
2923
|
+
props.onLocate?.(props.node, props.isResponse);
|
|
2924
|
+
};
|
|
2925
|
+
return /* @__PURE__ */ React.createElement("button", {
|
|
2926
|
+
type: "button",
|
|
2927
|
+
className: cls,
|
|
2928
|
+
onClick: locate
|
|
2929
|
+
}, inner);
|
|
2930
|
+
}
|
|
2931
|
+
/** One-line identity of a surface node: text preview, call breadcrumb, tool name, or a localized placeholder. */
|
|
2932
|
+
function nodeLine(n, conv) {
|
|
2933
|
+
/* v8 ignore if -- chipParts returns for tool cats upstream; only
|
|
2934
|
+
inject-with-skill and drift nodes ever reach nodeLine. Defensive. */
|
|
2935
|
+
if (n.cat === "tool") return callSummaryOf(conv) ?? n.tool ?? t("node.toolResult");
|
|
2936
|
+
if (n.text !== void 0 && n.text !== "") return n.text;
|
|
2937
|
+
if (n.skill !== void 0) return t("node.skillTag", { name: n.skill });
|
|
2938
|
+
if (n.calls !== void 0 && n.calls.length > 0) {
|
|
2939
|
+
const summary = blockSummaryOf(conv);
|
|
2940
|
+
return n.calls.join(" › ") + (summary !== null ? " · " + summary : "");
|
|
2941
|
+
}
|
|
2942
|
+
/* v8 ignore if -- chipParts returns for assistant cats upstream. Defensive. */
|
|
2943
|
+
if (n.cat === "assistant") return t("node.empty");
|
|
2944
|
+
/* v8 ignore if -- inject nodes arrive only WITH a skill (the text/skill
|
|
2945
|
+
arms above return first); inject-without-skill never reaches nodeLine. */
|
|
2946
|
+
if (n.cat === "inject") return t("form." + (n.form ?? "context"));
|
|
2947
|
+
return t("node.nonText");
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2950
|
+
* One line's anatomy — a compact FACT tag plus the preview text, mirroring the Context browser's element rows so a
|
|
2951
|
+
* brief line reads exactly like the browser row its click reveals: tool results tag the tool name (skill results the
|
|
2952
|
+
* skill name), assistant replies tag the call breadcrumb, injections tag the form, user messages tag image attachments.
|
|
2953
|
+
*/
|
|
2954
|
+
function chipParts(n, conv) {
|
|
2955
|
+
if (n.cat === "tool") {
|
|
2956
|
+
const summary = callSummaryOf(conv);
|
|
2957
|
+
if (n.skill !== void 0) return {
|
|
2958
|
+
tag: t("node.skillTag", { name: n.skill }),
|
|
2959
|
+
text: summary ?? ""
|
|
2960
|
+
};
|
|
2961
|
+
if (n.tool !== void 0) return {
|
|
2962
|
+
tag: n.tool,
|
|
2963
|
+
text: summary ?? ""
|
|
2964
|
+
};
|
|
2965
|
+
return {
|
|
2966
|
+
tag: null,
|
|
2967
|
+
text: summary ?? t("node.toolResult")
|
|
2968
|
+
};
|
|
2969
|
+
}
|
|
2970
|
+
if (n.cat === "assistant") {
|
|
2971
|
+
const names = n.calls !== void 0 && n.calls.length > 0 ? n.calls : callNamesOf(conv);
|
|
2972
|
+
const own = n.text !== void 0 && n.text !== "" ? n.text : blockSummaryOf(conv) ?? "";
|
|
2973
|
+
return {
|
|
2974
|
+
tag: names.length > 0 ? names.join(" › ") : null,
|
|
2975
|
+
text: own !== "" ? own : names.length > 0 ? "" : t("node.empty")
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
if (n.cat === "inject" && n.skill === void 0) {
|
|
2979
|
+
const text = n.text !== void 0 && n.text !== "" ? n.form === "snapshot" ? t("node.snapshot") + n.text : n.text : "";
|
|
2980
|
+
return {
|
|
2981
|
+
tag: t("form." + (n.form ?? "context")),
|
|
2982
|
+
text
|
|
2983
|
+
};
|
|
2984
|
+
}
|
|
2985
|
+
if (n.cat === "user") {
|
|
2986
|
+
const imgs = n.imgs ?? 0;
|
|
2987
|
+
return {
|
|
2988
|
+
tag: imgs > 0 ? t("attach.image") + (imgs > 1 ? " ×" + String(imgs) : "") : null,
|
|
2989
|
+
text: n.text ?? ""
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
return {
|
|
2993
|
+
tag: null,
|
|
2994
|
+
text: nodeLine(n, conv)
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
/** The native-title line for a fact+text pair: 'tag · text', degrading to whichever half exists. */
|
|
2998
|
+
function factTitle(tag, text) {
|
|
2999
|
+
return tag !== null ? text !== "" ? tag + " · " + text : tag : text;
|
|
3000
|
+
}
|
|
3001
|
+
function BriefSection(props) {
|
|
3002
|
+
const { opener, inputs, response } = props.brief;
|
|
3003
|
+
if (opener === void 0 && inputs.length === 0 && response === void 0) return null;
|
|
3004
|
+
const convOf = props.convOf ?? (() => void 0);
|
|
3005
|
+
const hint = props.onLocate !== void 0 ? " — " + t("brief.locate") : "";
|
|
3006
|
+
const locateChip = props.onLocate === void 0 ? void 0 : (n) => (e) => {
|
|
3007
|
+
e?.stopPropagation();
|
|
3008
|
+
props.onLocate?.(n, false);
|
|
3009
|
+
};
|
|
3010
|
+
const MAX_CHIPS = 3;
|
|
3011
|
+
/**
|
|
3012
|
+
* The ONE content unit of the brief — inputs and the reply share the same chip anatomy (error dot, fact tag,
|
|
3013
|
+
* preview text). Input chips are compact and individually clickable (each locates its own node); the reply is a
|
|
3014
|
+
* single chip grown to the row's width, left inert because the row button already locates it.
|
|
3015
|
+
*/
|
|
3016
|
+
const nodeChip = (n, onClick, grow = false) => {
|
|
3017
|
+
const { tag, text } = chipParts(n, convOf(n.seq));
|
|
3018
|
+
return /* @__PURE__ */ React.createElement("span", {
|
|
3019
|
+
key: n.seq,
|
|
3020
|
+
className: "lc-brief-chip" + (grow ? " lc-brief-chip-grow" : "") + (onClick !== void 0 ? " lc-brief-chip-link" : ""),
|
|
3021
|
+
title: factTitle(tag, text) + hint,
|
|
3022
|
+
onClick
|
|
3023
|
+
}, n.err === true ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-err-dot" }) : null, tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-chip-tag" }, tag) : null, text !== "" ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-chip-text" }, text) : null);
|
|
3024
|
+
};
|
|
3025
|
+
const openerParts = opener !== void 0 ? chipParts(opener, convOf(opener.seq)) : null;
|
|
3026
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-brief" }, opener !== void 0 && openerParts !== null ? /* @__PURE__ */ React.createElement(BriefRow, {
|
|
3027
|
+
tag: t("brief.turn"),
|
|
3028
|
+
tagTip: t("brief.turnTip"),
|
|
3029
|
+
node: opener,
|
|
3030
|
+
isResponse: false,
|
|
3031
|
+
onLocate: props.onLocate
|
|
3032
|
+
}, openerParts.tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-fact" }, openerParts.tag) : null, openerParts.text !== "" ? /* @__PURE__ */ React.createElement("span", {
|
|
3033
|
+
className: "lc-brief-text",
|
|
3034
|
+
title: factTitle(openerParts.tag, openerParts.text) + hint
|
|
3035
|
+
}, openerParts.text) : null) : null, inputs.length > 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
|
|
3036
|
+
tag: t("brief.input"),
|
|
3037
|
+
tagTip: t("brief.inputTip"),
|
|
3038
|
+
node: inputs[0],
|
|
3039
|
+
isResponse: false,
|
|
3040
|
+
onLocate: props.onLocate
|
|
3041
|
+
}, inputs.slice(0, MAX_CHIPS).map((n) => nodeChip(n, locateChip?.(n))), inputs.length > MAX_CHIPS ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-more" }, t("brief.more", { n: inputs.length - MAX_CHIPS })) : null) : null, response !== void 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
|
|
3042
|
+
tag: t("brief.reply"),
|
|
3043
|
+
tagTip: t("brief.replyTip"),
|
|
3044
|
+
node: response,
|
|
3045
|
+
isResponse: true,
|
|
3046
|
+
onLocate: props.onLocate
|
|
3047
|
+
}, nodeChip(response, void 0, true)) : null);
|
|
3048
|
+
}
|
|
2261
3049
|
return function RequestDetail(props) {
|
|
2262
3050
|
const req = props.request;
|
|
2263
3051
|
if (!req) return null;
|
|
2264
3052
|
const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
|
|
2265
3053
|
const head = isTurn ? t("detail.turn", {
|
|
2266
3054
|
t: req.turn ?? 0,
|
|
3055
|
+
/* v8 ignore next -- isTurn already guarantees stepCount !== undefined;
|
|
3056
|
+
the fallback is defensive. */
|
|
2267
3057
|
n: req.stepCount ?? 0
|
|
2268
3058
|
}) : t("detail.step", {
|
|
2269
3059
|
t: req.turn ?? 0,
|
|
@@ -2288,7 +3078,11 @@ window.__ModuleLoader__.load({
|
|
|
2288
3078
|
return /* @__PURE__ */ React.createElement("div", { className: "lc-detail" }, /* @__PURE__ */ React.createElement("div", { className: "lc-detail-head" }, /* @__PURE__ */ React.createElement("b", null, head), marker !== null && markerAt !== null ? /* @__PURE__ */ React.createElement("span", {
|
|
2289
3079
|
className: "lc-detail-marker",
|
|
2290
3080
|
title: eventLabel(marker)
|
|
2291
|
-
}, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("gran.delta")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-detail-time" }, fmtTime(req.time)), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" + (net > 0 ? " lc-detail-metric-up" : net < 0 ? " lc-detail-metric-down" : "") }, t("tip.delta", { n: (net > 0 ? "+" : "") + fmt(net) })) : null, !delta && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.actual", { n: fmt(req.prompt) })) : null, !delta && req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.output", { n: fmt(req.output) })) : null, !delta && req.prompt !== void 0 && req.cacheRead !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.cache", { n: cacheHitPercent$1(req.cacheRead, req.prompt) ?? "—" })) : null), /* @__PURE__ */ React.createElement(
|
|
3081
|
+
}, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("gran.delta")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-detail-time" }, fmtTime(req.time)), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" + (net > 0 ? " lc-detail-metric-up" : net < 0 ? " lc-detail-metric-down" : "") }, t("tip.delta", { n: (net > 0 ? "+" : "") + fmt(net) })) : null, !delta && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.actual", { n: fmt(req.prompt) })) : null, !delta && req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.output", { n: fmt(req.output) })) : null, !delta && req.prompt !== void 0 && req.cacheRead !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.cache", { n: cacheHitPercent$1(req.cacheRead, req.prompt) ?? "—" })) : null), props.brief !== null && props.brief !== void 0 ? /* @__PURE__ */ React.createElement(BriefSection, {
|
|
3082
|
+
brief: props.brief,
|
|
3083
|
+
convOf: props.convOf,
|
|
3084
|
+
onLocate: props.onLocate
|
|
3085
|
+
}) : null, /* @__PURE__ */ React.createElement(StackedBar, {
|
|
2292
3086
|
parts,
|
|
2293
3087
|
height: 10
|
|
2294
3088
|
}), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c, i) => {
|
|
@@ -2691,6 +3485,8 @@ window.__ModuleLoader__.load({
|
|
|
2691
3485
|
};
|
|
2692
3486
|
React.useLayoutEffect(() => {
|
|
2693
3487
|
const el = scrollRef.current;
|
|
3488
|
+
/* v8 ignore next 1 -- the scroll div renders unconditionally and React
|
|
3489
|
+
attaches refs before layout effects run; el is never null here. */
|
|
2694
3490
|
if (el === null) return;
|
|
2695
3491
|
if (props.granularity !== lastGranRef.current) {
|
|
2696
3492
|
lastGranRef.current = props.granularity;
|
|
@@ -2719,11 +3515,15 @@ window.__ModuleLoader__.load({
|
|
|
2719
3515
|
t: req.turn ?? 0,
|
|
2720
3516
|
s: req.step ?? 0
|
|
2721
3517
|
});
|
|
3518
|
+
const reply = props.replyTips?.get(req.seq);
|
|
3519
|
+
const tail = reply !== void 0 ? " · “" + (reply.length > 48 ? reply.slice(0, 48) + "…" : reply) + "”" : "";
|
|
2722
3520
|
if (delta) {
|
|
3521
|
+
/* v8 ignore next 1 -- delta mode only receives records from
|
|
3522
|
+
deltaOf, which always assigns net; the fallback is defensive. */
|
|
2723
3523
|
const n = req.net ?? 0;
|
|
2724
|
-
return head + " · " + fmtTime(req.time) + " · " + t("tip.delta", { n: (n > 0 ? "+" : "") + fmt(n) });
|
|
3524
|
+
return head + " · " + fmtTime(req.time) + " · " + t("tip.delta", { n: (n > 0 ? "+" : "") + fmt(n) }) + tail;
|
|
2725
3525
|
}
|
|
2726
|
-
return head + " · " + fmtTime(req.time) + " · " + t("tip.total", { n: fmt(req.total) }) + (req.prompt !== void 0 ? " · " + t("tip.actual", { n: fmt(req.prompt) }) : "");
|
|
3526
|
+
return head + " · " + fmtTime(req.time) + " · " + t("tip.total", { n: fmt(req.total) }) + (req.prompt !== void 0 ? " · " + t("tip.actual", { n: fmt(req.prompt) }) : "") + tail;
|
|
2727
3527
|
};
|
|
2728
3528
|
const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
|
|
2729
3529
|
const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
|
|
@@ -2803,7 +3603,7 @@ window.__ModuleLoader__.load({
|
|
|
2803
3603
|
const TrendChart = makeTrendChart(kit);
|
|
2804
3604
|
const RequestDetail = makeRequestDetail(kit, StackedBar);
|
|
2805
3605
|
const EventList = makeEventList(kit);
|
|
2806
|
-
const
|
|
3606
|
+
const FileCard = makeFileCard(kit, settings);
|
|
2807
3607
|
const StatsBoard = makeStatsBoard(kit);
|
|
2808
3608
|
const PluginInfo = makePluginInfo(kit);
|
|
2809
3609
|
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
@@ -2830,9 +3630,9 @@ window.__ModuleLoader__.load({
|
|
|
2830
3630
|
return p.length === 1 ? [...EVENT_KINDS] : p.filter((x) => x !== k);
|
|
2831
3631
|
});
|
|
2832
3632
|
};
|
|
2833
|
-
const [
|
|
2834
|
-
const
|
|
2835
|
-
|
|
3633
|
+
const [nodeFocus, setNodeFocus] = React.useState(null);
|
|
3634
|
+
const clearNodeFocus = React.useCallback(() => {
|
|
3635
|
+
setNodeFocus(null);
|
|
2836
3636
|
}, []);
|
|
2837
3637
|
const loadImage = React.useMemo(() => {
|
|
2838
3638
|
if (typeof sessionId !== "string" || sessionId === "") return void 0;
|
|
@@ -2840,6 +3640,7 @@ window.__ModuleLoader__.load({
|
|
|
2840
3640
|
if (conversation === void 0 || typeof conversation.resolveImage !== "function") return void 0;
|
|
2841
3641
|
return (attachment) => conversation.resolveImage(sessionId, attachment);
|
|
2842
3642
|
}, [sessionId]);
|
|
3643
|
+
const fetchContent = React.useMemo(() => typeof sessionId === "string" && sessionId !== "" ? makeContentFetcher(ctx, sessionId) : void 0, [ctx, sessionId]);
|
|
2843
3644
|
const rootRef = React.useRef(null);
|
|
2844
3645
|
const scrollerRef = React.useRef(null);
|
|
2845
3646
|
const restoredRef = React.useRef(null);
|
|
@@ -2847,6 +3648,8 @@ window.__ModuleLoader__.load({
|
|
|
2847
3648
|
if (typeof sessionId !== "string" || sessionId === "" || data === null) return;
|
|
2848
3649
|
if (restoredRef.current === sessionId) return;
|
|
2849
3650
|
restoredRef.current = sessionId;
|
|
3651
|
+
/* v8 ignore next 3 -- a layout effect body only runs while mounted and
|
|
3652
|
+
both render paths attach rootRef, so the null arm cannot fire. */
|
|
2850
3653
|
const scroller = rootRef.current !== null ? rootRef.current.closest("[data-conversation-scroll]") : null;
|
|
2851
3654
|
if (scroller === null) return;
|
|
2852
3655
|
scrollerRef.current = scroller;
|
|
@@ -2863,15 +3666,24 @@ window.__ModuleLoader__.load({
|
|
|
2863
3666
|
const requests = data ? data.requests : [];
|
|
2864
3667
|
const events = data ? data.events : [];
|
|
2865
3668
|
const shownEvents = pickedKinds.length === EVENT_KINDS.length ? events : events.filter((e) => pickedKinds.includes(e.kind));
|
|
2866
|
-
const nodes = data ? data.nodes : [];
|
|
2867
3669
|
const displayRequests = React.useMemo(() => granularity === "turn" ? aggregateByTurn(requests) : requests, [requests, granularity]);
|
|
2868
3670
|
const markers = React.useMemo(() => attachMarkers(displayRequests, events), [displayRequests, events]);
|
|
3671
|
+
const briefList = React.useMemo(() => data ? briefNodes(data) : [], [data]);
|
|
3672
|
+
const replyTips = React.useMemo(() => replyTipsOf(briefList), [briefList]);
|
|
3673
|
+
const convNodes = typeof props.useSession === "function" ? props.useSession((s) => s.nodes) : void 0;
|
|
3674
|
+
const bySeq = React.useMemo(() => {
|
|
3675
|
+
const m = /* @__PURE__ */ new Map();
|
|
3676
|
+
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
3677
|
+
return m;
|
|
3678
|
+
}, [convNodes]);
|
|
2869
3679
|
if (!data) return /* @__PURE__ */ React.createElement("div", {
|
|
2870
3680
|
className: "lc-root",
|
|
2871
3681
|
ref: rootRef
|
|
2872
3682
|
}, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
|
|
2873
3683
|
const markerOf = (req) => {
|
|
2874
3684
|
const i = displayRequests.indexOf(req);
|
|
3685
|
+
/* v8 ignore next 1 -- the only caller passes displayRequests[activeIdx],
|
|
3686
|
+
an element of the very array indexOf scans. */
|
|
2875
3687
|
return i >= 0 ? markers[i] : void 0;
|
|
2876
3688
|
};
|
|
2877
3689
|
let pinnedIdx = -1;
|
|
@@ -2887,6 +3699,43 @@ window.__ModuleLoader__.load({
|
|
|
2887
3699
|
if (activeIdx < 0) activeIdx = pinnedIdx;
|
|
2888
3700
|
if (activeIdx < 0 && displayRequests.length > 0) activeIdx = displayRequests.length - 1;
|
|
2889
3701
|
const activeReq = activeIdx >= 0 ? displayRequests[activeIdx] : null;
|
|
3702
|
+
const brief = activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null;
|
|
3703
|
+
const convOf = (seq) => bySeq.get(seq);
|
|
3704
|
+
let filesBefore = null;
|
|
3705
|
+
if (activeReq !== null) {
|
|
3706
|
+
const ri = requests.findIndex((r) => r.seq === activeReq.seq);
|
|
3707
|
+
filesBefore = ri + 1 < requests.length ? requests[ri + 1].seq : null;
|
|
3708
|
+
}
|
|
3709
|
+
let fileScope = t("files.scopeLatest");
|
|
3710
|
+
if (activeReq !== null && filesBefore !== null) fileScope = activeReq.stepCount !== void 0 && activeReq.stepCount > 1 ? t("detail.turn", {
|
|
3711
|
+
t: activeReq.turn ?? 0,
|
|
3712
|
+
n: activeReq.stepCount
|
|
3713
|
+
}) : t("detail.step", {
|
|
3714
|
+
t: activeReq.turn ?? 0,
|
|
3715
|
+
s: activeReq.step ?? 0
|
|
3716
|
+
});
|
|
3717
|
+
const fileActivity = activityOf(briefList, convOf, filesBefore);
|
|
3718
|
+
const locateFileOp = (op) => {
|
|
3719
|
+
const step = locateStepOf(requests, op.seq, op.gone);
|
|
3720
|
+
if (step === null) return;
|
|
3721
|
+
setNodeFocus({
|
|
3722
|
+
step,
|
|
3723
|
+
seq: op.seq,
|
|
3724
|
+
cat: "tool"
|
|
3725
|
+
});
|
|
3726
|
+
};
|
|
3727
|
+
const locateNode = (node, isResponse) => {
|
|
3728
|
+
/* v8 ignore next 1 -- locateNode is only wired to brief rows, and
|
|
3729
|
+
brief !== null guarantees activeReq !== null in the same closure. */
|
|
3730
|
+
if (activeReq === null) return;
|
|
3731
|
+
const next = isResponse && activeIdx + 1 < displayRequests.length ? displayRequests[activeIdx + 1] : null;
|
|
3732
|
+
const step = isResponse ? next !== null ? next.seq : "live" : activeReq.seq;
|
|
3733
|
+
setNodeFocus({
|
|
3734
|
+
step,
|
|
3735
|
+
seq: node.seq,
|
|
3736
|
+
cat: node.cat
|
|
3737
|
+
});
|
|
3738
|
+
};
|
|
2890
3739
|
let activeTurn = hoverTurn;
|
|
2891
3740
|
if (activeTurn === null && hoveredSeq !== null) {
|
|
2892
3741
|
for (const req of displayRequests) if (req.seq === hoveredSeq) {
|
|
@@ -2912,9 +3761,7 @@ window.__ModuleLoader__.load({
|
|
|
2912
3761
|
head,
|
|
2913
3762
|
subtitle: (data.model ? data.model : "") + (data.provider ? " · " + data.provider : ""),
|
|
2914
3763
|
hoverKey: hoverCat,
|
|
2915
|
-
onHoverKey: setHoverCat
|
|
2916
|
-
tools: data.toolList,
|
|
2917
|
-
onToolFocus: setToolFocus
|
|
3764
|
+
onHoverKey: setHoverCat
|
|
2918
3765
|
}), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("trend.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-trend-ctl" }, /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement("button", {
|
|
2919
3766
|
className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
|
|
2920
3767
|
onClick: () => {
|
|
@@ -2957,22 +3804,29 @@ window.__ModuleLoader__.load({
|
|
|
2957
3804
|
},
|
|
2958
3805
|
onFocusTurnHandled: () => {
|
|
2959
3806
|
setFocusTurn(null);
|
|
2960
|
-
}
|
|
3807
|
+
},
|
|
3808
|
+
replyTips
|
|
2961
3809
|
}), /* @__PURE__ */ React.createElement(RequestDetail, {
|
|
2962
3810
|
request: activeReq,
|
|
2963
3811
|
prev: trendMode === "delta" && activeIdx >= 0 ? activeIdx > 0 ? displayRequests[activeIdx - 1] : null : void 0,
|
|
2964
|
-
|
|
3812
|
+
/* v8 ignore next 1 -- RequestDetail renders only when
|
|
3813
|
+
displayRequests.length > 0, which forces activeReq
|
|
3814
|
+
non-null via the activeIdx fallback above. */
|
|
3815
|
+
marker: activeReq !== null ? markerOf(activeReq) : void 0,
|
|
3816
|
+
brief,
|
|
3817
|
+
convOf,
|
|
3818
|
+
onLocate: locateNode
|
|
2965
3819
|
})))), /* @__PURE__ */ React.createElement("div", { className: "lc-col lc-col-browser" }, /* @__PURE__ */ React.createElement(ContextBrowser, {
|
|
2966
3820
|
data,
|
|
2967
3821
|
headers,
|
|
2968
3822
|
useSession: props.useSession,
|
|
2969
|
-
|
|
3823
|
+
fetchContent,
|
|
2970
3824
|
previewSeq: hoveredSeq,
|
|
2971
3825
|
pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
|
|
2972
3826
|
hoverKey: hoverCat,
|
|
2973
3827
|
onHoverKey: setHoverCat,
|
|
2974
|
-
|
|
2975
|
-
|
|
3828
|
+
nodeFocus,
|
|
3829
|
+
onNodeFocusHandled: clearNodeFocus,
|
|
2976
3830
|
loadImage
|
|
2977
3831
|
}))), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("events.title")), /* @__PURE__ */ React.createElement("div", { className: "lc-kinds" }, EVENT_KINDS.map((k) => /* @__PURE__ */ React.createElement("button", {
|
|
2978
3832
|
key: k,
|
|
@@ -2980,10 +3834,11 @@ window.__ModuleLoader__.load({
|
|
|
2980
3834
|
onClick: () => {
|
|
2981
3835
|
toggleKind(k);
|
|
2982
3836
|
}
|
|
2983
|
-
}, t("kind." + k))))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement(
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3837
|
+
}, t("kind." + k))))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement(FileCard, {
|
|
3838
|
+
activity: fileActivity,
|
|
3839
|
+
scope: fileScope,
|
|
3840
|
+
onLocate: locateFileOp
|
|
3841
|
+
})), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
|
|
2987
3842
|
}
|
|
2988
3843
|
return function ContextView(props) {
|
|
2989
3844
|
return h(ErrorBoundary, null, h(ContextViewBody, props));
|
|
@@ -3004,7 +3859,7 @@ window.__ModuleLoader__.load({
|
|
|
3004
3859
|
}
|
|
3005
3860
|
//#endregion
|
|
3006
3861
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles.css.mjs
|
|
3007
|
-
const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i,.lc-node i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-tools{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;display:flex}.lc-tools-label,.lc-tools-more{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;padding:0;font-family:inherit;font-size:12px}.lc-tools-label:hover,.lc-tools-more:hover{text-decoration:underline}.lc-tool-chip{background:var(--dsw-alias-bg-layer-2);font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:0;border-radius:4px;padding:1px 7px;font-size:12px}.lc-tool-chip:hover{text-decoration:underline}.lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;scrollbar-gutter:stable;flex:1;min-width:0;padding-bottom:8px;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px 8px;margin-bottom:10px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary);font-size:13px}.lc-detail-time{font-variant-numeric:tabular-nums;white-space:nowrap;margin-left:auto;font-size:12px}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-metric{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 14%, transparent);border-radius:6px;padding:2px 8px;font-size:12px;font-weight:600}.lc-detail-metric-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-detail-metric-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;position:relative;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-bar-zero{border-left:1px solid #80808073;position:absolute;top:0;bottom:0;left:50%}.lc-bar-fill-up,.lc-bar-fill-down{height:100%;display:block;position:absolute;top:0}.lc-bar-fill-up{border-radius:0 3px 3px 0;left:50%}.lc-bar-fill-down{border-radius:3px 0 0 3px;right:50%}.lc-detail-num{text-align:right;font-variant-numeric:tabular-nums;width:52px}.lc-detail-num-up{color:var(--dsw-alias-state-success-primary)}.lc-detail-num-down{color:var(--dsw-alias-state-error-primary)}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-nodes{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-node{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-node-preview{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);flex:1;overflow:hidden}.lc-node-time{color:var(--dsw-alias-label-secondary);text-align:right;min-width:54px;font-size:12px}.lc-node-tokens{color:var(--dsw-alias-label-secondary)}.lc-nodes-more{color:var(--dsw-alias-label-secondary);padding:3px 0}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
|
|
3862
|
+
const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;scrollbar-gutter:stable;flex:1;min-width:0;padding-bottom:8px;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px 8px;margin-bottom:10px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary);font-size:13px}.lc-detail-time{font-variant-numeric:tabular-nums;white-space:nowrap;margin-left:auto;font-size:12px}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-metric{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 14%, transparent);border-radius:6px;padding:2px 8px;font-size:12px;font-weight:600}.lc-detail-metric-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-detail-metric-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-brief{flex-direction:column;gap:2px;margin:-4px 0 10px;display:flex}.lc-brief-row{min-width:0;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;margin-left:-6px;margin-right:-6px;padding:3px 6px;font-family:inherit;font-size:12px;line-height:1.5;transition:background-color .12s;display:flex}.lc-brief-row-link:hover{background:var(--dsw-alias-interactive-bg-hover)}button.lc-brief-row-link{cursor:pointer}.lc-brief-tag{box-sizing:border-box;text-align:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:56px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;position:relative}.lc-brief-tip{z-index:6;white-space:normal;text-align:left;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:240px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-brief-tag:hover .lc-brief-tip{opacity:1}.lc-brief-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-fact{box-sizing:border-box;text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:160px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:0;max-width:260px;color:var(--dsw-alias-label-primary);border-radius:5px;align-items:center;gap:5px;padding:0 7px;font-size:12px;transition:background-color .12s;display:inline-flex;overflow:hidden}.lc-brief-chip-grow{flex:1;max-width:none}.lc-brief-chip-tag{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);max-width:120px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-chip-link{cursor:pointer}.lc-brief-chip-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-brief-more{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;position:relative;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-bar-zero{border-left:1px solid #80808073;position:absolute;top:0;bottom:0;left:50%}.lc-bar-fill-up,.lc-bar-fill-down{height:100%;display:block;position:absolute;top:0}.lc-bar-fill-up{border-radius:0 3px 3px 0;left:50%}.lc-bar-fill-down{border-radius:3px 0 0 3px;right:50%}.lc-detail-num{text-align:right;font-variant-numeric:tabular-nums;width:52px}.lc-detail-num-up{color:var(--dsw-alias-state-success-primary)}.lc-detail-num-down{color:var(--dsw-alias-state-error-primary)}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-fa-ctl{align-items:center;gap:8px;margin-bottom:8px;display:flex}.lc-fa-ctl .lc-gran{margin-left:0}.lc-fa-n{opacity:.75;margin-left:4px;font-weight:700}.lc-fa-search{min-width:80px;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;outline:none;flex:1;padding:3px 8px;font-size:12px}.lc-fa-search:focus{border-color:var(--dsw-alias-label-dimmed)}.lc-fa-meta{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;margin-bottom:6px;font-size:12px;display:flex}.lc-fa-meta .lc-fa-sort{flex:none}.lc-fa-meta-delta{position:relative}.lc-fa-meta-tip{z-index:6;white-space:normal;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:260px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-fa-meta-delta:hover .lc-fa-meta-tip{opacity:1}.lc-fa-item{border:1px solid #0000;border-radius:8px;transition:background-color .12s,border-color .12s}.lc-fa-item-on{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l1)}.lc-fa-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:7px;align-items:center;gap:7px;padding:6px 8px;font-size:12px;transition:background-color .12s;display:flex}.lc-fa-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-form{text-align:center;opacity:.85;flex:none;width:16px;font-size:11px}.lc-fa-path{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-fa-path em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-fa-badge{font-variant-numeric:tabular-nums;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:1px 7px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-badge i{background:currentColor;border-radius:50%;width:5px;height:5px;display:inline-block}.lc-fa-b-read{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent)}.lc-fa-b-write{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent)}.lc-fa-b-search{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent)}.lc-fa-chip-read,.lc-fa-chip-read:hover{color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write,.lc-fa-chip-write:hover{color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search,.lc-fa-chip-search:hover{color:var(--dsw-alias-state-warn-primary)}.lc-fa-chip-read.lc-gran-on,.lc-fa-chip-read.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write.lc-gran-on,.lc-fa-chip-write.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search.lc-gran-on,.lc-fa-chip-search.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);color:var(--dsw-alias-state-warn-primary)}.lc-fa-delta{font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;gap:5px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-up{color:var(--dsw-alias-state-success-primary)}.lc-fa-down{color:var(--dsw-alias-state-error-primary)}.lc-fa-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.lc-fa-ops{border-left:2px solid var(--dsw-alias-border-l1);flex-direction:column;gap:1px;margin:0 8px 8px 15px;padding:2px 0 2px 10px;display:flex}.lc-fa-op{min-width:0;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;background:0 0;border:0;border-radius:5px;align-items:center;gap:8px;padding:3px 6px;font-size:11px;transition:background-color .12s;display:flex}.lc-fa-op-link{cursor:pointer}.lc-fa-op-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-op-time{width:58px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none}.lc-fa-op-tool{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:0 6px;font-size:11px}.lc-fa-op-detail{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;overflow:hidden}.lc-fa-more{color:var(--dsw-alias-label-secondary);padding:2px 6px;font-size:11px}.lc-fa-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-retry:hover{filter:brightness(1.15)}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
|
|
3008
3863
|
const tagId = "dsh-context/styles.css";
|
|
3009
3864
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
3010
3865
|
const tag = document.createElement("style");
|
|
@@ -3049,19 +3904,6 @@ window.__ModuleLoader__.load({
|
|
|
3049
3904
|
const kit = makeViewKit(t);
|
|
3050
3905
|
const settings = createContextSettings();
|
|
3051
3906
|
const ContextView = makeContextView(ctx, kit, settings);
|
|
3052
|
-
ctx.effect(() => {
|
|
3053
|
-
const noop = () => {};
|
|
3054
|
-
const sessions = ctx.get("sessions");
|
|
3055
|
-
if (sessions === void 0 || typeof sessions.provide !== "function") return noop;
|
|
3056
|
-
try {
|
|
3057
|
-
return sessions.provide({
|
|
3058
|
-
props: ["loadOlderHistory"],
|
|
3059
|
-
resolve: (binding) => ({ props: { loadOlderHistory: () => binding.session.loadOlder() } })
|
|
3060
|
-
});
|
|
3061
|
-
} catch {
|
|
3062
|
-
return noop;
|
|
3063
|
-
}
|
|
3064
|
-
}, "dsh-context: loadOlderHistory prop");
|
|
3065
3907
|
ctx.slots.inject("conversation.view", () => {
|
|
3066
3908
|
return ctx.slots.register({
|
|
3067
3909
|
name: "conversation.view",
|
|
@@ -3079,7 +3921,7 @@ window.__ModuleLoader__.load({
|
|
|
3079
3921
|
id: "context-modal",
|
|
3080
3922
|
order: 10,
|
|
3081
3923
|
locale: NS,
|
|
3082
|
-
inject: (sessionId) => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
|
|
3924
|
+
inject: (sessionId = "") => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
|
|
3083
3925
|
}, (props) => h(ContextModal, props));
|
|
3084
3926
|
});
|
|
3085
3927
|
ctx.inject(["settingsScope"], (raw) => {
|