dsh-context 0.56.2 → 0.57.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/lib/client.js +646 -473
- package/lib/index.js +41 -4
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -356,6 +356,7 @@ window.__ModuleLoader__.load({
|
|
|
356
356
|
"ov.kpi.sessionsSub": "含 {n} 个会话",
|
|
357
357
|
"ov.kpi.toolSub": "工具执行 {dur}",
|
|
358
358
|
"ov.kpi.wallSub": "模型调用 {n}",
|
|
359
|
+
"ov.stats.empty": "统计范围内暂无 Token 用量",
|
|
359
360
|
"balance.title": "DeepSeek 余额",
|
|
360
361
|
"balance.tip.granted": "赠送余额",
|
|
361
362
|
"balance.tip.toppedUp": "充值余额",
|
|
@@ -735,6 +736,7 @@ window.__ModuleLoader__.load({
|
|
|
735
736
|
"ov.kpi.sessionsSub": "across {n} sessions",
|
|
736
737
|
"ov.kpi.toolSub": "tool runs {dur}",
|
|
737
738
|
"ov.kpi.wallSub": "{n} model calls",
|
|
739
|
+
"ov.stats.empty": "No billed tokens in this range",
|
|
738
740
|
"balance.title": "DeepSeek balance",
|
|
739
741
|
"balance.tip.granted": "Granted balance",
|
|
740
742
|
"balance.tip.toppedUp": "Topped-up balance",
|
|
@@ -6206,6 +6208,105 @@ window.__ModuleLoader__.load({
|
|
|
6206
6208
|
totals.total = totals.input + totals.cacheRead + totals.cacheWrite + totals.output;
|
|
6207
6209
|
return totals;
|
|
6208
6210
|
}
|
|
6211
|
+
/**
|
|
6212
|
+
* The range's summed timing totals — the aggregate Timing Stats card's
|
|
6213
|
+
* source, folded from the rows' host-folded whole-session totals exactly as
|
|
6214
|
+
* the KPI band folds the scalar figures. Null when no row carries timing.
|
|
6215
|
+
* The additive-optional fields (the decode split, the throughput seat) sum
|
|
6216
|
+
* their carriers only: a row cached before a field existed contributes
|
|
6217
|
+
* nothing to it, and a field no row carries stays absent — the card keeps
|
|
6218
|
+
* its un-split shape / no-chip fallback instead of materialized zeros.
|
|
6219
|
+
*/
|
|
6220
|
+
function timingSumOf(rows) {
|
|
6221
|
+
let out = null;
|
|
6222
|
+
for (const row of rows) {
|
|
6223
|
+
const t = row.timeline?.timing;
|
|
6224
|
+
if (t === void 0) continue;
|
|
6225
|
+
if (out === null) {
|
|
6226
|
+
out = {
|
|
6227
|
+
...t,
|
|
6228
|
+
tools: { ...t.tools }
|
|
6229
|
+
};
|
|
6230
|
+
continue;
|
|
6231
|
+
}
|
|
6232
|
+
out.wallMs += t.wallMs;
|
|
6233
|
+
out.ttftMs += t.ttftMs;
|
|
6234
|
+
out.genMs += t.genMs;
|
|
6235
|
+
out.calls += t.calls;
|
|
6236
|
+
out.toolsMs += t.toolsMs;
|
|
6237
|
+
out.toolCalls += t.toolCalls;
|
|
6238
|
+
addOptionalOf(out, t, "reasoningMs");
|
|
6239
|
+
addOptionalOf(out, t, "reasoningBlocks");
|
|
6240
|
+
addOptionalOf(out, t, "textMs");
|
|
6241
|
+
addOptionalOf(out, t, "textBlocks");
|
|
6242
|
+
addOptionalOf(out, t, "toolArgMs");
|
|
6243
|
+
addOptionalOf(out, t, "toolArgBlocks");
|
|
6244
|
+
addOptionalOf(out, t, "speedTokens");
|
|
6245
|
+
addOptionalOf(out, t, "speedMs");
|
|
6246
|
+
for (const name of Object.keys(t.tools)) {
|
|
6247
|
+
const tool = t.tools[name];
|
|
6248
|
+
const prev = out.tools[name];
|
|
6249
|
+
out.tools[name] = prev === void 0 ? { ...tool } : {
|
|
6250
|
+
calls: prev.calls + tool.calls,
|
|
6251
|
+
ms: prev.ms + tool.ms
|
|
6252
|
+
};
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
return out;
|
|
6256
|
+
}
|
|
6257
|
+
/** One additive-optional timing field's in-place merge: absent on both sides stays absent (never a materialized undefined). */
|
|
6258
|
+
function addOptionalOf(target, source, key) {
|
|
6259
|
+
const a = target[key];
|
|
6260
|
+
const b = source[key];
|
|
6261
|
+
if (a !== void 0 || b !== void 0) target[key] = (a ?? 0) + (b ?? 0);
|
|
6262
|
+
}
|
|
6263
|
+
/**
|
|
6264
|
+
* The range's billed tokens split by WHAT they are — the Context tab's Token
|
|
6265
|
+
* card categorization, folded across sessions: each session's
|
|
6266
|
+
* `billedParts` estimate (the composition ratios proportioning that
|
|
6267
|
+
* session's provider-reported prompt total, output exact) sums by category,
|
|
6268
|
+
* and every session's parts total its own billed figure, so the aggregate's
|
|
6269
|
+
* total stays the exact merged billed volume while the split inherits the
|
|
6270
|
+
* per-session card's `≈` estimate convention. Null when no session billed
|
|
6271
|
+
* anything. A non-finite part estimate (a hostile fast-path composition)
|
|
6272
|
+
* drops whole instead of poisoning the sums.
|
|
6273
|
+
*/
|
|
6274
|
+
function tokenPartsOf(rows) {
|
|
6275
|
+
const sums = /* @__PURE__ */ new Map();
|
|
6276
|
+
let total = 0;
|
|
6277
|
+
let any = false;
|
|
6278
|
+
for (const row of rows) {
|
|
6279
|
+
const timeline = row.timeline;
|
|
6280
|
+
if (timeline === null) continue;
|
|
6281
|
+
const totals = usageTotalsOf(timeline.cost);
|
|
6282
|
+
if (totals === null) continue;
|
|
6283
|
+
any = true;
|
|
6284
|
+
const usage = {
|
|
6285
|
+
uncachedInputTokens: totals.input,
|
|
6286
|
+
cacheReadTokens: totals.cacheRead,
|
|
6287
|
+
cacheWriteTokens: totals.cacheWrite,
|
|
6288
|
+
outputTokens: totals.output
|
|
6289
|
+
};
|
|
6290
|
+
for (const part of billedParts(timeline.current, null, usage)) {
|
|
6291
|
+
if (!Number.isFinite(part.value) || part.value <= 0) continue;
|
|
6292
|
+
total += part.value;
|
|
6293
|
+
const prev = sums.get(part.key);
|
|
6294
|
+
sums.set(part.key, prev === void 0 ? {
|
|
6295
|
+
key: part.key,
|
|
6296
|
+
color: part.color,
|
|
6297
|
+
value: part.value
|
|
6298
|
+
} : {
|
|
6299
|
+
...prev,
|
|
6300
|
+
value: prev.value + part.value
|
|
6301
|
+
});
|
|
6302
|
+
}
|
|
6303
|
+
}
|
|
6304
|
+
if (!any) return null;
|
|
6305
|
+
return {
|
|
6306
|
+
parts: [...sums.values()],
|
|
6307
|
+
total
|
|
6308
|
+
};
|
|
6309
|
+
}
|
|
6209
6310
|
function kpisOf(rows, listed, prices, currency) {
|
|
6210
6311
|
const usage = mergeCostUsage(...rows.map((row) => row.timeline?.cost));
|
|
6211
6312
|
const totals = usageTotalsOf(usage);
|
|
@@ -6239,7 +6340,9 @@ window.__ModuleLoader__.load({
|
|
|
6239
6340
|
toolCalls,
|
|
6240
6341
|
toolsMs,
|
|
6241
6342
|
calls,
|
|
6242
|
-
wallMs
|
|
6343
|
+
wallMs,
|
|
6344
|
+
tokenParts: tokenPartsOf(rows),
|
|
6345
|
+
timing: timingSumOf(rows)
|
|
6243
6346
|
};
|
|
6244
6347
|
}
|
|
6245
6348
|
/**
|
|
@@ -6939,6 +7042,304 @@ window.__ModuleLoader__.load({
|
|
|
6939
7042
|
};
|
|
6940
7043
|
}
|
|
6941
7044
|
//#endregion
|
|
7045
|
+
//#region src/client/components/sliceList.tsx
|
|
7046
|
+
/**
|
|
7047
|
+
* The stats cards' slice rows: the donut's legend with numbers, two tiers
|
|
7048
|
+
* per slice — the primary line pairs the color dot and the name with the
|
|
7049
|
+
* bold share closing a fixed right column, and the secondary line drops the
|
|
7050
|
+
* absolute quantity (plus a qualifier like the call count) under the name in
|
|
7051
|
+
* muted small print. Hovering a row lights it and its donut segment together
|
|
7052
|
+
* (the shared hover key), and vice versa. No tracks or bars — the donut IS
|
|
7053
|
+
* the proportion chart; the rows are its legend. Preformatted strings in,
|
|
7054
|
+
* dumb markup out.
|
|
7055
|
+
*/
|
|
7056
|
+
function makeSliceList(kit) {
|
|
7057
|
+
return function SliceList(props) {
|
|
7058
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7059
|
+
className: "lc-sl flex-auto @max-[240px]/lc-card:basis-full",
|
|
7060
|
+
onMouseLeave: () => {
|
|
7061
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
7062
|
+
},
|
|
7063
|
+
children: props.rows.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7064
|
+
className: "lc-sl-row" + (r.dim ? " lc-sl-row-dim" : "") + (props.hoverKey !== void 0 && props.hoverKey === r.key ? " lc-sl-row-on" : ""),
|
|
7065
|
+
onMouseEnter: () => {
|
|
7066
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(r.key);
|
|
7067
|
+
},
|
|
7068
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7069
|
+
className: "lc-sl-main",
|
|
7070
|
+
children: [
|
|
7071
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
|
|
7072
|
+
className: "lc-sl-dot",
|
|
7073
|
+
style: { background: r.color }
|
|
7074
|
+
}),
|
|
7075
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7076
|
+
className: "lc-sl-label",
|
|
7077
|
+
title: r.label,
|
|
7078
|
+
children: r.label
|
|
7079
|
+
}),
|
|
7080
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7081
|
+
className: "lc-sl-pct",
|
|
7082
|
+
children: r.pct
|
|
7083
|
+
})
|
|
7084
|
+
]
|
|
7085
|
+
}), r.count !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7086
|
+
className: "lc-sl-sub",
|
|
7087
|
+
title: r.count,
|
|
7088
|
+
children: r.count
|
|
7089
|
+
}) : null]
|
|
7090
|
+
}, r.key))
|
|
7091
|
+
});
|
|
7092
|
+
};
|
|
7093
|
+
}
|
|
7094
|
+
//#endregion
|
|
7095
|
+
//#region src/client/components/overviewTokens.tsx
|
|
7096
|
+
/**
|
|
7097
|
+
* The Context Insights panel's aggregate Token Stats card — the first row's
|
|
7098
|
+
* token half, categorized exactly like the Context tab's Token card
|
|
7099
|
+
* (statsTokens.tsx): the range's billed volume split by WHAT the tokens are —
|
|
7100
|
+
* the composition categories' `≈` estimates proportioning each session's
|
|
7101
|
+
* provider-reported prompt total, plus the exact output — folded by
|
|
7102
|
+
* tokenPartsOf. The center figure stays the exact merged billed total the
|
|
7103
|
+
* KPI band's Tokens Used cell shows.
|
|
7104
|
+
*/
|
|
7105
|
+
function makeOverviewTokens(kit, Donut) {
|
|
7106
|
+
const { t, fmt, fmtShare, catLabel } = kit;
|
|
7107
|
+
const SliceList = makeSliceList(kit);
|
|
7108
|
+
return function OverviewTokens(props) {
|
|
7109
|
+
const [hoverKey, setHoverKey] = (0, react.useState)(null);
|
|
7110
|
+
const tokens = props.tokens;
|
|
7111
|
+
const total = tokens?.total ?? 0;
|
|
7112
|
+
const shown = tokens !== null && total > 0 ? tokens.parts.filter((p) => p.value > 0) : [];
|
|
7113
|
+
const rows = shown.map((p) => ({
|
|
7114
|
+
key: p.key,
|
|
7115
|
+
color: p.color,
|
|
7116
|
+
label: p.key === "output" ? t("tokens.output") : catLabel(p.key),
|
|
7117
|
+
pct: fmtShare(p.value, total),
|
|
7118
|
+
count: p.key === "output" ? `${fmt(p.value)} · ${t("tokens.outputNote")}` : "≈" + fmt(p.value)
|
|
7119
|
+
}));
|
|
7120
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7121
|
+
className: "lc-card lc-col-stats lc-col-donut flex-1 min-w-[min(360px,100%)]",
|
|
7122
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7123
|
+
className: "lc-card-title",
|
|
7124
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7125
|
+
className: "lc-card-title-text",
|
|
7126
|
+
children: t("tokens.title")
|
|
7127
|
+
})
|
|
7128
|
+
}), shown.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7129
|
+
className: "lc-empty",
|
|
7130
|
+
children: t("ov.stats.empty")
|
|
7131
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7132
|
+
className: "lc-donut-row flex items-center justify-start gap-3 min-w-0 @max-[320px]/lc-card:gap-2 @max-[240px]/lc-card:flex-wrap",
|
|
7133
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Donut, {
|
|
7134
|
+
segments: shown.map((p) => ({
|
|
7135
|
+
key: p.key,
|
|
7136
|
+
color: p.color,
|
|
7137
|
+
value: p.value
|
|
7138
|
+
})),
|
|
7139
|
+
size: 96,
|
|
7140
|
+
centerTop: fmt(total),
|
|
7141
|
+
centerSub: t("tokens.total"),
|
|
7142
|
+
hoverKey,
|
|
7143
|
+
onHoverKey: setHoverKey
|
|
7144
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliceList, {
|
|
7145
|
+
rows,
|
|
7146
|
+
hoverKey,
|
|
7147
|
+
onHoverKey: setHoverKey
|
|
7148
|
+
})]
|
|
7149
|
+
})]
|
|
7150
|
+
});
|
|
7151
|
+
};
|
|
7152
|
+
}
|
|
7153
|
+
//#endregion
|
|
7154
|
+
//#region src/client/components/statsTiming.tsx
|
|
7155
|
+
/**
|
|
7156
|
+
* The Timing card: where the session's ACTIVE time went. The donut splits
|
|
7157
|
+
* whole-step wall time into the model-call slices — TTFT (step start → first
|
|
7158
|
+
* token, the wait) and LLM generation (first token → assistant message) —
|
|
7159
|
+
* and the tool-execution slice, with the residue as overhead; a call whose
|
|
7160
|
+
* stream recorded no token delta (legacy or aborted) stays unattributed and
|
|
7161
|
+
* lands in the residue. When the host folded the generation split (see
|
|
7162
|
+
* TimingTotals), the generation slice expands into what was being decoded:
|
|
7163
|
+
* thinking, answer text, and tool-call arguments. The slice rows lead with
|
|
7164
|
+
* the true duration and qualify it with a tally on the secondary line — the
|
|
7165
|
+
* model calls (TTFT), the tool runs, and the decode slices' own BLOCK counts
|
|
7166
|
+
* (zero to many per call, so a call count would be a false tally there). A
|
|
7167
|
+
* slice that never happened (zero time) is omitted entirely — the ring already skips
|
|
7168
|
+
* its arc, and a zero row would otherwise borrow the session's call count and
|
|
7169
|
+
* read as "no time, many calls". The donut and the rows sit side by side so
|
|
7170
|
+
* the head row stays half-height. Parallel tool calls each count, so the tools
|
|
7171
|
+
* figure can overlap — the ring clamps it into the post-model window while the
|
|
7172
|
+
* row numbers stay true.
|
|
7173
|
+
*/
|
|
7174
|
+
const COLOR = {
|
|
7175
|
+
ttft: "var(--color-blue-500)",
|
|
7176
|
+
reasoning: "var(--color-violet-500)",
|
|
7177
|
+
text: "var(--color-pink-500)",
|
|
7178
|
+
toolarg: "var(--color-amber-500)",
|
|
7179
|
+
tools: "var(--color-teal-500)",
|
|
7180
|
+
other: "var(--color-slate-400)"
|
|
7181
|
+
};
|
|
7182
|
+
/**
|
|
7183
|
+
* The decode-throughput figure, formatted exactly as the harness's own
|
|
7184
|
+
* session-stats seat formats it: whole tokens from ten up, one decimal below.
|
|
7185
|
+
*/
|
|
7186
|
+
function formatTokensPerSecond(tps) {
|
|
7187
|
+
const clamped = Math.max(0, tps);
|
|
7188
|
+
return clamped >= 10 ? String(Math.round(clamped)) : String(Math.round(clamped * 10) / 10);
|
|
7189
|
+
}
|
|
7190
|
+
function makeStatsTiming(kit, Donut) {
|
|
7191
|
+
const { t, fmt, fmtDuration, fmtShare } = kit;
|
|
7192
|
+
const SliceList = makeSliceList(kit);
|
|
7193
|
+
return function StatsTiming(props) {
|
|
7194
|
+
const [hoverKey, setHoverKey] = (0, react.useState)(null);
|
|
7195
|
+
const timing = props.timing;
|
|
7196
|
+
const wall = timing !== null && Number.isFinite(timing.wallMs) && timing.wallMs > 0 ? timing.wallMs : 0;
|
|
7197
|
+
const tps = timing !== null && typeof timing.speedMs === "number" && Number.isFinite(timing.speedMs) && timing.speedMs > 0 && typeof timing.speedTokens === "number" && Number.isFinite(timing.speedTokens) && timing.speedTokens >= 0 ? formatTokensPerSecond(timing.speedTokens / (timing.speedMs / 1e3)) : null;
|
|
7198
|
+
let segments = [];
|
|
7199
|
+
let rows = [];
|
|
7200
|
+
if (timing !== null && (wall > 0 || timing.calls > 0 || timing.toolCalls > 0)) {
|
|
7201
|
+
const callTimes = t("timing.callTimes", { n: fmt(timing.calls) });
|
|
7202
|
+
const modelSlices = [{
|
|
7203
|
+
key: "ttft",
|
|
7204
|
+
color: COLOR.ttft,
|
|
7205
|
+
label: t("timing.ttft"),
|
|
7206
|
+
ms: timing.ttftMs,
|
|
7207
|
+
times: callTimes
|
|
7208
|
+
}];
|
|
7209
|
+
const blockTimes = (blocks) => blocks !== void 0 && blocks > 0 ? t("timing.blockTimes", { n: fmt(blocks) }) : void 0;
|
|
7210
|
+
const buckets = [
|
|
7211
|
+
[
|
|
7212
|
+
"reasoning",
|
|
7213
|
+
COLOR.reasoning,
|
|
7214
|
+
t("timing.reasoning"),
|
|
7215
|
+
timing.reasoningMs ?? 0,
|
|
7216
|
+
blockTimes(timing.reasoningBlocks)
|
|
7217
|
+
],
|
|
7218
|
+
[
|
|
7219
|
+
"text",
|
|
7220
|
+
COLOR.text,
|
|
7221
|
+
t("timing.text"),
|
|
7222
|
+
timing.textMs ?? 0,
|
|
7223
|
+
blockTimes(timing.textBlocks)
|
|
7224
|
+
],
|
|
7225
|
+
[
|
|
7226
|
+
"toolarg",
|
|
7227
|
+
COLOR.toolarg,
|
|
7228
|
+
t("timing.toolArgs"),
|
|
7229
|
+
timing.toolArgMs ?? 0,
|
|
7230
|
+
blockTimes(timing.toolArgBlocks)
|
|
7231
|
+
]
|
|
7232
|
+
];
|
|
7233
|
+
if (buckets.some(([, , , ms]) => ms > 0)) for (const [key, color, label, ms, times] of buckets) modelSlices.push({
|
|
7234
|
+
key,
|
|
7235
|
+
color,
|
|
7236
|
+
label,
|
|
7237
|
+
ms,
|
|
7238
|
+
...times !== void 0 ? { times } : {}
|
|
7239
|
+
});
|
|
7240
|
+
else modelSlices.push({
|
|
7241
|
+
key: "gen",
|
|
7242
|
+
color: COLOR.reasoning,
|
|
7243
|
+
label: t("timing.gen"),
|
|
7244
|
+
ms: timing.genMs
|
|
7245
|
+
});
|
|
7246
|
+
const ttftRing = Math.min(timing.ttftMs, wall);
|
|
7247
|
+
const genRing = Math.max(0, Math.min(timing.genMs, wall - ttftRing));
|
|
7248
|
+
const modelRings = [ttftRing];
|
|
7249
|
+
let used = ttftRing;
|
|
7250
|
+
const genEnd = ttftRing + genRing;
|
|
7251
|
+
for (const slice of modelSlices.slice(1)) {
|
|
7252
|
+
const take = Math.max(0, Math.min(slice.ms, genEnd - used));
|
|
7253
|
+
modelRings.push(take);
|
|
7254
|
+
used += take;
|
|
7255
|
+
}
|
|
7256
|
+
const toolRing = Math.max(0, Math.min(timing.toolsMs, wall - ttftRing - genRing));
|
|
7257
|
+
const other = Math.max(0, wall - ttftRing - genRing - toolRing);
|
|
7258
|
+
const share = (ms) => wall > 0 ? ms / wall : 0;
|
|
7259
|
+
const countOf = (ms, times) => {
|
|
7260
|
+
const dur = fmtDuration(ms);
|
|
7261
|
+
if (times === void 0) return dur;
|
|
7262
|
+
return ms > 0 ? `${dur} · ${times}` : times;
|
|
7263
|
+
};
|
|
7264
|
+
const modelSegments = modelSlices.map((slice, index) => ({
|
|
7265
|
+
key: slice.key,
|
|
7266
|
+
color: slice.color,
|
|
7267
|
+
value: share(modelRings[index])
|
|
7268
|
+
}));
|
|
7269
|
+
const toolSlice = {
|
|
7270
|
+
key: "tools",
|
|
7271
|
+
color: COLOR.tools,
|
|
7272
|
+
label: t("timing.tools"),
|
|
7273
|
+
ms: timing.toolsMs,
|
|
7274
|
+
times: t("timing.toolTimes", { n: fmt(timing.toolCalls) })
|
|
7275
|
+
};
|
|
7276
|
+
const otherSlice = {
|
|
7277
|
+
key: "other",
|
|
7278
|
+
color: COLOR.other,
|
|
7279
|
+
label: t("timing.other"),
|
|
7280
|
+
ms: other
|
|
7281
|
+
};
|
|
7282
|
+
segments = [
|
|
7283
|
+
...modelSegments,
|
|
7284
|
+
{
|
|
7285
|
+
key: "tools",
|
|
7286
|
+
color: COLOR.tools,
|
|
7287
|
+
value: share(toolRing)
|
|
7288
|
+
},
|
|
7289
|
+
{
|
|
7290
|
+
key: "other",
|
|
7291
|
+
color: COLOR.other,
|
|
7292
|
+
value: share(other)
|
|
7293
|
+
}
|
|
7294
|
+
];
|
|
7295
|
+
const toRow = (slice) => ({
|
|
7296
|
+
key: slice.key,
|
|
7297
|
+
color: slice.color,
|
|
7298
|
+
label: slice.label,
|
|
7299
|
+
dim: slice.ms === 0,
|
|
7300
|
+
pct: fmtShare(slice.ms, wall),
|
|
7301
|
+
count: countOf(slice.ms, slice.times)
|
|
7302
|
+
});
|
|
7303
|
+
rows = [
|
|
7304
|
+
...modelSlices.map(toRow),
|
|
7305
|
+
toRow(toolSlice),
|
|
7306
|
+
toRow(otherSlice)
|
|
7307
|
+
].filter((row) => !row.dim);
|
|
7308
|
+
}
|
|
7309
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7310
|
+
className: "lc-card lc-col-stats lc-col-donut flex-1 min-w-[min(360px,100%)]",
|
|
7311
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7312
|
+
className: "lc-card-title",
|
|
7313
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7314
|
+
className: "lc-card-title-text",
|
|
7315
|
+
children: t("timing.title")
|
|
7316
|
+
}), tps !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7317
|
+
className: "lc-timing-tps",
|
|
7318
|
+
title: t("timing.tpsTip"),
|
|
7319
|
+
children: t("timing.tps", { tps })
|
|
7320
|
+
})]
|
|
7321
|
+
}), rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7322
|
+
className: "lc-empty",
|
|
7323
|
+
children: t("timing.empty")
|
|
7324
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7325
|
+
className: "lc-donut-row flex items-center justify-start gap-3 min-w-0 @max-[320px]/lc-card:gap-2 @max-[240px]/lc-card:flex-wrap",
|
|
7326
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Donut, {
|
|
7327
|
+
segments,
|
|
7328
|
+
size: 96,
|
|
7329
|
+
centerTop: wall > 0 ? fmtDuration(wall) : "—",
|
|
7330
|
+
centerSub: t("timing.total"),
|
|
7331
|
+
hoverKey,
|
|
7332
|
+
onHoverKey: setHoverKey
|
|
7333
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliceList, {
|
|
7334
|
+
rows,
|
|
7335
|
+
hoverKey,
|
|
7336
|
+
onHoverKey: setHoverKey
|
|
7337
|
+
})]
|
|
7338
|
+
})]
|
|
7339
|
+
});
|
|
7340
|
+
};
|
|
7341
|
+
}
|
|
7342
|
+
//#endregion
|
|
6942
7343
|
//#region src/client/components/overviewCard.tsx
|
|
6943
7344
|
function makeOverviewCard(kit) {
|
|
6944
7345
|
const { t } = kit;
|
|
@@ -7082,12 +7483,17 @@ window.__ModuleLoader__.load({
|
|
|
7082
7483
|
* projection values), so the panel draws every session's insight without
|
|
7083
7484
|
* opening one log.
|
|
7084
7485
|
*
|
|
7085
|
-
* The
|
|
7086
|
-
*
|
|
7087
|
-
*
|
|
7088
|
-
*
|
|
7089
|
-
*
|
|
7090
|
-
*
|
|
7486
|
+
* The panel's first row is the KPI metrics band: the range's six figures
|
|
7487
|
+
* (sessions, billed tokens, cost, cache hit, tool calls, active time) in one
|
|
7488
|
+
* full-width line. Below it, the aggregate stats row folds the same range's
|
|
7489
|
+
* sessions into the two donut cards the per-session Context tab opens with —
|
|
7490
|
+
* Token Stats (the composition-split billed volume) and Timing Stats (the
|
|
7491
|
+
* summed totals). The body is then a 3:7 column pair: the insight column (the
|
|
7492
|
+
* activity heatmap, then the preferences entry row) beside the session column
|
|
7493
|
+
* (search, group chips, and the card grid); the heatmap keeps its own fixed
|
|
7494
|
+
* 8-week window and PINs the list to a picked day (the panel's drill-down
|
|
7495
|
+
* gesture). A session card click jumps to that session through the harness's
|
|
7496
|
+
* own selection verb (openSessionVia) and closes the panel.
|
|
7091
7497
|
*/
|
|
7092
7498
|
const RANGES = [
|
|
7093
7499
|
"24h",
|
|
@@ -7107,6 +7513,9 @@ window.__ModuleLoader__.load({
|
|
|
7107
7513
|
const Heatmap = makeHeatmap(kit);
|
|
7108
7514
|
const OverviewCard = makeOverviewCard(kit);
|
|
7109
7515
|
const BalanceCapsule = makeBalanceCapsule(ctx, kit);
|
|
7516
|
+
const Donut = makeDonut(kit);
|
|
7517
|
+
const OverviewTokens = makeOverviewTokens(kit, Donut);
|
|
7518
|
+
const StatsTiming = makeStatsTiming(kit, Donut);
|
|
7110
7519
|
const ErrorBoundary = makeErrorBoundary(t);
|
|
7111
7520
|
/** The display currency follows the active locale (zh → CNY), read per render — the slot outlet re-renders on a locale switch. */
|
|
7112
7521
|
function activeCurrency() {
|
|
@@ -7217,123 +7626,127 @@ window.__ModuleLoader__.load({
|
|
|
7217
7626
|
}), rows === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7218
7627
|
className: "lc-empty",
|
|
7219
7628
|
children: t("ov.unavailable")
|
|
7220
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
className: "lc-ov-left",
|
|
7629
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
7630
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7631
|
+
className: "lc-ov-kpis",
|
|
7224
7632
|
children: [
|
|
7225
7633
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7226
|
-
className: "lc-ov-
|
|
7634
|
+
className: "lc-stat lc-ov-kpi",
|
|
7227
7635
|
children: [
|
|
7228
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
7229
|
-
className: "lc-stat
|
|
7230
|
-
children:
|
|
7231
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7232
|
-
className: "lc-stat-label",
|
|
7233
|
-
children: t("ov.kpi.sessions")
|
|
7234
|
-
}),
|
|
7235
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7236
|
-
className: "lc-stat-value",
|
|
7237
|
-
children: kpi.sessions
|
|
7238
|
-
}),
|
|
7239
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7240
|
-
className: "lc-stat-sub",
|
|
7241
|
-
children: t("ov.kpi.ofTotal", { n: kpi.listed })
|
|
7242
|
-
})
|
|
7243
|
-
]
|
|
7636
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7637
|
+
className: "lc-stat-label",
|
|
7638
|
+
children: t("ov.kpi.sessions")
|
|
7244
7639
|
}),
|
|
7245
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
7246
|
-
className: "lc-stat
|
|
7247
|
-
children:
|
|
7248
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7249
|
-
className: "lc-stat-label",
|
|
7250
|
-
children: t("ov.kpi.tokens")
|
|
7251
|
-
}),
|
|
7252
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7253
|
-
className: "lc-stat-value",
|
|
7254
|
-
children: fmt(kpi.tokens)
|
|
7255
|
-
}),
|
|
7256
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7257
|
-
className: "lc-stat-sub",
|
|
7258
|
-
children: [
|
|
7259
|
-
t("stats.turns"),
|
|
7260
|
-
" ",
|
|
7261
|
-
fmt(kpi.turns)
|
|
7262
|
-
]
|
|
7263
|
-
})
|
|
7264
|
-
]
|
|
7640
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7641
|
+
className: "lc-stat-value",
|
|
7642
|
+
children: kpi.sessions
|
|
7265
7643
|
}),
|
|
7266
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
7267
|
-
className: "lc-stat
|
|
7268
|
-
children:
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
className: "lc-stat-sub",
|
|
7279
|
-
children: t("ov.kpi.sessionsSub", { n: kpi.costSessions })
|
|
7280
|
-
})
|
|
7281
|
-
]
|
|
7644
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7645
|
+
className: "lc-stat-sub",
|
|
7646
|
+
children: t("ov.kpi.ofTotal", { n: kpi.listed })
|
|
7647
|
+
})
|
|
7648
|
+
]
|
|
7649
|
+
}),
|
|
7650
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7651
|
+
className: "lc-stat lc-ov-kpi",
|
|
7652
|
+
children: [
|
|
7653
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7654
|
+
className: "lc-stat-label",
|
|
7655
|
+
children: t("ov.kpi.tokens")
|
|
7282
7656
|
}),
|
|
7283
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
7284
|
-
className: "lc-stat
|
|
7285
|
-
children:
|
|
7286
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7287
|
-
className: "lc-stat-label",
|
|
7288
|
-
children: t("stats.cacheHit")
|
|
7289
|
-
}),
|
|
7290
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7291
|
-
className: "lc-stat-value",
|
|
7292
|
-
children: kpi.cacheHit === null ? "—" : kpi.cacheHit + "%"
|
|
7293
|
-
}),
|
|
7294
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7295
|
-
className: "lc-stat-sub",
|
|
7296
|
-
children: t("ov.kpi.sessionsSub", { n: kpi.usageSessions })
|
|
7297
|
-
})
|
|
7298
|
-
]
|
|
7657
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7658
|
+
className: "lc-stat-value",
|
|
7659
|
+
children: fmt(kpi.tokens)
|
|
7299
7660
|
}),
|
|
7300
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
7301
|
-
className: "lc-stat
|
|
7661
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7662
|
+
className: "lc-stat-sub",
|
|
7302
7663
|
children: [
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
}),
|
|
7307
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7308
|
-
className: "lc-stat-value",
|
|
7309
|
-
children: fmt(kpi.toolCalls)
|
|
7310
|
-
}),
|
|
7311
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7312
|
-
className: "lc-stat-sub",
|
|
7313
|
-
children: t("ov.kpi.toolSub", { dur: fmtDuration(kpi.toolsMs) })
|
|
7314
|
-
})
|
|
7664
|
+
t("stats.turns"),
|
|
7665
|
+
" ",
|
|
7666
|
+
fmt(kpi.turns)
|
|
7315
7667
|
]
|
|
7668
|
+
})
|
|
7669
|
+
]
|
|
7670
|
+
}),
|
|
7671
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7672
|
+
className: "lc-stat lc-ov-kpi",
|
|
7673
|
+
children: [
|
|
7674
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7675
|
+
className: "lc-stat-label",
|
|
7676
|
+
children: t("stats.cost")
|
|
7316
7677
|
}),
|
|
7317
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
7318
|
-
className: "lc-stat
|
|
7319
|
-
children:
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7325
|
-
className: "lc-stat-value",
|
|
7326
|
-
children: fmtDuration(kpi.wallMs)
|
|
7327
|
-
}),
|
|
7328
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7329
|
-
className: "lc-stat-sub",
|
|
7330
|
-
children: t("ov.kpi.wallSub", { n: fmt(kpi.calls) })
|
|
7331
|
-
})
|
|
7332
|
-
]
|
|
7678
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7679
|
+
className: "lc-stat-value",
|
|
7680
|
+
children: kpi.cost === null ? "—" : formatCost(kpi.cost, currency)
|
|
7681
|
+
}),
|
|
7682
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7683
|
+
className: "lc-stat-sub",
|
|
7684
|
+
children: t("ov.kpi.sessionsSub", { n: kpi.costSessions })
|
|
7333
7685
|
})
|
|
7334
7686
|
]
|
|
7335
7687
|
}),
|
|
7336
7688
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7689
|
+
className: "lc-stat lc-ov-kpi",
|
|
7690
|
+
children: [
|
|
7691
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7692
|
+
className: "lc-stat-label",
|
|
7693
|
+
children: t("stats.cacheHit")
|
|
7694
|
+
}),
|
|
7695
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7696
|
+
className: "lc-stat-value",
|
|
7697
|
+
children: kpi.cacheHit === null ? "—" : kpi.cacheHit + "%"
|
|
7698
|
+
}),
|
|
7699
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7700
|
+
className: "lc-stat-sub",
|
|
7701
|
+
children: t("ov.kpi.sessionsSub", { n: kpi.usageSessions })
|
|
7702
|
+
})
|
|
7703
|
+
]
|
|
7704
|
+
}),
|
|
7705
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7706
|
+
className: "lc-stat lc-ov-kpi",
|
|
7707
|
+
children: [
|
|
7708
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7709
|
+
className: "lc-stat-label",
|
|
7710
|
+
children: t("stats.toolCalls")
|
|
7711
|
+
}),
|
|
7712
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7713
|
+
className: "lc-stat-value",
|
|
7714
|
+
children: fmt(kpi.toolCalls)
|
|
7715
|
+
}),
|
|
7716
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7717
|
+
className: "lc-stat-sub",
|
|
7718
|
+
children: t("ov.kpi.toolSub", { dur: fmtDuration(kpi.toolsMs) })
|
|
7719
|
+
})
|
|
7720
|
+
]
|
|
7721
|
+
}),
|
|
7722
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7723
|
+
className: "lc-stat lc-ov-kpi",
|
|
7724
|
+
children: [
|
|
7725
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7726
|
+
className: "lc-stat-label",
|
|
7727
|
+
children: t("timing.total")
|
|
7728
|
+
}),
|
|
7729
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7730
|
+
className: "lc-stat-value",
|
|
7731
|
+
children: fmtDuration(kpi.wallMs)
|
|
7732
|
+
}),
|
|
7733
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7734
|
+
className: "lc-stat-sub",
|
|
7735
|
+
children: t("ov.kpi.wallSub", { n: fmt(kpi.calls) })
|
|
7736
|
+
})
|
|
7737
|
+
]
|
|
7738
|
+
})
|
|
7739
|
+
]
|
|
7740
|
+
}),
|
|
7741
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7742
|
+
className: "lc-ov-stats",
|
|
7743
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewTokens, { tokens: kpi.tokenParts }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTiming, { timing: kpi.timing })]
|
|
7744
|
+
}),
|
|
7745
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7746
|
+
className: "lc-ov-body",
|
|
7747
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7748
|
+
className: "lc-ov-left",
|
|
7749
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7337
7750
|
className: "lc-card lc-ov-heat-card",
|
|
7338
7751
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7339
7752
|
className: "lc-card-title",
|
|
@@ -7366,8 +7779,7 @@ window.__ModuleLoader__.load({
|
|
|
7366
7779
|
onSelect: setDay,
|
|
7367
7780
|
today: todayKey()
|
|
7368
7781
|
})]
|
|
7369
|
-
}),
|
|
7370
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7782
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7371
7783
|
type: "button",
|
|
7372
7784
|
className: "lc-ov-settings",
|
|
7373
7785
|
onClick: () => {
|
|
@@ -7381,131 +7793,131 @@ window.__ModuleLoader__.load({
|
|
|
7381
7793
|
className: "lc-ov-settings-hint",
|
|
7382
7794
|
children: t("plugin.settingsOpen")
|
|
7383
7795
|
})]
|
|
7384
|
-
})
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
day !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7401
|
-
type: "button",
|
|
7402
|
-
className: "lc-ov-day-chip",
|
|
7403
|
-
title: t("ov.list.dayClear"),
|
|
7404
|
-
onClick: () => {
|
|
7405
|
-
setDay(null);
|
|
7406
|
-
},
|
|
7407
|
-
children: [t("ov.list.dayFilter", { day }), " ×"]
|
|
7408
|
-
}),
|
|
7409
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
7410
|
-
className: "lc-ov-search",
|
|
7411
|
-
type: "search",
|
|
7412
|
-
value: query,
|
|
7413
|
-
placeholder: t("ov.list.search"),
|
|
7414
|
-
"aria-label": t("ov.list.search"),
|
|
7415
|
-
onChange: (ev) => {
|
|
7416
|
-
setQuery(ev.target.value);
|
|
7417
|
-
}
|
|
7418
|
-
}),
|
|
7419
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7420
|
-
className: "lc-gran",
|
|
7421
|
-
role: "group",
|
|
7422
|
-
"aria-label": t("ov.list.sortLabel"),
|
|
7423
|
-
children: SORTS.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7796
|
+
})]
|
|
7797
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7798
|
+
className: "lc-ov-right",
|
|
7799
|
+
children: [
|
|
7800
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7801
|
+
className: "lc-ov-list-head",
|
|
7802
|
+
children: [
|
|
7803
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7804
|
+
className: "lc-ov-list-title",
|
|
7805
|
+
children: t("ov.list.title")
|
|
7806
|
+
}),
|
|
7807
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7808
|
+
className: "lc-ov-list-count",
|
|
7809
|
+
children: visible.length
|
|
7810
|
+
}),
|
|
7811
|
+
day !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7424
7812
|
type: "button",
|
|
7425
|
-
className: "lc-
|
|
7813
|
+
className: "lc-ov-day-chip",
|
|
7814
|
+
title: t("ov.list.dayClear"),
|
|
7426
7815
|
onClick: () => {
|
|
7427
|
-
|
|
7816
|
+
setDay(null);
|
|
7428
7817
|
},
|
|
7429
|
-
children: t("ov.list.
|
|
7430
|
-
},
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
className: "lc-empty",
|
|
7462
|
-
children: t(allRows.length === 0 ? "ov.list.empty" : "ov.list.noMatch")
|
|
7463
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7464
|
-
className: "lc-ov-grid",
|
|
7465
|
-
children: paged.items.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewCard, {
|
|
7466
|
-
row,
|
|
7467
|
-
...groups?.[row.id] !== void 0 ? { group: groups[row.id] } : {},
|
|
7468
|
-
costLabel: cardCostOf(row, prices, currency),
|
|
7469
|
-
now,
|
|
7470
|
-
onOpen: openOne
|
|
7471
|
-
}, row.id))
|
|
7472
|
-
}), paged.count > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7473
|
-
className: "lc-ov-pager",
|
|
7474
|
-
role: "navigation",
|
|
7475
|
-
"aria-label": t("ov.list.pager"),
|
|
7476
|
-
children: [
|
|
7477
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7818
|
+
children: [t("ov.list.dayFilter", { day }), " ×"]
|
|
7819
|
+
}),
|
|
7820
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
7821
|
+
className: "lc-ov-search",
|
|
7822
|
+
type: "search",
|
|
7823
|
+
value: query,
|
|
7824
|
+
placeholder: t("ov.list.search"),
|
|
7825
|
+
"aria-label": t("ov.list.search"),
|
|
7826
|
+
onChange: (ev) => {
|
|
7827
|
+
setQuery(ev.target.value);
|
|
7828
|
+
}
|
|
7829
|
+
}),
|
|
7830
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7831
|
+
className: "lc-gran",
|
|
7832
|
+
role: "group",
|
|
7833
|
+
"aria-label": t("ov.list.sortLabel"),
|
|
7834
|
+
children: SORTS.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7835
|
+
type: "button",
|
|
7836
|
+
className: "lc-gran-btn" + (sort === s ? " lc-gran-on" : ""),
|
|
7837
|
+
onClick: () => {
|
|
7838
|
+
setSort(s);
|
|
7839
|
+
},
|
|
7840
|
+
children: t("ov.list.sort." + s)
|
|
7841
|
+
}, s))
|
|
7842
|
+
})
|
|
7843
|
+
]
|
|
7844
|
+
}),
|
|
7845
|
+
chips.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7846
|
+
className: "lc-ov-groups",
|
|
7847
|
+
role: "group",
|
|
7848
|
+
"aria-label": t("ov.group.label"),
|
|
7849
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7478
7850
|
type: "button",
|
|
7479
|
-
className: "lc-ov-
|
|
7480
|
-
disabled: paged.index === 0,
|
|
7481
|
-
"aria-label": t("ov.list.prev"),
|
|
7851
|
+
className: "lc-ov-chip" + (group === null ? " lc-ov-chip-on" : ""),
|
|
7482
7852
|
onClick: () => {
|
|
7483
|
-
|
|
7853
|
+
setGroup(null);
|
|
7484
7854
|
},
|
|
7485
|
-
children: "
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7855
|
+
children: [t("ov.range.all"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7856
|
+
className: "lc-ov-chip-n",
|
|
7857
|
+
children: scoped.length
|
|
7858
|
+
})]
|
|
7859
|
+
}), chips.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7860
|
+
type: "button",
|
|
7861
|
+
className: "lc-ov-chip" + (group === c.key ? " lc-ov-chip-on" : ""),
|
|
7862
|
+
onClick: () => {
|
|
7863
|
+
setGroup(group === c.key ? null : c.key);
|
|
7864
|
+
},
|
|
7865
|
+
children: [c.key === "__ungrouped__" ? t("ov.group.ungrouped") : c.key, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7866
|
+
className: "lc-ov-chip-n",
|
|
7867
|
+
children: c.count
|
|
7868
|
+
})]
|
|
7869
|
+
}, c.key))]
|
|
7870
|
+
}),
|
|
7871
|
+
visible.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7872
|
+
className: "lc-empty",
|
|
7873
|
+
children: t(allRows.length === 0 ? "ov.list.empty" : "ov.list.noMatch")
|
|
7874
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7875
|
+
className: "lc-ov-grid",
|
|
7876
|
+
children: paged.items.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewCard, {
|
|
7877
|
+
row,
|
|
7878
|
+
...groups?.[row.id] !== void 0 ? { group: groups[row.id] } : {},
|
|
7879
|
+
costLabel: cardCostOf(row, prices, currency),
|
|
7880
|
+
now,
|
|
7881
|
+
onOpen: openOne
|
|
7882
|
+
}, row.id))
|
|
7883
|
+
}), paged.count > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7884
|
+
className: "lc-ov-pager",
|
|
7885
|
+
role: "navigation",
|
|
7886
|
+
"aria-label": t("ov.list.pager"),
|
|
7887
|
+
children: [
|
|
7888
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7889
|
+
type: "button",
|
|
7890
|
+
className: "lc-ov-pager-btn",
|
|
7891
|
+
disabled: paged.index === 0,
|
|
7892
|
+
"aria-label": t("ov.list.prev"),
|
|
7893
|
+
onClick: () => {
|
|
7894
|
+
setPage(paged.index - 1);
|
|
7895
|
+
},
|
|
7896
|
+
children: "‹"
|
|
7897
|
+
}),
|
|
7898
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7899
|
+
className: "lc-ov-pager-n",
|
|
7900
|
+
children: t("ov.list.page", {
|
|
7901
|
+
n: paged.index + 1,
|
|
7902
|
+
total: paged.count
|
|
7903
|
+
})
|
|
7904
|
+
}),
|
|
7905
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7906
|
+
type: "button",
|
|
7907
|
+
className: "lc-ov-pager-btn",
|
|
7908
|
+
disabled: paged.index === paged.count - 1,
|
|
7909
|
+
"aria-label": t("ov.list.next"),
|
|
7910
|
+
onClick: () => {
|
|
7911
|
+
setPage(paged.index + 1);
|
|
7912
|
+
},
|
|
7913
|
+
children: "›"
|
|
7492
7914
|
})
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
onClick: () => {
|
|
7500
|
-
setPage(paged.index + 1);
|
|
7501
|
-
},
|
|
7502
|
-
children: "›"
|
|
7503
|
-
})
|
|
7504
|
-
]
|
|
7505
|
-
})] })
|
|
7506
|
-
]
|
|
7507
|
-
})]
|
|
7508
|
-
})]
|
|
7915
|
+
]
|
|
7916
|
+
})] })
|
|
7917
|
+
]
|
|
7918
|
+
})]
|
|
7919
|
+
})
|
|
7920
|
+
] })]
|
|
7509
7921
|
})
|
|
7510
7922
|
});
|
|
7511
7923
|
}
|
|
@@ -9992,7 +10404,7 @@ window.__ModuleLoader__.load({
|
|
|
9992
10404
|
return function PluginInfo() {
|
|
9993
10405
|
const [latest, setLatest] = (0, react.useState)(null);
|
|
9994
10406
|
(0, react.useEffect)(() => {
|
|
9995
|
-
if ("0.
|
|
10407
|
+
if ("0.57.0".includes("-dev")) return;
|
|
9996
10408
|
let on = true;
|
|
9997
10409
|
fetchLatestVersion().then((v) => {
|
|
9998
10410
|
if (on && v) setLatest(v);
|
|
@@ -10001,8 +10413,8 @@ window.__ModuleLoader__.load({
|
|
|
10001
10413
|
on = false;
|
|
10002
10414
|
};
|
|
10003
10415
|
}, []);
|
|
10004
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
10005
|
-
const nameText = "dsh-context (v0.
|
|
10416
|
+
const update = latest !== null && isNewerVersion(latest, "0.57.0") ? latest : null;
|
|
10417
|
+
const nameText = "dsh-context (v0.57.0)";
|
|
10006
10418
|
const nameValue = [nameText];
|
|
10007
10419
|
if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10008
10420
|
className: "lc-pi-update",
|
|
@@ -10658,245 +11070,6 @@ window.__ModuleLoader__.load({
|
|
|
10658
11070
|
};
|
|
10659
11071
|
}
|
|
10660
11072
|
//#endregion
|
|
10661
|
-
//#region src/client/components/sliceList.tsx
|
|
10662
|
-
/**
|
|
10663
|
-
* The stats cards' slice rows: the donut's legend with numbers, two tiers
|
|
10664
|
-
* per slice — the primary line pairs the color dot and the name with the
|
|
10665
|
-
* bold share closing a fixed right column, and the secondary line drops the
|
|
10666
|
-
* absolute quantity (plus a qualifier like the call count) under the name in
|
|
10667
|
-
* muted small print. Hovering a row lights it and its donut segment together
|
|
10668
|
-
* (the shared hover key), and vice versa. No tracks or bars — the donut IS
|
|
10669
|
-
* the proportion chart; the rows are its legend. Preformatted strings in,
|
|
10670
|
-
* dumb markup out.
|
|
10671
|
-
*/
|
|
10672
|
-
function makeSliceList(kit) {
|
|
10673
|
-
return function SliceList(props) {
|
|
10674
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10675
|
-
className: "lc-sl flex-auto @max-[240px]/lc-card:basis-full",
|
|
10676
|
-
onMouseLeave: () => {
|
|
10677
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
10678
|
-
},
|
|
10679
|
-
children: props.rows.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10680
|
-
className: "lc-sl-row" + (r.dim ? " lc-sl-row-dim" : "") + (props.hoverKey !== void 0 && props.hoverKey === r.key ? " lc-sl-row-on" : ""),
|
|
10681
|
-
onMouseEnter: () => {
|
|
10682
|
-
if (props.onHoverKey !== void 0) props.onHoverKey(r.key);
|
|
10683
|
-
},
|
|
10684
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10685
|
-
className: "lc-sl-main",
|
|
10686
|
-
children: [
|
|
10687
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
|
|
10688
|
-
className: "lc-sl-dot",
|
|
10689
|
-
style: { background: r.color }
|
|
10690
|
-
}),
|
|
10691
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10692
|
-
className: "lc-sl-label",
|
|
10693
|
-
title: r.label,
|
|
10694
|
-
children: r.label
|
|
10695
|
-
}),
|
|
10696
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10697
|
-
className: "lc-sl-pct",
|
|
10698
|
-
children: r.pct
|
|
10699
|
-
})
|
|
10700
|
-
]
|
|
10701
|
-
}), r.count !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10702
|
-
className: "lc-sl-sub",
|
|
10703
|
-
title: r.count,
|
|
10704
|
-
children: r.count
|
|
10705
|
-
}) : null]
|
|
10706
|
-
}, r.key))
|
|
10707
|
-
});
|
|
10708
|
-
};
|
|
10709
|
-
}
|
|
10710
|
-
//#endregion
|
|
10711
|
-
//#region src/client/components/statsTiming.tsx
|
|
10712
|
-
/**
|
|
10713
|
-
* The Timing card: where the session's ACTIVE time went. The donut splits
|
|
10714
|
-
* whole-step wall time into the model-call slices — TTFT (step start → first
|
|
10715
|
-
* token, the wait) and LLM generation (first token → assistant message) —
|
|
10716
|
-
* and the tool-execution slice, with the residue as overhead; a call whose
|
|
10717
|
-
* stream recorded no token delta (legacy or aborted) stays unattributed and
|
|
10718
|
-
* lands in the residue. When the host folded the generation split (see
|
|
10719
|
-
* TimingTotals), the generation slice expands into what was being decoded:
|
|
10720
|
-
* thinking, answer text, and tool-call arguments. The slice rows lead with
|
|
10721
|
-
* the true duration and qualify it with a tally on the secondary line — the
|
|
10722
|
-
* model calls (TTFT), the tool runs, and the decode slices' own BLOCK counts
|
|
10723
|
-
* (zero to many per call, so a call count would be a false tally there). A
|
|
10724
|
-
* slice that never happened (zero time) is omitted entirely — the ring already skips
|
|
10725
|
-
* its arc, and a zero row would otherwise borrow the session's call count and
|
|
10726
|
-
* read as "no time, many calls". The donut and the rows sit side by side so
|
|
10727
|
-
* the head row stays half-height. Parallel tool calls each count, so the tools
|
|
10728
|
-
* figure can overlap — the ring clamps it into the post-model window while the
|
|
10729
|
-
* row numbers stay true.
|
|
10730
|
-
*/
|
|
10731
|
-
const COLOR = {
|
|
10732
|
-
ttft: "var(--color-blue-500)",
|
|
10733
|
-
reasoning: "var(--color-violet-500)",
|
|
10734
|
-
text: "var(--color-pink-500)",
|
|
10735
|
-
toolarg: "var(--color-amber-500)",
|
|
10736
|
-
tools: "var(--color-teal-500)",
|
|
10737
|
-
other: "var(--color-slate-400)"
|
|
10738
|
-
};
|
|
10739
|
-
/**
|
|
10740
|
-
* The decode-throughput figure, formatted exactly as the harness's own
|
|
10741
|
-
* session-stats seat formats it: whole tokens from ten up, one decimal below.
|
|
10742
|
-
*/
|
|
10743
|
-
function formatTokensPerSecond(tps) {
|
|
10744
|
-
const clamped = Math.max(0, tps);
|
|
10745
|
-
return clamped >= 10 ? String(Math.round(clamped)) : String(Math.round(clamped * 10) / 10);
|
|
10746
|
-
}
|
|
10747
|
-
function makeStatsTiming(kit, Donut) {
|
|
10748
|
-
const { t, fmt, fmtDuration, fmtShare } = kit;
|
|
10749
|
-
const SliceList = makeSliceList(kit);
|
|
10750
|
-
return function StatsTiming(props) {
|
|
10751
|
-
const [hoverKey, setHoverKey] = (0, react.useState)(null);
|
|
10752
|
-
const timing = props.timing;
|
|
10753
|
-
const wall = timing !== null && Number.isFinite(timing.wallMs) && timing.wallMs > 0 ? timing.wallMs : 0;
|
|
10754
|
-
const tps = timing !== null && typeof timing.speedMs === "number" && Number.isFinite(timing.speedMs) && timing.speedMs > 0 && typeof timing.speedTokens === "number" && Number.isFinite(timing.speedTokens) && timing.speedTokens >= 0 ? formatTokensPerSecond(timing.speedTokens / (timing.speedMs / 1e3)) : null;
|
|
10755
|
-
let segments = [];
|
|
10756
|
-
let rows = [];
|
|
10757
|
-
if (timing !== null && (wall > 0 || timing.calls > 0 || timing.toolCalls > 0)) {
|
|
10758
|
-
const callTimes = t("timing.callTimes", { n: fmt(timing.calls) });
|
|
10759
|
-
const modelSlices = [{
|
|
10760
|
-
key: "ttft",
|
|
10761
|
-
color: COLOR.ttft,
|
|
10762
|
-
label: t("timing.ttft"),
|
|
10763
|
-
ms: timing.ttftMs,
|
|
10764
|
-
times: callTimes
|
|
10765
|
-
}];
|
|
10766
|
-
const blockTimes = (blocks) => blocks !== void 0 && blocks > 0 ? t("timing.blockTimes", { n: fmt(blocks) }) : void 0;
|
|
10767
|
-
const buckets = [
|
|
10768
|
-
[
|
|
10769
|
-
"reasoning",
|
|
10770
|
-
COLOR.reasoning,
|
|
10771
|
-
t("timing.reasoning"),
|
|
10772
|
-
timing.reasoningMs ?? 0,
|
|
10773
|
-
blockTimes(timing.reasoningBlocks)
|
|
10774
|
-
],
|
|
10775
|
-
[
|
|
10776
|
-
"text",
|
|
10777
|
-
COLOR.text,
|
|
10778
|
-
t("timing.text"),
|
|
10779
|
-
timing.textMs ?? 0,
|
|
10780
|
-
blockTimes(timing.textBlocks)
|
|
10781
|
-
],
|
|
10782
|
-
[
|
|
10783
|
-
"toolarg",
|
|
10784
|
-
COLOR.toolarg,
|
|
10785
|
-
t("timing.toolArgs"),
|
|
10786
|
-
timing.toolArgMs ?? 0,
|
|
10787
|
-
blockTimes(timing.toolArgBlocks)
|
|
10788
|
-
]
|
|
10789
|
-
];
|
|
10790
|
-
if (buckets.some(([, , , ms]) => ms > 0)) for (const [key, color, label, ms, times] of buckets) modelSlices.push({
|
|
10791
|
-
key,
|
|
10792
|
-
color,
|
|
10793
|
-
label,
|
|
10794
|
-
ms,
|
|
10795
|
-
...times !== void 0 ? { times } : {}
|
|
10796
|
-
});
|
|
10797
|
-
else modelSlices.push({
|
|
10798
|
-
key: "gen",
|
|
10799
|
-
color: COLOR.reasoning,
|
|
10800
|
-
label: t("timing.gen"),
|
|
10801
|
-
ms: timing.genMs
|
|
10802
|
-
});
|
|
10803
|
-
const ttftRing = Math.min(timing.ttftMs, wall);
|
|
10804
|
-
const genRing = Math.max(0, Math.min(timing.genMs, wall - ttftRing));
|
|
10805
|
-
const modelRings = [ttftRing];
|
|
10806
|
-
let used = ttftRing;
|
|
10807
|
-
const genEnd = ttftRing + genRing;
|
|
10808
|
-
for (const slice of modelSlices.slice(1)) {
|
|
10809
|
-
const take = Math.max(0, Math.min(slice.ms, genEnd - used));
|
|
10810
|
-
modelRings.push(take);
|
|
10811
|
-
used += take;
|
|
10812
|
-
}
|
|
10813
|
-
const toolRing = Math.max(0, Math.min(timing.toolsMs, wall - ttftRing - genRing));
|
|
10814
|
-
const other = Math.max(0, wall - ttftRing - genRing - toolRing);
|
|
10815
|
-
const share = (ms) => wall > 0 ? ms / wall : 0;
|
|
10816
|
-
const countOf = (ms, times) => {
|
|
10817
|
-
const dur = fmtDuration(ms);
|
|
10818
|
-
if (times === void 0) return dur;
|
|
10819
|
-
return ms > 0 ? `${dur} · ${times}` : times;
|
|
10820
|
-
};
|
|
10821
|
-
const modelSegments = modelSlices.map((slice, index) => ({
|
|
10822
|
-
key: slice.key,
|
|
10823
|
-
color: slice.color,
|
|
10824
|
-
value: share(modelRings[index])
|
|
10825
|
-
}));
|
|
10826
|
-
const toolSlice = {
|
|
10827
|
-
key: "tools",
|
|
10828
|
-
color: COLOR.tools,
|
|
10829
|
-
label: t("timing.tools"),
|
|
10830
|
-
ms: timing.toolsMs,
|
|
10831
|
-
times: t("timing.toolTimes", { n: fmt(timing.toolCalls) })
|
|
10832
|
-
};
|
|
10833
|
-
const otherSlice = {
|
|
10834
|
-
key: "other",
|
|
10835
|
-
color: COLOR.other,
|
|
10836
|
-
label: t("timing.other"),
|
|
10837
|
-
ms: other
|
|
10838
|
-
};
|
|
10839
|
-
segments = [
|
|
10840
|
-
...modelSegments,
|
|
10841
|
-
{
|
|
10842
|
-
key: "tools",
|
|
10843
|
-
color: COLOR.tools,
|
|
10844
|
-
value: share(toolRing)
|
|
10845
|
-
},
|
|
10846
|
-
{
|
|
10847
|
-
key: "other",
|
|
10848
|
-
color: COLOR.other,
|
|
10849
|
-
value: share(other)
|
|
10850
|
-
}
|
|
10851
|
-
];
|
|
10852
|
-
const toRow = (slice) => ({
|
|
10853
|
-
key: slice.key,
|
|
10854
|
-
color: slice.color,
|
|
10855
|
-
label: slice.label,
|
|
10856
|
-
dim: slice.ms === 0,
|
|
10857
|
-
pct: fmtShare(slice.ms, wall),
|
|
10858
|
-
count: countOf(slice.ms, slice.times)
|
|
10859
|
-
});
|
|
10860
|
-
rows = [
|
|
10861
|
-
...modelSlices.map(toRow),
|
|
10862
|
-
toRow(toolSlice),
|
|
10863
|
-
toRow(otherSlice)
|
|
10864
|
-
].filter((row) => !row.dim);
|
|
10865
|
-
}
|
|
10866
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10867
|
-
className: "lc-card lc-col-stats lc-col-donut flex-1 min-w-[min(360px,100%)]",
|
|
10868
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10869
|
-
className: "lc-card-title",
|
|
10870
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10871
|
-
className: "lc-card-title-text",
|
|
10872
|
-
children: t("timing.title")
|
|
10873
|
-
}), tps !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10874
|
-
className: "lc-timing-tps",
|
|
10875
|
-
title: t("timing.tpsTip"),
|
|
10876
|
-
children: t("timing.tps", { tps })
|
|
10877
|
-
})]
|
|
10878
|
-
}), rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10879
|
-
className: "lc-empty",
|
|
10880
|
-
children: t("timing.empty")
|
|
10881
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10882
|
-
className: "lc-donut-row flex items-center justify-start gap-3 min-w-0 @max-[320px]/lc-card:gap-2 @max-[240px]/lc-card:flex-wrap",
|
|
10883
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Donut, {
|
|
10884
|
-
segments,
|
|
10885
|
-
size: 96,
|
|
10886
|
-
centerTop: wall > 0 ? fmtDuration(wall) : "—",
|
|
10887
|
-
centerSub: t("timing.total"),
|
|
10888
|
-
hoverKey,
|
|
10889
|
-
onHoverKey: setHoverKey
|
|
10890
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliceList, {
|
|
10891
|
-
rows,
|
|
10892
|
-
hoverKey,
|
|
10893
|
-
onHoverKey: setHoverKey
|
|
10894
|
-
})]
|
|
10895
|
-
})]
|
|
10896
|
-
});
|
|
10897
|
-
};
|
|
10898
|
-
}
|
|
10899
|
-
//#endregion
|
|
10900
11073
|
//#region src/client/components/statsTokens.tsx
|
|
10901
11074
|
/**
|
|
10902
11075
|
* The Token card: the session's whole billed token usage — the SAME total
|
|
@@ -11947,7 +12120,7 @@ window.__ModuleLoader__.load({
|
|
|
11947
12120
|
}
|
|
11948
12121
|
//#endregion
|
|
11949
12122
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/overview.css.mjs
|
|
11950
|
-
const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);height:calc(100% - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:inherit;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex}.lc-ov-balance:hover .lc-ov-balance-value{text-decoration:underline}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-ov-settings{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex:none;align-items:center;gap:8px;padding:9px 12px;font-size:12px;display:flex}.lc-ov-settings:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.lc-ov-settings-label{white-space:nowrap;flex:none;align-items:center;gap:6px;display:inline-flex}.lc-ov-settings-hint{text-align:right;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-heat-wrap{flex-direction:column;flex:1;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:8px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:18px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{height:20px;color:var(--dsw-alias-label-secondary);text-align:center;white-space:nowrap;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:18px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-heat-legend{justify-content:flex-end;align-items:center;gap:3px;padding-top:6px;display:flex}.lc-heat-key{color:var(--dsw-alias-label-secondary);font-size:9px}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
|
|
12123
|
+
const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);height:calc(100% - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:inherit;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex}.lc-ov-balance:hover .lc-ov-balance-value{text-decoration:underline}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-kpis{flex:none;grid-template-columns:repeat(6,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}@container lc-ov (width<=1000px){.lc-ov-kpis{grid-template-columns:repeat(3,minmax(0,1fr))}}@container lc-ov (width<=640px){.lc-ov-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}}.lc-ov-stats{flex:none;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-stats>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}@container lc-ov (width<=700px){.lc-ov-stats{grid-template-columns:minmax(0,1fr)}}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-ov-settings{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex:none;align-items:center;gap:8px;padding:9px 12px;font-size:12px;display:flex}.lc-ov-settings:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.lc-ov-settings-label{white-space:nowrap;flex:none;align-items:center;gap:6px;display:inline-flex}.lc-ov-settings-hint{text-align:right;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-heat-wrap{flex-direction:column;flex:1;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:8px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:18px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{height:20px;color:var(--dsw-alias-label-secondary);text-align:center;white-space:nowrap;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:18px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-heat-legend{justify-content:flex-end;align-items:center;gap:3px;padding-top:6px;display:flex}.lc-heat-key{color:var(--dsw-alias-label-secondary);font-size:9px}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
|
|
11951
12124
|
const tagId = "dsh-context/overview.css";
|
|
11952
12125
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
11953
12126
|
const tag = document.createElement("style");
|