@cassius0924/dsh-usage-dashboard 0.8.1 → 0.8.3
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 +55 -3
- package/lib/client.js +849 -277
- package/lib/client.js.map +4 -4
- package/lib/index.js +506 -61
- package/lib/index.js.map +4 -4
- package/lib/types/breaker-gate.d.ts +94 -0
- package/lib/types/breaker-settings.d.ts +28 -0
- package/lib/types/breaker.d.ts +49 -0
- package/lib/types/client/api.d.ts +8 -1
- package/lib/types/client/breaker-pill.d.ts +21 -0
- package/lib/types/client/breaker-state.d.ts +8 -0
- package/lib/types/client/breaker.d.ts +60 -0
- package/lib/types/client/charts.d.ts +8 -0
- package/lib/types/client/i18n.d.ts +21 -0
- package/lib/types/client/locales.d.ts +30 -0
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/client/top-up.d.ts +13 -0
- package/lib/types/context.d.ts +36 -0
- package/lib/types/contract.d.ts +65 -0
- package/lib/types/index.d.ts +19 -16
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -26,7 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
name: () => name
|
|
27
27
|
});
|
|
28
28
|
module.exports = __toCommonJS(index_exports);
|
|
29
|
-
var
|
|
29
|
+
var import_react10 = require("react");
|
|
30
30
|
|
|
31
31
|
// src/client/boundary.tsx
|
|
32
32
|
var import_react = require("react");
|
|
@@ -89,9 +89,9 @@ function loadPersisted(storageKey, isUsable) {
|
|
|
89
89
|
function pruneOldVersions(storageKey) {
|
|
90
90
|
if (typeof localStorage === "undefined") return;
|
|
91
91
|
try {
|
|
92
|
-
const
|
|
92
|
+
const current2 = `${storageKey}:v${LS_VERSION}`;
|
|
93
93
|
for (const key of Object.keys(localStorage)) {
|
|
94
|
-
if (key.startsWith(`${storageKey}:v`) && key !==
|
|
94
|
+
if (key.startsWith(`${storageKey}:v`) && key !== current2) localStorage.removeItem(key);
|
|
95
95
|
}
|
|
96
96
|
} catch {
|
|
97
97
|
}
|
|
@@ -118,6 +118,17 @@ function createCache(ttlMs, storageKey, isUsable) {
|
|
|
118
118
|
|
|
119
119
|
// src/contract.ts
|
|
120
120
|
var USAGE_WINDOW_DAYS = [7, 30, 90, 365];
|
|
121
|
+
var SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
122
|
+
var isValidSessionId = (id) => SESSION_ID_PATTERN.test(id);
|
|
123
|
+
var BREAKER_API = {
|
|
124
|
+
state: "/api/dsh-usage-dashboard/breaker",
|
|
125
|
+
release: "/api/dsh-usage-dashboard/breaker/release"
|
|
126
|
+
};
|
|
127
|
+
var isBreakerSettings = (value) => {
|
|
128
|
+
if (typeof value !== "object" || value === null) return false;
|
|
129
|
+
const candidate = value;
|
|
130
|
+
return typeof candidate.enabled === "boolean" && Array.isArray(candidate.sessions) && candidate.sessions.every((session) => typeof session === "string" && isValidSessionId(session));
|
|
131
|
+
};
|
|
121
132
|
|
|
122
133
|
// src/client/api.ts
|
|
123
134
|
var BALANCE_TTL_MS = 6e4;
|
|
@@ -195,15 +206,15 @@ var sessionUsageCache = /* @__PURE__ */ new Map();
|
|
|
195
206
|
var sessionUsageListeners = /* @__PURE__ */ new Map();
|
|
196
207
|
var getCachedSessionUsage = (sessionId) => sessionUsageCache.get(sessionId) ?? null;
|
|
197
208
|
var subscribeSessionUsage = (sessionId, fn) => {
|
|
198
|
-
let
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
sessionUsageListeners.set(sessionId,
|
|
209
|
+
let listeners2 = sessionUsageListeners.get(sessionId);
|
|
210
|
+
if (listeners2 === void 0) {
|
|
211
|
+
listeners2 = /* @__PURE__ */ new Set();
|
|
212
|
+
sessionUsageListeners.set(sessionId, listeners2);
|
|
202
213
|
}
|
|
203
|
-
|
|
214
|
+
listeners2.add(fn);
|
|
204
215
|
return () => {
|
|
205
|
-
|
|
206
|
-
if (
|
|
216
|
+
listeners2?.delete(fn);
|
|
217
|
+
if (listeners2?.size === 0) sessionUsageListeners.delete(sessionId);
|
|
207
218
|
};
|
|
208
219
|
};
|
|
209
220
|
var publishSessionUsage = (sessionId, response) => {
|
|
@@ -230,20 +241,118 @@ async function fetchSessionUsage(sessionId, force = false) {
|
|
|
230
241
|
sessionUsageInflight.set(sessionId, tracked);
|
|
231
242
|
return tracked;
|
|
232
243
|
}
|
|
244
|
+
async function breakerRequest(url, init) {
|
|
245
|
+
try {
|
|
246
|
+
const response = await fetch(url, {
|
|
247
|
+
method: init.method,
|
|
248
|
+
cache: "no-store",
|
|
249
|
+
headers: init.body === void 0 ? { accept: "application/json" } : { accept: "application/json", "content-type": "application/json" },
|
|
250
|
+
...init.body === void 0 ? {} : { body: init.body }
|
|
251
|
+
});
|
|
252
|
+
if (!response.ok) return { ok: false, error: `HTTP ${response.status}` };
|
|
253
|
+
return await response.json();
|
|
254
|
+
} catch (error) {
|
|
255
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async function fetchBreaker() {
|
|
259
|
+
return breakerRequest(BREAKER_API.state, { method: "GET" });
|
|
260
|
+
}
|
|
261
|
+
async function updateBreaker(request) {
|
|
262
|
+
return breakerRequest(BREAKER_API.state, { method: "PUT", body: JSON.stringify(request) });
|
|
263
|
+
}
|
|
264
|
+
async function releaseBreaker(request) {
|
|
265
|
+
return breakerRequest(
|
|
266
|
+
BREAKER_API.release,
|
|
267
|
+
request === void 0 ? { method: "POST" } : { method: "POST", body: JSON.stringify(request) }
|
|
268
|
+
);
|
|
269
|
+
}
|
|
233
270
|
|
|
234
271
|
// src/client/dashboard.tsx
|
|
235
|
-
var
|
|
272
|
+
var import_react5 = require("react");
|
|
236
273
|
|
|
237
|
-
// src/client/
|
|
274
|
+
// src/client/breaker.ts
|
|
238
275
|
var BEIJING_OFFSET_MS = 8 * 60 * 60 * 1e3;
|
|
276
|
+
var SHORT_SESSION_ID = 8;
|
|
277
|
+
function breakerIsUsable(res) {
|
|
278
|
+
if (res === null || res === void 0 || res.ok !== true) return false;
|
|
279
|
+
const data = res.data;
|
|
280
|
+
if (data === void 0 || data === null) return false;
|
|
281
|
+
return isBreakerSettings(data) && Array.isArray(data.holding) && data.holding.every((id) => typeof id === "string") && typeof data.now === "string" && typeof data.peak === "boolean" && (data.resumeAt === null || typeof data.resumeAt === "string") && Array.isArray(data.peakWindows) && data.peakWindows.every((window2) => typeof window2 === "string");
|
|
282
|
+
}
|
|
283
|
+
function beijingClock(iso) {
|
|
284
|
+
if (typeof iso !== "string" || iso === "") return null;
|
|
285
|
+
const ms = Date.parse(iso);
|
|
286
|
+
if (!Number.isFinite(ms)) return null;
|
|
287
|
+
const beijing = new Date(ms + BEIJING_OFFSET_MS);
|
|
288
|
+
const hours = String(beijing.getUTCHours()).padStart(2, "0");
|
|
289
|
+
const minutes = String(beijing.getUTCMinutes()).padStart(2, "0");
|
|
290
|
+
return `${hours}:${minutes}`;
|
|
291
|
+
}
|
|
292
|
+
function isSessionOptedIn(data, sessionId) {
|
|
293
|
+
if (data === null || sessionId === null || sessionId === void 0 || sessionId === "") return false;
|
|
294
|
+
return Array.isArray(data.sessions) && data.sessions.includes(sessionId);
|
|
295
|
+
}
|
|
296
|
+
function isSessionEnabled(data, sessionId) {
|
|
297
|
+
if (data === null || data.enabled !== true) return false;
|
|
298
|
+
return isSessionOptedIn(data, sessionId);
|
|
299
|
+
}
|
|
300
|
+
function sessionBreakerState(data, sessionId) {
|
|
301
|
+
if (!isSessionEnabled(data, sessionId)) return "off";
|
|
302
|
+
return data !== null && Array.isArray(data.holding) && data.holding.includes(sessionId) ? "holding" : "on";
|
|
303
|
+
}
|
|
304
|
+
function withSessionToggled(data, sessionId, enabled) {
|
|
305
|
+
if (data === null) return null;
|
|
306
|
+
const sessions = enabled ? data.sessions.includes(sessionId) ? data.sessions : [...data.sessions, sessionId] : data.sessions.filter((id) => id !== sessionId);
|
|
307
|
+
const holding = enabled ? data.holding : data.holding.filter((id) => id !== sessionId);
|
|
308
|
+
return { ...data, sessions, holding };
|
|
309
|
+
}
|
|
310
|
+
function withMasterToggled(data, enabled) {
|
|
311
|
+
if (data === null) return null;
|
|
312
|
+
return { ...data, enabled, holding: enabled ? data.holding : [] };
|
|
313
|
+
}
|
|
314
|
+
function shortSessionId(sessionId) {
|
|
315
|
+
const trimmed = sessionId.trim();
|
|
316
|
+
return trimmed.length <= SHORT_SESSION_ID ? trimmed : `${trimmed.slice(0, SHORT_SESSION_ID)}\u2026`;
|
|
317
|
+
}
|
|
318
|
+
function breakerPhase(data) {
|
|
319
|
+
if (data === null) return "unavailable";
|
|
320
|
+
if (!data.enabled) return "masterOff";
|
|
321
|
+
if (!data.peak) return "offPeak";
|
|
322
|
+
return data.holding.length > 0 ? "peakHolding" : "peak";
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/client/breaker-state.ts
|
|
326
|
+
var import_react2 = require("react");
|
|
327
|
+
var current = null;
|
|
328
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
329
|
+
function getBreakerData() {
|
|
330
|
+
return current;
|
|
331
|
+
}
|
|
332
|
+
function publishBreakerData(data) {
|
|
333
|
+
current = data;
|
|
334
|
+
for (const listener of [...listeners]) listener();
|
|
335
|
+
}
|
|
336
|
+
function subscribeBreakerData(listener) {
|
|
337
|
+
listeners.add(listener);
|
|
338
|
+
return () => {
|
|
339
|
+
listeners.delete(listener);
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function useBreakerData() {
|
|
343
|
+
return (0, import_react2.useSyncExternalStore)(subscribeBreakerData, getBreakerData, getBreakerData);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/client/budget.ts
|
|
347
|
+
var BEIJING_OFFSET_MS2 = 8 * 60 * 60 * 1e3;
|
|
239
348
|
function budgetSnapshot(spentValue, budgetValue, nowMs = Date.now()) {
|
|
240
349
|
const spent = Number.isFinite(spentValue) ? Math.max(0, spentValue) : 0;
|
|
241
350
|
const budget = Number.isFinite(budgetValue) ? Math.max(0, budgetValue) : 0;
|
|
242
|
-
const beijingNow = new Date(nowMs +
|
|
351
|
+
const beijingNow = new Date(nowMs + BEIJING_OFFSET_MS2);
|
|
243
352
|
const year = beijingNow.getUTCFullYear();
|
|
244
353
|
const month = beijingNow.getUTCMonth();
|
|
245
|
-
const start = Date.UTC(year, month, 1) -
|
|
246
|
-
const end = Date.UTC(year, month + 1, 1) -
|
|
354
|
+
const start = Date.UTC(year, month, 1) - BEIJING_OFFSET_MS2;
|
|
355
|
+
const end = Date.UTC(year, month + 1, 1) - BEIJING_OFFSET_MS2;
|
|
247
356
|
const elapsedRatio = Math.min(1, Math.max(1 / (end - start), (nowMs - start) / (end - start)));
|
|
248
357
|
const forecast = spent / elapsedRatio;
|
|
249
358
|
const ratio = budget > 0 ? spent / budget : 0;
|
|
@@ -254,12 +363,12 @@ function budgetSnapshot(spentValue, budgetValue, nowMs = Date.now()) {
|
|
|
254
363
|
}
|
|
255
364
|
|
|
256
365
|
// src/client/charts.tsx
|
|
257
|
-
var
|
|
366
|
+
var import_react4 = require("react");
|
|
258
367
|
|
|
259
368
|
// src/client/chart-focus.ts
|
|
260
|
-
function nextChartFocus(
|
|
369
|
+
function nextChartFocus(current2, count, key, horizontalStep = 1) {
|
|
261
370
|
if (count <= 0) return 0;
|
|
262
|
-
const index = Math.min(Math.max(0,
|
|
371
|
+
const index = Math.min(Math.max(0, current2), count - 1);
|
|
263
372
|
if (key === "Home") return 0;
|
|
264
373
|
if (key === "End") return count - 1;
|
|
265
374
|
const step = key === "ArrowLeft" || key === "ArrowRight" ? Math.max(1, horizontalStep) : 1;
|
|
@@ -272,8 +381,8 @@ function lastPopulatedIndex(values) {
|
|
|
272
381
|
}
|
|
273
382
|
return Math.max(0, values.length - 1);
|
|
274
383
|
}
|
|
275
|
-
function nextPinnedIndex(
|
|
276
|
-
return
|
|
384
|
+
function nextPinnedIndex(current2, tapped) {
|
|
385
|
+
return current2 === tapped ? null : tapped;
|
|
277
386
|
}
|
|
278
387
|
function isTapGesture(dx, dy, dt, moveThreshold = 10, timeThreshold = 500) {
|
|
279
388
|
return Math.hypot(dx, dy) <= moveThreshold && dt <= timeThreshold;
|
|
@@ -288,7 +397,7 @@ function maxOrOne(values) {
|
|
|
288
397
|
}
|
|
289
398
|
|
|
290
399
|
// src/client/i18n.tsx
|
|
291
|
-
var
|
|
400
|
+
var import_react3 = require("react");
|
|
292
401
|
|
|
293
402
|
// src/client/locales.ts
|
|
294
403
|
var NS = "dsh-usage-dashboard";
|
|
@@ -481,6 +590,7 @@ var zh = {
|
|
|
481
590
|
"balance.granted": "\u8D60\u9001\u989D\u5EA6 {amount}",
|
|
482
591
|
"balance.runway": "\u9884\u8BA1\u53EF\u7528",
|
|
483
592
|
"balance.runwayBreakdownTitle": "\u67E5\u770B\u9884\u8BA1\u53EF\u7528\u5929\u6570\u8BF4\u660E",
|
|
593
|
+
"balance.topUpTitle": "\u4F59\u989D\u504F\u4F4E\uFF0C\u524D\u5F80 DeepSeek \u5F00\u653E\u5E73\u53F0\u5145\u503C\uFF08\u65B0\u6807\u7B7E\u9875\u6253\u5F00\uFF09",
|
|
484
594
|
"balance.days": "{count} \u5929",
|
|
485
595
|
"balance.overYear": "> 365 \u5929",
|
|
486
596
|
"balance.noRunway": "\u8FD1 {days} \u5929\u6CA1\u6709\u7528\u91CF\uFF0C\u65E0\u6CD5\u4F30\u7B97",
|
|
@@ -519,6 +629,9 @@ var zh = {
|
|
|
519
629
|
"settings.widgetTabs": "\u5C55\u793A\u6D6E\u7A97\u7684\u9875\u9762",
|
|
520
630
|
"settings.widgetTabsHint": "\u5217\u8868\u4F1A\u968F\u5DF2\u5B89\u88C5\u7684\u9875\u9762\u81EA\u52A8\u66F4\u65B0\u3002",
|
|
521
631
|
"settings.widgetTabsEmpty": "\u5F53\u524D\u6CA1\u6709\u53EF\u914D\u7F6E\u7684\u4F1A\u8BDD\u9875\u9762\u3002",
|
|
632
|
+
"settings.peakPause": "\u9AD8\u5CF0\u6682\u505C",
|
|
633
|
+
"settings.peakPauseHint": "\u603B\u5F00\u5173\uFF1B\u5173\u95ED\u540E\u6240\u6709\u4F1A\u8BDD\u90FD\u4E0D\u6682\u505C\u3002\u6BCF\u4E2A\u4F1A\u8BDD\u7684\u5F00\u5173\u4F1A\u4FDD\u7559\uFF0C\u53EF\u968F\u65F6\u91CD\u65B0\u6253\u5F00\u3002",
|
|
634
|
+
"settings.peakPauseLoading": "\u6B63\u5728\u8BFB\u53D6\u603B\u5F00\u5173\u72B6\u6001\u2026",
|
|
522
635
|
"settings.lowBalance": "\u4F59\u989D\u9884\u8B66\u7EBF",
|
|
523
636
|
"settings.lowBalanceOn": "\u4F59\u989D\u4F4E\u4E8E {amount} {currency} \u65F6\uFF0C\u8FD9\u91CC\u548C\u60AC\u6D6E\u7A97\u90FD\u4F1A\u8F6C\u4E3A\u8B66\u793A\u8272\u3002\u586B 0 \u5173\u95ED\u3002",
|
|
524
637
|
"settings.lowBalanceOff": "\u5DF2\u5173\u95ED\u4F59\u989D\u9884\u8B66\u3002\u586B\u4E00\u4E2A\u5927\u4E8E 0 \u7684\u6570\u5F00\u542F\u3002",
|
|
@@ -535,6 +648,32 @@ var zh = {
|
|
|
535
648
|
"settings.tabDragAria": "\u62D6\u52A8\u300C\u989D\u5EA6\u300D\u8C03\u6574\u6807\u7B7E\u4F4D\u7F6E",
|
|
536
649
|
"settings.tabMoveEarlier": "\u524D\u79FB",
|
|
537
650
|
"settings.tabMoveLater": "\u540E\u79FB",
|
|
651
|
+
"breaker.title": "\u9AD8\u5CF0\u6682\u505C",
|
|
652
|
+
"breaker.desc": "\u5728\u5BF9\u8BDD\u8F93\u5165\u6846\u65C1\u6309\u4F1A\u8BDD\u5F00\u542F\uFF1B\u9AD8\u5CF0\u65F6\u6BB5\u81EA\u52A8\u6682\u505C\u8BE5\u4F1A\u8BDD\uFF0C18:00 \u7EE7\u7EED\u3002\u81EA\u52A8\u5316\u4EFB\u52A1\u4E0D\u53D7\u5F71\u54CD\uFF1B\u603B\u5F00\u5173\u5728\u300C\u8BBE\u7F6E\u300D\u5361\u91CC\u3002",
|
|
653
|
+
"breaker.pillOff": "\u9AD8\u5CF0\u6682\u505C \xB7 \u5173",
|
|
654
|
+
"breaker.pillOn": "\u9AD8\u5CF0\u6682\u505C \xB7 \u5F00",
|
|
655
|
+
"breaker.pillHolding": "\u5DF2\u6682\u505C \xB7 {clock}",
|
|
656
|
+
"breaker.pillAriaOff": "\u9AD8\u5CF0\u6682\u505C\uFF1A\u672C\u4F1A\u8BDD\u5173\u95ED\uFF0C\u70B9\u51FB\u5F00\u542F",
|
|
657
|
+
"breaker.pillAriaOn": "\u9AD8\u5CF0\u6682\u505C\uFF1A\u672C\u4F1A\u8BDD\u5F00\u542F\uFF0C{clock} \u7EE7\u7EED\uFF0C\u70B9\u51FB\u5173\u95ED",
|
|
658
|
+
"breaker.pillAriaHolding": "\u9AD8\u5CF0\u6682\u505C\uFF1A\u672C\u4F1A\u8BDD\u6B63\u5728\u6682\u505C\uFF0C{clock} \u7EE7\u7EED\uFF0C\u70B9\u51FB\u5173\u95ED",
|
|
659
|
+
"breaker.pillAriaMasterOff": "\u9AD8\u5CF0\u6682\u505C\uFF1A\u603B\u5F00\u5173\u5DF2\u5173\u95ED\uFF0C\u672C\u4F1A\u8BDD\u4E0D\u4F1A\u6682\u505C\uFF1B\u70B9\u51FB\u53EA\u5207\u6362\u672C\u4F1A\u8BDD\u5F00\u5173",
|
|
660
|
+
"breaker.pillTitleOff": "\u9AD8\u5CF0\u65F6\u6BB5\u6682\u505C\u672C\u4F1A\u8BDD\uFF1A\u5F53\u524D\u5173\u95ED\uFF0C\u70B9\u51FB\u5F00\u542F",
|
|
661
|
+
"breaker.pillTitleOn": "\u9AD8\u5CF0\u65F6\u6BB5\u6682\u505C\u672C\u4F1A\u8BDD\uFF1A\u5F53\u524D\u5F00\u542F\uFF0C{clock} \u7EE7\u7EED\uFF1B\u70B9\u51FB\u5173\u95ED",
|
|
662
|
+
"breaker.pillTitleHolding": "\u672C\u4F1A\u8BDD\u6B63\u5728\u6682\u505C\uFF0C{clock} \u7EE7\u7EED\uFF1B\u70B9\u51FB\u5173\u95ED",
|
|
663
|
+
"breaker.pillTitleMasterOff": "\u9AD8\u5CF0\u6682\u505C\u7684\u603B\u5F00\u5173\u5DF2\u5728\u989D\u5EA6\u9875\u5173\u95ED\uFF0C\u672C\u4F1A\u8BDD\u4E0D\u4F1A\u6682\u505C\uFF1B\u70B9\u51FB\u53EA\u5207\u6362\u672C\u4F1A\u8BDD\u5F00\u5173",
|
|
664
|
+
"breaker.actionError": "\u64CD\u4F5C\u5931\u8D25\uFF1A{error}",
|
|
665
|
+
"breaker.loading": "\u8F7D\u5165\u4E2D\u2026",
|
|
666
|
+
"breaker.statusUnavailable": "\u72B6\u6001\u6682\u4E0D\u53EF\u7528",
|
|
667
|
+
"breaker.statusMasterOff": "\u603B\u5F00\u5173\u5DF2\u5173\u95ED\uFF0C\u6240\u6709\u4F1A\u8BDD\u90FD\u4E0D\u6682\u505C\uFF1B\u53EF\u5728\u300C\u8BBE\u7F6E\u300D\u5361\u91CD\u65B0\u6253\u5F00",
|
|
668
|
+
"breaker.statusOffPeak": "\u95F2\u65F6 \xB7 \u4E0D\u6682\u505C\u4F1A\u8BDD",
|
|
669
|
+
"breaker.statusPeak": "\u9AD8\u5CF0\u65F6\u6BB5 \xB7 \u5F00\u542F\u7684\u4F1A\u8BDD\u4F1A\u81EA\u52A8\u6682\u505C",
|
|
670
|
+
"breaker.statusHolding": "\u5DF2\u6682\u505C {count} \u4E2A\u4F1A\u8BDD \xB7 {clock} \u7EE7\u7EED",
|
|
671
|
+
"breaker.statusHoldingNoClock": "\u5DF2\u6682\u505C {count} \u4E2A\u4F1A\u8BDD",
|
|
672
|
+
"breaker.release": "\u7ACB\u5373\u5168\u90E8\u7EE7\u7EED",
|
|
673
|
+
"breaker.releaseBusy": "\u6B63\u5728\u7EE7\u7EED\u2026",
|
|
674
|
+
"breaker.empty": "\u8FD8\u6CA1\u6709\u4F1A\u8BDD\u5F00\u542F\u9AD8\u5CF0\u6682\u505C\u3002\u5728\u5BF9\u8BDD\u8F93\u5165\u6846\u65C1\u7684\u300C\u9AD8\u5CF0\u6682\u505C\u300D\u5F00\u5173\u91CC\u5F00\u542F\u3002",
|
|
675
|
+
"breaker.rowDisableAria": "\u5173\u95ED\u300C{title}\u300D\u7684\u9AD8\u5CF0\u6682\u505C",
|
|
676
|
+
"breaker.holding": "\u6682\u505C\u4E2D \xB7 {clock}",
|
|
538
677
|
"panel.caption": "\u8D26\u6237\u7EA7 \xB7 \u8DE8\u5168\u90E8\u4F1A\u8BDD",
|
|
539
678
|
"panel.openAria": "\u6253\u5F00\u989D\u5EA6\u9875",
|
|
540
679
|
"widget.updatedNow": "\u521A\u521A\u66F4\u65B0",
|
|
@@ -752,6 +891,7 @@ var en = {
|
|
|
752
891
|
"balance.granted": "Granted {amount}",
|
|
753
892
|
"balance.runway": "Estimated runway",
|
|
754
893
|
"balance.runwayBreakdownTitle": "View estimated runway details",
|
|
894
|
+
"balance.topUpTitle": "Low balance \u2014 top up on the DeepSeek platform (opens a new tab)",
|
|
755
895
|
"balance.days": "{count} days",
|
|
756
896
|
"balance.overYear": "> 365 days",
|
|
757
897
|
"balance.noRunway": "No usage in the last {days} days, so runway cannot be estimated",
|
|
@@ -790,6 +930,9 @@ var en = {
|
|
|
790
930
|
"settings.widgetTabs": "Show widget on",
|
|
791
931
|
"settings.widgetTabsHint": "Updates automatically as views are installed.",
|
|
792
932
|
"settings.widgetTabsEmpty": "There are no configurable conversation views right now.",
|
|
933
|
+
"settings.peakPause": "Peak pause",
|
|
934
|
+
"settings.peakPauseHint": "Master switch; when off no conversation pauses. Per-conversation switches are kept and can be turned back on at any time.",
|
|
935
|
+
"settings.peakPauseLoading": "Reading the master switch\u2026",
|
|
793
936
|
"settings.lowBalance": "Balance warning",
|
|
794
937
|
"settings.lowBalanceOn": "This page and the widget turn amber below {amount} {currency}. Enter 0 to disable.",
|
|
795
938
|
"settings.lowBalanceOff": "Balance warnings are off. Enter an amount above 0 to enable them.",
|
|
@@ -806,6 +949,32 @@ var en = {
|
|
|
806
949
|
"settings.tabDragAria": "Drag Usage to change its tab position",
|
|
807
950
|
"settings.tabMoveEarlier": "Move earlier",
|
|
808
951
|
"settings.tabMoveLater": "Move later",
|
|
952
|
+
"breaker.title": "Peak pause",
|
|
953
|
+
"breaker.desc": "Turn it on per conversation beside the composer; during peak hours that session pauses and resumes at 18:00. Automated tasks are unaffected; the master switch is in Settings.",
|
|
954
|
+
"breaker.pillOff": "Peak pause \xB7 off",
|
|
955
|
+
"breaker.pillOn": "Peak pause \xB7 on",
|
|
956
|
+
"breaker.pillHolding": "Paused \xB7 {clock}",
|
|
957
|
+
"breaker.pillAriaOff": "Peak pause: off for this conversation, click to turn on",
|
|
958
|
+
"breaker.pillAriaOn": "Peak pause: on for this conversation, resumes at {clock}, click to turn off",
|
|
959
|
+
"breaker.pillAriaHolding": "Peak pause: this conversation is paused now, resumes at {clock}, click to turn off",
|
|
960
|
+
"breaker.pillAriaMasterOff": "Peak pause: the master switch is off, so this conversation will not pause; clicking only flips this conversation",
|
|
961
|
+
"breaker.pillTitleOff": "Pause this conversation during peak hours: currently off, click to turn on",
|
|
962
|
+
"breaker.pillTitleOn": "Pause this conversation during peak hours: currently on, resumes at {clock}; click to turn off",
|
|
963
|
+
"breaker.pillTitleHolding": "This conversation is paused and resumes at {clock}; click to turn off",
|
|
964
|
+
"breaker.pillTitleMasterOff": "The Peak pause master switch is off on the Usage page, so this conversation will not pause; clicking only flips this conversation",
|
|
965
|
+
"breaker.actionError": "Action failed: {error}",
|
|
966
|
+
"breaker.loading": "Loading\u2026",
|
|
967
|
+
"breaker.statusUnavailable": "Status unavailable",
|
|
968
|
+
"breaker.statusMasterOff": "Master switch is off; no conversation pauses. Turn it back on in Settings.",
|
|
969
|
+
"breaker.statusOffPeak": "Off-peak \xB7 nothing is paused",
|
|
970
|
+
"breaker.statusPeak": "Peak hours \xB7 conversations with it on pause automatically",
|
|
971
|
+
"breaker.statusHolding": "{count} conversations paused \xB7 resumes at {clock}",
|
|
972
|
+
"breaker.statusHoldingNoClock": "{count} conversations paused",
|
|
973
|
+
"breaker.release": "Resume all now",
|
|
974
|
+
"breaker.releaseBusy": "Resuming\u2026",
|
|
975
|
+
"breaker.empty": "No conversation has Peak pause on yet. Turn it on with the Peak pause switch beside the composer.",
|
|
976
|
+
"breaker.rowDisableAria": "Turn off Peak pause for {title}",
|
|
977
|
+
"breaker.holding": "Paused \xB7 {clock}",
|
|
809
978
|
"panel.caption": "Account-wide \xB7 across all sessions",
|
|
810
979
|
"panel.openAria": "Open Usage",
|
|
811
980
|
"widget.updatedNow": "Updated just now",
|
|
@@ -837,17 +1006,24 @@ var en = {
|
|
|
837
1006
|
|
|
838
1007
|
// src/client/i18n.tsx
|
|
839
1008
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1009
|
+
var INTL_LOCALES = {
|
|
1010
|
+
zh: "zh-CN",
|
|
1011
|
+
en: "en-US"
|
|
1012
|
+
};
|
|
1013
|
+
function intlLocale(locale) {
|
|
1014
|
+
return INTL_LOCALES[locale];
|
|
1015
|
+
}
|
|
840
1016
|
var fallbackT = (key, params) => {
|
|
841
1017
|
const template = zh[key];
|
|
842
1018
|
if (params === void 0) return template;
|
|
843
1019
|
return template.replace(/\{(\w+)\}/g, (match, name2) => name2 in params ? String(params[name2]) : match);
|
|
844
1020
|
};
|
|
845
|
-
var TranslationContext = (0,
|
|
1021
|
+
var TranslationContext = (0, import_react3.createContext)(fallbackT);
|
|
846
1022
|
function LocaleProvider(props) {
|
|
847
1023
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TranslationContext.Provider, { value: props.t, children: props.children });
|
|
848
1024
|
}
|
|
849
1025
|
function useI18n() {
|
|
850
|
-
const t = (0,
|
|
1026
|
+
const t = (0, import_react3.useContext)(TranslationContext);
|
|
851
1027
|
return { t, locale: t("meta.locale") === "en" ? "en" : "zh" };
|
|
852
1028
|
}
|
|
853
1029
|
function localizeApiError(message, t, fallback) {
|
|
@@ -878,11 +1054,11 @@ function localizeApiError(message, t, fallback) {
|
|
|
878
1054
|
// src/client/charts.tsx
|
|
879
1055
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
880
1056
|
function useChartTip() {
|
|
881
|
-
const wrapRef = (0,
|
|
882
|
-
const [tip, setTip] = (0,
|
|
883
|
-
const [pinned, setPinned] = (0,
|
|
884
|
-
const pinnedRef = (0,
|
|
885
|
-
const touchStart = (0,
|
|
1057
|
+
const wrapRef = (0, import_react4.useRef)(null);
|
|
1058
|
+
const [tip, setTip] = (0, import_react4.useState)(null);
|
|
1059
|
+
const [pinned, setPinned] = (0, import_react4.useState)(null);
|
|
1060
|
+
const pinnedRef = (0, import_react4.useRef)(null);
|
|
1061
|
+
const touchStart = (0, import_react4.useRef)(null);
|
|
886
1062
|
const setPin = (next) => {
|
|
887
1063
|
pinnedRef.current = next;
|
|
888
1064
|
setPinned(next);
|
|
@@ -921,7 +1097,7 @@ function useChartTip() {
|
|
|
921
1097
|
if (next === null) setTip(null);
|
|
922
1098
|
else showAt(e.currentTarget, text, e.clientX, e.clientY);
|
|
923
1099
|
};
|
|
924
|
-
(0,
|
|
1100
|
+
(0, import_react4.useEffect)(() => {
|
|
925
1101
|
if (pinned === null) return void 0;
|
|
926
1102
|
const onDocPointerDown = (e) => {
|
|
927
1103
|
const wrap = wrapRef.current;
|
|
@@ -935,9 +1111,9 @@ function useChartTip() {
|
|
|
935
1111
|
return { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd };
|
|
936
1112
|
}
|
|
937
1113
|
function useRovingChartPoints(count, initial, horizontalStep = 1) {
|
|
938
|
-
const refs = (0,
|
|
939
|
-
const [active, setActive] = (0,
|
|
940
|
-
(0,
|
|
1114
|
+
const refs = (0, import_react4.useRef)([]);
|
|
1115
|
+
const [active, setActive] = (0, import_react4.useState)(initial);
|
|
1116
|
+
(0, import_react4.useEffect)(() => setActive(Math.min(Math.max(0, initial), Math.max(0, count - 1))), [count, initial]);
|
|
941
1117
|
const onKeyDown = (event, index) => {
|
|
942
1118
|
if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) return;
|
|
943
1119
|
event.preventDefault();
|
|
@@ -962,10 +1138,10 @@ var MODEL_COLORS = ["#4176e6", "#2da44e", "#e16f24", "#8250df", "#bf8700", "#cf2
|
|
|
962
1138
|
function Bars(props) {
|
|
963
1139
|
const { data, height = 120, labelEvery = 1, minWidth } = props;
|
|
964
1140
|
const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip();
|
|
965
|
-
const scrollRef = (0,
|
|
1141
|
+
const scrollRef = (0, import_react4.useRef)(null);
|
|
966
1142
|
const initialFocus = lastPopulatedIndex(data.map((datum) => datum.value));
|
|
967
1143
|
const roving = useRovingChartPoints(data.length, initialFocus);
|
|
968
|
-
(0,
|
|
1144
|
+
(0, import_react4.useEffect)(() => {
|
|
969
1145
|
const scroll = scrollRef.current;
|
|
970
1146
|
if (scroll !== null && minWidth !== void 0) scroll.scrollLeft = scroll.scrollWidth;
|
|
971
1147
|
}, [data.length, minWidth]);
|
|
@@ -1007,11 +1183,11 @@ function Bars(props) {
|
|
|
1007
1183
|
function GroupedBars(props) {
|
|
1008
1184
|
const { series, height = 120, labelEvery = 1, minWidth } = props;
|
|
1009
1185
|
const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip();
|
|
1010
|
-
const scrollRef = (0,
|
|
1186
|
+
const scrollRef = (0, import_react4.useRef)(null);
|
|
1011
1187
|
const count = series.length > 0 ? series[0].bars.length : 0;
|
|
1012
1188
|
const pointValues = Array.from({ length: count }, (_, i) => series.map((s) => s.bars[i]?.value ?? 0)).flat();
|
|
1013
1189
|
const roving = useRovingChartPoints(pointValues.length, lastPopulatedIndex(pointValues));
|
|
1014
|
-
(0,
|
|
1190
|
+
(0, import_react4.useEffect)(() => {
|
|
1015
1191
|
const scroll = scrollRef.current;
|
|
1016
1192
|
if (scroll !== null && minWidth !== void 0) scroll.scrollLeft = scroll.scrollWidth;
|
|
1017
1193
|
}, [count, minWidth]);
|
|
@@ -1102,7 +1278,7 @@ function Heatmap(props) {
|
|
|
1102
1278
|
if (datum === null) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-heat-cell dq-heat-cell--pad" }, d);
|
|
1103
1279
|
const text = t("chart.tooltip", {
|
|
1104
1280
|
head: datum.date,
|
|
1105
|
-
tokens: datum.total.toLocaleString(locale
|
|
1281
|
+
tokens: datum.total.toLocaleString(intlLocale(locale)),
|
|
1106
1282
|
cost: datum.cost.toFixed(2),
|
|
1107
1283
|
calls: t("common.calls", { count: datum.calls })
|
|
1108
1284
|
});
|
|
@@ -1213,9 +1389,9 @@ function getActiveConversationViewId(frame, viewIds) {
|
|
|
1213
1389
|
}
|
|
1214
1390
|
|
|
1215
1391
|
// src/client/export.ts
|
|
1216
|
-
var
|
|
1392
|
+
var BEIJING_OFFSET_MS3 = 8 * 60 * 60 * 1e3;
|
|
1217
1393
|
function exportDateStamp(nowMs = Date.now()) {
|
|
1218
|
-
const date = new Date(nowMs +
|
|
1394
|
+
const date = new Date(nowMs + BEIJING_OFFSET_MS3);
|
|
1219
1395
|
return [
|
|
1220
1396
|
date.getUTCFullYear(),
|
|
1221
1397
|
String(date.getUTCMonth() + 1).padStart(2, "0"),
|
|
@@ -1331,19 +1507,19 @@ var isBoolean = (value) => typeof value === "boolean";
|
|
|
1331
1507
|
|
|
1332
1508
|
// src/client/store.ts
|
|
1333
1509
|
function createStore(key, isValid, fallback) {
|
|
1334
|
-
let
|
|
1335
|
-
const
|
|
1510
|
+
let current2 = loadPref(key, isValid, fallback);
|
|
1511
|
+
const listeners2 = /* @__PURE__ */ new Set();
|
|
1336
1512
|
return {
|
|
1337
|
-
get: () =>
|
|
1513
|
+
get: () => current2,
|
|
1338
1514
|
set: (value) => {
|
|
1339
|
-
|
|
1515
|
+
current2 = value;
|
|
1340
1516
|
savePref(key, value);
|
|
1341
|
-
for (const listener of [...
|
|
1517
|
+
for (const listener of [...listeners2]) listener();
|
|
1342
1518
|
},
|
|
1343
1519
|
subscribe: (fn) => {
|
|
1344
|
-
|
|
1520
|
+
listeners2.add(fn);
|
|
1345
1521
|
return () => {
|
|
1346
|
-
|
|
1522
|
+
listeners2.delete(fn);
|
|
1347
1523
|
};
|
|
1348
1524
|
}
|
|
1349
1525
|
};
|
|
@@ -1395,9 +1571,27 @@ function viewOrderForIndex(entries, index, selfId = "balance", fallback = 20) {
|
|
|
1395
1571
|
return previous >= next ? previous + 0.5 : (previous + next) / 2;
|
|
1396
1572
|
}
|
|
1397
1573
|
|
|
1574
|
+
// src/client/top-up.ts
|
|
1575
|
+
var TOP_UP_URL = "https://platform.deepseek.com/top_up";
|
|
1576
|
+
var TOP_UP_FLOOR = 10;
|
|
1577
|
+
function needsTopUp(balance, warningLine) {
|
|
1578
|
+
if (!Number.isFinite(balance)) return false;
|
|
1579
|
+
return balance < TOP_UP_FLOOR || warningLine > 0 && balance < warningLine;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1398
1582
|
// src/client/dashboard.tsx
|
|
1399
1583
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1400
1584
|
var COMPOSER_FALLBACK_PX = 126;
|
|
1585
|
+
function usageTitleMap(usage) {
|
|
1586
|
+
const titles = /* @__PURE__ */ new Map();
|
|
1587
|
+
for (const session of usage?.sessions ?? []) titles.set(session.id, session.title);
|
|
1588
|
+
for (const window2 of usage?.windows ?? []) {
|
|
1589
|
+
for (const session of window2.sessions) {
|
|
1590
|
+
if (!titles.has(session.id)) titles.set(session.id, session.title);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
return titles;
|
|
1594
|
+
}
|
|
1401
1595
|
function Skel(props) {
|
|
1402
1596
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-skel", style: { width: `${props.w}px`, height: `${props.h}px` } });
|
|
1403
1597
|
}
|
|
@@ -1447,12 +1641,12 @@ function HeatmapSkeleton() {
|
|
|
1447
1641
|
}
|
|
1448
1642
|
function Delta(props) {
|
|
1449
1643
|
const { t } = useI18n();
|
|
1450
|
-
const { current, previous, label } = props;
|
|
1644
|
+
const { current: current2, previous, label } = props;
|
|
1451
1645
|
if (previous <= 0) {
|
|
1452
|
-
if (
|
|
1646
|
+
if (current2 <= 0) return null;
|
|
1453
1647
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta dq-delta--new", title: t("delta.noBaselineTitle", { label }), children: t("delta.noBaseline") });
|
|
1454
1648
|
}
|
|
1455
|
-
const percent = Math.round((
|
|
1649
|
+
const percent = Math.round((current2 - previous) / previous * 100);
|
|
1456
1650
|
const title = `${label} \xA5${fmt(previous)}`;
|
|
1457
1651
|
if (percent === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta dq-delta--flat", title, children: t("delta.flat") });
|
|
1458
1652
|
const up = percent > 0;
|
|
@@ -1540,7 +1734,7 @@ function PricingNote(props) {
|
|
|
1540
1734
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.inputCacheMiss") }),
|
|
1541
1735
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.output") })
|
|
1542
1736
|
] }) }),
|
|
1543
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: pricing.tiers.map((tier) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1737
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: pricing.tiers.map((tier) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react5.Fragment, { children: [
|
|
1544
1738
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { children: [
|
|
1545
1739
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { rowSpan: 2, children: tier.model }),
|
|
1546
1740
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: t("pricing.peak") }),
|
|
@@ -1569,7 +1763,7 @@ function CoverageDiagnostics(props) {
|
|
|
1569
1763
|
const { t, locale } = useI18n();
|
|
1570
1764
|
const { coverage } = props;
|
|
1571
1765
|
const hasGaps = coverage.failedSessions > 0 || coverage.skippedRecords > 0 || coverage.scannedSessions < coverage.listedSessions;
|
|
1572
|
-
const coverageTime = new Intl.DateTimeFormat(locale
|
|
1766
|
+
const coverageTime = new Intl.DateTimeFormat(intlLocale(locale), {
|
|
1573
1767
|
timeZone: "Asia/Shanghai",
|
|
1574
1768
|
year: "numeric",
|
|
1575
1769
|
month: "numeric",
|
|
@@ -1627,13 +1821,13 @@ function Link(props) {
|
|
|
1627
1821
|
}
|
|
1628
1822
|
function ExportMenu(props) {
|
|
1629
1823
|
const { t } = useI18n();
|
|
1630
|
-
const [open, setOpen] = (0,
|
|
1631
|
-
const [done, setDone] = (0,
|
|
1632
|
-
const rootRef = (0,
|
|
1633
|
-
const triggerRef = (0,
|
|
1634
|
-
const menuRef = (0,
|
|
1635
|
-
const doneTimerRef = (0,
|
|
1636
|
-
(0,
|
|
1824
|
+
const [open, setOpen] = (0, import_react5.useState)(false);
|
|
1825
|
+
const [done, setDone] = (0, import_react5.useState)(false);
|
|
1826
|
+
const rootRef = (0, import_react5.useRef)(null);
|
|
1827
|
+
const triggerRef = (0, import_react5.useRef)(null);
|
|
1828
|
+
const menuRef = (0, import_react5.useRef)(null);
|
|
1829
|
+
const doneTimerRef = (0, import_react5.useRef)(null);
|
|
1830
|
+
(0, import_react5.useEffect)(() => {
|
|
1637
1831
|
if (!open) return;
|
|
1638
1832
|
const onDown = (event) => {
|
|
1639
1833
|
if (rootRef.current !== null && !rootRef.current.contains(event.target)) setOpen(false);
|
|
@@ -1651,7 +1845,7 @@ function ExportMenu(props) {
|
|
|
1651
1845
|
document.removeEventListener("keydown", onKey);
|
|
1652
1846
|
};
|
|
1653
1847
|
}, [open]);
|
|
1654
|
-
(0,
|
|
1848
|
+
(0, import_react5.useEffect)(() => () => {
|
|
1655
1849
|
if (doneTimerRef.current !== null) window.clearTimeout(doneTimerRef.current);
|
|
1656
1850
|
}, []);
|
|
1657
1851
|
const finish = () => {
|
|
@@ -1711,7 +1905,7 @@ function SessionUsageCard(props) {
|
|
|
1711
1905
|
const { t, locale } = useI18n();
|
|
1712
1906
|
const { data } = props;
|
|
1713
1907
|
if (data.calls === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("sessionUsage.empty") });
|
|
1714
|
-
const rangeTime = new Intl.DateTimeFormat(locale
|
|
1908
|
+
const rangeTime = new Intl.DateTimeFormat(intlLocale(locale), {
|
|
1715
1909
|
timeZone: "Asia/Shanghai",
|
|
1716
1910
|
year: "numeric",
|
|
1717
1911
|
month: "numeric",
|
|
@@ -1925,8 +2119,8 @@ function SessionDetail(props) {
|
|
|
1925
2119
|
function SessionRanking(props) {
|
|
1926
2120
|
const { t, locale } = useI18n();
|
|
1927
2121
|
const { sessions, count, totalCost } = props;
|
|
1928
|
-
const [details, setDetails] = (0,
|
|
1929
|
-
const startedRef = (0,
|
|
2122
|
+
const [details, setDetails] = (0, import_react5.useState)({});
|
|
2123
|
+
const startedRef = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
|
|
1930
2124
|
const loadDetail = (id) => {
|
|
1931
2125
|
if (startedRef.current.has(id)) return;
|
|
1932
2126
|
startedRef.current.add(id);
|
|
@@ -2027,11 +2221,11 @@ function ModelRanking(props) {
|
|
|
2027
2221
|
function ModelPicker(props) {
|
|
2028
2222
|
const { t, locale } = useI18n();
|
|
2029
2223
|
const { models } = props;
|
|
2030
|
-
const [open, setOpen] = (0,
|
|
2031
|
-
const rootRef = (0,
|
|
2032
|
-
const triggerRef = (0,
|
|
2033
|
-
const menuRef = (0,
|
|
2034
|
-
(0,
|
|
2224
|
+
const [open, setOpen] = (0, import_react5.useState)(false);
|
|
2225
|
+
const rootRef = (0, import_react5.useRef)(null);
|
|
2226
|
+
const triggerRef = (0, import_react5.useRef)(null);
|
|
2227
|
+
const menuRef = (0, import_react5.useRef)(null);
|
|
2228
|
+
(0, import_react5.useEffect)(() => {
|
|
2035
2229
|
if (!open) return;
|
|
2036
2230
|
const onDown = (e) => {
|
|
2037
2231
|
if (rootRef.current !== null && !rootRef.current.contains(e.target)) setOpen(false);
|
|
@@ -2133,45 +2327,55 @@ function BalanceDashboard(props) {
|
|
|
2133
2327
|
const cachedBalance = getCachedBalance();
|
|
2134
2328
|
const cachedUsage = getCachedUsage();
|
|
2135
2329
|
const cachedUsageAt = getCachedUsageAt();
|
|
2136
|
-
const [balance, setBalance] = (0,
|
|
2137
|
-
const [usage, setUsage] = (0,
|
|
2138
|
-
const [error, setError] = (0,
|
|
2139
|
-
const [notice, setNotice] = (0,
|
|
2140
|
-
const [loadingBalance, setLoadingBalance] = (0,
|
|
2141
|
-
const [loadingUsage, setLoadingUsage] = (0,
|
|
2142
|
-
const [refreshing, setRefreshing] = (0,
|
|
2143
|
-
const [usageUpdatedAt, setUsageUpdatedAt] = (0,
|
|
2144
|
-
const [syncState, setSyncState] = (0,
|
|
2145
|
-
const [freshnessNow, setFreshnessNow] = (0,
|
|
2146
|
-
const hadDataRef = (0,
|
|
2147
|
-
const [widgetOn, setWidgetOn] = (0,
|
|
2148
|
-
const [widgetTabIds, setWidgetTabIds] = (0,
|
|
2149
|
-
const [viewTabs, setViewTabs] = (0,
|
|
2150
|
-
(0,
|
|
2151
|
-
const [lowBalance, setLowBalance] = (0,
|
|
2152
|
-
const [monthlyBudget, setMonthlyBudget] = (0,
|
|
2153
|
-
const
|
|
2154
|
-
const [
|
|
2155
|
-
const [
|
|
2156
|
-
const [
|
|
2157
|
-
const [
|
|
2158
|
-
|
|
2159
|
-
const [
|
|
2160
|
-
const
|
|
2161
|
-
const
|
|
2162
|
-
const
|
|
2163
|
-
const [
|
|
2164
|
-
const [
|
|
2165
|
-
const [
|
|
2166
|
-
const
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
const [
|
|
2170
|
-
const [
|
|
2171
|
-
const [
|
|
2172
|
-
const [
|
|
2173
|
-
const
|
|
2174
|
-
(0,
|
|
2330
|
+
const [balance, setBalance] = (0, import_react5.useState)(cachedBalance?.data ?? null);
|
|
2331
|
+
const [usage, setUsage] = (0, import_react5.useState)(cachedUsage?.data ?? null);
|
|
2332
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
2333
|
+
const [notice, setNotice] = (0, import_react5.useState)(null);
|
|
2334
|
+
const [loadingBalance, setLoadingBalance] = (0, import_react5.useState)(cachedBalance === null);
|
|
2335
|
+
const [loadingUsage, setLoadingUsage] = (0, import_react5.useState)(cachedUsage === null);
|
|
2336
|
+
const [refreshing, setRefreshing] = (0, import_react5.useState)(false);
|
|
2337
|
+
const [usageUpdatedAt, setUsageUpdatedAt] = (0, import_react5.useState)(cachedUsageAt);
|
|
2338
|
+
const [syncState, setSyncState] = (0, import_react5.useState)(cachedUsageAt === null ? "syncing" : "cached");
|
|
2339
|
+
const [freshnessNow, setFreshnessNow] = (0, import_react5.useState)(Date.now());
|
|
2340
|
+
const hadDataRef = (0, import_react5.useRef)(cachedBalance !== null || cachedUsage !== null);
|
|
2341
|
+
const [widgetOn, setWidgetOn] = (0, import_react5.useState)(widgetVisibleStore.get());
|
|
2342
|
+
const [widgetTabIds, setWidgetTabIds] = (0, import_react5.useState)(widgetTabIdsStore.get());
|
|
2343
|
+
const [viewTabs, setViewTabs] = (0, import_react5.useState)(() => props.views.list());
|
|
2344
|
+
(0, import_react5.useEffect)(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views]);
|
|
2345
|
+
const [lowBalance, setLowBalance] = (0, import_react5.useState)(lowBalanceStore.get());
|
|
2346
|
+
const [monthlyBudget, setMonthlyBudget] = (0, import_react5.useState)(monthlyBudgetStore.get());
|
|
2347
|
+
const breakerData = useBreakerData();
|
|
2348
|
+
const [breakerLoadError, setBreakerLoadError] = (0, import_react5.useState)(null);
|
|
2349
|
+
const [breakerActionError, setBreakerActionError] = (0, import_react5.useState)(null);
|
|
2350
|
+
const [breakerReleasing, setBreakerReleasing] = (0, import_react5.useState)(false);
|
|
2351
|
+
const [breakerSavingId, setBreakerSavingId] = (0, import_react5.useState)(null);
|
|
2352
|
+
const [breakerSavingMaster, setBreakerSavingMaster] = (0, import_react5.useState)(false);
|
|
2353
|
+
const [breakerTitles, setBreakerTitles] = (0, import_react5.useState)({});
|
|
2354
|
+
const breakerTitleRequests = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
|
|
2355
|
+
const breakerMutationSeq = (0, import_react5.useRef)(0);
|
|
2356
|
+
const breakerMounted = (0, import_react5.useRef)(true);
|
|
2357
|
+
const [barMode, setBarMode] = (0, import_react5.useState)("daily");
|
|
2358
|
+
const [selectedModels, setSelectedModels] = (0, import_react5.useState)([]);
|
|
2359
|
+
const [windowDays, setWindowDays] = (0, import_react5.useState)(usageWindowStore.get());
|
|
2360
|
+
const [chartMetric, setChartMetric] = (0, import_react5.useState)(chartMetricStore.get());
|
|
2361
|
+
const [sidebarEntry, setSidebarEntry] = (0, import_react5.useState)(sidebarEntryStore.get());
|
|
2362
|
+
(0, import_react5.useEffect)(() => sidebarEntryStore.subscribe(() => setSidebarEntry(sidebarEntryStore.get())), []);
|
|
2363
|
+
const [tabDragging, setTabDragging] = (0, import_react5.useState)(false);
|
|
2364
|
+
const [tabDropTarget, setTabDropTarget] = (0, import_react5.useState)(null);
|
|
2365
|
+
const [retainedVision, setRetainedVision] = (0, import_react5.useState)(null);
|
|
2366
|
+
const [runwayHovering, setRunwayHovering] = (0, import_react5.useState)(false);
|
|
2367
|
+
const [runwayManuallyOpened, setRunwayManuallyOpened] = (0, import_react5.useState)(false);
|
|
2368
|
+
const [remainingHovering, setRemainingHovering] = (0, import_react5.useState)(false);
|
|
2369
|
+
const [remainingManuallyOpened, setRemainingManuallyOpened] = (0, import_react5.useState)(false);
|
|
2370
|
+
const budgetInputRef = (0, import_react5.useRef)(null);
|
|
2371
|
+
const rootRef = (0, import_react5.useRef)(null);
|
|
2372
|
+
const [composerHeight, setComposerHeight] = (0, import_react5.useState)(null);
|
|
2373
|
+
const [composerWidth, setComposerWidth] = (0, import_react5.useState)(null);
|
|
2374
|
+
const [sessionUsage, setSessionUsage] = (0, import_react5.useState)(null);
|
|
2375
|
+
const [loadingSessionUsage, setLoadingSessionUsage] = (0, import_react5.useState)(props.sessionId !== void 0 && props.sessionId !== "");
|
|
2376
|
+
const [sessionUsageError, setSessionUsageError] = (0, import_react5.useState)(null);
|
|
2377
|
+
const sessionUsageIdRef = (0, import_react5.useRef)(null);
|
|
2378
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
2175
2379
|
const node = rootRef.current;
|
|
2176
2380
|
if (node === null) return;
|
|
2177
2381
|
const measure = () => {
|
|
@@ -2202,13 +2406,13 @@ function BalanceDashboard(props) {
|
|
|
2202
2406
|
window.removeEventListener("resize", measure);
|
|
2203
2407
|
};
|
|
2204
2408
|
}, []);
|
|
2205
|
-
(0,
|
|
2409
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
2206
2410
|
const node = rootRef.current;
|
|
2207
2411
|
if (node === null) return;
|
|
2208
2412
|
const scrollport = node.closest("[data-conversation-scroll]");
|
|
2209
2413
|
if (scrollport instanceof HTMLElement) scrollport.scrollTop = 0;
|
|
2210
2414
|
}, [props.sessionId]);
|
|
2211
|
-
(0,
|
|
2415
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
2212
2416
|
quotaViewActiveStore.set(true);
|
|
2213
2417
|
return () => quotaViewActiveStore.set(false);
|
|
2214
2418
|
}, []);
|
|
@@ -2282,10 +2486,10 @@ function BalanceDashboard(props) {
|
|
|
2282
2486
|
if (sessionUsageIdRef.current === id) setLoadingSessionUsage(false);
|
|
2283
2487
|
}
|
|
2284
2488
|
};
|
|
2285
|
-
(0,
|
|
2489
|
+
(0, import_react5.useEffect)(() => {
|
|
2286
2490
|
void load(false);
|
|
2287
2491
|
}, []);
|
|
2288
|
-
(0,
|
|
2492
|
+
(0, import_react5.useEffect)(() => {
|
|
2289
2493
|
const id = props.sessionId;
|
|
2290
2494
|
sessionUsageIdRef.current = id ?? null;
|
|
2291
2495
|
if (id === void 0 || id === "") {
|
|
@@ -2299,7 +2503,57 @@ function BalanceDashboard(props) {
|
|
|
2299
2503
|
setLoadingSessionUsage(true);
|
|
2300
2504
|
void loadSessionUsage(id, false);
|
|
2301
2505
|
}, [props.sessionId]);
|
|
2302
|
-
(0,
|
|
2506
|
+
(0, import_react5.useEffect)(() => {
|
|
2507
|
+
let cancelled = false;
|
|
2508
|
+
let timer;
|
|
2509
|
+
let fast = false;
|
|
2510
|
+
const pull = async () => {
|
|
2511
|
+
const seqAtStart = breakerMutationSeq.current;
|
|
2512
|
+
const res = await fetchBreaker();
|
|
2513
|
+
if (cancelled) return;
|
|
2514
|
+
if (!breakerIsUsable(res)) {
|
|
2515
|
+
setBreakerLoadError(res.error ?? "");
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
fast = res.data.enabled && res.data.peak && res.data.sessions.length > 0;
|
|
2519
|
+
setBreakerLoadError(null);
|
|
2520
|
+
if (seqAtStart !== breakerMutationSeq.current) return;
|
|
2521
|
+
publishBreakerData(res.data);
|
|
2522
|
+
};
|
|
2523
|
+
const loop = async () => {
|
|
2524
|
+
await pull();
|
|
2525
|
+
if (cancelled) return;
|
|
2526
|
+
timer = window.setTimeout(() => {
|
|
2527
|
+
void loop();
|
|
2528
|
+
}, fast ? 5e3 : 3e4);
|
|
2529
|
+
};
|
|
2530
|
+
void loop();
|
|
2531
|
+
return () => {
|
|
2532
|
+
cancelled = true;
|
|
2533
|
+
if (timer !== void 0) window.clearTimeout(timer);
|
|
2534
|
+
};
|
|
2535
|
+
}, []);
|
|
2536
|
+
(0, import_react5.useEffect)(() => {
|
|
2537
|
+
breakerMounted.current = true;
|
|
2538
|
+
return () => {
|
|
2539
|
+
breakerMounted.current = false;
|
|
2540
|
+
};
|
|
2541
|
+
}, []);
|
|
2542
|
+
(0, import_react5.useEffect)(() => {
|
|
2543
|
+
if (breakerData === null) return;
|
|
2544
|
+
const known = usageTitleMap(usage);
|
|
2545
|
+
for (const sessionId of breakerData.sessions) {
|
|
2546
|
+
if (known.has(sessionId) || breakerTitles[sessionId] !== void 0) continue;
|
|
2547
|
+
if (breakerTitleRequests.current.has(sessionId)) continue;
|
|
2548
|
+
breakerTitleRequests.current.add(sessionId);
|
|
2549
|
+
void fetchSessionUsage(sessionId).then((res) => {
|
|
2550
|
+
if (!breakerMounted.current) return;
|
|
2551
|
+
const title = res.ok === true ? res.data?.title?.trim() ?? "" : "";
|
|
2552
|
+
setBreakerTitles((current2) => ({ ...current2, [sessionId]: title }));
|
|
2553
|
+
});
|
|
2554
|
+
}
|
|
2555
|
+
}, [breakerData, usage, breakerTitles]);
|
|
2556
|
+
(0, import_react5.useEffect)(() => {
|
|
2303
2557
|
const timer = window.setInterval(() => setFreshnessNow(Date.now()), 3e4);
|
|
2304
2558
|
return () => window.clearInterval(timer);
|
|
2305
2559
|
}, []);
|
|
@@ -2323,6 +2577,66 @@ function BalanceDashboard(props) {
|
|
|
2323
2577
|
setMonthlyBudget(value);
|
|
2324
2578
|
monthlyBudgetStore.set(value);
|
|
2325
2579
|
};
|
|
2580
|
+
const disableBreakerSession = async (sessionId) => {
|
|
2581
|
+
if (breakerSavingId !== null || breakerReleasing || breakerSavingMaster) return;
|
|
2582
|
+
const previous = breakerData;
|
|
2583
|
+
publishBreakerData(withSessionToggled(previous, sessionId, false));
|
|
2584
|
+
setBreakerActionError(null);
|
|
2585
|
+
setBreakerSavingId(sessionId);
|
|
2586
|
+
const seq = ++breakerMutationSeq.current;
|
|
2587
|
+
try {
|
|
2588
|
+
const res = await updateBreaker({ sessionId, enabled: false });
|
|
2589
|
+
if (seq !== breakerMutationSeq.current) return;
|
|
2590
|
+
if (breakerIsUsable(res)) {
|
|
2591
|
+
publishBreakerData(res.data);
|
|
2592
|
+
setBreakerLoadError(null);
|
|
2593
|
+
} else {
|
|
2594
|
+
publishBreakerData(previous);
|
|
2595
|
+
setBreakerActionError(res.error ?? "");
|
|
2596
|
+
}
|
|
2597
|
+
} finally {
|
|
2598
|
+
setBreakerSavingId(null);
|
|
2599
|
+
}
|
|
2600
|
+
};
|
|
2601
|
+
const toggleBreakerMaster = async (enabled) => {
|
|
2602
|
+
if (breakerSavingMaster || breakerSavingId !== null || breakerReleasing) return;
|
|
2603
|
+
const previous = breakerData;
|
|
2604
|
+
publishBreakerData(withMasterToggled(previous, enabled));
|
|
2605
|
+
setBreakerActionError(null);
|
|
2606
|
+
setBreakerSavingMaster(true);
|
|
2607
|
+
const seq = ++breakerMutationSeq.current;
|
|
2608
|
+
try {
|
|
2609
|
+
const res = await updateBreaker({ enabled });
|
|
2610
|
+
if (seq !== breakerMutationSeq.current) return;
|
|
2611
|
+
if (breakerIsUsable(res)) {
|
|
2612
|
+
publishBreakerData(res.data);
|
|
2613
|
+
setBreakerLoadError(null);
|
|
2614
|
+
} else {
|
|
2615
|
+
publishBreakerData(previous);
|
|
2616
|
+
setBreakerActionError(res.error ?? "");
|
|
2617
|
+
}
|
|
2618
|
+
} finally {
|
|
2619
|
+
setBreakerSavingMaster(false);
|
|
2620
|
+
}
|
|
2621
|
+
};
|
|
2622
|
+
const releaseBreakerNow = async () => {
|
|
2623
|
+
if (breakerReleasing || breakerSavingId !== null || breakerSavingMaster) return;
|
|
2624
|
+
setBreakerActionError(null);
|
|
2625
|
+
setBreakerReleasing(true);
|
|
2626
|
+
const seq = ++breakerMutationSeq.current;
|
|
2627
|
+
try {
|
|
2628
|
+
const res = await releaseBreaker();
|
|
2629
|
+
if (seq !== breakerMutationSeq.current) return;
|
|
2630
|
+
if (breakerIsUsable(res)) {
|
|
2631
|
+
publishBreakerData(res.data);
|
|
2632
|
+
setBreakerLoadError(null);
|
|
2633
|
+
} else {
|
|
2634
|
+
setBreakerActionError(res.error ?? "");
|
|
2635
|
+
}
|
|
2636
|
+
} finally {
|
|
2637
|
+
setBreakerReleasing(false);
|
|
2638
|
+
}
|
|
2639
|
+
};
|
|
2326
2640
|
const changeWindowDays = (value) => {
|
|
2327
2641
|
setWindowDays(value);
|
|
2328
2642
|
usageWindowStore.set(value);
|
|
@@ -2401,6 +2715,7 @@ function BalanceDashboard(props) {
|
|
|
2401
2715
|
const avgDailyCost = recentDays.length > 0 ? recentDays.reduce((sum, d) => sum + d.cost, 0) / recentDays.length : 0;
|
|
2402
2716
|
const balanceValue = primary !== null ? Number(primary.total) : Number.NaN;
|
|
2403
2717
|
const balanceLow = lowBalance > 0 && Number.isFinite(balanceValue) && balanceValue < lowBalance;
|
|
2718
|
+
const topUpPrompt = needsTopUp(balanceValue, lowBalance);
|
|
2404
2719
|
const daysLeft = avgDailyCost > 0 && Number.isFinite(balanceValue) ? balanceValue / avgDailyCost : null;
|
|
2405
2720
|
const daysLeftText = daysLeft === null ? "\u2014" : daysLeft >= 365 ? t("balance.overYear") : t("balance.days", { count: daysLeft < 10 ? daysLeft.toFixed(1) : Math.round(daysLeft) });
|
|
2406
2721
|
const runwayTitle = daysLeft === null ? t("balance.noRunway", { days: recentDays.length || 7 }) : t("balance.runwayTitle", { days: recentDays.length, cost: fmt(avgDailyCost) });
|
|
@@ -2417,7 +2732,7 @@ function BalanceDashboard(props) {
|
|
|
2417
2732
|
costTotal: window2.totals.cost
|
|
2418
2733
|
});
|
|
2419
2734
|
const liveVision = visionOpen && activeWindow !== null && visionStats !== void 0 ? visionPayload(activeWindow, visionStats, activeWindowName) : null;
|
|
2420
|
-
(0,
|
|
2735
|
+
(0, import_react5.useEffect)(() => {
|
|
2421
2736
|
if (visionOpen && activeWindow !== null && visionStats !== void 0) {
|
|
2422
2737
|
setRetainedVision(visionPayload(activeWindow, visionStats, activeWindowName));
|
|
2423
2738
|
return;
|
|
@@ -2501,6 +2816,15 @@ function BalanceDashboard(props) {
|
|
|
2501
2816
|
{ error: localizeApiError(notice.error, t, "error.query") }
|
|
2502
2817
|
);
|
|
2503
2818
|
const errorText = error === null ? null : localizeApiError(error, t, "error.query");
|
|
2819
|
+
const breakerPhaseNow = breakerLoadError !== null ? "unavailable" : breakerPhase(breakerData);
|
|
2820
|
+
const breakerResumeClock = beijingClock(breakerData?.resumeAt ?? null);
|
|
2821
|
+
const breakerHeldCount = breakerData?.holding.length ?? 0;
|
|
2822
|
+
const breakerStatusText = breakerPhaseNow === "unavailable" ? t("breaker.statusUnavailable") : breakerPhaseNow === "masterOff" ? t("breaker.statusMasterOff") : breakerPhaseNow === "peakHolding" ? breakerResumeClock === null ? t("breaker.statusHoldingNoClock", { count: breakerHeldCount }) : t("breaker.statusHolding", { count: breakerHeldCount, clock: breakerResumeClock }) : breakerPhaseNow === "peak" ? t("breaker.statusPeak") : t("breaker.statusOffPeak");
|
|
2823
|
+
const breakerUsageTitles = usageTitleMap(usage);
|
|
2824
|
+
const breakerTitleFor = (sessionId) => {
|
|
2825
|
+
const title = breakerUsageTitles.get(sessionId) ?? breakerTitles[sessionId];
|
|
2826
|
+
return title !== void 0 && title !== "" ? title : shortSessionId(sessionId);
|
|
2827
|
+
};
|
|
2504
2828
|
const composerSafeAreaStyle = {
|
|
2505
2829
|
"--dq-composer-h": `${composerHeight ?? COMPOSER_FALLBACK_PX}px`,
|
|
2506
2830
|
// Only set once measured; .dq-balance's max-width falls back to 860px
|
|
@@ -2517,7 +2841,7 @@ function BalanceDashboard(props) {
|
|
|
2517
2841
|
className: `dq-sync dq-sync--${syncState}`,
|
|
2518
2842
|
role: "status",
|
|
2519
2843
|
"aria-live": "polite",
|
|
2520
|
-
title: usageUpdatedAt === null ? t("status.neverSynced") : t("status.usageTime", { time: new Date(usageUpdatedAt).toLocaleString(locale
|
|
2844
|
+
title: usageUpdatedAt === null ? t("status.neverSynced") : t("status.usageTime", { time: new Date(usageUpdatedAt).toLocaleString(intlLocale(locale), { timeZone: "Asia/Shanghai" }) }),
|
|
2521
2845
|
children: [
|
|
2522
2846
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-sync-dot", "aria-hidden": "true" }),
|
|
2523
2847
|
syncStatusText(syncState, usageUpdatedAt, freshnessNow, t)
|
|
@@ -2551,7 +2875,7 @@ function BalanceDashboard(props) {
|
|
|
2551
2875
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: t("alert.lowTitle", { balance: fmt(balanceValue), currency: primary?.currency, threshold: fmt(lowBalance) }) }),
|
|
2552
2876
|
daysLeft !== null && t("alert.runway", { days: daysLeftText }),
|
|
2553
2877
|
t("common.period"),
|
|
2554
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { className: "dq-alert-link", href:
|
|
2878
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { className: "dq-alert-link", href: TOP_UP_URL, target: "_blank", rel: "noreferrer", children: t("alert.topUp") })
|
|
2555
2879
|
] })
|
|
2556
2880
|
] }),
|
|
2557
2881
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--hero", children: [
|
|
@@ -2592,29 +2916,35 @@ function BalanceDashboard(props) {
|
|
|
2592
2916
|
] }),
|
|
2593
2917
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
2594
2918
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("balance.runway") }),
|
|
2595
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
e
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2919
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-runway-line", children: [
|
|
2920
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2921
|
+
"details",
|
|
2922
|
+
{
|
|
2923
|
+
className: `dq-stat-value dq-runway${daysLeft !== null && daysLeft < 3 ? " dq-stat-value--bad" : ""}`,
|
|
2924
|
+
open: runwayHovering || runwayManuallyOpened,
|
|
2925
|
+
onPointerEnter: () => setRunwayHovering(true),
|
|
2926
|
+
onPointerLeave: () => setRunwayHovering(false),
|
|
2927
|
+
children: [
|
|
2928
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2929
|
+
"summary",
|
|
2930
|
+
{
|
|
2931
|
+
title: t("balance.runwayBreakdownTitle"),
|
|
2932
|
+
onClick: (e) => {
|
|
2933
|
+
e.preventDefault();
|
|
2934
|
+
setRunwayManuallyOpened((v) => !v);
|
|
2935
|
+
},
|
|
2936
|
+
children: daysLeftText
|
|
2937
|
+
}
|
|
2938
|
+
),
|
|
2939
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-runway-detail", children: runwayTitle })
|
|
2940
|
+
]
|
|
2941
|
+
}
|
|
2942
|
+
),
|
|
2943
|
+
topUpPrompt && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("a", { className: "dq-topup", href: TOP_UP_URL, target: "_blank", rel: "noreferrer", title: t("balance.topUpTitle"), children: [
|
|
2944
|
+
t("alert.topUp"),
|
|
2945
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-topup-arrow", "aria-hidden": "true", children: "\u2197" })
|
|
2946
|
+
] })
|
|
2947
|
+
] })
|
|
2618
2948
|
] }),
|
|
2619
2949
|
balance?.isAvailable === false && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("common.status"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-value dq-stat-value--bad", children: t("common.unavailable") }) })
|
|
2620
2950
|
] }) : loadingBalance ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BalanceSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("balance.empty") })
|
|
@@ -2773,6 +3103,57 @@ function BalanceDashboard(props) {
|
|
|
2773
3103
|
] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("cache.empty") })
|
|
2774
3104
|
] })
|
|
2775
3105
|
] }),
|
|
3106
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
3107
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("breaker.title") }),
|
|
3108
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-hint dq-breaker-desc", children: t("breaker.desc") }),
|
|
3109
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-breaker-status", children: [
|
|
3110
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3111
|
+
"span",
|
|
3112
|
+
{
|
|
3113
|
+
role: "status",
|
|
3114
|
+
className: breakerPhaseNow === "peakHolding" ? "dq-warn" : void 0,
|
|
3115
|
+
title: breakerLoadError !== null && breakerLoadError !== "" ? breakerLoadError : void 0,
|
|
3116
|
+
children: breakerStatusText
|
|
3117
|
+
}
|
|
3118
|
+
),
|
|
3119
|
+
breakerPhaseNow === "peakHolding" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3120
|
+
"button",
|
|
3121
|
+
{
|
|
3122
|
+
type: "button",
|
|
3123
|
+
className: "dq-refresh-btn",
|
|
3124
|
+
disabled: breakerReleasing || breakerSavingId !== null,
|
|
3125
|
+
"aria-busy": breakerReleasing,
|
|
3126
|
+
onClick: () => {
|
|
3127
|
+
void releaseBreakerNow();
|
|
3128
|
+
},
|
|
3129
|
+
children: breakerReleasing ? t("breaker.releaseBusy") : t("breaker.release")
|
|
3130
|
+
}
|
|
3131
|
+
)
|
|
3132
|
+
] }),
|
|
3133
|
+
breakerData === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: breakerLoadError === null ? t("breaker.loading") : t("breaker.statusUnavailable") }) : breakerData.sessions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("breaker.empty") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: `dq-breaker-list${breakerPhaseNow === "masterOff" ? " dq-breaker-list--muted" : ""}`, children: breakerData.sessions.map((sessionId) => {
|
|
3134
|
+
const title = breakerTitleFor(sessionId);
|
|
3135
|
+
const holding = breakerData.holding.includes(sessionId);
|
|
3136
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "dq-breaker-row", children: [
|
|
3137
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dq-breaker-row-main", children: [
|
|
3138
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-breaker-row-title", title, children: title }),
|
|
3139
|
+
holding && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-breaker-row-state", children: t("breaker.holding", { clock: breakerResumeClock ?? "18:00" }) })
|
|
3140
|
+
] }),
|
|
3141
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-breaker-switch", title: t("breaker.rowDisableAria", { title }), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3142
|
+
"input",
|
|
3143
|
+
{
|
|
3144
|
+
type: "checkbox",
|
|
3145
|
+
checked: true,
|
|
3146
|
+
disabled: breakerSavingId !== null || breakerReleasing || breakerSavingMaster,
|
|
3147
|
+
"aria-label": t("breaker.rowDisableAria", { title }),
|
|
3148
|
+
onChange: () => {
|
|
3149
|
+
void disableBreakerSession(sessionId);
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
) })
|
|
3153
|
+
] }, sessionId);
|
|
3154
|
+
}) }),
|
|
3155
|
+
breakerActionError !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-breaker-error dq-error", role: "alert", children: t("breaker.actionError", { error: localizeApiError(breakerActionError, t, "error.query") }) })
|
|
3156
|
+
] }),
|
|
2776
3157
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2777
3158
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("settings.title") }),
|
|
2778
3159
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-links-title", children: t("links.title") }),
|
|
@@ -2803,6 +3184,22 @@ function BalanceDashboard(props) {
|
|
|
2803
3184
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: widgetTabs.length === 0 ? t("settings.widgetTabsEmpty") : t("settings.widgetTabsHint") })
|
|
2804
3185
|
] })
|
|
2805
3186
|
] }),
|
|
3187
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-toggle", children: [
|
|
3188
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3189
|
+
"input",
|
|
3190
|
+
{
|
|
3191
|
+
type: "checkbox",
|
|
3192
|
+
checked: breakerData === null ? true : breakerData.enabled,
|
|
3193
|
+
disabled: breakerData === null || breakerSavingMaster,
|
|
3194
|
+
"aria-describedby": "dq-peak-pause-hint",
|
|
3195
|
+
onChange: (e) => {
|
|
3196
|
+
void toggleBreakerMaster(e.target.checked);
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
),
|
|
3200
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("settings.peakPause") })
|
|
3201
|
+
] }),
|
|
3202
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { id: "dq-peak-pause-hint", className: "dq-toggle-hint", children: breakerData === null ? breakerLoadError === null ? t("settings.peakPauseLoading") : t("breaker.statusUnavailable") : t("settings.peakPauseHint") }),
|
|
2806
3203
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2807
3204
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-low-balance", children: t("settings.lowBalance") }),
|
|
2808
3205
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
@@ -2874,21 +3271,21 @@ function BalanceDashboard(props) {
|
|
|
2874
3271
|
onDragOver: onOrderDragOver,
|
|
2875
3272
|
onDrop: onOrderDrop,
|
|
2876
3273
|
children: viewTabs.map((tab, index) => {
|
|
2877
|
-
const
|
|
2878
|
-
const dropTarget = tabDragging && !
|
|
3274
|
+
const current2 = tab.id === "balance";
|
|
3275
|
+
const dropTarget = tabDragging && !current2 && tabDropTarget === index;
|
|
2879
3276
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2880
3277
|
"span",
|
|
2881
3278
|
{
|
|
2882
|
-
className: `dq-tab-chip${
|
|
2883
|
-
draggable:
|
|
2884
|
-
tabIndex:
|
|
2885
|
-
role:
|
|
2886
|
-
"aria-label":
|
|
2887
|
-
onDragStart:
|
|
2888
|
-
onDragEnd:
|
|
3279
|
+
className: `dq-tab-chip${current2 ? " dq-tab-chip--current" : ""}${current2 && tabDragging ? " dq-tab-chip--dragging" : ""}${dropTarget ? " dq-tab-chip--drop-target" : ""}`,
|
|
3280
|
+
draggable: current2,
|
|
3281
|
+
tabIndex: current2 ? 0 : void 0,
|
|
3282
|
+
role: current2 ? "button" : void 0,
|
|
3283
|
+
"aria-label": current2 ? t("settings.tabDragAria") : void 0,
|
|
3284
|
+
onDragStart: current2 ? onQuotaTabDragStart : void 0,
|
|
3285
|
+
onDragEnd: current2 ? endTabDrag : void 0,
|
|
2889
3286
|
onDragOver: (event) => onChipDragOver(event, index),
|
|
2890
3287
|
onDrop: (event) => onChipDrop(event, index),
|
|
2891
|
-
onKeyDown:
|
|
3288
|
+
onKeyDown: current2 ? onQuotaTabKeyDown : void 0,
|
|
2892
3289
|
children: tab.label
|
|
2893
3290
|
},
|
|
2894
3291
|
tab.id
|
|
@@ -2900,13 +3297,145 @@ function BalanceDashboard(props) {
|
|
|
2900
3297
|
] })
|
|
2901
3298
|
] })
|
|
2902
3299
|
] }),
|
|
2903
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.8.
|
|
3300
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.8.3" })
|
|
2904
3301
|
] });
|
|
2905
3302
|
}
|
|
2906
3303
|
|
|
2907
|
-
// src/client/
|
|
2908
|
-
var
|
|
3304
|
+
// src/client/breaker-pill.tsx
|
|
3305
|
+
var import_react6 = require("react");
|
|
2909
3306
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
3307
|
+
var POLL_MS = 1e4;
|
|
3308
|
+
function BreakerIcon({ state }) {
|
|
3309
|
+
if (state === "off") {
|
|
3310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
3311
|
+
"svg",
|
|
3312
|
+
{
|
|
3313
|
+
width: "14",
|
|
3314
|
+
height: "14",
|
|
3315
|
+
viewBox: "0 0 16 16",
|
|
3316
|
+
fill: "none",
|
|
3317
|
+
stroke: "currentColor",
|
|
3318
|
+
strokeWidth: "1.2",
|
|
3319
|
+
strokeLinecap: "round",
|
|
3320
|
+
strokeLinejoin: "round",
|
|
3321
|
+
"aria-hidden": "true",
|
|
3322
|
+
focusable: "false",
|
|
3323
|
+
children: [
|
|
3324
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "8", cy: "8", r: "5.9" }),
|
|
3325
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M6.5 5.9v4.2" }),
|
|
3326
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M9.5 5.9v4.2" })
|
|
3327
|
+
]
|
|
3328
|
+
}
|
|
3329
|
+
);
|
|
3330
|
+
}
|
|
3331
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3332
|
+
"path",
|
|
3333
|
+
{
|
|
3334
|
+
fillRule: "evenodd",
|
|
3335
|
+
clipRule: "evenodd",
|
|
3336
|
+
d: "M8 1.6a6.4 6.4 0 1 0 0 12.8A6.4 6.4 0 0 0 8 1.6Zm-1.85 3.5h1.3v5.8h-1.3V5.1Zm2.4 0h1.3v5.8h-1.3V5.1Z"
|
|
3337
|
+
}
|
|
3338
|
+
) });
|
|
3339
|
+
}
|
|
3340
|
+
function BreakerPill({ sessionId }) {
|
|
3341
|
+
const { t } = useI18n();
|
|
3342
|
+
const data = useBreakerData();
|
|
3343
|
+
const [loadError, setLoadError] = (0, import_react6.useState)(null);
|
|
3344
|
+
const [actionError, setActionError] = (0, import_react6.useState)(null);
|
|
3345
|
+
const [saving, setSaving] = (0, import_react6.useState)(false);
|
|
3346
|
+
const savingRef = (0, import_react6.useRef)(false);
|
|
3347
|
+
const mutationSeq = (0, import_react6.useRef)(0);
|
|
3348
|
+
const sessionIdRef = (0, import_react6.useRef)(sessionId);
|
|
3349
|
+
(0, import_react6.useEffect)(() => {
|
|
3350
|
+
sessionIdRef.current = sessionId;
|
|
3351
|
+
}, [sessionId]);
|
|
3352
|
+
(0, import_react6.useEffect)(() => {
|
|
3353
|
+
if (sessionId === void 0 || sessionId === "") return;
|
|
3354
|
+
let cancelled = false;
|
|
3355
|
+
setLoadError(null);
|
|
3356
|
+
setActionError(null);
|
|
3357
|
+
const pull = async () => {
|
|
3358
|
+
const seqAtStart = mutationSeq.current;
|
|
3359
|
+
const res = await fetchBreaker();
|
|
3360
|
+
if (cancelled) return;
|
|
3361
|
+
if (!breakerIsUsable(res)) {
|
|
3362
|
+
setLoadError(res.error ?? "");
|
|
3363
|
+
return;
|
|
3364
|
+
}
|
|
3365
|
+
setLoadError(null);
|
|
3366
|
+
if (seqAtStart !== mutationSeq.current || savingRef.current) return;
|
|
3367
|
+
publishBreakerData(res.data);
|
|
3368
|
+
};
|
|
3369
|
+
void pull();
|
|
3370
|
+
const timer = window.setInterval(() => {
|
|
3371
|
+
void pull();
|
|
3372
|
+
}, POLL_MS);
|
|
3373
|
+
return () => {
|
|
3374
|
+
cancelled = true;
|
|
3375
|
+
window.clearInterval(timer);
|
|
3376
|
+
};
|
|
3377
|
+
}, [sessionId]);
|
|
3378
|
+
if (sessionId === void 0 || sessionId === "") return null;
|
|
3379
|
+
const state = sessionBreakerState(data, sessionId);
|
|
3380
|
+
const on = state !== "off";
|
|
3381
|
+
const masterOff = data !== null && !data.enabled;
|
|
3382
|
+
const optedIn = isSessionOptedIn(data, sessionId);
|
|
3383
|
+
const resumeClock = beijingClock(data?.resumeAt ?? null) ?? "18:00";
|
|
3384
|
+
const errorSource = actionError ?? loadError;
|
|
3385
|
+
const errorText = errorSource === null ? null : t("breaker.actionError", { error: localizeApiError(errorSource, t, "error.query") });
|
|
3386
|
+
const label = state === "holding" ? t("breaker.pillHolding", { clock: resumeClock }) : state === "on" ? t("breaker.pillOn") : t("breaker.pillOff");
|
|
3387
|
+
const baseTitle = masterOff ? t("breaker.pillTitleMasterOff") : state === "holding" ? t("breaker.pillTitleHolding", { clock: resumeClock }) : state === "on" ? t("breaker.pillTitleOn", { clock: resumeClock }) : t("breaker.pillTitleOff");
|
|
3388
|
+
const title = errorText === null ? baseTitle : `${baseTitle} \xB7 ${errorText}`;
|
|
3389
|
+
const baseAria = masterOff ? t("breaker.pillAriaMasterOff") : state === "holding" ? t("breaker.pillAriaHolding", { clock: resumeClock }) : state === "on" ? t("breaker.pillAriaOn", { clock: resumeClock }) : t("breaker.pillAriaOff");
|
|
3390
|
+
const ariaLabel = errorText === null ? baseAria : `${baseAria} \xB7 ${errorText}`;
|
|
3391
|
+
const toggle = async () => {
|
|
3392
|
+
if (savingRef.current) return;
|
|
3393
|
+
const next = !optedIn;
|
|
3394
|
+
const previous = data;
|
|
3395
|
+
publishBreakerData(withSessionToggled(previous, sessionId, next));
|
|
3396
|
+
setActionError(null);
|
|
3397
|
+
savingRef.current = true;
|
|
3398
|
+
setSaving(true);
|
|
3399
|
+
const seq = ++mutationSeq.current;
|
|
3400
|
+
try {
|
|
3401
|
+
const res = await updateBreaker({ sessionId, enabled: next });
|
|
3402
|
+
if (seq !== mutationSeq.current || sessionIdRef.current !== sessionId) return;
|
|
3403
|
+
if (breakerIsUsable(res)) {
|
|
3404
|
+
publishBreakerData(res.data);
|
|
3405
|
+
setLoadError(null);
|
|
3406
|
+
return;
|
|
3407
|
+
}
|
|
3408
|
+
publishBreakerData(previous);
|
|
3409
|
+
setActionError(res.error ?? "");
|
|
3410
|
+
} finally {
|
|
3411
|
+
savingRef.current = false;
|
|
3412
|
+
setSaving(false);
|
|
3413
|
+
}
|
|
3414
|
+
};
|
|
3415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dq-cost-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
3416
|
+
"button",
|
|
3417
|
+
{
|
|
3418
|
+
type: "button",
|
|
3419
|
+
className: `dq-cost-pill dq-breaker-pill dq-breaker-pill--${state}`,
|
|
3420
|
+
"aria-pressed": on,
|
|
3421
|
+
"aria-busy": saving,
|
|
3422
|
+
"aria-label": ariaLabel,
|
|
3423
|
+
title,
|
|
3424
|
+
disabled: saving,
|
|
3425
|
+
onClick: () => {
|
|
3426
|
+
void toggle();
|
|
3427
|
+
},
|
|
3428
|
+
children: [
|
|
3429
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(BreakerIcon, { state }),
|
|
3430
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dq-cost-pill-label", children: label })
|
|
3431
|
+
]
|
|
3432
|
+
}
|
|
3433
|
+
) });
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
// src/client/message-cost.tsx
|
|
3437
|
+
var import_react7 = require("react");
|
|
3438
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2910
3439
|
var fmtTurnCost = (cost) => cost.toLocaleString(void 0, {
|
|
2911
3440
|
minimumFractionDigits: cost > 0 && cost < 0.01 ? 4 : 2,
|
|
2912
3441
|
maximumFractionDigits: cost > 0 && cost < 0.01 ? 6 : 4
|
|
@@ -2917,8 +3446,8 @@ var cachedData = (sessionId) => {
|
|
|
2917
3446
|
};
|
|
2918
3447
|
function MessageCost({ sessionId, messageId }) {
|
|
2919
3448
|
const { t } = useI18n();
|
|
2920
|
-
const [data, setData] = (0,
|
|
2921
|
-
(0,
|
|
3449
|
+
const [data, setData] = (0, import_react7.useState)(() => cachedData(sessionId));
|
|
3450
|
+
(0, import_react7.useEffect)(() => {
|
|
2922
3451
|
setData(cachedData(sessionId));
|
|
2923
3452
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData(sessionId)));
|
|
2924
3453
|
const hit = cachedData(sessionId)?.turns.some((turn2) => turn2.messageId === messageId) ?? false;
|
|
@@ -2929,7 +3458,7 @@ function MessageCost({ sessionId, messageId }) {
|
|
|
2929
3458
|
if (turn === void 0) return null;
|
|
2930
3459
|
const amount = `\xA5${fmtTurnCost(turn.cost)}`;
|
|
2931
3460
|
const tooltip = t("messageCost.tooltip");
|
|
2932
|
-
return /* @__PURE__ */ (0,
|
|
3461
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2933
3462
|
"span",
|
|
2934
3463
|
{
|
|
2935
3464
|
className: "dq-message-cost",
|
|
@@ -2937,15 +3466,15 @@ function MessageCost({ sessionId, messageId }) {
|
|
|
2937
3466
|
"data-dq-tooltip": tooltip,
|
|
2938
3467
|
"aria-label": t("messageCost.aria", { amount }),
|
|
2939
3468
|
children: [
|
|
2940
|
-
/* @__PURE__ */ (0,
|
|
2941
|
-
/* @__PURE__ */ (0,
|
|
3469
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dq-message-cost-separator", "aria-hidden": true, children: "\xB7" }),
|
|
3470
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dq-message-cost-amount", children: amount })
|
|
2942
3471
|
]
|
|
2943
3472
|
}
|
|
2944
3473
|
);
|
|
2945
3474
|
}
|
|
2946
3475
|
|
|
2947
3476
|
// src/client/session-cost-pill.tsx
|
|
2948
|
-
var
|
|
3477
|
+
var import_react8 = require("react");
|
|
2949
3478
|
var import_react_dom = require("react-dom");
|
|
2950
3479
|
|
|
2951
3480
|
// src/pricing.ts
|
|
@@ -2953,7 +3482,7 @@ var PEAK_PRICING_FROM_MS = Date.UTC(2026, 7, 16, 16, 0, 0);
|
|
|
2953
3482
|
var V41_FLASH_PRICING_FROM_MS = Date.UTC(2026, 8, 10, 4, 0, 0);
|
|
2954
3483
|
var PRO_ROUTED_TO_FLASH_FROM_MS = Date.UTC(2026, 8, 14, 4, 0, 0);
|
|
2955
3484
|
var PEAK_WINDOWS = [[9, 12], [14, 18]];
|
|
2956
|
-
var
|
|
3485
|
+
var BEIJING_OFFSET_MS4 = 8 * 36e5;
|
|
2957
3486
|
var FLAT_RATES = {
|
|
2958
3487
|
pro: { cacheHit: 0.025, input: 3, output: 6 },
|
|
2959
3488
|
flash: { cacheHit: 0.02, input: 1, output: 2 },
|
|
@@ -2998,12 +3527,12 @@ function billedTierOf(timeMs, model) {
|
|
|
2998
3527
|
return tier === "pro" && timeMs >= PRO_ROUTED_TO_FLASH_FROM_MS ? "flash" : tier;
|
|
2999
3528
|
}
|
|
3000
3529
|
function isBeijingWeekday(timeMs) {
|
|
3001
|
-
const day = new Date(timeMs +
|
|
3530
|
+
const day = new Date(timeMs + BEIJING_OFFSET_MS4).getUTCDay();
|
|
3002
3531
|
return day >= 1 && day <= 5;
|
|
3003
3532
|
}
|
|
3004
3533
|
function isPeak(timeMs) {
|
|
3005
3534
|
if (eraAt(timeMs).weekdaysOnly && !isBeijingWeekday(timeMs)) return false;
|
|
3006
|
-
const hour = Math.floor(((timeMs +
|
|
3535
|
+
const hour = Math.floor(((timeMs + BEIJING_OFFSET_MS4) % 864e5 + 864e5) % 864e5 / 36e5);
|
|
3007
3536
|
return PEAK_WINDOWS.some(([from, to]) => hour >= from && hour < to);
|
|
3008
3537
|
}
|
|
3009
3538
|
function ratesAt(timeMs, model) {
|
|
@@ -3013,7 +3542,7 @@ function ratesAt(timeMs, model) {
|
|
|
3013
3542
|
}
|
|
3014
3543
|
|
|
3015
3544
|
// src/client/session-cost-pill.tsx
|
|
3016
|
-
var
|
|
3545
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3017
3546
|
var PANEL_MARGIN = 12;
|
|
3018
3547
|
var PANEL_GAP = 8;
|
|
3019
3548
|
var cachedData2 = (sessionId) => {
|
|
@@ -3021,7 +3550,7 @@ var cachedData2 = (sessionId) => {
|
|
|
3021
3550
|
return response?.ok === true ? response.data ?? null : null;
|
|
3022
3551
|
};
|
|
3023
3552
|
function CostIcon() {
|
|
3024
|
-
return /* @__PURE__ */ (0,
|
|
3553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3025
3554
|
"svg",
|
|
3026
3555
|
{
|
|
3027
3556
|
width: "14",
|
|
@@ -3035,32 +3564,32 @@ function CostIcon() {
|
|
|
3035
3564
|
"aria-hidden": "true",
|
|
3036
3565
|
focusable: "false",
|
|
3037
3566
|
children: [
|
|
3038
|
-
/* @__PURE__ */ (0,
|
|
3039
|
-
/* @__PURE__ */ (0,
|
|
3040
|
-
/* @__PURE__ */ (0,
|
|
3041
|
-
/* @__PURE__ */ (0,
|
|
3042
|
-
/* @__PURE__ */ (0,
|
|
3567
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: "8", cy: "8", r: "6" }),
|
|
3568
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M5.5 5.3 8 8.4l2.5-3.1" }),
|
|
3569
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M8 8.4v2.9" }),
|
|
3570
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M5.8 9.5h4.4" }),
|
|
3571
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M5.8 10.9h4.4" })
|
|
3043
3572
|
]
|
|
3044
3573
|
}
|
|
3045
3574
|
);
|
|
3046
3575
|
}
|
|
3047
3576
|
function SessionCostPill({ sessionId }) {
|
|
3048
3577
|
const { t, locale } = useI18n();
|
|
3049
|
-
const [data, setData] = (0,
|
|
3578
|
+
const [data, setData] = (0, import_react8.useState)(
|
|
3050
3579
|
() => sessionId === void 0 || sessionId === "" ? null : cachedData2(sessionId)
|
|
3051
3580
|
);
|
|
3052
|
-
const [open, setOpen] = (0,
|
|
3053
|
-
const anchorRef = (0,
|
|
3054
|
-
const panelRef = (0,
|
|
3055
|
-
const [pos, setPos] = (0,
|
|
3056
|
-
(0,
|
|
3581
|
+
const [open, setOpen] = (0, import_react8.useState)(false);
|
|
3582
|
+
const anchorRef = (0, import_react8.useRef)(null);
|
|
3583
|
+
const panelRef = (0, import_react8.useRef)(null);
|
|
3584
|
+
const [pos, setPos] = (0, import_react8.useState)(null);
|
|
3585
|
+
(0, import_react8.useEffect)(() => {
|
|
3057
3586
|
if (sessionId === void 0 || sessionId === "") return;
|
|
3058
3587
|
setData(cachedData2(sessionId));
|
|
3059
3588
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData2(sessionId)));
|
|
3060
3589
|
if (getCachedSessionUsage(sessionId) === null) void fetchSessionUsage(sessionId);
|
|
3061
3590
|
return unsubscribe;
|
|
3062
3591
|
}, [sessionId]);
|
|
3063
|
-
(0,
|
|
3592
|
+
(0, import_react8.useLayoutEffect)(() => {
|
|
3064
3593
|
if (!open) {
|
|
3065
3594
|
setPos(null);
|
|
3066
3595
|
return;
|
|
@@ -3075,7 +3604,7 @@ function SessionCostPill({ sessionId }) {
|
|
|
3075
3604
|
bottom: window.innerHeight - pill.top + PANEL_GAP
|
|
3076
3605
|
});
|
|
3077
3606
|
}, [open, data]);
|
|
3078
|
-
(0,
|
|
3607
|
+
(0, import_react8.useEffect)(() => {
|
|
3079
3608
|
if (!open) return;
|
|
3080
3609
|
const close = () => setOpen(false);
|
|
3081
3610
|
const onPointerDown = (event) => {
|
|
@@ -3127,8 +3656,8 @@ function SessionCostPill({ sessionId }) {
|
|
|
3127
3656
|
hour12: false
|
|
3128
3657
|
});
|
|
3129
3658
|
const range = data.firstActive !== null && data.lastActive !== null ? `${rangeTime.format(data.firstActive)} \u2013 ${rangeTime.format(data.lastActive)}` : null;
|
|
3130
|
-
return /* @__PURE__ */ (0,
|
|
3131
|
-
/* @__PURE__ */ (0,
|
|
3659
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
3660
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { ref: anchorRef, className: "dq-cost-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3132
3661
|
"button",
|
|
3133
3662
|
{
|
|
3134
3663
|
type: "button",
|
|
@@ -3138,13 +3667,13 @@ function SessionCostPill({ sessionId }) {
|
|
|
3138
3667
|
"aria-label": t("sessionCost.aria", { amount }),
|
|
3139
3668
|
onClick: () => setOpen((value) => !value),
|
|
3140
3669
|
children: [
|
|
3141
|
-
/* @__PURE__ */ (0,
|
|
3142
|
-
/* @__PURE__ */ (0,
|
|
3670
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CostIcon, {}),
|
|
3671
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dq-cost-pill-label", children: amount })
|
|
3143
3672
|
]
|
|
3144
3673
|
}
|
|
3145
3674
|
) }),
|
|
3146
3675
|
open && (0, import_react_dom.createPortal)(
|
|
3147
|
-
/* @__PURE__ */ (0,
|
|
3676
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3148
3677
|
"div",
|
|
3149
3678
|
{
|
|
3150
3679
|
ref: panelRef,
|
|
@@ -3153,35 +3682,35 @@ function SessionCostPill({ sessionId }) {
|
|
|
3153
3682
|
"aria-label": t("sessionCost.title"),
|
|
3154
3683
|
style: pos === null ? { visibility: "hidden", left: 0, top: 0 } : { visibility: "visible", left: pos.left, bottom: pos.bottom, top: "auto" },
|
|
3155
3684
|
children: [
|
|
3156
|
-
/* @__PURE__ */ (0,
|
|
3157
|
-
/* @__PURE__ */ (0,
|
|
3158
|
-
/* @__PURE__ */ (0,
|
|
3685
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dq-cost-panel-title", children: [
|
|
3686
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dq-cost-panel-title-label", children: [
|
|
3687
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CostIcon, {}),
|
|
3159
3688
|
t("sessionCost.title")
|
|
3160
3689
|
] }),
|
|
3161
|
-
/* @__PURE__ */ (0,
|
|
3690
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dq-cost-panel-value", children: amount })
|
|
3162
3691
|
] }),
|
|
3163
|
-
/* @__PURE__ */ (0,
|
|
3164
|
-
/* @__PURE__ */ (0,
|
|
3165
|
-
/* @__PURE__ */ (0,
|
|
3166
|
-
/* @__PURE__ */ (0,
|
|
3692
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dq-cost-panel-rule", "aria-hidden": true }),
|
|
3693
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("dl", { className: "dq-cost-details", children: [
|
|
3694
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { children: t("common.input") }),
|
|
3695
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("dd", { children: [
|
|
3167
3696
|
"\xA5",
|
|
3168
3697
|
fmtTurnCost(split.input)
|
|
3169
3698
|
] }),
|
|
3170
|
-
/* @__PURE__ */ (0,
|
|
3171
|
-
/* @__PURE__ */ (0,
|
|
3699
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { children: t("common.cacheHit") }),
|
|
3700
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("dd", { children: [
|
|
3172
3701
|
"\xA5",
|
|
3173
3702
|
fmtTurnCost(split.cache)
|
|
3174
3703
|
] }),
|
|
3175
|
-
/* @__PURE__ */ (0,
|
|
3176
|
-
/* @__PURE__ */ (0,
|
|
3704
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { children: t("common.output") }),
|
|
3705
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("dd", { children: [
|
|
3177
3706
|
"\xA5",
|
|
3178
3707
|
fmtTurnCost(split.output)
|
|
3179
3708
|
] }),
|
|
3180
|
-
/* @__PURE__ */ (0,
|
|
3181
|
-
/* @__PURE__ */ (0,
|
|
3182
|
-
range !== null && /* @__PURE__ */ (0,
|
|
3183
|
-
/* @__PURE__ */ (0,
|
|
3184
|
-
/* @__PURE__ */ (0,
|
|
3709
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { children: t("sessionUsage.calls") }),
|
|
3710
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { children: fmtInt(data.calls) }),
|
|
3711
|
+
range !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
3712
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { children: t("sessionCost.active") }),
|
|
3713
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { children: range })
|
|
3185
3714
|
] })
|
|
3186
3715
|
] })
|
|
3187
3716
|
]
|
|
@@ -3244,6 +3773,18 @@ var css = `
|
|
|
3244
3773
|
.dq-cost-pill:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3245
3774
|
.dq-cost-pill svg{flex:none}
|
|
3246
3775
|
.dq-cost-pill-label{min-width:0;overflow:hidden;text-overflow:ellipsis}
|
|
3776
|
+
/* \u9AD8\u5CF0\u6682\u505C pill: same dock/skin as the cost pill. The three states never rely
|
|
3777
|
+
on colour alone \u2014 copy changes and the glyph switches outline\u2192solid \u2014 but the
|
|
3778
|
+
tone reinforces them: off stays neutral tertiary, on uses the primary colour,
|
|
3779
|
+
holding uses the warn tone. Nothing here may widen the dock row. */
|
|
3780
|
+
.dq-breaker-pill{font-variant-numeric:normal}
|
|
3781
|
+
.dq-breaker-pill--off{color:var(--dsw-alias-label-tertiary,#59636e)}
|
|
3782
|
+
.dq-breaker-pill--off:hover{color:var(--dsw-alias-label-secondary,#59636e)}
|
|
3783
|
+
.dq-breaker-pill--on{color:var(--dsw-alias-state-business-primary,#0969da)}
|
|
3784
|
+
.dq-breaker-pill--on:hover{color:var(--dsw-alias-state-business-primary,#0969da)}
|
|
3785
|
+
.dq-breaker-pill--holding{color:var(--dq-text-warn,#9a6700)}
|
|
3786
|
+
.dq-breaker-pill--holding:hover{color:var(--dq-text-warn,#9a6700)}
|
|
3787
|
+
.dq-breaker-pill:disabled{cursor:default;opacity:.7}
|
|
3247
3788
|
/* Click-open breakdown: the host stat-dialog skin (menu surface, prominent
|
|
3248
3789
|
elevation, 12/18 rows), portaled to the body and placed above the pill. */
|
|
3249
3790
|
.dq-cost-panel{position:fixed;z-index:1100;box-sizing:border-box;width:max-content;min-width:min(300px,calc(100vw - 24px));max-width:min(440px,calc(100vw - 24px));padding:16px;border:0;border-radius:var(--dsw-radius-lg,16px);background:var(--dsw-specific-menu,var(--dsw-alias-bg-overlay,#fff));-webkit-backdrop-filter:var(--dsw-menu-backdrop-filter,blur(40px) saturate(150%));backdrop-filter:var(--dsw-menu-backdrop-filter,blur(40px) saturate(150%));box-shadow:var(--dsw-elevation-prominent,0 12px 32px rgba(0,0,0,.24));font-family:var(--dsw-font-family,-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei","Helvetica Neue",Helvetica,Arial,sans-serif);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#59636e);cursor:default}
|
|
@@ -3350,6 +3891,17 @@ var css = `
|
|
|
3350
3891
|
.dq-remaining-breakdown{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;flex-direction:column;gap:4px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:nowrap;font-size:12px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}
|
|
3351
3892
|
.dq-remaining[open] .dq-remaining-breakdown{display:flex}
|
|
3352
3893
|
.dq-remaining-granted{color:var(--dsw-alias-label-secondary,#59636e);font-weight:400;font-size:12px}
|
|
3894
|
+
/* Low-balance top-up shortcut beside the runway value. It is an <a>, not a
|
|
3895
|
+
<button>, because it leaves the app; the \u2197 glyph states that. Colours come
|
|
3896
|
+
from the host's primary-button tokens (the same pair DSH's own filled
|
|
3897
|
+
buttons use) so the page keeps exactly one filled call to action instead of
|
|
3898
|
+
reading as another filter chip. Height stays on the runway text line so the
|
|
3899
|
+
hero row does not grow. */
|
|
3900
|
+
.dq-runway-line{display:flex;align-items:center;flex-wrap:wrap;gap:4px 10px}
|
|
3901
|
+
.dq-topup{display:inline-flex;align-items:center;gap:2px;height:26px;padding:0 10px;border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-button-primary-fill,#0969da);color:var(--dsw-alias-label-primary-foreground,#fff);font-size:12px;font-weight:600;line-height:1;text-decoration:none;white-space:nowrap;transition:background .15s ease}
|
|
3902
|
+
.dq-topup:hover{background:var(--dsw-alias-button-primary-hover,#0757ba)}
|
|
3903
|
+
.dq-topup:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3904
|
+
.dq-topup-arrow{font-size:10px;line-height:1;transform:translateY(-1px)}
|
|
3353
3905
|
/* narrow screens: .dq-balance-grid wraps its 3 stats onto separate lines, so the absolutely-positioned
|
|
3354
3906
|
.dq-remaining-breakdown/.dq-runway-detail popovers can float down over whichever stat now sits in the
|
|
3355
3907
|
next row (e.g. "\u4F59\u989D\u660E\u7EC6" open covering "\u9884\u8BA1\u53EF\u7528"'s clickable summary) \u2014 force each stat onto its own
|
|
@@ -3506,6 +4058,22 @@ var css = `
|
|
|
3506
4058
|
package.json (see build.mjs) so it can never drift from the release. */
|
|
3507
4059
|
.dq-footer{margin:16px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}
|
|
3508
4060
|
.dq-number{width:82px;padding:5px 8px;font-size:13px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
|
|
4061
|
+
/* \u9AD8\u5CF0\u6682\u505C\u5361\uFF1A\u8BF4\u660E\u3001\u72B6\u6001\u884C\u4E0E\u4F1A\u8BDD\u5217\u8868\u3002320px \u4E0B\u6807\u9898\u7701\u7565\u3001\u72B6\u6001\u4E0E\u5F00\u5173\u6362\u884C\uFF0C
|
|
4062
|
+
\u5217\u8868\u884C\u4E0D\u5141\u8BB8\u6491\u51FA\u5361\u7247\u5BBD\u5EA6\u3002\u603B\u5F00\u5173\u5173\u95ED\u65F6\u5217\u8868\u6574\u4F53\u5F31\u5316\uFF0C\u4F46\u6BCF\u4F1A\u8BDD\u5F00\u5173\u4ECD\u53EF\u64CD\u4F5C\u3002 */
|
|
4063
|
+
.dq-breaker-desc{margin:-6px 0 0}
|
|
4064
|
+
.dq-breaker-status{display:flex;align-items:center;flex-wrap:wrap;gap:8px;min-width:0;margin-top:10px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
4065
|
+
.dq-breaker-list{display:flex;flex-direction:column;margin:12px 0 0;padding:0;list-style:none;min-width:0}
|
|
4066
|
+
.dq-breaker-list--muted{opacity:.62}
|
|
4067
|
+
.dq-breaker-row{display:flex;align-items:center;gap:12px;min-width:0;padding:9px 0;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.10))}
|
|
4068
|
+
.dq-breaker-row:first-child{border-top:0;padding-top:0}
|
|
4069
|
+
.dq-breaker-row-main{display:flex;align-items:center;flex-wrap:wrap;gap:4px 8px;flex:1 1 auto;min-width:0}
|
|
4070
|
+
.dq-breaker-row-title{min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--dsw-alias-label-primary,#1f2328)}
|
|
4071
|
+
.dq-breaker-row-state{font-size:12px;line-height:1.5;white-space:nowrap;color:var(--dq-text-warn)}
|
|
4072
|
+
.dq-breaker-switch{display:inline-flex;align-items:center;justify-content:center;flex:none;cursor:pointer}
|
|
4073
|
+
.dq-breaker-switch input{width:16px;height:16px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer}
|
|
4074
|
+
.dq-breaker-switch input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
4075
|
+
.dq-breaker-switch input:disabled{cursor:default;opacity:.6}
|
|
4076
|
+
.dq-breaker-error{margin-top:8px;font-size:12px;line-height:1.5}
|
|
3509
4077
|
.dq-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;border-color:transparent}
|
|
3510
4078
|
.dq-links-title{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 12px}
|
|
3511
4079
|
.dq-links{display:flex;flex-wrap:wrap;gap:8px}
|
|
@@ -3722,7 +4290,7 @@ html[data-dq-quota-view] .dq-balance{padding-bottom:48px}
|
|
|
3722
4290
|
`;
|
|
3723
4291
|
|
|
3724
4292
|
// src/client/widget.tsx
|
|
3725
|
-
var
|
|
4293
|
+
var import_react9 = require("react");
|
|
3726
4294
|
|
|
3727
4295
|
// src/client/sessions.ts
|
|
3728
4296
|
function currentSessionIdOf(state) {
|
|
@@ -3731,7 +4299,7 @@ function currentSessionIdOf(state) {
|
|
|
3731
4299
|
}
|
|
3732
4300
|
|
|
3733
4301
|
// src/client/widget.tsx
|
|
3734
|
-
var
|
|
4302
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
3735
4303
|
var MARGIN = 16;
|
|
3736
4304
|
var CORNER_KEY = "widget.corner";
|
|
3737
4305
|
var COLLAPSED_KEY = "widget.collapsed";
|
|
@@ -3782,47 +4350,47 @@ function cornerPos(corner, node, bounds) {
|
|
|
3782
4350
|
function QuotaWidget(props) {
|
|
3783
4351
|
const { t } = useI18n();
|
|
3784
4352
|
const sessionId = props.useSessions(currentSessionIdOf);
|
|
3785
|
-
const [visible, setVisible] = (0,
|
|
3786
|
-
(0,
|
|
3787
|
-
const [quotaViewActive2, setQuotaViewActive] = (0,
|
|
3788
|
-
(0,
|
|
3789
|
-
const [lowBalance, setLowBalance] = (0,
|
|
3790
|
-
(0,
|
|
3791
|
-
const [selectedTabIds, setSelectedTabIds] = (0,
|
|
3792
|
-
(0,
|
|
3793
|
-
const [viewTabs, setViewTabs] = (0,
|
|
3794
|
-
(0,
|
|
3795
|
-
const [activeViewId, setActiveViewId] = (0,
|
|
4353
|
+
const [visible, setVisible] = (0, import_react9.useState)(widgetVisibleStore.get());
|
|
4354
|
+
(0, import_react9.useEffect)(() => widgetVisibleStore.subscribe(() => setVisible(widgetVisibleStore.get())), []);
|
|
4355
|
+
const [quotaViewActive2, setQuotaViewActive] = (0, import_react9.useState)(quotaViewActiveStore.get());
|
|
4356
|
+
(0, import_react9.useEffect)(() => quotaViewActiveStore.subscribe(() => setQuotaViewActive(quotaViewActiveStore.get())), []);
|
|
4357
|
+
const [lowBalance, setLowBalance] = (0, import_react9.useState)(lowBalanceStore.get());
|
|
4358
|
+
(0, import_react9.useEffect)(() => lowBalanceStore.subscribe(() => setLowBalance(lowBalanceStore.get())), []);
|
|
4359
|
+
const [selectedTabIds, setSelectedTabIds] = (0, import_react9.useState)(widgetTabIdsStore.get());
|
|
4360
|
+
(0, import_react9.useEffect)(() => widgetTabIdsStore.subscribe(() => setSelectedTabIds(widgetTabIdsStore.get())), []);
|
|
4361
|
+
const [viewTabs, setViewTabs] = (0, import_react9.useState)(() => props.views.list());
|
|
4362
|
+
(0, import_react9.useEffect)(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views]);
|
|
4363
|
+
const [activeViewId, setActiveViewId] = (0, import_react9.useState)(null);
|
|
3796
4364
|
const cachedBalance = getCachedBalance();
|
|
3797
|
-
const [data, setData] = (0,
|
|
3798
|
-
const [error, setError] = (0,
|
|
3799
|
-
const [loading, setLoading] = (0,
|
|
3800
|
-
const [collapsed, setCollapsed] = (0,
|
|
3801
|
-
const [pos, setPos] = (0,
|
|
3802
|
-
const [corner, setCorner] = (0,
|
|
3803
|
-
const [dragging, setDragging] = (0,
|
|
3804
|
-
const rootRef = (0,
|
|
3805
|
-
const dragRef = (0,
|
|
4365
|
+
const [data, setData] = (0, import_react9.useState)(cachedBalance?.data ?? null);
|
|
4366
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
4367
|
+
const [loading, setLoading] = (0, import_react9.useState)(cachedBalance === null);
|
|
4368
|
+
const [collapsed, setCollapsed] = (0, import_react9.useState)(() => loadPref(COLLAPSED_KEY, isBoolean, false));
|
|
4369
|
+
const [pos, setPos] = (0, import_react9.useState)(null);
|
|
4370
|
+
const [corner, setCorner] = (0, import_react9.useState)(() => loadPref(CORNER_KEY, isCorner, "br"));
|
|
4371
|
+
const [dragging, setDragging] = (0, import_react9.useState)(false);
|
|
4372
|
+
const rootRef = (0, import_react9.useRef)(null);
|
|
4373
|
+
const dragRef = (0, import_react9.useRef)(null);
|
|
3806
4374
|
const readUsage = () => {
|
|
3807
4375
|
const cached = getCachedUsage();
|
|
3808
4376
|
return { data: cached?.ok === true ? cached.data ?? null : null, at: getCachedUsageAt() };
|
|
3809
4377
|
};
|
|
3810
|
-
const [usage, setUsage] = (0,
|
|
3811
|
-
(0,
|
|
4378
|
+
const [usage, setUsage] = (0, import_react9.useState)(readUsage);
|
|
4379
|
+
(0, import_react9.useEffect)(() => subscribeUsage(() => setUsage(readUsage())), []);
|
|
3812
4380
|
const readSessionUsage = (id) => {
|
|
3813
4381
|
if (id === void 0) return null;
|
|
3814
4382
|
const response = getCachedSessionUsage(id);
|
|
3815
4383
|
return response?.ok === true ? response.data ?? null : null;
|
|
3816
4384
|
};
|
|
3817
|
-
const [sessionUsage, setSessionUsage] = (0,
|
|
3818
|
-
(0,
|
|
4385
|
+
const [sessionUsage, setSessionUsage] = (0, import_react9.useState)(() => readSessionUsage(sessionId));
|
|
4386
|
+
(0, import_react9.useEffect)(() => {
|
|
3819
4387
|
setSessionUsage(readSessionUsage(sessionId));
|
|
3820
4388
|
if (sessionId === void 0) return;
|
|
3821
4389
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setSessionUsage(readSessionUsage(sessionId)));
|
|
3822
4390
|
void fetchSessionUsage(sessionId);
|
|
3823
4391
|
return unsubscribe;
|
|
3824
4392
|
}, [sessionId]);
|
|
3825
|
-
(0,
|
|
4393
|
+
(0, import_react9.useLayoutEffect)(() => {
|
|
3826
4394
|
const viewIds = viewTabs.map((tab) => tab.id);
|
|
3827
4395
|
const read = () => {
|
|
3828
4396
|
const next = getActiveConversationViewId(getShellFrame(rootRef.current), viewIds);
|
|
@@ -3859,14 +4427,14 @@ function QuotaWidget(props) {
|
|
|
3859
4427
|
setLoading(false);
|
|
3860
4428
|
}
|
|
3861
4429
|
};
|
|
3862
|
-
(0,
|
|
4430
|
+
(0, import_react9.useEffect)(() => {
|
|
3863
4431
|
void load(true);
|
|
3864
4432
|
const id = setInterval(() => {
|
|
3865
4433
|
void load(false);
|
|
3866
4434
|
}, 6e4);
|
|
3867
4435
|
return () => clearInterval(id);
|
|
3868
4436
|
}, []);
|
|
3869
|
-
(0,
|
|
4437
|
+
(0, import_react9.useLayoutEffect)(() => {
|
|
3870
4438
|
if (!visible || quotaViewActive2) return;
|
|
3871
4439
|
const node = rootRef.current;
|
|
3872
4440
|
if (node === null) return;
|
|
@@ -3994,9 +4562,9 @@ function QuotaWidget(props) {
|
|
|
3994
4562
|
const dotClass = primary === null ? "dsh-quota-dot dsh-quota-dot--idle" : data?.isAvailable === false ? "dsh-quota-dot dsh-quota-dot--error" : low ? "dsh-quota-dot dsh-quota-dot--warn" : "dsh-quota-dot";
|
|
3995
4563
|
const ageMinutes = usage.at === null ? null : Math.floor((Date.now() - usage.at) / 6e4);
|
|
3996
4564
|
const usageAgeText = ageMinutes === null || ageMinutes < 1 ? t("widget.updatedNow") : t("widget.stale", { minutes: ageMinutes });
|
|
3997
|
-
const sessionCostRow = sessionUsage === null ? null : /* @__PURE__ */ (0,
|
|
3998
|
-
/* @__PURE__ */ (0,
|
|
3999
|
-
/* @__PURE__ */ (0,
|
|
4565
|
+
const sessionCostRow = sessionUsage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-row", title: t("widget.sessionCostTitle"), children: [
|
|
4566
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-quota-label", children: t("widget.sessionCost") }),
|
|
4567
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
4000
4568
|
"\xA5",
|
|
4001
4569
|
fmtTurnCost(sessionUsage.cost)
|
|
4002
4570
|
] })
|
|
@@ -4004,50 +4572,50 @@ function QuotaWidget(props) {
|
|
|
4004
4572
|
let body = null;
|
|
4005
4573
|
if (!collapsed) {
|
|
4006
4574
|
if (loading && data === null && error === null) {
|
|
4007
|
-
body = /* @__PURE__ */ (0,
|
|
4008
|
-
/* @__PURE__ */ (0,
|
|
4575
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4576
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { children: t("widget.loading") }),
|
|
4009
4577
|
sessionCostRow
|
|
4010
4578
|
] });
|
|
4011
4579
|
} else if (error !== null && data === null) {
|
|
4012
|
-
body = /* @__PURE__ */ (0,
|
|
4013
|
-
/* @__PURE__ */ (0,
|
|
4580
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4581
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-quota-error", children: localizeApiError(error, t, "error.query") }),
|
|
4014
4582
|
sessionCostRow
|
|
4015
4583
|
] });
|
|
4016
4584
|
} else if (primary !== null) {
|
|
4017
|
-
body = /* @__PURE__ */ (0,
|
|
4018
|
-
/* @__PURE__ */ (0,
|
|
4019
|
-
/* @__PURE__ */ (0,
|
|
4020
|
-
/* @__PURE__ */ (0,
|
|
4021
|
-
/* @__PURE__ */ (0,
|
|
4585
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4586
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-quota-remaining-label", children: t("balance.remaining") }),
|
|
4587
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { title: low ? t("widget.lowTitle", { amount: fmt(lowBalance) }) : void 0, children: [
|
|
4588
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `dsh-quota-total${low ? " dsh-quota-total--low" : ""}`, children: fmt(primary.total) }),
|
|
4589
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-quota-currency", children: primary.currency })
|
|
4022
4590
|
] }),
|
|
4023
|
-
usage.data !== null && /* @__PURE__ */ (0,
|
|
4024
|
-
/* @__PURE__ */ (0,
|
|
4025
|
-
/* @__PURE__ */ (0,
|
|
4591
|
+
usage.data !== null && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-row", title: t("widget.todayCostTitle", { age: usageAgeText }), children: [
|
|
4592
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-quota-label", children: t("widget.todayCost") }),
|
|
4593
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
4026
4594
|
"\xA5",
|
|
4027
4595
|
fmt(usage.data.summary.today.cost)
|
|
4028
4596
|
] })
|
|
4029
4597
|
] }),
|
|
4030
4598
|
sessionCostRow,
|
|
4031
|
-
data?.isAvailable === false && /* @__PURE__ */ (0,
|
|
4032
|
-
/* @__PURE__ */ (0,
|
|
4033
|
-
/* @__PURE__ */ (0,
|
|
4599
|
+
data?.isAvailable === false && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-row", children: [
|
|
4600
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-quota-label", children: t("common.status") }),
|
|
4601
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-quota-value dsh-quota-error", children: t("common.unavailable") })
|
|
4034
4602
|
] })
|
|
4035
4603
|
] });
|
|
4036
4604
|
} else {
|
|
4037
|
-
body = /* @__PURE__ */ (0,
|
|
4038
|
-
/* @__PURE__ */ (0,
|
|
4605
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4606
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-quota-error", children: t("widget.noBalance") }),
|
|
4039
4607
|
sessionCostRow
|
|
4040
4608
|
] });
|
|
4041
4609
|
}
|
|
4042
4610
|
}
|
|
4043
4611
|
const style = pos !== null ? { left: `${pos.x}px`, top: `${pos.y}px`, right: "auto", bottom: "auto" } : void 0;
|
|
4044
|
-
return /* @__PURE__ */ (0,
|
|
4612
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
4045
4613
|
"div",
|
|
4046
4614
|
{
|
|
4047
4615
|
className: `dsh-quota-root${dragging ? " dsh-quota-dragging" : ""}${hidden ? " dsh-quota-root--hidden" : ""}`,
|
|
4048
4616
|
style,
|
|
4049
4617
|
"aria-hidden": hidden,
|
|
4050
|
-
children: /* @__PURE__ */ (0,
|
|
4618
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
4051
4619
|
"div",
|
|
4052
4620
|
{
|
|
4053
4621
|
ref: rootRef,
|
|
@@ -4059,14 +4627,14 @@ function QuotaWidget(props) {
|
|
|
4059
4627
|
onPointerCancel: () => endDrag(false),
|
|
4060
4628
|
onLostPointerCapture: () => endDrag(false),
|
|
4061
4629
|
children: [
|
|
4062
|
-
/* @__PURE__ */ (0,
|
|
4063
|
-
/* @__PURE__ */ (0,
|
|
4064
|
-
/* @__PURE__ */ (0,
|
|
4065
|
-
/* @__PURE__ */ (0,
|
|
4066
|
-
/* @__PURE__ */ (0,
|
|
4630
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-header", children: [
|
|
4631
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-title", children: [
|
|
4632
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-quota-grip", title: t("widget.dragGrip"), children: "\u2807" }),
|
|
4633
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: dotClass }),
|
|
4634
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dsh-quota-name", children: t("widget.title") })
|
|
4067
4635
|
] }),
|
|
4068
|
-
/* @__PURE__ */ (0,
|
|
4069
|
-
/* @__PURE__ */ (0,
|
|
4636
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dsh-quota-actions", children: [
|
|
4637
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
4070
4638
|
"button",
|
|
4071
4639
|
{
|
|
4072
4640
|
className: "dsh-quota-btn",
|
|
@@ -4082,7 +4650,7 @@ function QuotaWidget(props) {
|
|
|
4082
4650
|
children: "\u21BB"
|
|
4083
4651
|
}
|
|
4084
4652
|
),
|
|
4085
|
-
/* @__PURE__ */ (0,
|
|
4653
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
4086
4654
|
"button",
|
|
4087
4655
|
{
|
|
4088
4656
|
className: "dsh-quota-btn",
|
|
@@ -4100,7 +4668,7 @@ function QuotaWidget(props) {
|
|
|
4100
4668
|
)
|
|
4101
4669
|
] })
|
|
4102
4670
|
] }),
|
|
4103
|
-
collapsed && primary !== null && /* @__PURE__ */ (0,
|
|
4671
|
+
collapsed && primary !== null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "dsh-quota-collapsed-total-row", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: `dsh-quota-collapsed-total${low ? " dsh-quota-total--low" : ""}`, children: [
|
|
4104
4672
|
fmt(primary.total),
|
|
4105
4673
|
" ",
|
|
4106
4674
|
primary.currency
|
|
@@ -4131,7 +4699,7 @@ function conversationViewsSource(slots) {
|
|
|
4131
4699
|
}
|
|
4132
4700
|
|
|
4133
4701
|
// src/client/index.tsx
|
|
4134
|
-
var
|
|
4702
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
4135
4703
|
var name = "dsh-usage-dashboard";
|
|
4136
4704
|
var inject = ["slots", "locale", "layout"];
|
|
4137
4705
|
var openQuotaTab = null;
|
|
@@ -4152,11 +4720,15 @@ function apply(ctx) {
|
|
|
4152
4720
|
});
|
|
4153
4721
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register(
|
|
4154
4722
|
{ name: "shell.overlay", id: "deepseek-quota", order: 1e3, locale: NS, label: () => t("widget.title") },
|
|
4155
|
-
({ t: slotT, useSessions }) => /* @__PURE__ */ (0,
|
|
4723
|
+
({ t: slotT, useSessions }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(QuotaWidget, { useSessions, views }) }) })
|
|
4156
4724
|
));
|
|
4157
4725
|
ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register(
|
|
4158
4726
|
{ name: "conversation.composer.dock", id: "quota-cost", order: 2, locale: NS },
|
|
4159
|
-
({ t: slotT, sessionId }) => /* @__PURE__ */ (0,
|
|
4727
|
+
({ t: slotT, sessionId }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SessionCostPill, { sessionId }) }) })
|
|
4728
|
+
));
|
|
4729
|
+
ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register(
|
|
4730
|
+
{ name: "conversation.composer.dock", id: "peak-breaker", order: 3, locale: NS },
|
|
4731
|
+
({ t: slotT, sessionId }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BreakerPill, { sessionId }) }) })
|
|
4160
4732
|
));
|
|
4161
4733
|
ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register(
|
|
4162
4734
|
{
|
|
@@ -4166,13 +4738,13 @@ function apply(ctx) {
|
|
|
4166
4738
|
locale: NS,
|
|
4167
4739
|
label: () => t("messageCost.label")
|
|
4168
4740
|
},
|
|
4169
|
-
({ t: slotT, sessionId, messageId }) => /* @__PURE__ */ (0,
|
|
4741
|
+
({ t: slotT, sessionId, messageId }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MessageCost, { sessionId, messageId }) }) })
|
|
4170
4742
|
));
|
|
4171
4743
|
const QuotaView = ({ t: slotT, sessionId, openView }) => {
|
|
4172
|
-
(0,
|
|
4744
|
+
(0, import_react10.useEffect)(() => {
|
|
4173
4745
|
if (openView !== void 0) openQuotaTab = openView;
|
|
4174
4746
|
}, [openView]);
|
|
4175
|
-
return /* @__PURE__ */ (0,
|
|
4747
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ErrorBoundary, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BalanceDashboard, { sessionId, views }) }) });
|
|
4176
4748
|
};
|
|
4177
4749
|
ctx.effect(() => {
|
|
4178
4750
|
let disposeView = null;
|
|
@@ -4208,14 +4780,14 @@ function apply(ctx) {
|
|
|
4208
4780
|
disposeView?.();
|
|
4209
4781
|
};
|
|
4210
4782
|
}, "dsh-usage-dashboard: quota tab registration");
|
|
4211
|
-
const QuotaPanel = ({ t: slotT }) => /* @__PURE__ */ (0,
|
|
4212
|
-
/* @__PURE__ */ (0,
|
|
4213
|
-
/* @__PURE__ */ (0,
|
|
4214
|
-
/* @__PURE__ */ (0,
|
|
4783
|
+
const QuotaPanel = ({ t: slotT }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dq-panel", children: [
|
|
4784
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "dq-panel-head", "data-window-drag": true, children: [
|
|
4785
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: "dq-panel-title", children: slotT("nav.quota") }),
|
|
4786
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dq-panel-caption", children: slotT("panel.caption") })
|
|
4215
4787
|
] }),
|
|
4216
|
-
/* @__PURE__ */ (0,
|
|
4788
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ErrorBoundary, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BalanceDashboard, { views }) }) })
|
|
4217
4789
|
] });
|
|
4218
|
-
const QuotaGlyph = ({ size, active }) => /* @__PURE__ */ (0,
|
|
4790
|
+
const QuotaGlyph = ({ size, active }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
4219
4791
|
"svg",
|
|
4220
4792
|
{
|
|
4221
4793
|
className: "dq-side-icon",
|
|
@@ -4231,23 +4803,23 @@ function apply(ctx) {
|
|
|
4231
4803
|
"aria-hidden": "true",
|
|
4232
4804
|
focusable: "false",
|
|
4233
4805
|
children: [
|
|
4234
|
-
/* @__PURE__ */ (0,
|
|
4235
|
-
/* @__PURE__ */ (0,
|
|
4236
|
-
/* @__PURE__ */ (0,
|
|
4237
|
-
/* @__PURE__ */ (0,
|
|
4806
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M3.5 3.3 8 8.7l4.5-5.4" }),
|
|
4807
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M8 8.7V13.2" }),
|
|
4808
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M3.5 10.2h9" }),
|
|
4809
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M3.5 12.1h9" })
|
|
4238
4810
|
]
|
|
4239
4811
|
}
|
|
4240
4812
|
);
|
|
4241
4813
|
const QuotaFooterButton = ({ wide, t: slotT }) => {
|
|
4242
|
-
const [active, setActive] = (0,
|
|
4243
|
-
(0,
|
|
4814
|
+
const [active, setActive] = (0, import_react10.useState)(() => ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
|
|
4815
|
+
(0, import_react10.useEffect)(() => {
|
|
4244
4816
|
const sync = () => {
|
|
4245
4817
|
setActive(ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
|
|
4246
4818
|
};
|
|
4247
4819
|
sync();
|
|
4248
4820
|
return ctx.layout.panelInfo.subscribe(sync);
|
|
4249
4821
|
}, []);
|
|
4250
|
-
return /* @__PURE__ */ (0,
|
|
4822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
4251
4823
|
"button",
|
|
4252
4824
|
{
|
|
4253
4825
|
type: "button",
|
|
@@ -4259,8 +4831,8 @@ function apply(ctx) {
|
|
|
4259
4831
|
ctx.layout.selectPanel(active ? null : QUOTA_PANEL_ID);
|
|
4260
4832
|
},
|
|
4261
4833
|
children: [
|
|
4262
|
-
/* @__PURE__ */ (0,
|
|
4263
|
-
wide && /* @__PURE__ */ (0,
|
|
4834
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dq-side-entry-icon", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(QuotaGlyph, { size: 16 }) }),
|
|
4835
|
+
wide && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "dq-side-entry-label", children: slotT("nav.quota") })
|
|
4264
4836
|
]
|
|
4265
4837
|
}
|
|
4266
4838
|
);
|