dsh-token-total 1.1.0 → 1.1.2
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 +407 -523
- 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 +30 -22
- 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,8 +31661,13 @@ 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
|
-
*
|
|
31664
|
+
* figure with a live estimate of what is happening now. The figure is the
|
|
31665
|
+
* streaming step's measured rate while one streams and the LAST measured rate
|
|
31666
|
+
* once it settles — a session average describes the whole session's past, and
|
|
31667
|
+
* it cannot match the pace of the answer on screen, which is what users read
|
|
31668
|
+
* as the pill disagreeing with the stream in front of them. The official
|
|
31669
|
+
* session figure only shows on a session that has never streamed. That
|
|
31670
|
+
* subscription lives in
|
|
31657
31671
|
* its own child component so stream 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
|
|
@@ -31740,17 +31754,19 @@ 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. The figure it carries is the CURRENT
|
|
31758
|
+
* stream's measured rate, and it stays that rate once the step settles:
|
|
31759
|
+
* tool execution and the gap between steps happen inside the same answer, and
|
|
31760
|
+
* the session average this slot used to fall back to there is a figure from
|
|
31761
|
+
* the whole session's past — a number that cannot match the pace of the answer
|
|
31762
|
+
* on screen, which is what reads as "not the real-time speed". The official
|
|
31763
|
+
* session figure is only a session's opening state, shown until the first
|
|
31764
|
+
* window measures anything.
|
|
31747
31765
|
*
|
|
31748
31766
|
* One window covers a whole step, across every output kind the accumulator
|
|
31749
31767
|
* carries — reasoning, answer text, and Tool-call arguments — because the
|
|
31750
31768
|
* 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.
|
|
31769
|
+
* sequence.
|
|
31754
31770
|
*
|
|
31755
31771
|
* Sampling happens in a layout effect and the figure it yields is published as
|
|
31756
31772
|
* state, so no render reads a clock or mutates the window: a render React starts
|
|
@@ -31768,10 +31784,7 @@ window.__ModuleLoader__.load({
|
|
|
31768
31784
|
rate: new StreamingRateSampler()
|
|
31769
31785
|
};
|
|
31770
31786
|
const rate = sampler.current.rate;
|
|
31771
|
-
const [
|
|
31772
|
-
key: null,
|
|
31773
|
-
rate: null
|
|
31774
|
-
});
|
|
31787
|
+
const [reading, setReading] = (0, react.useState)(null);
|
|
31775
31788
|
const scaleRef = (0, react.useRef)(scale);
|
|
31776
31789
|
const [beat, setBeat] = (0, react.useState)(0);
|
|
31777
31790
|
(0, react.useLayoutEffect)(() => {
|
|
@@ -31781,10 +31794,7 @@ window.__ModuleLoader__.load({
|
|
|
31781
31794
|
}
|
|
31782
31795
|
rate.observe(tokens, Date.now());
|
|
31783
31796
|
const next = rate.ratePerSecond();
|
|
31784
|
-
|
|
31785
|
-
key,
|
|
31786
|
-
rate: next
|
|
31787
|
-
});
|
|
31797
|
+
if (next !== null) setReading((previous) => previous === next ? previous : next);
|
|
31788
31798
|
}, [
|
|
31789
31799
|
tokens,
|
|
31790
31800
|
key,
|
|
@@ -31802,7 +31812,6 @@ window.__ModuleLoader__.load({
|
|
|
31802
31812
|
clearInterval(handle);
|
|
31803
31813
|
};
|
|
31804
31814
|
}, [streaming]);
|
|
31805
|
-
const reading = published.key === key ? published.rate : null;
|
|
31806
31815
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SpeedFigure, { text: reading === null ? fallback : t("stats.tokensPerSecond", { throughput: formatTokensPerSecond(reading) }) });
|
|
31807
31816
|
}
|
|
31808
31817
|
function TimePill({ stats, t, dialog, useChat, scale, dock }) {
|
|
@@ -32707,24 +32716,55 @@ window.__ModuleLoader__.load({
|
|
|
32707
32716
|
};
|
|
32708
32717
|
}
|
|
32709
32718
|
//#endregion
|
|
32710
|
-
//#region src/
|
|
32711
|
-
|
|
32712
|
-
|
|
32713
|
-
"
|
|
32714
|
-
"
|
|
32715
|
-
"
|
|
32716
|
-
"
|
|
32717
|
-
"
|
|
32718
|
-
"
|
|
32719
|
+
//#region src/usage-buckets.ts
|
|
32720
|
+
const USAGE_BUCKET_ORDER = [
|
|
32721
|
+
"systemPrompt",
|
|
32722
|
+
"user",
|
|
32723
|
+
"skill",
|
|
32724
|
+
"toolResult",
|
|
32725
|
+
"toolDef",
|
|
32726
|
+
"injected",
|
|
32727
|
+
"assistant",
|
|
32728
|
+
"output"
|
|
32719
32729
|
];
|
|
32720
|
-
/**
|
|
32721
|
-
|
|
32730
|
+
/** The buckets that describe the live window (output never occupies it). */
|
|
32731
|
+
const CONTEXT_BUCKET_ORDER = USAGE_BUCKET_ORDER.filter((key) => key !== "output");
|
|
32732
|
+
//#endregion
|
|
32733
|
+
//#region src/client/context-window-core.ts
|
|
32734
|
+
/** Which legend bucket one window row belongs to.
|
|
32735
|
+
*
|
|
32736
|
+
* The row categories are the treemap's (they group files separately); the
|
|
32737
|
+
* cards speak the eight-bucket legend instead. A folded `rest` row lost its
|
|
32738
|
+
* kind, so a conversation tail lands with the assistant — the larger half of
|
|
32739
|
+
* what a turn replays.
|
|
32740
|
+
*/
|
|
32741
|
+
function contextBucketOf(item) {
|
|
32742
|
+
switch (item.category) {
|
|
32743
|
+
case "system": return "systemPrompt";
|
|
32744
|
+
case "tools": return "toolDef";
|
|
32745
|
+
case "memory": return item.kind === "skill-catalog" ? "skill" : "injected";
|
|
32746
|
+
case "files":
|
|
32747
|
+
case "results": return "toolResult";
|
|
32748
|
+
case "conversation": return item.kind === "user" ? "user" : "assistant";
|
|
32749
|
+
default: return "injected";
|
|
32750
|
+
}
|
|
32751
|
+
}
|
|
32752
|
+
/** The live window folded into the same legend the spend card prints, in
|
|
32753
|
+
* legend order and keeping zero buckets so both cards stay aligned. */
|
|
32754
|
+
function contextBucketTotals(items) {
|
|
32722
32755
|
const totals = /* @__PURE__ */ new Map();
|
|
32723
|
-
for (const item of items)
|
|
32724
|
-
|
|
32756
|
+
for (const item of items) {
|
|
32757
|
+
const key = contextBucketOf(item);
|
|
32758
|
+
totals.set(key, (totals.get(key) ?? 0) + item.tokens);
|
|
32759
|
+
}
|
|
32760
|
+
return CONTEXT_BUCKET_ORDER.map((key) => ({
|
|
32725
32761
|
key,
|
|
32726
32762
|
tokens: totals.get(key) ?? 0
|
|
32727
|
-
}))
|
|
32763
|
+
}));
|
|
32764
|
+
}
|
|
32765
|
+
/** What a share of the whole is, for the legend column. */
|
|
32766
|
+
function bucketPercent(tokens, total) {
|
|
32767
|
+
return total > 0 ? tokens / total * 100 : 0;
|
|
32728
32768
|
}
|
|
32729
32769
|
/** Window occupancy in whole percent, or null when the host never reported the
|
|
32730
32770
|
* model's window (the panel then says "unknown" instead of guessing one). */
|
|
@@ -32743,98 +32783,10 @@ window.__ModuleLoader__.load({
|
|
|
32743
32783
|
estimateDiffers: context.promptTokens !== null && gap > .15
|
|
32744
32784
|
};
|
|
32745
32785
|
}
|
|
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
32786
|
//#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/
|
|
32787
|
+
//#region \0token-total-css:/home/runner/work/dsh-token-total/dsh-token-total/src/client/UsageCards.module.css?inline
|
|
32788
|
+
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";
|
|
32789
|
+
const tagId$1 = "dsh-token-total/UsageCards.module.css";
|
|
32838
32790
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$1 + "\"]") === null) {
|
|
32839
32791
|
const tag = document.createElement("style");
|
|
32840
32792
|
tag.dataset.plugin = "dsh-token-total";
|
|
@@ -32842,423 +32794,305 @@ window.__ModuleLoader__.load({
|
|
|
32842
32794
|
tag.textContent = css$1;
|
|
32843
32795
|
document.head.appendChild(tag);
|
|
32844
32796
|
}
|
|
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"
|
|
32797
|
+
var UsageCards_module_default = {
|
|
32798
|
+
"grid": "grid_673061119",
|
|
32799
|
+
"card": "card_673061119",
|
|
32800
|
+
"head": "head_673061119",
|
|
32801
|
+
"title": "title_673061119",
|
|
32802
|
+
"model": "model_673061119",
|
|
32803
|
+
"cacheBadge": "cacheBadge_673061119",
|
|
32804
|
+
"body": "body_673061119",
|
|
32805
|
+
"totalRow": "totalRow_673061119",
|
|
32806
|
+
"total": "total_673061119",
|
|
32807
|
+
"totalShare": "totalShare_673061119",
|
|
32808
|
+
"bar": "bar_673061119",
|
|
32809
|
+
"barSeg": "barSeg_673061119",
|
|
32810
|
+
"barEmpty": "barEmpty_673061119",
|
|
32811
|
+
"statsMain": "statsMain_673061119",
|
|
32812
|
+
"ringWrap": "ringWrap_673061119",
|
|
32813
|
+
"ring": "ring_673061119",
|
|
32814
|
+
"ringTrack": "ringTrack_673061119",
|
|
32815
|
+
"ringCenter": "ringCenter_673061119",
|
|
32816
|
+
"ringTotal": "ringTotal_673061119",
|
|
32817
|
+
"ringCaption": "ringCaption_673061119",
|
|
32818
|
+
"legend": "legend_673061119",
|
|
32819
|
+
"legendRow": "legendRow_673061119",
|
|
32820
|
+
"swatch": "swatch_673061119",
|
|
32821
|
+
"legendName": "legendName_673061119",
|
|
32822
|
+
"legendPercent": "legendPercent_673061119",
|
|
32823
|
+
"legendValue": "legendValue_673061119",
|
|
32824
|
+
"legendSuffix": "legendSuffix_673061119",
|
|
32825
|
+
"empty": "empty_673061119",
|
|
32826
|
+
"toneSystemPrompt": "toneSystemPrompt_673061119",
|
|
32827
|
+
"toneUser": "toneUser_673061119",
|
|
32828
|
+
"toneSkill": "toneSkill_673061119",
|
|
32829
|
+
"toneToolResult": "toneToolResult_673061119",
|
|
32830
|
+
"toneToolDef": "toneToolDef_673061119",
|
|
32831
|
+
"toneInjected": "toneInjected_673061119",
|
|
32832
|
+
"toneAssistant": "toneAssistant_673061119",
|
|
32833
|
+
"toneOutput": "toneOutput_673061119"
|
|
32898
32834
|
};
|
|
32899
32835
|
//#endregion
|
|
32900
|
-
//#region src/client/
|
|
32901
|
-
|
|
32902
|
-
|
|
32903
|
-
|
|
32904
|
-
|
|
32905
|
-
|
|
32906
|
-
|
|
32907
|
-
|
|
32836
|
+
//#region src/client/UsageCards.tsx
|
|
32837
|
+
/** Legend names, one per bucket (the context-window panel's category names are
|
|
32838
|
+
* a different split — files live inside tool results here). */
|
|
32839
|
+
const BUCKET_LABEL = {
|
|
32840
|
+
systemPrompt: "bucketSystemPrompt",
|
|
32841
|
+
user: "bucketUser",
|
|
32842
|
+
skill: "bucketSkill",
|
|
32843
|
+
toolResult: "bucketToolResult",
|
|
32844
|
+
toolDef: "bucketToolDef",
|
|
32845
|
+
injected: "bucketInjected",
|
|
32846
|
+
assistant: "bucketAssistant",
|
|
32847
|
+
output: "bucketOutput"
|
|
32908
32848
|
};
|
|
32909
|
-
|
|
32910
|
-
|
|
32911
|
-
|
|
32912
|
-
|
|
32913
|
-
|
|
32914
|
-
|
|
32915
|
-
|
|
32849
|
+
/** Each bucket brings its own accent as a CSS custom property, so the swatch,
|
|
32850
|
+
* the ring segment, the bar segment and the value text all read one variable. */
|
|
32851
|
+
const BUCKET_TONE = {
|
|
32852
|
+
systemPrompt: UsageCards_module_default.toneSystemPrompt,
|
|
32853
|
+
user: UsageCards_module_default.toneUser,
|
|
32854
|
+
skill: UsageCards_module_default.toneSkill,
|
|
32855
|
+
toolResult: UsageCards_module_default.toneToolResult,
|
|
32856
|
+
toolDef: UsageCards_module_default.toneToolDef,
|
|
32857
|
+
injected: UsageCards_module_default.toneInjected,
|
|
32858
|
+
assistant: UsageCards_module_default.toneAssistant,
|
|
32859
|
+
output: UsageCards_module_default.toneOutput
|
|
32916
32860
|
};
|
|
32917
|
-
/**
|
|
32918
|
-
const
|
|
32919
|
-
|
|
32920
|
-
|
|
32921
|
-
|
|
32922
|
-
|
|
32923
|
-
|
|
32924
|
-
|
|
32925
|
-
|
|
32926
|
-
|
|
32927
|
-
|
|
32928
|
-
|
|
32929
|
-
|
|
32930
|
-
|
|
32931
|
-
const label = item.kind === void 0 ? void 0 : KIND_LABEL[item.kind];
|
|
32932
|
-
const base = label !== void 0 ? t(label) : item.label || item.kind || "";
|
|
32933
|
-
return item.count > 1 ? `${base} ${t("contextTimes", { count: String(item.count) })}` : base;
|
|
32934
|
-
}
|
|
32935
|
-
const sumOf = (items) => items.reduce((sum, item) => sum + item.tokens, 0);
|
|
32936
|
-
/** Wall-clock stamp of the last successful read, same shape as the page's. */
|
|
32937
|
-
const clock$1 = (ms) => new Date(ms).toLocaleTimeString([], {
|
|
32938
|
-
hour: "2-digit",
|
|
32939
|
-
minute: "2-digit",
|
|
32940
|
-
second: "2-digit"
|
|
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);
|
|
32861
|
+
/** Approximate value as the card prints it: "≈299.1k". */
|
|
32862
|
+
const approx = (n) => `≈${formatCompact(n)}`;
|
|
32863
|
+
/** Ring geometry: a 116 px donut with a 15 px stroke. */
|
|
32864
|
+
const RING_SIZE = 116;
|
|
32865
|
+
const RING_STROKE = 15;
|
|
32866
|
+
const RING_RADIUS = 101 / 2 - 2;
|
|
32867
|
+
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
|
|
32868
|
+
/** Donut of bucket shares with the grand total in the middle. Segments are
|
|
32869
|
+
* stroked along one circle and offset by the running length, so the ring is
|
|
32870
|
+
* DPI-independent and needs no chart library. `total` sizes the segments (the
|
|
32871
|
+
* replay estimate); `center` is the exact billed figure the card headlines. */
|
|
32872
|
+
function Ring({ rows, total, center, centerTitle, caption }) {
|
|
32873
|
+
const drawn = rows.filter((row) => row.tokens > 0);
|
|
32874
|
+
let offset = 0;
|
|
32954
32875
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
32955
|
-
className:
|
|
32956
|
-
|
|
32957
|
-
|
|
32958
|
-
|
|
32959
|
-
|
|
32960
|
-
|
|
32876
|
+
className: UsageCards_module_default.ringWrap,
|
|
32877
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
32878
|
+
className: UsageCards_module_default.ring,
|
|
32879
|
+
width: RING_SIZE,
|
|
32880
|
+
height: RING_SIZE,
|
|
32881
|
+
viewBox: `0 0 ${RING_SIZE} ${RING_SIZE}`,
|
|
32882
|
+
role: "img",
|
|
32883
|
+
"aria-label": caption,
|
|
32884
|
+
children: [
|
|
32885
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("title", { children: caption }),
|
|
32886
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
32887
|
+
className: UsageCards_module_default.ringTrack,
|
|
32888
|
+
cx: RING_SIZE / 2,
|
|
32889
|
+
cy: RING_SIZE / 2,
|
|
32890
|
+
r: RING_RADIUS,
|
|
32891
|
+
fill: "none",
|
|
32892
|
+
strokeWidth: RING_STROKE
|
|
32893
|
+
}),
|
|
32894
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
|
|
32895
|
+
transform: `rotate(-90 ${RING_SIZE / 2} ${RING_SIZE / 2})`,
|
|
32896
|
+
children: drawn.map((row) => {
|
|
32897
|
+
const length = total > 0 ? row.tokens / total * RING_CIRCUMFERENCE : 0;
|
|
32898
|
+
const visible = Math.max(length - 1.2, .6);
|
|
32899
|
+
const dash = `${visible} ${RING_CIRCUMFERENCE - visible}`;
|
|
32900
|
+
const circle = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
32901
|
+
className: BUCKET_TONE[row.key],
|
|
32902
|
+
cx: RING_SIZE / 2,
|
|
32903
|
+
cy: RING_SIZE / 2,
|
|
32904
|
+
r: RING_RADIUS,
|
|
32905
|
+
fill: "none",
|
|
32906
|
+
stroke: "var(--bucket)",
|
|
32907
|
+
strokeWidth: RING_STROKE,
|
|
32908
|
+
strokeDasharray: dash,
|
|
32909
|
+
strokeDashoffset: -offset
|
|
32910
|
+
}, row.key);
|
|
32911
|
+
offset += length;
|
|
32912
|
+
return circle;
|
|
32913
|
+
})
|
|
32914
|
+
})
|
|
32915
|
+
]
|
|
32916
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
32917
|
+
className: UsageCards_module_default.ringCenter,
|
|
32918
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
32919
|
+
className: UsageCards_module_default.ringTotal,
|
|
32920
|
+
title: centerTitle,
|
|
32921
|
+
children: center
|
|
32922
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
32923
|
+
className: UsageCards_module_default.ringCaption,
|
|
32924
|
+
children: caption
|
|
32925
|
+
})]
|
|
32926
|
+
})]
|
|
32927
|
+
});
|
|
32928
|
+
}
|
|
32929
|
+
/** One legend entry: swatch, name, share, approximate value. */
|
|
32930
|
+
function LegendRow({ row, percent, suffix, t }) {
|
|
32931
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
32932
|
+
className: `${UsageCards_module_default.legendRow} ${BUCKET_TONE[row.key]}`,
|
|
32961
32933
|
children: [
|
|
32962
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
32963
|
-
className:
|
|
32964
|
-
|
|
32934
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
|
|
32935
|
+
className: UsageCards_module_default.swatch,
|
|
32936
|
+
"aria-hidden": "true"
|
|
32965
32937
|
}),
|
|
32966
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
32967
|
-
className:
|
|
32968
|
-
children: [
|
|
32969
|
-
|
|
32970
|
-
|
|
32971
|
-
|
|
32972
|
-
]
|
|
32938
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
32939
|
+
className: UsageCards_module_default.legendName,
|
|
32940
|
+
children: t(BUCKET_LABEL[row.key])
|
|
32941
|
+
}),
|
|
32942
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
32943
|
+
className: UsageCards_module_default.legendPercent,
|
|
32944
|
+
children: [percent.toFixed(1), "%"]
|
|
32973
32945
|
}),
|
|
32974
|
-
|
|
32975
|
-
className:
|
|
32976
|
-
|
|
32946
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
32947
|
+
className: UsageCards_module_default.legendValue,
|
|
32948
|
+
title: formatTokens(row.tokens),
|
|
32949
|
+
children: [approx(row.tokens), suffix === void 0 ? "" : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
32950
|
+
className: UsageCards_module_default.legendSuffix,
|
|
32951
|
+
children: [" · ", suffix]
|
|
32952
|
+
})]
|
|
32977
32953
|
})
|
|
32978
32954
|
]
|
|
32979
32955
|
});
|
|
32980
32956
|
}
|
|
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);
|
|
32957
|
+
/** Left card: the live window's composition, its occupancy and the hard cap. */
|
|
32958
|
+
function ContextCard({ context, t }) {
|
|
33073
32959
|
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", {
|
|
32960
|
+
const rows = contextBucketTotals(context.items);
|
|
32961
|
+
const total = rows.reduce((sum, row) => sum + row.tokens, 0);
|
|
32962
|
+
const capacity = context.contextWindow !== null && context.contextWindow > 0 ? context.contextWindow : total;
|
|
32963
|
+
const occupied = Math.min(100, bucketPercent(reading.usedTokens, capacity));
|
|
32964
|
+
const windowText = context.contextWindow !== null && context.contextWindow > 0 ? t("contextWindowTotal", {
|
|
33091
32965
|
used: formatCompact(reading.usedTokens),
|
|
33092
|
-
window: formatCompact(context.contextWindow)
|
|
33093
|
-
|
|
33094
|
-
});
|
|
32966
|
+
window: formatCompact(context.contextWindow)
|
|
32967
|
+
}) : t("contextWindowTotalUnknown", { used: formatCompact(reading.usedTokens) });
|
|
33095
32968
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
33096
|
-
className:
|
|
33097
|
-
"data-dsh-context-window": true,
|
|
32969
|
+
className: `${UsageCards_module_default.card} ${UsageCards_module_default.contextCard}`,
|
|
33098
32970
|
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
|
-
})]
|
|
32971
|
+
className: UsageCards_module_default.head,
|
|
32972
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
32973
|
+
className: UsageCards_module_default.title,
|
|
32974
|
+
children: t("currentContextTitle")
|
|
32975
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
32976
|
+
className: UsageCards_module_default.model,
|
|
32977
|
+
title: `${context.provider} / ${context.model}`,
|
|
32978
|
+
children: [context.provider, context.model].filter(Boolean).join(" · ") || "—"
|
|
33158
32979
|
})]
|
|
33159
32980
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33160
|
-
|
|
33161
|
-
className: ContextWindowPanel_module_default.body,
|
|
33162
|
-
hidden: !open,
|
|
32981
|
+
className: UsageCards_module_default.body,
|
|
33163
32982
|
children: [
|
|
33164
32983
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33165
|
-
className:
|
|
33166
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
33167
|
-
className:
|
|
32984
|
+
className: UsageCards_module_default.totalRow,
|
|
32985
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
32986
|
+
className: UsageCards_module_default.total,
|
|
32987
|
+
title: formatTokens(reading.usedTokens),
|
|
32988
|
+
children: windowText
|
|
32989
|
+
}), reading.percent !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
32990
|
+
className: UsageCards_module_default.totalShare,
|
|
33168
32991
|
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
|
-
})
|
|
32992
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: formatPercent(reading.percent) }),
|
|
32993
|
+
" ",
|
|
32994
|
+
t("contextUsedShort")
|
|
33223
32995
|
]
|
|
33224
32996
|
})]
|
|
33225
32997
|
}),
|
|
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
|
|
32998
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
32999
|
+
className: UsageCards_module_default.bar,
|
|
33000
|
+
role: "img",
|
|
33001
|
+
"aria-label": t("currentContextTitle"),
|
|
33002
|
+
children: [rows.map((row) => row.tokens > 0 && total > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33003
|
+
className: `${UsageCards_module_default.barSeg} ${BUCKET_TONE[row.key]}`,
|
|
33004
|
+
style: { width: `${bucketPercent(row.tokens, total) * occupied / 100}%` }
|
|
33005
|
+
}, row.key) : null), total <= 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: UsageCards_module_default.barEmpty })]
|
|
33240
33006
|
}),
|
|
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))
|
|
33007
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
33008
|
+
className: UsageCards_module_default.legend,
|
|
33009
|
+
children: rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LegendRow, {
|
|
33010
|
+
row,
|
|
33011
|
+
percent: bucketPercent(row.tokens, total),
|
|
33012
|
+
t
|
|
33013
|
+
}, row.key))
|
|
33257
33014
|
})
|
|
33258
33015
|
]
|
|
33259
33016
|
})]
|
|
33260
33017
|
});
|
|
33261
33018
|
}
|
|
33019
|
+
/** Right card: every request's replayed content plus the billed output. */
|
|
33020
|
+
function StatsCard({ detail, t }) {
|
|
33021
|
+
const composition = detail.composition ?? null;
|
|
33022
|
+
const rows = composition?.buckets ?? [];
|
|
33023
|
+
const total = rows.reduce((sum, row) => sum + row.tokens, 0);
|
|
33024
|
+
const billed = composition?.billedTokens ?? 0;
|
|
33025
|
+
const hit = composition !== null ? cacheHitPercentOf(composition.totals) : detail.context?.cacheKnown ? cacheRate(detail.context.cacheHitTokens, detail.context.cacheMissTokens) : null;
|
|
33026
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
33027
|
+
className: `${UsageCards_module_default.card} ${UsageCards_module_default.statsCard}`,
|
|
33028
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
33029
|
+
className: UsageCards_module_default.head,
|
|
33030
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
33031
|
+
className: UsageCards_module_default.title,
|
|
33032
|
+
children: t("statsTitle")
|
|
33033
|
+
}), hit !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33034
|
+
className: UsageCards_module_default.cacheBadge,
|
|
33035
|
+
children: [
|
|
33036
|
+
t("statsCacheHit"),
|
|
33037
|
+
" ",
|
|
33038
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("b", { children: [hit.toFixed(2), "%"] })
|
|
33039
|
+
]
|
|
33040
|
+
})]
|
|
33041
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33042
|
+
className: UsageCards_module_default.body,
|
|
33043
|
+
children: composition === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
33044
|
+
className: UsageCards_module_default.empty,
|
|
33045
|
+
children: t("statsEmpty")
|
|
33046
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33047
|
+
className: UsageCards_module_default.statsMain,
|
|
33048
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Ring, {
|
|
33049
|
+
rows,
|
|
33050
|
+
total,
|
|
33051
|
+
center: formatCompact(billed),
|
|
33052
|
+
centerTitle: formatTokens(billed),
|
|
33053
|
+
caption: t("statsTotal")
|
|
33054
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
33055
|
+
className: UsageCards_module_default.legend,
|
|
33056
|
+
children: USAGE_BUCKET_ORDER.map((key) => {
|
|
33057
|
+
const row = rows.find((item) => item.key === key) ?? {
|
|
33058
|
+
key,
|
|
33059
|
+
tokens: 0
|
|
33060
|
+
};
|
|
33061
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LegendRow, {
|
|
33062
|
+
row,
|
|
33063
|
+
percent: bucketPercent(row.tokens, total),
|
|
33064
|
+
suffix: key === "output" ? t("statsIncludesThinking") : void 0,
|
|
33065
|
+
t
|
|
33066
|
+
}, key);
|
|
33067
|
+
})
|
|
33068
|
+
})]
|
|
33069
|
+
}) })
|
|
33070
|
+
})]
|
|
33071
|
+
});
|
|
33072
|
+
}
|
|
33073
|
+
/** The pair, rendered where the context-window panel used to sit. */
|
|
33074
|
+
function SessionUsageCards({ detail, t }) {
|
|
33075
|
+
if (detail.context === null || detail.context === void 0) {
|
|
33076
|
+
if (detail.composition === null || detail.composition === void 0) return null;
|
|
33077
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33078
|
+
className: UsageCards_module_default.grid,
|
|
33079
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsCard, {
|
|
33080
|
+
detail,
|
|
33081
|
+
t
|
|
33082
|
+
})
|
|
33083
|
+
});
|
|
33084
|
+
}
|
|
33085
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33086
|
+
className: UsageCards_module_default.grid,
|
|
33087
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextCard, {
|
|
33088
|
+
context: detail.context,
|
|
33089
|
+
t
|
|
33090
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsCard, {
|
|
33091
|
+
detail,
|
|
33092
|
+
t
|
|
33093
|
+
})]
|
|
33094
|
+
});
|
|
33095
|
+
}
|
|
33262
33096
|
//#endregion
|
|
33263
33097
|
//#region src/client/model-logos-data.ts
|
|
33264
33098
|
/**
|
|
@@ -34003,11 +33837,10 @@ window.__ModuleLoader__.load({
|
|
|
34003
33837
|
detail,
|
|
34004
33838
|
t
|
|
34005
33839
|
}),
|
|
34006
|
-
|
|
34007
|
-
|
|
34008
|
-
t
|
|
34009
|
-
|
|
34010
|
-
}) : null,
|
|
33840
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionUsageCards, {
|
|
33841
|
+
detail,
|
|
33842
|
+
t
|
|
33843
|
+
}),
|
|
34011
33844
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Records, {
|
|
34012
33845
|
detail,
|
|
34013
33846
|
t
|
|
@@ -34152,6 +33985,7 @@ window.__ModuleLoader__.load({
|
|
|
34152
33985
|
asOf: "统计截至",
|
|
34153
33986
|
refresh: "刷新",
|
|
34154
33987
|
contextTitle: "上下文窗口",
|
|
33988
|
+
currentContextTitle: "当前上下文",
|
|
34155
33989
|
contextUsed: "已用上下文",
|
|
34156
33990
|
contextLive: "实时",
|
|
34157
33991
|
contextWindowKnown: "模型窗口 {value} · 已用 {percent}%",
|
|
@@ -34181,7 +34015,23 @@ window.__ModuleLoader__.load({
|
|
|
34181
34015
|
contextEmpty: "本会话还没有可统计的上下文",
|
|
34182
34016
|
contextTipShare: "占上下文 {percent}",
|
|
34183
34017
|
contextTipWindow: "占窗口 {percent}",
|
|
34184
|
-
contextEstimateNote: "色块按历史重放估算 {estimate};供应商实测提示词 {billed}(上游已压缩或裁剪)"
|
|
34018
|
+
contextEstimateNote: "色块按历史重放估算 {estimate};供应商实测提示词 {billed}(上游已压缩或裁剪)",
|
|
34019
|
+
statsTitle: "Token 统计",
|
|
34020
|
+
statsTotal: "总用量",
|
|
34021
|
+
statsCacheHit: "缓存命中",
|
|
34022
|
+
statsIncludesThinking: "含思考",
|
|
34023
|
+
statsEmpty: "本会话还没有可统计的 token",
|
|
34024
|
+
contextWindowTotal: "{used} / {window} tokens",
|
|
34025
|
+
contextWindowTotalUnknown: "{used} tokens",
|
|
34026
|
+
contextUsedShort: "上下文已用",
|
|
34027
|
+
bucketSystemPrompt: "系统提示词",
|
|
34028
|
+
bucketUser: "用户消息",
|
|
34029
|
+
bucketSkill: "技能注入",
|
|
34030
|
+
bucketToolResult: "工具结果",
|
|
34031
|
+
bucketToolDef: "工具定义",
|
|
34032
|
+
bucketInjected: "注入内容",
|
|
34033
|
+
bucketAssistant: "助手消息",
|
|
34034
|
+
bucketOutput: "输出"
|
|
34185
34035
|
};
|
|
34186
34036
|
const usageEn = {
|
|
34187
34037
|
tab: "Usage",
|
|
@@ -34243,6 +34093,7 @@ window.__ModuleLoader__.load({
|
|
|
34243
34093
|
asOf: "as of",
|
|
34244
34094
|
refresh: "Refresh",
|
|
34245
34095
|
contextTitle: "Context window",
|
|
34096
|
+
currentContextTitle: "Current context",
|
|
34246
34097
|
contextUsed: "Context used",
|
|
34247
34098
|
contextLive: "Live",
|
|
34248
34099
|
contextWindowKnown: "Window {value} · {percent}% used",
|
|
@@ -34272,7 +34123,23 @@ window.__ModuleLoader__.load({
|
|
|
34272
34123
|
contextEmpty: "Nothing in this session’s context yet",
|
|
34273
34124
|
contextTipShare: "{percent} of context",
|
|
34274
34125
|
contextTipWindow: "{percent} of window",
|
|
34275
|
-
contextEstimateNote: "Blocks estimate {estimate} by replay; the provider billed {billed} after upstream compression"
|
|
34126
|
+
contextEstimateNote: "Blocks estimate {estimate} by replay; the provider billed {billed} after upstream compression",
|
|
34127
|
+
statsTitle: "Token stats",
|
|
34128
|
+
statsTotal: "Total usage",
|
|
34129
|
+
statsCacheHit: "Cache hit",
|
|
34130
|
+
statsIncludesThinking: "incl. thinking",
|
|
34131
|
+
statsEmpty: "No token spend recorded for this session yet",
|
|
34132
|
+
contextWindowTotal: "{used} / {window} tokens",
|
|
34133
|
+
contextWindowTotalUnknown: "{used} tokens",
|
|
34134
|
+
contextUsedShort: "context used",
|
|
34135
|
+
bucketSystemPrompt: "System prompt",
|
|
34136
|
+
bucketUser: "User messages",
|
|
34137
|
+
bucketSkill: "Skill injection",
|
|
34138
|
+
bucketToolResult: "Tool results",
|
|
34139
|
+
bucketToolDef: "Tool definitions",
|
|
34140
|
+
bucketInjected: "Injected content",
|
|
34141
|
+
bucketAssistant: "Assistant messages",
|
|
34142
|
+
bucketOutput: "Output"
|
|
34276
34143
|
};
|
|
34277
34144
|
const usageZhTW = {
|
|
34278
34145
|
...usageZh,
|
|
@@ -34334,6 +34201,7 @@ window.__ModuleLoader__.load({
|
|
|
34334
34201
|
asOf: "統計截至",
|
|
34335
34202
|
refresh: "重新整理",
|
|
34336
34203
|
contextTitle: "上下文視窗",
|
|
34204
|
+
currentContextTitle: "當前上下文",
|
|
34337
34205
|
contextUsed: "已用上下文",
|
|
34338
34206
|
contextLive: "即時",
|
|
34339
34207
|
contextWindowKnown: "模型視窗 {value} · 已用 {percent}%",
|
|
@@ -34363,7 +34231,23 @@ window.__ModuleLoader__.load({
|
|
|
34363
34231
|
contextEmpty: "本會話還沒有可統計的上下文",
|
|
34364
34232
|
contextTipShare: "佔上下文 {percent}",
|
|
34365
34233
|
contextTipWindow: "佔視窗 {percent}",
|
|
34366
|
-
contextEstimateNote: "色塊按歷史重放估算 {estimate};供應商實測提示詞 {billed}(上游已壓縮或裁剪)"
|
|
34234
|
+
contextEstimateNote: "色塊按歷史重放估算 {estimate};供應商實測提示詞 {billed}(上游已壓縮或裁剪)",
|
|
34235
|
+
statsTitle: "Token 統計",
|
|
34236
|
+
statsTotal: "總用量",
|
|
34237
|
+
statsCacheHit: "快取命中",
|
|
34238
|
+
statsIncludesThinking: "含思考",
|
|
34239
|
+
statsEmpty: "本會話還沒有可統計的 token",
|
|
34240
|
+
contextWindowTotal: "{used} / {window} tokens",
|
|
34241
|
+
contextWindowTotalUnknown: "{used} tokens",
|
|
34242
|
+
contextUsedShort: "上下文已用",
|
|
34243
|
+
bucketSystemPrompt: "系統提示詞",
|
|
34244
|
+
bucketUser: "使用者訊息",
|
|
34245
|
+
bucketSkill: "技能注入",
|
|
34246
|
+
bucketToolResult: "工具結果",
|
|
34247
|
+
bucketToolDef: "工具定義",
|
|
34248
|
+
bucketInjected: "注入內容",
|
|
34249
|
+
bucketAssistant: "助手訊息",
|
|
34250
|
+
bucketOutput: "輸出"
|
|
34367
34251
|
};
|
|
34368
34252
|
//#endregion
|
|
34369
34253
|
//#region src/client/index.tsx
|