dsh-context 0.57.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -354,9 +354,15 @@ window.__ModuleLoader__.load({
354
354
  "ov.kpi.ofTotal": "共 {n} 个",
355
355
  "ov.kpi.tokens": "累计消耗",
356
356
  "ov.kpi.sessionsSub": "含 {n} 个会话",
357
+ "ov.kpi.pricedSub": "已计价 {n}/{total} 个会话",
357
358
  "ov.kpi.toolSub": "工具执行 {dur}",
358
359
  "ov.kpi.wallSub": "模型调用 {n}",
359
360
  "ov.stats.empty": "统计范围内暂无 Token 用量",
361
+ "ov.usage.title": "近 7 天用量",
362
+ "ov.usage.tokens": "Token",
363
+ "ov.usage.cost": "费用(仅含支持计价映射的模型)",
364
+ "ov.usage.costTip": "费用",
365
+ "ov.usage.empty": "近 7 天暂无用量 — 会话产生模型请求后,这里会显示每日 Token 与费用",
360
366
  "balance.title": "DeepSeek 余额",
361
367
  "balance.tip.granted": "赠送余额",
362
368
  "balance.tip.toppedUp": "充值余额",
@@ -734,9 +740,15 @@ window.__ModuleLoader__.load({
734
740
  "ov.kpi.ofTotal": "of {n} total",
735
741
  "ov.kpi.tokens": "Tokens Used",
736
742
  "ov.kpi.sessionsSub": "across {n} sessions",
743
+ "ov.kpi.pricedSub": "{n} of {total} sessions priced",
737
744
  "ov.kpi.toolSub": "tool runs {dur}",
738
745
  "ov.kpi.wallSub": "{n} model calls",
739
746
  "ov.stats.empty": "No billed tokens in this range",
747
+ "ov.usage.title": "Last 7 Days",
748
+ "ov.usage.tokens": "Tokens",
749
+ "ov.usage.cost": "Cost (price-mapped models only)",
750
+ "ov.usage.costTip": "Cost",
751
+ "ov.usage.empty": "No usage in the last 7 days — daily tokens and cost appear once sessions make model requests",
740
752
  "balance.title": "DeepSeek balance",
741
753
  "balance.tip.granted": "Granted balance",
742
754
  "balance.tip.toppedUp": "Topped-up balance",
@@ -1625,12 +1637,13 @@ window.__ModuleLoader__.load({
1625
1637
  }
1626
1638
  /**
1627
1639
  * Narrow a delivered `contextActivity` value (the per-day ledger the Context
1628
- * Overview's heatmap reads off each session-list row) to a render-safe shape.
1629
- * Absent or non-record stays null (an older host serves no such key — the
1630
- * heatmap renders its empty note). Per-day re-proved: a malformed key or a
1631
- * wrong-typed figure drops just that entry, a hostile day record can never
1632
- * produce a NaN cell, and a well-formed payload passes through untouched
1633
- * (reference-stable for the selector equality).
1640
+ * Overview's heatmap and usage chart read off each session-list row) to a
1641
+ * render-safe shape. Absent or non-record stays null (an older host serves no
1642
+ * such key — the heatmap renders its empty note). Per-day re-proved: a
1643
+ * malformed key or a wrong-typed figure drops just that entry, a malformed
1644
+ * pricing record drops just the fee (the day's tokens survive), a hostile day
1645
+ * record can never produce a NaN cell, and a well-formed payload passes
1646
+ * through untouched (reference-stable for the selector equality).
1634
1647
  */
1635
1648
  function activityOf$1(value) {
1636
1649
  const data = asRecord(value);
@@ -1647,9 +1660,13 @@ window.__ModuleLoader__.load({
1647
1660
  dirty = true;
1648
1661
  continue;
1649
1662
  }
1663
+ const proved = costOf(entry.cost);
1664
+ const cost = proved !== void 0 && Object.keys(proved).length > 0 ? proved : void 0;
1665
+ if (entry.cost !== void 0 && cost === void 0) dirty = true;
1650
1666
  days[key] = {
1651
1667
  tokens,
1652
- requests
1668
+ requests,
1669
+ ...cost !== void 0 ? { cost } : {}
1653
1670
  };
1654
1671
  }
1655
1672
  return dirty ? { days } : data;
@@ -2906,6 +2923,7 @@ window.__ModuleLoader__.load({
2906
2923
  const BAR_W = 14;
2907
2924
  const BAR_GAP = 2;
2908
2925
  const STAGGER_CAP = 20;
2926
+ const STEP_FLAG_EVERY = 5;
2909
2927
  const TURN_FILLS = ["color-mix(in srgb, var(--color-neutral-500) 12%, transparent)", "color-mix(in srgb, var(--color-neutral-500) 26%, transparent)"];
2910
2928
  const LABEL_OVERHANG = 48;
2911
2929
  const LABEL_GAP = 2;
@@ -2962,62 +2980,73 @@ window.__ModuleLoader__.load({
2962
2980
  onMouseEnter: () => {
2963
2981
  props.onHover(req.seq);
2964
2982
  },
2965
- children: [marker !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2966
- className: "lc-bar-marker",
2967
- title: "✂ " + (markerAt !== null ? markerAt + " — " : "") + eventLabel(marker),
2968
- children: "✂"
2969
- }) : null, diverge ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2970
- className: "lc-bar-up animate-lc-bar-in motion-reduce:animate-none",
2971
- style: {
2972
- bottom: `${props.downPx}px`,
2973
- ...enterStyle
2974
- },
2975
- children: CATS.map((c) => {
2976
- const d = req[c.key] || 0;
2977
- if (d <= 0) return null;
2978
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2979
- "data-cat": c.key,
2980
- className: "lc-cat-seg",
2981
- style: {
2982
- height: `${Math.max(1, Math.round(d * props.deltaScale))}px`,
2983
- background: c.color
2984
- }
2985
- }, c.key);
2986
- })
2987
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2988
- className: "lc-bar-down animate-lc-bar-in motion-reduce:animate-none",
2989
- style: {
2990
- top: `${props.upPx}px`,
2991
- ...enterStyle
2992
- },
2993
- children: CATS.map((c) => {
2994
- const d = req[c.key] || 0;
2995
- if (d >= 0) return null;
2996
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2997
- "data-cat": c.key,
2998
- className: "lc-cat-seg",
2999
- style: {
3000
- height: `${Math.max(1, Math.round(-d * props.deltaScale))}px`,
3001
- background: c.color
3002
- }
3003
- }, c.key);
3004
- })
3005
- })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3006
- className: "lc-bar-stack animate-lc-bar-in motion-reduce:animate-none",
3007
- style: enterStyle,
3008
- children: CATS.map((c) => {
3009
- const v = req[c.key] || 0;
3010
- if (!v) return null;
3011
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3012
- "data-cat": c.key,
3013
- className: "lc-cat-seg",
3014
- style: {
3015
- height: `${Math.max(1, Math.round(v / props.maxTotal * CHART_H))}px`,
3016
- background: c.color
3017
- }
3018
- }, c.key);
2983
+ children: [
2984
+ props.flag !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2985
+ className: "lc-step-flag",
2986
+ "aria-hidden": "true",
2987
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2988
+ className: "lc-step-flag-label",
2989
+ children: props.flag
2990
+ })
2991
+ }) : null,
2992
+ marker !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2993
+ className: "lc-bar-marker",
2994
+ title: "✂ " + (markerAt !== null ? markerAt + " — " : "") + eventLabel(marker),
2995
+ children: "✂"
2996
+ }) : null,
2997
+ diverge ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2998
+ className: "lc-bar-up animate-lc-bar-in motion-reduce:animate-none",
2999
+ style: {
3000
+ bottom: `${props.downPx}px`,
3001
+ ...enterStyle
3002
+ },
3003
+ children: CATS.map((c) => {
3004
+ const d = req[c.key] || 0;
3005
+ if (d <= 0) return null;
3006
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3007
+ "data-cat": c.key,
3008
+ className: "lc-cat-seg",
3009
+ style: {
3010
+ height: `${Math.max(1, Math.round(d * props.deltaScale))}px`,
3011
+ background: c.color
3012
+ }
3013
+ }, c.key);
3014
+ })
3015
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3016
+ className: "lc-bar-down animate-lc-bar-in motion-reduce:animate-none",
3017
+ style: {
3018
+ top: `${props.upPx}px`,
3019
+ ...enterStyle
3020
+ },
3021
+ children: CATS.map((c) => {
3022
+ const d = req[c.key] || 0;
3023
+ if (d >= 0) return null;
3024
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3025
+ "data-cat": c.key,
3026
+ className: "lc-cat-seg",
3027
+ style: {
3028
+ height: `${Math.max(1, Math.round(-d * props.deltaScale))}px`,
3029
+ background: c.color
3030
+ }
3031
+ }, c.key);
3032
+ })
3033
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3034
+ className: "lc-bar-stack animate-lc-bar-in motion-reduce:animate-none",
3035
+ style: enterStyle,
3036
+ children: CATS.map((c) => {
3037
+ const v = req[c.key] || 0;
3038
+ if (!v) return null;
3039
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3040
+ "data-cat": c.key,
3041
+ className: "lc-cat-seg",
3042
+ style: {
3043
+ height: `${Math.max(1, Math.round(v / props.maxTotal * CHART_H))}px`,
3044
+ background: c.color
3045
+ }
3046
+ }, c.key);
3047
+ })
3019
3048
  })
