@mars-sea/dsh-commandcode-provider 0.12.10 → 0.13.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
@@ -2766,6 +2766,7 @@ window.__ModuleLoader__.load({
2766
2766
  //#endregion
2767
2767
  //#region src/client/locales.ts
2768
2768
  const zh = {
2769
+ openAnalytics: "查看模型用量、成本与性能分析",
2769
2770
  nav: "Command Code",
2770
2771
  title: "Command Code",
2771
2772
  intro: "账户操作立即生效;其余设置修改后点击底部「保存」。API 密钥仅保存在本机凭据服务中,不会回显。",
@@ -2872,7 +2873,7 @@ window.__ModuleLoader__.load({
2872
2873
  usageNetworkError: "无法连接 Command Code 服务",
2873
2874
  usageNetworkHint: "所有请求都没有到达服务端。请检查网络连接或 API 地址设置。",
2874
2875
  usageInvalidResponse: "Command Code 响应无法读取",
2875
- usageInvalidResponseHint: "请求已收到响应,但内容无法解析。请检查宿主的 HTTP 代理或响应解压配置。",
2876
+ usageInvalidResponseHint: "已收到响应,但正文读取或解析失败。下方详情可区分读取超时、中断与格式异常;可先点击刷新重试。",
2876
2877
  usageUpdated: "更新于",
2877
2878
  usagePeriodEnd: "账期截止",
2878
2879
  usageActive: "当前使用",
@@ -2940,6 +2941,7 @@ window.__ModuleLoader__.load({
2940
2941
  integrationsTitle: "集成与显示"
2941
2942
  };
2942
2943
  const en = {
2944
+ openAnalytics: "View model usage, cost and performance",
2943
2945
  nav: "Command Code",
2944
2946
  title: "Command Code",
2945
2947
  intro: "Account actions apply immediately; other settings are written by Save at the bottom. The API key stays in the local credential service and is never echoed.",
@@ -3046,7 +3048,7 @@ window.__ModuleLoader__.load({
3046
3048
  usageNetworkError: "Could not reach the Command Code service",
3047
3049
  usageNetworkHint: "No request reached the server. Check your network connection or the API base setting.",
3048
3050
  usageInvalidResponse: "Could not read the Command Code response",
3049
- usageInvalidResponseHint: "The requests received responses, but their bodies could not be parsed. Check the host HTTP proxy or response decoding.",
3051
+ usageInvalidResponseHint: "Responses arrived, but reading or parsing their bodies failed. Details below distinguish timeouts, interrupted reads, and invalid formats. Try Refresh again.",
3050
3052
  usageUpdated: "Updated",
3051
3053
  usagePeriodEnd: "Period ends",
3052
3054
  usageActive: "Active",
@@ -3273,7 +3275,7 @@ window.__ModuleLoader__.load({
3273
3275
  }
3274
3276
  //#endregion
3275
3277
  //#region package.json
3276
- var version = "0.12.10";
3278
+ var version = "0.13.1";
3277
3279
  var repository = {
3278
3280
  "type": "git",
3279
3281
  "url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
@@ -5232,6 +5234,12 @@ window.__ModuleLoader__.load({
5232
5234
  className: "cc-intro",
5233
5235
  children: t("intro")
5234
5236
  }),
5237
+ props.openAnalytics ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5238
+ variant: "ghost",
5239
+ size: "sm",
5240
+ onClick: props.openAnalytics,
5241
+ children: t("openAnalytics")
5242
+ }) : null,
5235
5243
  !state.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5236
5244
  className: "cc-readOnly",
5237
5245
  role: "status",
@@ -5544,11 +5552,215 @@ window.__ModuleLoader__.load({
5544
5552
  });
5545
5553
  }
5546
5554
  //#endregion
5555
+ //#region src/client/analytics-copy.ts
5556
+ /** 模型分析沿用宿主语言;每个数值有独立标签,缺失样本与试算前提必须可见。 */
5557
+ const ANALYTICS_COPY_ZH = {
5558
+ analyticsNav: "模型分析",
5559
+ quotaNav: "账号额度",
5560
+ analysisRecent: "最近会话",
5561
+ analysisRange: "指定时间",
5562
+ analysisLimit: "会话数量",
5563
+ analysisFrom: "开始日期",
5564
+ analysisTo: "结束日期",
5565
+ analysisApply: "读取统计",
5566
+ analysisLoading: "正在读取历史记录…",
5567
+ analysisUnavailable: "宿主未提供历史查询服务,无法统计关闭后的会话。",
5568
+ analysisEmpty: "所选范围内没有 Command Code 请求记录。",
5569
+ analysisInvalid: "请输入 1–100 个会话,或不超过 366 天的有效日期范围。",
5570
+ analysisScope: "统计范围",
5571
+ analysisScanned: "已读取会话",
5572
+ analysisMatched: "含请求会话",
5573
+ analysisUnreadable: "读取失败",
5574
+ analysisSkipped: "已跳过",
5575
+ analysisPartial: "结果不完整:历史超过读取上限,或部分记录不可读。金额只覆盖已读取且可计价的请求。",
5576
+ analysisCatalog: "模型目录暂不可用,历史统计仍可查看。",
5577
+ analysisUnpriced: "部分请求缺少用量或价格,金额与词元仅为已知小计。展开明细可查看样本覆盖。",
5578
+ analysisOverview: "用量总览",
5579
+ analysisSections: "分析内容",
5580
+ analysisHistory: "历史用量",
5581
+ analysisSimulation: "价格试算",
5582
+ analysisByModel: "模型用量比较",
5583
+ analysisModelHint: "比例条表示占已知成本小计的份额;展开查看明细。",
5584
+ analysisIntro: "查看历史用量,比较模型成本与响应表现。",
5585
+ analysisModel: "模型",
5586
+ analysisCostShare: "已知成本 / 占比",
5587
+ analysisNoShare: "无占比",
5588
+ analysisShareHint: "占全部已知成本小计",
5589
+ analysisSimulationHeading: "相同工作量价格试算",
5590
+ analysisSimulationHint: "计价覆盖不同的金额不能直接比较。",
5591
+ analysisSimulationCost: "试算成本",
5592
+ analysisCoverage: "计价覆盖",
5593
+ analysisUsd: "美元",
5594
+ analysisTokenUnit: "词元",
5595
+ analysisCost: "已知成本 · 美元",
5596
+ analysisRequests: "模型请求",
5597
+ analysisSessionCount: "会话数",
5598
+ analysisResponse: "平均响应",
5599
+ analysisFirstToken: "平均首词元",
5600
+ analysisSpeed: "输出速度",
5601
+ analysisSeconds: "秒",
5602
+ analysisSpeedUnit: "词元/秒",
5603
+ analysisPriced: "计价请求",
5604
+ analysisTimed: "响应样本",
5605
+ analysisFirstTokenSamples: "首词元样本",
5606
+ analysisDecodeSamples: "解码样本",
5607
+ analysisUsageSamples: "用量样本",
5608
+ analysisAverage: "每会话均摊",
5609
+ analysisMissing: "无数据",
5610
+ analysisDetails: "查看汇总明细",
5611
+ analysisTokens: "词元与缓存",
5612
+ analysisInput: "未缓存输入",
5613
+ analysisOutput: "输出",
5614
+ analysisCacheRead: "缓存读取",
5615
+ analysisCacheWrite: "缓存写入",
5616
+ analysisOutcome: "请求状态",
5617
+ analysisCompleted: "完成",
5618
+ analysisFailed: "失败",
5619
+ analysisRetries: "宿主重试",
5620
+ analysisCancelled: "取消",
5621
+ analysisUnknown: "未知状态",
5622
+ analysisSamples: "统计样本",
5623
+ analysisSessionCost: "会话与成本",
5624
+ analysisMonthly: "按该时间段折算 30 天",
5625
+ analysisMonthlyHint: "仅完整、全部计价且跨度至少一天的日期范围提供折算。",
5626
+ analysisRules: "统计口径与试算说明",
5627
+ analysisShortBasis: "按当前价格估算,非历史账单。缺失用量与耗时不会补零。",
5628
+ analysisBasis: "按当前价格快照重算;不是历史账单,不含订阅费和充值,词元用量不等于账号积分。仅有请求数量、没有任务边界;不计算任务质量或智能评分。旧记录缺失的用量和耗时不会补零。",
5629
+ analysisAssumption: "沿用原请求的词元与时段,对比不同模型的价格。换模型后的生成长度、缓存命中、重试和质量可能变化;不会调用模型或自动推荐。",
5630
+ analysisContext: "上下文词元",
5631
+ analysisCapabilities: "模型能力",
5632
+ analysisEfforts: "推理力度",
5633
+ analysisExcluded: "超出上下文",
5634
+ analysisUnknownPrice: "未知用量或价格",
5635
+ analysisSearch: "筛选试算模型",
5636
+ analysisSearchPlaceholder: "输入模型名称或标识",
5637
+ analysisNoMatches: "没有匹配的模型,请调整筛选条件。",
5638
+ analysisImages: "支持图片",
5639
+ analysisTextOnly: "仅文本",
5640
+ analysisNoEfforts: "未提供推理力度",
5641
+ analysisCoverageHint: "耗时从步骤或重试开始计至模型结算;包含前置处理,不含后续工具执行。首词元包含推理与工具参数。速度按有解码计时的输出词元总量计算。试算未校验图片及工具兼容性,也不代表账号已获授权。",
5642
+ effortNone: "关闭 (none)",
5643
+ effortLow: "低 (low)",
5644
+ effortMedium: "中 (medium)",
5645
+ effortHigh: "高 (high)",
5646
+ effortXhigh: "极高 (xhigh)",
5647
+ effortMax: "最高 (max)"
5648
+ };
5649
+ /** 按项目约定,错误、缺失与读取状态在各语言环境中统一为中文。 */
5650
+ const ANALYTICS_COPY_EN = {
5651
+ analyticsNav: "Model analysis",
5652
+ quotaNav: "Account quota",
5653
+ analysisRecent: "Recent sessions",
5654
+ analysisRange: "Date range",
5655
+ analysisLimit: "Session count",
5656
+ analysisFrom: "Start date",
5657
+ analysisTo: "End date",
5658
+ analysisApply: "Read statistics",
5659
+ analysisLoading: ANALYTICS_COPY_ZH.analysisLoading,
5660
+ analysisUnavailable: ANALYTICS_COPY_ZH.analysisUnavailable,
5661
+ analysisEmpty: ANALYTICS_COPY_ZH.analysisEmpty,
5662
+ analysisInvalid: ANALYTICS_COPY_ZH.analysisInvalid,
5663
+ analysisScope: "Scope",
5664
+ analysisScanned: "Sessions read",
5665
+ analysisMatched: "Sessions with requests",
5666
+ analysisUnreadable: "Read failures",
5667
+ analysisSkipped: "Skipped",
5668
+ analysisPartial: ANALYTICS_COPY_ZH.analysisPartial,
5669
+ analysisCatalog: ANALYTICS_COPY_ZH.analysisCatalog,
5670
+ analysisUnpriced: ANALYTICS_COPY_ZH.analysisUnpriced,
5671
+ analysisOverview: "Usage overview",
5672
+ analysisSections: "Analysis sections",
5673
+ analysisHistory: "Recorded usage",
5674
+ analysisSimulation: "Price simulation",
5675
+ analysisByModel: "Model usage comparison",
5676
+ analysisModelHint: "Bars show each share of the known cost subtotal; expand for details.",
5677
+ analysisIntro: "Review recorded usage and compare model cost and response times.",
5678
+ analysisModel: "Model",
5679
+ analysisCostShare: "Known cost / share",
5680
+ analysisNoShare: ANALYTICS_COPY_ZH.analysisNoShare,
5681
+ analysisShareHint: "Share of the known cost subtotal",
5682
+ analysisSimulationHeading: "Price simulation for the same workload",
5683
+ analysisSimulationHint: "Amounts with different priced coverage are not directly comparable.",
5684
+ analysisSimulationCost: "Simulated cost",
5685
+ analysisCoverage: "Priced coverage",
5686
+ analysisUsd: "USD",
5687
+ analysisTokenUnit: "tokens",
5688
+ analysisCost: "Known cost · USD",
5689
+ analysisRequests: "Model requests",
5690
+ analysisSessionCount: "Sessions",
5691
+ analysisResponse: "Mean response",
5692
+ analysisFirstToken: "Mean first token",
5693
+ analysisSpeed: "Output speed",
5694
+ analysisSeconds: "s",
5695
+ analysisSpeedUnit: "tokens/s",
5696
+ analysisPriced: "Priced requests",
5697
+ analysisTimed: "Response samples",
5698
+ analysisFirstTokenSamples: "First-token samples",
5699
+ analysisDecodeSamples: "Decode samples",
5700
+ analysisUsageSamples: "Usage samples",
5701
+ analysisAverage: "Per session",
5702
+ analysisMissing: ANALYTICS_COPY_ZH.analysisMissing,
5703
+ analysisDetails: "View aggregate details",
5704
+ analysisTokens: "Tokens and cache",
5705
+ analysisInput: "Uncached input",
5706
+ analysisOutput: "Output",
5707
+ analysisCacheRead: "Cache read",
5708
+ analysisCacheWrite: "Cache write",
5709
+ analysisOutcome: "Request outcomes",
5710
+ analysisCompleted: "Completed",
5711
+ analysisFailed: "Failed",
5712
+ analysisRetries: "Host retries",
5713
+ analysisCancelled: "Cancelled",
5714
+ analysisUnknown: "Unknown outcome",
5715
+ analysisSamples: "Samples",
5716
+ analysisSessionCost: "Sessions and cost",
5717
+ analysisMonthly: "30-day extrapolation from this range",
5718
+ analysisMonthlyHint: "Requires a complete, fully priced date range spanning at least one day.",
5719
+ analysisRules: "Statistics and simulation notes",
5720
+ analysisShortBasis: "Estimated at current prices, not an invoice. Missing usage and timing stay unknown.",
5721
+ analysisBasis: "Repriced with the current snapshot; not an invoice. Excludes subscriptions and top-ups. Tokens are not account credits. Requests have no task boundaries; no task quality or intelligence scores. Missing usage and timing stay unknown.",
5722
+ analysisAssumption: "Compares model prices using original tokens and request times. Output length, cache hits, retries and quality may change with another model. Makes no model calls or automatic recommendations.",
5723
+ analysisContext: "Context tokens",
5724
+ analysisCapabilities: "Model capabilities",
5725
+ analysisEfforts: "Reasoning efforts",
5726
+ analysisExcluded: "Over context limit",
5727
+ analysisUnknownPrice: "Unknown usage or price",
5728
+ analysisSearch: "Filter simulation models",
5729
+ analysisSearchPlaceholder: "Model name or ID",
5730
+ analysisNoMatches: ANALYTICS_COPY_ZH.analysisNoMatches,
5731
+ analysisImages: "Images supported",
5732
+ analysisTextOnly: "Text only",
5733
+ analysisNoEfforts: "No reasoning efforts listed",
5734
+ analysisCoverageHint: "Response time runs from step/retry start to model settlement, includes preparation and excludes subsequent tools. First token includes reasoning and tool arguments. Speed uses output tokens with decode timing. Simulation does not validate image/tool compatibility or prove account entitlement.",
5735
+ effortNone: "None",
5736
+ effortLow: "Low",
5737
+ effortMedium: "Medium",
5738
+ effortHigh: "High",
5739
+ effortXhigh: "Extra high",
5740
+ effortMax: "Max"
5741
+ };
5742
+ //#endregion
5547
5743
  //#region src/client/panel-copy.ts
5744
+ /**
5745
+ * Bilingual copy for the Command Code **plans & quota panel** (the sidebar
5746
+ * footer card and the center-column dashboard it opens).
5747
+ *
5748
+ * The panel follows the harness's active language: `PANEL_LOCALE_NS` is a
5749
+ * registered locale namespace (`ctx.locale.register`), both panel slot
5750
+ * registrations declare it, and the renderer hands the component a `t` seat
5751
+ * whose identity changes on a language switch (so a memoized panel re-renders).
5752
+ * {@link buildPanelView} takes that translator as an input and builds every
5753
+ * string through it, so the projection stays React-free and testable and no
5754
+ * component carries copy of its own. The settings page keeps its own
5755
+ * `settings.commandcode` namespace (see `./locales.ts`); the two never mix.
5756
+ *
5757
+ * @module dsh-commandcode-provider/client/panel-copy
5758
+ */
5548
5759
  /** The locale namespace both panel slots bind their `t` seat to. */
5549
5760
  const PANEL_LOCALE_NS = "panel.commandcode";
5550
5761
  /** The panel's Simplified Chinese string table (the key-set source of truth). */
5551
5762
  const PANEL_COPY_ZH = {
5763
+ ...ANALYTICS_COPY_ZH,
5552
5764
  nav: "Command Code",
5553
5765
  subtitle: "套餐、额度与配额窗口",
5554
5766
  refresh: "刷新",
@@ -5598,11 +5810,12 @@ window.__ModuleLoader__.load({
5598
5810
  errorNetwork: "无法连接 Command Code 服务",
5599
5811
  errorNetworkHint: "所有请求都没有到达服务端。请检查网络连接或 API 地址设置。",
5600
5812
  errorInvalidResponse: "Command Code 响应无法读取",
5601
- errorInvalidResponseHint: "请求已收到响应,但内容无法解析。请检查宿主的 HTTP 代理或响应解压配置。",
5813
+ errorInvalidResponseHint: "已收到响应,但正文读取或解析失败。下方详情可区分读取超时、中断与格式异常;可先点击刷新重试。",
5602
5814
  errorGeneric: "用量获取失败"
5603
5815
  };
5604
5816
  /** The panel's English string table (must mirror {@link PANEL_COPY_ZH}). */
5605
5817
  const PANEL_COPY_EN = {
5818
+ ...ANALYTICS_COPY_EN,
5606
5819
  nav: "Command Code",
5607
5820
  subtitle: "Plans, credits and quota windows",
5608
5821
  refresh: "Refresh",
@@ -5652,7 +5865,7 @@ window.__ModuleLoader__.load({
5652
5865
  errorNetwork: "Could not reach the Command Code service",
5653
5866
  errorNetworkHint: "No request reached the server. Check your network connection or the API base setting.",
5654
5867
  errorInvalidResponse: "Could not read the Command Code response",
5655
- errorInvalidResponseHint: "The requests received responses, but their bodies could not be parsed. Check the host HTTP proxy or response decoding.",
5868
+ errorInvalidResponseHint: "Responses arrived, but reading or parsing their bodies failed. Details below distinguish timeouts, interrupted reads, and invalid formats. Try Refresh again.",
5656
5869
  errorGeneric: "Could not fetch account usage"
5657
5870
  };
5658
5871
  /** Every panel key, in the English table's declaration order. */
@@ -5668,7 +5881,7 @@ window.__ModuleLoader__.load({
5668
5881
  //#endregion
5669
5882
  //#region src/client/panel.ts
5670
5883
  /** `$1.23`, and `$0.0123` only when the amount is too small for cents to show it. */
5671
- function money(value) {
5884
+ function money$1(value) {
5672
5885
  if (value === 0) return formatMoney(0);
5673
5886
  return Math.abs(value) < .01 ? formatMoneyExact(value) : formatMoney(value);
5674
5887
  }
@@ -5711,7 +5924,7 @@ window.__ModuleLoader__.load({
5711
5924
  const capped = limit.cap > 0;
5712
5925
  return {
5713
5926
  label,
5714
- value: capped ? `${money(limit.used)} / ${money(limit.cap)}` : money(limit.used),
5927
+ value: capped ? `${money$1(limit.used)} / ${money$1(limit.cap)}` : money$1(limit.used),
5715
5928
  capped,
5716
5929
  percent,
5717
5930
  barPercent: barPercent(percent),
@@ -5747,11 +5960,11 @@ window.__ModuleLoader__.load({
5747
5960
  const percent = known ? rawPercent(used, limitValue) : 0;
5748
5961
  return {
5749
5962
  known,
5750
- limit: limitValue !== null ? money(limitValue) : UNREPORTED,
5751
- used: known ? money(used) : UNREPORTED,
5752
- remaining: remainingReported ? money(remaining) : UNREPORTED,
5753
- purchased: credits !== void 0 && credits.purchasedReported !== false ? money(Math.max(0, credits.purchasedCredits)) : UNREPORTED,
5754
- free: credits !== void 0 && credits.freeReported !== false ? money(Math.max(0, credits.freeCredits)) : UNREPORTED,
5963
+ limit: limitValue !== null ? money$1(limitValue) : UNREPORTED,
5964
+ used: known ? money$1(used) : UNREPORTED,
5965
+ remaining: remainingReported ? money$1(remaining) : UNREPORTED,
5966
+ purchased: credits !== void 0 && credits.purchasedReported !== false ? money$1(Math.max(0, credits.purchasedCredits)) : UNREPORTED,
5967
+ free: credits !== void 0 && credits.freeReported !== false ? money$1(Math.max(0, credits.freeCredits)) : UNREPORTED,
5755
5968
  percent,
5756
5969
  barPercent: barPercent(percent),
5757
5970
  exhausted: known && remaining <= 0,
@@ -5881,11 +6094,11 @@ window.__ModuleLoader__.load({
5881
6094
  percent: `${percent}%`,
5882
6095
  barPercent: barPercent(percent),
5883
6096
  warn: window.exceeded,
5884
- detail: `${money(window.used)} / ${money(window.cap)}`
6097
+ detail: `${money$1(window.used)} / ${money$1(window.cap)}`
5885
6098
  });
5886
6099
  }
5887
6100
  const totalCost = selectedEntry?.report.usage?.totalCost;
5888
- const cost = totalCost === void 0 ? "" : money(totalCost);
6101
+ const cost = totalCost === void 0 ? "" : money$1(totalCost);
5889
6102
  let status = "";
5890
6103
  if (entries.length > 0 && !entries.some((entry) => entry.configured)) status = t("unconfigured");
5891
6104
  else if (selectedView?.mark !== void 0 && selectedView.mark !== "active") status = t(selectedView.mark);
@@ -5975,6 +6188,795 @@ window.__ModuleLoader__.load({
5975
6188
  };
5976
6189
  }
5977
6190
  //#endregion
6191
+ //#region src/analytics-wire.ts
6192
+ const g = makeBoundaryValidator("commandcode/analytics");
6193
+ const nonnegative = (s, k) => {
6194
+ const n = g.numberField(s, k, k);
6195
+ return n >= 0 ? n : g.reject(k);
6196
+ };
6197
+ const count$1 = (s, k) => {
6198
+ const n = nonnegative(s, k);
6199
+ return Number.isSafeInteger(n) ? n : g.reject(k);
6200
+ };
6201
+ function parseAnalyticsQuery(value) {
6202
+ const s = g.record(value, "query");
6203
+ if (s.mode === "recent") {
6204
+ const limit = count$1(s, "limit");
6205
+ if (limit < 1 || limit > 100) return g.reject("limit");
6206
+ return {
6207
+ mode: "recent",
6208
+ limit
6209
+ };
6210
+ }
6211
+ if (s.mode !== "range") return g.reject("mode");
6212
+ const from = count$1(s, "from"), to = count$1(s, "to");
6213
+ if (to <= from || to - from > 316224e5 || to > 864e13) return g.reject("range");
6214
+ return {
6215
+ mode: "range",
6216
+ from,
6217
+ to
6218
+ };
6219
+ }
6220
+ const counts = [
6221
+ "sessions",
6222
+ "requests",
6223
+ "completed",
6224
+ "failed",
6225
+ "cancelled",
6226
+ "unknown",
6227
+ "retries",
6228
+ "usageSamples",
6229
+ "pricedRequests",
6230
+ "latencySamples",
6231
+ "ttftSamples",
6232
+ "decodeSamples"
6233
+ ];
6234
+ const quantities = [
6235
+ "cost",
6236
+ "latencyMs",
6237
+ "ttftMs",
6238
+ "decodeMs",
6239
+ "decodeTokens"
6240
+ ];
6241
+ const tokenKeys = [
6242
+ "uncachedInputTokens",
6243
+ "outputTokens",
6244
+ "cacheReadTokens",
6245
+ "cacheWriteTokens"
6246
+ ];
6247
+ function metrics(value) {
6248
+ const s = g.record(value, "metrics"), result = {};
6249
+ for (const k of counts) result[k] = count$1(s, k);
6250
+ for (const k of quantities) result[k] = nonnegative(s, k);
6251
+ const tokens = g.record(s.tokens, "tokens");
6252
+ result.tokens = Object.fromEntries(tokenKeys.map((k) => [k, nonnegative(tokens, k)]));
6253
+ if (result.completed + result.failed + result.cancelled + result.unknown !== result.requests || result.pricedRequests > result.usageSamples || result.usageSamples > result.requests || result.retries > result.requests || result.ttftSamples > result.requests || result.latencySamples > result.requests || result.decodeSamples > result.requests) return g.reject("coverage");
6254
+ return result;
6255
+ }
6256
+ const nullable = (s, k) => s[k] === null ? null : nonnegative(s, k);
6257
+ function parseAnalyticsReport(value) {
6258
+ const s = g.record(value, "report");
6259
+ if (s.status !== "ready" && s.status !== "unavailable") return g.reject("status");
6260
+ if (!Array.isArray(s.models) || !Array.isArray(s.scenarios) || s.models.length > 1e3 || s.scenarios.length > 1e3) return g.reject("models");
6261
+ return {
6262
+ generatedAt: count$1(s, "generatedAt"),
6263
+ pricingKey: count$1(s, "pricingKey"),
6264
+ query: parseAnalyticsQuery(s.query),
6265
+ status: s.status,
6266
+ scannedSessions: count$1(s, "scannedSessions"),
6267
+ matchedSessions: count$1(s, "matchedSessions"),
6268
+ unreadableSessions: count$1(s, "unreadableSessions"),
6269
+ skippedSessions: count$1(s, "skippedSessions"),
6270
+ truncated: g.booleanField(s, "truncated", "truncated"),
6271
+ catalogUnavailable: g.booleanField(s, "catalogUnavailable", "catalogUnavailable"),
6272
+ totals: metrics(s.totals),
6273
+ monthlyEstimate: nullable(s, "monthlyEstimate"),
6274
+ models: s.models.map((value) => {
6275
+ const m = g.record(value, "model");
6276
+ return {
6277
+ ...metrics(m),
6278
+ id: g.stringField(m, "id", "id")
6279
+ };
6280
+ }),
6281
+ scenarios: s.scenarios.map((value) => {
6282
+ const m = g.record(value, "scenario");
6283
+ if (!Array.isArray(m.efforts) || !m.efforts.every((v) => typeof v === "string")) return g.reject("efforts");
6284
+ return {
6285
+ id: g.stringField(m, "id", "id"),
6286
+ name: g.stringField(m, "name", "name"),
6287
+ contextWindow: nullable(m, "contextWindow"),
6288
+ images: g.booleanField(m, "images", "images"),
6289
+ efforts: m.efforts,
6290
+ cost: nonnegative(m, "cost"),
6291
+ pricedRequests: count$1(m, "pricedRequests"),
6292
+ excludedRequests: count$1(m, "excludedRequests"),
6293
+ unknownRequests: count$1(m, "unknownRequests")
6294
+ };
6295
+ })
6296
+ };
6297
+ }
6298
+ const ANALYTICS_REMOTE_CONTRIBUTION = {
6299
+ package: REMOTE_PACKAGE,
6300
+ descriptors: [{
6301
+ ...makeRemoteDescriptor("commandcode/analytics", "analytics", `${REMOTE_PACKAGE}#AnalyticsReport`, { parse: parseAnalyticsReport }),
6302
+ parameters: [{
6303
+ name: "query",
6304
+ wire: "query",
6305
+ source: "json",
6306
+ codec: makeStrictCodec(`${REMOTE_PACKAGE}#AnalyticsQuery`, { parse: parseAnalyticsQuery })
6307
+ }]
6308
+ }]
6309
+ };
6310
+ //#endregion
6311
+ //#region src/client/analytics-view.tsx
6312
+ /** 只展示数字快照;筛选提交后才查询,页签、展开与模型筛选不触发历史读取。 */
6313
+ function localDate(time) {
6314
+ const d = new Date(time);
6315
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
6316
+ }
6317
+ /** 日期按用户本地日历展开为半开区间;截止日包括当日全部时间,夏令时不固定加 24 小时。 */
6318
+ function dateRange(from, to) {
6319
+ const start = /* @__PURE__ */ new Date(`${from}T00:00:00`), end = /* @__PURE__ */ new Date(`${to}T00:00:00`);
6320
+ if (localDate(start.getTime()) !== from || localDate(end.getTime()) !== to) throw new TypeError("无效日期");
6321
+ end.setDate(end.getDate() + 1);
6322
+ return parseAnalyticsQuery({
6323
+ mode: "range",
6324
+ from: start.getTime(),
6325
+ to: end.getTime()
6326
+ });
6327
+ }
6328
+ const money = (n) => `$${n.toFixed(4)}`;
6329
+ const digits = (n) => n.toLocaleString();
6330
+ const meanSeconds = (ms, samples, text) => samples ? `${(ms / samples / 1e3).toFixed(2)} ${text("analysisSeconds")}` : text("analysisMissing");
6331
+ const speed = (m, text) => m.decodeSamples && m.decodeMs ? `${(m.decodeTokens * 1e3 / m.decodeMs).toFixed(1)} ${text("analysisSpeedUnit")}` : text("analysisMissing");
6332
+ function effortName(effort, text) {
6333
+ const keys = {
6334
+ none: "effortNone",
6335
+ low: "effortLow",
6336
+ medium: "effortMedium",
6337
+ high: "effortHigh",
6338
+ xhigh: "effortXhigh",
6339
+ max: "effortMax"
6340
+ };
6341
+ return keys[effort] ? text(keys[effort]) : effort;
6342
+ }
6343
+ function Chevron() {
6344
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
6345
+ className: "ccp-analysisChevron",
6346
+ width: "16",
6347
+ height: "16",
6348
+ viewBox: "0 0 24 24",
6349
+ fill: "none",
6350
+ stroke: "currentColor",
6351
+ strokeWidth: "1.6",
6352
+ strokeLinecap: "round",
6353
+ strokeLinejoin: "round",
6354
+ "aria-hidden": "true",
6355
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m9 5 7 7-7 7" })
6356
+ });
6357
+ }
6358
+ function AnalysisIcon({ kind }) {
6359
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
6360
+ className: "ccp-analysisIcon",
6361
+ width: "16",
6362
+ height: "16",
6363
+ viewBox: "0 0 24 24",
6364
+ fill: "none",
6365
+ stroke: "currentColor",
6366
+ strokeWidth: "1.6",
6367
+ strokeLinecap: "round",
6368
+ strokeLinejoin: "round",
6369
+ "aria-hidden": "true",
6370
+ children: kind === "info" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
6371
+ cx: "12",
6372
+ cy: "12",
6373
+ r: "9"
6374
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 11v6M12 7h.01" })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
6375
+ cx: "10.5",
6376
+ cy: "10.5",
6377
+ r: "6.5"
6378
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m16 16 4.5 4.5" })] })
6379
+ });
6380
+ }
6381
+ function Metric({ label, value, sub, children }) {
6382
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6383
+ className: "ccp-analysisMetric",
6384
+ children: [
6385
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6386
+ className: "ccp-analysisLabel ccp-analysisMetricLabel",
6387
+ children: label
6388
+ }),
6389
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6390
+ className: "ccp-analysisValue",
6391
+ children: value
6392
+ }),
6393
+ sub ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6394
+ className: "ccp-analysisSub",
6395
+ children: sub
6396
+ }) : null,
6397
+ children
6398
+ ]
6399
+ });
6400
+ }
6401
+ /** 只比较已知成本;没有计价样本或已知小计为零时,不制造百分比。 */
6402
+ function knownCostShare(m, totalCost) {
6403
+ return m.pricedRequests > 0 && totalCost > 0 ? m.cost / totalCost * 100 : null;
6404
+ }
6405
+ function CostShare({ m, totalCost, text }) {
6406
+ const share = knownCostShare(m, totalCost);
6407
+ if (share === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6408
+ className: "ccp-analysisSub",
6409
+ children: text("analysisNoShare")
6410
+ });
6411
+ const percentage = `${share.toFixed(1)}%`;
6412
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6413
+ className: "ccp-analysisShare",
6414
+ title: `${text("analysisShareHint")} ${percentage}`,
6415
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6416
+ className: "ccp-analysisShareTrack",
6417
+ "aria-hidden": "true",
6418
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6419
+ className: "ccp-analysisShareFill",
6420
+ style: { width: `${Math.min(100, Math.max(0, share))}%` }
6421
+ })
6422
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6423
+ className: "ccp-analysisShareValue",
6424
+ children: percentage
6425
+ })]
6426
+ });
6427
+ }
6428
+ function DetailGroup({ title, values, text }) {
6429
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6430
+ className: "ccp-analysisDetailGroup",
6431
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
6432
+ className: "ccp-analysisDetailTitle",
6433
+ children: text(title)
6434
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dl", {
6435
+ className: "ccp-analysisFacts",
6436
+ children: values.map(([key, value]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6437
+ className: "ccp-analysisFact",
6438
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
6439
+ className: "ccp-analysisLabel",
6440
+ children: text(key)
6441
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
6442
+ className: "ccp-analysisFactValue",
6443
+ children: value
6444
+ })]
6445
+ }, key))
6446
+ })]
6447
+ });
6448
+ }
6449
+ /** 样本为零时词元仍为未知;汇总和单模型复用相同口径,展开只改变展示。 */
6450
+ function MetricsDetails({ m, text }) {
6451
+ const tokens = m.tokens, tokenValue = (n) => m.usageSamples ? digits(n) : text("analysisMissing");
6452
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6453
+ className: "ccp-analysisDetails",
6454
+ children: [
6455
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailGroup, {
6456
+ title: "analysisTokens",
6457
+ text,
6458
+ values: [
6459
+ ["analysisInput", tokenValue(tokens.uncachedInputTokens)],
6460
+ ["analysisOutput", tokenValue(tokens.outputTokens)],
6461
+ ["analysisCacheRead", tokenValue(tokens.cacheReadTokens)],
6462
+ ["analysisCacheWrite", tokenValue(tokens.cacheWriteTokens)]
6463
+ ]
6464
+ }),
6465
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailGroup, {
6466
+ title: "analysisOutcome",
6467
+ text,
6468
+ values: [
6469
+ ["analysisCompleted", digits(m.completed)],
6470
+ ["analysisFailed", digits(m.failed)],
6471
+ ["analysisRetries", digits(m.retries)],
6472
+ ["analysisCancelled", digits(m.cancelled)],
6473
+ ["analysisUnknown", digits(m.unknown)]
6474
+ ]
6475
+ }),
6476
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailGroup, {
6477
+ title: "analysisSamples",
6478
+ text,
6479
+ values: [
6480
+ ["analysisUsageSamples", digits(m.usageSamples)],
6481
+ ["analysisPriced", digits(m.pricedRequests)],
6482
+ ["analysisTimed", digits(m.latencySamples)],
6483
+ ["analysisFirstTokenSamples", digits(m.ttftSamples)],
6484
+ ["analysisDecodeSamples", digits(m.decodeSamples)]
6485
+ ]
6486
+ }),
6487
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailGroup, {
6488
+ title: "analysisSessionCost",
6489
+ text,
6490
+ values: [
6491
+ ["analysisSessionCount", digits(m.sessions)],
6492
+ ["analysisRequests", digits(m.requests)],
6493
+ ["analysisAverage", m.sessions && m.pricedRequests ? money(m.cost / m.sessions) : text("analysisMissing")],
6494
+ ["analysisSpeed", speed(m, text)]
6495
+ ]
6496
+ })
6497
+ ]
6498
+ });
6499
+ }
6500
+ function ModelRow({ m, name, totalCost, text }) {
6501
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
6502
+ className: "ccp-analysisModelRow",
6503
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
6504
+ className: "ccp-analysisModelSummary",
6505
+ children: [
6506
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6507
+ className: "ccp-analysisIdentity",
6508
+ children: [
6509
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6510
+ className: "ccp-analysisModelName",
6511
+ children: name || m.id
6512
+ }),
6513
+ name && name !== m.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6514
+ className: "ccp-analysisSub",
6515
+ children: m.id
6516
+ }) : null,
6517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6518
+ className: "ccp-analysisSub ccp-analysisModelCount",
6519
+ children: [
6520
+ text("analysisRequests"),
6521
+ " ",
6522
+ digits(m.requests),
6523
+ " · ",
6524
+ text("analysisSessionCount"),
6525
+ " ",
6526
+ digits(m.sessions)
6527
+ ]
6528
+ })
6529
+ ]
6530
+ }),
6531
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6532
+ label: text("analysisCostShare"),
6533
+ value: m.pricedRequests ? money(m.cost) : text("analysisMissing"),
6534
+ sub: `${text("analysisPriced")} ${m.pricedRequests}/${m.requests}`,
6535
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CostShare, {
6536
+ m,
6537
+ totalCost,
6538
+ text
6539
+ })
6540
+ }),
6541
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6542
+ label: text("analysisResponse"),
6543
+ value: meanSeconds(m.latencyMs, m.latencySamples, text)
6544
+ }),
6545
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6546
+ label: text("analysisFirstToken"),
6547
+ value: meanSeconds(m.ttftMs, m.ttftSamples, text)
6548
+ }),
6549
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6550
+ label: text("analysisSpeed"),
6551
+ value: speed(m, text)
6552
+ }),
6553
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {})
6554
+ ]
6555
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MetricsDetails, {
6556
+ m,
6557
+ text
6558
+ })]
6559
+ });
6560
+ }
6561
+ function ScenarioRow({ m, text }) {
6562
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
6563
+ className: "ccp-analysisModelRow",
6564
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
6565
+ className: "ccp-analysisScenarioSummary",
6566
+ children: [
6567
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6568
+ className: "ccp-analysisIdentity",
6569
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6570
+ className: "ccp-analysisModelName",
6571
+ children: m.name
6572
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6573
+ className: "ccp-analysisSub",
6574
+ children: m.id
6575
+ })]
6576
+ }),
6577
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6578
+ label: text("analysisSimulationCost"),
6579
+ value: m.pricedRequests ? money(m.cost) : text("analysisMissing"),
6580
+ sub: text("analysisUsd")
6581
+ }),
6582
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6583
+ label: text("analysisCoverage"),
6584
+ value: `${m.pricedRequests}/${m.pricedRequests + m.excludedRequests + m.unknownRequests}`,
6585
+ sub: `${text("analysisExcluded")} ${digits(m.excludedRequests)} · ${text("analysisUnknownPrice")} ${digits(m.unknownRequests)}`
6586
+ }),
6587
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6588
+ label: text("analysisCapabilities"),
6589
+ value: m.contextWindow === null ? text("analysisMissing") : `${digits(m.contextWindow)} ${text("analysisTokenUnit")}`,
6590
+ sub: text(m.images ? "analysisImages" : "analysisTextOnly")
6591
+ }),
6592
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {})
6593
+ ]
6594
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6595
+ className: "ccp-analysisDetails ccp-analysisScenarioDetails",
6596
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailGroup, {
6597
+ title: "analysisSamples",
6598
+ text,
6599
+ values: [
6600
+ ["analysisPriced", digits(m.pricedRequests)],
6601
+ ["analysisExcluded", digits(m.excludedRequests)],
6602
+ ["analysisUnknownPrice", digits(m.unknownRequests)]
6603
+ ]
6604
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailGroup, {
6605
+ title: "analysisCapabilities",
6606
+ text,
6607
+ values: [["analysisEfforts", m.efforts.length ? m.efforts.map((e) => effortName(e, text)).join(", ") : text("analysisNoEfforts")]]
6608
+ })]
6609
+ })]
6610
+ });
6611
+ }
6612
+ function AnalyticsView(props) {
6613
+ const { text } = props, state = props.useCommandCodeAnalytics((s) => s), report = state.report;
6614
+ const [mode, setMode] = (0, react.useState)(report?.query.mode ?? "recent");
6615
+ const [limit, setLimit] = (0, react.useState)(report?.query.mode === "recent" ? String(report.query.limit) : "20");
6616
+ const [from, setFrom] = (0, react.useState)(localDate(report?.query.mode === "range" ? report.query.from : Date.now() - 25056e5));
6617
+ const [to, setTo] = (0, react.useState)(localDate(report?.query.mode === "range" ? report.query.to - 1 : Date.now()));
6618
+ const [invalid, setInvalid] = (0, react.useState)(false), [filter, setFilter] = (0, react.useState)("");
6619
+ const [page, setPage] = (0, react.useState)("history"), id = (0, react.useId)();
6620
+ const refresh = props.refreshAnalytics;
6621
+ (0, react.useEffect)(() => {
6622
+ if (state.status === "idle") refresh();
6623
+ }, [state.status, refresh]);
6624
+ const busy = state.status === "loading";
6625
+ const apply = () => {
6626
+ try {
6627
+ const query = mode === "recent" ? parseAnalyticsQuery({
6628
+ mode,
6629
+ limit: Number(limit)
6630
+ }) : dateRange(from, to);
6631
+ setInvalid(false);
6632
+ refresh(query);
6633
+ } catch {
6634
+ setInvalid(true);
6635
+ }
6636
+ };
6637
+ const navigateTab = (event) => {
6638
+ if (![
6639
+ "ArrowLeft",
6640
+ "ArrowRight",
6641
+ "Home",
6642
+ "End"
6643
+ ].includes(event.key)) return;
6644
+ event.preventDefault();
6645
+ const next = event.key === "Home" ? "history" : event.key === "End" ? "simulation" : page === "history" ? "simulation" : "history";
6646
+ setPage(next);
6647
+ event.currentTarget.querySelector(`[data-page="${next}"]`)?.focus();
6648
+ };
6649
+ const scenarios = report?.scenarios.filter((m) => `${m.id} ${m.name}`.toLowerCase().includes(filter.trim().toLowerCase())) ?? [];
6650
+ const names = new Map(report?.scenarios.map((m) => [m.id, m.name]));
6651
+ const m = report?.totals;
6652
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
6653
+ className: "ccp-analysis",
6654
+ "aria-label": text("analyticsNav"),
6655
+ "aria-busy": busy,
6656
+ children: [
6657
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
6658
+ className: "ccp-analysisFilters",
6659
+ "data-mode": mode,
6660
+ noValidate: true,
6661
+ onSubmit: (e) => {
6662
+ e.preventDefault();
6663
+ if (!busy) apply();
6664
+ },
6665
+ children: [
6666
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
6667
+ className: "ccp-analysisField ccp-analysisScopeField",
6668
+ children: [text("analysisScope"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6669
+ className: "ccp-analysisSelectShell",
6670
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
6671
+ className: "ccp-analysisInput ccp-analysisSelect",
6672
+ value: mode,
6673
+ disabled: busy,
6674
+ onChange: (e) => setMode(e.target.value),
6675
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
6676
+ value: "recent",
6677
+ children: text("analysisRecent")
6678
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
6679
+ value: "range",
6680
+ children: text("analysisRange")
6681
+ })]
6682
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {})]
6683
+ })]
6684
+ }),
6685
+ mode === "recent" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
6686
+ className: "ccp-analysisField",
6687
+ children: [text("analysisLimit"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6688
+ className: "ccp-analysisInput",
6689
+ type: "number",
6690
+ min: "1",
6691
+ max: "100",
6692
+ required: true,
6693
+ value: limit,
6694
+ disabled: busy,
6695
+ onChange: (e) => setLimit(e.target.value)
6696
+ })]
6697
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
6698
+ className: "ccp-analysisField ccp-analysisDateField",
6699
+ children: [text("analysisFrom"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6700
+ className: "ccp-analysisInput",
6701
+ type: "date",
6702
+ required: true,
6703
+ value: from,
6704
+ disabled: busy,
6705
+ onChange: (e) => setFrom(e.target.value)
6706
+ })]
6707
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
6708
+ className: "ccp-analysisField ccp-analysisDateField",
6709
+ children: [text("analysisTo"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6710
+ className: "ccp-analysisInput",
6711
+ type: "date",
6712
+ required: true,
6713
+ value: to,
6714
+ disabled: busy,
6715
+ onChange: (e) => setTo(e.target.value)
6716
+ })]
6717
+ })] }),
6718
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
6719
+ className: "ccp-analysisApply",
6720
+ type: "submit",
6721
+ disabled: busy,
6722
+ children: text("analysisApply")
6723
+ })
6724
+ ]
6725
+ }),
6726
+ invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6727
+ className: "ccp-analysisMessage ccp-analysisError",
6728
+ role: "alert",
6729
+ children: text("analysisInvalid")
6730
+ }) : null,
6731
+ busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6732
+ className: "ccp-analysisMessage",
6733
+ role: "status",
6734
+ children: text("analysisLoading")
6735
+ }) : null,
6736
+ state.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6737
+ className: "ccp-analysisMessage ccp-analysisError",
6738
+ role: "alert",
6739
+ children: state.error
6740
+ }) : null,
6741
+ report?.status === "unavailable" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6742
+ className: "ccp-analysisMessage",
6743
+ role: "status",
6744
+ children: text("analysisUnavailable")
6745
+ }) : null,
6746
+ report?.status === "ready" && m ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
6747
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6748
+ className: "ccp-analysisScope",
6749
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
6750
+ text("analysisScope"),
6751
+ ": ",
6752
+ report.query.mode === "recent" ? `${text("analysisRecent")} ${report.query.limit}` : `${localDate(report.query.from)} — ${localDate(report.query.to - 1)}`
6753
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
6754
+ text("analysisScanned"),
6755
+ " ",
6756
+ report.scannedSessions,
6757
+ " · ",
6758
+ text("analysisMatched"),
6759
+ " ",
6760
+ report.matchedSessions,
6761
+ " · ",
6762
+ text("analysisUnreadable"),
6763
+ " ",
6764
+ report.unreadableSessions,
6765
+ " · ",
6766
+ text("analysisSkipped"),
6767
+ " ",
6768
+ report.skippedSessions
6769
+ ] })]
6770
+ }),
6771
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6772
+ className: "ccp-analysisOverview",
6773
+ role: "group",
6774
+ "aria-label": text("analysisOverview"),
6775
+ children: [
6776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6777
+ label: text("analysisCost"),
6778
+ value: m.pricedRequests ? money(m.cost) : text("analysisMissing"),
6779
+ sub: `${text("analysisPriced")} ${m.pricedRequests}/${m.requests}`
6780
+ }),
6781
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6782
+ label: text("analysisRequests"),
6783
+ value: digits(m.requests),
6784
+ sub: `${text("analysisSessionCount")} ${digits(m.sessions)}`
6785
+ }),
6786
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6787
+ label: text("analysisResponse"),
6788
+ value: meanSeconds(m.latencyMs, m.latencySamples, text),
6789
+ sub: `${text("analysisTimed")} ${digits(m.latencySamples)}`
6790
+ }),
6791
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
6792
+ label: text("analysisFirstToken"),
6793
+ value: meanSeconds(m.ttftMs, m.ttftSamples, text),
6794
+ sub: `${text("analysisFirstTokenSamples")} ${digits(m.ttftSamples)}`
6795
+ })
6796
+ ]
6797
+ }),
6798
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6799
+ className: "ccp-analysisNote",
6800
+ children: text("analysisShortBasis")
6801
+ }),
6802
+ report.truncated || report.unreadableSessions > 0 || report.skippedSessions > 0 || m.pricedRequests < m.requests ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6803
+ className: "ccp-analysisMessage ccp-analysisWarning",
6804
+ role: "status",
6805
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnalysisIcon, { kind: "info" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [report.truncated || report.unreadableSessions > 0 || report.skippedSessions > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: text("analysisPartial") }) : null, m.pricedRequests < m.requests ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: text("analysisUnpriced") }) : null] })]
6806
+ }) : null,
6807
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6808
+ className: "ccp-analysisTabs",
6809
+ role: "tablist",
6810
+ "aria-label": text("analysisSections"),
6811
+ onKeyDown: navigateTab,
6812
+ children: ["history", "simulation"].map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
6813
+ className: "ccp-analysisTab",
6814
+ id: `${id}-${tab}-tab`,
6815
+ "data-page": tab,
6816
+ type: "button",
6817
+ role: "tab",
6818
+ "aria-selected": page === tab,
6819
+ "aria-controls": `${id}-${tab}`,
6820
+ tabIndex: page === tab ? 0 : -1,
6821
+ onClick: () => setPage(tab),
6822
+ children: [text(tab === "history" ? "analysisHistory" : "analysisSimulation"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6823
+ className: "ccp-analysisTabCount",
6824
+ children: digits(tab === "history" ? report.models.length : report.scenarios.length)
6825
+ })]
6826
+ }, tab))
6827
+ }),
6828
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
6829
+ className: "ccp-analysisPanel",
6830
+ id: `${id}-history`,
6831
+ role: "tabpanel",
6832
+ "aria-labelledby": `${id}-history-tab`,
6833
+ tabIndex: 0,
6834
+ hidden: page !== "history",
6835
+ children: [
6836
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6837
+ className: "ccp-analysisSectionHead",
6838
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
6839
+ className: "ccp-analysisHeading",
6840
+ children: text("analysisByModel")
6841
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6842
+ className: "ccp-analysisNote",
6843
+ children: text("analysisModelHint")
6844
+ })]
6845
+ }),
6846
+ m.requests === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6847
+ className: "ccp-analysisEmpty",
6848
+ children: text("analysisEmpty")
6849
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6850
+ className: "ccp-analysisModels",
6851
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6852
+ className: "ccp-analysisTableHead",
6853
+ "aria-hidden": "true",
6854
+ children: [
6855
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisModel") }),
6856
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisCostShare") }),
6857
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisResponse") }),
6858
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisFirstToken") }),
6859
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisSpeed") }),
6860
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
6861
+ ]
6862
+ }), report.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelRow, {
6863
+ m: model,
6864
+ name: names.get(model.id),
6865
+ totalCost: m.cost,
6866
+ text
6867
+ }, model.id))]
6868
+ }),
6869
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6870
+ className: "ccp-analysisFooter",
6871
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
6872
+ className: "ccp-analysisAggregate",
6873
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
6874
+ className: "ccp-analysisDisclosure",
6875
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {}), text("analysisDetails")]
6876
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MetricsDetails, {
6877
+ m,
6878
+ text
6879
+ })]
6880
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6881
+ className: "ccp-analysisMonthly",
6882
+ children: [
6883
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisMonthly") }),
6884
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
6885
+ className: "ccp-analysisMonthlyValue",
6886
+ children: report.monthlyEstimate === null || report.truncated || report.unreadableSessions || report.skippedSessions ? text("analysisMissing") : money(report.monthlyEstimate)
6887
+ }),
6888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6889
+ className: "ccp-analysisNote",
6890
+ children: text("analysisMonthlyHint")
6891
+ })
6892
+ ]
6893
+ })]
6894
+ })
6895
+ ]
6896
+ }),
6897
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
6898
+ className: "ccp-analysisPanel",
6899
+ id: `${id}-simulation`,
6900
+ role: "tabpanel",
6901
+ "aria-labelledby": `${id}-simulation-tab`,
6902
+ tabIndex: 0,
6903
+ hidden: page !== "simulation",
6904
+ children: [
6905
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6906
+ className: "ccp-analysisAssumption",
6907
+ children: text("analysisAssumption")
6908
+ }),
6909
+ report.catalogUnavailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6910
+ className: "ccp-analysisMessage ccp-analysisWarning",
6911
+ role: "status",
6912
+ children: text("analysisCatalog")
6913
+ }) : null,
6914
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6915
+ className: "ccp-analysisSectionHead",
6916
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
6917
+ className: "ccp-analysisHeading",
6918
+ children: text("analysisSimulationHeading")
6919
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6920
+ className: "ccp-analysisNote",
6921
+ children: text("analysisSimulationHint")
6922
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
6923
+ className: "ccp-analysisField ccp-analysisSearch",
6924
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6925
+ className: "ccp-analysisSearchLabel",
6926
+ children: text("analysisSearch")
6927
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6928
+ className: "ccp-analysisSearchShell",
6929
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnalysisIcon, { kind: "search" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
6930
+ className: "ccp-analysisInput",
6931
+ type: "search",
6932
+ placeholder: text("analysisSearchPlaceholder"),
6933
+ value: filter,
6934
+ onChange: (e) => setFilter(e.target.value)
6935
+ })]
6936
+ })]
6937
+ })]
6938
+ }),
6939
+ m.requests === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6940
+ className: "ccp-analysisEmpty",
6941
+ children: text("analysisEmpty")
6942
+ }) : scenarios.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6943
+ className: "ccp-analysisModels",
6944
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6945
+ className: "ccp-analysisTableHead ccp-analysisScenarioHead",
6946
+ "aria-hidden": "true",
6947
+ children: [
6948
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisModel") }),
6949
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisSimulationCost") }),
6950
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisCoverage") }),
6951
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisCapabilities") }),
6952
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
6953
+ ]
6954
+ }), scenarios.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScenarioRow, {
6955
+ m: model,
6956
+ text
6957
+ }, model.id))]
6958
+ }) : !report.catalogUnavailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6959
+ className: "ccp-analysisEmpty",
6960
+ role: "status",
6961
+ children: text("analysisNoMatches")
6962
+ }) : null
6963
+ ]
6964
+ }),
6965
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
6966
+ className: "ccp-analysisRules",
6967
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
6968
+ className: "ccp-analysisDisclosure",
6969
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {}), text("analysisRules")]
6970
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6971
+ className: "ccp-analysisRulesBody",
6972
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: text("analysisBasis") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: text("analysisCoverageHint") })]
6973
+ })]
6974
+ })
6975
+ ] }) : null
6976
+ ]
6977
+ });
6978
+ }
6979
+ //#endregion
5978
6980
  //#region src/client/panel-view.tsx
