@linxin666/dsh-usage 0.4.4 → 0.4.5

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
@@ -72,6 +72,13 @@ window.__ModuleLoader__.load({
72
72
  "usage.current": "当前",
73
73
  "usage.today": "今日用量",
74
74
  "usage.today.cost": "今日消费(估算)",
75
+ "usage.day.label": "查看日期",
76
+ "usage.day.today": "今日",
77
+ "usage.day.title": "{date} 用量",
78
+ "usage.day.cost": "当日消费(估算)",
79
+ "usage.day.empty": "该日没有用量记录",
80
+ "usage.day.loading": "正在加载该日用量…",
81
+ "usage.day.error": "该日用量加载失败:{error}",
75
82
  "usage.peak.on": "DeepSeek 高峰时段:计价 ×2,{time} 结束",
76
83
  "usage.peak.off": "DeepSeek 空闲时段:计价为高峰一半,{time} 进入高峰",
77
84
  "usage.calls": "{n} 次调用",
@@ -106,6 +113,7 @@ window.__ModuleLoader__.load({
106
113
  "usage.bank.spend.observed": "官方余额实测花费 ¥{cost}(自 {since} 起)",
107
114
  "usage.bank.spend.estimated": "消费估算:约 ¥{cost}",
108
115
  "usage.bank.window": "统计窗口 {from} ~ {to}",
116
+ "usage.bank.windowAll": "全部保留期",
109
117
  "usage.bank.save": "保存图片",
110
118
  "usage.bank.share": "分享",
111
119
  "usage.bank.drawError": "票券生成失败:{error}",
@@ -130,6 +138,13 @@ window.__ModuleLoader__.load({
130
138
  "usage.current": "Current",
131
139
  "usage.today": "Today",
132
140
  "usage.today.cost": "Today spend (estimated)",
141
+ "usage.day.label": "Day",
142
+ "usage.day.today": "Today",
143
+ "usage.day.title": "Usage · {date}",
144
+ "usage.day.cost": "Day spend (estimated)",
145
+ "usage.day.empty": "No usage recorded on this day",
146
+ "usage.day.loading": "Loading that day…",
147
+ "usage.day.error": "Failed to load that day: {error}",
133
148
  "usage.peak.on": "DeepSeek peak hours: 2x pricing, ends {time}",
134
149
  "usage.peak.off": "DeepSeek off-peak: half of peak pricing, peak returns {time}",
135
150
  "usage.calls": "{n} calls",
@@ -164,6 +179,7 @@ window.__ModuleLoader__.load({
164
179
  "usage.bank.spend.observed": "Spent ¥{cost} observed on the official balance (watching since {since})",
165
180
  "usage.bank.spend.estimated": "Estimated spend: about ¥{cost}",
166
181
  "usage.bank.window": "Window {from} - {to}",
182
+ "usage.bank.windowAll": "All retained days",
167
183
  "usage.bank.save": "Save image",
168
184
  "usage.bank.share": "Share",
169
185
  "usage.bank.drawError": "Failed to render the voucher: {error}",
@@ -189,7 +205,7 @@ window.__ModuleLoader__.load({
189
205
  }
190
206
  //#endregion
191
207
  //#region \0dsh-css:packages/dsh-usage/src/client/usage.module.css.mjs
192
- const css = ".cvtkAW_section{color:inherit;flex-direction:column;gap:16px;display:flex}.cvtkAW_header{justify-content:space-between;align-items:center;gap:12px;display:flex}.cvtkAW_currentProvider{opacity:.75;font-size:13px}.cvtkAW_refreshBtn{appearance:none;color:inherit;font:inherit;cursor:pointer;opacity:.85;background:0 0;border:1px solid;border-radius:8px;padding:4px 12px;font-size:12px;transition:opacity .12s,background-color .12s}.cvtkAW_refreshBtn:hover:not(:disabled){opacity:1;background:color-mix(in srgb, currentColor 8%, transparent)}.cvtkAW_refreshBtn:disabled{cursor:default;opacity:.5}.cvtkAW_refreshBtn:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_tabs{border-bottom:1px solid color-mix(in srgb, currentColor 14%, transparent);gap:4px;display:flex}.cvtkAW_tab{appearance:none;color:inherit;font:inherit;cursor:pointer;opacity:.65;background:0 0;border:none;border-bottom:2px solid #0000;margin-bottom:-1px;padding:6px 14px;font-size:13px}.cvtkAW_tab:hover{opacity:.9}.cvtkAW_tab:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_tabActive{opacity:1;border-bottom-color:currentColor;font-weight:600}.cvtkAW_card{border:1px solid color-mix(in srgb, currentColor 14%, transparent);border-radius:12px;flex-direction:column;gap:10px;padding:14px 16px;display:flex}.cvtkAW_cardTitle{letter-spacing:.04em;text-transform:uppercase;opacity:.6;font-size:12px;font-weight:600}.cvtkAW_statRow{flex-wrap:wrap;gap:18px;display:flex}.cvtkAW_stat{flex-direction:column;gap:2px;display:flex}.cvtkAW_statValue{font-variant-numeric:tabular-nums;font-size:18px;font-weight:600}.cvtkAW_statLabel{opacity:.6;font-size:11px}.cvtkAW_providerRow{border-top:1px solid color-mix(in srgb, currentColor 8%, transparent);justify-content:space-between;align-items:center;gap:12px;padding:6px 0;font-size:13px;display:flex}.cvtkAW_providerRow:first-of-type{border-top:none}.cvtkAW_providerName{align-items:center;gap:8px;min-width:0;display:flex}.cvtkAW_providerTokens{font-variant-numeric:tabular-nums;opacity:.75;white-space:nowrap}.cvtkAW_providerBalance{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.cvtkAW_currentBadge{border:1px solid color-mix(in srgb, currentColor 35%, transparent);opacity:.8;border-radius:999px;flex:none;padding:1px 7px;font-size:10px;font-weight:600}.cvtkAW_chart{flex-direction:column;gap:12px;display:flex}.cvtkAW_chartProvider{flex-direction:column;gap:4px;display:flex}.cvtkAW_chartHead{justify-content:space-between;align-items:baseline;gap:10px;font-size:13px;display:flex}.cvtkAW_chartTokens{font-variant-numeric:tabular-nums;opacity:.65;white-space:nowrap;font-size:11px}.cvtkAW_chartBar{background:color-mix(in srgb, currentColor 8%, transparent);border-radius:999px;height:8px;display:block;overflow:hidden}.cvtkAW_chartFill{background:color-mix(in srgb, currentColor 55%, transparent);border-radius:999px;height:100%;transition:width .3s;display:block}.cvtkAW_chartModel{opacity:.8;grid-template-columns:minmax(80px,180px) 1fr auto;align-items:center;gap:8px;padding-left:14px;font-size:11px;display:grid}.cvtkAW_chartModelName{text-overflow:ellipsis;white-space:nowrap;opacity:.8;overflow:hidden}.cvtkAW_chartModelBar{background:color-mix(in srgb, currentColor 6%, transparent);border-radius:999px;height:4px;display:block;overflow:hidden}.cvtkAW_chartModelFill{background:color-mix(in srgb, currentColor 35%, transparent);border-radius:999px;height:100%;transition:width .3s;display:block}.cvtkAW_trendAxis{opacity:.5;font-variant-numeric:tabular-nums;justify-content:space-between;font-size:10px;display:flex}.cvtkAW_muted{opacity:.6;font-size:12px}.cvtkAW_voucherPreview canvas{border-radius:8px;width:100%;height:auto;display:block}.cvtkAW_buttonRow{gap:8px;display:flex}.cvtkAW_errorLine{opacity:.75;font-size:12px}.cvtkAW_planCard{flex-direction:column;gap:8px;display:flex}.cvtkAW_planHead{justify-content:space-between;align-items:baseline;gap:10px;display:flex}.cvtkAW_planName{font-size:14px;font-weight:600}.cvtkAW_windowRow{flex-direction:column;gap:4px;display:flex}.cvtkAW_windowLabel{opacity:.8;font-variant-numeric:tabular-nums;justify-content:space-between;font-size:12px;display:flex}.cvtkAW_bar{background:color-mix(in srgb, currentColor 10%, transparent);border-radius:999px;height:6px;overflow:hidden}.cvtkAW_barFill{background:color-mix(in srgb, currentColor 55%, transparent);border-radius:999px;height:100%;transition:width .3s}.cvtkAW_barWarn{background:#d97706}.cvtkAW_barLow{background:#dc2626}.cvtkAW_resetLine{opacity:.55;font-variant-numeric:tabular-nums;font-size:11px}.cvtkAW_settingsGrid{flex-wrap:wrap;align-items:center;gap:16px;display:flex}.cvtkAW_settingItem{align-items:center;gap:8px;font-size:13px;display:flex}.cvtkAW_settingItem input[type=checkbox]{accent-color:currentColor}.cvtkAW_settingItem input[type=number]{border:1px solid color-mix(in srgb, currentColor 25%, transparent);width:90px;color:inherit;font:inherit;background:0 0;border-radius:6px;padding:3px 8px;font-size:13px}.cvtkAW_settingItem select{border:1px solid color-mix(in srgb, currentColor 25%, transparent);color:inherit;font:inherit;background:0 0;border-radius:6px;padding:3px 8px;font-size:13px}.cvtkAW_settingItem input:focus-visible,.cvtkAW_settingItem select:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_footCard{box-sizing:border-box;background:color-mix(in srgb, currentColor 4%, transparent);width:100%;color:inherit;border:none;border-radius:12px;margin:2px 0 4px;transition:background-color .12s;position:relative}.cvtkAW_footCard:hover{background:var(--dsw-alias-interactive-bg-hover)}.cvtkAW_footCardCollapsed{background:0 0}.cvtkAW_footMain{appearance:none;box-sizing:border-box;width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:12px;flex-direction:column;gap:4px;padding:8px;display:flex}.cvtkAW_footMain:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_footToggle{appearance:none;width:20px;height:20px;color:inherit;cursor:pointer;opacity:.55;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;transition:opacity .12s,background-color .12s;display:inline-flex;position:absolute;top:6px;right:6px}.cvtkAW_footCard:hover .cvtkAW_footToggle{opacity:.8}.cvtkAW_footToggle:hover{opacity:1;background:color-mix(in srgb, currentColor 10%, transparent)}.cvtkAW_footToggle:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_footToggle svg{display:block}.cvtkAW_footHead{justify-content:space-between;align-items:baseline;gap:8px;display:flex}.cvtkAW_footTitle{opacity:.65;white-space:nowrap;align-items:center;gap:6px;min-width:0;font-size:12px;display:inline-flex}.cvtkAW_footTitle svg{flex:none;display:block}.cvtkAW_footValue{font-variant-numeric:tabular-nums;white-space:nowrap;margin-right:18px;font-size:15px;font-weight:600}.cvtkAW_footLine{opacity:.7;font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.cvtkAW_footMeta{opacity:.5;font-variant-numeric:tabular-nums;font-size:10px}.cvtkAW_footCardCollapsed .cvtkAW_footMain{min-height:36px;padding:7px 8px}.cvtkAW_footStrip{white-space:nowrap;align-items:center;gap:6px;padding-right:20px;font-size:12px;display:flex;overflow:hidden}.cvtkAW_footStrip svg{opacity:.65;flex:none;display:block}.cvtkAW_footStripProvider{text-overflow:ellipsis;flex:none;max-width:45%;font-weight:500;overflow:hidden}.cvtkAW_footStripLabel{opacity:.65;text-overflow:ellipsis;overflow:hidden}.cvtkAW_footStripValue{font-variant-numeric:tabular-nums;margin-left:auto;font-weight:600}[data-dsh-frame][data-sidebar-collapsed] .cvtkAW_footCard,[data-sidebar-collapsed] .cvtkAW_footCard{display:none}";
208
+ const css = ".cvtkAW_section{color:inherit;flex-direction:column;gap:16px;display:flex}.cvtkAW_header{justify-content:space-between;align-items:center;gap:12px;display:flex}.cvtkAW_currentProvider{opacity:.75;font-size:13px}.cvtkAW_refreshBtn{appearance:none;color:inherit;font:inherit;cursor:pointer;opacity:.85;background:0 0;border:1px solid;border-radius:8px;padding:4px 12px;font-size:12px;transition:opacity .12s,background-color .12s}.cvtkAW_refreshBtn:hover:not(:disabled){opacity:1;background:color-mix(in srgb, currentColor 8%, transparent)}.cvtkAW_refreshBtn:disabled{cursor:default;opacity:.5}.cvtkAW_refreshBtn:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_tabs{border-bottom:1px solid color-mix(in srgb, currentColor 14%, transparent);gap:4px;display:flex}.cvtkAW_tab{appearance:none;color:inherit;font:inherit;cursor:pointer;opacity:.65;background:0 0;border:none;border-bottom:2px solid #0000;margin-bottom:-1px;padding:6px 14px;font-size:13px}.cvtkAW_tab:hover{opacity:.9}.cvtkAW_tab:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_tabActive{opacity:1;border-bottom-color:currentColor;font-weight:600}.cvtkAW_card{border:1px solid color-mix(in srgb, currentColor 14%, transparent);border-radius:12px;flex-direction:column;gap:10px;padding:14px 16px;display:flex}.cvtkAW_cardTitle{letter-spacing:.04em;text-transform:uppercase;opacity:.6;font-size:12px;font-weight:600}.cvtkAW_dayPicker{align-items:center;gap:8px;display:flex}.cvtkAW_dayPickerLabel{opacity:.6;font-size:11px}.cvtkAW_daySelect{border:1px solid color-mix(in srgb, currentColor 25%, transparent);color:inherit;font:inherit;font-variant-numeric:tabular-nums;background:0 0;border-radius:6px;padding:3px 8px;font-size:12px}.cvtkAW_daySelect:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_daySelect option,.cvtkAW_settingItem select option{background-color:var(--dsw-alias-bg-multi-select,Canvas);color:var(--dsw-alias-label-primary,CanvasText)}.cvtkAW_statRow{flex-wrap:wrap;gap:18px;display:flex}.cvtkAW_stat{flex-direction:column;gap:2px;display:flex}.cvtkAW_statValue{font-variant-numeric:tabular-nums;font-size:18px;font-weight:600}.cvtkAW_statLabel{opacity:.6;font-size:11px}.cvtkAW_providerRow{border-top:1px solid color-mix(in srgb, currentColor 8%, transparent);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;padding:6px 0;font-size:13px;display:flex}.cvtkAW_providerRow:first-of-type{border-top:none}.cvtkAW_providerName{align-items:center;gap:8px;min-width:0;display:flex}.cvtkAW_providerTokens{font-variant-numeric:tabular-nums;opacity:.75;white-space:nowrap}.cvtkAW_providerBalance{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.cvtkAW_currentBadge{border:1px solid color-mix(in srgb, currentColor 35%, transparent);opacity:.8;border-radius:999px;flex:none;padding:1px 7px;font-size:10px;font-weight:600}.cvtkAW_chart{flex-direction:column;gap:12px;display:flex}.cvtkAW_chartProvider{flex-direction:column;gap:4px;display:flex}.cvtkAW_chartHead{justify-content:space-between;align-items:baseline;gap:10px;font-size:13px;display:flex}.cvtkAW_chartTokens{font-variant-numeric:tabular-nums;opacity:.65;white-space:nowrap;font-size:11px}.cvtkAW_chartBar{background:color-mix(in srgb, currentColor 8%, transparent);border-radius:999px;height:8px;display:block;overflow:hidden}.cvtkAW_chartFill{background:color-mix(in srgb, currentColor 55%, transparent);border-radius:999px;height:100%;transition:width .3s;display:block}.cvtkAW_chartModel{opacity:.8;grid-template-columns:minmax(80px,180px) 1fr auto;align-items:center;gap:8px;padding-left:14px;font-size:11px;display:grid}.cvtkAW_chartModelName{text-overflow:ellipsis;white-space:nowrap;opacity:.8;overflow:hidden}.cvtkAW_chartModelBar{background:color-mix(in srgb, currentColor 6%, transparent);border-radius:999px;height:4px;display:block;overflow:hidden}.cvtkAW_chartModelFill{background:color-mix(in srgb, currentColor 35%, transparent);border-radius:999px;height:100%;transition:width .3s;display:block}.cvtkAW_trendAxis{opacity:.5;font-variant-numeric:tabular-nums;justify-content:space-between;font-size:10px;display:flex}.cvtkAW_muted{opacity:.6;font-size:12px}.cvtkAW_voucherPreview canvas{border-radius:8px;width:100%;height:auto;display:block}.cvtkAW_buttonRow{gap:8px;display:flex}.cvtkAW_errorLine{opacity:.75;font-size:12px}.cvtkAW_planCard{flex-direction:column;gap:8px;display:flex}.cvtkAW_planHead{justify-content:space-between;align-items:baseline;gap:10px;display:flex}.cvtkAW_planName{font-size:14px;font-weight:600}.cvtkAW_windowRow{flex-direction:column;gap:4px;display:flex}.cvtkAW_windowLabel{opacity:.8;font-variant-numeric:tabular-nums;justify-content:space-between;font-size:12px;display:flex}.cvtkAW_bar{background:color-mix(in srgb, currentColor 10%, transparent);border-radius:999px;height:6px;overflow:hidden}.cvtkAW_barFill{background:color-mix(in srgb, currentColor 55%, transparent);border-radius:999px;height:100%;transition:width .3s}.cvtkAW_barWarn{background:#d97706}.cvtkAW_barLow{background:#dc2626}.cvtkAW_resetLine{opacity:.55;font-variant-numeric:tabular-nums;font-size:11px}.cvtkAW_settingsGrid{flex-wrap:wrap;align-items:center;gap:16px;display:flex}.cvtkAW_settingItem{align-items:center;gap:8px;font-size:13px;display:flex}.cvtkAW_settingItem input[type=checkbox]{accent-color:currentColor}.cvtkAW_settingItem input[type=number]{border:1px solid color-mix(in srgb, currentColor 25%, transparent);width:90px;color:inherit;font:inherit;background:0 0;border-radius:6px;padding:3px 8px;font-size:13px}.cvtkAW_settingItem select{border:1px solid color-mix(in srgb, currentColor 25%, transparent);color:inherit;font:inherit;background:0 0;border-radius:6px;padding:3px 8px;font-size:13px}.cvtkAW_settingItem input:focus-visible,.cvtkAW_settingItem select:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_footCard{box-sizing:border-box;background:color-mix(in srgb, currentColor 4%, transparent);width:100%;color:inherit;border:none;border-radius:12px;margin:2px 0 4px;transition:background-color .12s;position:relative}.cvtkAW_footCard:hover{background:var(--dsw-alias-interactive-bg-hover)}.cvtkAW_footCardCollapsed{background:0 0}.cvtkAW_footMain{appearance:none;box-sizing:border-box;width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:12px;flex-direction:column;gap:4px;padding:8px;display:flex}.cvtkAW_footMain:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_footToggle{appearance:none;width:20px;height:20px;color:inherit;cursor:pointer;opacity:.55;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;transition:opacity .12s,background-color .12s;display:inline-flex;position:absolute;top:6px;right:6px}.cvtkAW_footCard:hover .cvtkAW_footToggle{opacity:.8}.cvtkAW_footToggle:hover{opacity:1;background:color-mix(in srgb, currentColor 10%, transparent)}.cvtkAW_footToggle:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, currentColor 45%, transparent);outline:none}.cvtkAW_footToggle svg{display:block}.cvtkAW_footHead{justify-content:space-between;align-items:baseline;gap:8px;display:flex}.cvtkAW_footTitle{opacity:.65;white-space:nowrap;align-items:center;gap:6px;min-width:0;font-size:12px;display:inline-flex}.cvtkAW_footTitle svg{flex:none;display:block}.cvtkAW_footValue{font-variant-numeric:tabular-nums;white-space:nowrap;margin-right:18px;font-size:15px;font-weight:600}.cvtkAW_footLine{opacity:.7;font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.cvtkAW_footMeta{opacity:.5;font-variant-numeric:tabular-nums;font-size:10px}.cvtkAW_footCardCollapsed .cvtkAW_footMain{min-height:36px;padding:7px 8px}.cvtkAW_footStrip{white-space:nowrap;align-items:center;gap:6px;padding-right:20px;font-size:12px;display:flex;overflow:hidden}.cvtkAW_footStrip svg{opacity:.65;flex:none;display:block}.cvtkAW_footStripProvider{text-overflow:ellipsis;flex:none;max-width:45%;font-weight:500;overflow:hidden}.cvtkAW_footStripLabel{opacity:.65;text-overflow:ellipsis;overflow:hidden}.cvtkAW_footStripValue{font-variant-numeric:tabular-nums;margin-left:auto;font-weight:600}[data-dsh-frame][data-sidebar-collapsed] .cvtkAW_footCard,[data-sidebar-collapsed] .cvtkAW_footCard{display:none}";
193
209
  const tagId = "@linxin666/dsh-usage/packages/dsh-usage/src/client/usage.module.css";
194
210
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
195
211
  const tag = document.createElement("style");
@@ -218,6 +234,9 @@ window.__ModuleLoader__.load({
218
234
  "chartTokens": "cvtkAW_chartTokens",
219
235
  "currentBadge": "cvtkAW_currentBadge",
220
236
  "currentProvider": "cvtkAW_currentProvider",
237
+ "dayPicker": "cvtkAW_dayPicker",
238
+ "dayPickerLabel": "cvtkAW_dayPickerLabel",
239
+ "daySelect": "cvtkAW_daySelect",
221
240
  "errorLine": "cvtkAW_errorLine",
222
241
  "footCard": "cvtkAW_footCard",
223
242
  "footCardCollapsed": "cvtkAW_footCardCollapsed",
@@ -305,6 +324,7 @@ window.__ModuleLoader__.load({
305
324
  }
306
325
  const DEEPSEEK = {
307
326
  ids: ["deepseek", "deepseek-official"],
327
+ familyOnlyIds: ["deepseek-account"],
308
328
  displayName: "DeepSeek",
309
329
  balance: {
310
330
  origin: "https://api.deepseek.com",
@@ -690,13 +710,268 @@ window.__ModuleLoader__.load({
690
710
  /**
691
711
  * Whether a provider route belongs to the official DeepSeek family: the only
692
712
  * family with a spend price book and a settings-section-owned env credential
693
- * (llm-deepseek) rather than a pi-ai profile. Drives the env fallback in
694
- * credential resolution and the fold-time cost stamping.
713
+ * (llm-deepseek) rather than a pi-ai profile. Drives the fold-time cost
714
+ * stamping, the client fold-time spend estimate, and the whale-yuan bank.
715
+ *
716
+ * Family membership is wider than the probe directory. The account route
717
+ * (deepseek-account) bills the same account but authenticates with the account
718
+ * token, so it prices and mints while `adapterFor()` still returns undefined
719
+ * for it and no probe or env-credential fallback can reach it. Every caller of
720
+ * this predicate is an accounting decision; the credential and probing
721
+ * decisions read `adapterFor()` instead, so the two never widen together
722
+ * (issue #1772).
695
723
  */
696
724
  function isDeepSeekProviderRoute(provider) {
697
- return adapterFor(provider) === DEEPSEEK;
725
+ if (adapterFor(provider) === DEEPSEEK) return true;
726
+ return DEEPSEEK.familyOnlyIds?.includes(provider) === true;
698
727
  }
699
728
  //#endregion
729
+ //#region src/core/holidays.ts
730
+ /**
731
+ * Chinese public-holiday dates, Beijing calendar days, as `YYYY-MM-DD`.
732
+ *
733
+ * The DeepSeek peak rule bills a public holiday off-peak in full, and the
734
+ * State Council publishes these dates per year, so they cannot be derived
735
+ * from a rule. This module is that data; `pricing.ts` keeps the arithmetic.
736
+ *
737
+ * Adjusted workdays are deliberately absent: the provider counts the
738
+ * calendar day, so an adjusted Saturday or Sunday is off-peak exactly like
739
+ * any other weekend and listing it would price it as peak.
740
+ *
741
+ * Source: the yearly schedules in bastengao/chinese-holidays-node
742
+ * (`data/<year>.json`, MIT). Only the `holiday` entries are taken; that
743
+ * package's runtime code (which fetches over the network) and its
744
+ * dependencies are not vendored.
745
+ *
746
+ * Coverage ends at the newest year below. A year with no entry is priced as
747
+ * if it had no holidays, which is the documented default of
748
+ * {@link PublicHolidayCalendar} in `pricing.ts`.
749
+ * @module @linxin666/dsh-usage/core/holidays
750
+ */
751
+ /** Public-holiday dates keyed by Beijing calendar year. */
752
+ const CHINESE_PUBLIC_HOLIDAYS = {
753
+ "2020": [
754
+ "2020-01-24",
755
+ "2020-01-25",
756
+ "2020-01-26",
757
+ "2020-01-27",
758
+ "2020-01-28",
759
+ "2020-01-29",
760
+ "2020-01-30",
761
+ "2020-04-04",
762
+ "2020-04-05",
763
+ "2020-04-06",
764
+ "2020-05-01",
765
+ "2020-05-02",
766
+ "2020-05-03",
767
+ "2020-05-04",
768
+ "2020-05-05",
769
+ "2020-06-25",
770
+ "2020-06-26",
771
+ "2020-06-27",
772
+ "2020-10-01",
773
+ "2020-10-02",
774
+ "2020-10-03",
775
+ "2020-10-04",
776
+ "2020-10-05",
777
+ "2020-10-06",
778
+ "2020-10-07",
779
+ "2020-10-08"
780
+ ],
781
+ "2021": [
782
+ "2021-01-01",
783
+ "2021-01-02",
784
+ "2021-01-03",
785
+ "2021-02-11",
786
+ "2021-02-12",
787
+ "2021-02-13",
788
+ "2021-02-14",
789
+ "2021-02-15",
790
+ "2021-02-16",
791
+ "2021-02-17",
792
+ "2021-04-03",
793
+ "2021-04-04",
794
+ "2021-04-05",
795
+ "2021-05-01",
796
+ "2021-05-02",
797
+ "2021-05-03",
798
+ "2021-05-04",
799
+ "2021-05-05",
800
+ "2021-06-12",
801
+ "2021-06-13",
802
+ "2021-06-14",
803
+ "2021-09-19",
804
+ "2021-09-20",
805
+ "2021-09-21",
806
+ "2021-10-01",
807
+ "2021-10-02",
808
+ "2021-10-03",
809
+ "2021-10-04",
810
+ "2021-10-05",
811
+ "2021-10-06",
812
+ "2021-10-07"
813
+ ],
814
+ "2022": [
815
+ "2022-01-01",
816
+ "2022-01-02",
817
+ "2022-01-03",
818
+ "2022-01-31",
819
+ "2022-02-01",
820
+ "2022-02-02",
821
+ "2022-02-03",
822
+ "2022-02-04",
823
+ "2022-02-05",
824
+ "2022-02-06",
825
+ "2022-04-03",
826
+ "2022-04-04",
827
+ "2022-04-05",
828
+ "2022-04-30",
829
+ "2022-05-01",
830
+ "2022-05-02",
831
+ "2022-05-03",
832
+ "2022-05-04",
833
+ "2022-06-03",
834
+ "2022-06-04",
835
+ "2022-06-05",
836
+ "2022-09-10",
837
+ "2022-09-11",
838
+ "2022-09-12",
839
+ "2022-10-01",
840
+ "2022-10-02",
841
+ "2022-10-03",
842
+ "2022-10-04",
843
+ "2022-10-05",
844
+ "2022-10-06",
845
+ "2022-10-07"
846
+ ],
847
+ "2023": [
848
+ "2022-12-31",
849
+ "2023-01-01",
850
+ "2023-01-02",
851
+ "2023-01-21",
852
+ "2023-01-22",
853
+ "2023-01-23",
854
+ "2023-01-24",
855
+ "2023-01-25",
856
+ "2023-01-26",
857
+ "2023-01-27",
858
+ "2023-04-29",
859
+ "2023-04-30",
860
+ "2023-05-01",
861
+ "2023-05-02",
862
+ "2023-05-03",
863
+ "2023-06-22",
864
+ "2023-06-23",
865
+ "2023-06-24",
866
+ "2023-09-29",
867
+ "2023-09-30",
868
+ "2023-10-01",
869
+ "2023-10-02",
870
+ "2023-10-03",
871
+ "2023-10-04",
872
+ "2023-10-05",
873
+ "2023-10-06"
874
+ ],
875
+ "2024": [
876
+ "2023-12-30",
877
+ "2023-12-31",
878
+ "2024-01-01",
879
+ "2024-02-10",
880
+ "2024-02-11",
881
+ "2024-02-12",
882
+ "2024-02-13",
883
+ "2024-02-14",
884
+ "2024-02-15",
885
+ "2024-02-16",
886
+ "2024-02-17",
887
+ "2024-04-04",
888
+ "2024-04-05",
889
+ "2024-04-06",
890
+ "2024-05-01",
891
+ "2024-05-02",
892
+ "2024-05-03",
893
+ "2024-05-04",
894
+ "2024-05-05",
895
+ "2024-06-08",
896
+ "2024-06-09",
897
+ "2024-06-10",
898
+ "2024-09-15",
899
+ "2024-09-16",
900
+ "2024-09-17",
901
+ "2024-10-01",
902
+ "2024-10-02",
903
+ "2024-10-03",
904
+ "2024-10-04",
905
+ "2024-10-05",
906
+ "2024-10-06",
907
+ "2024-10-07"
908
+ ],
909
+ "2025": [
910
+ "2025-01-28",
911
+ "2025-01-29",
912
+ "2025-01-30",
913
+ "2025-01-31",
914
+ "2025-02-01",
915
+ "2025-02-02",
916
+ "2025-02-03",
917
+ "2025-02-04",
918
+ "2025-04-04",
919
+ "2025-04-05",
920
+ "2025-04-06",
921
+ "2025-05-01",
922
+ "2025-05-02",
923
+ "2025-05-03",
924
+ "2025-05-04",
925
+ "2025-05-05",
926
+ "2025-05-31",
927
+ "2025-06-01",
928
+ "2025-06-02",
929
+ "2025-10-01",
930
+ "2025-10-02",
931
+ "2025-10-03",
932
+ "2025-10-04",
933
+ "2025-10-05",
934
+ "2025-10-06",
935
+ "2025-10-07",
936
+ "2025-10-08"
937
+ ],
938
+ "2026": [
939
+ "2026-01-01",
940
+ "2026-01-02",
941
+ "2026-01-03",
942
+ "2026-02-15",
943
+ "2026-02-16",
944
+ "2026-02-17",
945
+ "2026-02-18",
946
+ "2026-02-19",
947
+ "2026-02-20",
948
+ "2026-02-21",
949
+ "2026-02-22",
950
+ "2026-02-23",
951
+ "2026-04-04",
952
+ "2026-04-05",
953
+ "2026-04-06",
954
+ "2026-05-01",
955
+ "2026-05-02",
956
+ "2026-05-03",
957
+ "2026-05-04",
958
+ "2026-05-05",
959
+ "2026-06-19",
960
+ "2026-06-20",
961
+ "2026-06-21",
962
+ "2026-09-25",
963
+ "2026-09-26",
964
+ "2026-09-27",
965
+ "2026-10-01",
966
+ "2026-10-02",
967
+ "2026-10-03",
968
+ "2026-10-04",
969
+ "2026-10-05",
970
+ "2026-10-06",
971
+ "2026-10-07"
972
+ ]
973
+ };
974
+ //#endregion
700
975
  //#region src/core/pricing.ts
701
976
  /** The published peak windows: weekday 09:00-12:00 and 14:00-18:00. */
702
977
  const PEAK_WINDOWS = [{
@@ -712,23 +987,52 @@ window.__ModuleLoader__.load({
712
987
  return PEAK_WINDOWS.find((window) => minuteOfDay >= window.from && minuteOfDay < window.to);
713
988
  }
714
989
  /**
990
+ * The default calendar: the published Chinese public-holiday table, so a holiday
991
+ * weekday prices off-peak instead of over-reporting at the peak rate. A year the
992
+ * table does not cover holds no dates and falls back to peak on weekdays, which
993
+ * is the safe direction to be wrong in: the estimate reads high rather than low.
994
+ */
995
+ const CN_PUBLIC_HOLIDAYS = { isPublicHoliday: (date) => {
996
+ const year = date.slice(0, 4);
997
+ if (Number(year) > 2026) return false;
998
+ return CHINESE_PUBLIC_HOLIDAYS[year]?.includes(date) ?? false;
999
+ } };
1000
+ /** The Beijing-time `YYYY-MM-DD` calendar day of `ms` (UTC+8 has no DST, so the shift is exact). */
1001
+ function beijingDate(ms) {
1002
+ const shifted = new Date(ms + BEIJING_UTC_OFFSET_MS);
1003
+ const month = `${shifted.getUTCMonth() + 1}`.padStart(2, "0");
1004
+ const day = `${shifted.getUTCDate()}`.padStart(2, "0");
1005
+ return `${shifted.getUTCFullYear()}-${month}-${day}`;
1006
+ }
1007
+ /**
1008
+ * Whether `ms` falls on a peak-eligible Beijing day: Monday-Friday that is
1009
+ * not a public holiday. A Saturday or Sunday is never eligible, including on
1010
+ * an adjusted workday — the provider counts the calendar day, not the
1011
+ * working schedule.
1012
+ */
1013
+ function isPeakDay(ms, calendar) {
1014
+ const weekday = new Date(ms + BEIJING_UTC_OFFSET_MS).getUTCDay();
1015
+ if (weekday < 1 || weekday > 5) return false;
1016
+ return !calendar.isPublicHoliday(beijingDate(ms));
1017
+ }
1018
+ /**
715
1019
  * The DeepSeek billing period at `ms`, plus when it next flips. The clock is
716
1020
  * Beijing time regardless of the host timezone (UTC+8 has no DST, so a fixed
717
1021
  * shift is exact). `boundaryMs` is the instant the current period ends — the
718
1022
  * window's close while peaking, the next window's open otherwise.
719
1023
  */
720
- function deepseekPeriodAt(ms) {
1024
+ function deepseekPeriodAt(ms, calendar = CN_PUBLIC_HOLIDAYS) {
721
1025
  const shifted = new Date(ms + BEIJING_UTC_OFFSET_MS);
722
- const weekday = shifted.getUTCDay();
723
1026
  const minuteOfDay = shifted.getUTCHours() * 60 + shifted.getUTCMinutes();
724
- const current = weekday >= 1 && weekday <= 5 ? withinWindow(minuteOfDay) : void 0;
1027
+ const current = isPeakDay(ms, calendar) ? withinWindow(minuteOfDay) : void 0;
725
1028
  if (current !== void 0) return {
726
1029
  peak: true,
727
1030
  boundaryMs: ms + (current.to - minuteOfDay) * 6e4 - shifted.getUTCSeconds() * 1e3 - shifted.getUTCMilliseconds()
728
1031
  };
729
1032
  for (let dayOffset = 0; dayOffset < 8; dayOffset += 1) {
730
- const day = new Date(ms + BEIJING_UTC_OFFSET_MS + dayOffset * 864e5);
731
- if (day.getUTCDay() < 1 || day.getUTCDay() > 5) continue;
1033
+ const dayStartMs = ms + dayOffset * 864e5;
1034
+ if (!isPeakDay(dayStartMs, calendar)) continue;
1035
+ const day = new Date(dayStartMs + BEIJING_UTC_OFFSET_MS);
732
1036
  const realDayStart = Date.UTC(day.getUTCFullYear(), day.getUTCMonth(), day.getUTCDate()) - BEIJING_UTC_OFFSET_MS;
733
1037
  for (const window of PEAK_WINDOWS) {
734
1038
  if (dayOffset === 0 && window.from <= minuteOfDay) continue;
@@ -888,6 +1192,11 @@ window.__ModuleLoader__.load({
888
1192
  */
889
1193
  /** Poll cadence while the section is open. */
890
1194
  const SECTION_POLL_MS = 1e4;
1195
+ /**
1196
+ * The Token bank's default window: the whole retained ledger. It is a select
1197
+ * value rather than a date, so it can never collide with a day key.
1198
+ */
1199
+ const BANK_ALL_DAYS = "all";
891
1200
  /** Compact token count: 12345 -> 12.3k, 1234567 -> 1.23M. */
892
1201
  function formatTokens(value) {
893
1202
  if (!Number.isFinite(value) || value <= 0) return "0";
@@ -1041,13 +1350,83 @@ window.__ModuleLoader__.load({
1041
1350
  })]
1042
1351
  });
1043
1352
  }
1353
+ /**
1354
+ * Fetch the day a picker selected, or stay idle for the default window. Today
1355
+ * never fetches: the overview already carries it live, so switching back to
1356
+ * today is instant and keeps counting with the poll. A superseded selection
1357
+ * never lands (the effect cancels), and a failed load reports its own message
1358
+ * instead of leaving the card on a stale day.
1359
+ */
1360
+ function useDayView(loadDay, date, today) {
1361
+ const [load, setLoad] = (0, react.useState)(void 0);
1362
+ (0, react.useEffect)(() => {
1363
+ if (date === void 0 || date === today) {
1364
+ setLoad((previous) => previous === void 0 ? previous : void 0);
1365
+ return;
1366
+ }
1367
+ let cancelled = false;
1368
+ setLoad({ status: "loading" });
1369
+ loadDay(date).then((view) => {
1370
+ if (!cancelled) setLoad({
1371
+ status: "ready",
1372
+ view
1373
+ });
1374
+ }, (error) => {
1375
+ if (!cancelled) setLoad({
1376
+ status: "error",
1377
+ error: error instanceof Error ? error.message : String(error)
1378
+ });
1379
+ });
1380
+ return () => {
1381
+ cancelled = true;
1382
+ };
1383
+ }, [
1384
+ date,
1385
+ today,
1386
+ loadDay
1387
+ ]);
1388
+ return load;
1389
+ }
1390
+ /**
1391
+ * The day picker: today plus every retained day that recorded usage, newest
1392
+ * first (the host serves the list ascending). Without that list (an older host
1393
+ * document) no picker renders and the card stays on its default window.
1394
+ */
1395
+ function DayPicker(props) {
1396
+ const { value, options, today, leading, onSelect, part } = props;
1397
+ if (options.length === 0) return null;
1398
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1399
+ className: usage_module_css_default.dayPicker,
1400
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1401
+ className: usage_module_css_default.dayPickerLabel,
1402
+ htmlFor: "dsh-usage-day-" + part,
1403
+ children: t("usage.day.label")
1404
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1405
+ id: "dsh-usage-day-" + part,
1406
+ className: usage_module_css_default.daySelect,
1407
+ value,
1408
+ onChange: (event) => {
1409
+ onSelect(event.target.value);
1410
+ },
1411
+ children: [leading !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1412
+ value: leading.value,
1413
+ children: leading.label
1414
+ }), options.map((date) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1415
+ value: date,
1416
+ children: date === today ? t("usage.day.today") : date
1417
+ }, date))]
1418
+ })]
1419
+ });
1420
+ }
1044
1421
  /** The section component; the slot merges the face into these props. */
1045
1422
  function UsageSectionCard(props) {
1046
- const { store, poll, refresh, settings } = props;
1423
+ const { store, poll, refresh, loadDay, settings } = props;
1047
1424
  const ui = (0, react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
1048
1425
  const settingsSnapshot = settings.getSnapshot();
1049
1426
  const settingsValue = settingsSnapshot.value ?? {};
1050
1427
  const [tab, setTab] = (0, react.useState)("usage");
1428
+ const [usageDay, setUsageDay] = (0, react.useState)(void 0);
1429
+ const [bankDay, setBankDay] = (0, react.useState)(void 0);
1051
1430
  const [refreshing, setRefreshing] = (0, react.useState)(false);
1052
1431
  const [, bumpSettings] = (0, react.useState)(0);
1053
1432
  (0, react.useEffect)(() => settings.subscribe(() => bumpSettings((count) => count + 1)), [settings]);
@@ -1076,6 +1455,22 @@ window.__ModuleLoader__.load({
1076
1455
  };
1077
1456
  }, [poll, enabled]);
1078
1457
  const snapshot = ui.snapshot;
1458
+ const todayDate = snapshot?.usage.today.date;
1459
+ const dayOptions = snapshot === null ? [] : [...new Set([todayDate, ...snapshot.usage.availableDays ?? []].filter((date) => date !== void 0))].sort().reverse();
1460
+ const usageSelection = usageDay !== void 0 && dayOptions.includes(usageDay) ? usageDay : void 0;
1461
+ const bankSelection = bankDay !== void 0 && dayOptions.includes(bankDay) ? bankDay : void 0;
1462
+ const selectedDay = usageSelection ?? todayDate;
1463
+ const isToday = selectedDay === todayDate;
1464
+ const usageDayLoad = useDayView(loadDay, usageSelection, todayDate);
1465
+ const bankDayLoad = useDayView(loadDay, bankSelection, todayDate);
1466
+ const day = isToday ? snapshot?.usage.today : usageDayLoad?.view;
1467
+ const bankDayView = bankSelection === void 0 ? void 0 : bankSelection === todayDate ? snapshot?.usage.today : bankDayLoad?.view;
1468
+ const bankWindow = bankSelection === void 0 ? snapshot?.usage.all ?? snapshot?.usage.range : bankDayView === void 0 ? void 0 : {
1469
+ from: bankDayView.date,
1470
+ to: bankDayView.date,
1471
+ totals: bankDayView.totals,
1472
+ providers: bankDayView.providers
1473
+ };
1079
1474
  const onRefresh = () => {
1080
1475
  setRefreshing(true);
1081
1476
  try {
@@ -1100,7 +1495,7 @@ window.__ModuleLoader__.load({
1100
1495
  const current = snapshot.current;
1101
1496
  const currentProvider = snapshot.providers.find((provider) => provider.provider === current.provider);
1102
1497
  const deepseekPeriod = deepseekPeriodAt(Date.now());
1103
- const deepseekVisible = current.provider !== void 0 && isDeepSeekProviderRoute(current.provider) || snapshot.usage.today.providers.some((row) => isDeepSeekProviderRoute(row.provider));
1498
+ const deepseekVisible = isToday && (current.provider !== void 0 && isDeepSeekProviderRoute(current.provider) || snapshot.usage.today.providers.some((row) => isDeepSeekProviderRoute(row.provider)));
1104
1499
  const planProviders = snapshot.providers.filter((provider) => isConfigured(provider) && (provider.planSupported === true || provider.planSupported === void 0 && provider.plan !== void 0));
1105
1500
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1106
1501
  className: usage_module_css_default.section,
@@ -1168,31 +1563,46 @@ window.__ModuleLoader__.load({
1168
1563
  children: [
1169
1564
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1170
1565
  className: usage_module_css_default.cardTitle,
1171
- children: t("usage.today")
1566
+ children: isToday ? t("usage.today") : t("usage.day.title", { date: selectedDay ?? "" })
1567
+ }),
1568
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DayPicker, {
1569
+ part: "usage",
1570
+ value: selectedDay ?? "",
1571
+ options: dayOptions,
1572
+ today: todayDate ?? "",
1573
+ onSelect: (date) => {
1574
+ setUsageDay(date === todayDate ? void 0 : date);
1575
+ }
1172
1576
  }),
1173
1577
  deepseekVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1174
1578
  className: usage_module_css_default.muted,
1175
1579
  "data-dsh-part": "peak-status",
1176
1580
  children: t(deepseekPeriod.peak ? "usage.peak.on" : "usage.peak.off", { time: formatClock$1(deepseekPeriod.boundaryMs) })
1177
1581
  }),
1178
- snapshot.usage.today.totals.calls === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1582
+ usageDayLoad?.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1583
+ className: usage_module_css_default.errorLine,
1584
+ children: t("usage.day.error", { error: usageDayLoad.error ?? "" })
1585
+ }) : day === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1586
+ className: usage_module_css_default.muted,
1587
+ children: t("usage.day.loading")
1588
+ }) : day.totals.calls === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1179
1589
  className: usage_module_css_default.muted,
1180
- children: t("usage.noData")
1181
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TotalsRow, { totals: snapshot.usage.today.totals }),
1182
- snapshot.usage.today.totals.cost > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1590
+ children: isToday ? t("usage.noData") : t("usage.day.empty")
1591
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TotalsRow, { totals: day.totals }),
1592
+ (day?.totals.cost ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1183
1593
  className: usage_module_css_default.providerRow,
1184
1594
  "data-dsh-part": "today-cost",
1185
1595
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1186
1596
  className: usage_module_css_default.providerName,
1187
- children: t("usage.today.cost")
1597
+ children: isToday ? t("usage.today.cost") : t("usage.day.cost")
1188
1598
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1189
1599
  className: usage_module_css_default.providerTokens,
1190
- children: formatCost(snapshot.usage.today.totals.cost)
1600
+ children: formatCost(day?.totals.cost ?? 0)
1191
1601
  })]
1192
1602
  }),
1193
- snapshot.usage.today.providers.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1603
+ (day?.providers.length ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1194
1604
  "data-dsh-part": "provider-list",
1195
- children: snapshot.usage.today.providers.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1605
+ children: (day?.providers ?? []).map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1196
1606
  className: usage_module_css_default.providerRow,
1197
1607
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1198
1608
  className: usage_module_css_default.providerName,
@@ -1260,10 +1670,24 @@ window.__ModuleLoader__.load({
1260
1670
  provider,
1261
1671
  current: current.provider
1262
1672
  }, provider.provider))),
1263
- tab === "bank" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VoucherCard, {
1264
- window: snapshot.usage.all ?? snapshot.usage.range,
1265
- observedSpend: snapshot.usage.observedSpend
1266
- })
1673
+ tab === "bank" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DayPicker, {
1674
+ part: "bank",
1675
+ value: bankSelection ?? BANK_ALL_DAYS,
1676
+ options: dayOptions,
1677
+ today: todayDate ?? "",
1678
+ leading: {
1679
+ value: BANK_ALL_DAYS,
1680
+ label: t("usage.bank.windowAll")
1681
+ },
1682
+ onSelect: (date) => {
1683
+ setBankDay(date === BANK_ALL_DAYS ? void 0 : date);
1684
+ }
1685
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VoucherCard, {
1686
+ window: bankWindow,
1687
+ observedSpend: bankSelection === void 0 ? snapshot.usage.observedSpend : void 0,
1688
+ loading: bankSelection !== void 0 && bankDayLoad?.status === "loading",
1689
+ ...bankDayLoad?.status === "error" ? { error: bankDayLoad.error ?? "" } : {}
1690
+ })] })
1267
1691
  ]
