dsh-context 0.7.3 → 0.9.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 +4 -0
- package/lib/client.js +284 -439
- package/lib/index.js +301 -194
- package/package.json +9 -2
package/lib/client.js
CHANGED
|
@@ -19,6 +19,8 @@ var DICT_ZH = {
|
|
|
19
19
|
"overview.ofWindow": "tokens\uFF08\u7EA6 {p}%\uFF09",
|
|
20
20
|
"overview.estimate": "tokens\uFF08\u4F30\u7B97\uFF09",
|
|
21
21
|
"overview.free": "\u5269\u4F59\u7A97\u53E3",
|
|
22
|
+
"overview.splitEst": "\xB7 \u5206\u7C7B\u6309\u4F30\u7B97\u6BD4\u4F8B\u5206\u914D",
|
|
23
|
+
"overview.ofUsed": "\u5360\u5DF2\u7528\u4E0A\u4E0B\u6587",
|
|
22
24
|
"stats.title": "\u4E0A\u4E0B\u6587\u7EDF\u8BA1",
|
|
23
25
|
"stats.hint": "\u7EDF\u8BA1\u5F53\u524D\u4FDD\u7559\u7684\u5386\u53F2\u7A97\u53E3\uFF08\u4E0E\u8D8B\u52BF\u56FE\u4E00\u81F4\uFF09",
|
|
24
26
|
"stats.turns": "\u8F6E\u6B21",
|
|
@@ -88,6 +90,8 @@ var DICT_EN = {
|
|
|
88
90
|
"overview.ofWindow": "tokens (~{p}%)",
|
|
89
91
|
"overview.estimate": "tokens (estimated)",
|
|
90
92
|
"overview.free": "Free window",
|
|
93
|
+
"overview.splitEst": "\xB7 category split is estimated",
|
|
94
|
+
"overview.ofUsed": "of used context",
|
|
91
95
|
"stats.title": "Context stats",
|
|
92
96
|
"stats.hint": "Over the retained history window (same as the History chart)",
|
|
93
97
|
"stats.turns": "Turns",
|
|
@@ -165,11 +169,25 @@ var STYLES = [
|
|
|
165
169
|
".lc-overview-num b { font-size: 20px; }",
|
|
166
170
|
".lc-overview-num span { color: var(--dsw-alias-label-secondary); }",
|
|
167
171
|
".lc-stacked-wrap { position: relative; width: 100%; }",
|
|
168
|
-
".lc-stacked { display: flex; width: 100%; border-radius: 5px; overflow: hidden; background: rgba(128,128,128,0.18); }",
|
|
172
|
+
".lc-stacked { display: flex; width: 100%; border-radius: 5px; overflow: hidden; background: rgba(128,128,128,0.18); position: relative; }",
|
|
173
|
+
// Hover reference frame around the OCCUPIED region of the composition bar:
|
|
174
|
+
// dashes from the left edge to the used/window boundary, so the legend's
|
|
175
|
+
// "share of used" percentages visibly map to the boxed part (the free track
|
|
176
|
+
// sits outside it). pointer-events: none keeps hover on the segments/free.
|
|
177
|
+
// Deliberately high-contrast (label-primary) and thick so the frame reads at
|
|
178
|
+
// a glance, and the other parts dim underneath it (`.lc-stacked-dim`).
|
|
179
|
+
".lc-occupied-box { position: absolute; top: 0; bottom: 0; left: 0; border: 2px dashed var(--dsw-alias-label-primary); border-radius: 5px; box-sizing: border-box; pointer-events: none; opacity: 1; box-shadow: 0 0 0 1px var(--dsw-alias-bg-layer-2); }",
|
|
169
180
|
".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; }",
|
|
170
181
|
".lc-stacked > div { height: 100%; }",
|
|
171
182
|
".lc-stacked-seg-on { filter: brightness(1.18); }",
|
|
172
183
|
".lc-stacked-free-on { box-shadow: inset 0 0 0 1px var(--dsw-alias-label-secondary); border-radius: 3px; }",
|
|
184
|
+
// Hover focus: everything except the hovered part (segment, legend chip, or
|
|
185
|
+
// free track) recedes, so the composition highlight and the occupied-region
|
|
186
|
+
// frame read clearly. The selected segment/free keeps full opacity.
|
|
187
|
+
".lc-stacked-dim .lc-stacked-seg { opacity: 0.35; }",
|
|
188
|
+
".lc-stacked-dim .lc-stacked-seg-on { opacity: 1; }",
|
|
189
|
+
".lc-stacked-dim .lc-stacked-free { opacity: 0.35; }",
|
|
190
|
+
".lc-stacked-dim .lc-stacked-free-on { opacity: 1; }",
|
|
173
191
|
".lc-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; }",
|
|
174
192
|
".lc-chip { display: inline-flex; align-items: center; gap: 5px; color: var(--dsw-alias-label-primary); }",
|
|
175
193
|
".lc-chip i, .lc-detail-row i, .lc-node i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; }",
|
|
@@ -196,7 +214,14 @@ var STYLES = [
|
|
|
196
214
|
".lc-grid { position: absolute; left: 0; right: 0; border-top: 1px dashed var(--dsw-alias-border-l1); pointer-events: none; }",
|
|
197
215
|
".lc-grid-top { top: 18px; }",
|
|
198
216
|
".lc-grid-mid { top: 74px; }",
|
|
199
|
-
".lc-bar { position: relative; width: 14px; flex: none; height: 100%; display: flex; align-items: flex-end; cursor: pointer; border-radius: 2px; }",
|
|
217
|
+
".lc-bar { position: relative; width: 14px; flex: none; height: 100%; display: flex; align-items: flex-end; cursor: pointer; border-radius: 2px; transition: opacity 120ms ease; }",
|
|
218
|
+
// Turn-aware dimming: while a turn is focused, bars OUTSIDE the active
|
|
219
|
+
// turn fade to 35% and the whole current turn stays fully opaque.
|
|
220
|
+
".lc-chart-dim .lc-bar { opacity: 0.35; }",
|
|
221
|
+
".lc-chart-dim .lc-bar-in-turn { opacity: 1; }",
|
|
222
|
+
".lc-chart-dim .lc-turn { opacity: 0.35; }",
|
|
223
|
+
".lc-chart-dim .lc-turn-on { opacity: 1; }",
|
|
224
|
+
".lc-chart-tip { position: absolute; top: 0; 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; }",
|
|
200
225
|
".lc-bar:hover { background: var(--dsw-alias-bg-layer-2); }",
|
|
201
226
|
".lc-bar-selected { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }",
|
|
202
227
|
".lc-bar-hovered { outline: 1px dashed var(--dsw-alias-brand-primary); outline-offset: 1px; }",
|
|
@@ -205,7 +230,7 @@ var STYLES = [
|
|
|
205
230
|
".lc-bar-stack > div { width: 100%; }",
|
|
206
231
|
".lc-bar-marker { position: absolute; top: -16px; left: 50%; transform: translateX(-50%); font-size: 11px; color: var(--dsw-alias-state-warn-primary); }",
|
|
207
232
|
".lc-turns { display: flex; gap: 2px; width: max-content; min-width: 100%; margin-top: 4px; }",
|
|
208
|
-
".lc-turn { flex: none; box-sizing: border-box; text-align: center; font-size: 10px; line-height: 14px; font-weight: 600; color: #fff; border-radius: 3px; height: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: default; transition: filter 120ms; }",
|
|
233
|
+
".lc-turn { flex: none; box-sizing: border-box; text-align: center; font-size: 10px; line-height: 14px; font-weight: 600; color: #fff; border-radius: 3px; height: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: default; transition: filter 120ms, opacity 120ms; }",
|
|
209
234
|
".lc-turn-on { filter: brightness(1.35); box-shadow: 0 0 0 1px rgba(255,255,255,0.4); }",
|
|
210
235
|
".lc-detail { margin-top: 12px; border-top: 1px solid var(--dsw-alias-border-l1); padding-top: 12px; }",
|
|
211
236
|
".lc-detail-head { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-bottom: 8px; color: var(--dsw-alias-label-secondary); }",
|
|
@@ -241,20 +266,6 @@ var STYLES = [
|
|
|
241
266
|
".lc-foot { color: var(--dsw-alias-label-secondary); font-size: 12px; margin-top: 4px; }"
|
|
242
267
|
].join("\n");
|
|
243
268
|
|
|
244
|
-
// src/client/cache.ts
|
|
245
|
-
var MAX_CACHED_SESSIONS = 10;
|
|
246
|
-
var sessionCache = /* @__PURE__ */ new Map();
|
|
247
|
-
function cacheGet(sessionId) {
|
|
248
|
-
return sessionCache.get(sessionId);
|
|
249
|
-
}
|
|
250
|
-
function cachePut(sessionId, snapshot) {
|
|
251
|
-
sessionCache.set(sessionId, snapshot);
|
|
252
|
-
if (sessionCache.size > MAX_CACHED_SESSIONS) {
|
|
253
|
-
const oldest = sessionCache.keys().next().value;
|
|
254
|
-
if (oldest !== void 0) sessionCache.delete(oldest);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
269
|
// src/client/categories.ts
|
|
259
270
|
var CATS = [
|
|
260
271
|
{ key: "system", color: "#6366f1" },
|
|
@@ -269,6 +280,23 @@ function partsOf(breakdown) {
|
|
|
269
280
|
return { key: c.key, color: c.color, value: breakdown[c.key] || 0 };
|
|
270
281
|
});
|
|
271
282
|
}
|
|
283
|
+
function anchoredParts(parts, target) {
|
|
284
|
+
if (target === null || target <= 0) return parts;
|
|
285
|
+
let total = 0;
|
|
286
|
+
for (const p of parts) total += p.value;
|
|
287
|
+
if (total <= 0 || total === target) return parts;
|
|
288
|
+
const scale = target / total;
|
|
289
|
+
return parts.map((p) => ({ ...p, value: Math.round(p.value * scale) }));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/client/services.ts
|
|
293
|
+
function timelineOf(value) {
|
|
294
|
+
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
295
|
+
return value;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/client/components/events.tsx
|
|
299
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
272
300
|
|
|
273
301
|
// src/client/format.ts
|
|
274
302
|
function fmt(n) {
|
|
@@ -289,7 +317,7 @@ function fmtTime(t) {
|
|
|
289
317
|
var React = require("react");
|
|
290
318
|
var h = React.createElement;
|
|
291
319
|
|
|
292
|
-
// src/client/components/events.
|
|
320
|
+
// src/client/components/events.tsx
|
|
293
321
|
var EVENT_ICONS = { compaction: "\u2702", prune: "\u2702", inject: "\uFF0B", model: "\u21C4" };
|
|
294
322
|
function makeEventText(t, tr) {
|
|
295
323
|
function eventLabel(ev) {
|
|
@@ -325,34 +353,19 @@ function makeEventList(kit) {
|
|
|
325
353
|
const { t, fmt: fmt3, fmtTime: fmtTime2, eventLabel, eventAt } = kit;
|
|
326
354
|
return function EventList(props) {
|
|
327
355
|
if (props.events.length === 0) {
|
|
328
|
-
return
|
|
356
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
|
|
329
357
|
}
|
|
330
358
|
const sorted = props.events.slice().reverse();
|
|
331
|
-
return
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
return h(
|
|
338
|
-
"div",
|
|
339
|
-
{ key: ev.seq + "-" + i, className: "lc-event" },
|
|
340
|
-
h("span", { className: "lc-event-icon lc-event-" + ev.kind }, EVENT_ICONS[ev.kind] || "\u2022"),
|
|
341
|
-
h("span", { className: "lc-event-label", title: label }, label),
|
|
342
|
-
at !== null ? h("span", { className: "lc-event-at" }, at) : null,
|
|
343
|
-
ev.tokens ? h(
|
|
344
|
-
"span",
|
|
345
|
-
{ className: "lc-event-tokens" + (ev.kind === "inject" ? " lc-up" : " lc-down") },
|
|
346
|
-
(ev.kind === "inject" ? "+" : "\u2212") + fmt3(ev.tokens)
|
|
347
|
-
) : null,
|
|
348
|
-
h("span", { className: "lc-event-time" }, fmtTime2(ev.time))
|
|
349
|
-
);
|
|
350
|
-
})
|
|
351
|
-
);
|
|
359
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-events" }, sorted.map((ev, i) => {
|
|
360
|
+
const label = eventLabel(ev);
|
|
361
|
+
const at = eventAt(ev);
|
|
362
|
+
const glyph = ev.kind === "inject" ? /* @__PURE__ */ React.createElement(import_dsh_client_ui_primitives.IconPlusOutline16, null) : ev.kind === "model" ? /* @__PURE__ */ React.createElement(import_dsh_client_ui_primitives.IconBranchOutline16, null) : EVENT_ICONS[ev.kind] || "\u2022";
|
|
363
|
+
return /* @__PURE__ */ React.createElement("div", { key: ev.seq + "-" + i, className: "lc-event" }, /* @__PURE__ */ React.createElement("span", { className: "lc-event-icon lc-event-" + ev.kind }, glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-event-label", title: label }, label), at !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-at" }, at) : null, ev.tokens ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-tokens" + (ev.kind === "inject" ? " lc-up" : " lc-down") }, (ev.kind === "inject" ? "+" : "\u2212") + fmt3(ev.tokens)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-event-time" }, fmtTime2(ev.time)));
|
|
364
|
+
}));
|
|
352
365
|
};
|
|
353
366
|
}
|
|
354
367
|
|
|
355
|
-
// src/client/components/nodes.
|
|
368
|
+
// src/client/components/nodes.tsx
|
|
356
369
|
function makeNodeList(kit) {
|
|
357
370
|
const { t, tr, fmt: fmt3, fmtTime: fmtTime2 } = kit;
|
|
358
371
|
function nodeText(n) {
|
|
@@ -368,32 +381,19 @@ function makeNodeList(kit) {
|
|
|
368
381
|
}
|
|
369
382
|
return function NodeList(props) {
|
|
370
383
|
if (props.nodes.length === 0) {
|
|
371
|
-
return
|
|
384
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("nodes.empty"));
|
|
372
385
|
}
|
|
373
386
|
const catColor = {};
|
|
374
387
|
for (const c of CATS) catColor[c.key] = c.color;
|
|
375
388
|
const rows = props.nodes.slice().reverse();
|
|
376
|
-
return
|
|
377
|
-
|
|
378
|
-
{ className: "lc-
|
|
379
|
-
|
|
380
|
-
rows.map((n) => {
|
|
381
|
-
const text = nodeText(n);
|
|
382
|
-
return h(
|
|
383
|
-
"div",
|
|
384
|
-
{ key: n.seq, className: "lc-node" },
|
|
385
|
-
h("i", { style: { background: catColor[n.cat] || "#999" } }),
|
|
386
|
-
h("span", { className: "lc-node-preview", title: text }, text),
|
|
387
|
-
// Timestamp when the host event carried one.
|
|
388
|
-
typeof n.time === "number" ? h("span", { className: "lc-node-time" }, fmtTime2(n.time)) : null,
|
|
389
|
-
h("span", { className: "lc-node-tokens" }, fmt3(n.tokens))
|
|
390
|
-
);
|
|
391
|
-
})
|
|
392
|
-
);
|
|
389
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-nodes" }, props.dropped > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-nodes-more" }, tr("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)));
|
|
392
|
+
}));
|
|
393
393
|
};
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
// src/client/components/requestDetail.
|
|
396
|
+
// src/client/components/requestDetail.tsx
|
|
397
397
|
function makeRequestDetail(kit, StackedBar) {
|
|
398
398
|
const { t, tr, fmt: fmt3, fmtTime: fmtTime2, catLabel, eventLabel, eventAt } = kit;
|
|
399
399
|
return function RequestDetail(props) {
|
|
@@ -403,46 +403,14 @@ function makeRequestDetail(kit, StackedBar) {
|
|
|
403
403
|
const head = isTurn ? tr("detail.turn", { t: req.turn ?? 0, n: req.stepCount ?? 0 }) : tr("detail.step", { t: req.turn ?? 0, s: req.step ?? 0 });
|
|
404
404
|
const marker = props.marker ?? null;
|
|
405
405
|
const markerAt = marker !== null ? eventAt(marker) : null;
|
|
406
|
-
return
|
|
407
|
-
|
|
408
|
-
{ className: "lc-detail" },
|
|
409
|
-
|
|
410
|
-
"div",
|
|
411
|
-
{ className: "lc-detail-head" },
|
|
412
|
-
h("b", null, head),
|
|
413
|
-
marker !== null && markerAt !== null ? h("span", { className: "lc-detail-marker", title: eventLabel(marker) }, "\u2702 " + markerAt) : null,
|
|
414
|
-
isTurn ? h("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null,
|
|
415
|
-
h("span", null, fmtTime2(req.time)),
|
|
416
|
-
h("span", null, tr("detail.estTotal", { n: fmt3(req.total) })),
|
|
417
|
-
req.prompt !== void 0 ? h("span", { className: "lc-actual" }, tr("detail.actual", { n: fmt3(req.prompt) })) : null,
|
|
418
|
-
req.output !== void 0 ? h("span", null, tr("detail.output", { n: fmt3(req.output) })) : null
|
|
419
|
-
),
|
|
420
|
-
h(StackedBar, { parts: partsOf(req), height: 10 }),
|
|
421
|
-
h(
|
|
422
|
-
"div",
|
|
423
|
-
{ className: "lc-detail-rows" },
|
|
424
|
-
CATS.map((c) => {
|
|
425
|
-
const v = req[c.key] || 0;
|
|
426
|
-
return h(
|
|
427
|
-
"div",
|
|
428
|
-
{ key: c.key, className: "lc-detail-row" },
|
|
429
|
-
h("i", { style: { background: c.color } }),
|
|
430
|
-
h("span", { className: "lc-detail-label" }, catLabel(c.key)),
|
|
431
|
-
h(
|
|
432
|
-
"span",
|
|
433
|
-
{ className: "lc-bar-track" },
|
|
434
|
-
h("span", { className: "lc-bar-fill", style: { width: (req.total > 0 ? v / req.total * 100 : 0) + "%", background: c.color } })
|
|
435
|
-
),
|
|
436
|
-
h("span", { className: "lc-detail-num" }, fmt3(v)),
|
|
437
|
-
h("span", { className: "lc-detail-pct" }, req.total > 0 ? Math.round(v / req.total * 100) + "%" : "")
|
|
438
|
-
);
|
|
439
|
-
})
|
|
440
|
-
)
|
|
441
|
-
);
|
|
406
|
+
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, tr("detail.estTotal", { n: fmt3(req.total) })), req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, tr("detail.actual", { n: fmt3(req.prompt) })) : null, req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", null, tr("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) => {
|
|
407
|
+
const v = req[c.key] || 0;
|
|
408
|
+
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) + "%" : ""));
|
|
409
|
+
})));
|
|
442
410
|
};
|
|
443
411
|
}
|
|
444
412
|
|
|
445
|
-
// src/client/components/statsBoard.
|
|
413
|
+
// src/client/components/statsBoard.tsx
|
|
446
414
|
function makeStatsBoard(kit) {
|
|
447
415
|
const { t, tr, fmt: fmt3 } = kit;
|
|
448
416
|
return function StatsBoard(props) {
|
|
@@ -466,50 +434,26 @@ function makeStatsBoard(kit) {
|
|
|
466
434
|
} else if (ev.kind === "inject") injects++;
|
|
467
435
|
else if (ev.kind === "model") switches++;
|
|
468
436
|
}
|
|
469
|
-
const cell = (label, value, sub) =>
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
);
|
|
476
|
-
return h(
|
|
477
|
-
"div",
|
|
478
|
-
{ className: "lc-card" },
|
|
479
|
-
h(
|
|
480
|
-
"div",
|
|
481
|
-
{ className: "lc-card-title" },
|
|
482
|
-
t("stats.title"),
|
|
483
|
-
h("span", { className: "lc-card-sub" }, t("stats.hint"))
|
|
484
|
-
),
|
|
485
|
-
h(
|
|
486
|
-
"div",
|
|
487
|
-
{ className: "lc-stats" },
|
|
488
|
-
cell(t("stats.turns"), fmt3(turns.size)),
|
|
489
|
-
cell(t("stats.steps"), fmt3(steps)),
|
|
490
|
-
cell(
|
|
491
|
-
t("stats.recycled"),
|
|
492
|
-
recycled > 0 ? "\u2212" + fmt3(recycled) : "0",
|
|
493
|
-
tr("stats.recycleSub", { c: compactions, p: prunes })
|
|
494
|
-
),
|
|
495
|
-
cell(t("stats.injects"), fmt3(injects)),
|
|
496
|
-
cell(t("stats.switches"), fmt3(switches)),
|
|
497
|
-
cell(t("stats.est"), "\u2248 " + fmt3(est)),
|
|
498
|
-
cell(t("stats.actualPrompt"), fmt3(prompt)),
|
|
499
|
-
cell(t("stats.output"), fmt3(output))
|
|
500
|
-
)
|
|
501
|
-
);
|
|
437
|
+
const cell = (label, value, sub) => /* @__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), sub !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-stat-sub" }, sub) : null);
|
|
438
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, 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(
|
|
439
|
+
t("stats.recycled"),
|
|
440
|
+
recycled > 0 ? "\u2212" + fmt3(recycled) : "0",
|
|
441
|
+
tr("stats.recycleSub", { c: compactions, p: prunes })
|
|
442
|
+
), cell(t("stats.injects"), fmt3(injects)), cell(t("stats.switches"), fmt3(switches)), cell(t("stats.est"), "\u2248 " + fmt3(est)), cell(t("stats.actualPrompt"), fmt3(prompt)), cell(t("stats.output"), fmt3(output))));
|
|
502
443
|
};
|
|
503
444
|
}
|
|
504
445
|
|
|
505
|
-
// src/client/components/stackedBar.
|
|
446
|
+
// src/client/components/stackedBar.tsx
|
|
506
447
|
function makeStackedBar(kit) {
|
|
507
|
-
const { t, fmt: fmt3, catLabel } = kit;
|
|
448
|
+
const { t, tr, fmt: fmt3, catLabel } = kit;
|
|
508
449
|
return function StackedBar(props) {
|
|
509
450
|
let total = 0;
|
|
510
451
|
for (const p of props.parts) total += p.value;
|
|
511
452
|
const scale = props.max !== void 0 && props.max > total ? props.max : total;
|
|
512
453
|
const free = props.max !== void 0 && props.max > total ? props.max - total : 0;
|
|
454
|
+
const usedPct = scale > 0 ? total / scale * 100 : 0;
|
|
455
|
+
const hovering = props.hoverKey !== null && props.hoverKey !== void 0;
|
|
456
|
+
const showBox = free > 0 && hovering;
|
|
513
457
|
let tip = null;
|
|
514
458
|
if (props.hoverKey !== null && props.hoverKey !== void 0) {
|
|
515
459
|
if (props.hoverKey === "free" && free > 0) {
|
|
@@ -524,7 +468,9 @@ function makeStackedBar(kit) {
|
|
|
524
468
|
const pct = scale > 0 ? p.value / scale * 100 : 0;
|
|
525
469
|
if (p.key === props.hoverKey && p.value > 0) {
|
|
526
470
|
tip = {
|
|
527
|
-
|
|
471
|
+
// "(pct%)" is a share of the OCCUPIED total — the dashed box
|
|
472
|
+
// that appears on hover frames exactly this reference region.
|
|
473
|
+
text: catLabel(p.key) + " \u2248" + fmt3(p.value) + " (" + Math.round(p.value / total * 100) + "%) " + tr("overview.ofUsed"),
|
|
528
474
|
leftPct: Math.max(12, Math.min(acc + pct / 2, 88))
|
|
529
475
|
};
|
|
530
476
|
break;
|
|
@@ -533,75 +479,74 @@ function makeStackedBar(kit) {
|
|
|
533
479
|
}
|
|
534
480
|
}
|
|
535
481
|
}
|
|
536
|
-
return
|
|
482
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-stacked-wrap" }, /* @__PURE__ */ React.createElement(
|
|
537
483
|
"div",
|
|
538
|
-
{
|
|
539
|
-
|
|
540
|
-
"
|
|
541
|
-
{
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
return h("div", {
|
|
484
|
+
{
|
|
485
|
+
className: "lc-stacked" + (hovering ? " lc-stacked-dim" : ""),
|
|
486
|
+
style: { height: (props.height || 14) + "px" },
|
|
487
|
+
onMouseLeave: () => {
|
|
488
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
total > 0 ? props.parts.map((p) => {
|
|
492
|
+
if (!p.value) return null;
|
|
493
|
+
const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
|
|
494
|
+
return /* @__PURE__ */ React.createElement(
|
|
495
|
+
"div",
|
|
496
|
+
{
|
|
552
497
|
key: p.key,
|
|
553
498
|
className: "lc-stacked-seg" + (on ? " lc-stacked-seg-on" : ""),
|
|
554
499
|
style: { width: p.value / scale * 100 + "%", background: p.color },
|
|
555
500
|
onMouseEnter: () => {
|
|
556
501
|
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
557
502
|
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
|
|
503
|
+
}
|
|
504
|
+
);
|
|
505
|
+
}) : null,
|
|
506
|
+
free > 0 ? /* @__PURE__ */ React.createElement(
|
|
507
|
+
"div",
|
|
508
|
+
{
|
|
561
509
|
key: "free",
|
|
562
510
|
className: "lc-stacked-free" + (props.hoverKey === "free" ? " lc-stacked-free-on" : ""),
|
|
563
511
|
style: { width: free / scale * 100 + "%" },
|
|
564
512
|
onMouseEnter: () => {
|
|
565
513
|
if (props.onHoverKey !== void 0) props.onHoverKey("free");
|
|
566
514
|
}
|
|
567
|
-
}
|
|
568
|
-
),
|
|
569
|
-
|
|
570
|
-
);
|
|
515
|
+
}
|
|
516
|
+
) : null,
|
|
517
|
+
showBox ? /* @__PURE__ */ React.createElement("div", { className: "lc-occupied-box", style: { width: usedPct + "%" } }) : null
|
|
518
|
+
), tip ? /* @__PURE__ */ React.createElement("div", { className: "lc-bar-tip", style: { left: tip.leftPct + "%" } }, tip.text) : null);
|
|
571
519
|
};
|
|
572
520
|
}
|
|
573
521
|
function makeLegend(kit) {
|
|
574
|
-
const { fmt: fmt3, catLabel } = kit;
|
|
522
|
+
const { tr, fmt: fmt3, catLabel } = kit;
|
|
575
523
|
return function Legend(props) {
|
|
576
524
|
let total = 0;
|
|
577
525
|
for (const p of props.parts) total += p.value;
|
|
578
|
-
return
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
"
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
onMouseEnter: () => {
|
|
589
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
590
|
-
},
|
|
591
|
-
onMouseLeave: () => {
|
|
592
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
593
|
-
}
|
|
526
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-legend" }, props.parts.map((p) => {
|
|
527
|
+
const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
|
|
528
|
+
return /* @__PURE__ */ React.createElement(
|
|
529
|
+
"span",
|
|
530
|
+
{
|
|
531
|
+
key: p.key,
|
|
532
|
+
className: "lc-chip" + (on ? " lc-chip-on" : ""),
|
|
533
|
+
title: tr("overview.ofUsed"),
|
|
534
|
+
onMouseEnter: () => {
|
|
535
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
594
536
|
},
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
537
|
+
onMouseLeave: () => {
|
|
538
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
/* @__PURE__ */ React.createElement("i", { style: { background: p.color } }),
|
|
542
|
+
catLabel(p.key) + " \u2248" + fmt3(p.value),
|
|
543
|
+
total > 0 ? /* @__PURE__ */ React.createElement("em", null, Math.round(p.value / total * 100) + "%") : null
|
|
544
|
+
);
|
|
545
|
+
}));
|
|
601
546
|
};
|
|
602
547
|
}
|
|
603
548
|
|
|
604
|
-
// src/client/components/trendChart.
|
|
549
|
+
// src/client/components/trendChart.tsx
|
|
605
550
|
function aggregateByTurn(requests) {
|
|
606
551
|
const out = [];
|
|
607
552
|
let runSteps = 0;
|
|
@@ -639,8 +584,13 @@ function makeTrendChart(kit) {
|
|
|
639
584
|
return function TrendChart(props) {
|
|
640
585
|
const requests = props.requests;
|
|
641
586
|
const markers = props.markers;
|
|
587
|
+
const anchorOf = (req) => typeof req.prompt === "number" && req.prompt > 0 && req.total > 0 ? req.prompt / req.total : 1;
|
|
588
|
+
const barTotalOf = (req) => typeof req.prompt === "number" && req.prompt > 0 ? req.prompt : req.total;
|
|
642
589
|
let maxTotal = 1;
|
|
643
|
-
for (const req of requests)
|
|
590
|
+
for (const req of requests) {
|
|
591
|
+
const bt = barTotalOf(req);
|
|
592
|
+
if (bt > maxTotal) maxTotal = bt;
|
|
593
|
+
}
|
|
644
594
|
const groups = [];
|
|
645
595
|
for (const req of requests) {
|
|
646
596
|
let grp = groups.length > 0 ? groups[groups.length - 1] : null;
|
|
@@ -679,112 +629,103 @@ function makeTrendChart(kit) {
|
|
|
679
629
|
}
|
|
680
630
|
updateEdges(el);
|
|
681
631
|
});
|
|
682
|
-
|
|
632
|
+
const tipOf = (req) => {
|
|
633
|
+
const head = req.stepCount !== void 0 && req.stepCount > 1 ? tr("tip.turn", { t: req.turn ?? 0, n: req.stepCount }) : tr("tip.step", { t: req.turn ?? 0, s: req.step ?? 0 });
|
|
634
|
+
return head + " \xB7 " + fmtTime2(req.time) + " \xB7 " + tr("tip.total", { n: fmt3(req.total) }) + (req.prompt !== void 0 ? " \xB7 " + tr("tip.actual", { n: fmt3(req.prompt) }) : "");
|
|
635
|
+
};
|
|
636
|
+
const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
|
|
637
|
+
const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
|
|
638
|
+
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(
|
|
683
639
|
"div",
|
|
684
|
-
{
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
),
|
|
692
|
-
|
|
640
|
+
{
|
|
641
|
+
className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
|
|
642
|
+
ref: scrollRef,
|
|
643
|
+
onScroll: (e) => {
|
|
644
|
+
updateEdges(e.currentTarget);
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
edges.left ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-l" }) : null,
|
|
648
|
+
/* @__PURE__ */ React.createElement(
|
|
693
649
|
"div",
|
|
694
650
|
{
|
|
695
|
-
className: "lc-chart
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
updateEdges(e.currentTarget);
|
|
651
|
+
className: "lc-chart",
|
|
652
|
+
onMouseLeave: () => {
|
|
653
|
+
props.onHover(null);
|
|
699
654
|
}
|
|
700
655
|
},
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
const hovered = props.hoveredSeq === req.seq;
|
|
718
|
-
const inTurn = props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn;
|
|
719
|
-
const tip = (req.stepCount !== void 0 && req.stepCount > 1 ? tr("tip.turn", { t: req.turn ?? 0, n: req.stepCount }) : tr("tip.step", { t: req.turn ?? 0, s: req.step ?? 0 })) + " \xB7 " + fmtTime2(req.time) + "\n" + tr("tip.total", { n: fmt3(req.total) }) + (req.prompt !== void 0 ? tr("tip.actual", { n: fmt3(req.prompt) }) : "") + "\n" + CATS.map((c) => catLabel(c.key) + " " + fmt3(req[c.key] || 0)).join(" / ");
|
|
720
|
-
return h(
|
|
721
|
-
"div",
|
|
722
|
-
{
|
|
723
|
-
key: req.seq,
|
|
724
|
-
// Uniform column width in BOTH granularities: turn aggregates
|
|
725
|
-
// keep the same fixed width as step bars.
|
|
726
|
-
className: "lc-bar" + (selected ? " lc-bar-selected" : "") + (hovered ? " lc-bar-hovered" : "") + (inTurn ? " lc-bar-in-turn" : ""),
|
|
727
|
-
style: { width: BAR_W + "px" },
|
|
728
|
-
title: tip,
|
|
729
|
-
onClick: () => {
|
|
730
|
-
props.onSelect(selected ? null : req.seq);
|
|
731
|
-
},
|
|
732
|
-
onMouseEnter: () => {
|
|
733
|
-
props.onHover(req.seq);
|
|
734
|
-
}
|
|
656
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-grid lc-grid-top" }),
|
|
657
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-grid lc-grid-mid" }),
|
|
658
|
+
requests.map((req, i) => {
|
|
659
|
+
const marker = markers[i];
|
|
660
|
+
const markerAt = marker !== void 0 ? eventAt(marker) : null;
|
|
661
|
+
const selected = props.selectedSeq === req.seq;
|
|
662
|
+
const hovered = props.hoveredSeq === req.seq;
|
|
663
|
+
const inTurn = props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn;
|
|
664
|
+
return /* @__PURE__ */ React.createElement(
|
|
665
|
+
"div",
|
|
666
|
+
{
|
|
667
|
+
key: req.seq,
|
|
668
|
+
className: "lc-bar" + (selected ? " lc-bar-selected" : "") + (hovered ? " lc-bar-hovered" : "") + (inTurn ? " lc-bar-in-turn" : ""),
|
|
669
|
+
style: { width: BAR_W + "px" },
|
|
670
|
+
onClick: () => {
|
|
671
|
+
props.onSelect(selected ? null : req.seq);
|
|
735
672
|
},
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
673
|
+
onMouseEnter: () => {
|
|
674
|
+
props.onHover(req.seq);
|
|
675
|
+
}
|
|
676
|
+
},
|
|
677
|
+
marker !== void 0 ? /* @__PURE__ */ React.createElement(
|
|
678
|
+
"span",
|
|
679
|
+
{
|
|
739
680
|
className: "lc-bar-marker",
|
|
740
681
|
title: "\u2702 " + (markerAt !== null ? markerAt + " \u2014 " : "") + eventLabel(marker)
|
|
741
|
-
}, "\u2702") : null,
|
|
742
|
-
h(
|
|
743
|
-
"div",
|
|
744
|
-
{ className: "lc-bar-stack" },
|
|
745
|
-
CATS.map((c) => {
|
|
746
|
-
const v = req[c.key] || 0;
|
|
747
|
-
if (!v) return null;
|
|
748
|
-
return h("div", { key: c.key, style: { height: Math.max(1, Math.round(v / maxTotal * CHART_H)) + "px", background: c.color } });
|
|
749
|
-
})
|
|
750
|
-
)
|
|
751
|
-
);
|
|
752
|
-
})
|
|
753
|
-
),
|
|
754
|
-
// Turn strip: one COLOR BLOCK per turn, spanning exactly its bars'
|
|
755
|
-
// columns, so the partition reads at a glance and lines up with the
|
|
756
|
-
// steps above. Hovering a block highlights that turn's bars in the
|
|
757
|
-
// chart (and hovering a bar highlights its block — the active turn
|
|
758
|
-
// is shared hover-only state).
|
|
759
|
-
h(
|
|
760
|
-
"div",
|
|
761
|
-
{ className: "lc-turns", onMouseLeave: () => {
|
|
762
|
-
props.onHoverTurn(null);
|
|
763
|
-
} },
|
|
764
|
-
groups.map((grp, gi) => {
|
|
765
|
-
const on = props.activeTurn === grp.turn;
|
|
766
|
-
const blockW = grp.agg ? BAR_W : grp.span * (BAR_W + BAR_GAP) - BAR_GAP;
|
|
767
|
-
return h("span", {
|
|
768
|
-
key: "turn-" + gi,
|
|
769
|
-
className: "lc-turn" + (on ? " lc-turn-on" : ""),
|
|
770
|
-
style: {
|
|
771
|
-
width: blockW + "px",
|
|
772
|
-
background: TURN_COLORS[gi % TURN_COLORS.length]
|
|
773
682
|
},
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
683
|
+
"\u2702"
|
|
684
|
+
) : null,
|
|
685
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-bar-stack" }, CATS.map((c) => {
|
|
686
|
+
const v = (req[c.key] || 0) * anchorOf(req);
|
|
687
|
+
if (!v) return null;
|
|
688
|
+
return /* @__PURE__ */ React.createElement("div", { key: c.key, style: { height: Math.max(1, Math.round(v / maxTotal * CHART_H)) + "px", background: c.color } });
|
|
689
|
+
}))
|
|
690
|
+
);
|
|
691
|
+
})
|
|
781
692
|
),
|
|
782
|
-
|
|
783
|
-
|
|
693
|
+
hoveredReq !== null ? /* @__PURE__ */ React.createElement(
|
|
694
|
+
"div",
|
|
695
|
+
{
|
|
696
|
+
className: "lc-chart-tip",
|
|
697
|
+
style: { left: hoveredIdx * (BAR_W + BAR_GAP) + BAR_W / 2 + "px" }
|
|
698
|
+
},
|
|
699
|
+
tipOf(hoveredReq)
|
|
700
|
+
) : null,
|
|
701
|
+
/* @__PURE__ */ React.createElement("div", { className: "lc-turns", onMouseLeave: () => {
|
|
702
|
+
props.onHoverTurn(null);
|
|
703
|
+
} }, groups.map((grp, gi) => {
|
|
704
|
+
const on = props.activeTurn === grp.turn;
|
|
705
|
+
const blockW = grp.agg ? BAR_W : grp.span * (BAR_W + BAR_GAP) - BAR_GAP;
|
|
706
|
+
return /* @__PURE__ */ React.createElement(
|
|
707
|
+
"span",
|
|
708
|
+
{
|
|
709
|
+
key: "turn-" + gi,
|
|
710
|
+
className: "lc-turn" + (on ? " lc-turn-on" : ""),
|
|
711
|
+
style: {
|
|
712
|
+
width: blockW + "px",
|
|
713
|
+
background: TURN_COLORS[gi % TURN_COLORS.length]
|
|
714
|
+
},
|
|
715
|
+
title: "T" + grp.turn,
|
|
716
|
+
onMouseEnter: () => {
|
|
717
|
+
props.onHoverTurn(grp.turn);
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
"T" + grp.turn
|
|
721
|
+
);
|
|
722
|
+
}))
|
|
723
|
+
), edges.right ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-r" }) : null);
|
|
784
724
|
};
|
|
785
725
|
}
|
|
786
726
|
|
|
787
|
-
// src/client/components/contextView.
|
|
727
|
+
// src/client/components/contextView.tsx
|
|
728
|
+
var viewScroll = /* @__PURE__ */ new Map();
|
|
788
729
|
function makeContextView(ctx, kit) {
|
|
789
730
|
const { t, tr, fmt: fmt3, fmtTime: fmtTime2, catLabel } = kit;
|
|
790
731
|
const StackedBar = makeStackedBar(kit);
|
|
@@ -796,51 +737,31 @@ function makeContextView(ctx, kit) {
|
|
|
796
737
|
const StatsBoard = makeStatsBoard(kit);
|
|
797
738
|
return function ContextView(props) {
|
|
798
739
|
const sessionId = props.sessionId;
|
|
799
|
-
const
|
|
800
|
-
const [data, setData] = React.useState(initial);
|
|
801
|
-
const [error, setError] = React.useState(null);
|
|
740
|
+
const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
|
|
802
741
|
const [selectedSeq, setSelectedSeq] = React.useState(null);
|
|
803
742
|
const [hoveredSeq, setHoveredSeq] = React.useState(null);
|
|
804
743
|
const [hoverTurn, setHoverTurn] = React.useState(null);
|
|
805
744
|
const [tick, setTick] = React.useState(0);
|
|
806
745
|
const [granularity, setGranularity] = React.useState("step");
|
|
807
746
|
const [hoverCat, setHoverCat] = React.useState(null);
|
|
808
|
-
const
|
|
809
|
-
React.
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
if (
|
|
814
|
-
|
|
815
|
-
const
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
setData(snap);
|
|
822
|
-
setError(null);
|
|
823
|
-
} else if (dataRef.current === null) {
|
|
824
|
-
setError(res && res.error ? String(res.error.message || res.error.code) : "failed");
|
|
825
|
-
}
|
|
826
|
-
}, (err) => {
|
|
827
|
-
if (alive && dataRef.current === null) {
|
|
828
|
-
setError(String(err instanceof Error ? err.message : err));
|
|
829
|
-
}
|
|
830
|
-
});
|
|
831
|
-
};
|
|
832
|
-
load();
|
|
833
|
-
const timerId = setInterval(() => {
|
|
834
|
-
if (document.visibilityState !== "hidden") load();
|
|
835
|
-
}, 2e3);
|
|
836
|
-
const onVisible = () => {
|
|
837
|
-
if (document.visibilityState === "visible") load();
|
|
838
|
-
};
|
|
839
|
-
document.addEventListener("visibilitychange", onVisible);
|
|
747
|
+
const rootRef = React.useRef(null);
|
|
748
|
+
const scrollerRef = React.useRef(null);
|
|
749
|
+
const restoredRef = React.useRef(null);
|
|
750
|
+
React.useLayoutEffect(() => {
|
|
751
|
+
if (typeof sessionId !== "string" || sessionId === "" || data === null) return;
|
|
752
|
+
if (restoredRef.current === sessionId) return;
|
|
753
|
+
restoredRef.current = sessionId;
|
|
754
|
+
const scroller = rootRef.current !== null ? rootRef.current.closest("[data-conversation-scroll]") : null;
|
|
755
|
+
if (scroller === null) return;
|
|
756
|
+
scrollerRef.current = scroller;
|
|
757
|
+
scroller.scrollTop = viewScroll.get(sessionId) ?? 0;
|
|
758
|
+
}, [sessionId, data]);
|
|
759
|
+
React.useLayoutEffect(() => {
|
|
840
760
|
return () => {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
761
|
+
if (typeof sessionId !== "string" || sessionId === "") return;
|
|
762
|
+
const scroller = scrollerRef.current;
|
|
763
|
+
if (scroller === null) return;
|
|
764
|
+
viewScroll.set(sessionId, scroller.scrollTop);
|
|
844
765
|
};
|
|
845
766
|
}, [sessionId]);
|
|
846
767
|
React.useEffect(() => {
|
|
@@ -849,11 +770,8 @@ function makeContextView(ctx, kit) {
|
|
|
849
770
|
return localeSvc.subscribe(() => setTick((x) => x + 1));
|
|
850
771
|
}, []);
|
|
851
772
|
void tick;
|
|
852
|
-
if (error) {
|
|
853
|
-
return h("div", { className: "lc-root" }, h("div", { className: "lc-empty" }, t("error") + error));
|
|
854
|
-
}
|
|
855
773
|
if (!data) {
|
|
856
|
-
return
|
|
774
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
|
|
857
775
|
}
|
|
858
776
|
const current = data.current;
|
|
859
777
|
const requests = data.requests || [];
|
|
@@ -880,125 +798,50 @@ function makeContextView(ctx, kit) {
|
|
|
880
798
|
break;
|
|
881
799
|
}
|
|
882
800
|
}
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
data.toolList.length > 5 ? h("span", { className: "lc-card-sub" }, " " + tr("tools.more", { n: data.toolList.length })) : null
|
|
928
|
-
) : null
|
|
929
|
-
),
|
|
930
|
-
// ---- trend ----
|
|
931
|
-
h(
|
|
932
|
-
"div",
|
|
933
|
-
{ className: "lc-card" },
|
|
934
|
-
h(
|
|
935
|
-
"div",
|
|
936
|
-
{ className: "lc-card-title" },
|
|
937
|
-
t("trend.title"),
|
|
938
|
-
h("span", { className: "lc-card-sub" }, t("trend.hint")),
|
|
939
|
-
h(
|
|
940
|
-
"div",
|
|
941
|
-
{ className: "lc-gran" },
|
|
942
|
-
h("button", {
|
|
943
|
-
className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
|
|
944
|
-
onClick: () => {
|
|
945
|
-
setGranularity("step");
|
|
946
|
-
}
|
|
947
|
-
}, t("gran.step")),
|
|
948
|
-
h("button", {
|
|
949
|
-
className: "lc-gran-btn" + (granularity === "turn" ? " lc-gran-on" : ""),
|
|
950
|
-
onClick: () => {
|
|
951
|
-
setGranularity("turn");
|
|
952
|
-
}
|
|
953
|
-
}, t("gran.turn"))
|
|
954
|
-
)
|
|
955
|
-
),
|
|
956
|
-
displayRequests.length === 0 ? h("div", { className: "lc-empty" }, t("trend.empty")) : h(
|
|
957
|
-
"div",
|
|
958
|
-
null,
|
|
959
|
-
h(TrendChart, {
|
|
960
|
-
// Remount per session: switching sessions re-anchors the chart
|
|
961
|
-
// at the newest bars instead of inheriting stale scroll state.
|
|
962
|
-
key: sessionId,
|
|
963
|
-
// The host caps the log at 160 requests; render them ALL so
|
|
964
|
-
// earlier turns/steps stay reachable via horizontal scroll.
|
|
965
|
-
requests: displayRequests,
|
|
966
|
-
markers,
|
|
967
|
-
selectedSeq: pinnedReq ? pinnedReq.seq : null,
|
|
968
|
-
hoveredSeq,
|
|
969
|
-
activeTurn,
|
|
970
|
-
granularity,
|
|
971
|
-
onSelect: setSelectedSeq,
|
|
972
|
-
onHover: setHoveredSeq,
|
|
973
|
-
onHoverTurn: setHoverTurn
|
|
974
|
-
}),
|
|
975
|
-
h(RequestDetail, { request: activeReq, marker: activeReq !== null ? markerOf(activeReq) : void 0 })
|
|
976
|
-
)
|
|
977
|
-
),
|
|
978
|
-
// ---- events + messages ----
|
|
979
|
-
h(
|
|
980
|
-
"div",
|
|
981
|
-
{ className: "lc-cols" },
|
|
982
|
-
h(
|
|
983
|
-
"div",
|
|
984
|
-
{ className: "lc-card lc-col" },
|
|
985
|
-
h("div", { className: "lc-card-title" }, t("events.title")),
|
|
986
|
-
h(EventList, { events })
|
|
987
|
-
),
|
|
988
|
-
h(
|
|
989
|
-
"div",
|
|
990
|
-
{ className: "lc-card lc-col" },
|
|
991
|
-
h(
|
|
992
|
-
"div",
|
|
993
|
-
{ className: "lc-card-title" },
|
|
994
|
-
t("nodes.title"),
|
|
995
|
-
h("span", { className: "lc-card-sub" }, t("nodes.hint"))
|
|
996
|
-
),
|
|
997
|
-
h(NodeList, { nodes, dropped: data.droppedNodes || 0 })
|
|
998
|
-
)
|
|
999
|
-
),
|
|
1000
|
-
h("div", { className: "lc-foot" }, t("footer"))
|
|
1001
|
-
);
|
|
801
|
+
const occ = data.occupancy;
|
|
802
|
+
const projected = occ !== void 0 && typeof occ.projectedTokens === "number" ? occ.projectedTokens : void 0;
|
|
803
|
+
const lastReq = displayRequests.length > 0 ? displayRequests[displayRequests.length - 1] : null;
|
|
804
|
+
const derived = lastReq !== null && typeof lastReq.prompt === "number" ? lastReq.prompt + (current.total - lastReq.total) : void 0;
|
|
805
|
+
const occupancyTokens = projected ?? derived ?? null;
|
|
806
|
+
const occupancyWindow = occ !== void 0 && typeof occ.contextWindow === "number" ? occ.contextWindow : data.contextWindow;
|
|
807
|
+
const headline = occupancyTokens ?? current.total;
|
|
808
|
+
const headlinePct = occupancyWindow ? Math.min(100, Math.round(headline / occupancyWindow * 100)) : null;
|
|
809
|
+
const parts = anchoredParts(partsOf(current), occupancyTokens !== null && headline > 0 ? headline : null);
|
|
810
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement(StatsBoard, { requests, events }), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("overview.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : ""))), /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt3(headline)), /* @__PURE__ */ React.createElement("span", null, occupancyWindow ? " / " + fmt3(occupancyWindow) + " " + tr("overview.ofWindow", { p: headlinePct ?? 0 }) : " " + t("overview.estimate")), occupancyTokens !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("overview.splitEst")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts, height: 16, max: occupancyWindow, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts, hoverKey: hoverCat, onHoverKey: setHoverCat }), data.toolList && data.toolList.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-tools" }, t("tools.top"), data.toolList.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => {
|
|
811
|
+
return /* @__PURE__ */ React.createElement("span", { key: tool.name, className: "lc-tool-chip" }, tool.name + " " + fmt3(tool.tokens));
|
|
812
|
+
}), data.toolList.length > 5 ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, " " + tr("tools.more", { n: data.toolList.length })) : null) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("trend.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement(
|
|
813
|
+
"button",
|
|
814
|
+
{
|
|
815
|
+
className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
|
|
816
|
+
onClick: () => {
|
|
817
|
+
setGranularity("step");
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
t("gran.step")
|
|
821
|
+
), /* @__PURE__ */ React.createElement(
|
|
822
|
+
"button",
|
|
823
|
+
{
|
|
824
|
+
className: "lc-gran-btn" + (granularity === "turn" ? " lc-gran-on" : ""),
|
|
825
|
+
onClick: () => {
|
|
826
|
+
setGranularity("turn");
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
t("gran.turn")
|
|
830
|
+
))), displayRequests.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("trend.empty")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
831
|
+
TrendChart,
|
|
832
|
+
{
|
|
833
|
+
key: sessionId,
|
|
834
|
+
requests: displayRequests,
|
|
835
|
+
markers,
|
|
836
|
+
selectedSeq: pinnedReq ? pinnedReq.seq : null,
|
|
837
|
+
hoveredSeq,
|
|
838
|
+
activeTurn,
|
|
839
|
+
granularity,
|
|
840
|
+
onSelect: setSelectedSeq,
|
|
841
|
+
onHover: setHoveredSeq,
|
|
842
|
+
onHoverTurn: setHoverTurn
|
|
843
|
+
}
|
|
844
|
+
), /* @__PURE__ */ React.createElement(RequestDetail, { request: activeReq, marker: activeReq !== null ? markerOf(activeReq) : void 0 }))), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("events.title")), /* @__PURE__ */ React.createElement(EventList, { events })), /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("nodes.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("nodes.hint"))), /* @__PURE__ */ React.createElement(NodeList, { nodes, dropped: data.droppedNodes || 0 }))), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
|
|
1002
845
|
};
|
|
1003
846
|
}
|
|
1004
847
|
|
|
@@ -1017,11 +860,12 @@ function makeViewKit(t) {
|
|
|
1017
860
|
}
|
|
1018
861
|
|
|
1019
862
|
// src/client/index.ts
|
|
863
|
+
var NS = "dsh-context";
|
|
1020
864
|
function apply(ctx) {
|
|
1021
865
|
ctx.effect(() => {
|
|
1022
|
-
return ctx.locale.register(
|
|
866
|
+
return ctx.locale.register(NS, { zh: DICT_ZH, en: DICT_EN });
|
|
1023
867
|
}, "dsh-context: dictionaries");
|
|
1024
|
-
const t = ctx.locale.bind(
|
|
868
|
+
const t = ctx.locale.bind(NS);
|
|
1025
869
|
ctx.effect(() => {
|
|
1026
870
|
const tag = document.createElement("style");
|
|
1027
871
|
tag.setAttribute("data-plugin", "dsh-context");
|
|
@@ -1034,15 +878,16 @@ function apply(ctx) {
|
|
|
1034
878
|
const ContextView = makeContextView(ctx, makeViewKit(t));
|
|
1035
879
|
ctx.slots.inject("conversation.view", () => {
|
|
1036
880
|
return ctx.slots.register(
|
|
1037
|
-
// order 20 renders right of Chat (0) and Trajectory (10)
|
|
1038
|
-
|
|
881
|
+
// order 20 renders right of Chat (0) and Trajectory (10); the locale
|
|
882
|
+
// namespace put the framework `t` seat on the component's props too.
|
|
883
|
+
{ name: "conversation.view", id: "context", order: 20, locale: NS, label: () => t("tab") },
|
|
1039
884
|
(props) => h(ContextView, props)
|
|
1040
885
|
);
|
|
1041
886
|
});
|
|
1042
887
|
}
|
|
1043
888
|
module.exports = {
|
|
1044
889
|
name: "dsh-context",
|
|
1045
|
-
inject: ["
|
|
890
|
+
inject: ["slots", "locale"],
|
|
1046
891
|
apply
|
|
1047
892
|
};
|
|
1048
893
|
|