5979
6981
  /**
5980
6982
  * React components for the Command Code plans & quota panel: the sidebar footer
@@ -6277,10 +7279,17 @@ window.__ModuleLoader__.load({
6277
7279
  */
6278
7280
  function CommandCodePanel(props) {
6279
7281
  const view = usePanelView(props);
7282
+ const [fallbackMode, setFallbackMode] = (0, react.useState)("quota");
7283
+ const mode = props.useCommandCodePanelMode?.((s) => s) ?? fallbackMode;
7284
+ const analyticsBusy = props.useCommandCodeAnalytics?.((s) => s.status === "loading") ?? false;
7285
+ const selectMode = (next) => {
7286
+ setFallbackMode(next);
7287
+ props.selectPanelMode?.(next);
7288
+ };
6280
7289
  const text = (key) => view.text[key] ?? key;
6281
7290
  const [selectedId, setSelectedId] = (0, react.useState)(void 0);
6282
7291
  const startAutoRefresh = props.startAutoRefresh;
6283
- (0, react.useEffect)(() => startAutoRefresh(), [startAutoRefresh]);
7292
+ (0, react.useEffect)(() => mode === "quota" ? startAutoRefresh() : void 0, [startAutoRefresh, mode]);
6284
7293
  const accounts = view.accounts;
6285
7294
  const selected = accounts.find((account) => account.id === selectedId) ?? view.selected ?? accounts[0];
6286
7295
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -6288,7 +7297,7 @@ window.__ModuleLoader__.load({
6288
7297
  role: "region",
6289
7298
  "aria-label": text("nav"),
6290
7299
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6291
- className: "ccp-mainInner",
7300
+ className: `ccp-mainInner${mode === "analysis" ? " ccp-mainInnerAnalysis" : ""}`,
6292
7301
  children: [
6293
7302
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
6294
7303
  className: "ccp-header",
@@ -6297,14 +7306,14 @@ window.__ModuleLoader__.load({
6297
7306
  className: "ccp-headerText",
6298
7307
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
6299
7308
  className: "ccp-title",
6300
- children: text("nav")
7309
+ children: mode === "analysis" ? `Command Code · ${text("analyticsNav")}` : text("nav")
6301
7310
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6302
7311
  className: "ccp-subtitle",
6303
- children: text("subtitle")
7312
+ children: text(mode === "quota" ? "subtitle" : "analysisIntro")
6304
7313
  })]
6305
7314
  }),
6306
7315
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "ccp-spacer" }),
6307
- view.updatedAt !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7316
+ mode === "quota" && view.updatedAt !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6308
7317
  className: "ccp-meta",