1268
1692
  });
1269
1693
  }
@@ -1368,15 +1792,16 @@ window.__ModuleLoader__.load({
1368
1792
  }
1369
1793
  /**
1370
1794
  * The Token 银行 card: the DeepSeek official family's retained-ledger usage
1371
- * minted onto the whale-yuan note at 1,000,000 tokens per whale yuan. The window
1372
- * prefers the host's whole-ledger aggregate and falls back to the 30-day
1373
- * trend when an older host serves no `all`; the spend line prefers the
1374
- * official balance watch and falls back to the fold-time estimate; the
1375
- * artwork draw failure degrades to an error line and never takes the
1376
- * section down.
1795
+ * minted onto the whale-yuan note at 1,000,000 tokens per whale yuan. The
1796
+ * window is the whole retained ledger by default (the host's aggregate,
1797
+ * falling back to the 30-day trend when an older host serves no `all`) or the
1798
+ * single day the user picked; the spend line prefers the official balance
1799
+ * watch (whole window only) and falls back to the fold-time estimate; the
1800
+ * artwork draw failure degrades to an error line and never takes the section
1801
+ * down.
1377
1802
  */
1378
1803
  function VoucherCard(props) {
1379
- const { window: ledger, observedSpend } = props;
1804
+ const { window: ledger, observedSpend, loading, error } = props;
1380
1805
  const data = deepseekVoucherData(ledger);
1381
1806
  const canvasRef = (0, react.useRef)(null);
1382
1807
  const [drawError, setDrawError] = (0, react.useState)(void 0);
@@ -1434,7 +1859,13 @@ window.__ModuleLoader__.load({
1434
1859
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1435
1860
  className: usage_module_css_default.cardTitle,
1436
1861
  children: t("usage.bank.title")
1437
- }), data === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1862
+ }), error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1863
+ className: usage_module_css_default.errorLine,
1864
+ children: t("usage.day.error", { error })
1865
+ }) : loading === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1866
+ className: usage_module_css_default.muted,
1867
+ children: t("usage.day.loading")
1868
+ }) : data === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1438
1869
  className: usage_module_css_default.muted,
1439
1870
  children: t("usage.bank.noUsage")
1440
1871
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
@@ -2221,6 +2652,7 @@ window.__ModuleLoader__.load({
2221
2652
  }
2222
2653
  const usageApi = {
2223
2654
  overview: () => usageFetch("api/dsh-usage/overview", "GET"),
2655
+ day: async (date) => (await usageFetch("api/dsh-usage/day?date=" + encodeURIComponent(date), "GET")).day,
2224
2656
  refresh: () => usageFetch("api/dsh-usage/refresh", "POST")
2225
2657
  };
2226
2658
  /**
@@ -2290,10 +2722,12 @@ window.__ModuleLoader__.load({
2290
2722
  store.actions.setState("error", error instanceof Error ? error.message : String(error));
2291
2723
  });
2292
2724
  };
2725
+ const loadDay = (date) => usageApi.day(date);
2293
2726
  const face = () => ({
2294
2727
  store,
2295
2728
  poll,
2296
2729
  refresh,
2730
+ loadDay,
2297
2731
  settings: settingsForm
2298
2732
  });
2299
2733
  const disposeFootCard = mountUsageFootCard({