@cassius0924/dsh-usage-dashboard 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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_react7 = 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": "\u8FD8\u6CA1\u8BBE\u7F6E\u6708\u5EA6\u9884\u7B97\uFF0C\u65E0\u6CD5\u63D0\u524D\u5224\u65AD\u662F\u5426\u4F1A\u8D85\u652F\u3002",
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": "\u8FD8\u6CA1\u6709 prompt token \u53EF\u7EDF\u8BA1\u7F13\u5B58\u547D\u4E2D\u3002",
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
- "cache.lowHint": "\u547D\u4E2D\u7387\u504F\u4F4E\uFF1A\u9891\u7E41\u6539\u52A8 system prompt / \u5DE5\u5177\u5B9A\u4E49\u4F1A\u8BA9\u524D\u7F00\u7F13\u5B58\u5931\u6548\uFF0C\u628A\u7A33\u5B9A\u5185\u5BB9\u653E\u5728\u5BF9\u8BDD\u6700\u524D\u9762\u80FD\u63D0\u9AD8\u547D\u4E2D\u7387\u3002",
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": "\u56FE\u7247\u6309\u5B98\u65B9\u6362\u7B97\u89C4\u5219\u4F30\u7B97\uFF1A\u5148\u6309\u6BD4\u4F8B\u7F29\u653E\uFF08\u4E0A\u9650 ~800\xD7800\u3001\u4E0B\u9650 384\xD7384\uFF09\uFF0Ctoken \u4E0E\u7F29\u653E\u540E\u9762\u79EF\u6210\u6B63\u6BD4\u3001\u6BCF\u5F20\u4E0A\u9650 384\uFF1B\u56FE\u7247\u5F52\u5C5E\u5230\u968F\u540E\u4E00\u6B21\u6A21\u578B\u8C03\u7528\uFF0C\u6309\u8BE5\u8C03\u7528\u7684\u65F6\u6BB5\u4E0E\u6A21\u578B\u5355\u4EF7\u4F30\u7B97\uFF0C\u5B9E\u9645\u4EE5\u63A5\u53E3\u8FD4\u56DE\u7684\u7528\u91CF\u4E3A\u51C6\u3002",
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\u6709\u7528\u91CF\uFF0C\u4E0A\u9762\u662F\u6700\u8D35\u7684 {shown} \u4E2A\u3002",
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\uFF0C\u6362\u4E2A\u66F4\u957F\u5468\u671F\u770B\u770B\u3002",
425
- "chart.heatmapTitle": "\u8FD1\u4E00\u5E74 \xB7 \u6BCF\u65E5\u7528\u91CF\u70ED\u529B\u56FE",
426
- "chart.empty": "\u8FD8\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\u3002\u5728 DSH \u91CC\u8DD1\u4E00\u8F6E\u5BF9\u8BDD\uFF0C\u8FD9\u91CC\u4F1A\u51FA\u73B0\u9010\u5929 / \u9010\u5C0F\u65F6\u7EDF\u8BA1\u4E0E\u8D39\u7528\u4F30\u7B97\u3002",
420
+ "chart.noWindowData": "{window}\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\u3002",
421
+ "chart.heatmapTitle": "\u8FD1\u4E00\u5E74 \xB7 \u6BCF\u65E5",
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,37 +485,39 @@ 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\u3002\u8BF7\u786E\u8BA4\u8D26\u53F7\u5DF2\u767B\u5F55\uFF0C\u6216\u5728\u300C\u8BBE\u7F6E \u2192 \u6A21\u578B\u300D\u4E2D\u586B\u5199 DEEPSEEK_API_KEY\uFF0C\u7136\u540E\u70B9\u53F3\u4E0A\u89D2\u5237\u65B0\u3002",
493
- "sessionUsage.title": "\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\uFF08\u8D39\u7528\u4E3A\u4F30\u7B97\uFF09",
494
- "sessionUsage.cost": "\u672C\u4F1A\u8BDD\u8D39\u7528",
495
- "sessionUsage.tokens": "\u672C\u4F1A\u8BDD tokens",
496
- "sessionUsage.calls": "\u672C\u4F1A\u8BDD\u8C03\u7528",
488
+ "balance.empty": "\u8BFB\u4E0D\u5230\u4F59\u989D\uFF0C\u8BF7\u68C0\u67E5\u8D26\u53F7\u767B\u5F55\u6216 API Key \u540E\u5237\u65B0\u3002",
489
+ "sessionUsage.title": "\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017",
490
+ "common.estimated": "\u8D39\u7528\u4E3A\u4F30\u7B97",
491
+ "sessionUsage.cost": "\u8D39\u7528",
492
+ "sessionUsage.tokens": "Tokens",
493
+ "sessionUsage.calls": "\u8C03\u7528\u6B21\u6570",
497
494
  "sessionUsage.range": "\u6D3B\u8DC3\u65F6\u95F4 {range}",
498
- "sessionUsage.empty": "\u8FD9\u4E2A\u4F1A\u8BDD\u8FD8\u6CA1\u6709\u4EA7\u751F\u7528\u91CF\u3002\u53D1\u4E00\u6761\u6D88\u606F\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u8FD9\u4E2A\u4F1A\u8BDD\u4E13\u5C5E\u7684\u8D39\u7528\u4E0E\u6A21\u578B\u660E\u7EC6\u3002",
499
- "overview.title": "\u6D88\u8017\u6982\u89C8\uFF08\u8D39\u7528\u4E3A\u4F30\u7B97\uFF09",
495
+ "sessionUsage.single": "{model} \xB7 \u8F93\u5165 {input} / \u8F93\u51FA {output} / \u7F13\u5B58 {cache}",
496
+ "sessionUsage.empty": "\u8FD9\u4E2A\u4F1A\u8BDD\u8FD8\u6CA1\u6709\u7528\u91CF\u3002",
497
+ "overview.title": "\u6D88\u8017\u6982\u89C8",
500
498
  "overview.today": "\u4ECA\u65E5",
501
499
  "overview.month": "\u672C\u6708",
502
500
  "overview.total": "\u7D2F\u8BA1",
503
501
  "overview.vsYesterday": "\u8F83\u6628\u65E5",
504
502
  "overview.vsLastMonth": "\u8F83\u4E0A\u6708\u540C\u671F",
505
503
  "overview.empty": "\u8FD8\u6CA1\u6709\u53EF\u7EDF\u8BA1\u7684\u6D88\u8017\u3002",
506
- "usage.trendTitle": "\u7528\u91CF\u8D8B\u52BF\uFF08tokens\uFF09",
504
+ "usage.trendTitle": "\u7528\u91CF\u8D8B\u52BF",
507
505
  "usage.windowInput": "{window}\u8F93\u5165",
508
506
  "usage.modelCalls": "\u6A21\u578B\u8C03\u7528",
509
507
  "heatmap.title": "\u7528\u91CF\u70ED\u529B\u56FE",
510
508
  "peak.title": "\u9AD8\u5CF0 / \u95F2\u65F6\u5206\u5E03",
511
509
  "cache.title": "\u7F13\u5B58\u547D\u4E2D\u4E0E\u8282\u7701",
512
- "models.title": "\u6A21\u578B\u6210\u672C\u6392\u884C \xB7 {window}",
513
- "sessions.title": "\u4F1A\u8BDD\u6210\u672C\u6392\u884C \xB7 {window}",
510
+ "models.title": "\u6A21\u578B\u6210\u672C\u6392\u884C",
511
+ "sessions.title": "\u4F1A\u8BDD\u6210\u672C\u6392\u884C",
514
512
  "links.title": "\u5FEB\u6377\u5165\u53E3",
515
513
  "links.usage": "\u67E5\u770B\u989D\u5EA6 / \u7528\u91CF",
516
514
  "links.apiKey": "\u751F\u6210 API Key",
517
515
  "links.status": "\u670D\u52A1\u72B6\u6001",
518
516
  "settings.title": "\u8BBE\u7F6E",
519
517
  "settings.widget": "\u5728\u5176\u4ED6\u9875\u9762\u663E\u793A\u53F3\u4E0B\u89D2\u60AC\u6D6E\u989D\u5EA6\u7A97\u53E3",
520
- "settings.widgetHint": "\u989D\u5EA6\u9875\u4F1A\u81EA\u52A8\u9690\u85CF\u6D6E\u7A97\uFF1B\u5176\u4ED6\u9875\u9762\u662F\u5426\u5C55\u793A\u7531\u4E0B\u65B9\u9009\u62E9\u51B3\u5B9A\u3002",
518
+ "settings.widgetHint": "\u989D\u5EA6\u9875\u81EA\u52A8\u9690\u85CF\uFF1B\u5176\u4ED6\u9875\u9762\u6309\u4E0B\u65B9\u9009\u62E9\u663E\u793A\u3002",
521
519
  "settings.widgetTabs": "\u5C55\u793A\u6D6E\u7A97\u7684\u9875\u9762",
522
- "settings.widgetTabsHint": "\u9875\u9762\u5217\u8868\u6765\u81EA DSH \u7684\u5B9E\u65F6 Tab \u6CE8\u518C\u8868\uFF1B\u5176\u4ED6\u63D2\u4EF6\u65B0\u589E\u7684 Tab \u4E5F\u4F1A\u81EA\u52A8\u51FA\u73B0\u5728\u8FD9\u91CC\u3002",
520
+ "settings.widgetTabsHint": "\u5217\u8868\u4F1A\u968F\u5DF2\u5B89\u88C5\u7684\u9875\u9762\u81EA\u52A8\u66F4\u65B0\u3002",
523
521
  "settings.widgetTabsEmpty": "\u5F53\u524D\u6CA1\u6709\u53EF\u914D\u7F6E\u7684\u4F1A\u8BDD\u9875\u9762\u3002",
524
522
  "settings.lowBalance": "\u4F59\u989D\u9884\u8B66\u7EBF",
