opencode-visual-cache 1.3.0 → 1.4.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/dist/tui.js CHANGED
@@ -13,7 +13,7 @@ import { createElement as _$createElement } from "@opentui/solid";
13
13
  import { createMemo, createSignal, createEffect, onMount, onCleanup, Show, untrack } from "solid-js";
14
14
 
15
15
  // src/_version.ts
16
- var PLUGIN_VERSION = "1.3.0";
16
+ var PLUGIN_VERSION = "1.4.0";
17
17
 
18
18
  // src/index.tsx
19
19
  function charColumns(c) {
@@ -89,7 +89,15 @@ var ZH_T = {
89
89
  distOut: "\u8F93\u51FA:",
90
90
  secDetail: "\u660E\u7EC6",
91
91
  secModel: "\u6A21\u578B",
92
- secSkills: "\u5DF2\u52A0\u8F7D\u6280\u80FD"
92
+ secSkills: "\u5DF2\u52A0\u8F7D\u6280\u80FD",
93
+ balTotal: "\u603B\u4F59\u989D:",
94
+ balNoKey: "\u672A\u914D\u7F6E API Key",
95
+ balLoading: "\u67E5\u8BE2\u4E2D...",
96
+ balError: "\u67E5\u8BE2\u5931\u8D25",
97
+ balErr401: "API Key \u65E0\u6548",
98
+ balErr403: "\u4F59\u989D\u67E5\u8BE2\u88AB\u62D2\u7EDD",
99
+ balErrEmpty: "\u672A\u83B7\u53D6\u5230\u4F59\u989D\u6570\u636E",
100
+ balErrTimeout: "\u67E5\u8BE2\u8D85\u65F6"
93
101
  };
94
102
  var EN_T = {
95
103
  title: "Token Cache",
@@ -120,7 +128,15 @@ var EN_T = {
120
128
  distOut: "Output:",
121
129
  secDetail: "Detail",
122
130
  secModel: "Model",
123
- secSkills: "Loaded Skills"
131
+ secSkills: "Loaded Skills",
132
+ balTotal: "Total:",
133
+ balNoKey: "No API Key set",
134
+ balLoading: "Fetching...",
135
+ balError: "Fetch failed",
136
+ balErr401: "Invalid API Key",
137
+ balErr403: "Balance request rejected",
138
+ balErrEmpty: "No balance data",
139
+ balErrTimeout: "Request timed out"
124
140
  };
125
141
  function rgb(raw) {
126
142
  if (typeof raw === "string" && raw.startsWith("#")) {
@@ -232,6 +248,38 @@ function estimateTokens(text) {
232
248
  const asciiPerToken = jsonLike ? 3.5 : codeLike ? 3.5 : 4;
233
249
  return Math.max(1, Math.ceil(ascii / asciiPerToken + cjk / 1));
234
250
  }
251
+ var BALANCE_POLL_MS = 5 * 60 * 1e3;
252
+ async function fetchDeepSeekBalance(apiKey, signal) {
253
+ const res = await fetch("https://api.deepseek.com/user/balance", {
254
+ headers: {
255
+ Authorization: `Bearer ${apiKey}`,
256
+ Accept: "application/json"
257
+ },
258
+ signal
259
+ });
260
+ if (!res.ok) {
261
+ if (res.status === 401) throw new Error("401");
262
+ if (res.status === 402 || res.status === 403) throw new Error("403");
263
+ throw new Error(String(res.status));
264
+ }
265
+ const json = await res.json();
266
+ const infos = json.balance_infos ?? [];
267
+ if (infos.length === 0) throw new Error("EMPTY");
268
+ return infos.map((info) => ({
269
+ currency: info.currency ?? "CNY",
270
+ total: info.total_balance ?? "0"
271
+ }));
272
+ }
273
+ function convertBalance(target, targetRate, amount, from) {
274
+ if (from === target) return amount;
275
+ const fromRate = DEFAULT_RATES[from] ?? 1;
276
+ const usd = from === "USD" ? amount : amount / fromRate;
277
+ return target === "USD" ? usd : usd * targetRate;
278
+ }
279
+ function balanceSymbol(currency) {
280
+ const sym = CURRENCIES[currency];
281
+ return sym ?? currency + " ";
282
+ }
235
283
  var CURRENCIES = {
236
284
  USD: "$",
237
285
  CNY: "\xA5",
@@ -279,6 +327,11 @@ function TokenCachePanel(props) {
279
327
  setSectionDist,
280
328
  sectionSkills,
281
329
  setSectionSkills,
330
+ sectionBalance,
331
+ setSectionBalance,
332
+ balanceRefresh,
333
+ balanceCurrency,
334
+ setBalanceCurrency,
282
335
  borderVisible,
283
336
  setBorderVisible
284
337
  } = props.signals;
@@ -329,6 +382,70 @@ function TokenCachePanel(props) {
329
382
  hasSkills: false
330
383
  });
331
384
  const [refreshTick, setRefreshTick] = createSignal(0);
385
+ const [balanceState, setBalanceState] = createSignal({
386
+ status: "idle",
387
+ data: null,
388
+ lastFetch: 0
389
+ });
390
+ let balanceSeq = 0;
391
+ const pollBalance = async () => {
392
+ const key = props.api.kv.get(`${KV_PREFIX}.ds_key`, "");
393
+ if (!key) {
394
+ setBalanceState({
395
+ status: "idle",
396
+ data: null,
397
+ lastFetch: 0,
398
+ error: void 0,
399
+ key: void 0
400
+ });
401
+ return;
402
+ }
403
+ const now = Date.now();
404
+ const prev = balanceState();
405
+ if (prev.status === "ok" && prev.key === key && now - prev.lastFetch < BALANCE_POLL_MS) return;
406
+ const seq = ++balanceSeq;
407
+ setBalanceState({
408
+ ...prev,
409
+ status: "loading",
410
+ error: void 0,
411
+ key
412
+ });
413
+ const controller = new AbortController();
414
+ let timedOut = false;
415
+ const timer = setTimeout(() => {
416
+ timedOut = true;
417
+ controller.abort();
418
+ }, 1e4);
419
+ try {
420
+ const data2 = await fetchDeepSeekBalance(key, controller.signal);
421
+ clearTimeout(timer);
422
+ if (seq !== balanceSeq) return;
423
+ setBalanceState({
424
+ status: "ok",
425
+ data: data2,
426
+ lastFetch: Date.now(),
427
+ error: void 0,
428
+ key
429
+ });
430
+ } catch (err) {
431
+ clearTimeout(timer);
432
+ if (seq !== balanceSeq) return;
433
+ const code = timedOut ? "TIMEOUT" : err instanceof Error ? err.message : "";
434
+ setBalanceState({
435
+ status: "error",
436
+ data: null,
437
+ lastFetch: 0,
438
+ error: code,
439
+ key
440
+ });
441
+ }
442
+ };
443
+ createEffect(() => {
444
+ void balanceRefresh();
445
+ untrack(() => {
446
+ void pollBalance();
447
+ });
448
+ });
332
449
  let lastMainSid = props.sessionId;
333
450
  createEffect(() => {
334
451
  const sid = props.sessionId;
@@ -570,10 +687,13 @@ function TokenCachePanel(props) {
570
687
  const rate = props.api.kv.get(`${KV_PREFIX}.rate`);
571
688
  if (typeof sym === "string") setCurrencySymbol(sym);
572
689
  if (typeof rate === "number" && rate > 0) setExchangeRate(rate);
690
+ const balCur = props.api.kv.get(`${KV_PREFIX}.balance_currency`);
691
+ if (typeof balCur === "string") setBalanceCurrency(balCur);
573
692
  setSectionDetail(Boolean(props.api.kv.get(`${KV_PREFIX}.section.detail`, true)));
574
693
  setSectionModel(Boolean(props.api.kv.get(`${KV_PREFIX}.section.model`, true)));
575
694
  setSectionDist(Boolean(props.api.kv.get(`${KV_PREFIX}.section.dist`, true)));
576
695
  setSectionSkills(Boolean(props.api.kv.get(`${KV_PREFIX}.section.skills`, true)));
696
+ setSectionBalance(Boolean(props.api.kv.get(`${KV_PREFIX}.section.balance`, true)));
577
697
  const bv = props.api.kv.get(`${KV_PREFIX}.border`, true);
578
698
  setBorderVisible(bv !== false);
579
699
  const savedLang = props.api.kv.get(`${KV_PREFIX}.lang`);
@@ -626,8 +746,10 @@ function TokenCachePanel(props) {
626
746
  setRefreshTick((v) => v + 1);
627
747
  });
628
748
  setRefreshTick((v) => v + 1);
749
+ const balanceTimer = setInterval(pollBalance, BALANCE_POLL_MS);
629
750
  onCleanup(() => {
630
751
  clearTimeout(partTimer);
752
+ clearInterval(balanceTimer);
631
753
  unsubPart();
632
754
  unsubMsg();
633
755
  unsubSession();
@@ -798,25 +920,25 @@ function TokenCachePanel(props) {
798
920
  const prefix = " \u21B3 " + (langZH() ? "\u5B50\u4EE3\u7406: " : "Sub: ");
799
921
  const maxSidW = Math.max(6, panelWidth() - visualWidth(prefix));
800
922
  return (() => {
801
- var _el$51 = _$createElement("text"), _el$52 = _$createElement("span"), _el$53 = _$createElement("span");
802
- _$insertNode(_el$51, _el$52);
803
- _$insertNode(_el$51, _el$53);
804
- _$insert(_el$52, prefix);
805
- _$insert(_el$53, () => truncateVisual(props.signals.overrideSessionId(), maxSidW));
923
+ var _el$64 = _$createElement("text"), _el$65 = _$createElement("span"), _el$66 = _$createElement("span");
924
+ _$insertNode(_el$64, _el$65);
925
+ _$insertNode(_el$64, _el$66);
926
+ _$insert(_el$65, prefix);
927
+ _$insert(_el$66, () => truncateVisual(props.signals.overrideSessionId(), maxSidW));
806
928
  _$effect((_p$) => {
807
- var _v$1 = {
929
+ var _v$15 = {
808
930
  fg: pal().muted
809
- }, _v$10 = {
931
+ }, _v$16 = {
810
932
  fg: pal().text
811
933
  };
812
- _v$1 !== _p$.e && (_p$.e = _$setProp(_el$52, "style", _v$1, _p$.e));
813
- _v$10 !== _p$.t && (_p$.t = _$setProp(_el$53, "style", _v$10, _p$.t));
934
+ _v$15 !== _p$.e && (_p$.e = _$setProp(_el$65, "style", _v$15, _p$.e));
935
+ _v$16 !== _p$.t && (_p$.t = _$setProp(_el$66, "style", _v$16, _p$.t));
814
936
  return _p$;
815
937
  }, {
816
938
  e: void 0,
817
939
  t: void 0
818
940
  });
819
- return _el$51;
941
+ return _el$64;
820
942
  })();
821
943
  })();
822
944
  }
@@ -826,30 +948,30 @@ function TokenCachePanel(props) {
826
948
  },
827
949
  get fallback() {
828
950
  return [(() => {
829
- var _el$54 = _$createElement("text");
830
- _$insert(_el$54, sep);
831
- _$effect((_$p) => _$setProp(_el$54, "fg", pal().muted, _$p));
832
- return _el$54;
951
+ var _el$67 = _$createElement("text");
952
+ _$insert(_el$67, sep);
953
+ _$effect((_$p) => _$setProp(_el$67, "fg", pal().muted, _$p));
954
+ return _el$67;
833
955
  })(), (() => {
834
- var _el$55 = _$createElement("text"), _el$56 = _$createElement("span"), _el$58 = _$createElement("span");
835
- _$insertNode(_el$55, _el$56);
836
- _$insertNode(_el$55, _el$58);
837
- _$insertNode(_el$56, _$createTextNode(`> `));
838
- _$insert(_el$58, () => t().noData);
956
+ var _el$68 = _$createElement("text"), _el$69 = _$createElement("span"), _el$71 = _$createElement("span");
957
+ _$insertNode(_el$68, _el$69);
958
+ _$insertNode(_el$68, _el$71);
959
+ _$insertNode(_el$69, _$createTextNode(`> `));
960
+ _$insert(_el$71, () => t().noData);
839
961
  _$effect((_p$) => {
840
- var _v$11 = {
962
+ var _v$17 = {
841
963
  fg: pal().muted
842
- }, _v$12 = {
964
+ }, _v$18 = {
843
965
  fg: pal().muted
844
966
  };
845
- _v$11 !== _p$.e && (_p$.e = _$setProp(_el$56, "style", _v$11, _p$.e));
846
- _v$12 !== _p$.t && (_p$.t = _$setProp(_el$58, "style", _v$12, _p$.t));
967
+ _v$17 !== _p$.e && (_p$.e = _$setProp(_el$69, "style", _v$17, _p$.e));
968
+ _v$18 !== _p$.t && (_p$.t = _$setProp(_el$71, "style", _v$18, _p$.t));
847
969
  return _p$;
848
970
  }, {
849
971
  e: void 0,
850
972
  t: void 0
851
973
  });
852
- return _el$55;
974
+ return _el$68;
853
975
  })()];
854
976
  },
855
977
  get children() {
@@ -1016,37 +1138,37 @@ function TokenCachePanel(props) {
1016
1138
  },
1017
1139
  get children() {
1018
1140
  return [_$memo(() => (() => {
1019
- var _el$59 = _$createElement("text"), _el$60 = _$createElement("span"), _el$61 = _$createElement("span"), _el$62 = _$createElement("b"), _el$63 = _$createElement("span");
1020
- _$insertNode(_el$59, _el$60);
1021
- _$insertNode(_el$59, _el$61);
1022
- _$insertNode(_el$59, _el$63);
1023
- _$setProp(_el$59, "onMouseUp", () => setModelOpen((o) => {
1141
+ var _el$72 = _$createElement("text"), _el$73 = _$createElement("span"), _el$74 = _$createElement("span"), _el$75 = _$createElement("b"), _el$76 = _$createElement("span");
1142
+ _$insertNode(_el$72, _el$73);
1143
+ _$insertNode(_el$72, _el$74);
1144
+ _$insertNode(_el$72, _el$76);
1145
+ _$setProp(_el$72, "onMouseUp", () => setModelOpen((o) => {
1024
1146
  const n = !o;
1025
1147
  persistFold("model", n);
1026
1148
  return n;
1027
1149
  }));
1028
- _$insert(_el$60, () => modelOpen() ? "\u25BC " : "\u25B6 ");
1029
- _$insertNode(_el$61, _el$62);
1030
- _$insert(_el$62, () => t().secModel);
1031
- _$insert(_el$63, () => sep().slice(visualWidth((modelOpen() ? "\u25BC " : "\u25B6 ") + t().secModel)));
1150
+ _$insert(_el$73, () => modelOpen() ? "\u25BC " : "\u25B6 ");
1151
+ _$insertNode(_el$74, _el$75);
1152
+ _$insert(_el$75, () => t().secModel);
1153
+ _$insert(_el$76, () => sep().slice(visualWidth((modelOpen() ? "\u25BC " : "\u25B6 ") + t().secModel)));
1032
1154
  _$effect((_p$) => {
1033
- var _v$13 = {
1155
+ var _v$19 = {
1034
1156
  fg: pal().muted
1035
- }, _v$14 = {
1157
+ }, _v$20 = {
1036
1158
  fg: pal().primary
1037
- }, _v$15 = {
1159
+ }, _v$21 = {
1038
1160
  fg: pal().muted
1039
1161
  };
1040
- _v$13 !== _p$.e && (_p$.e = _$setProp(_el$60, "style", _v$13, _p$.e));
1041
- _v$14 !== _p$.t && (_p$.t = _$setProp(_el$61, "style", _v$14, _p$.t));
1042
- _v$15 !== _p$.a && (_p$.a = _$setProp(_el$63, "style", _v$15, _p$.a));
1162
+ _v$19 !== _p$.e && (_p$.e = _$setProp(_el$73, "style", _v$19, _p$.e));
1163
+ _v$20 !== _p$.t && (_p$.t = _$setProp(_el$74, "style", _v$20, _p$.t));
1164
+ _v$21 !== _p$.a && (_p$.a = _$setProp(_el$76, "style", _v$21, _p$.a));
1043
1165
  return _p$;
1044
1166
  }, {
1045
1167
  e: void 0,
1046
1168
  t: void 0,
1047
1169
  a: void 0
1048
1170
  });
1049
- return _el$59;
1171
+ return _el$72;
1050
1172
  })()), _$createComponent(Show, {
1051
1173
  get when() {
1052
1174
  return modelOpen();
@@ -1119,37 +1241,37 @@ function TokenCachePanel(props) {
1119
1241
  },
1120
1242
  get children() {
1121
1243
  return [_$memo(() => (() => {
1122
- var _el$64 = _$createElement("text"), _el$65 = _$createElement("span"), _el$66 = _$createElement("span"), _el$67 = _$createElement("b"), _el$68 = _$createElement("span");
1123
- _$insertNode(_el$64, _el$65);
1124
- _$insertNode(_el$64, _el$66);
1125
- _$insertNode(_el$64, _el$68);
1126
- _$setProp(_el$64, "onMouseUp", () => setDistOpen((o) => {
1244
+ var _el$77 = _$createElement("text"), _el$78 = _$createElement("span"), _el$79 = _$createElement("span"), _el$80 = _$createElement("b"), _el$81 = _$createElement("span");
1245
+ _$insertNode(_el$77, _el$78);
1246
+ _$insertNode(_el$77, _el$79);
1247
+ _$insertNode(_el$77, _el$81);
1248
+ _$setProp(_el$77, "onMouseUp", () => setDistOpen((o) => {
1127
1249
  const n = !o;
1128
1250
  persistFold("dist", n);
1129
1251
  return n;
1130
1252
  }));
1131
- _$insert(_el$65, () => distOpen() ? "\u25BC " : "\u25B6 ");
1132
- _$insertNode(_el$66, _el$67);
1133
- _$insert(_el$67, () => t().distTitle);
1134
- _$insert(_el$68, () => sep().slice(visualWidth((distOpen() ? "\u25BC " : "\u25B6 ") + t().distTitle)));
1253
+ _$insert(_el$78, () => distOpen() ? "\u25BC " : "\u25B6 ");
1254
+ _$insertNode(_el$79, _el$80);
1255
+ _$insert(_el$80, () => t().distTitle);
1256
+ _$insert(_el$81, () => sep().slice(visualWidth((distOpen() ? "\u25BC " : "\u25B6 ") + t().distTitle)));
1135
1257
  _$effect((_p$) => {
1136
- var _v$16 = {
1258
+ var _v$22 = {
1137
1259
  fg: pal().muted
1138
- }, _v$17 = {
1260
+ }, _v$23 = {
1139
1261
  fg: pal().primary
1140
- }, _v$18 = {
1262
+ }, _v$24 = {
1141
1263
  fg: pal().muted
1142
1264
  };
1143
- _v$16 !== _p$.e && (_p$.e = _$setProp(_el$65, "style", _v$16, _p$.e));
1144
- _v$17 !== _p$.t && (_p$.t = _$setProp(_el$66, "style", _v$17, _p$.t));
1145
- _v$18 !== _p$.a && (_p$.a = _$setProp(_el$68, "style", _v$18, _p$.a));
1265
+ _v$22 !== _p$.e && (_p$.e = _$setProp(_el$78, "style", _v$22, _p$.e));
1266
+ _v$23 !== _p$.t && (_p$.t = _$setProp(_el$79, "style", _v$23, _p$.t));
1267
+ _v$24 !== _p$.a && (_p$.a = _$setProp(_el$81, "style", _v$24, _p$.a));
1146
1268
  return _p$;
1147
1269
  }, {
1148
1270
  e: void 0,
1149
1271
  t: void 0,
1150
1272
  a: void 0
1151
1273
  });
1152
- return _el$64;
1274
+ return _el$77;
1153
1275
  })()), _$createComponent(Show, {
1154
1276
  get when() {
1155
1277
  return distOpen();
@@ -1227,37 +1349,37 @@ function TokenCachePanel(props) {
1227
1349
  },
1228
1350
  get children() {
1229
1351
  return [_$memo(() => (() => {
1230
- var _el$69 = _$createElement("text"), _el$70 = _$createElement("span"), _el$71 = _$createElement("span"), _el$72 = _$createElement("b"), _el$73 = _$createElement("span"), _el$74 = _$createTextNode(` (`), _el$75 = _$createTextNode(`)`), _el$76 = _$createElement("span");
1231
- _$insertNode(_el$69, _el$70);
1232
- _$insertNode(_el$69, _el$71);
1233
- _$insertNode(_el$69, _el$73);
1234
- _$insertNode(_el$69, _el$76);
1235
- _$setProp(_el$69, "onMouseUp", () => setSkillsOpen((o) => {
1352
+ var _el$82 = _$createElement("text"), _el$83 = _$createElement("span"), _el$84 = _$createElement("span"), _el$85 = _$createElement("b"), _el$86 = _$createElement("span"), _el$87 = _$createTextNode(` (`), _el$88 = _$createTextNode(`)`), _el$89 = _$createElement("span");
1353
+ _$insertNode(_el$82, _el$83);
1354
+ _$insertNode(_el$82, _el$84);
1355
+ _$insertNode(_el$82, _el$86);
1356
+ _$insertNode(_el$82, _el$89);
1357
+ _$setProp(_el$82, "onMouseUp", () => setSkillsOpen((o) => {
1236
1358
  const n = !o;
1237
1359
  persistFold("skills", n);
1238
1360
  return n;
1239
1361
  }));
1240
- _$insert(_el$70, () => skillsOpen() ? "\u25BC " : "\u25B6 ");
1241
- _$insertNode(_el$71, _el$72);
1242
- _$insert(_el$72, () => t().secSkills);
1243
- _$insertNode(_el$73, _el$74);
1244
- _$insertNode(_el$73, _el$75);
1245
- _$insert(_el$73, () => data().skills.length, _el$75);
1246
- _$insert(_el$76, () => sep().slice(visualWidth((skillsOpen() ? "\u25BC " : "\u25B6 ") + t().secSkills + ` (${data().skills.length})`)));
1362
+ _$insert(_el$83, () => skillsOpen() ? "\u25BC " : "\u25B6 ");
1363
+ _$insertNode(_el$84, _el$85);
1364
+ _$insert(_el$85, () => t().secSkills);
1365
+ _$insertNode(_el$86, _el$87);
1366
+ _$insertNode(_el$86, _el$88);
1367
+ _$insert(_el$86, () => data().skills.length, _el$88);
1368
+ _$insert(_el$89, () => sep().slice(visualWidth((skillsOpen() ? "\u25BC " : "\u25B6 ") + t().secSkills + ` (${data().skills.length})`)));
1247
1369
  _$effect((_p$) => {
1248
- var _v$19 = {
1370
+ var _v$25 = {
1249
1371
  fg: pal().muted
1250
- }, _v$20 = {
1372
+ }, _v$26 = {
1251
1373
  fg: pal().primary
1252
- }, _v$21 = {
1374
+ }, _v$27 = {
1253
1375
  fg: pal().muted
1254
- }, _v$22 = {
1376
+ }, _v$28 = {
1255
1377
  fg: pal().muted
1256
1378
  };
1257
- _v$19 !== _p$.e && (_p$.e = _$setProp(_el$70, "style", _v$19, _p$.e));
1258
- _v$20 !== _p$.t && (_p$.t = _$setProp(_el$71, "style", _v$20, _p$.t));
1259
- _v$21 !== _p$.a && (_p$.a = _$setProp(_el$73, "style", _v$21, _p$.a));
1260
- _v$22 !== _p$.o && (_p$.o = _$setProp(_el$76, "style", _v$22, _p$.o));
1379
+ _v$25 !== _p$.e && (_p$.e = _$setProp(_el$83, "style", _v$25, _p$.e));
1380
+ _v$26 !== _p$.t && (_p$.t = _$setProp(_el$84, "style", _v$26, _p$.t));
1381
+ _v$27 !== _p$.a && (_p$.a = _$setProp(_el$86, "style", _v$27, _p$.a));
1382
+ _v$28 !== _p$.o && (_p$.o = _$setProp(_el$89, "style", _v$28, _p$.o));
1261
1383
  return _p$;
1262
1384
  }, {
1263
1385
  e: void 0,
@@ -1265,7 +1387,7 @@ function TokenCachePanel(props) {
1265
1387
  a: void 0,
1266
1388
  o: void 0
1267
1389
  });
1268
- return _el$69;
1390
+ return _el$82;
1269
1391
  })()), _$createComponent(Show, {
1270
1392
  get when() {
1271
1393
  return skillsOpen();
@@ -1276,10 +1398,10 @@ function TokenCachePanel(props) {
1276
1398
  const maxLabel = Math.max(4, panelWidth() - gutter() - rightW - 1);
1277
1399
  const label = truncateVisual(sk.name, maxLabel);
1278
1400
  return (() => {
1279
- var _el$77 = _$createElement("text");
1280
- _$insert(_el$77, () => justify(label, fmt(sk.tokens), t().tok));
1281
- _$effect((_$p) => _$setProp(_el$77, "fg", pal().muted, _$p));
1282
- return _el$77;
1401
+ var _el$90 = _$createElement("text");
1402
+ _$insert(_el$90, () => justify(label, fmt(sk.tokens), t().tok));
1403
+ _$effect((_$p) => _$setProp(_el$90, "fg", pal().muted, _$p));
1404
+ return _el$90;
1283
1405
  })();
1284
1406
  });
1285
1407
  }
@@ -1287,19 +1409,138 @@ function TokenCachePanel(props) {
1287
1409
  }
1288
1410
  });
1289
1411
  }
1412
+ }), _$createComponent(Show, {
1413
+ get when() {
1414
+ return sectionBalance();
1415
+ },
1416
+ get children() {
1417
+ return [(() => {
1418
+ var _el$51 = _$createElement("text");
1419
+ _$insert(_el$51, sep);
1420
+ _$effect((_$p) => _$setProp(_el$51, "fg", pal().muted, _$p));
1421
+ return _el$51;
1422
+ })(), _$createComponent(Show, {
1423
+ get when() {
1424
+ return balanceState().status === "idle";
1425
+ },
1426
+ get children() {
1427
+ var _el$52 = _$createElement("text"), _el$53 = _$createElement("span"), _el$55 = _$createElement("span");
1428
+ _$insertNode(_el$52, _el$53);
1429
+ _$insertNode(_el$52, _el$55);
1430
+ _$insertNode(_el$53, _$createTextNode(`> `));
1431
+ _$insert(_el$55, () => t().balNoKey);
1432
+ _$effect((_p$) => {
1433
+ var _v$9 = pal().muted, _v$0 = {
1434
+ fg: pal().muted
1435
+ };
1436
+ _v$9 !== _p$.e && (_p$.e = _$setProp(_el$52, "fg", _v$9, _p$.e));
1437
+ _v$0 !== _p$.t && (_p$.t = _$setProp(_el$53, "style", _v$0, _p$.t));
1438
+ return _p$;
1439
+ }, {
1440
+ e: void 0,
1441
+ t: void 0
1442
+ });
1443
+ return _el$52;
1444
+ }
1445
+ }), _$createComponent(Show, {
1446
+ get when() {
1447
+ return balanceState().status === "loading";
1448
+ },
1449
+ get children() {
1450
+ var _el$56 = _$createElement("text"), _el$57 = _$createElement("span"), _el$59 = _$createElement("span");
1451
+ _$insertNode(_el$56, _el$57);
1452
+ _$insertNode(_el$56, _el$59);
1453
+ _$insertNode(_el$57, _$createTextNode(`> `));
1454
+ _$insert(_el$59, () => t().balLoading);
1455
+ _$effect((_p$) => {
1456
+ var _v$1 = pal().muted, _v$10 = {
1457
+ fg: pal().muted
1458
+ };
1459
+ _v$1 !== _p$.e && (_p$.e = _$setProp(_el$56, "fg", _v$1, _p$.e));
1460
+ _v$10 !== _p$.t && (_p$.t = _$setProp(_el$57, "style", _v$10, _p$.t));
1461
+ return _p$;
1462
+ }, {
1463
+ e: void 0,
1464
+ t: void 0
1465
+ });
1466
+ return _el$56;
1467
+ }
1468
+ }), _$createComponent(Show, {
1469
+ get when() {
1470
+ return balanceState().status === "error";
1471
+ },
1472
+ get children() {
1473
+ var _el$60 = _$createElement("text"), _el$61 = _$createElement("span"), _el$63 = _$createElement("span");
1474
+ _$insertNode(_el$60, _el$61);
1475
+ _$insertNode(_el$60, _el$63);
1476
+ _$insertNode(_el$61, _$createTextNode(`> `));
1477
+ _$insert(_el$63, () => {
1478
+ const code = balanceState().error;
1479
+ if (code === "401") return t().balErr401;
1480
+ if (code === "403") return t().balErr403;
1481
+ if (code === "EMPTY") return t().balErrEmpty;
1482
+ if (code === "TIMEOUT") return t().balErrTimeout;
1483
+ return t().balError + (code ? ` (${code})` : "");
1484
+ });
1485
+ _$effect((_p$) => {
1486
+ var _v$11 = pal().error, _v$12 = {
1487
+ fg: pal().muted
1488
+ };
1489
+ _v$11 !== _p$.e && (_p$.e = _$setProp(_el$60, "fg", _v$11, _p$.e));
1490
+ _v$12 !== _p$.t && (_p$.t = _$setProp(_el$61, "style", _v$12, _p$.t));
1491
+ return _p$;
1492
+ }, {
1493
+ e: void 0,
1494
+ t: void 0
1495
+ });
1496
+ return _el$60;
1497
+ }
1498
+ }), _$createComponent(Show, {
1499
+ get when() {
1500
+ return _$memo(() => balanceState().status === "ok")() && balanceState().data;
1501
+ },
1502
+ get children() {
1503
+ return (() => {
1504
+ const list = balanceState().data;
1505
+ const pref = balanceCurrency();
1506
+ const native = pref ? list.find((x) => x.currency === pref) : void 0;
1507
+ if (native) {
1508
+ return (() => {
1509
+ var _el$91 = _$createElement("text");
1510
+ _$insert(_el$91, () => justify(t().balTotal, balanceSymbol(native.currency) + native.total));
1511
+ _$effect((_$p) => _$setProp(_el$91, "fg", pal().text, _$p));
1512
+ return _el$91;
1513
+ })();
1514
+ }
1515
+ const base = list[0];
1516
+ const baseAmt = parseFloat(base.total);
1517
+ const converted = Number.isFinite(baseAmt) ? convertBalance(pref || base.currency, exchangeRate(), baseAmt, base.currency) : baseAmt;
1518
+ const shown = pref && base.currency !== pref ? converted.toLocaleString("en-US", {
1519
+ maximumFractionDigits: 2
1520
+ }) : base.total;
1521
+ return (() => {
1522
+ var _el$92 = _$createElement("text");
1523
+ _$insert(_el$92, () => justify(t().balTotal, balanceSymbol(pref || base.currency) + shown));
1524
+ _$effect((_$p) => _$setProp(_el$92, "fg", pal().text, _$p));
1525
+ return _el$92;
1526
+ })();
1527
+ })();
1528
+ }
1529
+ })];
1530
+ }
1290
1531
  })];
1291
1532
  }
1292
1533
  })];
1293
1534
  }
1294
1535
  }), null);
1295
1536
  _$effect((_p$) => {
1296
- var _v$9 = {
1537
+ var _v$13 = {
1297
1538
  fg: pal().muted
1298
- }, _v$0 = {
1539
+ }, _v$14 = {
1299
1540
  fg: pal().primary
1300
1541
  };
1301
- _v$9 !== _p$.e && (_p$.e = _$setProp(_el$3, "style", _v$9, _p$.e));
1302
- _v$0 !== _p$.t && (_p$.t = _$setProp(_el$4, "style", _v$0, _p$.t));
1542
+ _v$13 !== _p$.e && (_p$.e = _$setProp(_el$3, "style", _v$13, _p$.e));
1543
+ _v$14 !== _p$.t && (_p$.t = _$setProp(_el$4, "style", _v$14, _p$.t));
1303
1544
  return _p$;
1304
1545
  }, {
1305
1546
  e: void 0,
@@ -1342,6 +1583,9 @@ var tui = async (api) => {
1342
1583
  const [sectionModel, setSectionModel] = createSignal(true);
1343
1584
  const [sectionDist, setSectionDist] = createSignal(true);
1344
1585
  const [sectionSkills, setSectionSkills] = createSignal(true);
1586
+ const [sectionBalance, setSectionBalance] = createSignal(true);
1587
+ const [balanceRefresh, setBalanceRefresh] = createSignal(0);
1588
+ const [balanceCurrency, setBalanceCurrency] = createSignal("");
1345
1589
  const [borderVisible, setBorderVisible] = createSignal(true);
1346
1590
  const [langZH, setLangZH] = createSignal(LANG_ZH);
1347
1591
  const [overrideSessionId, setOverrideSessionId] = createSignal(void 0);
@@ -1360,6 +1604,12 @@ var tui = async (api) => {
1360
1604
  setSectionDist,
1361
1605
  sectionSkills,
1362
1606
  setSectionSkills,
1607
+ sectionBalance,
1608
+ setSectionBalance,
1609
+ balanceRefresh,
1610
+ setBalanceRefresh,
1611
+ balanceCurrency,
1612
+ setBalanceCurrency,
1363
1613
  borderVisible,
1364
1614
  setBorderVisible,
1365
1615
  overrideSessionId,
@@ -1388,6 +1638,8 @@ var tui = async (api) => {
1388
1638
  const defRate = DEFAULT_RATES[opt.value] ?? 1;
1389
1639
  api.kv.set(`${KV_PREFIX}.currency`, sym);
1390
1640
  api.kv.set(`${KV_PREFIX}.rate`, defRate);
1641
+ api.kv.set(`${KV_PREFIX}.balance_currency`, opt.value);
1642
+ signals.setBalanceCurrency(opt.value);
1391
1643
  signals.setCurrencySymbol(sym);
1392
1644
  signals.setExchangeRate(defRate);
1393
1645
  api.ui.toast({
@@ -1408,9 +1660,9 @@ var tui = async (api) => {
1408
1660
  dialog?.replace(() => _$createComponent(api.ui.DialogPrompt, {
1409
1661
  title: "Exchange Rate",
1410
1662
  description: () => (() => {
1411
- var _el$78 = _$createElement("text");
1412
- _$insertNode(_el$78, _$createTextNode(`Enter the exchange rate from USD to your currency (e.g. 7.2 for CNY)`));
1413
- return _el$78;
1663
+ var _el$93 = _$createElement("text");
1664
+ _$insertNode(_el$93, _$createTextNode(`Enter the exchange rate from USD to your currency (e.g. 7.2 for CNY)`));
1665
+ return _el$93;
1414
1666
  })(),
1415
1667
  placeholder: "1.0",
1416
1668
  get value() {
@@ -1441,6 +1693,7 @@ var tui = async (api) => {
1441
1693
  const modelOn = Boolean(api.kv.get(`${KV_PREFIX}.section.model`, true));
1442
1694
  const distOn = Boolean(api.kv.get(`${KV_PREFIX}.section.dist`, true));
1443
1695
  const skillsOn = Boolean(api.kv.get(`${KV_PREFIX}.section.skills`, true));
1696
+ const balanceOn = Boolean(api.kv.get(`${KV_PREFIX}.section.balance`, true));
1444
1697
  const borderOn = Boolean(api.kv.get(`${KV_PREFIX}.border`, true));
1445
1698
  dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
1446
1699
  title: "Toggle Section",
@@ -1456,6 +1709,9 @@ var tui = async (api) => {
1456
1709
  }, {
1457
1710
  title: `Loaded Skills [${skillsOn ? "ON" : "OFF"}]`,
1458
1711
  value: "skills"
1712
+ }, {
1713
+ title: `DS Balance [${balanceOn ? "ON" : "OFF"}]`,
1714
+ value: "balance"
1459
1715
  }, {
1460
1716
  title: `Panel Border [${borderOn ? "ON" : "OFF"}]`,
1461
1717
  value: "border"
@@ -1476,6 +1732,7 @@ var tui = async (api) => {
1476
1732
  if (opt.value === "model") signals.setSectionModel(!cur);
1477
1733
  if (opt.value === "dist") signals.setSectionDist(!cur);
1478
1734
  if (opt.value === "skills") signals.setSectionSkills(!cur);
1735
+ if (opt.value === "balance") signals.setSectionBalance(!cur);
1479
1736
  api.ui.toast({
1480
1737
  message: `${opt.value} section ${!cur ? "shown" : "hidden"}`
1481
1738
  });
@@ -1498,9 +1755,10 @@ var tui = async (api) => {
1498
1755
  const model = Boolean(api.kv.get(`${KV_PREFIX}.section.model`, true));
1499
1756
  const dist = Boolean(api.kv.get(`${KV_PREFIX}.section.dist`, true));
1500
1757
  const skills = Boolean(api.kv.get(`${KV_PREFIX}.section.skills`, true));
1758
+ const balance = Boolean(api.kv.get(`${KV_PREFIX}.section.balance`, true));
1501
1759
  api.ui.toast({
1502
1760
  title: "Cache Panel Config",
1503
- message: `Currency: ${sym} | Rate: ${rate} | Detail: ${detail ? "ON" : "OFF"} | Model: ${model ? "ON" : "OFF"} | Dist: ${dist ? "ON" : "OFF"} | Skills: ${skills ? "ON" : "OFF"}`,
1761
+ message: `Currency: ${sym} | Rate: ${rate} | Detail: ${detail ? "ON" : "OFF"} | Model: ${model ? "ON" : "OFF"} | Dist: ${dist ? "ON" : "OFF"} | Skills: ${skills ? "ON" : "OFF"} | Balance: ${balance ? "ON" : "OFF"}`,
1504
1762
  duration: 8e3
1505
1763
  });
1506
1764
  dialog?.clear();
@@ -1534,6 +1792,59 @@ var tui = async (api) => {
1534
1792
  }
1535
1793
  }));
1536
1794
  }
1795
+ }, {
1796
+ title: "Cache: Set DeepSeek API Key",
1797
+ value: "cache.balance.key",
1798
+ description: "Set or update the DeepSeek API key for balance display",
1799
+ slash: {
1800
+ name: "cache-balance-key"
1801
+ },
1802
+ onSelect: (dialog) => {
1803
+ const zh = langZH();
1804
+ const current = api.kv.get(`${KV_PREFIX}.ds_key`, "");
1805
+ const maskKey = (k) => {
1806
+ if (!k) return "";
1807
+ const prefix = k.startsWith("sk-") ? "sk-" : "";
1808
+ const body = prefix ? k.slice(3) : k;
1809
+ if (body.length <= 10) return prefix + body.slice(0, 5) + "*".repeat(Math.max(3, body.length - 5));
1810
+ return prefix + body.slice(0, 5) + "*".repeat(Math.max(3, body.length - 10)) + body.slice(-5);
1811
+ };
1812
+ const masked = maskKey(current);
1813
+ dialog?.replace(() => _$createComponent(api.ui.DialogPrompt, {
1814
+ title: zh ? "DeepSeek API Key" : "DeepSeek API Key",
1815
+ description: () => (() => {
1816
+ var _el$95 = _$createElement("text");
1817
+ _$insert(_el$95, zh ? "\u8F93\u5165 DeepSeek API Key \u4EE5\u663E\u793A\u8D26\u6237\u4F59\u989D\uFF08\u7559\u7A7A\u6E05\u9664\uFF09" : "Enter your DeepSeek API key to show account balance (leave empty to clear)");
1818
+ return _el$95;
1819
+ })(),
1820
+ placeholder: "sk-...",
1821
+ value: masked,
1822
+ onConfirm: (val) => {
1823
+ const input = val.trim();
1824
+ let key;
1825
+ if (input === "") {
1826
+ key = "";
1827
+ } else if (input.includes("*")) {
1828
+ key = current;
1829
+ } else {
1830
+ key = input;
1831
+ }
1832
+ api.kv.set(`${KV_PREFIX}.ds_key`, key);
1833
+ setBalanceRefresh((v) => v + 1);
1834
+ if (key) {
1835
+ api.ui.toast({
1836
+ message: zh ? "API Key \u5DF2\u4FDD\u5B58\uFF0C\u6B63\u5728\u67E5\u8BE2\u4F59\u989D..." : "API Key saved, fetching balance..."
1837
+ });
1838
+ } else {
1839
+ api.ui.toast({
1840
+ message: zh ? "API Key \u5DF2\u6E05\u9664" : "API Key cleared"
1841
+ });
1842
+ }
1843
+ dialog?.clear();
1844
+ },
1845
+ onCancel: () => dialog?.clear()
1846
+ }));
1847
+ }
1537
1848
  }, {
1538
1849
  title: "Cache: Debug Skills Detection",
1539
1850
  value: "cache.debug-skills",
@@ -1684,9 +1995,9 @@ var tui = async (api) => {
1684
1995
  return signals.overrideSessionId() ? zh ? "\u5207\u6362\u5B50\u4EE3\u7406" : "Switch Sub" : zh ? "\u67E5\u770B\u5B50\u4EE3\u7406\u7F13\u5B58" : "View Sub Cache";
1685
1996
  },
1686
1997
  description: () => (() => {
1687
- var _el$80 = _$createElement("text");
1688
- _$insert(_el$80, zh ? "\u672A\u627E\u5230\u5B50\u4EE3\u7406\uFF0C\u8BF7\u624B\u52A8\u7C98\u8D34 Session ID" : "No sub-agents found. Paste a Session ID manually");
1689
- return _el$80;
1998
+ var _el$96 = _$createElement("text");
1999
+ _$insert(_el$96, zh ? "\u672A\u627E\u5230\u5B50\u4EE3\u7406\uFF0C\u8BF7\u624B\u52A8\u7C98\u8D34 Session ID" : "No sub-agents found. Paste a Session ID manually");
2000
+ return _el$96;
1690
2001
  })(),
1691
2002
  placeholder: "ses_...",
1692
2003
  get value() {