dsh-context 0.54.0 → 0.54.1

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
@@ -56,6 +56,8 @@ window.__ModuleLoader__.load({
56
56
  "timing.callTimes": "{n}次",
57
57
  "timing.toolTimes": "{n}次",
58
58
  "timing.empty": "暂无耗时数据 · 对话开始后这里会显示时长分布",
59
+ "timing.tps": "{tps} tok/s",
60
+ "timing.tpsTip": "输出速度(TPS):累计解码输出 tokens ÷ 累计解码时长,与 DeepSeek Harness 会话统计同口径。",
59
61
  "tokens.title": "Token 统计",
60
62
  "tokens.total": "总用量",
61
63
  "tokens.output": "输出",
@@ -320,6 +322,7 @@ window.__ModuleLoader__.load({
320
322
  "ov.title": "上下文洞察",
321
323
  "ov.unavailable": "会话列表暂不可用",
322
324
  "ov.range.label": "统计范围",
325
+ "ov.range.24h": "近 24 小时",
323
326
  "ov.range.7d": "近 7 天",
324
327
  "ov.range.30d": "近 30 天",
325
328
  "ov.range.all": "全部",
@@ -330,7 +333,6 @@ window.__ModuleLoader__.load({
330
333
  "ov.kpi.toolSub": "工具执行 {dur}",
331
334
  "ov.kpi.wallSub": "模型调用 {n}",
332
335
  "balance.title": "DeepSeek 余额",
333
- "balance.tip.total": "总余额",
334
336
  "balance.tip.granted": "赠送余额",
335
337
  "balance.tip.toppedUp": "充值余额",
336
338
  "ov.heat.title": "活跃度",
@@ -426,6 +428,8 @@ window.__ModuleLoader__.load({
426
428
  "timing.callTimes": "{n} calls",
427
429
  "timing.toolTimes": "{n} runs",
428
430
  "timing.empty": "No timing data yet — durations appear as the conversation runs",
431
+ "timing.tps": "{tps} tok/s",
432
+ "timing.tpsTip": "Output speed (TPS): summed decoded output tokens over summed decode time — the same pairing as the harness session stats.",
429
433
  "tokens.title": "Token Stats",
430
434
  "tokens.total": "Total",
431
435
  "tokens.output": "Output",
@@ -690,6 +694,7 @@ window.__ModuleLoader__.load({
690
694
  "ov.title": "Context Insights",
691
695
  "ov.unavailable": "The session list is unavailable",
692
696
  "ov.range.label": "Range",
697
+ "ov.range.24h": "Last 24 hours",
693
698
  "ov.range.7d": "Last 7 days",
694
699
  "ov.range.30d": "Last 30 days",
695
700
  "ov.range.all": "All",
@@ -700,7 +705,6 @@ window.__ModuleLoader__.load({
700
705
  "ov.kpi.toolSub": "tool runs {dur}",
701
706
  "ov.kpi.wallSub": "{n} model calls",
702
707
  "balance.title": "DeepSeek balance",
703
- "balance.tip.total": "Total balance",
704
708
  "balance.tip.granted": "Granted balance",
705
709
  "balance.tip.toppedUp": "Topped-up balance",
706
710
  "ov.heat.title": "Activity",
@@ -5978,6 +5982,7 @@ window.__ModuleLoader__.load({
5978
5982
  }
5979
5983
  /** The range window's start instant (epoch ms), or null for "all". */
5980
5984
  function rangeStartOf(range, now) {
5985
+ if (range === "24h") return now - 864e5;
5981
5986
  if (range === "7d") return now - 6048e5;
5982
5987
  if (range === "30d") return now - 2592e6;
5983
5988
  return null;
@@ -6209,8 +6214,8 @@ window.__ModuleLoader__.load({
6209
6214
  //#endregion
6210
6215
  //#region src/client/balance.ts
6211
6216
  const BALANCE_ROUTE = "/api/dsh-context/balance";
6212
- /** How long a settled answer (including a `null`) serves identical reads. */
6213
- const TTL_MS$1 = 6e4;
6217
+ /** Where the last live figure is remembered across opens, per browser. */
6218
+ const STORAGE_KEY = "dsh-context:platform-balance";
6214
6219
  /** One platform amount ('110.00', or an already-numeric producer variant), or null. */
6215
6220
  function amountOf(value) {
6216
6221
  const n = typeof value === "string" ? Number(value) : value;
@@ -6260,45 +6265,86 @@ window.__ModuleLoader__.load({
6260
6265
  return balance.balances.find((entry) => entry.currency === want) ?? balance.balances[0];
6261
6266
  }
6262
6267
  let cached$1 = null;
6268
+ /** The route read in flight, so a second open joins it instead of starting one. */
6269
+ let refreshing = null;
6270
+ /** The storage this module reads and writes; swapped in tests. */
6271
+ let storage;
6272
+ function storageFn() {
6273
+ if (storage !== void 0) return storage;
6274
+ try {
6275
+ const found = globalThis.localStorage;
6276
+ storage = found === null || found === void 0 ? null : found;
6277
+ } catch {
6278
+ storage = null;
6279
+ }
6280
+ return storage;
6281
+ }
6282
+ /** The stored figure, or `null` when absent, unreadable, or no longer a valid balance. */
6283
+ function storedBalance() {
6284
+ try {
6285
+ const raw = storageFn()?.getItem(STORAGE_KEY);
6286
+ return raw === null || raw === void 0 ? null : platformBalanceOf(JSON.parse(raw));
6287
+ } catch {
6288
+ return null;
6289
+ }
6290
+ }
6291
+ function rememberBalance(value) {
6292
+ try {
6293
+ storageFn()?.setItem(STORAGE_KEY, JSON.stringify(value));
6294
+ } catch {}
6295
+ }
6296
+ /** One route POST, narrowed through `platformBalanceOf`. Never rejects. */
6297
+ async function readRoute() {
6298
+ try {
6299
+ const response = await fetch(BALANCE_ROUTE, {
6300
+ method: "POST",
6301
+ headers: { "content-type": "application/json" }
6302
+ });
6303
+ if (!response.ok) return null;
6304
+ const r = asRecord(await response.json());
6305
+ if (r === null || r.ok !== true || r.value === null || r.value === void 0) return null;
6306
+ return platformBalanceOf(r.value);
6307
+ } catch {
6308
+ return null;
6309
+ }
6310
+ }
6263
6311
  /**
6264
- * The capsule's read: one route POST, narrowed through
6265
- * `platformBalanceOf`, deduplicated in flight, TTL-cached. Never rejects.
6266
- */
6267
- function fetchPlatformBalance() {
6268
- if (cached$1 !== null && Date.now() - cached$1.at < TTL_MS$1) return cached$1.promise;
6269
- const at = Date.now();
6270
- const promise = (async () => {
6271
- try {
6272
- const response = await fetch(BALANCE_ROUTE, {
6273
- method: "POST",
6274
- headers: { "content-type": "application/json" }
6275
- });
6276
- if (!response.ok) return null;
6277
- const r = asRecord(await response.json());
6278
- if (r === null || r.ok !== true || r.value === null || r.value === void 0) return null;
6279
- return platformBalanceOf(r.value);
6280
- } catch {
6281
- return null;
6282
- }
6283
- })();
6284
- cached$1 = {
6285
- at,
6286
- promise
6287
- };
6288
- return promise;
6312
+ * One route read per open, with `onRefresh` once it lands on a figure: the
6313
+ * capsule paints the remembered total immediately and settles on the live
6314
+ * one. `refreshing` is assigned before anything awaits, so an open arriving
6315
+ * while a read is still in flight joins it rather than starting a second one;
6316
+ * it paints the remembered figure and takes the in-flight read's landing for
6317
+ * its own. A failed or absent read notifies nobody and keeps serving the
6318
+ * remembered figure, so an offline open never trades a number for a blank
6319
+ * pill; `null` (whether pending, absent, or failed) means nothing to show yet.
6320
+ */
6321
+ function readPlatformBalance(onRefresh) {
6322
+ if (refreshing === null) refreshing = (async () => {
6323
+ const value = await readRoute();
6324
+ if (value === null) return;
6325
+ cached$1 = value;
6326
+ rememberBalance(value);
6327
+ onRefresh(value);
6328
+ })().finally(() => {
6329
+ refreshing = null;
6330
+ });
6331
+ return cached$1 ?? storedBalance();
6289
6332
  }
6290
6333
  //#endregion
6291
6334
  //#region src/client/components/balanceCapsule.tsx
6292
6335
  /**
6293
6336
  * BalanceCapsule — the DeepSeek open-platform balance pill in the Context
6294
6337
  * Dashboard's header (the plugin's one account-level figure, beside the
6295
- * session-level ones). Mounted only by the dashboard header; it reads the
6296
- * plugin's balance route once per open through client/balance.ts and renders
6297
- * NOTHING while the read is pending, absent, or failed — the pill exists
6298
- * only when there is a live figure to show. The tooltip carries the
6299
- * breakdown (total / granted / topped-up) in the entry's own currency; the
6300
- * entry follows the active locale (zh → CNY) with the account's first
6301
- * currency as the fallback.
6338
+ * session-level ones). Clicking it opens the platform's usage console in a
6339
+ * new tab. Mounted only by the dashboard header; it paints the figure the
6340
+ * previous open remembered while client/balance.ts revalidates it in the
6341
+ * background, and renders NOTHING when nothing is remembered and the route
6342
+ * answers nothing — the pill exists only when there is a figure to show. The
6343
+ * non-zero parts of the breakdown (topped-up / granted) ride the harness
6344
+ * Tooltip primitive — immediate on hover, where a native `title` lags — in
6345
+ * the entry's own currency (the pill itself carries the total); the entry
6346
+ * follows the active locale (zh → CNY) with the account's first currency as
6347
+ * the fallback.
6302
6348
  */
6303
6349
  /** The symbol prefix for the currencies the platform serves; others show their code. */
6304
6350
  function symbolOf(currency) {
@@ -6306,31 +6352,25 @@ window.__ModuleLoader__.load({
6306
6352
  if (currency === "USD") return "$";
6307
6353
  return currency + " ";
6308
6354
  }
6355
+ /** The platform console page behind the capsule's click — usage and top-ups. */
6356
+ const USAGE_URL = "https://platform.deepseek.com/usage";
6309
6357
  function makeBalanceCapsule(ctx, kit) {
6310
6358
  const { t } = kit;
6311
6359
  return function BalanceCapsule() {
6312
- const [balance, setBalance] = (0, react.useState)(null);
6313
- (0, react.useEffect)(() => {
6314
- let on = true;
6315
- fetchPlatformBalance().then((v) => {
6316
- if (on) setBalance(v);
6317
- });
6318
- return () => {
6319
- on = false;
6320
- };
6321
- }, []);
6360
+ const [balance, setBalance] = (0, react.useState)(() => readPlatformBalance((v) => {
6361
+ setBalance(v);
6362
+ }));
6322
6363
  const locale = ctx.locale;
6323
6364
  const entry = balanceEntryOf(balance, (typeof locale.getLocale === "function" ? locale.getLocale().active : "en") === "zh" ? "cny" : "usd");
6324
6365
  if (entry === null) return null;
6325
6366
  const money = (amount) => symbolOf(entry.currency) + amount.toFixed(2);
6326
- const tip = [
6327
- t("balance.tip.total") + ": " + money(entry.total),
6328
- t("balance.tip.granted") + ": " + money(entry.granted),
6329
- t("balance.tip.toppedUp") + ": " + money(entry.toppedUp)
6330
- ].join("\n");
6331
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6367
+ const tipLines = [...entry.toppedUp > 0 ? [t("balance.tip.toppedUp") + ": " + money(entry.toppedUp)] : [], ...entry.granted > 0 ? [t("balance.tip.granted") + ": " + money(entry.granted)] : []];
6368
+ const pill = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
6332
6369
  className: "lc-ov-balance",
6333
- title: tip,
6370
+ "aria-label": tipLines.length > 0 ? tipLines.join("\n") : void 0,
6371
+ href: USAGE_URL,
6372
+ target: "_blank",
6373
+ rel: "noreferrer",
6334
6374
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6335
6375
  className: "lc-ov-balance-label",
6336
6376
  children: t("balance.title")
@@ -6339,6 +6379,11 @@ window.__ModuleLoader__.load({
6339
6379
  children: money(entry.total)
6340
6380
  })]
6341
6381
  });
6382
+ return tipLines.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
6383
+ label: tipLines.join("\n"),
6384
+ side: "bottom",
6385
+ children: pill
6386
+ }) : pill;
6342
6387
  };
6343
6388
  }
6344
6389
  //#endregion
@@ -6790,6 +6835,7 @@ window.__ModuleLoader__.load({
6790
6835
  * closes the panel.
6791
6836
  */
6792
6837
  const RANGES = [
6838
+ "24h",
6793
6839
  "7d",
6794
6840
  "30d",
6795
6841
  "all"
@@ -9639,7 +9685,7 @@ window.__ModuleLoader__.load({
9639
9685
  return function PluginInfo() {
9640
9686
  const [latest, setLatest] = (0, react.useState)(null);
9641
9687
  (0, react.useEffect)(() => {
9642
- if ("0.54.0".includes("-dev")) return;
9688
+ if ("0.54.1".includes("-dev")) return;
9643
9689
  let on = true;
9644
9690
  fetchLatestVersion().then((v) => {
9645
9691
  if (on && v) setLatest(v);
@@ -9648,8 +9694,8 @@ window.__ModuleLoader__.load({
9648
9694
  on = false;
9649
9695
  };
9650
9696
  }, []);
9651
- const update = latest !== null && isNewerVersion(latest, "0.54.0") ? latest : null;
9652
- const nameText = "dsh-context (v0.54.0)";
9697
+ const update = latest !== null && isNewerVersion(latest, "0.54.1") ? latest : null;
9698
+ const nameText = "dsh-context (v0.54.1)";
9653
9699
  const nameValue = [nameText];
9654
9700
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9655
9701
  className: "lc-pi-update",
@@ -10380,6 +10426,14 @@ window.__ModuleLoader__.load({
10380
10426
  tools: "var(--color-teal-500)",
10381
10427
  other: "var(--color-slate-400)"
10382
10428
  };
10429
+ /**
10430
+ * The decode-throughput figure, formatted exactly as the harness's own
10431
+ * session-stats seat formats it: whole tokens from ten up, one decimal below.
10432
+ */
10433
+ function formatTokensPerSecond(tps) {
10434
+ const clamped = Math.max(0, tps);
10435
+ return clamped >= 10 ? String(Math.round(clamped)) : String(Math.round(clamped * 10) / 10);
10436
+ }
10383
10437
  function makeStatsTiming(kit, Donut) {
10384
10438
  const { t, fmt, fmtDuration, fmtShare } = kit;
10385
10439
  const SliceList = makeSliceList(kit);
@@ -10387,6 +10441,7 @@ window.__ModuleLoader__.load({
10387
10441
  const [hoverKey, setHoverKey] = (0, react.useState)(null);
10388
10442
  const timing = props.timing;
10389
10443
  const wall = timing !== null && Number.isFinite(timing.wallMs) && timing.wallMs > 0 ? timing.wallMs : 0;
10444
+ const tps = timing !== null && typeof timing.speedMs === "number" && Number.isFinite(timing.speedMs) && timing.speedMs > 0 && typeof timing.speedTokens === "number" && Number.isFinite(timing.speedTokens) && timing.speedTokens >= 0 ? formatTokensPerSecond(timing.speedTokens / (timing.speedMs / 1e3)) : null;
10390
10445
  let segments = [];
10391
10446
  let rows = [];
10392
10447
  if (timing !== null && (wall > 0 || timing.calls > 0 || timing.toolCalls > 0)) {
@@ -10495,12 +10550,16 @@ window.__ModuleLoader__.load({
10495
10550
  }
10496
10551
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10497
10552
  className: "lc-card lc-col-stats lc-col-donut flex-1 min-w-[min(360px,100%)]",
10498
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10553
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10499
10554
  className: "lc-card-title",
10500
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10555
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10501
10556
  className: "lc-card-title-text",
10502
10557
  children: t("timing.title")
10503
- })
10558
+ }), tps !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10559
+ className: "lc-timing-tps",
10560
+ title: t("timing.tpsTip"),
10561
+ children: t("timing.tps", { tps })
10562
+ })]
10504
10563
  }), rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10505
10564
  className: "lc-empty",
10506
10565
  children: t("timing.empty")
@@ -11429,7 +11488,7 @@ window.__ModuleLoader__.load({
11429
11488
  }
11430
11489
  //#endregion
11431
11490
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/stats.css.mjs
11432
- const css$13 = ".lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;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);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:color-mix(in srgb, var(--color-neutral-500) 18%, transparent)}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:4px 20px;min-width:0;display:grid;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-hint{text-decoration:none}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-pi-row-btn .lc-pi-value{font-weight:400}";
11491
+ const css$13 = ".lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;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);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-timing-tps{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);font-variant-numeric:tabular-nums;cursor:default;border-radius:999px;flex:none;margin-left:auto;padding:1px 8px;font-size:12px;line-height:18px}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:color-mix(in srgb, var(--color-neutral-500) 18%, transparent)}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:4px 20px;min-width:0;display:grid;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-hint{text-decoration:none}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-pi-row-btn .lc-pi-value{font-weight:400}";
11433
11492
  const tagId$13 = "dsh-context/stats.css";
11434
11493
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
11435
11494
  const tag = document.createElement("style");
@@ -11572,7 +11631,7 @@ window.__ModuleLoader__.load({
11572
11631
  }
11573
11632
  //#endregion
11574
11633
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/overview.css.mjs
11575
- 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);max-height:min(86vh,900px);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);cursor:default;border-radius:999px;flex:none;align-items:center;gap:6px;max-width:100%;padding:2px 10px;font-size:12px;line-height:18px;display:inline-flex}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:13px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;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:13px;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-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}";
11634
+ 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);max-height:min(86vh,900px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:inherit;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex}.lc-ov-balance:hover .lc-ov-balance-value{text-decoration:underline}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:13px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;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:13px;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-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}";
11576
11635
  const tagId = "dsh-context/overview.css";
11577
11636
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
11578
11637
  const tag = document.createElement("style");
package/lib/index.d.ts CHANGED
@@ -638,6 +638,17 @@ interface TimingTotals {
638
638
  textMs?: number;
639
639
  /** Tool-call-argument decode slice of `genMs`. */
640
640
  toolArgMs?: number;
641
+ /**
642
+ * The decode-throughput seat, paired exactly as the harness's own
643
+ * session-stats fold pairs them: `speedTokens` sums provider-reported
644
+ * output tokens and `speedMs` the first-token → assistant-message
645
+ * windows, over the calls that carried BOTH a first-token stamp and a
646
+ * usage report — a subset of `genMs`, which counts every token-stamped
647
+ * call regardless of usage. Additive-optional: cached rows written before
648
+ * the seat existed lack them, and the card falls back to no chip.
649
+ */
650
+ speedTokens?: number;
651
+ speedMs?: number;
641
652
  /** Completed model calls (assistant messages folded). */
642
653
  calls: number;
643
654
  /** Summed per-call durations of completed tool calls. */
@@ -800,7 +811,9 @@ interface ContextHeaders {
800
811
  interface PlatformBalanceEntry {
801
812
  /** The ISO code the platform reported (`CNY` / `USD`). */
802
813
  currency: string;
803
- /** Total available funds: the granted and topped-up balances summed. */
814
+ /** Total available funds: `granted` + `toppedUp`. Derived here rather than read
815
+ * from the platform's own `total_balance`, which rounds independently of its
816
+ * parts and can land a cent away from what the breakdown beside it shows. */
804
817
  total: number;
805
818
  /** The not-expired granted (gift) balance. */
806
819
  granted: number;
package/lib/index.js CHANGED
@@ -1430,11 +1430,12 @@ function applyTimeline(state, event, bounds) {
1430
1430
  };
1431
1431
  if (data && typeof data.turn === "number") record.turn = data.turn;
1432
1432
  if (data && typeof data.step === "number") record.step = data.step;
1433
+ let output = null;
1433
1434
  if (usage !== null && typeof usage === "object") {
1434
1435
  const input = tokenCountOf(usage.inputTokens);
1435
1436
  const cacheRead = tokenCountOf(usage.cacheReadTokens);
1436
1437
  const cacheWrite = tokenCountOf(usage.cacheWriteTokens);
1437
- const output = tokenCountOf(usage.outputTokens);
1438
+ output = tokenCountOf(usage.outputTokens);
1438
1439
  if (input !== null || cacheRead !== null || cacheWrite !== null || output !== null) {
1439
1440
  record.prompt = (input ?? 0) + (cacheRead ?? 0) + (cacheWrite ?? 0);
1440
1441
  if (cacheRead !== null) record.cacheRead = cacheRead;
@@ -1456,6 +1457,10 @@ function applyTimeline(state, event, bounds) {
1456
1457
  if (firstToken !== void 0) {
1457
1458
  timing.ttftMs += durOf(stepStart.time, firstToken);
1458
1459
  timing.genMs += durOf(firstToken, event.time);
1460
+ if (output !== null) {
1461
+ timing.speedMs = (timing.speedMs ?? 0) + durOf(firstToken, event.time);
1462
+ timing.speedTokens = (timing.speedTokens ?? 0) + output;
1463
+ }
1459
1464
  if (stepStart.decode !== void 0) {
1460
1465
  const decode = { ...stepStart.decode };
1461
1466
  if (stepStart.block !== void 0) decode[stepStart.block.kind] += durOf(stepStart.block.since, event.time);
@@ -2066,9 +2071,6 @@ const DEEPSEEK_SETTINGS_NS = "llm-deepseek";
2066
2071
  const DEFAULT_API_KEY_ENV = "DEEPSEEK_API_KEY";
2067
2072
  /** The platform's public API root (llm-deepseek's PUBLIC_BASE_URL default). */
2068
2073
  const PUBLIC_BASE_URL = "https://api.deepseek.com";
2069
- /** A settled read serves identical reads for this long; failures decay faster. */
2070
- const CACHE_TTL_MS = 3e5;
2071
- const CACHE_FAIL_TTL_MS = 6e4;
2072
2074
  /** One platform read's whole budget — never worth blocking the route longer. */
2073
2075
  const FETCH_TIMEOUT_MS = 1e4;
2074
2076
  /** Narrow an unknown value to a string-keyed record, or null. */
@@ -2109,9 +2111,12 @@ async function resolveFacts(ctx) {
2109
2111
  }
2110
2112
  /**
2111
2113
  * Narrow the platform's payload to the wire value (the boundary rigor every
2112
- * parser owes untrusted input): each entry's currency and three amounts are
2113
- * re-proved, an entry failing the shape drops whole, and a payload with no
2114
- * valid entry is no balance at all.
2114
+ * parser owes untrusted input): each entry's currency and parts are re-proved
2115
+ * and the total is the two summed — the platform's own `total_balance` rounds
2116
+ * independently of them, so it can land a cent away from what the breakdown
2117
+ * beside it adds up to, and the viewer's arithmetic is the authority. An entry
2118
+ * failing the shape drops whole, and a payload with no valid entry is no
2119
+ * balance at all.
2115
2120
  */
2116
2121
  function balanceOfPayload(value) {
2117
2122
  const data = asRecord$1(value);
@@ -2122,13 +2127,12 @@ function balanceOfPayload(value) {
2122
2127
  const entry = asRecord$1(info);
2123
2128
  if (entry === null) continue;
2124
2129
  const currency = entry.currency;
2125
- const total = amountOf(entry.total_balance);
2126
2130
  const granted = amountOf(entry.granted_balance);
2127
2131
  const toppedUp = amountOf(entry.topped_up_balance);
2128
- if (typeof currency !== "string" || currency === "" || total === null || granted === null || toppedUp === null) continue;
2132
+ if (typeof currency !== "string" || currency === "" || granted === null || toppedUp === null) continue;
2129
2133
  balances.push({
2130
2134
  currency,
2131
- total,
2135
+ total: granted + toppedUp,
2132
2136
  granted,
2133
2137
  toppedUp
2134
2138
  });
@@ -2143,18 +2147,17 @@ function balanceOfPayload(value) {
2143
2147
  /** The outbound platform fetch through the real global fetch. */
2144
2148
  const defaultFetcher = (url, init) => fetch(url, init);
2145
2149
  let fetcher = defaultFetcher;
2146
- let cache = null;
2147
2150
  let inFlight = null;
2148
2151
  /**
2149
- * One platform read for these facts, shared across identical concurrent
2150
- * reads and remembered briefly: successes for CACHE_TTL_MS, failures for
2151
- * CACHE_FAIL_TTL_MS (a blip must not pin the capsule away for the full
2152
- * window). Every failure path — transport, timeout, non-ok status, malformed
2153
- * payload — resolves `null`; the route never throws into the transport.
2152
+ * One platform read for these facts, shared by the identical reads already in
2153
+ * flight. Each read that finds none goes to the platform: the account's
2154
+ * balance moves with every request the harness bills, so a remembered figure
2155
+ * would disagree with the console page the capsule links to. The client's own
2156
+ * open-time read is the rate limit. Every failure path — transport, timeout,
2157
+ * non-ok status, malformed payload — resolves `null`; the route never throws
2158
+ * into the transport.
2154
2159
  */
2155
2160
  async function readBalance(facts) {
2156
- const key = facts.baseUrl + "\0" + facts.apiKey;
2157
- if (cache !== null && cache.key === key && Date.now() - cache.at < (cache.value === null ? CACHE_FAIL_TTL_MS : CACHE_TTL_MS)) return cache.value;
2158
2161
  if (inFlight !== null) return inFlight;
2159
2162
  inFlight = (async () => {
2160
2163
  let value = null;
@@ -2168,11 +2171,6 @@ async function readBalance(facts) {
2168
2171
  });
2169
2172
  if (response.ok) value = balanceOfPayload(await response.json());
2170
2173
  } catch {}
2171
- cache = {
2172
- key,
2173
- at: Date.now(),
2174
- value
2175
- };
2176
2174
  return value;
2177
2175
  })();
2178
2176
  const settled = inFlight;
@@ -2891,6 +2889,8 @@ const timingTotalsSchema = z.object({
2891
2889
  reasoningMs: z.number().nonnegative().optional(),
2892
2890
  textMs: z.number().nonnegative().optional(),
2893
2891
  toolArgMs: z.number().nonnegative().optional(),
2892
+ speedTokens: z.number().nonnegative().optional(),
2893
+ speedMs: z.number().nonnegative().optional(),
2894
2894
  calls: z.number().int().nonnegative(),
2895
2895
  toolsMs: z.number().nonnegative(),
2896
2896
  toolCalls: z.number().int().nonnegative(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.54.0",
3
+ "version": "0.54.1",
4
4
  "description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {