dsh-token-total 1.1.0 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/lib/client.js +423 -524
- package/lib/index.js +209 -6
- package/lib/types/client/UsageCards.d.ts +28 -0
- package/lib/types/client/context-window-core.d.ts +9 -0
- package/lib/types/client/session-usage-locales.d.ts +17 -0
- package/lib/types/context-window.d.ts +5 -0
- package/lib/types/usage-buckets.d.ts +14 -0
- package/lib/types/usage-composition.d.ts +12 -0
- package/lib/types/wire.d.ts +30 -0
- package/package.json +1 -1
- package/src/client/SessionUsageView.tsx +6 -6
- package/src/client/StatsLineEnhanced.tsx +59 -29
- package/src/client/UsageCards.module.css +74 -0
- package/src/client/UsageCards.tsx +244 -0
- package/src/client/context-window-core.ts +39 -0
- package/src/client/session-usage-locales.ts +27 -3
- package/src/client/stats-line-core.ts +14 -5
- package/src/context-window.ts +15 -5
- package/src/session-detail.ts +3 -2
- package/src/usage-buckets.ts +27 -0
- package/src/usage-composition.ts +203 -0
- package/src/wire.ts +37 -0
package/lib/client.js
CHANGED
|
@@ -31281,16 +31281,25 @@ window.__ModuleLoader__.load({
|
|
|
31281
31281
|
const CJK_TOKENS_PER_CHAR = .6;
|
|
31282
31282
|
const ASCII_TOKENS_PER_CHAR = .3;
|
|
31283
31283
|
/**
|
|
31284
|
-
* Shortest observable decode window before a live rate is published, in ms
|
|
31285
|
-
*
|
|
31286
|
-
*
|
|
31287
|
-
*
|
|
31284
|
+
* Shortest observable decode window before a live rate is published, in ms.
|
|
31285
|
+
* Below it the first chunk's burst would read as hundreds of tok/s; the
|
|
31286
|
+
* sampler also publishes its FIRST reading at exactly this mark rather than at
|
|
31287
|
+
* the next steady cadence.
|
|
31288
|
+
*
|
|
31289
|
+
* The floor is deliberately short — 200 ms, down from the 500 ms MiMo-Code's
|
|
31290
|
+
* sidebar tps holds — because every step opens a window of its own, and at
|
|
31291
|
+
* 500 ms a step that finishes inside the floor never published at all: a run
|
|
31292
|
+
* of short steps (one burst of tool-call arguments, then settle) kept showing
|
|
31293
|
+
* a figure measured somewhere else while the screen was full of live output.
|
|
31294
|
+
* At this length the opening burst is judged by the plausibility check below
|
|
31295
|
+
* rather than by waiting it out, and the smoothing constant still carries the
|
|
31296
|
+
* first reading only part of the way into an established figure.
|
|
31288
31297
|
*
|
|
31289
31298
|
* The floor is a publishing gate, NOT a rejection gate: it is applied after the
|
|
31290
31299
|
* plausibility check below, so a backlog frame can never dodge that check by
|
|
31291
31300
|
* arriving early (see {@link StreamingRateSampler.observe}).
|
|
31292
31301
|
*/
|
|
31293
|
-
const MIN_STREAM_WINDOW_MS =
|
|
31302
|
+
const MIN_STREAM_WINDOW_MS = 200;
|
|
31294
31303
|
/**
|
|
31295
31304
|
* Longest window a live rate may span, in ms. This is the bound the previous
|
|
31296
31305
|
* cumulative quotient lacked: any artifact inside the window — a frame the UI
|
|
@@ -31652,9 +31661,14 @@ window.__ModuleLoader__.load({
|
|
|
31652
31661
|
* cannot see.
|
|
31653
31662
|
*
|
|
31654
31663
|
* Streaming throughput replaces the gauge pill's speed
|
|
31655
|
-
* figure with a live estimate
|
|
31656
|
-
* the
|
|
31657
|
-
*
|
|
31664
|
+
* figure with a live estimate of what is happening now, and hands the slot back
|
|
31665
|
+
* to the session's decode average when nothing is streaming. While a step
|
|
31666
|
+
* streams the live rate is the only reading that can match the pace of the
|
|
31667
|
+
* answer on screen; once the step settles it becomes a figure from a window
|
|
31668
|
+
* nobody is watching any more, so the pill returns to the same quotient the
|
|
31669
|
+
* usage view quotes (ΣoutputTokens / ΣdecodeMs) instead of freezing on the last
|
|
31670
|
+
* measurement. That subscription lives in its own child component, so stream
|
|
31671
|
+
* deltas never re-render the pills. The live
|
|
31658
31672
|
* figure itself is measured by a bounded sliding window over observed token
|
|
31659
31673
|
* growth (stats-line-core's StreamingRateSampler): the earlier cumulative
|
|
31660
31674
|
* quotient charged a whole step's text to a window that had just opened, so a
|
|
@@ -31740,23 +31754,35 @@ window.__ModuleLoader__.load({
|
|
|
31740
31754
|
* The gauge pill's always-on streaming speed slot.
|
|
31741
31755
|
*
|
|
31742
31756
|
* It subscribes to `legacy.partial` itself so per-frame deltas re-render only
|
|
31743
|
-
* this child, not the surrounding pills.
|
|
31744
|
-
*
|
|
31745
|
-
*
|
|
31746
|
-
*
|
|
31757
|
+
* this child, not the surrounding pills. Two figures share the slot, chosen by
|
|
31758
|
+
* whether a step is streaming right now:
|
|
31759
|
+
*
|
|
31760
|
+
* - while one is (`partial != null`), the measured rate of THAT stream — a
|
|
31761
|
+
* bounded sliding window over observed token growth (stats-line-core's
|
|
31762
|
+
* StreamingRateSampler), the only reading that can track the pace of the
|
|
31763
|
+
* answer currently on screen;
|
|
31764
|
+
* - otherwise the session's own decode average, ΣoutputTokens / ΣdecodeMs —
|
|
31765
|
+
* the same quotient `decodeRateOf` quotes in the usage view. An idle composer
|
|
31766
|
+
* must not freeze on whatever the last window happened to measure: a settled
|
|
31767
|
+
* thirty-step turn kept showing its final second-long reading (57 tok/s)
|
|
31768
|
+
* beside the usage view's 156 tok/s for the same work, and the two read as a
|
|
31769
|
+
* contradiction rather than as two scopes of one number.
|
|
31770
|
+
*
|
|
31771
|
+
* The measured reading carries the step it was taken in, so a new step cannot
|
|
31772
|
+
* inherit the previous one's figure before its own window publishes; nothing is
|
|
31773
|
+
* held across the gap — the gap is exactly when the average applies.
|
|
31747
31774
|
*
|
|
31748
31775
|
* One window covers a whole step, across every output kind the accumulator
|
|
31749
31776
|
* carries — reasoning, answer text, and Tool-call arguments — because the
|
|
31750
31777
|
* provider bills all three as completion tokens and a step emits them in
|
|
31751
|
-
* sequence.
|
|
31752
|
-
* settles it and the next step has not begun), so Tool execution shows the
|
|
31753
|
-
* official figure until the next step opens its own window.
|
|
31778
|
+
* sequence.
|
|
31754
31779
|
*
|
|
31755
31780
|
* Sampling happens in a layout effect and the figure it yields is published as
|
|
31756
31781
|
* state, so no render reads a clock or mutates the window: a render React starts
|
|
31757
31782
|
* and then discards must not decide what the next committed one displays. The
|
|
31758
31783
|
* rate itself comes from stats-line-core's StreamingRateSampler — a bounded
|
|
31759
|
-
* window over observed token growth, smoothed, and held while nothing arrives
|
|
31784
|
+
* window over observed token growth, smoothed, and held while nothing arrives
|
|
31785
|
+
* inside the step it belongs to.
|
|
31760
31786
|
*/
|
|
31761
31787
|
function LiveSpeed({ useChat, scale, fallback, t }) {
|
|
31762
31788
|
const partial = useChat((s) => s.legacy.partial);
|
|
@@ -31768,22 +31794,20 @@ window.__ModuleLoader__.load({
|
|
|
31768
31794
|
rate: new StreamingRateSampler()
|
|
31769
31795
|
};
|
|
31770
31796
|
const rate = sampler.current.rate;
|
|
31771
|
-
const [
|
|
31772
|
-
key: null,
|
|
31773
|
-
rate: null
|
|
31774
|
-
});
|
|
31797
|
+
const [reading, setReading] = (0, react.useState)(null);
|
|
31775
31798
|
const scaleRef = (0, react.useRef)(scale);
|
|
31776
31799
|
const [beat, setBeat] = (0, react.useState)(0);
|
|
31777
31800
|
(0, react.useLayoutEffect)(() => {
|
|
31801
|
+
if (key === null) return;
|
|
31778
31802
|
if (scaleRef.current !== scale) {
|
|
31779
31803
|
scaleRef.current = scale;
|
|
31780
31804
|
rate.reset();
|
|
31781
31805
|
}
|
|
31782
31806
|
rate.observe(tokens, Date.now());
|
|
31783
31807
|
const next = rate.ratePerSecond();
|
|
31784
|
-
|
|
31808
|
+
if (next !== null) setReading((previous) => previous !== null && previous.key === key && previous.value === next ? previous : {
|
|
31785
31809
|
key,
|
|
31786
|
-
|
|
31810
|
+
value: next
|
|
31787
31811
|
});
|
|
31788
31812
|
}, [
|
|
31789
31813
|
tokens,
|
|
@@ -31802,8 +31826,8 @@ window.__ModuleLoader__.load({
|
|
|
31802
31826
|
clearInterval(handle);
|
|
31803
31827
|
};
|
|
31804
31828
|
}, [streaming]);
|
|
31805
|
-
const
|
|
31806
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SpeedFigure, { text:
|
|
31829
|
+
const measured = reading !== null && reading.key === key ? reading.value : null;
|
|
31830
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SpeedFigure, { text: measured === null ? fallback : t("stats.tokensPerSecond", { throughput: formatTokensPerSecond(measured) }) });
|
|
31807
31831
|
}
|
|
31808
31832
|
function TimePill({ stats, t, dialog, useChat, scale, dock }) {
|
|
31809
31833
|
const { open, setOpen, rootRef, panelRef, pos } = useStatDialog(dialog);
|
|
@@ -32707,24 +32731,55 @@ window.__ModuleLoader__.load({
|
|
|
32707
32731
|
};
|
|
32708
32732
|
}
|
|
32709
32733
|
//#endregion
|
|
32710
|
-
//#region src/
|
|
32711
|
-
|
|
32712
|
-
|
|
32713
|
-
"
|
|
32714
|
-
"
|
|
32715
|
-
"
|
|
32716
|
-
"
|
|
32717
|
-
"
|
|
32718
|
-
"
|
|
32734
|
+
//#region src/usage-buckets.ts
|
|
32735
|
+
const USAGE_BUCKET_ORDER = [
|
|
32736
|
+
"systemPrompt",
|
|
32737
|
+
"user",
|
|
32738
|
+
"skill",
|
|
32739
|
+
"toolResult",
|
|
32740
|
+
"toolDef",
|
|
32741
|
+
"injected",
|
|
32742
|
+
"assistant",
|
|
32743
|
+
"output"
|
|
32719
32744
|
];
|
|
32720
|
-
/**
|
|
32721
|
-
|
|
32745
|
+
/** The buckets that describe the live window (output never occupies it). */
|
|
32746
|
+
const CONTEXT_BUCKET_ORDER = USAGE_BUCKET_ORDER.filter((key) => key !== "output");
|
|
32747
|
+
//#endregion
|
|
32748
|
+
//#region src/client/context-window-core.ts
|
|
32749
|
+
/** Which legend bucket one window row belongs to.
|
|
32750
|
+
*
|
|
32751
|
+
* The row categories are the treemap's (they group files separately); the
|
|
32752
|
+
* cards speak the eight-bucket legend instead. A folded `rest` row lost its
|
|
32753
|
+
* kind, so a conversation tail lands with the assistant — the larger half of
|
|
32754
|
+
* what a turn replays.
|
|
32755
|
+
*/
|
|
32756
|
+
function contextBucketOf(item) {
|
|
32757
|
+
switch (item.category) {
|
|
32758
|
+
case "system": return "systemPrompt";
|
|
32759
|
+
case "tools": return "toolDef";
|
|
32760
|
+
case "memory": return item.kind === "skill-catalog" ? "skill" : "injected";
|
|
32761
|
+
case "files":
|
|
32762
|
+
case "results": return "toolResult";
|
|
32763
|
+
case "conversation": return item.kind === "user" ? "user" : "assistant";
|
|
32764
|
+
default: return "injected";
|
|
32765
|
+
}
|
|
32766
|
+
}
|
|
32767
|
+
/** The live window folded into the same legend the spend card prints, in
|
|
32768
|
+
* legend order and keeping zero buckets so both cards stay aligned. */
|
|
32769
|
+
function contextBucketTotals(items) {
|
|
32722
32770
|
const totals = /* @__PURE__ */ new Map();
|
|
32723
|
-
for (const item of items)
|
|
32724
|
-
|
|
32771
|
+
for (const item of items) {
|
|
32772
|
+
const key = contextBucketOf(item);
|
|
32773
|
+
totals.set(key, (totals.get(key) ?? 0) + item.tokens);
|
|
32774
|
+
}
|
|
32775
|
+
return CONTEXT_BUCKET_ORDER.map((key) => ({
|
|
32725
32776
|
key,
|
|
32726
32777
|
tokens: totals.get(key) ?? 0
|
|
32727
|
-
}))
|
|
32778
|
+
}));
|
|
32779
|
+
}
|
|
32780
|
+
/** What a share of the whole is, for the legend column. */
|
|
32781
|
+
function bucketPercent(tokens, total) {
|
|
32782
|
+
return total > 0 ? tokens / total * 100 : 0;
|
|
32728
32783
|
}
|
|
32729
32784
|
/** Window occupancy in whole percent, or null when the host never reported the
|
|
32730
32785
|
* model's window (the panel then says "unknown" instead of guessing one). */
|
|
@@ -32743,98 +32798,10 @@ window.__ModuleLoader__.load({
|
|
|
32743
32798
|
estimateDiffers: context.promptTokens !== null && gap > .15
|
|
32744
32799
|
};
|
|
32745
32800
|
}
|
|
32746
|
-
/** Aspect ratio of the worst tile in a row of `sum` area, given the strip's
|
|
32747
|
-
* short side — the squarified-treemap cost function. */
|
|
32748
|
-
function worstRatio(sum, max, min, side) {
|
|
32749
|
-
const squared = sum * sum;
|
|
32750
|
-
const sideSquared = side * side;
|
|
32751
|
-
return Math.max(sideSquared * max / squared, squared / (sideSquared * min));
|
|
32752
|
-
}
|
|
32753
|
-
const round = (value) => Math.round(value * 100) / 100;
|
|
32754
|
-
/**
|
|
32755
|
-
* Squarified treemap (Bruls, Huizing & van Wijk): tile `width`×`height` with
|
|
32756
|
-
* one block per positive weight, proportional to its tokens and as close to
|
|
32757
|
-
* square as the greedy row packing allows. Blocks come back in the input's
|
|
32758
|
-
* descending-weight order, covering the whole area.
|
|
32759
|
-
*/
|
|
32760
|
-
function treemapLayout(rows, width, height) {
|
|
32761
|
-
const ordered = rows.filter((row) => row.tokens > 0 && Number.isFinite(row.tokens)).sort((a, b) => b.tokens - a.tokens);
|
|
32762
|
-
if (ordered.length === 0 || !(width > 0) || !(height > 0)) return [];
|
|
32763
|
-
const total = ordered.reduce((sum, row) => sum + row.tokens, 0);
|
|
32764
|
-
const scale = width * height / total;
|
|
32765
|
-
const areas = ordered.map((row) => ({
|
|
32766
|
-
key: row.key,
|
|
32767
|
-
area: row.tokens * scale
|
|
32768
|
-
}));
|
|
32769
|
-
const boxes = [];
|
|
32770
|
-
let x = 0;
|
|
32771
|
-
let y = 0;
|
|
32772
|
-
let w = width;
|
|
32773
|
-
let h = height;
|
|
32774
|
-
let index = 0;
|
|
32775
|
-
while (index < areas.length) {
|
|
32776
|
-
const side = Math.min(w, h);
|
|
32777
|
-
if (!(side > 0)) break;
|
|
32778
|
-
let end = index + 1;
|
|
32779
|
-
let sum = areas[index].area;
|
|
32780
|
-
let max = sum;
|
|
32781
|
-
let min = sum;
|
|
32782
|
-
let worst = worstRatio(sum, max, min, side);
|
|
32783
|
-
while (end < areas.length) {
|
|
32784
|
-
const area = areas[end].area;
|
|
32785
|
-
const nextSum = sum + area;
|
|
32786
|
-
const nextMax = Math.max(max, area);
|
|
32787
|
-
const nextMin = Math.min(min, area);
|
|
32788
|
-
const nextWorst = worstRatio(nextSum, nextMax, nextMin, side);
|
|
32789
|
-
if (nextWorst > worst) break;
|
|
32790
|
-
sum = nextSum;
|
|
32791
|
-
max = nextMax;
|
|
32792
|
-
min = nextMin;
|
|
32793
|
-
worst = nextWorst;
|
|
32794
|
-
end++;
|
|
32795
|
-
}
|
|
32796
|
-
const thickness = sum / side;
|
|
32797
|
-
if (!(thickness > 0)) break;
|
|
32798
|
-
let offset = 0;
|
|
32799
|
-
for (let k = index; k < end; k++) {
|
|
32800
|
-
const length = areas[k].area / thickness;
|
|
32801
|
-
const box = w >= h ? {
|
|
32802
|
-
key: areas[k].key,
|
|
32803
|
-
x,
|
|
32804
|
-
y: y + offset,
|
|
32805
|
-
width: thickness,
|
|
32806
|
-
height: length
|
|
32807
|
-
} : {
|
|
32808
|
-
key: areas[k].key,
|
|
32809
|
-
x: x + offset,
|
|
32810
|
-
y,
|
|
32811
|
-
width: length,
|
|
32812
|
-
height: thickness
|
|
32813
|
-
};
|
|
32814
|
-
boxes.push({
|
|
32815
|
-
key: box.key,
|
|
32816
|
-
x: round(box.x),
|
|
32817
|
-
y: round(box.y),
|
|
32818
|
-
width: round(box.width),
|
|
32819
|
-
height: round(box.height)
|
|
32820
|
-
});
|
|
32821
|
-
offset += length;
|
|
32822
|
-
}
|
|
32823
|
-
if (w >= h) {
|
|
32824
|
-
x += thickness;
|
|
32825
|
-
w -= thickness;
|
|
32826
|
-
} else {
|
|
32827
|
-
y += thickness;
|
|
32828
|
-
h -= thickness;
|
|
32829
|
-
}
|
|
32830
|
-
index = end;
|
|
32831
|
-
}
|
|
32832
|
-
return boxes;
|
|
32833
|
-
}
|
|
32834
32801
|
//#endregion
|
|
32835
|
-
//#region \0token-total-css:/home/runner/work/dsh-token-total/dsh-token-total/src/client/
|
|
32836
|
-
const css$1 = "/* The context-window panel:
|
|
32837
|
-
const tagId$1 = "dsh-token-total/
|
|
32802
|
+
//#region \0token-total-css:/home/runner/work/dsh-token-total/dsh-token-total/src/client/UsageCards.module.css?inline
|
|
32803
|
+
const css$1 = "/* The two live cards that replaced the context-window panel: the window's\n * current composition on the left, the session's spend composition on the\n * right. Surfaces ride the host semantic tokens (`--dsw-alias-*`) like every\n * other card on this page, so light/dark flip for free; only the eight bucket\n * hues are this plugin's own (they match the reference design and the\n * dashboard's chart accents). Each bucket sets `--bucket`, and the swatch, ring\n * segment, bar segment and value text all read that one variable. */\n.grid_673061119 { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:18px; margin:0 0 18px; align-items:stretch; }\n.card_673061119 { min-width:0; display:flex; flex-direction:column; background:var(--dsw-alias-bg-layer-1); border:1px solid var(--dsw-alias-border-l2); border-radius:12px; overflow:hidden; }\n.head_673061119 { display:flex; align-items:center; justify-content:space-between; gap:10px; min-height:42px; padding:12px 16px 0; }\n.title_673061119 { margin:0; font-size:13px; font-weight:600; color:var(--dsw-alias-label-primary); white-space:nowrap; }\n.model_673061119 { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:11px; color:var(--dsw-alias-label-tertiary); }\n.cacheBadge_673061119 { flex:none; font-size:11px; color:var(--dsw-alias-label-tertiary); white-space:nowrap; }\n.cacheBadge_673061119 b { margin-left:2px; font-weight:600; color:var(--dsw-alias-state-success-primary); font-variant-numeric:tabular-nums; }\n.body_673061119 { flex:1 1 auto; padding:12px 16px 16px; }\n/* ── Left card: the live window ─────────────────────────────────────────── */\n.totalRow_673061119 { display:flex; align-items:baseline; justify-content:space-between; gap:12px; }\n.total_673061119 { font-size:20px; font-weight:650; letter-spacing:-.3px; color:var(--dsw-alias-label-primary); font-variant-numeric:tabular-nums; white-space:nowrap; }\n.totalShare_673061119 { flex:none; font-size:11px; color:var(--dsw-alias-label-tertiary); white-space:nowrap; }\n.totalShare_673061119 b { color:var(--dsw-alias-label-primary-dimmed); font-weight:600; font-variant-numeric:tabular-nums; }\n/* A stacked share bar over the drawn content; the empty state is the same\n * diagonal hatch the old panel used for \"no data yet\". */\n.bar_673061119 { display:flex; width:100%; height:10px; margin:12px 0 14px; border-radius:999px; overflow:hidden; background:var(--dsw-alias-border-l1); }\n.barSeg_673061119 { height:100%; flex:none; background:var(--bucket); transition:width .35s ease; }\n.barEmpty_673061119 { display:block; width:100%; height:100%; background:repeating-linear-gradient(90deg,var(--dsw-alias-border-l2) 0 4px,transparent 4px 8px); }\n/* ── Right card: the spend composition ──────────────────────────────────── */\n.statsMain_673061119 { display:flex; align-items:center; gap:16px; }\n.ringWrap_673061119 { position:relative; flex:none; width:116px; height:116px; }\n.ring_673061119 { display:block; }\n.ringTrack_673061119 { stroke:var(--dsw-alias-border-l1); }\n.ringCenter_673061119 { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:1px; text-align:center; pointer-events:none; }\n.ringTotal_673061119 { font-size:17px; font-weight:650; letter-spacing:-.3px; color:var(--dsw-alias-label-primary); font-variant-numeric:tabular-nums; }\n.ringCaption_673061119 { font-size:10px; color:var(--dsw-alias-label-tertiary); }\n/* Legend: two columns, column-major (left column is the first four buckets,\n * the right column the rest), so the reading order matches the ring. */\n.legend_673061119 { flex:1 1 auto; min-width:0; display:grid; grid-template-rows:repeat(4,auto); grid-auto-flow:column; gap:7px 16px; margin:0; padding:0; list-style:none; }\n.legendRow_673061119 { display:grid; grid-template-columns:7px minmax(0,1fr) auto auto; align-items:center; gap:6px; min-width:0; font-size:11px; }\n.swatch_673061119 { width:7px; height:7px; border-radius:50%; background:var(--bucket); }\n.legendName_673061119 { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--dsw-alias-label-secondary); }\n.legendPercent_673061119 { color:var(--dsw-alias-label-tertiary); font-variant-numeric:tabular-nums; text-align:right; white-space:nowrap; }\n.legendValue_673061119 { color:var(--bucket); font-variant-numeric:tabular-nums; font-weight:500; text-align:right; white-space:nowrap; }\n.legendSuffix_673061119 { color:var(--dsw-alias-label-tertiary); font-weight:400; }\n.empty_673061119 { margin:16px 0 18px; text-align:center; font-size:12px; color:var(--dsw-alias-label-tertiary); }\n/* ── Bucket palette ─────────────────────────────────────────────────────── */\n.toneSystemPrompt_673061119 { --bucket:#6f6bf5; }\n.toneUser_673061119 { --bucket:#2bb673; }\n.toneSkill_673061119 { --bucket:#16b8a6; }\n.toneToolResult_673061119 { --bucket:#4d6bfe; }\n.toneToolDef_673061119 { --bucket:#f0a53a; }\n.toneInjected_673061119 { --bucket:#a75ff0; }\n.toneAssistant_673061119 { --bucket:#7c8cff; }\n.toneOutput_673061119 { --bucket:#ef5da8; }\n/* Narrow surfaces: one card per row, and the legends go back to wrapping rows\n * (a two-column legend cannot keep 11 px labels readable under ~340 px). */\n@container (max-width:880px) {\n .grid_673061119 { grid-template-columns:minmax(0,1fr); }\n}\n@container (max-width:520px) {\n .statsMain_673061119 { flex-wrap:wrap; justify-content:center; }\n .legend_673061119 { grid-template-rows:none; grid-auto-flow:row; grid-template-columns:minmax(0,1fr); width:100%; }\n}\n@media (prefers-reduced-motion:reduce) {\n .barSeg_673061119 { transition:none; }\n}\n/* Dark theme: the host re-resolves its aliases; only the bucket hues need a\n * brighter variant so chips and ring segments hold their weight. */\nbody[data-ds-dark-theme] .toneSystemPrompt_673061119 { --bucket:#9b96ff; }\nbody[data-ds-dark-theme] .toneUser_673061119 { --bucket:#3ed08a; }\nbody[data-ds-dark-theme] .toneSkill_673061119 { --bucket:#2fd6c2; }\nbody[data-ds-dark-theme] .toneToolResult_673061119 { --bucket:#7d95ff; }\nbody[data-ds-dark-theme] .toneToolDef_673061119 { --bucket:#f5b861; }\nbody[data-ds-dark-theme] .toneInjected_673061119 { --bucket:#c184ff; }\nbody[data-ds-dark-theme] .toneAssistant_673061119 { --bucket:#9aabff; }\nbody[data-ds-dark-theme] .toneOutput_673061119 { --bucket:#ff85c0; }\n";
|
|
32804
|
+
const tagId$1 = "dsh-token-total/UsageCards.module.css";
|
|
32838
32805
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$1 + "\"]") === null) {
|
|
32839
32806
|
const tag = document.createElement("style");
|
|
32840
32807
|
tag.dataset.plugin = "dsh-token-total";
|
|
@@ -32842,423 +32809,305 @@ window.__ModuleLoader__.load({
|
|
|
32842
32809
|
tag.textContent = css$1;
|
|
32843
32810
|
document.head.appendChild(tag);
|
|
32844
32811
|
}
|
|
32845
|
-
var
|
|
32846
|
-
"
|
|
32847
|
-
"
|
|
32848
|
-
"
|
|
32849
|
-
"
|
|
32850
|
-
"
|
|
32851
|
-
"
|
|
32852
|
-
"
|
|
32853
|
-
"
|
|
32854
|
-
"
|
|
32855
|
-
"
|
|
32856
|
-
"
|
|
32857
|
-
"
|
|
32858
|
-
"
|
|
32859
|
-
"
|
|
32860
|
-
"
|
|
32861
|
-
"
|
|
32862
|
-
"
|
|
32863
|
-
"
|
|
32864
|
-
"
|
|
32865
|
-
"
|
|
32866
|
-
"
|
|
32867
|
-
"
|
|
32868
|
-
"
|
|
32869
|
-
"
|
|
32870
|
-
"
|
|
32871
|
-
"
|
|
32872
|
-
"
|
|
32873
|
-
"
|
|
32874
|
-
"
|
|
32875
|
-
"
|
|
32876
|
-
"
|
|
32877
|
-
"
|
|
32878
|
-
"
|
|
32879
|
-
"
|
|
32880
|
-
"
|
|
32881
|
-
"
|
|
32882
|
-
"tileSize": "tileSize_1441117854",
|
|
32883
|
-
"tip": "tip_1441117854",
|
|
32884
|
-
"tipName": "tipName_1441117854",
|
|
32885
|
-
"tipMeta": "tipMeta_1441117854",
|
|
32886
|
-
"tipShare": "tipShare_1441117854",
|
|
32887
|
-
"legend": "legend_1441117854",
|
|
32888
|
-
"legendName": "legendName_1441117854",
|
|
32889
|
-
"legendValue": "legendValue_1441117854",
|
|
32890
|
-
"swatch": "swatch_1441117854",
|
|
32891
|
-
"empty": "empty_1441117854",
|
|
32892
|
-
"toneSystem": "toneSystem_1441117854",
|
|
32893
|
-
"toneTools": "toneTools_1441117854",
|
|
32894
|
-
"toneMemory": "toneMemory_1441117854",
|
|
32895
|
-
"toneFiles": "toneFiles_1441117854",
|
|
32896
|
-
"toneResults": "toneResults_1441117854",
|
|
32897
|
-
"toneConversation": "toneConversation_1441117854"
|
|
32812
|
+
var UsageCards_module_default = {
|
|
32813
|
+
"grid": "grid_673061119",
|
|
32814
|
+
"card": "card_673061119",
|
|
32815
|
+
"head": "head_673061119",
|
|
32816
|
+
"title": "title_673061119",
|
|
32817
|
+
"model": "model_673061119",
|
|
32818
|
+
"cacheBadge": "cacheBadge_673061119",
|
|
32819
|
+
"body": "body_673061119",
|
|
32820
|
+
"totalRow": "totalRow_673061119",
|
|
32821
|
+
"total": "total_673061119",
|
|
32822
|
+
"totalShare": "totalShare_673061119",
|
|
32823
|
+
"bar": "bar_673061119",
|
|
32824
|
+
"barSeg": "barSeg_673061119",
|
|
32825
|
+
"barEmpty": "barEmpty_673061119",
|
|
32826
|
+
"statsMain": "statsMain_673061119",
|
|
32827
|
+
"ringWrap": "ringWrap_673061119",
|
|
32828
|
+
"ring": "ring_673061119",
|
|
32829
|
+
"ringTrack": "ringTrack_673061119",
|
|
32830
|
+
"ringCenter": "ringCenter_673061119",
|
|
32831
|
+
"ringTotal": "ringTotal_673061119",
|
|
32832
|
+
"ringCaption": "ringCaption_673061119",
|
|
32833
|
+
"legend": "legend_673061119",
|
|
32834
|
+
"legendRow": "legendRow_673061119",
|
|
32835
|
+
"swatch": "swatch_673061119",
|
|
32836
|
+
"legendName": "legendName_673061119",
|
|
32837
|
+
"legendPercent": "legendPercent_673061119",
|
|
32838
|
+
"legendValue": "legendValue_673061119",
|
|
32839
|
+
"legendSuffix": "legendSuffix_673061119",
|
|
32840
|
+
"empty": "empty_673061119",
|
|
32841
|
+
"toneSystemPrompt": "toneSystemPrompt_673061119",
|
|
32842
|
+
"toneUser": "toneUser_673061119",
|
|
32843
|
+
"toneSkill": "toneSkill_673061119",
|
|
32844
|
+
"toneToolResult": "toneToolResult_673061119",
|
|
32845
|
+
"toneToolDef": "toneToolDef_673061119",
|
|
32846
|
+
"toneInjected": "toneInjected_673061119",
|
|
32847
|
+
"toneAssistant": "toneAssistant_673061119",
|
|
32848
|
+
"toneOutput": "toneOutput_673061119"
|
|
32898
32849
|
};
|
|
32899
32850
|
//#endregion
|
|
32900
|
-
//#region src/client/
|
|
32901
|
-
|
|
32902
|
-
|
|
32903
|
-
|
|
32904
|
-
|
|
32905
|
-
|
|
32906
|
-
|
|
32907
|
-
|
|
32908
|
-
|
|
32909
|
-
|
|
32910
|
-
|
|
32911
|
-
|
|
32912
|
-
memory: ContextWindowPanel_module_default.toneMemory,
|
|
32913
|
-
files: ContextWindowPanel_module_default.toneFiles,
|
|
32914
|
-
results: ContextWindowPanel_module_default.toneResults,
|
|
32915
|
-
conversation: ContextWindowPanel_module_default.toneConversation
|
|
32851
|
+
//#region src/client/UsageCards.tsx
|
|
32852
|
+
/** Legend names, one per bucket (the context-window panel's category names are
|
|
32853
|
+
* a different split — files live inside tool results here). */
|
|
32854
|
+
const BUCKET_LABEL = {
|
|
32855
|
+
systemPrompt: "bucketSystemPrompt",
|
|
32856
|
+
user: "bucketUser",
|
|
32857
|
+
skill: "bucketSkill",
|
|
32858
|
+
toolResult: "bucketToolResult",
|
|
32859
|
+
toolDef: "bucketToolDef",
|
|
32860
|
+
injected: "bucketInjected",
|
|
32861
|
+
assistant: "bucketAssistant",
|
|
32862
|
+
output: "bucketOutput"
|
|
32916
32863
|
};
|
|
32917
|
-
/**
|
|
32918
|
-
|
|
32919
|
-
|
|
32920
|
-
|
|
32921
|
-
|
|
32922
|
-
|
|
32923
|
-
|
|
32924
|
-
|
|
32925
|
-
|
|
32864
|
+
/** Each bucket brings its own accent as a CSS custom property, so the swatch,
|
|
32865
|
+
* the ring segment, the bar segment and the value text all read one variable. */
|
|
32866
|
+
const BUCKET_TONE = {
|
|
32867
|
+
systemPrompt: UsageCards_module_default.toneSystemPrompt,
|
|
32868
|
+
user: UsageCards_module_default.toneUser,
|
|
32869
|
+
skill: UsageCards_module_default.toneSkill,
|
|
32870
|
+
toolResult: UsageCards_module_default.toneToolResult,
|
|
32871
|
+
toolDef: UsageCards_module_default.toneToolDef,
|
|
32872
|
+
injected: UsageCards_module_default.toneInjected,
|
|
32873
|
+
assistant: UsageCards_module_default.toneAssistant,
|
|
32874
|
+
output: UsageCards_module_default.toneOutput
|
|
32926
32875
|
};
|
|
32927
|
-
/**
|
|
32928
|
-
|
|
32929
|
-
|
|
32930
|
-
|
|
32931
|
-
|
|
32932
|
-
|
|
32933
|
-
|
|
32934
|
-
|
|
32935
|
-
|
|
32936
|
-
|
|
32937
|
-
|
|
32938
|
-
|
|
32939
|
-
|
|
32940
|
-
|
|
32941
|
-
});
|
|
32942
|
-
/** Hover-card footprint estimate, so the tip can be kept inside the map
|
|
32943
|
-
* without measuring itself first. */
|
|
32944
|
-
const TIP_W = 220;
|
|
32945
|
-
const TIP_H = 70;
|
|
32946
|
-
/** The hover card for one tile: name and call count, the exact token count
|
|
32947
|
-
* with its category, and the block's share of the estimate and of the
|
|
32948
|
-
* model's window — the figures a tile has no room to print. */
|
|
32949
|
-
function Tip({ item, box, map, t, total, contextWindow }) {
|
|
32950
|
-
const share = total > 0 ? formatPercent(item.tokens / total * 100) : null;
|
|
32951
|
-
const windowShare = contextWindow !== null && contextWindow > 0 ? formatPercent(item.tokens / contextWindow * 100) : null;
|
|
32952
|
-
const left = Math.min(Math.max(box.x + box.width / 2 - TIP_W / 2, 4), Math.max(4, map.width - TIP_W - 4));
|
|
32953
|
-
const top = box.y + box.height + 6 + TIP_H < map.height - 4 ? box.y + box.height + 6 : Math.max(4, box.y - TIP_H - 6);
|
|
32876
|
+
/** Approximate value as the card prints it: "≈299.1k". */
|
|
32877
|
+
const approx = (n) => `≈${formatCompact(n)}`;
|
|
32878
|
+
/** Ring geometry: a 116 px donut with a 15 px stroke. */
|
|
32879
|
+
const RING_SIZE = 116;
|
|
32880
|
+
const RING_STROKE = 15;
|
|
32881
|
+
const RING_RADIUS = 101 / 2 - 2;
|
|
32882
|
+
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
|
|
32883
|
+
/** Donut of bucket shares with the grand total in the middle. Segments are
|
|
32884
|
+
* stroked along one circle and offset by the running length, so the ring is
|
|
32885
|
+
* DPI-independent and needs no chart library. `total` sizes the segments (the
|
|
32886
|
+
* replay estimate); `center` is the exact billed figure the card headlines. */
|
|
32887
|
+
function Ring({ rows, total, center, centerTitle, caption }) {
|
|
32888
|
+
const drawn = rows.filter((row) => row.tokens > 0);
|
|
32889
|
+
let offset = 0;
|
|
32954
32890
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
32955
|
-
className:
|
|
32956
|
-
|
|
32957
|
-
|
|
32958
|
-
|
|
32959
|
-
|
|
32960
|
-
|
|
32891
|
+
className: UsageCards_module_default.ringWrap,
|
|
32892
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
32893
|
+
className: UsageCards_module_default.ring,
|
|
32894
|
+
width: RING_SIZE,
|
|
32895
|
+
height: RING_SIZE,
|
|
32896
|
+
viewBox: `0 0 ${RING_SIZE} ${RING_SIZE}`,
|
|
32897
|
+
role: "img",
|
|
32898
|
+
"aria-label": caption,
|
|
32899
|
+
children: [
|
|
32900
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("title", { children: caption }),
|
|
32901
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
32902
|
+
className: UsageCards_module_default.ringTrack,
|
|
32903
|
+
cx: RING_SIZE / 2,
|
|
32904
|
+
cy: RING_SIZE / 2,
|
|
32905
|
+
r: RING_RADIUS,
|
|
32906
|
+
fill: "none",
|
|
32907
|
+
strokeWidth: RING_STROKE
|
|
32908
|
+
}),
|
|
32909
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
|
|
32910
|
+
transform: `rotate(-90 ${RING_SIZE / 2} ${RING_SIZE / 2})`,
|
|
32911
|
+
children: drawn.map((row) => {
|
|
32912
|
+
const length = total > 0 ? row.tokens / total * RING_CIRCUMFERENCE : 0;
|
|
32913
|
+
const visible = Math.max(length - 1.2, .6);
|
|
32914
|
+
const dash = `${visible} ${RING_CIRCUMFERENCE - visible}`;
|
|
32915
|
+
const circle = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
32916
|
+
className: BUCKET_TONE[row.key],
|
|
32917
|
+
cx: RING_SIZE / 2,
|
|
32918
|
+
cy: RING_SIZE / 2,
|
|
32919
|
+
r: RING_RADIUS,
|
|
32920
|
+
fill: "none",
|
|
32921
|
+
stroke: "var(--bucket)",
|
|
32922
|
+
strokeWidth: RING_STROKE,
|
|
32923
|
+
strokeDasharray: dash,
|
|
32924
|
+
strokeDashoffset: -offset
|
|
32925
|
+
}, row.key);
|
|
32926
|
+
offset += length;
|
|
32927
|
+
return circle;
|
|
32928
|
+
})
|
|
32929
|
+
})
|
|
32930
|
+
]
|
|
32931
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
32932
|
+
className: UsageCards_module_default.ringCenter,
|
|
32933
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
32934
|
+
className: UsageCards_module_default.ringTotal,
|
|
32935
|
+
title: centerTitle,
|
|
32936
|
+
children: center
|
|
32937
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
32938
|
+
className: UsageCards_module_default.ringCaption,
|
|
32939
|
+
children: caption
|
|
32940
|
+
})]
|
|
32941
|
+
})]
|
|
32942
|
+
});
|
|
32943
|
+
}
|
|
32944
|
+
/** One legend entry: swatch, name, share, approximate value. */
|
|
32945
|
+
function LegendRow({ row, percent, suffix, t }) {
|
|
32946
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
32947
|
+
className: `${UsageCards_module_default.legendRow} ${BUCKET_TONE[row.key]}`,
|
|
32961
32948
|
children: [
|
|
32962
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
32963
|
-
className:
|
|
32964
|
-
|
|
32949
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
|
|
32950
|
+
className: UsageCards_module_default.swatch,
|
|
32951
|
+
"aria-hidden": "true"
|
|
32965
32952
|
}),
|
|
32966
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
32967
|
-
className:
|
|
32968
|
-
children: [
|
|
32969
|
-
|
|
32970
|
-
|
|
32971
|
-
|
|
32972
|
-
]
|
|
32953
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
32954
|
+
className: UsageCards_module_default.legendName,
|
|
32955
|
+
children: t(BUCKET_LABEL[row.key])
|
|
32956
|
+
}),
|
|
32957
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
32958
|
+
className: UsageCards_module_default.legendPercent,
|
|
32959
|
+
children: [percent.toFixed(1), "%"]
|
|
32973
32960
|
}),
|
|
32974
|
-
|
|
32975
|
-
className:
|
|
32976
|
-
|
|
32961
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
32962
|
+
className: UsageCards_module_default.legendValue,
|
|
32963
|
+
title: formatTokens(row.tokens),
|
|
32964
|
+
children: [approx(row.tokens), suffix === void 0 ? "" : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
32965
|
+
className: UsageCards_module_default.legendSuffix,
|
|
32966
|
+
children: [" · ", suffix]
|
|
32967
|
+
})]
|
|
32977
32968
|
})
|
|
32978
32969
|
]
|
|
32979
32970
|
});
|
|
32980
32971
|
}
|
|
32981
|
-
|
|
32982
|
-
|
|
32983
|
-
const [size, setSize] = (0, react.useState)({
|
|
32984
|
-
width: 0,
|
|
32985
|
-
height: 0
|
|
32986
|
-
});
|
|
32987
|
-
const [hoverKey, setHoverKey] = (0, react.useState)(null);
|
|
32988
|
-
(0, react.useLayoutEffect)(() => {
|
|
32989
|
-
const host = hostRef.current;
|
|
32990
|
-
if (host === null) return;
|
|
32991
|
-
const measure = () => setSize((previous) => {
|
|
32992
|
-
const width = host.clientWidth;
|
|
32993
|
-
const height = host.clientHeight;
|
|
32994
|
-
return previous.width === width && previous.height === height ? previous : {
|
|
32995
|
-
width,
|
|
32996
|
-
height
|
|
32997
|
-
};
|
|
32998
|
-
});
|
|
32999
|
-
measure();
|
|
33000
|
-
if (typeof ResizeObserver === "undefined") {
|
|
33001
|
-
window.addEventListener("resize", measure);
|
|
33002
|
-
return () => window.removeEventListener("resize", measure);
|
|
33003
|
-
}
|
|
33004
|
-
const observer = new ResizeObserver(measure);
|
|
33005
|
-
observer.observe(host);
|
|
33006
|
-
return () => observer.disconnect();
|
|
33007
|
-
}, []);
|
|
33008
|
-
const boxes = (0, react.useMemo)(() => treemapLayout(items.map((item) => ({
|
|
33009
|
-
key: item.key,
|
|
33010
|
-
tokens: item.tokens
|
|
33011
|
-
})), size.width, size.height), [
|
|
33012
|
-
items,
|
|
33013
|
-
size.width,
|
|
33014
|
-
size.height
|
|
33015
|
-
]);
|
|
33016
|
-
const byKey = (0, react.useMemo)(() => new Map(items.map((item) => [item.key, item])), [items]);
|
|
33017
|
-
const hoverBox = hoverKey === null ? void 0 : boxes.find((box) => box.key === hoverKey);
|
|
33018
|
-
const hoverItem = hoverBox === void 0 ? void 0 : byKey.get(hoverBox.key);
|
|
33019
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33020
|
-
ref: hostRef,
|
|
33021
|
-
className: ContextWindowPanel_module_default.map,
|
|
33022
|
-
onMouseLeave: () => setHoverKey(null),
|
|
33023
|
-
children: [boxes.map((box) => {
|
|
33024
|
-
const item = byKey.get(box.key);
|
|
33025
|
-
if (item === void 0 || box.width <= 0 || box.height <= 0) return null;
|
|
33026
|
-
const text = itemText(item, t);
|
|
33027
|
-
const showLabel = box.width >= 46 && box.height >= 22;
|
|
33028
|
-
const showSize = box.width >= 62 && box.height >= 40;
|
|
33029
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33030
|
-
className: ContextWindowPanel_module_default.slot,
|
|
33031
|
-
style: {
|
|
33032
|
-
left: box.x,
|
|
33033
|
-
top: box.y,
|
|
33034
|
-
width: box.width,
|
|
33035
|
-
height: box.height
|
|
33036
|
-
},
|
|
33037
|
-
onMouseEnter: () => setHoverKey(box.key),
|
|
33038
|
-
"aria-label": `${text} · ${formatTokens(item.tokens)} token · ${t(CATEGORY_LABEL[item.category])}`,
|
|
33039
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33040
|
-
className: `${ContextWindowPanel_module_default.tile} ${CATEGORY_TONE[item.category]}`,
|
|
33041
|
-
children: [showLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33042
|
-
className: ContextWindowPanel_module_default.tileLabel,
|
|
33043
|
-
children: text
|
|
33044
|
-
}), showSize && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33045
|
-
className: ContextWindowPanel_module_default.tileSize,
|
|
33046
|
-
children: [
|
|
33047
|
-
formatCompact(item.tokens),
|
|
33048
|
-
" · ",
|
|
33049
|
-
t(CATEGORY_LABEL[item.category])
|
|
33050
|
-
]
|
|
33051
|
-
})]
|
|
33052
|
-
})
|
|
33053
|
-
}, box.key);
|
|
33054
|
-
}), hoverBox !== void 0 && hoverItem !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tip, {
|
|
33055
|
-
item: hoverItem,
|
|
33056
|
-
box: hoverBox,
|
|
33057
|
-
map: size,
|
|
33058
|
-
t,
|
|
33059
|
-
total: sumOf(items),
|
|
33060
|
-
contextWindow
|
|
33061
|
-
})]
|
|
33062
|
-
});
|
|
33063
|
-
}
|
|
33064
|
-
/** The context-window panel above the Usage cards, in magpie's shape: the used
|
|
33065
|
-
* total and the model's window on the left, the newest answer's prompt-cache
|
|
33066
|
-
* split on the right, a treemap of what occupies the window below (with a
|
|
33067
|
-
* hover card per tile), and one legend row naming the six buckets with their
|
|
33068
|
-
* sizes. Every figure is an estimate, so the panel never claims an exact
|
|
33069
|
-
* count — and the live pill stamps when the estimate was last refreshed. */
|
|
33070
|
-
function ContextWindowPanel({ context, t, updatedAt }) {
|
|
33071
|
-
const [open, setOpen] = (0, react.useState)(true);
|
|
33072
|
-
const totals = contextCategoryTotals(context.items);
|
|
32972
|
+
/** Left card: the live window's composition, its occupancy and the hard cap. */
|
|
32973
|
+
function ContextCard({ context, t }) {
|
|
33073
32974
|
const reading = contextReading(context);
|
|
33074
|
-
const
|
|
33075
|
-
const
|
|
33076
|
-
const
|
|
33077
|
-
const
|
|
33078
|
-
|
|
33079
|
-
percent: String(percent ?? 0)
|
|
33080
|
-
});
|
|
33081
|
-
const categoryTotal = totals.reduce((sum, row) => sum + row.tokens, 0);
|
|
33082
|
-
const windowKnown = context.contextWindow !== null && context.contextWindow > 0;
|
|
33083
|
-
const usedShare = windowKnown ? Math.min(100, Math.max(0, reading.usedTokens / context.contextWindow * 100)) : 100;
|
|
33084
|
-
const freeTokens = windowKnown ? Math.max(0, context.contextWindow - reading.usedTokens) : 0;
|
|
33085
|
-
const digestLabel = [
|
|
33086
|
-
`${t("contextUsed")} ${formatTokens(reading.usedTokens)} token`,
|
|
33087
|
-
...totals.map((row) => `${t(CATEGORY_LABEL[row.key])} ${formatCompact(row.tokens)}`),
|
|
33088
|
-
windowKnown ? `${t("contextFree")} ${formatTokens(freeTokens)} token` : windowText
|
|
33089
|
-
].join(" · ");
|
|
33090
|
-
const digestText = context.contextWindow === null ? t("contextDigestUnknown", { used: formatCompact(reading.usedTokens) }) : t("contextDigest", {
|
|
32975
|
+
const rows = contextBucketTotals(context.items);
|
|
32976
|
+
const total = rows.reduce((sum, row) => sum + row.tokens, 0);
|
|
32977
|
+
const capacity = context.contextWindow !== null && context.contextWindow > 0 ? context.contextWindow : total;
|
|
32978
|
+
const occupied = Math.min(100, bucketPercent(reading.usedTokens, capacity));
|
|
32979
|
+
const windowText = context.contextWindow !== null && context.contextWindow > 0 ? t("contextWindowTotal", {
|
|
33091
32980
|
used: formatCompact(reading.usedTokens),
|
|
33092
|
-
window: formatCompact(context.contextWindow)
|
|
33093
|
-
|
|
33094
|
-
});
|
|
32981
|
+
window: formatCompact(context.contextWindow)
|
|
32982
|
+
}) : t("contextWindowTotalUnknown", { used: formatCompact(reading.usedTokens) });
|
|
33095
32983
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
33096
|
-
className:
|
|
33097
|
-
"data-dsh-context-window": true,
|
|
32984
|
+
className: `${UsageCards_module_default.card} ${UsageCards_module_default.contextCard}`,
|
|
33098
32985
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
33099
|
-
className:
|
|
33100
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
33101
|
-
className:
|
|
33102
|
-
children:
|
|
33103
|
-
|
|
33104
|
-
|
|
33105
|
-
|
|
33106
|
-
|
|
33107
|
-
"aria-controls": "dsh-context-window-body",
|
|
33108
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
33109
|
-
className: `${ContextWindowPanel_module_default.chevron} ${open ? ContextWindowPanel_module_default.chevronOpen : ""}`,
|
|
33110
|
-
width: "12",
|
|
33111
|
-
height: "12",
|
|
33112
|
-
viewBox: "0 0 12 12",
|
|
33113
|
-
"aria-hidden": "true",
|
|
33114
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
33115
|
-
d: "m4 2 4 4-4 4",
|
|
33116
|
-
stroke: "currentColor",
|
|
33117
|
-
strokeWidth: "1.5",
|
|
33118
|
-
fill: "none"
|
|
33119
|
-
})
|
|
33120
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("contextTitle") })]
|
|
33121
|
-
}), !open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33122
|
-
className: ContextWindowPanel_module_default.digest,
|
|
33123
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33124
|
-
className: ContextWindowPanel_module_default.digestTotal,
|
|
33125
|
-
title: `${t("contextUsed")} ${formatTokens(reading.usedTokens)} token · ${windowText}`,
|
|
33126
|
-
children: digestText
|
|
33127
|
-
}), categoryTotal > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33128
|
-
className: ContextWindowPanel_module_default.digestBar,
|
|
33129
|
-
role: "img",
|
|
33130
|
-
"aria-label": digestLabel,
|
|
33131
|
-
title: digestLabel,
|
|
33132
|
-
children: totals.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33133
|
-
className: `${ContextWindowPanel_module_default.digestSeg} ${CATEGORY_TONE[row.key]}`,
|
|
33134
|
-
style: { width: `${row.tokens / categoryTotal * usedShare}%` }
|
|
33135
|
-
}, row.key))
|
|
33136
|
-
})]
|
|
33137
|
-
})]
|
|
33138
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33139
|
-
className: ContextWindowPanel_module_default.headRight,
|
|
33140
|
-
children: [model !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33141
|
-
className: ContextWindowPanel_module_default.model,
|
|
33142
|
-
title: `${context.provider} / ${context.model}`,
|
|
33143
|
-
children: model
|
|
33144
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33145
|
-
className: ContextWindowPanel_module_default.livePill,
|
|
33146
|
-
children: [
|
|
33147
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33148
|
-
className: ContextWindowPanel_module_default.liveDot,
|
|
33149
|
-
"aria-hidden": "true"
|
|
33150
|
-
}),
|
|
33151
|
-
t("contextLive"),
|
|
33152
|
-
typeof updatedAt === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33153
|
-
className: ContextWindowPanel_module_default.liveTime,
|
|
33154
|
-
children: [" · ", clock$1(updatedAt)]
|
|
33155
|
-
})
|
|
33156
|
-
]
|
|
33157
|
-
})]
|
|
32986
|
+
className: UsageCards_module_default.head,
|
|
32987
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
32988
|
+
className: UsageCards_module_default.title,
|
|
32989
|
+
children: t("currentContextTitle")
|
|
32990
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
32991
|
+
className: UsageCards_module_default.model,
|
|
32992
|
+
title: `${context.provider} / ${context.model}`,
|
|
32993
|
+
children: [context.provider, context.model].filter(Boolean).join(" · ") || "—"
|
|
33158
32994
|
})]
|
|
33159
32995
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33160
|
-
|
|
33161
|
-
className: ContextWindowPanel_module_default.body,
|
|
33162
|
-
hidden: !open,
|
|
32996
|
+
className: UsageCards_module_default.body,
|
|
33163
32997
|
children: [
|
|
33164
32998
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33165
|
-
className:
|
|
33166
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
33167
|
-
className:
|
|
32999
|
+
className: UsageCards_module_default.totalRow,
|
|
33000
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
33001
|
+
className: UsageCards_module_default.total,
|
|
33002
|
+
title: formatTokens(reading.usedTokens),
|
|
33003
|
+
children: windowText
|
|
33004
|
+
}), reading.percent !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33005
|
+
className: UsageCards_module_default.totalShare,
|
|
33168
33006
|
children: [
|
|
33169
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
33170
|
-
|
|
33171
|
-
|
|
33172
|
-
}),
|
|
33173
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", {
|
|
33174
|
-
className: ContextWindowPanel_module_default.total,
|
|
33175
|
-
title: formatTokens(reading.usedTokens),
|
|
33176
|
-
children: [formatCompact(reading.usedTokens), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "token" })]
|
|
33177
|
-
}),
|
|
33178
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33179
|
-
className: ContextWindowPanel_module_default.window,
|
|
33180
|
-
children: windowText
|
|
33181
|
-
})
|
|
33182
|
-
]
|
|
33183
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33184
|
-
className: ContextWindowPanel_module_default.cache,
|
|
33185
|
-
children: [
|
|
33186
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33187
|
-
className: ContextWindowPanel_module_default.label,
|
|
33188
|
-
children: t("contextCache")
|
|
33189
|
-
}),
|
|
33190
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33191
|
-
className: ContextWindowPanel_module_default.cacheBar,
|
|
33192
|
-
children: hitRate === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: ContextWindowPanel_module_default.cacheEmpty }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33193
|
-
className: ContextWindowPanel_module_default.cacheHit,
|
|
33194
|
-
style: { width: `${hitRate}%` }
|
|
33195
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33196
|
-
className: ContextWindowPanel_module_default.cacheMiss,
|
|
33197
|
-
style: { width: `${100 - hitRate}%` }
|
|
33198
|
-
})] })
|
|
33199
|
-
}),
|
|
33200
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33201
|
-
className: ContextWindowPanel_module_default.cacheLegend,
|
|
33202
|
-
children: hitRate === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33203
|
-
className: ContextWindowPanel_module_default.cachePending,
|
|
33204
|
-
children: t("contextCachePending")
|
|
33205
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
33206
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
|
|
33207
|
-
className: ContextWindowPanel_module_default.cacheHitDot,
|
|
33208
|
-
"aria-hidden": "true"
|
|
33209
|
-
}),
|
|
33210
|
-
t("contextCacheHit"),
|
|
33211
|
-
" ",
|
|
33212
|
-
formatPercent(hitRate)
|
|
33213
|
-
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
33214
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
|
|
33215
|
-
className: ContextWindowPanel_module_default.cacheMissDot,
|
|
33216
|
-
"aria-hidden": "true"
|
|
33217
|
-
}),
|
|
33218
|
-
t("contextCacheMiss"),
|
|
33219
|
-
" ",
|
|
33220
|
-
formatPercent(100 - hitRate)
|
|
33221
|
-
] })] })
|
|
33222
|
-
})
|
|
33007
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: formatPercent(reading.percent) }),
|
|
33008
|
+
" ",
|
|
33009
|
+
t("contextUsedShort")
|
|
33223
33010
|
]
|
|
33224
33011
|
})]
|
|
33225
33012
|
}),
|
|
33226
|
-
|
|
33227
|
-
className:
|
|
33228
|
-
|
|
33229
|
-
|
|
33230
|
-
|
|
33231
|
-
|
|
33232
|
-
|
|
33233
|
-
|
|
33234
|
-
className: ContextWindowPanel_module_default.empty,
|
|
33235
|
-
children: t("contextEmpty")
|
|
33236
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Treemap, {
|
|
33237
|
-
items: context.items,
|
|
33238
|
-
t,
|
|
33239
|
-
contextWindow: context.contextWindow
|
|
33013
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33014
|
+
className: UsageCards_module_default.bar,
|
|
33015
|
+
role: "img",
|
|
33016
|
+
"aria-label": t("currentContextTitle"),
|
|
33017
|
+
children: [rows.map((row) => row.tokens > 0 && total > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33018
|
+
className: `${UsageCards_module_default.barSeg} ${BUCKET_TONE[row.key]}`,
|
|
33019
|
+
style: { width: `${bucketPercent(row.tokens, total) * occupied / 100}%` }
|
|
33020
|
+
}, row.key) : null), total <= 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: UsageCards_module_default.barEmpty })]
|
|
33240
33021
|
}),
|
|
33241
|
-
|
|
33242
|
-
className:
|
|
33243
|
-
children:
|
|
33244
|
-
|
|
33245
|
-
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33249
|
-
className: ContextWindowPanel_module_default.legendName,
|
|
33250
|
-
children: t(CATEGORY_LABEL[row.key])
|
|
33251
|
-
}),
|
|
33252
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33253
|
-
className: ContextWindowPanel_module_default.legendValue,
|
|
33254
|
-
children: formatCompact(row.tokens)
|
|
33255
|
-
})
|
|
33256
|
-
] }, row.key))
|
|
33022
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
33023
|
+
className: UsageCards_module_default.legend,
|
|
33024
|
+
children: rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LegendRow, {
|
|
33025
|
+
row,
|
|
33026
|
+
percent: bucketPercent(row.tokens, total),
|
|
33027
|
+
t
|
|
33028
|
+
}, row.key))
|
|
33257
33029
|
})
|
|
33258
33030
|
]
|
|
33259
33031
|
})]
|
|
33260
33032
|
});
|
|
33261
33033
|
}
|
|
33034
|
+
/** Right card: every request's replayed content plus the billed output. */
|
|
33035
|
+
function StatsCard({ detail, t }) {
|
|
33036
|
+
const composition = detail.composition ?? null;
|
|
33037
|
+
const rows = composition?.buckets ?? [];
|
|
33038
|
+
const total = rows.reduce((sum, row) => sum + row.tokens, 0);
|
|
33039
|
+
const billed = composition?.billedTokens ?? 0;
|
|
33040
|
+
const hit = composition !== null ? cacheHitPercentOf(composition.totals) : detail.context?.cacheKnown ? cacheRate(detail.context.cacheHitTokens, detail.context.cacheMissTokens) : null;
|
|
33041
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
33042
|
+
className: `${UsageCards_module_default.card} ${UsageCards_module_default.statsCard}`,
|
|
33043
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
33044
|
+
className: UsageCards_module_default.head,
|
|
33045
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
33046
|
+
className: UsageCards_module_default.title,
|
|
33047
|
+
children: t("statsTitle")
|
|
33048
|
+
}), hit !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33049
|
+
className: UsageCards_module_default.cacheBadge,
|
|
33050
|
+
children: [
|
|
33051
|
+
t("statsCacheHit"),
|
|
33052
|
+
" ",
|
|
33053
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", { children: [hit.toFixed(2), "%"] })
|
|
33054
|
+
]
|
|
33055
|
+
})]
|
|
33056
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33057
|
+
className: UsageCards_module_default.body,
|
|
33058
|
+
children: composition === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
33059
|
+
className: UsageCards_module_default.empty,
|
|
33060
|
+
children: t("statsEmpty")
|
|
33061
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33062
|
+
className: UsageCards_module_default.statsMain,
|
|
33063
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Ring, {
|
|
33064
|
+
rows,
|
|
33065
|
+
total,
|
|
33066
|
+
center: formatCompact(billed),
|
|
33067
|
+
centerTitle: formatTokens(billed),
|
|
33068
|
+
caption: t("statsTotal")
|
|
33069
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
33070
|
+
className: UsageCards_module_default.legend,
|
|
33071
|
+
children: USAGE_BUCKET_ORDER.map((key) => {
|
|
33072
|
+
const row = rows.find((item) => item.key === key) ?? {
|
|
33073
|
+
key,
|
|
33074
|
+
tokens: 0
|
|
33075
|
+
};
|
|
33076
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LegendRow, {
|
|
33077
|
+
row,
|
|
33078
|
+
percent: bucketPercent(row.tokens, total),
|
|
33079
|
+
suffix: key === "output" ? t("statsIncludesThinking") : void 0,
|
|
33080
|
+
t
|
|
33081
|
+
}, key);
|
|
33082
|
+
})
|
|
33083
|
+
})]
|
|
33084
|
+
}) })
|
|
33085
|
+
})]
|
|
33086
|
+
});
|
|
33087
|
+
}
|
|
33088
|
+
/** The pair, rendered where the context-window panel used to sit. */
|
|
33089
|
+
function SessionUsageCards({ detail, t }) {
|
|
33090
|
+
if (detail.context === null || detail.context === void 0) {
|
|
33091
|
+
if (detail.composition === null || detail.composition === void 0) return null;
|
|
33092
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33093
|
+
className: UsageCards_module_default.grid,
|
|
33094
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsCard, {
|
|
33095
|
+
detail,
|
|
33096
|
+
t
|
|
33097
|
+
})
|
|
33098
|
+
});
|
|
33099
|
+
}
|
|
33100
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33101
|
+
className: UsageCards_module_default.grid,
|
|
33102
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextCard, {
|
|
33103
|
+
context: detail.context,
|
|
33104
|
+
t
|
|
33105
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsCard, {
|
|
33106
|
+
detail,
|
|
33107
|
+
t
|
|
33108
|
+
})]
|
|
33109
|
+
});
|
|
33110
|
+
}
|
|
33262
33111
|
//#endregion
|
|
33263
33112
|
//#region src/client/model-logos-data.ts
|
|
33264
33113
|
/**
|
|
@@ -34003,11 +33852,10 @@ window.__ModuleLoader__.load({
|
|
|
34003
33852
|
detail,
|
|
34004
33853
|
t
|
|
34005
33854
|
}),
|
|
34006
|
-
|
|
34007
|
-
|
|
34008
|
-
t
|
|
34009
|
-
|
|
34010
|
-
}) : null,
|
|
33855
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionUsageCards, {
|
|
33856
|
+
detail,
|
|
33857
|
+
t
|
|
33858
|
+
}),
|
|
34011
33859
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Records, {
|
|
34012
33860
|
detail,
|
|
34013
33861
|
t
|
|
@@ -34152,6 +34000,7 @@ window.__ModuleLoader__.load({
|
|
|
34152
34000
|
asOf: "统计截至",
|
|
34153
34001
|
refresh: "刷新",
|
|
34154
34002
|
contextTitle: "上下文窗口",
|
|
34003
|
+
currentContextTitle: "当前上下文",
|
|
34155
34004
|
contextUsed: "已用上下文",
|
|
34156
34005
|
contextLive: "实时",
|
|
34157
34006
|
contextWindowKnown: "模型窗口 {value} · 已用 {percent}%",
|
|
@@ -34181,7 +34030,23 @@ window.__ModuleLoader__.load({
|
|
|
34181
34030
|
contextEmpty: "本会话还没有可统计的上下文",
|
|
34182
34031
|
contextTipShare: "占上下文 {percent}",
|
|
34183
34032
|
contextTipWindow: "占窗口 {percent}",
|
|
34184
|
-
contextEstimateNote: "色块按历史重放估算 {estimate};供应商实测提示词 {billed}(上游已压缩或裁剪)"
|
|
34033
|
+
contextEstimateNote: "色块按历史重放估算 {estimate};供应商实测提示词 {billed}(上游已压缩或裁剪)",
|
|
34034
|
+
statsTitle: "Token 统计",
|
|
34035
|
+
statsTotal: "总用量",
|
|
34036
|
+
statsCacheHit: "缓存命中",
|
|
34037
|
+
statsIncludesThinking: "含思考",
|
|
34038
|
+
statsEmpty: "本会话还没有可统计的 token",
|
|
34039
|
+
contextWindowTotal: "{used} / {window} tokens",
|
|
34040
|
+
contextWindowTotalUnknown: "{used} tokens",
|
|
34041
|
+
contextUsedShort: "上下文已用",
|
|
34042
|
+
bucketSystemPrompt: "系统提示词",
|
|
34043
|
+
bucketUser: "用户消息",
|
|
34044
|
+
bucketSkill: "技能注入",
|
|
34045
|
+
bucketToolResult: "工具结果",
|
|
34046
|
+
bucketToolDef: "工具定义",
|
|
34047
|
+
bucketInjected: "注入内容",
|
|
34048
|
+
bucketAssistant: "助手消息",
|
|
34049
|
+
bucketOutput: "输出"
|
|
34185
34050
|
};
|
|
34186
34051
|
const usageEn = {
|
|
34187
34052
|
tab: "Usage",
|
|
@@ -34243,6 +34108,7 @@ window.__ModuleLoader__.load({
|
|
|
34243
34108
|
asOf: "as of",
|
|
34244
34109
|
refresh: "Refresh",
|
|
34245
34110
|
contextTitle: "Context window",
|
|
34111
|
+
currentContextTitle: "Current context",
|
|
34246
34112
|
contextUsed: "Context used",
|
|
34247
34113
|
contextLive: "Live",
|
|
34248
34114
|
contextWindowKnown: "Window {value} · {percent}% used",
|
|
@@ -34272,7 +34138,23 @@ window.__ModuleLoader__.load({
|
|
|
34272
34138
|
contextEmpty: "Nothing in this session’s context yet",
|
|
34273
34139
|
contextTipShare: "{percent} of context",
|
|
34274
34140
|
contextTipWindow: "{percent} of window",
|
|
34275
|
-
contextEstimateNote: "Blocks estimate {estimate} by replay; the provider billed {billed} after upstream compression"
|
|
34141
|
+
contextEstimateNote: "Blocks estimate {estimate} by replay; the provider billed {billed} after upstream compression",
|
|
34142
|
+
statsTitle: "Token stats",
|
|
34143
|
+
statsTotal: "Total usage",
|
|
34144
|
+
statsCacheHit: "Cache hit",
|
|
34145
|
+
statsIncludesThinking: "incl. thinking",
|
|
34146
|
+
statsEmpty: "No token spend recorded for this session yet",
|
|
34147
|
+
contextWindowTotal: "{used} / {window} tokens",
|
|
34148
|
+
contextWindowTotalUnknown: "{used} tokens",
|
|
34149
|
+
contextUsedShort: "context used",
|
|
34150
|
+
bucketSystemPrompt: "System prompt",
|
|
34151
|
+
bucketUser: "User messages",
|
|
34152
|
+
bucketSkill: "Skill injection",
|
|
34153
|
+
bucketToolResult: "Tool results",
|
|
34154
|
+
bucketToolDef: "Tool definitions",
|
|
34155
|
+
bucketInjected: "Injected content",
|
|
34156
|
+
bucketAssistant: "Assistant messages",
|
|
34157
|
+
bucketOutput: "Output"
|
|
34276
34158
|
};
|
|
34277
34159
|
const usageZhTW = {
|
|
34278
34160
|
...usageZh,
|
|
@@ -34334,6 +34216,7 @@ window.__ModuleLoader__.load({
|
|
|
34334
34216
|
asOf: "統計截至",
|
|
34335
34217
|
refresh: "重新整理",
|
|
34336
34218
|
contextTitle: "上下文視窗",
|
|
34219
|
+
currentContextTitle: "當前上下文",
|
|
34337
34220
|
contextUsed: "已用上下文",
|
|
34338
34221
|
contextLive: "即時",
|
|
34339
34222
|
contextWindowKnown: "模型視窗 {value} · 已用 {percent}%",
|
|
@@ -34363,7 +34246,23 @@ window.__ModuleLoader__.load({
|
|
|
34363
34246
|
contextEmpty: "本會話還沒有可統計的上下文",
|
|
34364
34247
|
contextTipShare: "佔上下文 {percent}",
|
|
34365
34248
|
contextTipWindow: "佔視窗 {percent}",
|
|
34366
|
-
contextEstimateNote: "色塊按歷史重放估算 {estimate};供應商實測提示詞 {billed}(上游已壓縮或裁剪)"
|
|
34249
|
+
contextEstimateNote: "色塊按歷史重放估算 {estimate};供應商實測提示詞 {billed}(上游已壓縮或裁剪)",
|
|
34250
|
+
statsTitle: "Token 統計",
|
|
34251
|
+
statsTotal: "總用量",
|
|
34252
|
+
statsCacheHit: "快取命中",
|
|
34253
|
+
statsIncludesThinking: "含思考",
|
|
34254
|
+
statsEmpty: "本會話還沒有可統計的 token",
|
|
34255
|
+
contextWindowTotal: "{used} / {window} tokens",
|
|
34256
|
+
contextWindowTotalUnknown: "{used} tokens",
|
|
34257
|
+
contextUsedShort: "上下文已用",
|
|
34258
|
+
bucketSystemPrompt: "系統提示詞",
|
|
34259
|
+
bucketUser: "使用者訊息",
|
|
34260
|
+
bucketSkill: "技能注入",
|
|
34261
|
+
bucketToolResult: "工具結果",
|
|
34262
|
+
bucketToolDef: "工具定義",
|
|
34263
|
+
bucketInjected: "注入內容",
|
|
34264
|
+
bucketAssistant: "助手訊息",
|
|
34265
|
+
bucketOutput: "輸出"
|
|
34367
34266
|
};
|
|
34368
34267
|
//#endregion
|
|
34369
34268
|
//#region src/client/index.tsx
|