3020
- })]
3049
+ ]
3021
3050
  });
3022
3051
  });
3023
3052
  return function TrendChart(props) {
@@ -3371,6 +3400,7 @@ window.__ModuleLoader__.load({
3371
3400
  selected: props.selectedSeq === req.seq,
3372
3401
  hovered: props.hoveredSeq === req.seq,
3373
3402
  inTurn: props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn,
3403
+ flag: props.granularity === "step" && (i + 1) % STEP_FLAG_EVERY === 0 ? i + 1 : null,
3374
3404
  maxTotal,
3375
3405
  upPx: delta ? upPx : void 0,
3376
3406
  downPx: delta ? downPx : void 0,
@@ -5489,11 +5519,14 @@ window.__ModuleLoader__.load({
5489
5519
  * The provider-id seam between dsh request envelopes and the models.dev
5490
5520
  * registry: client/cost.ts resolves price-book branches through it, and
5491
5521
  * host/fold.ts uses the DeepSeek resolution to split the session-cost totals
5492
- * into peak/off-peak periods. Only the renames live here — an id absent
5493
- * from the table passes through verbatim.
5522
+ * into peak/off-peak periods. Only the renames live here — a dsh route key
5523
+ * that is not a registry id (user-declared gateways, `claude`/`codex`
5524
+ * subscriptions, org-prefixed catalogs) passes through and prices through
5525
+ * the model-side resolution index instead (client/cost.ts).
5494
5526
  */
5495
5527
  const MODELS_DEV_PROVIDER_IDS = {
5496
5528
  "deepseek-official": "deepseek",
5529
+ "deepseek-account": "deepseek",
5497
5530
  "kimi-coding": "moonshotai",
5498
5531
  "minimax-cn": "minimax",
5499
5532
  "zai-coding-cn": "zhipuai"
@@ -5516,6 +5549,120 @@ window.__ModuleLoader__.load({
5516
5549
  function toCurrency(usd, currency) {
5517
5550
  return currency === "cny" ? usd / USD_PER_CNY : usd;
5518
5551
  }
5552
+ /** Rate equality within the registry's float noise (sync drift prints 0.125000…003). */
5553
+ function sameRate(a, b) {
5554
+ const close = (x, y) => Math.abs(x - y) <= 1e-9 * Math.max(1, Math.abs(x));
5555
+ return close(a.hit, b.hit) && close(a.miss, b.miss) && close(a.write, b.write) && close(a.out, b.out);
5556
+ }
5557
+ /** One proven candidate rate off a built book branch, or null. */
5558
+ function rateOfEntry(value) {
5559
+ const v = value;
5560
+ if (v === null || typeof v !== "object") return null;
5561
+ const r = v;
5562
+ const num = (x) => typeof x === "number" && Number.isFinite(x);
5563
+ return num(r.hit) && num(r.miss) && num(r.write) && num(r.out) ? {
5564
+ hit: r.hit,
5565
+ miss: r.miss,
5566
+ write: r.write,
5567
+ out: r.out
5568
+ } : null;
5569
+ }
5570
+ /**
5571
+ * Build the resolution index over a proven book. `npmOf` carries each
5572
+ * provider's registry `npm` package (null when absent) — the vendor signal:
5573
+ * a provider whose package is `@ai-sdk/<its own id>` is the model's first
5574
+ * party, while mirrors and gateways ride `@ai-sdk/openai-compatible` or
5575
+ * someone else's package. Suffix tails are indexed alongside exact ids so a
5576
+ * short dsh spelling (`k3`) resolves book-wide too.
5577
+ */
5578
+ function priceIndexOf(prices, npmOf) {
5579
+ const byModel = /* @__PURE__ */ new Map();
5580
+ const push = (key, cand) => {
5581
+ const list = byModel.get(key);
5582
+ if (list === void 0) byModel.set(key, [cand]);
5583
+ else if (!list.some((c) => c.pid === cand.pid && sameRate(c.rate, cand.rate))) list.push(cand);
5584
+ };
5585
+ for (const pid of Object.keys(prices)) {
5586
+ const branch = branchOf(prices, pid);
5587
+ if (branch === null) continue;
5588
+ const npm = npmOf[pid];
5589
+ const primary = typeof npm === "string" && npm.startsWith("@ai-sdk/") && npm.slice(8) === pid;
5590
+ for (const mid of Object.keys(branch)) {
5591
+ const rate = rateOfEntry(branch[mid]);
5592
+ if (rate === null) continue;
5593
+ const lower = mid.toLowerCase();
5594
+ const cand = {
5595
+ pid,
5596
+ rate,
5597
+ primary
5598
+ };
5599
+ push(lower, cand);
5600
+ const parts = lower.split("-");
5601
+ for (let i = 1; i < parts.length; i++) push(parts.slice(i).join("-"), cand);
5602
+ }
5603
+ }
5604
+ return { byModel };
5605
+ }
5606
+ /**
5607
+ * The rate shared by a candidate group, or null: the largest equal-rate
5608
+ * group wins, a tie refuses. Agreeing mirrors are noise; disagreeing ones
5609
+ * price nothing rather than guess.
5610
+ */
5611
+ function majorityRate(cands) {
5612
+ const groups = [];
5613
+ for (const c of cands) {
5614
+ const g = groups.find((g) => sameRate(g.rate, c.rate));
5615
+ if (g === void 0) groups.push({
5616
+ rate: c.rate,
5617
+ n: 1
5618
+ });
5619
+ else g.n++;
5620
+ }
5621
+ groups.sort((a, b) => b.n - a.n);
5622
+ return groups.length > 1 && groups[0].n === groups[1].n ? null : groups[0].rate;
5623
+ }
5624
+ /**
5625
+ * Pick one candidate group's rate: the model's own vendor (unique or
5626
+ * unanimous), then a provider the model id itself names (`deepseek-v4-flash`
5627
+ * under `deepseek`), then a lone carrier. Everything else prices null.
5628
+ */
5629
+ function resolveCandidates(lower, cands, org) {
5630
+ if (org !== null) {
5631
+ const named = cands.filter((c) => c.pid === org || org.startsWith(c.pid + "-") || org.length >= 4 && org.startsWith(c.pid));
5632
+ if (named.length > 0) {
5633
+ const rate = majorityRate(named);
5634
+ if (rate !== null) return rate;
5635
+ }
5636
+ }
5637
+ const primaries = cands.filter((c) => c.primary);
5638
+ if (primaries.length > 0) {
5639
+ const rate = majorityRate(primaries);
5640
+ if (rate !== null) return rate;
5641
+ }
5642
+ const prefixed = cands.filter((c) => lower.startsWith(c.pid + "-") || lower.startsWith(c.pid + "/"));
5643
+ if (prefixed.length > 0) {
5644
+ const rate = majorityRate(prefixed);
5645
+ if (rate !== null) return rate;
5646
+ }
5647
+ return cands.length === 1 ? cands[0].rate : null;
5648
+ }
5649
+ /**
5650
+ * Resolve a model id book-wide: the full id first, then its last `/`-segment
5651
+ * (an org-prefixed dialect whose exact spelling no registry provider lists).
5652
+ * Null when every tier refuses.
5653
+ */
5654
+ function resolveRate(index, model) {
5655
+ const lower = model.toLowerCase();
5656
+ const slash = lower.lastIndexOf("/");
5657
+ const org = slash > 0 ? lower.slice(0, slash) : null;
5658
+ for (const key of slash > 0 ? [lower, lower.slice(slash + 1)] : [lower]) {
5659
+ const cands = index.byModel.get(key);
5660
+ if (cands === void 0) continue;
5661
+ const rate = resolveCandidates(key, cands, org);
5662
+ if (rate !== null) return rate;
5663
+ }
5664
+ return null;
5665
+ }
5519
5666
  /** One rate triple at the doubled peak rate (the tooltip's `peak | off` pair). */
5520
5667
  function peakOf(rate) {
5521
5668
  return {
@@ -5555,22 +5702,16 @@ window.__ModuleLoader__.load({
5555
5702
  * The book's rates for one folded (provider, model) bucket, or null when
5556
5703
  * the book cannot price it: the dsh provider id resolves through
5557
5704
  * modelsDevProviderOf (unmapped ids pass through) and prices by model id —
5558
- * exact, case-insensitive, or suffix; a provider the book does not carry
5559
- * falls back to a cross-provider scan, priced only when exactly one branch
5560
- * carries the model id.
5561
- */
5562
- function priceOf(prices, provider, model) {
5563
- if (prices === null || prices === void 0) return null;
5564
- const direct = branchOf(prices, modelsDevProviderOf(provider));
5705
+ * exact, case-insensitive, or suffix. A provider the book does not carry
5706
+ * falls back to the model-side resolution index, which names the vendor
5707
+ * from data (org segment, own-vendor SDK, id prefix, lone carrier) — never
5708
+ * from a per-model hardcode.
5709
+ */
5710
+ function priceOf(book, provider, model) {
5711
+ if (book === null || book === void 0) return null;
5712
+ const direct = branchOf(book.prices, modelsDevProviderOf(provider));
5565
5713
  if (direct !== null) return lookup(direct, model);
5566
- let found = null;
5567
- for (const models of Object.values(prices)) {
5568
- const rate = lookup(models, model);
5569
- if (rate === null) continue;
5570
- if (found !== null) return null;
5571
- found = rate;
5572
- }
5573
- return found;
5714
+ return resolveRate(book.index, model);
5574
5715
  }
5575
5716
  /**
5576
5717
  * Price the session's cumulative billed-token totals. Cache reads bill at
@@ -5581,8 +5722,8 @@ window.__ModuleLoader__.load({
5581
5722
  * Null when nothing was priced (no usage folded, no book yet, or no model
5582
5723
  * the book prices), so the cell can show a dash.
5583
5724
  */
5584
- function estimateSessionCost(usage, prices, currency) {
5585
- if (usage === null || usage === void 0 || prices === null || prices === void 0) return null;
5725
+ function estimateSessionCost(usage, book, currency) {
5726
+ if (usage === null || usage === void 0 || book === null || book === void 0) return null;
5586
5727
  let total = 0;
5587
5728
  let any = false;
5588
5729
  for (const provider of Object.keys(usage)) {
@@ -5590,7 +5731,7 @@ window.__ModuleLoader__.load({
5590
5731
  if (models === null) continue;
5591
5732
  const deepseek = isDeepSeekProvider(provider);
5592
5733
  for (const model of Object.keys(models)) {
5593
- const rate = priceOf(prices, provider, model);
5734
+ const rate = priceOf(book, provider, model);
5594
5735
  const periods = asRecord(models[model]);
5595
5736
  if (rate === null || periods === null) continue;
5596
5737
  for (const period of ["peak", "off"]) {
@@ -5662,7 +5803,7 @@ window.__ModuleLoader__.load({
5662
5803
  return formatCost(amount, currency).replace(/0+$/, "").replace(/\.$/, "");
5663
5804
  }
5664
5805
  //#endregion
5665
- //#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/error.js
5806
+ //#region node_modules/.pnpm/@opencode-ai+models@0.0.87/node_modules/@opencode-ai/models/dist/error.js
5666
5807
  /**
5667
5808
  * The only error thrown by the models.dev client.
5668
5809
  *
@@ -5679,7 +5820,7 @@ window.__ModuleLoader__.load({
5679
5820
  }
5680
5821
  };
5681
5822
  //#endregion
5682
- //#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/client.js
5823
+ //#region node_modules/.pnpm/@opencode-ai+models@0.0.87/node_modules/@opencode-ai/models/dist/client.js
5683
5824
  /**
5684
5825
  * Creates a stateless models.dev client. Every method performs exactly one
5685
5826
  * `GET` and nothing is ever cached — callers who want caching should wrap
@@ -5696,7 +5837,11 @@ window.__ModuleLoader__.load({
5696
5837
  for (const [key, value] of new Headers(requestOptions?.headers)) headers.set(key, value);
5697
5838
  let response;
5698
5839
  try {
5699
- response = await fetch(new URL(path, base), {
5840
+ const url = new URL(path, base);
5841
+ const modelTypes = requestOptions?.modelTypes;
5842
+ if (modelTypes === "all") url.searchParams.set("type", "all");
5843
+ else if (modelTypes && modelTypes.length > 0) url.searchParams.set("type", modelTypes.join(","));
5844
+ response = await fetch(url, {
5700
5845
  method: "GET",
5701
5846
  headers,
5702
5847
  signal: requestOptions?.signal
@@ -5741,10 +5886,12 @@ window.__ModuleLoader__.load({
5741
5886
  * re-proves every field at the boundary — a non-conforming provider/model/
5742
5887
  * cost entry drops whole. The book keeps EVERY provider that prices (keyed
5743
5888
  * by the registry's own provider id), so a dsh provider id outside the
5744
- * rename table still prices by direct passthrough. One fetch per page load,
5745
- * kicked on first subscribe; a failure degrades to a visible state the cost
5746
- * cell notes, with a backed-off automatic retry — never a spinner, and
5747
- * never an unhandled rejection.
5889
+ * rename table still prices by direct passthrough, and carries each
5890
+ * provider's `npm` package into the model-side resolution index
5891
+ * (cost.ts `priceIndexOf`) so unknown provider ids price by model id alone.
5892
+ * One fetch per page load, kicked on first subscribe; a failure degrades to
5893
+ * a visible state the cost cell notes, with a backed-off automatic retry —
5894
+ * never a spinner, and never an unhandled rejection.
5748
5895
  */
5749
5896
  /** One finite non-negative registry figure, or null. */
5750
5897
  function rateOf(value) {
@@ -5752,18 +5899,23 @@ window.__ModuleLoader__.load({
5752
5899
  }
5753
5900
  /**
5754
5901
  * Extract the whole registry's per-model USD rates from a delivered
5755
- * providers payload (`/api.json`), keyed by the registry's provider ids.
5756
- * Any shape failure skips just that entry; a payload that is not a record
5757
- * at all returns null (the store treats it as a failed fetch and retries).
5902
+ * providers payload (`/api.json`), keyed by the registry's provider ids,
5903
+ * with the resolution index built over it. Any shape failure skips just
5904
+ * that entry; a payload that is not a record at all returns null (the store
5905
+ * treats it as a failed fetch and retries).
5758
5906
  */
5759
5907
  function pricesBookOf(value) {
5760
5908
  const data = asRecord(value);
5761
5909
  if (data === null || Array.isArray(data)) return null;
5762
5910
  const book = {};
5911
+ const npmOf = {};
5763
5912
  for (const providerId of Object.keys(data)) {
5764
5913
  const provider = asRecord(data[providerId]);
5765
- const modelList = provider !== null ? asRecord(provider.models) : null;
5914
+ if (provider === null) continue;
5915
+ const modelList = asRecord(provider.models);
5766
5916
  if (modelList === null) continue;
5917
+ const npm = provider.npm;
5918
+ npmOf[providerId] = typeof npm === "string" ? npm : null;
5767
5919
  const models = {};
5768
5920
  for (const id of Object.keys(modelList)) {
5769
5921
  const model = asRecord(modelList[id]);
@@ -5781,14 +5933,17 @@ window.__ModuleLoader__.load({
5781
5933
  }
5782
5934
  if (Object.keys(models).length > 0) book[providerId] = models;
5783
5935
  }
5784
- return book;
5936
+ return {
5937
+ prices: book,
5938
+ index: priceIndexOf(book, npmOf)
5939
+ };
5785
5940
  }
5786
5941
  const defaultLoader = () => make().providers();
5787
5942
  /** The retry backoff base; each consecutive failure doubles the wait, capped at 3 doublings. */
5788
5943
  const RETRY_BASE_MS = 3e4;
5789
5944
  let loader = defaultLoader;
5790
5945
  let snap = {
5791
- prices: null,
5946
+ book: null,
5792
5947
  failed: false
5793
5948
  };
5794
5949
  let inFlight = false;
@@ -5812,7 +5967,7 @@ window.__ModuleLoader__.load({
5812
5967
  const book = pricesBookOf(await loader());
5813
5968
  if (book !== null) {
5814
5969
  snap = {
5815
- prices: book,
5970
+ book,
5816
5971
  failed: false
5817
5972
  };
5818
5973
  failures = 0;
@@ -5824,7 +5979,7 @@ window.__ModuleLoader__.load({
5824
5979
  for (const fn of listeners) fn();
5825
5980
  }
5826
5981
  function kick() {
5827
- if (snap.prices !== null || inFlight || timer !== null) return;
5982
+ if (snap.book !== null || inFlight || timer !== null) return;
5828
5983
  fire();
5829
5984
  }
5830
5985
  /** The useSyncExternalStore seam: subscribing also kicks the first fetch. */
@@ -6307,7 +6462,7 @@ window.__ModuleLoader__.load({
6307
6462
  total
6308
6463
  };
6309
6464
  }
6310
- function kpisOf(rows, listed, prices, currency) {
6465
+ function kpisOf(rows, listed, book, currency) {
6311
6466
  const usage = mergeCostUsage(...rows.map((row) => row.timeline?.cost));
6312
6467
  const totals = usageTotalsOf(usage);
6313
6468
  let turns = 0;
@@ -6319,7 +6474,7 @@ window.__ModuleLoader__.load({
6319
6474
  let usageSessions = 0;
6320
6475
  for (const row of rows) {
6321
6476
  const cost = row.timeline?.cost;
6322
- if (estimateSessionCost(cost, prices, currency) !== null) costSessions++;
6477
+ if (estimateSessionCost(cost, book, currency) !== null) costSessions++;
6323
6478
  if (usageTotalsOf(cost) !== null) usageSessions++;
6324
6479
  turns += turnsOf(row.timeline);
6325
6480
  const timing = row.timeline?.timing;
@@ -6333,7 +6488,7 @@ window.__ModuleLoader__.load({
6333
6488
  listed,
6334
6489
  tokens: totals?.total ?? 0,
6335
6490
  turns,
6336
- cost: estimateSessionCost(usage, prices, currency),
6491
+ cost: estimateSessionCost(usage, book, currency),
6337
6492
  costSessions,
6338
6493
  cacheHit: totals === null ? null : cacheHitPercent(totals.cacheRead, totals.input + totals.cacheRead + totals.cacheWrite),
6339
6494
  usageSessions,
@@ -6346,33 +6501,44 @@ window.__ModuleLoader__.load({
6346
6501
  };
6347
6502
  }
6348
6503
  /**
6349
- * Merge every row's daily ledger into one — the heatmap's data. A session
6350
- * counts toward a day only when its own entry carries activity, mirroring
6351
- * the day filter's predicate, so the cell's tooltip previews the click; a
6352
- * zeroed entry is skipped whole. The merged record stays small even over
6353
- * long histories.
6504
+ * Merge every row's daily ledger into one — the heatmap's and the usage
6505
+ * chart's data. A session counts toward a day only when its own entry
6506
+ * carries activity, mirroring the day filter's predicate, so the cell's
6507
+ * tooltip previews the click; a zeroed entry is skipped whole. Each day's
6508
+ * pricing records merge into one SessionCostUsage and price off the SAME
6509
+ * book/estimator the KPI band's cost cell rides (null fee on anything
6510
+ * unpriced). The merged record stays small even over long histories.
6354
6511
  */
6355
- function aggregateDays(rows) {
6356
- const days = {};
6357
- const byKey = days;
6512
+ function aggregateDays(rows, book, currency) {
6513
+ const merged = {};
6514
+ const byKey = merged;
6358
6515
  for (const row of rows) {
6359
6516
  if (row.activity === null) continue;
6360
6517
  for (const key of Object.keys(row.activity.days)) {
6361
6518
  const entry = row.activity.days[key];
6362
6519
  if (entry.tokens <= 0 && entry.requests <= 0) continue;
6363
6520
  const prev = byKey[key];
6364
- if (prev === void 0) days[key] = {
6521
+ if (prev === void 0) merged[key] = {
6365
6522
  tokens: entry.tokens,
6366
6523
  requests: entry.requests,
6367
- sessions: 1
6524
+ sessions: 1,
6525
+ fees: entry.cost !== void 0 ? [entry.cost] : []
6368
6526
  };
6369
6527
  else {
6370
6528
  prev.tokens += entry.tokens;
6371
6529
  prev.requests += entry.requests;
6372
6530
  prev.sessions++;
6531
+ if (entry.cost !== void 0) prev.fees.push(entry.cost);
6373
6532
  }
6374
6533
  }
6375
6534
  }
6535
+ const days = {};
6536
+ for (const [key, day] of Object.entries(merged)) days[key] = {
6537
+ tokens: day.tokens,
6538
+ requests: day.requests,
6539
+ sessions: day.sessions,
6540
+ cost: estimateSessionCost(mergeCostUsage(...day.fees), book, currency)
6541
+ };
6376
6542
  return days;
6377
6543
  }
6378
6544
  /**
@@ -7151,6 +7317,166 @@ window.__ModuleLoader__.load({
7151
7317
  };
7152
7318
  }
7153
7319
  //#endregion
7320
+ //#region src/client/components/overviewUsage.tsx
7321
+ /**
7322
+ * The Context Insights panel's first-row usage chart — the RIGHT half of the
7323
+ * KPI row. Seven day columns ending today, each pairing two bars priced off
7324
+ * the merged daily ledger (overview.ts aggregateDays): billed TOKENS (the
7325
+ * heatmap's own blue) and the day's estimated COST (green). The fee rides
7326
+ * the same per-day pricing records the host fold books at each turn's
7327
+ * initiation, priced by the same book and estimator as the KPI band's cost
7328
+ * cell — never a whole session's spend dumped on the day it was last
7329
+ * active. The two series scale on their OWN window maxima (a $2 week must
7330
+ * not flatten under a 169M-token week), so each bar reads as a share of its
7331
+ * series' peak; the tooltip carries the exact figures. The window is fixed
7332
+ * to the last 7 days — the range selector above scopes the KPI figures, not
7333
+ * this calendar.
7334
+ *
7335
+ * The plot is framed like a chart: hairline gridlines at 0/25/50/75/100% of
7336
+ * each series' own peak, a solid baseline at 0, and a tick rail on each
7337
+ * side — TOKENS on the left, COST on the right, computed from the same
7338
+ * maxima the bars scale to. The cost rail reads '—' until some day actually
7339
+ * prices.
7340
+ */
7341
+ /** The chart's window: today and the six days before it, oldest first. */
7342
+ function usageWindowOf(today) {
7343
+ const columns = [];
7344
+ for (let d = -6; d <= 0; d++) {
7345
+ const key = shiftDayKey(today, d);
7346
+ if (key === null) return [];
7347
+ columns.push(key);
7348
+ }
7349
+ return columns;
7350
+ }
7351
+ /** The shared gridline/tick positions, in percent of each series' peak. */
7352
+ const TICKS = [
7353
+ 0,
7354
+ 25,
7355
+ 50,
7356
+ 75,
7357
+ 100
7358
+ ];
7359
+ function makeOverviewUsage(kit) {
7360
+ const { t, fmt } = kit;
7361
+ return function OverviewUsage(props) {
7362
+ const window = usageWindowOf(props.today);
7363
+ const byKey = props.days;
7364
+ const columns = window.map((key) => ({
7365
+ key,
7366
+ entry: byKey[key]
7367
+ }));
7368
+ const empty = columns.every((c) => c.entry === void 0 || c.entry.tokens <= 0 && c.entry.cost === null);
7369
+ let maxTokens = 0;
7370
+ let maxCost = 0;
7371
+ for (const c of columns) {
7372
+ if (c.entry === void 0) continue;
7373
+ if (c.entry.tokens > maxTokens) maxTokens = c.entry.tokens;
7374
+ if (c.entry.cost !== null && c.entry.cost > maxCost) maxCost = c.entry.cost;
7375
+ }
7376
+ const heightOf = (value, max) => max <= 0 || value <= 0 ? "0%" : `${Math.max(4, Math.round(value / max * 100))}%`;
7377
+ const tickOf = (percent) => fmt(Math.round(maxTokens * percent / 100));
7378
+ const costTickOf = (percent) => {
7379
+ if (maxCost <= 0) return "—";
7380
+ return percent === 0 ? "0" : formatCost(maxCost * percent / 100, props.currency);
7381
+ };
7382
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7383
+ className: "lc-card lc-ov-usage",
7384
+ children: [
7385
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7386
+ className: "lc-card-title",
7387
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7388
+ className: "lc-card-title-text",
7389
+ children: t("ov.usage.title")
7390
+ })
7391
+ }),
7392
+ empty || window.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7393
+ className: "lc-empty",
7394
+ children: t("ov.usage.empty")
7395
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7396
+ className: "lc-ov-usage-plot",
7397
+ role: "group",
7398
+ "aria-label": t("ov.usage.title"),
7399
+ children: [
7400
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7401
+ className: "lc-ov-usage-rail lc-ov-usage-rail-l",
7402
+ "aria-hidden": "true",
7403
+ children: TICKS.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7404
+ className: "lc-ov-usage-tick",
7405
+ style: { top: `${100 - p}%` },
7406
+ children: tickOf(p)
7407
+ }, p))
7408
+ }),
7409
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7410
+ className: "lc-ov-usage-grid",
7411
+ children: [TICKS.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7412
+ className: "lc-ov-usage-line" + (p === 0 ? " lc-ov-usage-line-0" : ""),
7413
+ style: { bottom: `${p}%` },
7414
+ "aria-hidden": "true"
7415
+ }, p)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7416
+ className: "lc-ov-usage-cols",
7417
+ children: columns.map((c) => {
7418
+ const tokens = c.entry?.tokens ?? 0;
7419
+ const cost = c.entry?.cost ?? null;
7420
+ const label = `${c.key}\n${t("ov.usage.tokens")} ${fmt(tokens)}\n${t("ov.usage.costTip")} ${cost === null ? "—" : formatCost(cost, props.currency)}`;
7421
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
7422
+ label,
7423
+ side: "top",
7424
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7425
+ className: "lc-ov-usage-col",
7426
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7427
+ className: "lc-ov-usage-bars",
7428
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7429
+ className: "lc-ov-usage-bar lc-ov-usage-tokens",
7430
+ style: { height: heightOf(tokens, maxTokens) }
7431
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7432
+ className: "lc-ov-usage-bar lc-ov-usage-cost",
7433
+ style: { height: cost === null ? "0%" : heightOf(cost, maxCost) }
7434
+ })]
7435
+ })
7436
+ })
7437
+ }, c.key);
7438
+ })
7439
+ })]
7440
+ }),
7441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7442
+ className: "lc-ov-usage-rail lc-ov-usage-rail-r",
7443
+ "aria-hidden": "true",
7444
+ children: TICKS.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7445
+ className: "lc-ov-usage-tick",
7446
+ style: { top: `${100 - p}%` },
7447
+ children: costTickOf(p)
7448
+ }, p))
7449
+ })
7450
+ ]
7451
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7452
+ className: "lc-ov-usage-days",
7453
+ "aria-hidden": "true",
7454
+ children: columns.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7455
+ className: "lc-ov-usage-day",
7456
+ children: c.key.slice(5)
7457
+ }, c.key))
7458
+ })] }),
7459
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7460
+ className: "lc-ov-usage-key",
7461
+ "aria-hidden": "true",
7462
+ children: [
7463
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "lc-ov-usage-swatch lc-ov-usage-tokens" }),
7464
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7465
+ className: "lc-ov-usage-key-label",
7466
+ children: t("ov.usage.tokens")
7467
+ }),
7468
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "lc-ov-usage-swatch lc-ov-usage-cost" }),
7469
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7470
+ className: "lc-ov-usage-key-label",
7471
+ children: t("ov.usage.cost")
7472
+ })
7473
+ ]
7474
+ })
7475
+ ]
7476
+ });
7477
+ };
7478
+ }
7479
+ //#endregion
7154
7480
  //#region src/client/components/statsTiming.tsx
