dsh-context 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/lib/client.js +963 -696
- package/lib/index.js +1 -1
- package/package.json +4 -5
package/lib/client.js
CHANGED
|
@@ -20,6 +20,7 @@ var DICT_ZH = {
|
|
|
20
20
|
"overview.free": "\u5269\u4F59\u7A97\u53E3",
|
|
21
21
|
"overview.used": "\u4E0A\u4E0B\u6587\u5DF2\u7528",
|
|
22
22
|
"overview.ofUsed": "\u5360\u5DF2\u7528\u4E0A\u4E0B\u6587",
|
|
23
|
+
"overview.compactReserve": "\u81EA\u52A8\u538B\u7F29\u9884\u7559\uFF1A\u5360\u7528\u8FBE {pct}% \u7A97\u53E3\u65F6\u89E6\u53D1\u538B\u7F29\uFF0C\u6B64\u533A\u57DF\u4E00\u822C\u4E0D\u5B9E\u9645\u5360\u7528",
|
|
23
24
|
"stats.title": "\u4E0A\u4E0B\u6587\u7EDF\u8BA1",
|
|
24
25
|
"stats.hint": "\u7EDF\u8BA1\u5F53\u524D\u4FDD\u7559\u7684\u5386\u53F2\u7A97\u53E3\uFF08\u4E0E\u8D8B\u52BF\u56FE\u4E00\u81F4\uFF09",
|
|
25
26
|
"stats.turns": "\u8F6E\u6B21",
|
|
@@ -27,6 +28,7 @@ var DICT_ZH = {
|
|
|
27
28
|
"stats.injects": "\u6CE8\u5165",
|
|
28
29
|
"stats.compactions": "\u538B\u7F29",
|
|
29
30
|
"stats.prunes": "\u526A\u679D",
|
|
31
|
+
"stats.cacheHit": "\u7F13\u5B58\u547D\u4E2D",
|
|
30
32
|
"plugin.title": "\u63D2\u4EF6\u4FE1\u606F",
|
|
31
33
|
"plugin.hint": "The best DSH context plugin \u2B50",
|
|
32
34
|
"plugin.name": "\u63D2\u4EF6",
|
|
@@ -80,7 +82,7 @@ var DICT_ZH = {
|
|
|
80
82
|
"node.empty": "(\u7A7A\u56DE\u590D)",
|
|
81
83
|
"node.nonText": "(\u975E\u6587\u672C\u6D88\u606F)",
|
|
82
84
|
"node.snapshot": "\u5FEB\u7167: ",
|
|
83
|
-
"cmd.desc": "\u67E5\u770B\u4E0A\u4E0B\u6587\
|
|
85
|
+
"cmd.desc": "\u67E5\u770B\u5F53\u524D\u4E0A\u4E0B\u6587\u6784\u6210\uFF0C\u6D4F\u89C8\u5404\u6B65\u9AA4\u7EC4\u6210",
|
|
84
86
|
"cmd.close": "\u5173\u95ED",
|
|
85
87
|
"browser.title": "\u4E0A\u4E0B\u6587\u6D4F\u89C8\u5668",
|
|
86
88
|
"browser.live": "\u5F53\u524D\uFF08\u4E0B\u4E00\u6B21\u8BF7\u6C42\uFF09",
|
|
@@ -88,11 +90,20 @@ var DICT_ZH = {
|
|
|
88
90
|
"browser.items": "{n} \u9879",
|
|
89
91
|
"browser.missingLive": "\u2026 \u53E6\u6709 {n} \u6761\u66F4\u65E9\u7684\u6D88\u606F\u4E5F\u5728\u4E0A\u4E0B\u6587\u4E2D\uFF08\u8D85\u51FA\u5C55\u793A\u7A97\u53E3\uFF09",
|
|
90
92
|
"browser.approx": "\u8BE5\u6B65\u9AA4\u6D89\u53CA\u7684\u90E8\u5206\u5DF2\u79FB\u9664\u6D88\u606F\u8D85\u51FA\u4FDD\u7559\u8303\u56F4\uFF0C\u4EE5\u4E0B\u4E3A\u8FD1\u4F3C\u6784\u6210",
|
|
93
|
+
"browser.deltaHint": "\u5BF9\u6BD4\u4E0A\u8F6E\u672B\u6B65\u7684\u53D8\u52A8",
|
|
91
94
|
"browser.noHeader": "\u6B64\u6570\u636E\u6765\u81EA\u65E7\u7248\u63D2\u4EF6\uFF1A\u4EC5\u63D0\u4F9B token \u4F30\u7B97\uFF0C\u65E0\u5B9E\u9645\u5185\u5BB9",
|
|
92
95
|
"browser.noEpoch": "\u8BE5\u6B65\u9AA4\u7684\u5934\u90E8\u5185\u5BB9\uFF08\u7CFB\u7EDF\u63D0\u793A\u8BCD / \u5DE5\u5177\u5B9A\u4E49\uFF09\u4E0D\u5728\u4FDD\u7559\u8303\u56F4\u5185",
|
|
93
96
|
"browser.noContent": "\u5B8C\u6574\u5185\u5BB9\u4E0D\u5728\u5F53\u524D\u52A0\u8F7D\u7684\u6D88\u606F\u7A97\u53E3\u5185\uFF08\u5728\u804A\u5929\u9875\u52A0\u8F7D\u66F4\u65E9\u5386\u53F2\u540E\u53EF\u67E5\u770B\uFF09",
|
|
94
97
|
"browser.loading": "\u6B63\u5728\u4ECE\u66F4\u65E9\u7684\u4F1A\u8BDD\u5386\u53F2\u52A0\u8F7D\u5B8C\u6574\u5185\u5BB9\u2026",
|
|
95
|
-
"browser.preview": "\u9884\u89C8"
|
|
98
|
+
"browser.preview": "\u9884\u89C8",
|
|
99
|
+
// Tool schema browser card: parsed parameter table sits above the (collapsed
|
|
100
|
+
// by default) raw JSON so the common case — skimming arguments — stays
|
|
101
|
+
// scannable; the JSON is still one click away for the full payload.
|
|
102
|
+
"tool.desc": "\u63CF\u8FF0",
|
|
103
|
+
"tool.params": "\u53C2\u6570",
|
|
104
|
+
"tool.paramsEmpty": "\uFF08\u65E0\u53C2\u6570\uFF09",
|
|
105
|
+
"tool.jsonToggle": "\u67E5\u770B\u539F\u59CB JSON",
|
|
106
|
+
"tool.jsonHide": "\u6536\u8D77"
|
|
96
107
|
};
|
|
97
108
|
var DICT_EN = {
|
|
98
109
|
"tab": "Context",
|
|
@@ -107,6 +118,7 @@ var DICT_EN = {
|
|
|
107
118
|
"overview.free": "Free Window",
|
|
108
119
|
"overview.used": "of context used",
|
|
109
120
|
"overview.ofUsed": "of used context",
|
|
121
|
+
"overview.compactReserve": "Auto-compaction reserve: auto-compaction triggers at {pct}% of the window \u2014 this area is normally kept as headroom, not actually used",
|
|
110
122
|
"stats.title": "Context Stats",
|
|
111
123
|
"stats.hint": "Over the retained history window (same as the History chart)",
|
|
112
124
|
"stats.turns": "Turns",
|
|
@@ -114,6 +126,7 @@ var DICT_EN = {
|
|
|
114
126
|
"stats.injects": "Injections",
|
|
115
127
|
"stats.compactions": "Compactions",
|
|
116
128
|
"stats.prunes": "Prunes",
|
|
129
|
+
"stats.cacheHit": "Cache Hits",
|
|
117
130
|
"plugin.title": "Plugin Info",
|
|
118
131
|
"plugin.hint": "The best DSH context plugin \u2B50",
|
|
119
132
|
"plugin.name": "Plugin",
|
|
@@ -167,7 +180,7 @@ var DICT_EN = {
|
|
|
167
180
|
"node.empty": "(empty reply)",
|
|
168
181
|
"node.nonText": "(non-text message)",
|
|
169
182
|
"node.snapshot": "Snapshot: ",
|
|
170
|
-
"cmd.desc": "View context makeup
|
|
183
|
+
"cmd.desc": "View current context makeup, browse per-step composition",
|
|
171
184
|
"cmd.close": "Close",
|
|
172
185
|
"browser.title": "Context Browser",
|
|
173
186
|
"browser.live": "Live (Next Request)",
|
|
@@ -175,11 +188,20 @@ var DICT_EN = {
|
|
|
175
188
|
"browser.items": "{n} Items",
|
|
176
189
|
"browser.missingLive": "\u2026 {n} earlier messages are also part of the context (outside the served window)",
|
|
177
190
|
"browser.approx": "Some removed messages of this step exceed retention \u2014 the makeup below is approximate",
|
|
191
|
+
"browser.deltaHint": "vs previous turn",
|
|
178
192
|
"browser.noHeader": "Served by an older plugin build: token estimates only, no content",
|
|
179
193
|
"browser.noEpoch": "The header content (System Prompt / Tool Schemas) of this step is outside retention",
|
|
180
194
|
"browser.noContent": "Full content is outside the loaded message window (load older history in Chat to view)",
|
|
181
195
|
"browser.loading": "Loading full content from older session history\u2026",
|
|
182
|
-
"browser.preview": "Preview"
|
|
196
|
+
"browser.preview": "Preview",
|
|
197
|
+
// Tool schema browser card: parsed parameter table sits above the (collapsed
|
|
198
|
+
// by default) raw JSON so the common case — skimming arguments — stays
|
|
199
|
+
// scannable; the JSON is still one click away for the full payload.
|
|
200
|
+
"tool.desc": "Description",
|
|
201
|
+
"tool.params": "Parameters",
|
|
202
|
+
"tool.paramsEmpty": "(no parameters)",
|
|
203
|
+
"tool.jsonToggle": "View Raw JSON",
|
|
204
|
+
"tool.jsonHide": "Collapse"
|
|
183
205
|
};
|
|
184
206
|
|
|
185
207
|
// src/client/modalStore.ts
|
|
@@ -294,343 +316,606 @@ function asRecord(value) {
|
|
|
294
316
|
}
|
|
295
317
|
var timelineOf = (value) => asRecord(value);
|
|
296
318
|
var contextPressureOf = (value) => asRecord(value);
|
|
319
|
+
var tokenUsageOf = (value) => asRecord(value);
|
|
297
320
|
function headersOf(value) {
|
|
298
321
|
const headers = asRecord(value);
|
|
299
322
|
return headers !== null && Array.isArray(headers.headers) ? headers : null;
|
|
300
323
|
}
|
|
301
324
|
|
|
325
|
+
// src/client/assemble.ts
|
|
326
|
+
function headerAt(headers, seq) {
|
|
327
|
+
if (headers === null || headers.headers.length === 0) return null;
|
|
328
|
+
if (seq === null) return headers.headers[headers.headers.length - 1];
|
|
329
|
+
for (let i = headers.headers.length - 1; i >= 0; i--) {
|
|
330
|
+
if (headers.headers[i].seq < seq) return headers.headers[i];
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
function assemble(data, headers, seq) {
|
|
335
|
+
const live = seq === null;
|
|
336
|
+
let nodes;
|
|
337
|
+
if (live) {
|
|
338
|
+
nodes = data.nodes.slice();
|
|
339
|
+
} else {
|
|
340
|
+
const picked = [];
|
|
341
|
+
for (const n of data.nodes) {
|
|
342
|
+
if (n.seq < seq) picked.push(n);
|
|
343
|
+
}
|
|
344
|
+
for (const n of data.archive || []) {
|
|
345
|
+
if (n.seq < seq && n.gone !== void 0 && n.gone > seq) picked.push(n);
|
|
346
|
+
}
|
|
347
|
+
nodes = picked;
|
|
348
|
+
}
|
|
349
|
+
nodes.sort((a, b) => a.seq - b.seq);
|
|
350
|
+
let missingLive = 0;
|
|
351
|
+
if (data.droppedNodes > 0) {
|
|
352
|
+
if (live || data.surfaceFloor !== void 0 && seq > data.surfaceFloor) {
|
|
353
|
+
missingLive = data.droppedNodes;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const approximate = !live && data.archiveFloor !== void 0 && seq < data.archiveFloor;
|
|
357
|
+
return { live, header: headerAt(headers, seq), nodes, missingLive, approximate };
|
|
358
|
+
}
|
|
359
|
+
|
|
302
360
|
// src/client/react.ts
|
|
303
361
|
var React = require("react");
|
|
304
362
|
var h = React.createElement;
|
|
305
363
|
|
|
306
|
-
// src/client/components/
|
|
307
|
-
function
|
|
308
|
-
const { t
|
|
309
|
-
return function
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
|
|
313
|
-
const head = isTurn ? t("detail.turn", { t: req.turn ?? 0, n: req.stepCount ?? 0 }) : t("detail.step", { t: req.turn ?? 0, s: req.step ?? 0 });
|
|
314
|
-
const marker = props.marker ?? null;
|
|
315
|
-
const markerAt = marker !== null ? eventAt(marker) : null;
|
|
316
|
-
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", { className: "lc-detail-marker", title: eventLabel(marker) }, "\u2702 " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, /* @__PURE__ */ React.createElement("span", null, fmtTime2(req.time)), /* @__PURE__ */ React.createElement("span", null, t("detail.estTotal", { n: fmt3(req.total) })), req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt3(req.prompt) })) : null, req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", null, t("detail.output", { n: fmt3(req.output) })) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: partsOf(req), height: 10 }), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c) => {
|
|
317
|
-
const v = req[c.key] || 0;
|
|
318
|
-
return /* @__PURE__ */ React.createElement("div", { key: c.key, className: "lc-detail-row" }, /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-label" }, catLabel(c.key)), /* @__PURE__ */ React.createElement("span", { className: "lc-bar-track" }, /* @__PURE__ */ React.createElement("span", { className: "lc-bar-fill", style: { width: (req.total > 0 ? v / req.total * 100 : 0) + "%", background: c.color } })), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" }, "\u2248" + fmt3(v)), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-pct" }, req.total > 0 ? Math.round(v / req.total * 100) + "%" : ""));
|
|
319
|
-
})));
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// src/client/components/stackedBar.tsx
|
|
324
|
-
function makeStackedBar(kit) {
|
|
325
|
-
const { t, fmt: fmt3, catLabel } = kit;
|
|
326
|
-
return function StackedBar(props) {
|
|
327
|
-
let total = 0;
|
|
328
|
-
for (const p of props.parts) total += p.value;
|
|
329
|
-
const scale = props.max !== void 0 && props.max > total ? props.max : total;
|
|
330
|
-
const free = props.max !== void 0 && props.max > total ? props.max - total : 0;
|
|
331
|
-
const usedPct = scale > 0 ? total / scale * 100 : 0;
|
|
332
|
-
const hovering = props.hoverKey !== null && props.hoverKey !== void 0;
|
|
333
|
-
const showBox = free > 0 && hovering;
|
|
334
|
-
let tip = null;
|
|
335
|
-
if (props.hoverKey !== null && props.hoverKey !== void 0) {
|
|
336
|
-
if (props.hoverKey === "free" && free > 0) {
|
|
337
|
-
const pct = scale > 0 ? free / scale * 100 : 0;
|
|
338
|
-
tip = {
|
|
339
|
-
text: t("overview.free") + " " + fmt3(free) + " (" + Math.round(pct) + "%)",
|
|
340
|
-
leftPct: Math.max(12, Math.min(total / scale * 100 + pct / 2, 88))
|
|
341
|
-
};
|
|
342
|
-
} else {
|
|
343
|
-
let acc = 0;
|
|
344
|
-
for (const p of props.parts) {
|
|
345
|
-
const pct = scale > 0 ? p.value / scale * 100 : 0;
|
|
346
|
-
if (p.key === props.hoverKey && p.value > 0) {
|
|
347
|
-
tip = {
|
|
348
|
-
// "(pct%)" is a share of the OCCUPIED total — the dashed box
|
|
349
|
-
// that appears on hover frames exactly this reference region.
|
|
350
|
-
text: catLabel(p.key) + " \u2248" + fmt3(p.value) + " (" + Math.round(p.value / total * 100) + "%) " + t("overview.ofUsed"),
|
|
351
|
-
leftPct: Math.max(12, Math.min(acc + pct / 2, 88))
|
|
352
|
-
};
|
|
353
|
-
break;
|
|
354
|
-
}
|
|
355
|
-
acc += pct;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
364
|
+
// src/client/components/nodes.tsx
|
|
365
|
+
function makeNodeText(kit) {
|
|
366
|
+
const { t } = kit;
|
|
367
|
+
return function nodeText(n) {
|
|
368
|
+
if (n.cat === "tool") {
|
|
369
|
+
return t("node.toolResult") + (n.tool ? " \u2190 " + n.tool : "") + (n.err ? " \u26A0" : "");
|
|
358
370
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
total > 0 ? props.parts.map((p) => {
|
|
369
|
-
if (!p.value) return null;
|
|
370
|
-
const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
|
|
371
|
-
return /* @__PURE__ */ React.createElement(
|
|
372
|
-
"div",
|
|
373
|
-
{
|
|
374
|
-
key: p.key,
|
|
375
|
-
className: "lc-stacked-seg" + (on ? " lc-stacked-seg-on" : ""),
|
|
376
|
-
style: { width: p.value / scale * 100 + "%", background: p.color },
|
|
377
|
-
onMouseEnter: () => {
|
|
378
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
);
|
|
382
|
-
}) : null,
|
|
383
|
-
free > 0 ? /* @__PURE__ */ React.createElement(
|
|
384
|
-
"div",
|
|
385
|
-
{
|
|
386
|
-
key: "free",
|
|
387
|
-
className: "lc-stacked-free" + (props.hoverKey === "free" ? " lc-stacked-free-on" : ""),
|
|
388
|
-
style: { width: free / scale * 100 + "%" },
|
|
389
|
-
onMouseEnter: () => {
|
|
390
|
-
if (props.onHoverKey !== void 0) props.onHoverKey("free");
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
) : null,
|
|
394
|
-
/* @__PURE__ */ React.createElement(
|
|
395
|
-
"div",
|
|
396
|
-
{
|
|
397
|
-
className: "lc-occupied-box" + (showBox ? " lc-occupied-box-on" : ""),
|
|
398
|
-
style: { width: usedPct + "%" }
|
|
399
|
-
}
|
|
400
|
-
)
|
|
401
|
-
), props.tip !== false ? /* @__PURE__ */ React.createElement(
|
|
402
|
-
"div",
|
|
403
|
-
{
|
|
404
|
-
className: "lc-bar-tip" + (tip ? " lc-bar-tip-on" : ""),
|
|
405
|
-
style: { left: tip ? tip.leftPct + "%" : "50%" }
|
|
406
|
-
},
|
|
407
|
-
tip ? tip.text : ""
|
|
408
|
-
) : null);
|
|
371
|
+
if (n.skill) return "Skill: " + n.skill;
|
|
372
|
+
if (n.calls) return t("node.calls") + n.calls.join(", ");
|
|
373
|
+
if (n.text) return n.form === "snapshot" ? t("node.snapshot") + n.text : n.text;
|
|
374
|
+
if (n.cat === "assistant") return t("node.empty");
|
|
375
|
+
if (n.cat === "inject") return t("form." + (n.form || "context"));
|
|
376
|
+
return t("node.nonText");
|
|
409
377
|
};
|
|
410
378
|
}
|
|
411
|
-
function
|
|
412
|
-
const { t, fmt: fmt3,
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
onMouseEnter: () => {
|
|
425
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
426
|
-
},
|
|
427
|
-
onMouseLeave: () => {
|
|
428
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
429
|
-
}
|
|
430
|
-
},
|
|
431
|
-
/* @__PURE__ */ React.createElement("i", { style: { background: p.color } }),
|
|
432
|
-
catLabel(p.key) + " \u2248" + fmt3(p.value),
|
|
433
|
-
total > 0 ? /* @__PURE__ */ React.createElement("em", null, Math.round(p.value / total * 100) + "%") : null
|
|
434
|
-
);
|
|
379
|
+
function makeNodeList(kit) {
|
|
380
|
+
const { t, fmt: fmt3, fmtTime: fmtTime2 } = kit;
|
|
381
|
+
const nodeText = makeNodeText(kit);
|
|
382
|
+
return function NodeList(props) {
|
|
383
|
+
if (props.nodes.length === 0) {
|
|
384
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("nodes.empty"));
|
|
385
|
+
}
|
|
386
|
+
const catColor = {};
|
|
387
|
+
for (const c of CATS) catColor[c.key] = c.color;
|
|
388
|
+
const rows = props.nodes.slice().reverse();
|
|
389
|
+
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) => {
|
|
390
|
+
const text = nodeText(n);
|
|
391
|
+
return /* @__PURE__ */ React.createElement("div", { key: n.seq, className: "lc-node" }, /* @__PURE__ */ React.createElement("i", { style: { background: catColor[n.cat] || "#999" } }), /* @__PURE__ */ React.createElement("span", { className: "lc-node-preview", title: text }, text), typeof n.time === "number" ? /* @__PURE__ */ React.createElement("span", { className: "lc-node-time" }, fmtTime2(n.time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-node-tokens" }, fmt3(n.tokens)));
|
|
435
392
|
}));
|
|
436
393
|
};
|
|
437
394
|
}
|
|
438
395
|
|
|
439
|
-
// src/client/components/
|
|
440
|
-
function
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
} else {
|
|
449
|
-
runSteps = 1;
|
|
450
|
-
out.push({ ...req, stepCount: 1 });
|
|
451
|
-
}
|
|
396
|
+
// src/client/components/browser.tsx
|
|
397
|
+
function unionTypesOf(p) {
|
|
398
|
+
const branches = [];
|
|
399
|
+
if (Array.isArray(p.anyOf)) branches.push(...p.anyOf);
|
|
400
|
+
if (Array.isArray(p.oneOf)) branches.push(...p.oneOf);
|
|
401
|
+
if (branches.length === 0) return null;
|
|
402
|
+
const parts = [];
|
|
403
|
+
for (const b of branches) {
|
|
404
|
+
if (b !== null && typeof b === "object") parts.push(typeOf(b));
|
|
452
405
|
}
|
|
453
|
-
return
|
|
406
|
+
return parts.length > 0 ? parts.join(" | ") : null;
|
|
454
407
|
}
|
|
455
|
-
function
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
408
|
+
function typeOf(p) {
|
|
409
|
+
const u = unionTypesOf(p);
|
|
410
|
+
if (u !== null) return u;
|
|
411
|
+
const t = p.type;
|
|
412
|
+
if (t === "array") {
|
|
413
|
+
const items = p.items;
|
|
414
|
+
if (items !== null && typeof items === "object") {
|
|
415
|
+
const inner = typeOf(items);
|
|
416
|
+
return "array<" + inner + ">";
|
|
417
|
+
}
|
|
418
|
+
return "array";
|
|
419
|
+
}
|
|
420
|
+
if (typeof t === "string") {
|
|
421
|
+
if (t === "object") {
|
|
422
|
+
const props = p.properties;
|
|
423
|
+
if (props !== null && typeof props === "object" && Object.keys(props).length > 0) {
|
|
424
|
+
return "object{" + Object.keys(props).length + "}";
|
|
463
425
|
}
|
|
464
426
|
}
|
|
427
|
+
if (Array.isArray(p.enum) && p.enum.length > 0) {
|
|
428
|
+
return t + " (enum)";
|
|
429
|
+
}
|
|
430
|
+
return t;
|
|
465
431
|
}
|
|
466
|
-
return
|
|
432
|
+
if (Array.isArray(p.enum) && p.enum.length > 0) return "(enum)";
|
|
433
|
+
return "unknown";
|
|
467
434
|
}
|
|
468
|
-
function
|
|
469
|
-
|
|
470
|
-
const
|
|
471
|
-
const
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
435
|
+
function paramsOf(schema) {
|
|
436
|
+
if (schema === null || typeof schema !== "object") return null;
|
|
437
|
+
const s = schema;
|
|
438
|
+
const candidate = (v) => v !== null && typeof v === "object" ? v : null;
|
|
439
|
+
const nested = candidate(s.parameters) ?? candidate(s.input_schema) ?? candidate(s.inputSchema);
|
|
440
|
+
if (nested !== null) return nested;
|
|
441
|
+
if (s.type === "object" && s.properties !== void 0 && typeof s.properties === "object") {
|
|
442
|
+
return s;
|
|
443
|
+
}
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
function ParamRow(props) {
|
|
447
|
+
const typeLabel = typeOf(props.schema);
|
|
448
|
+
const desc = props.schema.description;
|
|
449
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-param-row" }, /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-name" }, props.name), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-type" }, typeLabel), /* @__PURE__ */ React.createElement("span", { className: props.required ? "lc-ts-param-req" : "lc-ts-param-req-off" }, props.required ? "\u2713" : "\xB7"), typeof desc === "string" && desc !== "" ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-desc" }, desc) : null);
|
|
450
|
+
}
|
|
451
|
+
function ToolSchema(props) {
|
|
452
|
+
const [jsonOpen, setJsonOpen] = React.useState(false);
|
|
453
|
+
const params = React.useMemo(() => paramsOf(props.schema), [props.schema]);
|
|
454
|
+
const rows = React.useMemo(() => {
|
|
455
|
+
if (params === null) return [];
|
|
456
|
+
const props2 = params.properties;
|
|
457
|
+
if (props2 === null || typeof props2 !== "object") return [];
|
|
458
|
+
const req = Array.isArray(params.required) ? new Set(params.required.filter((x) => typeof x === "string")) : /* @__PURE__ */ new Set();
|
|
459
|
+
const out = [];
|
|
460
|
+
for (const k of Object.keys(props2)) {
|
|
461
|
+
const v = props2[k];
|
|
462
|
+
if (v === null || typeof v !== "object") continue;
|
|
463
|
+
out.push({ name: k, schema: v, required: req.has(k) });
|
|
483
464
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
465
|
+
return out;
|
|
466
|
+
}, [params]);
|
|
467
|
+
const schemaJson = React.useMemo(
|
|
468
|
+
() => props.schema !== void 0 ? JSON.stringify(props.schema, null, 2) : "",
|
|
469
|
+
[props.schema]
|
|
470
|
+
);
|
|
471
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.description !== void 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", null, props.labels.desc)), /* @__PURE__ */ React.createElement("pre", { className: "lc-ts-desc-body" }, props.description)) : null, params !== null && rows.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", null, props.labels.title), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-count" }, rows.length)), rows.map((r) => /* @__PURE__ */ React.createElement(ParamRow, { key: r.name, name: r.name, schema: r.schema, required: r.required }))) : params !== null ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-params-empty" }, props.labels.empty) : null, schemaJson !== "" ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-json" }, /* @__PURE__ */ React.createElement(
|
|
472
|
+
"button",
|
|
473
|
+
{
|
|
474
|
+
type: "button",
|
|
475
|
+
className: "lc-ts-json-toggle",
|
|
476
|
+
onClick: () => {
|
|
477
|
+
setJsonOpen((o) => !o);
|
|
490
478
|
}
|
|
491
|
-
|
|
492
|
-
|
|
479
|
+
},
|
|
480
|
+
(jsonOpen ? "\u25BE " : "\u25B8 ") + (jsonOpen ? props.labels.hide : props.labels.show)
|
|
481
|
+
), jsonOpen ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre lc-br-dim" }, schemaJson) : null) : null);
|
|
482
|
+
}
|
|
483
|
+
function RawBlocks(props) {
|
|
484
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.blocks.map((b, i) => {
|
|
485
|
+
const blk = b;
|
|
486
|
+
if (blk !== null && typeof blk === "object" && (blk.type === "text" || blk.type === "reasoning") && typeof blk.text === "string") {
|
|
487
|
+
return /* @__PURE__ */ React.createElement("pre", { key: i, className: "lc-br-pre" + (blk.type === "reasoning" ? " lc-br-dim" : "") }, blk.text);
|
|
493
488
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
if (props.granularity !== lastGranRef.current) {
|
|
511
|
-
lastGranRef.current = props.granularity;
|
|
512
|
-
scrolledOnce.current = false;
|
|
489
|
+
if (blk !== null && typeof blk === "object" && blk.type === "tool-result" && Array.isArray(blk.content)) {
|
|
490
|
+
return /* @__PURE__ */ React.createElement(RawBlocks, { key: i, blocks: blk.content });
|
|
491
|
+
}
|
|
492
|
+
return /* @__PURE__ */ React.createElement("pre", { key: i, className: "lc-br-pre lc-br-dim" }, JSON.stringify(b, null, 2));
|
|
493
|
+
}));
|
|
494
|
+
}
|
|
495
|
+
function NodeContent(props) {
|
|
496
|
+
const { node, conv } = props;
|
|
497
|
+
if (conv === void 0) {
|
|
498
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, node.text !== void 0 && node.text !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre" }, node.text) : null, /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
|
|
499
|
+
}
|
|
500
|
+
if (conv.kind === "assistant" && Array.isArray(conv.blocks)) {
|
|
501
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, conv.blocks.map((b, i) => {
|
|
502
|
+
const blk = b;
|
|
503
|
+
if (blk.kind === "text" && typeof blk.text === "string") {
|
|
504
|
+
return /* @__PURE__ */ React.createElement("pre", { key: i, className: "lc-br-pre" }, blk.text);
|
|
513
505
|
}
|
|
514
|
-
if (
|
|
515
|
-
|
|
516
|
-
el.scrollLeft = el.scrollWidth;
|
|
517
|
-
} else if (el.scrollLeft + el.clientWidth >= el.scrollWidth - 24) {
|
|
518
|
-
el.scrollLeft = el.scrollWidth;
|
|
506
|
+
if (blk.kind === "reasoning" && typeof blk.text === "string") {
|
|
507
|
+
return /* @__PURE__ */ React.createElement("pre", { key: i, className: "lc-br-pre lc-br-dim" }, blk.text);
|
|
519
508
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
509
|
+
if (blk.kind === "tool-call") {
|
|
510
|
+
return /* @__PURE__ */ React.createElement("div", { key: i, className: "lc-br-call" }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, "\u2192 " + String(blk.name ?? "?")), typeof blk.argsRaw === "string" && blk.argsRaw !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre lc-br-dim" }, blk.argsRaw) : null);
|
|
511
|
+
}
|
|
512
|
+
return null;
|
|
513
|
+
}));
|
|
514
|
+
}
|
|
515
|
+
if (conv.kind === "tool-result") {
|
|
516
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, conv.call != null ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-call" }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, "\u2190 " + conv.call.name), conv.call.argsRaw !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre lc-br-dim" }, conv.call.argsRaw) : null) : null, Array.isArray(conv.content) ? /* @__PURE__ */ React.createElement(RawBlocks, { blocks: conv.content }) : null);
|
|
517
|
+
}
|
|
518
|
+
if (conv.kind === "compaction") {
|
|
519
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, typeof conv.summary === "string" && conv.summary !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre" }, conv.summary) : null);
|
|
520
|
+
}
|
|
521
|
+
if (Array.isArray(conv.content)) {
|
|
522
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement(RawBlocks, { blocks: conv.content }));
|
|
523
|
+
}
|
|
524
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
|
|
525
|
+
}
|
|
526
|
+
function byCatOf(asm) {
|
|
527
|
+
var _a;
|
|
528
|
+
const m = {};
|
|
529
|
+
for (const n of asm.nodes) (m[_a = n.cat] ?? (m[_a] = [])).push(n);
|
|
530
|
+
return m;
|
|
531
|
+
}
|
|
532
|
+
function countOf(asm, c) {
|
|
533
|
+
if (c === "system") return asm.header !== null && asm.header.system !== void 0 ? 1 : 0;
|
|
534
|
+
if (c === "tools") return asm.header !== null ? asm.header.tools.length : 0;
|
|
535
|
+
return byCatOf(asm)[c]?.length ?? 0;
|
|
536
|
+
}
|
|
537
|
+
function lastOfTurn(requests, turn) {
|
|
538
|
+
for (let i = requests.length - 1; i >= 0; i--) if ((requests[i].turn ?? 0) === turn) return requests[i];
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
function makeContextBrowser(kit, StackedBar) {
|
|
542
|
+
const { t, fmt: fmt3, fmtTime: fmtTime2, catLabel } = kit;
|
|
543
|
+
const nodeText = makeNodeText(kit);
|
|
544
|
+
const catColor = {};
|
|
545
|
+
for (const c of CATS) catColor[c.key] = c.color;
|
|
546
|
+
const MAX_AUTO_PAGES = 20;
|
|
547
|
+
return function ContextBrowser(props) {
|
|
548
|
+
const { data, headers } = props;
|
|
549
|
+
const [sel, setSel] = React.useState("live");
|
|
550
|
+
const [openCat, setOpenCat] = React.useState(null);
|
|
551
|
+
const [openElem, setOpenElem] = React.useState(null);
|
|
552
|
+
const convNodes = typeof props.useSession === "function" ? props.useSession((s) => s.nodes) : void 0;
|
|
553
|
+
const bySeq = React.useMemo(() => {
|
|
554
|
+
const m = /* @__PURE__ */ new Map();
|
|
555
|
+
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
556
|
+
return m;
|
|
557
|
+
}, [convNodes]);
|
|
558
|
+
const hasMore = typeof props.useSession === "function" ? props.useSession((s) => s.hasMore === true) : false;
|
|
559
|
+
const loadingOlder = typeof props.useSession === "function" ? props.useSession((s) => s.loadingOlder === true) : false;
|
|
560
|
+
const openSeq = openElem !== null && openElem.startsWith("n") ? Number(openElem.slice(1)) : null;
|
|
561
|
+
const missingSeq = openSeq !== null && !bySeq.has(openSeq) ? openSeq : null;
|
|
562
|
+
const [exhausted, setExhausted] = React.useState(false);
|
|
563
|
+
const pagesRef = React.useRef(0);
|
|
564
|
+
React.useEffect(() => {
|
|
565
|
+
pagesRef.current = 0;
|
|
566
|
+
setExhausted(false);
|
|
567
|
+
}, [openElem]);
|
|
568
|
+
const loadOlderHistory = props.loadOlderHistory;
|
|
569
|
+
React.useEffect(() => {
|
|
570
|
+
if (missingSeq === null || !hasMore || loadingOlder || exhausted) return;
|
|
571
|
+
if (loadOlderHistory === void 0) return;
|
|
572
|
+
if (pagesRef.current >= MAX_AUTO_PAGES) {
|
|
573
|
+
setExhausted(true);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
pagesRef.current += 1;
|
|
577
|
+
void loadOlderHistory();
|
|
578
|
+
}, [missingSeq, hasMore, loadingOlder, exhausted, bySeq, loadOlderHistory]);
|
|
579
|
+
React.useEffect(() => {
|
|
580
|
+
if (!hasMore && missingSeq !== null && !exhausted) setExhausted(true);
|
|
581
|
+
}, [hasMore, missingSeq, exhausted]);
|
|
582
|
+
const pinSeq = props.pinSeq;
|
|
583
|
+
React.useEffect(() => {
|
|
584
|
+
setSel(pinSeq === null || pinSeq === void 0 ? "live" : pinSeq);
|
|
585
|
+
setOpenCat(null);
|
|
586
|
+
setOpenElem(null);
|
|
587
|
+
}, [pinSeq]);
|
|
588
|
+
const toolFocus = props.toolFocus;
|
|
589
|
+
React.useEffect(() => {
|
|
590
|
+
if (toolFocus === null || toolFocus === void 0) return;
|
|
591
|
+
setSel("live");
|
|
592
|
+
setOpenCat("tools");
|
|
593
|
+
setOpenElem(toolFocus.tool !== void 0 ? "tool:" + toolFocus.tool : null);
|
|
594
|
+
if (props.onToolFocusHandled !== void 0) props.onToolFocusHandled();
|
|
595
|
+
}, [toolFocus, props.onToolFocusHandled]);
|
|
596
|
+
const awaiting = missingSeq !== null && !exhausted && loadOlderHistory !== void 0 && hasMore;
|
|
597
|
+
const requests = data.requests || [];
|
|
598
|
+
const hoverReq = props.previewSeq !== null && props.previewSeq !== void 0 ? requests.find((r) => r.seq === props.previewSeq) ?? null : null;
|
|
599
|
+
const req = hoverReq ?? (sel === "live" ? null : requests.find((r) => r.seq === sel) ?? null);
|
|
600
|
+
const seq = req !== null ? req.seq : null;
|
|
601
|
+
const linked = req === null && props.onHoverKey !== void 0;
|
|
602
|
+
const linkKey = linked && props.hoverKey !== null && props.hoverKey !== "free" ? props.hoverKey : null;
|
|
603
|
+
const view = assemble(data, headers, seq);
|
|
604
|
+
const breakdown = req !== null ? req : data.current;
|
|
605
|
+
const parts = partsOf(breakdown);
|
|
606
|
+
const total = breakdown.total;
|
|
607
|
+
const pick = (v) => {
|
|
608
|
+
setSel(v === "live" ? "live" : Number(v));
|
|
609
|
+
setOpenCat(null);
|
|
610
|
+
setOpenElem(null);
|
|
525
611
|
};
|
|
526
|
-
const
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
612
|
+
const refReq = req === null ? requests.length > 0 ? requests[requests.length - 1] : null : lastOfTurn(requests, (req.turn ?? 0) - 1);
|
|
613
|
+
const prevView = refReq !== null ? assemble(data, headers, refReq.seq) : null;
|
|
614
|
+
const byCat = byCatOf(view);
|
|
615
|
+
const toolCount = (c) => countOf(view, c);
|
|
616
|
+
const toggleCat = (c) => {
|
|
617
|
+
const openable = toolCount(c) > 0 || (c === "system" || c === "tools") && view.header === null;
|
|
618
|
+
if (!openable) return;
|
|
619
|
+
setOpenCat(openCat === c ? null : c);
|
|
620
|
+
setOpenElem(null);
|
|
621
|
+
};
|
|
622
|
+
const toggleElem = (key) => setOpenElem(openElem === key ? null : key);
|
|
623
|
+
const elemRow = (key, tag, preview, tokens, time, body) => {
|
|
624
|
+
const open = openElem === key;
|
|
625
|
+
return /* @__PURE__ */ React.createElement("div", { key, className: "lc-br-elem" + (open ? " lc-br-elem-on" : "") }, /* @__PURE__ */ React.createElement("button", { type: "button", className: "lc-br-elem-row", onClick: () => {
|
|
626
|
+
toggleElem(key);
|
|
627
|
+
} }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "\u25B8"), tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, tag) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-preview" }, preview), time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-time" }, fmtTime2(time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "\u2248" + fmt3(tokens))), open ? body : null);
|
|
628
|
+
};
|
|
629
|
+
const catBody = (c) => {
|
|
630
|
+
if (c === "system") {
|
|
631
|
+
if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
|
|
632
|
+
const system = view.header.system;
|
|
633
|
+
if (system === void 0) return null;
|
|
634
|
+
return elemRow(
|
|
635
|
+
"sys",
|
|
636
|
+
null,
|
|
637
|
+
system.replace(/\s+/g, " ").trim().slice(0, 80),
|
|
638
|
+
breakdown.system,
|
|
639
|
+
void 0,
|
|
640
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre" }, system))
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
if (c === "tools") {
|
|
644
|
+
if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
|
|
645
|
+
const labels = {
|
|
646
|
+
desc: t("tool.desc"),
|
|
647
|
+
title: t("tool.params"),
|
|
648
|
+
empty: t("tool.paramsEmpty"),
|
|
649
|
+
show: t("tool.jsonToggle"),
|
|
650
|
+
hide: t("tool.jsonHide")
|
|
651
|
+
};
|
|
652
|
+
return view.header.tools.slice().sort((a, b) => b.tokens - a.tokens).map((tool) => {
|
|
653
|
+
return elemRow(
|
|
654
|
+
"tool:" + tool.name,
|
|
655
|
+
null,
|
|
656
|
+
tool.name,
|
|
657
|
+
tool.tokens,
|
|
658
|
+
void 0,
|
|
659
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement(ToolSchema, { description: tool.description, schema: tool.schema, labels }))
|
|
660
|
+
);
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
const nodes = (byCat[c] ?? []).slice().reverse();
|
|
664
|
+
return nodes.map((n) => {
|
|
665
|
+
let tag = null;
|
|
666
|
+
let preview = nodeText(n);
|
|
667
|
+
if (n.cat === "tool") {
|
|
668
|
+
tag = (n.tool ?? "?") + (n.err ? " \u26A0" : "");
|
|
669
|
+
preview = t("node.toolResult") + (n.err ? " \u26A0" : "");
|
|
670
|
+
} else if (n.cat === "inject" && !n.skill) {
|
|
671
|
+
tag = t("form." + (n.form || "context"));
|
|
672
|
+
if (n.text !== void 0 && n.text !== "") {
|
|
673
|
+
preview = n.form === "snapshot" ? t("node.snapshot") + n.text : n.text;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return elemRow(
|
|
677
|
+
"n" + n.seq,
|
|
678
|
+
tag,
|
|
679
|
+
preview,
|
|
680
|
+
n.tokens,
|
|
681
|
+
n.time,
|
|
682
|
+
/* @__PURE__ */ React.createElement(
|
|
683
|
+
NodeContent,
|
|
684
|
+
{
|
|
685
|
+
node: n,
|
|
686
|
+
conv: bySeq.get(n.seq),
|
|
687
|
+
hint: bySeq.get(n.seq) === void 0 && awaiting ? t("browser.loading") : t("browser.noContent")
|
|
688
|
+
}
|
|
689
|
+
)
|
|
690
|
+
);
|
|
691
|
+
});
|
|
692
|
+
};
|
|
693
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("browser.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-br-hint" }, t("browser.deltaHint")), /* @__PURE__ */ React.createElement(
|
|
694
|
+
"select",
|
|
530
695
|
{
|
|
531
|
-
className: "lc-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
696
|
+
className: "lc-br-pick",
|
|
697
|
+
value: seq === null ? "live" : String(seq),
|
|
698
|
+
onChange: (e) => {
|
|
699
|
+
pick(e.target.value);
|
|
535
700
|
}
|
|
536
701
|
},
|
|
537
|
-
|
|
538
|
-
/* @__PURE__ */ React.createElement(
|
|
539
|
-
|
|
702
|
+
/* @__PURE__ */ React.createElement("option", { value: "live" }, t("browser.live")),
|
|
703
|
+
requests.slice().reverse().map((r) => /* @__PURE__ */ React.createElement("option", { key: r.seq, value: String(r.seq) }, t("detail.step", { t: r.turn ?? 0, s: r.step ?? 0 }) + " \xB7 " + fmtTime2(r.time)))
|
|
704
|
+
)), /* @__PURE__ */ React.createElement("div", { className: "lc-br-meta" }, /* @__PURE__ */ React.createElement("b", null, req !== null ? t("detail.step", { t: req.turn ?? 0, s: req.step ?? 0 }) : t("browser.liveNow")), req !== null ? /* @__PURE__ */ React.createElement("span", null, fmtTime2(req.time)) : null, hoverReq !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("browser.preview")) : null, /* @__PURE__ */ React.createElement("span", null, t("detail.estTotal", { n: fmt3(total) })), req !== null && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt3(req.prompt) })) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-br-bar" }, /* @__PURE__ */ React.createElement(
|
|
705
|
+
StackedBar,
|
|
706
|
+
{
|
|
707
|
+
parts,
|
|
708
|
+
height: 10,
|
|
709
|
+
hoverKey: linked ? linkKey : void 0,
|
|
710
|
+
onHoverKey: linked ? props.onHoverKey : void 0,
|
|
711
|
+
tip: false
|
|
712
|
+
}
|
|
713
|
+
)), view.missingLive > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.missingLive", { n: view.missingLive })) : null, view.approximate ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.approx")) : null, /* @__PURE__ */ React.createElement("div", { className: "lc-br-cats" }, CATS.map((c) => {
|
|
714
|
+
const count = toolCount(c.key);
|
|
715
|
+
const v = breakdown[c.key] || 0;
|
|
716
|
+
const prevCount = prevView !== null ? countOf(prevView, c.key) : null;
|
|
717
|
+
const countDelta = prevCount !== null ? count - prevCount : null;
|
|
718
|
+
const prevTokens = refReq !== null ? refReq[c.key] || 0 : null;
|
|
719
|
+
const tokenDelta = prevTokens !== null ? v - prevTokens : null;
|
|
720
|
+
const openable = count > 0 || (c.key === "system" || c.key === "tools") && view.header === null;
|
|
721
|
+
const open = openCat === c.key && openable;
|
|
722
|
+
return /* @__PURE__ */ React.createElement("div", { key: c.key, className: "lc-br-cat" + (openable ? "" : " lc-br-cat-empty") }, /* @__PURE__ */ React.createElement(
|
|
723
|
+
"button",
|
|
540
724
|
{
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
725
|
+
type: "button",
|
|
726
|
+
className: "lc-br-cat-row" + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
|
|
727
|
+
onMouseEnter: linked ? () => {
|
|
728
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(c.key);
|
|
729
|
+
} : void 0,
|
|
730
|
+
onMouseLeave: linked ? () => {
|
|
731
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
732
|
+
} : void 0,
|
|
733
|
+
onClick: () => {
|
|
734
|
+
toggleCat(c.key);
|
|
544
735
|
}
|
|
545
736
|
},
|
|
546
|
-
/* @__PURE__ */ React.createElement("
|
|
547
|
-
/* @__PURE__ */ React.createElement("
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
737
|
+
/* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "\u25B8"),
|
|
738
|
+
/* @__PURE__ */ React.createElement("i", { style: { background: c.color } }),
|
|
739
|
+
/* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-label" }, catLabel(c.key)),
|
|
740
|
+
/* @__PURE__ */ React.createElement("span", { className: "lc-br-count-grp" }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-count" }, t("browser.items", { n: count })), countDelta !== null && countDelta !== 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-delta lc-br-delta-" + (countDelta > 0 ? "up" : "down") }, (countDelta > 0 ? "+" : "") + countDelta) : null),
|
|
741
|
+
/* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens-grp" }, tokenDelta !== null && tokenDelta !== 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-tdelta lc-br-tdelta-" + (tokenDelta > 0 ? "up" : "down") }, (tokenDelta > 0 ? "+" : "") + fmt3(tokenDelta)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "\u2248" + fmt3(v))),
|
|
742
|
+
/* @__PURE__ */ React.createElement("span", { className: "lc-br-pct" }, total > 0 ? Math.round(v / total * 100) + "%" : "")
|
|
743
|
+
), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-body" }, catBody(c.key)) : null);
|
|
744
|
+
})));
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/client/components/stackedBar.tsx
|
|
749
|
+
var AUTO_COMPACT_RATIO = 0.8;
|
|
750
|
+
function makeStackedBar(kit) {
|
|
751
|
+
const { t, fmt: fmt3, catLabel } = kit;
|
|
752
|
+
return function StackedBar(props) {
|
|
753
|
+
const [reserveOn, setReserveOn] = React.useState(false);
|
|
754
|
+
let total = 0;
|
|
755
|
+
for (const p of props.parts) total += p.value;
|
|
756
|
+
const scale = props.max !== void 0 && props.max > total ? props.max : total;
|
|
757
|
+
const free = props.max !== void 0 && props.max > total ? props.max - total : 0;
|
|
758
|
+
const usedPct = scale > 0 ? total / scale * 100 : 0;
|
|
759
|
+
const hovering = props.hoverKey !== null && props.hoverKey !== void 0;
|
|
760
|
+
const showBox = free > 0 && hovering;
|
|
761
|
+
const reserve = props.reserve !== void 0 && props.max !== void 0 && props.max > 0 ? props.reserve : null;
|
|
762
|
+
const reserveLeft = reserve !== null ? Math.round(props.max * reserve.ratio / scale * 1e3) / 10 : 0;
|
|
763
|
+
const reserveWidth = reserve !== null ? Math.round((1 - reserve.ratio) * props.max / scale * 1e3) / 10 : 0;
|
|
764
|
+
let tip = null;
|
|
765
|
+
if (reserveOn && reserve !== null) {
|
|
766
|
+
tip = {
|
|
767
|
+
text: reserve.label,
|
|
768
|
+
leftPct: Math.max(12, Math.min(reserveLeft + reserveWidth / 2, 88))
|
|
769
|
+
};
|
|
770
|
+
} else if (props.hoverKey !== null && props.hoverKey !== void 0) {
|
|
771
|
+
if (props.hoverKey === "free" && free > 0) {
|
|
772
|
+
const pct = scale > 0 ? free / scale * 100 : 0;
|
|
773
|
+
tip = {
|
|
774
|
+
text: t("overview.free") + " " + fmt3(free) + " (" + Math.round(pct) + "%)",
|
|
775
|
+
leftPct: Math.max(12, Math.min(total / scale * 100 + pct / 2, 88))
|
|
776
|
+
};
|
|
777
|
+
} else {
|
|
778
|
+
let acc = 0;
|
|
779
|
+
for (const p of props.parts) {
|
|
780
|
+
const pct = scale > 0 ? p.value / scale * 100 : 0;
|
|
781
|
+
if (p.key === props.hoverKey && p.value > 0) {
|
|
782
|
+
tip = {
|
|
783
|
+
// "(pct%)" is a share of the OCCUPIED total — the solid box
|
|
784
|
+
// that appears on hover frames exactly this reference region.
|
|
785
|
+
text: catLabel(p.key) + " \u2248" + fmt3(p.value) + " (" + Math.round(p.value / total * 100) + "%) " + t("overview.ofUsed"),
|
|
786
|
+
leftPct: Math.max(12, Math.min(acc + pct / 2, 88))
|
|
787
|
+
};
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
acc += pct;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-stacked-wrap" }, /* @__PURE__ */ React.createElement(
|
|
795
|
+
"div",
|
|
796
|
+
{
|
|
797
|
+
className: "lc-stacked" + (hovering ? " lc-stacked-dim" : ""),
|
|
798
|
+
style: { height: (props.height || 14) + "px" },
|
|
799
|
+
onMouseLeave: () => {
|
|
800
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
801
|
+
setReserveOn(false);
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
total > 0 ? props.parts.map((p) => {
|
|
805
|
+
if (!p.value) return null;
|
|
806
|
+
const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
|
|
596
807
|
return /* @__PURE__ */ React.createElement(
|
|
597
|
-
"
|
|
808
|
+
"div",
|
|
598
809
|
{
|
|
599
|
-
key:
|
|
600
|
-
className: "lc-
|
|
601
|
-
style: {
|
|
602
|
-
width: blockW + "px",
|
|
603
|
-
background: TURN_FILLS[gi % TURN_FILLS.length]
|
|
604
|
-
},
|
|
605
|
-
title: "T" + grp.turn,
|
|
810
|
+
key: p.key,
|
|
811
|
+
className: "lc-stacked-seg" + (on ? " lc-stacked-seg-on" : ""),
|
|
812
|
+
style: { width: p.value / scale * 100 + "%", background: p.color },
|
|
606
813
|
onMouseEnter: () => {
|
|
607
|
-
props.
|
|
814
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
608
815
|
}
|
|
609
|
-
}
|
|
610
|
-
"T" + grp.turn
|
|
816
|
+
}
|
|
611
817
|
);
|
|
612
|
-
})
|
|
613
|
-
|
|
818
|
+
}) : null,
|
|
819
|
+
free > 0 ? /* @__PURE__ */ React.createElement(
|
|
820
|
+
"div",
|
|
821
|
+
{
|
|
822
|
+
key: "free",
|
|
823
|
+
className: "lc-stacked-free" + (props.hoverKey === "free" ? " lc-stacked-free-on" : ""),
|
|
824
|
+
style: { width: free / scale * 100 + "%" },
|
|
825
|
+
onMouseEnter: () => {
|
|
826
|
+
if (props.onHoverKey !== void 0) props.onHoverKey("free");
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
) : null,
|
|
830
|
+
reserve !== null ? /* @__PURE__ */ React.createElement(
|
|
831
|
+
"div",
|
|
832
|
+
{
|
|
833
|
+
className: "lc-reserve",
|
|
834
|
+
style: { left: reserveLeft + "%", width: reserveWidth + "%" },
|
|
835
|
+
onMouseEnter: () => {
|
|
836
|
+
setReserveOn(true);
|
|
837
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
838
|
+
},
|
|
839
|
+
onMouseLeave: () => setReserveOn(false)
|
|
840
|
+
}
|
|
841
|
+
) : null,
|
|
842
|
+
/* @__PURE__ */ React.createElement(
|
|
843
|
+
"div",
|
|
844
|
+
{
|
|
845
|
+
className: "lc-occupied-box" + (showBox ? " lc-occupied-box-on" : ""),
|
|
846
|
+
style: { width: usedPct + "%" }
|
|
847
|
+
}
|
|
848
|
+
)
|
|
849
|
+
), props.tip !== false ? /* @__PURE__ */ React.createElement(
|
|
850
|
+
"div",
|
|
851
|
+
{
|
|
852
|
+
className: "lc-bar-tip" + (tip ? " lc-bar-tip-on" : ""),
|
|
853
|
+
style: { left: tip ? tip.leftPct + "%" : "50%" }
|
|
854
|
+
},
|
|
855
|
+
tip ? tip.text : ""
|
|
856
|
+
) : null);
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function makeLegend(kit) {
|
|
860
|
+
const { t, fmt: fmt3, catLabel } = kit;
|
|
861
|
+
return function Legend(props) {
|
|
862
|
+
let total = 0;
|
|
863
|
+
for (const p of props.parts) total += p.value;
|
|
864
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-legend" }, props.parts.map((p) => {
|
|
865
|
+
const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
|
|
866
|
+
return /* @__PURE__ */ React.createElement(
|
|
867
|
+
"span",
|
|
868
|
+
{
|
|
869
|
+
key: p.key,
|
|
870
|
+
className: "lc-chip" + (on ? " lc-chip-on" : ""),
|
|
871
|
+
title: t("overview.ofUsed"),
|
|
872
|
+
onMouseEnter: () => {
|
|
873
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
874
|
+
},
|
|
875
|
+
onMouseLeave: () => {
|
|
876
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
877
|
+
}
|
|
878
|
+
},
|
|
879
|
+
/* @__PURE__ */ React.createElement("i", { style: { background: p.color } }),
|
|
880
|
+
/* @__PURE__ */ React.createElement("span", { className: "lc-chip-label" }, catLabel(p.key)),
|
|
881
|
+
/* @__PURE__ */ React.createElement("span", { className: "lc-chip-nums" }, "\u2248" + fmt3(p.value), total > 0 ? /* @__PURE__ */ React.createElement("em", null, Math.round(p.value / total * 100) + "%") : null)
|
|
882
|
+
);
|
|
883
|
+
}));
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// src/client/components/currentComposition.tsx
|
|
888
|
+
function makeCurrentComposition(kit, StackedBar, Legend) {
|
|
889
|
+
const { t, fmt: fmt3 } = kit;
|
|
890
|
+
return function CurrentComposition(props) {
|
|
891
|
+
const head = props.head;
|
|
892
|
+
const reserve = head.window != null && head.window > 0 ? { ratio: AUTO_COMPACT_RATIO, label: t("overview.compactReserve", { pct: Math.round(AUTO_COMPACT_RATIO * 100) }) } : void 0;
|
|
893
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("overview.title")), props.subtitle !== void 0 && props.subtitle !== "" ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, props.subtitle) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt3(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt3(head.window) + " tokens" : " " + t("overview.estimate")), head.pct !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-overview-pct" }, /* @__PURE__ */ React.createElement("b", null, head.pct + "%"), t("overview.used")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: head.parts, height: 16, max: head.window, hoverKey: props.hoverKey, onHoverKey: props.onHoverKey, reserve }), /* @__PURE__ */ React.createElement(Legend, { parts: head.parts, hoverKey: props.hoverKey, onHoverKey: props.onHoverKey }), props.tools !== void 0 && props.tools.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-tools" }, /* @__PURE__ */ React.createElement("button", { type: "button", className: "lc-tools-label", onClick: () => {
|
|
894
|
+
if (props.onToolFocus !== void 0) props.onToolFocus({});
|
|
895
|
+
} }, t("tools.top")), props.tools.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => /* @__PURE__ */ React.createElement("button", { key: tool.name, type: "button", className: "lc-tool-chip", onClick: () => {
|
|
896
|
+
if (props.onToolFocus !== void 0) props.onToolFocus({ tool: tool.name });
|
|
897
|
+
} }, tool.name + " " + fmt3(tool.tokens))), props.tools.length > 5 ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "lc-tools-more", onClick: () => {
|
|
898
|
+
if (props.onToolFocus !== void 0) props.onToolFocus({});
|
|
899
|
+
} }, t("tools.more", { n: props.tools.length })) : null) : null);
|
|
614
900
|
};
|
|
615
901
|
}
|
|
616
902
|
|
|
617
903
|
// src/client/components/contextModal.tsx
|
|
618
|
-
var TREND_TURNS = 10;
|
|
619
904
|
function makeContextModal(ctx, kit) {
|
|
620
|
-
const { t
|
|
905
|
+
const { t } = kit;
|
|
621
906
|
const sessions = ctx.get("sessions");
|
|
622
907
|
const StackedBar = makeStackedBar(kit);
|
|
623
908
|
const Legend = makeLegend(kit);
|
|
624
|
-
const
|
|
625
|
-
const
|
|
909
|
+
const CurrentComposition = makeCurrentComposition(kit, StackedBar, Legend);
|
|
910
|
+
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
626
911
|
return function ContextModal(props) {
|
|
627
912
|
const sessionId = typeof props.sessionId === "string" ? props.sessionId : "";
|
|
628
913
|
const open = typeof props.useContextModal === "function" ? props.useContextModal((s) => s) : false;
|
|
629
914
|
const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
|
|
630
915
|
const pressure = typeof props.useProjection === "function" ? contextPressureOf(props.useProjection("contextPressure")) : null;
|
|
631
|
-
const
|
|
632
|
-
const [hoveredSeq, setHoveredSeq] = React.useState(null);
|
|
916
|
+
const headers = typeof props.useProjection === "function" ? headersOf(props.useProjection("contextHeaders")) : null;
|
|
633
917
|
const [hoverCat, setHoverCat] = React.useState(null);
|
|
918
|
+
const [toolFocus, setToolFocus] = React.useState(null);
|
|
634
919
|
const close = React.useCallback(() => {
|
|
635
920
|
if (sessionId === "") return;
|
|
636
921
|
modalStoreOf(sessionId).set(false);
|
|
@@ -655,40 +940,35 @@ function makeContextModal(ctx, kit) {
|
|
|
655
940
|
};
|
|
656
941
|
}, [open, close]);
|
|
657
942
|
if (!open) return null;
|
|
658
|
-
const requests = data !== null ? data.requests || [] : [];
|
|
659
|
-
const events = data !== null ? data.events || [] : [];
|
|
660
|
-
const turns = aggregateByTurn(requests).slice(-TREND_TURNS);
|
|
661
|
-
const markers = attachMarkers(turns, events);
|
|
662
|
-
let pinnedReq = null;
|
|
663
|
-
for (const req of turns) if (req.seq === selectedSeq) pinnedReq = req;
|
|
664
|
-
let activeReq = null;
|
|
665
|
-
if (hoveredSeq !== null) {
|
|
666
|
-
for (const req of turns) if (req.seq === hoveredSeq) activeReq = req;
|
|
667
|
-
}
|
|
668
|
-
if (activeReq === null) activeReq = pinnedReq;
|
|
669
|
-
if (activeReq === null && turns.length > 0) activeReq = turns[turns.length - 1];
|
|
670
|
-
const markerOf = (req) => {
|
|
671
|
-
const i = turns.indexOf(req);
|
|
672
|
-
return i >= 0 ? markers[i] : void 0;
|
|
673
|
-
};
|
|
674
943
|
const head = data !== null ? headlineOf(data, pressure) : null;
|
|
944
|
+
const subtitle = data !== null ? (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : "") : "";
|
|
675
945
|
return /* @__PURE__ */ React.createElement("div", { className: "lc-modal-backdrop", onClick: close }, /* @__PURE__ */ React.createElement("div", { className: "lc-modal-card", onClick: (ev) => {
|
|
676
946
|
ev.stopPropagation();
|
|
677
|
-
} }, /* @__PURE__ */ React.createElement("div", { className: "lc-modal-head" }, /* @__PURE__ */ React.createElement("span", { className: "lc-modal-title" }, t("tab")),
|
|
678
|
-
|
|
947
|
+
} }, /* @__PURE__ */ React.createElement("div", { className: "lc-modal-head" }, /* @__PURE__ */ React.createElement("span", { className: "lc-modal-title" }, t("tab")), /* @__PURE__ */ React.createElement("button", { className: "lc-modal-close", "aria-label": t("cmd.close"), onClick: close }, "\xD7")), data === null || head === null ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
948
|
+
CurrentComposition,
|
|
679
949
|
{
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
950
|
+
head,
|
|
951
|
+
subtitle,
|
|
952
|
+
hoverKey: hoverCat,
|
|
953
|
+
onHoverKey: setHoverCat,
|
|
954
|
+
tools: data.toolList,
|
|
955
|
+
onToolFocus: setToolFocus
|
|
956
|
+
}
|
|
957
|
+
), /* @__PURE__ */ React.createElement(
|
|
958
|
+
ContextBrowser,
|
|
959
|
+
{
|
|
960
|
+
data,
|
|
961
|
+
headers,
|
|
962
|
+
useSession: props.useSession,
|
|
963
|
+
loadOlderHistory: props.loadOlderHistory,
|
|
964
|
+
hoverKey: hoverCat,
|
|
965
|
+
onHoverKey: setHoverCat,
|
|
966
|
+
toolFocus,
|
|
967
|
+
onToolFocusHandled: () => {
|
|
968
|
+
setToolFocus(null);
|
|
689
969
|
}
|
|
690
970
|
}
|
|
691
|
-
)
|
|
971
|
+
))));
|
|
692
972
|
};
|
|
693
973
|
}
|
|
694
974
|
|
|
@@ -718,12 +998,14 @@ var STYLES = [
|
|
|
718
998
|
".lc-stacked-wrap { position: relative; width: 100%; }",
|
|
719
999
|
".lc-stacked { display: flex; width: 100%; border-radius: 5px; overflow: hidden; background: rgba(128,128,128,0.18); position: relative; }",
|
|
720
1000
|
// Hover reference frame around the OCCUPIED region of the composition bar:
|
|
721
|
-
//
|
|
722
|
-
// "share of used" percentages visibly map to the boxed part (the
|
|
723
|
-
// sits outside it). pointer-events: none keeps hover on the
|
|
724
|
-
//
|
|
725
|
-
//
|
|
726
|
-
|
|
1001
|
+
// a SOLID box from the left edge to the used/window boundary, so the
|
|
1002
|
+
// legend's "share of used" percentages visibly map to the boxed part (the
|
|
1003
|
+
// free track sits outside it). pointer-events: none keeps hover on the
|
|
1004
|
+
// segments/free. A soft LIGHT-GRAY stroke — a gentle guide rather than a
|
|
1005
|
+
// hard black box — that still reads through its 2px thickness against the
|
|
1006
|
+
// dimmed parts underneath it (`.lc-stacked-dim`). The free track echoes
|
|
1007
|
+
// the same weight with a dashed box, so the two read as one system.
|
|
1008
|
+
".lc-occupied-box { position: absolute; top: 0; bottom: 0; left: 0; border: 2px solid var(--dsw-alias-label-tertiary); border-radius: 5px; box-sizing: border-box; pointer-events: none; opacity: 0; box-shadow: 0 0 0 1px var(--dsw-alias-bg-layer-2); transition: opacity 120ms ease; }",
|
|
727
1009
|
".lc-occupied-box-on { opacity: 1; }",
|
|
728
1010
|
".lc-bar-tip { position: absolute; bottom: calc(100% + 6px); transform: translateX(-50%); z-index: 5; white-space: nowrap; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; padding: 3px 8px; font-size: 12px; color: var(--dsw-alias-label-primary); box-shadow: 0 2px 8px rgba(0,0,0,0.18); pointer-events: none; opacity: 0; transition: opacity 120ms ease; }",
|
|
729
1011
|
".lc-bar-tip-on { opacity: 1; }",
|
|
@@ -732,9 +1014,18 @@ var STYLES = [
|
|
|
732
1014
|
// overview or the mirrored browser bar) glides in and out — same 120ms
|
|
733
1015
|
// rhythm as the rest of the view.
|
|
734
1016
|
".lc-stacked-seg { transition: filter 120ms ease, opacity 120ms ease; }",
|
|
735
|
-
".lc-stacked-free { transition: box-shadow 120ms ease, opacity 120ms ease; }",
|
|
1017
|
+
".lc-stacked-free { box-sizing: border-box; transition: box-shadow 120ms ease, opacity 120ms ease; }",
|
|
736
1018
|
".lc-stacked-seg-on { filter: brightness(1.18); }",
|
|
737
|
-
|
|
1019
|
+
// The free track mirrors the occupied frame: a DASHED 2px box (the occupied
|
|
1020
|
+
// frame is solid) at the same weight — border, not the old inset shadow, so
|
|
1021
|
+
// the dash pattern renders; the base box-sizing keeps the width from shifting.
|
|
1022
|
+
// Auto-compaction reserve band: the rightmost (1−ratio) of the window,
|
|
1023
|
+
// striped diagonally like a warning plate so it reads as "headroom, not
|
|
1024
|
+
// real usage" rather than plain free space. Sits above the track/segments
|
|
1025
|
+
// (overlay visible even past the threshold) with its own pointer so the
|
|
1026
|
+
// hover explains it (help cursor signals "more info").
|
|
1027
|
+
".lc-reserve { position: absolute; top: 0; bottom: 0; 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); }",
|
|
1028
|
+
".lc-stacked-free-on { border: 2px dashed var(--dsw-alias-label-secondary); border-radius: 3px; }",
|
|
738
1029
|
// Hover focus: everything except the hovered part (segment, legend chip, or
|
|
739
1030
|
// free track) recedes, so the composition highlight and the occupied-region
|
|
740
1031
|
// frame read clearly. The selected segment/free keeps full opacity.
|
|
@@ -742,8 +1033,19 @@ var STYLES = [
|
|
|
742
1033
|
".lc-stacked-dim .lc-stacked-seg-on { opacity: 1; }",
|
|
743
1034
|
".lc-stacked-dim .lc-stacked-free { opacity: 0.35; }",
|
|
744
1035
|
".lc-stacked-dim .lc-stacked-free-on { opacity: 1; }",
|
|
745
|
-
|
|
746
|
-
|
|
1036
|
+
// Current-composition legend: an auto-flowing grid (as many columns as the
|
|
1037
|
+
// card fits, narrowing flows to fewer) whose cells are label-left /
|
|
1038
|
+
// numbers-right rows — each category a tidy "● 系统提示词 ≈2.0k 2%".
|
|
1039
|
+
".lc-legend { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 6px 14px; margin-top: 10px; }",
|
|
1040
|
+
".lc-chip { display: flex; align-items: center; gap: 5px; min-width: 0; color: var(--dsw-alias-label-primary); padding: 1px 6px; border-radius: 6px; cursor: pointer; transition: background-color 120ms ease; }",
|
|
1041
|
+
// The category name is the row's emphasis: bold at rest (the chip is a
|
|
1042
|
+
// legend label, not inline prose), ellipsized if a narrow cell squeezes it
|
|
1043
|
+
// while the right-aligned figures stay intact.
|
|
1044
|
+
".lc-chip-label { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }",
|
|
1045
|
+
// The numeric group (≈tokens + percent) pins to the cell's right edge while
|
|
1046
|
+
// the category label stays left; a baseline acts as the shared vertical
|
|
1047
|
+
// rhythm for the two figures.
|
|
1048
|
+
".lc-chip-nums { margin-left: auto; flex: none; display: inline-flex; align-items: baseline; gap: 6px; white-space: nowrap; }",
|
|
747
1049
|
".lc-chip i, .lc-detail-row i, .lc-node i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; }",
|
|
748
1050
|
".lc-chip i { transition: box-shadow 120ms ease; }",
|
|
749
1051
|
".lc-chip em { font-style: normal; color: var(--dsw-alias-label-secondary); }",
|
|
@@ -753,7 +1055,14 @@ var STYLES = [
|
|
|
753
1055
|
".lc-chip-on { font-weight: 600; background: var(--dsw-alias-interactive-bg-hover); }",
|
|
754
1056
|
".lc-chip-on i { box-shadow: 0 0 0 1px var(--dsw-alias-brand-primary); }",
|
|
755
1057
|
".lc-tools { margin-top: 10px; color: var(--dsw-alias-label-secondary); display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }",
|
|
756
|
-
|
|
1058
|
+
// The overview tool list links into the Context browser: the "工具定义 Top"
|
|
1059
|
+
// label, its "等 N 个" overflow link, and every tool chip are buttons, and
|
|
1060
|
+
// hover underlines them (the chip already reads as a tag, so the underline
|
|
1061
|
+
// is the only affordance).
|
|
1062
|
+
".lc-tools-label, .lc-tools-more { border: 0; background: transparent; padding: 0; color: var(--dsw-alias-label-secondary); font-family: inherit; font-size: 12px; cursor: pointer; }",
|
|
1063
|
+
".lc-tools-label:hover, .lc-tools-more:hover { text-decoration: underline; }",
|
|
1064
|
+
".lc-tool-chip { background: var(--dsw-alias-bg-layer-2); border: 0; border-radius: 4px; padding: 1px 7px; font: inherit; font-size: 12px; color: var(--dsw-alias-label-primary); cursor: pointer; }",
|
|
1065
|
+
".lc-tool-chip:hover { text-decoration: underline; }",
|
|
757
1066
|
".lc-chartrow { display: flex; gap: 6px; align-items: stretch; }",
|
|
758
1067
|
".lc-axis { position: relative; width: 40px; height: 150px; padding-top: 18px; box-sizing: border-box; color: var(--dsw-alias-label-secondary); font-size: 11px; }",
|
|
759
1068
|
".lc-axis span { position: absolute; right: 0; line-height: 1; }",
|
|
@@ -848,368 +1157,108 @@ var STYLES = [
|
|
|
848
1157
|
".lc-kind-inject { background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent); color: var(--dsw-alias-state-success-primary); }",
|
|
849
1158
|
".lc-kind-compaction { background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent); color: var(--dsw-alias-state-error-primary); }",
|
|
850
1159
|
".lc-kind-prune { background: color-mix(in srgb, #8b5cf6 15%, transparent); color: #8b5cf6; }",
|
|
851
|
-
".lc-kind-model { background: color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent); color: var(--dsw-alias-brand-primary); }",
|
|
852
|
-
".lc-event-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
|
|
853
|
-
".lc-event-at { flex: none; color: var(--dsw-alias-label-secondary); font-size: 11px; white-space: nowrap; }",
|
|
854
|
-
".lc-event-tokens { color: var(--dsw-alias-state-success-primary); font-weight: 600; white-space: nowrap; }",
|
|
855
|
-
".lc-event-tokens.lc-up { color: var(--dsw-alias-state-warn-primary); }",
|
|
856
|
-
".lc-event-time { color: var(--dsw-alias-label-secondary); font-size: 12px; }",
|
|
857
|
-
".lc-node { display: flex; align-items: center; gap: 8px; padding: 3px 0; }",
|
|
858
|
-
".lc-node-preview { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-primary); }",
|
|
859
|
-
".lc-node-time { color: var(--dsw-alias-label-secondary); font-size: 12px; min-width: 54px; text-align: right; }",
|
|
860
|
-
".lc-node-tokens { color: var(--dsw-alias-label-secondary); }",
|
|
861
|
-
".lc-nodes-more { color: var(--dsw-alias-label-secondary); padding: 3px 0; }",
|
|
862
|
-
".lc-empty { color: var(--dsw-alias-label-secondary); padding: 18px 0; text-align: center; }",
|
|
863
|
-
".lc-foot { color: var(--dsw-alias-label-secondary); font-size: 12px; margin-top: 4px; }",
|
|
864
|
-
// ---- /context modal (centered dialog; escapes the composer anchor via fixed positioning) ----
|
|
865
|
-
".lc-modal-backdrop { position: fixed; inset: 0; z-index: 200; background: rgba(0, 0, 0, 0.45); display: flex; align-items: center; justify-content: center; }",
|
|
866
|
-
".lc-modal-card { width: min(720px, calc(100vw - 48px)); max-height: min(82vh, 760px); overflow-y: auto; box-sizing: border-box; background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l1); border-radius: 12px; box-shadow: var(--dsw-shadow-lv3, 0 12px 32px rgba(0, 0, 0, 0.4)); padding: 16px 18px 18px; color: var(--dsw-alias-label-primary); font-size: 13px; }",
|
|
867
|
-
".lc-modal-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 12px; }",
|
|
868
|
-
".lc-modal-title { font-weight: 600; font-size: 14px; }",
|
|
869
|
-
".lc-modal-close { margin-left: auto; border: 0; background: transparent; color: var(--dsw-alias-label-secondary); font-size: 18px; line-height: 1; padding: 2px 6px; border-radius: 6px; cursor: pointer; font-family: inherit; }",
|
|
870
|
-
".lc-modal-close:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-2); }",
|
|
871
|
-
|
|
872
|
-
//
|
|
873
|
-
//
|
|
874
|
-
|
|
875
|
-
".lc-br-
|
|
876
|
-
".lc-br-
|
|
877
|
-
".lc-br-meta
|
|
878
|
-
".lc-br-
|
|
879
|
-
".lc-br-
|
|
880
|
-
".lc-br-
|
|
881
|
-
".lc-br-
|
|
882
|
-
".lc-br-cat
|
|
883
|
-
".lc-br-cat-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
//
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
".lc-br-cat-
|
|
890
|
-
".lc-br-cat-
|
|
891
|
-
".lc-br-cat-
|
|
892
|
-
".lc-br-
|
|
893
|
-
".lc-br-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
".lc-br-
|
|
898
|
-
".lc-br-
|
|
899
|
-
".lc-br-
|
|
900
|
-
".lc-br-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
".lc-br-
|
|
905
|
-
".lc-br-
|
|
906
|
-
".lc-br-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
//
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
if (n.calls) return t("node.calls") + n.calls.join(", ");
|
|
954
|
-
if (n.text) return n.form === "snapshot" ? t("node.snapshot") + n.text : n.text;
|
|
955
|
-
if (n.cat === "assistant") return t("node.empty");
|
|
956
|
-
if (n.cat === "inject") return t("form." + (n.form || "context"));
|
|
957
|
-
return t("node.nonText");
|
|
958
|
-
};
|
|
959
|
-
}
|
|
960
|
-
function makeNodeList(kit) {
|
|
961
|
-
const { t, fmt: fmt3, fmtTime: fmtTime2 } = kit;
|
|
962
|
-
const nodeText = makeNodeText(kit);
|
|
963
|
-
return function NodeList(props) {
|
|
964
|
-
if (props.nodes.length === 0) {
|
|
965
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("nodes.empty"));
|
|
966
|
-
}
|
|
967
|
-
const catColor = {};
|
|
968
|
-
for (const c of CATS) catColor[c.key] = c.color;
|
|
969
|
-
const rows = props.nodes.slice().reverse();
|
|
970
|
-
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) => {
|
|
971
|
-
const text = nodeText(n);
|
|
972
|
-
return /* @__PURE__ */ React.createElement("div", { key: n.seq, className: "lc-node" }, /* @__PURE__ */ React.createElement("i", { style: { background: catColor[n.cat] || "#999" } }), /* @__PURE__ */ React.createElement("span", { className: "lc-node-preview", title: text }, text), typeof n.time === "number" ? /* @__PURE__ */ React.createElement("span", { className: "lc-node-time" }, fmtTime2(n.time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-node-tokens" }, fmt3(n.tokens)));
|
|
973
|
-
}));
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
// src/client/components/browser.tsx
|
|
978
|
-
function RawBlocks(props) {
|
|
979
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.blocks.map((b, i) => {
|
|
980
|
-
const blk = b;
|
|
981
|
-
if (blk !== null && typeof blk === "object" && (blk.type === "text" || blk.type === "reasoning") && typeof blk.text === "string") {
|
|
982
|
-
return /* @__PURE__ */ React.createElement("pre", { key: i, className: "lc-br-pre" + (blk.type === "reasoning" ? " lc-br-dim" : "") }, blk.text);
|
|
983
|
-
}
|
|
984
|
-
if (blk !== null && typeof blk === "object" && blk.type === "tool-result" && Array.isArray(blk.content)) {
|
|
985
|
-
return /* @__PURE__ */ React.createElement(RawBlocks, { key: i, blocks: blk.content });
|
|
986
|
-
}
|
|
987
|
-
return /* @__PURE__ */ React.createElement("pre", { key: i, className: "lc-br-pre lc-br-dim" }, JSON.stringify(b, null, 2));
|
|
988
|
-
}));
|
|
989
|
-
}
|
|
990
|
-
function NodeContent(props) {
|
|
991
|
-
const { node, conv } = props;
|
|
992
|
-
if (conv === void 0) {
|
|
993
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, node.text !== void 0 && node.text !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre" }, node.text) : null, /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
|
|
994
|
-
}
|
|
995
|
-
if (conv.kind === "assistant" && Array.isArray(conv.blocks)) {
|
|
996
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, conv.blocks.map((b, i) => {
|
|
997
|
-
const blk = b;
|
|
998
|
-
if (blk.kind === "text" && typeof blk.text === "string") {
|
|
999
|
-
return /* @__PURE__ */ React.createElement("pre", { key: i, className: "lc-br-pre" }, blk.text);
|
|
1000
|
-
}
|
|
1001
|
-
if (blk.kind === "reasoning" && typeof blk.text === "string") {
|
|
1002
|
-
return /* @__PURE__ */ React.createElement("pre", { key: i, className: "lc-br-pre lc-br-dim" }, blk.text);
|
|
1003
|
-
}
|
|
1004
|
-
if (blk.kind === "tool-call") {
|
|
1005
|
-
return /* @__PURE__ */ React.createElement("div", { key: i, className: "lc-br-call" }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, "\u2192 " + String(blk.name ?? "?")), typeof blk.argsRaw === "string" && blk.argsRaw !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre lc-br-dim" }, blk.argsRaw) : null);
|
|
1006
|
-
}
|
|
1007
|
-
return null;
|
|
1008
|
-
}));
|
|
1009
|
-
}
|
|
1010
|
-
if (conv.kind === "tool-result") {
|
|
1011
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, conv.call != null ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-call" }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, "\u2190 " + conv.call.name), conv.call.argsRaw !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre lc-br-dim" }, conv.call.argsRaw) : null) : null, Array.isArray(conv.content) ? /* @__PURE__ */ React.createElement(RawBlocks, { blocks: conv.content }) : null);
|
|
1012
|
-
}
|
|
1013
|
-
if (conv.kind === "compaction") {
|
|
1014
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, typeof conv.summary === "string" && conv.summary !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre" }, conv.summary) : null);
|
|
1015
|
-
}
|
|
1016
|
-
if (Array.isArray(conv.content)) {
|
|
1017
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement(RawBlocks, { blocks: conv.content }));
|
|
1018
|
-
}
|
|
1019
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
|
|
1020
|
-
}
|
|
1021
|
-
function makeContextBrowser(kit, StackedBar) {
|
|
1022
|
-
const { t, fmt: fmt3, fmtTime: fmtTime2, catLabel } = kit;
|
|
1023
|
-
const nodeText = makeNodeText(kit);
|
|
1024
|
-
const catColor = {};
|
|
1025
|
-
for (const c of CATS) catColor[c.key] = c.color;
|
|
1026
|
-
const MAX_AUTO_PAGES = 20;
|
|
1027
|
-
return function ContextBrowser(props) {
|
|
1028
|
-
var _a;
|
|
1029
|
-
const { data, headers } = props;
|
|
1030
|
-
const [sel, setSel] = React.useState("live");
|
|
1031
|
-
const [openCat, setOpenCat] = React.useState(null);
|
|
1032
|
-
const [openElem, setOpenElem] = React.useState(null);
|
|
1033
|
-
const convNodes = typeof props.useSession === "function" ? props.useSession((s) => s.nodes) : void 0;
|
|
1034
|
-
const bySeq = React.useMemo(() => {
|
|
1035
|
-
const m = /* @__PURE__ */ new Map();
|
|
1036
|
-
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
1037
|
-
return m;
|
|
1038
|
-
}, [convNodes]);
|
|
1039
|
-
const hasMore = typeof props.useSession === "function" ? props.useSession((s) => s.hasMore === true) : false;
|
|
1040
|
-
const loadingOlder = typeof props.useSession === "function" ? props.useSession((s) => s.loadingOlder === true) : false;
|
|
1041
|
-
const openSeq = openElem !== null && openElem.startsWith("n") ? Number(openElem.slice(1)) : null;
|
|
1042
|
-
const missingSeq = openSeq !== null && !bySeq.has(openSeq) ? openSeq : null;
|
|
1043
|
-
const [exhausted, setExhausted] = React.useState(false);
|
|
1044
|
-
const pagesRef = React.useRef(0);
|
|
1045
|
-
React.useEffect(() => {
|
|
1046
|
-
pagesRef.current = 0;
|
|
1047
|
-
setExhausted(false);
|
|
1048
|
-
}, [openElem]);
|
|
1049
|
-
const loadOlderHistory = props.loadOlderHistory;
|
|
1050
|
-
React.useEffect(() => {
|
|
1051
|
-
if (missingSeq === null || !hasMore || loadingOlder || exhausted) return;
|
|
1052
|
-
if (loadOlderHistory === void 0) return;
|
|
1053
|
-
if (pagesRef.current >= MAX_AUTO_PAGES) {
|
|
1054
|
-
setExhausted(true);
|
|
1055
|
-
return;
|
|
1056
|
-
}
|
|
1057
|
-
pagesRef.current += 1;
|
|
1058
|
-
void loadOlderHistory();
|
|
1059
|
-
}, [missingSeq, hasMore, loadingOlder, exhausted, bySeq, loadOlderHistory]);
|
|
1060
|
-
React.useEffect(() => {
|
|
1061
|
-
if (!hasMore && missingSeq !== null && !exhausted) setExhausted(true);
|
|
1062
|
-
}, [hasMore, missingSeq, exhausted]);
|
|
1063
|
-
const pinSeq = props.pinSeq;
|
|
1064
|
-
React.useEffect(() => {
|
|
1065
|
-
setSel(pinSeq === null || pinSeq === void 0 ? "live" : pinSeq);
|
|
1066
|
-
setOpenCat(null);
|
|
1067
|
-
setOpenElem(null);
|
|
1068
|
-
}, [pinSeq]);
|
|
1069
|
-
const awaiting = missingSeq !== null && !exhausted && loadOlderHistory !== void 0 && hasMore;
|
|
1070
|
-
const requests = data.requests || [];
|
|
1071
|
-
const hoverReq = props.previewSeq !== null && props.previewSeq !== void 0 ? requests.find((r) => r.seq === props.previewSeq) ?? null : null;
|
|
1072
|
-
const req = hoverReq ?? (sel === "live" ? null : requests.find((r) => r.seq === sel) ?? null);
|
|
1073
|
-
const seq = req !== null ? req.seq : null;
|
|
1074
|
-
const linked = req === null && props.onHoverKey !== void 0;
|
|
1075
|
-
const linkKey = linked && props.hoverKey !== null && props.hoverKey !== "free" ? props.hoverKey : null;
|
|
1076
|
-
const view = assemble(data, headers, seq);
|
|
1077
|
-
const breakdown = req !== null ? req : data.current;
|
|
1078
|
-
const parts = partsOf(breakdown);
|
|
1079
|
-
const total = breakdown.total;
|
|
1080
|
-
const pick = (v) => {
|
|
1081
|
-
setSel(v === "live" ? "live" : Number(v));
|
|
1082
|
-
setOpenCat(null);
|
|
1083
|
-
setOpenElem(null);
|
|
1084
|
-
};
|
|
1085
|
-
const byCat = {};
|
|
1086
|
-
for (const n of view.nodes) (byCat[_a = n.cat] ?? (byCat[_a] = [])).push(n);
|
|
1087
|
-
const toolCount = (c) => {
|
|
1088
|
-
if (c === "system") return view.header !== null && view.header.system !== void 0 ? 1 : 0;
|
|
1089
|
-
if (c === "tools") return view.header !== null ? view.header.tools.length : 0;
|
|
1090
|
-
return byCat[c]?.length ?? 0;
|
|
1091
|
-
};
|
|
1092
|
-
const toggleCat = (c) => {
|
|
1093
|
-
const openable = toolCount(c) > 0 || (c === "system" || c === "tools") && view.header === null;
|
|
1094
|
-
if (!openable) return;
|
|
1095
|
-
setOpenCat(openCat === c ? null : c);
|
|
1096
|
-
setOpenElem(null);
|
|
1097
|
-
};
|
|
1098
|
-
const toggleElem = (key) => setOpenElem(openElem === key ? null : key);
|
|
1099
|
-
const elemRow = (key, tag, preview, tokens, time, body) => {
|
|
1100
|
-
const open = openElem === key;
|
|
1101
|
-
return /* @__PURE__ */ React.createElement("div", { key, className: "lc-br-elem" + (open ? " lc-br-elem-on" : "") }, /* @__PURE__ */ React.createElement("button", { type: "button", className: "lc-br-elem-row", onClick: () => {
|
|
1102
|
-
toggleElem(key);
|
|
1103
|
-
} }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "\u25B8"), tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, tag) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-preview" }, preview), time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-time" }, fmtTime2(time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "\u2248" + fmt3(tokens))), open ? body : null);
|
|
1104
|
-
};
|
|
1105
|
-
const catBody = (c) => {
|
|
1106
|
-
if (c === "system") {
|
|
1107
|
-
if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
|
|
1108
|
-
const system = view.header.system;
|
|
1109
|
-
if (system === void 0) return null;
|
|
1110
|
-
return elemRow(
|
|
1111
|
-
"sys",
|
|
1112
|
-
null,
|
|
1113
|
-
system.replace(/\s+/g, " ").trim().slice(0, 80),
|
|
1114
|
-
breakdown.system,
|
|
1115
|
-
void 0,
|
|
1116
|
-
/* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre" }, system))
|
|
1117
|
-
);
|
|
1118
|
-
}
|
|
1119
|
-
if (c === "tools") {
|
|
1120
|
-
if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
|
|
1121
|
-
return view.header.tools.map((tool) => {
|
|
1122
|
-
const schema = tool.schema !== void 0 ? JSON.stringify(tool.schema, null, 2) : "";
|
|
1123
|
-
return elemRow(
|
|
1124
|
-
"tool:" + tool.name,
|
|
1125
|
-
null,
|
|
1126
|
-
tool.name,
|
|
1127
|
-
tool.tokens,
|
|
1128
|
-
void 0,
|
|
1129
|
-
/* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, tool.description !== void 0 ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre" }, tool.description) : null, schema !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre lc-br-dim" }, schema) : null)
|
|
1130
|
-
);
|
|
1131
|
-
});
|
|
1132
|
-
}
|
|
1133
|
-
const nodes = byCat[c] ?? [];
|
|
1134
|
-
return nodes.map((n) => {
|
|
1135
|
-
let tag = null;
|
|
1136
|
-
let preview = nodeText(n);
|
|
1137
|
-
if (n.cat === "tool") {
|
|
1138
|
-
tag = (n.tool ?? "?") + (n.err ? " \u26A0" : "");
|
|
1139
|
-
preview = t("node.toolResult") + (n.err ? " \u26A0" : "");
|
|
1140
|
-
} else if (n.cat === "inject" && !n.skill) {
|
|
1141
|
-
tag = t("form." + (n.form || "context"));
|
|
1142
|
-
if (n.text !== void 0 && n.text !== "") {
|
|
1143
|
-
preview = n.form === "snapshot" ? t("node.snapshot") + n.text : n.text;
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
return elemRow(
|
|
1147
|
-
"n" + n.seq,
|
|
1148
|
-
tag,
|
|
1149
|
-
preview,
|
|
1150
|
-
n.tokens,
|
|
1151
|
-
n.time,
|
|
1152
|
-
/* @__PURE__ */ React.createElement(
|
|
1153
|
-
NodeContent,
|
|
1154
|
-
{
|
|
1155
|
-
node: n,
|
|
1156
|
-
conv: bySeq.get(n.seq),
|
|
1157
|
-
hint: bySeq.get(n.seq) === void 0 && awaiting ? t("browser.loading") : t("browser.noContent")
|
|
1158
|
-
}
|
|
1159
|
-
)
|
|
1160
|
-
);
|
|
1161
|
-
});
|
|
1162
|
-
};
|
|
1163
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("browser.title")), /* @__PURE__ */ React.createElement(
|
|
1164
|
-
"select",
|
|
1165
|
-
{
|
|
1166
|
-
className: "lc-br-pick",
|
|
1167
|
-
value: seq === null ? "live" : String(seq),
|
|
1168
|
-
onChange: (e) => {
|
|
1169
|
-
pick(e.target.value);
|
|
1170
|
-
}
|
|
1171
|
-
},
|
|
1172
|
-
/* @__PURE__ */ React.createElement("option", { value: "live" }, t("browser.live")),
|
|
1173
|
-
requests.slice().reverse().map((r) => /* @__PURE__ */ React.createElement("option", { key: r.seq, value: String(r.seq) }, t("detail.step", { t: r.turn ?? 0, s: r.step ?? 0 }) + " \xB7 " + fmtTime2(r.time)))
|
|
1174
|
-
)), /* @__PURE__ */ React.createElement("div", { className: "lc-br-meta" }, /* @__PURE__ */ React.createElement("b", null, req !== null ? t("detail.step", { t: req.turn ?? 0, s: req.step ?? 0 }) : t("browser.liveNow")), req !== null ? /* @__PURE__ */ React.createElement("span", null, fmtTime2(req.time)) : null, hoverReq !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("browser.preview")) : null, /* @__PURE__ */ React.createElement("span", null, t("detail.estTotal", { n: fmt3(total) })), req !== null && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt3(req.prompt) })) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-br-bar" }, /* @__PURE__ */ React.createElement(
|
|
1175
|
-
StackedBar,
|
|
1176
|
-
{
|
|
1177
|
-
parts,
|
|
1178
|
-
height: 10,
|
|
1179
|
-
hoverKey: linked ? linkKey : void 0,
|
|
1180
|
-
onHoverKey: linked ? props.onHoverKey : void 0,
|
|
1181
|
-
tip: false
|
|
1182
|
-
}
|
|
1183
|
-
)), view.missingLive > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.missingLive", { n: view.missingLive })) : null, view.approximate ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.approx")) : null, /* @__PURE__ */ React.createElement("div", { className: "lc-br-cats" }, CATS.map((c) => {
|
|
1184
|
-
const count = toolCount(c.key);
|
|
1185
|
-
const v = breakdown[c.key] || 0;
|
|
1186
|
-
const openable = count > 0 || (c.key === "system" || c.key === "tools") && view.header === null;
|
|
1187
|
-
const open = openCat === c.key && openable;
|
|
1188
|
-
return /* @__PURE__ */ React.createElement("div", { key: c.key, className: "lc-br-cat" + (openable ? "" : " lc-br-cat-empty") }, /* @__PURE__ */ React.createElement(
|
|
1189
|
-
"button",
|
|
1190
|
-
{
|
|
1191
|
-
type: "button",
|
|
1192
|
-
className: "lc-br-cat-row" + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
|
|
1193
|
-
onMouseEnter: linked ? () => {
|
|
1194
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(c.key);
|
|
1195
|
-
} : void 0,
|
|
1196
|
-
onMouseLeave: linked ? () => {
|
|
1197
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
1198
|
-
} : void 0,
|
|
1199
|
-
onClick: () => {
|
|
1200
|
-
toggleCat(c.key);
|
|
1201
|
-
}
|
|
1202
|
-
},
|
|
1203
|
-
/* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "\u25B8"),
|
|
1204
|
-
/* @__PURE__ */ React.createElement("i", { style: { background: c.color } }),
|
|
1205
|
-
/* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-label" }, catLabel(c.key)),
|
|
1206
|
-
/* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-count" }, t("browser.items", { n: count })),
|
|
1207
|
-
/* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "\u2248" + fmt3(v)),
|
|
1208
|
-
/* @__PURE__ */ React.createElement("span", { className: "lc-br-pct" }, total > 0 ? Math.round(v / total * 100) + "%" : "")
|
|
1209
|
-
), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-body" }, catBody(c.key)) : null);
|
|
1210
|
-
})));
|
|
1211
|
-
};
|
|
1212
|
-
}
|
|
1160
|
+
".lc-kind-model { background: color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent); color: var(--dsw-alias-brand-primary); }",
|
|
1161
|
+
".lc-event-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
|
|
1162
|
+
".lc-event-at { flex: none; color: var(--dsw-alias-label-secondary); font-size: 11px; white-space: nowrap; }",
|
|
1163
|
+
".lc-event-tokens { color: var(--dsw-alias-state-success-primary); font-weight: 600; white-space: nowrap; }",
|
|
1164
|
+
".lc-event-tokens.lc-up { color: var(--dsw-alias-state-warn-primary); }",
|
|
1165
|
+
".lc-event-time { color: var(--dsw-alias-label-secondary); font-size: 12px; }",
|
|
1166
|
+
".lc-node { display: flex; align-items: center; gap: 8px; padding: 3px 0; }",
|
|
1167
|
+
".lc-node-preview { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-primary); }",
|
|
1168
|
+
".lc-node-time { color: var(--dsw-alias-label-secondary); font-size: 12px; min-width: 54px; text-align: right; }",
|
|
1169
|
+
".lc-node-tokens { color: var(--dsw-alias-label-secondary); }",
|
|
1170
|
+
".lc-nodes-more { color: var(--dsw-alias-label-secondary); padding: 3px 0; }",
|
|
1171
|
+
".lc-empty { color: var(--dsw-alias-label-secondary); padding: 18px 0; text-align: center; }",
|
|
1172
|
+
".lc-foot { color: var(--dsw-alias-label-secondary); font-size: 12px; margin-top: 4px; }",
|
|
1173
|
+
// ---- /context modal (centered dialog; escapes the composer anchor via fixed positioning) ----
|
|
1174
|
+
".lc-modal-backdrop { position: fixed; inset: 0; z-index: 200; background: rgba(0, 0, 0, 0.45); display: flex; align-items: center; justify-content: center; }",
|
|
1175
|
+
".lc-modal-card { width: min(720px, calc(100vw - 48px)); max-height: min(82vh, 760px); overflow-y: auto; box-sizing: border-box; background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l1); border-radius: 12px; box-shadow: var(--dsw-shadow-lv3, 0 12px 32px rgba(0, 0, 0, 0.4)); padding: 16px 18px 18px; color: var(--dsw-alias-label-primary); font-size: 13px; }",
|
|
1176
|
+
".lc-modal-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 12px; }",
|
|
1177
|
+
".lc-modal-title { font-weight: 600; font-size: 14px; }",
|
|
1178
|
+
".lc-modal-close { margin-left: auto; border: 0; background: transparent; color: var(--dsw-alias-label-secondary); font-size: 18px; line-height: 1; padding: 2px 6px; border-radius: 6px; cursor: pointer; font-family: inherit; }",
|
|
1179
|
+
".lc-modal-close:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-2); }",
|
|
1180
|
+
// ---- Context browser card (progressive disclosure: category accordion ->
|
|
1181
|
+
// element rows -> per-kind content) ----
|
|
1182
|
+
// The δ-baseline caption sits at the card title's right edge, just left of
|
|
1183
|
+
// the step picker (it takes the row's auto margin the picker used to own).
|
|
1184
|
+
".lc-br-hint { margin-left: auto; color: var(--dsw-alias-label-secondary); font-size: 11px; font-weight: 400; white-space: nowrap; }",
|
|
1185
|
+
".lc-br-pick { font: inherit; font-size: 12px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; padding: 3px 6px; max-width: 240px; }",
|
|
1186
|
+
".lc-br-meta { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-bottom: 8px; color: var(--dsw-alias-label-secondary); }",
|
|
1187
|
+
".lc-br-meta b { color: var(--dsw-alias-label-primary); }",
|
|
1188
|
+
".lc-br-meta .lc-actual { color: var(--dsw-alias-state-success-primary); }",
|
|
1189
|
+
".lc-br-note { margin-top: 8px; color: var(--dsw-alias-label-secondary); font-size: 12px; }",
|
|
1190
|
+
".lc-br-cats { margin-top: 10px; display: flex; flex-direction: column; gap: 4px; }",
|
|
1191
|
+
".lc-br-cat { border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; background: var(--dsw-alias-bg-layer-2); overflow: hidden; }",
|
|
1192
|
+
".lc-br-cat-empty { opacity: 0.55; }",
|
|
1193
|
+
".lc-br-cat-row { display: flex; align-items: center; gap: 8px; width: 100%; border: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; padding: 7px 10px; cursor: pointer; text-align: left; transition: background-color 120ms ease; }",
|
|
1194
|
+
".lc-br-cat-row:hover { background: var(--dsw-alias-interactive-bg-hover); }",
|
|
1195
|
+
// Current-composition hover echo: the category row lit by the shared hover
|
|
1196
|
+
// link (hovering the Current Composition card's bar/legend) looks hovered
|
|
1197
|
+
// too — same tint as the physical hover, plus a ring on its color dot.
|
|
1198
|
+
".lc-br-cat-on { background: var(--dsw-alias-interactive-bg-hover); }",
|
|
1199
|
+
".lc-br-cat-on i { box-shadow: 0 0 0 1px var(--dsw-alias-brand-primary); }",
|
|
1200
|
+
".lc-br-cat-row i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; flex: none; transition: box-shadow 120ms ease; }",
|
|
1201
|
+
".lc-br-cat-label { font-weight: 600; }",
|
|
1202
|
+
".lc-br-cat-count { color: var(--dsw-alias-label-secondary); font-size: 12px; white-space: nowrap; }",
|
|
1203
|
+
// Count + Δ pill form one attached group: the group absorbs the row's free
|
|
1204
|
+
// space (tokens/percent stay right-aligned) with a tight 4px inner gap so
|
|
1205
|
+
// the pill hugs the item count instead of standing at the row's 8px rhythm.
|
|
1206
|
+
".lc-br-count-grp { flex: 1; min-width: 0; display: inline-flex; align-items: center; gap: 4px; }",
|
|
1207
|
+
".lc-br-chev { flex: none; width: 12px; color: var(--dsw-alias-label-secondary); transition: transform 120ms ease; }",
|
|
1208
|
+
".lc-br-chev-on { transform: rotate(90deg); }",
|
|
1209
|
+
".lc-br-tokens { flex: none; color: var(--dsw-alias-label-secondary); font-size: 12px; }",
|
|
1210
|
+
// Δ pills on a category row: count and token swings vs the previous turn's
|
|
1211
|
+
// last step, tinted by direction (added = success, freed = error — the same
|
|
1212
|
+
// language as the events card's kind chips).
|
|
1213
|
+
".lc-br-delta, .lc-br-tdelta { flex: none; font-size: 11px; font-weight: 600; padding: 0 5px; border-radius: 4px; white-space: nowrap; }",
|
|
1214
|
+
".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); }",
|
|
1215
|
+
".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); }",
|
|
1216
|
+
// The token Δ pill hugs the LEFT of the token figure (tight 4px gap, like
|
|
1217
|
+
// the count group) while the figure itself keeps its right-aligned slot.
|
|
1218
|
+
".lc-br-tokens-grp { flex: none; min-width: 0; display: inline-flex; align-items: center; gap: 4px; }",
|
|
1219
|
+
".lc-br-pct { flex: none; width: 36px; text-align: right; color: var(--dsw-alias-label-secondary); font-size: 12px; }",
|
|
1220
|
+
".lc-br-body { border-top: 1px solid var(--dsw-alias-border-l1); padding: 4px 6px; display: flex; flex-direction: column; gap: 2px; }",
|
|
1221
|
+
".lc-br-elem { border-radius: 6px; }",
|
|
1222
|
+
".lc-br-elem-on { background: var(--dsw-alias-interactive-bg-active); }",
|
|
1223
|
+
".lc-br-elem-row { display: flex; align-items: center; gap: 8px; width: 100%; border: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; padding: 5px 6px; cursor: pointer; text-align: left; border-radius: 6px; transition: background-color 120ms ease; }",
|
|
1224
|
+
".lc-br-elem-row:hover { background: var(--dsw-alias-interactive-bg-hover); }",
|
|
1225
|
+
".lc-br-tag { flex: none; font-size: 11px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l1); border-radius: 4px; padding: 0 6px; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
|
|
1226
|
+
".lc-br-preview { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
|
|
1227
|
+
".lc-br-time { flex: none; color: var(--dsw-alias-label-secondary); font-size: 11px; }",
|
|
1228
|
+
".lc-br-content { padding: 2px 6px 8px 26px; display: flex; flex-direction: column; gap: 6px; }",
|
|
1229
|
+
".lc-br-pre { margin: 0; padding: 8px 10px; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; color: var(--dsw-alias-label-primary); font-size: 12px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; max-height: 320px; overflow-y: auto; scrollbar-width: thin; }",
|
|
1230
|
+
".lc-br-dim { color: var(--dsw-alias-label-secondary); }",
|
|
1231
|
+
".lc-br-call { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }",
|
|
1232
|
+
// ---- Tool schema body (browser card, tools category). The description
|
|
1233
|
+
// and parameter table share one "card with a small title" chrome — a
|
|
1234
|
+
// labeled bordered block so a reader can scan three stacked sections
|
|
1235
|
+
// without losing track of which is which. The raw JSON sits below as a
|
|
1236
|
+
// collapsed toggle (no card chrome — it's a debug view, not a primary
|
|
1237
|
+
// section). ----
|
|
1238
|
+
".lc-ts-card { background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; overflow: hidden; }",
|
|
1239
|
+
".lc-ts-card-head { display: flex; align-items: center; gap: 8px; padding: 6px 10px; font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary); border-bottom: 1px solid var(--dsw-alias-border-l1); }",
|
|
1240
|
+
".lc-ts-card-head b { color: var(--dsw-alias-label-primary); }",
|
|
1241
|
+
".lc-ts-card-count { margin-left: auto; }",
|
|
1242
|
+
".lc-ts-desc-body { margin: 0; padding: 8px 10px; color: var(--dsw-alias-label-primary); font-size: 12px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; max-height: 160px; overflow-y: auto; scrollbar-width: thin; }",
|
|
1243
|
+
// Grid: name + type + required chip + description. Columns wrap on narrow
|
|
1244
|
+
// rows; the description cell spans the full second row so a long blurb
|
|
1245
|
+
// never breaks the one-line rhythm of name/type/required.
|
|
1246
|
+
".lc-ts-param-row { display: grid; grid-template-columns: 140px 90px 56px 1fr; column-gap: 10px; row-gap: 2px; padding: 6px 10px; border-top: 1px solid var(--dsw-alias-border-l1); font-size: 12px; align-items: baseline; }",
|
|
1247
|
+
".lc-ts-param-row:first-of-type { border-top: 0; }",
|
|
1248
|
+
".lc-ts-param-name { font-weight: 600; color: var(--dsw-alias-label-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
|
|
1249
|
+
".lc-ts-param-type { color: var(--dsw-alias-label-secondary); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
|
|
1250
|
+
".lc-ts-param-req { color: var(--dsw-alias-state-warn-primary); font-size: 11px; font-weight: 600; }",
|
|
1251
|
+
".lc-ts-param-req-off { color: var(--dsw-alias-label-secondary); font-size: 11px; }",
|
|
1252
|
+
".lc-ts-param-desc { grid-column: 1 / -1; color: var(--dsw-alias-label-secondary); line-height: 1.5; }",
|
|
1253
|
+
".lc-ts-params-empty { padding: 8px 10px; color: var(--dsw-alias-label-secondary); font-size: 12px; }",
|
|
1254
|
+
// Raw JSON toggle: a slim button that owns the open/closed state, sitting
|
|
1255
|
+
// just under the parameter table. The <pre> itself only mounts when open
|
|
1256
|
+
// (avoids stringifying the schema on every render and keeps the default
|
|
1257
|
+
// body compact).
|
|
1258
|
+
".lc-ts-json { display: flex; flex-direction: column; gap: 4px; }",
|
|
1259
|
+
".lc-ts-json-toggle { align-self: flex-start; border: 0; background: transparent; color: var(--dsw-alias-label-secondary); font: inherit; font-size: 11px; padding: 0; cursor: pointer; }",
|
|
1260
|
+
".lc-ts-json-toggle:hover { color: var(--dsw-alias-label-primary); text-decoration: underline; }"
|
|
1261
|
+
].join("\n");
|
|
1213
1262
|
|
|
1214
1263
|
// src/client/components/events.tsx
|
|
1215
1264
|
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
@@ -1288,7 +1337,7 @@ function makeEventList(kit) {
|
|
|
1288
1337
|
|
|
1289
1338
|
// src/client/meta.ts
|
|
1290
1339
|
var PLUGIN_NAME = "dsh-context";
|
|
1291
|
-
var PLUGIN_VERSION = true ? "0.
|
|
1340
|
+
var PLUGIN_VERSION = true ? "0.17.0" : "0.0.0-dev";
|
|
1292
1341
|
var PLUGIN_REPO = true ? "https://github.com/bowenliang123/dsh-context" : "https://github.com/bowenliang123/dsh-context";
|
|
1293
1342
|
var PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
|
|
1294
1343
|
|
|
@@ -1340,7 +1389,30 @@ function makePluginInfo(kit) {
|
|
|
1340
1389
|
};
|
|
1341
1390
|
}
|
|
1342
1391
|
|
|
1392
|
+
// src/client/components/requestDetail.tsx
|
|
1393
|
+
function makeRequestDetail(kit, StackedBar) {
|
|
1394
|
+
const { t, fmt: fmt3, fmtTime: fmtTime2, catLabel, eventLabel, eventAt } = kit;
|
|
1395
|
+
return function RequestDetail(props) {
|
|
1396
|
+
const req = props.request;
|
|
1397
|
+
if (!req) return null;
|
|
1398
|
+
const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
|
|
1399
|
+
const head = isTurn ? t("detail.turn", { t: req.turn ?? 0, n: req.stepCount ?? 0 }) : t("detail.step", { t: req.turn ?? 0, s: req.step ?? 0 });
|
|
1400
|
+
const marker = props.marker ?? null;
|
|
1401
|
+
const markerAt = marker !== null ? eventAt(marker) : null;
|
|
1402
|
+
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", { className: "lc-detail-marker", title: eventLabel(marker) }, "\u2702 " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, /* @__PURE__ */ React.createElement("span", null, fmtTime2(req.time)), /* @__PURE__ */ React.createElement("span", null, t("detail.estTotal", { n: fmt3(req.total) })), req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt3(req.prompt) })) : null, req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", null, t("detail.output", { n: fmt3(req.output) })) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: partsOf(req), height: 10 }), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c) => {
|
|
1403
|
+
const v = req[c.key] || 0;
|
|
1404
|
+
return /* @__PURE__ */ React.createElement("div", { key: c.key, className: "lc-detail-row" }, /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-label" }, catLabel(c.key)), /* @__PURE__ */ React.createElement("span", { className: "lc-bar-track" }, /* @__PURE__ */ React.createElement("span", { className: "lc-bar-fill", style: { width: (req.total > 0 ? v / req.total * 100 : 0) + "%", background: c.color } })), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" }, "\u2248" + fmt3(v)), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-pct" }, req.total > 0 ? Math.round(v / req.total * 100) + "%" : ""));
|
|
1405
|
+
})));
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1343
1409
|
// src/client/components/statsBoard.tsx
|
|
1410
|
+
function cacheHitPercent(usage) {
|
|
1411
|
+
const billed = usage.uncachedInputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
|
|
1412
|
+
if (billed === 0) return null;
|
|
1413
|
+
const hundredths = Math.trunc(usage.cacheReadTokens / billed * 1e4 + 1e-9);
|
|
1414
|
+
return `${Math.floor(hundredths / 100)}.${String(hundredths % 100).padStart(2, "0")}`;
|
|
1415
|
+
}
|
|
1344
1416
|
function makeStatsBoard(kit) {
|
|
1345
1417
|
const { t, fmt: fmt3 } = kit;
|
|
1346
1418
|
return function StatsBoard(props) {
|
|
@@ -1355,8 +1427,187 @@ function makeStatsBoard(kit) {
|
|
|
1355
1427
|
else if (ev.kind === "prune") prunes++;
|
|
1356
1428
|
else if (ev.kind === "inject") injects++;
|
|
1357
1429
|
}
|
|
1430
|
+
const hit = props.usage !== null ? cacheHitPercent(props.usage) : null;
|
|
1358
1431
|
const cell = (label, value) => /* @__PURE__ */ React.createElement("div", { className: "lc-stat" }, /* @__PURE__ */ React.createElement("span", { className: "lc-stat-label" }, label), /* @__PURE__ */ React.createElement("b", { className: "lc-stat-value" }, value));
|
|
1359
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col lc-col-stats" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("stats.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("stats.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-stats" }, cell(t("stats.turns"), fmt3(turns.size)), cell(t("stats.steps"), fmt3(steps)), cell(t("stats.injects"), fmt3(injects)), cell(t("stats.compactions"), fmt3(compactions)), cell(t("stats.prunes"), fmt3(prunes))));
|
|
1432
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col lc-col-stats" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("stats.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("stats.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-stats" }, cell(t("stats.turns"), fmt3(turns.size)), cell(t("stats.steps"), fmt3(steps)), cell(t("stats.injects"), fmt3(injects)), cell(t("stats.compactions"), fmt3(compactions)), cell(t("stats.prunes"), fmt3(prunes)), cell(t("stats.cacheHit"), hit === null ? "\u2014" : hit + "%")));
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// src/client/components/trendChart.tsx
|
|
1437
|
+
function aggregateByTurn(requests) {
|
|
1438
|
+
const out = [];
|
|
1439
|
+
let runSteps = 0;
|
|
1440
|
+
for (const req of requests) {
|
|
1441
|
+
const last = out.length > 0 ? out[out.length - 1] : null;
|
|
1442
|
+
if (last !== null && (last.turn ?? 0) === (req.turn ?? 0)) {
|
|
1443
|
+
runSteps++;
|
|
1444
|
+
out[out.length - 1] = { ...req, stepCount: runSteps };
|
|
1445
|
+
} else {
|
|
1446
|
+
runSteps = 1;
|
|
1447
|
+
out.push({ ...req, stepCount: 1 });
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
return out;
|
|
1451
|
+
}
|
|
1452
|
+
function attachMarkers(requests, events) {
|
|
1453
|
+
const markers = new Array(requests.length);
|
|
1454
|
+
for (const ev of events) {
|
|
1455
|
+
if (ev.kind !== "compaction" && ev.kind !== "prune") continue;
|
|
1456
|
+
for (let r = 0; r < requests.length; r++) {
|
|
1457
|
+
if (requests[r].seq >= ev.seq) {
|
|
1458
|
+
if (markers[r] === void 0) markers[r] = ev;
|
|
1459
|
+
break;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
return markers;
|
|
1464
|
+
}
|
|
1465
|
+
function makeTrendChart(kit) {
|
|
1466
|
+
const { t, fmt: fmt3, fmtTime: fmtTime2, catLabel, eventLabel, eventAt } = kit;
|
|
1467
|
+
const CHART_H = 112;
|
|
1468
|
+
const BAR_W = 14;
|
|
1469
|
+
const BAR_GAP = 2;
|
|
1470
|
+
const TURN_FILLS = ["rgba(128,128,128,0.12)", "rgba(128,128,128,0.26)"];
|
|
1471
|
+
return function TrendChart(props) {
|
|
1472
|
+
const requests = props.requests;
|
|
1473
|
+
const markers = props.markers;
|
|
1474
|
+
const anchorOf = (req) => typeof req.prompt === "number" && req.prompt > 0 && req.total > 0 ? req.prompt / req.total : 1;
|
|
1475
|
+
const barTotalOf = (req) => typeof req.prompt === "number" && req.prompt > 0 ? req.prompt : req.total;
|
|
1476
|
+
let maxTotal = 1;
|
|
1477
|
+
for (const req of requests) {
|
|
1478
|
+
const bt = barTotalOf(req);
|
|
1479
|
+
if (bt > maxTotal) maxTotal = bt;
|
|
1480
|
+
}
|
|
1481
|
+
const groups = [];
|
|
1482
|
+
for (const req of requests) {
|
|
1483
|
+
let grp = groups.length > 0 ? groups[groups.length - 1] : null;
|
|
1484
|
+
if (grp === null || grp.turn !== (req.turn ?? 0)) {
|
|
1485
|
+
grp = { turn: req.turn ?? 0, count: 0, span: 0, agg: req.stepCount !== void 0 };
|
|
1486
|
+
groups.push(grp);
|
|
1487
|
+
}
|
|
1488
|
+
grp.count++;
|
|
1489
|
+
grp.span += req.stepCount ?? 1;
|
|
1490
|
+
}
|
|
1491
|
+
const scrollRef = React.useRef(null);
|
|
1492
|
+
const scrolledOnce = React.useRef(false);
|
|
1493
|
+
const lastGranRef = React.useRef(props.granularity);
|
|
1494
|
+
const [edges, setEdges] = React.useState({ left: false, right: false });
|
|
1495
|
+
const edgesRef = React.useRef(edges);
|
|
1496
|
+
const updateEdges = (el) => {
|
|
1497
|
+
const left = el.scrollLeft > 4;
|
|
1498
|
+
const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 4;
|
|
1499
|
+
const prev = edgesRef.current;
|
|
1500
|
+
if (prev.left === left && prev.right === right) return;
|
|
1501
|
+
edgesRef.current = { left, right };
|
|
1502
|
+
setEdges({ left, right });
|
|
1503
|
+
};
|
|
1504
|
+
React.useLayoutEffect(() => {
|
|
1505
|
+
const el = scrollRef.current;
|
|
1506
|
+
if (el === null) return;
|
|
1507
|
+
if (props.granularity !== lastGranRef.current) {
|
|
1508
|
+
lastGranRef.current = props.granularity;
|
|
1509
|
+
scrolledOnce.current = false;
|
|
1510
|
+
}
|
|
1511
|
+
if (!scrolledOnce.current) {
|
|
1512
|
+
scrolledOnce.current = true;
|
|
1513
|
+
el.scrollLeft = el.scrollWidth;
|
|
1514
|
+
} else if (el.scrollLeft + el.clientWidth >= el.scrollWidth - 24) {
|
|
1515
|
+
el.scrollLeft = el.scrollWidth;
|
|
1516
|
+
}
|
|
1517
|
+
updateEdges(el);
|
|
1518
|
+
});
|
|
1519
|
+
const tipOf = (req) => {
|
|
1520
|
+
const head = req.stepCount !== void 0 && req.stepCount > 1 ? t("tip.turn", { t: req.turn ?? 0, n: req.stepCount }) : t("tip.step", { t: req.turn ?? 0, s: req.step ?? 0 });
|
|
1521
|
+
return head + " \xB7 " + fmtTime2(req.time) + " \xB7 " + t("tip.total", { n: fmt3(req.total) }) + (req.prompt !== void 0 ? " \xB7 " + t("tip.actual", { n: fmt3(req.prompt) }) : "");
|
|
1522
|
+
};
|
|
1523
|
+
const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
|
|
1524
|
+
const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
|
|
1525
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-chartrow" }, /* @__PURE__ */ React.createElement("div", { className: "lc-axis" }, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, fmt3(maxTotal)), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-mid" }, fmt3(Math.round(maxTotal / 2))), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, "0")), /* @__PURE__ */ React.createElement(
|
|
1526
|
+
"div",
|
|
1527
|
+
{
|
|
1528
|
+
className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
|
|
1529
|
+
ref: scrollRef,
|
|
1530
|
+
onScroll: (e) => {
|
|
1531
|
+
updateEdges(e.currentTarget);
|
|
1532
|
+
}
|
|
1533
|
+
},
|
|
1534
|
+
edges.left ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-l" }) : null,
|
|
1535
|
+
/* @__PURE__ */ React.createElement(
|
|
1536
|
+
"div",
|
|
1537
|
+
{
|
|
1538
|
+
className: "lc-chart",
|
|
1539
|
+
onMouseLeave: () => {
|
|
1540
|
+
props.onHover(null);
|
|
1541
|
+
}
|
|
1542
|
+
},
|
|
1543
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-grid lc-grid-top" }),
|
|
1544
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-grid lc-grid-mid" }),
|
|
1545
|
+
requests.map((req, i) => {
|
|
1546
|
+
const marker = markers[i];
|
|
1547
|
+
const markerAt = marker !== void 0 ? eventAt(marker) : null;
|
|
1548
|
+
const selected = props.selectedSeq === req.seq;
|
|
1549
|
+
const hovered = props.hoveredSeq === req.seq;
|
|
1550
|
+
const inTurn = props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn;
|
|
1551
|
+
return /* @__PURE__ */ React.createElement(
|
|
1552
|
+
"div",
|
|
1553
|
+
{
|
|
1554
|
+
key: req.seq,
|
|
1555
|
+
className: "lc-bar" + (selected ? " lc-bar-selected" : "") + (hovered ? " lc-bar-hovered" : "") + (inTurn ? " lc-bar-in-turn" : ""),
|
|
1556
|
+
style: { width: BAR_W + "px" },
|
|
1557
|
+
onClick: () => {
|
|
1558
|
+
props.onSelect(selected ? null : req.seq);
|
|
1559
|
+
},
|
|
1560
|
+
onMouseEnter: () => {
|
|
1561
|
+
props.onHover(req.seq);
|
|
1562
|
+
}
|
|
1563
|
+
},
|
|
1564
|
+
marker !== void 0 ? /* @__PURE__ */ React.createElement(
|
|
1565
|
+
"span",
|
|
1566
|
+
{
|
|
1567
|
+
className: "lc-bar-marker",
|
|
1568
|
+
title: "\u2702 " + (markerAt !== null ? markerAt + " \u2014 " : "") + eventLabel(marker)
|
|
1569
|
+
},
|
|
1570
|
+
"\u2702"
|
|
1571
|
+
) : null,
|
|
1572
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-bar-stack" }, CATS.map((c) => {
|
|
1573
|
+
const v = (req[c.key] || 0) * anchorOf(req);
|
|
1574
|
+
if (!v) return null;
|
|
1575
|
+
return /* @__PURE__ */ React.createElement("div", { key: c.key, style: { height: Math.max(1, Math.round(v / maxTotal * CHART_H)) + "px", background: c.color } });
|
|
1576
|
+
}))
|
|
1577
|
+
);
|
|
1578
|
+
})
|
|
1579
|
+
),
|
|
1580
|
+
hoveredReq !== null ? /* @__PURE__ */ React.createElement(
|
|
1581
|
+
"div",
|
|
1582
|
+
{
|
|
1583
|
+
className: "lc-chart-tip",
|
|
1584
|
+
style: { left: hoveredIdx * (BAR_W + BAR_GAP) + BAR_W / 2 + "px" }
|
|
1585
|
+
},
|
|
1586
|
+
tipOf(hoveredReq)
|
|
1587
|
+
) : null,
|
|
1588
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-turns", onMouseLeave: () => {
|
|
1589
|
+
props.onHoverTurn(null);
|
|
1590
|
+
} }, groups.map((grp, gi) => {
|
|
1591
|
+
const on = props.activeTurn === grp.turn;
|
|
1592
|
+
const blockW = grp.agg ? BAR_W : grp.span * (BAR_W + BAR_GAP) - BAR_GAP;
|
|
1593
|
+
return /* @__PURE__ */ React.createElement(
|
|
1594
|
+
"span",
|
|
1595
|
+
{
|
|
1596
|
+
key: "turn-" + gi,
|
|
1597
|
+
className: "lc-turn" + (on ? " lc-turn-on" : ""),
|
|
1598
|
+
style: {
|
|
1599
|
+
width: blockW + "px",
|
|
1600
|
+
background: TURN_FILLS[gi % TURN_FILLS.length]
|
|
1601
|
+
},
|
|
1602
|
+
title: "T" + grp.turn,
|
|
1603
|
+
onMouseEnter: () => {
|
|
1604
|
+
props.onHoverTurn(grp.turn);
|
|
1605
|
+
}
|
|
1606
|
+
},
|
|
1607
|
+
"T" + grp.turn
|
|
1608
|
+
);
|
|
1609
|
+
}))
|
|
1610
|
+
), edges.right ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-r" }) : null);
|
|
1360
1611
|
};
|
|
1361
1612
|
}
|
|
1362
1613
|
|
|
@@ -1364,9 +1615,10 @@ function makeStatsBoard(kit) {
|
|
|
1364
1615
|
var viewScroll = /* @__PURE__ */ new Map();
|
|
1365
1616
|
var EVENT_KINDS = ["inject", "compaction", "prune", "model"];
|
|
1366
1617
|
function makeContextView(ctx, kit) {
|
|
1367
|
-
const { t
|
|
1618
|
+
const { t } = kit;
|
|
1368
1619
|
const StackedBar = makeStackedBar(kit);
|
|
1369
1620
|
const Legend = makeLegend(kit);
|
|
1621
|
+
const CurrentComposition = makeCurrentComposition(kit, StackedBar, Legend);
|
|
1370
1622
|
const TrendChart = makeTrendChart(kit);
|
|
1371
1623
|
const RequestDetail = makeRequestDetail(kit, StackedBar);
|
|
1372
1624
|
const EventList = makeEventList(kit);
|
|
@@ -1378,6 +1630,7 @@ function makeContextView(ctx, kit) {
|
|
|
1378
1630
|
const sessionId = props.sessionId;
|
|
1379
1631
|
const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
|
|
1380
1632
|
const pressure = typeof props.useProjection === "function" ? contextPressureOf(props.useProjection("contextPressure")) : null;
|
|
1633
|
+
const usage = typeof props.useProjection === "function" ? tokenUsageOf(props.useProjection("tokenUsage")) : null;
|
|
1381
1634
|
const headers = typeof props.useProjection === "function" ? headersOf(props.useProjection("contextHeaders")) : null;
|
|
1382
1635
|
const [selectedSeq, setSelectedSeq] = React.useState(null);
|
|
1383
1636
|
const [hoveredSeq, setHoveredSeq] = React.useState(null);
|
|
@@ -1393,6 +1646,10 @@ function makeContextView(ctx, kit) {
|
|
|
1393
1646
|
return p.length === 1 ? [...EVENT_KINDS] : p.filter((x) => x !== k);
|
|
1394
1647
|
});
|
|
1395
1648
|
};
|
|
1649
|
+
const [toolFocus, setToolFocus] = React.useState(null);
|
|
1650
|
+
const clearToolFocus = React.useCallback(() => {
|
|
1651
|
+
setToolFocus(null);
|
|
1652
|
+
}, []);
|
|
1396
1653
|
const rootRef = React.useRef(null);
|
|
1397
1654
|
const scrollerRef = React.useRef(null);
|
|
1398
1655
|
const restoredRef = React.useRef(null);
|
|
@@ -1448,9 +1705,17 @@ function makeContextView(ctx, kit) {
|
|
|
1448
1705
|
}
|
|
1449
1706
|
}
|
|
1450
1707
|
const head = headlineOf(data, pressure);
|
|
1451
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement("div", { className: "lc-cols lc-head" }, /* @__PURE__ */ React.createElement(StatsBoard, { requests, events }), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement(
|
|
1452
|
-
|
|
1453
|
-
|
|
1708
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement("div", { className: "lc-cols lc-head" }, /* @__PURE__ */ React.createElement(StatsBoard, { requests, events, usage }), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement(
|
|
1709
|
+
CurrentComposition,
|
|
1710
|
+
{
|
|
1711
|
+
head,
|
|
1712
|
+
subtitle: (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : ""),
|
|
1713
|
+
hoverKey: hoverCat,
|
|
1714
|
+
onHoverKey: setHoverCat,
|
|
1715
|
+
tools: data.toolList,
|
|
1716
|
+
onToolFocus: setToolFocus
|
|
1717
|
+
}
|
|
1718
|
+
), /* @__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-gran" }, /* @__PURE__ */ React.createElement(
|
|
1454
1719
|
"button",
|
|
1455
1720
|
{
|
|
1456
1721
|
className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
|
|
@@ -1492,7 +1757,9 @@ function makeContextView(ctx, kit) {
|
|
|
1492
1757
|
previewSeq: hoveredSeq,
|
|
1493
1758
|
pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
|
|
1494
1759
|
hoverKey: hoverCat,
|
|
1495
|
-
onHoverKey: setHoverCat
|
|
1760
|
+
onHoverKey: setHoverCat,
|
|
1761
|
+
toolFocus,
|
|
1762
|
+
onToolFocusHandled: clearToolFocus
|
|
1496
1763
|
}
|
|
1497
1764
|
))), /* @__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(
|
|
1498
1765
|
"button",
|