525
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",
@@ -527,6 +525,18 @@ var zh = {
527
525
  "settings.monthlyBudget": "\u6708\u5EA6\u9884\u7B97",
528
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",
529
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",
530
540
  "widget.updatedNow": "\u521A\u521A\u66F4\u65B0",
531
541
  "widget.stale": "{minutes} \u5206\u949F\u524D\u7684\u6570\u636E\uFF0C\u70B9 \u21BB \u66F4\u65B0",
532
542
  "widget.loading": "\u67E5\u8BE2\u4E2D\u2026",
@@ -576,7 +586,7 @@ var en = {
576
586
  "delta.noBaseline": "No baseline",
577
587
  "delta.flat": "No change",
578
588
  "period.volume": "{tokens} tokens \xB7 {calls}",
579
- "budget.unset": "Set a monthly budget to see whether current spending is on track.",
589
+ "budget.unset": "No monthly budget set.",
580
590
  "budget.configure": "Set budget",
581
591
  "budget.over": "Over budget by \xA5{amount}",
582
592
  "budget.forecastOver": "At this pace, \xA5{amount} over by month-end",
@@ -599,7 +609,6 @@ var en = {
599
609
  "pricing.unit": "Unit: {currency} / million tokens. ",
600
610
  "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. ",
601
611
  "pricing.visionNote": "Images are converted to tokens by size and charged together with text. ",
602
- "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. ",
603
612
  "pricing.unknown": "Unknown models use deepseek-v4-pro rates.",
604
613
  "coverage.noRange": "No valid usage timestamps",
605
614
  "coverage.title": "Coverage",
@@ -620,23 +629,19 @@ var en = {
620
629
  "export.modelCsv": "Model CSV",
621
630
  "export.fullJson": "Full JSON",
622
631
  "export.jsonHint": "Charts and rankings included",
623
- "cache.empty": "No prompt tokens are available for cache analysis yet.",
632
+ "cache.empty": "No cache data yet.",
624
633
  "cache.rate": "Cache hit rate",
625
634
  "cache.saved": "Estimated savings",
626
635
  "cache.allMiss": "Cost with no hits",
627
636
  "cache.aria": "{hit}% cache hits, {miss}% misses",
628
637
  "cache.hitLegend": "Cache hits {tokens}",
629
638
  "cache.missLegend": "Misses {tokens} (charged at miss rate)",
630
- "cache.lowHint": "Low hit rate: frequent changes to the system prompt or tool definitions invalidate prefix caching. Put stable content first to improve reuse.",
631
- "cache.goodHint": "Prefix caching charges repeated prompt prefixes at the hit rate, making it the largest saving lever on this bill.",
632
- "peak.empty": "No usage can be classified by pricing period yet.",
639
+ "peak.empty": "No usage yet.",
633
640
  "peak.aria": "{peak}% peak, {offPeak}% off-peak",
634
641
  "peak.peakLegend": "Peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
635
642
  "peak.offPeakLegend": "Off-peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
636
643
  "peak.shift": "If all peak usage moved off-peak",
637
644
  "peak.saving": "Save \xA5{amount}",
638
- "peak.schedule": "Peak periods are {windows} Beijing time, Monday to Friday; off-peak rates are half price. ",
639
- "peak.activeHint": "Run batch or delay-tolerant work off-peak to pay half as much for the same tokens.",
640
645
  "vision.empty": "No image input in {window}.",
641
646
  "vision.title": "Image / multimodal usage",
642
647
  "vision.images": "Images",
@@ -644,7 +649,7 @@ var en = {
644
649
  "vision.cost": "Estimated cost",
645
650
  "vision.tokensShare": "{percent}% of input",
646
651
  "vision.costShare": "{percent}% of cost",
647
- "vision.note": "Images are estimated with the official rule: resized keeping aspect ratio (cap ~800\xD7800, floor 384\xD7384), tokens proportional to the resized area with a 384-token cap per image. Each image is attributed to the next model call and priced at that call's period and model rate; the provider usage numbers remain the source of truth.",
652
+ "vision.note": "Estimated with the official rule; provider usage numbers are the source of truth.",
648
653
  "vision.sessionsTitle": "Sessions with the most images",
649
654
  "vision.sessionRow": "{images} images \xB7 \xA5{cost}",
650
655
  "vision.dailyTitle": "{window} \xB7 images per day",
@@ -653,7 +658,7 @@ var en = {
653
658
  "sessions.empty": "No sessions have incurred cost yet.",
654
659
  "sessions.detail": "{tokens} tokens \xB7 {calls} \xB7 {ago}",
655
660
  "sessions.share": "{percent}% of total",
656
- "sessions.more": "{count} sessions have usage; the {shown} most expensive are shown above.",
661
+ "sessions.more": "{count} sessions; showing the {shown} most expensive.",
657
662
  "sessions.fallbackTitle": "Session {id}",
658
663
  "models.empty": "No model usage can be classified yet.",
659
664
  "models.shareAria": "{name} accounts for {percent}% of total cost",
@@ -680,9 +685,9 @@ var en = {
680
685
  "chart.dimensionLabel": "Chart interval",
681
686
  "chart.daily": "Daily",
682
687
  "chart.hourly": "Hourly",
683
- "chart.noWindowData": "No usage in {window}. Try a longer range.",
684
- "chart.heatmapTitle": "Last year \xB7 daily usage heatmap",
685
- "chart.empty": "No usage yet. Run a conversation in DSH to see daily and hourly totals with cost estimates.",
688
+ "chart.noWindowData": "No usage in {window}.",
689
+ "chart.heatmapTitle": "Last 12 months \xB7 daily",
690
+ "chart.empty": "No usage yet.",
686
691
  "chart.hourPoint": "{hour}:00",
687
692
  "chart.tooltip": "{head} \xB7 {tokens} tokens \xB7 \xA5{cost} \xB7 {calls}",
688
693
  "chart.less": "Less",
@@ -748,37 +753,39 @@ var en = {
748
753
  "balance.overYear": "> 365 days",
749
754
  "balance.noRunway": "No usage in the last {days} days, so runway cannot be estimated",
750
755
  "balance.runwayTitle": "Based on \xA5{cost} average daily spend over the last {days} days (including idle days; today is incomplete)",
751
- "balance.empty": "Balance is unavailable. Sign in to your account or add DEEPSEEK_API_KEY under Settings \u2192 Models, then refresh.",
752
- "sessionUsage.title": "Current session spend (estimated)",
753
- "sessionUsage.cost": "This session",
754
- "sessionUsage.tokens": "Session tokens",
755
- "sessionUsage.calls": "Session calls",
756
+ "balance.empty": "Balance is unavailable. Check your sign-in or API key, then refresh.",
757
+ "sessionUsage.title": "Current session spend",
758
+ "common.estimated": "Estimated cost",
759
+ "sessionUsage.cost": "Cost",
760
+ "sessionUsage.tokens": "Tokens",
761
+ "sessionUsage.calls": "Calls",
756
762
  "sessionUsage.range": "Active {range}",
757
- "sessionUsage.empty": "No usage in this session yet. Send a message and this session\u2019s own cost and model breakdown will show up here.",
758
- "overview.title": "Spend overview (estimated)",
763
+ "sessionUsage.single": "{model} \xB7 input {input} / output {output} / cache {cache}",
764
+ "sessionUsage.empty": "No usage in this session yet.",
765
+ "overview.title": "Spend overview",
759
766
  "overview.today": "Today",
760
767
  "overview.month": "This month",
761
768
  "overview.total": "All time",
762
769
  "overview.vsYesterday": "vs yesterday",
763
770
  "overview.vsLastMonth": "vs same period last month",
764
771
  "overview.empty": "No spend to summarize yet.",
765
- "usage.trendTitle": "Usage trend (tokens)",
772
+ "usage.trendTitle": "Usage trend",
766
773
  "usage.windowInput": "{window} input",
767
774
  "usage.modelCalls": "Model calls",
768
775
  "heatmap.title": "Usage heatmap",
769
776
  "peak.title": "Peak / off-peak distribution",
770
777
  "cache.title": "Cache hits and savings",
771
- "models.title": "Model cost ranking \xB7 {window}",
772
- "sessions.title": "Session cost ranking \xB7 {window}",
778
+ "models.title": "Model cost ranking",
779
+ "sessions.title": "Session cost ranking",
773
780
  "links.title": "Quick Links",
774
781
  "links.usage": "View balance / usage",
775
782
  "links.apiKey": "Create API key",
776
783
  "links.status": "Service status",
777
784
  "settings.title": "Settings",
778
785
  "settings.widget": "Show the floating balance widget on other views",
779
- "settings.widgetHint": "The widget always hides on Usage; choose its other views below.",
786
+ "settings.widgetHint": "Hidden on Usage; shown on the views selected below.",
780
787
  "settings.widgetTabs": "Show widget on",
781
- "settings.widgetTabsHint": "This live list comes from DSH\u2019s tab registry, including tabs added by other plugins.",
788
+ "settings.widgetTabsHint": "Updates automatically as views are installed.",
782
789
  "settings.widgetTabsEmpty": "There are no configurable conversation views right now.",
783
790
  "settings.lowBalance": "Balance warning",
784
791
  "settings.lowBalanceOn": "This page and the widget turn amber below {amount} {currency}. Enter 0 to disable.",
@@ -786,6 +793,18 @@ var en = {
786
793
  "settings.monthlyBudget": "Monthly budget",
787
794
  "settings.monthlyBudgetOn": "Track a {amount} CNY budget by Beijing calendar month and forecast month-end spend. Enter 0 to disable.",
788
795
  "settings.monthlyBudgetOff": "Enter an amount above 0 to show progress and a month-end forecast.",
796
+ "settings.sidebarEntry": "Sidebar Usage entry",
797
+ "settings.sidebarEntryHint": "Add a Usage entry at the top or bottom of the sidebar; it opens the account-wide Usage page.",
798
+ "settings.sidebarEntryOff": "Off",
799
+ "settings.sidebarEntryTop": "Top",
800
+ "settings.sidebarEntryBottom": "Bottom",
801
+ "settings.tabPosition": "Usage tab position",
802
+ "settings.tabPositionHint": "Drag Usage to change its tab position.",
803
+ "settings.tabDragAria": "Drag Usage to change its tab position",
804
+ "settings.tabMoveEarlier": "Move earlier",
805
+ "settings.tabMoveLater": "Move later",
806
+ "panel.caption": "Account-wide \xB7 across all sessions",
807
+ "panel.openAria": "Open Usage",
789
808
  "widget.updatedNow": "Updated just now",
790
809
  "widget.stale": "Data from {minutes}m ago; press \u21BB to update",
791
810
  "widget.loading": "Loading\u2026",
@@ -1349,6 +1368,27 @@ var quotaViewActiveStore = {
1349
1368
  }
1350
1369
  };
1351
1370
 
1371
+ // src/client/layout-prefs.ts
1372
+ var QUOTA_PANEL_ID = "quota";
1373
+ var isPlacement = (value) => value === "off" || value === "top" || value === "bottom";
1374
+ var sidebarEntryStore = createStore("layout.sidebarEntry", isPlacement, "off");
1375
+ var isTabIndex = (value) => typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 50;
1376
+ var quotaTabIndexStore = createStore("layout.tabIndex", isTabIndex, 2);
1377
+ function clampTabIndex(index, count) {
1378
+ return Math.max(0, Math.min(index, Math.max(count, 0)));
1379
+ }
1380
+ function viewOrderForIndex(entries, index, selfId = "balance", fallback = 20) {
1381
+ const others = entries.filter((entry) => entry.options.id !== selfId);
1382
+ if (others.length === 0) return fallback;
1383
+ const clamped = clampTabIndex(index, others.length);
1384
+ const orderAt = (position) => others[position]?.options.order ?? 0;
1385
+ if (clamped === 0) return orderAt(0) - 10;
1386
+ if (clamped === others.length) return orderAt(others.length - 1) + 10;
1387
+ const previous = orderAt(clamped - 1);
1388
+ const next = orderAt(clamped);
1389
+ return previous >= next ? previous + 0.5 : (previous + next) / 2;
1390
+ }
1391
+
1352
1392
  // src/client/dashboard.tsx
1353
1393
  var import_jsx_runtime4 = require("react/jsx-runtime");
1354
1394
  var COMPOSER_FALLBACK_PX = 126;
@@ -1426,7 +1466,7 @@ function Period(props) {
1426
1466
  previous !== void 0 && compare !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Delta, { current: period.cost, previous: previous.cost, label: compare })
1427
1467
  ] }),
1428
1468
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-period-cost", children: [
1429
- "\xA5 ",
1469
+ "\xA5",
1430
1470
  fmt(period.cost)
1431
1471
  ] }),
1432
1472
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-period-sub", children: t("period.volume", {
@@ -1451,9 +1491,9 @@ function BudgetMeter(props) {
1451
1491
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-budget-head", children: [
1452
1492
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-budget-title", children: t("budget.title") }),
1453
1493
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dq-budget-amount", children: [
1454
- "\xA5 ",
1494
+ "\xA5",
1455
1495
  fmt(snapshot.spent),
1456
- " / \xA5 ",
1496
+ " / \xA5",
1457
1497
  fmt(snapshot.budget)
1458
1498
  ] })
1459
1499
  ] }),
@@ -1514,7 +1554,6 @@ function PricingNote(props) {
1514
1554
  t("pricing.unit", { currency: pricing.currency }),
1515
1555
  t("pricing.splitNote", { windows: pricing.peakWindows.join(t("common.listSeparator")) }),
1516
1556
  t("pricing.visionNote"),
1517
- t("pricing.modelNote"),
1518
1557
  t("pricing.unknown")
1519
1558
  ] })
1520
1559
  ] })
@@ -1565,6 +1604,12 @@ function CoverageDiagnostics(props) {
1565
1604
  ] })
1566
1605
  ] });
1567
1606
  }
1607
+ function CardTitle(props) {
1608
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h2", { className: `dq-card-title${props.srOnly === true ? " dq-sr-only" : ""}`, children: [
1609
+ props.children,
1610
+ props.caption !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-card-caption", children: props.caption })
1611
+ ] });
1612
+ }
1568
1613
  function Stat(props) {
1569
1614
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
1570
1615
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: props.label }),
@@ -1673,14 +1718,22 @@ function SessionUsageCard(props) {
1673
1718
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1674
1719
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-balance-grid", children: [
1675
1720
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("sessionUsage.cost"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat-value", children: [
1676
- "\xA5 ",
1721
+ "\xA5",
1677
1722
  fmt(data.cost)
1678
1723
  ] }) }),
1679
1724
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("sessionUsage.tokens"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-value", children: fmtCompact(data.total, locale) }) }),
1680
1725
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("sessionUsage.calls"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-value", children: fmtInt(data.calls) }) })
1681
1726
  ] }),
1682
- range !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-session-sub", children: t("sessionUsage.range", { range }) }),
1683
- data.models.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-rank", children: data.models.map((m, index) => {
1727
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-session-meta", children: [
1728
+ range !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-session-sub", children: t("sessionUsage.range", { range }) }),
1729
+ data.models.length === 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-session-sub", children: t("sessionUsage.single", {
1730
+ model: data.models[0].model !== "" ? data.models[0].model : t("common.unknownModel"),
1731
+ input: fmtCompact(data.models[0].input, locale),
1732
+ output: fmtCompact(data.models[0].output, locale),
1733
+ cache: fmtCompact(data.models[0].cache, locale)
1734
+ }) })
1735
+ ] }),
1736
+ data.models.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-rank", children: data.models.map((m, index) => {
1684
1737
  const key = m.provider !== "" ? `${m.provider}/${m.model}` : m.model !== "" ? m.model : `model-${index}`;
1685
1738
  const name2 = m.model !== "" ? m.model : t("common.unknownModel");
1686
1739
  const color = MODEL_COLORS[index % MODEL_COLORS.length];
@@ -1692,7 +1745,7 @@ function SessionUsageCard(props) {
1692
1745
  name2
1693
1746
  ] }),
1694
1747
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dq-rank-cost", children: [
1695
- "\xA5 ",
1748
+ "\xA5",
1696
1749
  fmt(m.cost),
1697
1750
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dq-rank-share", children: [
1698
1751
  (share * 100).toFixed(1),
@@ -1740,14 +1793,14 @@ function CacheCard(props) {
1740
1793
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
1741
1794
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("cache.saved") }),
1742
1795
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat-value", children: [
1743
- "\xA5 ",
1796
+ "\xA5",
1744
1797
  fmt(totals.cacheSavings)
1745
1798
  ] })
1746
1799
  ] }),
1747
1800
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
1748
1801
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("cache.allMiss") }),
1749
1802
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat-value dq-muted-value", children: [
1750
- "\xA5 ",
1803
+ "\xA5",
1751
1804
  fmt(wouldHaveCost)
1752
1805
  ] })
1753
1806
  ] })
@@ -1762,8 +1815,7 @@ function CacheCard(props) {
1762
1815
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-legend-swatch dq-legend-swatch--miss" }),
1763
1816
  t("cache.missLegend", { tokens: fmtCompact(totals.input, locale) })
1764
1817
  ] })
1765
- ] }),
1766
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-cache-foot", children: low ? t("cache.lowHint") : t("cache.goodHint") })
1818
+ ] })
1767
1819
  ] });
1768
1820
  }
1769
1821
  function VisionCard(props) {
@@ -1786,7 +1838,7 @@ function VisionCard(props) {
1786
1838
  tokensShare > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-vision-share", children: t("vision.tokensShare", { percent: tokensShare.toFixed(1) }) })
1787
1839
  ] }) }),
1788
1840
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("vision.cost"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat-value", children: [
1789
- "\xA5 ",
1841
+ "\xA5",
1790
1842
  fmt(vision.cost),
1791
1843
  costShare > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-vision-share", children: t("vision.costShare", { percent: costShare.toFixed(1) }) })
1792
1844
  ] }) })
@@ -1841,15 +1893,11 @@ function PeakCard(props) {
1841
1893
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-figures", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
1842
1894
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("peak.shift") }),
1843
1895
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat-value", children: [
1844
- "\xA5 ",
1896
+ "\xA5",
1845
1897
  fmt(split.offPeakEraCost),
1846
1898
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-peak-delta dq-peak-delta--save", children: t("peak.saving", { amount: fmt(shiftSaving) }) })
1847
1899
  ] })
1848
- ] }) }),
1849
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "dq-peak-foot", children: [
1850
- t("peak.schedule", { windows: pricing.peakWindows.join(t("common.listSeparator")) }),
1851
- t("peak.activeHint")
1852
- ] })
1900
+ ] }) })
1853
1901
  ] });
1854
1902
  }
