@cassius0924/dsh-usage-dashboard 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -10
- package/lib/client.js +488 -124
- package/lib/client.js.map +4 -4
- package/lib/index.js +198 -27
- package/lib/index.js.map +3 -3
- package/lib/types/client/api.d.ts +2 -0
- package/lib/types/client/context.d.ts +3 -0
- package/lib/types/client/dashboard.d.ts +2 -0
- package/lib/types/client/dom.d.ts +23 -1
- package/lib/types/client/locales.d.ts +26 -9
- package/lib/types/client/message-cost.d.ts +10 -0
- package/lib/types/client/store.d.ts +4 -0
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/client/views.d.ts +23 -0
- package/lib/types/client/widget.d.ts +10 -1
- package/lib/types/context.d.ts +44 -1
- package/lib/types/contract.d.ts +62 -10
- package/lib/types/pricing.d.ts +46 -15
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -131,7 +131,7 @@ async function getJson(path, cache) {
|
|
|
131
131
|
var usageIsUsable = (res) => {
|
|
132
132
|
if (res.ok !== true) return false;
|
|
133
133
|
const data = res.data;
|
|
134
|
-
return data !== void 0 && Array.isArray(data.daily) && Array.isArray(data.hourly) && Array.isArray(data.heatmap) && Array.isArray(data.models) && Array.isArray(data.sessions) && typeof data.sessionCount === "number" && data.coverage?.scope === "local-dsh-session-logs" && typeof data.coverage.scannedSessions === "number" && typeof data.coverage.skippedRecords === "number" && Array.isArray(data.windows) && data.windows.length === USAGE_WINDOW_DAYS.length && USAGE_WINDOW_DAYS.every((days) => data.windows.some((window2) => window2.days === days)) && data.windows.every((window2) => Array.isArray(window2.daily) && Array.isArray(window2.hourly) && Array.isArray(window2.models) && Array.isArray(window2.sessions)) && data.totals !== void 0 && typeof data.totals.cacheSavings === "number" && data.summary?.today !== void 0 && Array.isArray(data.pricing?.tiers) && data.peakSplit?.peak !== void 0;
|
|
134
|
+
return data !== void 0 && Array.isArray(data.daily) && Array.isArray(data.hourly) && Array.isArray(data.heatmap) && Array.isArray(data.models) && Array.isArray(data.sessions) && typeof data.sessionCount === "number" && data.coverage?.scope === "local-dsh-session-logs" && typeof data.coverage.scannedSessions === "number" && typeof data.coverage.skippedRecords === "number" && Array.isArray(data.windows) && data.windows.length === USAGE_WINDOW_DAYS.length && USAGE_WINDOW_DAYS.every((days) => data.windows.some((window2) => window2.days === days)) && data.windows.every((window2) => Array.isArray(window2.daily) && Array.isArray(window2.hourly) && Array.isArray(window2.models) && Array.isArray(window2.sessions)) && data.vision !== void 0 && typeof data.vision.images === "number" && typeof data.vision.imageTokens === "number" && typeof data.vision.cost === "number" && Array.isArray(data.visionDaily) && Array.isArray(data.visionSessions) && data.windows.every((window2) => window2.vision !== void 0 && Array.isArray(window2.visionDaily) && Array.isArray(window2.visionSessions)) && data.totals !== void 0 && typeof data.totals.cacheSavings === "number" && data.summary?.today !== void 0 && Array.isArray(data.pricing?.tiers) && data.peakSplit?.peak !== void 0;
|
|
135
135
|
};
|
|
136
136
|
var balanceIsUsable = (res) => res.ok === true && Array.isArray(res.data?.balances);
|
|
137
137
|
var balanceCache = createCache(BALANCE_TTL_MS, "dsh-usage-dashboard:balance", balanceIsUsable);
|
|
@@ -174,8 +174,8 @@ async function fetchUsage(force = false) {
|
|
|
174
174
|
if (usageInflight !== null) return usageInflight;
|
|
175
175
|
}
|
|
176
176
|
const suffix = force ? "?refresh=1" : "";
|
|
177
|
-
const task = getJson(`/api/dsh-usage-dashboard/usage${suffix}`,
|
|
178
|
-
if (res.ok) {
|
|
177
|
+
const task = getJson(`/api/dsh-usage-dashboard/usage${suffix}`, "no-store").then((res) => {
|
|
178
|
+
if (res.ok && usageIsUsable(res)) {
|
|
179
179
|
usageCache.put(res);
|
|
180
180
|
for (const listener of [...usageListeners]) listener();
|
|
181
181
|
}
|
|
@@ -190,6 +190,26 @@ async function fetchUsage(force = false) {
|
|
|
190
190
|
return task;
|
|
191
191
|
}
|
|
192
192
|
var sessionUsageInflight = /* @__PURE__ */ new Map();
|
|
193
|
+
var sessionUsageCache = /* @__PURE__ */ new Map();
|
|
194
|
+
var sessionUsageListeners = /* @__PURE__ */ new Map();
|
|
195
|
+
var getCachedSessionUsage = (sessionId) => sessionUsageCache.get(sessionId) ?? null;
|
|
196
|
+
var subscribeSessionUsage = (sessionId, fn) => {
|
|
197
|
+
let listeners = sessionUsageListeners.get(sessionId);
|
|
198
|
+
if (listeners === void 0) {
|
|
199
|
+
listeners = /* @__PURE__ */ new Set();
|
|
200
|
+
sessionUsageListeners.set(sessionId, listeners);
|
|
201
|
+
}
|
|
202
|
+
listeners.add(fn);
|
|
203
|
+
return () => {
|
|
204
|
+
listeners?.delete(fn);
|
|
205
|
+
if (listeners?.size === 0) sessionUsageListeners.delete(sessionId);
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
var publishSessionUsage = (sessionId, response) => {
|
|
209
|
+
if (!response.ok) return;
|
|
210
|
+
sessionUsageCache.set(sessionId, response);
|
|
211
|
+
for (const listener of [...sessionUsageListeners.get(sessionId) ?? []]) listener();
|
|
212
|
+
};
|
|
193
213
|
async function fetchSessionUsage(sessionId, force = false) {
|
|
194
214
|
if (typeof sessionId !== "string" || sessionId === "") {
|
|
195
215
|
return { ok: false, error: "\u7F3A\u5C11\u4F1A\u8BDD id" };
|
|
@@ -198,7 +218,10 @@ async function fetchSessionUsage(sessionId, force = false) {
|
|
|
198
218
|
const inflight = sessionUsageInflight.get(sessionId);
|
|
199
219
|
if (inflight !== void 0) return inflight;
|
|
200
220
|
}
|
|
201
|
-
const task = getJson(`/api/dsh-usage-dashboard/session?id=${encodeURIComponent(sessionId)}`, "no-store")
|
|
221
|
+
const task = getJson(`/api/dsh-usage-dashboard/session?id=${encodeURIComponent(sessionId)}`, "no-store").then((response) => {
|
|
222
|
+
publishSessionUsage(sessionId, response);
|
|
223
|
+
return response;
|
|
224
|
+
});
|
|
202
225
|
if (force) return task;
|
|
203
226
|
const tracked = task.finally(() => {
|
|
204
227
|
if (sessionUsageInflight.get(sessionId) === tracked) sessionUsageInflight.delete(sessionId);
|
|
@@ -309,16 +332,15 @@ var zh = {
|
|
|
309
332
|
"pricing.currentOffPeak": "\u5F53\u524D \u95F2\u65F6",
|
|
310
333
|
"pricing.model": "\u6A21\u578B",
|
|
311
334
|
"pricing.period": "\u65F6\u6BB5",
|
|
312
|
-
"pricing.unitPrice": "\u5355\u4EF7",
|
|
313
335
|
"pricing.inputCacheHit": "\u8F93\u5165\xB7\u7F13\u5B58\u547D\u4E2D",
|
|
314
336
|
"pricing.inputCacheMiss": "\u8F93\u5165\xB7\u672A\u547D\u4E2D",
|
|
315
337
|
"pricing.output": "\u8F93\u51FA",
|
|
316
338
|
"pricing.peak": "\u9AD8\u5CF0",
|
|
317
|
-
"pricing.fixed": "\u56FA\u5B9A",
|
|
318
339
|
"pricing.offPeak": "\u95F2\u65F6",
|
|
319
340
|
"pricing.unit": "\u5355\u4F4D\uFF1A{currency} / \u767E\u4E07 tokens\u3002",
|
|
320
|
-
"pricing.splitNote": "\u9AD8\u5CF0\u65F6\u6BB5\u4E3A\u5317\u4EAC\u65F6\u95F4 {windows}\uFF0C\u5176\u4F59\u4E3A\u95F2\u65F6\uFF08\u4EF7\u683C\u51CF\u534A\uFF09\uFF1B
|
|
321
|
-
"pricing.
|
|
341
|
+
"pricing.splitNote": "\u9AD8\u5CF0\u65F6\u6BB5\u4E3A\u5317\u4EAC\u65F6\u95F4 {windows}\uFF08\u5468\u4E00\u81F3\u5468\u4E94\uFF09\uFF0C\u5176\u4F59\u4E3A\u95F2\u65F6\uFF08\u4EF7\u683C\u51CF\u534A\uFF09\uFF1B\u6BCF\u6761\u7528\u91CF\u6309\u53D1\u751F\u65F6\u523B\u751F\u6548\u7684\u5B98\u65B9\u4EF7\u683C\u4F30\u7B97\u3002",
|
|
342
|
+
"pricing.visionNote": "\u56FE\u7247\u6309\u5C3A\u5BF8\u6298\u7B97 token\uFF0C\u4E0E\u6587\u672C\u4E00\u5E76\u8BA1\u8D39\u3002",
|
|
343
|
+
"pricing.modelNote": "deepseek-v4-flash\u3001deepseek-v4-flash-vision-exp \u662F\u65E7\u6A21\u578B\u540D\uFF0C\u73B0\u7531 deepseek-flash \u627F\u63A5\u5E76\u6309 Flash \u4EF7\u8BA1\u8D39\uFF1B2026-09-14 12:00 \u8D77 deepseek-v4-pro \u7684\u8BF7\u6C42\u4E5F\u4F1A\u8DEF\u7531\u5230 V4.1-Flash\uFF0C\u6309 Flash \u4EF7\u8BA1\u8D39\u3002",
|
|
322
344
|
"pricing.unknown": "\u672A\u77E5\u6A21\u578B\u6309 deepseek-v4-pro \u8BA1\u4EF7\u3002",
|
|
323
345
|
"coverage.noRange": "\u6682\u65E0\u6709\u6548\u7528\u91CF\u65F6\u95F4",
|
|
324
346
|
"coverage.title": "\u7EDF\u8BA1\u8303\u56F4",
|
|
@@ -352,13 +374,23 @@ var zh = {
|
|
|
352
374
|
"peak.aria": "\u9AD8\u5CF0\u65F6\u6BB5 {peak}%\uFF0C\u95F2\u65F6 {offPeak}%",
|
|
353
375
|
"peak.peakLegend": "\u9AD8\u5CF0 {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
354
376
|
"peak.offPeakLegend": "\u95F2\u65F6 {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
355
|
-
"peak.newPrice": "\u540C\u6837\u7528\u91CF\u5728\u65B0\u4EF7\u4E0B",
|
|
356
|
-
"peak.increase": "\u8F83\u73B0\u4EF7 +{percent}%",
|
|
357
377
|
"peak.shift": "\u82E5\u9AD8\u5CF0\u7528\u91CF\u90FD\u632A\u5230\u95F2\u65F6",
|
|
358
378
|
"peak.saving": "\u53EF\u7701 \xA5{amount}",
|
|
359
|
-
"peak.schedule": "\u9AD8\u5CF0\u65F6\u6BB5\u4E3A\u5317\u4EAC\u65F6\u95F4 {windows}\uFF0C\u95F2\u65F6\u4EF7\u683C\u51CF\u534A\u3002",
|
|
379
|
+
"peak.schedule": "\u9AD8\u5CF0\u65F6\u6BB5\u4E3A\u5317\u4EAC\u65F6\u95F4 {windows}\uFF08\u5468\u4E00\u81F3\u5468\u4E94\uFF09\uFF0C\u95F2\u65F6\u4EF7\u683C\u51CF\u534A\u3002",
|
|
360
380
|
"peak.activeHint": "\u628A\u6279\u91CF\u3001\u53EF\u5EF6\u540E\u7684\u4EFB\u52A1\u653E\u5230\u95F2\u65F6\u8DD1\uFF0C\u540C\u6837\u7684 token \u53EA\u8981\u4E00\u534A\u7684\u94B1\u3002",
|
|
361
|
-
"
|
|
381
|
+
"vision.empty": "{window}\u6CA1\u6709\u56FE\u7247\u8F93\u5165\u3002",
|
|
382
|
+
"vision.title": "\u56FE\u7247 / \u591A\u6A21\u6001\u7528\u91CF",
|
|
383
|
+
"vision.images": "\u56FE\u7247\u5F20\u6570",
|
|
384
|
+
"vision.tokens": "\u4F30\u7B97\u56FE\u7247 tokens",
|
|
385
|
+
"vision.cost": "\u4F30\u7B97\u8D39\u7528",
|
|
386
|
+
"vision.tokensShare": "\u5360\u8F93\u5165 {percent}%",
|
|
387
|
+
"vision.costShare": "\u5360\u8D39\u7528 {percent}%",
|
|
388
|
+
"vision.note": "\u56FE\u7247\u6309\u5B98\u65B9\u6362\u7B97\u89C4\u5219\u4F30\u7B97\uFF1A\u5148\u6309\u6BD4\u4F8B\u7F29\u653E\uFF08\u4E0A\u9650 ~800\xD7800\u3001\u4E0B\u9650 384\xD7384\uFF09\uFF0Ctoken \u4E0E\u7F29\u653E\u540E\u9762\u79EF\u6210\u6B63\u6BD4\u3001\u6BCF\u5F20\u4E0A\u9650 384\uFF1B\u56FE\u7247\u5F52\u5C5E\u5230\u968F\u540E\u4E00\u6B21\u6A21\u578B\u8C03\u7528\uFF0C\u6309\u8BE5\u8C03\u7528\u7684\u65F6\u6BB5\u4E0E\u6A21\u578B\u5355\u4EF7\u4F30\u7B97\uFF0C\u5B9E\u9645\u4EE5\u63A5\u53E3\u8FD4\u56DE\u7684\u7528\u91CF\u4E3A\u51C6\u3002",
|
|
389
|
+
"vision.sessionsTitle": "\u56FE\u7247\u6700\u591A\u7684\u4F1A\u8BDD",
|
|
390
|
+
"vision.sessionRow": "{images} \u5F20 \xB7 \xA5{cost}",
|
|
391
|
+
"vision.dailyTitle": "{window} \xB7 \u6BCF\u5929\u56FE\u7247\u6570",
|
|
392
|
+
"vision.barAria": "{date} \xB7 {images} \u5F20\u56FE\u7247 \xB7 \u4F30\u7B97 {tokens} tokens",
|
|
393
|
+
"vision.barTitle": "{date} \xB7 {images} \u5F20\u56FE\u7247 \xB7 \u4F30\u7B97 {tokens} tokens \xB7 \xA5{cost}",
|
|
362
394
|
"sessions.empty": "\u8FD8\u6CA1\u6709\u4EA7\u751F\u8D39\u7528\u7684\u4F1A\u8BDD\u3002",
|
|
363
395
|
"sessions.detail": "{tokens} tokens \xB7 {calls} \xB7 {ago}",
|
|
364
396
|
"sessions.share": "\u5360 {percent}%",
|
|
@@ -484,7 +516,10 @@ var zh = {
|
|
|
484
516
|
"links.status": "\u670D\u52A1\u72B6\u6001",
|
|
485
517
|
"settings.title": "\u8BBE\u7F6E",
|
|
486
518
|
"settings.widget": "\u5728\u5176\u4ED6\u9875\u9762\u663E\u793A\u53F3\u4E0B\u89D2\u60AC\u6D6E\u989D\u5EA6\u7A97\u53E3",
|
|
487
|
-
"settings.widgetHint": "\
|
|
519
|
+
"settings.widgetHint": "\u989D\u5EA6\u9875\u4F1A\u81EA\u52A8\u9690\u85CF\u6D6E\u7A97\uFF1B\u5176\u4ED6\u9875\u9762\u662F\u5426\u5C55\u793A\u7531\u4E0B\u65B9\u9009\u62E9\u51B3\u5B9A\u3002",
|
|
520
|
+
"settings.widgetTabs": "\u5C55\u793A\u6D6E\u7A97\u7684\u9875\u9762",
|
|
521
|
+
"settings.widgetTabsHint": "\u9875\u9762\u5217\u8868\u6765\u81EA DSH \u7684\u5B9E\u65F6 Tab \u6CE8\u518C\u8868\uFF1B\u5176\u4ED6\u63D2\u4EF6\u65B0\u589E\u7684 Tab \u4E5F\u4F1A\u81EA\u52A8\u51FA\u73B0\u5728\u8FD9\u91CC\u3002",
|
|
522
|
+
"settings.widgetTabsEmpty": "\u5F53\u524D\u6CA1\u6709\u53EF\u914D\u7F6E\u7684\u4F1A\u8BDD\u9875\u9762\u3002",
|
|
488
523
|
"settings.lowBalance": "\u4F59\u989D\u9884\u8B66\u7EBF",
|
|
489
524
|
"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",
|
|
490
525
|
"settings.lowBalanceOff": "\u5DF2\u5173\u95ED\u4F59\u989D\u9884\u8B66\u3002\u586B\u4E00\u4E2A\u5927\u4E8E 0 \u7684\u6570\u5F00\u542F\u3002",
|
|
@@ -497,6 +532,8 @@ var zh = {
|
|
|
497
532
|
"widget.lowTitle": "\u4F59\u989D\u4F4E\u4E8E\u9884\u8B66\u7EBF {amount}",
|
|
498
533
|
"widget.todayCostTitle": "\u4ECA\u65E5\u6D88\u8017\uFF08\u4F30\u7B97\uFF09\xB7 {age}",
|
|
499
534
|
"widget.todayCost": "\u4ECA\u65E5\u6D88\u8017",
|
|
535
|
+
"widget.sessionCostTitle": "\u5F53\u524D\u4F1A\u8BDD\u7D2F\u8BA1\u6D88\u8017\uFF08\u4F30\u7B97\uFF09",
|
|
536
|
+
"widget.sessionCost": "\u5F53\u524D\u4F1A\u8BDD",
|
|
500
537
|
"widget.noBalance": "\u6682\u65E0\u4F59\u989D\u6570\u636E",
|
|
501
538
|
"widget.dragCard": "\u62D6\u52A8\u5230\u56DB\u4E2A\u89D2\u843D",
|
|
502
539
|
"widget.dragGrip": "\u62D6\u52A8\u4EE5\u79FB\u52A8",
|
|
@@ -505,6 +542,9 @@ var zh = {
|
|
|
505
542
|
"widget.collapse": "\u6536\u8D77",
|
|
506
543
|
"widget.expandAria": "\u5C55\u5F00\u989D\u5EA6\u7A97\u53E3",
|
|
507
544
|
"widget.collapseAria": "\u6536\u8D77\u989D\u5EA6\u7A97\u53E3",
|
|
545
|
+
"messageCost.label": "\u672C\u8F6E\u8D39\u7528",
|
|
546
|
+
"messageCost.tooltip": "\u7531 dsh-usage-dashboard \u63D0\u4F9B \xB7 \u672C\u8F6E\u5BF9\u8BDD\u8D39\u7528\u4E3A\u4F30\u7B97\u503C",
|
|
547
|
+
"messageCost.aria": "{amount}\uFF0C\u672C\u8F6E\u5BF9\u8BDD\u4F30\u7B97\u8D39\u7528\uFF0C\u7531 dsh-usage-dashboard \u63D0\u4F9B",
|
|
508
548
|
"boundary.title": "\u989D\u5EA6\u9762\u677F\u51FA\u9519\u4E86",
|
|
509
549
|
"boundary.body": "\u6E32\u67D3\u65F6\u629B\u4E86\u5F02\u5E38\uFF0C\u901A\u5E38\u662F\u6D4F\u89C8\u5668\u91CC\u5B58\u7740\u65E7\u7248\u672C\u7684\u7F13\u5B58\u6570\u636E\u3002\u6E05\u6389\u672C\u63D2\u4EF6\u7684\u672C\u5730\u6570\u636E\u518D\u91CD\u65B0\u52A0\u8F7D\u5C31\u80FD\u6062\u590D\u3002",
|
|
510
550
|
"boundary.reset": "\u6E05\u9664\u672C\u5730\u6570\u636E\u5E76\u91CD\u65B0\u52A0\u8F7D"
|
|
@@ -550,16 +590,15 @@ var en = {
|
|
|
550
590
|
"pricing.currentOffPeak": "Off-peak now",
|
|
551
591
|
"pricing.model": "Model",
|
|
552
592
|
"pricing.period": "Period",
|
|
553
|
-
"pricing.unitPrice": "Rate",
|
|
554
593
|
"pricing.inputCacheHit": "Input \xB7 cache hit",
|
|
555
594
|
"pricing.inputCacheMiss": "Input \xB7 cache miss",
|
|
556
595
|
"pricing.output": "Output",
|
|
557
596
|
"pricing.peak": "Peak",
|
|
558
|
-
"pricing.fixed": "Fixed",
|
|
559
597
|
"pricing.offPeak": "Off-peak",
|
|
560
598
|
"pricing.unit": "Unit: {currency} / million tokens. ",
|
|
561
|
-
"pricing.splitNote": "Peak periods are {windows} Beijing time; off-peak rates are half price.
|
|
562
|
-
"pricing.
|
|
599
|
+
"pricing.splitNote": "Peak periods are {windows} Beijing time, Monday to Friday; off-peak rates are half price. Each record is estimated at the official rates in force when it was written. ",
|
|
600
|
+
"pricing.visionNote": "Images are converted to tokens by size and charged together with text. ",
|
|
601
|
+
"pricing.modelNote": "deepseek-v4-flash and deepseek-v4-flash-vision-exp are legacy names, now served by deepseek-flash at Flash rates; from 2026-09-14 12:00 deepseek-v4-pro requests are routed to V4.1-Flash and billed at Flash rates. ",
|
|
563
602
|
"pricing.unknown": "Unknown models use deepseek-v4-pro rates.",
|
|
564
603
|
"coverage.noRange": "No valid usage timestamps",
|
|
565
604
|
"coverage.title": "Coverage",
|
|
@@ -593,13 +632,23 @@ var en = {
|
|
|
593
632
|
"peak.aria": "{peak}% peak, {offPeak}% off-peak",
|
|
594
633
|
"peak.peakLegend": "Peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
595
634
|
"peak.offPeakLegend": "Off-peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
596
|
-
"peak.newPrice": "Same usage at new rates",
|
|
597
|
-
"peak.increase": "+{percent}% vs current rates",
|
|
598
635
|
"peak.shift": "If all peak usage moved off-peak",
|
|
599
636
|
"peak.saving": "Save \xA5{amount}",
|
|
600
|
-
"peak.schedule": "Peak periods are {windows} Beijing time; off-peak rates are half price. ",
|
|
637
|
+
"peak.schedule": "Peak periods are {windows} Beijing time, Monday to Friday; off-peak rates are half price. ",
|
|
601
638
|
"peak.activeHint": "Run batch or delay-tolerant work off-peak to pay half as much for the same tokens.",
|
|
602
|
-
"
|
|
639
|
+
"vision.empty": "No image input in {window}.",
|
|
640
|
+
"vision.title": "Image / multimodal usage",
|
|
641
|
+
"vision.images": "Images",
|
|
642
|
+
"vision.tokens": "Estimated image tokens",
|
|
643
|
+
"vision.cost": "Estimated cost",
|
|
644
|
+
"vision.tokensShare": "{percent}% of input",
|
|
645
|
+
"vision.costShare": "{percent}% of cost",
|
|
646
|
+
"vision.note": "Images are estimated with the official rule: resized keeping aspect ratio (cap ~800\xD7800, floor 384\xD7384), tokens proportional to the resized area with a 384-token cap per image. Each image is attributed to the next model call and priced at that call's period and model rate; the provider usage numbers remain the source of truth.",
|
|
647
|
+
"vision.sessionsTitle": "Sessions with the most images",
|
|
648
|
+
"vision.sessionRow": "{images} images \xB7 \xA5{cost}",
|
|
649
|
+
"vision.dailyTitle": "{window} \xB7 images per day",
|
|
650
|
+
"vision.barAria": "{date} \xB7 {images} images \xB7 est. {tokens} tokens",
|
|
651
|
+
"vision.barTitle": "{date} \xB7 {images} images \xB7 est. {tokens} tokens \xB7 \xA5{cost}",
|
|
603
652
|
"sessions.empty": "No sessions have incurred cost yet.",
|
|
604
653
|
"sessions.detail": "{tokens} tokens \xB7 {calls} \xB7 {ago}",
|
|
605
654
|
"sessions.share": "{percent}% of total",
|
|
@@ -725,7 +774,10 @@ var en = {
|
|
|
725
774
|
"links.status": "Service status",
|
|
726
775
|
"settings.title": "Settings",
|
|
727
776
|
"settings.widget": "Show the floating balance widget on other views",
|
|
728
|
-
"settings.widgetHint": "The widget hides
|
|
777
|
+
"settings.widgetHint": "The widget always hides on Usage; choose its other views below.",
|
|
778
|
+
"settings.widgetTabs": "Show widget on",
|
|
779
|
+
"settings.widgetTabsHint": "This live list comes from DSH\u2019s tab registry, including tabs added by other plugins.",
|
|
780
|
+
"settings.widgetTabsEmpty": "There are no configurable conversation views right now.",
|
|
729
781
|
"settings.lowBalance": "Balance warning",
|
|
730
782
|
"settings.lowBalanceOn": "This page and the widget turn amber below {amount} {currency}. Enter 0 to disable.",
|
|
731
783
|
"settings.lowBalanceOff": "Balance warnings are off. Enter an amount above 0 to enable them.",
|
|
@@ -738,6 +790,8 @@ var en = {
|
|
|
738
790
|
"widget.lowTitle": "Balance below warning threshold {amount}",
|
|
739
791
|
"widget.todayCostTitle": "Today\u2019s spend (estimated) \xB7 {age}",
|
|
740
792
|
"widget.todayCost": "Today\u2019s spend",
|
|
793
|
+
"widget.sessionCostTitle": "Estimated spend for the current session",
|
|
794
|
+
"widget.sessionCost": "Current session",
|
|
741
795
|
"widget.noBalance": "No balance data",
|
|
742
796
|
"widget.dragCard": "Drag to any corner",
|
|
743
797
|
"widget.dragGrip": "Drag to move",
|
|
@@ -746,6 +800,9 @@ var en = {
|
|
|
746
800
|
"widget.collapse": "Collapse",
|
|
747
801
|
"widget.expandAria": "Expand balance widget",
|
|
748
802
|
"widget.collapseAria": "Collapse balance widget",
|
|
803
|
+
"messageCost.label": "Turn cost",
|
|
804
|
+
"messageCost.tooltip": "Provided by dsh-usage-dashboard \xB7 estimated cost for this turn",
|
|
805
|
+
"messageCost.aria": "{amount}, estimated cost for this turn, provided by dsh-usage-dashboard",
|
|
749
806
|
"boundary.title": "The usage dashboard crashed",
|
|
750
807
|
"boundary.body": "Rendering failed, usually because this browser still has cached data from an older version. Clear this plugin\u2019s local data and reload to recover.",
|
|
751
808
|
"boundary.reset": "Clear local data and reload"
|
|
@@ -1098,9 +1155,34 @@ function getShellFrame(node) {
|
|
|
1098
1155
|
return overlay?.parentElement ?? null;
|
|
1099
1156
|
}
|
|
1100
1157
|
function getComposerElement(frame) {
|
|
1101
|
-
if (frame === null)
|
|
1158
|
+
if (frame === null) {
|
|
1159
|
+
return slotElement(document.documentElement, "conversation.composer.dock", "ancestor");
|
|
1160
|
+
}
|
|
1102
1161
|
return slotElement(frame, "conversation.composer.dock", "ancestor");
|
|
1103
1162
|
}
|
|
1163
|
+
function getComposerInputElement(frame) {
|
|
1164
|
+
const root = frame ?? document.documentElement;
|
|
1165
|
+
const textarea = root.querySelector(
|
|
1166
|
+
'[data-slot="conversation.composer"] textarea, [data-slot="conversation.composer.bar"] textarea, textarea'
|
|
1167
|
+
);
|
|
1168
|
+
if (textarea === null) return null;
|
|
1169
|
+
let el = textarea.parentElement;
|
|
1170
|
+
for (let depth = 0; depth < 6 && el !== null; depth++) {
|
|
1171
|
+
const cs = getComputedStyle(el);
|
|
1172
|
+
if (cs.borderRadius !== "0px" && cs.backgroundColor !== "rgba(0, 0, 0, 0)") return el;
|
|
1173
|
+
el = el.parentElement;
|
|
1174
|
+
}
|
|
1175
|
+
return null;
|
|
1176
|
+
}
|
|
1177
|
+
function getActiveConversationViewId(frame, viewIds) {
|
|
1178
|
+
if (viewIds.length === 0) return null;
|
|
1179
|
+
const header = frame === null ? null : slotElement(frame, "conversation.session.header", "child");
|
|
1180
|
+
const tablist = header?.querySelector('[role="tablist"]') ?? null;
|
|
1181
|
+
if (tablist === null) return viewIds[0] ?? null;
|
|
1182
|
+
const tabs = [...tablist.querySelectorAll('[role="tab"]')];
|
|
1183
|
+
const activeIndex = tabs.findIndex((tab) => tab.getAttribute("aria-selected") === "true");
|
|
1184
|
+
return activeIndex < 0 ? viewIds[0] ?? null : viewIds[activeIndex] ?? null;
|
|
1185
|
+
}
|
|
1104
1186
|
|
|
1105
1187
|
// src/client/export.ts
|
|
1106
1188
|
var BEIJING_OFFSET_MS2 = 8 * 60 * 60 * 1e3;
|
|
@@ -1240,6 +1322,8 @@ function createStore(key, isValid, fallback) {
|
|
|
1240
1322
|
}
|
|
1241
1323
|
var isThreshold = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1242
1324
|
var widgetVisibleStore = createStore("widget.visible", isBoolean, true);
|
|
1325
|
+
var isWidgetTabIds = (value) => value === null || Array.isArray(value) && value.every((id) => typeof id === "string" && id !== "") && new Set(value).size === value.length;
|
|
1326
|
+
var widgetTabIdsStore = createStore("widget.tabIds", isWidgetTabIds, null);
|
|
1243
1327
|
var lowBalanceStore = createStore("alert.lowBalance", isThreshold, 10);
|
|
1244
1328
|
var monthlyBudgetStore = createStore("budget.monthly", isThreshold, 0);
|
|
1245
1329
|
var isUsageWindowDays = (value) => typeof value === "number" && USAGE_WINDOW_DAYS.some((days) => days === value);
|
|
@@ -1393,30 +1477,29 @@ var rate = (value) => `\xA5${value.toLocaleString(void 0, { maximumFractionDigit
|
|
|
1393
1477
|
function PricingNote(props) {
|
|
1394
1478
|
const { t } = useI18n();
|
|
1395
1479
|
const { pricing } = props;
|
|
1396
|
-
const split = pricing.splitActive;
|
|
1397
1480
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { className: "dq-pricing", children: [
|
|
1398
1481
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("summary", { className: "dq-pricing-summary", children: [
|
|
1399
1482
|
t("pricing.title"),
|
|
1400
|
-
|
|
1483
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-pricing-now${pricing.inPeakNow ? " dq-pricing-now--peak" : ""}`, children: pricing.inPeakNow ? t("pricing.currentPeak") : t("pricing.currentOffPeak") })
|
|
1401
1484
|
] }),
|
|
1402
1485
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-pricing-body", children: [
|
|
1403
1486
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("table", { className: "dq-pricing-table", children: [
|
|
1404
1487
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { children: [
|
|
1405
1488
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.model") }),
|
|
1406
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children:
|
|
1489
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.period") }),
|
|
1407
1490
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.inputCacheHit") }),
|
|
1408
1491
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.inputCacheMiss") }),
|
|
1409
1492
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { children: t("pricing.output") })
|
|
1410
1493
|
] }) }),
|
|
1411
1494
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: pricing.tiers.map((tier) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react4.Fragment, { children: [
|
|
1412
1495
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { children: [
|
|
1413
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { rowSpan:
|
|
1414
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children:
|
|
1496
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { rowSpan: 2, children: tier.model }),
|
|
1497
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: t("pricing.peak") }),
|
|
1415
1498
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: rate(tier.peak.cacheHit) }),
|
|
1416
1499
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: rate(tier.peak.input) }),
|
|
1417
1500
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: rate(tier.peak.output) })
|
|
1418
1501
|
] }),
|
|
1419
|
-
|
|
1502
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { children: [
|
|
1420
1503
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: t("pricing.offPeak") }),
|
|
1421
1504
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: rate(tier.offPeak.cacheHit) }),
|
|
1422
1505
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { children: rate(tier.offPeak.input) }),
|
|
@@ -1426,7 +1509,9 @@ function PricingNote(props) {
|
|
|
1426
1509
|
] }),
|
|
1427
1510
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "dq-pricing-foot", children: [
|
|
1428
1511
|
t("pricing.unit", { currency: pricing.currency }),
|
|
1429
|
-
|
|
1512
|
+
t("pricing.splitNote", { windows: pricing.peakWindows.join(t("common.listSeparator")) }),
|
|
1513
|
+
t("pricing.visionNote"),
|
|
1514
|
+
t("pricing.modelNote"),
|
|
1430
1515
|
t("pricing.unknown")
|
|
1431
1516
|
] })
|
|
1432
1517
|
] })
|
|
@@ -1678,15 +1763,50 @@ function CacheCard(props) {
|
|
|
1678
1763
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-cache-foot", children: low ? t("cache.lowHint") : t("cache.goodHint") })
|
|
1679
1764
|
] });
|
|
1680
1765
|
}
|
|
1766
|
+
function VisionCard(props) {
|
|
1767
|
+
const { t, locale } = useI18n();
|
|
1768
|
+
const { vision, daily, sessions, windowLabel, inputTokens, costTotal } = props;
|
|
1769
|
+
if (vision.images === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("vision.empty", { window: windowLabel }) });
|
|
1770
|
+
const tokensShare = inputTokens > 0 ? vision.imageTokens / inputTokens * 100 : 0;
|
|
1771
|
+
const costShare = costTotal > 0 ? vision.cost / costTotal * 100 : 0;
|
|
1772
|
+
const labelEvery = daily.length <= 7 ? 1 : daily.length <= 30 ? 5 : daily.length <= 90 ? 15 : 30;
|
|
1773
|
+
const bars = daily.map((d) => ({
|
|
1774
|
+
label: d.date.slice(8, 10),
|
|
1775
|
+
value: d.images,
|
|
1776
|
+
title: t("vision.barTitle", { date: d.date, images: fmtInt(d.images), tokens: fmtCompact(d.imageTokens, locale), cost: fmt(d.cost) })
|
|
1777
|
+
}));
|
|
1778
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1779
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-usage-totals", children: [
|
|
1780
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("vision.images"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-value", children: fmtInt(vision.images) }) }),
|
|
1781
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("vision.tokens"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat-value", children: [
|
|
1782
|
+
fmtCompact(vision.imageTokens, locale),
|
|
1783
|
+
tokensShare > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-vision-share", children: t("vision.tokensShare", { percent: tokensShare.toFixed(1) }) })
|
|
1784
|
+
] }) }),
|
|
1785
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("vision.cost"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat-value", children: [
|
|
1786
|
+
"\xA5 ",
|
|
1787
|
+
fmt(vision.cost),
|
|
1788
|
+
costShare > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-vision-share", children: t("vision.costShare", { percent: costShare.toFixed(1) }) })
|
|
1789
|
+
] }) })
|
|
1790
|
+
] }),
|
|
1791
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-chart-title", children: t("vision.dailyTitle", { window: windowLabel }) }),
|
|
1792
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Bars, { data: bars, height: 80, labelEvery }),
|
|
1793
|
+
sessions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-vision-sessions", children: [
|
|
1794
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-chart-title", children: t("vision.sessionsTitle") }),
|
|
1795
|
+
sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-session", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-session-head", children: [
|
|
1796
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-session-title", title: session.id, children: session.title }),
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-session-cost", children: t("vision.sessionRow", { images: fmtInt(session.images), cost: fmt(session.cost) }) })
|
|
1798
|
+
] }) }, session.id))
|
|
1799
|
+
] }),
|
|
1800
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-cache-foot", children: t("vision.note") })
|
|
1801
|
+
] });
|
|
1802
|
+
}
|
|
1681
1803
|
function PeakCard(props) {
|
|
1682
1804
|
const { t, locale } = useI18n();
|
|
1683
|
-
const { split, pricing
|
|
1805
|
+
const { split, pricing } = props;
|
|
1684
1806
|
const total = split.peak.total + split.offPeak.total;
|
|
1685
1807
|
if (total === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("peak.empty") });
|
|
1686
1808
|
const peakShare = split.peak.total / total;
|
|
1687
1809
|
const shiftSaving = split.peakEraCost - split.offPeakEraCost;
|
|
1688
|
-
const increase = split.peakEraCost - currentCost;
|
|
1689
|
-
const increasePercent = currentCost > 0 ? increase / currentCost * 100 : 0;
|
|
1690
1810
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-peak", children: [
|
|
1691
1811
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1692
1812
|
"div",
|
|
@@ -1715,27 +1835,17 @@ function PeakCard(props) {
|
|
|
1715
1835
|
})
|
|
1716
1836
|
] })
|
|
1717
1837
|
] }),
|
|
1718
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-peak-delta", children: t("peak.increase", { percent: increasePercent.toFixed(0) }) })
|
|
1725
|
-
] })
|
|
1726
|
-
] }),
|
|
1727
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
1728
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("peak.shift") }),
|
|
1729
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat-value", children: [
|
|
1730
|
-
"\xA5 ",
|
|
1731
|
-
fmt(split.offPeakEraCost),
|
|
1732
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-peak-delta dq-peak-delta--save", children: t("peak.saving", { amount: fmt(shiftSaving) }) })
|
|
1733
|
-
] })
|
|
1838
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-figures", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
1839
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("peak.shift") }),
|
|
1840
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat-value", children: [
|
|
1841
|
+
"\xA5 ",
|
|
1842
|
+
fmt(split.offPeakEraCost),
|
|
1843
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-peak-delta dq-peak-delta--save", children: t("peak.saving", { amount: fmt(shiftSaving) }) })
|
|
1734
1844
|
] })
|
|
1735
|
-
] }),
|
|
1845
|
+
] }) }),
|
|
1736
1846
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "dq-peak-foot", children: [
|
|
1737
1847
|
t("peak.schedule", { windows: pricing.peakWindows.join(t("common.listSeparator")) }),
|
|
1738
|
-
|
|
1848
|
+
t("peak.activeHint")
|
|
1739
1849
|
] })
|
|
1740
1850
|
] });
|
|
1741
1851
|
}
|
|
@@ -1978,6 +2088,9 @@ function BalanceDashboard(props) {
|
|
|
1978
2088
|
const [freshnessNow, setFreshnessNow] = (0, import_react4.useState)(Date.now());
|
|
1979
2089
|
const hadDataRef = (0, import_react4.useRef)(cachedBalance !== null || cachedUsage !== null);
|
|
1980
2090
|
const [widgetOn, setWidgetOn] = (0, import_react4.useState)(widgetVisibleStore.get());
|
|
2091
|
+
const [widgetTabIds, setWidgetTabIds] = (0, import_react4.useState)(widgetTabIdsStore.get());
|
|
2092
|
+
const [viewTabs, setViewTabs] = (0, import_react4.useState)(() => props.views.list());
|
|
2093
|
+
(0, import_react4.useEffect)(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views]);
|
|
1981
2094
|
const [lowBalance, setLowBalance] = (0, import_react4.useState)(lowBalanceStore.get());
|
|
1982
2095
|
const [monthlyBudget, setMonthlyBudget] = (0, import_react4.useState)(monthlyBudgetStore.get());
|
|
1983
2096
|
const [barMode, setBarMode] = (0, import_react4.useState)("daily");
|
|
@@ -1986,9 +2099,12 @@ function BalanceDashboard(props) {
|
|
|
1986
2099
|
const [chartMetric, setChartMetric] = (0, import_react4.useState)(chartMetricStore.get());
|
|
1987
2100
|
const [runwayHovering, setRunwayHovering] = (0, import_react4.useState)(false);
|
|
1988
2101
|
const [runwayManuallyOpened, setRunwayManuallyOpened] = (0, import_react4.useState)(false);
|
|
2102
|
+
const [remainingHovering, setRemainingHovering] = (0, import_react4.useState)(false);
|
|
2103
|
+
const [remainingManuallyOpened, setRemainingManuallyOpened] = (0, import_react4.useState)(false);
|
|
1989
2104
|
const budgetInputRef = (0, import_react4.useRef)(null);
|
|
1990
2105
|
const rootRef = (0, import_react4.useRef)(null);
|
|
1991
2106
|
const [composerHeight, setComposerHeight] = (0, import_react4.useState)(null);
|
|
2107
|
+
const [composerWidth, setComposerWidth] = (0, import_react4.useState)(null);
|
|
1992
2108
|
const [sessionUsage, setSessionUsage] = (0, import_react4.useState)(null);
|
|
1993
2109
|
const [loadingSessionUsage, setLoadingSessionUsage] = (0, import_react4.useState)(props.sessionId !== void 0 && props.sessionId !== "");
|
|
1994
2110
|
const [sessionUsageError, setSessionUsageError] = (0, import_react4.useState)(null);
|
|
@@ -2000,16 +2116,22 @@ function BalanceDashboard(props) {
|
|
|
2000
2116
|
const frame2 = getShellFrame(node);
|
|
2001
2117
|
const composer2 = getComposerElement(frame2);
|
|
2002
2118
|
const rect = composer2?.getBoundingClientRect() ?? null;
|
|
2003
|
-
const
|
|
2004
|
-
setComposerHeight((prev) => prev ===
|
|
2119
|
+
const nextHeight = rect !== null && rect.height > 0 ? Math.round(rect.height) : null;
|
|
2120
|
+
setComposerHeight((prev) => prev === nextHeight ? prev : nextHeight);
|
|
2121
|
+
const input2 = getComposerInputElement(frame2);
|
|
2122
|
+
const inputRect = input2?.getBoundingClientRect() ?? null;
|
|
2123
|
+
const nextWidth = inputRect !== null && inputRect.width > 0 ? Math.round(inputRect.width) : null;
|
|
2124
|
+
setComposerWidth((prev) => prev === nextWidth ? prev : nextWidth);
|
|
2005
2125
|
};
|
|
2006
2126
|
measure();
|
|
2007
2127
|
const frame = getShellFrame(node);
|
|
2008
2128
|
const composer = getComposerElement(frame);
|
|
2129
|
+
const input = getComposerInputElement(frame);
|
|
2009
2130
|
let observer = null;
|
|
2010
2131
|
if (typeof ResizeObserver === "function") {
|
|
2011
2132
|
observer = new ResizeObserver(measure);
|
|
2012
2133
|
if (composer !== null) observer.observe(composer);
|
|
2134
|
+
if (input !== null) observer.observe(input);
|
|
2013
2135
|
if (frame !== null) observer.observe(frame);
|
|
2014
2136
|
}
|
|
2015
2137
|
window.addEventListener("resize", measure);
|
|
@@ -2018,6 +2140,12 @@ function BalanceDashboard(props) {
|
|
|
2018
2140
|
window.removeEventListener("resize", measure);
|
|
2019
2141
|
};
|
|
2020
2142
|
}, []);
|
|
2143
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
2144
|
+
const node = rootRef.current;
|
|
2145
|
+
if (node === null) return;
|
|
2146
|
+
const scrollport = node.closest("[data-conversation-scroll]");
|
|
2147
|
+
if (scrollport instanceof HTMLElement) scrollport.scrollTop = 0;
|
|
2148
|
+
}, [props.sessionId]);
|
|
2021
2149
|
(0, import_react4.useLayoutEffect)(() => {
|
|
2022
2150
|
quotaViewActiveStore.set(true);
|
|
2023
2151
|
return () => quotaViewActiveStore.set(false);
|
|
@@ -2118,6 +2246,13 @@ function BalanceDashboard(props) {
|
|
|
2118
2246
|
setWidgetOn(next);
|
|
2119
2247
|
widgetVisibleStore.set(next);
|
|
2120
2248
|
};
|
|
2249
|
+
const widgetTabs = viewTabs.filter((tab) => tab.id !== "balance");
|
|
2250
|
+
const toggleWidgetTab = (id) => {
|
|
2251
|
+
const selected = widgetTabIds ?? widgetTabs.map((tab) => tab.id);
|
|
2252
|
+
const next = selected.includes(id) ? selected.filter((tabId) => tabId !== id) : [...selected, id];
|
|
2253
|
+
setWidgetTabIds(next);
|
|
2254
|
+
widgetTabIdsStore.set(next);
|
|
2255
|
+
};
|
|
2121
2256
|
const changeLowBalance = (value) => {
|
|
2122
2257
|
setLowBalance(value);
|
|
2123
2258
|
lowBalanceStore.set(value);
|
|
@@ -2220,7 +2355,11 @@ function BalanceDashboard(props) {
|
|
|
2220
2355
|
);
|
|
2221
2356
|
const errorText = error === null ? null : localizeApiError(error, t, "error.query");
|
|
2222
2357
|
const composerSafeAreaStyle = {
|
|
2223
|
-
"--dq-composer-h": `${composerHeight ?? COMPOSER_FALLBACK_PX}px
|
|
2358
|
+
"--dq-composer-h": `${composerHeight ?? COMPOSER_FALLBACK_PX}px`,
|
|
2359
|
+
// Only set once measured; .dq-balance's max-width falls back to 860px
|
|
2360
|
+
// without it (pre-measurement render keeps the settled wide-viewport
|
|
2361
|
+
// column, then narrows in place — no flash of the old width).
|
|
2362
|
+
...composerWidth !== null ? { "--dq-composer-w": `${composerWidth}px` } : {}
|
|
2224
2363
|
};
|
|
2225
2364
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-balance", ref: rootRef, style: composerSafeAreaStyle, children: [
|
|
2226
2365
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-row", children: [
|
|
@@ -2273,17 +2412,36 @@ function BalanceDashboard(props) {
|
|
|
2273
2412
|
primary !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-balance-grid", children: [
|
|
2274
2413
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
2275
2414
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("balance.remaining") }),
|
|
2276
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
" ",
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2415
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2416
|
+
"details",
|
|
2417
|
+
{
|
|
2418
|
+
className: "dq-stat-value dq-remaining",
|
|
2419
|
+
open: remainingHovering || remainingManuallyOpened,
|
|
2420
|
+
onPointerEnter: () => setRemainingHovering(true),
|
|
2421
|
+
onPointerLeave: () => setRemainingHovering(false),
|
|
2422
|
+
children: [
|
|
2423
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2424
|
+
"summary",
|
|
2425
|
+
{
|
|
2426
|
+
title: t("balance.breakdownTitle"),
|
|
2427
|
+
onClick: (e) => {
|
|
2428
|
+
e.preventDefault();
|
|
2429
|
+
setRemainingManuallyOpened((v) => !v);
|
|
2430
|
+
},
|
|
2431
|
+
children: [
|
|
2432
|
+
fmt(primary.total),
|
|
2433
|
+
" ",
|
|
2434
|
+
primary.currency
|
|
2435
|
+
]
|
|
2436
|
+
}
|
|
2437
|
+
),
|
|
2438
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-remaining-breakdown", children: [
|
|
2439
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("balance.toppedUp", { amount: fmt(primary.toppedUp) }) }),
|
|
2440
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-remaining-granted", children: t("balance.granted", { amount: fmt(primary.granted) }) })
|
|
2441
|
+
] })
|
|
2442
|
+
]
|
|
2443
|
+
}
|
|
2444
|
+
)
|
|
2287
2445
|
] }),
|
|
2288
2446
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
2289
2447
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("balance.runway") }),
|
|
@@ -2345,6 +2503,31 @@ function BalanceDashboard(props) {
|
|
|
2345
2503
|
),
|
|
2346
2504
|
usage !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PricingNote, { pricing: usage.pricing })
|
|
2347
2505
|
] }),
|
|
2506
|
+
usage !== null && activeWindow !== null && activeWindow.vision !== void 0 && Array.isArray(activeWindow.visionDaily) && Array.isArray(activeWindow.visionSessions) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2507
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("vision.title") }),
|
|
2508
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2509
|
+
VisionCard,
|
|
2510
|
+
{
|
|
2511
|
+
vision: activeWindow.vision,
|
|
2512
|
+
daily: activeWindow.visionDaily,
|
|
2513
|
+
sessions: activeWindow.visionSessions,
|
|
2514
|
+
windowLabel: activeWindowName,
|
|
2515
|
+
inputTokens: activeWindow.totals.input,
|
|
2516
|
+
costTotal: activeWindow.totals.cost
|
|
2517
|
+
}
|
|
2518
|
+
)
|
|
2519
|
+
] }) : usage === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2520
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("vision.title") }),
|
|
2521
|
+
loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-usage-totals", children: [64, 88, 72].map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w, h: 11 }),
|
|
2523
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 72, h: 20 })
|
|
2524
|
+
] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("vision.empty", { window: activeWindowName }) })
|
|
2525
|
+
] }) : (
|
|
2526
|
+
// Host serving a pre-vision payload (e.g. not restarted yet): hide the
|
|
2527
|
+
// whole card instead of leaving a titled shell — it reappears as soon
|
|
2528
|
+
// as the host half serves the new shape.
|
|
2529
|
+
null
|
|
2530
|
+
),
|
|
2348
2531
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--primary", children: [
|
|
2349
2532
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card-head", children: [
|
|
2350
2533
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("usage.trendTitle") }),
|
|
@@ -2421,7 +2604,7 @@ function BalanceDashboard(props) {
|
|
|
2421
2604
|
] }),
|
|
2422
2605
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2423
2606
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("peak.title") }),
|
|
2424
|
-
usage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PeakCard, { split: usage.peakSplit, pricing: usage.pricing
|
|
2607
|
+
usage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PeakCard, { split: usage.peakSplit, pricing: usage.pricing }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
2425
2608
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-track" }),
|
|
2426
2609
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-peak-figures", children: [
|
|
2427
2610
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 120, h: 11 }),
|
|
@@ -2465,6 +2648,23 @@ function BalanceDashboard(props) {
|
|
|
2465
2648
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("settings.widget") })
|
|
2466
2649
|
] }),
|
|
2467
2650
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-toggle-hint", children: t("settings.widgetHint") }),
|
|
2651
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting dq-setting--tabs", children: [
|
|
2652
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-widget-tabs-label", children: t("settings.widgetTabs") }),
|
|
2653
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control dq-widget-tabs", role: "group", "aria-labelledby": "dq-widget-tabs-label", children: [
|
|
2654
|
+
/* @__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: [
|
|
2655
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2656
|
+
"input",
|
|
2657
|
+
{
|
|
2658
|
+
type: "checkbox",
|
|
2659
|
+
checked: widgetTabIds === null || widgetTabIds.includes(tab.id),
|
|
2660
|
+
onChange: () => toggleWidgetTab(tab.id)
|
|
2661
|
+
}
|
|
2662
|
+
),
|
|
2663
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: tab.label })
|
|
2664
|
+
] }, tab.id)) }),
|
|
2665
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: widgetTabs.length === 0 ? t("settings.widgetTabsEmpty") : t("settings.widgetTabsHint") })
|
|
2666
|
+
] })
|
|
2667
|
+
] }),
|
|
2468
2668
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2469
2669
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-low-balance", children: t("settings.lowBalance") }),
|
|
2470
2670
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
@@ -2503,14 +2703,55 @@ function BalanceDashboard(props) {
|
|
|
2503
2703
|
/* @__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") })
|
|
2504
2704
|
] })
|
|
2505
2705
|
] })
|
|
2506
|
-
] })
|
|
2706
|
+
] }),
|
|
2707
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.6.0" })
|
|
2507
2708
|
] });
|
|
2508
2709
|
}
|
|
2509
2710
|
|
|
2711
|
+
// src/client/message-cost.tsx
|
|
2712
|
+
var import_react5 = require("react");
|
|
2713
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2714
|
+
var fmtTurnCost = (cost) => cost.toLocaleString(void 0, {
|
|
2715
|
+
minimumFractionDigits: cost > 0 && cost < 0.01 ? 4 : 2,
|
|
2716
|
+
maximumFractionDigits: cost > 0 && cost < 0.01 ? 6 : 4
|
|
2717
|
+
});
|
|
2718
|
+
var cachedData = (sessionId) => {
|
|
2719
|
+
const response = getCachedSessionUsage(sessionId);
|
|
2720
|
+
return response?.ok === true ? response.data ?? null : null;
|
|
2721
|
+
};
|
|
2722
|
+
function MessageCost({ sessionId, messageId }) {
|
|
2723
|
+
const { t } = useI18n();
|
|
2724
|
+
const [data, setData] = (0, import_react5.useState)(() => cachedData(sessionId));
|
|
2725
|
+
(0, import_react5.useEffect)(() => {
|
|
2726
|
+
setData(cachedData(sessionId));
|
|
2727
|
+
const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData(sessionId)));
|
|
2728
|
+
const hit = cachedData(sessionId)?.turns.some((turn2) => turn2.messageId === messageId) ?? false;
|
|
2729
|
+
if (!hit) void fetchSessionUsage(sessionId);
|
|
2730
|
+
return unsubscribe;
|
|
2731
|
+
}, [messageId, sessionId]);
|
|
2732
|
+
const turn = data?.turns.find((candidate) => candidate.messageId === messageId);
|
|
2733
|
+
if (turn === void 0) return null;
|
|
2734
|
+
const amount = `\xA5 ${fmtTurnCost(turn.cost)}`;
|
|
2735
|
+
const tooltip = t("messageCost.tooltip");
|
|
2736
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
2737
|
+
"span",
|
|
2738
|
+
{
|
|
2739
|
+
className: "dq-message-cost",
|
|
2740
|
+
tabIndex: 0,
|
|
2741
|
+
"data-dq-tooltip": tooltip,
|
|
2742
|
+
"aria-label": t("messageCost.aria", { amount }),
|
|
2743
|
+
children: [
|
|
2744
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dq-message-cost-separator", "aria-hidden": true, children: "\xB7" }),
|
|
2745
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dq-message-cost-amount", children: amount })
|
|
2746
|
+
]
|
|
2747
|
+
}
|
|
2748
|
+
);
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2510
2751
|
// src/client/styles.ts
|
|
2511
2752
|
var css = `
|
|
2512
2753
|
.dsh-quota-root{position:absolute;right:16px;bottom:16px;z-index:2147483000;pointer-events:auto;opacity:1;visibility:visible;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:13px;line-height:1.45;color:var(--dsw-alias-label-primary,#1f2328);transition:left .28s cubic-bezier(.22,1,.36,1),top .28s cubic-bezier(.22,1,.36,1),opacity .16s ease-out,visibility 0s linear}
|
|
2513
|
-
.dsh-quota-root.dsh-quota-root--
|
|
2754
|
+
.dsh-quota-root.dsh-quota-root--hidden{opacity:0;visibility:hidden;pointer-events:none;transition:opacity .14s ease-out,visibility 0s linear .14s}
|
|
2514
2755
|
.dsh-quota-root.dsh-quota-dragging{transition:none}
|
|
2515
2756
|
.dsh-quota-card,.dsh-quota-card *{box-sizing:border-box}
|
|
2516
2757
|
.dsh-quota-card{background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);padding:12px 14px;min-width:208px;max-width:270px;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none}
|
|
@@ -2540,12 +2781,28 @@ var css = `
|
|
|
2540
2781
|
.dsh-quota-label{color:var(--dsw-alias-label-secondary,#59636e)}
|
|
2541
2782
|
.dsh-quota-value{color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
|
|
2542
2783
|
.dsh-quota-error{color:var(--dsw-alias-state-error-primary,#cf222e)}
|
|
2784
|
+
.dq-message-cost{position:relative;display:inline-flex;align-items:center;order:10;gap:10px;height:28px;color:var(--dsw-alias-label-tertiary,#59636e);font-size:12px;line-height:20px;font-variant-numeric:tabular-nums;white-space:nowrap;cursor:help}
|
|
2785
|
+
.dq-message-cost-separator{cursor:default}
|
|
2786
|
+
.dq-message-cost-amount{text-decoration-line:underline;text-decoration-style:dotted;text-decoration-color:currentColor;text-underline-offset:4px}
|
|
2787
|
+
.dq-message-cost:hover{color:var(--dsw-alias-label-secondary,#3f4852)}
|
|
2788
|
+
.dq-message-cost:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px;border-radius:4px}
|
|
2789
|
+
.dq-message-cost::after{content:attr(data-dq-tooltip);position:absolute;left:0;top:calc(100% + 6px);z-index:2147483001;width:max-content;max-width:min(240px,calc(100vw - 24px));padding:6px 9px;border-radius:8px;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);box-shadow:0 6px 18px rgba(0,0,0,.22);font-size:12px;font-weight:400;line-height:1.45;text-align:left;white-space:normal;pointer-events:none;opacity:0;visibility:hidden;transform:translateY(-3px);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear .12s}
|
|
2790
|
+
.dq-message-cost:hover::after,.dq-message-cost:focus-visible::after{opacity:1;visibility:visible;transform:translateY(0);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear}
|
|
2791
|
+
@media (hover:hover){
|
|
2792
|
+
[data-time-hover-root] .dq-message-cost{opacity:0;transition:opacity 80ms ease}
|
|
2793
|
+
[data-time-hover-root]:hover .dq-message-cost,[data-time-hover-root]:focus-within .dq-message-cost{opacity:1}
|
|
2794
|
+
}
|
|
2543
2795
|
/* \u5361\u7247\u95F4\u8DDD\uFF08\u6B64\u5904 gap\uFF09\u523B\u610F\u5927\u4E8E\u5361\u5185\u5C0F\u8282\u95F4\u8DDD\uFF08margin-top:12/14/16px \u90A3\u4E00\u6863\uFF09\uFF0C\u5236\u9020"\u5361\u7247\u5916\u758F\u3001\u5361\u7247\u5185\u5BC6"\u7684
|
|
2544
2796
|
\u758F\u5BC6\u5BF9\u6BD4\uFF1A24px \u76F8\u5BF9\u5361\u5185\u6700\u5927\u6863 16px \u4ECD\u6709 1.5 \u500D\u7EA7\u5DEE\uFF0C\u76F8\u5BF9\u5E38\u89C1\u6863 12px \u6709 2 \u500D\u7EA7\u5DEE\uFF0C\u4E0D\u4E0E .dq-card \u7684
|
|
2545
2797
|
padding\uFF0816px/20px\uFF0C\u5361\u7247\u5185\u90E8\u547C\u5438\u611F\uFF0C\u8F6E\u6B21 36 \u5DF2\u5B9A\uFF09\u6DF7\u540C\u3002 */
|
|
2546
|
-
|
|
2798
|
+
/* Width parity with the host composer: the dashboard column reserves the
|
|
2799
|
+
same max-width as the conversation reading column (860px, \u8F6E\u6B21 42 kept it
|
|
2800
|
+
deliberately), but on narrower viewports the fixed 860px would overhang
|
|
2801
|
+
the input box \u2014 measured in dashboard.tsx as --dq-composer-w, capped at
|
|
2802
|
+
the reading-column width so wide viewports are unchanged. */
|
|
2803
|
+
.dq-balance{display:flex;flex-direction:column;gap:24px;padding:20px 24px 48px;max-width:min(860px,var(--dq-composer-w,860px));margin:0 auto;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}
|
|
2547
2804
|
@media (max-width:620px){.dq-balance{box-sizing:border-box;max-width:calc(100vw - 56px);padding:16px 16px calc(var(--dq-composer-h,126px) + 16px)}}
|
|
2548
|
-
.dq-card{background:var(--dsw-alias-bg-layer-1,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-radius:12px;padding:16px}
|
|
2805
|
+
.dq-card{background:var(--dsw-alias-bg-layer-1,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-radius:12px;padding:16px;min-width:0;max-width:100%}
|
|
2549
2806
|
.dq-card-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:0 0 12px;letter-spacing:.02em}
|
|
2550
2807
|
.dq-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}
|
|
2551
2808
|
.dq-card-head .dq-card-title{margin:0}
|
|
@@ -2606,12 +2863,15 @@ var css = `
|
|
|
2606
2863
|
.dq-runway[open] .dq-runway-detail{display:block}
|
|
2607
2864
|
/* hero number for the balance, matching .dq-period-cost; relies on cascade order (declared after
|
|
2608
2865
|
.dq-stat-value, same specificity) to win over the .dq-stat-value it's paired with in JSX \u2014 keep it below */
|
|
2866
|
+
/* \u8F6E\u6B21 47: .dq-remaining is a controlled <details> too now (see dashboard.tsx), same reason as
|
|
2867
|
+
.dq-runway above \u2014 an unopened <details>'s non-summary children don't actually render in current
|
|
2868
|
+
Chromium regardless of author display value, so the old :hover{display:flex} branch was dead code. */
|
|
2609
2869
|
.dq-remaining{position:relative;font-size:22px;font-weight:680;letter-spacing:-.01em;line-height:1.25}
|
|
2610
2870
|
.dq-remaining>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}
|
|
2611
2871
|
.dq-remaining>summary::-webkit-details-marker{display:none}
|
|
2612
2872
|
.dq-remaining>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}
|
|
2613
2873
|
.dq-remaining-breakdown{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;flex-direction:column;gap:4px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:nowrap;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328)}
|
|
2614
|
-
.dq-remaining
|
|
2874
|
+
.dq-remaining[open] .dq-remaining-breakdown{display:flex}
|
|
2615
2875
|
.dq-remaining-granted{color:var(--dsw-alias-label-tertiary,#59636e);font-weight:400;font-size:11px}
|
|
2616
2876
|
/* narrow screens: .dq-balance-grid wraps its 3 stats onto separate lines, so the absolutely-positioned
|
|
2617
2877
|
.dq-remaining-breakdown/.dq-runway-detail popovers can float down over whichever stat now sits in the
|
|
@@ -2758,6 +3018,9 @@ p.dq-session-sub{margin:8px 0}
|
|
|
2758
3018
|
.dq-setting-label{font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}
|
|
2759
3019
|
.dq-setting-control{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
|
|
2760
3020
|
.dq-setting-hint{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);line-height:1.5}
|
|
3021
|
+
/* Page footer: the package name + version, injected at build time from
|
|
3022
|
+
package.json (see build.mjs) so it can never drift from the release. */
|
|
3023
|
+
.dq-footer{margin:18px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}
|
|
2761
3024
|
.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}
|
|
2762
3025
|
.dq-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;border-color:transparent}
|
|
2763
3026
|
.dq-links-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 8px}
|
|
@@ -2772,7 +3035,11 @@ p.dq-session-sub{margin:8px 0}
|
|
|
2772
3035
|
.dq-toggle{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:13px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
|
|
2773
3036
|
.dq-toggle input{width:16px;height:16px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer}
|
|
2774
3037
|
.dq-toggle-hint{margin:5px 0 0 26px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
2775
|
-
.dq-
|
|
3038
|
+
.dq-widget-tabs{align-items:flex-start;flex-direction:column;gap:7px}
|
|
3039
|
+
.dq-widget-tab-list{display:flex;align-items:center;flex-wrap:wrap;gap:6px 14px}
|
|
3040
|
+
.dq-widget-tab{display:inline-flex;align-items:center;gap:7px;min-height:28px;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;cursor:pointer}
|
|
3041
|
+
.dq-widget-tab input{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}
|
|
3042
|
+
.dq-toggle input:focus-visible,.dq-model-item input:focus-visible,.dq-widget-tab input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
2776
3043
|
.dq-link:focus-visible,.dq-model-btn:focus-visible,.dq-chart-switch-btn:focus-visible,.dq-export-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
2777
3044
|
.dq-usage-totals{display:flex;flex-wrap:wrap;gap:12px 24px;margin-bottom:16px}
|
|
2778
3045
|
/* value itself (number+unit, e.g. "9999.9\u4E07") must never break across lines inside its own column \u2014
|
|
@@ -2780,6 +3047,16 @@ p.dq-session-sub{margin:8px 0}
|
|
|
2780
3047
|
case is the font-size drop below, which keeps every measured stress value comfortably under the
|
|
2781
3048
|
83px column width without ever hitting the ellipsis fallback (see round 38 PROGRESS entry) */
|
|
2782
3049
|
.dq-usage-totals .dq-stat-value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
3050
|
+
.dq-vision-share{display:inline-block;margin-left:6px;font-size:11px;font-weight:550;color:var(--dsw-alias-label-tertiary,#59636e)}
|
|
3051
|
+
.dq-vision-sessions{margin-top:12px;display:flex;flex-direction:column;gap:8px;min-width:0}
|
|
3052
|
+
/* The vision card's session rows reuse .dq-session-head, but \u2014 unlike the
|
|
3053
|
+
ranking card's rows \u2014 they sit directly under .dq-session with no
|
|
3054
|
+
.dq-session-summary-body wrapper to carry min-width:0. Without the chain
|
|
3055
|
+
below, a long session title (nowrap) sets the row's min-content width and
|
|
3056
|
+
can stretch the card past its siblings / the container. */
|
|
3057
|
+
.dq-vision-sessions .dq-session{min-width:0}
|
|
3058
|
+
.dq-vision-sessions .dq-session-head{min-width:0}
|
|
3059
|
+
.dq-vision-sessions .dq-session-title{flex:1 1 auto;min-width:0}
|
|
2783
3060
|
@media (max-width:620px){.dq-usage-totals{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 24px}.dq-usage-totals .dq-stat{min-width:0}.dq-usage-totals .dq-stat-value{font-size:13px}}
|
|
2784
3061
|
.dq-chart-peak{margin-left:10px;font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);font-variant-numeric:tabular-nums}
|
|
2785
3062
|
.dq-chart-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:16px 0 8px}
|
|
@@ -2882,14 +3159,15 @@ p.dq-session-sub{margin:8px 0}
|
|
|
2882
3159
|
@media (prefers-reduced-motion:reduce){
|
|
2883
3160
|
.dsh-quota-root,.dsh-quota-root *,
|
|
2884
3161
|
.dsh-quota-root *::before,.dsh-quota-root *::after,
|
|
3162
|
+
.dq-message-cost,.dq-message-cost::after,
|
|
2885
3163
|
.dq-balance,.dq-balance *,
|
|
2886
3164
|
.dq-balance *::before,.dq-balance *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
|
|
2887
3165
|
}
|
|
2888
3166
|
`;
|
|
2889
3167
|
|
|
2890
3168
|
// src/client/widget.tsx
|
|
2891
|
-
var
|
|
2892
|
-
var
|
|
3169
|
+
var import_react6 = require("react");
|
|
3170
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
2893
3171
|
var MARGIN = 16;
|
|
2894
3172
|
var CORNER_KEY = "widget.corner";
|
|
2895
3173
|
var COLLAPSED_KEY = "widget.collapsed";
|
|
@@ -2937,30 +3215,68 @@ function cornerPos(corner, node, bounds) {
|
|
|
2937
3215
|
if (corner === "bl") return { x: xLeft, y: yBottom };
|
|
2938
3216
|
return { x: xRight, y: yBottom };
|
|
2939
3217
|
}
|
|
2940
|
-
function QuotaWidget() {
|
|
3218
|
+
function QuotaWidget(props) {
|
|
2941
3219
|
const { t } = useI18n();
|
|
2942
|
-
const
|
|
2943
|
-
(0,
|
|
2944
|
-
|
|
2945
|
-
(0,
|
|
2946
|
-
|
|
2947
|
-
(0,
|
|
3220
|
+
const sessionId = props.useSessions((state) => state.current);
|
|
3221
|
+
const [visible, setVisible] = (0, import_react6.useState)(widgetVisibleStore.get());
|
|
3222
|
+
(0, import_react6.useEffect)(() => widgetVisibleStore.subscribe(() => setVisible(widgetVisibleStore.get())), []);
|
|
3223
|
+
const [quotaViewActive2, setQuotaViewActive] = (0, import_react6.useState)(quotaViewActiveStore.get());
|
|
3224
|
+
(0, import_react6.useEffect)(() => quotaViewActiveStore.subscribe(() => setQuotaViewActive(quotaViewActiveStore.get())), []);
|
|
3225
|
+
const [lowBalance, setLowBalance] = (0, import_react6.useState)(lowBalanceStore.get());
|
|
3226
|
+
(0, import_react6.useEffect)(() => lowBalanceStore.subscribe(() => setLowBalance(lowBalanceStore.get())), []);
|
|
3227
|
+
const [selectedTabIds, setSelectedTabIds] = (0, import_react6.useState)(widgetTabIdsStore.get());
|
|
3228
|
+
(0, import_react6.useEffect)(() => widgetTabIdsStore.subscribe(() => setSelectedTabIds(widgetTabIdsStore.get())), []);
|
|
3229
|
+
const [viewTabs, setViewTabs] = (0, import_react6.useState)(() => props.views.list());
|
|
3230
|
+
(0, import_react6.useEffect)(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views]);
|
|
3231
|
+
const [activeViewId, setActiveViewId] = (0, import_react6.useState)(null);
|
|
2948
3232
|
const cachedBalance = getCachedBalance();
|
|
2949
|
-
const [data, setData] = (0,
|
|
2950
|
-
const [error, setError] = (0,
|
|
2951
|
-
const [loading, setLoading] = (0,
|
|
2952
|
-
const [collapsed, setCollapsed] = (0,
|
|
2953
|
-
const [pos, setPos] = (0,
|
|
2954
|
-
const [corner, setCorner] = (0,
|
|
2955
|
-
const [dragging, setDragging] = (0,
|
|
2956
|
-
const rootRef = (0,
|
|
2957
|
-
const dragRef = (0,
|
|
3233
|
+
const [data, setData] = (0, import_react6.useState)(cachedBalance?.data ?? null);
|
|
3234
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
3235
|
+
const [loading, setLoading] = (0, import_react6.useState)(cachedBalance === null);
|
|
3236
|
+
const [collapsed, setCollapsed] = (0, import_react6.useState)(() => loadPref(COLLAPSED_KEY, isBoolean, false));
|
|
3237
|
+
const [pos, setPos] = (0, import_react6.useState)(null);
|
|
3238
|
+
const [corner, setCorner] = (0, import_react6.useState)(() => loadPref(CORNER_KEY, isCorner, "br"));
|
|
3239
|
+
const [dragging, setDragging] = (0, import_react6.useState)(false);
|
|
3240
|
+
const rootRef = (0, import_react6.useRef)(null);
|
|
3241
|
+
const dragRef = (0, import_react6.useRef)(null);
|
|
2958
3242
|
const readUsage = () => {
|
|
2959
3243
|
const cached = getCachedUsage();
|
|
2960
3244
|
return { data: cached?.ok === true ? cached.data ?? null : null, at: getCachedUsageAt() };
|
|
2961
3245
|
};
|
|
2962
|
-
const [usage, setUsage] = (0,
|
|
2963
|
-
(0,
|
|
3246
|
+
const [usage, setUsage] = (0, import_react6.useState)(readUsage);
|
|
3247
|
+
(0, import_react6.useEffect)(() => subscribeUsage(() => setUsage(readUsage())), []);
|
|
3248
|
+
const readSessionUsage = (id) => {
|
|
3249
|
+
if (id === void 0) return null;
|
|
3250
|
+
const response = getCachedSessionUsage(id);
|
|
3251
|
+
return response?.ok === true ? response.data ?? null : null;
|
|
3252
|
+
};
|
|
3253
|
+
const [sessionUsage, setSessionUsage] = (0, import_react6.useState)(() => readSessionUsage(sessionId));
|
|
3254
|
+
(0, import_react6.useEffect)(() => {
|
|
3255
|
+
setSessionUsage(readSessionUsage(sessionId));
|
|
3256
|
+
if (sessionId === void 0) return;
|
|
3257
|
+
const unsubscribe = subscribeSessionUsage(sessionId, () => setSessionUsage(readSessionUsage(sessionId)));
|
|
3258
|
+
void fetchSessionUsage(sessionId);
|
|
3259
|
+
return unsubscribe;
|
|
3260
|
+
}, [sessionId]);
|
|
3261
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
3262
|
+
const viewIds = viewTabs.map((tab) => tab.id);
|
|
3263
|
+
const read = () => {
|
|
3264
|
+
const next = getActiveConversationViewId(getShellFrame(rootRef.current), viewIds);
|
|
3265
|
+
setActiveViewId((previous) => previous === next ? previous : next);
|
|
3266
|
+
};
|
|
3267
|
+
read();
|
|
3268
|
+
const frame = getShellFrame(rootRef.current);
|
|
3269
|
+
let observer = null;
|
|
3270
|
+
if (frame !== null && typeof MutationObserver === "function") {
|
|
3271
|
+
observer = new MutationObserver(read);
|
|
3272
|
+
observer.observe(frame, { subtree: true, childList: true, attributes: true, attributeFilter: ["aria-selected"] });
|
|
3273
|
+
}
|
|
3274
|
+
const poll = setInterval(read, 1e3);
|
|
3275
|
+
return () => {
|
|
3276
|
+
observer?.disconnect();
|
|
3277
|
+
clearInterval(poll);
|
|
3278
|
+
};
|
|
3279
|
+
}, [sessionId, viewTabs]);
|
|
2964
3280
|
const load = async (showLoading, force = false) => {
|
|
2965
3281
|
if (showLoading) setLoading(true);
|
|
2966
3282
|
try {
|
|
@@ -2979,14 +3295,14 @@ function QuotaWidget() {
|
|
|
2979
3295
|
setLoading(false);
|
|
2980
3296
|
}
|
|
2981
3297
|
};
|
|
2982
|
-
(0,
|
|
3298
|
+
(0, import_react6.useEffect)(() => {
|
|
2983
3299
|
void load(true);
|
|
2984
3300
|
const id = setInterval(() => {
|
|
2985
3301
|
void load(false);
|
|
2986
3302
|
}, 6e4);
|
|
2987
3303
|
return () => clearInterval(id);
|
|
2988
3304
|
}, []);
|
|
2989
|
-
(0,
|
|
3305
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
2990
3306
|
if (!visible || quotaViewActive2) return;
|
|
2991
3307
|
const node = rootRef.current;
|
|
2992
3308
|
if (node === null) return;
|
|
@@ -3106,49 +3422,68 @@ function QuotaWidget() {
|
|
|
3106
3422
|
endDrag(true);
|
|
3107
3423
|
};
|
|
3108
3424
|
if (!visible) return null;
|
|
3425
|
+
const allowedOnActiveView = activeViewId === null ? sessionId === void 0 : activeViewId !== "balance" && (selectedTabIds === null || selectedTabIds.includes(activeViewId));
|
|
3426
|
+
const hidden = quotaViewActive2 || !allowedOnActiveView;
|
|
3109
3427
|
const primary = data !== null && data.balances.length > 0 ? data.balances[0] : null;
|
|
3110
3428
|
const balanceValue = primary !== null ? Number(primary.total) : Number.NaN;
|
|
3111
3429
|
const low = lowBalance > 0 && Number.isFinite(balanceValue) && balanceValue < lowBalance;
|
|
3112
3430
|
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";
|
|
3113
3431
|
const ageMinutes = usage.at === null ? null : Math.floor((Date.now() - usage.at) / 6e4);
|
|
3114
3432
|
const usageAgeText = ageMinutes === null || ageMinutes < 1 ? t("widget.updatedNow") : t("widget.stale", { minutes: ageMinutes });
|
|
3433
|
+
const sessionCostRow = sessionUsage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-row", title: t("widget.sessionCostTitle"), children: [
|
|
3434
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-quota-label", children: t("widget.sessionCost") }),
|
|
3435
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
3436
|
+
"\xA5 ",
|
|
3437
|
+
fmtTurnCost(sessionUsage.cost)
|
|
3438
|
+
] })
|
|
3439
|
+
] });
|
|
3115
3440
|
let body = null;
|
|
3116
3441
|
if (!collapsed) {
|
|
3117
3442
|
if (loading && data === null && error === null) {
|
|
3118
|
-
body = /* @__PURE__ */ (0,
|
|
3443
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
3444
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { children: t("widget.loading") }),
|
|
3445
|
+
sessionCostRow
|
|
3446
|
+
] });
|
|
3119
3447
|
} else if (error !== null && data === null) {
|
|
3120
|
-
body = /* @__PURE__ */ (0,
|
|
3448
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
3449
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-quota-error", children: localizeApiError(error, t, "error.query") }),
|
|
3450
|
+
sessionCostRow
|
|
3451
|
+
] });
|
|
3121
3452
|
} else if (primary !== null) {
|
|
3122
|
-
body = /* @__PURE__ */ (0,
|
|
3123
|
-
/* @__PURE__ */ (0,
|
|
3124
|
-
/* @__PURE__ */ (0,
|
|
3125
|
-
/* @__PURE__ */ (0,
|
|
3126
|
-
/* @__PURE__ */ (0,
|
|
3453
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
3454
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-quota-remaining-label", children: t("balance.remaining") }),
|
|
3455
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { title: low ? t("widget.lowTitle", { amount: fmt(lowBalance) }) : void 0, children: [
|
|
3456
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: `dsh-quota-total${low ? " dsh-quota-total--low" : ""}`, children: fmt(primary.total) }),
|
|
3457
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-quota-currency", children: primary.currency })
|
|
3127
3458
|
] }),
|
|
3128
|
-
usage.data !== null && /* @__PURE__ */ (0,
|
|
3129
|
-
/* @__PURE__ */ (0,
|
|
3130
|
-
/* @__PURE__ */ (0,
|
|
3459
|
+
usage.data !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-row", title: t("widget.todayCostTitle", { age: usageAgeText }), children: [
|
|
3460
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-quota-label", children: t("widget.todayCost") }),
|
|
3461
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
3131
3462
|
"\xA5 ",
|
|
3132
3463
|
fmt(usage.data.summary.today.cost)
|
|
3133
3464
|
] })
|
|
3134
3465
|
] }),
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
/* @__PURE__ */ (0,
|
|
3466
|
+
sessionCostRow,
|
|
3467
|
+
data?.isAvailable === false && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-row", children: [
|
|
3468
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-quota-label", children: t("common.status") }),
|
|
3469
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-quota-value dsh-quota-error", children: t("common.unavailable") })
|
|
3138
3470
|
] })
|
|
3139
3471
|
] });
|
|
3140
3472
|
} else {
|
|
3141
|
-
body = /* @__PURE__ */ (0,
|
|
3473
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
3474
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-quota-error", children: t("widget.noBalance") }),
|
|
3475
|
+
sessionCostRow
|
|
3476
|
+
] });
|
|
3142
3477
|
}
|
|
3143
3478
|
}
|
|
3144
3479
|
const style = pos !== null ? { left: `${pos.x}px`, top: `${pos.y}px`, right: "auto", bottom: "auto" } : void 0;
|
|
3145
|
-
return /* @__PURE__ */ (0,
|
|
3480
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3146
3481
|
"div",
|
|
3147
3482
|
{
|
|
3148
|
-
className: `dsh-quota-root${dragging ? " dsh-quota-dragging" : ""}${
|
|
3483
|
+
className: `dsh-quota-root${dragging ? " dsh-quota-dragging" : ""}${hidden ? " dsh-quota-root--hidden" : ""}`,
|
|
3149
3484
|
style,
|
|
3150
|
-
"aria-hidden":
|
|
3151
|
-
children: /* @__PURE__ */ (0,
|
|
3485
|
+
"aria-hidden": hidden,
|
|
3486
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3152
3487
|
"div",
|
|
3153
3488
|
{
|
|
3154
3489
|
ref: rootRef,
|
|
@@ -3160,14 +3495,14 @@ function QuotaWidget() {
|
|
|
3160
3495
|
onPointerCancel: () => endDrag(false),
|
|
3161
3496
|
onLostPointerCapture: () => endDrag(false),
|
|
3162
3497
|
children: [
|
|
3163
|
-
/* @__PURE__ */ (0,
|
|
3164
|
-
/* @__PURE__ */ (0,
|
|
3165
|
-
/* @__PURE__ */ (0,
|
|
3166
|
-
/* @__PURE__ */ (0,
|
|
3167
|
-
/* @__PURE__ */ (0,
|
|
3498
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-header", children: [
|
|
3499
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-title", children: [
|
|
3500
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-quota-grip", title: t("widget.dragGrip"), children: "\u2807" }),
|
|
3501
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: dotClass }),
|
|
3502
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-quota-name", children: t("widget.title") })
|
|
3168
3503
|
] }),
|
|
3169
|
-
/* @__PURE__ */ (0,
|
|
3170
|
-
/* @__PURE__ */ (0,
|
|
3504
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-actions", children: [
|
|
3505
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3171
3506
|
"button",
|
|
3172
3507
|
{
|
|
3173
3508
|
className: "dsh-quota-btn",
|
|
@@ -3178,11 +3513,12 @@ function QuotaWidget() {
|
|
|
3178
3513
|
onClick: () => {
|
|
3179
3514
|
void load(true, true);
|
|
3180
3515
|
void fetchUsage(true);
|
|
3516
|
+
if (sessionId !== void 0) void fetchSessionUsage(sessionId, true);
|
|
3181
3517
|
},
|
|
3182
3518
|
children: "\u21BB"
|
|
3183
3519
|
}
|
|
3184
3520
|
),
|
|
3185
|
-
/* @__PURE__ */ (0,
|
|
3521
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3186
3522
|
"button",
|
|
3187
3523
|
{
|
|
3188
3524
|
className: "dsh-quota-btn",
|
|
@@ -3200,7 +3536,7 @@ function QuotaWidget() {
|
|
|
3200
3536
|
)
|
|
3201
3537
|
] })
|
|
3202
3538
|
] }),
|
|
3203
|
-
collapsed && primary !== null && /* @__PURE__ */ (0,
|
|
3539
|
+
collapsed && primary !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-quota-collapsed-total-row", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: `dsh-quota-collapsed-total${low ? " dsh-quota-total--low" : ""}`, children: [
|
|
3204
3540
|
fmt(primary.total),
|
|
3205
3541
|
" ",
|
|
3206
3542
|
primary.currency
|
|
@@ -3213,13 +3549,31 @@ function QuotaWidget() {
|
|
|
3213
3549
|
);
|
|
3214
3550
|
}
|
|
3215
3551
|
|
|
3552
|
+
// src/client/views.ts
|
|
3553
|
+
var labelOf = (entry) => {
|
|
3554
|
+
const { id = "", label } = entry.options;
|
|
3555
|
+
try {
|
|
3556
|
+
const resolved = typeof label === "function" ? label() : label;
|
|
3557
|
+
return typeof resolved === "string" && resolved !== "" ? resolved : id;
|
|
3558
|
+
} catch {
|
|
3559
|
+
return id;
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
function conversationViewsSource(slots) {
|
|
3563
|
+
return {
|
|
3564
|
+
list: () => slots.entries("conversation.view").flatMap((entry) => entry.options.id === void 0 ? [] : [{ id: entry.options.id, label: labelOf(entry) }]),
|
|
3565
|
+
subscribe: (fn) => slots.subscribe("conversation.view", fn)
|
|
3566
|
+
};
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3216
3569
|
// src/client/index.tsx
|
|
3217
|
-
var
|
|
3570
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3218
3571
|
var name = "dsh-usage-dashboard";
|
|
3219
3572
|
var inject = ["slots", "locale"];
|
|
3220
3573
|
function apply(ctx) {
|
|
3221
3574
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-usage-dashboard: browser dictionaries");
|
|
3222
3575
|
const t = ctx.locale.bind(NS);
|
|
3576
|
+
const views = conversationViewsSource(ctx.slots);
|
|
3223
3577
|
if (typeof document !== "undefined") {
|
|
3224
3578
|
const tag = document.createElement("style");
|
|
3225
3579
|
tag.dataset.plugin = "dsh-usage-dashboard";
|
|
@@ -3232,7 +3586,17 @@ function apply(ctx) {
|
|
|
3232
3586
|
});
|
|
3233
3587
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register(
|
|
3234
3588
|
{ name: "shell.overlay", id: "deepseek-quota", order: 1e3, locale: NS, label: () => t("widget.title") },
|
|
3235
|
-
({ t: slotT }) => /* @__PURE__ */ (0,
|
|
3589
|
+
({ t: slotT, useSessions }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(QuotaWidget, { useSessions, views }) }) })
|
|
3590
|
+
));
|
|
3591
|
+
ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register(
|
|
3592
|
+
{
|
|
3593
|
+
name: "conversation.chat.assistant-actions",
|
|
3594
|
+
id: "dsh-usage-turn-cost",
|
|
3595
|
+
order: 5,
|
|
3596
|
+
locale: NS,
|
|
3597
|
+
label: () => t("messageCost.label")
|
|
3598
|
+
},
|
|
3599
|
+
({ t: slotT, sessionId, messageId }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageCost, { sessionId, messageId }) }) })
|
|
3236
3600
|
));
|
|
3237
3601
|
ctx.slots.inject("conversation.view", () => ctx.slots.register(
|
|
3238
3602
|
{ name: "conversation.view", id: "balance", order: 20, locale: NS, label: () => t("nav.quota") },
|
|
@@ -3243,7 +3607,7 @@ function apply(ctx) {
|
|
|
3243
3607
|
// `sessionId` is typed optional defensively even though the host is
|
|
3244
3608
|
// expected to always supply it: BalanceDashboard renders the current-
|
|
3245
3609
|
// session card only when it actually has one, never throws either way.
|
|
3246
|
-
({ t: slotT, sessionId }) => /* @__PURE__ */ (0,
|
|
3610
|
+
({ t: slotT, sessionId }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ErrorBoundary, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BalanceDashboard, { sessionId, views }) }) })
|
|
3247
3611
|
));
|
|
3248
3612
|
}
|
|
3249
3613
|
return module.exports; } });
|