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.
Files changed (3) hide show
  1. package/lib/client.js +646 -473
  2. package/lib/index.js +41 -4
  3. 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 body is a 3:7 column pair: the insight column (the KPI 2×3 block over
7086
- * the activity heatmap, then the preferences entry row) beside the session
7087
- * column (search, group chips, and the card grid); the heatmap keeps its own
7088
- * fixed 8-week window and PINs the list to a picked day (the panel's
7089
- * drill-down gesture). A session card click jumps to that session through
7090
- * the harness's own selection verb (openSessionVia) and closes the panel.
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)("div", {
7221
- className: "lc-ov-body",
7222
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
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-kpis",
7634
+ className: "lc-stat lc-ov-kpi",
7227
7635
  children: [
7228
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7229
- className: "lc-stat lc-ov-kpi",
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.jsxs)("div", {
7246
- className: "lc-stat lc-ov-kpi",
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.jsxs)("div", {
7267
- className: "lc-stat lc-ov-kpi",
7268
- children: [
7269
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7270
- className: "lc-stat-label",
7271
- children: t("stats.cost")
7272
- }),
7273
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7274
- className: "lc-stat-value",
7275
- children: kpi.cost === null ? "—" : formatCost(kpi.cost, currency)
7276
- }),
7277
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
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.jsxs)("div", {
7284
- className: "lc-stat lc-ov-kpi",
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)("div", {
7301
- className: "lc-stat lc-ov-kpi",
7661
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7662
+ className: "lc-stat-sub",
7302
7663
  children: [
7303
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7304
- className: "lc-stat-label",
7305
- children: t("stats.toolCalls")
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.jsxs)("div", {
7318
- className: "lc-stat lc-ov-kpi",
7319
- children: [
7320
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7321
- className: "lc-stat-label",
7322
- children: t("timing.total")
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
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7387
- className: "lc-ov-right",
7388
- children: [
7389
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7390
- className: "lc-ov-list-head",
7391
- children: [
7392
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7393
- className: "lc-ov-list-title",
7394
- children: t("ov.list.title")
7395
- }),
7396
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7397
- className: "lc-ov-list-count",
7398
- children: visible.length
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-gran-btn" + (sort === s ? " lc-gran-on" : ""),
7813
+ className: "lc-ov-day-chip",
7814
+ title: t("ov.list.dayClear"),
7426
7815
  onClick: () => {
7427
- setSort(s);
7816
+ setDay(null);
7428
7817
  },
7429
- children: t("ov.list.sort." + s)
7430
- }, s))
7431
- })
7432
- ]
7433
- }),
7434
- chips.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7435
- className: "lc-ov-groups",
7436
- role: "group",
7437
- "aria-label": t("ov.group.label"),
7438
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7439
- type: "button",
7440
- className: "lc-ov-chip" + (group === null ? " lc-ov-chip-on" : ""),
7441
- onClick: () => {
7442
- setGroup(null);
7443
- },
7444
- children: [t("ov.range.all"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7445
- className: "lc-ov-chip-n",
7446
- children: scoped.length
7447
- })]
7448
- }), chips.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7449
- type: "button",
7450
- className: "lc-ov-chip" + (group === c.key ? " lc-ov-chip-on" : ""),
7451
- onClick: () => {
7452
- setGroup(group === c.key ? null : c.key);
7453
- },
7454
- children: [c.key === "__ungrouped__" ? t("ov.group.ungrouped") : c.key, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7455
- className: "lc-ov-chip-n",
7456
- children: c.count
7457
- })]
7458
- }, c.key))]
7459
- }),
7460
- visible.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
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-pager-btn",
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
- setPage(paged.index - 1);
7853
+ setGroup(null);
7484
7854
  },
7485
- children: "‹"
7486
- }),
7487
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7488
- className: "lc-ov-pager-n",
7489
- children: t("ov.list.page", {
7490
- n: paged.index + 1,
7491
- total: paged.count
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
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7495
- type: "button",
7496
- className: "lc-ov-pager-btn",
7497
- disabled: paged.index === paged.count - 1,
7498
- "aria-label": t("ov.list.next"),
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.56.2".includes("-dev")) return;
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.56.2") ? latest : null;
10005
- const nameText = "dsh-context (v0.56.2)";
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");