7155
7481
  /**
7156
7482
  * The Timing card: where the session's ACTIVE time went. The donut splits
@@ -7483,9 +7809,12 @@ window.__ModuleLoader__.load({
7483
7809
  * projection values), so the panel draws every session's insight without
7484
7810
  * opening one log.
7485
7811
  *
7486
- * The panel's first row is the KPI metrics band: the range's six figures
7487
- * (sessions, billed tokens, cost, cache hit, tool calls, active time) in one
7488
- * full-width line. Below it, the aggregate stats row folds the same range's
7812
+ * The panel's first row is a 1:1 column pair: the KPI metrics band (the
7813
+ * range's six figures — sessions, billed tokens, cost, cache hit, tool
7814
+ * calls, active time — three per row) beside the last-7-days usage chart
7815
+ * (each day's billed tokens and estimated cost as a bar pair, folded off
7816
+ * the merged daily ledger). Below it, the aggregate stats row folds the
7817
+ * same range's
7489
7818
  * sessions into the two donut cards the per-session Context tab opens with —
7490
7819
  * Token Stats (the composition-split billed volume) and Timing Stats (the
7491
7820
  * summed totals). The body is then a 3:7 column pair: the insight column (the
@@ -7516,6 +7845,7 @@ window.__ModuleLoader__.load({
7516
7845
  const Donut = makeDonut(kit);
7517
7846
  const OverviewTokens = makeOverviewTokens(kit, Donut);
7518
7847
  const StatsTiming = makeStatsTiming(kit, Donut);
7848
+ const OverviewUsage = makeOverviewUsage(kit);
7519
7849
  const ErrorBoundary = makeErrorBoundary(t);
7520
7850
  /** The display currency follows the active locale (zh → CNY), read per render — the slot outlet re-renders on a locale switch. */