1855
1903
  function agoText(ms, t) {
@@ -1898,13 +1946,13 @@ function SessionRanking(props) {
1898
1946
  },
1899
1947
  children: [
1900
1948
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("summary", { className: "dq-session-summary", children: [
1901
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-session-chevron", "aria-hidden": "true", children: "\u25B8" }),
1949
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-session-chevron", "aria-hidden": "true" }),
1902
1950
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-session-summary-body", children: [
1903
1951
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-session-head", children: [
1904
1952
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-session-title", title: `${s.title}
1905
1953
  ${s.id}`, children: s.title || t("sessions.fallbackTitle", { id: s.id }) }),
1906
1954
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dq-session-cost", children: [
1907
- "\xA5 ",
1955
+ "\xA5",
1908
1956
  fmt(s.cost)
1909
1957
  ] })
1910
1958
  ] }),
@@ -1943,7 +1991,7 @@ function ModelRanking(props) {
1943
1991
  modelNameOf(m, t)
1944
1992
  ] }),
1945
1993
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dq-rank-cost", children: [
1946
- "\xA5 ",
1994
+ "\xA5",
1947
1995
  fmt(m.cost),
1948
1996
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dq-rank-share", children: [
1949
1997
  (share * 100).toFixed(1),
@@ -2100,6 +2148,11 @@ function BalanceDashboard(props) {
2100
2148
  const [selectedModels, setSelectedModels] = (0, import_react4.useState)([]);
2101
2149
  const [windowDays, setWindowDays] = (0, import_react4.useState)(usageWindowStore.get());
2102
2150
  const [chartMetric, setChartMetric] = (0, import_react4.useState)(chartMetricStore.get());
2151
+ const [sidebarEntry, setSidebarEntry] = (0, import_react4.useState)(sidebarEntryStore.get());
2152
+ (0, import_react4.useEffect)(() => sidebarEntryStore.subscribe(() => setSidebarEntry(sidebarEntryStore.get())), []);
2153
+ const [tabDragging, setTabDragging] = (0, import_react4.useState)(false);
2154
+ const [tabDropTarget, setTabDropTarget] = (0, import_react4.useState)(null);
2155
+ const [retainedVision, setRetainedVision] = (0, import_react4.useState)(null);
2103
2156
  const [runwayHovering, setRunwayHovering] = (0, import_react4.useState)(false);
2104
2157
  const [runwayManuallyOpened, setRunwayManuallyOpened] = (0, import_react4.useState)(false);
2105
2158
  const [remainingHovering, setRemainingHovering] = (0, import_react4.useState)(false);
@@ -2272,6 +2325,71 @@ function BalanceDashboard(props) {
2272
2325
  setChartMetric(value);
2273
2326
  chartMetricStore.set(value);
2274
2327
  };
2328
+ const changeSidebarEntry = (value) => {
2329
+ setSidebarEntry(value);
2330
+ sidebarEntryStore.set(value);
2331
+ };
2332
+ const balanceTabIndex = viewTabs.findIndex((tab) => tab.id === "balance");
2333
+ const finalTabIndex = (target, after) => {
2334
+ const gap = after ? target + 1 : target;
2335
+ const dropped = balanceTabIndex >= 0 && gap > balanceTabIndex ? gap - 1 : gap;
2336
+ return Math.max(0, Math.min(dropped, Math.max(viewTabs.length - 1, 0)));
2337
+ };
2338
+ const endTabDrag = () => {
2339
+ setTabDragging(false);
2340
+ setTabDropTarget(null);
2341
+ };
2342
+ const previewTabDropTarget = (index) => {
2343
+ setTabDropTarget((prev) => prev === index ? prev : index);
2344
+ };
2345
+ const applyQuotaTabIndex = (index) => {
2346
+ const next = Math.max(0, Math.min(index, Math.max(viewTabs.length - 1, 0)));
2347
+ if (next === balanceTabIndex) return;
2348
+ quotaTabIndexStore.set(next);
2349
+ window.setTimeout(() => {
2350
+ const chip = document.querySelector(".dq-tab-chip--current");
2351
+ chip?.focus();
2352
+ }, 80);
2353
+ };
2354
+ const dropQuotaTab = (target, after) => {
2355
+ applyQuotaTabIndex(finalTabIndex(target, after));
2356
+ endTabDrag();
2357
+ };
2358
+ const onQuotaTabDragStart = (event) => {
2359
+ setTabDragging(true);
2360
+ event.dataTransfer.effectAllowed = "move";
2361
+ event.dataTransfer.setData("text/plain", "balance");
2362
+ };
2363
+ const onChipDragOver = (event, index) => {
2364
+ if (!tabDragging) return;
2365
+ event.preventDefault();
2366
+ event.stopPropagation();
2367
+ previewTabDropTarget(index);
2368
+ };
2369
+ const onChipDrop = (event, index) => {
2370
+ if (!tabDragging) return;
2371
+ event.preventDefault();
2372
+ event.stopPropagation();
2373
+ const rect = event.currentTarget.getBoundingClientRect();
2374
+ dropQuotaTab(index, event.clientX > rect.left + rect.width / 2);
2375
+ };
2376
+ const onOrderDragOver = (event) => {
2377
+ if (!tabDragging) return;
2378
+ event.preventDefault();
2379
+ if (event.target === event.currentTarget) previewTabDropTarget(viewTabs.length - 1);
2380
+ };
2381
+ const onOrderDrop = (event) => {
2382
+ if (!tabDragging) return;
2383
+ event.preventDefault();
2384
+ if (event.target !== event.currentTarget) return;
2385
+ applyQuotaTabIndex(viewTabs.length - 1);
2386
+ endTabDrag();
2387
+ };
2388
+ const onQuotaTabKeyDown = (event) => {
2389
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
2390
+ event.preventDefault();
2391
+ applyQuotaTabIndex(balanceTabIndex + (event.key === "ArrowLeft" ? -1 : 1));
2392
+ };
2275
2393
  const primary = balance !== null && balance.balances.length > 0 ? balance.balances[0] : null;
2276
2394
  const recentDays = (usage?.daily ?? []).slice(-7);
2277
2395
  const avgDailyCost = recentDays.length > 0 ? recentDays.reduce((sum, d) => sum + d.cost, 0) / recentDays.length : 0;
@@ -2282,6 +2400,26 @@ function BalanceDashboard(props) {
2282
2400
  const runwayTitle = daysLeft === null ? t("balance.noRunway", { days: recentDays.length || 7 }) : t("balance.runwayTitle", { days: recentDays.length, cost: fmt(avgDailyCost) });
2283
2401
  const activeWindow = usage?.windows.find((window2) => window2.days === windowDays) ?? null;
2284
2402
  const activeWindowName = windowName(windowDays, t);
2403
+ const visionStats = activeWindow?.vision;
2404
+ const visionOpen = usage !== null && activeWindow !== null && visionStats !== void 0 && visionStats.images > 0 && Array.isArray(activeWindow.visionDaily) && Array.isArray(activeWindow.visionSessions);
2405
+ const visionPayload = (window2, stats, label) => ({
2406
+ vision: stats,
2407
+ daily: window2.visionDaily,
2408
+ sessions: window2.visionSessions,
2409
+ windowLabel: label,
2410
+ inputTokens: window2.totals.input,
2411
+ costTotal: window2.totals.cost
2412
+ });
2413
+ const liveVision = visionOpen && activeWindow !== null && visionStats !== void 0 ? visionPayload(activeWindow, visionStats, activeWindowName) : null;
2414
+ (0, import_react4.useEffect)(() => {
2415
+ if (visionOpen && activeWindow !== null && visionStats !== void 0) {
2416
+ setRetainedVision(visionPayload(activeWindow, visionStats, activeWindowName));
2417
+ return;
2418
+ }
2419
+ const timer = window.setTimeout(() => setRetainedVision(null), 320);
2420
+ return () => window.clearTimeout(timer);
2421
+ }, [visionOpen, activeWindow, activeWindowName]);
2422
+ const shownVision = visionOpen ? liveVision : retainedVision;
2285
2423
  const dailyBars = (activeWindow?.daily ?? []).map((d) => ({
2286
2424
  label: windowDays === 365 ? d.date.slice(5).replace("-", "/") : d.date.slice(8, 10),
2287
2425
  value: chartMetricValue(chartMetric, d),
@@ -2410,8 +2548,8 @@ function BalanceDashboard(props) {
2410
2548
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { className: "dq-alert-link", href: "https://platform.deepseek.com/top_up", target: "_blank", rel: "noreferrer", children: t("alert.topUp") })
2411
2549
  ] })
2412
2550
  ] }),
2413
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--primary", children: [
2414
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("balance.title") }),
2551
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--hero", children: [
2552
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { srOnly: true, children: t("balance.title") }),
2415
2553
  primary !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-balance-grid", children: [
2416
2554
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
2417
2555
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("balance.remaining") }),
@@ -2427,14 +2565,14 @@ function BalanceDashboard(props) {
2427
2565
  "summary",
2428
2566
  {
2429
2567
  title: t("balance.breakdownTitle"),
2568
+ "aria-label": `${fmt(primary.total)} ${primary.currency}`,
2430
2569
  onClick: (e) => {
2431
2570
  e.preventDefault();
2432
2571
  setRemainingManuallyOpened((v) => !v);
2433
2572
  },
2434
2573
  children: [
2435
2574
  fmt(primary.total),
2436
- " ",
2437
- primary.currency
2575
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-hero-unit", children: primary.currency })
2438
2576
  ]
2439
2577
  }
2440
2578
  ),
@@ -2472,15 +2610,15 @@ function BalanceDashboard(props) {
2472
2610
  }
2473
2611
  )
2474
2612
  ] }),
2475
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("common.status"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `dq-stat-value${balance?.isAvailable === false ? " dq-stat-value--bad" : " dq-stat-value--ok"}`, children: balance?.isAvailable === false ? t("common.unavailable") : t("common.available") }) })
2613
+ balance?.isAvailable === false && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("common.status"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-value dq-stat-value--bad", children: t("common.unavailable") }) })
2476
2614
  ] }) : loadingBalance ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BalanceSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("balance.empty") })
2477
2615
  ] }),
2478
2616
  props.sessionId !== void 0 && props.sessionId !== "" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--primary", children: [
2479
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("sessionUsage.title") }),
2617
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: t("common.estimated"), children: t("sessionUsage.title") }),
2480
2618
  sessionUsage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SessionUsageCard, { data: sessionUsage }) : loadingSessionUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SessionUsageSkeleton, {}) : sessionUsageError !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty dq-empty--error", children: localizeApiError(sessionUsageError, t, "error.query") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("sessionUsage.empty") })
2481
2619
  ] }),
2482
2620
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--primary", children: [
2483
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("overview.title") }),
2621
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: t("common.estimated"), children: t("overview.title") }),
2484
2622
  usage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-period-grid", children: [
2485
2623
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Period, { label: t("overview.today"), period: usage.summary.today, previous: usage.summary.yesterday, compare: t("overview.vsYesterday") }),
2486
2624
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Period, { label: t("overview.month"), period: usage.summary.month, previous: usage.summary.lastMonthToDate, compare: t("overview.vsLastMonth") }),
@@ -2506,34 +2644,9 @@ function BalanceDashboard(props) {
2506
2644
  ),
2507
2645
  usage !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PricingNote, { pricing: usage.pricing })
2508
2646
  ] }),
2509
- usage !== null && activeWindow !== null && activeWindow.vision !== void 0 && Array.isArray(activeWindow.visionDaily) && Array.isArray(activeWindow.visionSessions) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2510
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("vision.title") }),
2511
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2512
- VisionCard,
2513
- {
2514
- vision: activeWindow.vision,
2515
- daily: activeWindow.visionDaily,
2516
- sessions: activeWindow.visionSessions,
2517
- windowLabel: activeWindowName,
2518
- inputTokens: activeWindow.totals.input,
2519
- costTotal: activeWindow.totals.cost
2520
- }
2521
- )
2522
- ] }) : usage === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2523
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("vision.title") }),
2524
- 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: [
2525
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w, h: 11 }),
2526
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 72, h: 20 })
2527
- ] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("vision.empty", { window: activeWindowName }) })
2528
- ] }) : (
2529
- // Host serving a pre-vision payload (e.g. not restarted yet): hide the
2530
- // whole card instead of leaving a titled shell — it reappears as soon
2531
- // as the host half serves the new shape.
2532
- null
2533
- ),
2534
2647
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--primary", children: [
2535
2648
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card-head", children: [
2536
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("usage.trendTitle") }),
2649
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("usage.trendTitle") }),
2537
2650
  usage !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card-actions", children: [
2538
2651
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UsageWindowPicker, { value: windowDays, onChange: changeWindowDays }),
2539
2652
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ExportMenu, { usage })
@@ -2585,7 +2698,7 @@ function BalanceDashboard(props) {
2585
2698
  GroupedBars,
2586
2699
  {
2587
2700
  series: grouped,
2588
- height: barMode === "daily" ? 120 : 100,
2701
+ height: barMode === "daily" ? 160 : 140,
2589
2702
  labelEvery: barMode === "daily" ? dailyLabelEvery : 3,
2590
2703
  minWidth: barMode === "daily" ? dailyMinWidth : void 0
2591
2704
  }
@@ -2594,36 +2707,33 @@ function BalanceDashboard(props) {
2594
2707
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-legend-swatch", style: { background: s.color } }),
2595
2708
  s.name
2596
2709
  ] }, s.key)) })
2597
- ] }) : barMode === "daily" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Bars, { data: dailyBars, height: 120, labelEvery: dailyLabelEvery, minWidth: dailyMinWidth }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Bars, { data: hourlyBars, height: 100, labelEvery: 3 }),
2598
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CoverageDiagnostics, { coverage: usage.coverage })
2599
- ] }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UsageSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("chart.empty") })
2600
- ] }),
2601
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2602
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("heatmap.title") }),
2603
- usage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2604
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-chart-title", children: t("chart.heatmapTitle") }),
2605
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Heatmap, { data: usage.heatmap })
2606
- ] }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeatmapSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("chart.empty") })
2607
- ] }),
2608
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2609
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("peak.title") }),
2610
- 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: [
2611
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-track" }),
2612
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-peak-figures", children: [
2613
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 120, h: 11 }),
2614
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 96, h: 20 })
2615
- ] })
2616
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("peak.empty") })
2710
+ ] }) : 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 })
2711
+ ] }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UsageSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("chart.empty") }),
2712
+ /* @__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: [
2713
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-chart-title", children: [
2714
+ t("vision.title"),
2715
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-chart-peak", children: shownVision.windowLabel })
2716
+ ] }),
2717
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2718
+ VisionCard,
2719
+ {
2720
+ vision: shownVision.vision,
2721
+ daily: shownVision.daily,
2722
+ sessions: shownVision.sessions,
2723
+ windowLabel: shownVision.windowLabel,
2724
+ inputTokens: shownVision.inputTokens,
2725
+ costTotal: shownVision.costTotal
2726
+ }
2727
+ )
2728
+ ] }) }) }),
2729
+ usage !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CoverageDiagnostics, { coverage: usage.coverage })
2617
2730
  ] }),
2618
2731
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2619
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("cache.title") }),
2620
- 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: [
2621
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w, h: 11 }),
2622
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 72, h: 20 })
2623
- ] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("cache.empty") })
2732
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: t("chart.heatmapTitle"), children: t("heatmap.title") }),
2733
+ 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") })
2624
2734
  ] }),
2625
2735
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2626
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("models.title", { window: activeWindowName }) }),
2736
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: activeWindowName, children: t("models.title") }),
2627
2737
  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: [
2628
2738
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 148, h: 13 }),
2629
2739
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-rank-track" }),
@@ -2631,15 +2741,34 @@ function BalanceDashboard(props) {
2631
2741
  ] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("models.empty") })
2632
2742
  ] }),
2633
2743
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2634
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("sessions.title", { window: activeWindowName }) }),
2744
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: activeWindowName, children: t("sessions.title") }),
2635
2745
  activeWindow !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SessionRanking, { sessions: activeWindow.sessions, count: activeWindow.sessionCount, totalCost: activeWindow.totals.cost }) : loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-sessions", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-session", children: [
2636
2746
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 200, h: 13 }),
2637
2747
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-session-track" }),
2638
2748
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 250, h: 11 })
2639
2749
  ] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("sessions.empty") })
2640
2750
  ] }),
2751
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card-pair", children: [
2752
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2753
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("peak.title") }),
2754
+ 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: [
2755
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-track" }),
2756
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-peak-figures", children: [
2757
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 120, h: 11 }),
2758
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 96, h: 20 })
2759
+ ] })
2760
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("peak.empty") })
2761
+ ] }),
2762
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2763
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("cache.title") }),
2764
+ 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: [
2765
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w, h: 11 }),
2766
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Skel, { w: 72, h: 20 })
2767
+ ] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("cache.empty") })
2768
+ ] })
2769
+ ] }),
2641
2770
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card", children: [
2642
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-card-title", children: t("settings.title") }),
2771
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("settings.title") }),
2643
2772
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-links-title", children: t("links.title") }),
2644
2773
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-links", children: [
2645
2774
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/usage", children: t("links.usage") }),
@@ -2705,9 +2834,67 @@ function BalanceDashboard(props) {
2705
2834
  ),
2706
2835
  /* @__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") })
2707
2836
  ] })
2837
+ ] }),
2838
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
2839
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-sidebar-entry", children: t("settings.sidebarEntry") }),
2840
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
2841
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2842
+ "select",
2843
+ {
2844
+ id: "dq-sidebar-entry",
2845
+ className: "dq-select",
2846
+ value: sidebarEntry,
2847
+ onChange: (e) => changeSidebarEntry(e.target.value),
2848
+ children: [
2849
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "off", children: t("settings.sidebarEntryOff") }),
2850
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "top", children: t("settings.sidebarEntryTop") }),
2851
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "bottom", children: t("settings.sidebarEntryBottom") })
2852
+ ]
2853
+ }
2854
+ ),
2855
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: t("settings.sidebarEntryHint") })
2856
+ ] })
2857
+ ] }),
2858
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
2859
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-tab-order-label", children: t("settings.tabPosition") }),
2860
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
2861
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2862
+ "div",
2863
+ {
2864
+ className: "dq-tab-order",
2865
+ role: "group",
2866
+ "aria-labelledby": "dq-tab-order-label",
2867
+ "aria-describedby": "dq-tab-order-hint",
2868
+ onDragOver: onOrderDragOver,
2869
+ onDrop: onOrderDrop,
2870
+ children: viewTabs.map((tab, index) => {
2871
+ const current = tab.id === "balance";
2872
+ const dropTarget = tabDragging && !current && tabDropTarget === index;
2873
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2874
+ "span",
2875
+ {
2876
+ className: `dq-tab-chip${current ? " dq-tab-chip--current" : ""}${current && tabDragging ? " dq-tab-chip--dragging" : ""}${dropTarget ? " dq-tab-chip--drop-target" : ""}`,
2877
+ draggable: current,
2878
+ tabIndex: current ? 0 : void 0,
2879
+ role: current ? "button" : void 0,
2880
+ "aria-label": current ? t("settings.tabDragAria") : void 0,
2881
+ onDragStart: current ? onQuotaTabDragStart : void 0,
2882
+ onDragEnd: current ? endTabDrag : void 0,
2883
+ onDragOver: (event) => onChipDragOver(event, index),
2884
+ onDrop: (event) => onChipDrop(event, index),
2885
+ onKeyDown: current ? onQuotaTabKeyDown : void 0,
2886
+ children: tab.label
2887
+ },
2888
+ tab.id
2889
+ );
2890
+ })
2891
+ }
2892
+ ),
2893
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-tab-order-hint", className: "dq-setting-hint", children: t("settings.tabPositionHint") })
2894
+ ] })
2708
2895
  ] })
2709
2896
  ] }),
2710
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.6.1" })
2897
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.8.0" })
2711
2898
  ] });
2712
2899
  }
2713
2900
 
@@ -2734,7 +2921,7 @@ function MessageCost({ sessionId, messageId }) {
2734
2921
  }, [messageId, sessionId]);
2735
2922
  const turn = data?.turns.find((candidate) => candidate.messageId === messageId);
2736
2923
  if (turn === void 0) return null;
2737
- const amount = `\xA5 ${fmtTurnCost(turn.cost)}`;
2924
+ const amount = `\xA5${fmtTurnCost(turn.cost)}`;
2738
2925
  const tooltip = t("messageCost.tooltip");
2739
2926
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2740
2927
  "span",
@@ -2753,7 +2940,7 @@ function MessageCost({ sessionId, messageId }) {
2753
2940
 
2754
2941
  // src/client/styles.ts
2755
2942
  var css = `
2756
- .dsh-quota-root{position:absolute;right:16px;bottom:16px;z-index:2147483000;pointer-events:auto;opacity:1;visibility:visible;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:13px;line-height:1.45;color:var(--dsw-alias-label-primary,#1f2328);transition:left .28s cubic-bezier(.22,1,.36,1),top .28s cubic-bezier(.22,1,.36,1),opacity .16s ease-out,visibility 0s linear}
2943
+ .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}
2757
2944
  .dsh-quota-root.dsh-quota-root--hidden{opacity:0;visibility:hidden;pointer-events:none;transition:opacity .14s ease-out,visibility 0s linear .14s}
2758
2945
  .dsh-quota-root.dsh-quota-dragging{transition:none}
2759
2946
  .dsh-quota-card,.dsh-quota-card *{box-sizing:border-box}
@@ -2762,14 +2949,14 @@ var css = `
2762
2949
  .dsh-quota-header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;min-width:0}
2763
2950
  .dsh-quota-title{font-weight:600;font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);display:flex;align-items:center;gap:6px;min-width:0;overflow:hidden}
2764
2951
  .dsh-quota-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
2765
- .dsh-quota-grip{color:var(--dsw-alias-label-secondary,#59636e);opacity:.7;font-size:11px;letter-spacing:0;cursor:grab;flex:none}
2952
+ .dsh-quota-grip{color:var(--dsw-alias-label-secondary,#59636e);opacity:.7;font-size:12px;letter-spacing:0;cursor:grab;flex:none}
2766
2953
  .dsh-quota-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-success-primary,#2da44e);flex:none}
2767
2954
  .dsh-quota-dot--error{background:var(--dsw-alias-state-error-primary,#cf222e)}
2768
2955
  .dsh-quota-dot--warn{background:var(--dsw-alias-state-warning-primary,#bf8700)}
2769
- .dsh-quota-total--low{color:var(--dsw-alias-state-warning-primary,#9a6700)!important}
2956
+ .dsh-quota-total--low{color:var(--dq-text-warn)!important}
2770
2957
  .dsh-quota-dot--idle{background:var(--dsw-alias-label-secondary,#59636e);opacity:.6}
2771
2958
  .dsh-quota-collapsed-total-row{margin-top:6px}
2772
- .dsh-quota-collapsed-total{font-weight:700;font-size:15px;color:var(--dsw-alias-state-business-primary,#0969da);font-variant-numeric:tabular-nums}
2959
+ .dsh-quota-collapsed-total{font-weight:700;font-size:13px;color:var(--dsw-alias-state-business-primary,#0969da);font-variant-numeric:tabular-nums}
2773
2960
  .dsh-quota-actions{display:flex;gap:2px;flex:none}
2774
2961
  .dsh-quota-btn{border:none;background:transparent;cursor:pointer;color:var(--dsw-alias-label-secondary,#59636e);padding:3px 7px;border-radius:6px;font-size:13px;line-height:1}
2775
2962
  .dsh-quota-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#1f2328)}
@@ -2777,19 +2964,19 @@ var css = `
2777
2964
  .dsh-quota-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
2778
2965
  .dsh-quota-btn:disabled{opacity:.5;cursor:default}
2779
2966
  .dsh-quota-body{color:var(--dsw-alias-label-secondary,#59636e)}
2780
- .dsh-quota-remaining-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}
2781
- .dsh-quota-total{font-size:24px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
2782
- .dsh-quota-currency{font-size:14px;font-weight:600;margin-left:3px;color:var(--dsw-alias-label-secondary,#59636e)}
2967
+ .dsh-quota-remaining-label{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);margin-bottom:2px}
2968
+ .dsh-quota-total{font-size:18px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328);letter-spacing:-.02em;line-height:1.2;font-variant-numeric:tabular-nums}
2969
+ .dsh-quota-currency{font-size:13px;font-weight:500;margin-left:4px;color:var(--dsw-alias-label-secondary,#59636e)}
2783
2970
  .dsh-quota-row{display:flex;justify-content:space-between;gap:12px;margin-top:6px;font-size:12px}
2784
2971
  .dsh-quota-label{color:var(--dsw-alias-label-secondary,#59636e)}
2785
2972
  .dsh-quota-value{color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
2786
2973
  .dsh-quota-error{color:var(--dsw-alias-state-error-primary,#cf222e)}
2787
- .dq-message-cost{position:relative;display:inline-flex;align-items:center;order:10;gap:10px;height:28px;color:var(--dsw-alias-label-tertiary,#59636e);font-size:12px;line-height:20px;font-variant-numeric:tabular-nums;white-space:nowrap;cursor:help}
2974
+ .dq-message-cost{-webkit-text-size-adjust:100%;text-size-adjust:100%;position:relative;display:inline-flex;align-items:center;order:10;gap:10px;height:28px;color:var(--dsw-alias-label-tertiary,#59636e);font-size:12px;line-height:20px;font-variant-numeric:tabular-nums;white-space:nowrap;cursor:help}
2788
2975
  .dq-message-cost-separator{cursor:default}
2789
2976
  .dq-message-cost-amount{text-decoration-line:underline;text-decoration-style:dotted;text-decoration-color:currentColor;text-underline-offset:4px}
2790
2977
  .dq-message-cost:hover{color:var(--dsw-alias-label-secondary,#3f4852)}
2791
2978
  .dq-message-cost:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px;border-radius:4px}
2792
- .dq-message-cost::after{content:attr(data-dq-tooltip);position:absolute;left:0;top:calc(100% + 6px);z-index:2147483001;width:max-content;max-width:min(240px,calc(100vw - 24px));padding:6px 9px;border-radius:8px;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);box-shadow:0 6px 18px rgba(0,0,0,.22);font-size:12px;font-weight:400;line-height:1.45;text-align:left;white-space:normal;pointer-events:none;opacity:0;visibility:hidden;transform:translateY(-3px);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear .12s}
2979
+ .dq-message-cost::after{content:attr(data-dq-tooltip);position:absolute;left:0;top:calc(100% + 6px);z-index:2147483001;width:max-content;max-width:min(240px,calc(100vw - 24px));padding:6px 9px;border-radius:8px;background:var(--dsw-alias-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}
2793
2980
  .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}
2794
2981
  @media (hover:hover){
2795
2982
  [data-time-hover-root] .dq-message-cost{opacity:0;transition:opacity 80ms ease}
@@ -2803,13 +2990,16 @@ var css = `
2803
2990
  deliberately), but on narrower viewports the fixed 860px would overhang
2804
2991
  the input box \u2014 measured in dashboard.tsx as --dq-composer-w, capped at
2805
2992
  the reading-column width so wide viewports are unchanged. */
2806
- .dq-balance{display:flex;flex-direction:column;gap:24px;padding:20px 24px 48px;max-width:min(860px,var(--dq-composer-w,860px));margin:0 auto;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}
2807
- @media (max-width:620px){.dq-balance{box-sizing:border-box;max-width:calc(100vw - 56px);padding:16px 16px calc(var(--dq-composer-h,126px) + 16px)}}
2808
- .dq-card{background:var(--dsw-alias-bg-layer-1,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-radius:12px;padding:16px;min-width:0;max-width:100%}
2809
- .dq-card-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:0 0 12px;letter-spacing:.02em}
2810
- .dq-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}
2993
+ .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%);}
2994
+ @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}}
2995
+ /* \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
2996
+ \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 */
2997
+ .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}
2998
+ .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}
2999
+ .dq-card-caption{font-size:12px;font-weight:400;color:var(--dsw-alias-label-secondary,#59636e)}
3000
+ .dq-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px}
2811
3001
  .dq-card-head .dq-card-title{margin:0}
2812
- .dq-card-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;min-width:0}
3002
+ .dq-card-actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px;min-width:0}
2813
3003
  /* \u4E3B\u5361\u7247\uFF08\u4F59\u989D/\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017/\u6D88\u8017\u6982\u89C8/\u7528\u91CF\u8D8B\u52BF\uFF0C\u8F6E\u6B21 45 \u65B0\u589E\u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300D\uFF09\u89C6\u89C9\u6743\u91CD\u9AD8\u4E8E\u6B21\u7EA7\u5206\u6790
2814
3004
  \u5361\u7247\u4E0E\u5BFC\u822A\u7C7B\u5361\u7247\uFF1A\u53EA\u8C03\u6392\u7248\u5C5E\u6027\u2014\u2014\u5185\u8FB9\u8DDD\u3001\u6807\u9898\u5B57\u53F7\u5B57\u91CD\u4E0E\u53D6\u8272\u3001\u8FB9\u6846\u6DF1\u6D45\u2014\u2014\u4E0D\u5F15\u5165\u65B0\u914D\u8272\u4F53\u7CFB\u3002\u4E24\u6761\u89C4\u5219
2815
3005
  \u4E0E\u4E0A\u9762\u7684\u57FA\u7840\u89C4\u5219\u540C\u4E3A\u5355\u7C7B\u9009\u62E9\u5668\u4E14\u6E90\u7801\u987A\u5E8F\u66F4\u9760\u540E\uFF0C\u5929\u7136\u8986\u76D6\uFF0C\u4E0D\u9700\u8981 !important\u3002\u6B21\u7EA7\u5206\u6790\u5361\u7247\uFF08\u7528\u91CF
@@ -2818,8 +3008,18 @@ var css = `
2818
3008
  \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
2819
3009
  \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
2820
3010
  primary \u6743\u91CD\uFF0C\u4E0D\u65B0\u5F00\u7B2C\u4E09\u6863\u3002 */
2821
- .dq-card--primary{padding:20px;border-color:var(--dsw-alias-border-l2,rgba(0,0,0,.12))}
2822
- .dq-card--primary .dq-card-title{font-size:13px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328)}
3011
+ .dq-card--primary{padding:20px;border-color:var(--dsw-alias-border-l3,rgba(0,0,0,.12))}
3012
+ /* \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 */
3013
+ .dq-card--hero{padding:0 20px;background:transparent;border:0;border-radius:0}
3014
+ .dq-card-pair{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(340px,100%),1fr));gap:24px;min-width:0}
3015
+ .dq-card--hero .dq-stat-label{font-size:13px;margin-bottom:2px}
3016
+ .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}
3017
+ .dq-card--hero .dq-balance-grid{align-items:stretch;gap:12px 48px}
3018
+ .dq-card--hero .dq-stat{display:flex;flex-direction:column;justify-content:space-between}
3019
+ /* 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 */
3020
+ .dq-card--hero details>summary{text-decoration:none;margin:-2px -6px;padding:2px 6px;border-radius:8px;transition:background .15s ease}
3021
+ .dq-card--hero details>summary:hover,.dq-card--hero details[open]>summary{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.05))}
3022
+ @media (max-width:620px){.dq-card{padding:16px}.dq-card--hero{padding:0 16px}}
2823
3023
  .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))}
2824
3024
  .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}
2825
3025
  .dq-window-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}
@@ -2834,7 +3034,7 @@ var css = `
2834
3034
  .dq-export-menu button{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;border:0;border-radius:6px;padding:7px 8px;background:transparent;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;text-align:left;cursor:pointer}
2835
3035
  .dq-export-menu button:hover,.dq-export-menu button:focus-visible{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06))}
2836
3036
  .dq-export-menu button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-1px}
2837
- .dq-export-menu small{font-size:10.5px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap}
3037
+ .dq-export-menu small{font-size:12px;font-weight:400;color:var(--dsw-alias-label-secondary,#59636e);white-space:nowrap}
2838
3038
  /* Small-text tier system (\u8F6E\u6B21 41 \u6536\u655B): two whole-pixel tiers replace what used to be four values
2839
3039
  (11/11.5/12/12.5px) spaced only 0.5px apart \u2014 a gap browsers subpixel-round away, so it read as one
2840
3040
  size with extra maintenance cost, not an actual visual hierarchy. 11px = "micro" chrome the user
@@ -2850,9 +3050,9 @@ var css = `
2850
3050
  it belongs in an already-established different tier (10px icon glyphs, 13px+ hero-adjacent text). */
2851
3051
  .dq-balance-grid{display:flex;flex-wrap:wrap;gap:12px 24px}
2852
3052
  .dq-stat{min-width:110px}
2853
- .dq-stat-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}
2854
- .dq-stat-value{font-size:18px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
2855
- .dq-stat-value--ok{color:var(--dsw-alias-state-success-primary,#2da44e)}
3053
+ .dq-stat-label{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);margin-bottom:4px}
3054
+ .dq-stat-value{font-size:18px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
3055
+ .dq-stat-value--ok{color:var(--dq-text-success)}
2856
3056
  .dq-stat-value--bad{color:var(--dsw-alias-state-error-primary,#cf222e)}
2857
3057
  /* \u8F6E\u6B21 43: .dq-runway is a controlled <details> (see dashboard.tsx) \u2014 hover/toggle state
2858
3058
  drives the real open attribute now, so .dq-runway-detail only needs to react to [open];
@@ -2862,120 +3062,124 @@ var css = `
2862
3062
  .dq-runway>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}
2863
3063
  .dq-runway>summary::-webkit-details-marker{display:none}
2864
3064
  .dq-runway>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}
2865
- .dq-runway-detail{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;max-width:240px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:normal;font-size:12px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}
3065
+ .dq-runway-detail{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;max-width:240px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:normal;font-size:12px;font-weight:500;line-height:1.6;color:var(--dsw-alias-label-primary,#1f2328)}
2866
3066
  .dq-runway[open] .dq-runway-detail{display:block}
2867
3067
  /* hero number for the balance, matching .dq-period-cost; relies on cascade order (declared after
2868
3068
  .dq-stat-value, same specificity) to win over the .dq-stat-value it's paired with in JSX \u2014 keep it below */
2869
3069
  /* \u8F6E\u6B21 47: .dq-remaining is a controlled <details> too now (see dashboard.tsx), same reason as
2870
3070
  .dq-runway above \u2014 an unopened <details>'s non-summary children don't actually render in current
2871
3071
  Chromium regardless of author display value, so the old :hover{display:flex} branch was dead code. */
2872
- .dq-remaining{position:relative;font-size:22px;font-weight:680;letter-spacing:-.01em;line-height:1.25}
3072
+ .dq-remaining{position:relative;font-size:32px;font-weight:700;letter-spacing:-.025em;line-height:1.1}
3073
+ .dq-hero-unit{font-size:13px;font-weight:500;letter-spacing:0;margin-left:4px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}
2873
3074
  .dq-remaining>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}
2874
3075
  .dq-remaining>summary::-webkit-details-marker{display:none}
2875
3076
  .dq-remaining>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}
2876
- .dq-remaining-breakdown{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;flex-direction:column;gap:4px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:nowrap;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328)}
3077
+ .dq-remaining-breakdown{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;flex-direction:column;gap:4px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:nowrap;font-size:12px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}
2877
3078
  .dq-remaining[open] .dq-remaining-breakdown{display:flex}
2878
- .dq-remaining-granted{color:var(--dsw-alias-label-tertiary,#59636e);font-weight:400;font-size:11px}
3079
+ .dq-remaining-granted{color:var(--dsw-alias-label-secondary,#59636e);font-weight:400;font-size:12px}
2879
3080
  /* narrow screens: .dq-balance-grid wraps its 3 stats onto separate lines, so the absolutely-positioned
2880
3081
  .dq-remaining-breakdown/.dq-runway-detail popovers can float down over whichever stat now sits in the
2881
3082
  next row (e.g. "\u4F59\u989D\u660E\u7EC6" open covering "\u9884\u8BA1\u53EF\u7528"'s clickable summary) \u2014 force each stat onto its own
2882
3083
  full-width row and switch both popovers to normal in-flow layout so an open one pushes its own row
2883
3084
  taller instead of overlaying a sibling; desktop keeps the original hover-overlay behavior unchanged */
2884
3085
  @media (max-width:620px){
2885
- .dq-balance-grid .dq-stat{flex:1 1 100%}
3086
+ .dq-card--hero .dq-balance-grid{gap:12px 32px}
3087
+ .dq-card--primary .dq-balance-grid,.dq-session-expand .dq-balance-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px 16px}
3088
+ .dq-card--primary .dq-balance-grid .dq-stat,.dq-session-expand .dq-balance-grid .dq-stat{min-width:0}
2886
3089
  .dq-remaining-breakdown,.dq-runway-detail{position:static;margin-top:6px;box-shadow:none;max-width:none;white-space:normal}
2887
3090
  }
2888
- .dq-period-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0}
2889
- .dq-period{min-width:0;padding:4px 16px}
2890
- .dq-period:first-child{padding-left:0}
2891
- .dq-period:last-child{padding-right:0}
2892
- .dq-period+.dq-period{border-left:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
2893
- @media (max-width:620px){.dq-period-grid{grid-template-columns:1fr}.dq-period,.dq-period:first-child,.dq-period:last-child{padding:12px 0}.dq-period:first-child{padding-top:2px}.dq-period+.dq-period{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-left:0}}
2894
- .dq-period-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:4px}
2895
- .dq-period-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}
2896
- .dq-period-cost{font-size:22px;font-weight:680;letter-spacing:-.01em;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;line-height:1.25}
2897
- .dq-period-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);margin-top:2px;font-variant-numeric:tabular-nums;line-height:1.45}
2898
- .dq-budget{margin-top:12px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
3091
+ .dq-period-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px 32px}
3092
+ .dq-period{min-width:0}
3093
+ /* \u7A84\u5C4F\uFF1A\u4E09\u4E2A\u5468\u671F\u6539\u6210\u5217\u8868\u884C\u2014\u2014\u6807\u7B7E\u4E0E\u6DA8\u8DCC\u5728\u5DE6\u3001\u91D1\u989D\u53F3\u5BF9\u9F50\u3001\u660E\u7EC6\u5360\u7B2C\u4E8C\u884C\uFF0C\u4E09\u6BB5\u53EA\u5360\u7EA6 150px\u3002 */
3094
+ @media (max-width:620px){.dq-period-grid{grid-template-columns:1fr;gap:16px}.dq-period{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:12px;align-items:center}.dq-period-head{margin:0}.dq-period-cost{grid-column:2;grid-row:1}.dq-period-sub{grid-column:1/-1;margin-top:2px}}
3095
+ .dq-period-head{display:flex;align-items:center;flex-wrap:wrap;gap:4px 8px;margin-bottom:8px}
3096
+ .dq-period-label{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
3097
+ .dq-period-cost{font-size:18px;font-weight:700;letter-spacing:-.01em;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;line-height:1.2}
3098
+ .dq-period-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);margin-top:4px;font-variant-numeric:tabular-nums;line-height:1.5}
3099
+ .dq-budget{margin-top:24px}
2899
3100
  .dq-budget--unset{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
2900
3101
  .dq-budget-action{border:0;background:transparent;padding:3px 4px;border-radius:5px;color:var(--dsw-alias-state-business-primary,#0969da);font:inherit;font-weight:600;white-space:nowrap;cursor:pointer}
2901
3102
  .dq-budget-action:hover{text-decoration:underline;text-underline-offset:3px}
2902
3103
  .dq-budget-action:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
2903
3104
  .dq-budget-head,.dq-budget-meta{display:flex;align-items:center;justify-content:space-between;gap:12px}
2904
- .dq-budget-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}
2905
- .dq-budget-amount{font-size:12px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
3105
+ .dq-budget-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}
3106
+ .dq-budget-amount{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
2906
3107
  .dq-budget-track{height:8px;margin-top:8px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}
2907
3108
  .dq-budget-fill{width:100%;height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transform-origin:left center;transition:transform .35s cubic-bezier(.22,1,.36,1)}
2908
3109
  .dq-budget--risk .dq-budget-fill{background:var(--dsw-alias-state-warning-primary,#bf8700)}
2909
3110
  .dq-budget--over .dq-budget-fill{background:var(--dsw-alias-state-error-primary,#cf222e)}
2910
3111
  .dq-budget-meta{margin-top:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}
2911
- .dq-budget--risk .dq-budget-meta span:last-child{color:var(--dsw-alias-state-warning-primary,#9a6700);font-weight:600}
3112
+ .dq-budget--risk .dq-budget-meta span:last-child{color:var(--dq-text-warn);font-weight:600}
2912
3113
  .dq-budget--over .dq-budget-meta{color:var(--dsw-alias-state-error-primary,#cf222e);font-weight:600}
2913
- @media (max-width:620px){.dq-budget-head,.dq-budget-meta,.dq-budget--unset{align-items:flex-start;flex-direction:column;gap:4px}}
2914
- .dq-delta{display:inline-flex;align-items:center;gap:3px;font-size:11px;font-weight:600;padding:1px 6px;border-radius:999px;white-space:nowrap;cursor:help;font-variant-numeric:tabular-nums}
2915
- .dq-delta--up{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}
2916
- .dq-delta--down{color:var(--dsw-alias-state-success-primary,#1a7f37);background:rgba(45,164,78,.14)}
2917
- .dq-delta--flat,.dq-delta--new{color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14);font-weight:500}
3114
+ @media (max-width:620px){.dq-budget-head,.dq-budget-meta,.dq-budget--unset{align-items:flex-start;flex-direction:column;gap:4px}.dq-budget-action{margin-left:-4px}}
3115
+ .dq-delta{display:inline-flex;align-items:center;gap:3px;font-size:12px;font-weight:600;padding:1px 6px;border-radius:999px;white-space:nowrap;cursor:help;font-variant-numeric:tabular-nums}
3116
+ .dq-delta--up{color:var(--dq-text-warn);background:rgba(191,135,0,.14)}
3117
+ .dq-delta--down{color:var(--dq-text-success);background:rgba(45,164,78,.14)}
3118
+ .dq-delta--flat,.dq-delta--new{color:var(--dsw-alias-label-secondary,#59636e);background:rgba(120,130,150,.14);font-weight:500}
2918
3119
  .dq-delta-label{font-weight:400;opacity:.8}
2919
- .dq-pricing{margin-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-top:10px}
3120
+ .dq-pricing{margin-top:16px}
2920
3121
  .dq-pricing-summary{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);cursor:pointer;list-style:none;width:fit-content;border-radius:6px;padding:2px 4px;margin-left:-4px}
2921
3122
  .dq-pricing-summary::-webkit-details-marker{display:none}
2922
- .dq-pricing-summary::before{content:"\u25B8";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}
3123
+ .dq-pricing-summary::before{content:"";display:inline-block;flex:none;width:14px;height:14px;background:currentColor;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5'/%3E%3C/svg%3E") center/contain no-repeat;transition:transform .15s ease}
2923
3124
  .dq-pricing[open] .dq-pricing-summary::before{transform:rotate(90deg)}
2924
3125
  .dq-pricing-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}
2925
3126
  .dq-pricing-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
2926
- .dq-pricing-now{font-size:11px;font-weight:600;padding:1px 7px;border-radius:999px;color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14)}
2927
- .dq-pricing-now--peak{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}
2928
- .dq-pricing-body{margin-top:10px;overflow-x:auto}
3127
+ .dq-pricing-now{font-size:12px;font-weight:600;padding:1px 7px;border-radius:999px;color:var(--dsw-alias-label-secondary,#59636e);background:rgba(120,130,150,.14)}
3128
+ .dq-pricing-now--peak{color:var(--dq-text-warn);background:rgba(191,135,0,.14)}
3129
+ .dq-pricing-body{margin-top:8px;overflow-x:auto}
2929
3130
  .dq-pricing-table{border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums;min-width:100%}
2930
- .dq-pricing-table th{text-align:left;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);padding:4px 12px 4px 0;white-space:nowrap;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
3131
+ .dq-pricing-table th{text-align:left;font-weight:500;color:var(--dsw-alias-label-secondary,#59636e);padding:4px 12px 4px 0;white-space:nowrap;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
2931
3132
  .dq-pricing-table td{padding:4px 12px 4px 0;color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;vertical-align:top}
3133
+ .dq-pricing-table th:nth-child(n+3),.dq-pricing-table td:nth-child(n+3){text-align:right}
3134
+ .dq-pricing-table th:last-child,.dq-pricing-table td:last-child{padding-right:0}
3135
+ .dq-pricing-table tbody td:first-child{font-weight:500}
2932
3136
  .dq-pricing-table tbody tr+tr td{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.05))}
2933
- .dq-pricing-foot{margin:8px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}
2934
- .dq-coverage{margin-top:16px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
2935
- .dq-coverage-summary{display:flex;align-items:center;flex-wrap:wrap;gap:6px 9px;cursor:pointer;list-style:none;width:fit-content;max-width:100%;margin-left:-4px;padding:3px 4px;border-radius:6px;color:var(--dsw-alias-label-secondary,#59636e)}
3137
+ .dq-pricing-foot{margin:8px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e);text-wrap:pretty}
3138
+ .dq-coverage{margin-top:24px}
3139
+ .dq-coverage-summary{display:flex;align-items:center;flex-wrap:wrap;gap:4px 8px;cursor:pointer;list-style:none;width:fit-content;max-width:100%;margin-left:-4px;padding:3px 4px;border-radius:6px;color:var(--dsw-alias-label-secondary,#59636e)}
2936
3140
  .dq-coverage-summary::-webkit-details-marker{display:none}
2937
- .dq-coverage-summary::before{content:"\u25B8";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}
3141
+ .dq-coverage-summary::before{content:"";display:inline-block;flex:none;width:14px;height:14px;background:currentColor;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5'/%3E%3C/svg%3E") center/contain no-repeat;transition:transform .15s ease}
2938
3142
  .dq-coverage[open] .dq-coverage-summary::before{transform:rotate(90deg)}
2939
3143
  .dq-coverage-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}
2940
3144
  .dq-coverage-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
2941
- .dq-coverage-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}
2942
- .dq-coverage-status{padding:1px 7px;border-radius:999px;background:rgba(45,164,78,.14);color:var(--dsw-alias-state-success-primary,#1a7f37);font-size:11px;font-weight:600}
2943
- .dq-coverage--warn .dq-coverage-status{background:rgba(191,135,0,.14);color:var(--dsw-alias-state-warning-primary,#9a6700)}
3145
+ .dq-coverage-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e)}
3146
+ .dq-coverage-status{padding:1px 7px;border-radius:999px;background:rgba(45,164,78,.14);color:var(--dq-text-success);font-size:12px;font-weight:600}
3147
+ .dq-coverage--warn .dq-coverage-status{background:rgba(191,135,0,.14);color:var(--dq-text-warn)}
2944
3148
  .dq-coverage-brief{font-size:12px;font-variant-numeric:tabular-nums}
2945
- .dq-coverage-body{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}
2946
- .dq-coverage-metrics{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 20px;margin:0}
3149
+ .dq-coverage-body{margin-top:8px;padding:12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}
3150
+ .dq-coverage-metrics{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px 24px;margin:0}
2947
3151
  .dq-coverage-metrics div{min-width:0}
2948
- .dq-coverage-metrics dt{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}
2949
- .dq-coverage-metrics dd{margin:3px 0 0;font-size:13px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
2950
- .dq-coverage-range,.dq-coverage-note,.dq-coverage-warning{margin:9px 0 0;font-size:12px;line-height:1.55;color:var(--dsw-alias-label-secondary,#59636e)}
3152
+ .dq-coverage-metrics dt{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
3153
+ .dq-coverage-metrics dd{margin:3px 0 0;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
3154
+ .dq-coverage-range,.dq-coverage-note,.dq-coverage-warning{margin:8px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e);text-wrap:pretty}
2951
3155
  .dq-coverage-range{font-variant-numeric:tabular-nums}
2952
- .dq-coverage-warning{padding:8px 10px;border-radius:8px;background:rgba(191,135,0,.10);color:var(--dsw-alias-state-warning-primary,#7a5200)}
3156
+ .dq-coverage-warning{padding:8px 10px;border-radius:8px;background:rgba(191,135,0,.10);color:var(--dq-text-warn)}
2953
3157
  @media (max-width:620px){.dq-coverage-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.dq-coverage-brief{flex-basis:100%;margin-left:19px}}
2954
3158
  .dq-peak-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85;overflow:hidden}
2955
3159
  .dq-peak-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);transition:width .35s cubic-bezier(.22,1,.36,1)}
2956
- .dq-peak-legend{display:flex;flex-wrap:wrap;gap:6px 20px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}
3160
+ .dq-peak-legend{display:flex;flex-wrap:wrap;gap:4px 16px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}
2957
3161
  .dq-peak-legend span{display:inline-flex;align-items:center;gap:6px}
2958
3162
  .dq-legend-swatch--peak{background:var(--dsw-alias-state-warning-primary,#bf8700)}
2959
3163
  .dq-legend-swatch--offpeak{background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85}
2960
- .dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:14px}
2961
- .dq-peak-delta{font-size:11px;font-weight:500;margin-left:8px;color:var(--dsw-alias-state-warning-primary,#9a6700)}
2962
- .dq-peak-delta--save{color:var(--dsw-alias-state-success-primary,#1a7f37)}
2963
- .dq-peak-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}
3164
+ .dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:16px}
3165
+ .dq-peak-delta{font-size:12px;font-weight:500;margin-left:8px;color:var(--dq-text-warn)}
3166
+ .dq-peak-delta--save{color:var(--dq-text-success)}
2964
3167
  .dq-cache-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-bottom:12px}
2965
- .dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:550}
2966
- .dq-stat-value--warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}
3168
+ @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}}
3169
+ .dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:500}
3170
+ .dq-stat-value--warn{color:var(--dq-text-warn)}
2967
3171
  .dq-cache-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);overflow:hidden}
2968
3172
  .dq-cache-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transition:width .35s cubic-bezier(.22,1,.36,1)}
2969
- .dq-cache-legend{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}
3173
+ .dq-cache-legend{display:flex;flex-wrap:wrap;gap:4px 16px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}
2970
3174
  .dq-cache-legend span{display:inline-flex;align-items:center;gap:6px}
2971
3175
  .dq-legend-swatch--hit{background:var(--dsw-alias-state-success-primary,#2da44e)}
2972
3176
  .dq-legend-swatch--miss{background:var(--dsw-alias-state-warning-primary,#bf8700)}
2973
- .dq-cache-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}
2974
- .dq-sessions{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:14px}
2975
- .dq-session{display:flex;flex-direction:column;gap:5px;min-width:0}
3177
+ .dq-cache-foot{margin:12px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e);text-wrap:pretty}
3178
+ .dq-sessions{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:16px}
3179
+ .dq-session{display:flex;flex-direction:column;gap:4px;min-width:0}
2976
3180
  .dq-session-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
2977
- .dq-session-title{font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
2978
- .dq-session-cost{font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}
3181
+ .dq-session-title{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
3182
+ .dq-session-cost{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}
2979
3183
  .dq-session-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}
2980
3184
  .dq-session-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-business-primary,#4176e6);transition:width .35s cubic-bezier(.22,1,.36,1)}
2981
3185
  .dq-session-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}
@@ -2983,8 +3187,10 @@ var css = `
2983
3187
  to lean on, unlike the .dq-session usage above), so it needs its own
2984
3188
  explicit spacing rather than falling back to the browser's implicit <p>
2985
3189
  margin \u2014 this project always declares spacing explicitly. */
2986
- p.dq-session-sub{margin:8px 0}
2987
- .dq-session-foot{margin:12px 0 0;font-size:12px;color:var(--dsw-alias-label-tertiary,#59636e)}
3190
+ .dq-session-meta{margin:12px 0 0;display:flex;flex-direction:column;gap:2px}
3191
+ .dq-session-meta p.dq-session-sub{margin:0}
3192
+ .dq-session-meta+.dq-rank{margin-top:16px}
3193
+ .dq-session-foot{margin:12px 0 0;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
2988
3194
  /* \u8F6E\u6B21 46: each ranking row is now a <details> whose <summary> wraps the existing
2989
3195
  head/track/sub trio unchanged (in a flex-column body div) plus a leading chevron \u2014
2990
3196
  same disclosure grammar as .dq-pricing/.dq-coverage above (hidden native marker,
@@ -2997,60 +3203,63 @@ p.dq-session-sub{margin:8px 0}
2997
3203
  .dq-session-summary{display:flex;align-items:flex-start;gap:8px;border-radius:8px;padding:4px;margin:-4px}
2998
3204
  .dq-session-summary:hover{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.05))}
2999
3205
  .dq-session-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
3000
- .dq-session-chevron{flex:none;width:12px;margin-top:4px;font-size:10px;line-height:1;color:var(--dsw-alias-label-tertiary,#59636e);transition:transform .15s ease}
3206
+ /* \u5C55\u5F00\u7BAD\u5934\uFF1A14px \u7EBF\u6761\u56FE\u6807\uFF08mask \u53D6 currentColor\uFF09\uFF0C\u66FF\u4EE3\u539F\u6765 10px \u7684\u300C\u25B8\u300D\u5B57\u7B26\u2014\u2014\u90A3\u4E2A\u5B57\u5F62\u592A\u5C0F\u3001\u592A\u6D45\uFF0C\u70B9\u51FB\u76EE\u6807\u4E5F\u96BE\u627E\u3002 */
3207
+ .dq-session-chevron{display:inline-block;flex:none;width:14px;height:14px;background:currentColor;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5'/%3E%3C/svg%3E") center/contain no-repeat;transition:transform .15s ease;margin-top:3px;color:var(--dsw-alias-label-secondary,#59636e)}
3001
3208
  .dq-session-details[open] .dq-session-chevron{transform:rotate(90deg)}
3002
- .dq-session-summary-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}
3209
+ .dq-session-summary-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
3003
3210
  /* Expanded body reuses the same shaded-inset language as .dq-coverage-body (10px
3004
3211
  gap from the summary, 11px/12px padding, 10px radius, bg-layer-2) rather than
3005
3212
  inventing a new "nested panel" look. SessionUsageCard renders inside unchanged. */
3006
- .dq-session-expand{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}
3007
- .dq-rank{display:flex;flex-direction:column;gap:14px}
3008
- .dq-rank-row{display:flex;flex-direction:column;gap:5px;min-width:0}
3213
+ .dq-session-expand{margin-top:8px;padding:12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}
3214
+ .dq-rank{display:flex;flex-direction:column;gap:16px}
3215
+ .dq-rank-row{display:flex;flex-direction:column;gap:4px;min-width:0}
3009
3216
  .dq-rank-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
3010
- .dq-rank-name{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
3011
- .dq-rank-cost{display:inline-flex;align-items:baseline;gap:8px;font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}
3012
- .dq-rank-share{font-size:11px;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);min-width:30px;text-align:right}
3217
+ .dq-rank-name{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
3218
+ .dq-rank-cost{display:inline-flex;align-items:baseline;gap:8px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}
3219
+ .dq-rank-share{font-size:12px;font-weight:500;color:var(--dsw-alias-label-secondary,#59636e);min-width:30px;text-align:right}
3013
3220
  .dq-rank-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}
3014
3221
  .dq-rank-fill{height:100%;border-radius:999px;transition:width .35s cubic-bezier(.22,1,.36,1)}
3015
3222
  .dq-rank-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}
3016
- .dq-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;font-size:12px;line-height:1.6;color:var(--dsw-alias-state-warning-primary,#7a5200);background:rgba(191,135,0,.12);border:1px solid rgba(191,135,0,.35)}
3223
+ .dq-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;font-size:12px;line-height:1.6;color:var(--dq-text-warn);background:rgba(191,135,0,.12);border:1px solid rgba(191,135,0,.35);text-wrap:pretty}
3017
3224
  .dq-alert-icon{flex:none;width:18px;height:18px;border-radius:50%;background:var(--dsw-alias-state-warning-primary,#bf8700);color:#fff;font-weight:700;font-size:12px;line-height:18px;text-align:center}
3018
- .dq-alert strong{font-weight:650}
3225
+ .dq-alert strong{font-weight:600}
3019
3226
  .dq-alert-link{margin-left:8px;color:inherit;text-decoration:underline;text-underline-offset:2px;white-space:nowrap}
3020
- .dq-setting{display:flex;align-items:flex-start;gap:12px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
3021
- .dq-setting-label{font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}
3227
+ .dq-setting{display:flex;align-items:flex-start;gap:16px;margin-top:24px}
3228
+ @media (max-width:620px){.dq-setting{flex-direction:column;gap:8px}.dq-setting-label{width:auto;padding-top:0}}
3229
+ .dq-setting-label{width:128px;font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}
3022
3230
  .dq-setting-control{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
3023
3231
  .dq-setting-hint{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);line-height:1.5}
3024
3232
  /* Page footer: the package name + version, injected at build time from
3025
3233
  package.json (see build.mjs) so it can never drift from the release. */
3026
- .dq-footer{margin:18px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}
3234
+ .dq-footer{margin:16px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}
3027
3235
  .dq-number{width:82px;padding:5px 8px;font-size:13px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
3028
3236
  .dq-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;border-color:transparent}
3029
- .dq-links-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 8px}
3237
+ .dq-links-title{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 12px}
3030
3238
  .dq-links{display:flex;flex-wrap:wrap;gap:8px}
3031
- .dq-link{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-state-business-primary,#0969da);text-decoration:none;font-size:13px;font-weight:500;cursor:pointer}
3032
- .dq-link:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.04))}
3239
+ .dq-link{display:inline-flex;align-items:center;gap:4px;padding:8px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);text-decoration:none;font-size:13px;font-weight:500;cursor:pointer}
3240
+ .dq-link::after{content:"\u2197";font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
3241
+ .dq-link:hover{border-color:var(--dsw-alias-label-secondary,#59636e);background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.04))}
3033
3242
  /* .dq-toggle is the first settings control after the links row (\u8F6E\u6B21 39 merge) \u2014 its own
3034
3243
  border-top separates the "\u5FEB\u6377\u5165\u53E3" links (originally the standalone \u5B98\u65B9\u5E73\u53F0 card, renamed
3035
3244
  in \u8F6E\u6B21 39 fix-round-1 to read as part of \u8BBE\u7F6E's own navigation area) from \u8BBE\u7F6E controls
3036
3245
  inside the merged card, same 14px/14px/border-l1 recipe as .dq-setting so the two
3037
3246
  sub-sections read at equal weight. */
3038
- .dq-toggle{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:13px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
3247
+ .dq-toggle{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:13px;font-weight:500;margin-top:24px}
3039
3248
  .dq-toggle input{width:16px;height:16px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer}
3040
- .dq-toggle-hint{margin:5px 0 0 26px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}
3041
- .dq-widget-tabs{align-items:flex-start;flex-direction:column;gap:7px}
3042
- .dq-widget-tab-list{display:flex;align-items:center;flex-wrap:wrap;gap:6px 14px}
3043
- .dq-widget-tab{display:inline-flex;align-items:center;gap:7px;min-height:28px;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;cursor:pointer}
3249
+ .dq-toggle-hint{margin:4px 0 0 26px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}
3250
+ .dq-widget-tabs{align-items:flex-start;flex-direction:column;gap:8px}
3251
+ .dq-widget-tab-list{display:flex;align-items:center;flex-wrap:wrap;gap:4px 16px}
3252
+ .dq-widget-tab{display:inline-flex;align-items:center;gap:7px;min-height:28px;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;font-weight:500;cursor:pointer}
3044
3253
  .dq-widget-tab input{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}
3045
3254
  .dq-toggle input:focus-visible,.dq-model-item input:focus-visible,.dq-widget-tab input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
3046
3255
  .dq-link:focus-visible,.dq-model-btn:focus-visible,.dq-chart-switch-btn:focus-visible,.dq-export-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
3047
- .dq-usage-totals{display:flex;flex-wrap:wrap;gap:12px 24px;margin-bottom:16px}
3256
+ .dq-usage-totals{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px 24px;margin-bottom:20px}
3048
3257
  /* value itself (number+unit, e.g. "9999.9\u4E07") must never break across lines inside its own column \u2014
3049
3258
  nowrap+ellipsis is a defensive floor for all widths; the actual fix for the realistic 320px 2-col
3050
3259
  case is the font-size drop below, which keeps every measured stress value comfortably under the
3051
3260
  83px column width without ever hitting the ellipsis fallback (see round 38 PROGRESS entry) */
3052
3261
  .dq-usage-totals .dq-stat-value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
3053
- .dq-vision-share{display:inline-block;margin-left:6px;font-size:11px;font-weight:550;color:var(--dsw-alias-label-tertiary,#59636e)}
3262
+ .dq-vision-share{display:inline-block;margin-left:6px;font-size:12px;font-weight:500;color:var(--dsw-alias-label-secondary,#59636e)}
3054
3263
  .dq-vision-sessions{margin-top:12px;display:flex;flex-direction:column;gap:8px;min-width:0}
3055
3264
  /* The vision card's session rows reuse .dq-session-head, but \u2014 unlike the
3056
3265
  ranking card's rows \u2014 they sit directly under .dq-session with no
@@ -3061,13 +3270,13 @@ p.dq-session-sub{margin:8px 0}
3061
3270
  .dq-vision-sessions .dq-session-head{min-width:0}
3062
3271
  .dq-vision-sessions .dq-session-title{flex:1 1 auto;min-width:0}
3063
3272
  @media (max-width:620px){.dq-usage-totals{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 24px}.dq-usage-totals .dq-stat{min-width:0}.dq-usage-totals .dq-stat-value{font-size:13px}}
3064
- .dq-chart-peak{margin-left:10px;font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);font-variant-numeric:tabular-nums}
3065
- .dq-chart-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:16px 0 8px}
3273
+ .dq-chart-peak{margin-left:8px;font-size:12px;font-weight:400;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}
3274
+ .dq-chart-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);margin:16px 0 8px}
3066
3275
  .dq-chart-title:first-of-type{margin-top:0}
3067
- .dq-chart-block-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:16px 0 8px}
3276
+ .dq-chart-block-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 12px}
3068
3277
  .dq-chart-block-head .dq-chart-title{margin:0}
3069
3278
  .dq-chart-controls{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex:none;flex-wrap:wrap}
3070
- @media (max-width:620px){.dq-chart-block-head{align-items:flex-start;flex-direction:column}.dq-chart-controls{width:100%}}
3279
+ @media (max-width:620px){.dq-chart-block-head{align-items:flex-start;flex-direction:column}.dq-chart-controls{width:100%;justify-content:flex-start}}
3071
3280
  .dq-model-picker{position:relative}
3072
3281
  .dq-model-btn{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}
3073
3282
  .dq-model-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}
@@ -3078,13 +3287,13 @@ p.dq-session-sub{margin:8px 0}
3078
3287
  .dq-model-item input{width:14px;height:14px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}
3079
3288
  .dq-model-item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
3080
3289
  .dq-model-item--all{border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-bottom:8px;margin-bottom:2px;border-radius:6px}
3081
- .dq-model-tag{color:var(--dsw-alias-label-tertiary,#59636e);font-size:11px;flex:none}
3290
+ .dq-model-tag{color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;flex:none}
3082
3291
  .dq-bar-group{display:flex;flex-direction:column-reverse;width:100%}
3083
3292
  .dq-bar-group .dq-bar{flex:0 0 auto;width:100%;border-radius:0}
3084
3293
  .dq-bar-group .dq-bar:first-child{border-radius:0 0 2px 2px}
3085
3294
  .dq-bar-group .dq-bar:last-child{border-radius:2px 2px 0 0}
3086
3295
  .dq-bar-group .dq-bar:not(:last-child){box-shadow:inset 0 1px 0 rgba(0,0,0,.18)}
3087
- .dq-legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
3296
+ .dq-legend{display:flex;flex-wrap:wrap;gap:4px 16px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
3088
3297
  .dq-legend-item{display:inline-flex;align-items:center;gap:6px}
3089
3298
  .dq-legend-swatch{width:10px;height:10px;border-radius:3px;flex:none}
3090
3299
  .dq-chart-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));border-radius:999px;flex:none}
@@ -3093,9 +3302,9 @@ p.dq-session-sub{margin:8px 0}
3093
3302
  .dq-chart-switch-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.14)}
3094
3303
  .dq-chart-wrap{position:relative}
3095
3304
  .dq-bars-scroll{overflow-x:auto;overflow-y:hidden;padding-bottom:2px}
3096
- .dq-tip{position:absolute;transform:translate(-50%,14px);z-index:40;pointer-events:none;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);border-radius:8px;padding:7px 10px;font-size:12px;line-height:1.55;white-space:nowrap;box-shadow:0 6px 20px rgba(0,0,0,.28);font-variant-numeric:tabular-nums}
3305
+ .dq-tip{position:absolute;transform:translate(-50%,14px);z-index:40;pointer-events:none;background:var(--dsw-alias-tooltip-bg,#1f2328);color:var(--dsw-static-neutral-bluish-00,#fff);border-radius:8px;padding:7px 10px;font-size:12px;line-height:1.5;white-space:nowrap;box-shadow:0 6px 20px rgba(0,0,0,.28);font-variant-numeric:tabular-nums}
3097
3306
  .dq-tip--above{transform:translate(-50%,calc(-100% - 8px))}
3098
- .dq-tip-head{font-weight:650;margin-bottom:2px}
3307
+ .dq-tip-head{font-weight:600;margin-bottom:2px}
3099
3308
  .dq-tip-row{opacity:.85}
3100
3309
  .dq-bars{display:flex;align-items:flex-end;gap:2px;width:100%}
3101
3310
  .dq-bar-col{flex:1 1 0;min-width:0;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch}
@@ -3111,7 +3320,7 @@ p.dq-session-sub{margin:8px 0}
3111
3320
  .dq-heat-cell:not(.dq-heat-cell--pad):hover{outline:1.5px solid var(--dsw-alias-label-primary,#1f2328);outline-offset:1px}
3112
3321
  .dq-heat-cell:not(.dq-heat-cell--pad):focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}
3113
3322
  .dq-heat-cell--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}
3114
- .dq-bar-label{font-size:9px;color:var(--dsw-alias-label-tertiary,#59636e);text-align:center;margin-top:3px;overflow:visible;white-space:nowrap}
3323
+ .dq-bar-label{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);text-align:center;margin-top:4px;overflow:visible;white-space:nowrap}
3115
3324
  .dq-heat{display:flex;flex-direction:column;gap:8px;max-width:100%}
3116
3325
  .dq-heat-scroll{overflow-x:auto;padding-bottom:2px}
3117
3326
  .dq-heatmap{display:flex;gap:3px}
@@ -3119,16 +3328,16 @@ p.dq-session-sub{margin:8px 0}
3119
3328
  .dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:3px;flex:none}
3120
3329
  .dq-heat-cell--pad{background:transparent}
3121
3330
  .dq-heat-months{display:flex;gap:3px;margin-top:4px}
3122
- .dq-heat-month{flex:1 1 0;min-width:6px;font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap;overflow:visible}
3331
+ .dq-heat-month{flex:1 1 0;min-width:6px;font-size:12px;line-height:15px;color:var(--dsw-alias-label-secondary,#59636e);white-space:nowrap;overflow:visible}
3123
3332
  .dq-heat-key{width:12px;height:12px;border-radius:2px;flex:none}
3124
- .dq-heat-scale{display:flex;align-items:center;gap:3px;font-size:10px;color:var(--dsw-alias-label-tertiary,#59636e);align-self:flex-end}
3333
+ .dq-heat-scale{display:flex;align-items:center;gap:3px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);align-self:flex-end}
3125
3334
  .dq-heat-scale>span:first-child{margin-right:2px}
3126
3335
  .dq-heat-scale>span:last-child{margin-left:2px}
3127
3336
  .dq-muted{color:var(--dsw-alias-label-secondary,#59636e)}
3128
3337
  .dq-error{color:var(--dsw-alias-state-error-primary,#cf222e)}
3129
- .dq-warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}
3130
- .dq-crash{margin:10px 0 14px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10));border-radius:8px;font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary,#cf222e);white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
3131
- .dq-empty{color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1.6;padding:6px 0}
3338
+ .dq-warn{color:var(--dq-text-warn)}
3339
+ .dq-crash{margin:10px 0 14px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10));border-radius:8px;font-size:12px;line-height:1.6;color:var(--dsw-alias-state-error-primary,#cf222e);white-space:pre-wrap;word-break:break-word;font-family:var(--ds-font-family-code,ui-monospace,SFMono-Regular,Menlo,monospace)}
3340
+ .dq-empty{color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1.6;padding:6px 0;text-wrap:pretty}
3132
3341
  /* Error-state modifier for a card body that has nothing else to show (mirrors the .dq-stat-value--ok/
3133
3342
  --bad/--warn pattern: same layout as .dq-empty, declared after it so this color wins at equal
3134
3343
  specificity). Used by the current-session card when its own fetch failed and there is no prior
@@ -3137,17 +3346,17 @@ p.dq-session-sub{margin:8px 0}
3137
3346
  .dq-empty--error{color:var(--dsw-alias-state-error-primary,#cf222e)}
3138
3347
  .dq-skel{background:linear-gradient(90deg,rgba(125,135,155,.10) 25%,rgba(125,135,155,.20) 37%,rgba(125,135,155,.10) 63%);background-size:400% 100%;border-radius:5px;animation:dq-shimmer 1.5s ease-in-out infinite}
3139
3348
  .dq-stat .dq-skel+.dq-skel{margin-top:6px}
3140
- .dq-skel-bars{display:flex;align-items:flex-end;gap:2px;width:100%;height:120px;margin-top:10px}
3349
+ .dq-skel-bars{display:flex;align-items:flex-end;gap:2px;width:100%;height:160px;margin-top:12px}
3141
3350
  .dq-skel-bar{flex:1 1 0;min-width:0;border-radius:2px 2px 0 0}
3142
3351
  @keyframes dq-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}
3143
- .dq-status-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:20px}
3352
+ .dq-status-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:20px;padding:0 20px;margin-bottom:-12px}
3144
3353
  .dq-status-messages{display:flex;flex-direction:column;align-items:flex-start;gap:2px;min-width:0}
3145
- .dq-status-row .dq-warn,.dq-status-row .dq-error{font-size:12px;line-height:1.45}
3354
+ .dq-status-row .dq-warn,.dq-status-row .dq-error{font-size:12px;line-height:1.5}
3146
3355
  .dq-sync{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}
3147
3356
  .dq-sync-dot{width:7px;height:7px;border-radius:50%;flex:none;background:var(--dsw-alias-label-tertiary,#8c959f)}
3148
3357
  .dq-sync--fresh .dq-sync-dot{background:var(--dsw-alias-state-success-primary,#2da44e)}
3149
3358
  .dq-sync--syncing .dq-sync-dot{background:var(--dsw-alias-state-business-primary,#4176e6);animation:dq-sync-pulse 1.2s ease-in-out infinite}
3150
- .dq-sync--fallback{color:var(--dsw-alias-state-warning-primary,#9a6700)}
3359
+ .dq-sync--fallback{color:var(--dq-text-warn)}
3151
3360
  .dq-sync--fallback .dq-sync-dot{background:var(--dsw-alias-state-warning-primary,#bf8700)}
3152
3361
  .dq-sync--error{color:var(--dsw-alias-state-error-primary,#cf222e)}
3153
3362
  .dq-sync--error .dq-sync-dot{background:var(--dsw-alias-state-error-primary,#cf222e)}
@@ -3159,6 +3368,77 @@ p.dq-session-sub{margin:8px 0}
3159
3368
  .dq-refresh-icon{display:inline-block;line-height:1}
3160
3369
  .dq-refresh-icon--spin{animation:dq-spin .9s linear infinite}
3161
3370
  @keyframes dq-spin{to{transform:rotate(360deg)}}
3371
+ /* ============================================================================
3372
+ Host chrome: the \u300C\u989D\u5EA6\u300Dconversation view does not use the chat composer.
3373
+ The attribute is set by index.tsx only when the dashboard is mounted inside
3374
+ the conversation scrollport, so the account-wide panel never hides it.
3375
+ '!important' because the composer's slot anchor is a "display:contents"
3376
+ wrapper whose display we must beat regardless of host rule order.
3377
+ ============================================================================ */
3378
+ html[data-dq-quota-view] [data-slot="conversation.composer"]{display:none!important}
3379
+ /* The narrow-viewport rule pads for the fixed composer; with it hidden there
3380
+ is nothing to clear, so fall back to the page's own bottom breathing room. */
3381
+ html[data-dq-quota-view] .dq-balance{padding-bottom:48px}
3382
+ /* ============================================================================
3383
+ Account-wide \u300C\u989D\u5EA6\u300Dpanel (the optional sidebar entry opens this main
3384
+ keyed panel). The dashboard keeps its own width/padding; the panel only
3385
+ supplies the page title row and the scrolling box the main column lacks.
3386
+ ============================================================================ */
3387
+ /* Account-wide page frame: mirrors DSH's first-level pages (Plugins /
3388
+ Scheduled tasks) \u2014 full-height scroll box, 28px top inset on the heading,
3389
+ a 960px centred column, clamp(24px,4vw,48px) inline + 48px bottom insets and
3390
+ a 20px/28px/500 page title, so the three sibling entries line up. */
3391
+ .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}
3392
+ .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}
3393
+ .dq-panel-title{margin:0;font-size:20px;font-weight:500;line-height:28px;color:var(--dsw-alias-label-primary,#1f2328)}
3394
+ .dq-panel-caption{font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary,#59636e)}
3395
+ .dq-panel .dq-balance{box-sizing:border-box;width:100%;max-width:960px;padding:0}
3396
+ /* macOS hiddenInset titlebar: the frame's top clearance keeps the heading
3397
+ below the window's top strip, matching the host's sibling pages. */
3398
+ [data-platform="darwin"] .dq-panel-head{padding-top:calc(28px + var(--dsh-frame-top-clearance,0px))}
3399
+ @media (max-width:620px){
3400
+ .dq-panel-head{padding-top:24px;margin-bottom:24px}
3401
+ .dq-panel .dq-balance{max-width:100%;padding:0}
3402
+ }
3403
+ /* Optional sidebar entry. Top placement joins the host's global-panel icon
3404
+ row (only the glyph is ours); bottom placement renders a row beside
3405
+ Settings, collapsing to a square rail button with the sidebar. */
3406
+ /* The glyph is an SVG matching the host's 16px stroke icons; color inherits
3407
+ from the sidebar row so selected/hover states stay the host's business. */
3408
+ .dq-side-icon{flex:none;display:block}
3409
+ .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}
3410
+ .dq-side-entry:hover{background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10));color:var(--dsw-alias-label-primary,#1f2328)}
3411
+ .dq-side-entry:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
3412
+ .dq-side-entry[aria-pressed="true"]{color:var(--dsw-alias-label-primary,#1f2328)}
3413
+ .dq-side-entry--rail{justify-content:center;width:32px;height:32px;margin:0 auto;padding:0}
3414
+ .dq-side-entry-icon{flex:none;display:inline-flex;align-items:center;justify-content:center}
3415
+ .dq-side-entry-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
3416
+ /* ============================================================================
3417
+ Settings controls for the two layout preferences: a compact select and the
3418
+ draggable tab-order strip.
3419
+ ============================================================================ */
3420
+ .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}
3421
+ .dq-select:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
3422
+ .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))}
3423
+ .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}
3424
+ .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}
3425
+ .dq-tab-chip--current:active{cursor:grabbing}
3426
+ .dq-tab-chip--current:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
3427
+ .dq-tab-chip--dragging{opacity:.55}
3428
+ .dq-tab-chip--drop-target{border-style:dashed;border-color:var(--dsw-alias-state-business-primary,#0969da)}
3429
+ /* ============================================================================
3430
+ \u300C\u56FE\u7247 / \u591A\u6A21\u6001\u7528\u91CF\u300Dlives inside the usage-trend card. The grid-rows 0fr\u21921fr
3431
+ trick animates the section's real height as a window range makes it appear
3432
+ or disappear; opacity/translate make the change read as a reveal rather
3433
+ than a reflow. The body keeps 14px of top room in both states (clipped by
3434
+ the 0fr row when closed) so opening never jumps.
3435
+ ============================================================================ */
3436
+ .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)}
3437
+ .dq-vision-inline[data-open="true"]{grid-template-rows:1fr;margin-top:24px;opacity:1;transform:none}
3438
+ .dq-vision-inline-body{min-height:0;overflow:hidden}
3439
+ .dq-vision-inline-body>.dq-chart-title{margin-top:0}
3440
+ .dq-vision-inline-body>.dq-chart-title{margin-top:0}
3441
+
3162
3442
  @media (prefers-reduced-motion:reduce){
3163
3443
  .dsh-quota-root,.dsh-quota-root *,
3164
3444
  .dsh-quota-root *::before,.dsh-quota-root *::after,
@@ -3444,7 +3724,7 @@ function QuotaWidget(props) {
3444
3724
  const sessionCostRow = sessionUsage === null ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-row", title: t("widget.sessionCostTitle"), children: [
3445
3725
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-quota-label", children: t("widget.sessionCost") }),
3446
3726
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dsh-quota-value", children: [
3447
- "\xA5 ",
3727
+ "\xA5",
3448
3728
  fmtTurnCost(sessionUsage.cost)
3449
3729
  ] })
3450
3730
  ] });
@@ -3470,7 +3750,7 @@ function QuotaWidget(props) {
3470
3750
  usage.data !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-quota-row", title: t("widget.todayCostTitle", { age: usageAgeText }), children: [
3471
3751
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-quota-label", children: t("widget.todayCost") }),
3472
3752
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dsh-quota-value", children: [
3473
- "\xA5 ",
3753
+ "\xA5",
3474
3754
  fmt(usage.data.summary.today.cost)
3475
3755
  ] })
3476
3756
  ] }),
@@ -3580,12 +3860,14 @@ function conversationViewsSource(slots) {
3580
3860
  // src/client/index.tsx
3581
3861
  var import_jsx_runtime7 = require("react/jsx-runtime");
3582
3862
  var name = "dsh-usage-dashboard";
3583
- var inject = ["slots", "locale"];
3863
+ var inject = ["slots", "locale", "layout"];
3864
+ var openQuotaTab = null;
3584
3865
  function apply(ctx) {
3585
3866
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-usage-dashboard: browser dictionaries");
3586
3867
  const t = ctx.locale.bind(NS);
3587
3868
  const views = conversationViewsSource(ctx.slots);
3588
3869
  if (typeof document !== "undefined") {
3870
+ document.querySelectorAll('style[data-plugin="dsh-usage-dashboard"]').forEach((node) => node.remove());
3589
3871
  const tag = document.createElement("style");
3590
3872
  tag.dataset.plugin = "dsh-usage-dashboard";
3591
3873
  tag.textContent = css;
@@ -3609,17 +3891,153 @@ function apply(ctx) {
3609
3891
  },
3610
3892
  ({ t: slotT, sessionId, messageId }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LocaleProvider, { t: slotT, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ErrorBoundary, { t: slotT, silent: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageCost, { sessionId, messageId }) }) })
3611
3893
  ));
3612
- ctx.slots.inject("conversation.view", () => ctx.slots.register(
3613
- { name: "conversation.view", id: "balance", order: 20, locale: NS, label: () => t("nav.quota") },
3614
- // `conversation.view` is a session-scoped slot, so the host injects its
3615
- // standard session props (including `sessionId`) alongside `t` — this
3616
- // plugin only destructures the two fields it needs, per the existing
3617
- // "hand-write the minimal structural type" convention (see context.ts).
3618
- // `sessionId` is typed optional defensively even though the host is
3619
- // expected to always supply it: BalanceDashboard renders the current-
3620
- // session card only when it actually has one, never throws either way.
3621
- ({ 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 }) }) })
3622
- ));
3894
+ const QuotaView = ({ t: slotT, sessionId, openView }) => {
3895
+ (0, import_react7.useEffect)(() => {
3896
+ if (openView !== void 0) openQuotaTab = openView;
3897
+ }, [openView]);
3898
+ return /* @__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 }) }) });
3899
+ };
3900
+ ctx.effect(() => {
3901
+ let disposeView = null;
3902
+ const mount = () => {
3903
+ disposeView?.();
3904
+ disposeView = ctx.slots.inject("conversation.view", () => ctx.slots.register(
3905
+ {
3906
+ name: "conversation.view",
3907
+ id: "balance",
3908
+ order: viewOrderForIndex(ctx.slots.entries("conversation.view"), quotaTabIndexStore.get()),
3909
+ locale: NS,
3910
+ label: () => t("nav.quota")
3911
+ },
3912
+ // `conversation.view` is a session-scoped slot, so the host injects its
3913
+ // standard session props (including `sessionId`) alongside `t`; only the
3914
+ // fields this plugin needs are destructured, per the existing
3915
+ // "hand-write the minimal structural type" convention (see context.ts).
3916
+ // `sessionId` stays optional: BalanceDashboard renders the current-
3917
+ // session card only when it actually has one, never throws either way.
3918
+ QuotaView
3919
+ ));
3920
+ };
3921
+ mount();
3922
+ const unsubscribe = quotaTabIndexStore.subscribe(() => {
3923
+ const restore = document !== void 0 && document.documentElement.hasAttribute("data-dq-quota-view");
3924
+ disposeView?.();
3925
+ disposeView = null;
3926
+ mount();
3927
+ if (restore) queueMicrotask(() => openQuotaTab?.("balance", "dsh-usage-dashboard:tab-order"));
3928
+ });
3929
+ return () => {
3930
+ unsubscribe();
3931
+ disposeView?.();
3932
+ };
3933
+ }, "dsh-usage-dashboard: quota tab registration");
3934
+ const QuotaPanel = ({ t: slotT }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dq-panel", children: [
3935
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dq-panel-head", "data-window-drag": true, children: [
3936
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h1", { className: "dq-panel-title", children: slotT("nav.quota") }),
3937
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dq-panel-caption", children: slotT("panel.caption") })
3938
+ ] }),
3939
+ /* @__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, { views }) }) })
3940
+ ] });
3941
+ const QuotaGlyph = ({ size, active }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3942
+ "svg",
3943
+ {
3944
+ className: "dq-side-icon",
3945
+ "data-active": active === true ? "true" : "false",
3946
+ width: size,
3947
+ height: size,
3948
+ viewBox: "0 0 16 16",
3949
+ fill: "none",
3950
+ stroke: "currentColor",
3951
+ strokeWidth: 1.1,
3952
+ strokeLinecap: "round",
3953
+ strokeLinejoin: "round",
3954
+ "aria-hidden": "true",
3955
+ focusable: "false",
3956
+ children: [
3957
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M3.5 3.3 8 8.7l4.5-5.4" }),
3958
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M8 8.7V13.2" }),
3959
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M3.5 10.2h9" }),
3960
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M3.5 12.1h9" })
3961
+ ]
3962
+ }
3963
+ );
3964
+ const QuotaFooterButton = ({ wide, t: slotT }) => {
3965
+ const [active, setActive] = (0, import_react7.useState)(() => ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
3966
+ (0, import_react7.useEffect)(() => {
3967
+ const sync = () => {
3968
+ setActive(ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID);
3969
+ };
3970
+ sync();
3971
+ return ctx.layout.panelInfo.subscribe(sync);
3972
+ }, []);
3973
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3974
+ "button",
3975
+ {
3976
+ type: "button",
3977
+ className: `dq-side-entry${wide ? "" : " dq-side-entry--rail"}`,
3978
+ title: slotT("panel.openAria"),
3979
+ "aria-label": slotT("nav.quota"),
3980
+ "aria-pressed": active,
3981
+ onClick: () => {
3982
+ ctx.layout.selectPanel(active ? null : QUOTA_PANEL_ID);
3983
+ },
3984
+ children: [
3985
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dq-side-entry-icon", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(QuotaGlyph, { size: 16 }) }),
3986
+ wide && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dq-side-entry-label", children: slotT("nav.quota") })
3987
+ ]
3988
+ }
3989
+ );
3990
+ };
3991
+ ctx.effect(() => ctx.slots.inject("main", () => ctx.slots.register(
3992
+ { name: "main", key: QUOTA_PANEL_ID, locale: NS },
3993
+ QuotaPanel
3994
+ )), "dsh-usage-dashboard: quota panel");
3995
+ ctx.effect(() => {
3996
+ let disposeEntry = null;
3997
+ const sync = () => {
3998
+ disposeEntry?.();
3999
+ disposeEntry = null;
4000
+ const placement = sidebarEntryStore.get();
4001
+ if (placement === "top") {
4002
+ disposeEntry = ctx.slots.inject("sidebar.panellist", () => ctx.slots.register(
4003
+ { name: "sidebar.panellist", id: QUOTA_PANEL_ID, order: 1e3, label: () => t("nav.quota"), locale: NS },
4004
+ QuotaGlyph
4005
+ ));
4006
+ return;
4007
+ }
4008
+ if (placement === "bottom") {
4009
+ disposeEntry = ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register(
4010
+ { name: "sidebar.footer.action", id: "quota", order: 10, locale: NS },
4011
+ QuotaFooterButton
4012
+ ));
4013
+ return;
4014
+ }
4015
+ if (ctx.layout.panelInfo.getSnapshot().activePanelId === QUOTA_PANEL_ID) ctx.layout.selectPanel(null);
4016
+ };
4017
+ sync();
4018
+ const unsubscribe = sidebarEntryStore.subscribe(sync);
4019
+ return () => {
4020
+ unsubscribe();
4021
+ disposeEntry?.();
4022
+ };
4023
+ }, "dsh-usage-dashboard: sidebar entry");
4024
+ ctx.effect(() => {
4025
+ let applied = false;
4026
+ const sync = () => {
4027
+ const root = document.querySelector(".dq-balance");
4028
+ const should = quotaViewActiveStore.get() && root !== null && root.closest("[data-conversation-scroll]") !== null;
4029
+ if (should === applied) return;
4030
+ applied = should;
4031
+ if (should) document.documentElement.setAttribute("data-dq-quota-view", "");
4032
+ else document.documentElement.removeAttribute("data-dq-quota-view");
4033
+ };
4034
+ sync();
4035
+ const unsubscribe = quotaViewActiveStore.subscribe(sync);
4036
+ return () => {
4037
+ unsubscribe();
4038
+ if (applied) document.documentElement.removeAttribute("data-dq-quota-view");
4039
+ };
4040
+ }, "dsh-usage-dashboard: composer visibility");
3623
4041
  }
3624
4042
  return module.exports; } });
3625
4043
  //# sourceMappingURL=client.js.map