@cassius0924/dsh-usage-dashboard 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -9
- package/lib/client.js +616 -267
- package/lib/client.js.map +4 -4
- package/lib/types/client/charts.d.ts +8 -0
- package/lib/types/client/i18n.d.ts +21 -0
- package/lib/types/client/locales.d.ts +26 -5
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/client/top-up.d.ts +13 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -363,17 +363,24 @@ var zh = {
|
|
|
363
363
|
"export.jsonHint": "\u542B\u56FE\u8868\u4E0E\u6392\u884C",
|
|
364
364
|
"cache.empty": "\u6682\u65E0\u7F13\u5B58\u547D\u4E2D\u6570\u636E\u3002",
|
|
365
365
|
"cache.rate": "\u7F13\u5B58\u547D\u4E2D\u7387",
|
|
366
|
-
"cache.saved": "\
|
|
367
|
-
"cache.allMiss": "\u82E5\u5168\
|
|
366
|
+
"cache.saved": "\u8F83\u5168\u672A\u547D\u4E2D\u7701\u4E0B",
|
|
367
|
+
"cache.allMiss": "\u82E5\u5168\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.basis": "\u5B9E\u4ED8 \xA5{cost}\uFF1B\u82E5\u5168\u90E8\u672A\u547D\u4E2D\u7EA6 \xA5{miss}\uFF0C\u4E24\u8005\u5DEE\u989D\u5373\u547D\u4E2D\u7F13\u5B58\u7701\u4E0B\u7684\u8D39\u7528\u3002",
|
|
371
372
|
"peak.empty": "\u6682\u65E0\u7528\u91CF\u3002",
|
|
372
373
|
"peak.aria": "\u9AD8\u5CF0\u65F6\u6BB5 {peak}%\uFF0C\u95F2\u65F6 {offPeak}%",
|
|
373
374
|
"peak.peakLegend": "\u9AD8\u5CF0 {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
374
375
|
"peak.offPeakLegend": "\u95F2\u65F6 {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
375
|
-
"peak.shift": "\
|
|
376
|
-
"peak.saving": "\
|
|
376
|
+
"peak.shift": "\u9AD8\u5CF0\u6539\u8D70\u95F2\u65F6\u53EF\u7701",
|
|
377
|
+
"peak.saving": "\xA5{amount}",
|
|
378
|
+
"peak.basis": "\u5168\u90E8\u6539\u8D70\u95F2\u65F6\u7EA6 \xA5{total}\uFF1B\u6B64\u5904\u6309\u5F53\u524D\u4EF7\u76EE\u4F30\u7B97\uFF0C\u4E0E\u300C\u6D88\u8017\u6982\u89C8 \xB7 \u7D2F\u8BA1\u300D\u7684\u5B9E\u9645\u82B1\u8D39\u53E3\u5F84\u4E0D\u540C\u3002",
|
|
379
|
+
"peak.clockTitle": "\u4ECA\u65E5\u4EF7\u683C\u65F6\u6BB5",
|
|
380
|
+
"peak.nowPeak": "\u73B0\u5728\u9AD8\u5CF0 \xB7 \u5168\u4EF7",
|
|
381
|
+
"peak.nowOff": "\u73B0\u5728\u95F2\u65F6 \xB7 \u534A\u4EF7",
|
|
382
|
+
"peak.clockAria": "24 \u5C0F\u65F6\u4EF7\u683C\u65F6\u6BB5\uFF0C{state}",
|
|
383
|
+
"peak.clockHint": "\u5DE5\u4F5C\u65E5 09\u201312\u300114\u201318 \u4E3A\u9AD8\u5CF0\uFF0C\u5176\u4F59\u65F6\u6BB5\u534A\u4EF7\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09",
|
|
377
384
|
"vision.empty": "{window}\u6CA1\u6709\u56FE\u7247\u8F93\u5165\u3002",
|
|
378
385
|
"vision.title": "\u56FE\u7247 / \u591A\u6A21\u6001\u7528\u91CF",
|
|
379
386
|
"vision.images": "\u56FE\u7247\u5F20\u6570",
|
|
@@ -446,7 +453,7 @@ var zh = {
|
|
|
446
453
|
"sync.first": "\u9996\u6B21\u540C\u6B65\u4E2D",
|
|
447
454
|
"sync.syncing": "\u540C\u6B65\u4E2D \xB7 \u5F53\u524D\u6570\u636E{age}",
|
|
448
455
|
"sync.fresh": "\u5DF2\u540C\u6B65 \xB7 {age}",
|
|
449
|
-
"sync.cached": "\u7F13\u5B58
|
|
456
|
+
"sync.cached": "\u672C\u5730\u7F13\u5B58 \xB7 {age}",
|
|
450
457
|
"sync.fallback": "\u7F13\u5B58\u56DE\u9000 \xB7 {age}",
|
|
451
458
|
"sync.error": "\u540C\u6B65\u5931\u8D25 \xB7 \u6682\u65E0\u53EF\u663E\u793A\u7684\u7528\u91CF",
|
|
452
459
|
"status.neverSynced": "\u8FD8\u6CA1\u6709\u6210\u529F\u540C\u6B65\u8FC7\u7528\u91CF\u6570\u636E",
|
|
@@ -481,6 +488,7 @@ var zh = {
|
|
|
481
488
|
"balance.granted": "\u8D60\u9001\u989D\u5EA6 {amount}",
|
|
482
489
|
"balance.runway": "\u9884\u8BA1\u53EF\u7528",
|
|
483
490
|
"balance.runwayBreakdownTitle": "\u67E5\u770B\u9884\u8BA1\u53EF\u7528\u5929\u6570\u8BF4\u660E",
|
|
491
|
+
"balance.topUpTitle": "\u4F59\u989D\u504F\u4F4E\uFF0C\u524D\u5F80 DeepSeek \u5F00\u653E\u5E73\u53F0\u5145\u503C\uFF08\u65B0\u6807\u7B7E\u9875\u6253\u5F00\uFF09",
|
|
484
492
|
"balance.days": "{count} \u5929",
|
|
485
493
|
"balance.overYear": "> 365 \u5929",
|
|
486
494
|
"balance.noRunway": "\u8FD1 {days} \u5929\u6CA1\u6709\u7528\u91CF\uFF0C\u65E0\u6CD5\u4F30\u7B97",
|
|
@@ -510,10 +518,23 @@ var zh = {
|
|
|
510
518
|
"models.title": "\u6A21\u578B\u6210\u672C\u6392\u884C",
|
|
511
519
|
"sessions.title": "\u4F1A\u8BDD\u6210\u672C\u6392\u884C",
|
|
512
520
|
"links.title": "\u5FEB\u6377\u5165\u53E3",
|
|
521
|
+
"nav.label": "\u9875\u9762\u5927\u7EB2",
|
|
522
|
+
"nav.balance": "\u4F59\u989D",
|
|
523
|
+
"nav.session": "\u5F53\u524D\u4F1A\u8BDD",
|
|
524
|
+
"nav.overview": "\u6D88\u8017\u6982\u89C8",
|
|
525
|
+
"nav.trend": "\u7528\u91CF\u8D8B\u52BF",
|
|
526
|
+
"nav.heatmap": "\u7528\u91CF\u70ED\u529B\u56FE",
|
|
527
|
+
"nav.models": "\u6A21\u578B\u6210\u672C\u6392\u884C",
|
|
528
|
+
"nav.sessions": "\u4F1A\u8BDD\u6210\u672C\u6392\u884C",
|
|
529
|
+
"nav.peakcache": "\u9AD8\u5CF0 / \u7F13\u5B58",
|
|
530
|
+
"nav.settings": "\u8BBE\u7F6E",
|
|
513
531
|
"links.usage": "\u67E5\u770B\u989D\u5EA6 / \u7528\u91CF",
|
|
514
532
|
"links.apiKey": "\u751F\u6210 API Key",
|
|
515
533
|
"links.status": "\u670D\u52A1\u72B6\u6001",
|
|
516
534
|
"settings.title": "\u8BBE\u7F6E",
|
|
535
|
+
"settings.groupDisplay": "\u663E\u793A",
|
|
536
|
+
"settings.groupAlerts": "\u63D0\u9192",
|
|
537
|
+
"settings.groupLayout": "\u5E03\u5C40\u4E0E\u5165\u53E3",
|
|
517
538
|
"settings.widget": "\u5728\u5176\u4ED6\u9875\u9762\u663E\u793A\u53F3\u4E0B\u89D2\u60AC\u6D6E\u989D\u5EA6\u7A97\u53E3",
|
|
518
539
|
"settings.widgetHint": "\u989D\u5EA6\u9875\u81EA\u52A8\u9690\u85CF\uFF1B\u5176\u4ED6\u9875\u9762\u6309\u4E0B\u65B9\u9009\u62E9\u663E\u793A\u3002",
|
|
519
540
|
"settings.widgetTabs": "\u5C55\u793A\u6D6E\u7A97\u7684\u9875\u9762",
|
|
@@ -634,17 +655,24 @@ var en = {
|
|
|
634
655
|
"export.jsonHint": "Charts and rankings included",
|
|
635
656
|
"cache.empty": "No cache data yet.",
|
|
636
657
|
"cache.rate": "Cache hit rate",
|
|
637
|
-
"cache.saved": "
|
|
638
|
-
"cache.allMiss": "Cost
|
|
658
|
+
"cache.saved": "Saved vs. all-miss",
|
|
659
|
+
"cache.allMiss": "Cost if all miss",
|
|
639
660
|
"cache.aria": "{hit}% cache hits, {miss}% misses",
|
|
640
661
|
"cache.hitLegend": "Cache hits {tokens}",
|
|
641
662
|
"cache.missLegend": "Misses {tokens} (charged at miss rate)",
|
|
663
|
+
"cache.basis": "You paid \xA5{cost}; with no cache hits it would be about \xA5{miss}. The difference is what caching saved.",
|
|
642
664
|
"peak.empty": "No usage yet.",
|
|
643
665
|
"peak.aria": "{peak}% peak, {offPeak}% off-peak",
|
|
644
666
|
"peak.peakLegend": "Peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
645
667
|
"peak.offPeakLegend": "Off-peak {share}% \xB7 {tokens} tokens \xB7 {calls}",
|
|
646
|
-
"peak.shift": "
|
|
647
|
-
"peak.saving": "
|
|
668
|
+
"peak.shift": "Saving if peak runs off-peak",
|
|
669
|
+
"peak.saving": "\xA5{amount}",
|
|
670
|
+
"peak.basis": "All off-peak would cost about \xA5{total}, estimated at current prices \u2014 a different basis from \u201CSpend overview \xB7 All time\u201D.",
|
|
671
|
+
"peak.clockTitle": "Today's price windows",
|
|
672
|
+
"peak.nowPeak": "Peak now \xB7 full price",
|
|
673
|
+
"peak.nowOff": "Off-peak now \xB7 half price",
|
|
674
|
+
"peak.clockAria": "24-hour price schedule, {state}",
|
|
675
|
+
"peak.clockHint": "Peak 09\u201312 & 14\u201318 on weekdays; half price otherwise (Beijing time)",
|
|
648
676
|
"vision.empty": "No image input in {window}.",
|
|
649
677
|
"vision.title": "Image / multimodal usage",
|
|
650
678
|
"vision.images": "Images",
|
|
@@ -717,7 +745,7 @@ var en = {
|
|
|
717
745
|
"sync.first": "Syncing for the first time",
|
|
718
746
|
"sync.syncing": "Syncing \xB7 current data {age}",
|
|
719
747
|
"sync.fresh": "Synced \xB7 {age}",
|
|
720
|
-
"sync.cached": "
|
|
748
|
+
"sync.cached": "Local cache \xB7 {age}",
|
|
721
749
|
"sync.fallback": "Using cache \xB7 {age}",
|
|
722
750
|
"sync.error": "Sync failed \xB7 no usage to display",
|
|
723
751
|
"status.neverSynced": "Usage has not synced successfully yet",
|
|
@@ -752,6 +780,7 @@ var en = {
|
|
|
752
780
|
"balance.granted": "Granted {amount}",
|
|
753
781
|
"balance.runway": "Estimated runway",
|
|
754
782
|
"balance.runwayBreakdownTitle": "View estimated runway details",
|
|
783
|
+
"balance.topUpTitle": "Low balance \u2014 top up on the DeepSeek platform (opens a new tab)",
|
|
755
784
|
"balance.days": "{count} days",
|
|
756
785
|
"balance.overYear": "> 365 days",
|
|
757
786
|
"balance.noRunway": "No usage in the last {days} days, so runway cannot be estimated",
|
|
@@ -781,10 +810,23 @@ var en = {
|
|
|
781
810
|
"models.title": "Model cost ranking",
|
|
782
811
|
"sessions.title": "Session cost ranking",
|
|
783
812
|
"links.title": "Quick Links",
|
|
813
|
+
"nav.label": "On this page",
|
|
814
|
+
"nav.balance": "Balance",
|
|
815
|
+
"nav.session": "This session",
|
|
816
|
+
"nav.overview": "Spend overview",
|
|
817
|
+
"nav.trend": "Usage trend",
|
|
818
|
+
"nav.heatmap": "Usage heatmap",
|
|
819
|
+
"nav.models": "Model cost",
|
|
820
|
+
"nav.sessions": "Session cost",
|
|
821
|
+
"nav.peakcache": "Peak & cache",
|
|
822
|
+
"nav.settings": "Settings",
|
|
784
823
|
"links.usage": "View balance / usage",
|
|
785
824
|
"links.apiKey": "Create API key",
|
|
786
825
|
"links.status": "Service status",
|
|
787
826
|
"settings.title": "Settings",
|
|
827
|
+
"settings.groupDisplay": "Display",
|
|
828
|
+
"settings.groupAlerts": "Alerts",
|
|
829
|
+
"settings.groupLayout": "Layout & entry",
|
|
788
830
|
"settings.widget": "Show the floating balance widget on other views",
|
|
789
831
|
"settings.widgetHint": "Hidden on Usage; shown on the views selected below.",
|
|
790
832
|
"settings.widgetTabs": "Show widget on",
|
|
@@ -837,6 +879,13 @@ var en = {
|
|
|
837
879
|
|
|
838
880
|
// src/client/i18n.tsx
|
|
839
881
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
882
|
+
var INTL_LOCALES = {
|
|
883
|
+
zh: "zh-CN",
|
|
884
|
+
en: "en-US"
|
|
885
|
+
};
|
|
886
|
+
function intlLocale(locale) {
|
|
887
|
+
return INTL_LOCALES[locale];
|
|
888
|
+
}
|
|
840
889
|
var fallbackT = (key, params) => {
|
|
841
890
|
const template = zh[key];
|
|
842
891
|
if (params === void 0) return template;
|
|
@@ -961,6 +1010,7 @@ function ChartTip(props) {
|
|
|
961
1010
|
var MODEL_COLORS = ["#4176e6", "#2da44e", "#e16f24", "#8250df", "#bf8700", "#cf222e", "#1f883d", "#0969da"];
|
|
962
1011
|
function Bars(props) {
|
|
963
1012
|
const { data, height = 120, labelEvery = 1, minWidth } = props;
|
|
1013
|
+
const { locale } = useI18n();
|
|
964
1014
|
const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip();
|
|
965
1015
|
const scrollRef = (0, import_react3.useRef)(null);
|
|
966
1016
|
const initialFocus = lastPopulatedIndex(data.map((datum) => datum.value));
|
|
@@ -973,39 +1023,46 @@ function Bars(props) {
|
|
|
973
1023
|
for (const datum of data) if (datum.value > max) max = datum.value;
|
|
974
1024
|
const scale = max > 0 ? max : 1;
|
|
975
1025
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-chart-wrap", ref: wrapRef, onPointerLeave: hide, children: [
|
|
976
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
"
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1026
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-plot", children: [
|
|
1027
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-yaxis", style: { height: `${height}px` }, "aria-hidden": "true", children: [
|
|
1028
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: max > 0 ? fmtCompact(max, locale) : "" }),
|
|
1029
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "0" })
|
|
1030
|
+
] }),
|
|
1031
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bars-scroll", ref: scrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bars", style: { height: `${height + 18}px`, minWidth: minWidth === void 0 ? void 0 : `${minWidth}px` }, children: data.map((datum, i) => {
|
|
1032
|
+
const h = Math.max(1, Math.round(datum.value / scale * height));
|
|
1033
|
+
const text = datum.title ?? `${datum.label}: ${datum.value.toLocaleString()}`;
|
|
1034
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1035
|
+
"div",
|
|
1036
|
+
{
|
|
1037
|
+
className: `dq-bar-col${pinned === i ? " dq-bar-col--pinned" : ""}`,
|
|
1038
|
+
role: "img",
|
|
1039
|
+
"aria-label": text,
|
|
1040
|
+
"aria-keyshortcuts": "ArrowLeft ArrowRight ArrowUp ArrowDown Home End",
|
|
1041
|
+
tabIndex: i === roving.active ? 0 : -1,
|
|
1042
|
+
ref: (element) => {
|
|
1043
|
+
roving.refs.current[i] = element;
|
|
1044
|
+
},
|
|
1045
|
+
onPointerMove: (e) => show(e, text),
|
|
1046
|
+
onPointerDown: onPointStart,
|
|
1047
|
+
onPointerUp: (e) => onPointEnd(e, i, text),
|
|
1048
|
+
onFocus: (e) => showFocused(e, text),
|
|
1049
|
+
onBlur: hide,
|
|
1050
|
+
onKeyDown: (e) => roving.onKeyDown(e, i),
|
|
1051
|
+
children: [
|
|
1052
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar", style: { height: `${h}px` } }),
|
|
1053
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-label", style: { visibility: i % labelEvery === 0 ? "visible" : "hidden" }, children: datum.label })
|
|
1054
|
+
]
|
|
989
1055
|
},
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
onBlur: hide,
|
|
995
|
-
onKeyDown: (e) => roving.onKeyDown(e, i),
|
|
996
|
-
children: [
|
|
997
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar", style: { height: `${h}px` } }),
|
|
998
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-label", style: { visibility: i % labelEvery === 0 ? "visible" : "hidden" }, children: datum.label })
|
|
999
|
-
]
|
|
1000
|
-
},
|
|
1001
|
-
i
|
|
1002
|
-
);
|
|
1003
|
-
}) }) }),
|
|
1056
|
+
i
|
|
1057
|
+
);
|
|
1058
|
+
}) }) })
|
|
1059
|
+
] }),
|
|
1004
1060
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChartTip, { tip })
|
|
1005
1061
|
] });
|
|
1006
1062
|
}
|
|
1007
1063
|
function GroupedBars(props) {
|
|
1008
1064
|
const { series, height = 120, labelEvery = 1, minWidth } = props;
|
|
1065
|
+
const { locale } = useI18n();
|
|
1009
1066
|
const { wrapRef, tip, pinned, show, showFocused, hide, onPointStart, onPointEnd } = useChartTip();
|
|
1010
1067
|
const scrollRef = (0, import_react3.useRef)(null);
|
|
1011
1068
|
const count = series.length > 0 ? series[0].bars.length : 0;
|
|
@@ -1015,45 +1072,53 @@ function GroupedBars(props) {
|
|
|
1015
1072
|
const scroll = scrollRef.current;
|
|
1016
1073
|
if (scroll !== null && minWidth !== void 0) scroll.scrollLeft = scroll.scrollWidth;
|
|
1017
1074
|
}, [count, minWidth]);
|
|
1018
|
-
const
|
|
1075
|
+
const totals = stackedTotals(series, count);
|
|
1076
|
+
const scale = maxOrOne(totals);
|
|
1077
|
+
const axisMax = totals.reduce((m, v) => Math.max(m, v), 0);
|
|
1019
1078
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-chart-wrap", ref: wrapRef, onPointerLeave: hide, children: [
|
|
1020
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1021
|
-
"div",
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
"
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1079
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-plot", children: [
|
|
1080
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dq-yaxis", style: { height: `${height}px` }, "aria-hidden": "true", children: [
|
|
1081
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: axisMax > 0 ? fmtCompact(axisMax, locale) : "" }),
|
|
1082
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "0" })
|
|
1083
|
+
] }),
|
|
1084
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bars-scroll", ref: scrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bars", style: { height: `${height + 18}px`, minWidth: minWidth === void 0 ? void 0 : `${minWidth}px` }, children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1085
|
+
"div",
|
|
1086
|
+
{
|
|
1087
|
+
className: "dq-bar-col",
|
|
1088
|
+
children: [
|
|
1089
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-group", children: series.map((s, seriesIndex) => {
|
|
1090
|
+
const datum = s.bars[i];
|
|
1091
|
+
const h = Math.max(1, Math.round(datum.value / scale * height));
|
|
1092
|
+
const text = datum.title ?? `${s.key}: ${datum.value.toLocaleString()}`;
|
|
1093
|
+
const pointIndex = i * series.length + seriesIndex;
|
|
1094
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1095
|
+
"div",
|
|
1096
|
+
{
|
|
1097
|
+
className: `dq-bar${pinned === pointIndex ? " dq-bar--pinned" : ""}`,
|
|
1098
|
+
style: { height: `${h}px`, background: s.color },
|
|
1099
|
+
role: "img",
|
|
1100
|
+
"aria-label": text,
|
|
1101
|
+
"aria-keyshortcuts": "ArrowLeft ArrowRight ArrowUp ArrowDown Home End",
|
|
1102
|
+
tabIndex: pointIndex === roving.active ? 0 : -1,
|
|
1103
|
+
ref: (element) => {
|
|
1104
|
+
roving.refs.current[pointIndex] = element;
|
|
1105
|
+
},
|
|
1106
|
+
onPointerMove: (e) => show(e, text),
|
|
1107
|
+
onPointerDown: onPointStart,
|
|
1108
|
+
onPointerUp: (e) => onPointEnd(e, pointIndex, text),
|
|
1109
|
+
onFocus: (e) => showFocused(e, text),
|
|
1110
|
+
onBlur: hide,
|
|
1111
|
+
onKeyDown: (e) => roving.onKeyDown(e, pointIndex)
|
|
1041
1112
|
},
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
}) }),
|
|
1052
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-label", style: { visibility: i % labelEvery === 0 ? "visible" : "hidden" }, children: series[0].bars[i].label })
|
|
1053
|
-
]
|
|
1054
|
-
},
|
|
1055
|
-
i
|
|
1056
|
-
)) }) }),
|
|
1113
|
+
s.key
|
|
1114
|
+
);
|
|
1115
|
+
}) }),
|
|
1116
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-bar-label", style: { visibility: i % labelEvery === 0 ? "visible" : "hidden" }, children: series[0].bars[i].label })
|
|
1117
|
+
]
|
|
1118
|
+
},
|
|
1119
|
+
i
|
|
1120
|
+
)) }) })
|
|
1121
|
+
] }),
|
|
1057
1122
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChartTip, { tip })
|
|
1058
1123
|
] });
|
|
1059
1124
|
}
|
|
@@ -1102,7 +1167,7 @@ function Heatmap(props) {
|
|
|
1102
1167
|
if (datum === null) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dq-heat-cell dq-heat-cell--pad" }, d);
|
|
1103
1168
|
const text = t("chart.tooltip", {
|
|
1104
1169
|
head: datum.date,
|
|
1105
|
-
tokens: datum.total.toLocaleString(locale
|
|
1170
|
+
tokens: datum.total.toLocaleString(intlLocale(locale)),
|
|
1106
1171
|
cost: datum.cost.toFixed(2),
|
|
1107
1172
|
calls: t("common.calls", { count: datum.calls })
|
|
1108
1173
|
});
|
|
@@ -1395,6 +1460,14 @@ function viewOrderForIndex(entries, index, selfId = "balance", fallback = 20) {
|
|
|
1395
1460
|
return previous >= next ? previous + 0.5 : (previous + next) / 2;
|
|
1396
1461
|
}
|
|
1397
1462
|
|
|
1463
|
+
// src/client/top-up.ts
|
|
1464
|
+
var TOP_UP_URL = "https://platform.deepseek.com/top_up";
|
|
1465
|
+
var TOP_UP_FLOOR = 10;
|
|
1466
|
+
function needsTopUp(balance, warningLine) {
|
|
1467
|
+
if (!Number.isFinite(balance)) return false;
|
|
1468
|
+
return balance < TOP_UP_FLOOR || warningLine > 0 && balance < warningLine;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1398
1471
|
// src/client/dashboard.tsx
|
|
1399
1472
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1400
1473
|
var COMPOSER_FALLBACK_PX = 126;
|
|
@@ -1456,9 +1529,10 @@ function Delta(props) {
|
|
|
1456
1529
|
const title = `${label} \xA5${fmt(previous)}`;
|
|
1457
1530
|
if (percent === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta dq-delta--flat", title, children: t("delta.flat") });
|
|
1458
1531
|
const up = percent > 0;
|
|
1532
|
+
const magnitude = Math.abs(percent) >= 1e3 ? "999+" : String(Math.abs(percent));
|
|
1459
1533
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: `dq-delta ${up ? "dq-delta--up" : "dq-delta--down"}`, title, children: [
|
|
1460
1534
|
up ? "\u2191" : "\u2193",
|
|
1461
|
-
|
|
1535
|
+
magnitude,
|
|
1462
1536
|
"% ",
|
|
1463
1537
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-delta-label", children: label })
|
|
1464
1538
|
] });
|
|
@@ -1569,7 +1643,7 @@ function CoverageDiagnostics(props) {
|
|
|
1569
1643
|
const { t, locale } = useI18n();
|
|
1570
1644
|
const { coverage } = props;
|
|
1571
1645
|
const hasGaps = coverage.failedSessions > 0 || coverage.skippedRecords > 0 || coverage.scannedSessions < coverage.listedSessions;
|
|
1572
|
-
const coverageTime = new Intl.DateTimeFormat(locale
|
|
1646
|
+
const coverageTime = new Intl.DateTimeFormat(intlLocale(locale), {
|
|
1573
1647
|
timeZone: "Asia/Shanghai",
|
|
1574
1648
|
year: "numeric",
|
|
1575
1649
|
month: "numeric",
|
|
@@ -1616,6 +1690,137 @@ function CardTitle(props) {
|
|
|
1616
1690
|
props.caption !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-card-caption", children: props.caption })
|
|
1617
1691
|
] });
|
|
1618
1692
|
}
|
|
1693
|
+
function SectionNav(props) {
|
|
1694
|
+
const { rootRef, label, sections } = props;
|
|
1695
|
+
const [targets, setTargets] = (0, import_react4.useState)([]);
|
|
1696
|
+
const [active, setActive] = (0, import_react4.useState)(0);
|
|
1697
|
+
const [pos, setPos] = (0, import_react4.useState)({ right: 0, visible: false });
|
|
1698
|
+
const signature = sections.map((section) => `${section.key}:${section.label}`).join("|");
|
|
1699
|
+
(0, import_react4.useEffect)(() => {
|
|
1700
|
+
const root = rootRef.current;
|
|
1701
|
+
if (root === null) return;
|
|
1702
|
+
const nodes = Array.from(root.querySelectorAll(":scope > .dq-card, :scope > .dq-card-pair"));
|
|
1703
|
+
setTargets(nodes.map((el, i) => ({ el, label: sections[i]?.label ?? "" })).filter((item) => item.label !== ""));
|
|
1704
|
+
}, [rootRef, signature]);
|
|
1705
|
+
(0, import_react4.useEffect)(() => {
|
|
1706
|
+
const root = rootRef.current;
|
|
1707
|
+
if (root === null) return;
|
|
1708
|
+
const scrollport = root.closest("[data-conversation-scroll]");
|
|
1709
|
+
const measure = () => {
|
|
1710
|
+
const rect = root.getBoundingClientRect();
|
|
1711
|
+
setPos({ right: window.innerWidth - rect.left + 12, visible: window.innerWidth >= 1200 && rect.left >= 170 });
|
|
1712
|
+
};
|
|
1713
|
+
measure();
|
|
1714
|
+
const scroller = scrollport ?? window;
|
|
1715
|
+
scroller.addEventListener("scroll", measure, { passive: true });
|
|
1716
|
+
window.addEventListener("resize", measure);
|
|
1717
|
+
return () => {
|
|
1718
|
+
scroller.removeEventListener("scroll", measure);
|
|
1719
|
+
window.removeEventListener("resize", measure);
|
|
1720
|
+
};
|
|
1721
|
+
}, [rootRef]);
|
|
1722
|
+
(0, import_react4.useEffect)(() => {
|
|
1723
|
+
if (targets.length === 0) return;
|
|
1724
|
+
const root = rootRef.current;
|
|
1725
|
+
if (root === null) return;
|
|
1726
|
+
const scrollport = root.closest("[data-conversation-scroll]");
|
|
1727
|
+
const onScroll = () => {
|
|
1728
|
+
const base = scrollport === null ? 0 : scrollport.getBoundingClientRect().top;
|
|
1729
|
+
let current = 0;
|
|
1730
|
+
targets.forEach((target, i) => {
|
|
1731
|
+
if (target.el.getBoundingClientRect().top - base <= 120) current = i;
|
|
1732
|
+
});
|
|
1733
|
+
setActive(current);
|
|
1734
|
+
};
|
|
1735
|
+
onScroll();
|
|
1736
|
+
const scroller = scrollport ?? window;
|
|
1737
|
+
scroller.addEventListener("scroll", onScroll, { passive: true });
|
|
1738
|
+
return () => scroller.removeEventListener("scroll", onScroll);
|
|
1739
|
+
}, [targets, rootRef]);
|
|
1740
|
+
if (targets.length < 3) return null;
|
|
1741
|
+
const go = (el) => {
|
|
1742
|
+
const root = rootRef.current;
|
|
1743
|
+
const scrollport = root?.closest("[data-conversation-scroll]");
|
|
1744
|
+
const reduce = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1745
|
+
const behavior = reduce ? "auto" : "smooth";
|
|
1746
|
+
if (scrollport !== null) {
|
|
1747
|
+
const base = scrollport.getBoundingClientRect().top;
|
|
1748
|
+
scrollport.scrollTo({ top: scrollport.scrollTop + el.getBoundingClientRect().top - base - 12, behavior });
|
|
1749
|
+
} else {
|
|
1750
|
+
window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 12, behavior });
|
|
1751
|
+
}
|
|
1752
|
+
};
|
|
1753
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("nav", { className: `dq-sectionnav${pos.visible ? "" : " dq-sectionnav--hidden"}`, style: { right: `${pos.right}px` }, "aria-label": label, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ol", { children: targets.map((target, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1754
|
+
"button",
|
|
1755
|
+
{
|
|
1756
|
+
type: "button",
|
|
1757
|
+
className: `dq-sectionnav-dot${i === active ? " dq-sectionnav-dot--active" : ""}`,
|
|
1758
|
+
"aria-current": i === active ? "true" : void 0,
|
|
1759
|
+
onClick: () => go(target.el),
|
|
1760
|
+
children: [
|
|
1761
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-sectionnav-label", children: target.label }),
|
|
1762
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-sectionnav-pip", "aria-hidden": "true" })
|
|
1763
|
+
]
|
|
1764
|
+
}
|
|
1765
|
+
) }, `${target.label}-${String(i)}`)) }) });
|
|
1766
|
+
}
|
|
1767
|
+
function Collapsible(props) {
|
|
1768
|
+
const ref = (0, import_react4.useRef)(null);
|
|
1769
|
+
const animationRef = (0, import_react4.useRef)(null);
|
|
1770
|
+
const open = props.open;
|
|
1771
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
1772
|
+
const el = ref.current;
|
|
1773
|
+
if (el === null) return;
|
|
1774
|
+
animationRef.current?.cancel();
|
|
1775
|
+
animationRef.current = null;
|
|
1776
|
+
const reduce = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1777
|
+
const canAnimate = typeof el.animate === "function" && !reduce;
|
|
1778
|
+
const reset = () => {
|
|
1779
|
+
el.style.height = "";
|
|
1780
|
+
el.style.overflow = "";
|
|
1781
|
+
el.style.opacity = "";
|
|
1782
|
+
};
|
|
1783
|
+
if (open) {
|
|
1784
|
+
if (!canAnimate) {
|
|
1785
|
+
el.style.display = "";
|
|
1786
|
+
reset();
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
el.style.display = "block";
|
|
1790
|
+
el.style.overflow = "hidden";
|
|
1791
|
+
el.style.height = "0px";
|
|
1792
|
+
el.style.opacity = "0";
|
|
1793
|
+
const height2 = el.scrollHeight;
|
|
1794
|
+
const animation2 = el.animate(
|
|
1795
|
+
[{ height: "0px", opacity: 0 }, { height: `${height2}px`, opacity: 1 }],
|
|
1796
|
+
{ duration: 300, easing: "cubic-bezier(0.16, 1, 0.3, 1)" }
|
|
1797
|
+
);
|
|
1798
|
+
animationRef.current = animation2;
|
|
1799
|
+
animation2.addEventListener("finish", reset);
|
|
1800
|
+
return () => animation2.cancel();
|
|
1801
|
+
}
|
|
1802
|
+
if (el.style.display === "none") return;
|
|
1803
|
+
if (!canAnimate) {
|
|
1804
|
+
reset();
|
|
1805
|
+
el.style.display = "none";
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
const height = el.getBoundingClientRect().height || el.scrollHeight;
|
|
1809
|
+
el.style.overflow = "hidden";
|
|
1810
|
+
el.style.height = `${height}px`;
|
|
1811
|
+
const animation = el.animate(
|
|
1812
|
+
[{ height: `${height}px`, opacity: 1 }, { height: "0px", opacity: 0 }],
|
|
1813
|
+
{ duration: 220, easing: "cubic-bezier(0.4, 0, 1, 1)" }
|
|
1814
|
+
);
|
|
1815
|
+
animationRef.current = animation;
|
|
1816
|
+
animation.addEventListener("finish", () => {
|
|
1817
|
+
reset();
|
|
1818
|
+
el.style.display = "none";
|
|
1819
|
+
});
|
|
1820
|
+
return () => animation.cancel();
|
|
1821
|
+
}, [open]);
|
|
1822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref, children: props.children });
|
|
1823
|
+
}
|
|
1619
1824
|
function Stat(props) {
|
|
1620
1825
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
1621
1826
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: props.label }),
|
|
@@ -1711,7 +1916,7 @@ function SessionUsageCard(props) {
|
|
|
1711
1916
|
const { t, locale } = useI18n();
|
|
1712
1917
|
const { data } = props;
|
|
1713
1918
|
if (data.calls === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("sessionUsage.empty") });
|
|
1714
|
-
const rangeTime = new Intl.DateTimeFormat(locale
|
|
1919
|
+
const rangeTime = new Intl.DateTimeFormat(intlLocale(locale), {
|
|
1715
1920
|
timeZone: "Asia/Shanghai",
|
|
1716
1921
|
year: "numeric",
|
|
1717
1922
|
month: "numeric",
|
|
@@ -1821,7 +2026,8 @@ function CacheCard(props) {
|
|
|
1821
2026
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-legend-swatch dq-legend-swatch--miss" }),
|
|
1822
2027
|
t("cache.missLegend", { tokens: fmtCompact(totals.input, locale) })
|
|
1823
2028
|
] })
|
|
1824
|
-
] })
|
|
2029
|
+
] }),
|
|
2030
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-cache-foot", children: t("cache.basis", { cost: fmt(totals.cost), miss: fmt(wouldHaveCost) }) })
|
|
1825
2031
|
] });
|
|
1826
2032
|
}
|
|
1827
2033
|
function VisionCard(props) {
|
|
@@ -1864,11 +2070,44 @@ function VisionCard(props) {
|
|
|
1864
2070
|
function PeakCard(props) {
|
|
1865
2071
|
const { t, locale } = useI18n();
|
|
1866
2072
|
const { split, pricing } = props;
|
|
2073
|
+
const [nowMs, setNowMs] = (0, import_react4.useState)(() => Date.now());
|
|
2074
|
+
(0, import_react4.useEffect)(() => {
|
|
2075
|
+
const id = setInterval(() => setNowMs(Date.now()), 6e4);
|
|
2076
|
+
return () => clearInterval(id);
|
|
2077
|
+
}, []);
|
|
1867
2078
|
const total = split.peak.total + split.offPeak.total;
|
|
1868
2079
|
if (total === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("peak.empty") });
|
|
1869
2080
|
const peakShare = split.peak.total / total;
|
|
1870
2081
|
const shiftSaving = split.peakEraCost - split.offPeakEraCost;
|
|
2082
|
+
const beijing = new Date(nowMs + 8 * 36e5);
|
|
2083
|
+
const hour = beijing.getUTCHours();
|
|
2084
|
+
const minute = beijing.getUTCMinutes();
|
|
2085
|
+
const weekday = beijing.getUTCDay();
|
|
2086
|
+
const weekend = weekday === 0 || weekday === 6;
|
|
2087
|
+
const windows = pricing.peakWindows.map((w) => w.match(/(\d{1,2}):\d{2}\D+(\d{1,2}):\d{2}/)).filter((m) => m !== null).map((m) => [Number(m[1]), Number(m[2])]);
|
|
2088
|
+
const isPeakNow = !weekend && windows.some(([from, to]) => hour >= from && hour < to);
|
|
2089
|
+
const nowPercent = (hour + minute / 60) / 24 * 100;
|
|
2090
|
+
const clockCells = Array.from({ length: 24 }, (_, h) => !weekend && windows.some(([from, to]) => h >= from && h < to));
|
|
2091
|
+
const nowState = isPeakNow ? t("peak.nowPeak") : t("peak.nowOff");
|
|
1871
2092
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-peak", children: [
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock", children: [
|
|
2094
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock-head", children: [
|
|
2095
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-clock-title", children: t("peak.clockTitle") }),
|
|
2096
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-clock-status ${isPeakNow ? "dq-clock-status--peak" : "dq-clock-status--off"}`, children: nowState })
|
|
2097
|
+
] }),
|
|
2098
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock-track", role: "img", "aria-label": t("peak.clockAria", { state: nowState }), children: [
|
|
2099
|
+
clockCells.map((peak, h) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-clock-cell${peak ? " dq-clock-cell--peak" : ""}` }, h)),
|
|
2100
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-clock-now", style: { left: `${nowPercent}%` }, "aria-hidden": "true" })
|
|
2101
|
+
] }),
|
|
2102
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-clock-scale", "aria-hidden": "true", children: [
|
|
2103
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "0" }),
|
|
2104
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "6" }),
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "12" }),
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "18" }),
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "24" })
|
|
2108
|
+
] }),
|
|
2109
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-clock-hint", children: t("peak.clockHint") })
|
|
2110
|
+
] }),
|
|
1872
2111
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1873
2112
|
"div",
|
|
1874
2113
|
{
|
|
@@ -1898,12 +2137,9 @@ function PeakCard(props) {
|
|
|
1898
2137
|
] }),
|
|
1899
2138
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-peak-figures", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
1900
2139
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("peak.shift") }),
|
|
1901
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-peak-delta dq-peak-delta--save", children: t("peak.saving", { amount: fmt(shiftSaving) }) })
|
|
1905
|
-
] })
|
|
1906
|
-
] }) })
|
|
2140
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-value dq-stat-value--ok", children: t("peak.saving", { amount: fmt(shiftSaving) }) })
|
|
2141
|
+
] }) }),
|
|
2142
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-peak-foot", children: t("peak.basis", { total: fmt(split.offPeakEraCost) }) })
|
|
1907
2143
|
] });
|
|
1908
2144
|
}
|
|
1909
2145
|
function agoText(ms, t) {
|
|
@@ -2143,6 +2379,7 @@ function BalanceDashboard(props) {
|
|
|
2143
2379
|
const [usageUpdatedAt, setUsageUpdatedAt] = (0, import_react4.useState)(cachedUsageAt);
|
|
2144
2380
|
const [syncState, setSyncState] = (0, import_react4.useState)(cachedUsageAt === null ? "syncing" : "cached");
|
|
2145
2381
|
const [freshnessNow, setFreshnessNow] = (0, import_react4.useState)(Date.now());
|
|
2382
|
+
const [settingsOpen, setSettingsOpen] = (0, import_react4.useState)(false);
|
|
2146
2383
|
const hadDataRef = (0, import_react4.useRef)(cachedBalance !== null || cachedUsage !== null);
|
|
2147
2384
|
const [widgetOn, setWidgetOn] = (0, import_react4.useState)(widgetVisibleStore.get());
|
|
2148
2385
|
const [widgetTabIds, setWidgetTabIds] = (0, import_react4.useState)(widgetTabIdsStore.get());
|
|
@@ -2401,6 +2638,7 @@ function BalanceDashboard(props) {
|
|
|
2401
2638
|
const avgDailyCost = recentDays.length > 0 ? recentDays.reduce((sum, d) => sum + d.cost, 0) / recentDays.length : 0;
|
|
2402
2639
|
const balanceValue = primary !== null ? Number(primary.total) : Number.NaN;
|
|
2403
2640
|
const balanceLow = lowBalance > 0 && Number.isFinite(balanceValue) && balanceValue < lowBalance;
|
|
2641
|
+
const topUpPrompt = needsTopUp(balanceValue, lowBalance);
|
|
2404
2642
|
const daysLeft = avgDailyCost > 0 && Number.isFinite(balanceValue) ? balanceValue / avgDailyCost : null;
|
|
2405
2643
|
const daysLeftText = daysLeft === null ? "\u2014" : daysLeft >= 365 ? t("balance.overYear") : t("balance.days", { count: daysLeft < 10 ? daysLeft.toFixed(1) : Math.round(daysLeft) });
|
|
2406
2644
|
const runwayTitle = daysLeft === null ? t("balance.noRunway", { days: recentDays.length || 7 }) : t("balance.runwayTitle", { days: recentDays.length, cost: fmt(avgDailyCost) });
|
|
@@ -2508,7 +2746,18 @@ function BalanceDashboard(props) {
|
|
|
2508
2746
|
// column, then narrows in place — no flash of the old width).
|
|
2509
2747
|
...composerWidth !== null ? { "--dq-composer-w": `${composerWidth}px` } : {}
|
|
2510
2748
|
};
|
|
2749
|
+
const navSections = [
|
|
2750
|
+
{ key: "balance", label: t("nav.balance") },
|
|
2751
|
+
...props.sessionId !== void 0 && props.sessionId !== "" ? [{ key: "session", label: t("nav.session") }] : [],
|
|
2752
|
+
{ key: "overview", label: t("nav.overview") },
|
|
2753
|
+
{ key: "trend", label: t("nav.trend") },
|
|
2754
|
+
{ key: "heatmap", label: t("nav.heatmap") },
|
|
2755
|
+
{ key: "models", label: t("nav.models") },
|
|
2756
|
+
{ key: "sessions", label: t("nav.sessions") },
|
|
2757
|
+
{ key: "peakcache", label: t("nav.peakcache") }
|
|
2758
|
+
];
|
|
2511
2759
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-balance", ref: rootRef, style: composerSafeAreaStyle, children: [
|
|
2760
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SectionNav, { rootRef, label: t("nav.label"), sections: navSections }),
|
|
2512
2761
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-row", children: [
|
|
2513
2762
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-messages", children: [
|
|
2514
2763
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
@@ -2517,7 +2766,7 @@ function BalanceDashboard(props) {
|
|
|
2517
2766
|
className: `dq-sync dq-sync--${syncState}`,
|
|
2518
2767
|
role: "status",
|
|
2519
2768
|
"aria-live": "polite",
|
|
2520
|
-
title: usageUpdatedAt === null ? t("status.neverSynced") : t("status.usageTime", { time: new Date(usageUpdatedAt).toLocaleString(locale
|
|
2769
|
+
title: usageUpdatedAt === null ? t("status.neverSynced") : t("status.usageTime", { time: new Date(usageUpdatedAt).toLocaleString(intlLocale(locale), { timeZone: "Asia/Shanghai" }) }),
|
|
2521
2770
|
children: [
|
|
2522
2771
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-sync-dot", "aria-hidden": "true" }),
|
|
2523
2772
|
syncStatusText(syncState, usageUpdatedAt, freshnessNow, t)
|
|
@@ -2527,23 +2776,39 @@ function BalanceDashboard(props) {
|
|
|
2527
2776
|
noticeText !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-warn", children: noticeText }),
|
|
2528
2777
|
errorText !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-error", children: errorText })
|
|
2529
2778
|
] }),
|
|
2530
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2779
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-status-actions", children: [
|
|
2780
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2781
|
+
"button",
|
|
2782
|
+
{
|
|
2783
|
+
type: "button",
|
|
2784
|
+
className: "dq-settings-btn",
|
|
2785
|
+
"aria-expanded": settingsOpen,
|
|
2786
|
+
"aria-controls": "dq-settings-panel",
|
|
2787
|
+
onClick: () => setSettingsOpen((open) => !open),
|
|
2788
|
+
children: [
|
|
2789
|
+
t("settings.title"),
|
|
2790
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-settings-caret${settingsOpen ? " dq-settings-caret--open" : ""}`, "aria-hidden": "true", children: "\u25BE" })
|
|
2791
|
+
]
|
|
2792
|
+
}
|
|
2793
|
+
),
|
|
2794
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2795
|
+
"button",
|
|
2796
|
+
{
|
|
2797
|
+
type: "button",
|
|
2798
|
+
className: "dq-refresh-btn",
|
|
2799
|
+
title: t("status.forceRefresh"),
|
|
2800
|
+
disabled: refreshing,
|
|
2801
|
+
"aria-busy": refreshing,
|
|
2802
|
+
onClick: () => {
|
|
2803
|
+
void load(true);
|
|
2804
|
+
},
|
|
2805
|
+
children: [
|
|
2806
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dq-refresh-icon${refreshing ? " dq-refresh-icon--spin" : ""}`, children: "\u21BB" }),
|
|
2807
|
+
refreshing ? t("status.refreshing") : t("status.refresh")
|
|
2808
|
+
]
|
|
2809
|
+
}
|
|
2810
|
+
)
|
|
2811
|
+
] })
|
|
2547
2812
|
] }),
|
|
2548
2813
|
balanceLow && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-alert", role: "status", children: [
|
|
2549
2814
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-alert-icon", "aria-hidden": "true", children: "!" }),
|
|
@@ -2551,7 +2816,7 @@ function BalanceDashboard(props) {
|
|
|
2551
2816
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: t("alert.lowTitle", { balance: fmt(balanceValue), currency: primary?.currency, threshold: fmt(lowBalance) }) }),
|
|
2552
2817
|
daysLeft !== null && t("alert.runway", { days: daysLeftText }),
|
|
2553
2818
|
t("common.period"),
|
|
2554
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { className: "dq-alert-link", href:
|
|
2819
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { className: "dq-alert-link", href: TOP_UP_URL, target: "_blank", rel: "noreferrer", children: t("alert.topUp") })
|
|
2555
2820
|
] })
|
|
2556
2821
|
] }),
|
|
2557
2822
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--hero", children: [
|
|
@@ -2592,33 +2857,177 @@ function BalanceDashboard(props) {
|
|
|
2592
2857
|
] }),
|
|
2593
2858
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-stat", children: [
|
|
2594
2859
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-label", children: t("balance.runway") }),
|
|
2595
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
e
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2860
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-runway-line", children: [
|
|
2861
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2862
|
+
"details",
|
|
2863
|
+
{
|
|
2864
|
+
className: `dq-stat-value dq-runway${daysLeft !== null && daysLeft < 3 ? " dq-stat-value--bad" : ""}`,
|
|
2865
|
+
open: runwayHovering || runwayManuallyOpened,
|
|
2866
|
+
onPointerEnter: () => setRunwayHovering(true),
|
|
2867
|
+
onPointerLeave: () => setRunwayHovering(false),
|
|
2868
|
+
children: [
|
|
2869
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2870
|
+
"summary",
|
|
2871
|
+
{
|
|
2872
|
+
title: t("balance.runwayBreakdownTitle"),
|
|
2873
|
+
onClick: (e) => {
|
|
2874
|
+
e.preventDefault();
|
|
2875
|
+
setRunwayManuallyOpened((v) => !v);
|
|
2876
|
+
},
|
|
2877
|
+
children: daysLeftText
|
|
2878
|
+
}
|
|
2879
|
+
),
|
|
2880
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-runway-detail", children: runwayTitle })
|
|
2881
|
+
]
|
|
2882
|
+
}
|
|
2883
|
+
),
|
|
2884
|
+
topUpPrompt && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("a", { className: "dq-topup", href: TOP_UP_URL, target: "_blank", rel: "noreferrer", title: t("balance.topUpTitle"), children: [
|
|
2885
|
+
t("alert.topUp"),
|
|
2886
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-topup-arrow", "aria-hidden": "true", children: "\u2197" })
|
|
2887
|
+
] })
|
|
2888
|
+
] })
|
|
2618
2889
|
] }),
|
|
2619
2890
|
balance?.isAvailable === false && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("common.status"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-stat-value dq-stat-value--bad", children: t("common.unavailable") }) })
|
|
2620
2891
|
] }) : loadingBalance ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BalanceSkeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("balance.empty") })
|
|
2621
2892
|
] }),
|
|
2893
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Collapsible, { open: settingsOpen, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-settings-panel", id: "dq-settings-panel", role: "region", "aria-label": t("settings.title"), children: [
|
|
2894
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("settings.title") }),
|
|
2895
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
2896
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("settings.groupDisplay") }),
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-toggle", children: [
|
|
2898
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", checked: widgetOn, onChange: toggle }),
|
|
2899
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("settings.widget") })
|
|
2900
|
+
] }),
|
|
2901
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-toggle-hint", children: t("settings.widgetHint") }),
|
|
2902
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting dq-setting--tabs", children: [
|
|
2903
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-widget-tabs-label", children: t("settings.widgetTabs") }),
|
|
2904
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control dq-widget-tabs", role: "group", "aria-labelledby": "dq-widget-tabs-label", children: [
|
|
2905
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-widget-tab-list", children: widgetTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-widget-tab", children: [
|
|
2906
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2907
|
+
"input",
|
|
2908
|
+
{
|
|
2909
|
+
type: "checkbox",
|
|
2910
|
+
checked: widgetTabIds === null || widgetTabIds.includes(tab.id),
|
|
2911
|
+
onChange: () => toggleWidgetTab(tab.id)
|
|
2912
|
+
}
|
|
2913
|
+
),
|
|
2914
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: tab.label })
|
|
2915
|
+
] }, tab.id)) }),
|
|
2916
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: widgetTabs.length === 0 ? t("settings.widgetTabsEmpty") : t("settings.widgetTabsHint") })
|
|
2917
|
+
] })
|
|
2918
|
+
] })
|
|
2919
|
+
] }),
|
|
2920
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
2921
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("settings.groupAlerts") }),
|
|
2922
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2923
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-low-balance", children: t("settings.lowBalance") }),
|
|
2924
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2925
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2926
|
+
"input",
|
|
2927
|
+
{
|
|
2928
|
+
id: "dq-low-balance",
|
|
2929
|
+
className: "dq-number",
|
|
2930
|
+
type: "number",
|
|
2931
|
+
min: 0,
|
|
2932
|
+
step: 1,
|
|
2933
|
+
value: lowBalance,
|
|
2934
|
+
onChange: (e) => changeLowBalance(Math.max(0, Number(e.target.value) || 0))
|
|
2935
|
+
}
|
|
2936
|
+
),
|
|
2937
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: lowBalance > 0 ? t("settings.lowBalanceOn", { amount: fmt(lowBalance), currency: primary?.currency ?? "CNY" }) : t("settings.lowBalanceOff") })
|
|
2938
|
+
] })
|
|
2939
|
+
] }),
|
|
2940
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2941
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-monthly-budget", children: t("settings.monthlyBudget") }),
|
|
2942
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2943
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2944
|
+
"input",
|
|
2945
|
+
{
|
|
2946
|
+
ref: budgetInputRef,
|
|
2947
|
+
id: "dq-monthly-budget",
|
|
2948
|
+
className: "dq-number",
|
|
2949
|
+
type: "number",
|
|
2950
|
+
min: 0,
|
|
2951
|
+
step: 10,
|
|
2952
|
+
value: monthlyBudget,
|
|
2953
|
+
"aria-describedby": "dq-monthly-budget-hint",
|
|
2954
|
+
onChange: (e) => changeMonthlyBudget(Math.max(0, Number(e.target.value) || 0))
|
|
2955
|
+
}
|
|
2956
|
+
),
|
|
2957
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-monthly-budget-hint", className: "dq-setting-hint", children: monthlyBudget > 0 ? t("settings.monthlyBudgetOn", { amount: fmt(monthlyBudget) }) : t("settings.monthlyBudgetOff") })
|
|
2958
|
+
] })
|
|
2959
|
+
] })
|
|
2960
|
+
] }),
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
2962
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("settings.groupLayout") }),
|
|
2963
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2964
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-sidebar-entry", children: t("settings.sidebarEntry") }),
|
|
2965
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2966
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2967
|
+
"select",
|
|
2968
|
+
{
|
|
2969
|
+
id: "dq-sidebar-entry",
|
|
2970
|
+
className: "dq-select",
|
|
2971
|
+
value: sidebarEntry,
|
|
2972
|
+
onChange: (e) => changeSidebarEntry(e.target.value),
|
|
2973
|
+
children: [
|
|
2974
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "off", children: t("settings.sidebarEntryOff") }),
|
|
2975
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "top", children: t("settings.sidebarEntryTop") }),
|
|
2976
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "bottom", children: t("settings.sidebarEntryBottom") })
|
|
2977
|
+
]
|
|
2978
|
+
}
|
|
2979
|
+
),
|
|
2980
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: t("settings.sidebarEntryHint") })
|
|
2981
|
+
] })
|
|
2982
|
+
] }),
|
|
2983
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2984
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-tab-order-label", children: t("settings.tabPosition") }),
|
|
2985
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2986
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2987
|
+
"div",
|
|
2988
|
+
{
|
|
2989
|
+
className: "dq-tab-order",
|
|
2990
|
+
role: "group",
|
|
2991
|
+
"aria-labelledby": "dq-tab-order-label",
|
|
2992
|
+
"aria-describedby": "dq-tab-order-hint",
|
|
2993
|
+
onDragOver: onOrderDragOver,
|
|
2994
|
+
onDrop: onOrderDrop,
|
|
2995
|
+
children: viewTabs.map((tab, index) => {
|
|
2996
|
+
const current = tab.id === "balance";
|
|
2997
|
+
const dropTarget = tabDragging && !current && tabDropTarget === index;
|
|
2998
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2999
|
+
"span",
|
|
3000
|
+
{
|
|
3001
|
+
className: `dq-tab-chip${current ? " dq-tab-chip--current" : ""}${current && tabDragging ? " dq-tab-chip--dragging" : ""}${dropTarget ? " dq-tab-chip--drop-target" : ""}`,
|
|
3002
|
+
draggable: current,
|
|
3003
|
+
tabIndex: current ? 0 : void 0,
|
|
3004
|
+
role: current ? "button" : void 0,
|
|
3005
|
+
"aria-label": current ? t("settings.tabDragAria") : void 0,
|
|
3006
|
+
onDragStart: current ? onQuotaTabDragStart : void 0,
|
|
3007
|
+
onDragEnd: current ? endTabDrag : void 0,
|
|
3008
|
+
onDragOver: (event) => onChipDragOver(event, index),
|
|
3009
|
+
onDrop: (event) => onChipDrop(event, index),
|
|
3010
|
+
onKeyDown: current ? onQuotaTabKeyDown : void 0,
|
|
3011
|
+
children: tab.label
|
|
3012
|
+
},
|
|
3013
|
+
tab.id
|
|
3014
|
+
);
|
|
3015
|
+
})
|
|
3016
|
+
}
|
|
3017
|
+
),
|
|
3018
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-tab-order-hint", className: "dq-setting-hint", children: t("settings.tabPositionHint") })
|
|
3019
|
+
] })
|
|
3020
|
+
] })
|
|
3021
|
+
] }),
|
|
3022
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-group", children: [
|
|
3023
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-group-title", children: t("links.title") }),
|
|
3024
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-links", children: [
|
|
3025
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/usage", children: t("links.usage") }),
|
|
3026
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/api_keys", children: t("links.apiKey") }),
|
|
3027
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://status.deepseek.com", children: t("links.status") })
|
|
3028
|
+
] })
|
|
3029
|
+
] })
|
|
3030
|
+
] }) }),
|
|
2622
3031
|
props.sessionId !== void 0 && props.sessionId !== "" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-card dq-card--primary", children: [
|
|
2623
3032
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { caption: t("common.estimated"), children: t("sessionUsage.title") }),
|
|
2624
3033
|
sessionUsage !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SessionUsageCard, { data: sessionUsage }) : loadingSessionUsage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SessionUsageSkeleton, {}) : sessionUsageError !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty dq-empty--error", children: localizeApiError(sessionUsageError, t, "error.query") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("sessionUsage.empty") })
|
|
@@ -2645,7 +3054,10 @@ function BalanceDashboard(props) {
|
|
|
2645
3054
|
{
|
|
2646
3055
|
spent: usage.summary.month.cost,
|
|
2647
3056
|
budget: monthlyBudget,
|
|
2648
|
-
onConfigure: () =>
|
|
3057
|
+
onConfigure: () => {
|
|
3058
|
+
setSettingsOpen(true);
|
|
3059
|
+
window.setTimeout(() => budgetInputRef.current?.focus(), 80);
|
|
3060
|
+
}
|
|
2649
3061
|
}
|
|
2650
3062
|
),
|
|
2651
3063
|
usage !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PricingNote, { pricing: usage.pricing })
|
|
@@ -2773,134 +3185,7 @@ function BalanceDashboard(props) {
|
|
|
2773
3185
|
] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-empty", children: t("cache.empty") })
|
|
2774
3186
|
] })
|
|
2775
3187
|
] }),
|
|
2776
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
2777
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardTitle, { children: t("settings.title") }),
|
|
2778
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-links-title", children: t("links.title") }),
|
|
2779
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-links", children: [
|
|
2780
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/usage", children: t("links.usage") }),
|
|
2781
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://platform.deepseek.com/api_keys", children: t("links.apiKey") }),
|
|
2782
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Link, { href: "https://status.deepseek.com", children: t("links.status") })
|
|
2783
|
-
] }),
|
|
2784
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-toggle", children: [
|
|
2785
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", checked: widgetOn, onChange: toggle }),
|
|
2786
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("settings.widget") })
|
|
2787
|
-
] }),
|
|
2788
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-toggle-hint", children: t("settings.widgetHint") }),
|
|
2789
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting dq-setting--tabs", children: [
|
|
2790
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-widget-tabs-label", children: t("settings.widgetTabs") }),
|
|
2791
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control dq-widget-tabs", role: "group", "aria-labelledby": "dq-widget-tabs-label", children: [
|
|
2792
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-widget-tab-list", children: widgetTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dq-widget-tab", children: [
|
|
2793
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2794
|
-
"input",
|
|
2795
|
-
{
|
|
2796
|
-
type: "checkbox",
|
|
2797
|
-
checked: widgetTabIds === null || widgetTabIds.includes(tab.id),
|
|
2798
|
-
onChange: () => toggleWidgetTab(tab.id)
|
|
2799
|
-
}
|
|
2800
|
-
),
|
|
2801
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: tab.label })
|
|
2802
|
-
] }, tab.id)) }),
|
|
2803
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: widgetTabs.length === 0 ? t("settings.widgetTabsEmpty") : t("settings.widgetTabsHint") })
|
|
2804
|
-
] })
|
|
2805
|
-
] }),
|
|
2806
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2807
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-low-balance", children: t("settings.lowBalance") }),
|
|
2808
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2809
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2810
|
-
"input",
|
|
2811
|
-
{
|
|
2812
|
-
id: "dq-low-balance",
|
|
2813
|
-
className: "dq-number",
|
|
2814
|
-
type: "number",
|
|
2815
|
-
min: 0,
|
|
2816
|
-
step: 1,
|
|
2817
|
-
value: lowBalance,
|
|
2818
|
-
onChange: (e) => changeLowBalance(Math.max(0, Number(e.target.value) || 0))
|
|
2819
|
-
}
|
|
2820
|
-
),
|
|
2821
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: lowBalance > 0 ? t("settings.lowBalanceOn", { amount: fmt(lowBalance), currency: primary?.currency ?? "CNY" }) : t("settings.lowBalanceOff") })
|
|
2822
|
-
] })
|
|
2823
|
-
] }),
|
|
2824
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2825
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-monthly-budget", children: t("settings.monthlyBudget") }),
|
|
2826
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2827
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2828
|
-
"input",
|
|
2829
|
-
{
|
|
2830
|
-
ref: budgetInputRef,
|
|
2831
|
-
id: "dq-monthly-budget",
|
|
2832
|
-
className: "dq-number",
|
|
2833
|
-
type: "number",
|
|
2834
|
-
min: 0,
|
|
2835
|
-
step: 10,
|
|
2836
|
-
value: monthlyBudget,
|
|
2837
|
-
"aria-describedby": "dq-monthly-budget-hint",
|
|
2838
|
-
onChange: (e) => changeMonthlyBudget(Math.max(0, Number(e.target.value) || 0))
|
|
2839
|
-
}
|
|
2840
|
-
),
|
|
2841
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-monthly-budget-hint", className: "dq-setting-hint", children: monthlyBudget > 0 ? t("settings.monthlyBudgetOn", { amount: fmt(monthlyBudget) }) : t("settings.monthlyBudgetOff") })
|
|
2842
|
-
] })
|
|
2843
|
-
] }),
|
|
2844
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2845
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "dq-setting-label", htmlFor: "dq-sidebar-entry", children: t("settings.sidebarEntry") }),
|
|
2846
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2847
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2848
|
-
"select",
|
|
2849
|
-
{
|
|
2850
|
-
id: "dq-sidebar-entry",
|
|
2851
|
-
className: "dq-select",
|
|
2852
|
-
value: sidebarEntry,
|
|
2853
|
-
onChange: (e) => changeSidebarEntry(e.target.value),
|
|
2854
|
-
children: [
|
|
2855
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "off", children: t("settings.sidebarEntryOff") }),
|
|
2856
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "top", children: t("settings.sidebarEntryTop") }),
|
|
2857
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "bottom", children: t("settings.sidebarEntryBottom") })
|
|
2858
|
-
]
|
|
2859
|
-
}
|
|
2860
|
-
),
|
|
2861
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dq-setting-hint", children: t("settings.sidebarEntryHint") })
|
|
2862
|
-
] })
|
|
2863
|
-
] }),
|
|
2864
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting", children: [
|
|
2865
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dq-setting-label", id: "dq-tab-order-label", children: t("settings.tabPosition") }),
|
|
2866
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dq-setting-control", children: [
|
|
2867
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2868
|
-
"div",
|
|
2869
|
-
{
|
|
2870
|
-
className: "dq-tab-order",
|
|
2871
|
-
role: "group",
|
|
2872
|
-
"aria-labelledby": "dq-tab-order-label",
|
|
2873
|
-
"aria-describedby": "dq-tab-order-hint",
|
|
2874
|
-
onDragOver: onOrderDragOver,
|
|
2875
|
-
onDrop: onOrderDrop,
|
|
2876
|
-
children: viewTabs.map((tab, index) => {
|
|
2877
|
-
const current = tab.id === "balance";
|
|
2878
|
-
const dropTarget = tabDragging && !current && tabDropTarget === index;
|
|
2879
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2880
|
-
"span",
|
|
2881
|
-
{
|
|
2882
|
-
className: `dq-tab-chip${current ? " dq-tab-chip--current" : ""}${current && tabDragging ? " dq-tab-chip--dragging" : ""}${dropTarget ? " dq-tab-chip--drop-target" : ""}`,
|
|
2883
|
-
draggable: current,
|
|
2884
|
-
tabIndex: current ? 0 : void 0,
|
|
2885
|
-
role: current ? "button" : void 0,
|
|
2886
|
-
"aria-label": current ? t("settings.tabDragAria") : void 0,
|
|
2887
|
-
onDragStart: current ? onQuotaTabDragStart : void 0,
|
|
2888
|
-
onDragEnd: current ? endTabDrag : void 0,
|
|
2889
|
-
onDragOver: (event) => onChipDragOver(event, index),
|
|
2890
|
-
onDrop: (event) => onChipDrop(event, index),
|
|
2891
|
-
onKeyDown: current ? onQuotaTabKeyDown : void 0,
|
|
2892
|
-
children: tab.label
|
|
2893
|
-
},
|
|
2894
|
-
tab.id
|
|
2895
|
-
);
|
|
2896
|
-
})
|
|
2897
|
-
}
|
|
2898
|
-
),
|
|
2899
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { id: "dq-tab-order-hint", className: "dq-setting-hint", children: t("settings.tabPositionHint") })
|
|
2900
|
-
] })
|
|
2901
|
-
] })
|
|
2902
|
-
] }),
|
|
2903
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.8.1" })
|
|
3188
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dq-footer", children: "@cassius0924/dsh-usage-dashboard v0.9.0" })
|
|
2904
3189
|
] });
|
|
2905
3190
|
}
|
|
2906
3191
|
|
|
@@ -3350,6 +3635,17 @@ var css = `
|
|
|
3350
3635
|
.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)}
|
|
3351
3636
|
.dq-remaining[open] .dq-remaining-breakdown{display:flex}
|
|
3352
3637
|
.dq-remaining-granted{color:var(--dsw-alias-label-secondary,#59636e);font-weight:400;font-size:12px}
|
|
3638
|
+
/* Low-balance top-up shortcut beside the runway value. It is an <a>, not a
|
|
3639
|
+
<button>, because it leaves the app; the \u2197 glyph states that. Colours come
|
|
3640
|
+
from the host's primary-button tokens (the same pair DSH's own filled
|
|
3641
|
+
buttons use) so the page keeps exactly one filled call to action instead of
|
|
3642
|
+
reading as another filter chip. Height stays on the runway text line so the
|
|
3643
|
+
hero row does not grow. */
|
|
3644
|
+
.dq-runway-line{display:flex;align-items:center;flex-wrap:wrap;gap:4px 10px}
|
|
3645
|
+
.dq-topup{display:inline-flex;align-items:center;gap:2px;height:26px;padding:0 10px;border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-button-primary-fill,#0969da);color:var(--dsw-alias-label-primary-foreground,#fff);font-size:12px;font-weight:600;line-height:1;text-decoration:none;white-space:nowrap;transition:background .15s ease}
|
|
3646
|
+
.dq-topup:hover{background:var(--dsw-alias-button-primary-hover,#0757ba)}
|
|
3647
|
+
.dq-topup:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3648
|
+
.dq-topup-arrow{font-size:10px;line-height:1;transform:translateY(-1px)}
|
|
3353
3649
|
/* narrow screens: .dq-balance-grid wraps its 3 stats onto separate lines, so the absolutely-positioned
|
|
3354
3650
|
.dq-remaining-breakdown/.dq-runway-detail popovers can float down over whichever stat now sits in the
|
|
3355
3651
|
next row (e.g. "\u4F59\u989D\u660E\u7EC6" open covering "\u9884\u8BA1\u53EF\u7528"'s clickable summary) \u2014 force each stat onto its own
|
|
@@ -3403,7 +3699,11 @@ var css = `
|
|
|
3403
3699
|
.dq-pricing-table{border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums;min-width:100%}
|
|
3404
3700
|
.dq-pricing-table th{text-align:left;font-weight:500;color:var(--dsw-alias-label-secondary,#59636e);padding:4px 12px 4px 0;white-space:nowrap;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}
|
|
3405
3701
|
.dq-pricing-table td{padding:4px 12px 4px 0;color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;vertical-align:top}
|
|
3406
|
-
|
|
3702
|
+
/* Align by counting from the end: a peak row starts with the rowSpan'd model
|
|
3703
|
+
cell (5 cells) while its off-peak pair starts at \u65F6\u6BB5 (4 cells), so
|
|
3704
|
+
nth-child(n+3) left the off-peak cache-hit price stranded under \u65F6\u6BB5. The
|
|
3705
|
+
last three cells are the three price columns in both row shapes. */
|
|
3706
|
+
.dq-pricing-table th:nth-last-child(-n+3),.dq-pricing-table td:nth-last-child(-n+3){text-align:right}
|
|
3407
3707
|
.dq-pricing-table th:last-child,.dq-pricing-table td:last-child{padding-right:0}
|
|
3408
3708
|
.dq-pricing-table tbody td:first-child{font-weight:500}
|
|
3409
3709
|
.dq-pricing-table tbody tr+tr td{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.05))}
|
|
@@ -3435,8 +3735,23 @@ var css = `
|
|
|
3435
3735
|
.dq-legend-swatch--peak{background:var(--dsw-alias-state-warning-primary,#bf8700)}
|
|
3436
3736
|
.dq-legend-swatch--offpeak{background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85}
|
|
3437
3737
|
.dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:16px}
|
|
3438
|
-
.dq-peak-
|
|
3439
|
-
.dq-
|
|
3738
|
+
.dq-peak-foot{margin:12px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e);text-wrap:pretty}
|
|
3739
|
+
.dq-clock{margin-bottom:16px}
|
|
3740
|
+
.dq-clock-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:8px}
|
|
3741
|
+
.dq-clock-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
3742
|
+
.dq-clock-status{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;padding:2px 8px;border-radius:999px}
|
|
3743
|
+
.dq-clock-status::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor}
|
|
3744
|
+
.dq-clock-status--peak{color:var(--dq-text-warn);background:rgba(191,135,0,.12)}
|
|
3745
|
+
.dq-clock-status--off{color:var(--dq-text-success);background:rgba(45,164,78,.12)}
|
|
3746
|
+
.dq-clock-track{position:relative;display:flex;gap:1px;height:10px}
|
|
3747
|
+
.dq-clock-cell{flex:1 1 0;height:100%;background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.55}
|
|
3748
|
+
.dq-clock-cell--peak{background:var(--dsw-alias-state-warning-primary,#bf8700);opacity:.92}
|
|
3749
|
+
.dq-clock-cell:first-child{border-radius:999px 0 0 999px}
|
|
3750
|
+
.dq-clock-cell:last-child{border-radius:0 999px 999px 0}
|
|
3751
|
+
.dq-clock-now{position:absolute;top:-3px;bottom:-3px;width:2px;background:var(--dsw-alias-label-primary,#1f2328);border-radius:2px;transform:translateX(-1px)}
|
|
3752
|
+
.dq-clock-now::before{content:'';position:absolute;top:-4px;left:50%;transform:translateX(-50%);width:6px;height:6px;border-radius:50%;background:var(--dsw-alias-label-primary,#1f2328)}
|
|
3753
|
+
.dq-clock-scale{display:flex;justify-content:space-between;margin-top:4px;font-size:10px;color:var(--dsw-alias-label-tertiary,#59636e);font-variant-numeric:tabular-nums}
|
|
3754
|
+
.dq-clock-hint{margin:6px 0 0;font-size:11px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
3440
3755
|
.dq-cache-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-bottom:12px}
|
|
3441
3756
|
@media (max-width:620px){.dq-cache-figures{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px 16px}.dq-cache-figures .dq-stat{min-width:0}}
|
|
3442
3757
|
.dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:500}
|
|
@@ -3502,12 +3817,29 @@ var css = `
|
|
|
3502
3817
|
.dq-setting-label{width:128px;font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}
|
|
3503
3818
|
.dq-setting-control{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
|
|
3504
3819
|
.dq-setting-hint{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);line-height:1.5}
|
|
3820
|
+
.dq-setting-group{margin-top:28px}
|
|
3821
|
+
.dq-setting-group:first-of-type{margin-top:0}
|
|
3822
|
+
.dq-setting-group-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:0 0 10px;letter-spacing:.02em}
|
|
3823
|
+
.dq-setting-group .dq-toggle{margin-top:0}
|
|
3824
|
+
.dq-setting-group .dq-setting{margin-top:16px}
|
|
3505
3825
|
/* Page footer: the package name + version, injected at build time from
|
|
3506
3826
|
package.json (see build.mjs) so it can never drift from the release. */
|
|
3507
3827
|
.dq-footer{margin:16px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}
|
|
3828
|
+
.dq-sectionnav{position:fixed;top:50%;transform:translateY(-50%);z-index:20;width:max-content;pointer-events:none}
|
|
3829
|
+
.dq-sectionnav--hidden{display:none}
|
|
3830
|
+
.dq-sectionnav ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px;pointer-events:auto}
|
|
3831
|
+
.dq-sectionnav li{display:flex;justify-content:flex-end}
|
|
3832
|
+
.dq-sectionnav-dot{display:inline-flex;align-items:center;justify-content:flex-end;gap:8px;padding:4px 2px;border:0;background:transparent;border-radius:6px;cursor:pointer;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1.2;white-space:nowrap;transition:color .15s ease,background .15s ease}
|
|
3833
|
+
.dq-sectionnav-dot:hover,.dq-sectionnav-dot:focus-visible{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10))}
|
|
3834
|
+
.dq-sectionnav-dot:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}
|
|
3835
|
+
.dq-sectionnav-label{font-size:12px;color:inherit}
|
|
3836
|
+
.dq-sectionnav-pip{flex:none;width:9px;height:9px;border-radius:50%;background:var(--dsw-alias-border-l2,rgba(0,0,0,.22));transition:background .15s ease,transform .15s ease}
|
|
3837
|
+
.dq-sectionnav-dot:hover .dq-sectionnav-pip{background:var(--dsw-alias-label-secondary,#59636e)}
|
|
3838
|
+
.dq-sectionnav-dot--active{color:var(--dsw-alias-label-primary,#1f2328);font-weight:600}
|
|
3839
|
+
.dq-sectionnav-dot--active .dq-sectionnav-pip{background:var(--dsw-alias-state-business-primary,#0969da);transform:scale(1.25)}
|
|
3840
|
+
@media (max-width:1199px){.dq-sectionnav{display:none}}
|
|
3508
3841
|
.dq-number{width:82px;padding:5px 8px;font-size:13px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}
|
|
3509
3842
|
.dq-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;border-color:transparent}
|
|
3510
|
-
.dq-links-title{font-size:13px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 12px}
|
|
3511
3843
|
.dq-links{display:flex;flex-wrap:wrap;gap:8px}
|
|
3512
3844
|
.dq-link{display:inline-flex;align-items:center;gap:4px;padding:8px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);text-decoration:none;font-size:13px;font-weight:500;cursor:pointer}
|
|
3513
3845
|
.dq-link::after{content:"\u2197";font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}
|
|
@@ -3574,7 +3906,10 @@ var css = `
|
|
|
3574
3906
|
.dq-chart-switch-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}
|
|
3575
3907
|
.dq-chart-switch-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.14)}
|
|
3576
3908
|
.dq-chart-wrap{position:relative}
|
|
3577
|
-
.dq-
|
|
3909
|
+
.dq-plot{display:flex;align-items:flex-end;gap:8px}
|
|
3910
|
+
.dq-yaxis{flex:none;width:46px;display:flex;flex-direction:column;justify-content:space-between;margin-bottom:18px;font-size:11px;line-height:1;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;text-align:right;pointer-events:none}
|
|
3911
|
+
.dq-yaxis span{white-space:nowrap}
|
|
3912
|
+
.dq-bars-scroll{flex:1 1 auto;min-width:0;overflow-x:auto;overflow-y:hidden;padding-bottom:2px}
|
|
3578
3913
|
.dq-tip{position:absolute;transform:translate(-50%,14px);z-index:40;pointer-events:none;background:var(--dsw-alias-tooltip-bg,#1f2328);color:var(--dsw-static-neutral-bluish-00,#fff);border-radius:8px;padding:7px 10px;font-size:12px;line-height:1.5;white-space:nowrap;box-shadow:0 6px 20px rgba(0,0,0,.28);font-variant-numeric:tabular-nums}
|
|
3579
3914
|
.dq-tip--above{transform:translate(-50%,calc(-100% - 8px))}
|
|
3580
3915
|
.dq-tip-head{font-weight:600;margin-bottom:2px}
|
|
@@ -3595,13 +3930,13 @@ var css = `
|
|
|
3595
3930
|
.dq-heat-cell--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}
|
|
3596
3931
|
.dq-bar-label{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);text-align:center;margin-top:4px;overflow:visible;white-space:nowrap}
|
|
3597
3932
|
.dq-heat{display:flex;flex-direction:column;gap:8px;max-width:100%}
|
|
3598
|
-
.dq-heat-scroll{overflow
|
|
3599
|
-
.dq-heatmap{display:flex;gap:
|
|
3600
|
-
.dq-heat-week{display:flex;flex:1 1 0;min-width:
|
|
3601
|
-
.dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:
|
|
3933
|
+
.dq-heat-scroll{overflow:hidden;padding-bottom:2px}
|
|
3934
|
+
.dq-heatmap{display:flex;gap:2px}
|
|
3935
|
+
.dq-heat-week{display:flex;flex:1 1 0;min-width:0;flex-direction:column;gap:2px}
|
|
3936
|
+
.dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:2px;flex:none;min-width:0}
|
|
3602
3937
|
.dq-heat-cell--pad{background:transparent}
|
|
3603
|
-
.dq-heat-months{display:flex;gap:
|
|
3604
|
-
.dq-heat-month{flex:1 1 0;min-width:
|
|
3938
|
+
.dq-heat-months{display:flex;gap:2px;margin-top:4px}
|
|
3939
|
+
.dq-heat-month{flex:1 1 0;min-width:0;font-size:12px;line-height:15px;color:var(--dsw-alias-label-secondary,#59636e);white-space:nowrap;overflow:visible}
|
|
3605
3940
|
.dq-heat-key{width:12px;height:12px;border-radius:2px;flex:none}
|
|
3606
3941
|
.dq-heat-scale{display:flex;align-items:center;gap:3px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);align-self:flex-end}
|
|
3607
3942
|
.dq-heat-scale>span:first-child{margin-right:2px}
|
|
@@ -3635,6 +3970,20 @@ var css = `
|
|
|
3635
3970
|
.dq-sync--error .dq-sync-dot{background:var(--dsw-alias-state-error-primary,#cf222e)}
|
|
3636
3971
|
@keyframes dq-sync-pulse{50%{opacity:.35;transform:scale(.8)}}
|
|
3637
3972
|
.dq-refresh-btn{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:5px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease}
|
|
3973
|
+
.dq-status-actions{display:flex;align-items:center;gap:8px;flex:none}
|
|
3974
|
+
.dq-settings-btn{display:inline-flex;align-items:center;gap:4px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:5px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease}
|
|
3975
|
+
.dq-settings-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}
|
|
3976
|
+
.dq-settings-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3977
|
+
.dq-settings-caret{font-size:10px;line-height:1;transition:transform .2s cubic-bezier(.16,1,.3,1)}
|
|
3978
|
+
.dq-settings-caret--open{transform:rotate(180deg)}
|
|
3979
|
+
.dq-settings-panel{box-sizing:border-box;min-width:0;max-width:100%;padding:20px;background:var(--dsw-alias-bg-layer-1,#ffffff);border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.10));border-radius:12px}
|
|
3980
|
+
/* Motion: eased (easeOutExpo) arrivals. Operate surface \u2014 fast, purposeful,
|
|
3981
|
+
and fully disabled under prefers-reduced-motion (see the global reduce block). */
|
|
3982
|
+
.dq-export-menu,.dq-model-menu{transform-origin:top right;animation:dq-pop .18s cubic-bezier(.16,1,.3,1)}
|
|
3983
|
+
@keyframes dq-pop{from{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:none}}
|
|
3984
|
+
.dq-alert{animation:dq-alert-in .28s cubic-bezier(.16,1,.3,1)}
|
|
3985
|
+
@keyframes dq-alert-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
|
|
3986
|
+
@media (max-width:620px){.dq-settings-panel{padding:16px}}
|
|
3638
3987
|
.dq-refresh-btn:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}
|
|
3639
3988
|
.dq-refresh-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}
|
|
3640
3989
|
.dq-refresh-btn:disabled{cursor:default;opacity:.6}
|