7521
7851
  function activeCurrency() {
@@ -7524,7 +7854,7 @@ window.__ModuleLoader__.load({
7524
7854
  }
7525
7855
  function OverviewBody(props) {
7526
7856
  const open = (0, react.useSyncExternalStore)(overviewStore.subscribe, overviewStore.getSnapshot);
7527
- const { prices } = useModelPrices();
7857
+ const { book } = useModelPrices();
7528
7858
  const snapshot = sessionsSnapshotOf(props);
7529
7859
  const wsSnapshot = workspacesSnapshotOf(props);
7530
7860
  const [range, setRange] = (0, react.useState)("30d");
@@ -7576,8 +7906,8 @@ window.__ModuleLoader__.load({
7576
7906
  }] : counts;
7577
7907
  const visible = sortRows(group === null ? scoped : scoped.filter((row) => inGroup(row, group, groups)), sort);
7578
7908
  const paged = pageOf(visible, page);
7579
- const kpi = kpisOf(ranged, allRows.length, prices, currency);
7580
- const days = aggregateDays(allRows);
7909
+ const kpi = kpisOf(ranged, allRows.length, book, currency);
7910
+ const days = aggregateDays(allRows, book, currency);
7581
7911
  const openOne = (id) => {
7582
7912
  openSessionVia(ctx, id);
7583
7913
  overviewStore.set(false);
@@ -7626,298 +7956,311 @@ window.__ModuleLoader__.load({
7626
7956
  }), rows === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7627
7957
  className: "lc-empty",
7628
7958
  children: t("ov.unavailable")
7629
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
7630
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7631
- className: "lc-ov-kpis",
7632
- children: [
7633
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7634
- className: "lc-stat lc-ov-kpi",
7635
- children: [
7636
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7637
- className: "lc-stat-label",
7638
- children: t("ov.kpi.sessions")
7639
- }),
7640
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7641
- className: "lc-stat-value",
7642
- children: kpi.sessions
7643
- }),
7644
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7645
- className: "lc-stat-sub",
7646
- children: t("ov.kpi.ofTotal", { n: kpi.listed })
7647
- })
7648
- ]
7649
- }),
7650
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7651
- className: "lc-stat lc-ov-kpi",
7959
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7960
+ className: "lc-ov-scroll",
7961
+ children: [
7962
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7963
+ className: "lc-ov-first",
7964
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7965
+ className: "lc-ov-kpis",
7652
7966
  children: [
7653
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7654
- className: "lc-stat-label",
7655
- children: t("ov.kpi.tokens")
7656
- }),
7657
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7658
- className: "lc-stat-value",
7659
- children: fmt(kpi.tokens)
7660
- }),
7661
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7662
- className: "lc-stat-sub",
7967
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7968
+ className: "lc-stat lc-ov-kpi",
7663
7969
  children: [
7664
- t("stats.turns"),
7665
- " ",
7666
- fmt(kpi.turns)
7970
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7971
+ className: "lc-stat-label",
7972
+ children: t("ov.kpi.sessions")
7973
+ }),
7974
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7975
+ className: "lc-stat-value",
7976
+ children: kpi.sessions
7977
+ }),
7978
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7979
+ className: "lc-stat-sub",
7980
+ children: t("ov.kpi.ofTotal", { n: kpi.listed })
7981
+ })
7667
7982
  ]
