@cassius0924/dsh-usage-dashboard 0.8.3 → 0.9.1
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 +795 -1020
- 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/dom.d.ts +11 -5
- 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
|
}
|
|
@@ -1359,10 +1248,8 @@ function getShellFrame(node) {
|
|
|
1359
1248
|
return overlay?.parentElement ?? null;
|
|
1360
1249
|
}
|
|
1361
1250
|
function getComposerElement(frame) {
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
}
|
|
1365
|
-
return slotElement(frame, "conversation.composer.dock", "ancestor");
|
|
1251
|
+
const root = frame ?? document.documentElement;
|
|
1252
|
+
return slotElement(root, "conversation.composer", "ancestor") ?? slotElement(root, "conversation.composer.dock", "ancestor");
|
|
1366
1253
|
}
|
|
1367
1254
|
function getComposerInputElement(frame) {
|
|
1368
1255
|
const root = frame ?? document.documentElement;
|
|
@@ -1389,9 +1276,9 @@ function getActiveConversationViewId(frame, viewIds) {
|
|
|
1389
1276
|
}
|
|
1390
1277
|
|
|
1391
1278
|
// src/client/export.ts
|
|
1392
|
-
var
|
|
1279
|
+
var BEIJING_OFFSET_MS2 = 8 * 60 * 60 * 1e3;
|
|
1393
1280
|
function exportDateStamp(nowMs = Date.now()) {
|
|
1394
|
-
const date = new Date(nowMs +
|
|
1281
|
+
const date = new Date(nowMs + BEIJING_OFFSET_MS2);
|
|
1395
1282
|
return [
|
|
1396
1283
|
date.getUTCFullYear(),
|
|
1397
1284
|
String(date.getUTCMonth() + 1).padStart(2, "0"),
|
|
@@ -1507,19 +1394,19 @@ var isBoolean = (value) => typeof value === "boolean";
|
|
|
1507
1394
|
|
|
1508
1395
|
// src/client/store.ts
|
|
1509
1396
|
function createStore(key, isValid, fallback) {
|
|
1510
|
-
let
|
|
1511
|
-
const
|
|
1397
|
+
let current = loadPref(key, isValid, fallback);
|
|
1398
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1512
1399
|
return {
|
|
1513
|
-
get: () =>
|
|
1400
|
+
get: () => current,
|
|
1514
1401
|
set: (value) => {
|
|
1515
|
-
|
|
1402
|
+
current = value;
|
|
1516
1403
|
savePref(key, value);
|
|
1517
|
-
for (const listener of [...
|
|
1404
|
+
for (const listener of [...listeners]) listener();
|
|
1518
1405
|
},
|
|
1519
1406
|
subscribe: (fn) => {
|
|
1520
|
-
|
|
1407
|
+
listeners.add(fn);
|
|
1521
1408
|
return () => {
|
|
1522
|
-
|
|
1409
|
+
listeners.delete(fn);
|
|
1523
1410
|
};
|
|
1524
1411
|
}
|
|
1525
1412
|
};
|
|
@@ -1582,16 +1469,6 @@ function needsTopUp(balance, warningLine) {
|
|
|
1582
1469
|
// src/client/dashboard.tsx
|
|
1583
1470
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1584
1471
|
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
1472
|
function Skel(props) {
|
|
1596
1473
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-skel", style: { width: `${props.w}px`, height: `${props.h}px` } });
|
|
1597
1474
|
}
|
|
@@ -1641,18 +1518,19 @@ function HeatmapSkeleton() {
|
|
|
1641
1518
|
}
|
|
1642
1519
|
function Delta(props) {
|
|
1643
1520
|
const { t } = useI18n();
|
|
1644
|
-
const { current
|
|
1521
|
+
const { current, previous, label } = props;
|
|
1645
1522
|
if (previous <= 0) {
|
|
1646
|
-
if (
|
|
1523
|
+
if (current <= 0) return null;
|
|
1647
1524
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta dq-delta--new", title: t("delta.noBaselineTitle", { label }), children: t("delta.noBaseline") });
|
|
1648
1525
|
}
|
|
1649
|
-
const percent = Math.round((
|
|
1526
|
+
const percent = Math.round((current - previous) / previous * 100);
|
|
1650
1527
|
const title = `${label} \xA5${fmt(previous)}`;
|
|
1651
1528
|
if (percent === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta dq-delta--flat", title, children: t("delta.flat") });
|
|
1652
1529
|
const up = percent > 0;
|
|
1530
|
+
const magnitude = Math.abs(percent) >= 1e3 ? "999+" : String(Math.abs(percent));
|
|
1653
1531
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: `dq-delta ${up ? "dq-delta--up" : "dq-delta--down"}`, title, children: [
|
|
1654
1532
|
up ? "\u2191" : "\u2193",
|
|
1655
|
-
|
|
1533
|
+
magnitude,
|
|
1656
1534
|
"% ",
|
|
1657
1535
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta-label", children: label })
|
|
1658
1536
|
] });
|
|
@@ -1734,7 +1612,7 @@ function PricingNote(props) {
|
|
|
1734
1612
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.inputCacheMiss") }),
|
|
1735
1613
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.output") })
|
|
1736
1614
|
] }) }),
|
|
1737
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: pricing.tiers.map((tier) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1615
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: pricing.tiers.map((tier) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react4.Fragment, { children: [
|
|
1738
1616
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { children: [
|
|
1739
1617
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { rowSpan: 2, children: tier.model }),
|
|
1740
1618
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: t("pricing.peak") }),
|
|
@@ -1810,6 +1688,137 @@ function CardTitle(props) {
|
|
|
1810
1688
|
props.caption !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-card-caption", children: props.caption })
|
|
1811
1689
|
] });
|
|
1812
1690
|
}
|
|
1691
|
+
function SectionNav(props) {
|
|
1692
|
+
const { rootRef, label, sections } = props;
|
|
1693
|
+
const [targets, setTargets] = (0, import_react4.useState)([]);
|
|
1694
|
+
const [active, setActive] = (0, import_react4.useState)(0);
|
|
1695
|
+
const [pos, setPos] = (0, import_react4.useState)({ right: 0, visible: false });
|
|
1696
|
+
const signature = sections.map((section) => `${section.key}:${section.label}`).join("|");
|
|
1697
|
+
(0, import_react4.useEffect)(() => {
|
|
1698
|
+
const root = rootRef.current;
|
|
1699
|
+
if (root === null) return;
|
|
1700
|
+
const nodes = Array.from(root.querySelectorAll(":scope > .dq-card, :scope > .dq-card-pair"));
|
|
1701
|
+
setTargets(nodes.map((el, i) => ({ el, label: sections[i]?.label ?? "" })).filter((item) => item.label !== ""));
|
|
1702
|
+
}, [rootRef, signature]);
|
|
1703
|
+
(0, import_react4.useEffect)(() => {
|
|
1704
|
+
const root = rootRef.current;
|
|
1705
|
+
if (root === null) return;
|
|
1706
|
+
const scrollport = root.closest("[data-conversation-scroll]");
|
|
1707
|
+
const measure = () => {
|
|
1708
|
+
const rect = root.getBoundingClientRect();
|
|
1709
|
+
setPos({ right: window.innerWidth - rect.left + 12, visible: window.innerWidth >= 1200 && rect.left >= 170 });
|
|
1710
|
+
};
|
|
1711
|
+
measure();
|
|
1712
|
+
const scroller = scrollport ?? window;
|
|
1713
|
+
scroller.addEventListener("scroll", measure, { passive: true });
|
|
1714
|
+
window.addEventListener("resize", measure);
|
|
1715
|
+
return () => {
|
|
1716
|
+
scroller.removeEventListener("scroll", measure);
|
|
1717
|
+
window.removeEventListener("resize", measure);
|
|
1718
|
+
};
|
|
1719
|
+
}, [rootRef]);
|
|
1720
|
+
(0, import_react4.useEffect)(() => {
|
|
1721
|
+
if (targets.length === 0) return;
|
|
1722
|
+
const root = rootRef.current;
|
|
1723
|
+
if (root === null) return;
|
|
1724
|
+
const scrollport = root.closest("[data-conversation-scroll]");
|
|
1725
|
+
const onScroll = () => {
|
|
1726
|
+
const base = scrollport === null ? 0 : scrollport.getBoundingClientRect().top;
|
|
1727
|
+
let current = 0;
|
|
1728
|
+
targets.forEach((target, i) => {
|
|
1729
|
+
if (target.el.getBoundingClientRect().top - base <= 120) current = i;
|
|
1730
|
+
});
|
|
1731
|
+
setActive(current);
|
|
1732
|
+
};
|
|
1733
|
+
onScroll();
|
|
1734
|
+
const scroller = scrollport ?? window;
|
|
1735
|
+
scroller.addEventListener("scroll", onScroll, { passive: true });
|
|
1736
|
+
return () => scroller.removeEventListener("scroll", onScroll);
|
|
1737
|
+
}, [targets, rootRef]);
|
|
1738
|
+
if (targets.length < 3) return null;
|
|
1739
|
+
const go = (el) => {
|
|
1740
|
+
const root = rootRef.current;
|
|
1741
|
+
const scrollport = root?.closest("[data-conversation-scroll]");
|
|
1742
|
+
const reduce = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1743
|
+
const behavior = reduce ? "auto" : "smooth";
|
|
1744
|
+
if (scrollport !== null) {
|
|
1745
|
+
const base = scrollport.getBoundingClientRect().top;
|
|
1746
|
+
scrollport.scrollTo({ top: scrollport.scrollTop + el.getBoundingClientRect().top - base - 12, behavior });
|
|
1747
|
+
} else {
|
|
1748
|
+
window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 12, behavior });
|
|
1749
|
+
}
|
|
1750
|
+
};
|
|
1751
|
+
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)(
|
|
1752
|
+
"button",
|
|
1753
|
+
{
|
|
1754
|
+
type: "button",
|
|
1755
|
+
className: `dq-sectionnav-dot${i === active ? " dq-sectionnav-dot--active" : ""}`,
|
|
1756
|
+
"aria-current": i === active ? "true" : void 0,
|
|
1757
|
+
onClick: () => go(target.el),
|
|
1758
|
+
children: [
|
|
1759
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-sectionnav-label", children: target.label }),
|
|
1760
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-sectionnav-pip", "aria-hidden": "true" })
|
|
1761
|
+
]
|
|
1762
|
+
}
|
|
1763
|
+
) }, `${target.label}-${String(i)}`)) }) });
|
|
1764
|
+
}
|
|
1765
|
+
function Collapsible(props) {
|
|
1766
|
+
const ref = (0, import_react4.useRef)(null);
|
|
1767
|
+
const animationRef = (0, import_react4.useRef)(null);
|
|
1768
|
+
const open = props.open;
|
|
1769
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
1770
|
+
const el = ref.current;
|
|
1771
|
+
if (el === null) return;
|
|
1772
|
+
animationRef.current?.cancel();
|
|
1773
|
+
animationRef.current = null;
|
|
1774
|
+
const reduce = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1775
|
+
const canAnimate = typeof el.animate === "function" && !reduce;
|
|
1776
|
+
const reset = () => {
|
|
1777
|
+
el.style.height = "";
|
|
1778
|
+
el.style.overflow = "";
|
|
1779
|
+
el.style.opacity = "";
|
|
1780
|
+
};
|
|
1781
|
+
if (open) {
|
|
1782
|
+
if (!canAnimate) {
|
|
1783
|
+
el.style.display = "";
|
|
1784
|
+
reset();
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
el.style.display = "block";
|
|
1788
|
+
el.style.overflow = "hidden";
|
|
1789
|
+
el.style.height = "0px";
|
|
1790
|
+
el.style.opacity = "0";
|
|
1791
|
+
const height2 = el.scrollHeight;
|
|
1792
|
+
const animation2 = el.animate(
|
|
1793
|
+
[{ height: "0px", opacity: 0 }, { height: `${height2}px`, opacity: 1 }],
|
|
1794
|
+
{ duration: 300, easing: "cubic-bezier(0.16, 1, 0.3, 1)" }
|
|
1795
|
+
);
|
|
1796
|
+
animationRef.current = animation2;
|
|
1797
|
+
animation2.addEventListener("finish", reset);
|
|
1798
|
+
return () => animation2.cancel();
|
|
1799
|
+
}
|
|
1800
|
+
if (el.style.display === "none") return;
|
|
1801
|
+
if (!canAnimate) {
|
|
1802
|
+
reset();
|
|
1803
|
+
el.style.display = "none";
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
const height = el.getBoundingClientRect().height || el.scrollHeight;
|
|
1807
|
+
el.style.overflow = "hidden";
|
|
1808
|
+
el.style.height = `${height}px`;
|
|
1809
|
+
const animation = el.animate(
|
|
1810
|
+
[{ height: `${height}px`, opacity: 1 }, { height: "0px", opacity: 0 }],
|
|
1811
|
+
{ duration: 220, easing: "cubic-bezier(0.4, 0, 1, 1)" }
|
|
1812
|
+
);
|
|
1813
|
+
animationRef.current = animation;
|
|
1814
|
+
animation.addEventListener("finish", () => {
|
|
1815
|
+
reset();
|
|
1816
|
+
el.style.display = "none";
|
|
1817
|
+
});
|
|
1818
|
+
return () => animation.cancel();
|
|
1819
|
+
}, [open]);
|
|
1820
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref, children: props.children });
|
|
1821
|
+
}
|
|
1813
1822
|
function Stat(props) {
|
|
1814
1823
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
1815
1824
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: props.label }),
|
|
@@ -1821,13 +1830,13 @@ function Link(props) {
|
|
|
1821
1830
|
}
|
|
1822
1831
|
function ExportMenu(props) {
|
|
1823
1832
|
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,
|
|
1833
|
+
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
1834
|
+
const [done, setDone] = (0, import_react4.useState)(false);
|
|
1835
|
+
const rootRef = (0, import_react4.useRef)(null);
|
|
1836
|
+
const triggerRef = (0, import_react4.useRef)(null);
|
|
1837
|
+
const menuRef = (0, import_react4.useRef)(null);
|
|
1838
|
+
const doneTimerRef = (0, import_react4.useRef)(null);
|
|
1839
|
+
(0, import_react4.useEffect)(() => {
|
|
1831
1840
|
if (!open) return;
|
|
1832
1841
|
const onDown = (event) => {
|
|
1833
1842
|
if (rootRef.current !== null && !rootRef.current.contains(event.target)) setOpen(false);
|
|
@@ -1845,7 +1854,7 @@ function ExportMenu(props) {
|
|
|
1845
1854
|
document.removeEventListener("keydown", onKey);
|
|
1846
1855
|
};
|
|
1847
1856
|
}, [open]);
|
|
1848
|
-
(0,
|
|
1857
|
+
(0, import_react4.useEffect)(() => () => {
|
|
1849
1858
|
if (doneTimerRef.current !== null) window.clearTimeout(doneTimerRef.current);
|
|
1850
1859
|
}, []);
|
|
1851
1860
|
const finish = () => {
|
|
@@ -2015,7 +2024,8 @@ function CacheCard(props) {
|
|
|
2015
2024
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-legend-swatch dq-legend-swatch--miss" }),
|
|
2016
2025
|
t("cache.missLegend", { tokens: fmtCompact(totals.input, locale) })
|
|
2017
2026
|
] })
|
|
2018
|
-
] })
|
|
2027
|
+
] }),
|
|
2028
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-cache-foot", children: t("cache.basis", { cost: fmt(totals.cost), miss: fmt(wouldHaveCost) }) })
|
|
2019
2029
|
] });
|
|
2020
2030
|
}
|
|
2021
2031
|
function VisionCard(props) {
|
|
@@ -2058,11 +2068,44 @@ function VisionCard(props) {
|
|
|
2058
2068
|
function PeakCard(props) {
|
|
2059
2069
|
const { t, locale } = useI18n();
|
|
2060
2070
|
const { split, pricing } = props;
|
|
2071
|
+
const [nowMs, setNowMs] = (0, import_react4.useState)(() => Date.now());
|
|
2072
|
+
(0, import_react4.useEffect)(() => {
|
|
2073
|
+
const id = setInterval(() => setNowMs(Date.now()), 6e4);
|
|
2074
|
+
return () => clearInterval(id);
|
|
2075
|
+
}, []);
|
|
2061
2076
|
const total = split.peak.total + split.offPeak.total;
|
|
2062
2077
|
if (total === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("peak.empty") });
|
|
2063
2078
|
const peakShare = split.peak.total / total;
|
|
2064
2079
|
const shiftSaving = split.peakEraCost - split.offPeakEraCost;
|
|
2080
|
+
const beijing = new Date(nowMs + 8 * 36e5);
|
|
2081
|
+
const hour = beijing.getUTCHours();
|
|
2082
|
+
const minute = beijing.getUTCMinutes();
|
|
2083
|
+
const weekday = beijing.getUTCDay();
|
|
2084
|
+
const weekend = weekday === 0 || weekday === 6;
|
|
2085
|
+
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])]);
|
|
2086
|
+
const isPeakNow = !weekend && windows.some(([from, to]) => hour >= from && hour < to);
|
|
2087
|
+
const nowPercent = (hour + minute / 60) / 24 * 100;
|
|
2088
|
+
const clockCells = Array.from({ length: 24 }, (_, h) => !weekend && windows.some(([from, to]) => h >= from && h < to));
|
|
2089
|
+
const nowState = isPeakNow ? t("peak.nowPeak") : t("peak.nowOff");
|
|
2065
2090
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-peak", children: [
|
|
2091
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock", children: [
|
|
2092
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock-head", children: [
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-clock-title", children: t("peak.clockTitle") }),
|
|
2094
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-clock-status ${isPeakNow ? "dq-clock-status--peak" : "dq-clock-status--off"}`, children: nowState })
|
|
2095
|
+
] }),
|
|
2096
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock-track", role: "img", "aria-label": t("peak.clockAria", { state: nowState }), children: [
|
|
2097
|
+
clockCells.map((peak, h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-clock-cell${peak ? " dq-clock-cell--peak" : ""}` }, h)),
|
|
2098
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-clock-now", style: { left: `${nowPercent}%` }, "aria-hidden": "true" })
|
|
2099
|
+
] }),
|
|
2100
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock-scale", "aria-hidden": "true", children: [
|
|
2101
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "0" }),
|
|
2102
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "6" }),
|
|
2103
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "12" }),
|
|
2104
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "18" }),
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "24" })
|
|
2106
|
+
] }),
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-clock-hint", children: t("peak.clockHint") })
|
|
2108
|
+
] }),
|
|
2066
2109
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2067
2110
|
"div",
|
|
2068
2111
|
{
|
|
@@ -2092,12 +2135,9 @@ function PeakCard(props) {
|
|
|
2092
2135
|
] }),
|
|
2093
2136
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-figures", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
2094
2137
|
/* @__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
|
-
] }) })
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-value dq-stat-value--ok", children: t("peak.saving", { amount: fmt(shiftSaving) }) })
|
|
2139
|
+
] }) }),
|
|
2140
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-peak-foot", children: t("peak.basis", { total: fmt(split.offPeakEraCost) }) })
|
|
2101
2141
|
] });
|
|
2102
2142
|
}
|
|
2103
2143
|
function agoText(ms, t) {
|
|
@@ -2119,8 +2159,8 @@ function SessionDetail(props) {
|
|
|
2119
2159
|
function SessionRanking(props) {
|
|
2120
2160
|
const { t, locale } = useI18n();
|
|
2121
2161
|
const { sessions, count, totalCost } = props;
|
|
2122
|
-
const [details, setDetails] = (0,
|
|
2123
|
-
const startedRef = (0,
|
|
2162
|
+
const [details, setDetails] = (0, import_react4.useState)({});
|
|
2163
|
+
const startedRef = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
|
|
2124
2164
|
const loadDetail = (id) => {
|
|
2125
2165
|
if (startedRef.current.has(id)) return;
|
|
2126
2166
|
startedRef.current.add(id);
|
|
@@ -2221,11 +2261,11 @@ function ModelRanking(props) {
|
|
|
2221
2261
|
function ModelPicker(props) {
|
|
2222
2262
|
const { t, locale } = useI18n();
|
|
2223
2263
|
const { models } = props;
|
|
2224
|
-
const [open, setOpen] = (0,
|
|
2225
|
-
const rootRef = (0,
|
|
2226
|
-
const triggerRef = (0,
|
|
2227
|
-
const menuRef = (0,
|
|
2228
|
-
(0,
|
|
2264
|
+
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
2265
|
+
const rootRef = (0, import_react4.useRef)(null);
|
|
2266
|
+
const triggerRef = (0, import_react4.useRef)(null);
|
|
2267
|
+
const menuRef = (0, import_react4.useRef)(null);
|
|
2268
|
+
(0, import_react4.useEffect)(() => {
|
|
2229
2269
|
if (!open) return;
|
|
2230
2270
|
const onDown = (e) => {
|
|
2231
2271
|
if (rootRef.current !== null && !rootRef.current.contains(e.target)) setOpen(false);
|
|
@@ -2327,55 +2367,46 @@ function BalanceDashboard(props) {
|
|
|
2327
2367
|
const cachedBalance = getCachedBalance();
|
|
2328
2368
|
const cachedUsage = getCachedUsage();
|
|
2329
2369
|
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)(() => {
|
|
2370
|
+
const [balance, setBalance] = (0, import_react4.useState)(cachedBalance?.data ?? null);
|
|
2371
|
+
const [usage, setUsage] = (0, import_react4.useState)(cachedUsage?.data ?? null);
|
|
2372
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
2373
|
+
const [notice, setNotice] = (0, import_react4.useState)(null);
|
|
2374
|
+
const [loadingBalance, setLoadingBalance] = (0, import_react4.useState)(cachedBalance === null);
|
|
2375
|
+
const [loadingUsage, setLoadingUsage] = (0, import_react4.useState)(cachedUsage === null);
|
|
2376
|
+
const [refreshing, setRefreshing] = (0, import_react4.useState)(false);
|
|
2377
|
+
const [usageUpdatedAt, setUsageUpdatedAt] = (0, import_react4.useState)(cachedUsageAt);
|
|
2378
|
+
const [syncState, setSyncState] = (0, import_react4.useState)(cachedUsageAt === null ? "syncing" : "cached");
|
|
2379
|
+
const [freshnessNow, setFreshnessNow] = (0, import_react4.useState)(Date.now());
|
|
2380
|
+
const [settingsOpen, setSettingsOpen] = (0, import_react4.useState)(false);
|
|
2381
|
+
const hadDataRef = (0, import_react4.useRef)(cachedBalance !== null || cachedUsage !== null);
|
|
2382
|
+
const [widgetOn, setWidgetOn] = (0, import_react4.useState)(widgetVisibleStore.get());
|
|
2383
|
+
const [widgetTabIds, setWidgetTabIds] = (0, import_react4.useState)(widgetTabIdsStore.get());
|
|
2384
|
+
const [viewTabs, setViewTabs] = (0, import_react4.useState)(() => props.views.list());
|
|
2385
|
+
(0, import_react4.useEffect)(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views]);
|
|
2386
|
+
const [lowBalance, setLowBalance] = (0, import_react4.useState)(lowBalanceStore.get());
|
|
2387
|
+
const [monthlyBudget, setMonthlyBudget] = (0, import_react4.useState)(monthlyBudgetStore.get());
|
|
2388
|
+
const [barMode, setBarMode] = (0, import_react4.useState)("daily");
|
|
2389
|
+
const [selectedModels, setSelectedModels] = (0, import_react4.useState)([]);
|
|
2390
|
+
const [windowDays, setWindowDays] = (0, import_react4.useState)(usageWindowStore.get());
|
|
2391
|
+
const [chartMetric, setChartMetric] = (0, import_react4.useState)(chartMetricStore.get());
|
|
2392
|
+
const [sidebarEntry, setSidebarEntry] = (0, import_react4.useState)(sidebarEntryStore.get());
|
|
2393
|
+
(0, import_react4.useEffect)(() => sidebarEntryStore.subscribe(() => setSidebarEntry(sidebarEntryStore.get())), []);
|
|
2394
|
+
const [tabDragging, setTabDragging] = (0, import_react4.useState)(false);
|
|
2395
|
+
const [tabDropTarget, setTabDropTarget] = (0, import_react4.useState)(null);
|
|
2396
|
+
const [retainedVision, setRetainedVision] = (0, import_react4.useState)(null);
|
|
2397
|
+
const [runwayHovering, setRunwayHovering] = (0, import_react4.useState)(false);
|
|
2398
|
+
const [runwayManuallyOpened, setRunwayManuallyOpened] = (0, import_react4.useState)(false);
|
|
2399
|
+
const [remainingHovering, setRemainingHovering] = (0, import_react4.useState)(false);
|
|
2400
|
+
const [remainingManuallyOpened, setRemainingManuallyOpened] = (0, import_react4.useState)(false);
|
|
2401
|
+
const budgetInputRef = (0, import_react4.useRef)(null);
|
|
2402
|
+
const rootRef = (0, import_react4.useRef)(null);
|
|
2403
|
+
const [composerHeight, setComposerHeight] = (0, import_react4.useState)(null);
|
|
2404
|
+
const [composerWidth, setComposerWidth] = (0, import_react4.useState)(null);
|
|
2405
|
+
const [sessionUsage, setSessionUsage] = (0, import_react4.useState)(null);
|
|
2406
|
+
const [loadingSessionUsage, setLoadingSessionUsage] = (0, import_react4.useState)(props.sessionId !== void 0 && props.sessionId !== "");
|
|
2407
|
+
const [sessionUsageError, setSessionUsageError] = (0, import_react4.useState)(null);
|
|
2408
|
+
const sessionUsageIdRef = (0, import_react4.useRef)(null);
|
|
2409
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
2379
2410
|
const node = rootRef.current;
|
|
2380
2411
|
if (node === null) return;
|
|
2381
2412
|
const measure = () => {
|
|
@@ -2406,13 +2437,13 @@ function BalanceDashboard(props) {
|
|
|
2406
2437
|
window.removeEventListener("resize", measure);
|
|
2407
2438
|
};
|
|
2408
2439
|
}, []);
|
|
2409
|
-
(0,
|
|
2440
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
2410
2441
|
const node = rootRef.current;
|
|
2411
2442
|
if (node === null) return;
|
|
2412
2443
|
const scrollport = node.closest("[data-conversation-scroll]");
|
|
2413
2444
|
if (scrollport instanceof HTMLElement) scrollport.scrollTop = 0;
|
|
2414
2445
|
}, [props.sessionId]);
|
|
2415
|
-
(0,
|
|
2446
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
2416
2447
|
quotaViewActiveStore.set(true);
|
|
2417
2448
|
return () => quotaViewActiveStore.set(false);
|
|
2418
2449
|
}, []);
|
|
@@ -2486,10 +2517,10 @@ function BalanceDashboard(props) {
|
|
|
2486
2517
|
if (sessionUsageIdRef.current === id) setLoadingSessionUsage(false);
|
|
2487
2518
|
}
|
|
2488
2519
|
};
|
|
2489
|
-
(0,
|
|
2520
|
+
(0, import_react4.useEffect)(() => {
|
|
2490
2521
|
void load(false);
|
|
2491
2522
|
}, []);
|
|
2492
|
-
(0,
|
|
2523
|
+
(0, import_react4.useEffect)(() => {
|
|
2493
2524
|
const id = props.sessionId;
|
|
2494
2525
|
sessionUsageIdRef.current = id ?? null;
|
|
2495
2526
|
if (id === void 0 || id === "") {
|
|
@@ -2503,57 +2534,7 @@ function BalanceDashboard(props) {
|
|
|
2503
2534
|
setLoadingSessionUsage(true);
|
|
2504
2535
|
void loadSessionUsage(id, false);
|
|
2505
2536
|
}, [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)(() => {
|
|
2537
|
+
(0, import_react4.useEffect)(() => {
|
|
2557
2538
|
const timer = window.setInterval(() => setFreshnessNow(Date.now()), 3e4);
|
|
2558
2539
|
return () => window.clearInterval(timer);
|
|
2559
2540
|
}, []);
|
|
@@ -2577,66 +2558,6 @@ function BalanceDashboard(props) {
|
|
|
2577
2558
|
setMonthlyBudget(value);
|
|
2578
2559
|
monthlyBudgetStore.set(value);
|
|
2579
2560
|
};
|
|
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
2561
|
const changeWindowDays = (value) => {
|
|
2641
2562
|
setWindowDays(value);
|
|
2642
2563
|
usageWindowStore.set(value);
|
|
@@ -2732,7 +2653,7 @@ function BalanceDashboard(props) {
|
|
|
2732
2653
|
costTotal: window2.totals.cost
|
|
2733
2654
|
});
|
|
2734
2655
|
const liveVision = visionOpen && activeWindow !== null && visionStats !== void 0 ? visionPayload(activeWindow, visionStats, activeWindowName) : null;
|
|
2735
|
-
(0,
|
|
2656
|
+
(0, import_react4.useEffect)(() => {
|
|
2736
2657
|
if (visionOpen && activeWindow !== null && visionStats !== void 0) {
|
|
2737
2658
|
setRetainedVision(visionPayload(activeWindow, visionStats, activeWindowName));
|
|
2738
2659
|
return;
|
|
@@ -2816,15 +2737,6 @@ function BalanceDashboard(props) {
|
|
|
2816
2737
|
{ error: localizeApiError(notice.error, t, "error.query") }
|
|
2817
2738
|
);
|
|
2818
2739
|
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
2740
|
const composerSafeAreaStyle = {
|
|
2829
2741
|
"--dq-composer-h": `${composerHeight ?? COMPOSER_FALLBACK_PX}px`,
|
|
2830
2742
|
// Only set once measured; .dq-balance's max-width falls back to 860px
|
|
@@ -2832,7 +2744,18 @@ function BalanceDashboard(props) {
|
|
|
2832
2744
|
// column, then narrows in place — no flash of the old width).
|
|
2833
2745
|
...composerWidth !== null ? { "--dq-composer-w": `${composerWidth}px` } : {}
|
|
2834
2746
|
};
|
|
2747
|
+
const navSections = [
|
|
2748
|
+
{ key: "balance", label: t("nav.balance") },
|
|
2749
|
+
...props.sessionId !== void 0 && props.sessionId !== "" ? [{ key: "session", label: t("nav.session") }] : [],
|
|
2750
|
+
{ key: "overview", label: t("nav.overview") },
|
|
2751
|
+
{ key: "trend", label: t("nav.trend") },
|
|
2752
|
+
{ key: "heatmap", label: t("nav.heatmap") },
|
|
2753
|
+
{ key: "models", label: t("nav.models") },
|
|
2754
|
+
{ key: "sessions", label: t("nav.sessions") },
|
|
2755
|
+
{ key: "peakcache", label: t("nav.peakcache") }
|
|
2756
|
+
];
|
|
2835
2757
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-balance", ref: rootRef, style: composerSafeAreaStyle, children: [
|
|
2758
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SectionNav, { rootRef, label: t("nav.label"), sections: navSections }),
|
|
2836
2759
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-row", children: [
|
|
2837
2760
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-messages", children: [
|
|
2838
2761
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
@@ -2851,23 +2774,39 @@ function BalanceDashboard(props) {
|
|
|
2851
2774
|
noticeText !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-warn", children: noticeText }),
|
|
2852
2775
|
errorText !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-error", children: errorText })
|
|
2853
2776
|
] }),
|
|
2854
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2777
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-actions", children: [
|
|
2778
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2779
|
+
"button",
|
|
2780
|
+
{
|
|
2781
|
+
type: "button",
|
|
2782
|
+
className: "dq-settings-btn",
|
|
2783
|
+
"aria-expanded": settingsOpen,
|
|
2784
|
+
"aria-controls": "dq-settings-panel",
|
|
2785
|
+
onClick: () => setSettingsOpen((open) => !open),
|
|
2786
|
+
children: [
|
|
2787
|
+
t("settings.title"),
|
|
2788
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-settings-caret${settingsOpen ? " dq-settings-caret--open" : ""}`, "aria-hidden": "true", children: "\u25BE" })
|
|
2789
|
+
]
|
|
2790
|
+
}
|
|
2791
|
+
),
|
|
2792
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2793
|
+
"button",
|
|
2794
|
+
{
|
|
2795
|
+
type: "button",
|
|
2796
|
+
className: "dq-refresh-btn",
|
|
2797
|
+
title: t("status.forceRefresh"),
|
|
2798
|
+
disabled: refreshing,
|
|
2799
|
+
"aria-busy": refreshing,
|
|
2800
|
+
onClick: () => {
|
|
2801
|
+
void load(true);
|
|
2802
|
+
},
|
|
2803
|
+
children: [
|
|
2804
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-refresh-icon${refreshing ? " dq-refresh-icon--spin" : ""}`, children: "\u21BB" }),
|
|
2805
|
+
refreshing ? t("status.refreshing") : t("status.refresh")
|
|
2806
|
+
]
|
|
2807
|
+
}
|
|
2808
|
+
)
|
|
2809
|
+
] })
|
|
2871
2810
|
] }),
|
|
2872
2811
|
balanceLow && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-alert", role: "status", children: [
|
|
2873
2812
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-alert-icon", "aria-hidden": "true", children: "!" }),
|
|
@@ -2949,6 +2888,144 @@ function BalanceDashboard(props) {
|
|
|
2949
2888
|
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
2889
|
] }) : loadingBalance ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BalanceSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("balance.empty") })
|
|
2951
2890
|
] }),
|
|
2891
|
+
/* @__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: [
|
|
2892
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("settings.title") }),
|
|
2893
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
2894
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("settings.groupDisplay") }),
|
|
2895
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-toggle", children: [
|
|
2896
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", checked: widgetOn, onChange: toggle }),
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("settings.widget") })
|
|
2898
|
+
] }),
|
|
2899
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-toggle-hint", children: t("settings.widgetHint") }),
|
|
2900
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting dq-setting--tabs", children: [
|
|
2901
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-widget-tabs-label", children: t("settings.widgetTabs") }),
|
|
2902
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control dq-widget-tabs", role: "group", "aria-labelledby": "dq-widget-tabs-label", children: [
|
|
2903
|
+
/* @__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: [
|
|
2904
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2905
|
+
"input",
|
|
2906
|
+
{
|
|
2907
|
+
type: "checkbox",
|
|
2908
|
+
checked: widgetTabIds === null || widgetTabIds.includes(tab.id),
|
|
2909
|
+
onChange: () => toggleWidgetTab(tab.id)
|
|
2910
|
+
}
|
|
2911
|
+
),
|
|
2912
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: tab.label })
|
|
2913
|
+
] }, tab.id)) }),
|
|
2914
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: widgetTabs.length === 0 ? t("settings.widgetTabsEmpty") : t("settings.widgetTabsHint") })
|
|
2915
|
+
] })
|
|
2916
|
+
] })
|
|
2917
|
+
] }),
|
|
2918
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
2919
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("settings.groupAlerts") }),
|
|
2920
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2921
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-low-balance", children: t("settings.lowBalance") }),
|
|
2922
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2923
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2924
|
+
"input",
|
|
2925
|
+
{
|
|
2926
|
+
id: "dq-low-balance",
|
|
2927
|
+
className: "dq-number",
|
|
2928
|
+
type: "number",
|
|
2929
|
+
min: 0,
|
|
2930
|
+
step: 1,
|
|
2931
|
+
value: lowBalance,
|
|
2932
|
+
onChange: (e) => changeLowBalance(Math.max(0, Number(e.target.value) || 0))
|
|
2933
|
+
}
|
|
2934
|
+
),
|
|
2935
|
+
/* @__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") })
|
|
2936
|
+
] })
|
|
2937
|
+
] }),
|
|
2938
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2939
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-monthly-budget", children: t("settings.monthlyBudget") }),
|
|
2940
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2941
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2942
|
+
"input",
|
|
2943
|
+
{
|
|
2944
|
+
ref: budgetInputRef,
|
|
2945
|
+
id: "dq-monthly-budget",
|
|
2946
|
+
className: "dq-number",
|
|
2947
|
+
type: "number",
|
|
2948
|
+
min: 0,
|
|
2949
|
+
step: 10,
|
|
2950
|
+
value: monthlyBudget,
|
|
2951
|
+
"aria-describedby": "dq-monthly-budget-hint",
|
|
2952
|
+
onChange: (e) => changeMonthlyBudget(Math.max(0, Number(e.target.value) || 0))
|
|
2953
|
+
}
|
|
2954
|
+
),
|
|
2955
|
+
/* @__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") })
|
|
2956
|
+
] })
|
|
2957
|
+
] })
|
|
2958
|
+
] }),
|
|
2959
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
2960
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("settings.groupLayout") }),
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2962
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-sidebar-entry", children: t("settings.sidebarEntry") }),
|
|
2963
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2964
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2965
|
+
"select",
|
|
2966
|
+
{
|
|
2967
|
+
id: "dq-sidebar-entry",
|
|
2968
|
+
className: "dq-select",
|
|
2969
|
+
value: sidebarEntry,
|
|
2970
|
+
onChange: (e) => changeSidebarEntry(e.target.value),
|
|
2971
|
+
children: [
|
|
2972
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "off", children: t("settings.sidebarEntryOff") }),
|
|
2973
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "top", children: t("settings.sidebarEntryTop") }),
|
|
2974
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "bottom", children: t("settings.sidebarEntryBottom") })
|
|
2975
|
+
]
|
|
2976
|
+
}
|
|
2977
|
+
),
|
|
2978
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: t("settings.sidebarEntryHint") })
|
|
2979
|
+
] })
|
|
2980
|
+
] }),
|
|
2981
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2982
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-tab-order-label", children: t("settings.tabPosition") }),
|
|
2983
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2984
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2985
|
+
"div",
|
|
2986
|
+
{
|
|
2987
|
+
className: "dq-tab-order",
|
|
2988
|
+
role: "group",
|
|
2989
|
+
"aria-labelledby": "dq-tab-order-label",
|
|
2990
|
+
"aria-describedby": "dq-tab-order-hint",
|
|
2991
|
+
onDragOver: onOrderDragOver,
|
|
2992
|
+
onDrop: onOrderDrop,
|
|
2993
|
+
children: viewTabs.map((tab, index) => {
|
|
2994
|
+
const current = tab.id === "balance";
|
|
2995
|
+
const dropTarget = tabDragging && !current && tabDropTarget === index;
|
|
2996
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2997
|
+
"span",
|
|
2998
|
+
{
|
|
2999
|
+
className: `dq-tab-chip${current ? " dq-tab-chip--current" : ""}${current && tabDragging ? " dq-tab-chip--dragging" : ""}${dropTarget ? " dq-tab-chip--drop-target" : ""}`,
|
|
3000
|
+
draggable: current,
|
|
3001
|
+
tabIndex: current ? 0 : void 0,
|
|
3002
|
+
role: current ? "button" : void 0,
|
|
3003
|
+
"aria-label": current ? t("settings.tabDragAria") : void 0,
|
|
3004
|
+
onDragStart: current ? onQuotaTabDragStart : void 0,
|
|
3005
|
+
onDragEnd: current ? endTabDrag : void 0,
|
|
3006
|
+
onDragOver: (event) => onChipDragOver(event, index),
|
|
3007
|
+
onDrop: (event) => onChipDrop(event, index),
|
|
3008
|
+
onKeyDown: current ? onQuotaTabKeyDown : void 0,
|
|
3009
|
+
children: tab.label
|
|
3010
|
+
},
|
|
3011
|
+
tab.id
|
|
3012
|
+
);
|
|
3013
|
+
})
|
|
3014
|
+
}
|
|
3015
|
+
),
|
|
3016
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-tab-order-hint", className: "dq-setting-hint", children: t("settings.tabPositionHint") })
|
|
3017
|
+
] })
|
|
3018
|
+
] })
|
|
3019
|
+
] }),
|
|
3020
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
3021
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("links.title") }),
|
|
3022
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-links", children: [
|
|
3023
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/usage", children: t("links.usage") }),
|
|
3024
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/api_keys", children: t("links.apiKey") }),
|
|
3025
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://status.deepseek.com", children: t("links.status") })
|
|
3026
|
+
] })
|
|
3027
|
+
] })
|
|
3028
|
+
] }) }),
|
|
2952
3029
|
props.sessionId !== void 0 && props.sessionId !== "" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--primary", children: [
|
|
2953
3030
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: t("common.estimated"), children: t("sessionUsage.title") }),
|
|
2954
3031
|
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 +3052,10 @@ function BalanceDashboard(props) {
|
|
|
2975
3052
|
{
|
|
2976
3053
|
spent: usage.summary.month.cost,
|
|
2977
3054
|
budget: monthlyBudget,
|
|
2978
|
-
onConfigure: () =>
|
|
3055
|
+
onConfigure: () => {
|
|
3056
|
+
setSettingsOpen(true);
|
|
3057
|
+
window.setTimeout(() => budgetInputRef.current?.focus(), 80);
|
|
3058
|
+
}
|
|
2979
3059
|
}
|
|
2980
3060
|
),
|
|
2981
3061
|
usage !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PricingNote, { pricing: usage.pricing })
|
|
@@ -3103,339 +3183,13 @@ function BalanceDashboard(props) {
|
|
|
3103
3183
|
] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("cache.empty") })
|
|
3104
3184
|
] })
|
|
3105
3185
|
] }),
|
|
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" })
|
|
3186
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.9.1" })
|
|
3301
3187
|
] });
|
|
3302
3188
|
}
|
|
3303
3189
|
|
|
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
3190
|
// src/client/message-cost.tsx
|
|
3437
|
-
var
|
|
3438
|
-
var
|
|
3191
|
+
var import_react5 = require("react");
|
|
3192
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
3439
3193
|
var fmtTurnCost = (cost) => cost.toLocaleString(void 0, {
|
|
3440
3194
|
minimumFractionDigits: cost > 0 && cost < 0.01 ? 4 : 2,
|
|
3441
3195
|
maximumFractionDigits: cost > 0 && cost < 0.01 ? 6 : 4
|
|
@@ -3446,8 +3200,8 @@ var cachedData = (sessionId) => {
|
|
|
3446
3200
|
};
|
|
3447
3201
|
function MessageCost({ sessionId, messageId }) {
|
|
3448
3202
|
const { t } = useI18n();
|
|
3449
|
-
const [data, setData] = (0,
|
|
3450
|
-
(0,
|
|
3203
|
+
const [data, setData] = (0, import_react5.useState)(() => cachedData(sessionId));
|
|
3204
|
+
(0, import_react5.useEffect)(() => {
|
|
3451
3205
|
setData(cachedData(sessionId));
|
|
3452
3206
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData(sessionId)));
|
|
3453
3207
|
const hit = cachedData(sessionId)?.turns.some((turn2) => turn2.messageId === messageId) ?? false;
|
|
@@ -3458,7 +3212,7 @@ function MessageCost({ sessionId, messageId }) {
|
|
|
3458
3212
|
if (turn === void 0) return null;
|
|
3459
3213
|
const amount = `\xA5${fmtTurnCost(turn.cost)}`;
|
|
3460
3214
|
const tooltip = t("messageCost.tooltip");
|
|
3461
|
-
return /* @__PURE__ */ (0,
|
|
3215
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
3462
3216
|
"span",
|
|
3463
3217
|
{
|
|
3464
3218
|
className: "dq-message-cost",
|
|
@@ -3466,15 +3220,15 @@ function MessageCost({ sessionId, messageId }) {
|
|
|
3466
3220
|
"data-dq-tooltip": tooltip,
|
|
3467
3221
|
"aria-label": t("messageCost.aria", { amount }),
|
|
3468
3222
|
children: [
|
|
3469
|
-
/* @__PURE__ */ (0,
|
|
3470
|
-
/* @__PURE__ */ (0,
|
|
3223
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dq-message-cost-separator", "aria-hidden": true, children: "\xB7" }),
|
|
3224
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dq-message-cost-amount", children: amount })
|
|
3471
3225
|
]
|
|
3472
3226
|
}
|
|
3473
3227
|
);
|
|
3474
3228
|
}
|
|
3475
3229
|
|
|
3476
3230
|
// src/client/session-cost-pill.tsx
|
|
3477
|
-
var
|
|
3231
|
+
var import_react6 = require("react");
|
|
3478
3232
|
var import_react_dom = require("react-dom");
|
|
3479
3233
|
|
|
3480
3234
|
// src/pricing.ts
|
|
@@ -3482,7 +3236,7 @@ var PEAK_PRICING_FROM_MS = Date.UTC(2026, 7, 16, 16, 0, 0);
|
|
|
3482
3236
|
var V41_FLASH_PRICING_FROM_MS = Date.UTC(2026, 8, 10, 4, 0, 0);
|
|
3483
3237
|
var PRO_ROUTED_TO_FLASH_FROM_MS = Date.UTC(2026, 8, 14, 4, 0, 0);
|
|
3484
3238
|
var PEAK_WINDOWS = [[9, 12], [14, 18]];
|
|
3485
|
-
var
|
|
3239
|
+
var BEIJING_OFFSET_MS3 = 8 * 36e5;
|
|
3486
3240
|
var FLAT_RATES = {
|
|
3487
3241
|
pro: { cacheHit: 0.025, input: 3, output: 6 },
|
|
3488
3242
|
flash: { cacheHit: 0.02, input: 1, output: 2 },
|
|
@@ -3527,12 +3281,12 @@ function billedTierOf(timeMs, model) {
|
|
|
3527
3281
|
return tier === "pro" && timeMs >= PRO_ROUTED_TO_FLASH_FROM_MS ? "flash" : tier;
|
|
3528
3282
|
}
|
|
3529
3283
|
function isBeijingWeekday(timeMs) {
|
|
3530
|
-
const day = new Date(timeMs +
|
|
3284
|
+
const day = new Date(timeMs + BEIJING_OFFSET_MS3).getUTCDay();
|
|
3531
3285
|
return day >= 1 && day <= 5;
|
|
3532
3286
|
}
|
|
3533
3287
|
function isPeak(timeMs) {
|
|
3534
3288
|
if (eraAt(timeMs).weekdaysOnly && !isBeijingWeekday(timeMs)) return false;
|
|
3535
|
-
const hour = Math.floor(((timeMs +
|
|
3289
|
+
const hour = Math.floor(((timeMs + BEIJING_OFFSET_MS3) % 864e5 + 864e5) % 864e5 / 36e5);
|
|
3536
3290
|
return PEAK_WINDOWS.some(([from, to]) => hour >= from && hour < to);
|
|
3537
3291
|
}
|
|
3538
3292
|
function ratesAt(timeMs, model) {
|
|
@@ -3542,7 +3296,7 @@ function ratesAt(timeMs, model) {
|
|
|
3542
3296
|
}
|
|
3543
3297
|
|
|
3544
3298
|
// src/client/session-cost-pill.tsx
|
|
3545
|
-
var
|
|
3299
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
3546
3300
|
var PANEL_MARGIN = 12;
|
|
3547
3301
|
var PANEL_GAP = 8;
|
|
3548
3302
|
var cachedData2 = (sessionId) => {
|
|
@@ -3550,7 +3304,7 @@ var cachedData2 = (sessionId) => {
|
|
|
3550
3304
|
return response?.ok === true ? response.data ?? null : null;
|
|
3551
3305
|
};
|
|
3552
3306
|
function CostIcon() {
|
|
3553
|
-
return /* @__PURE__ */ (0,
|
|
3307
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3554
3308
|
"svg",
|
|
3555
3309
|
{
|
|
3556
3310
|
width: "14",
|
|
@@ -3564,32 +3318,32 @@ function CostIcon() {
|
|
|
3564
3318
|
"aria-hidden": "true",
|
|
3565
3319
|
focusable: "false",
|
|
3566
3320
|
children: [
|
|
3567
|
-
/* @__PURE__ */ (0,
|
|
3568
|
-
/* @__PURE__ */ (0,
|
|
3569
|
-
/* @__PURE__ */ (0,
|
|
3570
|
-
/* @__PURE__ */ (0,
|
|
3571
|
-
/* @__PURE__ */ (0,
|
|
3321
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "8", cy: "8", r: "6" }),
|
|
3322
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5.5 5.3 8 8.4l2.5-3.1" }),
|
|
3323
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M8 8.4v2.9" }),
|
|
3324
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5.8 9.5h4.4" }),
|
|
3325
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5.8 10.9h4.4" })
|
|
3572
3326
|
]
|
|
3573
3327
|
}
|
|
3574
3328
|
);
|
|
3575
3329
|
}
|
|
3576
3330
|
function SessionCostPill({ sessionId }) {
|
|
3577
3331
|
const { t, locale } = useI18n();
|
|
3578
|
-
const [data, setData] = (0,
|
|
3332
|
+
const [data, setData] = (0, import_react6.useState)(
|
|
3579
3333
|
() => sessionId === void 0 || sessionId === "" ? null : cachedData2(sessionId)
|
|
3580
3334
|
);
|
|
3581
|
-
const [open, setOpen] = (0,
|
|
3582
|
-
const anchorRef = (0,
|
|
3583
|
-
const panelRef = (0,
|
|
3584
|
-
const [pos, setPos] = (0,
|
|
3585
|
-
(0,
|
|
3335
|
+
const [open, setOpen] = (0, import_react6.useState)(false);
|
|
3336
|
+
const anchorRef = (0, import_react6.useRef)(null);
|
|
3337
|
+
const panelRef = (0, import_react6.useRef)(null);
|
|
3338
|
+
const [pos, setPos] = (0, import_react6.useState)(null);
|
|
3339
|
+
(0, import_react6.useEffect)(() => {
|
|
3586
3340
|
if (sessionId === void 0 || sessionId === "") return;
|
|
3587
3341
|
setData(cachedData2(sessionId));
|
|
3588
3342
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData2(sessionId)));
|
|
3589
3343
|
if (getCachedSessionUsage(sessionId) === null) void fetchSessionUsage(sessionId);
|
|
3590
3344
|
return unsubscribe;
|
|
3591
3345
|
}, [sessionId]);
|
|
3592
|
-
(0,
|
|
3346
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
3593
3347
|
if (!open) {
|
|
3594
3348
|
setPos(null);
|
|
3595
3349
|
return;
|
|
@@ -3604,7 +3358,7 @@ function SessionCostPill({ sessionId }) {
|
|
|
3604
3358
|
bottom: window.innerHeight - pill.top + PANEL_GAP
|
|
3605
3359
|
});
|
|
3606
3360
|
}, [open, data]);
|
|
3607
|
-
(0,
|
|
3361
|
+
(0, import_react6.useEffect)(() => {
|
|
3608
3362
|
if (!open) return;
|
|
3609
3363
|
const close = () => setOpen(false);
|
|
3610
3364
|
const onPointerDown = (event) => {
|
|
@@ -3656,8 +3410,8 @@ function SessionCostPill({ sessionId }) {
|
|
|
3656
3410
|
hour12: false
|
|
3657
3411
|
});
|
|
3658
3412
|
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,
|
|
3413
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
3414
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { ref: anchorRef, className: "dq-cost-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3661
3415
|
"button",
|
|
3662
3416
|
{
|
|
3663
3417
|
type: "button",
|
|
@@ -3667,13 +3421,13 @@ function SessionCostPill({ sessionId }) {
|
|
|
3667
3421
|
"aria-label": t("sessionCost.aria", { amount }),
|
|
3668
3422
|
onClick: () => setOpen((value) => !value),
|
|
3669
3423
|
children: [
|
|
3670
|
-
/* @__PURE__ */ (0,
|
|
3671
|
-
/* @__PURE__ */ (0,
|
|
3424
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CostIcon, {}),
|
|
3425
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dq-cost-pill-label", children: amount })
|
|
3672
3426
|
]
|
|
3673
3427
|
}
|
|
3674
3428
|
) }),
|
|
3675
3429
|
open && (0, import_react_dom.createPortal)(
|
|
3676
|
-
/* @__PURE__ */ (0,
|
|
3430
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3677
3431
|
"div",
|
|
3678
3432
|
{
|
|
3679
3433
|
ref: panelRef,
|
|
@@ -3682,35 +3436,35 @@ function SessionCostPill({ sessionId }) {
|
|
|
3682
3436
|
"aria-label": t("sessionCost.title"),
|
|
3683
3437
|
style: pos === null ? { visibility: "hidden", left: 0, top: 0 } : { visibility: "visible", left: pos.left, bottom: pos.bottom, top: "auto" },
|
|
3684
3438
|
children: [
|
|
3685
|
-
/* @__PURE__ */ (0,
|
|
3686
|
-
/* @__PURE__ */ (0,
|
|
3687
|
-
/* @__PURE__ */ (0,
|
|
3439
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dq-cost-panel-title", children: [
|
|
3440
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dq-cost-panel-title-label", children: [
|
|
3441
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CostIcon, {}),
|
|
3688
3442
|
t("sessionCost.title")
|
|
3689
3443
|
] }),
|
|
3690
|
-
/* @__PURE__ */ (0,
|
|
3444
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dq-cost-panel-value", children: amount })
|
|
3691
3445
|
] }),
|
|
3692
|
-
/* @__PURE__ */ (0,
|
|
3693
|
-
/* @__PURE__ */ (0,
|
|
3694
|
-
/* @__PURE__ */ (0,
|
|
3695
|
-
/* @__PURE__ */ (0,
|
|
3446
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dq-cost-panel-rule", "aria-hidden": true }),
|
|
3447
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dl", { className: "dq-cost-details", children: [
|
|
3448
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("common.input") }),
|
|
3449
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dd", { children: [
|
|
3696
3450
|
"\xA5",
|
|
3697
3451
|
fmtTurnCost(split.input)
|
|
3698
3452
|
] }),
|
|
3699
|
-
/* @__PURE__ */ (0,
|
|
3700
|
-
/* @__PURE__ */ (0,
|
|
3453
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("common.cacheHit") }),
|
|
3454
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dd", { children: [
|
|
3701
3455
|
"\xA5",
|
|
3702
3456
|
fmtTurnCost(split.cache)
|
|
3703
3457
|
] }),
|
|
3704
|
-
/* @__PURE__ */ (0,
|
|
3705
|
-
/* @__PURE__ */ (0,
|
|
3458
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("common.output") }),
|
|
3459
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dd", { children: [
|
|
3706
3460
|
"\xA5",
|
|
3707
3461
|
fmtTurnCost(split.output)
|
|
3708
3462
|
] }),
|
|
3709
|
-
/* @__PURE__ */ (0,
|
|
3710
|
-
/* @__PURE__ */ (0,
|
|
3711
|
-
range !== null && /* @__PURE__ */ (0,
|
|
3712
|
-
/* @__PURE__ */ (0,
|
|
3713
|
-
/* @__PURE__ */ (0,
|
|
3463
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("sessionUsage.calls") }),
|
|
3464
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dd", { children: fmtInt(data.calls) }),
|
|
3465
|
+
range !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
3466
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("sessionCost.active") }),
|
|
3467
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dd", { children: range })
|
|
3714
3468
|
] })
|
|
3715
3469
|
] })
|
|
3716
3470
|
]
|
|
@@ -3773,18 +3527,6 @@ var css = `
|
|
|
3773
3527
|
.dq-cost-pill:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3774
3528
|
.dq-cost-pill svg{flex:none}
|
|
3775
3529
|
.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
3530
|
/* Click-open breakdown: the host stat-dialog skin (menu surface, prominent
|
|
3789
3531
|
elevation, 12/18 rows), portaled to the body and placed above the pill. */
|
|
3790
3532
|
.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 +3697,11 @@ var css = `
|
|
|
3955
3697
|
.dq-pricing-table{border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums;min-width:100%}
|
|
3956
3698
|
.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
3699
|
.dq-pricing-table td{padding:4px 12px 4px 0;color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;vertical-align:top}
|
|
3958
|
-
|
|
3700
|
+
/* Align by counting from the end: a peak row starts with the rowSpan'd model
|
|
3701
|
+
cell (5 cells) while its off-peak pair starts at \u65F6\u6BB5 (4 cells), so
|
|
3702
|
+
nth-child(n+3) left the off-peak cache-hit price stranded under \u65F6\u6BB5. The
|
|
3703
|
+
last three cells are the three price columns in both row shapes. */
|
|
3704
|
+
.dq-pricing-table th:nth-last-child(-n+3),.dq-pricing-table td:nth-last-child(-n+3){text-align:right}
|
|
3959
3705
|
.dq-pricing-table th:last-child,.dq-pricing-table td:last-child{padding-right:0}
|
|
3960
3706
|
.dq-pricing-table tbody td:first-child{font-weight:500}
|
|
3961
3707
|
.dq-pricing-table tbody tr+tr td{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.05))}
|
|
@@ -3987,8 +3733,23 @@ var css = `
|
|
|
3987
3733
|
.dq-legend-swatch--peak{background:var(--dsw-alias-state-warning-primary,#bf8700)}
|
|
3988
3734
|
.dq-legend-swatch--offpeak{background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85}
|
|
3989
3735
|
.dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:16px}
|
|
3990
|
-
.dq-peak-
|
|
3991
|
-
.dq-
|
|
3736
|
+
.dq-peak-foot{margin:12px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e);text-wrap:pretty}
|
|
3737
|
+
.dq-clock{margin-bottom:16px}
|
|
3738
|
+
.dq-clock-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:8px}
|
|
3739
|
+
.dq-clock-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
3740
|
+
.dq-clock-status{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;padding:2px 8px;border-radius:999px}
|
|
3741
|
+
.dq-clock-status::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor}
|
|
3742
|
+
.dq-clock-status--peak{color:var(--dq-text-warn);background:rgba(191,135,0,.12)}
|
|
3743
|
+
.dq-clock-status--off{color:var(--dq-text-success);background:rgba(45,164,78,.12)}
|
|
3744
|
+
.dq-clock-track{position:relative;display:flex;gap:1px;height:10px}
|
|
3745
|
+
.dq-clock-cell{flex:1 1 0;height:100%;background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.55}
|
|
3746
|
+
.dq-clock-cell--peak{background:var(--dsw-alias-state-warning-primary,#bf8700);opacity:.92}
|
|
3747
|
+
.dq-clock-cell:first-child{border-radius:999px 0 0 999px}
|
|
3748
|
+
.dq-clock-cell:last-child{border-radius:0 999px 999px 0}
|
|
3749
|
+
.dq-clock-now{position:absolute;top:-3px;bottom:-3px;width:2px;background:var(--dsw-alias-label-primary,#1f2328);border-radius:2px;transform:translateX(-1px)}
|
|
3750
|
+
.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)}
|
|
3751
|
+
.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}
|
|
3752
|
+
.dq-clock-hint{margin:6px 0 0;font-size:11px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
3992
3753
|
.dq-cache-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-bottom:12px}
|
|
3993
3754
|
@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
3755
|
.dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:500}
|
|
@@ -4054,28 +3815,29 @@ var css = `
|
|
|
4054
3815
|
.dq-setting-label{width:128px;font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}
|
|
4055
3816
|
.dq-setting-control{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
|
|
4056
3817
|
.dq-setting-hint{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);line-height:1.5}
|
|
3818
|
+
.dq-setting-group{margin-top:28px}
|
|
3819
|
+
.dq-setting-group:first-of-type{margin-top:0}
|
|
3820
|
+
.dq-setting-group-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:0 0 10px;letter-spacing:.02em}
|
|
3821
|
+
.dq-setting-group .dq-toggle{margin-top:0}
|
|
3822
|
+
.dq-setting-group .dq-setting{margin-top:16px}
|
|
4057
3823
|
/* Page footer: the package name + version, injected at build time from
|
|
4058
3824
|
package.json (see build.mjs) so it can never drift from the release. */
|
|
4059
3825
|
.dq-footer{margin:16px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}
|
|
3826
|
+
.dq-sectionnav{position:fixed;top:50%;transform:translateY(-50%);z-index:20;width:max-content;pointer-events:none}
|
|
3827
|
+
.dq-sectionnav--hidden{display:none}
|
|
3828
|
+
.dq-sectionnav ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px;pointer-events:auto}
|
|
3829
|
+
.dq-sectionnav li{display:flex;justify-content:flex-end}
|
|
3830
|
+
.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}
|
|
3831
|
+
.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))}
|
|
3832
|
+
.dq-sectionnav-dot:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
|
|
3833
|
+
.dq-sectionnav-label{font-size:12px;color:inherit}
|
|
3834
|
+
.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}
|
|
3835
|
+
.dq-sectionnav-dot:hover .dq-sectionnav-pip{background:var(--dsw-alias-label-secondary,#59636e)}
|
|
3836
|
+
.dq-sectionnav-dot--active{color:var(--dsw-alias-label-primary,#1f2328);font-weight:600}
|
|
3837
|
+
.dq-sectionnav-dot--active .dq-sectionnav-pip{background:var(--dsw-alias-state-business-primary,#0969da);transform:scale(1.25)}
|
|
3838
|
+
@media (max-width:1199px){.dq-sectionnav{display:none}}
|
|
4060
3839
|
.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
3840
|
.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
3841
|
.dq-links{display:flex;flex-wrap:wrap;gap:8px}
|
|
4080
3842
|
.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
3843
|
.dq-link::after{content:"\u2197";font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
@@ -4142,7 +3904,10 @@ var css = `
|
|
|
4142
3904
|
.dq-chart-switch-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}
|
|
4143
3905
|
.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
3906
|
.dq-chart-wrap{position:relative}
|
|
4145
|
-
.dq-
|
|
3907
|
+
.dq-plot{display:flex;align-items:flex-end;gap:8px}
|
|
3908
|
+
.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}
|
|
3909
|
+
.dq-yaxis span{white-space:nowrap}
|
|
3910
|
+
.dq-bars-scroll{flex:1 1 auto;min-width:0;overflow-x:auto;overflow-y:hidden;padding-bottom:2px}
|
|
4146
3911
|
.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
3912
|
.dq-tip--above{transform:translate(-50%,calc(-100% - 8px))}
|
|
4148
3913
|
.dq-tip-head{font-weight:600;margin-bottom:2px}
|
|
@@ -4163,13 +3928,13 @@ var css = `
|
|
|
4163
3928
|
.dq-heat-cell--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}
|
|
4164
3929
|
.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
3930
|
.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:
|
|
3931
|
+
.dq-heat-scroll{overflow:hidden;padding-bottom:2px}
|
|
3932
|
+
.dq-heatmap{display:flex;gap:2px}
|
|
3933
|
+
.dq-heat-week{display:flex;flex:1 1 0;min-width:0;flex-direction:column;gap:2px}
|
|
3934
|
+
.dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:2px;flex:none;min-width:0}
|
|
4170
3935
|
.dq-heat-cell--pad{background:transparent}
|
|
4171
|
-
.dq-heat-months{display:flex;gap:
|
|
4172
|
-
.dq-heat-month{flex:1 1 0;min-width:
|
|
3936
|
+
.dq-heat-months{display:flex;gap:2px;margin-top:4px}
|
|
3937
|
+
.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
3938
|
.dq-heat-key{width:12px;height:12px;border-radius:2px;flex:none}
|
|
4174
3939
|
.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
3940
|
.dq-heat-scale>span:first-child{margin-right:2px}
|
|
@@ -4203,6 +3968,20 @@ var css = `
|
|
|
4203
3968
|
.dq-sync--error .dq-sync-dot{background:var(--dsw-alias-state-error-primary,#cf222e)}
|
|
4204
3969
|
@keyframes dq-sync-pulse{50%{opacity:.35;transform:scale(.8)}}
|
|
4205
3970
|
.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}
|
|
3971
|
+
.dq-status-actions{display:flex;align-items:center;gap:8px;flex:none}
|
|
3972
|
+
.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}
|
|
3973
|
+
.dq-settings-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}
|
|
3974
|
+
.dq-settings-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3975
|
+
.dq-settings-caret{font-size:10px;line-height:1;transition:transform .2s cubic-bezier(.16,1,.3,1)}
|
|
3976
|
+
.dq-settings-caret--open{transform:rotate(180deg)}
|
|
3977
|
+
.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}
|
|
3978
|
+
/* Motion: eased (easeOutExpo) arrivals. Operate surface \u2014 fast, purposeful,
|
|
3979
|
+
and fully disabled under prefers-reduced-motion (see the global reduce block). */
|
|
3980
|
+
.dq-export-menu,.dq-model-menu{transform-origin:top right;animation:dq-pop .18s cubic-bezier(.16,1,.3,1)}
|
|
3981
|
+
@keyframes dq-pop{from{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:none}}
|
|
3982
|
+
.dq-alert{animation:dq-alert-in .28s cubic-bezier(.16,1,.3,1)}
|
|
3983
|
+
@keyframes dq-alert-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
|
|
3984
|
+
@media (max-width:620px){.dq-settings-panel{padding:16px}}
|
|
4206
3985
|
.dq-refresh-btn:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}
|
|
4207
3986
|
.dq-refresh-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
4208
3987
|
.dq-refresh-btn:disabled{cursor:default;opacity:.6}
|
|
@@ -4290,7 +4069,7 @@ html[data-dq-quota-view] .dq-balance{padding-bottom:48px}
|
|
|
4290
4069
|
`;
|
|
4291
4070
|
|
|
4292
4071
|
// src/client/widget.tsx
|
|
4293
|
-
var
|
|
4072
|
+
var import_react7 = require("react");
|
|
4294
4073
|
|
|
4295
4074
|
// src/client/sessions.ts
|
|
4296
4075
|
function currentSessionIdOf(state) {
|
|
@@ -4299,7 +4078,7 @@ function currentSessionIdOf(state) {
|
|
|
4299
4078
|
}
|
|
4300
4079
|
|
|
4301
4080
|
// src/client/widget.tsx
|
|
4302
|
-
var
|
|
4081
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
4303
4082
|
var MARGIN = 16;
|
|
4304
4083
|
var CORNER_KEY = "widget.corner";
|
|
4305
4084
|
var COLLAPSED_KEY = "widget.collapsed";
|
|
@@ -4328,8 +4107,8 @@ function getBounds(node) {
|
|
|
4328
4107
|
if (headerRect !== null && headerRect.bottom > top && headerRect.bottom < bottom - MIN_REGION_HEIGHT) {
|
|
4329
4108
|
top = headerRect.bottom;
|
|
4330
4109
|
}
|
|
4331
|
-
const composerRect =
|
|
4332
|
-
if (composerRect !== null && composerRect.top < bottom && composerRect.top > top +
|
|
4110
|
+
const composerRect = getComposerElement(frame)?.getBoundingClientRect() ?? null;
|
|
4111
|
+
if (composerRect !== null && composerRect.top < bottom && composerRect.top > top + (bottom - top) / 2) {
|
|
4333
4112
|
bottom = composerRect.top;
|
|
4334
4113
|
}
|
|
4335
4114
|
return { left, top, right, bottom };
|
|
@@ -4350,47 +4129,47 @@ function cornerPos(corner, node, bounds) {
|
|
|
4350
4129
|
function QuotaWidget(props) {
|
|
4351
4130
|
const { t } = useI18n();
|
|
4352
4131
|
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,
|
|
4132
|
+
const [visible, setVisible] = (0, import_react7.useState)(widgetVisibleStore.get());
|
|
4133
|
+
(0, import_react7.useEffect)(() => widgetVisibleStore.subscribe(() => setVisible(widgetVisibleStore.get())), []);
|
|
4134
|
+
const [quotaViewActive2, setQuotaViewActive] = (0, import_react7.useState)(quotaViewActiveStore.get());
|
|
4135
|
+
(0, import_react7.useEffect)(() => quotaViewActiveStore.subscribe(() => setQuotaViewActive(quotaViewActiveStore.get())), []);
|
|
4136
|
+
const [lowBalance, setLowBalance] = (0, import_react7.useState)(lowBalanceStore.get());
|
|
4137
|
+
(0, import_react7.useEffect)(() => lowBalanceStore.subscribe(() => setLowBalance(lowBalanceStore.get())), []);
|
|
4138
|
+
const [selectedTabIds, setSelectedTabIds] = (0, import_react7.useState)(widgetTabIdsStore.get());
|
|
4139
|
+
(0, import_react7.useEffect)(() => widgetTabIdsStore.subscribe(() => setSelectedTabIds(widgetTabIdsStore.get())), []);
|
|
4140
|
+
const [viewTabs, setViewTabs] = (0, import_react7.useState)(() => props.views.list());
|
|
4141
|
+
(0, import_react7.useEffect)(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views]);
|
|
4142
|
+
const [activeViewId, setActiveViewId] = (0, import_react7.useState)(null);
|
|
4364
4143
|
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,
|
|
4144
|
+
const [data, setData] = (0, import_react7.useState)(cachedBalance?.data ?? null);
|
|
4145
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
4146
|
+
const [loading, setLoading] = (0, import_react7.useState)(cachedBalance === null);
|
|
4147
|
+
const [collapsed, setCollapsed] = (0, import_react7.useState)(() => loadPref(COLLAPSED_KEY, isBoolean, false));
|
|
4148
|
+
const [pos, setPos] = (0, import_react7.useState)(null);
|
|
4149
|
+
const [corner, setCorner] = (0, import_react7.useState)(() => loadPref(CORNER_KEY, isCorner, "br"));
|
|
4150
|
+
const [dragging, setDragging] = (0, import_react7.useState)(false);
|
|
4151
|
+
const rootRef = (0, import_react7.useRef)(null);
|
|
4152
|
+
const dragRef = (0, import_react7.useRef)(null);
|
|
4374
4153
|
const readUsage = () => {
|
|
4375
4154
|
const cached = getCachedUsage();
|
|
4376
4155
|
return { data: cached?.ok === true ? cached.data ?? null : null, at: getCachedUsageAt() };
|
|
4377
4156
|
};
|
|
4378
|
-
const [usage, setUsage] = (0,
|
|
4379
|
-
(0,
|
|
4157
|
+
const [usage, setUsage] = (0, import_react7.useState)(readUsage);
|
|
4158
|
+
(0, import_react7.useEffect)(() => subscribeUsage(() => setUsage(readUsage())), []);
|
|
4380
4159
|
const readSessionUsage = (id) => {
|
|
4381
4160
|
if (id === void 0) return null;
|
|
4382
4161
|
const response = getCachedSessionUsage(id);
|
|
4383
4162
|
return response?.ok === true ? response.data ?? null : null;
|
|
4384
4163
|
};
|
|
4385
|
-
const [sessionUsage, setSessionUsage] = (0,
|
|
4386
|
-
(0,
|
|
4164
|
+
const [sessionUsage, setSessionUsage] = (0, import_react7.useState)(() => readSessionUsage(sessionId));
|
|
4165
|
+
(0, import_react7.useEffect)(() => {
|
|
4387
4166
|
setSessionUsage(readSessionUsage(sessionId));
|
|
4388
4167
|
if (sessionId === void 0) return;
|
|
4389
4168
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setSessionUsage(readSessionUsage(sessionId)));
|
|
4390
4169
|
void fetchSessionUsage(sessionId);
|
|
4391
4170
|
return unsubscribe;
|
|
4392
4171
|
}, [sessionId]);
|
|
4393
|
-
(0,
|
|
4172
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
4394
4173
|
const viewIds = viewTabs.map((tab) => tab.id);
|
|
4395
4174
|
const read = () => {
|
|
4396
4175
|
const next = getActiveConversationViewId(getShellFrame(rootRef.current), viewIds);
|
|
@@ -4427,14 +4206,14 @@ function QuotaWidget(props) {
|
|
|
4427
4206
|
setLoading(false);
|
|
4428
4207
|
}
|
|
4429
4208
|
};
|
|
4430
|
-
(0,
|
|
4209
|
+
(0, import_react7.useEffect)(() => {
|
|
4431
4210
|
void load(true);
|
|
4432
4211
|
const id = setInterval(() => {
|
|
4433
4212
|
void load(false);
|
|
4434
4213
|
}, 6e4);
|
|
4435
4214
|
return () => clearInterval(id);
|
|
4436
4215
|
}, []);
|
|
4437
|
-
(0,
|
|
4216
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
4438
4217
|
if (!visible || quotaViewActive2) return;
|
|
4439
4218
|
const node = rootRef.current;
|
|
4440
4219
|
if (node === null) return;
|
|
@@ -4562,9 +4341,9 @@ function QuotaWidget(props) {
|
|
|
4562
4341
|
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
4342
|
const ageMinutes = usage.at === null ? null : Math.floor((Date.now() - usage.at) / 6e4);
|
|
4564
4343
|
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,
|
|
4344
|
+
const sessionCostRow = sessionUsage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-row", title: t("widget.sessionCostTitle"), children: [
|
|
4345
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-label", children: t("widget.sessionCost") }),
|
|
4346
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
4568
4347
|
"\xA5",
|
|
4569
4348
|
fmtTurnCost(sessionUsage.cost)
|
|
4570
4349
|
] })
|
|
@@ -4572,50 +4351,50 @@ function QuotaWidget(props) {
|
|
|
4572
4351
|
let body = null;
|
|
4573
4352
|
if (!collapsed) {
|
|
4574
4353
|
if (loading && data === null && error === null) {
|
|
4575
|
-
body = /* @__PURE__ */ (0,
|
|
4576
|
-
/* @__PURE__ */ (0,
|
|
4354
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4355
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { children: t("widget.loading") }),
|
|
4577
4356
|
sessionCostRow
|
|
4578
4357
|
] });
|
|
4579
4358
|
} else if (error !== null && data === null) {
|
|
4580
|
-
body = /* @__PURE__ */ (0,
|
|
4581
|
-
/* @__PURE__ */ (0,
|
|
4359
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4360
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-error", children: localizeApiError(error, t, "error.query") }),
|
|
4582
4361
|
sessionCostRow
|
|
4583
4362
|
] });
|
|
4584
4363
|
} else if (primary !== null) {
|
|
4585
|
-
body = /* @__PURE__ */ (0,
|
|
4586
|
-
/* @__PURE__ */ (0,
|
|
4587
|
-
/* @__PURE__ */ (0,
|
|
4588
|
-
/* @__PURE__ */ (0,
|
|
4589
|
-
/* @__PURE__ */ (0,
|
|
4364
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4365
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-remaining-label", children: t("balance.remaining") }),
|
|
4366
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { title: low ? t("widget.lowTitle", { amount: fmt(lowBalance) }) : void 0, children: [
|
|
4367
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: `dsh-quota-total${low ? " dsh-quota-total--low" : ""}`, children: fmt(primary.total) }),
|
|
4368
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-currency", children: primary.currency })
|
|
4590
4369
|
] }),
|
|
4591
|
-
usage.data !== null && /* @__PURE__ */ (0,
|
|
4592
|
-
/* @__PURE__ */ (0,
|
|
4593
|
-
/* @__PURE__ */ (0,
|
|
4370
|
+
usage.data !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-row", title: t("widget.todayCostTitle", { age: usageAgeText }), children: [
|
|
4371
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-label", children: t("widget.todayCost") }),
|
|
4372
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
4594
4373
|
"\xA5",
|
|
4595
4374
|
fmt(usage.data.summary.today.cost)
|
|
4596
4375
|
] })
|
|
4597
4376
|
] }),
|
|
4598
4377
|
sessionCostRow,
|
|
4599
|
-
data?.isAvailable === false && /* @__PURE__ */ (0,
|
|
4600
|
-
/* @__PURE__ */ (0,
|
|
4601
|
-
/* @__PURE__ */ (0,
|
|
4378
|
+
data?.isAvailable === false && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-row", children: [
|
|
4379
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-label", children: t("common.status") }),
|
|
4380
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-value dsh-quota-error", children: t("common.unavailable") })
|
|
4602
4381
|
] })
|
|
4603
4382
|
] });
|
|
4604
4383
|
} else {
|
|
4605
|
-
body = /* @__PURE__ */ (0,
|
|
4606
|
-
/* @__PURE__ */ (0,
|
|
4384
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4385
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-error", children: t("widget.noBalance") }),
|
|
4607
4386
|
sessionCostRow
|
|
4608
4387
|
] });
|
|
4609
4388
|
}
|
|
4610
4389
|
}
|
|
4611
4390
|
const style = pos !== null ? { left: `${pos.x}px`, top: `${pos.y}px`, right: "auto", bottom: "auto" } : void 0;
|
|
4612
|
-
return /* @__PURE__ */ (0,
|
|
4391
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4613
4392
|
"div",
|
|
4614
4393
|
{
|
|
4615
4394
|
className: `dsh-quota-root${dragging ? " dsh-quota-dragging" : ""}${hidden ? " dsh-quota-root--hidden" : ""}`,
|
|
4616
4395
|
style,
|
|
4617
4396
|
"aria-hidden": hidden,
|
|
4618
|
-
children: /* @__PURE__ */ (0,
|
|
4397
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
4619
4398
|
"div",
|
|
4620
4399
|
{
|
|
4621
4400
|
ref: rootRef,
|
|
@@ -4627,14 +4406,14 @@ function QuotaWidget(props) {
|
|
|
4627
4406
|
onPointerCancel: () => endDrag(false),
|
|
4628
4407
|
onLostPointerCapture: () => endDrag(false),
|
|
4629
4408
|
children: [
|
|
4630
|
-
/* @__PURE__ */ (0,
|
|
4631
|
-
/* @__PURE__ */ (0,
|
|
4632
|
-
/* @__PURE__ */ (0,
|
|
4633
|
-
/* @__PURE__ */ (0,
|
|
4634
|
-
/* @__PURE__ */ (0,
|
|
4409
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-header", children: [
|
|
4410
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-title", children: [
|
|
4411
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-grip", title: t("widget.dragGrip"), children: "\u2807" }),
|
|
4412
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: dotClass }),
|
|
4413
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-name", children: t("widget.title") })
|
|
4635
4414
|
] }),
|
|
4636
|
-
/* @__PURE__ */ (0,
|
|
4637
|
-
/* @__PURE__ */ (0,
|
|
4415
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-actions", children: [
|
|
4416
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4638
4417
|
"button",
|
|
4639
4418
|
{
|
|
4640
4419
|
className: "dsh-quota-btn",
|
|
@@ -4650,7 +4429,7 @@ function QuotaWidget(props) {
|
|
|
4650
4429
|
children: "\u21BB"
|
|
4651
4430
|
}
|
|
4652
4431
|
),
|
|
4653
|
-
/* @__PURE__ */ (0,
|
|
4432
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4654
4433
|
"button",
|
|
4655
4434
|
{
|
|
4656
4435
|
className: "dsh-quota-btn",
|
|
@@ -4668,7 +4447,7 @@ function QuotaWidget(props) {
|
|
|
4668
4447
|
)
|
|
4669
4448
|
] })
|
|
4670
4449
|
] }),
|
|
4671
|
-
collapsed && primary !== null && /* @__PURE__ */ (0,
|
|
4450
|
+
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
4451
|
fmt(primary.total),
|
|
4673
4452
|
" ",
|
|
4674
4453
|
primary.currency
|
|
@@ -4699,7 +4478,7 @@ function conversationViewsSource(slots) {
|
|
|
4699
4478
|
}
|
|
4700
4479
|
|
|
4701
4480
|
// src/client/index.tsx
|
|
4702
|
-
var
|
|
4481
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
4703
4482
|
var name = "dsh-usage-dashboard";
|
|
4704
4483
|
var inject = ["slots", "locale", "layout"];
|
|
4705
4484
|
var openQuotaTab = null;
|
|
@@ -4720,15 +4499,11 @@ function apply(ctx) {
|
|
|
4720
4499
|
});
|
|
4721
4500
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register(
|
|
4722
4501
|
{ name: "shell.overlay", id: "deepseek-quota", order: 1e3, locale: NS, label: () => t("widget.title") },
|
|
4723
|
-
({ t: slotT, useSessions }) => /* @__PURE__ */ (0,
|
|
4502
|
+
({ 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
4503
|
));
|
|
4725
4504
|
ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register(
|
|
4726
4505
|
{ 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 }) }) })
|
|
4506
|
+
({ 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
4507
|
));
|
|
4733
4508
|
ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register(
|
|
4734
4509
|
{
|
|
@@ -4738,13 +4513,13 @@ function apply(ctx) {
|
|
|
4738
4513
|
locale: NS,
|
|
4739
4514
|
label: () => t("messageCost.label")
|
|
4740
4515
|
},
|
|
4741
|
-
({ t: slotT, sessionId, messageId }) => /* @__PURE__ */ (0,
|
|
4516
|
+
({ 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
4517
|
));
|
|
4743
4518
|
const QuotaView = ({ t: slotT, sessionId, openView }) => {
|
|
4744
|
-
(0,
|
|
4519
|
+
(0, import_react8.useEffect)(() => {
|
|
4745
4520
|
if (openView !== void 0) openQuotaTab = openView;
|
|
4746
4521
|
}, [openView]);
|
|
4747
|
-
return /* @__PURE__ */ (0,
|
|
4522
|
+
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
4523
|
};
|
|
4749
4524
|
ctx.effect(() => {
|
|
4750
4525
|
let disposeView = null;
|
|
@@ -4780,14 +4555,14 @@ function apply(ctx) {
|
|
|
4780
4555
|
disposeView?.();
|
|
4781
4556
|
};
|
|
4782
4557
|
}, "dsh-usage-dashboard: quota tab registration");
|
|
4783
|
-
const QuotaPanel = ({ t: slotT }) => /* @__PURE__ */ (0,
|
|
4784
|
-
/* @__PURE__ */ (0,
|
|
4785
|
-
/* @__PURE__ */ (0,
|
|
4786
|
-
/* @__PURE__ */ (0,
|
|
4558
|
+
const QuotaPanel = ({ t: slotT }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dq-panel", children: [
|
|
4559
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dq-panel-head", "data-window-drag": true, children: [
|
|
4560
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { className: "dq-panel-title", children: slotT("nav.quota") }),
|
|
4561
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dq-panel-caption", children: slotT("panel.caption") })
|
|
4787
4562
|
] }),
|
|
4788
|
-
/* @__PURE__ */ (0,
|
|
4563
|
+
/* @__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
4564
|
] });
|
|
4790
|
-
const QuotaGlyph = ({ size, active }) => /* @__PURE__ */ (0,
|
|
4565
|
+
const QuotaGlyph = ({ size, active }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
4791
4566
|
"svg",
|
|
4792
4567
|
{
|
|
4793
4568
|
className: "dq-side-icon",
|
|
@@ -4803,23 +4578,23 @@ function apply(ctx) {
|
|
|
4803
4578
|
"aria-hidden": "true",
|
|
4804
4579
|
focusable: "false",
|
|
4805
4580
|
children: [
|
|
4806
|
-
/* @__PURE__ */ (0,
|
|
4807
|
-
/* @__PURE__ */ (0,
|
|
4808
|
-
/* @__PURE__ */ (0,
|
|
4809
|
-
/* @__PURE__ */ (0,
|
|
4581
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M3.5 3.3 8 8.7l4.5-5.4" }),
|
|
4582
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M8 8.7V13.2" }),
|
|
4583
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M3.5 10.2h9" }),
|
|
4584
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M3.5 12.1h9" })
|
|
4810
4585
|
]
|
|
4811
4586
|
}
|
|
4812
4587
|
);
|
|
4813
4588
|
const QuotaFooterButton = ({ wide, t: slotT }) => {
|
|
4814
|
-
const [active, setActive] = (0,
|
|
4815
|
-
(0,
|
|
4589
|
+
const [active, setActive] = (0, import_react8.useState)(() => ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
|
|
4590
|
+
(0, import_react8.useEffect)(() => {
|
|
4816
4591
|
const sync = () => {
|
|
4817
4592
|
setActive(ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
|
|
4818
4593
|
};
|
|
4819
4594
|
sync();
|
|
4820
4595
|
return ctx.layout.panelInfo.subscribe(sync);
|
|
4821
4596
|
}, []);
|
|
4822
|
-
return /* @__PURE__ */ (0,
|
|
4597
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
4823
4598
|
"button",
|
|
4824
4599
|
{
|
|
4825
4600
|
type: "button",
|
|
@@ -4831,8 +4606,8 @@ function apply(ctx) {
|
|
|
4831
4606
|
ctx.layout.selectPanel(active ? null : QUOTA_PANEL_ID);
|
|
4832
4607
|
},
|
|
4833
4608
|
children: [
|
|
4834
|
-
/* @__PURE__ */ (0,
|
|
4835
|
-
wide && /* @__PURE__ */ (0,
|
|
4609
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dq-side-entry-icon", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(QuotaGlyph, { size: 16 }) }),
|
|
4610
|
+
wide && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dq-side-entry-label", children: slotT("nav.quota") })
|
|
4836
4611
|
]
|
|
4837
4612
|
}
|
|
4838
4613
|
);
|