@cassius0924/dsh-usage-dashboard 0.8.3 → 0.9.0
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 +13 -46
- package/lib/client.js +791 -1014
- package/lib/client.js.map +4 -4
- package/lib/index.js +61 -506
- package/lib/index.js.map +4 -4
- package/lib/types/client/api.d.ts +1 -8
- package/lib/types/client/locales.d.ts +25 -34
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/context.d.ts +0 -36
- package/lib/types/contract.d.ts +0 -65
- package/lib/types/index.d.ts +16 -19
- package/package.json +2 -2
- package/lib/types/breaker-gate.d.ts +0 -94
- package/lib/types/breaker-settings.d.ts +0 -28
- package/lib/types/breaker.d.ts +0 -49
- package/lib/types/client/breaker-pill.d.ts +0 -21
- package/lib/types/client/breaker-state.d.ts +0 -8
- package/lib/types/client/breaker.d.ts +0 -60
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_react8 = 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 current = `${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 !== current) localStorage.removeItem(key);
|
|
95
95
|
}
|
|
96
96
|
} catch {
|
|
97
97
|
}
|
|
@@ -118,17 +118,6 @@ 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
|
-
};
|
|
132
121
|
|
|
133
122
|
// src/client/api.ts
|
|
134
123
|
var BALANCE_TTL_MS = 6e4;
|
|
@@ -206,15 +195,15 @@ var sessionUsageCache = /* @__PURE__ */ new Map();
|
|
|
206
195
|
var sessionUsageListeners = /* @__PURE__ */ new Map();
|
|
207
196
|
var getCachedSessionUsage = (sessionId) => sessionUsageCache.get(sessionId) ?? null;
|
|
208
197
|
var subscribeSessionUsage = (sessionId, fn) => {
|
|
209
|
-
let
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
sessionUsageListeners.set(sessionId,
|
|
198
|
+
let listeners = sessionUsageListeners.get(sessionId);
|
|
199
|
+
if (listeners === void 0) {
|
|
200
|
+
listeners = /* @__PURE__ */ new Set();
|
|
201
|
+
sessionUsageListeners.set(sessionId, listeners);
|
|
213
202
|
}
|
|
214
|
-
|
|
203
|
+
listeners.add(fn);
|
|
215
204
|
return () => {
|
|
216
|
-
|
|
217
|
-
if (
|
|
205
|
+
listeners?.delete(fn);
|
|
206
|
+
if (listeners?.size === 0) sessionUsageListeners.delete(sessionId);
|
|
218
207
|
};
|
|
219
208
|
};
|
|
220
209
|
var publishSessionUsage = (sessionId, response) => {
|
|
@@ -241,118 +230,20 @@ async function fetchSessionUsage(sessionId, force = false) {
|
|
|
241
230
|
sessionUsageInflight.set(sessionId, tracked);
|
|
242
231
|
return tracked;
|
|
243
232
|
}
|
|
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
|
-
}
|
|
270
233
|
|
|
271
234
|
// src/client/dashboard.tsx
|
|
272
|
-
var
|
|
273
|
-
|
|
274
|
-
// src/client/breaker.ts
|
|
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
|
-
}
|
|
235
|
+
var import_react4 = require("react");
|
|
345
236
|
|
|
346
237
|
// src/client/budget.ts
|
|
347
|
-
var
|
|
238
|
+
var BEIJING_OFFSET_MS = 8 * 60 * 60 * 1e3;
|
|
348
239
|
function budgetSnapshot(spentValue, budgetValue, nowMs = Date.now()) {
|
|
349
240
|
const spent = Number.isFinite(spentValue) ? Math.max(0, spentValue) : 0;
|
|
350
241
|
const budget = Number.isFinite(budgetValue) ? Math.max(0, budgetValue) : 0;
|
|
351
|
-
const beijingNow = new Date(nowMs +
|
|
242
|
+
const beijingNow = new Date(nowMs + BEIJING_OFFSET_MS);
|
|
352
243
|
const year = beijingNow.getUTCFullYear();
|
|
353
244
|
const month = beijingNow.getUTCMonth();
|
|
354
|
-
const start = Date.UTC(year, month, 1) -
|
|
355
|
-
const end = Date.UTC(year, month + 1, 1) -
|
|
245
|
+
const start = Date.UTC(year, month, 1) - BEIJING_OFFSET_MS;
|
|
246
|
+
const end = Date.UTC(year, month + 1, 1) - BEIJING_OFFSET_MS;
|
|
356
247
|
const elapsedRatio = Math.min(1, Math.max(1 / (end - start), (nowMs - start) / (end - start)));
|
|
357
248
|
const forecast = spent / elapsedRatio;
|
|
358
249
|
const ratio = budget > 0 ? spent / budget : 0;
|
|
@@ -363,12 +254,12 @@ function budgetSnapshot(spentValue, budgetValue, nowMs = Date.now()) {
|
|
|
363
254
|
}
|
|
364
255
|
|
|
365
256
|
// src/client/charts.tsx
|
|
366
|
-
var
|
|
257
|
+
var import_react3 = require("react");
|
|
367
258
|
|
|
368
259
|
// src/client/chart-focus.ts
|
|
369
|
-
function nextChartFocus(
|
|
260
|
+
function nextChartFocus(current, count, key, horizontalStep = 1) {
|
|
370
261
|
if (count <= 0) return 0;
|
|
371
|
-
const index = Math.min(Math.max(0,
|
|
262
|
+
const index = Math.min(Math.max(0, current), count - 1);
|
|
372
263
|
if (key === "Home") return 0;
|
|
373
264
|
if (key === "End") return count - 1;
|
|
374
265
|
const step = key === "ArrowLeft" || key === "ArrowRight" ? Math.max(1, horizontalStep) : 1;
|
|
@@ -381,8 +272,8 @@ function lastPopulatedIndex(values) {
|
|
|
381
272
|
}
|
|
382
273
|
return Math.max(0, values.length - 1);
|
|
383
274
|
}
|
|
384
|
-
function nextPinnedIndex(
|
|
385
|
-
return
|
|
275
|
+
function nextPinnedIndex(current, tapped) {
|
|
276
|
+
return current === tapped ? null : tapped;
|
|
386
277
|
}
|
|
387
278
|
function isTapGesture(dx, dy, dt, moveThreshold = 10, timeThreshold = 500) {
|
|
388
279
|
return Math.hypot(dx, dy) <= moveThreshold && dt <= timeThreshold;
|
|
@@ -397,7 +288,7 @@ function maxOrOne(values) {
|
|
|
397
288
|
}
|
|
398
289
|
|
|
399
290
|
// src/client/i18n.tsx
|
|
400
|
-
var
|
|
291
|
+
var import_react2 = require("react");
|
|
401
292
|
|
|
402
293
|
// src/client/locales.ts
|
|
403
294
|
var NS = "dsh-usage-dashboard";
|
|
@@ -472,17 +363,24 @@ var zh = {
|
|
|
472
363
|
"export.jsonHint": "\u542B\u56FE\u8868\u4E0E\u6392\u884C",
|
|
473
364
|
"cache.empty": "\u6682\u65E0\u7F13\u5B58\u547D\u4E2D\u6570\u636E\u3002",
|
|
474
365
|
"cache.rate": "\u7F13\u5B58\u547D\u4E2D\u7387",
|
|
475
|
-
"cache.saved": "\
|
|
476
|
-
"cache.allMiss": "\u82E5\u5168\
|
|
366
|
+
"cache.saved": "\u8F83\u5168\u672A\u547D\u4E2D\u7701\u4E0B",
|
|
367
|
+
"cache.allMiss": "\u82E5\u5168\u672A\u547D\u4E2D",
|
|
477
368
|
"cache.aria": "\u7F13\u5B58\u547D\u4E2D {hit}%\uFF0C\u672A\u547D\u4E2D {miss}%",
|
|
478
369
|
"cache.hitLegend": "\u7F13\u5B58\u547D\u4E2D {tokens}",
|
|
479
370
|
"cache.missLegend": "\u672A\u547D\u4E2D {tokens}\uFF08\u6309\u672A\u547D\u4E2D\u4EF7\u8BA1\u8D39\uFF09",
|
|
371
|
+
"cache.basis": "\u5B9E\u4ED8 \xA5{cost}\uFF1B\u82E5\u5168\u90E8\u672A\u547D\u4E2D\u7EA6 \xA5{miss}\uFF0C\u4E24\u8005\u5DEE\u989D\u5373\u547D\u4E2D\u7F13\u5B58\u7701\u4E0B\u7684\u8D39\u7528\u3002",
|
|
480
372
|
"peak.empty": "\u6682\u65E0\u7528\u91CF\u3002",
|
|
481
373
|
"peak.aria": "\u9AD8\u5CF0\u65F6\u6BB5 {peak}%\uFF0C\u95F2\u65F6 {offPeak}%",
|
|
482
374
|
"peak.peakLegend": "\u9AD8\u5CF0 {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
483
375
|
"peak.offPeakLegend": "\u95F2\u65F6 {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
484
|
-
"peak.shift": "\
|
|
485
|
-
"peak.saving": "\
|
|
376
|
+
"peak.shift": "\u9AD8\u5CF0\u6539\u8D70\u95F2\u65F6\u53EF\u7701",
|
|
377
|
+
"peak.saving": "\xA5{amount}",
|
|
378
|
+
"peak.basis": "\u5168\u90E8\u6539\u8D70\u95F2\u65F6\u7EA6 \xA5{total}\uFF1B\u6B64\u5904\u6309\u5F53\u524D\u4EF7\u76EE\u4F30\u7B97\uFF0C\u4E0E\u300C\u6D88\u8017\u6982\u89C8 \xB7 \u7D2F\u8BA1\u300D\u7684\u5B9E\u9645\u82B1\u8D39\u53E3\u5F84\u4E0D\u540C\u3002",
|
|
379
|
+
"peak.clockTitle": "\u4ECA\u65E5\u4EF7\u683C\u65F6\u6BB5",
|
|
380
|
+
"peak.nowPeak": "\u73B0\u5728\u9AD8\u5CF0 \xB7 \u5168\u4EF7",
|
|
381
|
+
"peak.nowOff": "\u73B0\u5728\u95F2\u65F6 \xB7 \u534A\u4EF7",
|
|
382
|
+
"peak.clockAria": "24 \u5C0F\u65F6\u4EF7\u683C\u65F6\u6BB5\uFF0C{state}",
|
|
383
|
+
"peak.clockHint": "\u5DE5\u4F5C\u65E5 09\u201312\u300114\u201318 \u4E3A\u9AD8\u5CF0\uFF0C\u5176\u4F59\u65F6\u6BB5\u534A\u4EF7\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09",
|
|
486
384
|
"vision.empty": "{window}\u6CA1\u6709\u56FE\u7247\u8F93\u5165\u3002",
|
|
487
385
|
"vision.title": "\u56FE\u7247 / \u591A\u6A21\u6001\u7528\u91CF",
|
|
488
386
|
"vision.images": "\u56FE\u7247\u5F20\u6570",
|
|
@@ -555,7 +453,7 @@ var zh = {
|
|
|
555
453
|
"sync.first": "\u9996\u6B21\u540C\u6B65\u4E2D",
|
|
556
454
|
"sync.syncing": "\u540C\u6B65\u4E2D \xB7 \u5F53\u524D\u6570\u636E{age}",
|
|
557
455
|
"sync.fresh": "\u5DF2\u540C\u6B65 \xB7 {age}",
|
|
558
|
-
"sync.cached": "\u7F13\u5B58
|
|
456
|
+
"sync.cached": "\u672C\u5730\u7F13\u5B58 \xB7 {age}",
|
|
559
457
|
"sync.fallback": "\u7F13\u5B58\u56DE\u9000 \xB7 {age}",
|
|
560
458
|
"sync.error": "\u540C\u6B65\u5931\u8D25 \xB7 \u6682\u65E0\u53EF\u663E\u793A\u7684\u7528\u91CF",
|
|
561
459
|
"status.neverSynced": "\u8FD8\u6CA1\u6709\u6210\u529F\u540C\u6B65\u8FC7\u7528\u91CF\u6570\u636E",
|
|
@@ -620,18 +518,28 @@ var zh = {
|
|
|
620
518
|
"models.title": "\u6A21\u578B\u6210\u672C\u6392\u884C",
|
|
621
519
|
"sessions.title": "\u4F1A\u8BDD\u6210\u672C\u6392\u884C",
|
|
622
520
|
"links.title": "\u5FEB\u6377\u5165\u53E3",
|
|
521
|
+
"nav.label": "\u9875\u9762\u5927\u7EB2",
|
|
522
|
+
"nav.balance": "\u4F59\u989D",
|
|
523
|
+
"nav.session": "\u5F53\u524D\u4F1A\u8BDD",
|
|
524
|
+
"nav.overview": "\u6D88\u8017\u6982\u89C8",
|
|
525
|
+
"nav.trend": "\u7528\u91CF\u8D8B\u52BF",
|
|
526
|
+
"nav.heatmap": "\u7528\u91CF\u70ED\u529B\u56FE",
|
|
527
|
+
"nav.models": "\u6A21\u578B\u6210\u672C\u6392\u884C",
|
|
528
|
+
"nav.sessions": "\u4F1A\u8BDD\u6210\u672C\u6392\u884C",
|
|
529
|
+
"nav.peakcache": "\u9AD8\u5CF0 / \u7F13\u5B58",
|
|
530
|
+
"nav.settings": "\u8BBE\u7F6E",
|
|
623
531
|
"links.usage": "\u67E5\u770B\u989D\u5EA6 / \u7528\u91CF",
|
|
624
532
|
"links.apiKey": "\u751F\u6210 API Key",
|
|
625
533
|
"links.status": "\u670D\u52A1\u72B6\u6001",
|
|
626
534
|
"settings.title": "\u8BBE\u7F6E",
|
|
535
|
+
"settings.groupDisplay": "\u663E\u793A",
|
|
536
|
+
"settings.groupAlerts": "\u63D0\u9192",
|
|
537
|
+
"settings.groupLayout": "\u5E03\u5C40\u4E0E\u5165\u53E3",
|
|
627
538
|
"settings.widget": "\u5728\u5176\u4ED6\u9875\u9762\u663E\u793A\u53F3\u4E0B\u89D2\u60AC\u6D6E\u989D\u5EA6\u7A97\u53E3",
|
|
628
539
|
"settings.widgetHint": "\u989D\u5EA6\u9875\u81EA\u52A8\u9690\u85CF\uFF1B\u5176\u4ED6\u9875\u9762\u6309\u4E0B\u65B9\u9009\u62E9\u663E\u793A\u3002",
|
|
629
540
|
"settings.widgetTabs": "\u5C55\u793A\u6D6E\u7A97\u7684\u9875\u9762",
|
|
630
541
|
"settings.widgetTabsHint": "\u5217\u8868\u4F1A\u968F\u5DF2\u5B89\u88C5\u7684\u9875\u9762\u81EA\u52A8\u66F4\u65B0\u3002",
|
|
631
542
|
"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",
|
|
635
543
|
"settings.lowBalance": "\u4F59\u989D\u9884\u8B66\u7EBF",
|
|
636
544
|
"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",
|
|
637
545
|
"settings.lowBalanceOff": "\u5DF2\u5173\u95ED\u4F59\u989D\u9884\u8B66\u3002\u586B\u4E00\u4E2A\u5927\u4E8E 0 \u7684\u6570\u5F00\u542F\u3002",
|
|
@@ -648,32 +556,6 @@ var zh = {
|
|
|
648
556
|
"settings.tabDragAria": "\u62D6\u52A8\u300C\u989D\u5EA6\u300D\u8C03\u6574\u6807\u7B7E\u4F4D\u7F6E",
|
|
649
557
|
"settings.tabMoveEarlier": "\u524D\u79FB",
|
|
650
558
|
"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}",
|
|
677
559
|
"panel.caption": "\u8D26\u6237\u7EA7 \xB7 \u8DE8\u5168\u90E8\u4F1A\u8BDD",
|
|
678
560
|
"panel.openAria": "\u6253\u5F00\u989D\u5EA6\u9875",
|
|
679
561
|
"widget.updatedNow": "\u521A\u521A\u66F4\u65B0",
|
|
@@ -773,17 +655,24 @@ var en = {
|
|
|
773
655
|
"export.jsonHint": "Charts and rankings included",
|
|
774
656
|
"cache.empty": "No cache data yet.",
|
|
775
657
|
"cache.rate": "Cache hit rate",
|
|
776
|
-
"cache.saved": "
|
|
777
|
-
"cache.allMiss": "Cost
|
|
658
|
+
"cache.saved": "Saved vs. all-miss",
|
|
659
|
+
"cache.allMiss": "Cost if all miss",
|
|
778
660
|
"cache.aria": "{hit}% cache hits, {miss}% misses",
|
|
779
661
|
"cache.hitLegend": "Cache hits {tokens}",
|
|
780
662
|
"cache.missLegend": "Misses {tokens} (charged at miss rate)",
|
|
663
|
+
"cache.basis": "You paid \xA5{cost}; with no cache hits it would be about \xA5{miss}. The difference is what caching saved.",
|
|
781
664
|
"peak.empty": "No usage yet.",
|
|
782
665
|
"peak.aria": "{peak}% peak, {offPeak}% off-peak",
|
|
783
666
|
"peak.peakLegend": "Peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
784
667
|
"peak.offPeakLegend": "Off-peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
785
|
-
"peak.shift": "
|
|
786
|
-
"peak.saving": "
|
|
668
|
+
"peak.shift": "Saving if peak runs off-peak",
|
|
669
|
+
"peak.saving": "\xA5{amount}",
|
|
670
|
+
"peak.basis": "All off-peak would cost about \xA5{total}, estimated at current prices \u2014 a different basis from \u201CSpend overview \xB7 All time\u201D.",
|
|
671
|
+
"peak.clockTitle": "Today's price windows",
|
|
672
|
+
"peak.nowPeak": "Peak now \xB7 full price",
|
|
673
|
+
"peak.nowOff": "Off-peak now \xB7 half price",
|
|
674
|
+
"peak.clockAria": "24-hour price schedule, {state}",
|
|
675
|
+
"peak.clockHint": "Peak 09\u201312 & 14\u201318 on weekdays; half price otherwise (Beijing time)",
|
|
787
676
|
"vision.empty": "No image input in {window}.",
|
|
788
677
|
"vision.title": "Image / multimodal usage",
|
|
789
678
|
"vision.images": "Images",
|
|
@@ -856,7 +745,7 @@ var en = {
|
|
|
856
745
|
"sync.first": "Syncing for the first time",
|
|
857
746
|
"sync.syncing": "Syncing \xB7 current data {age}",
|
|
858
747
|
"sync.fresh": "Synced \xB7 {age}",
|
|
859
|
-
"sync.cached": "
|
|
748
|
+
"sync.cached": "Local cache \xB7 {age}",
|
|
860
749
|
"sync.fallback": "Using cache \xB7 {age}",
|
|
861
750
|
"sync.error": "Sync failed \xB7 no usage to display",
|
|
862
751
|
"status.neverSynced": "Usage has not synced successfully yet",
|
|
@@ -921,18 +810,28 @@ var en = {
|
|
|
921
810
|
"models.title": "Model cost ranking",
|
|
922
811
|
"sessions.title": "Session cost ranking",
|
|
923
812
|
"links.title": "Quick Links",
|
|
813
|
+
"nav.label": "On this page",
|
|
814
|
+
"nav.balance": "Balance",
|
|
815
|
+
"nav.session": "This session",
|
|
816
|
+
"nav.overview": "Spend overview",
|
|
817
|
+
"nav.trend": "Usage trend",
|
|
818
|
+
"nav.heatmap": "Usage heatmap",
|
|
819
|
+
"nav.models": "Model cost",
|
|
820
|
+
"nav.sessions": "Session cost",
|
|
821
|
+
"nav.peakcache": "Peak & cache",
|
|
822
|
+
"nav.settings": "Settings",
|
|
924
823
|
"links.usage": "View balance / usage",
|
|
925
824
|
"links.apiKey": "Create API key",
|
|
926
825
|
"links.status": "Service status",
|
|
927
826
|
"settings.title": "Settings",
|
|
827
|
+
"settings.groupDisplay": "Display",
|
|
828
|
+
"settings.groupAlerts": "Alerts",
|
|
829
|
+
"settings.groupLayout": "Layout & entry",
|
|
928
830
|
"settings.widget": "Show the floating balance widget on other views",
|
|
929
831
|
"settings.widgetHint": "Hidden on Usage; shown on the views selected below.",
|
|
930
832
|
"settings.widgetTabs": "Show widget on",
|
|
931
833
|
"settings.widgetTabsHint": "Updates automatically as views are installed.",
|
|
932
834
|
"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",
|
|
936
835
|
"settings.lowBalance": "Balance warning",
|
|
937
836
|
"settings.lowBalanceOn": "This page and the widget turn amber below {amount} {currency}. Enter 0 to disable.",
|
|
938
837
|
"settings.lowBalanceOff": "Balance warnings are off. Enter an amount above 0 to enable them.",
|
|
@@ -949,32 +848,6 @@ var en = {
|
|
|
949
848
|
"settings.tabDragAria": "Drag Usage to change its tab position",
|
|
950
849
|
"settings.tabMoveEarlier": "Move earlier",
|
|
951
850
|
"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}",
|
|
978
851
|
"panel.caption": "Account-wide \xB7 across all sessions",
|
|
979
852
|
"panel.openAria": "Open Usage",
|
|
980
853
|
"widget.updatedNow": "Updated just now",
|
|
@@ -1018,12 +891,12 @@ var fallbackT = (key, params) => {
|
|
|
1018
891
|
if (params === void 0) return template;
|
|
1019
892
|
return template.replace(/\{(\w+)\}/g, (match, name2) => name2 in params ? String(params[name2]) : match);
|
|
1020
893
|
};
|
|
1021
|
-
var TranslationContext = (0,
|
|
894
|
+
var TranslationContext = (0, import_react2.createContext)(fallbackT);
|
|
1022
895
|
function LocaleProvider(props) {
|
|
1023
896
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TranslationContext.Provider, { value: props.t, children: props.children });
|
|
1024
897
|
}
|
|
1025
898
|
function useI18n() {
|
|
1026
|
-
const t = (0,
|
|
899
|
+
const t = (0, import_react2.useContext)(TranslationContext);
|
|
1027
900
|
return { t, locale: t("meta.locale") === "en" ? "en" : "zh" };
|
|
1028
901
|
}
|
|
1029
902
|
function localizeApiError(message, t, fallback) {
|
|
@@ -1054,11 +927,11 @@ function localizeApiError(message, t, fallback) {
|
|
|
1054
927
|
// src/client/charts.tsx
|
|
1055
928
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1056
929
|
function useChartTip() {
|
|
1057
|
-
const wrapRef = (0,
|
|
1058
|
-
const [tip, setTip] = (0,
|
|
1059
|
-
const [pinned, setPinned] = (0,
|
|
1060
|
-
const pinnedRef = (0,
|
|
1061
|
-
const touchStart = (0,
|
|
930
|
+
const wrapRef = (0, import_react3.useRef)(null);
|
|
931
|
+
const [tip, setTip] = (0, import_react3.useState)(null);
|
|
932
|
+
const [pinned, setPinned] = (0, import_react3.useState)(null);
|
|
933
|
+
const pinnedRef = (0, import_react3.useRef)(null);
|
|
934
|
+
const touchStart = (0, import_react3.useRef)(null);
|
|
1062
935
|
const setPin = (next) => {
|
|
1063
936
|
pinnedRef.current = next;
|
|
1064
937
|
setPinned(next);
|
|
@@ -1097,7 +970,7 @@ function useChartTip() {
|
|
|
1097
970
|
if (next === null) setTip(null);
|
|
1098
971
|
else showAt(e.currentTarget, text, e.clientX, e.clientY);
|
|
1099
972
|
};
|
|
1100
|
-
(0,
|
|
973
|
+
(0, import_react3.useEffect)(() => {
|
|
1101
974
|
if (pinned === null) return void 0;
|
|
1102
975
|
const onDocPointerDown = (e) => {
|
|
1103
976
|
const wrap = wrapRef.current;
|
|
@@ -1111,9 +984,9 @@ function useChartTip() {
|
|
|
1111
984
|
return { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd };
|
|
1112
985
|
}
|
|
1113
986
|
function useRovingChartPoints(count, initial, horizontalStep = 1) {
|
|
1114
|
-
const refs = (0,
|
|
1115
|
-
const [active, setActive] = (0,
|
|
1116
|
-
(0,
|
|
987
|
+
const refs = (0, import_react3.useRef)([]);
|
|
988
|
+
const [active, setActive] = (0, import_react3.useState)(initial);
|
|
989
|
+
(0, import_react3.useEffect)(() => setActive(Math.min(Math.max(0, initial), Math.max(0, count - 1))), [count, initial]);
|
|
1117
990
|
const onKeyDown = (event, index) => {
|
|
1118
991
|
if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) return;
|
|
1119
992
|
event.preventDefault();
|
|
@@ -1137,11 +1010,12 @@ function ChartTip(props) {
|
|
|
1137
1010
|
var MODEL_COLORS = ["#4176e6", "#2da44e", "#e16f24", "#8250df", "#bf8700", "#cf222e", "#1f883d", "#0969da"];
|
|
1138
1011
|
function Bars(props) {
|
|
1139
1012
|
const { data, height = 120, labelEvery = 1, minWidth } = props;
|
|
1013
|
+
const { locale } = useI18n();
|
|
1140
1014
|
const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip();
|
|
1141
|
-
const scrollRef = (0,
|
|
1015
|
+
const scrollRef = (0, import_react3.useRef)(null);
|
|
1142
1016
|
const initialFocus = lastPopulatedIndex(data.map((datum) => datum.value));
|
|
1143
1017
|
const roving = useRovingChartPoints(data.length, initialFocus);
|
|
1144
|
-
(0,
|
|
1018
|
+
(0, import_react3.useEffect)(() => {
|
|
1145
1019
|
const scroll = scrollRef.current;
|
|
1146
1020
|
if (scroll !== null && minWidth !== void 0) scroll.scrollLeft = scroll.scrollWidth;
|
|
1147
1021
|
}, [data.length, minWidth]);
|
|
@@ -1149,87 +1023,102 @@ function Bars(props) {
|
|
|
1149
1023
|
for (const datum of data) if (datum.value > max) max = datum.value;
|
|
1150
1024
|
const scale = max > 0 ? max : 1;
|
|
1151
1025
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-chart-wrap", ref: wrapRef, onPointerLeave: hide, children: [
|
|
1152
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
"
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1026
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-plot", children: [
|
|
1027
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-yaxis", style: { height: `${height}px` }, "aria-hidden": "true", children: [
|
|
1028
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: max > 0 ? fmtCompact(max, locale) : "" }),
|
|
1029
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "0" })
|
|
1030
|
+
] }),
|
|
1031
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bars-scroll", ref: scrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bars", style: { height: `${height + 18}px`, minWidth: minWidth === void 0 ? void 0 : `${minWidth}px` }, children: data.map((datum, i) => {
|
|
1032
|
+
const h = Math.max(1, Math.round(datum.value / scale * height));
|
|
1033
|
+
const text = datum.title ?? `${datum.label}: ${datum.value.toLocaleString()}`;
|
|
1034
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1035
|
+
"div",
|
|
1036
|
+
{
|
|
1037
|
+
className: `dq-bar-col${pinned === i ? " dq-bar-col--pinned" : ""}`,
|
|
1038
|
+
role: "img",
|
|
1039
|
+
"aria-label": text,
|
|
1040
|
+
"aria-keyshortcuts": "ArrowLeft ArrowRight ArrowUp ArrowDown Home End",
|
|
1041
|
+
tabIndex: i === roving.active ? 0 : -1,
|
|
1042
|
+
ref: (element) => {
|
|
1043
|
+
roving.refs.current[i] = element;
|
|
1044
|
+
},
|
|
1045
|
+
onPointerMove: (e) => show(e, text),
|
|
1046
|
+
onPointerDown: onPointStart,
|
|
1047
|
+
onPointerUp: (e) => onPointEnd(e, i, text),
|
|
1048
|
+
onFocus: (e) => showFocused(e, text),
|
|
1049
|
+
onBlur: hide,
|
|
1050
|
+
onKeyDown: (e) => roving.onKeyDown(e, i),
|
|
1051
|
+
children: [
|
|
1052
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar", style: { height: `${h}px` } }),
|
|
1053
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-label", style: { visibility: i % labelEvery === 0 ? "visible" : "hidden" }, children: datum.label })
|
|
1054
|
+
]
|
|
1165
1055
|
},
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
onBlur: hide,
|
|
1171
|
-
onKeyDown: (e) => roving.onKeyDown(e, i),
|
|
1172
|
-
children: [
|
|
1173
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar", style: { height: `${h}px` } }),
|
|
1174
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-label", style: { visibility: i % labelEvery === 0 ? "visible" : "hidden" }, children: datum.label })
|
|
1175
|
-
]
|
|
1176
|
-
},
|
|
1177
|
-
i
|
|
1178
|
-
);
|
|
1179
|
-
}) }) }),
|
|
1056
|
+
i
|
|
1057
|
+
);
|
|
1058
|
+
}) }) })
|
|
1059
|
+
] }),
|
|
1180
1060
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChartTip, { tip })
|
|
1181
1061
|
] });
|
|
1182
1062
|
}
|
|
1183
1063
|
function GroupedBars(props) {
|
|
1184
1064
|
const { series, height = 120, labelEvery = 1, minWidth } = props;
|
|
1065
|
+
const { locale } = useI18n();
|
|
1185
1066
|
const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip();
|
|
1186
|
-
const scrollRef = (0,
|
|
1067
|
+
const scrollRef = (0, import_react3.useRef)(null);
|
|
1187
1068
|
const count = series.length > 0 ? series[0].bars.length : 0;
|
|
1188
1069
|
const pointValues = Array.from({ length: count }, (_, i) => series.map((s) => s.bars[i]?.value ?? 0)).flat();
|
|
1189
1070
|
const roving = useRovingChartPoints(pointValues.length, lastPopulatedIndex(pointValues));
|
|
1190
|
-
(0,
|
|
1071
|
+
(0, import_react3.useEffect)(() => {
|
|
1191
1072
|
const scroll = scrollRef.current;
|
|
1192
1073
|
if (scroll !== null && minWidth !== void 0) scroll.scrollLeft = scroll.scrollWidth;
|
|
1193
1074
|
}, [count, minWidth]);
|
|
1194
|
-
const
|
|
1075
|
+
const totals = stackedTotals(series, count);
|
|
1076
|
+
const scale = maxOrOne(totals);
|
|
1077
|
+
const axisMax = totals.reduce((m, v) => Math.max(m, v), 0);
|
|
1195
1078
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-chart-wrap", ref: wrapRef, onPointerLeave: hide, children: [
|
|
1196
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1197
|
-
"div",
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
"
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1079
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-plot", children: [
|
|
1080
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-yaxis", style: { height: `${height}px` }, "aria-hidden": "true", children: [
|
|
1081
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: axisMax > 0 ? fmtCompact(axisMax, locale) : "" }),
|
|
1082
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "0" })
|
|
1083
|
+
] }),
|
|
1084
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bars-scroll", ref: scrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bars", style: { height: `${height + 18}px`, minWidth: minWidth === void 0 ? void 0 : `${minWidth}px` }, children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1085
|
+
"div",
|
|
1086
|
+
{
|
|
1087
|
+
className: "dq-bar-col",
|
|
1088
|
+
children: [
|
|
1089
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-group", children: series.map((s, seriesIndex) => {
|
|
1090
|
+
const datum = s.bars[i];
|
|
1091
|
+
const h = Math.max(1, Math.round(datum.value / scale * height));
|
|
1092
|
+
const text = datum.title ?? `${s.key}: ${datum.value.toLocaleString()}`;
|
|
1093
|
+
const pointIndex = i * series.length + seriesIndex;
|
|
1094
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1095
|
+
"div",
|
|
1096
|
+
{
|
|
1097
|
+
className: `dq-bar${pinned === pointIndex ? " dq-bar--pinned" : ""}`,
|
|
1098
|
+
style: { height: `${h}px`, background: s.color },
|
|
1099
|
+
role: "img",
|
|
1100
|
+
"aria-label": text,
|
|
1101
|
+
"aria-keyshortcuts": "ArrowLeft ArrowRight ArrowUp ArrowDown Home End",
|
|
1102
|
+
tabIndex: pointIndex === roving.active ? 0 : -1,
|
|
1103
|
+
ref: (element) => {
|
|
1104
|
+
roving.refs.current[pointIndex] = element;
|
|
1105
|
+
},
|
|
1106
|
+
onPointerMove: (e) => show(e, text),
|
|
1107
|
+
onPointerDown: onPointStart,
|
|
1108
|
+
onPointerUp: (e) => onPointEnd(e, pointIndex, text),
|
|
1109
|
+
onFocus: (e) => showFocused(e, text),
|
|
1110
|
+
onBlur: hide,
|
|
1111
|
+
onKeyDown: (e) => roving.onKeyDown(e, pointIndex)
|
|
1217
1112
|
},
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
}) }),
|
|
1228
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-label", style: { visibility: i % labelEvery === 0 ? "visible" : "hidden" }, children: series[0].bars[i].label })
|
|
1229
|
-
]
|
|
1230
|
-
},
|
|
1231
|
-
i
|
|
1232
|
-
)) }) }),
|
|
1113
|
+
s.key
|
|
1114
|
+
);
|
|
1115
|
+
}) }),
|
|
1116
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-label", style: { visibility: i % labelEvery === 0 ? "visible" : "hidden" }, children: series[0].bars[i].label })
|
|
1117
|
+
]
|
|
1118
|
+
},
|
|
1119
|
+
i
|
|
1120
|
+
)) }) })
|
|
1121
|
+
] }),
|
|
1233
1122
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChartTip, { tip })
|
|
1234
1123
|
] });
|
|
1235
1124
|
}
|
|
@@ -1389,9 +1278,9 @@ function getActiveConversationViewId(frame, viewIds) {
|
|
|
1389
1278
|
}
|
|
1390
1279
|
|
|
1391
1280
|
// src/client/export.ts
|
|
1392
|
-
var
|
|
1281
|
+
var BEIJING_OFFSET_MS2 = 8 * 60 * 60 * 1e3;
|
|
1393
1282
|
function exportDateStamp(nowMs = Date.now()) {
|
|
1394
|
-
const date = new Date(nowMs +
|
|
1283
|
+
const date = new Date(nowMs + BEIJING_OFFSET_MS2);
|
|
1395
1284
|
return [
|
|
1396
1285
|
date.getUTCFullYear(),
|
|
1397
1286
|
String(date.getUTCMonth() + 1).padStart(2, "0"),
|
|
@@ -1507,19 +1396,19 @@ var isBoolean = (value) => typeof value === "boolean";
|
|
|
1507
1396
|
|
|
1508
1397
|
// src/client/store.ts
|
|
1509
1398
|
function createStore(key, isValid, fallback) {
|
|
1510
|
-
let
|
|
1511
|
-
const
|
|
1399
|
+
let current = loadPref(key, isValid, fallback);
|
|
1400
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1512
1401
|
return {
|
|
1513
|
-
get: () =>
|
|
1402
|
+
get: () => current,
|
|
1514
1403
|
set: (value) => {
|
|
1515
|
-
|
|
1404
|
+
current = value;
|
|
1516
1405
|
savePref(key, value);
|
|
1517
|
-
for (const listener of [...
|
|
1406
|
+
for (const listener of [...listeners]) listener();
|
|
1518
1407
|
},
|
|
1519
1408
|
subscribe: (fn) => {
|
|
1520
|
-
|
|
1409
|
+
listeners.add(fn);
|
|
1521
1410
|
return () => {
|
|
1522
|
-
|
|
1411
|
+
listeners.delete(fn);
|
|
1523
1412
|
};
|
|
1524
1413
|
}
|
|
1525
1414
|
};
|
|
@@ -1582,16 +1471,6 @@ function needsTopUp(balance, warningLine) {
|
|
|
1582
1471
|
// src/client/dashboard.tsx
|
|
1583
1472
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1584
1473
|
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
|
-
}
|
|
1595
1474
|
function Skel(props) {
|
|
1596
1475
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-skel", style: { width: `${props.w}px`, height: `${props.h}px` } });
|
|
1597
1476
|
}
|
|
@@ -1641,18 +1520,19 @@ function HeatmapSkeleton() {
|
|
|
1641
1520
|
}
|
|
1642
1521
|
function Delta(props) {
|
|
1643
1522
|
const { t } = useI18n();
|
|
1644
|
-
const { current
|
|
1523
|
+
const { current, previous, label } = props;
|
|
1645
1524
|
if (previous <= 0) {
|
|
1646
|
-
if (
|
|
1525
|
+
if (current <= 0) return null;
|
|
1647
1526
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta dq-delta--new", title: t("delta.noBaselineTitle", { label }), children: t("delta.noBaseline") });
|
|
1648
1527
|
}
|
|
1649
|
-
const percent = Math.round((
|
|
1528
|
+
const percent = Math.round((current - previous) / previous * 100);
|
|
1650
1529
|
const title = `${label} \xA5${fmt(previous)}`;
|
|
1651
1530
|
if (percent === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta dq-delta--flat", title, children: t("delta.flat") });
|
|
1652
1531
|
const up = percent > 0;
|
|
1532
|
+
const magnitude = Math.abs(percent) >= 1e3 ? "999+" : String(Math.abs(percent));
|
|
1653
1533
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: `dq-delta ${up ? "dq-delta--up" : "dq-delta--down"}`, title, children: [
|
|
1654
1534
|
up ? "\u2191" : "\u2193",
|
|
1655
|
-
|
|
1535
|
+
magnitude,
|
|
1656
1536
|
"% ",
|
|
1657
1537
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta-label", children: label })
|
|
1658
1538
|
] });
|
|
@@ -1734,7 +1614,7 @@ function PricingNote(props) {
|
|
|
1734
1614
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.inputCacheMiss") }),
|
|
1735
1615
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.output") })
|
|
1736
1616
|
] }) }),
|
|
1737
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: pricing.tiers.map((tier) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1617
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: pricing.tiers.map((tier) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react4.Fragment, { children: [
|
|
1738
1618
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { children: [
|
|
1739
1619
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { rowSpan: 2, children: tier.model }),
|
|
1740
1620
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: t("pricing.peak") }),
|
|
@@ -1810,6 +1690,137 @@ function CardTitle(props) {
|
|
|
1810
1690
|
props.caption !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-card-caption", children: props.caption })
|
|
1811
1691
|
] });
|
|
1812
1692
|
}
|
|
1693
|
+
function SectionNav(props) {
|
|
1694
|
+
const { rootRef, label, sections } = props;
|
|
1695
|
+
const [targets, setTargets] = (0, import_react4.useState)([]);
|
|
1696
|
+
const [active, setActive] = (0, import_react4.useState)(0);
|
|
1697
|
+
const [pos, setPos] = (0, import_react4.useState)({ right: 0, visible: false });
|
|
1698
|
+
const signature = sections.map((section) => `${section.key}:${section.label}`).join("|");
|
|
1699
|
+
(0, import_react4.useEffect)(() => {
|
|
1700
|
+
const root = rootRef.current;
|
|
1701
|
+
if (root === null) return;
|
|
1702
|
+
const nodes = Array.from(root.querySelectorAll(":scope > .dq-card, :scope > .dq-card-pair"));
|
|
1703
|
+
setTargets(nodes.map((el, i) => ({ el, label: sections[i]?.label ?? "" })).filter((item) => item.label !== ""));
|
|
1704
|
+
}, [rootRef, signature]);
|
|
1705
|
+
(0, import_react4.useEffect)(() => {
|
|
1706
|
+
const root = rootRef.current;
|
|
1707
|
+
if (root === null) return;
|
|
1708
|
+
const scrollport = root.closest("[data-conversation-scroll]");
|
|
1709
|
+
const measure = () => {
|
|
1710
|
+
const rect = root.getBoundingClientRect();
|
|
1711
|
+
setPos({ right: window.innerWidth - rect.left + 12, visible: window.innerWidth >= 1200 && rect.left >= 170 });
|
|
1712
|
+
};
|
|
1713
|
+
measure();
|
|
1714
|
+
const scroller = scrollport ?? window;
|
|
1715
|
+
scroller.addEventListener("scroll", measure, { passive: true });
|
|
1716
|
+
window.addEventListener("resize", measure);
|
|
1717
|
+
return () => {
|
|
1718
|
+
scroller.removeEventListener("scroll", measure);
|
|
1719
|
+
window.removeEventListener("resize", measure);
|
|
1720
|
+
};
|
|
1721
|
+
}, [rootRef]);
|
|
1722
|
+
(0, import_react4.useEffect)(() => {
|
|
1723
|
+
if (targets.length === 0) return;
|
|
1724
|
+
const root = rootRef.current;
|
|
1725
|
+
if (root === null) return;
|
|
1726
|
+
const scrollport = root.closest("[data-conversation-scroll]");
|
|
1727
|
+
const onScroll = () => {
|
|
1728
|
+
const base = scrollport === null ? 0 : scrollport.getBoundingClientRect().top;
|
|
1729
|
+
let current = 0;
|
|
1730
|
+
targets.forEach((target, i) => {
|
|
1731
|
+
if (target.el.getBoundingClientRect().top - base <= 120) current = i;
|
|
1732
|
+
});
|
|
1733
|
+
setActive(current);
|
|
1734
|
+
};
|
|
1735
|
+
onScroll();
|
|
1736
|
+
const scroller = scrollport ?? window;
|
|
1737
|
+
scroller.addEventListener("scroll", onScroll, { passive: true });
|
|
1738
|
+
return () => scroller.removeEventListener("scroll", onScroll);
|
|
1739
|
+
}, [targets, rootRef]);
|
|
1740
|
+
if (targets.length < 3) return null;
|
|
1741
|
+
const go = (el) => {
|
|
1742
|
+
const root = rootRef.current;
|
|
1743
|
+
const scrollport = root?.closest("[data-conversation-scroll]");
|
|
1744
|
+
const reduce = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1745
|
+
const behavior = reduce ? "auto" : "smooth";
|
|
1746
|
+
if (scrollport !== null) {
|
|
1747
|
+
const base = scrollport.getBoundingClientRect().top;
|
|
1748
|
+
scrollport.scrollTo({ top: scrollport.scrollTop + el.getBoundingClientRect().top - base - 12, behavior });
|
|
1749
|
+
} else {
|
|
1750
|
+
window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 12, behavior });
|
|
1751
|
+
}
|
|
1752
|
+
};
|
|
1753
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("nav", { className: `dq-sectionnav${pos.visible ? "" : " dq-sectionnav--hidden"}`, style: { right: `${pos.right}px` }, "aria-label": label, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ol", { children: targets.map((target, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1754
|
+
"button",
|
|
1755
|
+
{
|
|
1756
|
+
type: "button",
|
|
1757
|
+
className: `dq-sectionnav-dot${i === active ? " dq-sectionnav-dot--active" : ""}`,
|
|
1758
|
+
"aria-current": i === active ? "true" : void 0,
|
|
1759
|
+
onClick: () => go(target.el),
|
|
1760
|
+
children: [
|
|
1761
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-sectionnav-label", children: target.label }),
|
|
1762
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-sectionnav-pip", "aria-hidden": "true" })
|
|
1763
|
+
]
|
|
1764
|
+
}
|
|
1765
|
+
) }, `${target.label}-${String(i)}`)) }) });
|
|
1766
|
+
}
|
|
1767
|
+
function Collapsible(props) {
|
|
1768
|
+
const ref = (0, import_react4.useRef)(null);
|
|
1769
|
+
const animationRef = (0, import_react4.useRef)(null);
|
|
1770
|
+
const open = props.open;
|
|
1771
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
1772
|
+
const el = ref.current;
|
|
1773
|
+
if (el === null) return;
|
|
1774
|
+
animationRef.current?.cancel();
|
|
1775
|
+
animationRef.current = null;
|
|
1776
|
+
const reduce = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1777
|
+
const canAnimate = typeof el.animate === "function" && !reduce;
|
|
1778
|
+
const reset = () => {
|
|
1779
|
+
el.style.height = "";
|
|
1780
|
+
el.style.overflow = "";
|
|
1781
|
+
el.style.opacity = "";
|
|
1782
|
+
};
|
|
1783
|
+
if (open) {
|
|
1784
|
+
if (!canAnimate) {
|
|
1785
|
+
el.style.display = "";
|
|
1786
|
+
reset();
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
el.style.display = "block";
|
|
1790
|
+
el.style.overflow = "hidden";
|
|
1791
|
+
el.style.height = "0px";
|
|
1792
|
+
el.style.opacity = "0";
|
|
1793
|
+
const height2 = el.scrollHeight;
|
|
1794
|
+
const animation2 = el.animate(
|
|
1795
|
+
[{ height: "0px", opacity: 0 }, { height: `${height2}px`, opacity: 1 }],
|
|
1796
|
+
{ duration: 300, easing: "cubic-bezier(0.16, 1, 0.3, 1)" }
|
|
1797
|
+
);
|
|
1798
|
+
animationRef.current = animation2;
|
|
1799
|
+
animation2.addEventListener("finish", reset);
|
|
1800
|
+
return () => animation2.cancel();
|
|
1801
|
+
}
|
|
1802
|
+
if (el.style.display === "none") return;
|
|
1803
|
+
if (!canAnimate) {
|
|
1804
|
+
reset();
|
|
1805
|
+
el.style.display = "none";
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
const height = el.getBoundingClientRect().height || el.scrollHeight;
|
|
1809
|
+
el.style.overflow = "hidden";
|
|
1810
|
+
el.style.height = `${height}px`;
|
|
1811
|
+
const animation = el.animate(
|
|
1812
|
+
[{ height: `${height}px`, opacity: 1 }, { height: "0px", opacity: 0 }],
|
|
1813
|
+
{ duration: 220, easing: "cubic-bezier(0.4, 0, 1, 1)" }
|
|
1814
|
+
);
|
|
1815
|
+
animationRef.current = animation;
|
|
1816
|
+
animation.addEventListener("finish", () => {
|
|
1817
|
+
reset();
|
|
1818
|
+
el.style.display = "none";
|
|
1819
|
+
});
|
|
1820
|
+
return () => animation.cancel();
|
|
1821
|
+
}, [open]);
|
|
1822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref, children: props.children });
|
|
1823
|
+
}
|
|
1813
1824
|
function Stat(props) {
|
|
1814
1825
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
1815
1826
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: props.label }),
|
|
@@ -1821,13 +1832,13 @@ function Link(props) {
|
|
|
1821
1832
|
}
|
|
1822
1833
|
function ExportMenu(props) {
|
|
1823
1834
|
const { t } = useI18n();
|
|
1824
|
-
const [open, setOpen] = (0,
|
|
1825
|
-
const [done, setDone] = (0,
|
|
1826
|
-
const rootRef = (0,
|
|
1827
|
-
const triggerRef = (0,
|
|
1828
|
-
const menuRef = (0,
|
|
1829
|
-
const doneTimerRef = (0,
|
|
1830
|
-
(0,
|
|
1835
|
+
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
1836
|
+
const [done, setDone] = (0, import_react4.useState)(false);
|
|
1837
|
+
const rootRef = (0, import_react4.useRef)(null);
|
|
1838
|
+
const triggerRef = (0, import_react4.useRef)(null);
|
|
1839
|
+
const menuRef = (0, import_react4.useRef)(null);
|
|
1840
|
+
const doneTimerRef = (0, import_react4.useRef)(null);
|
|
1841
|
+
(0, import_react4.useEffect)(() => {
|
|
1831
1842
|
if (!open) return;
|
|
1832
1843
|
const onDown = (event) => {
|
|
1833
1844
|
if (rootRef.current !== null && !rootRef.current.contains(event.target)) setOpen(false);
|
|
@@ -1845,7 +1856,7 @@ function ExportMenu(props) {
|
|
|
1845
1856
|
document.removeEventListener("keydown", onKey);
|
|
1846
1857
|
};
|
|
1847
1858
|
}, [open]);
|
|
1848
|
-
(0,
|
|
1859
|
+
(0, import_react4.useEffect)(() => () => {
|
|
1849
1860
|
if (doneTimerRef.current !== null) window.clearTimeout(doneTimerRef.current);
|
|
1850
1861
|
}, []);
|
|
1851
1862
|
const finish = () => {
|
|
@@ -2015,7 +2026,8 @@ function CacheCard(props) {
|
|
|
2015
2026
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-legend-swatch dq-legend-swatch--miss" }),
|
|
2016
2027
|
t("cache.missLegend", { tokens: fmtCompact(totals.input, locale) })
|
|
2017
2028
|
] })
|
|
2018
|
-
] })
|
|
2029
|
+
] }),
|
|
2030
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-cache-foot", children: t("cache.basis", { cost: fmt(totals.cost), miss: fmt(wouldHaveCost) }) })
|
|
2019
2031
|
] });
|
|
2020
2032
|
}
|
|
2021
2033
|
function VisionCard(props) {
|
|
@@ -2058,11 +2070,44 @@ function VisionCard(props) {
|
|
|
2058
2070
|
function PeakCard(props) {
|
|
2059
2071
|
const { t, locale } = useI18n();
|
|
2060
2072
|
const { split, pricing } = props;
|
|
2073
|
+
const [nowMs, setNowMs] = (0, import_react4.useState)(() => Date.now());
|
|
2074
|
+
(0, import_react4.useEffect)(() => {
|
|
2075
|
+
const id = setInterval(() => setNowMs(Date.now()), 6e4);
|
|
2076
|
+
return () => clearInterval(id);
|
|
2077
|
+
}, []);
|
|
2061
2078
|
const total = split.peak.total + split.offPeak.total;
|
|
2062
2079
|
if (total === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("peak.empty") });
|
|
2063
2080
|
const peakShare = split.peak.total / total;
|
|
2064
2081
|
const shiftSaving = split.peakEraCost - split.offPeakEraCost;
|
|
2082
|
+
const beijing = new Date(nowMs + 8 * 36e5);
|
|
2083
|
+
const hour = beijing.getUTCHours();
|
|
2084
|
+
const minute = beijing.getUTCMinutes();
|
|
2085
|
+
const weekday = beijing.getUTCDay();
|
|
2086
|
+
const weekend = weekday === 0 || weekday === 6;
|
|
2087
|
+
const windows = pricing.peakWindows.map((w) => w.match(/(\d{1,2}):\d{2}\D+(\d{1,2}):\d{2}/)).filter((m) => m !== null).map((m) => [Number(m[1]), Number(m[2])]);
|
|
2088
|
+
const isPeakNow = !weekend && windows.some(([from, to]) => hour >= from && hour < to);
|
|
2089
|
+
const nowPercent = (hour + minute / 60) / 24 * 100;
|
|
2090
|
+
const clockCells = Array.from({ length: 24 }, (_, h) => !weekend && windows.some(([from, to]) => h >= from && h < to));
|
|
2091
|
+
const nowState = isPeakNow ? t("peak.nowPeak") : t("peak.nowOff");
|
|
2065
2092
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-peak", children: [
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock", children: [
|
|
2094
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock-head", children: [
|
|
2095
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-clock-title", children: t("peak.clockTitle") }),
|
|
2096
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-clock-status ${isPeakNow ? "dq-clock-status--peak" : "dq-clock-status--off"}`, children: nowState })
|
|
2097
|
+
] }),
|
|
2098
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock-track", role: "img", "aria-label": t("peak.clockAria", { state: nowState }), children: [
|
|
2099
|
+
clockCells.map((peak, h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-clock-cell${peak ? " dq-clock-cell--peak" : ""}` }, h)),
|
|
2100
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-clock-now", style: { left: `${nowPercent}%` }, "aria-hidden": "true" })
|
|
2101
|
+
] }),
|
|
2102
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock-scale", "aria-hidden": "true", children: [
|
|
2103
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "0" }),
|
|
2104
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "6" }),
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "12" }),
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "18" }),
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "24" })
|
|
2108
|
+
] }),
|
|
2109
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-clock-hint", children: t("peak.clockHint") })
|
|
2110
|
+
] }),
|
|
2066
2111
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2067
2112
|
"div",
|
|
2068
2113
|
{
|
|
@@ -2092,12 +2137,9 @@ function PeakCard(props) {
|
|
|
2092
2137
|
] }),
|
|
2093
2138
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-figures", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
2094
2139
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("peak.shift") }),
|
|
2095
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-peak-delta dq-peak-delta--save", children: t("peak.saving", { amount: fmt(shiftSaving) }) })
|
|
2099
|
-
] })
|
|
2100
|
-
] }) })
|
|
2140
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-value dq-stat-value--ok", children: t("peak.saving", { amount: fmt(shiftSaving) }) })
|
|
2141
|
+
] }) }),
|
|
2142
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-peak-foot", children: t("peak.basis", { total: fmt(split.offPeakEraCost) }) })
|
|
2101
2143
|
] });
|
|
2102
2144
|
}
|
|
2103
2145
|
function agoText(ms, t) {
|
|
@@ -2119,8 +2161,8 @@ function SessionDetail(props) {
|
|
|
2119
2161
|
function SessionRanking(props) {
|
|
2120
2162
|
const { t, locale } = useI18n();
|
|
2121
2163
|
const { sessions, count, totalCost } = props;
|
|
2122
|
-
const [details, setDetails] = (0,
|
|
2123
|
-
const startedRef = (0,
|
|
2164
|
+
const [details, setDetails] = (0, import_react4.useState)({});
|
|
2165
|
+
const startedRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
|
|
2124
2166
|
const loadDetail = (id) => {
|
|
2125
2167
|
if (startedRef.current.has(id)) return;
|
|
2126
2168
|
startedRef.current.add(id);
|
|
@@ -2221,11 +2263,11 @@ function ModelRanking(props) {
|
|
|
2221
2263
|
function ModelPicker(props) {
|
|
2222
2264
|
const { t, locale } = useI18n();
|
|
2223
2265
|
const { models } = props;
|
|
2224
|
-
const [open, setOpen] = (0,
|
|
2225
|
-
const rootRef = (0,
|
|
2226
|
-
const triggerRef = (0,
|
|
2227
|
-
const menuRef = (0,
|
|
2228
|
-
(0,
|
|
2266
|
+
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
2267
|
+
const rootRef = (0, import_react4.useRef)(null);
|
|
2268
|
+
const triggerRef = (0, import_react4.useRef)(null);
|
|
2269
|
+
const menuRef = (0, import_react4.useRef)(null);
|
|
2270
|
+
(0, import_react4.useEffect)(() => {
|
|
2229
2271
|
if (!open) return;
|
|
2230
2272
|
const onDown = (e) => {
|
|
2231
2273
|
if (rootRef.current !== null && !rootRef.current.contains(e.target)) setOpen(false);
|
|
@@ -2327,55 +2369,46 @@ function BalanceDashboard(props) {
|
|
|
2327
2369
|
const cachedBalance = getCachedBalance();
|
|
2328
2370
|
const cachedUsage = getCachedUsage();
|
|
2329
2371
|
const cachedUsageAt = getCachedUsageAt();
|
|
2330
|
-
const [balance, setBalance] = (0,
|
|
2331
|
-
const [usage, setUsage] = (0,
|
|
2332
|
-
const [error, setError] = (0,
|
|
2333
|
-
const [notice, setNotice] = (0,
|
|
2334
|
-
const [loadingBalance, setLoadingBalance] = (0,
|
|
2335
|
-
const [loadingUsage, setLoadingUsage] = (0,
|
|
2336
|
-
const [refreshing, setRefreshing] = (0,
|
|
2337
|
-
const [usageUpdatedAt, setUsageUpdatedAt] = (0,
|
|
2338
|
-
const [syncState, setSyncState] = (0,
|
|
2339
|
-
const [freshnessNow, setFreshnessNow] = (0,
|
|
2340
|
-
const
|
|
2341
|
-
const
|
|
2342
|
-
const [
|
|
2343
|
-
const [
|
|
2344
|
-
(0,
|
|
2345
|
-
|
|
2346
|
-
const [
|
|
2347
|
-
const
|
|
2348
|
-
const [
|
|
2349
|
-
const [
|
|
2350
|
-
const [
|
|
2351
|
-
const [
|
|
2352
|
-
const [
|
|
2353
|
-
|
|
2354
|
-
const
|
|
2355
|
-
const
|
|
2356
|
-
const
|
|
2357
|
-
const [
|
|
2358
|
-
const [
|
|
2359
|
-
const [
|
|
2360
|
-
const [
|
|
2361
|
-
const
|
|
2362
|
-
(0,
|
|
2363
|
-
const [
|
|
2364
|
-
const [
|
|
2365
|
-
const [
|
|
2366
|
-
const [
|
|
2367
|
-
const [
|
|
2368
|
-
const
|
|
2369
|
-
|
|
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)(() => {
|
|
2372
|
+
const [balance, setBalance] = (0, import_react4.useState)(cachedBalance?.data ?? null);
|
|
2373
|
+
const [usage, setUsage] = (0, import_react4.useState)(cachedUsage?.data ?? null);
|
|
2374
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
2375
|
+
const [notice, setNotice] = (0, import_react4.useState)(null);
|
|
2376
|
+
const [loadingBalance, setLoadingBalance] = (0, import_react4.useState)(cachedBalance === null);
|
|
2377
|
+
const [loadingUsage, setLoadingUsage] = (0, import_react4.useState)(cachedUsage === null);
|
|
2378
|
+
const [refreshing, setRefreshing] = (0, import_react4.useState)(false);
|
|
2379
|
+
const [usageUpdatedAt, setUsageUpdatedAt] = (0, import_react4.useState)(cachedUsageAt);
|
|
2380
|
+
const [syncState, setSyncState] = (0, import_react4.useState)(cachedUsageAt === null ? "syncing" : "cached");
|
|
2381
|
+
const [freshnessNow, setFreshnessNow] = (0, import_react4.useState)(Date.now());
|
|
2382
|
+
const [settingsOpen, setSettingsOpen] = (0, import_react4.useState)(false);
|
|
2383
|
+
const hadDataRef = (0, import_react4.useRef)(cachedBalance !== null || cachedUsage !== null);
|
|
2384
|
+
const [widgetOn, setWidgetOn] = (0, import_react4.useState)(widgetVisibleStore.get());
|
|
2385
|
+
const [widgetTabIds, setWidgetTabIds] = (0, import_react4.useState)(widgetTabIdsStore.get());
|
|
2386
|
+
const [viewTabs, setViewTabs] = (0, import_react4.useState)(() => props.views.list());
|
|
2387
|
+
(0, import_react4.useEffect)(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views]);
|
|
2388
|
+
const [lowBalance, setLowBalance] = (0, import_react4.useState)(lowBalanceStore.get());
|
|
2389
|
+
const [monthlyBudget, setMonthlyBudget] = (0, import_react4.useState)(monthlyBudgetStore.get());
|
|
2390
|
+
const [barMode, setBarMode] = (0, import_react4.useState)("daily");
|
|
2391
|
+
const [selectedModels, setSelectedModels] = (0, import_react4.useState)([]);
|
|
2392
|
+
const [windowDays, setWindowDays] = (0, import_react4.useState)(usageWindowStore.get());
|
|
2393
|
+
const [chartMetric, setChartMetric] = (0, import_react4.useState)(chartMetricStore.get());
|
|
2394
|
+
const [sidebarEntry, setSidebarEntry] = (0, import_react4.useState)(sidebarEntryStore.get());
|
|
2395
|
+
(0, import_react4.useEffect)(() => sidebarEntryStore.subscribe(() => setSidebarEntry(sidebarEntryStore.get())), []);
|
|
2396
|
+
const [tabDragging, setTabDragging] = (0, import_react4.useState)(false);
|
|
2397
|
+
const [tabDropTarget, setTabDropTarget] = (0, import_react4.useState)(null);
|
|
2398
|
+
const [retainedVision, setRetainedVision] = (0, import_react4.useState)(null);
|
|
2399
|
+
const [runwayHovering, setRunwayHovering] = (0, import_react4.useState)(false);
|
|
2400
|
+
const [runwayManuallyOpened, setRunwayManuallyOpened] = (0, import_react4.useState)(false);
|
|
2401
|
+
const [remainingHovering, setRemainingHovering] = (0, import_react4.useState)(false);
|
|
2402
|
+
const [remainingManuallyOpened, setRemainingManuallyOpened] = (0, import_react4.useState)(false);
|
|
2403
|
+
const budgetInputRef = (0, import_react4.useRef)(null);
|
|
2404
|
+
const rootRef = (0, import_react4.useRef)(null);
|
|
2405
|
+
const [composerHeight, setComposerHeight] = (0, import_react4.useState)(null);
|
|
2406
|
+
const [composerWidth, setComposerWidth] = (0, import_react4.useState)(null);
|
|
2407
|
+
const [sessionUsage, setSessionUsage] = (0, import_react4.useState)(null);
|
|
2408
|
+
const [loadingSessionUsage, setLoadingSessionUsage] = (0, import_react4.useState)(props.sessionId !== void 0 && props.sessionId !== "");
|
|
2409
|
+
const [sessionUsageError, setSessionUsageError] = (0, import_react4.useState)(null);
|
|
2410
|
+
const sessionUsageIdRef = (0, import_react4.useRef)(null);
|
|
2411
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
2379
2412
|
const node = rootRef.current;
|
|
2380
2413
|
if (node === null) return;
|
|
2381
2414
|
const measure = () => {
|
|
@@ -2406,13 +2439,13 @@ function BalanceDashboard(props) {
|
|
|
2406
2439
|
window.removeEventListener("resize", measure);
|
|
2407
2440
|
};
|
|
2408
2441
|
}, []);
|
|
2409
|
-
(0,
|
|
2442
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
2410
2443
|
const node = rootRef.current;
|
|
2411
2444
|
if (node === null) return;
|
|
2412
2445
|
const scrollport = node.closest("[data-conversation-scroll]");
|
|
2413
2446
|
if (scrollport instanceof HTMLElement) scrollport.scrollTop = 0;
|
|
2414
2447
|
}, [props.sessionId]);
|
|
2415
|
-
(0,
|
|
2448
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
2416
2449
|
quotaViewActiveStore.set(true);
|
|
2417
2450
|
return () => quotaViewActiveStore.set(false);
|
|
2418
2451
|
}, []);
|
|
@@ -2486,10 +2519,10 @@ function BalanceDashboard(props) {
|
|
|
2486
2519
|
if (sessionUsageIdRef.current === id) setLoadingSessionUsage(false);
|
|
2487
2520
|
}
|
|
2488
2521
|
};
|
|
2489
|
-
(0,
|
|
2522
|
+
(0, import_react4.useEffect)(() => {
|
|
2490
2523
|
void load(false);
|
|
2491
2524
|
}, []);
|
|
2492
|
-
(0,
|
|
2525
|
+
(0, import_react4.useEffect)(() => {
|
|
2493
2526
|
const id = props.sessionId;
|
|
2494
2527
|
sessionUsageIdRef.current = id ?? null;
|
|
2495
2528
|
if (id === void 0 || id === "") {
|
|
@@ -2503,57 +2536,7 @@ function BalanceDashboard(props) {
|
|
|
2503
2536
|
setLoadingSessionUsage(true);
|
|
2504
2537
|
void loadSessionUsage(id, false);
|
|
2505
2538
|
}, [props.sessionId]);
|
|
2506
|
-
(0,
|
|
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)(() => {
|
|
2539
|
+
(0, import_react4.useEffect)(() => {
|
|
2557
2540
|
const timer = window.setInterval(() => setFreshnessNow(Date.now()), 3e4);
|
|
2558
2541
|
return () => window.clearInterval(timer);
|
|
2559
2542
|
}, []);
|
|
@@ -2577,66 +2560,6 @@ function BalanceDashboard(props) {
|
|
|
2577
2560
|
setMonthlyBudget(value);
|
|
2578
2561
|
monthlyBudgetStore.set(value);
|
|
2579
2562
|
};
|
|
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
|
-
};
|
|
2640
2563
|
const changeWindowDays = (value) => {
|
|
2641
2564
|
setWindowDays(value);
|
|
2642
2565
|
usageWindowStore.set(value);
|
|
@@ -2732,7 +2655,7 @@ function BalanceDashboard(props) {
|
|
|
2732
2655
|
costTotal: window2.totals.cost
|
|
2733
2656
|
});
|
|
2734
2657
|
const liveVision = visionOpen && activeWindow !== null && visionStats !== void 0 ? visionPayload(activeWindow, visionStats, activeWindowName) : null;
|
|
2735
|
-
(0,
|
|
2658
|
+
(0, import_react4.useEffect)(() => {
|
|
2736
2659
|
if (visionOpen && activeWindow !== null && visionStats !== void 0) {
|
|
2737
2660
|
setRetainedVision(visionPayload(activeWindow, visionStats, activeWindowName));
|
|
2738
2661
|
return;
|
|
@@ -2816,15 +2739,6 @@ function BalanceDashboard(props) {
|
|
|
2816
2739
|
{ error: localizeApiError(notice.error, t, "error.query") }
|
|
2817
2740
|
);
|
|
2818
2741
|
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
|
-
};
|
|
2828
2742
|
const composerSafeAreaStyle = {
|
|
2829
2743
|
"--dq-composer-h": `${composerHeight ?? COMPOSER_FALLBACK_PX}px`,
|
|
2830
2744
|
// Only set once measured; .dq-balance's max-width falls back to 860px
|
|
@@ -2832,7 +2746,18 @@ function BalanceDashboard(props) {
|
|
|
2832
2746
|
// column, then narrows in place — no flash of the old width).
|
|
2833
2747
|
...composerWidth !== null ? { "--dq-composer-w": `${composerWidth}px` } : {}
|
|
2834
2748
|
};
|
|
2749
|
+
const navSections = [
|
|
2750
|
+
{ key: "balance", label: t("nav.balance") },
|
|
2751
|
+
...props.sessionId !== void 0 && props.sessionId !== "" ? [{ key: "session", label: t("nav.session") }] : [],
|
|
2752
|
+
{ key: "overview", label: t("nav.overview") },
|
|
2753
|
+
{ key: "trend", label: t("nav.trend") },
|
|
2754
|
+
{ key: "heatmap", label: t("nav.heatmap") },
|
|
2755
|
+
{ key: "models", label: t("nav.models") },
|
|
2756
|
+
{ key: "sessions", label: t("nav.sessions") },
|
|
2757
|
+
{ key: "peakcache", label: t("nav.peakcache") }
|
|
2758
|
+
];
|
|
2835
2759
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-balance", ref: rootRef, style: composerSafeAreaStyle, children: [
|
|
2760
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SectionNav, { rootRef, label: t("nav.label"), sections: navSections }),
|
|
2836
2761
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-row", children: [
|
|
2837
2762
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-messages", children: [
|
|
2838
2763
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
@@ -2851,23 +2776,39 @@ function BalanceDashboard(props) {
|
|
|
2851
2776
|
noticeText !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-warn", children: noticeText }),
|
|
2852
2777
|
errorText !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-error", children: errorText })
|
|
2853
2778
|
] }),
|
|
2854
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2779
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-actions", children: [
|
|
2780
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2781
|
+
"button",
|
|
2782
|
+
{
|
|
2783
|
+
type: "button",
|
|
2784
|
+
className: "dq-settings-btn",
|
|
2785
|
+
"aria-expanded": settingsOpen,
|
|
2786
|
+
"aria-controls": "dq-settings-panel",
|
|
2787
|
+
onClick: () => setSettingsOpen((open) => !open),
|
|
2788
|
+
children: [
|
|
2789
|
+
t("settings.title"),
|
|
2790
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-settings-caret${settingsOpen ? " dq-settings-caret--open" : ""}`, "aria-hidden": "true", children: "\u25BE" })
|
|
2791
|
+
]
|
|
2792
|
+
}
|
|
2793
|
+
),
|
|
2794
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2795
|
+
"button",
|
|
2796
|
+
{
|
|
2797
|
+
type: "button",
|
|
2798
|
+
className: "dq-refresh-btn",
|
|
2799
|
+
title: t("status.forceRefresh"),
|
|
2800
|
+
disabled: refreshing,
|
|
2801
|
+
"aria-busy": refreshing,
|
|
2802
|
+
onClick: () => {
|
|
2803
|
+
void load(true);
|
|
2804
|
+
},
|
|
2805
|
+
children: [
|
|
2806
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-refresh-icon${refreshing ? " dq-refresh-icon--spin" : ""}`, children: "\u21BB" }),
|
|
2807
|
+
refreshing ? t("status.refreshing") : t("status.refresh")
|
|
2808
|
+
]
|
|
2809
|
+
}
|
|
2810
|
+
)
|
|
2811
|
+
] })
|
|
2871
2812
|
] }),
|
|
2872
2813
|
balanceLow && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-alert", role: "status", children: [
|
|
2873
2814
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-alert-icon", "aria-hidden": "true", children: "!" }),
|
|
@@ -2949,6 +2890,144 @@ function BalanceDashboard(props) {
|
|
|
2949
2890
|
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") }) })
|
|
2950
2891
|
] }) : loadingBalance ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BalanceSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("balance.empty") })
|
|
2951
2892
|
] }),
|
|
2893
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Collapsible, { open: settingsOpen, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-settings-panel", id: "dq-settings-panel", role: "region", "aria-label": t("settings.title"), children: [
|
|
2894
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("settings.title") }),
|
|
2895
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
2896
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("settings.groupDisplay") }),
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-toggle", children: [
|
|
2898
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", checked: widgetOn, onChange: toggle }),
|
|
2899
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("settings.widget") })
|
|
2900
|
+
] }),
|
|
2901
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-toggle-hint", children: t("settings.widgetHint") }),
|
|
2902
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting dq-setting--tabs", children: [
|
|
2903
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-widget-tabs-label", children: t("settings.widgetTabs") }),
|
|
2904
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control dq-widget-tabs", role: "group", "aria-labelledby": "dq-widget-tabs-label", children: [
|
|
2905
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-widget-tab-list", children: widgetTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-widget-tab", children: [
|
|
2906
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2907
|
+
"input",
|
|
2908
|
+
{
|
|
2909
|
+
type: "checkbox",
|
|
2910
|
+
checked: widgetTabIds === null || widgetTabIds.includes(tab.id),
|
|
2911
|
+
onChange: () => toggleWidgetTab(tab.id)
|
|
2912
|
+
}
|
|
2913
|
+
),
|
|
2914
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: tab.label })
|
|
2915
|
+
] }, tab.id)) }),
|
|
2916
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: widgetTabs.length === 0 ? t("settings.widgetTabsEmpty") : t("settings.widgetTabsHint") })
|
|
2917
|
+
] })
|
|
2918
|
+
] })
|
|
2919
|
+
] }),
|
|
2920
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
2921
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("settings.groupAlerts") }),
|
|
2922
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2923
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-low-balance", children: t("settings.lowBalance") }),
|
|
2924
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2925
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2926
|
+
"input",
|
|
2927
|
+
{
|
|
2928
|
+
id: "dq-low-balance",
|
|
2929
|
+
className: "dq-number",
|
|
2930
|
+
type: "number",
|
|
2931
|
+
min: 0,
|
|
2932
|
+
step: 1,
|
|
2933
|
+
value: lowBalance,
|
|
2934
|
+
onChange: (e) => changeLowBalance(Math.max(0, Number(e.target.value) || 0))
|
|
2935
|
+
}
|
|
2936
|
+
),
|
|
2937
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: lowBalance > 0 ? t("settings.lowBalanceOn", { amount: fmt(lowBalance), currency: primary?.currency ?? "CNY" }) : t("settings.lowBalanceOff") })
|
|
2938
|
+
] })
|
|
2939
|
+
] }),
|
|
2940
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2941
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-monthly-budget", children: t("settings.monthlyBudget") }),
|
|
2942
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2943
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2944
|
+
"input",
|
|
2945
|
+
{
|
|
2946
|
+
ref: budgetInputRef,
|
|
2947
|
+
id: "dq-monthly-budget",
|
|
2948
|
+
className: "dq-number",
|
|
2949
|
+
type: "number",
|
|
2950
|
+
min: 0,
|
|
2951
|
+
step: 10,
|
|
2952
|
+
value: monthlyBudget,
|
|
2953
|
+
"aria-describedby": "dq-monthly-budget-hint",
|
|
2954
|
+
onChange: (e) => changeMonthlyBudget(Math.max(0, Number(e.target.value) || 0))
|
|
2955
|
+
}
|
|
2956
|
+
),
|
|
2957
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-monthly-budget-hint", className: "dq-setting-hint", children: monthlyBudget > 0 ? t("settings.monthlyBudgetOn", { amount: fmt(monthlyBudget) }) : t("settings.monthlyBudgetOff") })
|
|
2958
|
+
] })
|
|
2959
|
+
] })
|
|
2960
|
+
] }),
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
2962
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("settings.groupLayout") }),
|
|
2963
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2964
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-sidebar-entry", children: t("settings.sidebarEntry") }),
|
|
2965
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2966
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2967
|
+
"select",
|
|
2968
|
+
{
|
|
2969
|
+
id: "dq-sidebar-entry",
|
|
2970
|
+
className: "dq-select",
|
|
2971
|
+
value: sidebarEntry,
|
|
2972
|
+
onChange: (e) => changeSidebarEntry(e.target.value),
|
|
2973
|
+
children: [
|
|
2974
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "off", children: t("settings.sidebarEntryOff") }),
|
|
2975
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "top", children: t("settings.sidebarEntryTop") }),
|
|
2976
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "bottom", children: t("settings.sidebarEntryBottom") })
|
|
2977
|
+
]
|
|
2978
|
+
}
|
|
2979
|
+
),
|
|
2980
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: t("settings.sidebarEntryHint") })
|
|
2981
|
+
] })
|
|
2982
|
+
] }),
|
|
2983
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2984
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-tab-order-label", children: t("settings.tabPosition") }),
|
|
2985
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2986
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2987
|
+
"div",
|
|
2988
|
+
{
|
|
2989
|
+
className: "dq-tab-order",
|
|
2990
|
+
role: "group",
|
|
2991
|
+
"aria-labelledby": "dq-tab-order-label",
|
|
2992
|
+
"aria-describedby": "dq-tab-order-hint",
|
|
2993
|
+
onDragOver: onOrderDragOver,
|
|
2994
|
+
onDrop: onOrderDrop,
|
|
2995
|
+
children: viewTabs.map((tab, index) => {
|
|
2996
|
+
const current = tab.id === "balance";
|
|
2997
|
+
const dropTarget = tabDragging && !current && tabDropTarget === index;
|
|
2998
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2999
|
+
"span",
|
|
3000
|
+
{
|
|
3001
|
+
className: `dq-tab-chip${current ? " dq-tab-chip--current" : ""}${current && tabDragging ? " dq-tab-chip--dragging" : ""}${dropTarget ? " dq-tab-chip--drop-target" : ""}`,
|
|
3002
|
+
draggable: current,
|
|
3003
|
+
tabIndex: current ? 0 : void 0,
|
|
3004
|
+
role: current ? "button" : void 0,
|
|
3005
|
+
"aria-label": current ? t("settings.tabDragAria") : void 0,
|
|
3006
|
+
onDragStart: current ? onQuotaTabDragStart : void 0,
|
|
3007
|
+
onDragEnd: current ? endTabDrag : void 0,
|
|
3008
|
+
onDragOver: (event) => onChipDragOver(event, index),
|
|
3009
|
+
onDrop: (event) => onChipDrop(event, index),
|
|
3010
|
+
onKeyDown: current ? onQuotaTabKeyDown : void 0,
|
|
3011
|
+
children: tab.label
|
|
3012
|
+
},
|
|
3013
|
+
tab.id
|
|
3014
|
+
);
|
|
3015
|
+
})
|
|
3016
|
+
}
|
|
3017
|
+
),
|
|
3018
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-tab-order-hint", className: "dq-setting-hint", children: t("settings.tabPositionHint") })
|
|
3019
|
+
] })
|
|
3020
|
+
] })
|
|
3021
|
+
] }),
|
|
3022
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
3023
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("links.title") }),
|
|
3024
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-links", children: [
|
|
3025
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/usage", children: t("links.usage") }),
|
|
3026
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/api_keys", children: t("links.apiKey") }),
|
|
3027
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://status.deepseek.com", children: t("links.status") })
|
|
3028
|
+
] })
|
|
3029
|
+
] })
|
|
3030
|
+
] }) }),
|
|
2952
3031
|
props.sessionId !== void 0 && props.sessionId !== "" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--primary", children: [
|
|
2953
3032
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: t("common.estimated"), children: t("sessionUsage.title") }),
|
|
2954
3033
|
sessionUsage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SessionUsageCard, { data: sessionUsage }) : loadingSessionUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SessionUsageSkeleton, {}) : sessionUsageError !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty dq-empty--error", children: localizeApiError(sessionUsageError, t, "error.query") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("sessionUsage.empty") })
|
|
@@ -2975,7 +3054,10 @@ function BalanceDashboard(props) {
|
|
|
2975
3054
|
{
|
|
2976
3055
|
spent: usage.summary.month.cost,
|
|
2977
3056
|
budget: monthlyBudget,
|
|
2978
|
-
onConfigure: () =>
|
|
3057
|
+
onConfigure: () => {
|
|
3058
|
+
setSettingsOpen(true);
|
|
3059
|
+
window.setTimeout(() => budgetInputRef.current?.focus(), 80);
|
|
3060
|
+
}
|
|
2979
3061
|
}
|
|
2980
3062
|
),
|
|
2981
3063
|
usage !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PricingNote, { pricing: usage.pricing })
|
|
@@ -3103,339 +3185,13 @@ function BalanceDashboard(props) {
|
|
|
3103
3185
|
] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("cache.empty") })
|
|
3104
3186
|
] })
|
|
3105
3187
|
] }),
|
|
3106
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
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
|
-
] }),
|
|
3157
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
3158
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("settings.title") }),
|
|
3159
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-links-title", children: t("links.title") }),
|
|
3160
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-links", children: [
|
|
3161
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/usage", children: t("links.usage") }),
|
|
3162
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/api_keys", children: t("links.apiKey") }),
|
|
3163
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://status.deepseek.com", children: t("links.status") })
|
|
3164
|
-
] }),
|
|
3165
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-toggle", children: [
|
|
3166
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", checked: widgetOn, onChange: toggle }),
|
|
3167
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("settings.widget") })
|
|
3168
|
-
] }),
|
|
3169
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-toggle-hint", children: t("settings.widgetHint") }),
|
|
3170
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting dq-setting--tabs", children: [
|
|
3171
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-widget-tabs-label", children: t("settings.widgetTabs") }),
|
|
3172
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control dq-widget-tabs", role: "group", "aria-labelledby": "dq-widget-tabs-label", children: [
|
|
3173
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-widget-tab-list", children: widgetTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-widget-tab", children: [
|
|
3174
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3175
|
-
"input",
|
|
3176
|
-
{
|
|
3177
|
-
type: "checkbox",
|
|
3178
|
-
checked: widgetTabIds === null || widgetTabIds.includes(tab.id),
|
|
3179
|
-
onChange: () => toggleWidgetTab(tab.id)
|
|
3180
|
-
}
|
|
3181
|
-
),
|
|
3182
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: tab.label })
|
|
3183
|
-
] }, tab.id)) }),
|
|
3184
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: widgetTabs.length === 0 ? t("settings.widgetTabsEmpty") : t("settings.widgetTabsHint") })
|
|
3185
|
-
] })
|
|
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") }),
|
|
3203
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
3204
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-low-balance", children: t("settings.lowBalance") }),
|
|
3205
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
3206
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3207
|
-
"input",
|
|
3208
|
-
{
|
|
3209
|
-
id: "dq-low-balance",
|
|
3210
|
-
className: "dq-number",
|
|
3211
|
-
type: "number",
|
|
3212
|
-
min: 0,
|
|
3213
|
-
step: 1,
|
|
3214
|
-
value: lowBalance,
|
|
3215
|
-
onChange: (e) => changeLowBalance(Math.max(0, Number(e.target.value) || 0))
|
|
3216
|
-
}
|
|
3217
|
-
),
|
|
3218
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: lowBalance > 0 ? t("settings.lowBalanceOn", { amount: fmt(lowBalance), currency: primary?.currency ?? "CNY" }) : t("settings.lowBalanceOff") })
|
|
3219
|
-
] })
|
|
3220
|
-
] }),
|
|
3221
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
3222
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-monthly-budget", children: t("settings.monthlyBudget") }),
|
|
3223
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
3224
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3225
|
-
"input",
|
|
3226
|
-
{
|
|
3227
|
-
ref: budgetInputRef,
|
|
3228
|
-
id: "dq-monthly-budget",
|
|
3229
|
-
className: "dq-number",
|
|
3230
|
-
type: "number",
|
|
3231
|
-
min: 0,
|
|
3232
|
-
step: 10,
|
|
3233
|
-
value: monthlyBudget,
|
|
3234
|
-
"aria-describedby": "dq-monthly-budget-hint",
|
|
3235
|
-
onChange: (e) => changeMonthlyBudget(Math.max(0, Number(e.target.value) || 0))
|
|
3236
|
-
}
|
|
3237
|
-
),
|
|
3238
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-monthly-budget-hint", className: "dq-setting-hint", children: monthlyBudget > 0 ? t("settings.monthlyBudgetOn", { amount: fmt(monthlyBudget) }) : t("settings.monthlyBudgetOff") })
|
|
3239
|
-
] })
|
|
3240
|
-
] }),
|
|
3241
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
3242
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-sidebar-entry", children: t("settings.sidebarEntry") }),
|
|
3243
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
3244
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3245
|
-
"select",
|
|
3246
|
-
{
|
|
3247
|
-
id: "dq-sidebar-entry",
|
|
3248
|
-
className: "dq-select",
|
|
3249
|
-
value: sidebarEntry,
|
|
3250
|
-
onChange: (e) => changeSidebarEntry(e.target.value),
|
|
3251
|
-
children: [
|
|
3252
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "off", children: t("settings.sidebarEntryOff") }),
|
|
3253
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "top", children: t("settings.sidebarEntryTop") }),
|
|
3254
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "bottom", children: t("settings.sidebarEntryBottom") })
|
|
3255
|
-
]
|
|
3256
|
-
}
|
|
3257
|
-
),
|
|
3258
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: t("settings.sidebarEntryHint") })
|
|
3259
|
-
] })
|
|
3260
|
-
] }),
|
|
3261
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
3262
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-tab-order-label", children: t("settings.tabPosition") }),
|
|
3263
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
3264
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3265
|
-
"div",
|
|
3266
|
-
{
|
|
3267
|
-
className: "dq-tab-order",
|
|
3268
|
-
role: "group",
|
|
3269
|
-
"aria-labelledby": "dq-tab-order-label",
|
|
3270
|
-
"aria-describedby": "dq-tab-order-hint",
|
|
3271
|
-
onDragOver: onOrderDragOver,
|
|
3272
|
-
onDrop: onOrderDrop,
|
|
3273
|
-
children: viewTabs.map((tab, index) => {
|
|
3274
|
-
const current2 = tab.id === "balance";
|
|
3275
|
-
const dropTarget = tabDragging && !current2 && tabDropTarget === index;
|
|
3276
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3277
|
-
"span",
|
|
3278
|
-
{
|
|
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,
|
|
3286
|
-
onDragOver: (event) => onChipDragOver(event, index),
|
|
3287
|
-
onDrop: (event) => onChipDrop(event, index),
|
|
3288
|
-
onKeyDown: current2 ? onQuotaTabKeyDown : void 0,
|
|
3289
|
-
children: tab.label
|
|
3290
|
-
},
|
|
3291
|
-
tab.id
|
|
3292
|
-
);
|
|
3293
|
-
})
|
|
3294
|
-
}
|
|
3295
|
-
),
|
|
3296
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-tab-order-hint", className: "dq-setting-hint", children: t("settings.tabPositionHint") })
|
|
3297
|
-
] })
|
|
3298
|
-
] })
|
|
3299
|
-
] }),
|
|
3300
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.8.3" })
|
|
3188
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.9.0" })
|
|
3301
3189
|
] });
|
|
3302
3190
|
}
|
|
3303
3191
|
|
|
3304
|
-
// src/client/breaker-pill.tsx
|
|
3305
|
-
var import_react6 = require("react");
|
|
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
3192
|
// src/client/message-cost.tsx
|
|
3437
|
-
var
|
|
3438
|
-
var
|
|
3193
|
+
var import_react5 = require("react");
|
|
3194
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
3439
3195
|
var fmtTurnCost = (cost) => cost.toLocaleString(void 0, {
|
|
3440
3196
|
minimumFractionDigits: cost > 0 && cost < 0.01 ? 4 : 2,
|
|
3441
3197
|
maximumFractionDigits: cost > 0 && cost < 0.01 ? 6 : 4
|
|
@@ -3446,8 +3202,8 @@ var cachedData = (sessionId) => {
|
|
|
3446
3202
|
};
|
|
3447
3203
|
function MessageCost({ sessionId, messageId }) {
|
|
3448
3204
|
const { t } = useI18n();
|
|
3449
|
-
const [data, setData] = (0,
|
|
3450
|
-
(0,
|
|
3205
|
+
const [data, setData] = (0, import_react5.useState)(() => cachedData(sessionId));
|
|
3206
|
+
(0, import_react5.useEffect)(() => {
|
|
3451
3207
|
setData(cachedData(sessionId));
|
|
3452
3208
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData(sessionId)));
|
|
3453
3209
|
const hit = cachedData(sessionId)?.turns.some((turn2) => turn2.messageId === messageId) ?? false;
|
|
@@ -3458,7 +3214,7 @@ function MessageCost({ sessionId, messageId }) {
|
|
|
3458
3214
|
if (turn === void 0) return null;
|
|
3459
3215
|
const amount = `\xA5${fmtTurnCost(turn.cost)}`;
|
|
3460
3216
|
const tooltip = t("messageCost.tooltip");
|
|
3461
|
-
return /* @__PURE__ */ (0,
|
|
3217
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
3462
3218
|
"span",
|
|
3463
3219
|
{
|
|
3464
3220
|
className: "dq-message-cost",
|
|
@@ -3466,15 +3222,15 @@ function MessageCost({ sessionId, messageId }) {
|
|
|
3466
3222
|
"data-dq-tooltip": tooltip,
|
|
3467
3223
|
"aria-label": t("messageCost.aria", { amount }),
|
|
3468
3224
|
children: [
|
|
3469
|
-
/* @__PURE__ */ (0,
|
|
3470
|
-
/* @__PURE__ */ (0,
|
|
3225
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dq-message-cost-separator", "aria-hidden": true, children: "\xB7" }),
|
|
3226
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dq-message-cost-amount", children: amount })
|
|
3471
3227
|
]
|
|
3472
3228
|
}
|
|
3473
3229
|
);
|
|
3474
3230
|
}
|
|
3475
3231
|
|
|
3476
3232
|
// src/client/session-cost-pill.tsx
|
|
3477
|
-
var
|
|
3233
|
+
var import_react6 = require("react");
|
|
3478
3234
|
var import_react_dom = require("react-dom");
|
|
3479
3235
|
|
|
3480
3236
|
// src/pricing.ts
|
|
@@ -3482,7 +3238,7 @@ var PEAK_PRICING_FROM_MS = Date.UTC(2026, 7, 16, 16, 0, 0);
|
|
|
3482
3238
|
var V41_FLASH_PRICING_FROM_MS = Date.UTC(2026, 8, 10, 4, 0, 0);
|
|
3483
3239
|
var PRO_ROUTED_TO_FLASH_FROM_MS = Date.UTC(2026, 8, 14, 4, 0, 0);
|
|
3484
3240
|
var PEAK_WINDOWS = [[9, 12], [14, 18]];
|
|
3485
|
-
var
|
|
3241
|
+
var BEIJING_OFFSET_MS3 = 8 * 36e5;
|
|
3486
3242
|
var FLAT_RATES = {
|
|
3487
3243
|
pro: { cacheHit: 0.025, input: 3, output: 6 },
|
|
3488
3244
|
flash: { cacheHit: 0.02, input: 1, output: 2 },
|
|
@@ -3527,12 +3283,12 @@ function billedTierOf(timeMs, model) {
|
|
|
3527
3283
|
return tier === "pro" && timeMs >= PRO_ROUTED_TO_FLASH_FROM_MS ? "flash" : tier;
|
|
3528
3284
|
}
|
|
3529
3285
|
function isBeijingWeekday(timeMs) {
|
|
3530
|
-
const day = new Date(timeMs +
|
|
3286
|
+
const day = new Date(timeMs + BEIJING_OFFSET_MS3).getUTCDay();
|
|
3531
3287
|
return day >= 1 && day <= 5;
|
|
3532
3288
|
}
|
|
3533
3289
|
function isPeak(timeMs) {
|
|
3534
3290
|
if (eraAt(timeMs).weekdaysOnly && !isBeijingWeekday(timeMs)) return false;
|
|
3535
|
-
const hour = Math.floor(((timeMs +
|
|
3291
|
+
const hour = Math.floor(((timeMs + BEIJING_OFFSET_MS3) % 864e5 + 864e5) % 864e5 / 36e5);
|
|
3536
3292
|
return PEAK_WINDOWS.some(([from, to]) => hour >= from && hour < to);
|
|
3537
3293
|
}
|
|
3538
3294
|
function ratesAt(timeMs, model) {
|
|
@@ -3542,7 +3298,7 @@ function ratesAt(timeMs, model) {
|
|
|
3542
3298
|
}
|
|
3543
3299
|
|
|
3544
3300
|
// src/client/session-cost-pill.tsx
|
|
3545
|
-
var
|
|
3301
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
3546
3302
|
var PANEL_MARGIN = 12;
|
|
3547
3303
|
var PANEL_GAP = 8;
|
|
3548
3304
|
var cachedData2 = (sessionId) => {
|
|
@@ -3550,7 +3306,7 @@ var cachedData2 = (sessionId) => {
|
|
|
3550
3306
|
return response?.ok === true ? response.data ?? null : null;
|
|
3551
3307
|
};
|
|
3552
3308
|
function CostIcon() {
|
|
3553
|
-
return /* @__PURE__ */ (0,
|
|
3309
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3554
3310
|
"svg",
|
|
3555
3311
|
{
|
|
3556
3312
|
width: "14",
|
|
@@ -3564,32 +3320,32 @@ function CostIcon() {
|
|
|
3564
3320
|
"aria-hidden": "true",
|
|
3565
3321
|
focusable: "false",
|
|
3566
3322
|
children: [
|
|
3567
|
-
/* @__PURE__ */ (0,
|
|
3568
|
-
/* @__PURE__ */ (0,
|
|
3569
|
-
/* @__PURE__ */ (0,
|
|
3570
|
-
/* @__PURE__ */ (0,
|
|
3571
|
-
/* @__PURE__ */ (0,
|
|
3323
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "8", cy: "8", r: "6" }),
|
|
3324
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5.5 5.3 8 8.4l2.5-3.1" }),
|
|
3325
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M8 8.4v2.9" }),
|
|
3326
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5.8 9.5h4.4" }),
|
|
3327
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5.8 10.9h4.4" })
|
|
3572
3328
|
]
|
|
3573
3329
|
}
|
|
3574
3330
|
);
|
|
3575
3331
|
}
|
|
3576
3332
|
function SessionCostPill({ sessionId }) {
|
|
3577
3333
|
const { t, locale } = useI18n();
|
|
3578
|
-
const [data, setData] = (0,
|
|
3334
|
+
const [data, setData] = (0, import_react6.useState)(
|
|
3579
3335
|
() => sessionId === void 0 || sessionId === "" ? null : cachedData2(sessionId)
|
|
3580
3336
|
);
|
|
3581
|
-
const [open, setOpen] = (0,
|
|
3582
|
-
const anchorRef = (0,
|
|
3583
|
-
const panelRef = (0,
|
|
3584
|
-
const [pos, setPos] = (0,
|
|
3585
|
-
(0,
|
|
3337
|
+
const [open, setOpen] = (0, import_react6.useState)(false);
|
|
3338
|
+
const anchorRef = (0, import_react6.useRef)(null);
|
|
3339
|
+
const panelRef = (0, import_react6.useRef)(null);
|
|
3340
|
+
const [pos, setPos] = (0, import_react6.useState)(null);
|
|
3341
|
+
(0, import_react6.useEffect)(() => {
|
|
3586
3342
|
if (sessionId === void 0 || sessionId === "") return;
|
|
3587
3343
|
setData(cachedData2(sessionId));
|
|
3588
3344
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData2(sessionId)));
|
|
3589
3345
|
if (getCachedSessionUsage(sessionId) === null) void fetchSessionUsage(sessionId);
|
|
3590
3346
|
return unsubscribe;
|
|
3591
3347
|
}, [sessionId]);
|
|
3592
|
-
(0,
|
|
3348
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
3593
3349
|
if (!open) {
|
|
3594
3350
|
setPos(null);
|
|
3595
3351
|
return;
|
|
@@ -3604,7 +3360,7 @@ function SessionCostPill({ sessionId }) {
|
|
|
3604
3360
|
bottom: window.innerHeight - pill.top + PANEL_GAP
|
|
3605
3361
|
});
|
|
3606
3362
|
}, [open, data]);
|
|
3607
|
-
(0,
|
|
3363
|
+
(0, import_react6.useEffect)(() => {
|
|
3608
3364
|
if (!open) return;
|
|
3609
3365
|
const close = () => setOpen(false);
|
|
3610
3366
|
const onPointerDown = (event) => {
|
|
@@ -3656,8 +3412,8 @@ function SessionCostPill({ sessionId }) {
|
|
|
3656
3412
|
hour12: false
|
|
3657
3413
|
});
|
|
3658
3414
|
const range = data.firstActive !== null && data.lastActive !== null ? `${rangeTime.format(data.firstActive)} \u2013 ${rangeTime.format(data.lastActive)}` : null;
|
|
3659
|
-
return /* @__PURE__ */ (0,
|
|
3660
|
-
/* @__PURE__ */ (0,
|
|
3415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
3416
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { ref: anchorRef, className: "dq-cost-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3661
3417
|
"button",
|
|
3662
3418
|
{
|
|
3663
3419
|
type: "button",
|
|
@@ -3667,13 +3423,13 @@ function SessionCostPill({ sessionId }) {
|
|
|
3667
3423
|
"aria-label": t("sessionCost.aria", { amount }),
|
|
3668
3424
|
onClick: () => setOpen((value) => !value),
|
|
3669
3425
|
children: [
|
|
3670
|
-
/* @__PURE__ */ (0,
|
|
3671
|
-
/* @__PURE__ */ (0,
|
|
3426
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CostIcon, {}),
|
|
3427
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dq-cost-pill-label", children: amount })
|
|
3672
3428
|
]
|
|
3673
3429
|
}
|
|
3674
3430
|
) }),
|
|
3675
3431
|
open && (0, import_react_dom.createPortal)(
|
|
3676
|
-
/* @__PURE__ */ (0,
|
|
3432
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3677
3433
|
"div",
|
|
3678
3434
|
{
|
|
3679
3435
|
ref: panelRef,
|
|
@@ -3682,35 +3438,35 @@ function SessionCostPill({ sessionId }) {
|
|
|
3682
3438
|
"aria-label": t("sessionCost.title"),
|
|
3683
3439
|
style: pos === null ? { visibility: "hidden", left: 0, top: 0 } : { visibility: "visible", left: pos.left, bottom: pos.bottom, top: "auto" },
|
|
3684
3440
|
children: [
|
|
3685
|
-
/* @__PURE__ */ (0,
|
|
3686
|
-
/* @__PURE__ */ (0,
|
|
3687
|
-
/* @__PURE__ */ (0,
|
|
3441
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dq-cost-panel-title", children: [
|
|
3442
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dq-cost-panel-title-label", children: [
|
|
3443
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CostIcon, {}),
|
|
3688
3444
|
t("sessionCost.title")
|
|
3689
3445
|
] }),
|
|
3690
|
-
/* @__PURE__ */ (0,
|
|
3446
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dq-cost-panel-value", children: amount })
|
|
3691
3447
|
] }),
|
|
3692
|
-
/* @__PURE__ */ (0,
|
|
3693
|
-
/* @__PURE__ */ (0,
|
|
3694
|
-
/* @__PURE__ */ (0,
|
|
3695
|
-
/* @__PURE__ */ (0,
|
|
3448
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dq-cost-panel-rule", "aria-hidden": true }),
|
|
3449
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dl", { className: "dq-cost-details", children: [
|
|
3450
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("common.input") }),
|
|
3451
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dd", { children: [
|
|
3696
3452
|
"\xA5",
|
|
3697
3453
|
fmtTurnCost(split.input)
|
|
3698
3454
|
] }),
|
|
3699
|
-
/* @__PURE__ */ (0,
|
|
3700
|
-
/* @__PURE__ */ (0,
|
|
3455
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("common.cacheHit") }),
|
|
3456
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dd", { children: [
|
|
3701
3457
|
"\xA5",
|
|
3702
3458
|
fmtTurnCost(split.cache)
|
|
3703
3459
|
] }),
|
|
3704
|
-
/* @__PURE__ */ (0,
|
|
3705
|
-
/* @__PURE__ */ (0,
|
|
3460
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("common.output") }),
|
|
3461
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dd", { children: [
|
|
3706
3462
|
"\xA5",
|
|
3707
3463
|
fmtTurnCost(split.output)
|
|
3708
3464
|
] }),
|
|
3709
|
-
/* @__PURE__ */ (0,
|
|
3710
|
-
/* @__PURE__ */ (0,
|
|
3711
|
-
range !== null && /* @__PURE__ */ (0,
|
|
3712
|
-
/* @__PURE__ */ (0,
|
|
3713
|
-
/* @__PURE__ */ (0,
|
|
3465
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("sessionUsage.calls") }),
|
|
3466
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dd", { children: fmtInt(data.calls) }),
|
|
3467
|
+
range !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
3468
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("sessionCost.active") }),
|
|
3469
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dd", { children: range })
|
|
3714
3470
|
] })
|
|
3715
3471
|
] })
|
|
3716
3472
|
]
|
|
@@ -3773,18 +3529,6 @@ var css = `
|
|
|
3773
3529
|
.dq-cost-pill:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3774
3530
|
.dq-cost-pill svg{flex:none}
|
|
3775
3531
|
.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}
|
|
3788
3532
|
/* Click-open breakdown: the host stat-dialog skin (menu surface, prominent
|
|
3789
3533
|
elevation, 12/18 rows), portaled to the body and placed above the pill. */
|
|
3790
3534
|
.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}
|
|
@@ -3955,7 +3699,11 @@ var css = `
|
|
|
3955
3699
|
.dq-pricing-table{border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums;min-width:100%}
|
|
3956
3700
|
.dq-pricing-table th{text-align:left;font-weight:500;color:var(--dsw-alias-label-secondary,#59636e);padding:4px 12px 4px 0;white-space:nowrap;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
|
|
3957
3701
|
.dq-pricing-table td{padding:4px 12px 4px 0;color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;vertical-align:top}
|
|
3958
|
-
|
|
3702
|
+
/* Align by counting from the end: a peak row starts with the rowSpan'd model
|
|
3703
|
+
cell (5 cells) while its off-peak pair starts at \u65F6\u6BB5 (4 cells), so
|
|
3704
|
+
nth-child(n+3) left the off-peak cache-hit price stranded under \u65F6\u6BB5. The
|
|
3705
|
+
last three cells are the three price columns in both row shapes. */
|
|
3706
|
+
.dq-pricing-table th:nth-last-child(-n+3),.dq-pricing-table td:nth-last-child(-n+3){text-align:right}
|
|
3959
3707
|
.dq-pricing-table th:last-child,.dq-pricing-table td:last-child{padding-right:0}
|
|
3960
3708
|
.dq-pricing-table tbody td:first-child{font-weight:500}
|
|
3961
3709
|
.dq-pricing-table tbody tr+tr td{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.05))}
|
|
@@ -3987,8 +3735,23 @@ var css = `
|
|
|
3987
3735
|
.dq-legend-swatch--peak{background:var(--dsw-alias-state-warning-primary,#bf8700)}
|
|
3988
3736
|
.dq-legend-swatch--offpeak{background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85}
|
|
3989
3737
|
.dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:16px}
|
|
3990
|
-
.dq-peak-
|
|
3991
|
-
.dq-
|
|
3738
|
+
.dq-peak-foot{margin:12px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e);text-wrap:pretty}
|
|
3739
|
+
.dq-clock{margin-bottom:16px}
|
|
3740
|
+
.dq-clock-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:8px}
|
|
3741
|
+
.dq-clock-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
3742
|
+
.dq-clock-status{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;padding:2px 8px;border-radius:999px}
|
|
3743
|
+
.dq-clock-status::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor}
|
|
3744
|
+
.dq-clock-status--peak{color:var(--dq-text-warn);background:rgba(191,135,0,.12)}
|
|
3745
|
+
.dq-clock-status--off{color:var(--dq-text-success);background:rgba(45,164,78,.12)}
|
|
3746
|
+
.dq-clock-track{position:relative;display:flex;gap:1px;height:10px}
|
|
3747
|
+
.dq-clock-cell{flex:1 1 0;height:100%;background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.55}
|
|
3748
|
+
.dq-clock-cell--peak{background:var(--dsw-alias-state-warning-primary,#bf8700);opacity:.92}
|
|
3749
|
+
.dq-clock-cell:first-child{border-radius:999px 0 0 999px}
|
|
3750
|
+
.dq-clock-cell:last-child{border-radius:0 999px 999px 0}
|
|
3751
|
+
.dq-clock-now{position:absolute;top:-3px;bottom:-3px;width:2px;background:var(--dsw-alias-label-primary,#1f2328);border-radius:2px;transform:translateX(-1px)}
|
|
3752
|
+
.dq-clock-now::before{content:'';position:absolute;top:-4px;left:50%;transform:translateX(-50%);width:6px;height:6px;border-radius:50%;background:var(--dsw-alias-label-primary,#1f2328)}
|
|
3753
|
+
.dq-clock-scale{display:flex;justify-content:space-between;margin-top:4px;font-size:10px;color:var(--dsw-alias-label-tertiary,#59636e);font-variant-numeric:tabular-nums}
|
|
3754
|
+
.dq-clock-hint{margin:6px 0 0;font-size:11px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
3992
3755
|
.dq-cache-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-bottom:12px}
|
|
3993
3756
|
@media (max-width:620px){.dq-cache-figures{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px 16px}.dq-cache-figures .dq-stat{min-width:0}}
|
|
3994
3757
|
.dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:500}
|
|
@@ -4054,28 +3817,29 @@ var css = `
|
|
|
4054
3817
|
.dq-setting-label{width:128px;font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}
|
|
4055
3818
|
.dq-setting-control{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
|
|
4056
3819
|
.dq-setting-hint{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);line-height:1.5}
|
|
3820
|
+
.dq-setting-group{margin-top:28px}
|
|
3821
|
+
.dq-setting-group:first-of-type{margin-top:0}
|
|
3822
|
+
.dq-setting-group-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:0 0 10px;letter-spacing:.02em}
|
|
3823
|
+
.dq-setting-group .dq-toggle{margin-top:0}
|
|
3824
|
+
.dq-setting-group .dq-setting{margin-top:16px}
|
|
4057
3825
|
/* Page footer: the package name + version, injected at build time from
|
|
4058
3826
|
package.json (see build.mjs) so it can never drift from the release. */
|
|
4059
3827
|
.dq-footer{margin:16px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}
|
|
3828
|
+
.dq-sectionnav{position:fixed;top:50%;transform:translateY(-50%);z-index:20;width:max-content;pointer-events:none}
|
|
3829
|
+
.dq-sectionnav--hidden{display:none}
|
|
3830
|
+
.dq-sectionnav ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px;pointer-events:auto}
|
|
3831
|
+
.dq-sectionnav li{display:flex;justify-content:flex-end}
|
|
3832
|
+
.dq-sectionnav-dot{display:inline-flex;align-items:center;justify-content:flex-end;gap:8px;padding:4px 2px;border:0;background:transparent;border-radius:6px;cursor:pointer;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1.2;white-space:nowrap;transition:color .15s ease,background .15s ease}
|
|
3833
|
+
.dq-sectionnav-dot:hover,.dq-sectionnav-dot:focus-visible{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10))}
|
|
3834
|
+
.dq-sectionnav-dot:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
|
|
3835
|
+
.dq-sectionnav-label{font-size:12px;color:inherit}
|
|
3836
|
+
.dq-sectionnav-pip{flex:none;width:9px;height:9px;border-radius:50%;background:var(--dsw-alias-border-l2,rgba(0,0,0,.22));transition:background .15s ease,transform .15s ease}
|
|
3837
|
+
.dq-sectionnav-dot:hover .dq-sectionnav-pip{background:var(--dsw-alias-label-secondary,#59636e)}
|
|
3838
|
+
.dq-sectionnav-dot--active{color:var(--dsw-alias-label-primary,#1f2328);font-weight:600}
|
|
3839
|
+
.dq-sectionnav-dot--active .dq-sectionnav-pip{background:var(--dsw-alias-state-business-primary,#0969da);transform:scale(1.25)}
|
|
3840
|
+
@media (max-width:1199px){.dq-sectionnav{display:none}}
|
|
4060
3841
|
.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}
|
|
4077
3842
|
.dq-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;border-color:transparent}
|
|
4078
|
-
.dq-links-title{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 12px}
|
|
4079
3843
|
.dq-links{display:flex;flex-wrap:wrap;gap:8px}
|
|
4080
3844
|
.dq-link{display:inline-flex;align-items:center;gap:4px;padding:8px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);text-decoration:none;font-size:13px;font-weight:500;cursor:pointer}
|
|
4081
3845
|
.dq-link::after{content:"\u2197";font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
@@ -4142,7 +3906,10 @@ var css = `
|
|
|
4142
3906
|
.dq-chart-switch-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}
|
|
4143
3907
|
.dq-chart-switch-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.14)}
|
|
4144
3908
|
.dq-chart-wrap{position:relative}
|
|
4145
|
-
.dq-
|
|
3909
|
+
.dq-plot{display:flex;align-items:flex-end;gap:8px}
|
|
3910
|
+
.dq-yaxis{flex:none;width:46px;display:flex;flex-direction:column;justify-content:space-between;margin-bottom:18px;font-size:11px;line-height:1;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;text-align:right;pointer-events:none}
|
|
3911
|
+
.dq-yaxis span{white-space:nowrap}
|
|
3912
|
+
.dq-bars-scroll{flex:1 1 auto;min-width:0;overflow-x:auto;overflow-y:hidden;padding-bottom:2px}
|
|
4146
3913
|
.dq-tip{position:absolute;transform:translate(-50%,14px);z-index:40;pointer-events:none;background:var(--dsw-alias-tooltip-bg,#1f2328);color:var(--dsw-static-neutral-bluish-00,#fff);border-radius:8px;padding:7px 10px;font-size:12px;line-height:1.5;white-space:nowrap;box-shadow:0 6px 20px rgba(0,0,0,.28);font-variant-numeric:tabular-nums}
|
|
4147
3914
|
.dq-tip--above{transform:translate(-50%,calc(-100% - 8px))}
|
|
4148
3915
|
.dq-tip-head{font-weight:600;margin-bottom:2px}
|
|
@@ -4163,13 +3930,13 @@ var css = `
|
|
|
4163
3930
|
.dq-heat-cell--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}
|
|
4164
3931
|
.dq-bar-label{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);text-align:center;margin-top:4px;overflow:visible;white-space:nowrap}
|
|
4165
3932
|
.dq-heat{display:flex;flex-direction:column;gap:8px;max-width:100%}
|
|
4166
|
-
.dq-heat-scroll{overflow
|
|
4167
|
-
.dq-heatmap{display:flex;gap:
|
|
4168
|
-
.dq-heat-week{display:flex;flex:1 1 0;min-width:
|
|
4169
|
-
.dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:
|
|
3933
|
+
.dq-heat-scroll{overflow:hidden;padding-bottom:2px}
|
|
3934
|
+
.dq-heatmap{display:flex;gap:2px}
|
|
3935
|
+
.dq-heat-week{display:flex;flex:1 1 0;min-width:0;flex-direction:column;gap:2px}
|
|
3936
|
+
.dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:2px;flex:none;min-width:0}
|
|
4170
3937
|
.dq-heat-cell--pad{background:transparent}
|
|
4171
|
-
.dq-heat-months{display:flex;gap:
|
|
4172
|
-
.dq-heat-month{flex:1 1 0;min-width:
|
|
3938
|
+
.dq-heat-months{display:flex;gap:2px;margin-top:4px}
|
|
3939
|
+
.dq-heat-month{flex:1 1 0;min-width:0;font-size:12px;line-height:15px;color:var(--dsw-alias-label-secondary,#59636e);white-space:nowrap;overflow:visible}
|
|
4173
3940
|
.dq-heat-key{width:12px;height:12px;border-radius:2px;flex:none}
|
|
4174
3941
|
.dq-heat-scale{display:flex;align-items:center;gap:3px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);align-self:flex-end}
|
|
4175
3942
|
.dq-heat-scale>span:first-child{margin-right:2px}
|
|
@@ -4203,6 +3970,20 @@ var css = `
|
|
|
4203
3970
|
.dq-sync--error .dq-sync-dot{background:var(--dsw-alias-state-error-primary,#cf222e)}
|
|
4204
3971
|
@keyframes dq-sync-pulse{50%{opacity:.35;transform:scale(.8)}}
|
|
4205
3972
|
.dq-refresh-btn{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:5px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease}
|
|
3973
|
+
.dq-status-actions{display:flex;align-items:center;gap:8px;flex:none}
|
|
3974
|
+
.dq-settings-btn{display:inline-flex;align-items:center;gap:4px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:5px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease}
|
|
3975
|
+
.dq-settings-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}
|
|
3976
|
+
.dq-settings-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3977
|
+
.dq-settings-caret{font-size:10px;line-height:1;transition:transform .2s cubic-bezier(.16,1,.3,1)}
|
|
3978
|
+
.dq-settings-caret--open{transform:rotate(180deg)}
|
|
3979
|
+
.dq-settings-panel{box-sizing:border-box;min-width:0;max-width:100%;padding:20px;background:var(--dsw-alias-bg-layer-1,#ffffff);border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.10));border-radius:12px}
|
|
3980
|
+
/* Motion: eased (easeOutExpo) arrivals. Operate surface \u2014 fast, purposeful,
|
|
3981
|
+
and fully disabled under prefers-reduced-motion (see the global reduce block). */
|
|
3982
|
+
.dq-export-menu,.dq-model-menu{transform-origin:top right;animation:dq-pop .18s cubic-bezier(.16,1,.3,1)}
|
|
3983
|
+
@keyframes dq-pop{from{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:none}}
|
|
3984
|
+
.dq-alert{animation:dq-alert-in .28s cubic-bezier(.16,1,.3,1)}
|
|
3985
|
+
@keyframes dq-alert-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
|
|
3986
|
+
@media (max-width:620px){.dq-settings-panel{padding:16px}}
|
|
4206
3987
|
.dq-refresh-btn:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}
|
|
4207
3988
|
.dq-refresh-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
4208
3989
|
.dq-refresh-btn:disabled{cursor:default;opacity:.6}
|
|
@@ -4290,7 +4071,7 @@ html[data-dq-quota-view] .dq-balance{padding-bottom:48px}
|
|
|
4290
4071
|
`;
|
|
4291
4072
|
|
|
4292
4073
|
// src/client/widget.tsx
|
|
4293
|
-
var
|
|
4074
|
+
var import_react7 = require("react");
|
|
4294
4075
|
|
|
4295
4076
|
// src/client/sessions.ts
|
|
4296
4077
|
function currentSessionIdOf(state) {
|
|
@@ -4299,7 +4080,7 @@ function currentSessionIdOf(state) {
|
|
|
4299
4080
|
}
|
|
4300
4081
|
|
|
4301
4082
|
// src/client/widget.tsx
|
|
4302
|
-
var
|
|
4083
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
4303
4084
|
var MARGIN = 16;
|
|
4304
4085
|
var CORNER_KEY = "widget.corner";
|
|
4305
4086
|
var COLLAPSED_KEY = "widget.collapsed";
|
|
@@ -4350,47 +4131,47 @@ function cornerPos(corner, node, bounds) {
|
|
|
4350
4131
|
function QuotaWidget(props) {
|
|
4351
4132
|
const { t } = useI18n();
|
|
4352
4133
|
const sessionId = props.useSessions(currentSessionIdOf);
|
|
4353
|
-
const [visible, setVisible] = (0,
|
|
4354
|
-
(0,
|
|
4355
|
-
const [quotaViewActive2, setQuotaViewActive] = (0,
|
|
4356
|
-
(0,
|
|
4357
|
-
const [lowBalance, setLowBalance] = (0,
|
|
4358
|
-
(0,
|
|
4359
|
-
const [selectedTabIds, setSelectedTabIds] = (0,
|
|
4360
|
-
(0,
|
|
4361
|
-
const [viewTabs, setViewTabs] = (0,
|
|
4362
|
-
(0,
|
|
4363
|
-
const [activeViewId, setActiveViewId] = (0,
|
|
4134
|
+
const [visible, setVisible] = (0, import_react7.useState)(widgetVisibleStore.get());
|
|
4135
|
+
(0, import_react7.useEffect)(() => widgetVisibleStore.subscribe(() => setVisible(widgetVisibleStore.get())), []);
|
|
4136
|
+
const [quotaViewActive2, setQuotaViewActive] = (0, import_react7.useState)(quotaViewActiveStore.get());
|
|
4137
|
+
(0, import_react7.useEffect)(() => quotaViewActiveStore.subscribe(() => setQuotaViewActive(quotaViewActiveStore.get())), []);
|
|
4138
|
+
const [lowBalance, setLowBalance] = (0, import_react7.useState)(lowBalanceStore.get());
|
|
4139
|
+
(0, import_react7.useEffect)(() => lowBalanceStore.subscribe(() => setLowBalance(lowBalanceStore.get())), []);
|
|
4140
|
+
const [selectedTabIds, setSelectedTabIds] = (0, import_react7.useState)(widgetTabIdsStore.get());
|
|
4141
|
+
(0, import_react7.useEffect)(() => widgetTabIdsStore.subscribe(() => setSelectedTabIds(widgetTabIdsStore.get())), []);
|
|
4142
|
+
const [viewTabs, setViewTabs] = (0, import_react7.useState)(() => props.views.list());
|
|
4143
|
+
(0, import_react7.useEffect)(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views]);
|
|
4144
|
+
const [activeViewId, setActiveViewId] = (0, import_react7.useState)(null);
|
|
4364
4145
|
const cachedBalance = getCachedBalance();
|
|
4365
|
-
const [data, setData] = (0,
|
|
4366
|
-
const [error, setError] = (0,
|
|
4367
|
-
const [loading, setLoading] = (0,
|
|
4368
|
-
const [collapsed, setCollapsed] = (0,
|
|
4369
|
-
const [pos, setPos] = (0,
|
|
4370
|
-
const [corner, setCorner] = (0,
|
|
4371
|
-
const [dragging, setDragging] = (0,
|
|
4372
|
-
const rootRef = (0,
|
|
4373
|
-
const dragRef = (0,
|
|
4146
|
+
const [data, setData] = (0, import_react7.useState)(cachedBalance?.data ?? null);
|
|
4147
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
4148
|
+
const [loading, setLoading] = (0, import_react7.useState)(cachedBalance === null);
|
|
4149
|
+
const [collapsed, setCollapsed] = (0, import_react7.useState)(() => loadPref(COLLAPSED_KEY, isBoolean, false));
|
|
4150
|
+
const [pos, setPos] = (0, import_react7.useState)(null);
|
|
4151
|
+
const [corner, setCorner] = (0, import_react7.useState)(() => loadPref(CORNER_KEY, isCorner, "br"));
|
|
4152
|
+
const [dragging, setDragging] = (0, import_react7.useState)(false);
|
|
4153
|
+
const rootRef = (0, import_react7.useRef)(null);
|
|
4154
|
+
const dragRef = (0, import_react7.useRef)(null);
|
|
4374
4155
|
const readUsage = () => {
|
|
4375
4156
|
const cached = getCachedUsage();
|
|
4376
4157
|
return { data: cached?.ok === true ? cached.data ?? null : null, at: getCachedUsageAt() };
|
|
4377
4158
|
};
|
|
4378
|
-
const [usage, setUsage] = (0,
|
|
4379
|
-
(0,
|
|
4159
|
+
const [usage, setUsage] = (0, import_react7.useState)(readUsage);
|
|
4160
|
+
(0, import_react7.useEffect)(() => subscribeUsage(() => setUsage(readUsage())), []);
|
|
4380
4161
|
const readSessionUsage = (id) => {
|
|
4381
4162
|
if (id === void 0) return null;
|
|
4382
4163
|
const response = getCachedSessionUsage(id);
|
|
4383
4164
|
return response?.ok === true ? response.data ?? null : null;
|
|
4384
4165
|
};
|
|
4385
|
-
const [sessionUsage, setSessionUsage] = (0,
|
|
4386
|
-
(0,
|
|
4166
|
+
const [sessionUsage, setSessionUsage] = (0, import_react7.useState)(() => readSessionUsage(sessionId));
|
|
4167
|
+
(0, import_react7.useEffect)(() => {
|
|
4387
4168
|
setSessionUsage(readSessionUsage(sessionId));
|
|
4388
4169
|
if (sessionId === void 0) return;
|
|
4389
4170
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setSessionUsage(readSessionUsage(sessionId)));
|
|
4390
4171
|
void fetchSessionUsage(sessionId);
|
|
4391
4172
|
return unsubscribe;
|
|
4392
4173
|
}, [sessionId]);
|
|
4393
|
-
(0,
|
|
4174
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
4394
4175
|
const viewIds = viewTabs.map((tab) => tab.id);
|
|
4395
4176
|
const read = () => {
|
|
4396
4177
|
const next = getActiveConversationViewId(getShellFrame(rootRef.current), viewIds);
|
|
@@ -4427,14 +4208,14 @@ function QuotaWidget(props) {
|
|
|
4427
4208
|
setLoading(false);
|
|
4428
4209
|
}
|
|
4429
4210
|
};
|
|
4430
|
-
(0,
|
|
4211
|
+
(0, import_react7.useEffect)(() => {
|
|
4431
4212
|
void load(true);
|
|
4432
4213
|
const id = setInterval(() => {
|
|
4433
4214
|
void load(false);
|
|
4434
4215
|
}, 6e4);
|
|
4435
4216
|
return () => clearInterval(id);
|
|
4436
4217
|
}, []);
|
|
4437
|
-
(0,
|
|
4218
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
4438
4219
|
if (!visible || quotaViewActive2) return;
|
|
4439
4220
|
const node = rootRef.current;
|
|
4440
4221
|
if (node === null) return;
|
|
@@ -4562,9 +4343,9 @@ function QuotaWidget(props) {
|
|
|
4562
4343
|
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";
|
|
4563
4344
|
const ageMinutes = usage.at === null ? null : Math.floor((Date.now() - usage.at) / 6e4);
|
|
4564
4345
|
const usageAgeText = ageMinutes === null || ageMinutes < 1 ? t("widget.updatedNow") : t("widget.stale", { minutes: ageMinutes });
|
|
4565
|
-
const sessionCostRow = sessionUsage === null ? null : /* @__PURE__ */ (0,
|
|
4566
|
-
/* @__PURE__ */ (0,
|
|
4567
|
-
/* @__PURE__ */ (0,
|
|
4346
|
+
const sessionCostRow = sessionUsage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-row", title: t("widget.sessionCostTitle"), children: [
|
|
4347
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-label", children: t("widget.sessionCost") }),
|
|
4348
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
4568
4349
|
"\xA5",
|
|
4569
4350
|
fmtTurnCost(sessionUsage.cost)
|
|
4570
4351
|
] })
|
|
@@ -4572,50 +4353,50 @@ function QuotaWidget(props) {
|
|
|
4572
4353
|
let body = null;
|
|
4573
4354
|
if (!collapsed) {
|
|
4574
4355
|
if (loading && data === null && error === null) {
|
|
4575
|
-
body = /* @__PURE__ */ (0,
|
|
4576
|
-
/* @__PURE__ */ (0,
|
|
4356
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4357
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { children: t("widget.loading") }),
|
|
4577
4358
|
sessionCostRow
|
|
4578
4359
|
] });
|
|
4579
4360
|
} else if (error !== null && data === null) {
|
|
4580
|
-
body = /* @__PURE__ */ (0,
|
|
4581
|
-
/* @__PURE__ */ (0,
|
|
4361
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4362
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-error", children: localizeApiError(error, t, "error.query") }),
|
|
4582
4363
|
sessionCostRow
|
|
4583
4364
|
] });
|
|
4584
4365
|
} else if (primary !== null) {
|
|
4585
|
-
body = /* @__PURE__ */ (0,
|
|
4586
|
-
/* @__PURE__ */ (0,
|
|
4587
|
-
/* @__PURE__ */ (0,
|
|
4588
|
-
/* @__PURE__ */ (0,
|
|
4589
|
-
/* @__PURE__ */ (0,
|
|
4366
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4367
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-remaining-label", children: t("balance.remaining") }),
|
|
4368
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { title: low ? t("widget.lowTitle", { amount: fmt(lowBalance) }) : void 0, children: [
|
|
4369
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: `dsh-quota-total${low ? " dsh-quota-total--low" : ""}`, children: fmt(primary.total) }),
|
|
4370
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-currency", children: primary.currency })
|
|
4590
4371
|
] }),
|
|
4591
|
-
usage.data !== null && /* @__PURE__ */ (0,
|
|
4592
|
-
/* @__PURE__ */ (0,
|
|
4593
|
-
/* @__PURE__ */ (0,
|
|
4372
|
+
usage.data !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-row", title: t("widget.todayCostTitle", { age: usageAgeText }), children: [
|
|
4373
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-label", children: t("widget.todayCost") }),
|
|
4374
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
4594
4375
|
"\xA5",
|
|
4595
4376
|
fmt(usage.data.summary.today.cost)
|
|
4596
4377
|
] })
|
|
4597
4378
|
] }),
|
|
4598
4379
|
sessionCostRow,
|
|
4599
|
-
data?.isAvailable === false && /* @__PURE__ */ (0,
|
|
4600
|
-
/* @__PURE__ */ (0,
|
|
4601
|
-
/* @__PURE__ */ (0,
|
|
4380
|
+
data?.isAvailable === false && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-row", children: [
|
|
4381
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-label", children: t("common.status") }),
|
|
4382
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-value dsh-quota-error", children: t("common.unavailable") })
|
|
4602
4383
|
] })
|
|
4603
4384
|
] });
|
|
4604
4385
|
} else {
|
|
4605
|
-
body = /* @__PURE__ */ (0,
|
|
4606
|
-
/* @__PURE__ */ (0,
|
|
4386
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4387
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-error", children: t("widget.noBalance") }),
|
|
4607
4388
|
sessionCostRow
|
|
4608
4389
|
] });
|
|
4609
4390
|
}
|
|
4610
4391
|
}
|
|
4611
4392
|
const style = pos !== null ? { left: `${pos.x}px`, top: `${pos.y}px`, right: "auto", bottom: "auto" } : void 0;
|
|
4612
|
-
return /* @__PURE__ */ (0,
|
|
4393
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4613
4394
|
"div",
|
|
4614
4395
|
{
|
|
4615
4396
|
className: `dsh-quota-root${dragging ? " dsh-quota-dragging" : ""}${hidden ? " dsh-quota-root--hidden" : ""}`,
|
|
4616
4397
|
style,
|
|
4617
4398
|
"aria-hidden": hidden,
|
|
4618
|
-
children: /* @__PURE__ */ (0,
|
|
4399
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
4619
4400
|
"div",
|
|
4620
4401
|
{
|
|
4621
4402
|
ref: rootRef,
|
|
@@ -4627,14 +4408,14 @@ function QuotaWidget(props) {
|
|
|
4627
4408
|
onPointerCancel: () => endDrag(false),
|
|
4628
4409
|
onLostPointerCapture: () => endDrag(false),
|
|
4629
4410
|
children: [
|
|
4630
|
-
/* @__PURE__ */ (0,
|
|
4631
|
-
/* @__PURE__ */ (0,
|
|
4632
|
-
/* @__PURE__ */ (0,
|
|
4633
|
-
/* @__PURE__ */ (0,
|
|
4634
|
-
/* @__PURE__ */ (0,
|
|
4411
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-header", children: [
|
|
4412
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-title", children: [
|
|
4413
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-grip", title: t("widget.dragGrip"), children: "\u2807" }),
|
|
4414
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: dotClass }),
|
|
4415
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-name", children: t("widget.title") })
|
|
4635
4416
|
] }),
|
|
4636
|
-
/* @__PURE__ */ (0,
|
|
4637
|
-
/* @__PURE__ */ (0,
|
|
4417
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-actions", children: [
|
|
4418
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4638
4419
|
"button",
|
|
4639
4420
|
{
|
|
4640
4421
|
className: "dsh-quota-btn",
|
|
@@ -4650,7 +4431,7 @@ function QuotaWidget(props) {
|
|
|
4650
4431
|
children: "\u21BB"
|
|
4651
4432
|
}
|
|
4652
4433
|
),
|
|
4653
|
-
/* @__PURE__ */ (0,
|
|
4434
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4654
4435
|
"button",
|
|
4655
4436
|
{
|
|
4656
4437
|
className: "dsh-quota-btn",
|
|
@@ -4668,7 +4449,7 @@ function QuotaWidget(props) {
|
|
|
4668
4449
|
)
|
|
4669
4450
|
] })
|
|
4670
4451
|
] }),
|
|
4671
|
-
collapsed && primary !== null && /* @__PURE__ */ (0,
|
|
4452
|
+
collapsed && primary !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-collapsed-total-row", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: `dsh-quota-collapsed-total${low ? " dsh-quota-total--low" : ""}`, children: [
|
|
4672
4453
|
fmt(primary.total),
|
|
4673
4454
|
" ",
|
|
4674
4455
|
primary.currency
|
|
@@ -4699,7 +4480,7 @@ function conversationViewsSource(slots) {
|
|
|
4699
4480
|
}
|
|
4700
4481
|
|
|
4701
4482
|
// src/client/index.tsx
|
|
4702
|
-
var
|
|
4483
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
4703
4484
|
var name = "dsh-usage-dashboard";
|
|
4704
4485
|
var inject = ["slots", "locale", "layout"];
|
|
4705
4486
|
var openQuotaTab = null;
|
|
@@ -4720,15 +4501,11 @@ function apply(ctx) {
|
|
|
4720
4501
|
});
|
|
4721
4502
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register(
|
|
4722
4503
|
{ name: "shell.overlay", id: "deepseek-quota", order: 1e3, locale: NS, label: () => t("widget.title") },
|
|
4723
|
-
({ t: slotT, useSessions }) => /* @__PURE__ */ (0,
|
|
4504
|
+
({ t: slotT, useSessions }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(QuotaWidget, { useSessions, views }) }) })
|
|
4724
4505
|
));
|
|
4725
4506
|
ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register(
|
|
4726
4507
|
{ name: "conversation.composer.dock", id: "quota-cost", order: 2, locale: NS },
|
|
4727
|
-
({ t: slotT, sessionId }) => /* @__PURE__ */ (0,
|
|
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 }) }) })
|
|
4508
|
+
({ t: slotT, sessionId }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SessionCostPill, { sessionId }) }) })
|
|
4732
4509
|
));
|
|
4733
4510
|
ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register(
|
|
4734
4511
|
{
|
|
@@ -4738,13 +4515,13 @@ function apply(ctx) {
|
|
|
4738
4515
|
locale: NS,
|
|
4739
4516
|
label: () => t("messageCost.label")
|
|
4740
4517
|
},
|
|
4741
|
-
({ t: slotT, sessionId, messageId }) => /* @__PURE__ */ (0,
|
|
4518
|
+
({ t: slotT, sessionId, messageId }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MessageCost, { sessionId, messageId }) }) })
|
|
4742
4519
|
));
|
|
4743
4520
|
const QuotaView = ({ t: slotT, sessionId, openView }) => {
|
|
4744
|
-
(0,
|
|
4521
|
+
(0, import_react8.useEffect)(() => {
|
|
4745
4522
|
if (openView !== void 0) openQuotaTab = openView;
|
|
4746
4523
|
}, [openView]);
|
|
4747
|
-
return /* @__PURE__ */ (0,
|
|
4524
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorBoundary, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(BalanceDashboard, { sessionId, views }) }) });
|
|
4748
4525
|
};
|
|
4749
4526
|
ctx.effect(() => {
|
|
4750
4527
|
let disposeView = null;
|
|
@@ -4780,14 +4557,14 @@ function apply(ctx) {
|
|
|
4780
4557
|
disposeView?.();
|
|
4781
4558
|
};
|
|
4782
4559
|
}, "dsh-usage-dashboard: quota tab registration");
|
|
4783
|
-
const QuotaPanel = ({ t: slotT }) => /* @__PURE__ */ (0,
|
|
4784
|
-
/* @__PURE__ */ (0,
|
|
4785
|
-
/* @__PURE__ */ (0,
|
|
4786
|
-
/* @__PURE__ */ (0,
|
|
4560
|
+
const QuotaPanel = ({ t: slotT }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dq-panel", children: [
|
|
4561
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dq-panel-head", "data-window-drag": true, children: [
|
|
4562
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { className: "dq-panel-title", children: slotT("nav.quota") }),
|
|
4563
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dq-panel-caption", children: slotT("panel.caption") })
|
|
4787
4564
|
] }),
|
|
4788
|
-
/* @__PURE__ */ (0,
|
|
4565
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorBoundary, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(BalanceDashboard, { views }) }) })
|
|
4789
4566
|
] });
|
|
4790
|
-
const QuotaGlyph = ({ size, active }) => /* @__PURE__ */ (0,
|
|
4567
|
+
const QuotaGlyph = ({ size, active }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
4791
4568
|
"svg",
|
|
4792
4569
|
{
|
|
4793
4570
|
className: "dq-side-icon",
|
|
@@ -4803,23 +4580,23 @@ function apply(ctx) {
|
|
|
4803
4580
|
"aria-hidden": "true",
|
|
4804
4581
|
focusable: "false",
|
|
4805
4582
|
children: [
|
|
4806
|
-
/* @__PURE__ */ (0,
|
|
4807
|
-
/* @__PURE__ */ (0,
|
|
4808
|
-
/* @__PURE__ */ (0,
|
|
4809
|
-
/* @__PURE__ */ (0,
|
|
4583
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M3.5 3.3 8 8.7l4.5-5.4" }),
|
|
4584
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M8 8.7V13.2" }),
|
|
4585
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M3.5 10.2h9" }),
|
|
4586
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M3.5 12.1h9" })
|
|
4810
4587
|
]
|
|
4811
4588
|
}
|
|
4812
4589
|
);
|
|
4813
4590
|
const QuotaFooterButton = ({ wide, t: slotT }) => {
|
|
4814
|
-
const [active, setActive] = (0,
|
|
4815
|
-
(0,
|
|
4591
|
+
const [active, setActive] = (0, import_react8.useState)(() => ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
|
|
4592
|
+
(0, import_react8.useEffect)(() => {
|
|
4816
4593
|
const sync = () => {
|
|
4817
4594
|
setActive(ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
|
|
4818
4595
|
};
|
|
4819
4596
|
sync();
|
|
4820
4597
|
return ctx.layout.panelInfo.subscribe(sync);
|
|
4821
4598
|
}, []);
|
|
4822
|
-
return /* @__PURE__ */ (0,
|
|
4599
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
4823
4600
|
"button",
|
|
4824
4601
|
{
|
|
4825
4602
|
type: "button",
|
|
@@ -4831,8 +4608,8 @@ function apply(ctx) {
|
|
|
4831
4608
|
ctx.layout.selectPanel(active ? null : QUOTA_PANEL_ID);
|
|
4832
4609
|
},
|
|
4833
4610
|
children: [
|
|
4834
|
-
/* @__PURE__ */ (0,
|
|
4835
|
-
wide && /* @__PURE__ */ (0,
|
|
4611
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dq-side-entry-icon", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(QuotaGlyph, { size: 16 }) }),
|
|
4612
|
+
wide && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dq-side-entry-label", children: slotT("nav.quota") })
|
|
4836
4613
|
]
|
|
4837
4614
|
}
|
|
4838
4615
|
);
|