7668
- })
7669
- ]
7670
- }),
7671
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7672
- className: "lc-stat lc-ov-kpi",
7673
- children: [
7674
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7675
- className: "lc-stat-label",
7676
- children: t("stats.cost")
7677
- }),
7678
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7679
- className: "lc-stat-value",
7680
- children: kpi.cost === null ? "—" : formatCost(kpi.cost, currency)
7681
- }),
7682
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7683
- className: "lc-stat-sub",
7684
- children: t("ov.kpi.sessionsSub", { n: kpi.costSessions })
7685
- })
7686
- ]
7687
- }),
7688
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7689
- className: "lc-stat lc-ov-kpi",
7690
- children: [
7691
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7692
- className: "lc-stat-label",
7693
- children: t("stats.cacheHit")
7694
7983
  }),
7695
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7696
- className: "lc-stat-value",
7697
- children: kpi.cacheHit === null ? "—" : kpi.cacheHit + "%"
7698
- }),
7699
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7700
- className: "lc-stat-sub",
7701
- children: t("ov.kpi.sessionsSub", { n: kpi.usageSessions })
7702
- })
7703
- ]
7704
- }),
7705
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7706
- className: "lc-stat lc-ov-kpi",
7707
- children: [
7708
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7709
- className: "lc-stat-label",
7710
- children: t("stats.toolCalls")
7984
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7985
+ className: "lc-stat lc-ov-kpi",
7986
+ children: [
7987
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7988
+ className: "lc-stat-label",
7989
+ children: t("ov.kpi.tokens")
7990
+ }),
7991
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7992
+ className: "lc-stat-value",
7993
+ children: fmt(kpi.tokens)
7994
+ }),
7995
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7996
+ className: "lc-stat-sub",
7997
+ children: [
7998
+ t("stats.turns"),
7999
+ " ",
8000
+ fmt(kpi.turns)
8001
+ ]
8002
+ })
8003
+ ]
7711
8004
  }),
7712
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7713
- className: "lc-stat-value",
7714
- children: fmt(kpi.toolCalls)
8005
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8006
+ className: "lc-stat lc-ov-kpi",
8007
+ children: [
8008
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8009
+ className: "lc-stat-label",
8010
+ children: t("stats.cost")
8011
+ }),
8012
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8013
+ className: "lc-stat-value",
8014
+ children: kpi.cost === null ? "—" : formatCost(kpi.cost, currency)
8015
+ }),
8016
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8017
+ className: "lc-stat-sub",
8018
+ children: t("ov.kpi.pricedSub", {
8019
+ n: kpi.costSessions,
8020
+ total: kpi.sessions
8021
+ })
8022
+ })
8023
+ ]
7715
8024
  }),