6309
7318
  children: [
6310
7319
  text("updated"),
@@ -6315,9 +7324,9 @@ window.__ModuleLoader__.load({
6315
7324
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
6316
7325
  variant: "ghost",
6317
7326
  size: "sm",
6318
- disabled: view.loading,
6319
- onClick: () => props.refresh(),
6320
- children: view.loading ? text("refreshing") : text("refresh")
7327
+ disabled: mode === "quota" ? view.loading : analyticsBusy,
7328
+ onClick: () => mode === "quota" ? props.refresh() : props.refreshAnalytics?.(),
7329
+ children: mode === "quota" && view.loading ? text("refreshing") : text("refresh")
6321
7330
  }),
6322
7331
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
6323
7332
  variant: "ghost",
@@ -6326,75 +7335,108 @@ window.__ModuleLoader__.load({
6326
7335
  "aria-label": text("close"),
6327
7336
  title: text("closeHint"),
6328
7337
  onClick: () => props.close(),
6329
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7338
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
7339
+ width: "16",
7340
+ height: "16",
7341
+ viewBox: "0 0 24 24",
7342
+ fill: "none",
7343
+ stroke: "currentColor",
7344
+ strokeWidth: "1.6",
7345
+ strokeLinecap: "round",
6330
7346
  "aria-hidden": "true",
6331
- children: "×"
7347
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m6 6 12 12M18 6 6 18" })
6332
7348
  })
6333
7349
  })
