@lancecheney/dsh-deepseek-balance 0.1.0 → 0.1.2
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.md +10 -0
- package/README.zh.md +10 -0
- package/lib/client.js +271 -246
- package/lib/index.js +491 -119
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -6,173 +6,91 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
8
|
|
|
9
|
-
const css = ".Dbg1_root{display:inline-flex;align-items:center;gap:6px;height:32px;border:1px solid var(--dsw-alias-border-l2);border-radius:18px;padding:0 12px;font-family:var(--dsw-font-family);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);background:0 0;white-space:nowrap;
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const MODEL_META = {
|
|
21
|
-
"deepseek-v4-pro": { label: "DeepSeek-V4-Pro" },
|
|
22
|
-
"deepseek-v4-flash": { label: "DeepSeek-V4-Flash" }
|
|
9
|
+
const css = ".Dbg1_root{display:inline-flex;align-items:center;gap:6px;height:32px;border:1px solid var(--dsw-alias-border-l2);border-radius:18px;padding:0 12px;font-family:var(--dsw-font-family);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);background:0 0;white-space:nowrap;cursor:pointer}.Dbg1_root:hover{background:var(--dsw-alias-interactive-bg-hover)}.Dbg1_num{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-weight:500}.Dbg1_sep{color:var(--dsw-alias-label-dimmed)}.Dbg1_chip{border-radius:9px;padding:0 8px;font-size:11px;line-height:18px;font-weight:600}.Dbg1_peak{color:var(--dsw-alias-state-warn-primary)}.Dbg1_offPeak{color:var(--dsw-alias-state-success-primary)}.Dbg1_legacy{color:var(--dsw-alias-label-tertiary)}.Dbg1_dimmed{color:var(--dsw-alias-label-dimmed)}.Dbg1_plus{font-size:9px;line-height:1;vertical-align:super;color:var(--dsw-alias-state-warn-primary);font-weight:700}.Dbg1_unit{font-size:10px}";
|
|
10
|
+
const css2 = ".Dp1_backdrop{position:fixed;top:0;bottom:0;right:0;background:rgba(0,0,0,.32);z-index:60}.Dp1_panel{position:fixed;top:0;bottom:0;right:0;z-index:61;display:flex;flex-direction:column;background:var(--dsw-alias-bg-base);border-left:1px solid var(--dsw-alias-border-l1);box-shadow:var(--dsw-shadow-lv3);overflow-y:auto}.Dp1_head{display:flex;flex-direction:column;align-items:center;gap:8px;padding:32px 56px 22px;position:relative}.Dp1_avatar{width:64px;height:64px;border-radius:50%;background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:24px;flex:none;border:2px solid var(--dsw-alias-border-l2)}.Dp1_name{font-size:18px;font-weight:600;color:var(--dsw-alias-label-primary)}.Dp1_key{font-family:var(--dsw-font-mono);color:var(--dsw-alias-label-tertiary);font-size:13px}.Dp1_close{position:fixed;top:16px;right:16px;cursor:pointer;background:0 0;border:0;color:var(--dsw-alias-label-secondary);font-size:22px;line-height:1;z-index:62}.Dp1_body{padding:0 56px 36px;display:flex;flex-direction:column;gap:26px}.Dp1_stats{display:flex;align-items:center;justify-content:center;gap:56px;flex-wrap:wrap;padding:18px 0}.Dp1_statItem{display:flex;flex-direction:column;align-items:center;gap:4px}.Dp1_statValue{font-size:18px;font-weight:600;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums}.Dp1_statLabel{font-size:12px;color:var(--dsw-alias-label-tertiary)}.Dp1_sepBar{color:var(--dsw-alias-label-dimmed);font-size:14px}.Dp1_balance{display:flex;flex-wrap:wrap;gap:16px;justify-content:center;margin:0 auto}.Dp1_balanceBlock{position:relative;box-sizing:border-box;width:200px;background:rgba(127,127,127,.10);border-radius:14px;padding:10px 14px;display:flex;flex-direction:column;align-items:flex-start;gap:2px;height:80px}.Dp1_balanceBlockLabel{font-size:12px;color:var(--dsw-alias-label-tertiary)}.Dp1_balanceBlockValue{font-size:28px;font-weight:700;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;margin-top:auto}.Dp1_balanceBlock .Dp1_btn{position:absolute;right:12px;top:10px;height:22px;padding:0 8px;font-size:12px}.Dp1_btn{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:0 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:0 0;color:var(--dsw-alias-label-primary);cursor:pointer;font-size:13px}.Dp1_btn:hover{background:var(--dsw-alias-interactive-bg-hover)}.Dp1_btnPrimary{background:#4d6bfe;color:#fff;border:none}.Dp1_section{padding:16px 0}.Dp1_title{font-size:13px;color:var(--dsw-alias-label-secondary);margin:0 0 12px;font-weight:600;letter-spacing:.02em}.Dp1_row{display:flex;align-items:center;gap:8px;padding:7px 0}.Dp1_twoCol{display:grid;grid-template-columns:1fr 1fr;gap:0 80px;align-items:start}.Dp1_rowTitle{flex:1;min-width:0;color:var(--dsw-alias-label-primary);font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.Dp1_rowTitleGray{color:var(--dsw-alias-label-dimmed)}.Dp1_rowVal{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:13px;flex:none}.Dp1_rank{color:var(--dsw-alias-label-tertiary);width:18px;text-align:right;flex:none;font-size:13px}.Dp1_monthNav{display:flex;align-items:center;justify-content:center;gap:16px;margin-bottom:14px}.Dp1_monthGrids{display:flex;flex-wrap:wrap;gap:20px}.Dp1_monthGrids .Dp1_monthGroup{flex:1 1 260px;min-width:0}.Dp1_monthGroup{display:flex;flex-direction:column;align-items:center;gap:8px}.Dp1_monthGroupTitle{font-size:12px;color:var(--dsw-alias-label-tertiary)}.Dp1_grid{display:grid;grid-template-columns:repeat(7,1fr);gap:5px;width:100%;max-width:170px}.Dp1_cell{aspect-ratio:1;border-radius:6px;background:rgba(127,127,127,.08);display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--dsw-alias-label-tertiary)}.Dp1_cellFlat{background:#60a5fa;color:#fff}.Dp1_cellPeak{background:#f97316;color:#fff}.Dp1_cellOff{background:#22c55e;color:#fff}.Dp1_cellBlue{color:#fff}.Dp1_legend{display:flex;gap:12px;margin-top:10px;justify-content:center;font-size:11px;color:var(--dsw-alias-label-tertiary);flex-wrap:wrap}.Dp1_dot{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:4px;vertical-align:middle}.Dp1_nameRow{display:flex;align-items:center;gap:6px}.Dp1_edit{border:0;background:0 0;cursor:pointer;color:var(--dsw-alias-label-tertiary);font-size:14px;padding:2px;line-height:1}.Dp1_edit:hover{color:var(--dsw-alias-label-primary)}.Dp1_editRow{display:flex;align-items:center;gap:8px}.Dp1_editInput{height:30px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-fill-l1);color:var(--dsw-alias-label-primary);font-size:13px;width:180px}@media (max-width:640px){.Dbg1_root{margin-top:80px}.Dp1_head{padding:24px 16px 16px}.Dp1_body{padding:0 16px 24px}.Dp1_twoCol{grid-template-columns:1fr;gap:0}.Dp1_stats{gap:20px;padding:12px 0}.Dp1_close{right:12px;top:12px}}";
|
|
11
|
+
const injectCss = (text, tagId) => {
|
|
12
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
13
|
+
const tag = document.createElement("style");
|
|
14
|
+
tag.dataset.plugin = "@lancecheney/dsh-deepseek-balance";
|
|
15
|
+
tag.dataset.pluginCss = tagId;
|
|
16
|
+
tag.textContent = text;
|
|
17
|
+
document.head.appendChild(tag);
|
|
18
|
+
}
|
|
23
19
|
};
|
|
20
|
+
injectCss(css, "@lancecheney/dsh-deepseek-balance/BillingBadge.css");
|
|
21
|
+
injectCss(css2, "@lancecheney/dsh-deepseek-balance/UsagePanel.css");
|
|
22
|
+
const C = { root: "Dbg1_root", num: "Dbg1_num", sep: "Dbg1_sep", chip: "Dbg1_chip", peak: "Dbg1_peak", offPeak: "Dbg1_offPeak", legacy: "Dbg1_legacy", dimmed: "Dbg1_dimmed", plus: "Dbg1_plus", unit: "Dbg1_unit" };
|
|
23
|
+
const P = { backdrop: "Dp1_backdrop", panel: "Dp1_panel", head: "Dp1_head", avatar: "Dp1_avatar", name: "Dp1_name", key: "Dp1_key", close: "Dp1_close", body: "Dp1_body", stats: "Dp1_stats", statItem: "Dp1_statItem", statValue: "Dp1_statValue", statLabel: "Dp1_statLabel", sepBar: "Dp1_sepBar", balance: "Dp1_balance", balanceBlock: "Dp1_balanceBlock", balanceBlockLabel: "Dp1_balanceBlockLabel", balanceBlockValue: "Dp1_balanceBlockValue", btn: "Dp1_btn", btnPrimary: "Dp1_btnPrimary", section: "Dp1_section", title: "Dp1_title", row: "Dp1_row", twoCol: "Dp1_twoCol", rowTitle: "Dp1_rowTitle", rowTitleGray: "Dp1_rowTitleGray", rowVal: "Dp1_rowVal", rank: "Dp1_rank", monthNav: "Dp1_monthNav", monthGrids: "Dp1_monthGrids", monthGroup: "Dp1_monthGroup", monthGroupTitle: "Dp1_monthGroupTitle", grid: "Dp1_grid", cell: "Dp1_cell", cellFlat: "Dp1_cellFlat", cellPeak: "Dp1_cellPeak", cellOff: "Dp1_cellOff", cellBlue: "Dp1_cellBlue", legend: "Dp1_legend", dot: "Dp1_dot", nameRow: "Dp1_nameRow", edit: "Dp1_edit", editRow: "Dp1_editRow", editInput: "Dp1_editInput" };
|
|
24
24
|
|
|
25
|
+
const MODEL_META = { "deepseek-v4-pro": { label: "DeepSeek-V4-Pro" }, "deepseek-v4-flash": { label: "DeepSeek-V4-Flash" } };
|
|
25
26
|
const FALLBACK_PRICING = {
|
|
26
27
|
effectiveFrom: "2026-08-17T00:00:00+08:00",
|
|
28
|
+
weekendFrom: "2026-08-23T00:00:00+08:00",
|
|
27
29
|
currencies: {
|
|
28
|
-
CNY: {
|
|
29
|
-
|
|
30
|
-
models: {
|
|
31
|
-
"deepseek-v4-pro": {
|
|
32
|
-
legacy: { hit: 0.025, miss: 3.0, output: 6.0 },
|
|
33
|
-
peak: { hit: 0.30, miss: 9.0, output: 27.0 },
|
|
34
|
-
offPeak: { hit: 0.15, miss: 4.5, output: 13.5 }
|
|
35
|
-
},
|
|
36
|
-
"deepseek-v4-flash": {
|
|
37
|
-
legacy: { hit: 0.02, miss: 1.0, output: 2.0 },
|
|
38
|
-
peak: { hit: 0.10, miss: 3.0, output: 9.0 },
|
|
39
|
-
offPeak: { hit: 0.05, miss: 1.5, output: 4.5 }
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
USD: {
|
|
44
|
-
symbol: "$",
|
|
45
|
-
models: {
|
|
46
|
-
"deepseek-v4-pro": {
|
|
47
|
-
legacy: { hit: 0.003625, miss: 0.435, output: 0.87 },
|
|
48
|
-
peak: { hit: 0.044, miss: 1.32, output: 3.96 },
|
|
49
|
-
offPeak: { hit: 0.022, miss: 0.66, output: 1.98 }
|
|
50
|
-
},
|
|
51
|
-
"deepseek-v4-flash": {
|
|
52
|
-
legacy: { hit: 0.0028, miss: 0.14, output: 0.28 },
|
|
53
|
-
peak: { hit: 0.014, miss: 0.44, output: 1.32 },
|
|
54
|
-
offPeak: { hit: 0.007, miss: 0.22, output: 0.66 }
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
30
|
+
CNY: { symbol: "¥", models: { "deepseek-v4-pro": { legacy: { hit: 0.025, miss: 3.0, output: 6.0 }, peak: { hit: 0.30, miss: 9.0, output: 27.0 }, offPeak: { hit: 0.15, miss: 4.5, output: 13.5 } }, "deepseek-v4-flash": { legacy: { hit: 0.02, miss: 1.0, output: 2.0 }, peak: { hit: 0.10, miss: 3.0, output: 9.0 }, offPeak: { hit: 0.05, miss: 1.5, output: 4.5 } }, "deepseek-v4-flash-vision-exp": { legacy: { hit: 0.02, miss: 1.0, output: 2.0 }, peak: { hit: 0.10, miss: 3.0, output: 9.0 }, offPeak: { hit: 0.05, miss: 1.5, output: 4.5 } } } },
|
|
31
|
+
USD: { symbol: "$", models: { "deepseek-v4-pro": { legacy: { hit: 0.003625, miss: 0.435, output: 0.87 }, peak: { hit: 0.044, miss: 1.32, output: 3.96 }, offPeak: { hit: 0.022, miss: 0.66, output: 1.98 } }, "deepseek-v4-flash": { legacy: { hit: 0.0028, miss: 0.14, output: 0.28 }, peak: { hit: 0.014, miss: 0.44, output: 1.32 }, offPeak: { hit: 0.007, miss: 0.22, output: 0.66 } }, "deepseek-v4-flash-vision-exp": { legacy: { hit: 0.0028, miss: 0.14, output: 0.28 }, peak: { hit: 0.014, miss: 0.44, output: 1.32 }, offPeak: { hit: 0.007, miss: 0.22, output: 0.66 } } } }
|
|
58
32
|
}
|
|
59
33
|
};
|
|
60
34
|
const DEFAULT_MODEL = "deepseek-v4-pro";
|
|
61
35
|
|
|
62
|
-
function beijingDecimalHour(now) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const h = beijingDecimalHour(now);
|
|
87
|
-
return (h >= 9 && h < 12) || (h >= 14 && h < 18) ? "peak" : "offPeak";
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function firstBalance(data) {
|
|
91
|
-
const infos = data && data.balance_infos;
|
|
92
|
-
if (!Array.isArray(infos) || infos.length === 0) return null;
|
|
93
|
-
return infos.find((row) => row.currency === "CNY") || infos[0];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function fmtPrice(value) {
|
|
97
|
-
const n = Number(value);
|
|
98
|
-
return Number.isFinite(n) ? String(n) : String(value);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function fmtMoney(value) {
|
|
102
|
-
const n = Number(value);
|
|
103
|
-
if (!Number.isFinite(n)) return "—";
|
|
104
|
-
if (n <= 0) return "0.00";
|
|
105
|
-
if (n < 0.01) return n.toFixed(4);
|
|
106
|
-
if (n < 1) return n.toFixed(3);
|
|
107
|
-
return n.toFixed(2);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function costOf(usage, p) {
|
|
111
|
-
const miss = (usage.uncachedInputTokens ?? 0) + (usage.cacheWriteTokens ?? 0);
|
|
112
|
-
const hit = usage.cacheReadTokens ?? 0;
|
|
113
|
-
const output = usage.outputTokens ?? 0;
|
|
114
|
-
return (miss * p.miss + hit * p.hit + output * p.output) / 1e6;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function sumBuckets(a, b) {
|
|
118
|
-
return {
|
|
119
|
-
uncachedInputTokens: (a.uncachedInputTokens ?? 0) + (b.uncachedInputTokens ?? 0),
|
|
120
|
-
outputTokens: (a.outputTokens ?? 0) + (b.outputTokens ?? 0),
|
|
121
|
-
cacheReadTokens: (a.cacheReadTokens ?? 0) + (b.cacheReadTokens ?? 0),
|
|
122
|
-
cacheWriteTokens: (a.cacheWriteTokens ?? 0) + (b.cacheWriteTokens ?? 0)
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function translate(dict, key, params) {
|
|
127
|
-
let s = dict[key];
|
|
128
|
-
if (s === void 0) return key;
|
|
129
|
-
if (params) {
|
|
130
|
-
for (const [k, v] of Object.entries(params)) s = s.split(`{${k}}`).join(String(v));
|
|
36
|
+
function beijingDecimalHour(now) { let h = 0, m = 0; try { const p = new Intl.DateTimeFormat("en-GB", { timeZone: "Asia/Shanghai", hour12: false, hour: "2-digit", minute: "2-digit" }).formatToParts(now); for (const x of p) { if (x.type === "hour") h = Number(x.value); if (x.type === "minute") m = Number(x.value); } } catch { h = now.getHours(); m = now.getMinutes(); } return h + m / 60; }
|
|
37
|
+
function periodFor(now, effectiveFrom, weekendFrom) { const eff = Date.parse(effectiveFrom); if (Number.isFinite(eff) && now.getTime() < eff) return "legacy"; const wk = Date.parse(weekendFrom); const d = new Date(now.getTime() + 8 * 3600 * 1000); const day = d.getUTCDay(); if ((day === 0 || day === 6) && Number.isFinite(wk) && now.getTime() >= wk) return "offPeak"; const h = beijingDecimalHour(now); return (h >= 9 && h < 12) || (h >= 14 && h < 18) ? "peak" : "offPeak"; }
|
|
38
|
+
function firstBalance(data) { const infos = data && data.balance_infos; if (!Array.isArray(infos) || infos.length === 0) return null; return infos.find((r) => r.currency === "CNY") || infos[0]; }
|
|
39
|
+
function fmtPrice(v) { const n = Number(v); return Number.isFinite(n) ? String(n) : String(v); }
|
|
40
|
+
function fmtMoney(v) { const n = Number(v); if (!Number.isFinite(n)) return "—"; if (n <= 0) return "0.00"; if (n < 0.01) return n.toFixed(4); if (n < 1) return n.toFixed(3); return n.toFixed(2); }
|
|
41
|
+
function translate(dict, key, params) { let s = dict[key]; if (s === void 0) return key; if (params) for (const [k, v] of Object.entries(params)) s = s.split(`{${k}}`).join(String(v)); return s; }
|
|
42
|
+
function fmtTokens(n) { if (!Number.isFinite(n)) return "—"; if (n < 1000) return String(Math.round(n)); const scaled = (v) => v >= 100 ? String(Math.round(v)) : String(Math.round(v * 10) / 10); if (n < 1e6) return scaled(n / 1e3) + "K"; return scaled(n / 1e6) + "M"; }
|
|
43
|
+
function fmtDuration(ms) { if (!Number.isFinite(ms) || ms <= 0) return "—"; const s = ms / 1e3; if (s < 60) return Math.round(s) + "s"; const w = Math.round(s); const h = Math.floor(w / 3600); const m = Math.floor((w % 3600) / 60); if (h > 0) return h + "h" + m + "m"; return m + "m" + (w % 60) + "s"; }
|
|
44
|
+
function beijingDayKey(ms) { return new Date(ms + 8 * 3600 * 1000).toISOString().slice(0, 10); }
|
|
45
|
+
function beijingMonthKey(ms) { return new Date(ms + 8 * 3600 * 1000).toISOString().slice(0, 7); }
|
|
46
|
+
function findFrame() { if (typeof document === "undefined") return null; const overlay = document.querySelector("[data-shell-overlay]"); return overlay && overlay.parentElement ? overlay.parentElement : null; }
|
|
47
|
+
function sidebarWidthOf(frame) { const col = frame && frame.firstElementChild; return col ? col.getBoundingClientRect().width : null; }
|
|
48
|
+
const noopSubscribe = () => () => {};
|
|
49
|
+
function dayColor(d) { if (!d || d.tokens <= 0) return "none"; if (d.flat >= d.peak && d.flat >= d.offPeak) return "flat"; return d.peak >= d.offPeak ? "peak" : "off"; }
|
|
50
|
+
function tokenBlue(tokens, max) { if (!tokens || tokens <= 0 || max <= 0) return null; const r = tokens / max; if (r < 0.2) return "#93c5fd"; if (r < 0.4) return "#60a5fa"; if (r < 0.6) return "#3b82f6"; if (r < 0.8) return "#2563eb"; return "#1e40af"; }
|
|
51
|
+
function monthGrid(year, month, daysData) {
|
|
52
|
+
const first = new Date(Date.UTC(year, month, 1));
|
|
53
|
+
const daysInMonth = new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
|
54
|
+
const lead = (first.getUTCDay() + 6) % 7;
|
|
55
|
+
const cells = [];
|
|
56
|
+
for (let i = 0; i < lead; i++) cells.push(null);
|
|
57
|
+
for (let d = 1; d <= daysInMonth; d++) {
|
|
58
|
+
const key = `${year}-${String(month + 1).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
|
|
59
|
+
cells.push({ day: d, key, data: daysData[key] });
|
|
131
60
|
}
|
|
132
|
-
return
|
|
61
|
+
return cells;
|
|
133
62
|
}
|
|
134
63
|
|
|
135
|
-
const noopSubscribe = () => () => {};
|
|
136
|
-
|
|
137
64
|
function BillingBadge(props) {
|
|
138
|
-
const useProjection = props.useProjection;
|
|
139
65
|
const directory = props.directory;
|
|
140
|
-
const
|
|
141
|
-
const
|
|
66
|
+
const sessionId = props.sessionId;
|
|
67
|
+
const [open, setOpen] = react.useState(false);
|
|
142
68
|
const [state, setState] = react.useState({ phase: "loading" });
|
|
143
69
|
|
|
144
70
|
react.useEffect(() => {
|
|
145
|
-
let alive = true;
|
|
146
|
-
let timer;
|
|
71
|
+
let alive = true; let timer;
|
|
147
72
|
const load = async () => {
|
|
148
73
|
try {
|
|
149
|
-
const
|
|
74
|
+
const usageUrl = sessionId ? `/api/deepseek-usage?session=${encodeURIComponent(sessionId)}` : "/api/deepseek-usage";
|
|
75
|
+
const [balanceRes, pricingRes, usageRes] = await Promise.all([
|
|
150
76
|
fetch("/api/deepseek-balance", { headers: { accept: "application/json" }, cache: "no-store" }),
|
|
151
|
-
fetch("/api/deepseek-pricing", { headers: { accept: "application/json" }, cache: "no-store" })
|
|
77
|
+
fetch("/api/deepseek-pricing", { headers: { accept: "application/json" }, cache: "no-store" }),
|
|
78
|
+
fetch(usageUrl, { headers: { accept: "application/json" }, cache: "no-store" })
|
|
152
79
|
]);
|
|
153
80
|
const balanceData = await balanceRes.json().catch(() => ({}));
|
|
154
81
|
const pricingData = await pricingRes.json().catch(() => null);
|
|
82
|
+
const usageData = await usageRes.json().catch(() => null);
|
|
155
83
|
if (!alive) return;
|
|
156
|
-
setState({
|
|
157
|
-
|
|
158
|
-
balance: balanceRes.ok ? balanceData : null,
|
|
159
|
-
balanceError: balanceRes.ok ? null : (balanceData.error || `HTTP ${balanceRes.status}`),
|
|
160
|
-
pricing: pricingData && pricingRes.ok && pricingData.currencies ? pricingData : FALLBACK_PRICING
|
|
161
|
-
});
|
|
162
|
-
} catch (error) {
|
|
163
|
-
if (alive) setState({ phase: "error", message: error instanceof Error ? error.message : String(error) });
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
load();
|
|
167
|
-
timer = setInterval(load, 60000);
|
|
168
|
-
return () => {
|
|
169
|
-
alive = false;
|
|
170
|
-
clearInterval(timer);
|
|
84
|
+
setState({ phase: "ok", balance: balanceRes.ok ? balanceData : null, pricing: pricingData && pricingRes.ok && pricingData.currencies ? pricingData : FALLBACK_PRICING, usage: usageData && usageData.spend ? usageData : null });
|
|
85
|
+
} catch (e) { if (alive) setState({ phase: "error" }); }
|
|
171
86
|
};
|
|
172
|
-
|
|
87
|
+
load(); timer = setInterval(load, 30000);
|
|
88
|
+
return () => { alive = false; clearInterval(timer); };
|
|
89
|
+
}, [sessionId]);
|
|
173
90
|
|
|
174
91
|
const pricing = state.pricing || FALLBACK_PRICING;
|
|
175
92
|
const effectiveFrom = pricing.effectiveFrom || FALLBACK_PRICING.effectiveFrom;
|
|
93
|
+
const weekendFrom = pricing.weekendFrom || FALLBACK_PRICING.weekendFrom;
|
|
176
94
|
const balanceRow = state.phase === "ok" && state.balance ? firstBalance(state.balance) : null;
|
|
177
95
|
const currency = balanceRow && balanceRow.currency ? balanceRow.currency : "CNY";
|
|
178
96
|
const dict = currency === "USD" ? en : zh;
|
|
@@ -181,132 +99,243 @@ window.__ModuleLoader__.load({
|
|
|
181
99
|
const symbol = pricingSet.symbol || "¥";
|
|
182
100
|
const models = pricingSet.models || FALLBACK_PRICING.currencies.CNY.models;
|
|
183
101
|
|
|
184
|
-
const modelState = react.useSyncExternalStore(
|
|
185
|
-
directory ? (fn) => directory.subscribe(fn) : noopSubscribe,
|
|
186
|
-
() => (directory ? directory.getSnapshot() : null)
|
|
187
|
-
);
|
|
102
|
+
const modelState = react.useSyncExternalStore(directory ? (fn) => directory.subscribe(fn) : noopSubscribe, () => (directory ? directory.getSnapshot() : null));
|
|
188
103
|
const currentSelection = modelState && modelState.current ? modelState.current : null;
|
|
189
104
|
const modelId = currentSelection && currentSelection.model ? currentSelection.model : DEFAULT_MODEL;
|
|
190
105
|
const reasoningEffort = currentSelection ? currentSelection.reasoningEffort : void 0;
|
|
191
106
|
|
|
192
|
-
const period = periodFor(new Date(), effectiveFrom);
|
|
107
|
+
const period = periodFor(new Date(), effectiveFrom, weekendFrom);
|
|
193
108
|
const isPeak = period === "peak";
|
|
194
109
|
const isLegacy = period === "legacy";
|
|
195
110
|
const model = models[modelId] || models[DEFAULT_MODEL] || FALLBACK_PRICING.currencies.CNY.models[DEFAULT_MODEL];
|
|
196
111
|
const p = (period === "peak" ? model.peak : period === "offPeak" ? model.offPeak : model.legacy) || model.peak;
|
|
197
112
|
const outputPrice = fmtPrice(p.output);
|
|
198
|
-
const cost =
|
|
199
|
-
if (byPeriod && byPeriod.peak && byPeriod.offPeak) {
|
|
200
|
-
if (isLegacy) {
|
|
201
|
-
return model.legacy ? costOf(sumBuckets(byPeriod.peak, byPeriod.offPeak), model.legacy) : null;
|
|
202
|
-
}
|
|
203
|
-
return costOf(byPeriod.peak, model.peak) + costOf(byPeriod.offPeak, model.offPeak);
|
|
204
|
-
}
|
|
205
|
-
return usage !== undefined && usage !== null ? costOf(usage, p) : null;
|
|
206
|
-
})();
|
|
207
|
-
|
|
208
|
-
// off -> base price; high (default) -> one +; max -> two +.
|
|
113
|
+
const cost = state.usage && state.usage.spend ? state.usage.spend.total : null;
|
|
209
114
|
const plus = reasoningEffort === "max" ? "++" : reasoningEffort === "off" ? "" : "+";
|
|
210
|
-
|
|
211
|
-
const periodClass = isLegacy ? cssModule.legacy : isPeak ? cssModule.peak : cssModule.offPeak;
|
|
115
|
+
const periodClass = isLegacy ? C.legacy : isPeak ? C.peak : C.offPeak;
|
|
212
116
|
const periodLabel = isLegacy ? tr("period.legacy") : isPeak ? tr("period.peak") : tr("period.offPeak");
|
|
213
117
|
|
|
214
|
-
const
|
|
215
|
-
isLegacy ? tr("period.legacyNote") : tr("period.peakHours"),
|
|
216
|
-
isLegacy ? "" : tr("period.offPeakNote"),
|
|
217
|
-
"",
|
|
218
|
-
`${tr("label.model")}: ${(MODEL_META[modelId] || {}).label || modelId}${reasoningEffort ? ` · ${reasoningEffort}` : ""}`,
|
|
219
|
-
tr("reasoning.note")
|
|
220
|
-
].filter((line) => line !== "");
|
|
221
|
-
if (cost !== null && usage) {
|
|
222
|
-
tooltipParts.push(
|
|
223
|
-
"",
|
|
224
|
-
`${tr("label.spent")}: ${symbol}${fmtMoney(cost)} (${tr("cost.estimate")})`,
|
|
225
|
-
tr("cost.breakdown", {
|
|
226
|
-
miss: (usage.uncachedInputTokens ?? 0) + (usage.cacheWriteTokens ?? 0),
|
|
227
|
-
hit: usage.cacheReadTokens ?? 0,
|
|
228
|
-
output: usage.outputTokens ?? 0
|
|
229
|
-
})
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
tooltipParts.push("");
|
|
233
|
-
for (const [id, info] of Object.entries(models)) {
|
|
234
|
-
const meta = MODEL_META[id] || {};
|
|
235
|
-
const row = tr("price.row", { sym: symbol, hit: fmtPrice(info.peak.hit), miss: fmtPrice(info.peak.miss), output: fmtPrice(info.peak.output) });
|
|
236
|
-
const off = tr("price.row", { sym: symbol, hit: fmtPrice(info.offPeak.hit), miss: fmtPrice(info.offPeak.miss), output: fmtPrice(info.offPeak.output) });
|
|
237
|
-
const legacy = info.legacy ? tr("price.row", { sym: symbol, hit: fmtPrice(info.legacy.hit), miss: fmtPrice(info.legacy.miss), output: fmtPrice(info.legacy.output) }) : null;
|
|
238
|
-
tooltipParts.push(
|
|
239
|
-
`${meta.label || id} — ${tr("period.peak")}: ${row} / ${tr("period.offPeak")}: ${off}${legacy ? ` / ${tr("period.legacy")}: ${legacy}` : ""}`
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
const tooltip = tooltipParts.join("\n");
|
|
243
|
-
|
|
244
|
-
const sep = (key) => react.createElement("span", { key, className: cssModule.sep, "aria-hidden": true }, "|");
|
|
245
|
-
|
|
118
|
+
const sep = (key) => react.createElement("span", { key, className: C.sep, "aria-hidden": true }, "|");
|
|
246
119
|
const segs = [];
|
|
247
|
-
segs.push(react.createElement("span", { key: "spent" },
|
|
248
|
-
tr("label.spent"), " ",
|
|
249
|
-
cost === null
|
|
250
|
-
? react.createElement("span", { className: cssModule.num }, "—")
|
|
251
|
-
: react.createElement("span", { className: cssModule.num },
|
|
252
|
-
react.createElement("span", { className: cssModule.unit }, symbol),
|
|
253
|
-
fmtMoney(cost))));
|
|
120
|
+
segs.push(react.createElement("span", { key: "spent" }, tr("label.spent"), " ", react.createElement("span", { className: C.num }, cost === null ? "—" : `${symbol}${fmtMoney(cost)}`)));
|
|
254
121
|
segs.push(sep("sep1"));
|
|
255
|
-
segs.push(react.createElement("span", { key: "balance" },
|
|
256
|
-
tr("label.balance"), " ",
|
|
257
|
-
balanceRow === null
|
|
258
|
-
? react.createElement("span", { className: cssModule.num }, state.phase === "loading" ? "…" : tr("balance.unavailable"))
|
|
259
|
-
: react.createElement("span", { className: cssModule.num },
|
|
260
|
-
react.createElement("span", { className: cssModule.unit }, symbol),
|
|
261
|
-
fmtMoney(Number(balanceRow.total_balance)))));
|
|
122
|
+
segs.push(react.createElement("span", { key: "balance" }, tr("label.balance"), " ", react.createElement("span", { className: C.num }, balanceRow !== null ? `${symbol}${fmtMoney(Number(balanceRow.total_balance))}` : (state.phase === "loading" ? "…" : tr("balance.unavailable")))));
|
|
262
123
|
segs.push(sep("sep2"));
|
|
263
|
-
segs.push(react.createElement("span", { key: "period", className: `${
|
|
124
|
+
segs.push(react.createElement("span", { key: "period", className: `${C.chip} ${periodClass}` }, periodLabel));
|
|
264
125
|
segs.push(sep("sep3"));
|
|
265
|
-
segs.push(react.createElement("span", { key: "price" },
|
|
266
|
-
react.createElement("span", { className: cssModule.unit }, symbol),
|
|
267
|
-
outputPrice,
|
|
268
|
-
react.createElement("span", { className: cssModule.unit }, tr("price.perM")),
|
|
269
|
-
plus !== "" ? react.createElement("span", { className: cssModule.plus }, plus) : null));
|
|
126
|
+
segs.push(react.createElement("span", { key: "price" }, react.createElement("span", { className: C.unit }, symbol), outputPrice, react.createElement("span", { className: C.unit }, "/M"), plus !== "" ? react.createElement("span", { className: C.plus }, plus) : null));
|
|
270
127
|
|
|
271
|
-
return react.createElement(
|
|
128
|
+
return react.createElement(react.Fragment, null,
|
|
129
|
+
react.createElement("button", { type: "button", className: C.root, onClick: () => setOpen(true) }, segs),
|
|
130
|
+
open ? react.createElement(UsagePanel, { sessionId: props.sessionId, currency, symbol, pricing, effectiveFrom, tr, onClose: () => setOpen(false) }) : null
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function UsagePanel({ currency, symbol, pricing, tr, onClose }) {
|
|
135
|
+
const [usageData, setUsageData] = react.useState(null);
|
|
136
|
+
const [balance, setBalance] = react.useState(null);
|
|
137
|
+
const [sidebarW, setSidebarW] = react.useState(280);
|
|
138
|
+
const [monthIdxPeriod, setMonthIdxPeriod] = react.useState(0);
|
|
139
|
+
const [monthIdxTokens, setMonthIdxTokens] = react.useState(0);
|
|
140
|
+
const [editing, setEditing] = react.useState(false);
|
|
141
|
+
const [draft, setDraft] = react.useState("");
|
|
142
|
+
|
|
143
|
+
react.useEffect(() => {
|
|
144
|
+
let alive = true;
|
|
145
|
+
(async () => {
|
|
146
|
+
try {
|
|
147
|
+
const [u, b] = await Promise.all([
|
|
148
|
+
fetch("/api/deepseek-usage", { headers: { accept: "application/json" }, cache: "no-store" }).then((r) => r.json()),
|
|
149
|
+
fetch("/api/deepseek-balance", { headers: { accept: "application/json" }, cache: "no-store" }).then((r) => r.json())
|
|
150
|
+
]);
|
|
151
|
+
if (!alive) return;
|
|
152
|
+
setUsageData(u); setBalance(firstBalance(b));
|
|
153
|
+
} catch (e) {}
|
|
154
|
+
})();
|
|
155
|
+
return () => { alive = false; };
|
|
156
|
+
}, []);
|
|
157
|
+
|
|
158
|
+
react.useEffect(() => {
|
|
159
|
+
const frame = findFrame();
|
|
160
|
+
const col = frame && frame.firstElementChild;
|
|
161
|
+
if (!col) return;
|
|
162
|
+
const apply = () => setSidebarW(col.getBoundingClientRect().width);
|
|
163
|
+
apply();
|
|
164
|
+
const ro = new ResizeObserver(apply);
|
|
165
|
+
ro.observe(col);
|
|
166
|
+
return () => ro.disconnect();
|
|
167
|
+
}, []);
|
|
168
|
+
|
|
169
|
+
const months = react.useMemo(() => {
|
|
170
|
+
const set = new Set();
|
|
171
|
+
const cur = beijingMonthKey(Date.now());
|
|
172
|
+
if (usageData && usageData.days) for (const k of Object.keys(usageData.days)) set.add(k.slice(0, 7));
|
|
173
|
+
set.add(cur);
|
|
174
|
+
return [...set].sort();
|
|
175
|
+
}, [usageData]);
|
|
176
|
+
const monthKeyPeriod = months[months.length - 1 - monthIdxPeriod] || beijingMonthKey(Date.now());
|
|
177
|
+
const [yP, mP] = monthKeyPeriod.split("-").map(Number);
|
|
178
|
+
const cellsPeriod = react.useMemo(() => monthGrid(yP, mP - 1, usageData ? usageData.days : {}), [yP, mP, usageData]);
|
|
179
|
+
const monthKeyTokens = months[months.length - 1 - monthIdxTokens] || beijingMonthKey(Date.now());
|
|
180
|
+
const [yT, mT] = monthKeyTokens.split("-").map(Number);
|
|
181
|
+
const cellsTokens = react.useMemo(() => monthGrid(yT, mT - 1, usageData ? usageData.days : {}), [yT, mT, usageData]);
|
|
182
|
+
const maxDayTokens = react.useMemo(() => {
|
|
183
|
+
if (!usageData || !usageData.days) return 0;
|
|
184
|
+
return Object.values(usageData.days).reduce((m, d) => Math.max(m, d.tokens || 0), 0);
|
|
185
|
+
}, [usageData]);
|
|
186
|
+
const pricingSet = (pricing.currencies && pricing.currencies[currency]) || pricing.currencies.CNY;
|
|
187
|
+
const sym = pricingSet.symbol || "¥";
|
|
188
|
+
|
|
189
|
+
const totalSpend = usageData && usageData.spend ? usageData.spend.total : null;
|
|
190
|
+
const todaySpend = usageData && usageData.spend ? usageData.spend.today : null;
|
|
191
|
+
|
|
192
|
+
const modelEffortList = react.useMemo(() => {
|
|
193
|
+
if (!usageData || !usageData.modelEffort) return [];
|
|
194
|
+
return Object.entries(usageData.modelEffort).map(([k, v]) => ({ key: k, tokens: v.tokens })).sort((a, b) => b.tokens - a.tokens);
|
|
195
|
+
}, [usageData]);
|
|
196
|
+
|
|
197
|
+
const cellFor = (cell) => {
|
|
198
|
+
if (!cell) return react.createElement("div", { key: "b" + Math.random(), className: P.cell });
|
|
199
|
+
const color = dayColor(cell.data);
|
|
200
|
+
const cls = color === "flat" ? P.cell + " " + P.cellFlat : color === "peak" ? P.cell + " " + P.cellPeak : color === "off" ? P.cell + " " + P.cellOff : P.cell;
|
|
201
|
+
return react.createElement("div", { key: cell.key, className: cls, title: cell.key }, cell.day);
|
|
202
|
+
};
|
|
203
|
+
const tokenCellFor = (cell) => {
|
|
204
|
+
if (!cell) return react.createElement("div", { key: "t" + Math.random(), className: P.cell });
|
|
205
|
+
const tokens = cell.data ? cell.data.tokens : 0;
|
|
206
|
+
const color = tokenBlue(tokens, maxDayTokens);
|
|
207
|
+
return react.createElement("div", { key: cell.key, className: P.cell + (color ? " " + P.cellBlue : ""), style: color ? { background: color } : void 0, title: cell.key }, cell.day);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const modelLabel = (k) => { const [m, e] = k.split("|"); return `${(MODEL_META[m] || {}).label || m}${e ? " · " + e : ""}`; };
|
|
211
|
+
const stat = (label, value) => react.createElement("div", { className: P.statItem }, react.createElement("div", { className: P.statValue }, value), react.createElement("div", { className: P.statLabel }, label));
|
|
212
|
+
const sepBar = (key) => react.createElement("span", { key, className: P.sepBar }, "|");
|
|
213
|
+
const tokenName = usageData && usageData.tokenName ? usageData.tokenName : "API key";
|
|
214
|
+
const saveName = async () => {
|
|
215
|
+
const name = draft.trim() || "API key";
|
|
216
|
+
try {
|
|
217
|
+
const r = await fetch("/api/deepseek-balance/token-name", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ tokenName: name }) });
|
|
218
|
+
const d = await r.json();
|
|
219
|
+
setUsageData((u) => (u ? { ...u, tokenName: d.tokenName } : u));
|
|
220
|
+
} catch {}
|
|
221
|
+
setEditing(false);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
return react.createElement(react.Fragment, null,
|
|
225
|
+
react.createElement("div", { className: P.backdrop, style: { left: sidebarW + "px" }, onClick: onClose }),
|
|
226
|
+
react.createElement("div", { className: P.panel, style: { left: sidebarW + "px" } },
|
|
227
|
+
react.createElement("div", { className: P.head },
|
|
228
|
+
react.createElement("div", { className: P.avatar }, tokenName.slice(0, 2).toUpperCase()),
|
|
229
|
+
editing ? react.createElement("div", { className: P.editRow }, react.createElement("input", { className: P.editInput, value: draft, onChange: (e) => setDraft(e.target.value), autoFocus: true }), react.createElement("button", { className: P.btn + " " + P.btnPrimary, onClick: saveName }, tr("panel.save"))) : react.createElement("div", { className: P.nameRow }, react.createElement("span", { className: P.name }, tokenName), react.createElement("button", { className: P.edit, onClick: () => { setDraft(tokenName); setEditing(true); } }, "✎")),
|
|
230
|
+
react.createElement("div", { className: P.key }, usageData && usageData.apiKeyPreview ? usageData.apiKeyPreview : tr("balance.unavailable")),
|
|
231
|
+
react.createElement("button", { className: P.close, onClick: onClose }, "×")
|
|
232
|
+
),
|
|
233
|
+
react.createElement("div", { className: P.body },
|
|
234
|
+
react.createElement("div", { className: P.stats },
|
|
235
|
+
stat(tr("stat.total"), usageData ? fmtTokens(usageData.totalTokens) : "…"), sepBar("s1"),
|
|
236
|
+
stat(tr("stat.dailyPeak"), usageData ? fmtTokens(usageData.dailyPeakTokens) : "…"), sepBar("s2"),
|
|
237
|
+
stat(tr("stat.longest"), usageData ? fmtDuration(usageData.longestChatMs) : "…"), sepBar("s3"),
|
|
238
|
+
stat(tr("stat.todayTokens"), usageData && usageData.days && usageData.days[beijingDayKey(Date.now())] ? fmtTokens(usageData.days[beijingDayKey(Date.now())].tokens) : "…"), sepBar("s4"),
|
|
239
|
+
stat(tr("stat.longStreak"), usageData ? usageData.longestStreak + tr("unit.day") : "…")
|
|
240
|
+
),
|
|
241
|
+
react.createElement("div", { className: P.balance },
|
|
242
|
+
react.createElement("div", { className: P.balanceBlock },
|
|
243
|
+
react.createElement("div", { className: P.balanceBlockLabel }, tr("stat.today")),
|
|
244
|
+
react.createElement("div", { className: P.balanceBlockValue }, todaySpend !== null ? `${sym}${fmtMoney(todaySpend)}` : "…")
|
|
245
|
+
),
|
|
246
|
+
react.createElement("div", { className: P.balanceBlock },
|
|
247
|
+
react.createElement("div", { className: P.balanceBlockLabel }, tr("stat.cumulative")),
|
|
248
|
+
react.createElement("div", { className: P.balanceBlockValue }, totalSpend !== null ? `${sym}${fmtMoney(totalSpend)}` : "…")
|
|
249
|
+
),
|
|
250
|
+
react.createElement("div", { className: P.balanceBlock },
|
|
251
|
+
react.createElement("div", { className: P.balanceBlockLabel }, tr("label.balance")),
|
|
252
|
+
react.createElement("div", { className: P.balanceBlockValue }, balance ? `${sym}${fmtMoney(Number(balance.total_balance))}` : "…"),
|
|
253
|
+
react.createElement("button", { className: P.btn + " " + P.btnPrimary, onClick: () => window.open("https://platform.deepseek.com/usage", "_blank", "noopener") }, tr("panel.topup"))
|
|
254
|
+
)
|
|
255
|
+
),
|
|
256
|
+
react.createElement("div", { className: P.section },
|
|
257
|
+
react.createElement("h3", { className: P.title }, tr("panel.month")),
|
|
258
|
+
react.createElement("div", { className: P.monthGrids },
|
|
259
|
+
react.createElement("div", { className: P.monthGroup },
|
|
260
|
+
react.createElement("div", { className: P.monthNav },
|
|
261
|
+
react.createElement("button", { className: P.btn, disabled: monthIdxPeriod >= months.length - 1, onClick: () => setMonthIdxPeriod(monthIdxPeriod + 1) }, "‹"),
|
|
262
|
+
react.createElement("span", { className: P.rowVal }, tr("panel.monthPeriod") + " · " + monthKeyPeriod),
|
|
263
|
+
react.createElement("button", { className: P.btn, disabled: monthIdxPeriod <= 0, onClick: () => setMonthIdxPeriod(monthIdxPeriod - 1) }, "›")
|
|
264
|
+
),
|
|
265
|
+
react.createElement("div", { className: P.grid }, cellsPeriod.map(cellFor)),
|
|
266
|
+
react.createElement("div", { className: P.legend },
|
|
267
|
+
react.createElement("span", null, react.createElement("span", { className: P.dot, style: { background: "#60a5fa" } }), tr("legend.flat")),
|
|
268
|
+
react.createElement("span", null, react.createElement("span", { className: P.dot, style: { background: "#f97316" } }), tr("legend.peak")),
|
|
269
|
+
react.createElement("span", null, react.createElement("span", { className: P.dot, style: { background: "#22c55e" } }), tr("legend.off")),
|
|
270
|
+
react.createElement("span", null, react.createElement("span", { className: P.dot, style: { background: "rgba(127,127,127,.15)" } }), tr("legend.none"))
|
|
271
|
+
)
|
|
272
|
+
),
|
|
273
|
+
react.createElement("div", { className: P.monthGroup },
|
|
274
|
+
react.createElement("div", { className: P.monthNav },
|
|
275
|
+
react.createElement("button", { className: P.btn, disabled: monthIdxTokens >= months.length - 1, onClick: () => setMonthIdxTokens(monthIdxTokens + 1) }, "‹"),
|
|
276
|
+
react.createElement("span", { className: P.rowVal }, tr("panel.monthTokens") + " · " + monthKeyTokens),
|
|
277
|
+
react.createElement("button", { className: P.btn, disabled: monthIdxTokens <= 0, onClick: () => setMonthIdxTokens(monthIdxTokens - 1) }, "›")
|
|
278
|
+
),
|
|
279
|
+
react.createElement("div", { className: P.grid }, cellsTokens.map(tokenCellFor)),
|
|
280
|
+
react.createElement("div", { className: P.legend },
|
|
281
|
+
react.createElement("span", null, react.createElement("span", { className: P.dot, style: { background: "#93c5fd" } }), tr("legend.less")),
|
|
282
|
+
react.createElement("span", null, react.createElement("span", { className: P.dot, style: { background: "#1e40af" } }), tr("legend.more"))
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
),
|
|
287
|
+
react.createElement("div", { className: P.twoCol },
|
|
288
|
+
react.createElement("div", { className: P.section },
|
|
289
|
+
react.createElement("h3", { className: P.title }, tr("panel.topSessions")),
|
|
290
|
+
(usageData ? (usageData.sessions || []).slice(0, 8) : []).map((s, i) => react.createElement("div", { key: s.id, className: P.row },
|
|
291
|
+
react.createElement("span", { className: P.rank }, String(i + 1)),
|
|
292
|
+
react.createElement("span", { className: P.rowTitle + (s.archived ? " " + P.rowTitleGray : "") }, s.title || s.id),
|
|
293
|
+
react.createElement("span", { className: P.rowVal }, fmtTokens(s.tokens))
|
|
294
|
+
))
|
|
295
|
+
),
|
|
296
|
+
react.createElement("div", { className: P.section },
|
|
297
|
+
react.createElement("h3", { className: P.title }, tr("panel.peakSessions")),
|
|
298
|
+
(usageData ? [...(usageData.sessions || [])].sort((a, b) => b.peakTokens - a.peakTokens).slice(0, 5) : []).map((s, i) => react.createElement("div", { key: s.id, className: P.row },
|
|
299
|
+
react.createElement("span", { className: P.rank }, String(i + 1)),
|
|
300
|
+
react.createElement("span", { className: P.rowTitle }, s.title || s.id),
|
|
301
|
+
react.createElement("span", { className: P.rowVal }, fmtTokens(s.peakTokens))
|
|
302
|
+
))
|
|
303
|
+
)
|
|
304
|
+
),
|
|
305
|
+
react.createElement("div", { className: P.twoCol },
|
|
306
|
+
react.createElement("div", { className: P.section },
|
|
307
|
+
react.createElement("h3", { className: P.title }, tr("panel.modelEffort")),
|
|
308
|
+
modelEffortList.map((m) => react.createElement("div", { key: m.key, className: P.row },
|
|
309
|
+
react.createElement("span", { className: P.rowTitle }, modelLabel(m.key)),
|
|
310
|
+
react.createElement("span", { className: P.rowVal }, fmtTokens(m.tokens))
|
|
311
|
+
))
|
|
312
|
+
)
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
);
|
|
272
317
|
}
|
|
273
318
|
|
|
274
319
|
const NS = "deepseek-balance";
|
|
275
320
|
const zh = {
|
|
276
|
-
"label.spent": "消耗",
|
|
277
|
-
"
|
|
278
|
-
"label.model": "模型",
|
|
279
|
-
"period.peak": "高峰",
|
|
280
|
-
"period.offPeak": "空闲",
|
|
281
|
-
"period.legacy": "平价",
|
|
282
|
-
"period.legacyNote": "当前为调价前平价(8/17 前)",
|
|
283
|
-
"period.peakHours": "高峰:09:00–12:00、14:00–18:00(北京时间)",
|
|
284
|
-
"period.offPeakNote": "空闲:其余时段",
|
|
321
|
+
"label.spent": "消耗", "label.balance": "余额", "label.model": "模型",
|
|
322
|
+
"period.peak": "高峰", "period.offPeak": "空闲", "period.legacy": "平价",
|
|
285
323
|
"balance.unavailable": "余额不可用",
|
|
286
|
-
"
|
|
287
|
-
"
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
"
|
|
291
|
-
"reasoning.note": "思考链按输出 token 计费;+ / ++ 表示 high / max 思考会额外增加输出量"
|
|
324
|
+
"panel.stats": "统计", "panel.balance": "余额与消耗", "panel.topup": "跳转API官网", "panel.save": "保存",
|
|
325
|
+
"panel.topSessions": "消耗最多的对话", "panel.month": "每日分布", "panel.monthPeriod": "每日时段分布", "panel.monthTokens": "每日 Token 消耗", "panel.peakSessions": "高峰消耗排行", "panel.modelEffort": "模型与思考强度",
|
|
326
|
+
"stat.total": "累计 Token", "stat.dailyPeak": "每日峰值", "stat.longest": "最长聊天", "stat.longStreak": "最长连续", "stat.todayTokens": "今日 Token", "stat.today": "今日消耗", "stat.cumulative": "累计消耗",
|
|
327
|
+
"unit.day": " 天",
|
|
328
|
+
"legend.flat": "平价", "legend.peak": "高峰", "legend.off": "空闲", "legend.none": "无数据", "legend.less": "少", "legend.more": "多"
|
|
292
329
|
};
|
|
293
330
|
const en = {
|
|
294
|
-
"label.spent": "Spent",
|
|
295
|
-
"
|
|
296
|
-
"label.model": "Model",
|
|
297
|
-
"period.peak": "Peak",
|
|
298
|
-
"period.offPeak": "Off-peak",
|
|
299
|
-
"period.legacy": "Flat",
|
|
300
|
-
"period.legacyNote": "Pre-change flat pricing (before Aug 17)",
|
|
301
|
-
"period.peakHours": "Peak: 01:00–04:00, 06:00–10:00 UTC",
|
|
302
|
-
"period.offPeakNote": "Off-peak: all other hours",
|
|
331
|
+
"label.spent": "Spent", "label.balance": "Balance", "label.model": "Model",
|
|
332
|
+
"period.peak": "Peak", "period.offPeak": "Off-peak", "period.legacy": "Flat",
|
|
303
333
|
"balance.unavailable": "balance unavailable",
|
|
304
|
-
"
|
|
305
|
-
"
|
|
306
|
-
"
|
|
307
|
-
"
|
|
308
|
-
"
|
|
309
|
-
"reasoning.note": "Reasoning tokens are billed as output; + / ++ mark high / max effort which adds extra output"
|
|
334
|
+
"panel.stats": "Stats", "panel.balance": "Balance & Spend", "panel.topup": "API site", "panel.save": "Save",
|
|
335
|
+
"panel.topSessions": "Top conversations", "panel.month": "Daily", "panel.monthPeriod": "Daily period", "panel.monthTokens": "Daily token usage", "panel.peakSessions": "Peak-hour ranking", "panel.modelEffort": "Model & effort",
|
|
336
|
+
"stat.total": "Total tokens", "stat.dailyPeak": "Daily peak", "stat.longest": "Longest chat", "stat.longStreak": "Longest streak", "stat.todayTokens": "Today tokens", "stat.today": "Today", "stat.cumulative": "Total",
|
|
337
|
+
"unit.day": "d",
|
|
338
|
+
"legend.flat": "Flat", "legend.peak": "Peak", "legend.off": "Off-peak", "legend.none": "None", "legend.less": "Less", "legend.more": "More"
|
|
310
339
|
};
|
|
311
340
|
|
|
312
341
|
const inject = ["slots", "locale"];
|
|
@@ -319,12 +348,8 @@ window.__ModuleLoader__.load({
|
|
|
319
348
|
inject: (sessionId) => {
|
|
320
349
|
const models = ctx.get("modelDirectories");
|
|
321
350
|
let directory;
|
|
322
|
-
if (models && typeof models.directoryFor === "function") {
|
|
323
|
-
|
|
324
|
-
directory = models.directoryFor(sessionId);
|
|
325
|
-
} catch {}
|
|
326
|
-
}
|
|
327
|
-
return { directory: directory ? directory.store : void 0 };
|
|
351
|
+
if (models && typeof models.directoryFor === "function") { try { directory = models.directoryFor(sessionId); } catch {} }
|
|
352
|
+
return { directory: directory ? directory.store : void 0, sessionId };
|
|
328
353
|
}
|
|
329
354
|
}, BillingBadge));
|
|
330
355
|
}
|