7716
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7717
- className: "lc-stat-sub",
7718
- children: t("ov.kpi.toolSub", { dur: fmtDuration(kpi.toolsMs) })
7719
- })
7720
- ]
7721
- }),
7722
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7723
- className: "lc-stat lc-ov-kpi",
7724
- children: [
7725
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7726
- className: "lc-stat-label",
7727
- children: t("timing.total")
8025
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8026
+ className: "lc-stat lc-ov-kpi",
8027
+ children: [
8028
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8029
+ className: "lc-stat-label",
8030
+ children: t("stats.cacheHit")
8031
+ }),
8032
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8033
+ className: "lc-stat-value",
8034
+ children: kpi.cacheHit === null ? "—" : kpi.cacheHit + "%"
8035
+ }),
8036
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8037
+ className: "lc-stat-sub",
8038
+ children: t("ov.kpi.sessionsSub", { n: kpi.usageSessions })
8039
+ })
8040
+ ]
7728
8041
  }),
7729
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7730
- className: "lc-stat-value",
7731
- children: fmtDuration(kpi.wallMs)
8042
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8043
+ className: "lc-stat lc-ov-kpi",
8044
+ children: [
8045
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8046
+ className: "lc-stat-label",
8047
+ children: t("stats.toolCalls")
8048
+ }),
8049
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8050
+ className: "lc-stat-value",
8051
+ children: fmt(kpi.toolCalls)
8052
+ }),
8053
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8054
+ className: "lc-stat-sub",
8055
+ children: t("ov.kpi.toolSub", { dur: fmtDuration(kpi.toolsMs) })
8056
+ })
8057
+ ]
7732
8058
  }),
7733
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7734
- className: "lc-stat-sub",
7735
- children: t("ov.kpi.wallSub", { n: fmt(kpi.calls) })
8059
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8060
+ className: "lc-stat lc-ov-kpi",
8061
+ children: [
8062
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8063
+ className: "lc-stat-label",
8064
+ children: t("timing.total")
8065
+ }),
8066
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8067
+ className: "lc-stat-value",
8068
+ children: fmtDuration(kpi.wallMs)
8069
+ }),
8070
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8071
+ className: "lc-stat-sub",
8072
+ children: t("ov.kpi.wallSub", { n: fmt(kpi.calls) })
8073
+ })
8074
+ ]
7736
8075
  })
7737
8076
  ]
7738
- })
7739
- ]
7740
- }),
7741
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7742
- className: "lc-ov-stats",
7743
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewTokens, { tokens: kpi.tokenParts }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTiming, { timing: kpi.timing })]
7744
- }),
7745
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7746
- className: "lc-ov-body",
7747
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7748
- className: "lc-ov-left",
8077
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewUsage, {
8078
+ days,
8079
+ currency,
8080
+ today: todayKey()
8081
+ })]
8082
+ }),
8083
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8084
+ className: "lc-ov-stats",
8085
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewTokens, { tokens: kpi.tokenParts }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTiming, { timing: kpi.timing })]
8086
+ }),
8087
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8088
+ className: "lc-ov-body",
7749
8089
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7750
- className: "lc-card lc-ov-heat-card",
8090
+ className: "lc-ov-left",
7751
8091
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7752
- className: "lc-card-title",
7753
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7754
- className: "lc-card-title-text",
7755
- children: t("ov.heat.title")
7756
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7757
- className: "lc-heat-ctl",
8092
+ className: "lc-card lc-ov-heat-card",
8093
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8094
+ className: "lc-card-title",
7758
8095
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7759
- className: "lc-card-sub",
7760
- children: t("ov.heat.sub")
7761
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7762
- className: "lc-gran",
7763
- role: "group",
7764
- "aria-label": t("ov.heat.metric"),
7765
- children: METRICS.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7766
- type: "button",
7767
- className: "lc-gran-btn" + (metric === m ? " lc-gran-on" : ""),
7768
- onClick: () => {
7769
- setMetric(m);
7770
- },
7771
- children: t("ov.heat.metric." + m)
7772
- }, m))
8096
+ className: "lc-card-title-text",
8097
+ children: t("ov.heat.title")
8098
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8099
+ className: "lc-heat-ctl",
8100
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8101
+ className: "lc-card-sub",
8102
+ children: t("ov.heat.sub")
8103
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8104
+ className: "lc-gran",
8105
+ role: "group",
8106
+ "aria-label": t("ov.heat.metric"),
8107
+ children: METRICS.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8108
+ type: "button",
8109
+ className: "lc-gran-btn" + (metric === m ? " lc-gran-on" : ""),
8110
+ onClick: () => {
8111
+ setMetric(m);
8112
+ },
8113
+ children: t("ov.heat.metric." + m)
8114
+ }, m))
8115
+ })]
7773
8116
  })]
8117
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Heatmap, {
8118
+ days,
8119
+ metric,
8120
+ selected: day,
8121
+ onSelect: setDay,
8122
+ today: todayKey()
8123
+ })]
8124
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
8125
+ type: "button",
8126
+ className: "lc-ov-settings",
8127
+ onClick: () => {
8128
+ openPluginSettings();
8129
+ close();
8130
+ },
8131
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8132
+ className: "lc-ov-settings-label",
8133
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconSettings, { size: 14 }), t("plugin.settings")]
8134
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8135
+ className: "lc-ov-settings-hint",
8136
+ children: t("plugin.settingsOpen")
7774
8137
  })]
7775
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Heatmap, {
7776
- days,
7777
- metric,
7778
- selected: day,
7779
- onSelect: setDay,
7780
- today: todayKey()
7781
- })]
7782
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7783
- type: "button",
7784
- className: "lc-ov-settings",
7785
- onClick: () => {
7786
- openPluginSettings();
7787
- close();
7788
- },
7789
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7790
- className: "lc-ov-settings-label",
7791
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconSettings, { size: 14 }), t("plugin.settings")]
7792
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7793
- className: "lc-ov-settings-hint",
7794
- children: t("plugin.settingsOpen")
7795
8138
  })]
7796
- })]
7797
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7798
- className: "lc-ov-right",
7799
- children: [
7800
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7801
- className: "lc-ov-list-head",
7802
- children: [
7803
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7804
- className: "lc-ov-list-title",
7805
- children: t("ov.list.title")
7806
- }),
7807
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7808
- className: "lc-ov-list-count",
7809
- children: visible.length
7810
- }),
7811
- day !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7812
- type: "button",
7813
- className: "lc-ov-day-chip",
7814
- title: t("ov.list.dayClear"),
7815
- onClick: () => {
7816
- setDay(null);
7817
- },
7818
- children: [t("ov.list.dayFilter", { day }), " ×"]
7819
- }),
7820
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
7821
- className: "lc-ov-search",
7822
- type: "search",
7823
- value: query,
7824
- placeholder: t("ov.list.search"),
7825
- "aria-label": t("ov.list.search"),
7826
- onChange: (ev) => {
7827
- setQuery(ev.target.value);
7828
- }
7829
- }),
7830
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7831
- className: "lc-gran",
7832
- role: "group",
7833
- "aria-label": t("ov.list.sortLabel"),
7834
- children: SORTS.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8139
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8140
+ className: "lc-ov-right",
8141
+ children: [
8142
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8143
+ className: "lc-ov-list-head",
8144
+ children: [
8145
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8146
+ className: "lc-ov-list-title",
8147
+ children: t("ov.list.title")
8148
+ }),
8149
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8150
+ className: "lc-ov-list-count",
8151
+ children: visible.length
8152
+ }),
8153
+ day !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7835
8154
  type: "button",
7836
- className: "lc-gran-btn" + (sort === s ? " lc-gran-on" : ""),
8155
+ className: "lc-ov-day-chip",
8156
+ title: t("ov.list.dayClear"),
7837
8157
  onClick: () => {
7838
- setSort(s);
8158
+ setDay(null);
7839
8159
  },
7840
- children: t("ov.list.sort." + s)
7841
- }, s))
7842
- })
7843
- ]
7844
- }),
7845
- chips.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7846
- className: "lc-ov-groups",
7847
- role: "group",
7848
- "aria-label": t("ov.group.label"),
7849
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7850
- type: "button",
7851
- className: "lc-ov-chip" + (group === null ? " lc-ov-chip-on" : ""),
7852
- onClick: () => {
7853
- setGroup(null);
7854
- },
7855
- children: [t("ov.range.all"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7856
- className: "lc-ov-chip-n",
7857
- children: scoped.length
7858
- })]
7859
- }), chips.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7860
- type: "button",
7861
- className: "lc-ov-chip" + (group === c.key ? " lc-ov-chip-on" : ""),
7862
- onClick: () => {
7863
- setGroup(group === c.key ? null : c.key);
7864
- },
7865
- children: [c.key === "__ungrouped__" ? t("ov.group.ungrouped") : c.key, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7866
- className: "lc-ov-chip-n",
7867
- children: c.count
7868
- })]
7869
- }, c.key))]
7870
- }),
7871
- visible.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7872
- className: "lc-empty",
7873
- children: t(allRows.length === 0 ? "ov.list.empty" : "ov.list.noMatch")
7874
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7875
- className: "lc-ov-grid",
7876
- children: paged.items.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewCard, {
7877
- row,
7878
- ...groups?.[row.id] !== void 0 ? { group: groups[row.id] } : {},
7879
- costLabel: cardCostOf(row, prices, currency),
7880
- now,
7881
- onOpen: openOne
7882
- }, row.id))
7883
- }), paged.count > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7884
- className: "lc-ov-pager",
7885
- role: "navigation",
7886
- "aria-label": t("ov.list.pager"),
7887
- children: [
7888
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8160
+ children: [t("ov.list.dayFilter", { day }), " ×"]
8161
+ }),
8162
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
8163
+ className: "lc-ov-search",
8164
+ type: "search",
8165
+ value: query,
8166
+ placeholder: t("ov.list.search"),
8167
+ "aria-label": t("ov.list.search"),
8168
+ onChange: (ev) => {
8169
+ setQuery(ev.target.value);
8170
+ }
8171
+ }),
8172
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8173
+ className: "lc-gran",
8174
+ role: "group",
8175
+ "aria-label": t("ov.list.sortLabel"),
8176
+ children: SORTS.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8177
+ type: "button",
8178
+ className: "lc-gran-btn" + (sort === s ? " lc-gran-on" : ""),
8179
+ onClick: () => {
8180
+ setSort(s);
8181
+ },
8182
+ children: t("ov.list.sort." + s)
8183
+ }, s))
8184
+ })
8185
+ ]
8186
+ }),
8187
+ chips.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8188
+ className: "lc-ov-groups",
8189
+ role: "group",
8190
+ "aria-label": t("ov.group.label"),
8191
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7889
8192
  type: "button",