6334
7350
  ]
6335
7351
  }),
6336
- view.noKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6337
- className: "ccp-notice",
6338
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6339
- className: "ccp-noticeTitle",
6340
- children: text("noKey")
6341
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6342
- className: "ccp-noticeHint",
6343
- children: text("noKeyHint")
6344
- })]
6345
- }) : null,
6346
- view.failure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6347
- className: "ccp-notice ccp-noticeError",
6348
- role: "alert",
6349
- children: [
6350
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6351
- className: "ccp-noticeTitle",
6352
- children: text(view.failure.title)
6353
- }),
6354
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6355
- className: "ccp-noticeHint",
6356
- children: text(view.failure.hint)
6357
- }),
6358
- view.failure.detail !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6359
- className: "ccp-noticeDetail",
6360
- children: view.failure.detail
6361
- }) : null
6362
- ]
6363
- }) : null,
6364
- !view.noKey && view.failure === void 0 && accounts.length === 0 && view.loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6365
- className: "ccp-hint",
6366
- children: text("loading")
6367
- }) : null,
6368
- view.staleError !== void 0 && view.staleError !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
6369
- className: "ccp-hint",
6370
- role: "status",
6371
- children: [
6372
- text("errorGeneric"),
6373
- " — ",
6374
- view.staleError
6375
- ]
6376
- }) : null,
6377
- accounts.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7352
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("nav", {
6378
7353
  className: "ccp-tabs",
6379
- role: "tablist",
6380
7354
  "aria-label": text("nav"),
6381
- children: accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7355
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7356
+ className: mode === "quota" ? "ccp-tab ccp-tabActive" : "ccp-tab",
6382
7357
  type: "button",
6383
- role: "tab",
6384
- "aria-selected": selected?.id === account.id,
6385
- className: selected?.id === account.id ? "ccp-tab ccp-tabActive" : "ccp-tab",
6386
- onClick: () => setSelectedId(account.id),
6387
- children: account.label
6388
- }, account.id))
6389
- }) : null,
6390
- selected !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountCard, {
6391
- account: selected,
6392
- view
6393
- }) : null,
6394
- view.partial !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7358
+ "aria-pressed": mode === "quota",
7359
+ onClick: () => selectMode("quota"),
7360
+ children: text("quotaNav")
7361
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7362
+ className: mode === "analysis" ? "ccp-tab ccp-tabActive" : "ccp-tab",
7363
+ type: "button",
7364
+ "aria-pressed": mode === "analysis",
7365
+ onClick: () => selectMode("analysis"),
7366
+ children: text("analyticsNav")
7367
+ })]
7368
+ }),
7369
+ mode === "analysis" ? props.useCommandCodeAnalytics && props.refreshAnalytics ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnalyticsView, {
7370
+ useCommandCodeAnalytics: props.useCommandCodeAnalytics,
7371
+ refreshAnalytics: props.refreshAnalytics,
7372
+ text
7373
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
6395
7374
  className: "ccp-hint",
6396
- children: text(view.partial)
6397
- }) : null
7375
+ children: text("analysisUnavailable")
7376
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
7377
+ view.noKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7378
+ className: "ccp-notice",
7379
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7380
+ className: "ccp-noticeTitle",
7381
+ children: text("noKey")
7382
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7383
+ className: "ccp-noticeHint",
7384
+ children: text("noKeyHint")
7385
+ })]
7386
+ }) : null,
7387
+ view.failure !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7388
+ className: "ccp-notice ccp-noticeError",
7389
+ role: "alert",
7390
+ children: [
7391
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7392
+ className: "ccp-noticeTitle",
7393
+ children: text(view.failure.title)
7394
+ }),
7395
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7396
+ className: "ccp-noticeHint",
7397
+ children: text(view.failure.hint)
7398
+ }),
7399
+ view.failure.detail !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7400
+ className: "ccp-noticeDetail",
7401
+ children: view.failure.detail
7402
+ }) : null
7403
+ ]
7404
+ }) : null,
7405
+ !view.noKey && view.failure === void 0 && accounts.length === 0 && view.loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7406
+ className: "ccp-hint",
7407
+ children: text("loading")
7408
+ }) : null,
7409
+ view.staleError !== void 0 && view.staleError !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
7410
+ className: "ccp-hint",
7411
+ role: "status",
7412
+ children: [
7413
+ text("errorGeneric"),
7414
+ " — ",
7415
+ view.staleError
7416
+ ]
7417
+ }) : null,
7418
+ accounts.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7419
+ className: "ccp-tabs",
7420
+ role: "tablist",
7421
+ "aria-label": text("nav"),
7422
+ children: accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
7423
+ type: "button",
7424
+ role: "tab",
7425
+ "aria-selected": selected?.id === account.id,
7426
+ className: selected?.id === account.id ? "ccp-tab ccp-tabActive" : "ccp-tab",
7427
+ onClick: () => setSelectedId(account.id),
7428
+ children: account.label
7429
+ }, account.id))
7430
+ }) : null,
7431
+ selected !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountCard, {
7432
+ account: selected,
7433
+ view
7434
+ }) : null,
7435
+ view.partial !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
7436
+ className: "ccp-hint",
7437
+ children: text(view.partial)
7438
+ }) : null
7439
+ ] })
6398
7440
  ]
