@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/README.i18n.yaml +2 -2
- package/README.md +21 -5
- package/README.zh.md +21 -5
- package/lib/client.js +466 -32
- 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/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 +14 -14
- package/src/client/UsageSectionCard.tsx +174 -26
- package/src/client/index.ts +10 -2
- package/src/client/locales.ts +16 -0
- package/src/client/usage.module.css +47 -0
- 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} 次调用",
|
|
@@ -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
|
|
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;
|
|
@@ -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
|
-
|
|
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:
|
|
1182
|
-
|
|
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(
|
|
1600
|
+
children: formatCost(day?.totals.cost ?? 0)
|
|
1191
1601
|
})]
|
|
1192
1602
|
}),
|
|
1193
|
-
|
|
1603
|
+
(day?.providers.length ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1194
1604
|
"data-dsh-part": "provider-list",
|
|
1195
|
-
children:
|
|
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)(
|
|
1264
|
-
|
|
1265
|
-
|
|
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
|
|
1372
|
-
*
|
|
1373
|
-
* trend when an older host serves no `all
|
|
1374
|
-
*
|
|
1375
|
-
*
|
|
1376
|
-
* section
|
|
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
|
-
}),
|
|
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({
|