7890
- className: "lc-ov-pager-btn",
7891
- disabled: paged.index === 0,
7892
- "aria-label": t("ov.list.prev"),
8193
+ className: "lc-ov-chip" + (group === null ? " lc-ov-chip-on" : ""),
7893
8194
  onClick: () => {
7894
- setPage(paged.index - 1);
8195
+ setGroup(null);
7895
8196
  },
7896
- children: "‹"
7897
- }),
7898
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7899
- className: "lc-ov-pager-n",
7900
- children: t("ov.list.page", {
7901
- n: paged.index + 1,
7902
- total: paged.count
7903
- })
7904
- }),
7905
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8197
+ children: [t("ov.range.all"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8198
+ className: "lc-ov-chip-n",
8199
+ children: scoped.length
8200
+ })]
8201
+ }), chips.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
7906
8202
  type: "button",
7907
- className: "lc-ov-pager-btn",
7908
- disabled: paged.index === paged.count - 1,
7909
- "aria-label": t("ov.list.next"),
8203
+ className: "lc-ov-chip" + (group === c.key ? " lc-ov-chip-on" : ""),
7910
8204
  onClick: () => {
7911
- setPage(paged.index + 1);
8205
+ setGroup(group === c.key ? null : c.key);
7912
8206
  },
7913
- children: "›"
7914
- })
7915
- ]
7916
- })] })
7917
- ]
7918
- })]
7919
- })
7920
- ] })]
8207
+ children: [c.key === "__ungrouped__" ? t("ov.group.ungrouped") : c.key, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8208
+ className: "lc-ov-chip-n",
8209
+ children: c.count
8210
+ })]
8211
+ }, c.key))]
8212
+ }),
8213
+ visible.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8214
+ className: "lc-empty",
8215
+ children: t(allRows.length === 0 ? "ov.list.empty" : "ov.list.noMatch")
8216
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
8217
+ className: "lc-ov-grid",
8218
+ children: paged.items.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewCard, {
8219
+ row,
8220
+ ...groups?.[row.id] !== void 0 ? { group: groups[row.id] } : {},
8221
+ costLabel: cardCostOf(row, book, currency),
8222
+ now,
8223
+ onOpen: openOne
8224
+ }, row.id))
8225
+ }), paged.count > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8226
+ className: "lc-ov-pager",
8227
+ role: "navigation",
8228
+ "aria-label": t("ov.list.pager"),
8229
+ children: [
8230
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8231
+ type: "button",
8232
+ className: "lc-ov-pager-btn",
8233
+ disabled: paged.index === 0,
8234
+ "aria-label": t("ov.list.prev"),
8235
+ onClick: () => {
8236
+ setPage(paged.index - 1);
8237
+ },
8238
+ children: "‹"
8239
+ }),
8240
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8241
+ className: "lc-ov-pager-n",
8242
+ children: t("ov.list.page", {
8243
+ n: paged.index + 1,
8244
+ total: paged.count
8245
+ })
8246
+ }),
8247
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8248
+ type: "button",
8249
+ className: "lc-ov-pager-btn",
8250
+ disabled: paged.index === paged.count - 1,
8251
+ "aria-label": t("ov.list.next"),
8252
+ onClick: () => {
8253
+ setPage(paged.index + 1);
8254
+ },
8255
+ children: "›"
8256
+ })
8257
+ ]
8258
+ })] })
8259
+ ]
8260
+ })]
8261
+ })
8262
+ ]
8263
+ })]
7921
8264
  })
7922
8265
  });
7923
8266
  }
@@ -7926,9 +8269,9 @@ window.__ModuleLoader__.load({
7926
8269
  };
7927
8270
  }
7928
8271
  /** One card's priced cost label, or the dash (no book yet, nothing billed, unpriceable model). */
7929
- function cardCostOf(row, prices, currency) {
8272
+ function cardCostOf(row, book, currency) {
7930
8273
  if (row.timeline?.cost === void 0) return "—";
7931
- const cost = estimateSessionCost(row.timeline.cost, prices, currency);
8274
+ const cost = estimateSessionCost(row.timeline.cost, book, currency);
7932
8275
  return cost === null ? "—" : formatCost(cost, currency);
7933
8276
  }
7934
8277
  //#endregion
@@ -10296,7 +10639,7 @@ window.__ModuleLoader__.load({
10296
10639
  });
10297
10640
  }
10298
10641
  //#endregion
10299
- //#region node_modules/.pnpm/simple-icons@16.31.0/node_modules/simple-icons/index.mjs
10642
+ //#region node_modules/.pnpm/simple-icons@16.32.0/node_modules/simple-icons/index.mjs
10300
10643
  const c = "<svg role=\"img\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><title>";
10301
10644
  const t = "</title><path d=\"";
10302
10645
  const a = "\"/></svg>";
