@linxin666/dsh-usage 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +21 -5
- package/README.zh.md +21 -5
- package/lib/client.js +501 -47
- package/lib/client.js.map +1 -1
- package/lib/index.js +364 -16
- package/lib/types/client/UsageSectionCard.d.ts +7 -0
- package/lib/types/client/locales.d.ts +8 -0
- package/lib/types/client/voucher.d.ts +27 -9
- package/lib/types/core/adapters.d.ts +28 -2
- package/lib/types/core/holidays.d.ts +25 -0
- package/lib/types/core/ledger.d.ts +8 -0
- package/lib/types/core/pricing.d.ts +34 -6
- package/lib/types/core/types.d.ts +26 -5
- package/lib/types/host/routes.d.ts +11 -0
- package/lib/types/host/usage-service.d.ts +9 -1
- package/package.json +15 -15
- package/src/client/UsageSectionCard.tsx +177 -29
- package/src/client/index.ts +10 -2
- package/src/client/locales.ts +20 -4
- package/src/client/usage.module.css +47 -0
- package/src/client/voucher.ts +33 -11
- package/src/core/adapters.ts +34 -3
- package/src/core/holidays.ts +248 -0
- package/src/core/ledger.ts +14 -5
- package/src/core/pricing.ts +75 -15
- package/src/core/types.ts +28 -1
- package/src/host/routes.ts +31 -0
- package/src/host/usage-service.ts +16 -2
- package/src/index.ts +3 -3
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} 次调用",
|
|
@@ -100,12 +107,13 @@ window.__ModuleLoader__.load({
|
|
|
100
107
|
"usage.config.pollIntervalSec": "轮询间隔(秒)",
|
|
101
108
|
"usage.config.saveFailed": "保存失败:设置未被接受。",
|
|
102
109
|
"usage.bank.title": "鲸元券",
|
|
103
|
-
"usage.bank.hint": "官方 API 每消耗 100 万 tokens 铸造 1 鲸元;保存或分享这张票券。",
|
|
104
|
-
"usage.bank.noUsage": "暂无 DeepSeek
|
|
110
|
+
"usage.bank.hint": "DeepSeek 官方 API 与 MiMo 每消耗 100 万 tokens 铸造 1 鲸元;保存或分享这张票券。",
|
|
111
|
+
"usage.bank.noUsage": "暂无 DeepSeek 官方 / MiMo 用量数据(统计自插件启用起)",
|
|
105
112
|
"usage.bank.minted": "累计铸造 {minted} 鲸元({tokens} tokens)",
|
|
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",
|
|
@@ -158,12 +173,13 @@ window.__ModuleLoader__.load({
|
|
|
158
173
|
"usage.config.pollIntervalSec": "Poll interval (seconds)",
|
|
159
174
|
"usage.config.saveFailed": "Save failed: the deployment did not accept the setting.",
|
|
160
175
|
"usage.bank.title": "Whale-yuan voucher",
|
|
161
|
-
"usage.bank.hint": "Every 1,000,000 tokens spent on the official API mint one whale yuan; save or share the note.",
|
|
162
|
-
"usage.bank.noUsage": "No official
|
|
176
|
+
"usage.bank.hint": "Every 1,000,000 tokens spent on the DeepSeek official API or on MiMo mint one whale yuan; save or share the note.",
|
|
177
|
+
"usage.bank.noUsage": "No DeepSeek official or MiMo usage yet (counting starts when the plugin is enabled)",
|
|
163
178
|
"usage.bank.minted": "Minted {minted} whale yuan ({tokens} tokens)",
|
|
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
|
|
694
|
-
*
|
|
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
|
-
|
|
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 =
|
|
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
|
|
731
|
-
if (
|
|
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;
|
|
@@ -763,8 +1067,8 @@ window.__ModuleLoader__.load({
|
|
|
763
1067
|
//#endregion
|
|
764
1068
|
//#region src/client/voucher.ts
|
|
765
1069
|
/**
|
|
766
|
-
* The Whale-yuan voucher (鲸元券): the
|
|
767
|
-
*
|
|
1070
|
+
* The Whale-yuan voucher (鲸元券): the mint family's retained ledger totals
|
|
1071
|
+
* stamped onto the banknote artwork as a denomination, ready
|
|
768
1072
|
* to save or share. The canvas copy is deliberately locale-neutral (digits,
|
|
769
1073
|
* latin captions, ISO dates) so the exported image needs no dictionary;
|
|
770
1074
|
* everything user-facing around it lives in the section's locales. The pure
|
|
@@ -786,21 +1090,41 @@ window.__ModuleLoader__.load({
|
|
|
786
1090
|
return `${date.getFullYear()}-${month}-${day}`;
|
|
787
1091
|
}
|
|
788
1092
|
/**
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
*
|
|
792
|
-
*
|
|
1093
|
+
* Provider route ids minted beside the DeepSeek official family: the MiMo
|
|
1094
|
+
* gateway, which a profile may run as its default route and which the ledger
|
|
1095
|
+
* records under its own route id. The bank counts the tokens a profile spends,
|
|
1096
|
+
* not only the ones DeepSeek bills. Route ids rather than adapter families
|
|
1097
|
+
* because minting is an accounting decision that must never widen the
|
|
1098
|
+
* credential or probing paths — the same split `isDeepSeekProviderRoute()`
|
|
1099
|
+
* keeps against `adapterFor()` (issue #1772). An empty list mints exactly the
|
|
1100
|
+
* official family, which is how a profile that never runs MiMo behaves.
|
|
1101
|
+
*/
|
|
1102
|
+
const EXTRA_MINT_ROUTE_IDS = ["mimo"];
|
|
1103
|
+
/** Whether one provider route's ledger rows mint whale yuan. */
|
|
1104
|
+
function mintsWhaleYuan(provider) {
|
|
1105
|
+
return isDeepSeekProviderRoute(provider) || EXTRA_MINT_ROUTE_IDS.includes(provider);
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Sum the mint family's rows out of a usage window: the whole DeepSeek
|
|
1109
|
+
* official family (the `deepseek` catalog alias, the live `deepseek-official`
|
|
1110
|
+
* route and the signed-in `deepseek-account` route all fold into one voucher)
|
|
1111
|
+
* plus {@link EXTRA_MINT_ROUTE_IDS}. Undefined when the window is absent
|
|
1112
|
+
* (older host) or the family has no usage — a zero-token note mints nothing.
|
|
1113
|
+
*
|
|
1114
|
+
* Only the priced family contributes `cost`: MiMo ships no price book, so it
|
|
1115
|
+
* mints tokens and calls while the spend line keeps reporting the official
|
|
1116
|
+
* family's fold-time estimate (and the observed-balance watch above it).
|
|
793
1117
|
*/
|
|
794
|
-
function
|
|
1118
|
+
function voucherData(window) {
|
|
795
1119
|
if (window === void 0) return void 0;
|
|
796
1120
|
let tokens = 0;
|
|
797
1121
|
let calls = 0;
|
|
798
1122
|
let cost = 0;
|
|
799
1123
|
for (const row of window.providers) {
|
|
800
|
-
if (!
|
|
1124
|
+
if (!mintsWhaleYuan(row.provider)) continue;
|
|
801
1125
|
tokens += totalTokens(row.totals);
|
|
802
1126
|
calls += row.totals.calls;
|
|
803
|
-
cost += row.totals.cost;
|
|
1127
|
+
if (isDeepSeekProviderRoute(row.provider)) cost += row.totals.cost;
|
|
804
1128
|
}
|
|
805
1129
|
if (tokens <= 0) return void 0;
|
|
806
1130
|
return {
|
|
@@ -888,6 +1212,11 @@ window.__ModuleLoader__.load({
|
|
|
888
1212
|
*/
|
|
889
1213
|
/** Poll cadence while the section is open. */
|
|
890
1214
|
const SECTION_POLL_MS = 1e4;
|
|
1215
|
+
/**
|
|
1216
|
+
* The Token bank's default window: the whole retained ledger. It is a select
|
|
1217
|
+
* value rather than a date, so it can never collide with a day key.
|
|
1218
|
+
*/
|
|
1219
|
+
const BANK_ALL_DAYS = "all";
|
|
891
1220
|
/** Compact token count: 12345 -> 12.3k, 1234567 -> 1.23M. */
|
|
892
1221
|
function formatTokens(value) {
|
|
893
1222
|
if (!Number.isFinite(value) || value <= 0) return "0";
|
|
@@ -1041,13 +1370,83 @@ window.__ModuleLoader__.load({
|
|
|
1041
1370
|
})]
|
|
1042
1371
|
});
|
|
1043
1372
|
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Fetch the day a picker selected, or stay idle for the default window. Today
|
|
1375
|
+
* never fetches: the overview already carries it live, so switching back to
|
|
1376
|
+
* today is instant and keeps counting with the poll. A superseded selection
|
|
1377
|
+
* never lands (the effect cancels), and a failed load reports its own message
|
|
1378
|
+
* instead of leaving the card on a stale day.
|
|
1379
|
+
*/
|
|
1380
|
+
function useDayView(loadDay, date, today) {
|
|
1381
|
+
const [load, setLoad] = (0, react.useState)(void 0);
|
|
1382
|
+
(0, react.useEffect)(() => {
|
|
1383
|
+
if (date === void 0 || date === today) {
|
|
1384
|
+
setLoad((previous) => previous === void 0 ? previous : void 0);
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
let cancelled = false;
|
|
1388
|
+
setLoad({ status: "loading" });
|
|
1389
|
+
loadDay(date).then((view) => {
|
|
1390
|
+
if (!cancelled) setLoad({
|
|
1391
|
+
status: "ready",
|
|
1392
|
+
view
|
|
1393
|
+
});
|
|
1394
|
+
}, (error) => {
|
|
1395
|
+
if (!cancelled) setLoad({
|
|
1396
|
+
status: "error",
|
|
1397
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1398
|
+
});
|
|
1399
|
+
});
|
|
1400
|
+
return () => {
|
|
1401
|
+
cancelled = true;
|
|
1402
|
+
};
|
|
1403
|
+
}, [
|
|
1404
|
+
date,
|
|
1405
|
+
today,
|
|
1406
|
+
loadDay
|
|
1407
|
+
]);
|
|
1408
|
+
return load;
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* The day picker: today plus every retained day that recorded usage, newest
|
|
1412
|
+
* first (the host serves the list ascending). Without that list (an older host
|
|
1413
|
+
* document) no picker renders and the card stays on its default window.
|
|
1414
|
+
*/
|
|
1415
|
+
function DayPicker(props) {
|
|
1416
|
+
const { value, options, today, leading, onSelect, part } = props;
|
|
1417
|
+
if (options.length === 0) return null;
|
|
1418
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1419
|
+
className: usage_module_css_default.dayPicker,
|
|
1420
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1421
|
+
className: usage_module_css_default.dayPickerLabel,
|
|
1422
|
+
htmlFor: "dsh-usage-day-" + part,
|
|
1423
|
+
children: t("usage.day.label")
|
|
1424
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1425
|
+
id: "dsh-usage-day-" + part,
|
|
1426
|
+
className: usage_module_css_default.daySelect,
|
|
1427
|
+
value,
|
|
1428
|
+
onChange: (event) => {
|
|
1429
|
+
onSelect(event.target.value);
|
|
1430
|
+
},
|
|
1431
|
+
children: [leading !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1432
|
+
value: leading.value,
|
|
1433
|
+
children: leading.label
|
|
1434
|
+
}), options.map((date) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1435
|
+
value: date,
|
|
1436
|
+
children: date === today ? t("usage.day.today") : date
|
|
1437
|
+
}, date))]
|
|
1438
|
+
})]
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1044
1441
|
/** The section component; the slot merges the face into these props. */
|
|
1045
1442
|
function UsageSectionCard(props) {
|
|
1046
|
-
const { store, poll, refresh, settings } = props;
|
|
1443
|
+
const { store, poll, refresh, loadDay, settings } = props;
|
|
1047
1444
|
const ui = (0, react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
1048
1445
|
const settingsSnapshot = settings.getSnapshot();
|
|
1049
1446
|
const settingsValue = settingsSnapshot.value ?? {};
|
|
1050
1447
|
const [tab, setTab] = (0, react.useState)("usage");
|
|
1448
|
+
const [usageDay, setUsageDay] = (0, react.useState)(void 0);
|
|
1449
|
+
const [bankDay, setBankDay] = (0, react.useState)(void 0);
|
|
1051
1450
|
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
1052
1451
|
const [, bumpSettings] = (0, react.useState)(0);
|
|
1053
1452
|
(0, react.useEffect)(() => settings.subscribe(() => bumpSettings((count) => count + 1)), [settings]);
|
|
@@ -1076,6 +1475,22 @@ window.__ModuleLoader__.load({
|
|
|
1076
1475
|
};
|
|
1077
1476
|
}, [poll, enabled]);
|
|
1078
1477
|
const snapshot = ui.snapshot;
|
|
1478
|
+
const todayDate = snapshot?.usage.today.date;
|
|
1479
|
+
const dayOptions = snapshot === null ? [] : [...new Set([todayDate, ...snapshot.usage.availableDays ?? []].filter((date) => date !== void 0))].sort().reverse();
|
|
1480
|
+
const usageSelection = usageDay !== void 0 && dayOptions.includes(usageDay) ? usageDay : void 0;
|
|
1481
|
+
const bankSelection = bankDay !== void 0 && dayOptions.includes(bankDay) ? bankDay : void 0;
|
|
1482
|
+
const selectedDay = usageSelection ?? todayDate;
|
|
1483
|
+
const isToday = selectedDay === todayDate;
|
|
1484
|
+
const usageDayLoad = useDayView(loadDay, usageSelection, todayDate);
|
|
1485
|
+
const bankDayLoad = useDayView(loadDay, bankSelection, todayDate);
|
|
1486
|
+
const day = isToday ? snapshot?.usage.today : usageDayLoad?.view;
|
|
1487
|
+
const bankDayView = bankSelection === void 0 ? void 0 : bankSelection === todayDate ? snapshot?.usage.today : bankDayLoad?.view;
|
|
1488
|
+
const bankWindow = bankSelection === void 0 ? snapshot?.usage.all ?? snapshot?.usage.range : bankDayView === void 0 ? void 0 : {
|
|
1489
|
+
from: bankDayView.date,
|
|
1490
|
+
to: bankDayView.date,
|
|
1491
|
+
totals: bankDayView.totals,
|
|
1492
|
+
providers: bankDayView.providers
|
|
1493
|
+
};
|
|
1079
1494
|
const onRefresh = () => {
|
|
1080
1495
|
setRefreshing(true);
|
|
1081
1496
|
try {
|
|
@@ -1100,7 +1515,7 @@ window.__ModuleLoader__.load({
|
|
|
1100
1515
|
const current = snapshot.current;
|
|
1101
1516
|
const currentProvider = snapshot.providers.find((provider) => provider.provider === current.provider);
|
|
1102
1517
|
const deepseekPeriod = deepseekPeriodAt(Date.now());
|
|
1103
|
-
const deepseekVisible = current.provider !== void 0 && isDeepSeekProviderRoute(current.provider) || snapshot.usage.today.providers.some((row) => isDeepSeekProviderRoute(row.provider));
|
|
1518
|
+
const deepseekVisible = isToday && (current.provider !== void 0 && isDeepSeekProviderRoute(current.provider) || snapshot.usage.today.providers.some((row) => isDeepSeekProviderRoute(row.provider)));
|
|
1104
1519
|
const planProviders = snapshot.providers.filter((provider) => isConfigured(provider) && (provider.planSupported === true || provider.planSupported === void 0 && provider.plan !== void 0));
|
|
1105
1520
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1106
1521
|
className: usage_module_css_default.section,
|
|
@@ -1168,31 +1583,46 @@ window.__ModuleLoader__.load({
|
|
|
1168
1583
|
children: [
|
|
1169
1584
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1170
1585
|
className: usage_module_css_default.cardTitle,
|
|
1171
|
-
children: t("usage.today")
|
|
1586
|
+
children: isToday ? t("usage.today") : t("usage.day.title", { date: selectedDay ?? "" })
|
|
1587
|
+
}),
|
|
1588
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DayPicker, {
|
|
1589
|
+
part: "usage",
|
|
1590
|
+
value: selectedDay ?? "",
|
|
1591
|
+
options: dayOptions,
|
|
1592
|
+
today: todayDate ?? "",
|
|
1593
|
+
onSelect: (date) => {
|
|
1594
|
+
setUsageDay(date === todayDate ? void 0 : date);
|
|
1595
|
+
}
|
|
1172
1596
|
}),
|
|
1173
1597
|
deepseekVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1174
1598
|
className: usage_module_css_default.muted,
|
|
1175
1599
|
"data-dsh-part": "peak-status",
|
|
1176
1600
|
children: t(deepseekPeriod.peak ? "usage.peak.on" : "usage.peak.off", { time: formatClock$1(deepseekPeriod.boundaryMs) })
|
|
1177
1601
|
}),
|
|
1178
|
-
|
|
1602
|
+
usageDayLoad?.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1603
|
+
className: usage_module_css_default.errorLine,
|
|
1604
|
+
children: t("usage.day.error", { error: usageDayLoad.error ?? "" })
|
|
1605
|
+
}) : day === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1606
|
+
className: usage_module_css_default.muted,
|
|
1607
|
+
children: t("usage.day.loading")
|
|
1608
|
+
}) : day.totals.calls === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1179
1609
|
className: usage_module_css_default.muted,
|
|
1180
|
-
children: t("usage.noData")
|
|
1181
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TotalsRow, { totals:
|
|
1182
|
-
|
|
1610
|
+
children: isToday ? t("usage.noData") : t("usage.day.empty")
|
|
1611
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TotalsRow, { totals: day.totals }),
|
|
1612
|
+
(day?.totals.cost ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1183
1613
|
className: usage_module_css_default.providerRow,
|
|
1184
1614
|
"data-dsh-part": "today-cost",
|
|
1185
1615
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1186
1616
|
className: usage_module_css_default.providerName,
|
|
1187
|
-
children: t("usage.today.cost")
|
|
1617
|
+
children: isToday ? t("usage.today.cost") : t("usage.day.cost")
|
|
1188
1618
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1189
1619
|
className: usage_module_css_default.providerTokens,
|
|
1190
|
-
children: formatCost(
|
|
1620
|
+
children: formatCost(day?.totals.cost ?? 0)
|
|
1191
1621
|
})]
|
|
1192
1622
|
}),
|
|
1193
|
-
|
|
1623
|
+
(day?.providers.length ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1194
1624
|
"data-dsh-part": "provider-list",
|
|
1195
|
-
children:
|
|
1625
|
+
children: (day?.providers ?? []).map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1196
1626
|
className: usage_module_css_default.providerRow,
|
|
1197
1627
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1198
1628
|
className: usage_module_css_default.providerName,
|
|
@@ -1260,10 +1690,24 @@ window.__ModuleLoader__.load({
|
|
|
1260
1690
|
provider,
|
|
1261
1691
|
current: current.provider
|
|
1262
1692
|
}, provider.provider))),
|
|
1263
|
-
tab === "bank" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1693
|
+
tab === "bank" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DayPicker, {
|
|
1694
|
+
part: "bank",
|
|
1695
|
+
value: bankSelection ?? BANK_ALL_DAYS,
|
|
1696
|
+
options: dayOptions,
|
|
1697
|
+
today: todayDate ?? "",
|
|
1698
|
+
leading: {
|
|
1699
|
+
value: BANK_ALL_DAYS,
|
|
1700
|
+
label: t("usage.bank.windowAll")
|
|
1701
|
+
},
|
|
1702
|
+
onSelect: (date) => {
|
|
1703
|
+
setBankDay(date === BANK_ALL_DAYS ? void 0 : date);
|
|
1704
|
+
}
|
|
1705
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VoucherCard, {
|
|
1706
|
+
window: bankWindow,
|
|
1707
|
+
observedSpend: bankSelection === void 0 ? snapshot.usage.observedSpend : void 0,
|
|
1708
|
+
loading: bankSelection !== void 0 && bankDayLoad?.status === "loading",
|
|
1709
|
+
...bankDayLoad?.status === "error" ? { error: bankDayLoad.error ?? "" } : {}
|
|
1710
|
+
})] })
|
|
1267
1711
|
]
|
|
1268
1712
|
});
|
|
1269
1713
|
}
|
|
@@ -1367,17 +1811,18 @@ window.__ModuleLoader__.load({
|
|
|
1367
1811
|
return totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens + totals.outputTokens;
|
|
1368
1812
|
}
|
|
1369
1813
|
/**
|
|
1370
|
-
* The Token 银行 card: the
|
|
1371
|
-
* minted onto the whale-yuan note at 1,000,000 tokens
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1374
|
-
*
|
|
1375
|
-
*
|
|
1376
|
-
*
|
|
1814
|
+
* The Token 银行 card: the mint family's (DeepSeek official + MiMo)
|
|
1815
|
+
* retained-ledger usage minted onto the whale-yuan note at 1,000,000 tokens
|
|
1816
|
+
* per whale yuan. The window is the whole retained ledger by default (the
|
|
1817
|
+
* host's aggregate, falling back to the 30-day trend when an older host serves
|
|
1818
|
+
* no `all`) or the single day the user picked; the spend line prefers the
|
|
1819
|
+
* official balance watch (whole window only) and falls back to the fold-time
|
|
1820
|
+
* estimate, which only the priced family feeds; the artwork draw failure
|
|
1821
|
+
* degrades to an error line and never takes the section down.
|
|
1377
1822
|
*/
|
|
1378
1823
|
function VoucherCard(props) {
|
|
1379
|
-
const { window: ledger, observedSpend } = props;
|
|
1380
|
-
const data =
|
|
1824
|
+
const { window: ledger, observedSpend, loading, error } = props;
|
|
1825
|
+
const data = voucherData(ledger);
|
|
1381
1826
|
const canvasRef = (0, react.useRef)(null);
|
|
1382
1827
|
const [drawError, setDrawError] = (0, react.useState)(void 0);
|
|
1383
1828
|
(0, react.useEffect)(() => {
|
|
@@ -1434,7 +1879,13 @@ window.__ModuleLoader__.load({
|
|
|
1434
1879
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1435
1880
|
className: usage_module_css_default.cardTitle,
|
|
1436
1881
|
children: t("usage.bank.title")
|
|
1437
|
-
}),
|
|
1882
|
+
}), error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1883
|
+
className: usage_module_css_default.errorLine,
|
|
1884
|
+
children: t("usage.day.error", { error })
|
|
1885
|
+
}) : loading === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1886
|
+
className: usage_module_css_default.muted,
|
|
1887
|
+
children: t("usage.day.loading")
|
|
1888
|
+
}) : data === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1438
1889
|
className: usage_module_css_default.muted,
|
|
1439
1890
|
children: t("usage.bank.noUsage")
|
|
1440
1891
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
@@ -2221,6 +2672,7 @@ window.__ModuleLoader__.load({
|
|
|
2221
2672
|
}
|
|
2222
2673
|
const usageApi = {
|
|
2223
2674
|
overview: () => usageFetch("api/dsh-usage/overview", "GET"),
|
|
2675
|
+
day: async (date) => (await usageFetch("api/dsh-usage/day?date=" + encodeURIComponent(date), "GET")).day,
|
|
2224
2676
|
refresh: () => usageFetch("api/dsh-usage/refresh", "POST")
|
|
2225
2677
|
};
|
|
2226
2678
|
/**
|
|
@@ -2290,10 +2742,12 @@ window.__ModuleLoader__.load({
|
|
|
2290
2742
|
store.actions.setState("error", error instanceof Error ? error.message : String(error));
|
|
2291
2743
|
});
|
|
2292
2744
|
};
|
|
2745
|
+
const loadDay = (date) => usageApi.day(date);
|
|
2293
2746
|
const face = () => ({
|
|
2294
2747
|
store,
|
|
2295
2748
|
poll,
|
|
2296
2749
|
refresh,
|
|
2750
|
+
loadDay,
|
|
2297
2751
|
settings: settingsForm
|
|
2298
2752
|
});
|
|
2299
2753
|
const disposeFootCard = mountUsageFootCard({
|