6399
7441
  })
6400
7442
  });
@@ -7657,10 +8699,8 @@ window.__ModuleLoader__.load({
7657
8699
  .ccp-subtitle{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}
7658
8700
  .ccp-spacer{flex:1}
7659
8701
  .ccp-meta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5;font-variant-numeric:tabular-nums}
7660
- /* The dashboard's exit: the primitive's own size="sm" geometry is kept; only the
7661
- glyph is enlarged and optically centred, so it reads as a dismiss control. */
8702
+ /* 关闭图标沿用原语义与按钮尺寸,线性 SVG 随宿主文字颜色变化。 */
7662
8703
  .ccp-close{min-width:28px;justify-content:center;padding-left:0;padding-right:0}
7663
- .ccp-close span{font-size:16px;line-height:1}
7664
8704
  .ccp-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
7665
8705
 
7666
8706
  .ccp-notice{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:12px 14px;flex-direction:column;gap:4px;display:flex}
@@ -7714,7 +8754,209 @@ window.__ModuleLoader__.load({
7714
8754
  .ccp-tabActive{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-brand-primary)}
7715
8755
 
7716
8756
  @media (prefers-reduced-motion:reduce){.ccp-footFill,.ccp-barFill{transition:none}}
8757
+ /* 分析页继承设置页和宿主控件的语义变量;独立选择器不影响账号额度页面。 */
8758
+ .ccp-mainInnerAnalysis{max-width:1040px;box-sizing:border-box}
8759
+ /* 宿主侧栏会缩窄中心面板,按实际可用宽度切换列数,而不是仅看浏览器宽度。 */
8760
+ .ccp-analysis{font-size:14px;line-height:22px;min-width:0;color:var(--dsw-alias-label-primary);container-type:inline-size;container-name:ccp-analysis}
8761
+ .ccp-analysisFilters{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;margin:8px 0 20px}
8762
+ .ccp-analysisField{display:flex;flex-direction:column;gap:6px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);min-width:0}
8763
+ .ccp-analysisFilters .ccp-analysisField{flex:0 1 158px}
8764
+ .ccp-analysisFilters .ccp-analysisScopeField{flex-basis:184px}
8765
+ .ccp-analysisFilters .ccp-analysisDateField{flex-basis:166px}
8766
+ /* 对齐原生选择器、日期输入与按钮的边框盒;保留独立箭头留白和点击区域。 */
8767
+ .ccp-analysisInput{box-sizing:border-box;width:100%;min-width:0;height:36px;padding:0 11px;font:inherit;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:.5px solid var(--dsw-alias-border-l4);border-radius:8px}
8768
+ .ccp-analysisSelectShell{position:relative;display:block;width:100%;min-width:0}
8769
+ .ccp-analysisSelect{appearance:none;-webkit-appearance:none;padding-inline-end:40px;text-overflow:ellipsis;cursor:pointer;border:0;border-radius:18px;background:var(--dsw-alias-bg-module-platform)}
8770
+ .ccp-analysisSelectShell > .ccp-analysisChevron{position:absolute;inset-inline-end:12px;top:50%;transform:translateY(-50%) rotate(90deg);pointer-events:none}
8771
+ .ccp-analysisSelect:disabled + .ccp-analysisChevron{opacity:.6}
8772
+ .ccp-analysisInput::placeholder{color:var(--dsw-alias-label-tertiary)}
8773
+ .ccp-analysisApply{box-sizing:border-box;flex:none;height:36px;font:inherit;font-size:14px;line-height:22px;white-space:nowrap;padding:0 20px;border:0;border-radius:var(--dsw-radius-md,18px);background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);cursor:pointer;margin-left:2px}
8774
+ .ccp-analysisApply:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}
8775
+ .ccp-analysisApply:disabled{opacity:.4;cursor:wait}
8776
+ .ccp-analysisInput:disabled{opacity:.6}
8777
+ .ccp-analysisInput:focus-visible,.ccp-analysisApply:focus-visible,.ccp-analysisTab:focus-visible,.ccp-analysisPanel:focus-visible,.ccp-analysisModelSummary:focus-visible,.ccp-analysisScenarioSummary:focus-visible,.ccp-analysisDisclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}
8778
+ /* 模型列表会裁切圆角,内收焦点框以保持第一行和最后一行也清晰可见。 */
8779
+ .ccp-analysisModelSummary:focus-visible,.ccp-analysisScenarioSummary:focus-visible{outline-offset:-3px;border-radius:6px}
8780
+ .ccp-analysisScope{display:flex;justify-content:space-between;gap:8px 18px;flex-wrap:wrap;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin-bottom:12px}
8781
+ .ccp-analysisScope > span:first-child{color:var(--dsw-alias-label-primary);font-weight:500}
8782
+ .ccp-analysisOverview{display:grid;grid-template-columns:1.45fr repeat(3,1fr);gap:20px;padding:16px 18px;background:var(--dsw-alias-bg-layer-3);border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;font-variant-numeric:tabular-nums}
8783
+ .ccp-analysisMetric{display:flex;flex-direction:column;gap:3px;min-width:0}
8784
+ .ccp-analysisLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);font-weight:400}
8785
+ .ccp-analysisValue{font-size:14px;line-height:22px;font-weight:500;overflow-wrap:anywhere}
8786
+ .ccp-analysisSub{display:block;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;font-weight:400;overflow-wrap:anywhere}
8787
+ .ccp-analysisOverview .ccp-analysisMetric{gap:5px}
8788
+ .ccp-analysisOverview .ccp-analysisValue{font-size:20px;line-height:28px;font-weight:500}
8789
+ .ccp-analysisNote{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);margin:9px 0 0}
8790
+ .ccp-analysisMessage{margin:12px 0 0;padding:12px 14px;border:.5px solid var(--dsw-alias-border-l4);border-radius:12px;background:var(--dsw-alias-bg-layer-3);font-size:14px;line-height:22px}
8791
+ .ccp-analysisWarning{display:flex;align-items:flex-start;gap:8px}
8792
+ .ccp-analysisIcon{flex:none}
8793
+ .ccp-analysisWarning > .ccp-analysisIcon{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-secondary));margin-top:3px}
8794
+ .ccp-analysisWarning p{margin:0}
8795
+ .ccp-analysisWarning p + p{margin-top:5px}
8796
+ .ccp-analysisError{color:var(--dsw-alias-state-error-primary)}
8797
+ /* 沿用宿主分段控件的轨道、浮层与胶囊尺寸,保留原有页签键盘语义。 */
8798
+ .ccp-analysisTabs{box-sizing:border-box;display:inline-grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:2px;padding:4px;border:0;border-radius:var(--dsw-radius-md,18px);background:var(--dsw-alias-interactive-bg-hover);margin:22px 0 0;max-width:100%}
8799
+ .ccp-analysisTab{height:28px;padding:0 16px;border:0;border-radius:var(--dsw-radius-sm,14px);background:transparent;font:inherit;font-size:13px;line-height:20px;font-weight:500;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer}
8800
+ .ccp-analysisTab:hover,.ccp-analysisTab[aria-selected="true"]{color:var(--dsw-alias-label-primary)}
8801
+ .ccp-analysisTab[aria-selected="true"]{background:var(--dsw-alias-bg-layer-1);box-shadow:var(--dsw-elevation-soft)}
8802
+ .ccp-analysisTabCount{display:inline-flex;justify-content:center;min-width:16px;padding:0 4px;margin-left:6px;border-radius:999px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
8803
+ .ccp-analysisPanel{padding-top:20px;min-width:0}
8804
+ .ccp-analysisPanel[hidden]{display:none}
8805
+ .ccp-analysisSectionHead{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:12px}
8806
+ .ccp-analysisHeading{font-size:14px;line-height:22px;font-weight:500;margin:0}
8807
+ .ccp-analysisSectionHead .ccp-analysisNote{margin:0;max-width:56ch}
8808
+ .ccp-analysisModels{border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;overflow:hidden}
8809
+ .ccp-analysisModelRow + .ccp-analysisModelRow{border-top:.5px solid var(--dsw-alias-border-l2)}
8810
+ .ccp-analysisTableHead,.ccp-analysisModelSummary,.ccp-analysisScenarioSummary{position:relative;display:grid;grid-template-columns:minmax(190px,2.1fr) minmax(150px,1.45fr) minmax(90px,.9fr) minmax(90px,.9fr) minmax(100px,1fr) 16px;gap:18px;align-items:center;padding:16px 18px;font-variant-numeric:tabular-nums}
8811
+ .ccp-analysisTableHead{padding-top:11px;padding-bottom:11px;background:var(--dsw-alias-interactive-bg-hover);border-bottom:.5px solid var(--dsw-alias-border-l2);font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
8812
+ .ccp-analysisTableHead > span:not(:first-child){text-align:right}
8813
+ .ccp-analysisScenarioHead,.ccp-analysisScenarioSummary{grid-template-columns:minmax(190px,2fr) minmax(118px,1.1fr) minmax(155px,1.35fr) minmax(142px,1.15fr) 16px}
8814
+ .ccp-analysisScenarioHead > span:nth-child(3),.ccp-analysisScenarioHead > span:nth-child(4){text-align:left}
8815
+ .ccp-analysisModelSummary,.ccp-analysisScenarioSummary{cursor:pointer;list-style:none;min-height:60px}
8816
+ .ccp-analysisModelSummary::-webkit-details-marker,.ccp-analysisScenarioSummary::-webkit-details-marker,.ccp-analysisDisclosure::-webkit-details-marker{display:none}
8817
+ .ccp-analysisModelSummary:hover,.ccp-analysisScenarioSummary:hover{background:var(--dsw-alias-interactive-bg-hover)}
8818
+ .ccp-analysisIdentity{display:flex;flex-direction:column;gap:2px;min-width:0}
8819
+ .ccp-analysisModelName{font-size:14px;line-height:22px;font-weight:500;overflow-wrap:anywhere}
8820
+ .ccp-analysisModelCount{margin-top:3px}
8821
+ .ccp-analysisModelSummary > .ccp-analysisMetric,.ccp-analysisScenarioSummary > .ccp-analysisMetric{align-items:flex-end}
8822
+ .ccp-analysisScenarioSummary > .ccp-analysisMetric:nth-child(3),.ccp-analysisScenarioSummary > .ccp-analysisMetric:nth-child(4){align-items:flex-start}
8823
+ /* 宽表使用集中表头,但保留每个指标的无障碍标签;窄面板恢复可见标签。 */
8824
+ .ccp-analysisModelSummary .ccp-analysisMetricLabel,.ccp-analysisScenarioSummary .ccp-analysisMetricLabel,.ccp-analysisSearchLabel{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
8825
+ .ccp-analysisShare{display:flex;align-items:center;gap:8px;width:100%;max-width:148px;margin-top:3px}
8826
+ .ccp-analysisShareTrack{display:block;flex:1;height:4px;overflow:hidden;background:var(--dsw-alias-bg-module-platform);border-radius:999px}
8827
+ .ccp-analysisShareFill{display:block;height:100%;background:var(--dsw-alias-label-secondary);border-radius:999px}
8828
+ .ccp-analysisShareValue{font-size:12px;line-height:18px;min-width:40px;text-align:right;color:var(--dsw-alias-label-tertiary)}
8829
+ .ccp-analysisChevron{flex:none;color:var(--dsw-alias-label-tertiary)}
8830
+ .ccp-analysisModelRow[open] > .ccp-analysisModelSummary > .ccp-analysisChevron,.ccp-analysisModelRow[open] > .ccp-analysisScenarioSummary > .ccp-analysisChevron,.ccp-analysisAggregate[open] > .ccp-analysisDisclosure > .ccp-analysisChevron,.ccp-analysisRules[open] > .ccp-analysisDisclosure > .ccp-analysisChevron{transform:rotate(90deg)}
8831
+ .ccp-analysisDetails{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:22px;padding:16px 18px;background:var(--dsw-alias-bg-module-platform);border-top:.5px solid var(--dsw-alias-border-l2)}
8832
+ .ccp-analysisDetailTitle{font-size:12px;line-height:18px;font-weight:600;margin:0 0 11px}
8833
+ .ccp-analysisFacts{margin:0;display:flex;flex-direction:column;gap:8px}
8834
+ .ccp-analysisFact{display:flex;align-items:baseline;justify-content:space-between;gap:10px;min-width:0}
8835
+ .ccp-analysisFactValue{font-size:14px;line-height:22px;margin:0;text-align:right;font-variant-numeric:tabular-nums;overflow-wrap:anywhere;min-width:0}
8836
+ .ccp-analysisFact .ccp-analysisLabel{flex-shrink:0}
8837
+ .ccp-analysisFooter{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-top:14px}
8838
+ .ccp-analysisAggregate{flex:1;min-width:210px}
8839
+ .ccp-analysisAggregate > .ccp-analysisDetails{border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;margin-top:6px}
8840
+ .ccp-analysisDisclosure{display:flex;gap:7px;align-items:center;cursor:pointer;list-style:none;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);padding:7px 0}
8841
+ .ccp-analysisDisclosure > .ccp-analysisChevron{width:14px;height:14px}
8842
+ .ccp-analysisMonthly{flex:none;max-width:340px;display:flex;justify-content:flex-end;flex-wrap:wrap;gap:2px 8px;align-items:baseline;text-align:right;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);padding-top:5px}
8843
+ .ccp-analysisMonthlyValue{font-size:14px;line-height:22px;font-weight:500;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums}
8844
+ .ccp-analysisMonthly .ccp-analysisNote{width:100%;margin:0}
8845
+ .ccp-analysisAssumption{font-size:14px;line-height:22px;color:var(--dsw-alias-label-tertiary);margin:0 0 18px;max-width:76ch}
8846
+ .ccp-analysisSearch{flex:0 1 270px}
8847
+ .ccp-analysisSearchShell{position:relative;display:block}
8848
+ .ccp-analysisSearchShell > .ccp-analysisIcon{position:absolute;top:10px;left:11px;color:var(--dsw-alias-label-tertiary);pointer-events:none}
8849
+ .ccp-analysisSearch .ccp-analysisInput{padding-left:35px}
8850
+ .ccp-analysisScenarioDetails{grid-template-columns:minmax(0,1fr) minmax(0,2fr)}
8851
+ .ccp-analysisScenarioDetails .ccp-analysisFactValue{max-width:70%}
8852
+ .ccp-analysisEmpty{font-size:14px;line-height:22px;color:var(--dsw-alias-label-tertiary);text-align:center;padding:34px 18px;margin:0;border:1px dashed var(--dsw-alias-border-l4);border-radius:8px}
8853
+ .ccp-analysisRules{margin-top:22px;border-top:.5px solid var(--dsw-alias-border-l2);padding-top:8px}
8854
+ .ccp-analysisRulesBody{display:grid;grid-template-columns:1fr 1fr;gap:24px;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px;margin-top:9px}
8855
+ .ccp-analysisRulesBody p{margin:0 0 10px}
8856
+ @container ccp-analysis (max-width:920px){
8857
+ .ccp-analysisTableHead{display:none}
8858
+ .ccp-analysisModelSummary{grid-template-columns:repeat(4,minmax(0,1fr)) 16px;gap:12px 18px}
8859
+ .ccp-analysisScenarioSummary{grid-template-columns:repeat(3,minmax(0,1fr)) 16px;gap:12px 18px}
8860
+ .ccp-analysisModelSummary > .ccp-analysisIdentity,.ccp-analysisScenarioSummary > .ccp-analysisIdentity{grid-column:1/-2}
8861
+ .ccp-analysisModelSummary > .ccp-analysisChevron{grid-column:5;grid-row:1;justify-self:end}
8862
+ .ccp-analysisScenarioSummary > .ccp-analysisChevron{grid-column:4;grid-row:1;justify-self:end}
8863
+ .ccp-analysisModelSummary > .ccp-analysisMetric,.ccp-analysisScenarioSummary > .ccp-analysisMetric{align-items:flex-start}
8864
+ .ccp-analysisModelSummary .ccp-analysisMetricLabel,.ccp-analysisScenarioSummary .ccp-analysisMetricLabel{position:static;width:auto;height:auto;padding:0;margin:0;overflow:visible;clip:auto;white-space:normal}
8865
+ .ccp-analysisDetails{grid-template-columns:repeat(2,minmax(0,1fr))}
8866
+ }
8867
+ @container ccp-analysis (max-width:588px){
8868
+ .ccp-analysisFilters{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
8869
+ .ccp-analysisFilters[data-mode="range"] .ccp-analysisScopeField{grid-column:1/-1}
8870
+ .ccp-analysisApply{grid-column:1/-1;margin:0;width:100%}
8871
+ .ccp-analysisOverview{grid-template-columns:repeat(2,minmax(0,1fr));gap:20px 18px;padding:16px}
8872
+ .ccp-analysisModelSummary,.ccp-analysisScenarioSummary{grid-template-columns:repeat(2,minmax(0,1fr));gap:15px 18px;padding:16px}
8873
+ .ccp-analysisModelSummary > .ccp-analysisIdentity,.ccp-analysisScenarioSummary > .ccp-analysisIdentity{grid-column:1/-1;padding-right:24px}
8874
+ .ccp-analysisModelSummary > .ccp-analysisChevron,.ccp-analysisScenarioSummary > .ccp-analysisChevron{position:absolute;top:18px;right:16px;grid-column:auto;grid-row:auto}
8875
+ .ccp-analysisDetails,.ccp-analysisScenarioDetails{gap:20px;grid-template-columns:1fr;padding:16px}
8876
+ .ccp-analysisAggregate{width:100%;min-width:0}
8877
+ .ccp-analysisMonthly{width:100%;max-width:none;justify-content:flex-start;text-align:left;padding-top:0}
8878
+ .ccp-analysisSectionHead{align-items:flex-start}
8879
+ .ccp-analysisSectionHead .ccp-analysisSearch{flex-basis:100%;width:100%;order:2}
8880
+ .ccp-analysisRulesBody{grid-template-columns:1fr;gap:12px}
8881
+ /* 英文页签带数量时允许换行,避免轨道或文字撑出宿主的窄面板。 */
8882
+ .ccp-analysisTabs{margin-top:18px;grid-auto-columns:minmax(0,1fr)}
8883
+ .ccp-analysisTab{height:auto;min-height:28px;padding:4px 8px;white-space:normal}
8884
+ }
8885
+ @media (max-width:640px){.ccp-mainInnerAnalysis{padding:20px 16px 32px}}
7717
8886
  `;
8887
+ //#endregion
8888
+ //#region src/client/analytics.ts
8889
+ /** 手动读取历史,旧宿主可降级;查询切换和卸载后不接受过期结果。 */
8890
+ var AnalyticsController = class {
8891
+ remote;
8892
+ current = { status: "idle" };
8893
+ listeners = /* @__PURE__ */ new Set();
8894
+ generation = 0;
8895
+ disposed = false;
8896
+ query = {
8897
+ mode: "recent",
8898
+ limit: 20
8899
+ };
8900
+ constructor(remote) {
8901
+ this.remote = remote;
8902
+ }
8903
+ state() {
8904
+ return this.current;
8905
+ }
8906
+ subscribe(listener) {
8907
+ this.listeners.add(listener);
8908
+ return () => {
8909
+ this.listeners.delete(listener);
8910
+ };
8911
+ }
8912
+ dispose() {
8913
+ this.disposed = true;
8914
+ this.generation++;
8915
+ this.listeners.clear();
8916
+ }
8917
+ reload() {
8918
+ this.generation++;
8919
+ this.set({ status: "idle" });
8920
+ }
8921
+ refresh(query = this.query) {
8922
+ if (this.disposed) return;
8923
+ const requested = parseAnalyticsQuery(query);
8924
+ this.query = requested;
8925
+ const generation = ++this.generation;
8926
+ this.set({ status: "loading" });
8927
+ const call = this.remote.analytics;
8928
+ if (!call) {
8929
+ this.set({
8930
+ status: "error",
8931
+ error: "宿主不支持模型分析,请更新插件"
8932
+ });
8933
+ return;
8934
+ }
8935
+ Promise.resolve().then(() => call(requested)).then((result) => {
8936
+ if (this.disposed || generation !== this.generation) return;
8937
+ if (!result.ok) {
8938
+ this.set({
8939
+ status: "error",
8940
+ error: result.error.message === "宿主不支持模型分析,请更新插件" ? result.error.message : "模型分析读取失败,请稍后刷新或检查宿主日志"
8941
+ });
8942
+ return;
8943
+ }
8944
+ this.set({
8945
+ status: "ready",
8946
+ report: parseAnalyticsReport(result.value)
8947
+ });
8948
+ }).catch(() => {
8949
+ if (!this.disposed && generation === this.generation) this.set({
8950
+ status: "error",
8951
+ error: "模型分析读取失败,请稍后刷新或检查宿主日志"
8952
+ });
8953
+ });
8954
+ }
8955
+ set(state) {
8956
+ this.current = state;
8957
+ for (const listener of this.listeners) listener();
8958
+ }
8959
+ };
7718
8960
  //#endregion
7719
8961
  //#region src/client/index.ts
7720
8962
  /** Install one stylesheet by id and return a disposer for its owning effect. */
@@ -7808,12 +9050,14 @@ window.__ModuleLoader__.load({
7808
9050
  let usageMountError;
7809
9051
  let pricesController;
7810
9052
  let refreshUsage;
9053
+ let analyticsController;
7811
9054
  const contribution = {
7812
9055
  package: USAGE_REMOTE_CONTRIBUTION.package,
7813
9056
  descriptors: [
7814
9057
  ...USAGE_REMOTE_CONTRIBUTION.descriptors,
7815
9058
  ...MODELS_REMOTE_CONTRIBUTION.descriptors,
7816
9059
  ...PRICES_REMOTE_CONTRIBUTION.descriptors,
9060
+ ...ANALYTICS_REMOTE_CONTRIBUTION.descriptors,
7817
9061
  ...LOGIN_REMOTE_CONTRIBUTION.descriptors,
7818
9062
  ...ENROLLMENT_REMOTE_CONTRIBUTION.descriptors
7819
9063
  ]
@@ -7831,9 +9075,11 @@ window.__ModuleLoader__.load({
7831
9075
  usageNamespace = namespaceCtx.remote.commandcode;
7832
9076
  controller.refreshCatalog();
7833
9077
  pricesController?.reload();
9078
+ analyticsController?.reload();
7834
9079
  refreshUsage?.();
7835
9080
  namespaceCtx.effect(() => () => {
7836
9081
  usageNamespace = void 0;
9082
+ analyticsController?.reload();
7837
9083
  }, "dsh-commandcode-provider: usage namespace");
7838
9084
  });
7839
9085
  }, (error) => {
@@ -7889,6 +9135,22 @@ window.__ModuleLoader__.load({
7889
9135
  ctx.effect(() => () => pricesController?.dispose(), "dsh-commandcode-provider: price table");
7890
9136
  const pricesStore = createSnapshotStore(pricesController.state());
7891
9137
  pricesController.subscribe(() => pricesStore.set(pricesController.state()));
9138
+ analyticsController = new AnalyticsController({ analytics: async (query) => {
9139
+ const namespace = usageNamespace;
9140
+ if (typeof namespace?.analytics !== "function") return {
9141
+ ok: false,
9142
+ error: { message: "宿主不支持模型分析,请更新插件" }
9143
+ };
9144
+ return namespace.analytics(query);
9145
+ } });
9146
+ const analyticsStore = createSnapshotStore(analyticsController.state());
9147
+ analyticsController.subscribe(() => analyticsStore.set(analyticsController.state()));
9148
+ ctx.effect(() => () => analyticsController?.dispose(), "commandcode analytics controller");
9149
+ const panelMode = createSnapshotStore("quota");
9150
+ const openAnalytics = () => {
9151
+ panelMode.set("analysis");
9152
+ ctx.get("layout")?.selectPanel(PANEL_ID);
9153
+ };
7892
9154
  const loginRemote = {
7893
9155
  loginBegin: async (targetRef) => {
7894
9156
  if (usageNamespace === void 0) return {
@@ -7952,6 +9214,7 @@ window.__ModuleLoader__.load({
7952
9214
  discard: () => controller.discard(),
7953
9215
  refreshCredentials: () => void controller.refreshCredentials(),
7954
9216
  refreshUsage: () => void usageController.refresh(),
9217
+ ...typeof ctx.get("layout")?.selectPanel === "function" ? { openAnalytics } : {},
7955
9218
  beginLogin: (targetRef) => void loginController.begin(targetRef),
7956
9219
  cancelLogin: () => void loginController.cancel(),
7957
9220
  renameAccount: (ref, label) => controller.renameAccount(ref, label),
@@ -7997,14 +9260,19 @@ window.__ModuleLoader__.load({
7997
9260
  const panelFace = () => ({
7998
9261
  hooks: {
7999
9262
  commandCodeUsage: usageStore,
8000
- commandCodeSettings: store
9263
+ commandCodeSettings: store,
9264
+ commandCodeAnalytics: analyticsStore,
9265
+ commandCodePanelMode: panelMode
8001
9266
  },
9267
+ selectPanelMode: (mode) => panelMode.set(mode),
9268
+ refreshAnalytics: (query) => analyticsController?.refresh(query),
8002
9269
  refresh: () => {
8003
9270
  pricesController?.ensure();
8004
9271
  usageController.refresh();
8005
9272
  },
8006
9273
  startAutoRefresh: () => startPanelAutoRefresh(usageController, () => true),
8007
9274
  open: () => {
9275
+ panelMode.set("quota");
8008
9276
  ctx.get("layout")?.selectPanel(PANEL_ID);
8009
9277
  },
8010
9278
  close: () => {