@cassius0924/dsh-usage-dashboard 0.7.0 → 0.8.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 +4 -1
- package/lib/client.js +825 -170
- package/lib/client.js.map +4 -4
- package/lib/types/client/context.d.ts +17 -2
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/layout-prefs.d.ts +56 -0
- package/lib/types/client/locales.d.ts +26 -16
- package/lib/types/client/session-cost-pill.d.ts +15 -0
- package/lib/types/client/store.d.ts +1 -0
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/client/views.d.ts +1 -0
- package/package.json +2 -1
package/lib/client.js
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
name: () => name
|
|
27
27
|
});
|
|
28
28
|
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
var import_react8 = require("react");
|
|
29
30
|
|
|
30
31
|
// src/client/boundary.tsx
|
|
31
32
|
var import_react = require("react");
|
|
@@ -317,7 +318,7 @@ var zh = {
|
|
|
317
318
|
"delta.noBaseline": "\u65E0\u5BF9\u6BD4",
|
|
318
319
|
"delta.flat": "\u6301\u5E73",
|
|
319
320
|
"period.volume": "{tokens} tokens \xB7 {calls}",
|
|
320
|
-
"budget.unset": "\
|
|
321
|
+
"budget.unset": "\u672A\u8BBE\u7F6E\u6708\u5EA6\u9884\u7B97\u3002",
|
|
321
322
|
"budget.configure": "\u8BBE\u7F6E\u9884\u7B97",
|
|
322
323
|
"budget.over": "\u5DF2\u8D85\u9884\u7B97 \xA5{amount}",
|
|
323
324
|
"budget.forecastOver": "\u7167\u5F53\u524D\u901F\u5EA6\uFF0C\u9884\u8BA1\u8D85\u51FA \xA5{amount}",
|
|
@@ -340,7 +341,6 @@ var zh = {
|
|
|
340
341
|
"pricing.unit": "\u5355\u4F4D\uFF1A{currency} / \u767E\u4E07 tokens\u3002",
|
|
341
342
|
"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
343
|
"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",
|
|
344
344
|
"pricing.unknown": "\u672A\u77E5\u6A21\u578B\u6309 deepseek-v4-pro \u8BA1\u4EF7\u3002",
|
|
345
345
|
"coverage.noRange": "\u6682\u65E0\u6709\u6548\u7528\u91CF\u65F6\u95F4",
|
|
346
346
|
"coverage.title": "\u7EDF\u8BA1\u8303\u56F4",
|
|
@@ -361,23 +361,19 @@ var zh = {
|
|
|
361
361
|
"export.modelCsv": "\u9010\u6A21\u578B CSV",
|
|
362
362
|
"export.fullJson": "\u5B8C\u6574 JSON",
|
|
363
363
|
"export.jsonHint": "\u542B\u56FE\u8868\u4E0E\u6392\u884C",
|
|
364
|
-
"cache.empty": "\
|
|
364
|
+
"cache.empty": "\u6682\u65E0\u7F13\u5B58\u547D\u4E2D\u6570\u636E\u3002",
|
|
365
365
|
"cache.rate": "\u7F13\u5B58\u547D\u4E2D\u7387",
|
|
366
366
|
"cache.saved": "\u5DF2\u8282\u7701\u8D39\u7528\uFF08\u4F30\u7B97\uFF09",
|
|
367
367
|
"cache.allMiss": "\u82E5\u5168\u90E8\u672A\u547D\u4E2D",
|
|
368
368
|
"cache.aria": "\u7F13\u5B58\u547D\u4E2D {hit}%\uFF0C\u672A\u547D\u4E2D {miss}%",
|
|
369
369
|
"cache.hitLegend": "\u7F13\u5B58\u547D\u4E2D {tokens}",
|
|
370
370
|
"cache.missLegend": "\u672A\u547D\u4E2D {tokens}\uFF08\u6309\u672A\u547D\u4E2D\u4EF7\u8BA1\u8D39\uFF09",
|
|
371
|
-
"
|
|
372
|
-
"cache.goodHint": "\u524D\u7F00\u7F13\u5B58\u628A\u91CD\u590D\u7684 prompt \u524D\u7F00\u6309\u547D\u4E2D\u4EF7\u8BA1\u8D39\uFF0C\u662F\u8FD9\u4EFD\u8D26\u5355\u4E0A\u6700\u5927\u7684\u7701\u94B1\u6760\u6746\u3002",
|
|
373
|
-
"peak.empty": "\u8FD8\u6CA1\u6709\u53EF\u6309\u65F6\u6BB5\u5F52\u7C7B\u7684\u7528\u91CF\u3002",
|
|
371
|
+
"peak.empty": "\u6682\u65E0\u7528\u91CF\u3002",
|
|
374
372
|
"peak.aria": "\u9AD8\u5CF0\u65F6\u6BB5 {peak}%\uFF0C\u95F2\u65F6 {offPeak}%",
|
|
375
373
|
"peak.peakLegend": "\u9AD8\u5CF0 {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
376
374
|
"peak.offPeakLegend": "\u95F2\u65F6 {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
377
375
|
"peak.shift": "\u82E5\u9AD8\u5CF0\u7528\u91CF\u90FD\u632A\u5230\u95F2\u65F6",
|
|
378
376
|
"peak.saving": "\u53EF\u7701 \xA5{amount}",
|
|
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",
|
|
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",
|
|
381
377
|
"vision.empty": "{window}\u6CA1\u6709\u56FE\u7247\u8F93\u5165\u3002",
|
|
382
378
|
"vision.title": "\u56FE\u7247 / \u591A\u6A21\u6001\u7528\u91CF",
|
|
383
379
|
"vision.images": "\u56FE\u7247\u5F20\u6570",
|
|
@@ -385,7 +381,7 @@ var zh = {
|
|
|
385
381
|
"vision.cost": "\u4F30\u7B97\u8D39\u7528",
|
|
386
382
|
"vision.tokensShare": "\u5360\u8F93\u5165 {percent}%",
|
|
387
383
|
"vision.costShare": "\u5360\u8D39\u7528 {percent}%",
|
|
388
|
-
"vision.note": "\
|
|
384
|
+
"vision.note": "\u6309\u5B98\u65B9\u89C4\u5219\u4F30\u7B97\uFF0C\u5B9E\u9645\u4EE5\u63A5\u53E3\u8FD4\u56DE\u7684\u7528\u91CF\u4E3A\u51C6\u3002",
|
|
389
385
|
"vision.sessionsTitle": "\u56FE\u7247\u6700\u591A\u7684\u4F1A\u8BDD",
|
|
390
386
|
"vision.sessionRow": "{images} \u5F20 \xB7 \xA5{cost}",
|
|
391
387
|
"vision.dailyTitle": "{window} \xB7 \u6BCF\u5929\u56FE\u7247\u6570",
|
|
@@ -394,7 +390,7 @@ var zh = {
|
|
|
394
390
|
"sessions.empty": "\u8FD8\u6CA1\u6709\u4EA7\u751F\u8D39\u7528\u7684\u4F1A\u8BDD\u3002",
|
|
395
391
|
"sessions.detail": "{tokens} tokens \xB7 {calls} \xB7 {ago}",
|
|
396
392
|
"sessions.share": "\u5360 {percent}%",
|
|
397
|
-
"sessions.more": "\u5171 {count} \u4E2A\u4F1A\u8BDD\
|
|
393
|
+
"sessions.more": "\u5171 {count} \u4E2A\u4F1A\u8BDD\uFF0C\u663E\u793A\u6700\u8D35\u7684 {shown} \u4E2A\u3002",
|
|
398
394
|
"sessions.fallbackTitle": "\u4F1A\u8BDD {id}",
|
|
399
395
|
"models.empty": "\u8FD8\u6CA1\u6709\u53EF\u5F52\u7C7B\u7684\u6A21\u578B\u7528\u91CF\u3002",
|
|
400
396
|
"models.shareAria": "{name} \u5360\u603B\u8D39\u7528 {percent}%",
|
|
@@ -421,9 +417,9 @@ var zh = {
|
|
|
421
417
|
"chart.dimensionLabel": "\u5207\u6362\u56FE\u8868\u7EF4\u5EA6",
|
|
422
418
|
"chart.daily": "\u9010\u5929",
|
|
423
419
|
"chart.hourly": "\u9010\u5C0F\u65F6",
|
|
424
|
-
"chart.noWindowData": "{window}\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\
|
|
420
|
+
"chart.noWindowData": "{window}\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\u3002",
|
|
425
421
|
"chart.heatmapTitle": "\u8FD1\u4E00\u5E74 \xB7 \u6BCF\u65E5",
|
|
426
|
-
"chart.empty": "\u8FD8\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\u3002
|
|
422
|
+
"chart.empty": "\u8FD8\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\u3002",
|
|
427
423
|
"chart.hourPoint": "{hour} \u70B9",
|
|
428
424
|
"chart.tooltip": "{head} \xB7 {tokens} tokens \xB7 \xA5{cost} \xB7 {calls}",
|
|
429
425
|
"chart.less": "\u5C11",
|
|
@@ -489,7 +485,7 @@ var zh = {
|
|
|
489
485
|
"balance.overYear": "> 365 \u5929",
|
|
490
486
|
"balance.noRunway": "\u8FD1 {days} \u5929\u6CA1\u6709\u7528\u91CF\uFF0C\u65E0\u6CD5\u4F30\u7B97",
|
|
491
487
|
"balance.runwayTitle": "\u6309\u8FD1 {days} \u5929\u65E5\u5747 \xA5{cost} \u4F30\u7B97\uFF08\u542B\u65E0\u7528\u91CF\u7684\u65E5\u5B50\uFF1B\u4ECA\u5929\u5C1A\u672A\u8FC7\u5B8C\uFF09",
|
|
492
|
-
"balance.empty": "\u8BFB\u4E0D\u5230\u4F59\u989D\
|
|
488
|
+
"balance.empty": "\u8BFB\u4E0D\u5230\u4F59\u989D\uFF0C\u8BF7\u68C0\u67E5\u8D26\u53F7\u767B\u5F55\u6216 API Key \u540E\u5237\u65B0\u3002",
|
|
493
489
|
"sessionUsage.title": "\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017",
|
|
494
490
|
"common.estimated": "\u8D39\u7528\u4E3A\u4F30\u7B97",
|
|
495
491
|
"sessionUsage.cost": "\u8D39\u7528",
|
|
@@ -497,7 +493,7 @@ var zh = {
|
|
|
497
493
|
"sessionUsage.calls": "\u8C03\u7528\u6B21\u6570",
|
|
498
494
|
"sessionUsage.range": "\u6D3B\u8DC3\u65F6\u95F4 {range}",
|
|
499
495
|
"sessionUsage.single": "{model} \xB7 \u8F93\u5165 {input} / \u8F93\u51FA {output} / \u7F13\u5B58 {cache}",
|
|
500
|
-
"sessionUsage.empty": "\u8FD9\u4E2A\u4F1A\u8BDD\u8FD8\u6CA1\u6709\
|
|
496
|
+
"sessionUsage.empty": "\u8FD9\u4E2A\u4F1A\u8BDD\u8FD8\u6CA1\u6709\u7528\u91CF\u3002",
|
|
501
497
|
"overview.title": "\u6D88\u8017\u6982\u89C8",
|
|
502
498
|
"overview.today": "\u4ECA\u65E5",
|
|
503
499
|
"overview.month": "\u672C\u6708",
|
|
@@ -519,9 +515,9 @@ var zh = {
|
|
|
519
515
|
"links.status": "\u670D\u52A1\u72B6\u6001",
|
|
520
516
|
"settings.title": "\u8BBE\u7F6E",
|
|
521
517
|
"settings.widget": "\u5728\u5176\u4ED6\u9875\u9762\u663E\u793A\u53F3\u4E0B\u89D2\u60AC\u6D6E\u989D\u5EA6\u7A97\u53E3",
|
|
522
|
-
"settings.widgetHint": "\u989D\u5EA6\u9875\
|
|
518
|
+
"settings.widgetHint": "\u989D\u5EA6\u9875\u81EA\u52A8\u9690\u85CF\uFF1B\u5176\u4ED6\u9875\u9762\u6309\u4E0B\u65B9\u9009\u62E9\u663E\u793A\u3002",
|
|
523
519
|
"settings.widgetTabs": "\u5C55\u793A\u6D6E\u7A97\u7684\u9875\u9762",
|
|
524
|
-
"settings.widgetTabsHint": "\
|
|
520
|
+
"settings.widgetTabsHint": "\u5217\u8868\u4F1A\u968F\u5DF2\u5B89\u88C5\u7684\u9875\u9762\u81EA\u52A8\u66F4\u65B0\u3002",
|
|
525
521
|
"settings.widgetTabsEmpty": "\u5F53\u524D\u6CA1\u6709\u53EF\u914D\u7F6E\u7684\u4F1A\u8BDD\u9875\u9762\u3002",
|
|
526
522
|
"settings.lowBalance": "\u4F59\u989D\u9884\u8B66\u7EBF",
|
|
527
523
|
"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",
|
|
@@ -529,6 +525,18 @@ var zh = {
|
|
|
529
525
|
"settings.monthlyBudget": "\u6708\u5EA6\u9884\u7B97",
|
|
530
526
|
"settings.monthlyBudgetOn": "\u6309\u5317\u4EAC\u65F6\u95F4\u81EA\u7136\u6708\u8DDF\u8E2A {amount} CNY \u9884\u7B97\uFF0C\u5E76\u5728\u6D88\u8017\u6982\u89C8\u9884\u6D4B\u6708\u5E95\u82B1\u8D39\u3002\u586B 0 \u5173\u95ED\u3002",
|
|
531
527
|
"settings.monthlyBudgetOff": "\u586B\u4E00\u4E2A\u5927\u4E8E 0 \u7684\u91D1\u989D\uFF0C\u6D88\u8017\u6982\u89C8\u4F1A\u663E\u793A\u8FDB\u5EA6\u4E0E\u6708\u5E95\u9884\u6D4B\u3002",
|
|
528
|
+
"settings.sidebarEntry": "\u4FA7\u8FB9\u680F\u989D\u5EA6\u5165\u53E3",
|
|
529
|
+
"settings.sidebarEntryHint": "\u5728\u4FA7\u8FB9\u680F\u9876\u90E8\u6216\u5E95\u90E8\u653E\u4E00\u4E2A\u300C\u989D\u5EA6\u300D\u5165\u53E3\uFF0C\u6253\u5F00\u8D26\u6237\u7EA7\u989D\u5EA6\u9875\u3002",
|
|
530
|
+
"settings.sidebarEntryOff": "\u5173\u95ED",
|
|
531
|
+
"settings.sidebarEntryTop": "\u9876\u90E8",
|
|
532
|
+
"settings.sidebarEntryBottom": "\u5E95\u90E8",
|
|
533
|
+
"settings.tabPosition": "\u300C\u989D\u5EA6\u300D\u6807\u7B7E\u4F4D\u7F6E",
|
|
534
|
+
"settings.tabPositionHint": "\u62D6\u52A8\u300C\u989D\u5EA6\u300D\u8C03\u6574\u6807\u7B7E\u4F4D\u7F6E\u3002",
|
|
535
|
+
"settings.tabDragAria": "\u62D6\u52A8\u300C\u989D\u5EA6\u300D\u8C03\u6574\u6807\u7B7E\u4F4D\u7F6E",
|
|
536
|
+
"settings.tabMoveEarlier": "\u524D\u79FB",
|
|
537
|
+
"settings.tabMoveLater": "\u540E\u79FB",
|
|
538
|
+
"panel.caption": "\u8D26\u6237\u7EA7 \xB7 \u8DE8\u5168\u90E8\u4F1A\u8BDD",
|
|
539
|
+
"panel.openAria": "\u6253\u5F00\u989D\u5EA6\u9875",
|
|
532
540
|
"widget.updatedNow": "\u521A\u521A\u66F4\u65B0",
|
|
533
541
|
"widget.stale": "{minutes} \u5206\u949F\u524D\u7684\u6570\u636E\uFF0C\u70B9 \u21BB \u66F4\u65B0",
|
|
534
542
|
"widget.loading": "\u67E5\u8BE2\u4E2D\u2026",
|
|
@@ -546,6 +554,9 @@ var zh = {
|
|
|
546
554
|
"widget.expandAria": "\u5C55\u5F00\u989D\u5EA6\u7A97\u53E3",
|
|
547
555
|
"widget.collapseAria": "\u6536\u8D77\u989D\u5EA6\u7A97\u53E3",
|
|
548
556
|
"messageCost.label": "\u672C\u8F6E\u8D39\u7528",
|
|
557
|
+
"sessionCost.title": "\u4F1A\u8BDD\u8D39\u7528",
|
|
558
|
+
"sessionCost.aria": "\u672C\u4F1A\u8BDD\u4F30\u7B97\u8D39\u7528 {amount}",
|
|
559
|
+
"sessionCost.active": "\u6D3B\u8DC3\u65F6\u95F4",
|
|
549
560
|
"messageCost.tooltip": "\u7531 dsh-usage-dashboard \u63D0\u4F9B \xB7 \u672C\u8F6E\u5BF9\u8BDD\u8D39\u7528\u4E3A\u4F30\u7B97\u503C",
|
|
550
561
|
"messageCost.aria": "{amount}\uFF0C\u672C\u8F6E\u5BF9\u8BDD\u4F30\u7B97\u8D39\u7528\uFF0C\u7531 dsh-usage-dashboard \u63D0\u4F9B",
|
|
551
562
|
"boundary.title": "\u989D\u5EA6\u9762\u677F\u51FA\u9519\u4E86",
|
|
@@ -578,7 +589,7 @@ var en = {
|
|
|
578
589
|
"delta.noBaseline": "No baseline",
|
|
579
590
|
"delta.flat": "No change",
|
|
580
591
|
"period.volume": "{tokens} tokens \xB7 {calls}",
|
|
581
|
-
"budget.unset": "
|
|
592
|
+
"budget.unset": "No monthly budget set.",
|
|
582
593
|
"budget.configure": "Set budget",
|
|
583
594
|
"budget.over": "Over budget by \xA5{amount}",
|
|
584
595
|
"budget.forecastOver": "At this pace, \xA5{amount} over by month-end",
|
|
@@ -601,7 +612,6 @@ var en = {
|
|
|
601
612
|
"pricing.unit": "Unit: {currency} / million tokens. ",
|
|
602
613
|
"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. ",
|
|
603
614
|
"pricing.visionNote": "Images are converted to tokens by size and charged together with text. ",
|
|
604
|
-
"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. ",
|
|
605
615
|
"pricing.unknown": "Unknown models use deepseek-v4-pro rates.",
|
|
606
616
|
"coverage.noRange": "No valid usage timestamps",
|
|
607
617
|
"coverage.title": "Coverage",
|
|
@@ -622,23 +632,19 @@ var en = {
|
|
|
622
632
|
"export.modelCsv": "Model CSV",
|
|
623
633
|
"export.fullJson": "Full JSON",
|
|
624
634
|
"export.jsonHint": "Charts and rankings included",
|
|
625
|
-
"cache.empty": "No
|
|
635
|
+
"cache.empty": "No cache data yet.",
|
|
626
636
|
"cache.rate": "Cache hit rate",
|
|
627
637
|
"cache.saved": "Estimated savings",
|
|
628
638
|
"cache.allMiss": "Cost with no hits",
|
|
629
639
|
"cache.aria": "{hit}% cache hits, {miss}% misses",
|
|
630
640
|
"cache.hitLegend": "Cache hits {tokens}",
|
|
631
641
|
"cache.missLegend": "Misses {tokens} (charged at miss rate)",
|
|
632
|
-
"
|
|
633
|
-
"cache.goodHint": "Prefix caching charges repeated prompt prefixes at the hit rate, making it the largest saving lever on this bill.",
|
|
634
|
-
"peak.empty": "No usage can be classified by pricing period yet.",
|
|
642
|
+
"peak.empty": "No usage yet.",
|
|
635
643
|
"peak.aria": "{peak}% peak, {offPeak}% off-peak",
|
|
636
644
|
"peak.peakLegend": "Peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
637
645
|
"peak.offPeakLegend": "Off-peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
638
646
|
"peak.shift": "If all peak usage moved off-peak",
|
|
639
647
|
"peak.saving": "Save \xA5{amount}",
|
|
640
|
-
"peak.schedule": "Peak periods are {windows} Beijing time, Monday to Friday; off-peak rates are half price. ",
|
|
641
|
-
"peak.activeHint": "Run batch or delay-tolerant work off-peak to pay half as much for the same tokens.",
|
|
642
648
|
"vision.empty": "No image input in {window}.",
|
|
643
649
|
"vision.title": "Image / multimodal usage",
|
|
644
650
|
"vision.images": "Images",
|
|
@@ -646,7 +652,7 @@ var en = {
|
|
|
646
652
|
"vision.cost": "Estimated cost",
|
|
647
653
|
"vision.tokensShare": "{percent}% of input",
|
|
648
654
|
"vision.costShare": "{percent}% of cost",
|
|
649
|
-
"vision.note": "
|
|
655
|
+
"vision.note": "Estimated with the official rule; provider usage numbers are the source of truth.",
|
|
650
656
|
"vision.sessionsTitle": "Sessions with the most images",
|
|
651
657
|
"vision.sessionRow": "{images} images \xB7 \xA5{cost}",
|
|
652
658
|
"vision.dailyTitle": "{window} \xB7 images per day",
|
|
@@ -655,7 +661,7 @@ var en = {
|
|
|
655
661
|
"sessions.empty": "No sessions have incurred cost yet.",
|
|
656
662
|
"sessions.detail": "{tokens} tokens \xB7 {calls} \xB7 {ago}",
|
|
657
663
|
"sessions.share": "{percent}% of total",
|
|
658
|
-
"sessions.more": "{count} sessions
|
|
664
|
+
"sessions.more": "{count} sessions; showing the {shown} most expensive.",
|
|
659
665
|
"sessions.fallbackTitle": "Session {id}",
|
|
660
666
|
"models.empty": "No model usage can be classified yet.",
|
|
661
667
|
"models.shareAria": "{name} accounts for {percent}% of total cost",
|
|
@@ -682,9 +688,9 @@ var en = {
|
|
|
682
688
|
"chart.dimensionLabel": "Chart interval",
|
|
683
689
|
"chart.daily": "Daily",
|
|
684
690
|
"chart.hourly": "Hourly",
|
|
685
|
-
"chart.noWindowData": "No usage in {window}.
|
|
691
|
+
"chart.noWindowData": "No usage in {window}.",
|
|
686
692
|
"chart.heatmapTitle": "Last 12 months \xB7 daily",
|
|
687
|
-
"chart.empty": "No usage yet.
|
|
693
|
+
"chart.empty": "No usage yet.",
|
|
688
694
|
"chart.hourPoint": "{hour}:00",
|
|
689
695
|
"chart.tooltip": "{head} \xB7 {tokens} tokens \xB7 \xA5{cost} \xB7 {calls}",
|
|
690
696
|
"chart.less": "Less",
|
|
@@ -750,7 +756,7 @@ var en = {
|
|
|
750
756
|
"balance.overYear": "> 365 days",
|
|
751
757
|
"balance.noRunway": "No usage in the last {days} days, so runway cannot be estimated",
|
|
752
758
|
"balance.runwayTitle": "Based on \xA5{cost} average daily spend over the last {days} days (including idle days; today is incomplete)",
|
|
753
|
-
"balance.empty": "Balance is unavailable.
|
|
759
|
+
"balance.empty": "Balance is unavailable. Check your sign-in or API key, then refresh.",
|
|
754
760
|
"sessionUsage.title": "Current session spend",
|
|
755
761
|
"common.estimated": "Estimated cost",
|
|
756
762
|
"sessionUsage.cost": "Cost",
|
|
@@ -758,7 +764,7 @@ var en = {
|
|
|
758
764
|
"sessionUsage.calls": "Calls",
|
|
759
765
|
"sessionUsage.range": "Active {range}",
|
|
760
766
|
"sessionUsage.single": "{model} \xB7 input {input} / output {output} / cache {cache}",
|
|
761
|
-
"sessionUsage.empty": "No usage in this session yet.
|
|
767
|
+
"sessionUsage.empty": "No usage in this session yet.",
|
|
762
768
|
"overview.title": "Spend overview",
|
|
763
769
|
"overview.today": "Today",
|
|
764
770
|
"overview.month": "This month",
|
|
@@ -780,9 +786,9 @@ var en = {
|
|
|
780
786
|
"links.status": "Service status",
|
|
781
787
|
"settings.title": "Settings",
|
|
782
788
|
"settings.widget": "Show the floating balance widget on other views",
|
|
783
|
-
"settings.widgetHint": "
|
|
789
|
+
"settings.widgetHint": "Hidden on Usage; shown on the views selected below.",
|
|
784
790
|
"settings.widgetTabs": "Show widget on",
|
|
785
|
-
"settings.widgetTabsHint": "
|
|
791
|
+
"settings.widgetTabsHint": "Updates automatically as views are installed.",
|
|
786
792
|
"settings.widgetTabsEmpty": "There are no configurable conversation views right now.",
|
|
787
793
|
"settings.lowBalance": "Balance warning",
|
|
788
794
|
"settings.lowBalanceOn": "This page and the widget turn amber below {amount} {currency}. Enter 0 to disable.",
|
|
@@ -790,6 +796,18 @@ var en = {
|
|
|
790
796
|
"settings.monthlyBudget": "Monthly budget",
|
|
791
797
|
"settings.monthlyBudgetOn": "Track a {amount} CNY budget by Beijing calendar month and forecast month-end spend. Enter 0 to disable.",
|
|
792
798
|
"settings.monthlyBudgetOff": "Enter an amount above 0 to show progress and a month-end forecast.",
|
|
799
|
+
"settings.sidebarEntry": "Sidebar Usage entry",
|
|
800
|
+
"settings.sidebarEntryHint": "Add a Usage entry at the top or bottom of the sidebar; it opens the account-wide Usage page.",
|
|
801
|
+
"settings.sidebarEntryOff": "Off",
|
|
802
|
+
"settings.sidebarEntryTop": "Top",
|
|
803
|
+
"settings.sidebarEntryBottom": "Bottom",
|
|
804
|
+
"settings.tabPosition": "Usage tab position",
|
|
805
|
+
"settings.tabPositionHint": "Drag Usage to change its tab position.",
|
|
806
|
+
"settings.tabDragAria": "Drag Usage to change its tab position",
|
|
807
|
+
"settings.tabMoveEarlier": "Move earlier",
|
|
808
|
+
"settings.tabMoveLater": "Move later",
|
|
809
|
+
"panel.caption": "Account-wide \xB7 across all sessions",
|
|
810
|
+
"panel.openAria": "Open Usage",
|
|
793
811
|
"widget.updatedNow": "Updated just now",
|
|
794
812
|
"widget.stale": "Data from {minutes}m ago; press \u21BB to update",
|
|
795
813
|
"widget.loading": "Loading\u2026",
|
|
@@ -807,6 +825,9 @@ var en = {
|
|
|
807
825
|
"widget.expandAria": "Expand balance widget",
|
|
808
826
|
"widget.collapseAria": "Collapse balance widget",
|
|
809
827
|
"messageCost.label": "Turn cost",
|
|
828
|
+
"sessionCost.title": "Session cost",
|
|
829
|
+
"sessionCost.aria": "Estimated session cost {amount}",
|
|
830
|
+
"sessionCost.active": "Active time",
|
|
810
831
|
"messageCost.tooltip": "Provided by dsh-usage-dashboard \xB7 estimated cost for this turn",
|
|
811
832
|
"messageCost.aria": "{amount}, estimated cost for this turn, provided by dsh-usage-dashboard",
|
|
812
833
|
"boundary.title": "The usage dashboard crashed",
|
|
@@ -1353,6 +1374,27 @@ var quotaViewActiveStore = {
|
|
|
1353
1374
|
}
|
|
1354
1375
|
};
|
|
1355
1376
|
|
|
1377
|
+
// src/client/layout-prefs.ts
|
|
1378
|
+
var QUOTA_PANEL_ID = "quota";
|
|
1379
|
+
var isPlacement = (value) => value === "off" || value === "top" || value === "bottom";
|
|
1380
|
+
var sidebarEntryStore = createStore("layout.sidebarEntry", isPlacement, "off");
|
|
1381
|
+
var isTabIndex = (value) => typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 50;
|
|
1382
|
+
var quotaTabIndexStore = createStore("layout.tabIndex", isTabIndex, 2);
|
|
1383
|
+
function clampTabIndex(index, count) {
|
|
1384
|
+
return Math.max(0, Math.min(index, Math.max(count, 0)));
|
|
1385
|
+
}
|
|
1386
|
+
function viewOrderForIndex(entries, index, selfId = "balance", fallback = 20) {
|
|
1387
|
+
const others = entries.filter((entry) => entry.options.id !== selfId);
|
|
1388
|
+
if (others.length === 0) return fallback;
|
|
1389
|
+
const clamped = clampTabIndex(index, others.length);
|
|
1390
|
+
const orderAt = (position) => others[position]?.options.order ?? 0;
|
|
1391
|
+
if (clamped === 0) return orderAt(0) - 10;
|
|
1392
|
+
if (clamped === others.length) return orderAt(others.length - 1) + 10;
|
|
1393
|
+
const previous = orderAt(clamped - 1);
|
|
1394
|
+
const next = orderAt(clamped);
|
|
1395
|
+
return previous >= next ? previous + 0.5 : (previous + next) / 2;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1356
1398
|
// src/client/dashboard.tsx
|
|
1357
1399
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1358
1400
|
var COMPOSER_FALLBACK_PX = 126;
|
|
@@ -1518,7 +1560,6 @@ function PricingNote(props) {
|
|
|
1518
1560
|
t("pricing.unit", { currency: pricing.currency }),
|
|
1519
1561
|
t("pricing.splitNote", { windows: pricing.peakWindows.join(t("common.listSeparator")) }),
|
|
1520
1562
|
t("pricing.visionNote"),
|
|
1521
|
-
t("pricing.modelNote"),
|
|
1522
1563
|
t("pricing.unknown")
|
|
1523
1564
|
] })
|
|
1524
1565
|
] })
|
|
@@ -1780,8 +1821,7 @@ function CacheCard(props) {
|
|
|
1780
1821
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-legend-swatch dq-legend-swatch--miss" }),
|
|
1781
1822
|
t("cache.missLegend", { tokens: fmtCompact(totals.input, locale) })
|
|
1782
1823
|
] })
|
|
1783
|
-
] })
|
|
1784
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-cache-foot", children: low ? t("cache.lowHint") : t("cache.goodHint") })
|
|
1824
|
+
] })
|
|
1785
1825
|
] });
|
|
1786
1826
|
}
|
|
1787
1827
|
function VisionCard(props) {
|
|
@@ -1863,11 +1903,7 @@ function PeakCard(props) {
|
|
|
1863
1903
|
fmt(split.offPeakEraCost),
|
|
1864
1904
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-peak-delta dq-peak-delta--save", children: t("peak.saving", { amount: fmt(shiftSaving) }) })
|
|
1865
1905
|
] })
|
|
1866
|
-
] }) })
|
|
1867
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "dq-peak-foot", children: [
|
|
1868
|
-
t("peak.schedule", { windows: pricing.peakWindows.join(t("common.listSeparator")) }),
|
|
1869
|
-
t("peak.activeHint")
|
|
1870
|
-
] })
|
|
1906
|
+
] }) })
|
|
1871
1907
|
] });
|
|
1872
1908
|
}
|
|
1873
1909
|
function agoText(ms, t) {
|
|
@@ -2118,6 +2154,11 @@ function BalanceDashboard(props) {
|
|
|
2118
2154
|
const [selectedModels, setSelectedModels] = (0, import_react4.useState)([]);
|
|
2119
2155
|
const [windowDays, setWindowDays] = (0, import_react4.useState)(usageWindowStore.get());
|
|
2120
2156
|
const [chartMetric, setChartMetric] = (0, import_react4.useState)(chartMetricStore.get());
|
|
2157
|
+
const [sidebarEntry, setSidebarEntry] = (0, import_react4.useState)(sidebarEntryStore.get());
|
|
2158
|
+
(0, import_react4.useEffect)(() => sidebarEntryStore.subscribe(() => setSidebarEntry(sidebarEntryStore.get())), []);
|
|
2159
|
+
const [tabDragging, setTabDragging] = (0, import_react4.useState)(false);
|
|
2160
|
+
const [tabDropTarget, setTabDropTarget] = (0, import_react4.useState)(null);
|
|
2161
|
+
const [retainedVision, setRetainedVision] = (0, import_react4.useState)(null);
|
|
2121
2162
|
const [runwayHovering, setRunwayHovering] = (0, import_react4.useState)(false);
|
|
2122
2163
|
const [runwayManuallyOpened, setRunwayManuallyOpened] = (0, import_react4.useState)(false);
|
|
2123
2164
|
const [remainingHovering, setRemainingHovering] = (0, import_react4.useState)(false);
|
|
@@ -2290,6 +2331,71 @@ function BalanceDashboard(props) {
|
|
|
2290
2331
|
setChartMetric(value);
|
|
2291
2332
|
chartMetricStore.set(value);
|
|
2292
2333
|
};
|
|
2334
|
+
const changeSidebarEntry = (value) => {
|
|
2335
|
+
setSidebarEntry(value);
|
|
2336
|
+
sidebarEntryStore.set(value);
|
|
2337
|
+
};
|
|
2338
|
+
const balanceTabIndex = viewTabs.findIndex((tab) => tab.id === "balance");
|
|
2339
|
+
const finalTabIndex = (target, after) => {
|
|
2340
|
+
const gap = after ? target + 1 : target;
|
|
2341
|
+
const dropped = balanceTabIndex >= 0 && gap > balanceTabIndex ? gap - 1 : gap;
|
|
2342
|
+
return Math.max(0, Math.min(dropped, Math.max(viewTabs.length - 1, 0)));
|
|
2343
|
+
};
|
|
2344
|
+
const endTabDrag = () => {
|
|
2345
|
+
setTabDragging(false);
|
|
2346
|
+
setTabDropTarget(null);
|
|
2347
|
+
};
|
|
2348
|
+
const previewTabDropTarget = (index) => {
|
|
2349
|
+
setTabDropTarget((prev) => prev === index ? prev : index);
|
|
2350
|
+
};
|
|
2351
|
+
const applyQuotaTabIndex = (index) => {
|
|
2352
|
+
const next = Math.max(0, Math.min(index, Math.max(viewTabs.length - 1, 0)));
|
|
2353
|
+
if (next === balanceTabIndex) return;
|
|
2354
|
+
quotaTabIndexStore.set(next);
|
|
2355
|
+
window.setTimeout(() => {
|
|
2356
|
+
const chip = document.querySelector(".dq-tab-chip--current");
|
|
2357
|
+
chip?.focus();
|
|
2358
|
+
}, 80);
|
|
2359
|
+
};
|
|
2360
|
+
const dropQuotaTab = (target, after) => {
|
|
2361
|
+
applyQuotaTabIndex(finalTabIndex(target, after));
|
|
2362
|
+
endTabDrag();
|
|
2363
|
+
};
|
|
2364
|
+
const onQuotaTabDragStart = (event) => {
|
|
2365
|
+
setTabDragging(true);
|
|
2366
|
+
event.dataTransfer.effectAllowed = "move";
|
|
2367
|
+
event.dataTransfer.setData("text/plain", "balance");
|
|
2368
|
+
};
|
|
2369
|
+
const onChipDragOver = (event, index) => {
|
|
2370
|
+
if (!tabDragging) return;
|
|
2371
|
+
event.preventDefault();
|
|
2372
|
+
event.stopPropagation();
|
|
2373
|
+
previewTabDropTarget(index);
|
|
2374
|
+
};
|
|
2375
|
+
const onChipDrop = (event, index) => {
|
|
2376
|
+
if (!tabDragging) return;
|
|
2377
|
+
event.preventDefault();
|
|
2378
|
+
event.stopPropagation();
|
|
2379
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
2380
|
+
dropQuotaTab(index, event.clientX > rect.left + rect.width / 2);
|
|
2381
|
+
};
|
|
2382
|
+
const onOrderDragOver = (event) => {
|
|
2383
|
+
if (!tabDragging) return;
|
|
2384
|
+
event.preventDefault();
|
|
2385
|
+
if (event.target === event.currentTarget) previewTabDropTarget(viewTabs.length - 1);
|
|
2386
|
+
};
|
|
2387
|
+
const onOrderDrop = (event) => {
|
|
2388
|
+
if (!tabDragging) return;
|
|
2389
|
+
event.preventDefault();
|
|
2390
|
+
if (event.target !== event.currentTarget) return;
|
|
2391
|
+
applyQuotaTabIndex(viewTabs.length - 1);
|
|
2392
|
+
endTabDrag();
|
|
2393
|
+
};
|
|
2394
|
+
const onQuotaTabKeyDown = (event) => {
|
|
2395
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
|
|
2396
|
+
event.preventDefault();
|
|
2397
|
+
applyQuotaTabIndex(balanceTabIndex + (event.key === "ArrowLeft" ? -1 : 1));
|
|
2398
|
+
};
|
|
2293
2399
|
const primary = balance !== null && balance.balances.length > 0 ? balance.balances[0] : null;
|
|
2294
2400
|
const recentDays = (usage?.daily ?? []).slice(-7);
|
|
2295
2401
|
const avgDailyCost = recentDays.length > 0 ? recentDays.reduce((sum, d) => sum + d.cost, 0) / recentDays.length : 0;
|
|
@@ -2300,6 +2406,26 @@ function BalanceDashboard(props) {
|
|
|
2300
2406
|
const runwayTitle = daysLeft === null ? t("balance.noRunway", { days: recentDays.length || 7 }) : t("balance.runwayTitle", { days: recentDays.length, cost: fmt(avgDailyCost) });
|
|
2301
2407
|
const activeWindow = usage?.windows.find((window2) => window2.days === windowDays) ?? null;
|
|
2302
2408
|
const activeWindowName = windowName(windowDays, t);
|
|
2409
|
+
const visionStats = activeWindow?.vision;
|
|
2410
|
+
const visionOpen = usage !== null && activeWindow !== null && visionStats !== void 0 && visionStats.images > 0 && Array.isArray(activeWindow.visionDaily) && Array.isArray(activeWindow.visionSessions);
|
|
2411
|
+
const visionPayload = (window2, stats, label) => ({
|
|
2412
|
+
vision: stats,
|
|
2413
|
+
daily: window2.visionDaily,
|
|
2414
|
+
sessions: window2.visionSessions,
|
|
2415
|
+
windowLabel: label,
|
|
2416
|
+
inputTokens: window2.totals.input,
|
|
2417
|
+
costTotal: window2.totals.cost
|
|
2418
|
+
});
|
|
2419
|
+
const liveVision = visionOpen && activeWindow !== null && visionStats !== void 0 ? visionPayload(activeWindow, visionStats, activeWindowName) : null;
|
|
2420
|
+
(0, import_react4.useEffect)(() => {
|
|
2421
|
+
if (visionOpen && activeWindow !== null && visionStats !== void 0) {
|
|
2422
|
+
setRetainedVision(visionPayload(activeWindow, visionStats, activeWindowName));
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
const timer = window.setTimeout(() => setRetainedVision(null), 320);
|
|
2426
|
+
return () => window.clearTimeout(timer);
|
|
2427
|
+
}, [visionOpen, activeWindow, activeWindowName]);
|
|
2428
|
+
const shownVision = visionOpen ? liveVision : retainedVision;
|
|
2303
2429
|
const dailyBars = (activeWindow?.daily ?? []).map((d) => ({
|
|
2304
2430
|
label: windowDays === 365 ? d.date.slice(5).replace("-", "/") : d.date.slice(8, 10),
|
|
2305
2431
|
value: chartMetricValue(chartMetric, d),
|
|
@@ -2524,34 +2650,6 @@ function BalanceDashboard(props) {
|
|
|
2524
2650
|
),
|
|
2525
2651
|
usage !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PricingNote, { pricing: usage.pricing })
|
|
2526
2652
|
] }),
|
|
2527
|
-
usage !== null && activeWindow !== null && activeWindow.vision !== void 0 && Array.isArray(activeWindow.visionDaily) && Array.isArray(activeWindow.visionSessions) ? (
|
|
2528
|
-
// 窗口内没有图片时整张卡不出现:一行「没有图片输入」不值得一个小节的位置。
|
|
2529
|
-
activeWindow.vision.images === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2530
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: activeWindowName, children: t("vision.title") }),
|
|
2531
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2532
|
-
VisionCard,
|
|
2533
|
-
{
|
|
2534
|
-
vision: activeWindow.vision,
|
|
2535
|
-
daily: activeWindow.visionDaily,
|
|
2536
|
-
sessions: activeWindow.visionSessions,
|
|
2537
|
-
windowLabel: activeWindowName,
|
|
2538
|
-
inputTokens: activeWindow.totals.input,
|
|
2539
|
-
costTotal: activeWindow.totals.cost
|
|
2540
|
-
}
|
|
2541
|
-
)
|
|
2542
|
-
] })
|
|
2543
|
-
) : usage === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2544
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: activeWindowName, children: t("vision.title") }),
|
|
2545
|
-
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: [
|
|
2546
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w, h: 11 }),
|
|
2547
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 72, h: 20 })
|
|
2548
|
-
] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("vision.empty", { window: activeWindowName }) })
|
|
2549
|
-
] }) : (
|
|
2550
|
-
// Host serving a pre-vision payload (e.g. not restarted yet): hide the
|
|
2551
|
-
// whole card instead of leaving a titled shell — it reappears as soon
|
|
2552
|
-
// as the host half serves the new shape.
|
|
2553
|
-
null
|
|
2554
|
-
),
|
|
2555
2653
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--primary", children: [
|
|
2556
2654
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card-head", children: [
|
|
2557
2655
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("usage.trendTitle") }),
|
|
@@ -2615,31 +2713,31 @@ function BalanceDashboard(props) {
|
|
|
2615
2713
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-legend-swatch", style: { background: s.color } }),
|
|
2616
2714
|
s.name
|
|
2617
2715
|
] }, s.key)) })
|
|
2618
|
-
] }) : barMode === "daily" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Bars, { data: dailyBars, height: 160, labelEvery: dailyLabelEvery, minWidth: dailyMinWidth }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Bars, { data: hourlyBars, height: 140, labelEvery: 3 })
|
|
2619
|
-
|
|
2620
|
-
|
|
2716
|
+
] }) : barMode === "daily" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Bars, { data: dailyBars, height: 160, labelEvery: dailyLabelEvery, minWidth: dailyMinWidth }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Bars, { data: hourlyBars, height: 140, labelEvery: 3 })
|
|
2717
|
+
] }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UsageSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("chart.empty") }),
|
|
2718
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-vision-inline", "data-open": visionOpen ? "true" : "false", "aria-hidden": !visionOpen, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-vision-inline-body", children: shownVision !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
2719
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-chart-title", children: [
|
|
2720
|
+
t("vision.title"),
|
|
2721
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-chart-peak", children: shownVision.windowLabel })
|
|
2722
|
+
] }),
|
|
2723
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2724
|
+
VisionCard,
|
|
2725
|
+
{
|
|
2726
|
+
vision: shownVision.vision,
|
|
2727
|
+
daily: shownVision.daily,
|
|
2728
|
+
sessions: shownVision.sessions,
|
|
2729
|
+
windowLabel: shownVision.windowLabel,
|
|
2730
|
+
inputTokens: shownVision.inputTokens,
|
|
2731
|
+
costTotal: shownVision.costTotal
|
|
2732
|
+
}
|
|
2733
|
+
)
|
|
2734
|
+
] }) }) }),
|
|
2735
|
+
usage !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CoverageDiagnostics, { coverage: usage.coverage })
|
|
2621
2736
|
] }),
|
|
2622
2737
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2623
2738
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: t("chart.heatmapTitle"), children: t("heatmap.title") }),
|
|
2624
2739
|
usage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Heatmap, { data: usage.heatmap }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeatmapSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("chart.empty") })
|
|
2625
2740
|
] }),
|
|
2626
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2627
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("peak.title") }),
|
|
2628
|
-
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: [
|
|
2629
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-track" }),
|
|
2630
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-peak-figures", children: [
|
|
2631
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 120, h: 11 }),
|
|
2632
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 96, h: 20 })
|
|
2633
|
-
] })
|
|
2634
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("peak.empty") })
|
|
2635
|
-
] }),
|
|
2636
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2637
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("cache.title") }),
|
|
2638
|
-
usage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CacheCard, { totals: usage.totals }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-cache-figures", children: [60, 96, 72].map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
2639
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w, h: 11 }),
|
|
2640
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 72, h: 20 })
|
|
2641
|
-
] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("cache.empty") })
|
|
2642
|
-
] }),
|
|
2643
2741
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2644
2742
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: activeWindowName, children: t("models.title") }),
|
|
2645
2743
|
activeWindow !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ModelRanking, { models: modelList, totalCost: modelCostTotal, colorOf }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-rank", children: [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-rank-row", children: [
|
|
@@ -2656,6 +2754,25 @@ function BalanceDashboard(props) {
|
|
|
2656
2754
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 250, h: 11 })
|
|
2657
2755
|
] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("sessions.empty") })
|
|
2658
2756
|
] }),
|
|
2757
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card-pair", children: [
|
|
2758
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2759
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("peak.title") }),
|
|
2760
|
+
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: [
|
|
2761
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-track" }),
|
|
2762
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-peak-figures", children: [
|
|
2763
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 120, h: 11 }),
|
|
2764
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 96, h: 20 })
|
|
2765
|
+
] })
|
|
2766
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("peak.empty") })
|
|
2767
|
+
] }),
|
|
2768
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2769
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("cache.title") }),
|
|
2770
|
+
usage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CacheCard, { totals: usage.totals }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-cache-figures", children: [60, 96, 72].map((w, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
2771
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w, h: 11 }),
|
|
2772
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 72, h: 20 })
|
|
2773
|
+
] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("cache.empty") })
|
|
2774
|
+
] })
|
|
2775
|
+
] }),
|
|
2659
2776
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
|
|
2660
2777
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("settings.title") }),
|
|
2661
2778
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-links-title", children: t("links.title") }),
|
|
@@ -2723,9 +2840,67 @@ function BalanceDashboard(props) {
|
|
|
2723
2840
|
),
|
|
2724
2841
|
/* @__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") })
|
|
2725
2842
|
] })
|
|
2843
|
+
] }),
|
|
2844
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2845
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-sidebar-entry", children: t("settings.sidebarEntry") }),
|
|
2846
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2847
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2848
|
+
"select",
|
|
2849
|
+
{
|
|
2850
|
+
id: "dq-sidebar-entry",
|
|
2851
|
+
className: "dq-select",
|
|
2852
|
+
value: sidebarEntry,
|
|
2853
|
+
onChange: (e) => changeSidebarEntry(e.target.value),
|
|
2854
|
+
children: [
|
|
2855
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "off", children: t("settings.sidebarEntryOff") }),
|
|
2856
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "top", children: t("settings.sidebarEntryTop") }),
|
|
2857
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "bottom", children: t("settings.sidebarEntryBottom") })
|
|
2858
|
+
]
|
|
2859
|
+
}
|
|
2860
|
+
),
|
|
2861
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: t("settings.sidebarEntryHint") })
|
|
2862
|
+
] })
|
|
2863
|
+
] }),
|
|
2864
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2865
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-tab-order-label", children: t("settings.tabPosition") }),
|
|
2866
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2867
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2868
|
+
"div",
|
|
2869
|
+
{
|
|
2870
|
+
className: "dq-tab-order",
|
|
2871
|
+
role: "group",
|
|
2872
|
+
"aria-labelledby": "dq-tab-order-label",
|
|
2873
|
+
"aria-describedby": "dq-tab-order-hint",
|
|
2874
|
+
onDragOver: onOrderDragOver,
|
|
2875
|
+
onDrop: onOrderDrop,
|
|
2876
|
+
children: viewTabs.map((tab, index) => {
|
|
2877
|
+
const current = tab.id === "balance";
|
|
2878
|
+
const dropTarget = tabDragging && !current && tabDropTarget === index;
|
|
2879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2880
|
+
"span",
|
|
2881
|
+
{
|
|
2882
|
+
className: `dq-tab-chip${current ? " dq-tab-chip--current" : ""}${current && tabDragging ? " dq-tab-chip--dragging" : ""}${dropTarget ? " dq-tab-chip--drop-target" : ""}`,
|
|
2883
|
+
draggable: current,
|
|
2884
|
+
tabIndex: current ? 0 : void 0,
|
|
2885
|
+
role: current ? "button" : void 0,
|
|
2886
|
+
"aria-label": current ? t("settings.tabDragAria") : void 0,
|
|
2887
|
+
onDragStart: current ? onQuotaTabDragStart : void 0,
|
|
2888
|
+
onDragEnd: current ? endTabDrag : void 0,
|
|
2889
|
+
onDragOver: (event) => onChipDragOver(event, index),
|
|
2890
|
+
onDrop: (event) => onChipDrop(event, index),
|
|
2891
|
+
onKeyDown: current ? onQuotaTabKeyDown : void 0,
|
|
2892
|
+
children: tab.label
|
|
2893
|
+
},
|
|
2894
|
+
tab.id
|
|
2895
|
+
);
|
|
2896
|
+
})
|
|
2897
|
+
}
|
|
2898
|
+
),
|
|
2899
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-tab-order-hint", className: "dq-setting-hint", children: t("settings.tabPositionHint") })
|
|
2900
|
+
] })
|
|
2726
2901
|
] })
|
|
2727
2902
|
] }),
|
|
2728
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.
|
|
2903
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.8.1" })
|
|
2729
2904
|
] });
|
|
2730
2905
|
}
|
|
2731
2906
|
|
|
@@ -2769,6 +2944,254 @@ function MessageCost({ sessionId, messageId }) {
|
|
|
2769
2944
|
);
|
|
2770
2945
|
}
|
|
2771
2946
|
|
|
2947
|
+
// src/client/session-cost-pill.tsx
|
|
2948
|
+
var import_react6 = require("react");
|
|
2949
|
+
var import_react_dom = require("react-dom");
|
|
2950
|
+
|
|
2951
|
+
// src/pricing.ts
|
|
2952
|
+
var PEAK_PRICING_FROM_MS = Date.UTC(2026, 7, 16, 16, 0, 0);
|
|
2953
|
+
var V41_FLASH_PRICING_FROM_MS = Date.UTC(2026, 8, 10, 4, 0, 0);
|
|
2954
|
+
var PRO_ROUTED_TO_FLASH_FROM_MS = Date.UTC(2026, 8, 14, 4, 0, 0);
|
|
2955
|
+
var PEAK_WINDOWS = [[9, 12], [14, 18]];
|
|
2956
|
+
var BEIJING_OFFSET_MS3 = 8 * 36e5;
|
|
2957
|
+
var FLAT_RATES = {
|
|
2958
|
+
pro: { cacheHit: 0.025, input: 3, output: 6 },
|
|
2959
|
+
flash: { cacheHit: 0.02, input: 1, output: 2 },
|
|
2960
|
+
// The vision model billed at flash rates from day one, so its pre-switch
|
|
2961
|
+
// history is costed at the same flat prices as flash.
|
|
2962
|
+
vision: { cacheHit: 0.02, input: 1, output: 2 }
|
|
2963
|
+
};
|
|
2964
|
+
var PEAK_RATES_08_17 = {
|
|
2965
|
+
pro: { cacheHit: 0.3, input: 9, output: 27 },
|
|
2966
|
+
flash: { cacheHit: 0.1, input: 3, output: 9 },
|
|
2967
|
+
// Official policy: the vision model is priced identically to flash; images
|
|
2968
|
+
// cost tokens, not their own multiplier.
|
|
2969
|
+
vision: { cacheHit: 0.1, input: 3, output: 9 }
|
|
2970
|
+
};
|
|
2971
|
+
var PEAK_RATES_09_10 = {
|
|
2972
|
+
pro: { cacheHit: 0.3, input: 9, output: 27 },
|
|
2973
|
+
flash: { cacheHit: 0.04, input: 2, output: 8 },
|
|
2974
|
+
vision: { cacheHit: 0.04, input: 2, output: 8 }
|
|
2975
|
+
};
|
|
2976
|
+
var PRICE_ERAS = [
|
|
2977
|
+
{ fromMs: PEAK_PRICING_FROM_MS, peak: PEAK_RATES_08_17, weekdaysOnly: false },
|
|
2978
|
+
{ fromMs: V41_FLASH_PRICING_FROM_MS, peak: PEAK_RATES_09_10, weekdaysOnly: true }
|
|
2979
|
+
];
|
|
2980
|
+
function eraAt(timeMs) {
|
|
2981
|
+
let era = PRICE_ERAS[0];
|
|
2982
|
+
for (const candidate of PRICE_ERAS) if (timeMs >= candidate.fromMs) era = candidate;
|
|
2983
|
+
return era;
|
|
2984
|
+
}
|
|
2985
|
+
var halved = (rates) => ({
|
|
2986
|
+
cacheHit: rates.cacheHit / 2,
|
|
2987
|
+
input: rates.input / 2,
|
|
2988
|
+
output: rates.output / 2
|
|
2989
|
+
});
|
|
2990
|
+
function tierOf(model) {
|
|
2991
|
+
const name2 = model.toLowerCase();
|
|
2992
|
+
if (name2.includes("vision")) return "vision";
|
|
2993
|
+
if (name2.includes("flash")) return "flash";
|
|
2994
|
+
return "pro";
|
|
2995
|
+
}
|
|
2996
|
+
function billedTierOf(timeMs, model) {
|
|
2997
|
+
const tier = tierOf(model);
|
|
2998
|
+
return tier === "pro" && timeMs >= PRO_ROUTED_TO_FLASH_FROM_MS ? "flash" : tier;
|
|
2999
|
+
}
|
|
3000
|
+
function isBeijingWeekday(timeMs) {
|
|
3001
|
+
const day = new Date(timeMs + BEIJING_OFFSET_MS3).getUTCDay();
|
|
3002
|
+
return day >= 1 && day <= 5;
|
|
3003
|
+
}
|
|
3004
|
+
function isPeak(timeMs) {
|
|
3005
|
+
if (eraAt(timeMs).weekdaysOnly && !isBeijingWeekday(timeMs)) return false;
|
|
3006
|
+
const hour = Math.floor(((timeMs + BEIJING_OFFSET_MS3) % 864e5 + 864e5) % 864e5 / 36e5);
|
|
3007
|
+
return PEAK_WINDOWS.some(([from, to]) => hour >= from && hour < to);
|
|
3008
|
+
}
|
|
3009
|
+
function ratesAt(timeMs, model) {
|
|
3010
|
+
if (timeMs < PEAK_PRICING_FROM_MS) return FLAT_RATES[tierOf(model)];
|
|
3011
|
+
const peak = eraAt(timeMs).peak[billedTierOf(timeMs, model)];
|
|
3012
|
+
return isPeak(timeMs) ? peak : halved(peak);
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
// src/client/session-cost-pill.tsx
|
|
3016
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
3017
|
+
var PANEL_MARGIN = 12;
|
|
3018
|
+
var PANEL_GAP = 8;
|
|
3019
|
+
var cachedData2 = (sessionId) => {
|
|
3020
|
+
const response = getCachedSessionUsage(sessionId);
|
|
3021
|
+
return response?.ok === true ? response.data ?? null : null;
|
|
3022
|
+
};
|
|
3023
|
+
function CostIcon() {
|
|
3024
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3025
|
+
"svg",
|
|
3026
|
+
{
|
|
3027
|
+
width: "14",
|
|
3028
|
+
height: "14",
|
|
3029
|
+
viewBox: "0 0 16 16",
|
|
3030
|
+
fill: "none",
|
|
3031
|
+
stroke: "currentColor",
|
|
3032
|
+
strokeWidth: "1",
|
|
3033
|
+
strokeLinecap: "round",
|
|
3034
|
+
strokeLinejoin: "round",
|
|
3035
|
+
"aria-hidden": "true",
|
|
3036
|
+
focusable: "false",
|
|
3037
|
+
children: [
|
|
3038
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: "8", cy: "8", r: "6" }),
|
|
3039
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5.5 5.3 8 8.4l2.5-3.1" }),
|
|
3040
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M8 8.4v2.9" }),
|
|
3041
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5.8 9.5h4.4" }),
|
|
3042
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5.8 10.9h4.4" })
|
|
3043
|
+
]
|
|
3044
|
+
}
|
|
3045
|
+
);
|
|
3046
|
+
}
|
|
3047
|
+
function SessionCostPill({ sessionId }) {
|
|
3048
|
+
const { t, locale } = useI18n();
|
|
3049
|
+
const [data, setData] = (0, import_react6.useState)(
|
|
3050
|
+
() => sessionId === void 0 || sessionId === "" ? null : cachedData2(sessionId)
|
|
3051
|
+
);
|
|
3052
|
+
const [open, setOpen] = (0, import_react6.useState)(false);
|
|
3053
|
+
const anchorRef = (0, import_react6.useRef)(null);
|
|
3054
|
+
const panelRef = (0, import_react6.useRef)(null);
|
|
3055
|
+
const [pos, setPos] = (0, import_react6.useState)(null);
|
|
3056
|
+
(0, import_react6.useEffect)(() => {
|
|
3057
|
+
if (sessionId === void 0 || sessionId === "") return;
|
|
3058
|
+
setData(cachedData2(sessionId));
|
|
3059
|
+
const unsubscribe = subscribeSessionUsage(sessionId, () => setData(cachedData2(sessionId)));
|
|
3060
|
+
if (getCachedSessionUsage(sessionId) === null) void fetchSessionUsage(sessionId);
|
|
3061
|
+
return unsubscribe;
|
|
3062
|
+
}, [sessionId]);
|
|
3063
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
3064
|
+
if (!open) {
|
|
3065
|
+
setPos(null);
|
|
3066
|
+
return;
|
|
3067
|
+
}
|
|
3068
|
+
const anchor = anchorRef.current;
|
|
3069
|
+
const panel = panelRef.current;
|
|
3070
|
+
if (anchor === null || panel === null) return;
|
|
3071
|
+
const pill = anchor.getBoundingClientRect();
|
|
3072
|
+
const width = panel.getBoundingClientRect().width;
|
|
3073
|
+
setPos({
|
|
3074
|
+
left: Math.min(Math.max(pill.left, PANEL_MARGIN), Math.max(PANEL_MARGIN, window.innerWidth - width - PANEL_MARGIN)),
|
|
3075
|
+
bottom: window.innerHeight - pill.top + PANEL_GAP
|
|
3076
|
+
});
|
|
3077
|
+
}, [open, data]);
|
|
3078
|
+
(0, import_react6.useEffect)(() => {
|
|
3079
|
+
if (!open) return;
|
|
3080
|
+
const close = () => setOpen(false);
|
|
3081
|
+
const onPointerDown = (event) => {
|
|
3082
|
+
const target = event.target;
|
|
3083
|
+
if (!(target instanceof Node)) return;
|
|
3084
|
+
if (anchorRef.current?.contains(target) === true || panelRef.current?.contains(target) === true) return;
|
|
3085
|
+
close();
|
|
3086
|
+
};
|
|
3087
|
+
const onKeyDown = (event) => {
|
|
3088
|
+
if (event.key === "Escape") close();
|
|
3089
|
+
};
|
|
3090
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
3091
|
+
document.addEventListener("keydown", onKeyDown);
|
|
3092
|
+
window.addEventListener("resize", close);
|
|
3093
|
+
document.addEventListener("scroll", close, true);
|
|
3094
|
+
return () => {
|
|
3095
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
3096
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
3097
|
+
window.removeEventListener("resize", close);
|
|
3098
|
+
document.removeEventListener("scroll", close, true);
|
|
3099
|
+
};
|
|
3100
|
+
}, [open]);
|
|
3101
|
+
if (sessionId === void 0 || sessionId === "" || data === null || data.calls === 0) return null;
|
|
3102
|
+
const amount = `\xA5${fmtTurnCost(data.cost)}`;
|
|
3103
|
+
const priceAt = data.lastActive ?? Date.now();
|
|
3104
|
+
const split = data.models.reduce(
|
|
3105
|
+
(sum, model) => {
|
|
3106
|
+
const rates = ratesAt(priceAt, model.model);
|
|
3107
|
+
const weighted = {
|
|
3108
|
+
input: model.input * rates.input,
|
|
3109
|
+
cache: model.cache * rates.cacheHit,
|
|
3110
|
+
output: model.output * rates.output
|
|
3111
|
+
};
|
|
3112
|
+
const weight = weighted.input + weighted.cache + weighted.output;
|
|
3113
|
+
if (weight <= 0) return sum;
|
|
3114
|
+
sum.input += model.cost * (weighted.input / weight);
|
|
3115
|
+
sum.cache += model.cost * (weighted.cache / weight);
|
|
3116
|
+
sum.output += model.cost * (weighted.output / weight);
|
|
3117
|
+
return sum;
|
|
3118
|
+
},
|
|
3119
|
+
{ input: 0, cache: 0, output: 0 }
|
|
3120
|
+
);
|
|
3121
|
+
const rangeTime = new Intl.DateTimeFormat(locale === "en" ? "en-US" : "zh-CN", {
|
|
3122
|
+
timeZone: "Asia/Shanghai",
|
|
3123
|
+
month: "numeric",
|
|
3124
|
+
day: "numeric",
|
|
3125
|
+
hour: "2-digit",
|
|
3126
|
+
minute: "2-digit",
|
|
3127
|
+
hour12: false
|
|
3128
|
+
});
|
|
3129
|
+
const range = data.firstActive !== null && data.lastActive !== null ? `${rangeTime.format(data.firstActive)} \u2013 ${rangeTime.format(data.lastActive)}` : null;
|
|
3130
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
3131
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { ref: anchorRef, className: "dq-cost-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3132
|
+
"button",
|
|
3133
|
+
{
|
|
3134
|
+
type: "button",
|
|
3135
|
+
className: "dq-cost-pill",
|
|
3136
|
+
"aria-haspopup": "dialog",
|
|
3137
|
+
"aria-expanded": open,
|
|
3138
|
+
"aria-label": t("sessionCost.aria", { amount }),
|
|
3139
|
+
onClick: () => setOpen((value) => !value),
|
|
3140
|
+
children: [
|
|
3141
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CostIcon, {}),
|
|
3142
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dq-cost-pill-label", children: amount })
|
|
3143
|
+
]
|
|
3144
|
+
}
|
|
3145
|
+
) }),
|
|
3146
|
+
open && (0, import_react_dom.createPortal)(
|
|
3147
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3148
|
+
"div",
|
|
3149
|
+
{
|
|
3150
|
+
ref: panelRef,
|
|
3151
|
+
className: "dq-cost-panel",
|
|
3152
|
+
role: "dialog",
|
|
3153
|
+
"aria-label": t("sessionCost.title"),
|
|
3154
|
+
style: pos === null ? { visibility: "hidden", left: 0, top: 0 } : { visibility: "visible", left: pos.left, bottom: pos.bottom, top: "auto" },
|
|
3155
|
+
children: [
|
|
3156
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dq-cost-panel-title", children: [
|
|
3157
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dq-cost-panel-title-label", children: [
|
|
3158
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CostIcon, {}),
|
|
3159
|
+
t("sessionCost.title")
|
|
3160
|
+
] }),
|
|
3161
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dq-cost-panel-value", children: amount })
|
|
3162
|
+
] }),
|
|
3163
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dq-cost-panel-rule", "aria-hidden": true }),
|
|
3164
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dl", { className: "dq-cost-details", children: [
|
|
3165
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("common.input") }),
|
|
3166
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dd", { children: [
|
|
3167
|
+
"\xA5",
|
|
3168
|
+
fmtTurnCost(split.input)
|
|
3169
|
+
] }),
|
|
3170
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("common.cacheHit") }),
|
|
3171
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dd", { children: [
|
|
3172
|
+
"\xA5",
|
|
3173
|
+
fmtTurnCost(split.cache)
|
|
3174
|
+
] }),
|
|
3175
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("common.output") }),
|
|
3176
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dd", { children: [
|
|
3177
|
+
"\xA5",
|
|
3178
|
+
fmtTurnCost(split.output)
|
|
3179
|
+
] }),
|
|
3180
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("sessionUsage.calls") }),
|
|
3181
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dd", { children: fmtInt(data.calls) }),
|
|
3182
|
+
range !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
3183
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { children: t("sessionCost.active") }),
|
|
3184
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dd", { children: range })
|
|
3185
|
+
] })
|
|
3186
|
+
] })
|
|
3187
|
+
]
|
|
3188
|
+
}
|
|
3189
|
+
),
|
|
3190
|
+
document.body
|
|
3191
|
+
)
|
|
3192
|
+
] });
|
|
3193
|
+
}
|
|
3194
|
+
|
|
2772
3195
|
// src/client/styles.ts
|
|
2773
3196
|
var css = `
|
|
2774
3197
|
.dsh-quota-root{-webkit-text-size-adjust:100%;text-size-adjust:100%;position:absolute;right:16px;bottom:16px;z-index:2147483000;pointer-events:auto;opacity:1;visibility:visible;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:13px;line-height:1.45;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary,#1f2328);--dq-text-success:#1a7f37;--dq-text-success:color-mix(in oklab,var(--dsw-alias-state-success-primary,#2da44e) 52%,var(--dsw-alias-label-primary,#1f2328) 48%);--dq-text-warn:#9a6700;--dq-text-warn:color-mix(in oklab,var(--dsw-alias-state-warn-primary,#bf8700) 52%,var(--dsw-alias-label-primary,#1f2328) 48%);transition:left .28s cubic-bezier(.22,1,.36,1),top .28s cubic-bezier(.22,1,.36,1),opacity .16s ease-out,visibility 0s linear}
|
|
@@ -2807,12 +3230,31 @@ var css = `
|
|
|
2807
3230
|
.dq-message-cost-amount{text-decoration-line:underline;text-decoration-style:dotted;text-decoration-color:currentColor;text-underline-offset:4px}
|
|
2808
3231
|
.dq-message-cost:hover{color:var(--dsw-alias-label-secondary,#3f4852)}
|
|
2809
3232
|
.dq-message-cost:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px;border-radius:4px}
|
|
2810
|
-
.dq-message-cost::after{content:attr(data-dq-tooltip);position:absolute;left:0;
|
|
3233
|
+
.dq-message-cost::after{content:attr(data-dq-tooltip);position:absolute;left:0;bottom:calc(100% + 6px);top:auto;z-index:2147483001;width:max-content;max-width:min(240px,calc(100vw - 24px));padding:6px 9px;border-radius:8px;background:var(--dsw-alias-tooltip-bg,#1f2328);color:var(--dsw-static-neutral-bluish-00,#fff);box-shadow:0 6px 18px rgba(0,0,0,.22);font-size:12px;font-weight:400;line-height:1.5;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}
|
|
2811
3234
|
.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}
|
|
2812
3235
|
@media (hover:hover){
|
|
2813
3236
|
[data-time-hover-root] .dq-message-cost{opacity:0;transition:opacity 80ms ease}
|
|
2814
3237
|
[data-time-hover-root]:hover .dq-message-cost,[data-time-hover-root]:focus-within .dq-message-cost{opacity:1}
|
|
2815
3238
|
}
|
|
3239
|
+
/* Composer-dock session cost pill: one more reading beside the host's own
|
|
3240
|
+
\u300C\u4F1A\u8BDD\u7EDF\u8BA1\u300D/\u300CToken \u7528\u91CF\u300Dpills (same slot, same compact skin). */
|
|
3241
|
+
.dq-cost-anchor{display:inline-flex;min-width:0;font-size:calc(var(--dsh-content-font-size-secondary,13px) - 1px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px))}
|
|
3242
|
+
.dq-cost-pill{display:inline-flex;align-items:center;gap:6px;box-sizing:border-box;max-width:100%;padding:1px 8px;border:0;border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--dsw-alias-label-tertiary,#59636e);font:inherit;font-variant-numeric:tabular-nums;line-height:inherit;white-space:nowrap;cursor:pointer}
|
|
3243
|
+
.dq-cost-pill:hover,.dq-cost-pill[aria-expanded="true"]{background:var(--dsw-alias-interactive-bg-hover,rgba(120,130,150,.12));color:var(--dsw-alias-label-secondary,#59636e)}
|
|
3244
|
+
.dq-cost-pill:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3245
|
+
.dq-cost-pill svg{flex:none}
|
|
3246
|
+
.dq-cost-pill-label{min-width:0;overflow:hidden;text-overflow:ellipsis}
|
|
3247
|
+
/* Click-open breakdown: the host stat-dialog skin (menu surface, prominent
|
|
3248
|
+
elevation, 12/18 rows), portaled to the body and placed above the pill. */
|
|
3249
|
+
.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}
|
|
3250
|
+
.dq-cost-panel-title{display:flex;justify-content:space-between;gap:16px;margin-bottom:8px;color:var(--dsw-alias-label-primary,#1f2328);font-weight:500}
|
|
3251
|
+
.dq-cost-panel-title-label{display:inline-flex;align-items:center;gap:6px;min-width:0}
|
|
3252
|
+
.dq-cost-panel-title-label svg{flex:none}
|
|
3253
|
+
.dq-cost-panel-value{font-variant-numeric:tabular-nums}
|
|
3254
|
+
.dq-cost-panel-rule{margin-bottom:10px;border-top:.5px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1))}
|
|
3255
|
+
.dq-cost-details{display:grid;grid-template-columns:minmax(76px,auto) minmax(0,1fr);gap:6px 16px;margin:0;color:var(--dsw-alias-label-tertiary,#59636e)}
|
|
3256
|
+
.dq-cost-details dt,.dq-cost-details dd{min-width:0;margin:0}
|
|
3257
|
+
.dq-cost-details dd{color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;text-align:right}
|
|
2816
3258
|
/* \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
|
|
2817
3259
|
\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
|
|
2818
3260
|
padding\uFF0816px/20px\uFF0C\u5361\u7247\u5185\u90E8\u547C\u5438\u611F\uFF0C\u8F6E\u6B21 36 \u5DF2\u5B9A\uFF09\u6DF7\u540C\u3002 */
|
|
@@ -2822,10 +3264,10 @@ var css = `
|
|
|
2822
3264
|
the input box \u2014 measured in dashboard.tsx as --dq-composer-w, capped at
|
|
2823
3265
|
the reading-column width so wide viewports are unchanged. */
|
|
2824
3266
|
.dq-balance{-webkit-text-size-adjust:100%;text-size-adjust:100%;box-sizing:border-box;width:100%;min-width:0;display:flex;flex-direction:column;gap:32px;padding:20px 24px 48px;max-width:min(860px,var(--dq-composer-w,860px));margin:0 auto;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:13px;line-height:1.5;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-primary,#1f2328);--dq-text-success:#1a7f37;--dq-text-success:color-mix(in oklab,var(--dsw-alias-state-success-primary,#2da44e) 52%,var(--dsw-alias-label-primary,#1f2328) 48%);--dq-text-warn:#9a6700;--dq-text-warn:color-mix(in oklab,var(--dsw-alias-state-warn-primary,#bf8700) 52%,var(--dsw-alias-label-primary,#1f2328) 48%);}
|
|
2825
|
-
@media (max-width:620px){.dq-balance{max-width:100%;gap:24px;padding:16px 12px calc(var(--dq-composer-h,126px) + 16px)}.dq-card
|
|
3267
|
+
@media (max-width:620px){.dq-balance{max-width:100%;gap:24px;padding:16px 12px calc(var(--dq-composer-h,126px) + 16px)}.dq-card{padding:16px}.dq-card--hero{padding:0 16px}.dq-status-row{padding:0 16px}}
|
|
2826
3268
|
/* \u8F6E\u6B21 55\uFF1A\u53EA\u6709\u4E3B\u5361\u7247\u5E26\u6846\u3002\u6B21\u7EA7\u5206\u6790\u4E0E\u8BBE\u7F6E\u662F\u65E0\u6846\u5C0F\u8282\uFF0C\u9760 32px \u6BB5\u8DDD\u548C 13px \u4E3B\u8272\u6807\u9898\u5206\u7EC4\uFF1B
|
|
2827
3269
|
\u5DE6\u53F3 20px \u5185\u8FB9\u8DDD\u8BA9\u5B83\u4EEC\u7684\u6587\u5B57\u8FB9\u4E0E\u4E3B\u5361\u7247\u5185\u6587\u5BF9\u9F50\uFF0C\u6574\u9875\u53EA\u6709\u4E00\u6761\u6587\u5B57\u5DE6\u8FB9\u7EBF\u3002 */
|
|
2828
|
-
.dq-card{box-sizing:border-box;min-width:0;max-width:100%;padding:0
|
|
3270
|
+
.dq-card{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}
|
|
2829
3271
|
.dq-card-title{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 8px;font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 16px;letter-spacing:0;text-wrap:balance}
|
|
2830
3272
|
.dq-card-caption{font-size:12px;font-weight:400;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
2831
3273
|
.dq-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}
|
|
@@ -2839,9 +3281,10 @@ var css = `
|
|
|
2839
3281
|
\u6253\u5F00\u8FD9\u4E2A\u300C\u989D\u5EA6\u300Dtab \u65F6\u4E0A\u4E0B\u6587\u6700\u76F4\u63A5\u76F8\u5173\u7684\u6570\u5B57\uFF08\u8FD9\u4E2A tab \u6302\u5728\u5177\u4F53\u4F1A\u8BDD\u5185\u90E8\uFF0C\u89C1 DESIGN_NOTES\u300C\u4FE1\u606F\u5C42\u7EA7\u300D\uFF09\uFF0C
|
|
2840
3282
|
\u4E0D\u5C5E\u4E8E\u539F\u6709\u8D26\u6237\u5168\u5C40\u5C42\u7EA7 1-3 \u7684\u4EFB\u4F55\u4E00\u7EA7\uFF0C\u800C\u662F\u4E0E\u4E4B\u6B63\u4EA4\u7684\u53E6\u4E00\u4E2A\u7EF4\u5EA6\uFF0C\u4F46\u540C\u6837\u662F\u9996\u5C4F\u5FC5\u8BFB\uFF0C\u56E0\u6B64\u540C\u6837\u7ED9
|
|
2841
3283
|
primary \u6743\u91CD\uFF0C\u4E0D\u65B0\u5F00\u7B2C\u4E09\u6863\u3002 */
|
|
2842
|
-
.dq-card--primary{padding:20px;
|
|
3284
|
+
.dq-card--primary{padding:20px;border-color:var(--dsw-alias-border-l3,rgba(0,0,0,.12))}
|
|
2843
3285
|
/* \u4F59\u989D\u662F\u6574\u9875\u552F\u4E00\u7684 hero\uFF1A\u4E0D\u5957\u6846\uFF0C32px \u6570\u5B57\u72EC\u5360\u4E00\u6863\uFF0C\u8BA9\u5B83\u5728\u906E\u4F4F\u6587\u5B57\u65F6\u4ECD\u662F\u7B2C\u4E00\u773C\u3002 */
|
|
2844
|
-
.dq-card--hero{padding:0 20px}
|
|
3286
|
+
.dq-card--hero{padding:0 20px;background:transparent;border:0;border-radius:0}
|
|
3287
|
+
.dq-card-pair{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(340px,100%),1fr));gap:24px;min-width:0}
|
|
2845
3288
|
.dq-card--hero .dq-stat-label{font-size:13px;margin-bottom:2px}
|
|
2846
3289
|
.dq-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
2847
3290
|
.dq-card--hero .dq-balance-grid{align-items:stretch;gap:12px 48px}
|
|
@@ -2849,6 +3292,7 @@ var css = `
|
|
|
2849
3292
|
/* hero \u6570\u5B57\u4E0D\u753B\u70B9\u72B6\u4E0B\u5212\u7EBF\uFF0840px \u4E0B\u592A\u91CD\uFF09\uFF1B\u53EF\u70B9\u6027\u9760\u60AC\u505C\u5E95\u8272\u548C title \u63D0\u793A\u3002 */
|
|
2850
3293
|
.dq-card--hero details>summary{text-decoration:none;margin:-2px -6px;padding:2px 6px;border-radius:8px;transition:background .15s ease}
|
|
2851
3294
|
.dq-card--hero details>summary:hover,.dq-card--hero details[open]>summary{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.05))}
|
|
3295
|
+
@media (max-width:620px){.dq-card{padding:16px}.dq-card--hero{padding:0 16px}}
|
|
2852
3296
|
.dq-window-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.08))}
|
|
2853
3297
|
.dq-window-btn{border:0;border-radius:6px;padding:4px 8px;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;white-space:nowrap;cursor:pointer;transition:background .15s ease,color .15s ease,box-shadow .15s ease}
|
|
2854
3298
|
.dq-window-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}
|
|
@@ -2993,7 +3437,6 @@ var css = `
|
|
|
2993
3437
|
.dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:16px}
|
|
2994
3438
|
.dq-peak-delta{font-size:12px;font-weight:500;margin-left:8px;color:var(--dq-text-warn)}
|
|
2995
3439
|
.dq-peak-delta--save{color:var(--dq-text-success)}
|
|
2996
|
-
.dq-peak-foot{margin:12px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e);text-wrap:pretty}
|
|
2997
3440
|
.dq-cache-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-bottom:12px}
|
|
2998
3441
|
@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}}
|
|
2999
3442
|
.dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:500}
|
|
@@ -3198,6 +3641,77 @@ var css = `
|
|
|
3198
3641
|
.dq-refresh-icon{display:inline-block;line-height:1}
|
|
3199
3642
|
.dq-refresh-icon--spin{animation:dq-spin .9s linear infinite}
|
|
3200
3643
|
@keyframes dq-spin{to{transform:rotate(360deg)}}
|
|
3644
|
+
/* ============================================================================
|
|
3645
|
+
Host chrome: the \u300C\u989D\u5EA6\u300Dconversation view does not use the chat composer.
|
|
3646
|
+
The attribute is set by index.tsx only when the dashboard is mounted inside
|
|
3647
|
+
the conversation scrollport, so the account-wide panel never hides it.
|
|
3648
|
+
'!important' because the composer's slot anchor is a "display:contents"
|
|
3649
|
+
wrapper whose display we must beat regardless of host rule order.
|
|
3650
|
+
============================================================================ */
|
|
3651
|
+
html[data-dq-quota-view] [data-slot="conversation.composer"]{display:none!important}
|
|
3652
|
+
/* The narrow-viewport rule pads for the fixed composer; with it hidden there
|
|
3653
|
+
is nothing to clear, so fall back to the page's own bottom breathing room. */
|
|
3654
|
+
html[data-dq-quota-view] .dq-balance{padding-bottom:48px}
|
|
3655
|
+
/* ============================================================================
|
|
3656
|
+
Account-wide \u300C\u989D\u5EA6\u300Dpanel (the optional sidebar entry opens this main
|
|
3657
|
+
keyed panel). The dashboard keeps its own width/padding; the panel only
|
|
3658
|
+
supplies the page title row and the scrolling box the main column lacks.
|
|
3659
|
+
============================================================================ */
|
|
3660
|
+
/* Account-wide page frame: mirrors DSH's first-level pages (Plugins /
|
|
3661
|
+
Scheduled tasks) \u2014 full-height scroll box, 28px top inset on the heading,
|
|
3662
|
+
a 960px centred column, clamp(24px,4vw,48px) inline + 48px bottom insets and
|
|
3663
|
+
a 20px/28px/500 page title, so the three sibling entries line up. */
|
|
3664
|
+
.dq-panel{box-sizing:border-box;height:100%;min-height:0;overflow-y:auto;overflow-x:hidden;padding:0 clamp(24px,4vw,48px) 48px;background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-label-primary,#1f2328);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:13px;line-height:1.5}
|
|
3665
|
+
.dq-panel-head{box-sizing:border-box;display:flex;flex-direction:column;align-items:flex-start;gap:4px;width:100%;max-width:960px;margin:0 auto 32px;padding-top:28px}
|
|
3666
|
+
.dq-panel-title{margin:0;font-size:20px;font-weight:500;line-height:28px;color:var(--dsw-alias-label-primary,#1f2328)}
|
|
3667
|
+
.dq-panel-caption{font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
3668
|
+
.dq-panel .dq-balance{box-sizing:border-box;width:100%;max-width:960px;padding:0}
|
|
3669
|
+
/* macOS hiddenInset titlebar: the frame's top clearance keeps the heading
|
|
3670
|
+
below the window's top strip, matching the host's sibling pages. */
|
|
3671
|
+
[data-platform="darwin"] .dq-panel-head{padding-top:calc(28px + var(--dsh-frame-top-clearance,0px))}
|
|
3672
|
+
@media (max-width:620px){
|
|
3673
|
+
.dq-panel-head{padding-top:24px;margin-bottom:24px}
|
|
3674
|
+
.dq-panel .dq-balance{max-width:100%;padding:0}
|
|
3675
|
+
}
|
|
3676
|
+
/* Optional sidebar entry. Top placement joins the host's global-panel icon
|
|
3677
|
+
row (only the glyph is ours); bottom placement renders a row beside
|
|
3678
|
+
Settings, collapsing to a square rail button with the sidebar. */
|
|
3679
|
+
/* The glyph is an SVG matching the host's 16px stroke icons; color inherits
|
|
3680
|
+
from the sidebar row so selected/hover states stay the host's business. */
|
|
3681
|
+
.dq-side-icon{flex:none;display:block}
|
|
3682
|
+
.dq-side-entry{display:flex;align-items:center;gap:8px;box-sizing:border-box;width:100%;border:0;border-radius:8px;padding:7px 8px;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font:inherit;font-size:13px;line-height:1.3;text-align:left;cursor:pointer}
|
|
3683
|
+
.dq-side-entry:hover{background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10));color:var(--dsw-alias-label-primary,#1f2328)}
|
|
3684
|
+
.dq-side-entry:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
|
|
3685
|
+
.dq-side-entry[aria-pressed="true"]{color:var(--dsw-alias-label-primary,#1f2328)}
|
|
3686
|
+
.dq-side-entry--rail{justify-content:center;width:32px;height:32px;margin:0 auto;padding:0}
|
|
3687
|
+
.dq-side-entry-icon{flex:none;display:inline-flex;align-items:center;justify-content:center}
|
|
3688
|
+
.dq-side-entry-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3689
|
+
/* ============================================================================
|
|
3690
|
+
Settings controls for the two layout preferences: a compact select and the
|
|
3691
|
+
draggable tab-order strip.
|
|
3692
|
+
============================================================================ */
|
|
3693
|
+
.dq-select{font:inherit;font-size:13px;line-height:1.3;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));border-radius:8px;padding:5px 8px;min-width:96px;cursor:pointer}
|
|
3694
|
+
.dq-select:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
|
|
3695
|
+
.dq-tab-order{display:flex;flex-wrap:wrap;align-items:center;gap:6px;box-sizing:border-box;max-width:100%;min-height:32px;padding:4px;border:1px dashed var(--dsw-alias-border-l2,rgba(0,0,0,.16));border-radius:8px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}
|
|
3696
|
+
.dq-tab-chip{display:inline-flex;align-items:center;gap:4px;padding:3px 9px;border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-radius:999px;background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1.4;white-space:nowrap}
|
|
3697
|
+
.dq-tab-chip--current{cursor:grab;color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-state-business-primary,#0969da);font-weight:600;user-select:none;-webkit-user-select:none;touch-action:none}
|
|
3698
|
+
.dq-tab-chip--current:active{cursor:grabbing}
|
|
3699
|
+
.dq-tab-chip--current:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
|
|
3700
|
+
.dq-tab-chip--dragging{opacity:.55}
|
|
3701
|
+
.dq-tab-chip--drop-target{border-style:dashed;border-color:var(--dsw-alias-state-business-primary,#0969da)}
|
|
3702
|
+
/* ============================================================================
|
|
3703
|
+
\u300C\u56FE\u7247 / \u591A\u6A21\u6001\u7528\u91CF\u300Dlives inside the usage-trend card. The grid-rows 0fr\u21921fr
|
|
3704
|
+
trick animates the section's real height as a window range makes it appear
|
|
3705
|
+
or disappear; opacity/translate make the change read as a reveal rather
|
|
3706
|
+
than a reflow. The body keeps 14px of top room in both states (clipped by
|
|
3707
|
+
the 0fr row when closed) so opening never jumps.
|
|
3708
|
+
============================================================================ */
|
|
3709
|
+
.dq-vision-inline{display:grid;grid-template-rows:0fr;margin-top:0;opacity:0;transform:translateY(-3px);overflow:hidden;transition:grid-template-rows .32s cubic-bezier(.22,1,.36,1),margin-top .32s cubic-bezier(.22,1,.36,1),opacity .2s ease,transform .32s cubic-bezier(.22,1,.36,1)}
|
|
3710
|
+
.dq-vision-inline[data-open="true"]{grid-template-rows:1fr;margin-top:24px;opacity:1;transform:none}
|
|
3711
|
+
.dq-vision-inline-body{min-height:0;overflow:hidden}
|
|
3712
|
+
.dq-vision-inline-body>.dq-chart-title{margin-top:0}
|
|
3713
|
+
.dq-vision-inline-body>.dq-chart-title{margin-top:0}
|
|
3714
|
+
|
|
3201
3715
|
@media (prefers-reduced-motion:reduce){
|
|
3202
3716
|
.dsh-quota-root,.dsh-quota-root *,
|
|
3203
3717
|
.dsh-quota-root *::before,.dsh-quota-root *::after,
|
|
@@ -3208,7 +3722,7 @@ var css = `
|
|
|
3208
3722
|
`;
|
|
3209
3723
|
|
|
3210
3724
|
// src/client/widget.tsx
|
|
3211
|
-
var
|
|
3725
|
+
var import_react7 = require("react");
|
|
3212
3726
|
|
|
3213
3727
|
// src/client/sessions.ts
|
|
3214
3728
|
function currentSessionIdOf(state) {
|
|
@@ -3217,7 +3731,7 @@ function currentSessionIdOf(state) {
|
|
|
3217
3731
|
}
|
|
3218
3732
|
|
|
3219
3733
|
// src/client/widget.tsx
|
|
3220
|
-
var
|
|
3734
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3221
3735
|
var MARGIN = 16;
|
|
3222
3736
|
var CORNER_KEY = "widget.corner";
|
|
3223
3737
|
var COLLAPSED_KEY = "widget.collapsed";
|
|
@@ -3268,47 +3782,47 @@ function cornerPos(corner, node, bounds) {
|
|
|
3268
3782
|
function QuotaWidget(props) {
|
|
3269
3783
|
const { t } = useI18n();
|
|
3270
3784
|
const sessionId = props.useSessions(currentSessionIdOf);
|
|
3271
|
-
const [visible, setVisible] = (0,
|
|
3272
|
-
(0,
|
|
3273
|
-
const [quotaViewActive2, setQuotaViewActive] = (0,
|
|
3274
|
-
(0,
|
|
3275
|
-
const [lowBalance, setLowBalance] = (0,
|
|
3276
|
-
(0,
|
|
3277
|
-
const [selectedTabIds, setSelectedTabIds] = (0,
|
|
3278
|
-
(0,
|
|
3279
|
-
const [viewTabs, setViewTabs] = (0,
|
|
3280
|
-
(0,
|
|
3281
|
-
const [activeViewId, setActiveViewId] = (0,
|
|
3785
|
+
const [visible, setVisible] = (0, import_react7.useState)(widgetVisibleStore.get());
|
|
3786
|
+
(0, import_react7.useEffect)(() => widgetVisibleStore.subscribe(() => setVisible(widgetVisibleStore.get())), []);
|
|
3787
|
+
const [quotaViewActive2, setQuotaViewActive] = (0, import_react7.useState)(quotaViewActiveStore.get());
|
|
3788
|
+
(0, import_react7.useEffect)(() => quotaViewActiveStore.subscribe(() => setQuotaViewActive(quotaViewActiveStore.get())), []);
|
|
3789
|
+
const [lowBalance, setLowBalance] = (0, import_react7.useState)(lowBalanceStore.get());
|
|
3790
|
+
(0, import_react7.useEffect)(() => lowBalanceStore.subscribe(() => setLowBalance(lowBalanceStore.get())), []);
|
|
3791
|
+
const [selectedTabIds, setSelectedTabIds] = (0, import_react7.useState)(widgetTabIdsStore.get());
|
|
3792
|
+
(0, import_react7.useEffect)(() => widgetTabIdsStore.subscribe(() => setSelectedTabIds(widgetTabIdsStore.get())), []);
|
|
3793
|
+
const [viewTabs, setViewTabs] = (0, import_react7.useState)(() => props.views.list());
|
|
3794
|
+
(0, import_react7.useEffect)(() => props.views.subscribe(() => setViewTabs(props.views.list())), [props.views]);
|
|
3795
|
+
const [activeViewId, setActiveViewId] = (0, import_react7.useState)(null);
|
|
3282
3796
|
const cachedBalance = getCachedBalance();
|
|
3283
|
-
const [data, setData] = (0,
|
|
3284
|
-
const [error, setError] = (0,
|
|
3285
|
-
const [loading, setLoading] = (0,
|
|
3286
|
-
const [collapsed, setCollapsed] = (0,
|
|
3287
|
-
const [pos, setPos] = (0,
|
|
3288
|
-
const [corner, setCorner] = (0,
|
|
3289
|
-
const [dragging, setDragging] = (0,
|
|
3290
|
-
const rootRef = (0,
|
|
3291
|
-
const dragRef = (0,
|
|
3797
|
+
const [data, setData] = (0, import_react7.useState)(cachedBalance?.data ?? null);
|
|
3798
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
3799
|
+
const [loading, setLoading] = (0, import_react7.useState)(cachedBalance === null);
|
|
3800
|
+
const [collapsed, setCollapsed] = (0, import_react7.useState)(() => loadPref(COLLAPSED_KEY, isBoolean, false));
|
|
3801
|
+
const [pos, setPos] = (0, import_react7.useState)(null);
|
|
3802
|
+
const [corner, setCorner] = (0, import_react7.useState)(() => loadPref(CORNER_KEY, isCorner, "br"));
|
|
3803
|
+
const [dragging, setDragging] = (0, import_react7.useState)(false);
|
|
3804
|
+
const rootRef = (0, import_react7.useRef)(null);
|
|
3805
|
+
const dragRef = (0, import_react7.useRef)(null);
|
|
3292
3806
|
const readUsage = () => {
|
|
3293
3807
|
const cached = getCachedUsage();
|
|
3294
3808
|
return { data: cached?.ok === true ? cached.data ?? null : null, at: getCachedUsageAt() };
|
|
3295
3809
|
};
|
|
3296
|
-
const [usage, setUsage] = (0,
|
|
3297
|
-
(0,
|
|
3810
|
+
const [usage, setUsage] = (0, import_react7.useState)(readUsage);
|
|
3811
|
+
(0, import_react7.useEffect)(() => subscribeUsage(() => setUsage(readUsage())), []);
|
|
3298
3812
|
const readSessionUsage = (id) => {
|
|
3299
3813
|
if (id === void 0) return null;
|
|
3300
3814
|
const response = getCachedSessionUsage(id);
|
|
3301
3815
|
return response?.ok === true ? response.data ?? null : null;
|
|
3302
3816
|
};
|
|
3303
|
-
const [sessionUsage, setSessionUsage] = (0,
|
|
3304
|
-
(0,
|
|
3817
|
+
const [sessionUsage, setSessionUsage] = (0, import_react7.useState)(() => readSessionUsage(sessionId));
|
|
3818
|
+
(0, import_react7.useEffect)(() => {
|
|
3305
3819
|
setSessionUsage(readSessionUsage(sessionId));
|
|
3306
3820
|
if (sessionId === void 0) return;
|
|
3307
3821
|
const unsubscribe = subscribeSessionUsage(sessionId, () => setSessionUsage(readSessionUsage(sessionId)));
|
|
3308
3822
|
void fetchSessionUsage(sessionId);
|
|
3309
3823
|
return unsubscribe;
|
|
3310
3824
|
}, [sessionId]);
|
|
3311
|
-
(0,
|
|
3825
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
3312
3826
|
const viewIds = viewTabs.map((tab) => tab.id);
|
|
3313
3827
|
const read = () => {
|
|
3314
3828
|
const next = getActiveConversationViewId(getShellFrame(rootRef.current), viewIds);
|
|
@@ -3345,14 +3859,14 @@ function QuotaWidget(props) {
|
|
|
3345
3859
|
setLoading(false);
|
|
3346
3860
|
}
|
|
3347
3861
|
};
|
|
3348
|
-
(0,
|
|
3862
|
+
(0, import_react7.useEffect)(() => {
|
|
3349
3863
|
void load(true);
|
|
3350
3864
|
const id = setInterval(() => {
|
|
3351
3865
|
void load(false);
|
|
3352
3866
|
}, 6e4);
|
|
3353
3867
|
return () => clearInterval(id);
|
|
3354
3868
|
}, []);
|
|
3355
|
-
(0,
|
|
3869
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
3356
3870
|
if (!visible || quotaViewActive2) return;
|
|
3357
3871
|
const node = rootRef.current;
|
|
3358
3872
|
if (node === null) return;
|
|
@@ -3480,9 +3994,9 @@ function QuotaWidget(props) {
|
|
|
3480
3994
|
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";
|
|
3481
3995
|
const ageMinutes = usage.at === null ? null : Math.floor((Date.now() - usage.at) / 6e4);
|
|
3482
3996
|
const usageAgeText = ageMinutes === null || ageMinutes < 1 ? t("widget.updatedNow") : t("widget.stale", { minutes: ageMinutes });
|
|
3483
|
-
const sessionCostRow = sessionUsage === null ? null : /* @__PURE__ */ (0,
|
|
3484
|
-
/* @__PURE__ */ (0,
|
|
3485
|
-
/* @__PURE__ */ (0,
|
|
3997
|
+
const sessionCostRow = sessionUsage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-row", title: t("widget.sessionCostTitle"), children: [
|
|
3998
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-label", children: t("widget.sessionCost") }),
|
|
3999
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
3486
4000
|
"\xA5",
|
|
3487
4001
|
fmtTurnCost(sessionUsage.cost)
|
|
3488
4002
|
] })
|
|
@@ -3490,50 +4004,50 @@ function QuotaWidget(props) {
|
|
|
3490
4004
|
let body = null;
|
|
3491
4005
|
if (!collapsed) {
|
|
3492
4006
|
if (loading && data === null && error === null) {
|
|
3493
|
-
body = /* @__PURE__ */ (0,
|
|
3494
|
-
/* @__PURE__ */ (0,
|
|
4007
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4008
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { children: t("widget.loading") }),
|
|
3495
4009
|
sessionCostRow
|
|
3496
4010
|
] });
|
|
3497
4011
|
} else if (error !== null && data === null) {
|
|
3498
|
-
body = /* @__PURE__ */ (0,
|
|
3499
|
-
/* @__PURE__ */ (0,
|
|
4012
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4013
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-error", children: localizeApiError(error, t, "error.query") }),
|
|
3500
4014
|
sessionCostRow
|
|
3501
4015
|
] });
|
|
3502
4016
|
} else if (primary !== null) {
|
|
3503
|
-
body = /* @__PURE__ */ (0,
|
|
3504
|
-
/* @__PURE__ */ (0,
|
|
3505
|
-
/* @__PURE__ */ (0,
|
|
3506
|
-
/* @__PURE__ */ (0,
|
|
3507
|
-
/* @__PURE__ */ (0,
|
|
4017
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4018
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-remaining-label", children: t("balance.remaining") }),
|
|
4019
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { title: low ? t("widget.lowTitle", { amount: fmt(lowBalance) }) : void 0, children: [
|
|
4020
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: `dsh-quota-total${low ? " dsh-quota-total--low" : ""}`, children: fmt(primary.total) }),
|
|
4021
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-currency", children: primary.currency })
|
|
3508
4022
|
] }),
|
|
3509
|
-
usage.data !== null && /* @__PURE__ */ (0,
|
|
3510
|
-
/* @__PURE__ */ (0,
|
|
3511
|
-
/* @__PURE__ */ (0,
|
|
4023
|
+
usage.data !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-row", title: t("widget.todayCostTitle", { age: usageAgeText }), children: [
|
|
4024
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-label", children: t("widget.todayCost") }),
|
|
4025
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dsh-quota-value", children: [
|
|
3512
4026
|
"\xA5",
|
|
3513
4027
|
fmt(usage.data.summary.today.cost)
|
|
3514
4028
|
] })
|
|
3515
4029
|
] }),
|
|
3516
4030
|
sessionCostRow,
|
|
3517
|
-
data?.isAvailable === false && /* @__PURE__ */ (0,
|
|
3518
|
-
/* @__PURE__ */ (0,
|
|
3519
|
-
/* @__PURE__ */ (0,
|
|
4031
|
+
data?.isAvailable === false && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-row", children: [
|
|
4032
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-label", children: t("common.status") }),
|
|
4033
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-value dsh-quota-error", children: t("common.unavailable") })
|
|
3520
4034
|
] })
|
|
3521
4035
|
] });
|
|
3522
4036
|
} else {
|
|
3523
|
-
body = /* @__PURE__ */ (0,
|
|
3524
|
-
/* @__PURE__ */ (0,
|
|
4037
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-body", children: [
|
|
4038
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dsh-quota-error", children: t("widget.noBalance") }),
|
|
3525
4039
|
sessionCostRow
|
|
3526
4040
|
] });
|
|
3527
4041
|
}
|
|
3528
4042
|
}
|
|
3529
4043
|
const style = pos !== null ? { left: `${pos.x}px`, top: `${pos.y}px`, right: "auto", bottom: "auto" } : void 0;
|
|
3530
|
-
return /* @__PURE__ */ (0,
|
|
4044
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3531
4045
|
"div",
|
|
3532
4046
|
{
|
|
3533
4047
|
className: `dsh-quota-root${dragging ? " dsh-quota-dragging" : ""}${hidden ? " dsh-quota-root--hidden" : ""}`,
|
|
3534
4048
|
style,
|
|
3535
4049
|
"aria-hidden": hidden,
|
|
3536
|
-
children: /* @__PURE__ */ (0,
|
|
4050
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3537
4051
|
"div",
|
|
3538
4052
|
{
|
|
3539
4053
|
ref: rootRef,
|
|
@@ -3545,14 +4059,14 @@ function QuotaWidget(props) {
|
|
|
3545
4059
|
onPointerCancel: () => endDrag(false),
|
|
3546
4060
|
onLostPointerCapture: () => endDrag(false),
|
|
3547
4061
|
children: [
|
|
3548
|
-
/* @__PURE__ */ (0,
|
|
3549
|
-
/* @__PURE__ */ (0,
|
|
3550
|
-
/* @__PURE__ */ (0,
|
|
3551
|
-
/* @__PURE__ */ (0,
|
|
3552
|
-
/* @__PURE__ */ (0,
|
|
4062
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-header", children: [
|
|
4063
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-title", children: [
|
|
4064
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-grip", title: t("widget.dragGrip"), children: "\u2807" }),
|
|
4065
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: dotClass }),
|
|
4066
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dsh-quota-name", children: t("widget.title") })
|
|
3553
4067
|
] }),
|
|
3554
|
-
/* @__PURE__ */ (0,
|
|
3555
|
-
/* @__PURE__ */ (0,
|
|
4068
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dsh-quota-actions", children: [
|
|
4069
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3556
4070
|
"button",
|
|
3557
4071
|
{
|
|
3558
4072
|
className: "dsh-quota-btn",
|
|
@@ -3568,7 +4082,7 @@ function QuotaWidget(props) {
|
|
|
3568
4082
|
children: "\u21BB"
|
|
3569
4083
|
}
|
|
3570
4084
|
),
|
|
3571
|
-
/* @__PURE__ */ (0,
|
|
4085
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3572
4086
|
"button",
|
|
3573
4087
|
{
|
|
3574
4088
|
className: "dsh-quota-btn",
|
|
@@ -3586,7 +4100,7 @@ function QuotaWidget(props) {
|
|
|
3586
4100
|
)
|
|
3587
4101
|
] })
|
|
3588
4102
|
] }),
|
|
3589
|
-
collapsed && primary !== null && /* @__PURE__ */ (0,
|
|
4103
|
+
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: [
|
|
3590
4104
|
fmt(primary.total),
|
|
3591
4105
|
" ",
|
|
3592
4106
|
primary.currency
|
|
@@ -3617,9 +4131,10 @@ function conversationViewsSource(slots) {
|
|
|
3617
4131
|
}
|
|
3618
4132
|
|
|
3619
4133
|
// src/client/index.tsx
|
|
3620
|
-
var
|
|
4134
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
3621
4135
|
var name = "dsh-usage-dashboard";
|
|
3622
|
-
var inject = ["slots", "locale"];
|
|
4136
|
+
var inject = ["slots", "locale", "layout"];
|
|
4137
|
+
var openQuotaTab = null;
|
|
3623
4138
|
function apply(ctx) {
|
|
3624
4139
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-usage-dashboard: browser dictionaries");
|
|
3625
4140
|
const t = ctx.locale.bind(NS);
|
|
@@ -3637,7 +4152,11 @@ function apply(ctx) {
|
|
|
3637
4152
|
});
|
|
3638
4153
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register(
|
|
3639
4154
|
{ name: "shell.overlay", id: "deepseek-quota", order: 1e3, locale: NS, label: () => t("widget.title") },
|
|
3640
|
-
({ t: slotT, useSessions }) => /* @__PURE__ */ (0,
|
|
4155
|
+
({ 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 }) }) })
|
|
4156
|
+
));
|
|
4157
|
+
ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register(
|
|
4158
|
+
{ name: "conversation.composer.dock", id: "quota-cost", order: 2, locale: NS },
|
|
4159
|
+
({ 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 }) }) })
|
|
3641
4160
|
));
|
|
3642
4161
|
ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register(
|
|
3643
4162
|
{
|
|
@@ -3647,19 +4166,155 @@ function apply(ctx) {
|
|
|
3647
4166
|
locale: NS,
|
|
3648
4167
|
label: () => t("messageCost.label")
|
|
3649
4168
|
},
|
|
3650
|
-
({ t: slotT, sessionId, messageId }) => /* @__PURE__ */ (0,
|
|
3651
|
-
));
|
|
3652
|
-
ctx.slots.inject("conversation.view", () => ctx.slots.register(
|
|
3653
|
-
{ name: "conversation.view", id: "balance", order: 20, locale: NS, label: () => t("nav.quota") },
|
|
3654
|
-
// `conversation.view` is a session-scoped slot, so the host injects its
|
|
3655
|
-
// standard session props (including `sessionId`) alongside `t` — this
|
|
3656
|
-
// plugin only destructures the two fields it needs, per the existing
|
|
3657
|
-
// "hand-write the minimal structural type" convention (see context.ts).
|
|
3658
|
-
// `sessionId` is typed optional defensively even though the host is
|
|
3659
|
-
// expected to always supply it: BalanceDashboard renders the current-
|
|
3660
|
-
// session card only when it actually has one, never throws either way.
|
|
3661
|
-
({ 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 }) }) })
|
|
4169
|
+
({ 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 }) }) })
|
|
3662
4170
|
));
|
|
4171
|
+
const QuotaView = ({ t: slotT, sessionId, openView }) => {
|
|
4172
|
+
(0, import_react8.useEffect)(() => {
|
|
4173
|
+
if (openView !== void 0) openQuotaTab = openView;
|
|
4174
|
+
}, [openView]);
|
|
4175
|
+
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 }) }) });
|
|
4176
|
+
};
|
|
4177
|
+
ctx.effect(() => {
|
|
4178
|
+
let disposeView = null;
|
|
4179
|
+
const mount = () => {
|
|
4180
|
+
disposeView?.();
|
|
4181
|
+
disposeView = ctx.slots.inject("conversation.view", () => ctx.slots.register(
|
|
4182
|
+
{
|
|
4183
|
+
name: "conversation.view",
|
|
4184
|
+
id: "balance",
|
|
4185
|
+
order: viewOrderForIndex(ctx.slots.entries("conversation.view"), quotaTabIndexStore.get()),
|
|
4186
|
+
locale: NS,
|
|
4187
|
+
label: () => t("nav.quota")
|
|
4188
|
+
},
|
|
4189
|
+
// `conversation.view` is a session-scoped slot, so the host injects its
|
|
4190
|
+
// standard session props (including `sessionId`) alongside `t`; only the
|
|
4191
|
+
// fields this plugin needs are destructured, per the existing
|
|
4192
|
+
// "hand-write the minimal structural type" convention (see context.ts).
|
|
4193
|
+
// `sessionId` stays optional: BalanceDashboard renders the current-
|
|
4194
|
+
// session card only when it actually has one, never throws either way.
|
|
4195
|
+
QuotaView
|
|
4196
|
+
));
|
|
4197
|
+
};
|
|
4198
|
+
mount();
|
|
4199
|
+
const unsubscribe = quotaTabIndexStore.subscribe(() => {
|
|
4200
|
+
const restore = document !== void 0 && document.documentElement.hasAttribute("data-dq-quota-view");
|
|
4201
|
+
disposeView?.();
|
|
4202
|
+
disposeView = null;
|
|
4203
|
+
mount();
|
|
4204
|
+
if (restore) queueMicrotask(() => openQuotaTab?.("balance", "dsh-usage-dashboard:tab-order"));
|
|
4205
|
+
});
|
|
4206
|
+
return () => {
|
|
4207
|
+
unsubscribe();
|
|
4208
|
+
disposeView?.();
|
|
4209
|
+
};
|
|
4210
|
+
}, "dsh-usage-dashboard: quota tab registration");
|
|
4211
|
+
const QuotaPanel = ({ t: slotT }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dq-panel", children: [
|
|
4212
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "dq-panel-head", "data-window-drag": true, children: [
|
|
4213
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { className: "dq-panel-title", children: slotT("nav.quota") }),
|
|
4214
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dq-panel-caption", children: slotT("panel.caption") })
|
|
4215
|
+
] }),
|
|
4216
|
+
/* @__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 }) }) })
|
|
4217
|
+
] });
|
|
4218
|
+
const QuotaGlyph = ({ size, active }) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
4219
|
+
"svg",
|
|
4220
|
+
{
|
|
4221
|
+
className: "dq-side-icon",
|
|
4222
|
+
"data-active": active === true ? "true" : "false",
|
|
4223
|
+
width: size,
|
|
4224
|
+
height: size,
|
|
4225
|
+
viewBox: "0 0 16 16",
|
|
4226
|
+
fill: "none",
|
|
4227
|
+
stroke: "currentColor",
|
|
4228
|
+
strokeWidth: 1.1,
|
|
4229
|
+
strokeLinecap: "round",
|
|
4230
|
+
strokeLinejoin: "round",
|
|
4231
|
+
"aria-hidden": "true",
|
|
4232
|
+
focusable: "false",
|
|
4233
|
+
children: [
|
|
4234
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M3.5 3.3 8 8.7l4.5-5.4" }),
|
|
4235
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M8 8.7V13.2" }),
|
|
4236
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M3.5 10.2h9" }),
|
|
4237
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M3.5 12.1h9" })
|
|
4238
|
+
]
|
|
4239
|
+
}
|
|
4240
|
+
);
|
|
4241
|
+
const QuotaFooterButton = ({ wide, t: slotT }) => {
|
|
4242
|
+
const [active, setActive] = (0, import_react8.useState)(() => ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
|
|
4243
|
+
(0, import_react8.useEffect)(() => {
|
|
4244
|
+
const sync = () => {
|
|
4245
|
+
setActive(ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
|
|
4246
|
+
};
|
|
4247
|
+
sync();
|
|
4248
|
+
return ctx.layout.panelInfo.subscribe(sync);
|
|
4249
|
+
}, []);
|
|
4250
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
4251
|
+
"button",
|
|
4252
|
+
{
|
|
4253
|
+
type: "button",
|
|
4254
|
+
className: `dq-side-entry${wide ? "" : " dq-side-entry--rail"}`,
|
|
4255
|
+
title: slotT("panel.openAria"),
|
|
4256
|
+
"aria-label": slotT("nav.quota"),
|
|
4257
|
+
"aria-pressed": active,
|
|
4258
|
+
onClick: () => {
|
|
4259
|
+
ctx.layout.selectPanel(active ? null : QUOTA_PANEL_ID);
|
|
4260
|
+
},
|
|
4261
|
+
children: [
|
|
4262
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dq-side-entry-icon", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(QuotaGlyph, { size: 16 }) }),
|
|
4263
|
+
wide && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dq-side-entry-label", children: slotT("nav.quota") })
|
|
4264
|
+
]
|
|
4265
|
+
}
|
|
4266
|
+
);
|
|
4267
|
+
};
|
|
4268
|
+
ctx.effect(() => ctx.slots.inject("main", () => ctx.slots.register(
|
|
4269
|
+
{ name: "main", key: QUOTA_PANEL_ID, locale: NS },
|
|
4270
|
+
QuotaPanel
|
|
4271
|
+
)), "dsh-usage-dashboard: quota panel");
|
|
4272
|
+
ctx.effect(() => {
|
|
4273
|
+
let disposeEntry = null;
|
|
4274
|
+
const sync = () => {
|
|
4275
|
+
disposeEntry?.();
|
|
4276
|
+
disposeEntry = null;
|
|
4277
|
+
const placement = sidebarEntryStore.get();
|
|
4278
|
+
if (placement === "top") {
|
|
4279
|
+
disposeEntry = ctx.slots.inject("sidebar.panellist", () => ctx.slots.register(
|
|
4280
|
+
{ name: "sidebar.panellist", id: QUOTA_PANEL_ID, order: 1e3, label: () => t("nav.quota"), locale: NS },
|
|
4281
|
+
QuotaGlyph
|
|
4282
|
+
));
|
|
4283
|
+
return;
|
|
4284
|
+
}
|
|
4285
|
+
if (placement === "bottom") {
|
|
4286
|
+
disposeEntry = ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register(
|
|
4287
|
+
{ name: "sidebar.footer.action", id: "quota", order: 10, locale: NS },
|
|
4288
|
+
QuotaFooterButton
|
|
4289
|
+
));
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4292
|
+
if (ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID) ctx.layout.selectPanel(null);
|
|
4293
|
+
};
|
|
4294
|
+
sync();
|
|
4295
|
+
const unsubscribe = sidebarEntryStore.subscribe(sync);
|
|
4296
|
+
return () => {
|
|
4297
|
+
unsubscribe();
|
|
4298
|
+
disposeEntry?.();
|
|
4299
|
+
};
|
|
4300
|
+
}, "dsh-usage-dashboard: sidebar entry");
|
|
4301
|
+
ctx.effect(() => {
|
|
4302
|
+
let applied = false;
|
|
4303
|
+
const sync = () => {
|
|
4304
|
+
const root = document.querySelector(".dq-balance");
|
|
4305
|
+
const should = quotaViewActiveStore.get() && root !== null && root.closest("[data-conversation-scroll]") !== null;
|
|
4306
|
+
if (should === applied) return;
|
|
4307
|
+
applied = should;
|
|
4308
|
+
if (should) document.documentElement.setAttribute("data-dq-quota-view", "");
|
|
4309
|
+
else document.documentElement.removeAttribute("data-dq-quota-view");
|
|
4310
|
+
};
|
|
4311
|
+
sync();
|
|
4312
|
+
const unsubscribe = quotaViewActiveStore.subscribe(sync);
|
|
4313
|
+
return () => {
|
|
4314
|
+
unsubscribe();
|
|
4315
|
+
if (applied) document.documentElement.removeAttribute("data-dq-quota-view");
|
|
4316
|
+
};
|
|
4317
|
+
}, "dsh-usage-dashboard: composer visibility");
|
|
3663
4318
|
}
|
|
3664
4319
|
return module.exports; } });
|
|
3665
4320
|
//# sourceMappingURL=client.js.map
|