dsh-token-total 1.0.7 → 1.0.9
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 +5 -2
- package/lib/client.js +678 -127
- package/lib/index.js +281 -4
- package/lib/types/client/ContextWindowPanel.d.ts +18 -0
- package/lib/types/client/SessionUsageView.d.ts +3 -2
- package/lib/types/client/context-window-core.d.ts +49 -0
- package/lib/types/client/session-usage-locales.d.ts +28 -0
- package/lib/types/context-window.d.ts +36 -0
- package/lib/types/store.d.ts +8 -1
- package/lib/types/wire.d.ts +50 -0
- package/package.json +1 -1
- package/src/client/ContextWindowPanel.module.css +77 -0
- package/src/client/ContextWindowPanel.tsx +222 -0
- package/src/client/SessionUsageView.module.css +89 -64
- package/src/client/SessionUsageView.tsx +30 -26
- package/src/client/context-window-core.ts +137 -0
- package/src/client/session-usage-locales.ts +36 -0
- package/src/collector.ts +4 -1
- package/src/context-window.ts +302 -0
- package/src/session-detail.ts +3 -2
- package/src/store.ts +9 -1
- package/src/wire.ts +53 -0
package/lib/client.js
CHANGED
|
@@ -29432,13 +29432,13 @@ window.__ModuleLoader__.load({
|
|
|
29432
29432
|
}
|
|
29433
29433
|
//#endregion
|
|
29434
29434
|
//#region \0token-total-css:/home/runner/work/dsh-token-total/dsh-token-total/src/client/TokenTotalPanel.module.css?inline
|
|
29435
|
-
const css$3 = "/**\n * Usage statistics panel styles — a DSH-native CSS Module. Every visual\n * value rides the DSH semantic tokens (--dsw-alias-* colors, --dsw-font-*\n * typography, --ds-* motion); no static palette values, no theme selectors.\n * The heatmap's 5 levels come from the brand accent via color-mix; model\n * colours are the fixed --dsw-chart-1..5 series (rank order by token\n * volume) plus the gray --dsw-chart-other step — the reasonix token-total\n * palette, with a lifted dark variant defined below.\n */\n\n/* The panel as a standalone main panel. It gets the same 960px content column\n the Plugins page gives it — the declarations below mirror the Plugins page's\n own `.page_768838850` so both entries render identically. Without the cap the charts\n would stretch to whatever the centre column happens to be.\n\n One deliberate difference from that page: its 28px top inset is NOT part of\n this container. A main panel starts at the window's top-left corner, so an\n inset painted by this scrolling container is a strip above the content that\n no window-drag region covers; `.backRow_768838850` below owns the inset instead, which\n is the shape the shipped page plugins use for the same reason\n (`ui-plugin-manager`'s `.pageHead_768838850`/`.detailTop_768838850` and `ui-schedule`'s page\n head). The rendered position is unchanged. */\n.page_768838850 {\n display: flex;\n flex-direction: column;\n align-items: center;\n box-sizing: border-box;\n height: 100%;\n overflow: auto;\n padding: 0 clamp(24px, 4vw, 48px) 48px;\n color: var(--dsw-alias-label-primary);\n}\n\n.page_768838850 > * {\n width: 100%;\n max-width: 960px;\n}\n\n/* The standalone panel's back control. Only the main panel renders this seat —\n the Plugins page entry already sits inside the page's own chrome, which\n draws its own crumb back to the bundle list. The spacing matches the gap the\n panel keeps between its own sections.\n\n The row also carries the panel's former 28px top inset: it starts at the\n window's top edge, so the whole strip above the back control drags the\n window (Electron app-regions are geometry, not stacking, so a strip owned by\n the scrolling container would subtract itself from the drag row beneath\n it). */\n.backRow_768838850 {\n display: flex;\n box-sizing: border-box;\n padding-top: 28px;\n margin-bottom: 18px;\n}\n\n.back_768838850 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.panel_768838850 {\n display: flex;\n flex-direction: column;\n gap: 18px;\n min-width: 0;\n}\n\n/* ── toolbar ───────────────────────────────────────────────────────────── */\n\n.toolbar_768838850 {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 10px;\n}\n\n/* Each picker (time range) lives in a framed group. */\n.group_768838850 {\n display: inline-flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 2px;\n padding: 3px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 9px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n/* A range-preset chip: ghost Button inside the group, active state tinted\n with the brand accent (same blue as the section bars and the total card). */\n.segItem_768838850 {\n border-radius: 6px;\n}\n\n.segItem_768838850:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.segActive_768838850,\n.segActive_768838850:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-tone-1) 14%, transparent);\n color: var(--dsw-tone-1);\n font-weight: 600;\n}\n\n.customRange_768838850 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.dateInput_768838850 {\n width: 150px;\n flex: 0 0 auto;\n}\n\n.customSep_768838850 {\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* Trailing toolbar edge: the \"as of\" stamp sits immediately left of the\n refresh action (it stamps the very data that button refreshes). */\n.toolbarEnd_768838850 {\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 10px;\n}\n\n.asOf_768838850 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n white-space: nowrap;\n}\n\n/* Keep the remaining toolbar action at the trailing edge. */\n.refresh_768838850 {\n flex: 0 0 auto;\n}\n\n/* ── states ────────────────────────────────────────────────────────────── */\n\n.errorBanner_768838850 {\n padding: 10px 14px;\n border: 1px solid var(--dsw-alias-state-warn-primary);\n border-radius: 8px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);\n color: var(--dsw-alias-state-warn-label);\n font-size: 13px;\n}\n\n.loading_768838850 {\n padding: 24px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n}\n\n.empty_768838850 {\n padding: 24px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 8px;\n}\n\n/* ── numeric cards ─────────────────────────────────────────────────────── */\n\n/* Six equal tracks: the overview row is one grid of six identical cards\n (long value or not), so no card crowds its neighbours. At the Plugins\n page's 960px content width that is six columns on one row. */\n.cards_768838850 {\n display: grid;\n grid-template-columns: repeat(6, minmax(0, 1fr));\n gap: 10px;\n}\n\n/* Below the width where six cards hold their own, the grid folds to three\n columns (two rows of three), then to two (three rows of two). */\n@media (max-width: 720px) {\n .cards_768838850 {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n }\n}\n\n@media (max-width: 430px) {\n .cards_768838850 {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n\n/* Each stat card wears one accent hue (tone 1..6 → --dsw-tone-1..6): a 3px\n accent bar on the left edge, the icon in a tinted badge, and a whisper of\n the hue washing the top-left corner — the overview row reads in colour\n instead of one gray register. --card-accent is set by the .cardToneN_768838850 map\n below; content sits above the wash via a z-index layer. */\n.card_768838850 {\n --card-accent: var(--dsw-alias-label-tertiary);\n position: relative;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 14px 14px 12px 18px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-1);\n box-shadow: 0 1px 2px var(--dsw-alias-bg-mask-2);\n min-width: 0;\n}\n\n.card_768838850::before {\n content: \"\";\n position: absolute;\n left: 0;\n top: 12px;\n bottom: 12px;\n width: 3px;\n border-radius: 0 3px 3px 0;\n background: var(--card-accent);\n}\n\n.card_768838850::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background: linear-gradient(135deg, color-mix(in srgb, var(--card-accent) 7%, transparent), transparent 60%);\n pointer-events: none;\n}\n\n/* Content must paint above the corner wash (the ::after pseudo is the last\n child, so it wins a same-layer paint order). */\n.card_768838850 > * {\n position: relative;\n z-index: 1;\n}\n\n.cardTone1_768838850 { --card-accent: var(--dsw-tone-1); }\n.cardTone2_768838850 { --card-accent: var(--dsw-tone-2); }\n.cardTone3_768838850 { --card-accent: var(--dsw-tone-3); }\n.cardTone4_768838850 { --card-accent: var(--dsw-tone-4); }\n.cardTone5_768838850 { --card-accent: var(--dsw-tone-5); }\n.cardTone6_768838850 { --card-accent: var(--dsw-tone-6); }\n\n.cardHead_768838850 {\n display: flex;\n align-items: center;\n gap: 7px;\n}\n\n/* Icon badge: the accent hue at 12% behind its own glyph. */\n.cardIcon_768838850 {\n box-sizing: border-box;\n width: 24px;\n height: 24px;\n padding: 5px;\n border-radius: 7px;\n color: var(--card-accent);\n background: color-mix(in srgb, var(--card-accent) 12%, transparent);\n flex: 0 0 auto;\n}\n\n.cardLabel_768838850 {\n color: var(--dsw-alias-label-secondary);\n font-size: 13px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Values never wrap: a wrapped number defeats FitText's measurement (the\n wrapped line is exactly as wide as its box) and reads as a layout break.\n Oversized text shrinks instead — see FitText in TokenTotalPanel.tsx_768838850. */\n.cardValue_768838850 {\n color: var(--dsw-alias-label-primary);\n font-weight: 600;\n line-height: 1.15;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.cardValueSm_768838850 {\n font-size: 14px;\n}\n\n.cardValueWrap_768838850 {\n word-break: break-word;\n}\n\n/* The \"most used model\" card value: the model name over the provider, the\n same two-line anatomy as the per-model list rows (modelName/modelProvider\n classes), instead of the raw \"provider/model\" ref. */\n.cardModelLines_768838850 {\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n/* A muted second line under a card value (the absolute cached-token volume\n under the hit-rate percentage). Same no-wrap rule as the value above. */\n.cardSub_768838850 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n line-height: 1.2;\n white-space: nowrap;\n overflow: hidden;\n}\n\n/* ── sections / chart chrome ───────────────────────────────────────────── */\n\n.section_768838850 {\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n padding: 14px 16px 16px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-1);\n box-shadow: 0 1px 2px var(--dsw-alias-bg-mask-2);\n}\n\n.sectionHead_768838850 {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n/* Section titles carry the same brand accent bar as the stat cards' tone\n language, so every card heading is anchored the same way. */\n.sectionTitle_768838850 {\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 15px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.sectionTitle_768838850::before {\n content: \"\";\n width: 3px;\n height: 13px;\n border-radius: 2px;\n background: var(--dsw-tone-1);\n flex: 0 0 auto;\n}\n\n/* Header meta cluster right of a section title (trend cap note + curve\n legend). Kept as one wrap-friendly row on a common baseline. */\n.headMeta_768838850 {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.trendNote_768838850 {\n font-size: 13px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* Heatmap and the token trend share ONE row — one card each, left and right.\n Both columns stretch to a common height; the heatmap centres its SVG in the\n taller partner. Below 720px the pair folds to a single column. */\n.duo_768838850 {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 12px;\n align-items: stretch;\n}\n\n@media (max-width: 720px) {\n .duo_768838850 {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n.chartWrap_768838850 {\n position: relative;\n min-width: 0;\n}\n\n/* ── heatmap ───────────────────────────────────────────────────────────── */\n\n.heatWrap_768838850 {\n position: relative;\n /* hidden, not auto: the SVG now fills the container exactly, and an\n auto scrollbar would flash when a fixed tooltip passes over the wrap's\n right edge. */\n overflow: hidden;\n min-width: 0;\n /* In the duo row the heatmap card shares height with the trend card; the\n grid centres itself in whatever slack the partner leaves. */\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n}\n\n.heatmap_768838850 {\n display: block;\n margin: 0 auto;\n /* The ~364 day cells are flat SVG rects (no per-cell filter), so there is no\n blur chain left to re-rasterize — but promoting the SVG onto its own\n compositing layer still keeps the whole grid off the scroll repaint path,\n which is what made scrolling this panel drop frames while idle was clean\n (#4). Cheap and invisible, so it stays. Note that the promotion is what\n does the work: `contain: paint` on its own measured no better than no\n mitigation at all. */\n transform: translateZ(0);\n}\n\n.heatLegend_768838850 {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 13px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* Axis labels drawn inside the heatmap SVG: the weekday column on the left and\n the month strip along the bottom. SVG <text> takes `fill`, not `color`. */\n.heatAxis_768838850 {\n fill: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n}\n\n.heatCell_768838850 {\n display: inline-block;\n border-radius: 2px;\n /* No width/height here: the SVG cells carry their size as geometry\n attributes (x/y/width/height), and a CSS width/height would override\n those on SVG2 rects — pinning every cell to a fixed px and widening the\n visual gap. The legend swatches carry the same size inline, so both\n always match the measured geom.size_768838850.\n No filter either: the reference chart paints each tile as a flat, matte\n rounded square, so a sheen/under-shadow would read as a smudge at this\n cell size rather than depth. */\n}\n\n/* The five heat levels ride a fixed emerald ramp sampled pixel-for-pixel from\n the reference chart (a GitHub-style green scale), with a darker variant for\n dark mode below — never the theme accent, which can read near-black against\n the base surface in dark mode. level0 (no data) is a neutral empty cell.\n The fills are fully opaque: the reference tiles are solid, and mixing toward\n transparent (the previous glass treatment) lightened every step and broke\n the sampled ramp. The cells are SVG <rect>s, so the color rides `fill`\n (background is a no-op on SVG geometry) — but the legend swatches are HTML,\n so both declarations stay in step. */\n.heatLevel0_768838850 {\n fill: var(--dsw-heat-0);\n background: var(--dsw-heat-0);\n}\n\n.heatLevel1_768838850 {\n fill: var(--dsw-heat-1);\n background: var(--dsw-heat-1);\n}\n\n.heatLevel2_768838850 {\n fill: var(--dsw-heat-2);\n background: var(--dsw-heat-2);\n}\n\n.heatLevel3_768838850 {\n fill: var(--dsw-heat-3);\n background: var(--dsw-heat-3);\n}\n\n.heatLevel4_768838850 {\n fill: var(--dsw-heat-4);\n background: var(--dsw-heat-4);\n}\n\n.heatLevel5_768838850 {\n fill: var(--dsw-heat-5);\n background: var(--dsw-heat-5);\n}\n\n/* ── daily trend ───────────────────────────────────────────────────────── */\n\n.chart_768838850 {\n display: block;\n}\n\n.grid_768838850 {\n stroke: var(--dsw-alias-border-l1);\n stroke-width: 1;\n}\n\n.axis_768838850 {\n fill: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n}\n\n.axisRate_768838850 {\n fill: var(--dsw-alias-label-tertiary);\n}\n\n.bar_768838850 {\n transition: opacity 0.12s ease;\n /* The hover scaleX must pivot on the bar's own centre: SVG geometry\n transforms around the SVG origin unless transform-box anchors it to the\n element's fill box. Without this the scaled bar slides sideways and\n looks like it vanishes. */\n transform-box: fill-box;\n transform-origin: center;\n}\n\n.barDim_768838850 {\n opacity: 0.35;\n}\n\n.barHit_768838850 {\n fill: transparent;\n pointer-events: all;\n}\n\n/* cache hit-rate trend curve: DeepSeek's brand blue (#4D6BFE, the same hue\n family as the heatmap ramp) — a fixed brand ramp instead of the theme\n accent, which can read near-black in some themes. */\n.trend_768838850 {\n stroke: var(--dsw-trend-line);\n stroke-width: 2;\n opacity: 0.9;\n pointer-events: none;\n}\n\n.trendDot_768838850 {\n fill: var(--dsw-trend-line);\n stroke: var(--dsw-alias-bg-layer-1);\n stroke-width: 2;\n pointer-events: none;\n}\n\n.legend_768838850 {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.legendItem_768838850 {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n cursor: default;\n}\n\n.legendSwatch_768838850 {\n display: inline-block;\n width: 10px;\n height: 10px;\n border-radius: 2px;\n}\n\n.legendTrend_768838850 {\n background: var(--dsw-trend-line);\n width: 14px;\n height: 3px;\n border-radius: 2px;\n}\n\n/* ── per-model / per-provider chart + list ─────────────────────────────── */\n\n/* The ring is centred against the list beside it. The list's height is never\n tied to the chart, so expanding a row cannot stretch anything. */\n.models_768838850 {\n display: flex;\n gap: 16px;\n align-items: center;\n flex-wrap: wrap;\n min-width: 0;\n}\n\n/* The ring's own wrapper. Its diameter comes from the measured row width (see\n Donut.tsx_768838850), so it must never flex. */\n.donutWrap_768838850 {\n position: relative;\n flex: 0 0 auto;\n}\n\n.donut_768838850 {\n display: block;\n}\n\n.donutTrack_768838850 {\n stroke: var(--dsw-alias-bg-mask-1);\n}\n\n.donutSeg_768838850 {\n transition: stroke-width 0.12s ease, opacity 0.12s ease;\n}\n\n.donutDim_768838850 {\n opacity: 0.35;\n}\n\n.donutCenter_768838850 {\n fill: var(--dsw-alias-label-primary);\n font-size: 18px;\n font-weight: 600;\n}\n\n.donutLabel_768838850 {\n fill: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n/* Two ranking cards, side by side: MODELS left, PROVIDERS right. Both are\n always on screen (no lens/tab switch), so they share one row grid and\n stretch to a common height; a narrow pane stacks them instead. */\n.rankCards_768838850 {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n}\n\n@media (max-width: 720px) {\n .rankCards_768838850 {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n/* Each card is the bordered surface its rows live in; the title is a recessed\n header strip, so the two rankings read as two equal panels. */\n.rankCard_768838850 {\n display: flex;\n flex-direction: column;\n min-width: 0;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n.rankCardTitle_768838850 {\n padding: 8px 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n background: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n font-weight: 600;\n}\n\n.rankList_768838850 {\n list-style: none;\n margin: 0;\n padding: 0;\n flex: 1 1 auto;\n min-width: 0;\n}\n\n/* Every row opens a hover tip (the cross-dimension detail), so it carries the\n same hover affordance as the chart marks. */\n.modelRow_768838850:hover {\n background: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 60%, transparent);\n}\n\n/* Position column: the row's rank inside its section. The Other bucket holds\n no single rank, so it renders an empty (but equally wide) cell. */\n.modelRank_768838850 {\n flex: 0 0 auto;\n min-width: 14px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n text-align: right;\n}\n\n.modelRow_768838850 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n font-size: 13px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.modelRow_768838850:last-child {\n border-bottom: none;\n}\n\n/* Share bar under a rank row's name: the row's slice of the section total,\n drawn in the row's own colour (a 3px track, like the design mock). */\n.rowTrack_768838850 {\n height: 3px;\n margin-top: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-border-l1);\n overflow: hidden;\n}\n\n.rowFill_768838850 {\n display: block;\n height: 3px;\n border-radius: 2px;\n}\n\n/* Top-3 ranks read as a podium: the ordinal takes the warm accent instead of\n the muted gray the tail ranks keep. */\n.rankList_768838850 > .modelRow_768838850:nth-child(-n + 3) .modelRank_768838850 {\n color: var(--dsw-rank-top);\n font-weight: 700;\n}\n\n/* \"Other\" aggregate row: the entire row is clickable to expand/collapse the\n detail list. The chevron button inside it stops propagation so\n double-firing is avoided. */\n.modelRowExpandable_768838850 {\n cursor: pointer;\n}\n\n/* \"Other\" row toggle (chevron) that expands the aggregated tail models. */\n.modelToggle_768838850 {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n margin-right: 4px;\n border: none;\n background: none;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n vertical-align: middle;\n}\n\n/* Expanded detail rows: indented, slightly recessed. */\n.modelRowSub_768838850 {\n padding-left: 28px;\n background: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent);\n font-size: 13px;\n}\n\n/* Second-level indent: a model row inside a provider that is itself folded\n into the Other bucket. Wider than .modelRowSub_768838850 so the nesting stays\n readable. Declared after it — same specificity, so this one wins. */\n.modelRowDeep_768838850 {\n padding-left: 48px;\n}\n\n/* \"Other\" details expand with an accordion: the wrapper is a grid whose\n single track animates 0fr → 1fr. */\n.modelOtherWrap_768838850 {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 0.25s ease;\n}\n\n.modelOtherOpen_768838850 {\n grid-template-rows: 1fr;\n}\n\n.modelOtherList_768838850 {\n overflow: hidden;\n min-height: 0;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/* Two-line row anatomy: [swatch] [name / provider] ……… [tokens / share].\n The name block takes the free width; the values block hugs the right. */\n.modelId_768838850 {\n display: flex;\n flex-direction: column;\n gap: 1px;\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.modelValues_768838850 {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 1px;\n flex: 0 0 auto;\n}\n\n.modelName_768838850 {\n color: var(--dsw-alias-label-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n.modelProvider_768838850 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.modelTokens_768838850 {\n color: var(--dsw-alias-label-secondary);\n font-variant-numeric: tabular-nums;\n}\n\n.modelPct_768838850 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n}\n\n/* ── tooltips ──────────────────────────────────────────────────────────── */\n\n/* Tooltips render as fixed-position portals on document.body_768838850 (see ChartTip),\n so they escape every ancestor's clipping and never overflow the settings\n panel: ChartTip clamps each tip inside the panel bounds and flips it above\n the hovered element when there is no room below. The plate follows the\n active theme: the DSH overlay token is a light gray in light mode and a\n dark gray under body[data-ds-dark-theme], so the tip reads correctly in\n both instead of staying dark in light mode. */\n.tip_768838850 {\n position: fixed;\n /* The tip is portaled to document.body_768838850, so it competes with the settings\n modal itself (z-index 1000) for the top layer: 100 would sit under the\n modal and the tip would be invisible. 1100 matches the official\n toast/portaled-menu layer, which is exactly the \"over a modal\" tier. */\n z-index: 1100;\n pointer-events: none;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-overlay);\n border: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n box-shadow: 0 2px 12px var(--dsw-alias-bg-mask-2);\n white-space: nowrap;\n}\n\n.tipTitle_768838850 {\n font-weight: 600;\n margin-bottom: 4px;\n}\n\n/* A provider row's raw route id, kept out of the row label but reachable on\n hover (the row shows the display name). */\n.tipSubtitle_768838850 {\n margin: -2px 0 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n/* Breakdown heading inside a row tip: names the cross-dimension list under it\n (\"By provider\" under a model, \"By model\" under a provider). */\n.tipLabel_768838850 {\n margin-bottom: 2px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n/* The breakdown line pushes its volume to the right edge of the plate. */\n.tipName_768838850 {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.tipValue_768838850 {\n margin-left: auto;\n padding-left: 12px;\n color: var(--dsw-alias-label-secondary);\n font-variant-numeric: tabular-nums;\n}\n\n.tipRow_768838850 {\n display: flex;\n align-items: center;\n gap: 5px;\n}\n\n/* Hover tooltip breakdown rows for the \"Other\" aggregate: indented, muted,\n same gray swatch as the Other step. */\n.tipRowOther_768838850 {\n padding-left: 8px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.tipBreakdown_768838850 {\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 4px;\n padding-top: 4px;\n}\n\n/* ── chart palette ──────────────────────────────────────────────────────── */\n\n/* Light variant (the theme's default): model colours are the reasonix\n token-total palette (rank order by token volume + gray Other) with a\n white veil mixed in (color-mix 30% white) — the raw hues read dark and\n heavy on the light card surface, and the veil lifts lightness and dulls\n saturation the way a translucent white overlay would, keeping the hues\n colourful instead of muddy. The heatmap ramp is a fixed emerald green\n scale (GitHub-style activity heatmap), stepping from a near-white tint to\n a deep green; the hit-rate curve rides the brand blue.\n Ranks 6-10 extend the reasonix five to ten (this plugin's addition): each\n is picked to keep the ten hues apart on both themes.\n The palette must ALSO live on :root: tooltips are fixed portals on\n document.body_768838850 (see ChartTip), outside the panel subtree, and the inline\n var(--dsw-chart-N) swatches they carry only resolve from :root. */\n:root, .panel_768838850 {\n --dsw-heat-0: #f4f5f5;\n --dsw-heat-1: #daf6ea;\n --dsw-heat-2: #c7f0df;\n --dsw-heat-3: #82edc0;\n --dsw-heat-4: #00b96b;\n --dsw-heat-5: #00663b;\n --dsw-trend-line: #4d6bfe;\n /* Overview-card tone accents (stat cards 1..6) and the podium ordinal.\n Six hues, each picked to sit apart from both the chart set and the heat\n ramp, at full strength so 12% badge tints stay visible on white. */\n --dsw-tone-1: #4d6bfe;\n --dsw-tone-2: #00b96b;\n --dsw-tone-3: #e07b39;\n --dsw-tone-4: #975bf1;\n --dsw-tone-5: #0d8a8a;\n --dsw-tone-6: #d34591;\n --dsw-rank-top: #c46212;\n --dsw-chart-1: color-mix(in srgb, #0576ff 70%, white);\n --dsw-chart-2: color-mix(in srgb, #2f6f37 70%, white);\n --dsw-chart-3: color-mix(in srgb, #c46212 70%, white);\n --dsw-chart-4: color-mix(in srgb, #975bf1 70%, white);\n --dsw-chart-5: color-mix(in srgb, #d34591 70%, white);\n --dsw-chart-6: color-mix(in srgb, #0d8a8a 70%, white);\n --dsw-chart-7: color-mix(in srgb, #b3341f 70%, white);\n --dsw-chart-8: color-mix(in srgb, #7a9018 70%, white);\n --dsw-chart-9: color-mix(in srgb, #4b3fd0 70%, white);\n --dsw-chart-10: color-mix(in srgb, #d4a017 70%, white);\n --dsw-chart-other: color-mix(in srgb, #576270 70%, white);\n /* Provider series: a separate five-hue set for the provider section, so a\n provider never wears a model's colour. */\n --dsw-provider-1: color-mix(in srgb, #1f7a8c 70%, white);\n --dsw-provider-2: color-mix(in srgb, #6a8e3a 70%, white);\n --dsw-provider-3: color-mix(in srgb, #a15c2f 70%, white);\n --dsw-provider-4: color-mix(in srgb, #7b5ea7 70%, white);\n --dsw-provider-5: color-mix(in srgb, #b03a5b 70%, white);\n --dsw-provider-other: color-mix(in srgb, #576270 70%, white);\n}\n\n/* Dark variant: the same reasonix hues under a thicker white veil\n (35% — dark surfaces need the extra lift), so segments read soft and\n bright instead of sinking into the background. */\n@media (prefers-color-scheme: dark) {\n :root, .panel_768838850 {\n --dsw-heat-0: #1b211e;\n --dsw-heat-1: #123a2a;\n --dsw-heat-2: #17553a;\n --dsw-heat-3: #1c7a4e;\n --dsw-heat-4: #2fbf7c;\n --dsw-heat-5: #7ef0b4;\n --dsw-trend-line: #7d95ff;\n /* Dark tones are lifted versions of the light six: same identity hue,\n brighter so 16% badge tints and 3px bars read on dark surfaces. */\n --dsw-tone-1: #7d95ff;\n --dsw-tone-2: #2fbf7c;\n --dsw-tone-3: #e89a5c;\n --dsw-tone-4: #b18cff;\n --dsw-tone-5: #3bc5c5;\n --dsw-tone-6: #f076ac;\n --dsw-rank-top: #e08543;\n --dsw-chart-1: color-mix(in srgb, #0576ff 65%, white);\n --dsw-chart-2: color-mix(in srgb, #2f6f37 65%, white);\n --dsw-chart-3: color-mix(in srgb, #c46212 65%, white);\n --dsw-chart-4: color-mix(in srgb, #975bf1 65%, white);\n --dsw-chart-5: color-mix(in srgb, #d34591 65%, white);\n --dsw-chart-6: color-mix(in srgb, #0d8a8a 65%, white);\n --dsw-chart-7: color-mix(in srgb, #b3341f 65%, white);\n --dsw-chart-8: color-mix(in srgb, #7a9018 65%, white);\n --dsw-chart-9: color-mix(in srgb, #4b3fd0 65%, white);\n --dsw-chart-10: color-mix(in srgb, #d4a017 65%, white);\n --dsw-chart-other: color-mix(in srgb, #576270 65%, white);\n --dsw-provider-1: color-mix(in srgb, #1f7a8c 65%, white);\n --dsw-provider-2: color-mix(in srgb, #6a8e3a 65%, white);\n --dsw-provider-3: color-mix(in srgb, #a15c2f 65%, white);\n --dsw-provider-4: color-mix(in srgb, #7b5ea7 65%, white);\n --dsw-provider-5: color-mix(in srgb, #b03a5b 65%, white);\n --dsw-provider-other: color-mix(in srgb, #576270 65%, white);\n }\n\n /* Dark surfaces swallow pale tints, so the card accents step up: badge\n 12% → 16%, corner wash 7% → 10%. */\n .cardIcon_768838850 {\n background: color-mix(in srgb, var(--card-accent) 16%, transparent);\n }\n\n .card_768838850::after {\n background: linear-gradient(135deg, color-mix(in srgb, var(--card-accent) 10%, transparent), transparent 60%);\n }\n}\n";
|
|
29436
|
-
const tagId$
|
|
29437
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$
|
|
29435
|
+
const css$4 = "/**\n * Usage statistics panel styles — a DSH-native CSS Module. Every visual\n * value rides the DSH semantic tokens (--dsw-alias-* colors, --dsw-font-*\n * typography, --ds-* motion); no static palette values, no theme selectors.\n * The heatmap's 5 levels come from the brand accent via color-mix; model\n * colours are the fixed --dsw-chart-1..5 series (rank order by token\n * volume) plus the gray --dsw-chart-other step — the reasonix token-total\n * palette, with a lifted dark variant defined below.\n */\n\n/* The panel as a standalone main panel. It gets the same 960px content column\n the Plugins page gives it — the declarations below mirror the Plugins page's\n own `.page_768838850` so both entries render identically. Without the cap the charts\n would stretch to whatever the centre column happens to be.\n\n One deliberate difference from that page: its 28px top inset is NOT part of\n this container. A main panel starts at the window's top-left corner, so an\n inset painted by this scrolling container is a strip above the content that\n no window-drag region covers; `.backRow_768838850` below owns the inset instead, which\n is the shape the shipped page plugins use for the same reason\n (`ui-plugin-manager`'s `.pageHead_768838850`/`.detailTop_768838850` and `ui-schedule`'s page\n head). The rendered position is unchanged. */\n.page_768838850 {\n display: flex;\n flex-direction: column;\n align-items: center;\n box-sizing: border-box;\n height: 100%;\n overflow: auto;\n padding: 0 clamp(24px, 4vw, 48px) 48px;\n color: var(--dsw-alias-label-primary);\n}\n\n.page_768838850 > * {\n width: 100%;\n max-width: 960px;\n}\n\n/* The standalone panel's back control. Only the main panel renders this seat —\n the Plugins page entry already sits inside the page's own chrome, which\n draws its own crumb back to the bundle list. The spacing matches the gap the\n panel keeps between its own sections.\n\n The row also carries the panel's former 28px top inset: it starts at the\n window's top edge, so the whole strip above the back control drags the\n window (Electron app-regions are geometry, not stacking, so a strip owned by\n the scrolling container would subtract itself from the drag row beneath\n it). */\n.backRow_768838850 {\n display: flex;\n box-sizing: border-box;\n padding-top: 28px;\n margin-bottom: 18px;\n}\n\n.back_768838850 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.panel_768838850 {\n display: flex;\n flex-direction: column;\n gap: 18px;\n min-width: 0;\n}\n\n/* ── toolbar ───────────────────────────────────────────────────────────── */\n\n.toolbar_768838850 {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 10px;\n}\n\n/* Each picker (time range) lives in a framed group. */\n.group_768838850 {\n display: inline-flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 2px;\n padding: 3px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 9px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n/* A range-preset chip: ghost Button inside the group, active state tinted\n with the brand accent (same blue as the section bars and the total card). */\n.segItem_768838850 {\n border-radius: 6px;\n}\n\n.segItem_768838850:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.segActive_768838850,\n.segActive_768838850:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-tone-1) 14%, transparent);\n color: var(--dsw-tone-1);\n font-weight: 600;\n}\n\n.customRange_768838850 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.dateInput_768838850 {\n width: 150px;\n flex: 0 0 auto;\n}\n\n.customSep_768838850 {\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* Trailing toolbar edge: the \"as of\" stamp sits immediately left of the\n refresh action (it stamps the very data that button refreshes). */\n.toolbarEnd_768838850 {\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 10px;\n}\n\n.asOf_768838850 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n white-space: nowrap;\n}\n\n/* Keep the remaining toolbar action at the trailing edge. */\n.refresh_768838850 {\n flex: 0 0 auto;\n}\n\n/* ── states ────────────────────────────────────────────────────────────── */\n\n.errorBanner_768838850 {\n padding: 10px 14px;\n border: 1px solid var(--dsw-alias-state-warn-primary);\n border-radius: 8px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);\n color: var(--dsw-alias-state-warn-label);\n font-size: 13px;\n}\n\n.loading_768838850 {\n padding: 24px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n}\n\n.empty_768838850 {\n padding: 24px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 8px;\n}\n\n/* ── numeric cards ─────────────────────────────────────────────────────── */\n\n/* Six equal tracks: the overview row is one grid of six identical cards\n (long value or not), so no card crowds its neighbours. At the Plugins\n page's 960px content width that is six columns on one row. */\n.cards_768838850 {\n display: grid;\n grid-template-columns: repeat(6, minmax(0, 1fr));\n gap: 10px;\n}\n\n/* Below the width where six cards hold their own, the grid folds to three\n columns (two rows of three), then to two (three rows of two). */\n@media (max-width: 720px) {\n .cards_768838850 {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n }\n}\n\n@media (max-width: 430px) {\n .cards_768838850 {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n\n/* Each stat card wears one accent hue (tone 1..6 → --dsw-tone-1..6): a 3px\n accent bar on the left edge, the icon in a tinted badge, and a whisper of\n the hue washing the top-left corner — the overview row reads in colour\n instead of one gray register. --card-accent is set by the .cardToneN_768838850 map\n below; content sits above the wash via a z-index layer. */\n.card_768838850 {\n --card-accent: var(--dsw-alias-label-tertiary);\n position: relative;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 14px 14px 12px 18px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-1);\n box-shadow: 0 1px 2px var(--dsw-alias-bg-mask-2);\n min-width: 0;\n}\n\n.card_768838850::before {\n content: \"\";\n position: absolute;\n left: 0;\n top: 12px;\n bottom: 12px;\n width: 3px;\n border-radius: 0 3px 3px 0;\n background: var(--card-accent);\n}\n\n.card_768838850::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background: linear-gradient(135deg, color-mix(in srgb, var(--card-accent) 7%, transparent), transparent 60%);\n pointer-events: none;\n}\n\n/* Content must paint above the corner wash (the ::after pseudo is the last\n child, so it wins a same-layer paint order). */\n.card_768838850 > * {\n position: relative;\n z-index: 1;\n}\n\n.cardTone1_768838850 { --card-accent: var(--dsw-tone-1); }\n.cardTone2_768838850 { --card-accent: var(--dsw-tone-2); }\n.cardTone3_768838850 { --card-accent: var(--dsw-tone-3); }\n.cardTone4_768838850 { --card-accent: var(--dsw-tone-4); }\n.cardTone5_768838850 { --card-accent: var(--dsw-tone-5); }\n.cardTone6_768838850 { --card-accent: var(--dsw-tone-6); }\n\n.cardHead_768838850 {\n display: flex;\n align-items: center;\n gap: 7px;\n}\n\n/* Icon badge: the accent hue at 12% behind its own glyph. */\n.cardIcon_768838850 {\n box-sizing: border-box;\n width: 24px;\n height: 24px;\n padding: 5px;\n border-radius: 7px;\n color: var(--card-accent);\n background: color-mix(in srgb, var(--card-accent) 12%, transparent);\n flex: 0 0 auto;\n}\n\n.cardLabel_768838850 {\n color: var(--dsw-alias-label-secondary);\n font-size: 13px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Values never wrap: a wrapped number defeats FitText's measurement (the\n wrapped line is exactly as wide as its box) and reads as a layout break.\n Oversized text shrinks instead — see FitText in TokenTotalPanel.tsx_768838850. */\n.cardValue_768838850 {\n color: var(--dsw-alias-label-primary);\n font-weight: 600;\n line-height: 1.15;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.cardValueSm_768838850 {\n font-size: 14px;\n}\n\n.cardValueWrap_768838850 {\n word-break: break-word;\n}\n\n/* The \"most used model\" card value: the model name over the provider, the\n same two-line anatomy as the per-model list rows (modelName/modelProvider\n classes), instead of the raw \"provider/model\" ref. */\n.cardModelLines_768838850 {\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n/* A muted second line under a card value (the absolute cached-token volume\n under the hit-rate percentage). Same no-wrap rule as the value above. */\n.cardSub_768838850 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n line-height: 1.2;\n white-space: nowrap;\n overflow: hidden;\n}\n\n/* ── sections / chart chrome ───────────────────────────────────────────── */\n\n.section_768838850 {\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n padding: 14px 16px 16px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-1);\n box-shadow: 0 1px 2px var(--dsw-alias-bg-mask-2);\n}\n\n.sectionHead_768838850 {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n/* Section titles carry the same brand accent bar as the stat cards' tone\n language, so every card heading is anchored the same way. */\n.sectionTitle_768838850 {\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 15px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.sectionTitle_768838850::before {\n content: \"\";\n width: 3px;\n height: 13px;\n border-radius: 2px;\n background: var(--dsw-tone-1);\n flex: 0 0 auto;\n}\n\n/* Header meta cluster right of a section title (trend cap note + curve\n legend). Kept as one wrap-friendly row on a common baseline. */\n.headMeta_768838850 {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.trendNote_768838850 {\n font-size: 13px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* Heatmap and the token trend share ONE row — one card each, left and right.\n Both columns stretch to a common height; the heatmap centres its SVG in the\n taller partner. Below 720px the pair folds to a single column. */\n.duo_768838850 {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 12px;\n align-items: stretch;\n}\n\n@media (max-width: 720px) {\n .duo_768838850 {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n.chartWrap_768838850 {\n position: relative;\n min-width: 0;\n}\n\n/* ── heatmap ───────────────────────────────────────────────────────────── */\n\n.heatWrap_768838850 {\n position: relative;\n /* hidden, not auto: the SVG now fills the container exactly, and an\n auto scrollbar would flash when a fixed tooltip passes over the wrap's\n right edge. */\n overflow: hidden;\n min-width: 0;\n /* In the duo row the heatmap card shares height with the trend card; the\n grid centres itself in whatever slack the partner leaves. */\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n}\n\n.heatmap_768838850 {\n display: block;\n margin: 0 auto;\n /* The ~364 day cells are flat SVG rects (no per-cell filter), so there is no\n blur chain left to re-rasterize — but promoting the SVG onto its own\n compositing layer still keeps the whole grid off the scroll repaint path,\n which is what made scrolling this panel drop frames while idle was clean\n (#4). Cheap and invisible, so it stays. Note that the promotion is what\n does the work: `contain: paint` on its own measured no better than no\n mitigation at all. */\n transform: translateZ(0);\n}\n\n.heatLegend_768838850 {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 13px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* Axis labels drawn inside the heatmap SVG: the weekday column on the left and\n the month strip along the bottom. SVG <text> takes `fill`, not `color`. */\n.heatAxis_768838850 {\n fill: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n}\n\n.heatCell_768838850 {\n display: inline-block;\n border-radius: 2px;\n /* No width/height here: the SVG cells carry their size as geometry\n attributes (x/y/width/height), and a CSS width/height would override\n those on SVG2 rects — pinning every cell to a fixed px and widening the\n visual gap. The legend swatches carry the same size inline, so both\n always match the measured geom.size_768838850.\n No filter either: the reference chart paints each tile as a flat, matte\n rounded square, so a sheen/under-shadow would read as a smudge at this\n cell size rather than depth. */\n}\n\n/* The five heat levels ride a fixed emerald ramp sampled pixel-for-pixel from\n the reference chart (a GitHub-style green scale), with a darker variant for\n dark mode below — never the theme accent, which can read near-black against\n the base surface in dark mode. level0 (no data) is a neutral empty cell.\n The fills are fully opaque: the reference tiles are solid, and mixing toward\n transparent (the previous glass treatment) lightened every step and broke\n the sampled ramp. The cells are SVG <rect>s, so the color rides `fill`\n (background is a no-op on SVG geometry) — but the legend swatches are HTML,\n so both declarations stay in step. */\n.heatLevel0_768838850 {\n fill: var(--dsw-heat-0);\n background: var(--dsw-heat-0);\n}\n\n.heatLevel1_768838850 {\n fill: var(--dsw-heat-1);\n background: var(--dsw-heat-1);\n}\n\n.heatLevel2_768838850 {\n fill: var(--dsw-heat-2);\n background: var(--dsw-heat-2);\n}\n\n.heatLevel3_768838850 {\n fill: var(--dsw-heat-3);\n background: var(--dsw-heat-3);\n}\n\n.heatLevel4_768838850 {\n fill: var(--dsw-heat-4);\n background: var(--dsw-heat-4);\n}\n\n.heatLevel5_768838850 {\n fill: var(--dsw-heat-5);\n background: var(--dsw-heat-5);\n}\n\n/* ── daily trend ───────────────────────────────────────────────────────── */\n\n.chart_768838850 {\n display: block;\n}\n\n.grid_768838850 {\n stroke: var(--dsw-alias-border-l1);\n stroke-width: 1;\n}\n\n.axis_768838850 {\n fill: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n}\n\n.axisRate_768838850 {\n fill: var(--dsw-alias-label-tertiary);\n}\n\n.bar_768838850 {\n transition: opacity 0.12s ease;\n /* The hover scaleX must pivot on the bar's own centre: SVG geometry\n transforms around the SVG origin unless transform-box anchors it to the\n element's fill box. Without this the scaled bar slides sideways and\n looks like it vanishes. */\n transform-box: fill-box;\n transform-origin: center;\n}\n\n.barDim_768838850 {\n opacity: 0.35;\n}\n\n.barHit_768838850 {\n fill: transparent;\n pointer-events: all;\n}\n\n/* cache hit-rate trend curve: DeepSeek's brand blue (#4D6BFE, the same hue\n family as the heatmap ramp) — a fixed brand ramp instead of the theme\n accent, which can read near-black in some themes. */\n.trend_768838850 {\n stroke: var(--dsw-trend-line);\n stroke-width: 2;\n opacity: 0.9;\n pointer-events: none;\n}\n\n.trendDot_768838850 {\n fill: var(--dsw-trend-line);\n stroke: var(--dsw-alias-bg-layer-1);\n stroke-width: 2;\n pointer-events: none;\n}\n\n.legend_768838850 {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.legendItem_768838850 {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n cursor: default;\n}\n\n.legendSwatch_768838850 {\n display: inline-block;\n width: 10px;\n height: 10px;\n border-radius: 2px;\n}\n\n.legendTrend_768838850 {\n background: var(--dsw-trend-line);\n width: 14px;\n height: 3px;\n border-radius: 2px;\n}\n\n/* ── per-model / per-provider chart + list ─────────────────────────────── */\n\n/* The ring is centred against the list beside it. The list's height is never\n tied to the chart, so expanding a row cannot stretch anything. */\n.models_768838850 {\n display: flex;\n gap: 16px;\n align-items: center;\n flex-wrap: wrap;\n min-width: 0;\n}\n\n/* The ring's own wrapper. Its diameter comes from the measured row width (see\n Donut.tsx_768838850), so it must never flex. */\n.donutWrap_768838850 {\n position: relative;\n flex: 0 0 auto;\n}\n\n.donut_768838850 {\n display: block;\n}\n\n.donutTrack_768838850 {\n stroke: var(--dsw-alias-bg-mask-1);\n}\n\n.donutSeg_768838850 {\n transition: stroke-width 0.12s ease, opacity 0.12s ease;\n}\n\n.donutDim_768838850 {\n opacity: 0.35;\n}\n\n.donutCenter_768838850 {\n fill: var(--dsw-alias-label-primary);\n font-size: 18px;\n font-weight: 600;\n}\n\n.donutLabel_768838850 {\n fill: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n}\n\n/* Two ranking cards, side by side: MODELS left, PROVIDERS right. Both are\n always on screen (no lens/tab switch), so they share one row grid and\n stretch to a common height; a narrow pane stacks them instead. */\n.rankCards_768838850 {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n}\n\n@media (max-width: 720px) {\n .rankCards_768838850 {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n/* Each card is the bordered surface its rows live in; the title is a recessed\n header strip, so the two rankings read as two equal panels. */\n.rankCard_768838850 {\n display: flex;\n flex-direction: column;\n min-width: 0;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n.rankCardTitle_768838850 {\n padding: 8px 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n background: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n font-weight: 600;\n}\n\n.rankList_768838850 {\n list-style: none;\n margin: 0;\n padding: 0;\n flex: 1 1 auto;\n min-width: 0;\n}\n\n/* Every row opens a hover tip (the cross-dimension detail), so it carries the\n same hover affordance as the chart marks. */\n.modelRow_768838850:hover {\n background: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 60%, transparent);\n}\n\n/* Position column: the row's rank inside its section. The Other bucket holds\n no single rank, so it renders an empty (but equally wide) cell. */\n.modelRank_768838850 {\n flex: 0 0 auto;\n min-width: 14px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n text-align: right;\n}\n\n.modelRow_768838850 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n font-size: 13px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.modelRow_768838850:last-child {\n border-bottom: none;\n}\n\n/* Share bar under a rank row's name: the row's slice of the section total,\n drawn in the row's own colour (a 3px track, like the design mock). */\n.rowTrack_768838850 {\n height: 3px;\n margin-top: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-border-l1);\n overflow: hidden;\n}\n\n.rowFill_768838850 {\n display: block;\n height: 3px;\n border-radius: 2px;\n}\n\n/* Top-3 ranks read as a podium: the ordinal takes the warm accent instead of\n the muted gray the tail ranks keep. */\n.rankList_768838850 > .modelRow_768838850:nth-child(-n + 3) .modelRank_768838850 {\n color: var(--dsw-rank-top);\n font-weight: 700;\n}\n\n/* \"Other\" aggregate row: the entire row is clickable to expand/collapse the\n detail list. The chevron button inside it stops propagation so\n double-firing is avoided. */\n.modelRowExpandable_768838850 {\n cursor: pointer;\n}\n\n/* \"Other\" row toggle (chevron) that expands the aggregated tail models. */\n.modelToggle_768838850 {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n margin-right: 4px;\n border: none;\n background: none;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n vertical-align: middle;\n}\n\n/* Expanded detail rows: indented, slightly recessed. */\n.modelRowSub_768838850 {\n padding-left: 28px;\n background: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent);\n font-size: 13px;\n}\n\n/* Second-level indent: a model row inside a provider that is itself folded\n into the Other bucket. Wider than .modelRowSub_768838850 so the nesting stays\n readable. Declared after it — same specificity, so this one wins. */\n.modelRowDeep_768838850 {\n padding-left: 48px;\n}\n\n/* \"Other\" details expand with an accordion: the wrapper is a grid whose\n single track animates 0fr → 1fr. */\n.modelOtherWrap_768838850 {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 0.25s ease;\n}\n\n.modelOtherOpen_768838850 {\n grid-template-rows: 1fr;\n}\n\n.modelOtherList_768838850 {\n overflow: hidden;\n min-height: 0;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/* Two-line row anatomy: [swatch] [name / provider] ……… [tokens / share].\n The name block takes the free width; the values block hugs the right. */\n.modelId_768838850 {\n display: flex;\n flex-direction: column;\n gap: 1px;\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.modelValues_768838850 {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 1px;\n flex: 0 0 auto;\n}\n\n.modelName_768838850 {\n color: var(--dsw-alias-label-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n.modelProvider_768838850 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.modelTokens_768838850 {\n color: var(--dsw-alias-label-secondary);\n font-variant-numeric: tabular-nums;\n}\n\n.modelPct_768838850 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n}\n\n/* ── tooltips ──────────────────────────────────────────────────────────── */\n\n/* Tooltips render as fixed-position portals on document.body_768838850 (see ChartTip),\n so they escape every ancestor's clipping and never overflow the settings\n panel: ChartTip clamps each tip inside the panel bounds and flips it above\n the hovered element when there is no room below. The plate follows the\n active theme: the DSH overlay token is a light gray in light mode and a\n dark gray under body[data-ds-dark-theme], so the tip reads correctly in\n both instead of staying dark in light mode. */\n.tip_768838850 {\n position: fixed;\n /* The tip is portaled to document.body_768838850, so it competes with the settings\n modal itself (z-index 1000) for the top layer: 100 would sit under the\n modal and the tip would be invisible. 1100 matches the official\n toast/portaled-menu layer, which is exactly the \"over a modal\" tier. */\n z-index: 1100;\n pointer-events: none;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-overlay);\n border: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n box-shadow: 0 2px 12px var(--dsw-alias-bg-mask-2);\n white-space: nowrap;\n}\n\n.tipTitle_768838850 {\n font-weight: 600;\n margin-bottom: 4px;\n}\n\n/* A provider row's raw route id, kept out of the row label but reachable on\n hover (the row shows the display name). */\n.tipSubtitle_768838850 {\n margin: -2px 0 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n/* Breakdown heading inside a row tip: names the cross-dimension list under it\n (\"By provider\" under a model, \"By model\" under a provider). */\n.tipLabel_768838850 {\n margin-bottom: 2px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n/* The breakdown line pushes its volume to the right edge of the plate. */\n.tipName_768838850 {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.tipValue_768838850 {\n margin-left: auto;\n padding-left: 12px;\n color: var(--dsw-alias-label-secondary);\n font-variant-numeric: tabular-nums;\n}\n\n.tipRow_768838850 {\n display: flex;\n align-items: center;\n gap: 5px;\n}\n\n/* Hover tooltip breakdown rows for the \"Other\" aggregate: indented, muted,\n same gray swatch as the Other step. */\n.tipRowOther_768838850 {\n padding-left: 8px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.tipBreakdown_768838850 {\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 4px;\n padding-top: 4px;\n}\n\n/* ── chart palette ──────────────────────────────────────────────────────── */\n\n/* Light variant (the theme's default): model colours are the reasonix\n token-total palette (rank order by token volume + gray Other) with a\n white veil mixed in (color-mix 30% white) — the raw hues read dark and\n heavy on the light card surface, and the veil lifts lightness and dulls\n saturation the way a translucent white overlay would, keeping the hues\n colourful instead of muddy. The heatmap ramp is a fixed emerald green\n scale (GitHub-style activity heatmap), stepping from a near-white tint to\n a deep green; the hit-rate curve rides the brand blue.\n Ranks 6-10 extend the reasonix five to ten (this plugin's addition): each\n is picked to keep the ten hues apart on both themes.\n The palette must ALSO live on :root: tooltips are fixed portals on\n document.body_768838850 (see ChartTip), outside the panel subtree, and the inline\n var(--dsw-chart-N) swatches they carry only resolve from :root. */\n:root, .panel_768838850 {\n --dsw-heat-0: #f4f5f5;\n --dsw-heat-1: #daf6ea;\n --dsw-heat-2: #c7f0df;\n --dsw-heat-3: #82edc0;\n --dsw-heat-4: #00b96b;\n --dsw-heat-5: #00663b;\n --dsw-trend-line: #4d6bfe;\n /* Overview-card tone accents (stat cards 1..6) and the podium ordinal.\n Six hues, each picked to sit apart from both the chart set and the heat\n ramp, at full strength so 12% badge tints stay visible on white. */\n --dsw-tone-1: #4d6bfe;\n --dsw-tone-2: #00b96b;\n --dsw-tone-3: #e07b39;\n --dsw-tone-4: #975bf1;\n --dsw-tone-5: #0d8a8a;\n --dsw-tone-6: #d34591;\n --dsw-rank-top: #c46212;\n --dsw-chart-1: color-mix(in srgb, #0576ff 70%, white);\n --dsw-chart-2: color-mix(in srgb, #2f6f37 70%, white);\n --dsw-chart-3: color-mix(in srgb, #c46212 70%, white);\n --dsw-chart-4: color-mix(in srgb, #975bf1 70%, white);\n --dsw-chart-5: color-mix(in srgb, #d34591 70%, white);\n --dsw-chart-6: color-mix(in srgb, #0d8a8a 70%, white);\n --dsw-chart-7: color-mix(in srgb, #b3341f 70%, white);\n --dsw-chart-8: color-mix(in srgb, #7a9018 70%, white);\n --dsw-chart-9: color-mix(in srgb, #4b3fd0 70%, white);\n --dsw-chart-10: color-mix(in srgb, #d4a017 70%, white);\n --dsw-chart-other: color-mix(in srgb, #576270 70%, white);\n /* Provider series: a separate five-hue set for the provider section, so a\n provider never wears a model's colour. */\n --dsw-provider-1: color-mix(in srgb, #1f7a8c 70%, white);\n --dsw-provider-2: color-mix(in srgb, #6a8e3a 70%, white);\n --dsw-provider-3: color-mix(in srgb, #a15c2f 70%, white);\n --dsw-provider-4: color-mix(in srgb, #7b5ea7 70%, white);\n --dsw-provider-5: color-mix(in srgb, #b03a5b 70%, white);\n --dsw-provider-other: color-mix(in srgb, #576270 70%, white);\n}\n\n/* Dark variant: the same reasonix hues under a thicker white veil\n (35% — dark surfaces need the extra lift), so segments read soft and\n bright instead of sinking into the background. */\n@media (prefers-color-scheme: dark) {\n :root, .panel_768838850 {\n --dsw-heat-0: #1b211e;\n --dsw-heat-1: #123a2a;\n --dsw-heat-2: #17553a;\n --dsw-heat-3: #1c7a4e;\n --dsw-heat-4: #2fbf7c;\n --dsw-heat-5: #7ef0b4;\n --dsw-trend-line: #7d95ff;\n /* Dark tones are lifted versions of the light six: same identity hue,\n brighter so 16% badge tints and 3px bars read on dark surfaces. */\n --dsw-tone-1: #7d95ff;\n --dsw-tone-2: #2fbf7c;\n --dsw-tone-3: #e89a5c;\n --dsw-tone-4: #b18cff;\n --dsw-tone-5: #3bc5c5;\n --dsw-tone-6: #f076ac;\n --dsw-rank-top: #e08543;\n --dsw-chart-1: color-mix(in srgb, #0576ff 65%, white);\n --dsw-chart-2: color-mix(in srgb, #2f6f37 65%, white);\n --dsw-chart-3: color-mix(in srgb, #c46212 65%, white);\n --dsw-chart-4: color-mix(in srgb, #975bf1 65%, white);\n --dsw-chart-5: color-mix(in srgb, #d34591 65%, white);\n --dsw-chart-6: color-mix(in srgb, #0d8a8a 65%, white);\n --dsw-chart-7: color-mix(in srgb, #b3341f 65%, white);\n --dsw-chart-8: color-mix(in srgb, #7a9018 65%, white);\n --dsw-chart-9: color-mix(in srgb, #4b3fd0 65%, white);\n --dsw-chart-10: color-mix(in srgb, #d4a017 65%, white);\n --dsw-chart-other: color-mix(in srgb, #576270 65%, white);\n --dsw-provider-1: color-mix(in srgb, #1f7a8c 65%, white);\n --dsw-provider-2: color-mix(in srgb, #6a8e3a 65%, white);\n --dsw-provider-3: color-mix(in srgb, #a15c2f 65%, white);\n --dsw-provider-4: color-mix(in srgb, #7b5ea7 65%, white);\n --dsw-provider-5: color-mix(in srgb, #b03a5b 65%, white);\n --dsw-provider-other: color-mix(in srgb, #576270 65%, white);\n }\n\n /* Dark surfaces swallow pale tints, so the card accents step up: badge\n 12% → 16%, corner wash 7% → 10%. */\n .cardIcon_768838850 {\n background: color-mix(in srgb, var(--card-accent) 16%, transparent);\n }\n\n .card_768838850::after {\n background: linear-gradient(135deg, color-mix(in srgb, var(--card-accent) 10%, transparent), transparent 60%);\n }\n}\n";
|
|
29436
|
+
const tagId$4 = "dsh-token-total/TokenTotalPanel.module.css";
|
|
29437
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$4 + "\"]") === null) {
|
|
29438
29438
|
const tag = document.createElement("style");
|
|
29439
29439
|
tag.dataset.plugin = "dsh-token-total";
|
|
29440
|
-
tag.dataset.pluginCss = tagId$
|
|
29441
|
-
tag.textContent = css$
|
|
29440
|
+
tag.dataset.pluginCss = tagId$4;
|
|
29441
|
+
tag.textContent = css$4;
|
|
29442
29442
|
document.head.appendChild(tag);
|
|
29443
29443
|
}
|
|
29444
29444
|
var TokenTotalPanel_module_default = {
|
|
@@ -31590,13 +31590,13 @@ window.__ModuleLoader__.load({
|
|
|
31590
31590
|
}
|
|
31591
31591
|
//#endregion
|
|
31592
31592
|
//#region \0token-total-css:/home/runner/work/dsh-token-total/dsh-token-total/src/client/StatsLineEnhanced.module.css?inline
|
|
31593
|
-
const css$
|
|
31594
|
-
const tagId$
|
|
31595
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$
|
|
31593
|
+
const css$3 = "/* Session stats pills under the composer (shadow of the official StatsPills):\n a gauge pill (counts + output speed, click opens the time dialog) and a\n database pill (total tokens + cache hit, click opens the usage dialog).\n Replicated from the official StatsPills.module_348665330.css_348665330 + stat-dialog.module_348665330.css_348665330\n (13/20 tertiary tier, hover-pill affordance, shared dialog skin).\n\n Host contract: the row serves two containers at once, and picks between them\n at attach time — the component marks which one it landed in (see\n StatsLineEnhanced's `markDockContainer`):\n - through 0.1.6-alpha.1 the dock slot renders straight into InputBar's own\n column, and this row owns the content-width clamp, its side gutters and\n its 4px top pad — the official StatsPills.module_348665330.css_348665330 of that line;\n - 0.1.6-alpha.2 renders it inside InputBar's flex `.dock_348665330` beside the resident\n ContextMeter, which took all three jobs over, so the row only has to\n shrink inside it (the official stylesheet's current form).\n Both forms are kept side by side because one published build has to render\n correctly on either host line. */\n\n.root_348665330 {\n display: flex;\n justify-content: center;\n gap: 12px;\n box-sizing: border-box;\n /* <= 0.1.6-alpha.1: this row is the composer column's content-width box. */\n width: 100%;\n max-width: var(--dsh-chat-content-width);\n margin: 0 auto;\n padding: 4px calc(var(--dsh-composer-side-clearance) + 16px) 0px;\n font-size: var(--dsh-content-font-size-secondary, 13px);\n line-height: calc(20px + var(--dsh-content-font-delta-secondary, 0px));\n}\n\n/* 0.1.6-alpha.2+: InputBar's `.dock_348665330` owns the centring, the 12px gap, the top\n pad and the side clearance. */\n.root_348665330[data-dock-row] {\n width: auto;\n min-width: 0;\n max-width: 100%;\n margin: 0;\n padding: 0;\n}\n\n/* Dialog anchor: shrink-wraps its pill so the panel clamp measures the pill. */\n.anchor_348665330 {\n display: inline-flex;\n min-width: 0;\n}\n\n/* Split-mode anchor (DSH >= 0.2.1-alpha.1). The host split the single `stats`\n dock entry into one entry per pill and let its own `.dock_348665330` flex row own the\n layout, so the anchor IS the entry root and carries the text tier `.root_348665330`\n used to set — the official StatsPills.module_348665330.css_348665330 moved exactly this pair of\n declarations from `.root_348665330` onto `.anchor_348665330`. The tier stays off the plain\n `.anchor_348665330` because the pre-split row must keep its own `.root_348665330` tier. */\n.dockAnchor_348665330 {\n font-size: var(--dsh-content-font-size-secondary, 13px);\n line-height: calc(20px + var(--dsh-content-font-delta-secondary, 0px));\n}\n\n.pill_348665330 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n box-sizing: border-box;\n max-width: 100%;\n padding: 1px 8px;\n border: none;\n border-radius: var(--dsw-radius-sm);\n background: none;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n font-variant-numeric: tabular-nums;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.pill_348665330 svg {\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n/* Only the button form (a pill with dialog rows to open) invites interaction;\n the static span form keeps the resting tertiary reading. */\nbutton.pill_348665330 {\n cursor: pointer;\n}\n\nbutton.pill_348665330:hover,\nbutton.pill_348665330[aria-expanded='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.label_348665330 {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sep_348665330 {\n color: var(--dsw-alias-separator-primary);\n margin: 0 6px;\n}\n\n/* The live throughput reading sits inline exactly like the official figure:\n no reserved width, no box of its own. The earlier `min-width: 10ch` slot kept\n the centred row from shifting while the number grew, but it also left a\n permanent gap before the separator — the official pill has no such slot, and\n native parity wins over that jitter fix. */\n.speed_348665330 {\n display: inline;\n}\n\n/* Portal dialog surface: fixed in the viewport, left/top supplied inline from\n the anchored-position clamp (12px viewport margin, layers above overlays).\n Material = the official stat-dialog PAIRING, which dsh 0.1.7's menu\n redesign made mandatory: `--dsw-specific-menu` is translucent there\n (rgba 58%/45%), so the blur token must ride along — the 0.1.5-era recipe\n this file copied was a single opaque line, and a fill-only copy reads as\n bare transparency over the page (issue #6). */\n.dialogPanel_348665330 {\n position: fixed;\n z-index: 1100;\n box-sizing: border-box;\n width: max-content;\n min-width: min(300px, calc(100vw - 24px));\n max-width: min(440px, calc(100vw - 24px));\n padding: 12px;\n border: 0;\n border-radius: var(--dsw-radius-lg);\n background: var(--dsw-specific-menu);\n backdrop-filter: var(--dsw-menu-backdrop-filter);\n --dsw-elevation-stroke-color: var(--dsw-alias-border-l1);\n box-shadow: var(--dsw-elevation-prominent);\n font-size: var(--dsh-content-font-size-secondary, 13px);\n line-height: calc(20px + var(--dsh-content-font-delta-secondary, 0px));\n color: var(--dsw-alias-label-secondary);\n cursor: default;\n}\n\n/* Dialog heading row: the section name left, the headline value right. */\n.dialogTitle_348665330 {\n display: flex;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 8px;\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n/* Rule under the heading, above its rows. */\n.dialogTitleRule_348665330 {\n margin-bottom: 8px;\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.dialogTitleValue_348665330 {\n font-variant-numeric: tabular-nums;\n}\n\n/* Heading glyph seated left of the section name. */\n.dialogTitleLabel_348665330 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n\n.dialogTitleLabel_348665330 svg {\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n.dialogDetails_348665330 {\n display: grid;\n grid-template-columns: minmax(76px, auto) minmax(0, 1fr);\n gap: 4px 12px;\n margin: 0;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dialogDetails_348665330 dt,\n.dialogDetails_348665330 dd {\n min-width: 0;\n margin: 0;\n}\n\n.dialogDetails_348665330 dd {\n color: var(--dsw-alias-label-secondary);\n font-variant-numeric: tabular-nums;\n text-align: right;\n}\n";
|
|
31594
|
+
const tagId$3 = "dsh-token-total/StatsLineEnhanced.module.css";
|
|
31595
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$3 + "\"]") === null) {
|
|
31596
31596
|
const tag = document.createElement("style");
|
|
31597
31597
|
tag.dataset.plugin = "dsh-token-total";
|
|
31598
|
-
tag.dataset.pluginCss = tagId$
|
|
31599
|
-
tag.textContent = css$
|
|
31598
|
+
tag.dataset.pluginCss = tagId$3;
|
|
31599
|
+
tag.textContent = css$3;
|
|
31600
31600
|
document.head.appendChild(tag);
|
|
31601
31601
|
}
|
|
31602
31602
|
var StatsLineEnhanced_module_default = {
|
|
@@ -32076,13 +32076,13 @@ window.__ModuleLoader__.load({
|
|
|
32076
32076
|
}
|
|
32077
32077
|
//#endregion
|
|
32078
32078
|
//#region \0token-total-css:/home/runner/work/dsh-token-total/dsh-token-total/src/client/ContextUsageEntry.module.css?inline
|
|
32079
|
-
const css$
|
|
32080
|
-
const tagId$
|
|
32081
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$
|
|
32079
|
+
const css$2 = "/* Mirror the host ContextMeter, including its theme and font-size tokens. */\n.root_1877794043 {\n flex: none;\n display: inline-flex;\n}\n\n.trigger_1877794043 {\n border-radius: var(--dsw-radius-sm);\n color: var(--dsw-alias-label-tertiary);\n font-family: inherit;\n font-size: var(--dsh-content-font-size-secondary, 13px);\n font-variant-numeric: tabular-nums;\n line-height: calc(20px + var(--dsh-content-font-delta-secondary, 0px));\n white-space: nowrap;\n cursor: pointer;\n background: transparent;\n border: none;\n flex: none;\n align-items: center;\n gap: 6px;\n padding: 1px 8px;\n display: inline-flex;\n}\n\n.trigger_1877794043:hover,\n.trigger_1877794043[aria-expanded='true'] {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.track_1877794043 {\n fill: none;\n stroke: var(--dsw-alias-border-l3);\n stroke-width: 2px;\n}\n\n.fill_1877794043 {\n fill: none;\n stroke: var(--dsw-alias-label-tertiary);\n stroke-width: 2px;\n stroke-linecap: round;\n}\n\n.panel_1877794043 {\n z-index: 1100;\n box-sizing: border-box;\n border-radius: var(--dsw-radius-lg);\n background: var(--dsw-specific-menu);\n width: min(264px, calc(100vw - 24px));\n backdrop-filter: var(--dsw-menu-backdrop-filter);\n --dsw-elevation-stroke-color: var(--dsw-alias-border-l1);\n box-shadow: var(--dsw-elevation-prominent);\n color: var(--dsw-alias-label-secondary);\n cursor: default;\n border: 0;\n padding: 12px;\n font-size: 12px;\n line-height: 20px;\n position: fixed;\n}\n\n.header_1877794043 {\n align-items: center;\n gap: 6px;\n display: flex;\n}\n\n.figures_1877794043 {\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-primary);\n margin-left: auto;\n font-weight: 500;\n}\n\n.percent_1877794043 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.headline_1877794043 { color: var(--dsw-alias-label-tertiary); }\n.headline_1877794043:empty { display: none; }\n\n.bar_1877794043 {\n corner-shape: round;\n background: var(--dsw-alias-interactive-bg-hover);\n border-radius: 999px;\n gap: 1px;\n height: 4px;\n margin: 10px 0 12px;\n display: flex;\n overflow: hidden;\n}\n\n.segment_1877794043 {\n background: var(--meter-tint, var(--dsw-alias-label-tertiary));\n border-radius: 1px;\n flex: none;\n min-width: 2px;\n height: 100%;\n}\n\n.swatch_1877794043 {\n background: var(--meter-tint);\n vertical-align: baseline;\n border-radius: 2px;\n width: 8px;\n height: 8px;\n margin-right: 6px;\n display: inline-block;\n}\n\n.colorSystem_1877794043 { --meter-tint: var(--dsw-static-neutral-bluish-400); }\n.colorTools_1877794043 { --meter-tint: #a78bfa; }\n.colorMessages_1877794043 { --meter-tint: var(--dsw-static-blue-450); }\n.rows_1877794043 { margin: 6px 0 0; }\n\n.row_1877794043 {\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n padding: 2px 0;\n display: flex;\n}\n\n.row_1877794043 dt { color: var(--dsw-alias-label-secondary); }\n.row_1877794043 dd {\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-primary);\n margin: 0;\n}\n";
|
|
32080
|
+
const tagId$2 = "dsh-token-total/ContextUsageEntry.module.css";
|
|
32081
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$2 + "\"]") === null) {
|
|
32082
32082
|
const tag = document.createElement("style");
|
|
32083
32083
|
tag.dataset.plugin = "dsh-token-total";
|
|
32084
|
-
tag.dataset.pluginCss = tagId$
|
|
32085
|
-
tag.textContent = css$
|
|
32084
|
+
tag.dataset.pluginCss = tagId$2;
|
|
32085
|
+
tag.textContent = css$2;
|
|
32086
32086
|
document.head.appendChild(tag);
|
|
32087
32087
|
}
|
|
32088
32088
|
var ContextUsageEntry_module_default = {
|
|
@@ -32707,6 +32707,521 @@ window.__ModuleLoader__.load({
|
|
|
32707
32707
|
};
|
|
32708
32708
|
}
|
|
32709
32709
|
//#endregion
|
|
32710
|
+
//#region src/client/context-window-core.ts
|
|
32711
|
+
/** Legend order, also the color assignment order (see the CSS module). */
|
|
32712
|
+
const CONTEXT_CATEGORY_ORDER = [
|
|
32713
|
+
"system",
|
|
32714
|
+
"tools",
|
|
32715
|
+
"memory",
|
|
32716
|
+
"files",
|
|
32717
|
+
"results",
|
|
32718
|
+
"conversation"
|
|
32719
|
+
];
|
|
32720
|
+
/** Per-category token totals in legend order, empty categories dropped. */
|
|
32721
|
+
function contextCategoryTotals(items) {
|
|
32722
|
+
const totals = /* @__PURE__ */ new Map();
|
|
32723
|
+
for (const item of items) totals.set(item.category, (totals.get(item.category) ?? 0) + item.tokens);
|
|
32724
|
+
return CONTEXT_CATEGORY_ORDER.map((key) => ({
|
|
32725
|
+
key,
|
|
32726
|
+
tokens: totals.get(key) ?? 0
|
|
32727
|
+
})).filter((row) => row.tokens > 0);
|
|
32728
|
+
}
|
|
32729
|
+
/** Window occupancy in whole percent, or null when the host never reported the
|
|
32730
|
+
* model's window (the panel then says "unknown" instead of guessing one). */
|
|
32731
|
+
function contextWindowPercent(usedTokens, contextWindow) {
|
|
32732
|
+
if (contextWindow === null || !(contextWindow > 0) || !(usedTokens >= 0)) return null;
|
|
32733
|
+
return Math.min(100, Math.round(usedTokens / contextWindow * 100));
|
|
32734
|
+
}
|
|
32735
|
+
function contextReading(context) {
|
|
32736
|
+
const estimateTokens = context.totalTokens;
|
|
32737
|
+
const usedTokens = context.promptTokens ?? estimateTokens;
|
|
32738
|
+
const gap = estimateTokens > 0 ? Math.abs(estimateTokens - usedTokens) / Math.max(estimateTokens, usedTokens) : 0;
|
|
32739
|
+
return {
|
|
32740
|
+
usedTokens,
|
|
32741
|
+
percent: contextWindowPercent(usedTokens, context.contextWindow),
|
|
32742
|
+
estimateTokens,
|
|
32743
|
+
estimateDiffers: context.promptTokens !== null && gap > .15
|
|
32744
|
+
};
|
|
32745
|
+
}
|
|
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
|
+
//#endregion
|
|
32835
|
+
//#region \0token-total-css:/home/runner/work/dsh-token-total/dsh-token-total/src/client/ContextWindowPanel.module.css?inline
|
|
32836
|
+
const css$1 = "/* The context-window panel: magpie's shape on this page's surfaces — a header\n * with the model and a live pill, the used total beside the newest answer's\n * prompt-cache split, a treemap of what occupies the window, and one legend row\n * naming the six buckets. Category colors live as custom properties on the tone\n * classes so the tile and the legend dot share one palette. */\n.panel_1441117854 { background:#fff; border:1px solid #e4e7ee; border-radius:12px; margin:0 0 16px; overflow:hidden; }\n.head_1441117854 { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:12px 16px 0; }\n.headCollapsed_1441117854 { padding-bottom:12px; }\n.toggle_1441117854 { display:flex; align-items:center; gap:6px; min-width:0; background:none; border:0; padding:0; font:inherit; color:#394354; cursor:pointer; }\n.toggle_1441117854 h2 { font-size:13px; font-weight:600; margin:0; white-space:nowrap; }\n.chevron_1441117854 { flex:none; color:#99a1af; transition:transform .2s; }\n.chevronOpen_1441117854 { transform:rotate(90deg); }\n.headRight_1441117854 { display:flex; align-items:center; gap:8px; min-width:0; }\n.model_1441117854 { min-width:0; max-width:220px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:11px; color:#8b93a4; }\n.livePill_1441117854 { display:inline-flex; align-items:center; gap:4px; flex:none; padding:1px 8px 1px 6px; border-radius:999px; background:#e9f8f1; color:#128a63; font-size:10px; font-weight:500; }\n.liveDot_1441117854 { width:5px; height:5px; border-radius:50%; background:#18a97b; box-shadow:0 0 0 2px #d6f2e5; }\n.liveTime_1441117854 { font-variant-numeric:tabular-nums; opacity:.85; }\n.toggle_1441117854:focus-visible { outline:2px solid #2ab1c6; outline-offset:2px; }\n.body_1441117854 { padding:10px 16px 14px; }\n.body_1441117854[hidden] { display:none; }\n.summary_1441117854 { display:grid; grid-template-columns:minmax(0,1fr) minmax(180px,260px); align-items:end; gap:18px; padding-bottom:12px; }\n.used_1441117854 { display:flex; flex-direction:column; gap:1px; min-width:0; }\n.label_1441117854 { font-size:11px; color:#8790a3; }\n.total_1441117854 { font-size:28px; font-weight:650; line-height:1.15; letter-spacing:-.6px; color:#1b2130; font-variant-numeric:tabular-nums; white-space:nowrap; }\n.total_1441117854 small { margin-left:4px; font-size:11px; font-weight:500; letter-spacing:0; color:#98a0ae; }\n.window_1441117854 { font-size:11px; color:#939aab; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.cache_1441117854 { display:flex; flex-direction:column; gap:6px; min-width:0; }\n.cacheBar_1441117854 { display:flex; width:100%; height:5px; border-radius:999px; overflow:hidden; background:#eef0f4; }\n.cacheHit_1441117854 { height:100%; background:#2ec4a0; }\n.cacheMiss_1441117854 { height:100%; background:#dfe3ea; }\n.cacheEmpty_1441117854 { display:block; width:100%; height:100%; background:repeating-linear-gradient(90deg,#e6e9ef 0 3px,transparent 3px 7px); }\n.cacheLegend_1441117854 { display:flex; flex-wrap:wrap; gap:2px 10px; font-size:10px; color:#8b93a4; font-variant-numeric:tabular-nums; }\n.cacheLegend_1441117854 i { display:inline-block; width:6px; height:6px; margin-right:4px; border-radius:50%; }\n.cacheHitDot_1441117854 { background:#2ec4a0; }\n.cacheMissDot_1441117854 { background:#dfe3ea; }\n.cachePending_1441117854 { color:#9aa3b3; }\n.note_1441117854 { margin:0 0 10px; padding:5px 8px; border:1px solid #f2e4c6; border-radius:6px; background:#fdf7ea; font-size:10.5px; line-height:1.5; color:#8a6a2a; font-variant-numeric:tabular-nums; }\n.map_1441117854 { position:relative; height:268px; border-radius:9px; overflow:hidden; }\n/* Slots glide into their new geometry when a refresh re-writes the estimate,\n * so a change in the data reads as motion instead of a silent repaint. */\n.slot_1441117854 { position:absolute; box-sizing:border-box; padding:1.5px; transition:left .35s, top .35s, width .35s, height .35s; }\n.tile_1441117854 { display:flex; flex-direction:column; gap:1px; box-sizing:border-box; width:100%; height:100%; padding:5px 6px; border:1px solid var(--ctx-line); border-radius:7px; background:var(--ctx-soft); color:var(--ctx-ink); overflow:hidden; }\n.tileLabel_1441117854 { font-size:11px; font-weight:600; line-height:1.25; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }\n.tileSize_1441117854 { font-size:10px; opacity:.72; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-variant-numeric:tabular-nums; }\n/* The hover card: exact figures a tile has no room to print. */\n.tip_1441117854 { position:absolute; z-index:2; box-sizing:border-box; width:220px; padding:8px 10px; border:1px solid #e0e4ec; border-radius:8px; background:#fff; box-shadow:0 8px 20px rgba(23,32,54,.12); font-size:11px; line-height:1.55; pointer-events:none; }\n.tipName_1441117854 { font-weight:600; color:#1b2130; overflow-wrap:anywhere; }\n.tipMeta_1441117854 { color:#6f7889; font-variant-numeric:tabular-nums; }\n.tipShare_1441117854 { color:#4a5567; font-variant-numeric:tabular-nums; }\n.legend_1441117854 { display:flex; flex-wrap:wrap; gap:5px 14px; list-style:none; margin:0; padding:10px 0 0; border-top:1px solid #f2f4f8; margin-top:10px; font-size:11px; color:#6f7889; }\n.legend_1441117854 li { display:flex; align-items:center; gap:5px; }\n.legendName_1441117854 { white-space:nowrap; }\n.legendValue_1441117854 { color:#98a0ae; font-variant-numeric:tabular-nums; }\n.swatch_1441117854 { display:inline-block; flex:none; box-sizing:border-box; width:9px; height:9px; border-radius:3px; background:var(--ctx-soft); background:color-mix(in srgb, var(--ctx) 50%, var(--ctx-soft)); border:1px solid var(--ctx-line); }\n.empty_1441117854 { margin:0; padding:24px 0; text-align:center; font-size:12px; color:#9aa2b1; }\n/* Category palette: `--ctx` is the accent tint, `--ctx-soft` the tile fill (and\n * the legend swatch, so the dots match the tiles exactly), `--ctx-ink` the tile\n * text, `--ctx-line` its hairline border. */\n.toneSystem_1441117854 { --ctx:#a346e6; --ctx-soft:#f6ecfd; --ctx-ink:#6c2e9e; --ctx-line:#ecdafa; }\n.toneTools_1441117854 { --ctx:#4f79f6; --ctx-soft:#eaf0fe; --ctx-ink:#3a5aa8; --ctx-line:#d9e3fb; }\n.toneMemory_1441117854 { --ctx:#8fb21e; --ctx-soft:#f1f5d8; --ctx-ink:#5a7012; --ctx-line:#dfe8bd; }\n.toneFiles_1441117854 { --ctx:#18a97b; --ctx-soft:#e6f6ee; --ctx-ink:#1c7a56; --ctx-line:#d2ecdf; }\n.toneResults_1441117854 { --ctx:#e8853f; --ctx-soft:#fdeee3; --ctx-ink:#a35d24; --ctx-line:#f7ddc8; }\n.toneConversation_1441117854 { --ctx:#c2327f; --ctx-soft:#fbe9f3; --ctx-ink:#8c2259; --ctx-line:#f2d3e4; }\n@media (max-width:700px) {\n .head_1441117854 { padding:11px 12px 0; }\n .headCollapsed_1441117854 { padding-bottom:11px; }\n .body_1441117854 { padding:9px 12px 12px; }\n .summary_1441117854 { grid-template-columns:minmax(0,1fr); gap:10px; }\n .total_1441117854 { font-size:24px; }\n .map_1441117854 { height:220px; }\n .model_1441117854 { display:none; }\n}\n@media (prefers-reduced-motion:reduce) {\n .chevron_1441117854 { transition:none; }\n .slot_1441117854 { transition:none; }\n}\n";
|
|
32837
|
+
const tagId$1 = "dsh-token-total/ContextWindowPanel.module.css";
|
|
32838
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$1 + "\"]") === null) {
|
|
32839
|
+
const tag = document.createElement("style");
|
|
32840
|
+
tag.dataset.plugin = "dsh-token-total";
|
|
32841
|
+
tag.dataset.pluginCss = tagId$1;
|
|
32842
|
+
tag.textContent = css$1;
|
|
32843
|
+
document.head.appendChild(tag);
|
|
32844
|
+
}
|
|
32845
|
+
var ContextWindowPanel_module_default = {
|
|
32846
|
+
"panel": "panel_1441117854",
|
|
32847
|
+
"head": "head_1441117854",
|
|
32848
|
+
"headCollapsed": "headCollapsed_1441117854",
|
|
32849
|
+
"toggle": "toggle_1441117854",
|
|
32850
|
+
"chevron": "chevron_1441117854",
|
|
32851
|
+
"chevronOpen": "chevronOpen_1441117854",
|
|
32852
|
+
"headRight": "headRight_1441117854",
|
|
32853
|
+
"model": "model_1441117854",
|
|
32854
|
+
"livePill": "livePill_1441117854",
|
|
32855
|
+
"liveDot": "liveDot_1441117854",
|
|
32856
|
+
"liveTime": "liveTime_1441117854",
|
|
32857
|
+
"body": "body_1441117854",
|
|
32858
|
+
"summary": "summary_1441117854",
|
|
32859
|
+
"used": "used_1441117854",
|
|
32860
|
+
"label": "label_1441117854",
|
|
32861
|
+
"total": "total_1441117854",
|
|
32862
|
+
"window": "window_1441117854",
|
|
32863
|
+
"cache": "cache_1441117854",
|
|
32864
|
+
"cacheBar": "cacheBar_1441117854",
|
|
32865
|
+
"cacheHit": "cacheHit_1441117854",
|
|
32866
|
+
"cacheMiss": "cacheMiss_1441117854",
|
|
32867
|
+
"cacheEmpty": "cacheEmpty_1441117854",
|
|
32868
|
+
"cacheLegend": "cacheLegend_1441117854",
|
|
32869
|
+
"cacheHitDot": "cacheHitDot_1441117854",
|
|
32870
|
+
"cacheMissDot": "cacheMissDot_1441117854",
|
|
32871
|
+
"cachePending": "cachePending_1441117854",
|
|
32872
|
+
"note": "note_1441117854",
|
|
32873
|
+
"map": "map_1441117854",
|
|
32874
|
+
"slot": "slot_1441117854",
|
|
32875
|
+
"tile": "tile_1441117854",
|
|
32876
|
+
"tileLabel": "tileLabel_1441117854",
|
|
32877
|
+
"tileSize": "tileSize_1441117854",
|
|
32878
|
+
"tip": "tip_1441117854",
|
|
32879
|
+
"tipName": "tipName_1441117854",
|
|
32880
|
+
"tipMeta": "tipMeta_1441117854",
|
|
32881
|
+
"tipShare": "tipShare_1441117854",
|
|
32882
|
+
"legend": "legend_1441117854",
|
|
32883
|
+
"legendName": "legendName_1441117854",
|
|
32884
|
+
"legendValue": "legendValue_1441117854",
|
|
32885
|
+
"swatch": "swatch_1441117854",
|
|
32886
|
+
"empty": "empty_1441117854",
|
|
32887
|
+
"toneSystem": "toneSystem_1441117854",
|
|
32888
|
+
"toneTools": "toneTools_1441117854",
|
|
32889
|
+
"toneMemory": "toneMemory_1441117854",
|
|
32890
|
+
"toneFiles": "toneFiles_1441117854",
|
|
32891
|
+
"toneResults": "toneResults_1441117854",
|
|
32892
|
+
"toneConversation": "toneConversation_1441117854"
|
|
32893
|
+
};
|
|
32894
|
+
//#endregion
|
|
32895
|
+
//#region src/client/ContextWindowPanel.tsx
|
|
32896
|
+
const CATEGORY_LABEL = {
|
|
32897
|
+
system: "contextSystem",
|
|
32898
|
+
tools: "contextTools",
|
|
32899
|
+
memory: "contextMemory",
|
|
32900
|
+
files: "contextFiles",
|
|
32901
|
+
results: "contextResults",
|
|
32902
|
+
conversation: "contextConversation"
|
|
32903
|
+
};
|
|
32904
|
+
const CATEGORY_TONE = {
|
|
32905
|
+
system: ContextWindowPanel_module_default.toneSystem,
|
|
32906
|
+
tools: ContextWindowPanel_module_default.toneTools,
|
|
32907
|
+
memory: ContextWindowPanel_module_default.toneMemory,
|
|
32908
|
+
files: ContextWindowPanel_module_default.toneFiles,
|
|
32909
|
+
results: ContextWindowPanel_module_default.toneResults,
|
|
32910
|
+
conversation: ContextWindowPanel_module_default.toneConversation
|
|
32911
|
+
};
|
|
32912
|
+
/** Which locale string names an injected/system row, keyed by its log kind. */
|
|
32913
|
+
const KIND_LABEL = {
|
|
32914
|
+
"system-prompt": "contextKindSystemPrompt",
|
|
32915
|
+
"runtime-context": "contextKindRuntimeContext",
|
|
32916
|
+
"skill-catalog": "contextKindSkillCatalog",
|
|
32917
|
+
"time-context": "contextKindTimeContext",
|
|
32918
|
+
"compaction-summary": "contextKindCompaction",
|
|
32919
|
+
user: "contextKindUser",
|
|
32920
|
+
assistant: "contextKindAssistant"
|
|
32921
|
+
};
|
|
32922
|
+
/** What one treemap tile is called: a tool name or path as-is, an
|
|
32923
|
+
* injected kind through the dictionary, and a folded tail as "N more". */
|
|
32924
|
+
function itemText(item, t) {
|
|
32925
|
+
if (item.kind === "rest") return t("contextKindRest", { count: String(item.count) });
|
|
32926
|
+
const label = item.kind === void 0 ? void 0 : KIND_LABEL[item.kind];
|
|
32927
|
+
const base = label !== void 0 ? t(label) : item.label || item.kind || "";
|
|
32928
|
+
return item.count > 1 ? `${base} ${t("contextTimes", { count: String(item.count) })}` : base;
|
|
32929
|
+
}
|
|
32930
|
+
const sumOf = (items) => items.reduce((sum, item) => sum + item.tokens, 0);
|
|
32931
|
+
/** Wall-clock stamp of the last successful read, same shape as the page's. */
|
|
32932
|
+
const clock$1 = (ms) => new Date(ms).toLocaleTimeString([], {
|
|
32933
|
+
hour: "2-digit",
|
|
32934
|
+
minute: "2-digit",
|
|
32935
|
+
second: "2-digit"
|
|
32936
|
+
});
|
|
32937
|
+
/** Hover-card footprint estimate, so the tip can be kept inside the map
|
|
32938
|
+
* without measuring itself first. */
|
|
32939
|
+
const TIP_W = 220;
|
|
32940
|
+
const TIP_H = 70;
|
|
32941
|
+
/** The hover card for one tile: name and call count, the exact token count
|
|
32942
|
+
* with its category, and the block's share of the estimate and of the
|
|
32943
|
+
* model's window — the figures a tile has no room to print. */
|
|
32944
|
+
function Tip({ item, box, map, t, total, contextWindow }) {
|
|
32945
|
+
const share = total > 0 ? formatPercent(item.tokens / total * 100) : null;
|
|
32946
|
+
const windowShare = contextWindow !== null && contextWindow > 0 ? formatPercent(item.tokens / contextWindow * 100) : null;
|
|
32947
|
+
const left = Math.min(Math.max(box.x + box.width / 2 - TIP_W / 2, 4), Math.max(4, map.width - TIP_W - 4));
|
|
32948
|
+
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);
|
|
32949
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
32950
|
+
className: ContextWindowPanel_module_default.tip,
|
|
32951
|
+
role: "tooltip",
|
|
32952
|
+
style: {
|
|
32953
|
+
left,
|
|
32954
|
+
top
|
|
32955
|
+
},
|
|
32956
|
+
children: [
|
|
32957
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
32958
|
+
className: ContextWindowPanel_module_default.tipName,
|
|
32959
|
+
children: itemText(item, t)
|
|
32960
|
+
}),
|
|
32961
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
32962
|
+
className: ContextWindowPanel_module_default.tipMeta,
|
|
32963
|
+
children: [
|
|
32964
|
+
formatTokens(item.tokens),
|
|
32965
|
+
" token · ",
|
|
32966
|
+
t(CATEGORY_LABEL[item.category])
|
|
32967
|
+
]
|
|
32968
|
+
}),
|
|
32969
|
+
share !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
32970
|
+
className: ContextWindowPanel_module_default.tipShare,
|
|
32971
|
+
children: [t("contextTipShare", { percent: share }), windowShare === null ? "" : ` · ${t("contextTipWindow", { percent: windowShare })}`]
|
|
32972
|
+
})
|
|
32973
|
+
]
|
|
32974
|
+
});
|
|
32975
|
+
}
|
|
32976
|
+
function Treemap({ items, t, contextWindow }) {
|
|
32977
|
+
const hostRef = (0, react.useRef)(null);
|
|
32978
|
+
const [size, setSize] = (0, react.useState)({
|
|
32979
|
+
width: 0,
|
|
32980
|
+
height: 0
|
|
32981
|
+
});
|
|
32982
|
+
const [hoverKey, setHoverKey] = (0, react.useState)(null);
|
|
32983
|
+
(0, react.useLayoutEffect)(() => {
|
|
32984
|
+
const host = hostRef.current;
|
|
32985
|
+
if (host === null) return;
|
|
32986
|
+
const measure = () => setSize((previous) => {
|
|
32987
|
+
const width = host.clientWidth;
|
|
32988
|
+
const height = host.clientHeight;
|
|
32989
|
+
return previous.width === width && previous.height === height ? previous : {
|
|
32990
|
+
width,
|
|
32991
|
+
height
|
|
32992
|
+
};
|
|
32993
|
+
});
|
|
32994
|
+
measure();
|
|
32995
|
+
if (typeof ResizeObserver === "undefined") {
|
|
32996
|
+
window.addEventListener("resize", measure);
|
|
32997
|
+
return () => window.removeEventListener("resize", measure);
|
|
32998
|
+
}
|
|
32999
|
+
const observer = new ResizeObserver(measure);
|
|
33000
|
+
observer.observe(host);
|
|
33001
|
+
return () => observer.disconnect();
|
|
33002
|
+
}, []);
|
|
33003
|
+
const boxes = (0, react.useMemo)(() => treemapLayout(items.map((item) => ({
|
|
33004
|
+
key: item.key,
|
|
33005
|
+
tokens: item.tokens
|
|
33006
|
+
})), size.width, size.height), [
|
|
33007
|
+
items,
|
|
33008
|
+
size.width,
|
|
33009
|
+
size.height
|
|
33010
|
+
]);
|
|
33011
|
+
const byKey = (0, react.useMemo)(() => new Map(items.map((item) => [item.key, item])), [items]);
|
|
33012
|
+
const hoverBox = hoverKey === null ? void 0 : boxes.find((box) => box.key === hoverKey);
|
|
33013
|
+
const hoverItem = hoverBox === void 0 ? void 0 : byKey.get(hoverBox.key);
|
|
33014
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33015
|
+
ref: hostRef,
|
|
33016
|
+
className: ContextWindowPanel_module_default.map,
|
|
33017
|
+
onMouseLeave: () => setHoverKey(null),
|
|
33018
|
+
children: [boxes.map((box) => {
|
|
33019
|
+
const item = byKey.get(box.key);
|
|
33020
|
+
if (item === void 0 || box.width <= 0 || box.height <= 0) return null;
|
|
33021
|
+
const text = itemText(item, t);
|
|
33022
|
+
const showLabel = box.width >= 46 && box.height >= 22;
|
|
33023
|
+
const showSize = box.width >= 62 && box.height >= 40;
|
|
33024
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33025
|
+
className: ContextWindowPanel_module_default.slot,
|
|
33026
|
+
style: {
|
|
33027
|
+
left: box.x,
|
|
33028
|
+
top: box.y,
|
|
33029
|
+
width: box.width,
|
|
33030
|
+
height: box.height
|
|
33031
|
+
},
|
|
33032
|
+
onMouseEnter: () => setHoverKey(box.key),
|
|
33033
|
+
"aria-label": `${text} · ${formatTokens(item.tokens)} token · ${t(CATEGORY_LABEL[item.category])}`,
|
|
33034
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33035
|
+
className: `${ContextWindowPanel_module_default.tile} ${CATEGORY_TONE[item.category]}`,
|
|
33036
|
+
children: [showLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33037
|
+
className: ContextWindowPanel_module_default.tileLabel,
|
|
33038
|
+
children: text
|
|
33039
|
+
}), showSize && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33040
|
+
className: ContextWindowPanel_module_default.tileSize,
|
|
33041
|
+
children: [
|
|
33042
|
+
formatCompact(item.tokens),
|
|
33043
|
+
" · ",
|
|
33044
|
+
t(CATEGORY_LABEL[item.category])
|
|
33045
|
+
]
|
|
33046
|
+
})]
|
|
33047
|
+
})
|
|
33048
|
+
}, box.key);
|
|
33049
|
+
}), hoverBox !== void 0 && hoverItem !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tip, {
|
|
33050
|
+
item: hoverItem,
|
|
33051
|
+
box: hoverBox,
|
|
33052
|
+
map: size,
|
|
33053
|
+
t,
|
|
33054
|
+
total: sumOf(items),
|
|
33055
|
+
contextWindow
|
|
33056
|
+
})]
|
|
33057
|
+
});
|
|
33058
|
+
}
|
|
33059
|
+
/** The context-window panel above the Usage cards, in magpie's shape: the used
|
|
33060
|
+
* total and the model's window on the left, the newest answer's prompt-cache
|
|
33061
|
+
* split on the right, a treemap of what occupies the window below (with a
|
|
33062
|
+
* hover card per tile), and one legend row naming the six buckets with their
|
|
33063
|
+
* sizes. Every figure is an estimate, so the panel never claims an exact
|
|
33064
|
+
* count — and the live pill stamps when the estimate was last refreshed. */
|
|
33065
|
+
function ContextWindowPanel({ context, t, updatedAt }) {
|
|
33066
|
+
const [open, setOpen] = (0, react.useState)(true);
|
|
33067
|
+
const totals = contextCategoryTotals(context.items);
|
|
33068
|
+
const reading = contextReading(context);
|
|
33069
|
+
const percent = reading.percent;
|
|
33070
|
+
const hitRate = context.cacheKnown ? cacheRate(context.cacheHitTokens, context.cacheMissTokens) : null;
|
|
33071
|
+
const model = context.model !== "" && !context.model.includes("/") && context.provider !== "" ? `${context.provider}/${context.model}` : context.model;
|
|
33072
|
+
const windowText = context.contextWindow === null ? t("contextWindowUnknown") : t("contextWindowKnown", {
|
|
33073
|
+
value: formatCompact(context.contextWindow),
|
|
33074
|
+
percent: String(percent ?? 0)
|
|
33075
|
+
});
|
|
33076
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
33077
|
+
className: ContextWindowPanel_module_default.panel,
|
|
33078
|
+
"data-dsh-context-window": true,
|
|
33079
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
33080
|
+
className: open ? ContextWindowPanel_module_default.head : `${ContextWindowPanel_module_default.head} ${ContextWindowPanel_module_default.headCollapsed}`,
|
|
33081
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
33082
|
+
type: "button",
|
|
33083
|
+
className: ContextWindowPanel_module_default.toggle,
|
|
33084
|
+
onClick: () => setOpen(!open),
|
|
33085
|
+
"aria-expanded": open,
|
|
33086
|
+
"aria-controls": "dsh-context-window-body",
|
|
33087
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
33088
|
+
className: `${ContextWindowPanel_module_default.chevron} ${open ? ContextWindowPanel_module_default.chevronOpen : ""}`,
|
|
33089
|
+
width: "12",
|
|
33090
|
+
height: "12",
|
|
33091
|
+
viewBox: "0 0 12 12",
|
|
33092
|
+
"aria-hidden": "true",
|
|
33093
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
33094
|
+
d: "m4 2 4 4-4 4",
|
|
33095
|
+
stroke: "currentColor",
|
|
33096
|
+
strokeWidth: "1.5",
|
|
33097
|
+
fill: "none"
|
|
33098
|
+
})
|
|
33099
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("contextTitle") })]
|
|
33100
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33101
|
+
className: ContextWindowPanel_module_default.headRight,
|
|
33102
|
+
children: [model !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33103
|
+
className: ContextWindowPanel_module_default.model,
|
|
33104
|
+
title: `${context.provider} / ${context.model}`,
|
|
33105
|
+
children: model
|
|
33106
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33107
|
+
className: ContextWindowPanel_module_default.livePill,
|
|
33108
|
+
children: [
|
|
33109
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33110
|
+
className: ContextWindowPanel_module_default.liveDot,
|
|
33111
|
+
"aria-hidden": "true"
|
|
33112
|
+
}),
|
|
33113
|
+
t("contextLive"),
|
|
33114
|
+
typeof updatedAt === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
33115
|
+
className: ContextWindowPanel_module_default.liveTime,
|
|
33116
|
+
children: [" · ", clock$1(updatedAt)]
|
|
33117
|
+
})
|
|
33118
|
+
]
|
|
33119
|
+
})]
|
|
33120
|
+
})]
|
|
33121
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33122
|
+
id: "dsh-context-window-body",
|
|
33123
|
+
className: ContextWindowPanel_module_default.body,
|
|
33124
|
+
hidden: !open,
|
|
33125
|
+
children: [
|
|
33126
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33127
|
+
className: ContextWindowPanel_module_default.summary,
|
|
33128
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33129
|
+
className: ContextWindowPanel_module_default.used,
|
|
33130
|
+
children: [
|
|
33131
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33132
|
+
className: ContextWindowPanel_module_default.label,
|
|
33133
|
+
children: t("contextUsed")
|
|
33134
|
+
}),
|
|
33135
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", {
|
|
33136
|
+
className: ContextWindowPanel_module_default.total,
|
|
33137
|
+
title: formatTokens(reading.usedTokens),
|
|
33138
|
+
children: [formatCompact(reading.usedTokens), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: "token" })]
|
|
33139
|
+
}),
|
|
33140
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33141
|
+
className: ContextWindowPanel_module_default.window,
|
|
33142
|
+
children: windowText
|
|
33143
|
+
})
|
|
33144
|
+
]
|
|
33145
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33146
|
+
className: ContextWindowPanel_module_default.cache,
|
|
33147
|
+
children: [
|
|
33148
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33149
|
+
className: ContextWindowPanel_module_default.label,
|
|
33150
|
+
children: t("contextCache")
|
|
33151
|
+
}),
|
|
33152
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33153
|
+
className: ContextWindowPanel_module_default.cacheBar,
|
|
33154
|
+
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", {
|
|
33155
|
+
className: ContextWindowPanel_module_default.cacheHit,
|
|
33156
|
+
style: { width: `${hitRate}%` }
|
|
33157
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33158
|
+
className: ContextWindowPanel_module_default.cacheMiss,
|
|
33159
|
+
style: { width: `${100 - hitRate}%` }
|
|
33160
|
+
})] })
|
|
33161
|
+
}),
|
|
33162
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
33163
|
+
className: ContextWindowPanel_module_default.cacheLegend,
|
|
33164
|
+
children: hitRate === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33165
|
+
className: ContextWindowPanel_module_default.cachePending,
|
|
33166
|
+
children: t("contextCachePending")
|
|
33167
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
33168
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
|
|
33169
|
+
className: ContextWindowPanel_module_default.cacheHitDot,
|
|
33170
|
+
"aria-hidden": "true"
|
|
33171
|
+
}),
|
|
33172
|
+
t("contextCacheHit"),
|
|
33173
|
+
" ",
|
|
33174
|
+
formatPercent(hitRate)
|
|
33175
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
33176
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
|
|
33177
|
+
className: ContextWindowPanel_module_default.cacheMissDot,
|
|
33178
|
+
"aria-hidden": "true"
|
|
33179
|
+
}),
|
|
33180
|
+
t("contextCacheMiss"),
|
|
33181
|
+
" ",
|
|
33182
|
+
formatPercent(100 - hitRate)
|
|
33183
|
+
] })] })
|
|
33184
|
+
})
|
|
33185
|
+
]
|
|
33186
|
+
})]
|
|
33187
|
+
}),
|
|
33188
|
+
reading.estimateDiffers && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
33189
|
+
className: ContextWindowPanel_module_default.note,
|
|
33190
|
+
children: t("contextEstimateNote", {
|
|
33191
|
+
estimate: formatCompact(reading.estimateTokens),
|
|
33192
|
+
billed: formatCompact(reading.usedTokens)
|
|
33193
|
+
})
|
|
33194
|
+
}),
|
|
33195
|
+
context.items.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
33196
|
+
className: ContextWindowPanel_module_default.empty,
|
|
33197
|
+
children: t("contextEmpty")
|
|
33198
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Treemap, {
|
|
33199
|
+
items: context.items,
|
|
33200
|
+
t,
|
|
33201
|
+
contextWindow: context.contextWindow
|
|
33202
|
+
}),
|
|
33203
|
+
totals.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
33204
|
+
className: ContextWindowPanel_module_default.legend,
|
|
33205
|
+
children: totals.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
33206
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33207
|
+
className: `${ContextWindowPanel_module_default.swatch} ${CATEGORY_TONE[row.key]}`,
|
|
33208
|
+
"aria-hidden": "true"
|
|
33209
|
+
}),
|
|
33210
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33211
|
+
className: ContextWindowPanel_module_default.legendName,
|
|
33212
|
+
children: t(CATEGORY_LABEL[row.key])
|
|
33213
|
+
}),
|
|
33214
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33215
|
+
className: ContextWindowPanel_module_default.legendValue,
|
|
33216
|
+
children: formatCompact(row.tokens)
|
|
33217
|
+
})
|
|
33218
|
+
] }, row.key))
|
|
33219
|
+
})
|
|
33220
|
+
]
|
|
33221
|
+
})]
|
|
33222
|
+
});
|
|
33223
|
+
}
|
|
33224
|
+
//#endregion
|
|
32710
33225
|
//#region src/client/model-logos-data.ts
|
|
32711
33226
|
/**
|
|
32712
33227
|
* Official per-brand logos for the usage panel: the inner markup of each
|
|
@@ -32876,7 +33391,7 @@ window.__ModuleLoader__.load({
|
|
|
32876
33391
|
}
|
|
32877
33392
|
//#endregion
|
|
32878
33393
|
//#region \0token-total-css:/home/runner/work/dsh-token-total/dsh-token-total/src/client/SessionUsageView.module.css?inline
|
|
32879
|
-
const css = "/* A light, self-contained Usage surface. Tables fit the available width:\n * no horizontal scroll containers, fixed column contracts, titles for names.\n * Stat cards follow the reference dashboard: tinted left edge + icon chip,\n * label, big value, small subtitle. Request log folds by conversation turn. */\n.scroll_900471699 { height:100%; min-height:0; overflow-y:auto; background:#ffffff; color:#252936; color-scheme:light; container-type:inline-size; }\n.page_900471699 { box-sizing:border-box; width:100%; max-width:1160px; margin:0 auto; padding:24px 26px 32px; font-family:system-ui,-apple-system,\"Segoe UI\",sans-serif; font-size:13px; }\n.page_900471699 * { box-sizing:border-box; }\n/* Freshness header: live badge + last-read stamp on the left, manual refresh\n * pinned to the right edge. */\n.head_900471699 { display:flex; align-items:center; justify-content:space-between; gap:12px; margin:0 0 12px; }\n.headStatus_900471699 { display:flex; align-items:center; gap:7px; min-width:0; font-size:11px; color:#8790a3; }\n.liveDot_900471699 { width:6px; height:6px; border-radius:50%; background:#18a97b; box-shadow:0 0 0 3px #e5f6f0; flex-shrink:0; }\n.asOf_900471699 { color:#9aa3b3; font-variant-numeric:tabular-nums; white-space:nowrap; }\n.refresh_900471699 { display:inline-flex; align-items:center; gap:5px; flex:none; color:#6f7a8d; background:#fff; border:1px solid #e4e7ee; border-radius:8px; padding:5px 11px; font:inherit; font-size:11px; cursor:pointer; }\n.refresh_900471699:hover { color:#279cb6; border-color:#cbd3e0; }\n.refresh_900471699:disabled { opacity:.55; cursor:default; }\n.scope_900471699 { color:#8790a3; font-size:11px; margin:0 0 14px; }\n.stats_900471699 { display:grid; gap:12px; margin-bottom:18px; }\n.statRow_900471699 { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; }\n.stat_900471699 { min-width:0; position:relative; overflow:hidden; background:#fff; border:1px solid #e4e7ee; border-left:4px solid var(--tone); border-radius:12px; padding:12px 16px 11px; }\n.statHead_900471699 { display:flex; align-items:center; gap:8px; margin-bottom:8px; }\n.statIcon_900471699 { display:flex; align-items:center; justify-content:center; width:32px; height:32px; border-radius:10px; color:#fff; background:linear-gradient(135deg, var(--tone), var(--tone-soft)); flex-shrink:0; box-shadow:0 3px 8px var(--tone-soft); }\n.statMark_900471699 { position:absolute; right:-14px; bottom:-18px; width:92px; height:92px; color:var(--tone); pointer-events:none; }\n.statLabel_900471699 { font-size:12px; color:#7d8495; }\n.statValue_900471699 { display:block; font-size:25px; font-weight:650; letter-spacing:-0.5px; white-space:nowrap; font-variant-numeric:tabular-nums; }\n.statSub_900471699 { display:block; margin-top:5px; font-size:11px; color:#939aac; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.toneBlue_900471699 { --tone:#4f79f6; --tone-soft:#eaf0fe; }\n.tonePurple_900471699 { --tone:#8b5cf6; --tone-soft:#f1ebfe; }\n.toneGreen_900471699 { --tone:#18a97b; --tone-soft:#e5f6f0; }\n.toneOrange_900471699 { --tone:#e8892c; --tone-soft:#fcf1e4; }\n.toneTeal_900471699 { --tone:#16a4b8; --tone-soft:#e4f5f8; }\n.tonePink_900471699 { --tone:#d9538c; --tone-soft:#fbeaf1; }\n.card_900471699 { min-width:0; background:#fff; border:1px solid #e4e7ee; border-radius:12px; margin-bottom:18px; overflow:hidden; }\n.cardHead_900471699 { display:flex; align-items:center; justify-content:space-between; gap:10px; min-height:54px; padding:15px 18px; border-bottom:1px solid #eef0f4; }\n.cardHead_900471699 h2 { font-size:14px; font-weight:600; margin:0; }\n.cardHead_900471699 > span { display:flex; gap:7px; align-items:center; font-size:11px; }\n.count_900471699 { display:inline-flex; align-items:center; justify-content:center; min-width:21px; padding:2px 6px; font-size:10px; color:#8b93a3; background:#f3f5f8; border-radius:5px; margin-left:5px; font-weight:500; }\n.mid_900471699 { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:18px; align-items:stretch; }\n.mid_900471699 .card_900471699 { height:100%; margin-bottom:0; }\n.mid_900471699 { margin-bottom:18px; }\n.table_900471699 { border-collapse:collapse; width:100%; table-layout:fixed; font-size:11px; }\n.table_900471699 th { color:#929aaa; font-weight:500; padding:12px 5px; text-align:right; vertical-align:middle; line-height:1.3; overflow-wrap:anywhere; }\n.table_900471699 td { padding:13px 5px; text-align:right; border-top:1px solid #eef0f4; color:#626c80; font-variant-numeric:tabular-nums; white-space:nowrap; }\n.table_900471699 th:first-child,.table_900471699 td:first-child { text-align:left; padding-left:16px; }\n.table_900471699 th:last-child,.table_900471699 td:last-child { padding-right:16px; }\n.childName_900471699 { width:28%; }\n.childRequests_900471699 { width:11%; }\n.modelName_900471699 { width:30%; }\n.modelRequests_900471699 { width:11%; }\n.modelHit_900471699 { width:16%; }\n.ellipsis_900471699 { display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:#394354; }\n/* By-model rows lead with the model's brand logo; the name keeps truncating\n * inside the flexible remainder of the cell. */\n.modelNameCell_900471699 { display:flex; align-items:center; gap:6px; min-width:0; }\n.modelNameCell_900471699 .ellipsis_900471699 { flex:1; min-width:0; }\n/* Gray provider label under a model name shown by several rows (同名模型). */\n.modelProvider_900471699 { display:block; margin-top:2px; font-size:10px; color:#939aaa; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.number_900471699 { font-variant-numeric:tabular-nums; }\n.speedCell_900471699 { display:inline-flex; align-items:baseline; gap:3px; white-space:nowrap; }\n.speedCell_900471699 small { font-size:9px; color:#929aaa; }\n.good_900471699 { color:#21a17b; }\n.warn_900471699 { color:#d69a21; }\n.bad_900471699 { color:#e06571; }\n.live_900471699 { color:#169bb3; }\n.muted_900471699 { color:#939aaa; }\n.empty_900471699 { margin:0; padding:28px 18px; color:#9aa2b1; font-size:12px; text-align:center; }\n.action_900471699 { color:#818b9e; background:none; border:0; padding:3px 0; font:inherit; font-size:11px; cursor:pointer; }\n.action_900471699:hover { color:#279cb6; }\n.action_900471699:focus-visible,.turnHead_900471699:focus-visible,.refresh_900471699:focus-visible { outline:2px solid #2ab1c6; outline-offset:-2px; }\n.turnList_900471699 { padding:0 14px 14px; }\n.turn_900471699 { margin-top:10px; border:1px solid #e7eaf0; border-radius:9px; background:#fff; overflow:hidden; }\n.turnHead_900471699 { display:grid; grid-template-columns:12px minmax(0,1fr) auto; align-items:center; gap:12px; padding:13px 14px; width:100%; text-align:left; background:#f8f9fc; border:0; cursor:pointer; color:#616c7f; font:inherit; font-size:11px; }\n.chevron_900471699 { color:#99a1af; transition:transform 0.2s; }\n.rotated_900471699 { transform:rotate(90deg); }\n.turnTitle_900471699 { display:flex; align-items:center; gap:6px; min-width:0; font-size:12px; font-weight:600; color:#394354; }\n/* Turn topic is the group title now (the page header owns the session name),\n * so it carries the primary text color and truncates before the metrics. */\n.turnTopic_900471699 { min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }\n/* A subagent's turns look like this session's turns apart from the agent tag;\n * the tag keeps the full session name in its tooltip. */\n.turnAgent_900471699 { flex:none; padding:1px 6px; border-radius:5px; background:#e9edf4; color:#6f7889; font-size:10px; font-weight:500; }\n.turnSummary_900471699 { display:flex; gap:5px; align-items:center; white-space:nowrap; }\n.turnSummary_900471699 > span:last-child { margin-left:8px; }\n.turnBody_900471699 { border-top:1px solid #eef0f4; }\n.turnBody_900471699[hidden] { display:none; }\n/* A request row is one line: time, what answered, and its metrics pinned to the\n * row end. The metrics never wrap; they truncate with a tooltip before the head\n * gives up its model name. */\n.turnRequest_900471699 { display:grid; grid-template-columns:64px minmax(0,1fr) auto; align-items:center; gap:12px; padding:10px 16px; border-top:1px solid #f3f5f9; }\n.turnRequest_900471699:first-child { border-top:0; }\n.turnRequestHead_900471699 { display:flex; align-items:center; gap:8px; font-size:11px; color:#616c7f; min-width:0; }\n.requestTime_900471699 { color:#9aa3b3; font-variant-numeric:tabular-nums; flex-shrink:0; }\n.statusDot_900471699 { width:5px; height:5px; border-radius:50%; background:currentColor; flex-shrink:0; }\n/* Per-model brand logo: colors are baked into each logo's markup, only the\n * layout contract lives here. Unknown models get the neutral gray fallback. */\n.modelLogo_900471699 { flex-shrink:0; }\n.recordModel_900471699 { min-width:0; display:flex; align-items:center; gap:6px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#394354; font-weight:500; }\n.routeText_900471699 { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#8b93a4; flex-shrink:1; }\n.effort_900471699 { white-space:nowrap; color:#9aa3b3; background:#f1f3f7; border-radius:5px; padding:1px 6px; }\n.recordMeta_900471699 { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:right; font-size:10px; color:#949cab; font-variant-numeric:tabular-nums; }\n.note_900471699 { font-size:10px; line-height:1.7; color:#9aa3b3; margin:0; }\n.error_900471699 { background:#fff4f1; color:#b86751; border:1px solid #f4d8ce; border-radius:9px; padding:12px 16px; margin-bottom:16px; font-size:12px; display:flex; gap:16px; justify-content:space-between; }\n@container (max-width:1050px) {\n .mid_900471699 { grid-template-columns:minmax(0,1fr); }\n .statRow_900471699 { grid-template-columns:repeat(2,minmax(0,1fr)); }\n .statValue_900471699 { font-size:23px; }\n .table_900471699 { font-size:12px; }\n .childName_900471699 { width:34%; }\n}\n@container (max-width:700px) {\n .page_900471699 { padding:18px 14px 24px; }\n .stat_900471699 { padding:12px 12px 11px; }\n .statValue_900471699 { font-size:20px; }\n .table_900471699 { font-size:10px; }\n .table_900471699 th,.table_900471699 td { padding:10px 3px; }\n .table_900471699 th:first-child,.table_900471699 td:first-child { padding-left:10px; }\n .table_900471699 th:last-child,.table_900471699 td:last-child { padding-right:10px; }\n .childName_900471699 { width:25%; }\n .childRequests_900471699 { width:11%; }\n .modelName_900471699 { width:25%; }\n .modelRequests_900471699 { width:11%; }\n .modelHit_900471699 { width:17%; }\n .turnHead_900471699 { gap:8px; padding:11px 10px; }\n .turnSummary_900471699 { grid-column:2; font-size:10px; }\n .turnRequest_900471699 { grid-template-columns:56px minmax(120px,1fr) auto; gap:8px; padding:10px; }\n .turnRequestHead_900471699 { gap:6px; }\n .routeText_900471699 { display:none; }\n}\n@media (prefers-reduced-motion:reduce) {\n .chevron_900471699 { transition:none; }\n}\n";
|
|
33394
|
+
const css = "/* DSH-native Usage surface: every colour rides the host semantic tokens\n * (--dsw-alias-*), so the page follows the DSH light/dark theme — the light\n * canvas is the host's pure-white bg-base, the dark canvas its near-black\n * base, and surfaces separate by border (the host's flat-light convention)\n * instead of by canvas tint. Tables fit the available width: no horizontal\n * scroll containers, fixed column contracts, titles for names. Six stat cards\n * sit in one row (1050px and below: 3-up, 700px and below: 2-up) as quiet\n * bordered surfaces: no accent edges or watermarks, a flat tone chip leading\n * each label, hierarchy from the type scale. Request log folds by\n * conversation turn.\n * Accent hues are this plugin's tone scale — the same accents the dashboard\n * cards wear. Dark values flip under body[data-ds-dark-theme], the host's own\n * dark mechanism (never a prefers-color-scheme media query: the host theme is\n * app-level and can disagree with the OS). */\n.scroll_900471699 { height:100%; min-height:0; overflow-y:auto; background:var(--dsw-alias-bg-base); color:var(--dsw-alias-label-primary); container-type:inline-size; }\n.page_900471699 { box-sizing:border-box; width:100%; max-width:1160px; margin:0 auto; padding:24px 26px 32px; font-family:var(--dsw-font-family); font-size:13px; }\n.page_900471699 * { box-sizing:border-box; }\n/* Freshness header: live badge + last-read stamp on the left, manual refresh\n * pinned to the right edge. */\n.head_900471699 { display:flex; align-items:center; justify-content:space-between; gap:12px; margin:0 0 12px; }\n.headStatus_900471699 { display:flex; align-items:center; gap:7px; min-width:0; font-size:11px; color:var(--dsw-alias-label-tertiary); }\n.liveDot_900471699 { width:6px; height:6px; border-radius:50%; background:var(--dsw-alias-state-success-primary); box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent); flex-shrink:0; }\n.asOf_900471699 { color:var(--dsw-alias-label-tertiary); font-variant-numeric:tabular-nums; white-space:nowrap; }\n.refresh_900471699 { display:inline-flex; align-items:center; gap:5px; flex:none; color:var(--dsw-alias-label-secondary); background:var(--dsw-alias-bg-layer-1); border:1px solid var(--dsw-alias-border-l2); border-radius:8px; padding:5px 11px; font:inherit; font-size:11px; cursor:pointer; }\n.refresh_900471699:hover { color:var(--dsw-alias-label-primary); border-color:var(--dsw-alias-border-l3); background:var(--dsw-alias-interactive-bg-hover); }\n.refresh_900471699:disabled { opacity:.55; cursor:default; }\n.scope_900471699 { color:var(--dsw-alias-label-tertiary); font-size:11px; margin:0 0 14px; }\n.stats_900471699 { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:8px; margin-bottom:16px; }\n.stat_900471699 { min-width:0; position:relative; background:var(--dsw-alias-bg-layer-1); border:1px solid var(--dsw-alias-border-l1); border-radius:12px; padding:12px 14px 11px; box-shadow:0 1px 2px var(--dsw-alias-bg-mask-2); transition:border-color .18s ease, box-shadow .18s ease, transform .18s ease; }\n.stat_900471699:hover { border-color:var(--dsw-alias-border-l2); box-shadow:0 14px 30px -22px var(--dsw-alias-bg-mask-1), 0 1px 2px var(--dsw-alias-bg-mask-2); transform:translateY(-1px); }\n.statHead_900471699 { display:flex; align-items:center; gap:5px; margin-bottom:6px; }\n.statIcon_900471699 { display:flex; align-items:center; justify-content:center; width:20px; height:20px; border-radius:6px; color:var(--tone); background:color-mix(in srgb, var(--tone) 14%, transparent); flex-shrink:0; }\n.statLabel_900471699 { font-size:12px; font-weight:500; color:var(--dsw-alias-label-tertiary); overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }\n.statValue_900471699 { display:block; font-size:21px; font-weight:650; letter-spacing:-0.4px; color:var(--dsw-alias-label-primary); white-space:nowrap; font-variant-numeric:tabular-nums; }\n.statSub_900471699 { display:block; margin-top:8px; padding-top:7px; border-top:1px solid var(--dsw-alias-border-l1); font-size:11px; color:var(--dsw-alias-label-tertiary); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n/* Accent hues: this plugin's tone scale, matching the dashboard cards' chart\n * accents. The soft chip is a translucent mix of the hue itself, so it flips\n * with the theme automatically; only the base hue needs a dark value (below). */\n.toneBlue_900471699 { --tone:#4d6bfe; }\n.tonePurple_900471699 { --tone:#975bf1; }\n.toneGreen_900471699 { --tone:#00b96b; }\n.toneOrange_900471699 { --tone:#e07b39; }\n.toneTeal_900471699 { --tone:#0d8a8a; }\n.card_900471699 { min-width:0; background:var(--dsw-alias-bg-layer-1); border:1px solid var(--dsw-alias-border-l2); border-radius:12px; margin-bottom:18px; overflow:hidden; }\n.cardHead_900471699 { display:flex; align-items:center; justify-content:space-between; gap:10px; min-height:54px; padding:15px 18px; border-bottom:1px solid var(--dsw-alias-border-l1); }\n.cardHead_900471699 h2 { font-size:14px; font-weight:600; margin:0; color:var(--dsw-alias-label-primary); }\n.cardHead_900471699 > span { display:flex; gap:7px; align-items:center; font-size:11px; }\n.count_900471699 { display:inline-flex; align-items:center; justify-content:center; min-width:21px; padding:2px 6px; font-size:10px; color:var(--dsw-alias-label-tertiary); background:var(--dsw-alias-markdown-tag); border-radius:5px; margin-left:5px; font-weight:500; }\n.mid_900471699 { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:18px; align-items:stretch; }\n.mid_900471699 .card_900471699 { height:100%; margin-bottom:0; }\n.mid_900471699 { margin-bottom:18px; }\n.table_900471699 { border-collapse:collapse; width:100%; table-layout:fixed; font-size:11px; }\n.table_900471699 th { color:var(--dsw-alias-label-tertiary); font-weight:500; padding:12px 5px; text-align:right; vertical-align:middle; line-height:1.3; overflow-wrap:anywhere; }\n.table_900471699 td { padding:13px 5px; text-align:right; border-top:1px solid var(--dsw-alias-border-l1); color:var(--dsw-alias-label-secondary); font-variant-numeric:tabular-nums; white-space:nowrap; }\n.table_900471699 th:first-child,.table_900471699 td:first-child { text-align:left; padding-left:16px; }\n.table_900471699 th:last-child,.table_900471699 td:last-child { padding-right:16px; }\n.childName_900471699 { width:28%; }\n.childRequests_900471699 { width:11%; }\n.modelName_900471699 { width:30%; }\n.modelRequests_900471699 { width:11%; }\n.modelHit_900471699 { width:16%; }\n.ellipsis_900471699 { display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--dsw-alias-label-primary-dimmed); }\n/* By-model rows lead with the model's brand logo; the name keeps truncating\n * inside the flexible remainder of the cell. */\n.modelNameCell_900471699 { display:flex; align-items:center; gap:6px; min-width:0; }\n.modelNameCell_900471699 .ellipsis_900471699 { flex:1; min-width:0; }\n/* Gray provider label under a model name shown by several rows (同名模型). */\n.modelProvider_900471699 { display:block; margin-top:2px; font-size:10px; color:var(--dsw-alias-label-tertiary); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.number_900471699 { font-variant-numeric:tabular-nums; }\n.speedCell_900471699 { display:inline-flex; align-items:baseline; gap:3px; white-space:nowrap; }\n.speedCell_900471699 small { font-size:9px; color:var(--dsw-alias-label-tertiary); }\n/* Status accents ride the host state tokens; the running/live teal is this\n * plugin's tone-5 accent (the host has no teal state token). */\n.good_900471699 { color:var(--dsw-alias-state-success-primary); }\n.warn_900471699 { color:var(--dsw-alias-state-warn-label); }\n.bad_900471699 { color:var(--dsw-alias-state-error-primary); }\n.live_900471699 { color:#0d8a8a; }\n.muted_900471699 { color:var(--dsw-alias-label-tertiary); }\n.empty_900471699 { margin:0; padding:28px 18px; color:var(--dsw-alias-label-tertiary); font-size:12px; text-align:center; }\n.action_900471699 { color:var(--dsw-alias-label-secondary); background:none; border:0; padding:3px 0; font:inherit; font-size:11px; cursor:pointer; }\n.action_900471699:hover { color:var(--dsw-alias-label-link); }\n.action_900471699:focus-visible,.turnHead_900471699:focus-visible,.refresh_900471699:focus-visible { outline:2px solid var(--dsw-alias-label-link); outline-offset:-2px; }\n.turnList_900471699 { padding:0 14px 14px; }\n.turn_900471699 { margin-top:10px; border:1px solid var(--dsw-alias-border-l1); border-radius:9px; background:var(--dsw-alias-bg-layer-1); overflow:hidden; }\n.turnHead_900471699 { display:grid; grid-template-columns:12px minmax(0,1fr) auto; align-items:center; gap:12px; padding:13px 14px; width:100%; text-align:left; background:var(--dsw-alias-menu-group-header-fill); border:0; cursor:pointer; color:var(--dsw-alias-label-secondary); font:inherit; font-size:11px; }\n.chevron_900471699 { color:var(--dsw-alias-label-tertiary); transition:transform 0.2s; }\n.rotated_900471699 { transform:rotate(90deg); }\n.turnTitle_900471699 { display:flex; align-items:center; gap:6px; min-width:0; font-size:12px; font-weight:600; color:var(--dsw-alias-label-primary-dimmed); }\n/* Turn topic is the group title now (the page header owns the session name),\n * so it carries the primary text color and truncates before the metrics. */\n.turnTopic_900471699 { min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }\n/* A subagent's turns look like this session's turns apart from the agent tag;\n * the tag keeps the full session name in its tooltip. */\n.turnAgent_900471699 { flex:none; padding:1px 6px; border-radius:5px; background:var(--dsw-alias-interactive-bg-active); color:var(--dsw-alias-label-secondary); font-size:10px; font-weight:500; }\n.turnSummary_900471699 { display:flex; gap:5px; align-items:center; white-space:nowrap; }\n.turnSummary_900471699 > span:last-child { margin-left:8px; }\n.turnBody_900471699 { border-top:1px solid var(--dsw-alias-border-l1); }\n.turnBody_900471699[hidden] { display:none; }\n/* A request row is one line: time, what answered, and its metrics pinned to the\n * row end. The metrics never wrap; they truncate with a tooltip before the head\n * gives up its model name. */\n.turnRequest_900471699 { display:grid; grid-template-columns:64px minmax(0,1fr) auto; align-items:center; gap:12px; padding:10px 16px; border-top:1px solid var(--dsw-alias-border-l1); }\n.turnRequest_900471699:first-child { border-top:0; }\n.turnRequestHead_900471699 { display:flex; align-items:center; gap:8px; font-size:11px; color:var(--dsw-alias-label-secondary); min-width:0; }\n.requestTime_900471699 { color:var(--dsw-alias-label-tertiary); font-variant-numeric:tabular-nums; flex-shrink:0; }\n.statusDot_900471699 { width:5px; height:5px; border-radius:50%; background:currentColor; flex-shrink:0; }\n/* Per-model brand logo: colors are baked into each logo's markup, only the\n * layout contract lives here. Unknown models get the neutral gray fallback. */\n.modelLogo_900471699 { flex-shrink:0; }\n.recordModel_900471699 { min-width:0; display:flex; align-items:center; gap:6px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:var(--dsw-alias-label-primary-dimmed); font-weight:500; }\n.routeText_900471699 { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:var(--dsw-alias-label-tertiary); flex-shrink:1; }\n.effort_900471699 { white-space:nowrap; color:var(--dsw-alias-label-tertiary); background:var(--dsw-alias-markdown-tag); border-radius:5px; padding:1px 6px; }\n.recordMeta_900471699 { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:right; font-size:10px; color:var(--dsw-alias-label-tertiary); font-variant-numeric:tabular-nums; }\n.note_900471699 { font-size:10px; line-height:1.7; color:var(--dsw-alias-label-tertiary); margin:0; }\n.error_900471699 { background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent); color:var(--dsw-alias-state-error-primary); border:1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 35%, transparent); border-radius:9px; padding:12px 16px; margin-bottom:16px; font-size:12px; display:flex; gap:16px; justify-content:space-between; }\n@container (max-width:1050px) {\n .mid_900471699 { grid-template-columns:minmax(0,1fr); }\n .stats_900471699 { grid-template-columns:repeat(3,minmax(0,1fr)); }\n .table_900471699 { font-size:12px; }\n .childName_900471699 { width:34%; }\n}\n@container (max-width:700px) {\n .page_900471699 { padding:18px 14px 24px; }\n .stats_900471699 { grid-template-columns:repeat(2,minmax(0,1fr)); }\n .stat_900471699 { padding:10px 12px 9px; }\n .statValue_900471699 { font-size:18px; }\n .table_900471699 { font-size:10px; }\n .table_900471699 th,.table_900471699 td { padding:10px 3px; }\n .table_900471699 th:first-child,.table_900471699 td:first-child { padding-left:10px; }\n .table_900471699 th:last-child,.table_900471699 td:last-child { padding-right:10px; }\n .childName_900471699 { width:25%; }\n .childRequests_900471699 { width:11%; }\n .modelName_900471699 { width:25%; }\n .modelRequests_900471699 { width:11%; }\n .modelHit_900471699 { width:17%; }\n .turnHead_900471699 { gap:8px; padding:11px 10px; }\n .turnSummary_900471699 { grid-column:2; font-size:10px; }\n .turnRequest_900471699 { grid-template-columns:56px minmax(120px,1fr) auto; gap:8px; padding:10px; }\n .turnRequestHead_900471699 { gap:6px; }\n .routeText_900471699 { display:none; }\n}\n@media (prefers-reduced-motion:reduce) {\n .chevron_900471699 { transition:none; }\n .stat_900471699 { transition:none; }\n .stat_900471699:hover { transform:none; }\n}\n/* Dark theme: the host sets body[data-ds-dark-theme] and every alias token\n * above re-resolves on its own; only this plugin's own accent hues need\n * explicit dark values — brighter variants of the dashboard's dark chart\n * accents, so chips and running states hold their weight on dark cards. */\nbody[data-ds-dark-theme] .toneBlue_900471699 { --tone:#7d95ff; }\nbody[data-ds-dark-theme] .tonePurple_900471699 { --tone:#b18cff; }\nbody[data-ds-dark-theme] .toneGreen_900471699 { --tone:#2fbf7c; }\nbody[data-ds-dark-theme] .toneOrange_900471699 { --tone:#e89a5c; }\nbody[data-ds-dark-theme] .toneTeal_900471699 { --tone:#3bc5c5; }\nbody[data-ds-dark-theme] .live_900471699 { color:#3bc5c5; }\n";
|
|
32880
33395
|
const tagId = "dsh-token-total/SessionUsageView.module.css";
|
|
32881
33396
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId + "\"]") === null) {
|
|
32882
33397
|
const tag = document.createElement("style");
|
|
@@ -32895,11 +33410,9 @@ window.__ModuleLoader__.load({
|
|
|
32895
33410
|
"refresh": "refresh_900471699",
|
|
32896
33411
|
"scope": "scope_900471699",
|
|
32897
33412
|
"stats": "stats_900471699",
|
|
32898
|
-
"statRow": "statRow_900471699",
|
|
32899
33413
|
"stat": "stat_900471699",
|
|
32900
33414
|
"statHead": "statHead_900471699",
|
|
32901
33415
|
"statIcon": "statIcon_900471699",
|
|
32902
|
-
"statMark": "statMark_900471699",
|
|
32903
33416
|
"statLabel": "statLabel_900471699",
|
|
32904
33417
|
"statValue": "statValue_900471699",
|
|
32905
33418
|
"statSub": "statSub_900471699",
|
|
@@ -32908,7 +33421,6 @@ window.__ModuleLoader__.load({
|
|
|
32908
33421
|
"toneGreen": "toneGreen_900471699",
|
|
32909
33422
|
"toneOrange": "toneOrange_900471699",
|
|
32910
33423
|
"toneTeal": "toneTeal_900471699",
|
|
32911
|
-
"tonePink": "tonePink_900471699",
|
|
32912
33424
|
"card": "card_900471699",
|
|
32913
33425
|
"cardHead": "cardHead_900471699",
|
|
32914
33426
|
"count": "count_900471699",
|
|
@@ -32998,50 +33510,20 @@ window.__ModuleLoader__.load({
|
|
|
32998
33510
|
hit: "M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0ZM16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z",
|
|
32999
33511
|
speed: "M13 2 4 14h7l-1 8 9-12h-7l1-8Z",
|
|
33000
33512
|
input: "M12 3v12M7 10l5 5 5-5M4 21h16",
|
|
33001
|
-
output: "M12 21V9M7 14l5-5 5 5M4 3h16"
|
|
33002
|
-
read: "M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7ZM15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z",
|
|
33003
|
-
write: "M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5Z"
|
|
33513
|
+
output: "M12 21V9M7 14l5-5 5 5M4 3h16"
|
|
33004
33514
|
};
|
|
33005
33515
|
function Stat({ tone, name, label, value, sub, title }) {
|
|
33006
33516
|
const icon = icons[name];
|
|
33007
33517
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
33008
33518
|
className: `${SessionUsageView_module_default.stat} ${tone}`,
|
|
33009
33519
|
children: [
|
|
33010
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
33011
|
-
className: SessionUsageView_module_default.statMark,
|
|
33012
|
-
viewBox: "0 0 24 24",
|
|
33013
|
-
"aria-hidden": "true",
|
|
33014
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
|
|
33015
|
-
id: `usage-mark-${name}`,
|
|
33016
|
-
x1: "0",
|
|
33017
|
-
y1: "0",
|
|
33018
|
-
x2: "1",
|
|
33019
|
-
y2: "1",
|
|
33020
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
|
|
33021
|
-
offset: "0",
|
|
33022
|
-
stopColor: "currentColor",
|
|
33023
|
-
stopOpacity: "0.3"
|
|
33024
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
|
|
33025
|
-
offset: "1",
|
|
33026
|
-
stopColor: "currentColor",
|
|
33027
|
-
stopOpacity: "0.03"
|
|
33028
|
-
})]
|
|
33029
|
-
}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
33030
|
-
d: icon,
|
|
33031
|
-
fill: `url(#usage-mark-${name})`,
|
|
33032
|
-
stroke: `url(#usage-mark-${name})`,
|
|
33033
|
-
strokeWidth: "1.1",
|
|
33034
|
-
strokeLinecap: "round",
|
|
33035
|
-
strokeLinejoin: "round"
|
|
33036
|
-
})]
|
|
33037
|
-
}),
|
|
33038
33520
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33039
33521
|
className: SessionUsageView_module_default.statHead,
|
|
33040
33522
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33041
33523
|
className: SessionUsageView_module_default.statIcon,
|
|
33042
33524
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
33043
|
-
width: "
|
|
33044
|
-
height: "
|
|
33525
|
+
width: "12",
|
|
33526
|
+
height: "12",
|
|
33045
33527
|
viewBox: "0 0 24 24",
|
|
33046
33528
|
fill: "none",
|
|
33047
33529
|
stroke: "currentColor",
|
|
@@ -33074,76 +33556,53 @@ window.__ModuleLoader__.load({
|
|
|
33074
33556
|
const failRate = detail.requests > 0 ? (failed / detail.requests * 100).toFixed(1) : "0.0";
|
|
33075
33557
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33076
33558
|
className: SessionUsageView_module_default.stats,
|
|
33077
|
-
children: [
|
|
33078
|
-
|
|
33079
|
-
|
|
33080
|
-
|
|
33081
|
-
|
|
33082
|
-
|
|
33083
|
-
|
|
33084
|
-
|
|
33085
|
-
|
|
33086
|
-
|
|
33087
|
-
|
|
33088
|
-
|
|
33089
|
-
|
|
33090
|
-
|
|
33091
|
-
|
|
33092
|
-
|
|
33093
|
-
|
|
33094
|
-
|
|
33095
|
-
|
|
33096
|
-
|
|
33097
|
-
|
|
33098
|
-
|
|
33099
|
-
|
|
33100
|
-
|
|
33101
|
-
|
|
33102
|
-
|
|
33103
|
-
|
|
33104
|
-
|
|
33105
|
-
|
|
33106
|
-
|
|
33107
|
-
|
|
33108
|
-
|
|
33109
|
-
|
|
33110
|
-
|
|
33111
|
-
|
|
33112
|
-
|
|
33113
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
33114
|
-
|
|
33115
|
-
|
|
33116
|
-
|
|
33117
|
-
|
|
33118
|
-
|
|
33119
|
-
|
|
33120
|
-
|
|
33121
|
-
|
|
33122
|
-
|
|
33123
|
-
|
|
33124
|
-
label: t("outputFull"),
|
|
33125
|
-
value: formatCompact(detail.totals.outputTokens),
|
|
33126
|
-
title: formatTokens(detail.totals.outputTokens),
|
|
33127
|
-
sub: formatTokens(detail.totals.outputTokens)
|
|
33128
|
-
}),
|
|
33129
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33130
|
-
tone: SessionUsageView_module_default.tonePurple,
|
|
33131
|
-
name: "read",
|
|
33132
|
-
label: t("readFull"),
|
|
33133
|
-
value: formatCompact(detail.totals.cacheReadTokens),
|
|
33134
|
-
title: formatTokens(detail.totals.cacheReadTokens),
|
|
33135
|
-
sub: formatTokens(detail.totals.cacheReadTokens)
|
|
33136
|
-
}),
|
|
33137
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33138
|
-
tone: SessionUsageView_module_default.tonePink,
|
|
33139
|
-
name: "write",
|
|
33140
|
-
label: t("writeFull"),
|
|
33141
|
-
value: formatCompact(detail.totals.cacheWriteTokens),
|
|
33142
|
-
title: formatTokens(detail.totals.cacheWriteTokens),
|
|
33143
|
-
sub: formatTokens(detail.totals.cacheWriteTokens)
|
|
33144
|
-
})
|
|
33145
|
-
]
|
|
33146
|
-
})]
|
|
33559
|
+
children: [
|
|
33560
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33561
|
+
tone: SessionUsageView_module_default.toneBlue,
|
|
33562
|
+
name: "requests",
|
|
33563
|
+
label: t("requestsCount"),
|
|
33564
|
+
value: formatTokens(detail.requests),
|
|
33565
|
+
sub: `${t("failed")} ${failed} · ${failRate}%`
|
|
33566
|
+
}),
|
|
33567
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33568
|
+
tone: SessionUsageView_module_default.tonePurple,
|
|
33569
|
+
name: "tokens",
|
|
33570
|
+
label: t("tokens"),
|
|
33571
|
+
value: formatCompact(billed),
|
|
33572
|
+
title: formatTokens(billed),
|
|
33573
|
+
sub: `${formatTokens(billed)} tokens`
|
|
33574
|
+
}),
|
|
33575
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33576
|
+
tone: SessionUsageView_module_default.toneTeal,
|
|
33577
|
+
name: "input",
|
|
33578
|
+
label: t("inputFull"),
|
|
33579
|
+
value: formatCompact(detail.totals.uncachedInputTokens),
|
|
33580
|
+
title: formatTokens(detail.totals.uncachedInputTokens),
|
|
33581
|
+
sub: formatTokens(detail.totals.uncachedInputTokens)
|
|
33582
|
+
}),
|
|
33583
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33584
|
+
tone: SessionUsageView_module_default.toneOrange,
|
|
33585
|
+
name: "output",
|
|
33586
|
+
label: t("outputFull"),
|
|
33587
|
+
value: formatCompact(detail.totals.outputTokens),
|
|
33588
|
+
title: formatTokens(detail.totals.outputTokens),
|
|
33589
|
+
sub: formatTokens(detail.totals.outputTokens)
|
|
33590
|
+
}),
|
|
33591
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33592
|
+
tone: SessionUsageView_module_default.toneGreen,
|
|
33593
|
+
name: "hit",
|
|
33594
|
+
label: t("hit"),
|
|
33595
|
+
value: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hit, { totals: detail.totals }),
|
|
33596
|
+
sub: `${formatCompact(detail.totals.cacheReadTokens)} ${t("cacheHit")}`
|
|
33597
|
+
}),
|
|
33598
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33599
|
+
tone: SessionUsageView_module_default.toneOrange,
|
|
33600
|
+
name: "speed",
|
|
33601
|
+
label: t("speed"),
|
|
33602
|
+
value: speed(detail.tokensPerSecond),
|
|
33603
|
+
sub: `${t("first")} ${seconds(detail.averageFirstTokenMs)}`
|
|
33604
|
+
})
|
|
33605
|
+
]
|
|
33147
33606
|
});
|
|
33148
33607
|
}
|
|
33149
33608
|
function Tables({ detail, t }) {
|
|
@@ -33317,6 +33776,8 @@ window.__ModuleLoader__.load({
|
|
|
33317
33776
|
}
|
|
33318
33777
|
group.rows.push(record);
|
|
33319
33778
|
}
|
|
33779
|
+
const latest = (group) => group.rows.reduce((max, row) => Math.max(max, row.startedAt), 0);
|
|
33780
|
+
groups.sort((a, b) => latest(b) - latest(a));
|
|
33320
33781
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
33321
33782
|
className: SessionUsageView_module_default.card,
|
|
33322
33783
|
children: [
|
|
@@ -33490,7 +33951,7 @@ window.__ModuleLoader__.load({
|
|
|
33490
33951
|
});
|
|
33491
33952
|
}
|
|
33492
33953
|
/** Pure presentation export also used by offline visual regression fixtures. */
|
|
33493
|
-
function SessionUsageContent({ detail, t }) {
|
|
33954
|
+
function SessionUsageContent({ detail, t, updatedAt }) {
|
|
33494
33955
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
33495
33956
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
33496
33957
|
className: SessionUsageView_module_default.scope,
|
|
@@ -33504,6 +33965,11 @@ window.__ModuleLoader__.load({
|
|
|
33504
33965
|
detail,
|
|
33505
33966
|
t
|
|
33506
33967
|
}),
|
|
33968
|
+
detail.context ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextWindowPanel, {
|
|
33969
|
+
context: detail.context,
|
|
33970
|
+
t,
|
|
33971
|
+
updatedAt
|
|
33972
|
+
}) : null,
|
|
33507
33973
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Records, {
|
|
33508
33974
|
detail,
|
|
33509
33975
|
t
|
|
@@ -33577,7 +34043,8 @@ window.__ModuleLoader__.load({
|
|
|
33577
34043
|
}),
|
|
33578
34044
|
detail && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionUsageContent, {
|
|
33579
34045
|
detail,
|
|
33580
|
-
t
|
|
34046
|
+
t,
|
|
34047
|
+
updatedAt
|
|
33581
34048
|
})
|
|
33582
34049
|
]
|
|
33583
34050
|
})
|
|
@@ -33645,7 +34112,35 @@ window.__ModuleLoader__.load({
|
|
|
33645
34112
|
inProgress: "个进行中",
|
|
33646
34113
|
live: "实时更新",
|
|
33647
34114
|
asOf: "统计截至",
|
|
33648
|
-
refresh: "刷新"
|
|
34115
|
+
refresh: "刷新",
|
|
34116
|
+
contextTitle: "上下文窗口",
|
|
34117
|
+
contextUsed: "已用上下文",
|
|
34118
|
+
contextLive: "实时",
|
|
34119
|
+
contextWindowKnown: "模型窗口 {value} · 已用 {percent}%",
|
|
34120
|
+
contextWindowUnknown: "模型的窗口大小未知",
|
|
34121
|
+
contextCache: "提示词缓存",
|
|
34122
|
+
contextCacheHit: "命中",
|
|
34123
|
+
contextCacheMiss: "未命中",
|
|
34124
|
+
contextCachePending: "供应商回答可见",
|
|
34125
|
+
contextSystem: "系统提示词",
|
|
34126
|
+
contextTools: "工具",
|
|
34127
|
+
contextMemory: "记忆",
|
|
34128
|
+
contextFiles: "文件",
|
|
34129
|
+
contextResults: "工具结果",
|
|
34130
|
+
contextConversation: "会话内容",
|
|
34131
|
+
contextKindSystemPrompt: "系统提示词",
|
|
34132
|
+
contextKindRuntimeContext: "运行时上下文",
|
|
34133
|
+
contextKindSkillCatalog: "技能目录",
|
|
34134
|
+
contextKindTimeContext: "时间上下文",
|
|
34135
|
+
contextKindCompaction: "压缩摘要",
|
|
34136
|
+
contextKindUser: "用户消息",
|
|
34137
|
+
contextKindAssistant: "助手消息",
|
|
34138
|
+
contextKindRest: "另外 {count} 项",
|
|
34139
|
+
contextTimes: "×{count}",
|
|
34140
|
+
contextEmpty: "本会话还没有可统计的上下文",
|
|
34141
|
+
contextTipShare: "占上下文 {percent}",
|
|
34142
|
+
contextTipWindow: "占窗口 {percent}",
|
|
34143
|
+
contextEstimateNote: "色块按历史重放估算 {estimate};供应商实测提示词 {billed}(上游已压缩或裁剪)"
|
|
33649
34144
|
};
|
|
33650
34145
|
const usageEn = {
|
|
33651
34146
|
tab: "Usage",
|
|
@@ -33705,7 +34200,35 @@ window.__ModuleLoader__.load({
|
|
|
33705
34200
|
inProgress: "in progress",
|
|
33706
34201
|
live: "Live",
|
|
33707
34202
|
asOf: "as of",
|
|
33708
|
-
refresh: "Refresh"
|
|
34203
|
+
refresh: "Refresh",
|
|
34204
|
+
contextTitle: "Context window",
|
|
34205
|
+
contextUsed: "Context used",
|
|
34206
|
+
contextLive: "Live",
|
|
34207
|
+
contextWindowKnown: "Window {value} · {percent}% used",
|
|
34208
|
+
contextWindowUnknown: "Model window size unknown",
|
|
34209
|
+
contextCache: "Prompt cache",
|
|
34210
|
+
contextCacheHit: "hit",
|
|
34211
|
+
contextCacheMiss: "miss",
|
|
34212
|
+
contextCachePending: "Visible once the provider answers",
|
|
34213
|
+
contextSystem: "System prompt",
|
|
34214
|
+
contextTools: "Tools",
|
|
34215
|
+
contextMemory: "Memory",
|
|
34216
|
+
contextFiles: "Files",
|
|
34217
|
+
contextResults: "Tool results",
|
|
34218
|
+
contextConversation: "Conversation",
|
|
34219
|
+
contextKindSystemPrompt: "System prompt",
|
|
34220
|
+
contextKindRuntimeContext: "Runtime context",
|
|
34221
|
+
contextKindSkillCatalog: "Skill catalog",
|
|
34222
|
+
contextKindTimeContext: "Time context",
|
|
34223
|
+
contextKindCompaction: "Compaction summary",
|
|
34224
|
+
contextKindUser: "User messages",
|
|
34225
|
+
contextKindAssistant: "Assistant messages",
|
|
34226
|
+
contextKindRest: "{count} more",
|
|
34227
|
+
contextTimes: "×{count}",
|
|
34228
|
+
contextEmpty: "Nothing in this session’s context yet",
|
|
34229
|
+
contextTipShare: "{percent} of context",
|
|
34230
|
+
contextTipWindow: "{percent} of window",
|
|
34231
|
+
contextEstimateNote: "Blocks estimate {estimate} by replay; the provider billed {billed} after upstream compression"
|
|
33709
34232
|
};
|
|
33710
34233
|
const usageZhTW = {
|
|
33711
34234
|
...usageZh,
|
|
@@ -33765,7 +34288,35 @@ window.__ModuleLoader__.load({
|
|
|
33765
34288
|
inProgress: "個進行中",
|
|
33766
34289
|
live: "即時更新",
|
|
33767
34290
|
asOf: "統計截至",
|
|
33768
|
-
refresh: "重新整理"
|
|
34291
|
+
refresh: "重新整理",
|
|
34292
|
+
contextTitle: "上下文視窗",
|
|
34293
|
+
contextUsed: "已用上下文",
|
|
34294
|
+
contextLive: "即時",
|
|
34295
|
+
contextWindowKnown: "模型視窗 {value} · 已用 {percent}%",
|
|
34296
|
+
contextWindowUnknown: "模型的視窗大小未知",
|
|
34297
|
+
contextCache: "提示詞快取",
|
|
34298
|
+
contextCacheHit: "命中",
|
|
34299
|
+
contextCacheMiss: "未命中",
|
|
34300
|
+
contextCachePending: "供應商回答可見",
|
|
34301
|
+
contextSystem: "系統提示詞",
|
|
34302
|
+
contextTools: "工具",
|
|
34303
|
+
contextMemory: "記憶",
|
|
34304
|
+
contextFiles: "檔案",
|
|
34305
|
+
contextResults: "工具結果",
|
|
34306
|
+
contextConversation: "會話內容",
|
|
34307
|
+
contextKindSystemPrompt: "系統提示詞",
|
|
34308
|
+
contextKindRuntimeContext: "執行環境上下文",
|
|
34309
|
+
contextKindSkillCatalog: "技能目錄",
|
|
34310
|
+
contextKindTimeContext: "時間上下文",
|
|
34311
|
+
contextKindCompaction: "壓縮摘要",
|
|
34312
|
+
contextKindUser: "使用者訊息",
|
|
34313
|
+
contextKindAssistant: "助手訊息",
|
|
34314
|
+
contextKindRest: "另外 {count} 項",
|
|
34315
|
+
contextTimes: "×{count}",
|
|
34316
|
+
contextEmpty: "本會話還沒有可統計的上下文",
|
|
34317
|
+
contextTipShare: "佔上下文 {percent}",
|
|
34318
|
+
contextTipWindow: "佔視窗 {percent}",
|
|
34319
|
+
contextEstimateNote: "色塊按歷史重放估算 {estimate};供應商實測提示詞 {billed}(上游已壓縮或裁剪)"
|
|
33769
34320
|
};
|
|
33770
34321
|
//#endregion
|
|
33771
34322
|
//#region src/client/index.tsx
|