@@ -10404,7 +10747,7 @@ window.__ModuleLoader__.load({
10404
10747
  return function PluginInfo() {
10405
10748
  const [latest, setLatest] = (0, react.useState)(null);
10406
10749
  (0, react.useEffect)(() => {
10407
- if ("0.57.0".includes("-dev")) return;
10750
+ if ("0.59.0".includes("-dev")) return;
10408
10751
  let on = true;
10409
10752
  fetchLatestVersion().then((v) => {
10410
10753
  if (on && v) setLatest(v);
@@ -10413,8 +10756,8 @@ window.__ModuleLoader__.load({
10413
10756
  on = false;
10414
10757
  };
10415
10758
  }, []);
10416
- const update = latest !== null && isNewerVersion(latest, "0.57.0") ? latest : null;
10417
- const nameText = "dsh-context (v0.57.0)";
10759
+ const update = latest !== null && isNewerVersion(latest, "0.59.0") ? latest : null;
10760
+ const nameText = "dsh-context (v0.59.0)";
10418
10761
  const nameValue = [nameText];
10419
10762
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10420
10763
  className: "lc-pi-update",
@@ -10887,8 +11230,8 @@ window.__ModuleLoader__.load({
10887
11230
  * with an off-peak bucket (DeepSeek's period-based list) shows the
10888
11231
  * peak | off-peak pair.
10889
11232
  */
10890
- function priceRowsOf(usage, prices) {
10891
- if (usage === void 0 || prices === null) return [];
11233
+ function priceRowsOf(usage, book) {
11234
+ if (usage === void 0 || book === null) return [];
10892
11235
  const rows = [];
10893
11236
  const multi = Object.keys(usage).length > 1;
10894
11237
  for (const provider of Object.keys(usage)) {
@@ -10898,7 +11241,7 @@ window.__ModuleLoader__.load({
10898
11241
  reaches here; the guard stays for the helper's own contract. */
10899
11242
  if (models === null) continue;
10900
11243
  for (const model of Object.keys(models)) {
10901
- const rate = priceOf(prices, provider, model);
11244
+ const rate = priceOf(book, provider, model);
10902
11245
  if (rate === null) continue;
10903
11246
  const periods = asRecord(models[model]);
10904
11247
  const deepseek = isDeepSeekProvider(provider);
@@ -10964,18 +11307,18 @@ window.__ModuleLoader__.load({
10964
11307
  const { t, fmt } = kit;
10965
11308
  return function StatsContext(props) {
10966
11309
  const currency = props.locale === "zh" ? "cny" : "usd";
10967
- const { prices, failed } = useModelPrices();
11310
+ const { book, failed } = useModelPrices();
10968
11311
  const subUsage = useSubagentCost(props.sessionId);
10969
11312
  const usage = mergeCostUsage(props.cost, subUsage) ?? void 0;
10970
- const cost = estimateSessionCost(usage, prices, currency);
10971
- const subCost = estimateSessionCost(subUsage, prices, currency);
11313
+ const cost = estimateSessionCost(usage, book, currency);
11314
+ const subCost = estimateSessionCost(subUsage, book, currency);
10972
11315
  const fmtRate = (usd) => formatPriceRate(toCurrency(usd, currency), currency);
10973
- const rows = priceRowsOf(usage, prices);
11316
+ const rows = priceRowsOf(usage, book);
10974
11317
  const deepseek = usage !== void 0 && Object.keys(usage).some((p) => isDeepSeekProvider(p));
10975
11318
  const subDeepseek = subUsage !== null && Object.keys(subUsage).some((p) => isDeepSeekProvider(p));
10976
11319
  const anyPair = rows.some((r) => r.offRate !== void 0);
10977
- const unpriced = rows.length === 0 && usage !== void 0 && Object.keys(usage).length > 0 && (failed || prices !== null);
10978
- const subUnpriced = subUsage !== null && priceRowsOf(subUsage, prices).length === 0 && (failed || prices !== null);
11320
+ const unpriced = rows.length === 0 && usage !== void 0 && Object.keys(usage).length > 0 && (failed || book !== null);
11321
+ const subUnpriced = subUsage !== null && priceRowsOf(subUsage, book).length === 0 && (failed || book !== null);
10979
11322
  const costTip = [
10980
11323
  t("stats.costTip") + (deepseek ? " " + t("stats.costTipDeepseek") : ""),
10981
11324
  rows.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
@@ -12021,7 +12364,7 @@ window.__ModuleLoader__.load({
12021
12364
  }
12022
12365
  //#endregion
12023
12366
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/trendChart.css.mjs
12024
- const css$9 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:color-mix(in srgb, var(--color-neutral-500) 50%, transparent);top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-chart-tip span+span{color:var(--dsw-alias-label-secondary)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:color-mix(in srgb, var(--color-neutral-500) 14%, transparent)}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-stack,.lc-bar-up,.lc-bar-down{transform-origin:50% 100%}.lc-bar-down{transform-origin:50% 0}.lc-bar-stack>div:first-child{border-radius:0 0 2px 2px}.lc-bar-stack>div:last-child{border-radius:2px 2px 0 0}.lc-bar-stack>div:only-child{border-radius:2px}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-up>div:last-child{border-radius:2px 2px 0 0}.lc-bar-down>div:last-child{border-radius:0 0 2px 2px}.lc-cat-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=skill] .lc-cat-seg[data-cat=skill],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1;filter:brightness(1.18)}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}";
12367
+ const css$9 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding:18px 2px 0;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:color-mix(in srgb, var(--color-neutral-500) 50%, transparent);top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-chart-tip span+span{color:var(--dsw-alias-label-secondary)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:color-mix(in srgb, var(--color-neutral-500) 14%, transparent)}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-stack,.lc-bar-up,.lc-bar-down{transform-origin:50% 100%}.lc-bar-down{transform-origin:50% 0}.lc-bar-stack>div:first-child{border-radius:0 0 2px 2px}.lc-bar-stack>div:last-child{border-radius:2px 2px 0 0}.lc-bar-stack>div:only-child{border-radius:2px}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-up>div:last-child{border-radius:2px 2px 0 0}.lc-bar-down>div:last-child{border-radius:0 0 2px 2px}.lc-cat-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=skill] .lc-cat-seg[data-cat=skill],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1;filter:brightness(1.18)}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-step-flag{background:color-mix(in srgb, var(--color-neutral-500) 45%, transparent);pointer-events:none;width:1px;position:absolute;top:-17px;bottom:0;right:-1px}.lc-step-flag-label{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--color-neutral-500) 45%, transparent);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:3px;padding:1px 3px;font-size:9px;font-weight:600;line-height:1;position:absolute;top:0;right:0}.lc-turns{box-sizing:border-box;gap:2px;width:max-content;min-width:100%;margin-top:4px;padding:0 2px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}";
12025
12368
  const tagId$9 = "dsh-context/trendChart.css";
12026
12369
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$9) + "]") === null) {
12027
12370
  const tag = document.createElement("style");
@@ -12120,7 +12463,7 @@ window.__ModuleLoader__.load({
12120
12463
  }
12121
12464
  //#endregion
12122
12465
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/overview.css.mjs
12123
- const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);height:calc(100% - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:inherit;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex}.lc-ov-balance:hover .lc-ov-balance-value{text-decoration:underline}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-kpis{flex:none;grid-template-columns:repeat(6,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}@container lc-ov (width<=1000px){.lc-ov-kpis{grid-template-columns:repeat(3,minmax(0,1fr))}}@container lc-ov (width<=640px){.lc-ov-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}}.lc-ov-stats{flex:none;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-stats>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}@container lc-ov (width<=700px){.lc-ov-stats{grid-template-columns:minmax(0,1fr)}}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-ov-settings{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex:none;align-items:center;gap:8px;padding:9px 12px;font-size:12px;display:flex}.lc-ov-settings:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.lc-ov-settings-label{white-space:nowrap;flex:none;align-items:center;gap:6px;display:inline-flex}.lc-ov-settings-hint{text-align:right;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-heat-wrap{flex-direction:column;flex:1;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:8px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:18px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{height:20px;color:var(--dsw-alias-label-secondary);text-align:center;white-space:nowrap;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:18px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-heat-legend{justify-content:flex-end;align-items:center;gap:3px;padding-top:6px;display:flex}.lc-heat-key{color:var(--dsw-alias-label-secondary);font-size:9px}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
12466
+ const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);height:calc(100% - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:inherit;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex}.lc-ov-balance:hover .lc-ov-balance-value{text-decoration:underline}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-scroll{scrollbar-gutter:stable;flex:auto;min-height:0;padding-right:8px;overflow-y:auto}.lc-ov-first{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-kpis{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;display:grid}.lc-ov-kpi{align-items:center;padding:8px}.lc-ov-kpi .lc-stat-value{font-size:20px}@container lc-ov (width<=932px){.lc-ov-first{grid-template-columns:minmax(0,1fr)}}@container lc-ov (width<=640px){.lc-ov-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}}.lc-ov-usage{box-sizing:border-box;flex-direction:column;flex:auto;min-width:0;margin-bottom:0;display:flex}.lc-ov-usage .lc-card-title{margin-bottom:4px}.lc-ov-usage-plot{flex:auto;align-items:stretch;gap:6px;min-height:0;display:flex}.lc-ov-usage-rail{width:42px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:9px;line-height:1.2;position:relative}.lc-ov-usage-rail-l .lc-ov-usage-tick{right:0}.lc-ov-usage-rail-r .lc-ov-usage-tick{left:0}.lc-ov-usage-tick{white-space:nowrap;position:absolute;transform:translateY(-50%)}.lc-ov-usage-grid{flex:auto;min-width:0;min-height:56px;display:flex;position:relative}.lc-ov-usage-line{background:color-mix(in srgb, var(--dsw-alias-border-l2) 70%, transparent);height:1px;position:absolute;left:0;right:0}.lc-ov-usage-line-0{background:var(--dsw-alias-border-l2)}.lc-ov-usage-cols{flex:auto;align-items:stretch;gap:6px;min-width:0;display:flex;position:relative}.lc-ov-usage-col{flex-direction:column;flex:1 1 0;min-width:0;display:flex}.lc-ov-usage-bars{flex:auto;justify-content:center;align-items:flex-end;gap:3px;width:100%;display:flex}.lc-ov-usage-bar{border-radius:3px 3px 0 0;flex:none;width:10px}.lc-ov-usage-tokens{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-ov-usage-cost{background:color-mix(in srgb, var(--color-green-500) 72%, transparent)}.lc-ov-usage-days{flex:none;gap:6px;padding-top:3px;display:flex}.lc-ov-usage-day{text-align:center;min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;flex:1 1 0;font-size:9px;line-height:1.2}.lc-ov-usage-key{flex:none;justify-content:flex-end;align-items:center;gap:5px;padding-top:5px;display:flex}.lc-ov-usage-swatch{border-radius:2px;flex:none;width:10px;height:8px}.lc-ov-usage-key-label{color:var(--dsw-alias-label-secondary);font-size:9px}.lc-ov-stats{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:10px;display:grid}.lc-ov-stats>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}@container lc-ov (width<=700px){.lc-ov-stats{grid-template-columns:minmax(0,1fr)}}.lc-ov-body{grid-template-columns:minmax(0,3fr) minmax(0,7fr);align-items:start;gap:10px;display:grid}.lc-ov-left{flex-direction:column;gap:10px;min-width:0;display:flex}.lc-ov-right{min-width:0}@container lc-ov (width<=932px){.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-heat-card{flex-direction:column;display:flex}.lc-ov-settings{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex:none;align-items:center;gap:8px;padding:9px 12px;font-size:12px;display:flex}.lc-ov-settings:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.lc-ov-settings-label{white-space:nowrap;flex:none;align-items:center;gap:6px;display:inline-flex}.lc-ov-settings-hint{text-align:right;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-heat-wrap{flex-direction:column;flex:1;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:8px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:18px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{height:20px;color:var(--dsw-alias-label-secondary);text-align:center;white-space:nowrap;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:18px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-heat-legend{justify-content:flex-end;align-items:center;gap:3px;padding-top:6px;display:flex}.lc-heat-key{color:var(--dsw-alias-label-secondary);font-size:9px}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
12124
12467
  const tagId = "dsh-context/overview.css";
12125
12468
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
12126
12469
  const tag = document.createElement("style");