opencode-visual-cache 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.5.0";
16
+ var PLUGIN_VERSION = "1.6.0";
17
17
 
18
18
  // src/balance-providers.ts
19
19
  var BalanceError = class extends Error {
@@ -117,51 +117,7 @@ function maskKey(k) {
117
117
  return k.slice(0, 5) + "*".repeat(Math.max(3, k.length - 10)) + k.slice(-5);
118
118
  }
119
119
 
120
- // src/index.tsx
121
- function charColumns(c) {
122
- const code = c.codePointAt(0) ?? 0;
123
- if (code < 32) return 0;
124
- if (code < 127) return 1;
125
- if (code < 160) return 0;
126
- if (code >= 4352 && code <= 4447 || // Hangul Jamo
127
- code >= 11904 && code <= 42191 || // CJK Radicals … Yi
128
- code >= 44032 && code <= 55203 || // Hangul
129
- code >= 63744 && code <= 64255 || // CJK Compat
130
- code >= 65040 && code <= 65135 || // Vertical / Compat
131
- code >= 65281 && code <= 65376 || // Fullwidth
132
- code >= 65504 && code <= 65510 || // Fullwidth signs
133
- code >= 127744 && code <= 128591 || // Misc Symbols (emoji)
134
- code >= 131072 && code <= 262141)
135
- return 2;
136
- return 1;
137
- }
138
- function visualWidth(s) {
139
- let w = 0;
140
- for (const c of s) w += charColumns(c);
141
- return w;
142
- }
143
- function truncateVisual(s, maxCols) {
144
- if (visualWidth(s) <= maxCols) return s;
145
- let result = "", w = 0;
146
- for (const c of s) {
147
- const cw = charColumns(c);
148
- if (w + cw > maxCols - 1) {
149
- result += "\u2026";
150
- break;
151
- }
152
- result += c;
153
- w += cw;
154
- }
155
- return result;
156
- }
157
- var DEBUG_LANG = typeof process !== "undefined" ? process.env?.CACHE_TUI_LANG : void 0;
158
- var LANG_ZH = DEBUG_LANG ? DEBUG_LANG === "zh" : (() => {
159
- try {
160
- return Intl.DateTimeFormat().resolvedOptions().locale.startsWith("zh");
161
- } catch {
162
- return false;
163
- }
164
- })();
120
+ // src/i18n.ts
165
121
  var ZH_T = {
166
122
  title: "\u7F13\u5B58\u7EDF\u8BA1",
167
123
  hit: "\u547D\u4E2D\u7387",
@@ -199,7 +155,49 @@ var ZH_T = {
199
155
  balErr401: "API Key \u65E0\u6548",
200
156
  balErr403: "\u4F59\u989D\u67E5\u8BE2\u88AB\u62D2\u7EDD",
201
157
  balErrEmpty: "\u672A\u83B7\u53D6\u5230\u4F59\u989D\u6570\u636E",
202
- balErrTimeout: "\u67E5\u8BE2\u8D85\u65F6"
158
+ balErrTimeout: "\u67E5\u8BE2\u8D85\u65F6",
159
+ balUnsupported: "\u5F53\u524D\u63D0\u4F9B\u5546\u4E0D\u652F\u6301\u4F59\u989D\u67E5\u8BE2",
160
+ barHit: "\u547D\u4E2D\u7387",
161
+ barBal: "\u4F59\u989D",
162
+ barTok: "Tokens",
163
+ // ── /cache-section 面板 ──
164
+ secToggle: "\u5207\u6362\u533A\u5757",
165
+ secBalance: "\u4F59\u989D",
166
+ secBottom: "\u5E95\u90E8\u72B6\u6001\u680F",
167
+ secBorder: "\u9762\u677F\u8FB9\u6846",
168
+ // ── toast ──
169
+ keySaved: "API Key \u5DF2\u4FDD\u5B58\uFF0C\u6B63\u5728\u67E5\u8BE2\u4F59\u989D...",
170
+ keyCleared: "API Key \u5DF2\u6E05\u9664",
171
+ currencySet: "\u5E01\u79CD: {v} ({s}), \u6C47\u7387: {r}",
172
+ rateSet: "\u6C47\u7387\u5DF2\u8BBE\u4E3A {r}",
173
+ panelConfigTitle: "\u7F13\u5B58\u9762\u677F\u914D\u7F6E",
174
+ panelConfigMsg: "\u5E01\u79CD: {c} | \u6C47\u7387: {r} | \u660E\u7EC6: {d} | \u6A21\u578B: {m} | \u5206\u5E03: {t} | \u6280\u80FD: {k} | \u4F59\u989D: {b} | \u5E95\u90E8: {f}",
175
+ borderShown: "\u9762\u677F\u8FB9\u6846 \u5DF2\u663E\u793A",
176
+ borderHidden: "\u9762\u677F\u8FB9\u6846 \u5DF2\u9690\u85CF",
177
+ sectionShown: "{s} \u5DF2\u663E\u793A",
178
+ sectionHidden: "{s} \u5DF2\u9690\u85CF",
179
+ langSwitched: "\u8BED\u8A00\u5DF2\u5207\u6362\u4E3A\u4E2D\u6587",
180
+ autoSwitchOn: "\u81EA\u52A8\u5207\u6362\u4F59\u989D\u63D0\u4F9B\u5546: \u5F00",
181
+ autoSwitchOff: "\u81EA\u52A8\u5207\u6362\u4F59\u989D\u63D0\u4F9B\u5546: \u5173",
182
+ providerManual: "\u4F59\u989D\u63D0\u4F9B\u5546: {p}\uFF08\u81EA\u52A8\u5207\u6362\u5DF2\u5173\u95ED\uFF09",
183
+ runInSession: "\u8BF7\u5728\u4F1A\u8BDD\u5185\u8FD0\u884C\u6B64\u547D\u4EE4",
184
+ backToMain: "\u5DF2\u5207\u56DE\u4E3B\u4F1A\u8BDD",
185
+ subAgentSwitched: "\u5DF2\u5207\u6362\u81F3\u5B50\u4EE3\u7406: {s}",
186
+ // ── 对话框 / 菜单 ──
187
+ langTitle: "\u663E\u793A\u8BED\u8A00",
188
+ subPrefix: "\u5B50\u4EE3\u7406: ",
189
+ keyUser: "\uFF08\u7528\u6237 key\uFF09",
190
+ keyOpenCode: "\uFF08OpenCode\uFF09",
191
+ keyNotSet: "\uFF08\u672A\u914D\u7F6E\uFF09",
192
+ balKeyPrompt: "\u8F93\u5165 {p} API Key \u4EE5\u663E\u793A\u8D26\u6237\u4F59\u989D\uFF08\u7559\u7A7A\u6E05\u9664\uFF09",
193
+ balProvTitle: "\u4F59\u989D\u63D0\u4F9B\u5546 / \u81EA\u52A8\u5207\u6362",
194
+ autoSwitchOpt: "\u81EA\u52A8\u5207\u6362\u63D0\u4F9B\u5546",
195
+ balSelectTitle: "\u9009\u62E9\u4F59\u989D\u63D0\u4F9B\u5546",
196
+ backToMainTitle: "\u56DE\u5230\u4E3B\u4F1A\u8BDD",
197
+ subSelectTitle: "\u9009\u62E9\u5B50\u4EE3\u7406",
198
+ subSwitchTitle: "\u5207\u6362\u5B50\u4EE3\u7406",
199
+ subViewTitle: "\u67E5\u770B\u5B50\u4EE3\u7406\u7F13\u5B58",
200
+ subNoFound: "\u672A\u627E\u5230\u5B50\u4EE3\u7406\uFF0C\u8BF7\u624B\u52A8\u7C98\u8D34 Session ID"
203
201
  };
204
202
  var EN_T = {
205
203
  title: "Token Cache",
@@ -238,8 +236,284 @@ var EN_T = {
238
236
  balErr401: "Invalid API Key",
239
237
  balErr403: "Balance request rejected",
240
238
  balErrEmpty: "No balance data",
241
- balErrTimeout: "Request timed out"
239
+ balErrTimeout: "Request timed out",
240
+ balUnsupported: "Balance query unsupported",
241
+ barHit: "Hit",
242
+ barBal: "Balance",
243
+ barTok: "Tokens",
244
+ // ── /cache-section panel ──
245
+ secToggle: "Toggle Section",
246
+ secBalance: "Balance",
247
+ secBottom: "Bottom Bar",
248
+ secBorder: "Panel Border",
249
+ // ── toasts ──
250
+ keySaved: "API Key saved, fetching balance...",
251
+ keyCleared: "API Key cleared",
252
+ currencySet: "Currency: {v} ({s}), rate: {r}",
253
+ rateSet: "Exchange rate set to {r}",
254
+ panelConfigTitle: "Cache Panel Config",
255
+ panelConfigMsg: "Currency: {c} | Rate: {r} | Detail: {d} | Model: {m} | Dist: {t} | Skills: {k} | Balance: {b} | Bottom: {f}",
256
+ borderShown: "Panel border shown",
257
+ borderHidden: "Panel border hidden",
258
+ sectionShown: "{s} section shown",
259
+ sectionHidden: "{s} section hidden",
260
+ langSwitched: "Switched to English",
261
+ autoSwitchOn: "Auto-switch balance provider: ON",
262
+ autoSwitchOff: "Auto-switch balance provider: OFF",
263
+ providerManual: "Balance provider: {p} (auto-switch off)",
264
+ runInSession: "Please run this command inside a session",
265
+ backToMain: "Switched to main session",
266
+ subAgentSwitched: "Showing sub-agent: {s}",
267
+ // ── dialogs / menus ──
268
+ langTitle: "Display Language",
269
+ subPrefix: "Sub: ",
270
+ keyUser: " (user key)",
271
+ keyOpenCode: " (OpenCode)",
272
+ keyNotSet: " (not set)",
273
+ balKeyPrompt: "Enter your {p} API key to show account balance (leave empty to clear)",
274
+ balProvTitle: "Balance Provider / Auto-switch",
275
+ autoSwitchOpt: "Auto-switch provider",
276
+ balSelectTitle: "Select Balance Provider",
277
+ backToMainTitle: "Back to Main",
278
+ subSelectTitle: "Select Sub-Agent",
279
+ subSwitchTitle: "Switch Sub",
280
+ subViewTitle: "View Sub Cache",
281
+ subNoFound: "No sub-agents found. Paste a Session ID manually"
242
282
  };
283
+ var JA_T = {
284
+ title: "\u30AD\u30E3\u30C3\u30B7\u30E5\u7D71\u8A08",
285
+ hit: "\u30D2\u30C3\u30C8\u7387",
286
+ totalHit: "\u7DCF\u30D2\u30C3\u30C8:",
287
+ read: "\u30AD\u30E3\u30C3\u30B7\u30E5\u8AAD\u8FBC:",
288
+ write: "\u30AD\u30E3\u30C3\u30B7\u30E5\u66F8\u8FBC:",
289
+ miss: "\u672A\u30D2\u30C3\u30C8:",
290
+ out: "\u51FA\u529B:",
291
+ cost: "\u8CBB\u7528:",
292
+ saved: "\u7D2F\u8A08\u7BC0\u7D04:",
293
+ model: "\u30E2\u30C7\u30EB:",
294
+ provider: "\u30D7\u30ED\u30D0\u30A4\u30C0:",
295
+ rate: "\u5358\u4FA1:",
296
+ hitFolded: "\u30D2\u30C3\u30C8",
297
+ inputRate: "\u5165\u529B",
298
+ cacheRate: "\u30AD\u30E3\u30C3\u30B7\u30E5",
299
+ writeRate: "\u66F8\u8FBC",
300
+ noData: "\u30AD\u30E3\u30C3\u30B7\u30E5\u30C7\u30FC\u30BF\u5F85\u6A5F\u4E2D...",
301
+ tok: "tok",
302
+ distTitle: "Token \u5206\u5E03\uFF08\u63A8\u5B9A\uFF09",
303
+ distSys: "\u30B7\u30B9\u30C6\u30E0:",
304
+ distUser: "\u30E6\u30FC\u30B6\u30FC:",
305
+ distAgent: "Agent \u6307\u793A:",
306
+ distTool: "Tool \u547C\u3073\u51FA\u3057:",
307
+ distRes: "Tool \u7D50\u679C:",
308
+ distTotal: "\u5408\u8A08:",
309
+ distOut: "\u51FA\u529B:",
310
+ secDetail: "\u660E\u7D30",
311
+ secModel: "\u30E2\u30C7\u30EB",
312
+ secSkills: "\u8AAD\u307F\u8FBC\u307F\u6E08\u307F\u30B9\u30AD\u30EB",
313
+ balTotal: "\u7DCF\u6B8B\u9AD8:",
314
+ balNoKey: "{p} \u306E API Key \u672A\u8A2D\u5B9A",
315
+ balLoading: "\u53D6\u5F97\u4E2D...",
316
+ balError: "\u53D6\u5F97\u5931\u6557",
317
+ balErr401: "API Key \u7121\u52B9",
318
+ balErr403: "\u6B8B\u9AD8\u7167\u4F1A\u304C\u62D2\u5426\u3055\u308C\u307E\u3057\u305F",
319
+ balErrEmpty: "\u6B8B\u9AD8\u30C7\u30FC\u30BF\u306A\u3057",
320
+ balErrTimeout: "\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8",
321
+ balUnsupported: "\u3053\u306E\u30D7\u30ED\u30D0\u30A4\u30C0\u306F\u6B8B\u9AD8\u7167\u4F1A\u975E\u5BFE\u5FDC",
322
+ barHit: "\u30D2\u30C3\u30C8\u7387",
323
+ barBal: "\u6B8B\u9AD8",
324
+ barTok: "Tokens",
325
+ // ── /cache-section パネル ──
326
+ secToggle: "\u30BB\u30AF\u30B7\u30E7\u30F3\u5207\u66FF",
327
+ secBalance: "\u6B8B\u9AD8",
328
+ secBottom: "\u4E0B\u90E8\u30B9\u30C6\u30FC\u30BF\u30B9\u30D0\u30FC",
329
+ secBorder: "\u30D1\u30CD\u30EB\u67A0\u7DDA",
330
+ // ── toast ──
331
+ keySaved: "API Key \u4FDD\u5B58\u6E08\u307F\u3001\u6B8B\u9AD8\u3092\u53D6\u5F97\u4E2D...",
332
+ keyCleared: "API Key \u3092\u30AF\u30EA\u30A2\u3057\u307E\u3057\u305F",
333
+ currencySet: "\u901A\u8CA8: {v} ({s}), \u30EC\u30FC\u30C8: {r}",
334
+ rateSet: "\u30EC\u30FC\u30C8\u3092 {r} \u306B\u8A2D\u5B9A\u3057\u307E\u3057\u305F",
335
+ panelConfigTitle: "\u30D1\u30CD\u30EB\u8A2D\u5B9A",
336
+ panelConfigMsg: "\u901A\u8CA8: {c} | \u30EC\u30FC\u30C8: {r} | \u660E\u7D30: {d} | \u30E2\u30C7\u30EB: {m} | \u5206\u5E03: {t} | \u30B9\u30AD\u30EB: {k} | \u6B8B\u9AD8: {b} | \u4E0B\u90E8: {f}",
337
+ borderShown: "\u30D1\u30CD\u30EB\u67A0\u7DDA \u8868\u793A",
338
+ borderHidden: "\u30D1\u30CD\u30EB\u67A0\u7DDA \u975E\u8868\u793A",
339
+ sectionShown: "{s} \u8868\u793A",
340
+ sectionHidden: "{s} \u975E\u8868\u793A",
341
+ langSwitched: "\u65E5\u672C\u8A9E\u306B\u5207\u308A\u66FF\u3048\u307E\u3057\u305F",
342
+ autoSwitchOn: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0\u81EA\u52D5\u5207\u66FF: \u30AA\u30F3",
343
+ autoSwitchOff: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0\u81EA\u52D5\u5207\u66FF: \u30AA\u30D5",
344
+ providerManual: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0: {p}\uFF08\u81EA\u52D5\u5207\u66FF\u30AA\u30D5\uFF09",
345
+ runInSession: "\u30BB\u30C3\u30B7\u30E7\u30F3\u5185\u3067\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044",
346
+ backToMain: "\u30E1\u30A4\u30F3\u30BB\u30C3\u30B7\u30E7\u30F3\u306B\u623B\u308A\u307E\u3057\u305F",
347
+ subAgentSwitched: "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3078\u5207\u66FF: {s}",
348
+ // ── ダイアログ / メニュー ──
349
+ langTitle: "\u8868\u793A\u8A00\u8A9E",
350
+ subPrefix: "\u30B5\u30D6: ",
351
+ keyUser: "\uFF08\u30E6\u30FC\u30B6\u30FC key\uFF09",
352
+ keyOpenCode: "\uFF08OpenCode\uFF09",
353
+ keyNotSet: "\uFF08\u672A\u8A2D\u5B9A\uFF09",
354
+ balKeyPrompt: "{p} \u306E API Key \u3092\u5165\u529B\u3057\u3066\u6B8B\u9AD8\u3092\u8868\u793A\uFF08\u7A7A\u6B04\u3067\u30AF\u30EA\u30A2\uFF09",
355
+ balProvTitle: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0 / \u81EA\u52D5\u5207\u66FF",
356
+ autoSwitchOpt: "\u81EA\u52D5\u5207\u66FF\u30D7\u30ED\u30D0\u30A4\u30C0",
357
+ balSelectTitle: "\u6B8B\u9AD8\u30D7\u30ED\u30D0\u30A4\u30C0\u9078\u629E",
358
+ backToMainTitle: "\u30E1\u30A4\u30F3\u306B\u623B\u308B",
359
+ subSelectTitle: "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u9078\u629E",
360
+ subSwitchTitle: "\u30B5\u30D6\u5207\u66FF",
361
+ subViewTitle: "\u30B5\u30D6\u30AD\u30E3\u30C3\u30B7\u30E5\u8868\u793A",
362
+ subNoFound: "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002Session ID \u3092\u624B\u52D5\u3067\u8CBC\u308A\u4ED8\u3051\u3066\u304F\u3060\u3055\u3044"
363
+ };
364
+ var KO_T = {
365
+ title: "\uCE90\uC2DC \uD1B5\uACC4",
366
+ hit: "\uD788\uD2B8\uC728",
367
+ totalHit: "\uCD1D \uD788\uD2B8:",
368
+ read: "\uCE90\uC2DC \uC77D\uAE30:",
369
+ write: "\uCE90\uC2DC \uC4F0\uAE30:",
370
+ miss: "\uBBF8\uD788\uD2B8:",
371
+ out: "\uCD9C\uB825:",
372
+ cost: "\uBE44\uC6A9:",
373
+ saved: "\uB204\uC801 \uC808\uC57D:",
374
+ model: "\uBAA8\uB378:",
375
+ provider: "\uD504\uB85C\uBC14\uC774\uB354:",
376
+ rate: "\uB2E8\uAC00:",
377
+ hitFolded: "\uD788\uD2B8",
378
+ inputRate: "\uC785\uB825",
379
+ cacheRate: "\uCE90\uC2DC",
380
+ writeRate: "\uC4F0\uAE30",
381
+ noData: "\uCE90\uC2DC \uB370\uC774\uD130 \uB300\uAE30 \uC911...",
382
+ tok: "tok",
383
+ distTitle: "Token \uBD84\uD3EC(\uCD94\uC815)",
384
+ distSys: "\uC2DC\uC2A4\uD15C:",
385
+ distUser: "\uC0AC\uC6A9\uC790:",
386
+ distAgent: "Agent \uC9C0\uC2DC:",
387
+ distTool: "Tool \uD638\uCD9C:",
388
+ distRes: "Tool \uACB0\uACFC:",
389
+ distTotal: "\uD569\uACC4:",
390
+ distOut: "\uCD9C\uB825:",
391
+ secDetail: "\uC0C1\uC138",
392
+ secModel: "\uBAA8\uB378",
393
+ secSkills: "\uB85C\uB4DC\uB41C \uC2A4\uD0AC",
394
+ balTotal: "\uCD1D \uC794\uC561:",
395
+ balNoKey: "{p} API Key \uBBF8\uC124\uC815",
396
+ balLoading: "\uC870\uD68C \uC911...",
397
+ balError: "\uC870\uD68C \uC2E4\uD328",
398
+ balErr401: "API Key \uBB34\uD6A8",
399
+ balErr403: "\uC794\uC561 \uC870\uD68C\uAC00 \uAC70\uBD80\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
400
+ balErrEmpty: "\uC794\uC561 \uB370\uC774\uD130 \uC5C6\uC74C",
401
+ balErrTimeout: "\uC2DC\uAC04 \uCD08\uACFC",
402
+ balUnsupported: "\uC774 \uD504\uB85C\uBC14\uC774\uB354\uB294 \uC794\uC561 \uC870\uD68C \uBBF8\uC9C0\uC6D0",
403
+ barHit: "\uD788\uD2B8\uC728",
404
+ barBal: "\uC794\uC561",
405
+ barTok: "Tokens",
406
+ // ── /cache-section 패널 ──
407
+ secToggle: "\uC139\uC158 \uC804\uD658",
408
+ secBalance: "\uC794\uC561",
409
+ secBottom: "\uD558\uB2E8 \uC0C1\uD0DC\uBC14",
410
+ secBorder: "\uD328\uB110 \uD14C\uB450\uB9AC",
411
+ // ── toast ──
412
+ keySaved: "API Key \uC800\uC7A5\uB428, \uC794\uC561 \uC870\uD68C \uC911...",
413
+ keyCleared: "API Key \uC0AD\uC81C\uB428",
414
+ currencySet: "\uD1B5\uD654: {v} ({s}), \uD658\uC728: {r}",
415
+ rateSet: "\uD658\uC728\uC744 {r}(\uC73C)\uB85C \uC124\uC815\uD588\uC2B5\uB2C8\uB2E4",
416
+ panelConfigTitle: "\uD328\uB110 \uC124\uC815",
417
+ panelConfigMsg: "\uD1B5\uD654: {c} | \uD658\uC728: {r} | \uC0C1\uC138: {d} | \uBAA8\uB378: {m} | \uBD84\uD3EC: {t} | \uC2A4\uD0AC: {k} | \uC794\uC561: {b} | \uD558\uB2E8: {f}",
418
+ borderShown: "\uD328\uB110 \uD14C\uB450\uB9AC \uD45C\uC2DC",
419
+ borderHidden: "\uD328\uB110 \uD14C\uB450\uB9AC \uC228\uAE40",
420
+ sectionShown: "{s} \uD45C\uC2DC",
421
+ sectionHidden: "{s} \uC228\uAE40",
422
+ langSwitched: "\uD55C\uAD6D\uC5B4\uB85C \uC804\uD658\uD588\uC2B5\uB2C8\uB2E4",
423
+ autoSwitchOn: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354 \uC790\uB3D9 \uC804\uD658: \uCF1C\uC9D0",
424
+ autoSwitchOff: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354 \uC790\uB3D9 \uC804\uD658: \uAEBC\uC9D0",
425
+ providerManual: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354: {p}(\uC790\uB3D9 \uC804\uD658 \uAEBC\uC9D0)",
426
+ runInSession: "\uC138\uC158 \uB0B4\uC5D0\uC11C \uC2E4\uD589\uD574 \uC8FC\uC138\uC694",
427
+ backToMain: "\uBA54\uC778 \uC138\uC158\uC73C\uB85C \uB3CC\uC544\uAC14\uC2B5\uB2C8\uB2E4",
428
+ subAgentSwitched: "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8\uB85C \uC804\uD658: {s}",
429
+ // ── 다이얼로그 / 메뉴 ──
430
+ langTitle: "\uD45C\uC2DC \uC5B8\uC5B4",
431
+ subPrefix: "\uC11C\uBE0C: ",
432
+ keyUser: "(\uC0AC\uC6A9\uC790 key)",
433
+ keyOpenCode: "(OpenCode)",
434
+ keyNotSet: "(\uBBF8\uC124\uC815)",
435
+ balKeyPrompt: "{p} API Key\uB97C \uC785\uB825\uD558\uC5EC \uC794\uC561 \uD45C\uC2DC(\uBE44\uC6B0\uBA74 \uC0AD\uC81C)",
436
+ balProvTitle: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354 / \uC790\uB3D9 \uC804\uD658",
437
+ autoSwitchOpt: "\uC790\uB3D9 \uC804\uD658 \uD504\uB85C\uBC14\uC774\uB354",
438
+ balSelectTitle: "\uC794\uC561 \uD504\uB85C\uBC14\uC774\uB354 \uC120\uD0DD",
439
+ backToMainTitle: "\uBA54\uC778\uC73C\uB85C \uB3CC\uC544\uAC00\uAE30",
440
+ subSelectTitle: "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uC120\uD0DD",
441
+ subSwitchTitle: "\uC11C\uBE0C \uC804\uD658",
442
+ subViewTitle: "\uC11C\uBE0C \uCE90\uC2DC \uBCF4\uAE30",
443
+ subNoFound: "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. Session ID\uB97C \uC9C1\uC811 \uBD99\uC5EC\uB123\uC73C\uC138\uC694"
444
+ };
445
+ var LANGS = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T };
446
+ var LANG_META = [
447
+ { code: "zh", label: "\u4E2D\u6587" },
448
+ { code: "en", label: "English" },
449
+ { code: "ja", label: "\u65E5\u672C\u8A9E" },
450
+ { code: "ko", label: "\uD55C\uAD6D\uC5B4" }
451
+ ];
452
+ function applyParams(tpl, params) {
453
+ if (!params) return tpl;
454
+ return tpl.replace(
455
+ /\{(\w+)\}/g,
456
+ (m, k) => k in params ? String(params[k]) : m
457
+ );
458
+ }
459
+ function createT(getCode) {
460
+ return (key, params) => applyParams(LANGS[getCode()][key], params);
461
+ }
462
+ function detectLang() {
463
+ try {
464
+ const loc = Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase();
465
+ if (loc.startsWith("zh")) return "zh";
466
+ if (loc.startsWith("ja")) return "ja";
467
+ if (loc.startsWith("ko")) return "ko";
468
+ return "en";
469
+ } catch {
470
+ return "en";
471
+ }
472
+ }
473
+
474
+ // src/index.tsx
475
+ function charColumns(c) {
476
+ const code = c.codePointAt(0) ?? 0;
477
+ if (code < 32) return 0;
478
+ if (code < 127) return 1;
479
+ if (code < 160) return 0;
480
+ if (code >= 4352 && code <= 4447 || // Hangul Jamo
481
+ code >= 11904 && code <= 42191 || // CJK Radicals … Yi
482
+ code >= 44032 && code <= 55203 || // Hangul
483
+ code >= 63744 && code <= 64255 || // CJK Compat
484
+ code >= 65040 && code <= 65135 || // Vertical / Compat
485
+ code >= 65281 && code <= 65376 || // Fullwidth
486
+ code >= 65504 && code <= 65510 || // Fullwidth signs
487
+ code >= 127744 && code <= 128591 || // Misc Symbols (emoji)
488
+ code >= 131072 && code <= 262141)
489
+ return 2;
490
+ return 1;
491
+ }
492
+ function visualWidth(s) {
493
+ let w = 0;
494
+ for (const c of s) w += charColumns(c);
495
+ return w;
496
+ }
497
+ function visualPadEnd(s, cols) {
498
+ const pad = cols - visualWidth(s);
499
+ return pad > 0 ? s + " ".repeat(pad) : s;
500
+ }
501
+ function truncateVisual(s, maxCols) {
502
+ if (visualWidth(s) <= maxCols) return s;
503
+ let result = "", w = 0;
504
+ for (const c of s) {
505
+ const cw = charColumns(c);
506
+ if (w + cw > maxCols - 1) {
507
+ result += "\u2026";
508
+ break;
509
+ }
510
+ result += c;
511
+ w += cw;
512
+ }
513
+ return result;
514
+ }
515
+ var DEBUG_LANG = typeof process !== "undefined" ? process.env?.CACHE_TUI_LANG : void 0;
516
+ var INIT_LANG = DEBUG_LANG !== void 0 && LANG_META.some((m) => m.code === DEBUG_LANG) ? DEBUG_LANG : detectLang();
243
517
  function rgb(raw) {
244
518
  if (typeof raw === "string" && raw.startsWith("#")) {
245
519
  const h = raw.slice(1);
@@ -374,6 +648,33 @@ function balanceSymbol(currency) {
374
648
  const sym = CURRENCIES[currency];
375
649
  return sym ?? currency + " ";
376
650
  }
651
+ function fmtCompact(n) {
652
+ if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
653
+ if (n >= 1e3) return (n / 1e3).toFixed(1) + "K";
654
+ return String(Math.round(n));
655
+ }
656
+ function formatBalanceAmount(total) {
657
+ const n = parseFloat(total);
658
+ if (!Number.isFinite(n)) return total;
659
+ if (n === 0 || n >= 1) return n.toLocaleString("en-US", {
660
+ minimumFractionDigits: 2,
661
+ maximumFractionDigits: 2
662
+ });
663
+ return n.toLocaleString("en-US", {
664
+ maximumFractionDigits: 6
665
+ });
666
+ }
667
+ function formatBalanceText(list, pref, rate) {
668
+ const native = pref ? list.find((x) => x.currency === pref) : void 0;
669
+ if (native) return balanceSymbol(native.currency) + formatBalanceAmount(native.total);
670
+ const base = list[0];
671
+ const baseAmt = parseFloat(base.total);
672
+ const converted = Number.isFinite(baseAmt) ? convertBalance(pref || base.currency, rate, baseAmt, base.currency) : baseAmt;
673
+ const shown = pref && base.currency !== pref ? converted.toLocaleString("en-US", {
674
+ maximumFractionDigits: 2
675
+ }) : formatBalanceAmount(base.total);
676
+ return balanceSymbol(pref || base.currency) + shown;
677
+ }
377
678
  var CURRENCIES = {
378
679
  USD: "$",
379
680
  CNY: "\xA5",
@@ -411,8 +712,7 @@ function TokenCachePanel(props) {
411
712
  setCurrencySymbol,
412
713
  exchangeRate,
413
714
  setExchangeRate,
414
- langZH,
415
- setLangZH,
715
+ langCode,
416
716
  sectionDetail,
417
717
  setSectionDetail,
418
718
  sectionModel,
@@ -428,12 +728,15 @@ function TokenCachePanel(props) {
428
728
  setBalanceProviderId,
429
729
  autoBalance,
430
730
  setAutoBalance,
731
+ balanceUnsupported,
732
+ setBalanceUnsupported,
733
+ balanceState,
431
734
  balanceCurrency,
432
735
  setBalanceCurrency,
433
736
  borderVisible,
434
737
  setBorderVisible
435
738
  } = props.signals;
436
- const t = createMemo(() => langZH() ? ZH_T : EN_T);
739
+ const t = createT(() => langCode());
437
740
  const [lastDist, setLastDist] = createSignal({
438
741
  system: 0,
439
742
  user: 0,
@@ -480,72 +783,7 @@ function TokenCachePanel(props) {
480
783
  hasSkills: false
481
784
  });
482
785
  const [refreshTick, setRefreshTick] = createSignal(0);
483
- const [balanceState, setBalanceState] = createSignal({
484
- status: "idle",
485
- data: null,
486
- lastFetch: 0
487
- });
488
- let balanceSeq = 0;
489
786
  const providerName = createMemo(() => getBalanceProvider(balanceProviderId()).name);
490
- const pollBalance = async () => {
491
- const provider = getBalanceProvider(balanceProviderId());
492
- const key = props.api.kv.get(`${KV_PREFIX}.balance.${provider.id}.key`, "") || findOpencodeKey(props.api, provider);
493
- if (!key) {
494
- setBalanceState({
495
- status: "idle",
496
- data: null,
497
- lastFetch: 0,
498
- error: void 0,
499
- key: void 0
500
- });
501
- return;
502
- }
503
- const now = Date.now();
504
- const prev = balanceState();
505
- if (prev.status === "ok" && prev.key === key && now - prev.lastFetch < BALANCE_POLL_MS) return;
506
- const seq = ++balanceSeq;
507
- setBalanceState({
508
- ...prev,
509
- status: "loading",
510
- error: void 0,
511
- key
512
- });
513
- const controller = new AbortController();
514
- let timedOut = false;
515
- const timer = setTimeout(() => {
516
- timedOut = true;
517
- controller.abort();
518
- }, 1e4);
519
- try {
520
- const data2 = await provider.fetchBalance(key, controller.signal);
521
- clearTimeout(timer);
522
- if (seq !== balanceSeq) return;
523
- setBalanceState({
524
- status: "ok",
525
- data: data2,
526
- lastFetch: Date.now(),
527
- error: void 0,
528
- key
529
- });
530
- } catch (err) {
531
- clearTimeout(timer);
532
- if (seq !== balanceSeq) return;
533
- const code = timedOut ? "TIMEOUT" : err instanceof Error ? err.message : "";
534
- setBalanceState({
535
- status: "error",
536
- data: null,
537
- lastFetch: 0,
538
- error: code,
539
- key
540
- });
541
- }
542
- };
543
- createEffect(() => {
544
- void balanceRefresh();
545
- untrack(() => {
546
- void pollBalance();
547
- });
548
- });
549
787
  createEffect(() => {
550
788
  if (!autoBalance()) return;
551
789
  const sid = props.signals.overrideSessionId() ?? props.sessionId;
@@ -567,9 +805,14 @@ function TokenCachePanel(props) {
567
805
  }
568
806
  if (!pid) return;
569
807
  const hit = matchBalanceProvider(pid);
570
- if (hit && hit.id !== balanceProviderId()) {
571
- setBalanceProviderId(hit.id);
572
- props.signals.setBalanceRefresh(props.signals.balanceRefresh() + 1);
808
+ if (hit) {
809
+ setBalanceUnsupported(false);
810
+ if (hit.id !== balanceProviderId()) {
811
+ setBalanceProviderId(hit.id);
812
+ props.signals.setBalanceRefresh(props.signals.balanceRefresh() + 1);
813
+ }
814
+ } else {
815
+ setBalanceUnsupported(true);
573
816
  }
574
817
  });
575
818
  let lastMainSid = props.sessionId;
@@ -602,18 +845,18 @@ function TokenCachePanel(props) {
602
845
  let prevMsgHitRate = -1, lastMsgHitRate = -1;
603
846
  for (const msg of msgs) {
604
847
  if (msg.role !== "assistant") continue;
605
- const t2 = msg.tokens;
606
- if (!t2) continue;
607
- const mit = num(t2.input) + num(t2.cache?.read), mrt = num(t2.cache?.read);
848
+ const tok = msg.tokens;
849
+ if (!tok) continue;
850
+ const mit = num(tok.input) + num(tok.cache?.read) + num(tok.cache?.write), mrt = num(tok.cache?.read);
608
851
  if (mit > 0) {
609
852
  prevMsgHitRate = lastMsgHitRate;
610
853
  lastMsgHitRate = mrt / mit * 100;
611
854
  }
612
855
  if (fallbackTokens) {
613
- input += num(t2.input);
614
- read += num(t2.cache?.read);
615
- write += num(t2.cache?.write);
616
- output += num(t2.output);
856
+ input += num(tok.input);
857
+ read += num(tok.cache?.read);
858
+ write += num(tok.cache?.write);
859
+ output += num(tok.output);
617
860
  }
618
861
  if (fallbackCost) {
619
862
  cost += num(msg.cost);
@@ -635,7 +878,7 @@ function TokenCachePanel(props) {
635
878
  break;
636
879
  }
637
880
  const hitRate = lastMsgHitRate >= 0 ? lastMsgHitRate : 0;
638
- const freshTotal = input + read, sessionHitRate = freshTotal > 0 ? read / freshTotal * 100 : 0;
881
+ const freshTotal = input + read + write, sessionHitRate = freshTotal > 0 ? read / freshTotal * 100 : 0;
639
882
  const model = mid.split("/").pop() ?? mid, hasPricing = inputRate > 0 || cacheReadRate > 0 || cacheWriteRate > 0;
640
883
  const hasTrendData = prevMsgHitRate >= 0 && lastMsgHitRate >= 0;
641
884
  const trend = hasTrendData ? lastMsgHitRate - prevMsgHitRate : 0, providerName2 = pid || "";
@@ -728,13 +971,13 @@ function TokenCachePanel(props) {
728
971
  }
729
972
  for (let i = msgs.length - 1; i >= 0; i--) {
730
973
  if (msgs[i].role !== "assistant") continue;
731
- const t2 = msgs[i].tokens;
732
- if (t2 && (t2.input > 0 || (t2.cache?.read ?? 0) > 0)) {
974
+ const tok = msgs[i].tokens;
975
+ if (tok && ((tok.input ?? 0) > 0 || (tok.cache?.read ?? 0) > 0 || (tok.cache?.write ?? 0) > 0)) {
733
976
  lastAssMsg = msgs[i];
734
977
  break;
735
978
  }
736
979
  }
737
- dist.apiInput = num(lastAssMsg?.tokens?.input) + num(lastAssMsg?.tokens?.cache?.read);
980
+ dist.apiInput = num(lastAssMsg?.tokens?.input) + num(lastAssMsg?.tokens?.cache?.read) + num(lastAssMsg?.tokens?.cache?.write);
738
981
  dist.apiOutput = num(lastAssMsg?.tokens?.output);
739
982
  hasDistData = dist.system + dist.user + dist.agent + dist.toolCall + dist.toolResult > 0 || dist.apiOutput > 0 || dist.apiInput > 0;
740
983
  } catch {
@@ -818,6 +1061,7 @@ function TokenCachePanel(props) {
818
1061
  const savedProvider = props.api.kv.get(`${KV_PREFIX}.balance.provider`);
819
1062
  if (typeof savedProvider === "string" && balanceProviders.some((p) => p.id === savedProvider)) {
820
1063
  setBalanceProviderId(savedProvider);
1064
+ setBalanceUnsupported(false);
821
1065
  }
822
1066
  const savedAuto = props.api.kv.get(`${KV_PREFIX}.balance.auto`);
823
1067
  if (typeof savedAuto === "boolean") setAutoBalance(savedAuto);
@@ -835,10 +1079,6 @@ function TokenCachePanel(props) {
835
1079
  setSectionBalance(Boolean(props.api.kv.get(`${KV_PREFIX}.section.balance`, true)));
836
1080
  const bv = props.api.kv.get(`${KV_PREFIX}.border`, true);
837
1081
  setBorderVisible(bv !== false);
838
- const savedLang = props.api.kv.get(`${KV_PREFIX}.lang`);
839
- if (savedLang === "zh" || savedLang === "en") {
840
- setLangZH(savedLang === "zh");
841
- }
842
1082
  const cachedDist = props.api.kv.get(`${KV_PREFIX}.dist_snapshot`);
843
1083
  if (cachedDist) {
844
1084
  setLastDist(cachedDist);
@@ -885,10 +1125,8 @@ function TokenCachePanel(props) {
885
1125
  setRefreshTick((v) => v + 1);
886
1126
  });
887
1127
  setRefreshTick((v) => v + 1);
888
- const balanceTimer = setInterval(pollBalance, BALANCE_POLL_MS);
889
1128
  onCleanup(() => {
890
1129
  clearTimeout(partTimer);
891
- clearInterval(balanceTimer);
892
1130
  unsubPart();
893
1131
  unsubMsg();
894
1132
  unsubSession();
@@ -916,11 +1154,12 @@ function TokenCachePanel(props) {
916
1154
  const gutter = createMemo(() => borderVisible() ? 6 : 0);
917
1155
  const sep = createMemo(() => "\u2500".repeat(Math.max(1, panelWidth() - gutter())));
918
1156
  function trendLabel(t2) {
919
- return (t2 > 0 ? "\u2191" : t2 < 0 ? "\u2193" : "-") + (t2 !== 0 ? Math.abs(t2).toFixed(1) + "%" : "");
1157
+ if (Math.abs(t2) < 0.05) return "-";
1158
+ return (t2 > 0 ? "\u2191" : "\u2193") + Math.abs(t2).toFixed(1) + "%";
920
1159
  }
921
1160
  const barW = createMemo(() => {
922
1161
  const trendSpace = data().hasTrendData ? LABEL_GAP + visualWidth(trendLabel(data().trend)) : 0;
923
- const overhead = visualWidth(t().hit) + LABEL_GAP + BAR_BRACKETS + BAR_GAP + PCT_FIXED_WIDTH + trendSpace + gutter();
1162
+ const overhead = visualWidth(t("hit")) + LABEL_GAP + BAR_BRACKETS + BAR_GAP + PCT_FIXED_WIDTH + trendSpace + gutter();
924
1163
  return Math.max(3, panelWidth() - overhead);
925
1164
  });
926
1165
  const bar = createMemo(() => progressBar(data().hitRate, barW()));
@@ -974,7 +1213,7 @@ function TokenCachePanel(props) {
974
1213
  }));
975
1214
  _$insert(_el$3, () => open() ? "\u25BC " : "\u25B6 ");
976
1215
  _$insertNode(_el$4, _el$5);
977
- _$insert(_el$5, () => t().title);
1216
+ _$insert(_el$5, () => t("title"));
978
1217
  _$insert(_el$4, _$createComponent(Show, {
979
1218
  get when() {
980
1219
  return open();
@@ -1001,13 +1240,13 @@ function TokenCachePanel(props) {
1001
1240
  get children() {
1002
1241
  return [(() => {
1003
1242
  var _el$8 = _$createElement("span");
1004
- _$insert(_el$8, () => " ".repeat(Math.max(1, panelWidth() - gutter() - HEADER_PREFIX - visualWidth(t().title) - visualWidth(pct() + " " + t().hitFolded + " " + trendLabel(data().trend)))));
1243
+ _$insert(_el$8, () => " ".repeat(Math.max(1, panelWidth() - gutter() - HEADER_PREFIX - visualWidth(t("title")) - visualWidth(pct() + " " + t("hitFolded") + " " + trendLabel(data().trend)))));
1005
1244
  return _el$8;
1006
1245
  })(), (() => {
1007
1246
  var _el$9 = _$createElement("span"), _el$0 = _$createTextNode(` `);
1008
1247
  _$insertNode(_el$9, _el$0);
1009
1248
  _$insert(_el$9, pct, _el$0);
1010
- _$insert(_el$9, () => t().hitFolded, null);
1249
+ _$insert(_el$9, () => t("hitFolded"), null);
1011
1250
  _$effect((_$p) => _$setProp(_el$9, "style", {
1012
1251
  fg: hitColor()
1013
1252
  }, _$p));
@@ -1017,7 +1256,7 @@ function TokenCachePanel(props) {
1017
1256
  _$insertNode(_el$1, _el$10);
1018
1257
  _$insert(_el$1, () => trendLabel(data().trend), null);
1019
1258
  _$effect((_$p) => _$setProp(_el$1, "style", {
1020
- fg: data().trend !== 0 ? data().trend > 0 ? pal().success : pal().error : pal().text
1259
+ fg: Math.abs(data().trend) >= 0.05 ? data().trend > 0 ? pal().success : pal().error : pal().text
1021
1260
  }, _$p));
1022
1261
  return _el$1;
1023
1262
  })()];
@@ -1029,13 +1268,13 @@ function TokenCachePanel(props) {
1029
1268
  get children() {
1030
1269
  return [(() => {
1031
1270
  var _el$11 = _$createElement("span");
1032
- _$insert(_el$11, () => " ".repeat(Math.max(1, panelWidth() - gutter() - HEADER_PREFIX - visualWidth(t().title) - visualWidth(pct() + " " + t().hitFolded))));
1271
+ _$insert(_el$11, () => " ".repeat(Math.max(1, panelWidth() - gutter() - HEADER_PREFIX - visualWidth(t("title")) - visualWidth(pct() + " " + t("hitFolded")))));
1033
1272
  return _el$11;
1034
1273
  })(), (() => {
1035
1274
  var _el$12 = _$createElement("span"), _el$13 = _$createTextNode(` `);
1036
1275
  _$insertNode(_el$12, _el$13);
1037
1276
  _$insert(_el$12, pct, _el$13);
1038
- _$insert(_el$12, () => t().hitFolded, null);
1277
+ _$insert(_el$12, () => t("hitFolded"), null);
1039
1278
  _$effect((_$p) => _$setProp(_el$12, "style", {
1040
1279
  fg: hitColor()
1041
1280
  }, _$p));
@@ -1056,28 +1295,28 @@ function TokenCachePanel(props) {
1056
1295
  },
1057
1296
  get children() {
1058
1297
  return (() => {
1059
- const prefix = " \u21B3 " + (langZH() ? "\u5B50\u4EE3\u7406: " : "Sub: ");
1298
+ const prefix = " \u21B3 " + t("subPrefix");
1060
1299
  const maxSidW = Math.max(6, panelWidth() - visualWidth(prefix));
1061
1300
  return (() => {
1062
- var _el$64 = _$createElement("text"), _el$65 = _$createElement("span"), _el$66 = _$createElement("span");
1063
- _$insertNode(_el$64, _el$65);
1064
- _$insertNode(_el$64, _el$66);
1065
- _$insert(_el$65, prefix);
1066
- _$insert(_el$66, () => truncateVisual(props.signals.overrideSessionId(), maxSidW));
1301
+ var _el$69 = _$createElement("text"), _el$70 = _$createElement("span"), _el$71 = _$createElement("span");
1302
+ _$insertNode(_el$69, _el$70);
1303
+ _$insertNode(_el$69, _el$71);
1304
+ _$insert(_el$70, prefix);
1305
+ _$insert(_el$71, () => truncateVisual(props.signals.overrideSessionId(), maxSidW));
1067
1306
  _$effect((_p$) => {
1068
- var _v$15 = {
1307
+ var _v$17 = {
1069
1308
  fg: pal().muted
1070
- }, _v$16 = {
1309
+ }, _v$18 = {
1071
1310
  fg: pal().text
1072
1311
  };
1073
- _v$15 !== _p$.e && (_p$.e = _$setProp(_el$65, "style", _v$15, _p$.e));
1074
- _v$16 !== _p$.t && (_p$.t = _$setProp(_el$66, "style", _v$16, _p$.t));
1312
+ _v$17 !== _p$.e && (_p$.e = _$setProp(_el$70, "style", _v$17, _p$.e));
1313
+ _v$18 !== _p$.t && (_p$.t = _$setProp(_el$71, "style", _v$18, _p$.t));
1075
1314
  return _p$;
1076
1315
  }, {
1077
1316
  e: void 0,
1078
1317
  t: void 0
1079
1318
  });
1080
- return _el$64;
1319
+ return _el$69;
1081
1320
  })();
1082
1321
  })();
1083
1322
  }
@@ -1087,30 +1326,30 @@ function TokenCachePanel(props) {
1087
1326
  },
1088
1327
  get fallback() {
1089
1328
  return [(() => {
1090
- var _el$67 = _$createElement("text");
1091
- _$insert(_el$67, sep);
1092
- _$effect((_$p) => _$setProp(_el$67, "fg", pal().muted, _$p));
1093
- return _el$67;
1329
+ var _el$72 = _$createElement("text");
1330
+ _$insert(_el$72, sep);
1331
+ _$effect((_$p) => _$setProp(_el$72, "fg", pal().muted, _$p));
1332
+ return _el$72;
1094
1333
  })(), (() => {
1095
- var _el$68 = _$createElement("text"), _el$69 = _$createElement("span"), _el$71 = _$createElement("span");
1096
- _$insertNode(_el$68, _el$69);
1097
- _$insertNode(_el$68, _el$71);
1098
- _$insertNode(_el$69, _$createTextNode(`> `));
1099
- _$insert(_el$71, () => t().noData);
1334
+ var _el$73 = _$createElement("text"), _el$74 = _$createElement("span"), _el$76 = _$createElement("span");
1335
+ _$insertNode(_el$73, _el$74);
1336
+ _$insertNode(_el$73, _el$76);
1337
+ _$insertNode(_el$74, _$createTextNode(`> `));
1338
+ _$insert(_el$76, () => t("noData"));
1100
1339
  _$effect((_p$) => {
1101
- var _v$17 = {
1340
+ var _v$19 = {
1102
1341
  fg: pal().muted
1103
- }, _v$18 = {
1342
+ }, _v$20 = {
1104
1343
  fg: pal().muted
1105
1344
  };
1106
- _v$17 !== _p$.e && (_p$.e = _$setProp(_el$69, "style", _v$17, _p$.e));
1107
- _v$18 !== _p$.t && (_p$.t = _$setProp(_el$71, "style", _v$18, _p$.t));
1345
+ _v$19 !== _p$.e && (_p$.e = _$setProp(_el$74, "style", _v$19, _p$.e));
1346
+ _v$20 !== _p$.t && (_p$.t = _$setProp(_el$76, "style", _v$20, _p$.t));
1108
1347
  return _p$;
1109
1348
  }, {
1110
1349
  e: void 0,
1111
1350
  t: void 0
1112
1351
  });
1113
- return _el$68;
1352
+ return _el$73;
1114
1353
  })()];
1115
1354
  },
1116
1355
  get children() {
@@ -1125,7 +1364,7 @@ function TokenCachePanel(props) {
1125
1364
  _$insertNode(_el$15, _el$18);
1126
1365
  _$insertNode(_el$15, _el$21);
1127
1366
  _$insertNode(_el$16, _el$17);
1128
- _$insert(_el$16, () => t().hit, _el$17);
1367
+ _$insert(_el$16, () => t("hit"), _el$17);
1129
1368
  _$insertNode(_el$18, _el$19);
1130
1369
  _$insertNode(_el$18, _el$20);
1131
1370
  _$insert(_el$18, bar, _el$20);
@@ -1139,7 +1378,7 @@ function TokenCachePanel(props) {
1139
1378
  _$insertNode(_el$22, _el$23);
1140
1379
  _$insert(_el$22, () => trendLabel(data().trend), null);
1141
1380
  _$effect((_$p) => _$setProp(_el$22, "style", {
1142
- fg: data().trend !== 0 ? data().trend > 0 ? pal().success : pal().error : pal().text
1381
+ fg: Math.abs(data().trend) >= 0.05 ? data().trend > 0 ? pal().success : pal().error : pal().text
1143
1382
  }, _$p));
1144
1383
  return _el$22;
1145
1384
  }
@@ -1164,7 +1403,7 @@ function TokenCachePanel(props) {
1164
1403
  return _el$15;
1165
1404
  })(), (() => {
1166
1405
  var _el$24 = _$createElement("text");
1167
- _$insert(_el$24, () => justify(t().totalHit, (Math.floor(data().sessionHitRate * 10) / 10).toFixed(1) + "%"));
1406
+ _$insert(_el$24, () => justify(t("totalHit"), (Math.floor(data().sessionHitRate * 10) / 10).toFixed(1) + "%"));
1168
1407
  _$effect((_$p) => _$setProp(_el$24, "fg", pal().muted, _$p));
1169
1408
  return _el$24;
1170
1409
  })(), _$createComponent(Show, {
@@ -1184,8 +1423,8 @@ function TokenCachePanel(props) {
1184
1423
  }));
1185
1424
  _$insert(_el$26, () => detailOpen() ? "\u25BC " : "\u25B6 ");
1186
1425
  _$insertNode(_el$27, _el$28);
1187
- _$insert(_el$28, () => t().secDetail);
1188
- _$insert(_el$29, () => sep().slice(visualWidth((detailOpen() ? "\u25BC " : "\u25B6 ") + t().secDetail)));
1426
+ _$insert(_el$28, () => t("secDetail"));
1427
+ _$insert(_el$29, () => sep().slice(visualWidth((detailOpen() ? "\u25BC " : "\u25B6 ") + t("secDetail"))));
1189
1428
  _$effect((_p$) => {
1190
1429
  var _v$4 = {
1191
1430
  fg: pal().muted
@@ -1215,7 +1454,7 @@ function TokenCachePanel(props) {
1215
1454
  },
1216
1455
  get children() {
1217
1456
  var _el$30 = _$createElement("text");
1218
- _$insert(_el$30, () => justify(t().read, fmt(data().read), t().tok));
1457
+ _$insert(_el$30, () => justify(t("read"), fmt(data().read), t("tok")));
1219
1458
  _$effect((_$p) => _$setProp(_el$30, "fg", pal().muted, _$p));
1220
1459
  return _el$30;
1221
1460
  }
@@ -1225,18 +1464,18 @@ function TokenCachePanel(props) {
1225
1464
  },
1226
1465
  get children() {
1227
1466
  var _el$31 = _$createElement("text");
1228
- _$insert(_el$31, () => justify(t().write, fmt(data().write), t().tok));
1467
+ _$insert(_el$31, () => justify(t("write"), fmt(data().write), t("tok")));
1229
1468
  _$effect((_$p) => _$setProp(_el$31, "fg", pal().muted, _$p));
1230
1469
  return _el$31;
1231
1470
  }
1232
1471
  }), (() => {
1233
1472
  var _el$32 = _$createElement("text");
1234
- _$insert(_el$32, () => justify(t().miss, fmt(data().freshInput), t().tok));
1473
+ _$insert(_el$32, () => justify(t("miss"), fmt(data().freshInput + data().write), t("tok")));
1235
1474
  _$effect((_$p) => _$setProp(_el$32, "fg", pal().muted, _$p));
1236
1475
  return _el$32;
1237
1476
  })(), (() => {
1238
1477
  var _el$33 = _$createElement("text");
1239
- _$insert(_el$33, () => justify(t().out, fmt(data().output), t().tok));
1478
+ _$insert(_el$33, () => justify(t("out"), fmt(data().output), t("tok")));
1240
1479
  _$effect((_$p) => _$setProp(_el$33, "fg", pal().muted, _$p));
1241
1480
  return _el$33;
1242
1481
  })(), _$createComponent(Show, {
@@ -1248,8 +1487,8 @@ function TokenCachePanel(props) {
1248
1487
  _$insertNode(_el$34, _el$35);
1249
1488
  _$insertNode(_el$34, _el$36);
1250
1489
  _$insertNode(_el$34, _el$37);
1251
- _$insert(_el$35, () => t().saved);
1252
- _$insert(_el$36, () => " ".repeat(Math.max(1, panelWidth() - gutter() - visualWidth(t().saved) - visualWidth("~" + fmtCost(data().saved, currencySymbol(), exchangeRate())))));
1490
+ _$insert(_el$35, () => t("saved"));
1491
+ _$insert(_el$36, () => " ".repeat(Math.max(1, panelWidth() - gutter() - visualWidth(t("saved")) - visualWidth("~" + fmtCost(data().saved, currencySymbol(), exchangeRate())))));
1253
1492
  _$insertNode(_el$37, _el$38);
1254
1493
  _$insert(_el$37, () => fmtCost(data().saved, currencySymbol(), exchangeRate()), null);
1255
1494
  _$effect((_p$) => {
@@ -1277,37 +1516,37 @@ function TokenCachePanel(props) {
1277
1516
  },
1278
1517
  get children() {
1279
1518
  return [_$memo(() => (() => {
1280
- var _el$72 = _$createElement("text"), _el$73 = _$createElement("span"), _el$74 = _$createElement("span"), _el$75 = _$createElement("b"), _el$76 = _$createElement("span");
1281
- _$insertNode(_el$72, _el$73);
1282
- _$insertNode(_el$72, _el$74);
1283
- _$insertNode(_el$72, _el$76);
1284
- _$setProp(_el$72, "onMouseUp", () => setModelOpen((o) => {
1519
+ var _el$77 = _$createElement("text"), _el$78 = _$createElement("span"), _el$79 = _$createElement("span"), _el$80 = _$createElement("b"), _el$81 = _$createElement("span");
1520
+ _$insertNode(_el$77, _el$78);
1521
+ _$insertNode(_el$77, _el$79);
1522
+ _$insertNode(_el$77, _el$81);
1523
+ _$setProp(_el$77, "onMouseUp", () => setModelOpen((o) => {
1285
1524
  const n = !o;
1286
1525
  persistFold("model", n);
1287
1526
  return n;
1288
1527
  }));
1289
- _$insert(_el$73, () => modelOpen() ? "\u25BC " : "\u25B6 ");
1290
- _$insertNode(_el$74, _el$75);
1291
- _$insert(_el$75, () => t().secModel);
1292
- _$insert(_el$76, () => sep().slice(visualWidth((modelOpen() ? "\u25BC " : "\u25B6 ") + t().secModel)));
1528
+ _$insert(_el$78, () => modelOpen() ? "\u25BC " : "\u25B6 ");
1529
+ _$insertNode(_el$79, _el$80);
1530
+ _$insert(_el$80, () => t("secModel"));
1531
+ _$insert(_el$81, () => sep().slice(visualWidth((modelOpen() ? "\u25BC " : "\u25B6 ") + t("secModel"))));
1293
1532
  _$effect((_p$) => {
1294
- var _v$19 = {
1533
+ var _v$21 = {
1295
1534
  fg: pal().muted
1296
- }, _v$20 = {
1535
+ }, _v$22 = {
1297
1536
  fg: pal().primary
1298
- }, _v$21 = {
1537
+ }, _v$23 = {
1299
1538
  fg: pal().muted
1300
1539
  };
1301
- _v$19 !== _p$.e && (_p$.e = _$setProp(_el$73, "style", _v$19, _p$.e));
1302
- _v$20 !== _p$.t && (_p$.t = _$setProp(_el$74, "style", _v$20, _p$.t));
1303
- _v$21 !== _p$.a && (_p$.a = _$setProp(_el$76, "style", _v$21, _p$.a));
1540
+ _v$21 !== _p$.e && (_p$.e = _$setProp(_el$78, "style", _v$21, _p$.e));
1541
+ _v$22 !== _p$.t && (_p$.t = _$setProp(_el$79, "style", _v$22, _p$.t));
1542
+ _v$23 !== _p$.a && (_p$.a = _$setProp(_el$81, "style", _v$23, _p$.a));
1304
1543
  return _p$;
1305
1544
  }, {
1306
1545
  e: void 0,
1307
1546
  t: void 0,
1308
1547
  a: void 0
1309
1548
  });
1310
- return _el$72;
1549
+ return _el$77;
1311
1550
  })()), _$createComponent(Show, {
1312
1551
  get when() {
1313
1552
  return modelOpen();
@@ -1315,7 +1554,7 @@ function TokenCachePanel(props) {
1315
1554
  get children() {
1316
1555
  return [(() => {
1317
1556
  var _el$39 = _$createElement("text");
1318
- _$insert(_el$39, () => justify(t().cost, fmtCost(data().cost, currencySymbol(), exchangeRate())));
1557
+ _$insert(_el$39, () => justify(t("cost"), fmtCost(data().cost, currencySymbol(), exchangeRate())));
1319
1558
  _$effect((_$p) => _$setProp(_el$39, "fg", pal().text, _$p));
1320
1559
  return _el$39;
1321
1560
  })(), _$createComponent(Show, {
@@ -1324,13 +1563,13 @@ function TokenCachePanel(props) {
1324
1563
  },
1325
1564
  get children() {
1326
1565
  var _el$40 = _$createElement("text");
1327
- _$insert(_el$40, () => justify(t().provider, data().providerName));
1566
+ _$insert(_el$40, () => justify(t("provider"), data().providerName));
1328
1567
  _$effect((_$p) => _$setProp(_el$40, "fg", pal().muted, _$p));
1329
1568
  return _el$40;
1330
1569
  }
1331
1570
  }), (() => {
1332
1571
  var _el$41 = _$createElement("text");
1333
- _$insert(_el$41, () => justify(t().model, data().model));
1572
+ _$insert(_el$41, () => justify(t("model"), data().model));
1334
1573
  _$effect((_$p) => _$setProp(_el$41, "fg", pal().muted, _$p));
1335
1574
  return _el$41;
1336
1575
  })(), _$createComponent(Show, {
@@ -1340,7 +1579,7 @@ function TokenCachePanel(props) {
1340
1579
  get children() {
1341
1580
  return [(() => {
1342
1581
  var _el$42 = _$createElement("text");
1343
- _$insert(_el$42, () => justify(t().rate, currencySymbol() + (data().inputRate * exchangeRate()).toFixed(2) + "/M " + t().inputRate));
1582
+ _$insert(_el$42, () => justify(t("rate"), currencySymbol() + (data().inputRate * exchangeRate()).toFixed(2) + "/M " + t("inputRate")));
1344
1583
  _$effect((_$p) => _$setProp(_el$42, "fg", pal().muted, _$p));
1345
1584
  return _el$42;
1346
1585
  })(), _$createComponent(Show, {
@@ -1349,7 +1588,7 @@ function TokenCachePanel(props) {
1349
1588
  },
1350
1589
  get children() {
1351
1590
  var _el$43 = _$createElement("text");
1352
- _$insert(_el$43, () => justify("", currencySymbol() + (data().cacheReadRate * exchangeRate()).toFixed(2) + "/M " + t().cacheRate));
1591
+ _$insert(_el$43, () => justify("", currencySymbol() + (data().cacheReadRate * exchangeRate()).toFixed(2) + "/M " + t("cacheRate")));
1353
1592
  _$effect((_$p) => _$setProp(_el$43, "fg", pal().muted, _$p));
1354
1593
  return _el$43;
1355
1594
  }
@@ -1359,7 +1598,7 @@ function TokenCachePanel(props) {
1359
1598
  },
1360
1599
  get children() {
1361
1600
  var _el$44 = _$createElement("text");
1362
- _$insert(_el$44, () => justify("", currencySymbol() + (data().cacheWriteRate * exchangeRate()).toFixed(2) + "/M " + t().writeRate));
1601
+ _$insert(_el$44, () => justify("", currencySymbol() + (data().cacheWriteRate * exchangeRate()).toFixed(2) + "/M " + t("writeRate")));
1363
1602
  _$effect((_$p) => _$setProp(_el$44, "fg", pal().muted, _$p));
1364
1603
  return _el$44;
1365
1604
  }
@@ -1380,37 +1619,37 @@ function TokenCachePanel(props) {
1380
1619
  },
1381
1620
  get children() {
1382
1621
  return [_$memo(() => (() => {
1383
- var _el$77 = _$createElement("text"), _el$78 = _$createElement("span"), _el$79 = _$createElement("span"), _el$80 = _$createElement("b"), _el$81 = _$createElement("span");
1384
- _$insertNode(_el$77, _el$78);
1385
- _$insertNode(_el$77, _el$79);
1386
- _$insertNode(_el$77, _el$81);
1387
- _$setProp(_el$77, "onMouseUp", () => setDistOpen((o) => {
1622
+ var _el$82 = _$createElement("text"), _el$83 = _$createElement("span"), _el$84 = _$createElement("span"), _el$85 = _$createElement("b"), _el$86 = _$createElement("span");
1623
+ _$insertNode(_el$82, _el$83);
1624
+ _$insertNode(_el$82, _el$84);
1625
+ _$insertNode(_el$82, _el$86);
1626
+ _$setProp(_el$82, "onMouseUp", () => setDistOpen((o) => {
1388
1627
  const n = !o;
1389
1628
  persistFold("dist", n);
1390
1629
  return n;
1391
1630
  }));
1392
- _$insert(_el$78, () => distOpen() ? "\u25BC " : "\u25B6 ");
1393
- _$insertNode(_el$79, _el$80);
1394
- _$insert(_el$80, () => t().distTitle);
1395
- _$insert(_el$81, () => sep().slice(visualWidth((distOpen() ? "\u25BC " : "\u25B6 ") + t().distTitle)));
1631
+ _$insert(_el$83, () => distOpen() ? "\u25BC " : "\u25B6 ");
1632
+ _$insertNode(_el$84, _el$85);
1633
+ _$insert(_el$85, () => t("distTitle"));
1634
+ _$insert(_el$86, () => sep().slice(visualWidth((distOpen() ? "\u25BC " : "\u25B6 ") + t("distTitle"))));
1396
1635
  _$effect((_p$) => {
1397
- var _v$22 = {
1636
+ var _v$24 = {
1398
1637
  fg: pal().muted
1399
- }, _v$23 = {
1638
+ }, _v$25 = {
1400
1639
  fg: pal().primary
1401
- }, _v$24 = {
1640
+ }, _v$26 = {
1402
1641
  fg: pal().muted
1403
1642
  };
1404
- _v$22 !== _p$.e && (_p$.e = _$setProp(_el$78, "style", _v$22, _p$.e));
1405
- _v$23 !== _p$.t && (_p$.t = _$setProp(_el$79, "style", _v$23, _p$.t));
1406
- _v$24 !== _p$.a && (_p$.a = _$setProp(_el$81, "style", _v$24, _p$.a));
1643
+ _v$24 !== _p$.e && (_p$.e = _$setProp(_el$83, "style", _v$24, _p$.e));
1644
+ _v$25 !== _p$.t && (_p$.t = _$setProp(_el$84, "style", _v$25, _p$.t));
1645
+ _v$26 !== _p$.a && (_p$.a = _$setProp(_el$86, "style", _v$26, _p$.a));
1407
1646
  return _p$;
1408
1647
  }, {
1409
1648
  e: void 0,
1410
1649
  t: void 0,
1411
1650
  a: void 0
1412
1651
  });
1413
- return _el$77;
1652
+ return _el$82;
1414
1653
  })()), _$createComponent(Show, {
1415
1654
  get when() {
1416
1655
  return distOpen();
@@ -1422,7 +1661,7 @@ function TokenCachePanel(props) {
1422
1661
  },
1423
1662
  get children() {
1424
1663
  var _el$45 = _$createElement("text");
1425
- _$insert(_el$45, () => justify(t().distSys, fmt(data().dist.system), t().tok));
1664
+ _$insert(_el$45, () => justify(t("distSys"), fmt(data().dist.system), t("tok")));
1426
1665
  _$effect((_$p) => _$setProp(_el$45, "fg", pal().muted, _$p));
1427
1666
  return _el$45;
1428
1667
  }
@@ -1432,7 +1671,7 @@ function TokenCachePanel(props) {
1432
1671
  },
1433
1672
  get children() {
1434
1673
  var _el$46 = _$createElement("text");
1435
- _$insert(_el$46, () => justify(t().distUser, fmt(data().dist.user), t().tok));
1674
+ _$insert(_el$46, () => justify(t("distUser"), fmt(data().dist.user), t("tok")));
1436
1675
  _$effect((_$p) => _$setProp(_el$46, "fg", pal().muted, _$p));
1437
1676
  return _el$46;
1438
1677
  }
@@ -1442,7 +1681,7 @@ function TokenCachePanel(props) {
1442
1681
  },
1443
1682
  get children() {
1444
1683
  var _el$47 = _$createElement("text");
1445
- _$insert(_el$47, () => justify(t().distAgent, fmt(data().dist.agent), t().tok));
1684
+ _$insert(_el$47, () => justify(t("distAgent"), fmt(data().dist.agent), t("tok")));
1446
1685
  _$effect((_$p) => _$setProp(_el$47, "fg", pal().muted, _$p));
1447
1686
  return _el$47;
1448
1687
  }
@@ -1452,7 +1691,7 @@ function TokenCachePanel(props) {
1452
1691
  },
1453
1692
  get children() {
1454
1693
  var _el$48 = _$createElement("text");
1455
- _$insert(_el$48, () => justify(t().distTool, fmt(data().dist.toolCall), t().tok));
1694
+ _$insert(_el$48, () => justify(t("distTool"), fmt(data().dist.toolCall), t("tok")));
1456
1695
  _$effect((_$p) => _$setProp(_el$48, "fg", pal().muted, _$p));
1457
1696
  return _el$48;
1458
1697
  }
@@ -1462,13 +1701,13 @@ function TokenCachePanel(props) {
1462
1701
  },
1463
1702
  get children() {
1464
1703
  var _el$49 = _$createElement("text");
1465
- _$insert(_el$49, () => justify(t().distRes, fmt(data().dist.toolResult), t().tok));
1704
+ _$insert(_el$49, () => justify(t("distRes"), fmt(data().dist.toolResult), t("tok")));
1466
1705
  _$effect((_$p) => _$setProp(_el$49, "fg", pal().muted, _$p));
1467
1706
  return _el$49;
1468
1707
  }
1469
1708
  }), (() => {
1470
1709
  var _el$50 = _$createElement("text");
1471
- _$insert(_el$50, () => justify(t().distTotal, fmt(data().dist.apiInput), t().tok));
1710
+ _$insert(_el$50, () => justify(t("distTotal"), fmt(data().dist.apiInput), t("tok")));
1472
1711
  _$effect((_$p) => _$setProp(_el$50, "fg", pal().text, _$p));
1473
1712
  return _el$50;
1474
1713
  })()];
@@ -1488,37 +1727,37 @@ function TokenCachePanel(props) {
1488
1727
  },
1489
1728
  get children() {
1490
1729
  return [_$memo(() => (() => {
1491
- 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");
1492
- _$insertNode(_el$82, _el$83);
1493
- _$insertNode(_el$82, _el$84);
1494
- _$insertNode(_el$82, _el$86);
1495
- _$insertNode(_el$82, _el$89);
1496
- _$setProp(_el$82, "onMouseUp", () => setSkillsOpen((o) => {
1730
+ var _el$87 = _$createElement("text"), _el$88 = _$createElement("span"), _el$89 = _$createElement("span"), _el$90 = _$createElement("b"), _el$91 = _$createElement("span"), _el$92 = _$createTextNode(` (`), _el$93 = _$createTextNode(`)`), _el$94 = _$createElement("span");
1731
+ _$insertNode(_el$87, _el$88);
1732
+ _$insertNode(_el$87, _el$89);
1733
+ _$insertNode(_el$87, _el$91);
1734
+ _$insertNode(_el$87, _el$94);
1735
+ _$setProp(_el$87, "onMouseUp", () => setSkillsOpen((o) => {
1497
1736
  const n = !o;
1498
1737
  persistFold("skills", n);
1499
1738
  return n;
1500
1739
  }));
1501
- _$insert(_el$83, () => skillsOpen() ? "\u25BC " : "\u25B6 ");
1502
- _$insertNode(_el$84, _el$85);
1503
- _$insert(_el$85, () => t().secSkills);
1504
- _$insertNode(_el$86, _el$87);
1505
- _$insertNode(_el$86, _el$88);
1506
- _$insert(_el$86, () => data().skills.length, _el$88);
1507
- _$insert(_el$89, () => sep().slice(visualWidth((skillsOpen() ? "\u25BC " : "\u25B6 ") + t().secSkills + ` (${data().skills.length})`)));
1740
+ _$insert(_el$88, () => skillsOpen() ? "\u25BC " : "\u25B6 ");
1741
+ _$insertNode(_el$89, _el$90);
1742
+ _$insert(_el$90, () => t("secSkills"));
1743
+ _$insertNode(_el$91, _el$92);
1744
+ _$insertNode(_el$91, _el$93);
1745
+ _$insert(_el$91, () => data().skills.length, _el$93);
1746
+ _$insert(_el$94, () => sep().slice(visualWidth((skillsOpen() ? "\u25BC " : "\u25B6 ") + t("secSkills") + ` (${data().skills.length})`)));
1508
1747
  _$effect((_p$) => {
1509
- var _v$25 = {
1748
+ var _v$27 = {
1510
1749
  fg: pal().muted
1511
- }, _v$26 = {
1750
+ }, _v$28 = {
1512
1751
  fg: pal().primary
1513
- }, _v$27 = {
1752
+ }, _v$29 = {
1514
1753
  fg: pal().muted
1515
- }, _v$28 = {
1754
+ }, _v$30 = {
1516
1755
  fg: pal().muted
1517
1756
  };
1518
- _v$25 !== _p$.e && (_p$.e = _$setProp(_el$83, "style", _v$25, _p$.e));
1519
- _v$26 !== _p$.t && (_p$.t = _$setProp(_el$84, "style", _v$26, _p$.t));
1520
- _v$27 !== _p$.a && (_p$.a = _$setProp(_el$86, "style", _v$27, _p$.a));
1521
- _v$28 !== _p$.o && (_p$.o = _$setProp(_el$89, "style", _v$28, _p$.o));
1757
+ _v$27 !== _p$.e && (_p$.e = _$setProp(_el$88, "style", _v$27, _p$.e));
1758
+ _v$28 !== _p$.t && (_p$.t = _$setProp(_el$89, "style", _v$28, _p$.t));
1759
+ _v$29 !== _p$.a && (_p$.a = _$setProp(_el$91, "style", _v$29, _p$.a));
1760
+ _v$30 !== _p$.o && (_p$.o = _$setProp(_el$94, "style", _v$30, _p$.o));
1522
1761
  return _p$;
1523
1762
  }, {
1524
1763
  e: void 0,
@@ -1526,21 +1765,21 @@ function TokenCachePanel(props) {
1526
1765
  a: void 0,
1527
1766
  o: void 0
1528
1767
  });
1529
- return _el$82;
1768
+ return _el$87;
1530
1769
  })()), _$createComponent(Show, {
1531
1770
  get when() {
1532
1771
  return skillsOpen();
1533
1772
  },
1534
1773
  get children() {
1535
1774
  return data().skills.map((sk) => {
1536
- const rightW = visualWidth(fmt(sk.tokens)) + UNIT_GAP + visualWidth(t().tok);
1775
+ const rightW = visualWidth(fmt(sk.tokens)) + UNIT_GAP + visualWidth(t("tok"));
1537
1776
  const maxLabel = Math.max(4, panelWidth() - gutter() - rightW - 1);
1538
1777
  const label = truncateVisual(sk.name, maxLabel);
1539
1778
  return (() => {
1540
- var _el$90 = _$createElement("text");
1541
- _$insert(_el$90, () => justify(label, fmt(sk.tokens), t().tok));
1542
- _$effect((_$p) => _$setProp(_el$90, "fg", pal().muted, _$p));
1543
- return _el$90;
1779
+ var _el$95 = _$createElement("text");
1780
+ _$insert(_el$95, () => justify(label, fmt(sk.tokens), t("tok")));
1781
+ _$effect((_$p) => _$setProp(_el$95, "fg", pal().muted, _$p));
1782
+ return _el$95;
1544
1783
  })();
1545
1784
  });
1546
1785
  }
@@ -1560,14 +1799,14 @@ function TokenCachePanel(props) {
1560
1799
  return _el$51;
1561
1800
  })(), _$createComponent(Show, {
1562
1801
  get when() {
1563
- return balanceState().status === "idle";
1802
+ return balanceUnsupported();
1564
1803
  },
1565
1804
  get children() {
1566
1805
  var _el$52 = _$createElement("text"), _el$53 = _$createElement("span"), _el$55 = _$createElement("span");
1567
1806
  _$insertNode(_el$52, _el$53);
1568
1807
  _$insertNode(_el$52, _el$55);
1569
1808
  _$insertNode(_el$53, _$createTextNode(`> `));
1570
- _$insert(_el$55, () => t().balNoKey.replace("{p}", providerName()));
1809
+ _$insert(_el$55, () => t("balUnsupported"));
1571
1810
  _$effect((_p$) => {
1572
1811
  var _v$9 = pal().muted, _v$0 = {
1573
1812
  fg: pal().muted
@@ -1583,87 +1822,98 @@ function TokenCachePanel(props) {
1583
1822
  }
1584
1823
  }), _$createComponent(Show, {
1585
1824
  get when() {
1586
- return balanceState().status === "loading";
1825
+ return !balanceUnsupported();
1587
1826
  },
1588
1827
  get children() {
1589
- var _el$56 = _$createElement("text"), _el$57 = _$createElement("span"), _el$59 = _$createElement("span");
1590
- _$insertNode(_el$56, _el$57);
1591
- _$insertNode(_el$56, _el$59);
1592
- _$insertNode(_el$57, _$createTextNode(`> `));
1593
- _$insert(_el$59, () => t().balLoading);
1594
- _$effect((_p$) => {
1595
- var _v$1 = pal().muted, _v$10 = {
1596
- fg: pal().muted
1597
- };
1598
- _v$1 !== _p$.e && (_p$.e = _$setProp(_el$56, "fg", _v$1, _p$.e));
1599
- _v$10 !== _p$.t && (_p$.t = _$setProp(_el$57, "style", _v$10, _p$.t));
1600
- return _p$;
1601
- }, {
1602
- e: void 0,
1603
- t: void 0
1604
- });
1605
- return _el$56;
1606
- }
1607
- }), _$createComponent(Show, {
1608
- get when() {
1609
- return balanceState().status === "error";
1610
- },
1611
- get children() {
1612
- var _el$60 = _$createElement("text"), _el$61 = _$createElement("span"), _el$63 = _$createElement("span");
1613
- _$insertNode(_el$60, _el$61);
1614
- _$insertNode(_el$60, _el$63);
1615
- _$insertNode(_el$61, _$createTextNode(`> `));
1616
- _$insert(_el$63, () => {
1617
- const code = balanceState().error;
1618
- if (code === "401") return t().balErr401;
1619
- if (code === "403") return t().balErr403;
1620
- if (code === "EMPTY") return t().balErrEmpty;
1621
- if (code === "TIMEOUT") return t().balErrTimeout;
1622
- return t().balError + (code ? ` (${code})` : "");
1623
- });
1624
- _$effect((_p$) => {
1625
- var _v$11 = pal().error, _v$12 = {
1626
- fg: pal().muted
1627
- };
1628
- _v$11 !== _p$.e && (_p$.e = _$setProp(_el$60, "fg", _v$11, _p$.e));
1629
- _v$12 !== _p$.t && (_p$.t = _$setProp(_el$61, "style", _v$12, _p$.t));
1630
- return _p$;
1631
- }, {
1632
- e: void 0,
1633
- t: void 0
1634
- });
1635
- return _el$60;
1636
- }
1637
- }), _$createComponent(Show, {
1638
- get when() {
1639
- return _$memo(() => balanceState().status === "ok")() && balanceState().data;
1640
- },
1641
- get children() {
1642
- return (() => {
1643
- const list = balanceState().data;
1644
- const pref = balanceCurrency();
1645
- const native = pref ? list.find((x) => x.currency === pref) : void 0;
1646
- if (native) {
1647
- return (() => {
1648
- var _el$91 = _$createElement("text");
1649
- _$insert(_el$91, () => justify(t().balTotal, balanceSymbol(native.currency) + native.total));
1650
- _$effect((_$p) => _$setProp(_el$91, "fg", pal().text, _$p));
1651
- return _el$91;
1652
- })();
1828
+ return [_$createComponent(Show, {
1829
+ get when() {
1830
+ return balanceState().status === "idle";
1831
+ },
1832
+ get children() {
1833
+ var _el$56 = _$createElement("text"), _el$57 = _$createElement("span"), _el$59 = _$createElement("span");
1834
+ _$insertNode(_el$56, _el$57);
1835
+ _$insertNode(_el$56, _el$59);
1836
+ _$insertNode(_el$57, _$createTextNode(`> `));
1837
+ _$insert(_el$59, () => t("balNoKey", {
1838
+ p: providerName()
1839
+ }));
1840
+ _$effect((_p$) => {
1841
+ var _v$1 = pal().muted, _v$10 = {
1842
+ fg: pal().muted
1843
+ };
1844
+ _v$1 !== _p$.e && (_p$.e = _$setProp(_el$56, "fg", _v$1, _p$.e));
1845
+ _v$10 !== _p$.t && (_p$.t = _$setProp(_el$57, "style", _v$10, _p$.t));
1846
+ return _p$;
1847
+ }, {
1848
+ e: void 0,
1849
+ t: void 0
1850
+ });
1851
+ return _el$56;
1852
+ }
1853
+ }), _$createComponent(Show, {
1854
+ get when() {
1855
+ return balanceState().status === "loading";
1856
+ },
1857
+ get children() {
1858
+ var _el$60 = _$createElement("text"), _el$61 = _$createElement("span"), _el$63 = _$createElement("span");
1859
+ _$insertNode(_el$60, _el$61);
1860
+ _$insertNode(_el$60, _el$63);
1861
+ _$insertNode(_el$61, _$createTextNode(`> `));
1862
+ _$insert(_el$63, () => t("balLoading"));
1863
+ _$effect((_p$) => {
1864
+ var _v$11 = pal().muted, _v$12 = {
1865
+ fg: pal().muted
1866
+ };
1867
+ _v$11 !== _p$.e && (_p$.e = _$setProp(_el$60, "fg", _v$11, _p$.e));
1868
+ _v$12 !== _p$.t && (_p$.t = _$setProp(_el$61, "style", _v$12, _p$.t));
1869
+ return _p$;
1870
+ }, {
1871
+ e: void 0,
1872
+ t: void 0
1873
+ });
1874
+ return _el$60;
1875
+ }
1876
+ }), _$createComponent(Show, {
1877
+ get when() {
1878
+ return balanceState().status === "error";
1879
+ },
1880
+ get children() {
1881
+ var _el$64 = _$createElement("text"), _el$65 = _$createElement("span"), _el$67 = _$createElement("span");
1882
+ _$insertNode(_el$64, _el$65);
1883
+ _$insertNode(_el$64, _el$67);
1884
+ _$insertNode(_el$65, _$createTextNode(`> `));
1885
+ _$insert(_el$67, () => {
1886
+ const code = balanceState().error;
1887
+ if (code === "401") return t("balErr401");
1888
+ if (code === "403") return t("balErr403");
1889
+ if (code === "EMPTY") return t("balErrEmpty");
1890
+ if (code === "TIMEOUT") return t("balErrTimeout");
1891
+ return t("balError") + (code ? ` (${code})` : "");
1892
+ });
1893
+ _$effect((_p$) => {
1894
+ var _v$13 = pal().error, _v$14 = {
1895
+ fg: pal().muted
1896
+ };
1897
+ _v$13 !== _p$.e && (_p$.e = _$setProp(_el$64, "fg", _v$13, _p$.e));
1898
+ _v$14 !== _p$.t && (_p$.t = _$setProp(_el$65, "style", _v$14, _p$.t));
1899
+ return _p$;
1900
+ }, {
1901
+ e: void 0,
1902
+ t: void 0
1903
+ });
1904
+ return _el$64;
1905
+ }
1906
+ }), _$createComponent(Show, {
1907
+ get when() {
1908
+ return _$memo(() => balanceState().status === "ok")() && balanceState().data;
1909
+ },
1910
+ get children() {
1911
+ var _el$68 = _$createElement("text");
1912
+ _$insert(_el$68, () => justify(t("balTotal"), formatBalanceText(balanceState().data, balanceCurrency(), exchangeRate())));
1913
+ _$effect((_$p) => _$setProp(_el$68, "fg", pal().text, _$p));
1914
+ return _el$68;
1653
1915
  }
1654
- const base = list[0];
1655
- const baseAmt = parseFloat(base.total);
1656
- const converted = Number.isFinite(baseAmt) ? convertBalance(pref || base.currency, exchangeRate(), baseAmt, base.currency) : baseAmt;
1657
- const shown = pref && base.currency !== pref ? converted.toLocaleString("en-US", {
1658
- maximumFractionDigits: 2
1659
- }) : base.total;
1660
- return (() => {
1661
- var _el$92 = _$createElement("text");
1662
- _$insert(_el$92, () => justify(t().balTotal, balanceSymbol(pref || base.currency) + shown));
1663
- _$effect((_$p) => _$setProp(_el$92, "fg", pal().text, _$p));
1664
- return _el$92;
1665
- })();
1666
- })();
1916
+ })];
1667
1917
  }
1668
1918
  })];
1669
1919
  }
@@ -1673,13 +1923,13 @@ function TokenCachePanel(props) {
1673
1923
  }
1674
1924
  }), null);
1675
1925
  _$effect((_p$) => {
1676
- var _v$13 = {
1926
+ var _v$15 = {
1677
1927
  fg: pal().muted
1678
- }, _v$14 = {
1928
+ }, _v$16 = {
1679
1929
  fg: pal().primary
1680
1930
  };
1681
- _v$13 !== _p$.e && (_p$.e = _$setProp(_el$3, "style", _v$13, _p$.e));
1682
- _v$14 !== _p$.t && (_p$.t = _$setProp(_el$4, "style", _v$14, _p$.t));
1931
+ _v$15 !== _p$.e && (_p$.e = _$setProp(_el$3, "style", _v$15, _p$.e));
1932
+ _v$16 !== _p$.t && (_p$.t = _$setProp(_el$4, "style", _v$16, _p$.t));
1683
1933
  return _p$;
1684
1934
  }, {
1685
1935
  e: void 0,
@@ -1688,6 +1938,233 @@ function TokenCachePanel(props) {
1688
1938
  return _el$;
1689
1939
  })();
1690
1940
  }
1941
+ function BottomStatusBar(props) {
1942
+ const KV_PREFIX = "cache_panel";
1943
+ const t = createT(() => props.signals.langCode());
1944
+ const sid = props.sessionId;
1945
+ const stats = createMemo(() => {
1946
+ const id = sid;
1947
+ if (!id) return null;
1948
+ const msgs = props.api.state.session.messages(id);
1949
+ const session = typeof props.api.state.session.get === "function" ? props.api.state.session.get(id) : void 0;
1950
+ let input = session?.tokens?.input ?? 0;
1951
+ let read = session?.tokens?.cache?.read ?? 0;
1952
+ let write = session?.tokens?.cache?.write ?? 0;
1953
+ if (session?.tokens == null) {
1954
+ for (const m of msgs) {
1955
+ if (m.role !== "assistant") continue;
1956
+ const tk = m.tokens;
1957
+ if (!tk) continue;
1958
+ input += num(tk.input);
1959
+ read += num(tk.cache?.read);
1960
+ write += num(tk.cache?.write);
1961
+ }
1962
+ }
1963
+ let hitRate = -1, prevHitRate = -1;
1964
+ for (let i = msgs.length - 1; i >= 0; i--) {
1965
+ const m = msgs[i];
1966
+ if (m.role !== "assistant") continue;
1967
+ const tk = m.tokens;
1968
+ if (!tk) continue;
1969
+ const mit = num(tk.input) + num(tk.cache?.read) + num(tk.cache?.write);
1970
+ const mrt = num(tk.cache?.read);
1971
+ if (mit <= 0) continue;
1972
+ const rate = mrt / mit * 100;
1973
+ if (hitRate < 0) {
1974
+ hitRate = rate;
1975
+ continue;
1976
+ }
1977
+ prevHitRate = rate;
1978
+ break;
1979
+ }
1980
+ return {
1981
+ hitRate,
1982
+ prevHitRate,
1983
+ input,
1984
+ read,
1985
+ write
1986
+ };
1987
+ });
1988
+ createEffect(() => {
1989
+ if (!props.signals.autoBalance()) return;
1990
+ const id = sid;
1991
+ if (!id) return;
1992
+ const msgs = props.api.state.session.messages(id);
1993
+ let pid = "";
1994
+ for (let i = msgs.length - 1; i >= 0; i--) {
1995
+ const m = msgs[i];
1996
+ if (m.role === "assistant" && m.providerID) {
1997
+ pid = m.providerID;
1998
+ break;
1999
+ }
2000
+ }
2001
+ if (!pid) {
2002
+ try {
2003
+ pid = props.api.state.session.get(id)?.model?.providerID ?? "";
2004
+ } catch {
2005
+ }
2006
+ }
2007
+ if (!pid) return;
2008
+ const hit = matchBalanceProvider(pid);
2009
+ if (hit) {
2010
+ props.signals.setBalanceUnsupported(false);
2011
+ if (hit.id !== props.signals.balanceProviderId()) {
2012
+ props.signals.setBalanceProviderId(hit.id);
2013
+ props.signals.setBalanceRefresh(props.signals.balanceRefresh() + 1);
2014
+ }
2015
+ } else {
2016
+ props.signals.setBalanceUnsupported(true);
2017
+ }
2018
+ });
2019
+ const pal = createMemo(() => {
2020
+ const th = props.api.theme.current;
2021
+ const sat = (k, fb) => desaturateTo(th[k], MAX_SAT, fb);
2022
+ return {
2023
+ text: sat("text", FALLBACK.text),
2024
+ muted: sat("textMuted", FALLBACK.muted),
2025
+ success: sat("success", FALLBACK.success),
2026
+ warning: sat("warning", FALLBACK.warning),
2027
+ error: sat("error", FALLBACK.error)
2028
+ };
2029
+ });
2030
+ const hitColor = createMemo(() => {
2031
+ const r = stats()?.hitRate ?? -1;
2032
+ if (r >= 85) return pal().success;
2033
+ if (r >= 70) return pal().warning;
2034
+ return pal().error;
2035
+ });
2036
+ const trend = createMemo(() => {
2037
+ const s = stats();
2038
+ if (!s || s.prevHitRate < 0 || s.hitRate < 0) return null;
2039
+ const d = s.hitRate - s.prevHitRate;
2040
+ return Math.abs(d) < 0.05 ? null : d;
2041
+ });
2042
+ const balanceText = createMemo(() => {
2043
+ const s = props.signals.balanceState();
2044
+ if (s.status === "ok" && s.data) return formatBalanceText(s.data, props.signals.balanceCurrency(), props.signals.exchangeRate());
2045
+ if (s.status === "loading") return "\u2026";
2046
+ if (s.status === "error") return "\u26A0";
2047
+ return "-";
2048
+ });
2049
+ const directory = createMemo(() => {
2050
+ try {
2051
+ return props.api.state.path.directory;
2052
+ } catch {
2053
+ return "";
2054
+ }
2055
+ });
2056
+ onMount(() => {
2057
+ try {
2058
+ const v = props.api.kv.get(`${KV_PREFIX}.section.bottom`, true);
2059
+ props.signals.setSectionBottom(v !== false);
2060
+ } catch {
2061
+ }
2062
+ });
2063
+ return _$createComponent(Show, {
2064
+ get when() {
2065
+ return props.signals.sectionBottom();
2066
+ },
2067
+ get fallback() {
2068
+ return (() => {
2069
+ var _el$110 = _$createElement("text");
2070
+ _$insert(_el$110, directory);
2071
+ _$effect((_$p) => _$setProp(_el$110, "fg", pal().muted, _$p));
2072
+ return _el$110;
2073
+ })();
2074
+ },
2075
+ get children() {
2076
+ var _el$96 = _$createElement("box"), _el$97 = _$createElement("text"), _el$98 = _$createElement("box"), _el$99 = _$createElement("text"), _el$100 = _$createElement("span"), _el$101 = _$createTextNode(` `), _el$102 = _$createElement("span"), _el$104 = _$createElement("span"), _el$105 = _$createElement("span"), _el$108 = _$createElement("text");
2077
+ _$insertNode(_el$96, _el$97);
2078
+ _$insertNode(_el$96, _el$98);
2079
+ _$setProp(_el$96, "marginLeft", 1);
2080
+ _$setProp(_el$96, "flexGrow", 1);
2081
+ _$setProp(_el$96, "flexShrink", 0);
2082
+ _$setProp(_el$96, "flexDirection", "row");
2083
+ _$setProp(_el$96, "justifyContent", "space-between");
2084
+ _$insert(_el$97, directory);
2085
+ _$insertNode(_el$98, _el$99);
2086
+ _$insertNode(_el$98, _el$108);
2087
+ _$setProp(_el$98, "flexDirection", "row");
2088
+ _$insertNode(_el$99, _el$100);
2089
+ _$insertNode(_el$99, _el$102);
2090
+ _$insertNode(_el$99, _el$104);
2091
+ _$insertNode(_el$99, _el$105);
2092
+ _$insertNode(_el$100, _el$101);
2093
+ _$insert(_el$100, () => t("barHit"), _el$101);
2094
+ _$insert(_el$102, (() => {
2095
+ var _c$ = _$memo(() => (stats()?.hitRate ?? -1) >= 0);
2096
+ return () => _c$() ? (Math.floor(stats().hitRate * 10) / 10).toFixed(1) + "%" : "--";
2097
+ })());
2098
+ _$insert(_el$99, _$createComponent(Show, {
2099
+ get when() {
2100
+ return trend() !== null;
2101
+ },
2102
+ get children() {
2103
+ var _el$103 = _$createElement("span");
2104
+ _$insert(_el$103, () => " " + (trend() > 0 ? "\u2191" : "\u2193") + Math.abs(trend()).toFixed(1) + "%");
2105
+ _$effect((_$p) => _$setProp(_el$103, "style", {
2106
+ fg: trend() > 0 ? pal().success : pal().error
2107
+ }, _$p));
2108
+ return _el$103;
2109
+ }
2110
+ }), _el$104);
2111
+ _$insert(_el$104, () => " \xB7 " + t("barTok") + " ");
2112
+ _$insert(_el$105, (() => {
2113
+ var _c$2 = _$memo(() => !!stats());
2114
+ return () => _c$2() ? fmtCompact(stats().input + stats().read + stats().write) : "--";
2115
+ })());
2116
+ _$insert(_el$99, _$createComponent(Show, {
2117
+ get when() {
2118
+ return !props.signals.balanceUnsupported();
2119
+ },
2120
+ get children() {
2121
+ return [(() => {
2122
+ var _el$106 = _$createElement("span");
2123
+ _$insert(_el$106, () => " \xB7 " + t("barBal") + " ");
2124
+ _$effect((_$p) => _$setProp(_el$106, "style", {
2125
+ fg: pal().muted
2126
+ }, _$p));
2127
+ return _el$106;
2128
+ })(), (() => {
2129
+ var _el$107 = _$createElement("span");
2130
+ _$insert(_el$107, balanceText);
2131
+ _$effect((_$p) => _$setProp(_el$107, "style", {
2132
+ fg: pal().text
2133
+ }, _$p));
2134
+ return _el$107;
2135
+ })()];
2136
+ }
2137
+ }), null);
2138
+ _$insertNode(_el$108, _$createTextNode(` \xB7 `));
2139
+ _$effect((_p$) => {
2140
+ var _v$31 = pal().muted, _v$32 = {
2141
+ fg: pal().muted
2142
+ }, _v$33 = {
2143
+ fg: hitColor()
2144
+ }, _v$34 = {
2145
+ fg: pal().muted
2146
+ }, _v$35 = {
2147
+ fg: pal().text
2148
+ }, _v$36 = pal().muted;
2149
+ _v$31 !== _p$.e && (_p$.e = _$setProp(_el$97, "fg", _v$31, _p$.e));
2150
+ _v$32 !== _p$.t && (_p$.t = _$setProp(_el$100, "style", _v$32, _p$.t));
2151
+ _v$33 !== _p$.a && (_p$.a = _$setProp(_el$102, "style", _v$33, _p$.a));
2152
+ _v$34 !== _p$.o && (_p$.o = _$setProp(_el$104, "style", _v$34, _p$.o));
2153
+ _v$35 !== _p$.i && (_p$.i = _$setProp(_el$105, "style", _v$35, _p$.i));
2154
+ _v$36 !== _p$.n && (_p$.n = _$setProp(_el$108, "fg", _v$36, _p$.n));
2155
+ return _p$;
2156
+ }, {
2157
+ e: void 0,
2158
+ t: void 0,
2159
+ a: void 0,
2160
+ o: void 0,
2161
+ i: void 0,
2162
+ n: void 0
2163
+ });
2164
+ return _el$96;
2165
+ }
2166
+ });
2167
+ }
1691
2168
  function createSidebarSlot(api, signals) {
1692
2169
  let lastSlotSid = "";
1693
2170
  return {
@@ -1723,20 +2200,28 @@ var tui = async (api) => {
1723
2200
  const [sectionDist, setSectionDist] = createSignal(true);
1724
2201
  const [sectionSkills, setSectionSkills] = createSignal(true);
1725
2202
  const [sectionBalance, setSectionBalance] = createSignal(true);
2203
+ const [sectionBottom, setSectionBottom] = createSignal(true);
1726
2204
  const [balanceRefresh, setBalanceRefresh] = createSignal(0);
1727
2205
  const [balanceProviderId, setBalanceProviderId] = createSignal("deepseek");
1728
2206
  const [autoBalance, setAutoBalance] = createSignal(true);
2207
+ const [balanceUnsupported, setBalanceUnsupported] = createSignal(false);
1729
2208
  const [balanceCurrency, setBalanceCurrency] = createSignal("");
1730
2209
  const [borderVisible, setBorderVisible] = createSignal(true);
1731
- const [langZH, setLangZH] = createSignal(LANG_ZH);
2210
+ const [langCode, setLangCode] = createSignal(INIT_LANG);
1732
2211
  const [overrideSessionId, setOverrideSessionId] = createSignal(void 0);
2212
+ const [balanceState, setBalanceState] = createSignal({
2213
+ status: "idle",
2214
+ data: null,
2215
+ lastFetch: 0
2216
+ });
2217
+ let balanceSeq = 0;
1733
2218
  const signals = {
1734
2219
  currencySymbol,
1735
2220
  setCurrencySymbol,
1736
2221
  exchangeRate,
1737
2222
  setExchangeRate,
1738
- langZH,
1739
- setLangZH,
2223
+ langCode,
2224
+ setLangCode,
1740
2225
  sectionDetail,
1741
2226
  setSectionDetail,
1742
2227
  sectionModel,
@@ -1747,12 +2232,17 @@ var tui = async (api) => {
1747
2232
  setSectionSkills,
1748
2233
  sectionBalance,
1749
2234
  setSectionBalance,
2235
+ sectionBottom,
2236
+ setSectionBottom,
1750
2237
  balanceRefresh,
1751
2238
  setBalanceRefresh,
1752
2239
  balanceProviderId,
1753
2240
  setBalanceProviderId,
1754
2241
  autoBalance,
1755
2242
  setAutoBalance,
2243
+ balanceUnsupported,
2244
+ setBalanceUnsupported,
2245
+ balanceState,
1756
2246
  balanceCurrency,
1757
2247
  setBalanceCurrency,
1758
2248
  borderVisible,
@@ -1761,16 +2251,139 @@ var tui = async (api) => {
1761
2251
  setOverrideSessionId
1762
2252
  };
1763
2253
  api.slots.register(createSidebarSlot(api, signals));
2254
+ api.slots.register({
2255
+ order: 55,
2256
+ slots: {
2257
+ session_prompt(_ctx, input) {
2258
+ return _$createComponent(api.ui.Prompt, {
2259
+ get sessionID() {
2260
+ return input.session_id;
2261
+ },
2262
+ get visible() {
2263
+ return input.visible;
2264
+ },
2265
+ get disabled() {
2266
+ return input.disabled;
2267
+ },
2268
+ get onSubmit() {
2269
+ return input.on_submit;
2270
+ },
2271
+ ref(r$) {
2272
+ var _ref$2 = input.ref;
2273
+ typeof _ref$2 === "function" ? _ref$2(r$) : input.ref = r$;
2274
+ },
2275
+ get hint() {
2276
+ return _$createComponent(BottomStatusBar, {
2277
+ api,
2278
+ signals,
2279
+ get sessionId() {
2280
+ return input.session_id;
2281
+ }
2282
+ });
2283
+ }
2284
+ });
2285
+ }
2286
+ }
2287
+ });
1764
2288
  const KV_PREFIX = "cache_panel";
2289
+ const restoreLang = () => {
2290
+ try {
2291
+ const saved = api.kv.get(`${KV_PREFIX}.lang`);
2292
+ if (saved && LANG_META.some((m) => m.code === saved)) setLangCode(saved);
2293
+ } catch {
2294
+ }
2295
+ };
2296
+ if (api.kv.ready) {
2297
+ restoreLang();
2298
+ } else {
2299
+ const langTimer = setInterval(() => {
2300
+ if (api.kv.ready) {
2301
+ clearInterval(langTimer);
2302
+ restoreLang();
2303
+ }
2304
+ }, 10);
2305
+ api.lifecycle.onDispose(() => clearInterval(langTimer));
2306
+ }
2307
+ const pollBalance = async () => {
2308
+ const provider = getBalanceProvider(balanceProviderId());
2309
+ const key = api.kv.get(`${KV_PREFIX}.balance.${provider.id}.key`, "") || findOpencodeKey(api, provider);
2310
+ if (balanceUnsupported()) {
2311
+ setBalanceState({
2312
+ status: "idle",
2313
+ data: null,
2314
+ lastFetch: 0,
2315
+ error: void 0,
2316
+ key: void 0
2317
+ });
2318
+ return;
2319
+ }
2320
+ if (!key) {
2321
+ setBalanceState({
2322
+ status: "idle",
2323
+ data: null,
2324
+ lastFetch: 0,
2325
+ error: void 0,
2326
+ key: void 0
2327
+ });
2328
+ return;
2329
+ }
2330
+ const now = Date.now();
2331
+ const prev = balanceState();
2332
+ if (prev.status === "ok" && prev.key === key && now - prev.lastFetch < BALANCE_POLL_MS) return;
2333
+ const seq = ++balanceSeq;
2334
+ setBalanceState({
2335
+ ...prev,
2336
+ status: "loading",
2337
+ error: void 0,
2338
+ key
2339
+ });
2340
+ const controller = new AbortController();
2341
+ let timedOut = false;
2342
+ const timer = setTimeout(() => {
2343
+ timedOut = true;
2344
+ controller.abort();
2345
+ }, 1e4);
2346
+ try {
2347
+ const data = await provider.fetchBalance(key, controller.signal);
2348
+ clearTimeout(timer);
2349
+ if (seq !== balanceSeq) return;
2350
+ setBalanceState({
2351
+ status: "ok",
2352
+ data,
2353
+ lastFetch: Date.now(),
2354
+ error: void 0,
2355
+ key
2356
+ });
2357
+ } catch (err) {
2358
+ clearTimeout(timer);
2359
+ if (seq !== balanceSeq) return;
2360
+ const code = timedOut ? "TIMEOUT" : err instanceof Error ? err.message : "";
2361
+ setBalanceState({
2362
+ status: "error",
2363
+ data: null,
2364
+ lastFetch: 0,
2365
+ error: code,
2366
+ key
2367
+ });
2368
+ }
2369
+ };
2370
+ createEffect(() => {
2371
+ void balanceRefresh();
2372
+ untrack(() => {
2373
+ void pollBalance();
2374
+ });
2375
+ });
2376
+ const balanceTimer = setInterval(pollBalance, BALANCE_POLL_MS);
2377
+ api.lifecycle.onDispose(() => clearInterval(balanceTimer));
1765
2378
  const providerOptionTitle = (p, current) => {
1766
- const zh = langZH();
2379
+ const t = createT(() => langCode());
1767
2380
  const hasManual = !!api.kv.get(`${KV_PREFIX}.balance.${p.id}.key`, "");
1768
2381
  const hasAuto = !hasManual && !!findOpencodeKey(api, p);
1769
- const mark = hasManual ? zh ? "\uFF08\u7528\u6237 key\uFF09" : " (user key)" : hasAuto ? zh ? "\uFF08OpenCode\uFF09" : " (OpenCode)" : zh ? "\uFF08\u672A\u914D\u7F6E\uFF09" : " (not set)";
2382
+ const mark = hasManual ? t("keyUser") : hasAuto ? t("keyOpenCode") : t("keyNotSet");
1770
2383
  return p.name + mark + (current && p.id === current ? " *" : "");
1771
2384
  };
1772
2385
  const promptBalanceKey = (dialog, provider) => {
1773
- const zh = langZH();
2386
+ const t = createT(() => langCode());
1774
2387
  const current = api.kv.get(`${KV_PREFIX}.balance.${provider.id}.key`, "");
1775
2388
  const masked = maskKey(current);
1776
2389
  dialog?.replace(() => _$createComponent(api.ui.DialogPrompt, {
@@ -1778,9 +2391,11 @@ var tui = async (api) => {
1778
2391
  return provider.name;
1779
2392
  },
1780
2393
  description: () => (() => {
1781
- var _el$93 = _$createElement("text");
1782
- _$insert(_el$93, () => zh ? `\u8F93\u5165 ${provider.name} API Key \u4EE5\u663E\u793A\u8D26\u6237\u4F59\u989D\uFF08\u7559\u7A7A\u6E05\u9664\uFF09` : `Enter your ${provider.name} API key to show account balance (leave empty to clear)`);
1783
- return _el$93;
2394
+ var _el$111 = _$createElement("text");
2395
+ _$insert(_el$111, () => t("balKeyPrompt", {
2396
+ p: provider.name
2397
+ }));
2398
+ return _el$111;
1784
2399
  })(),
1785
2400
  get placeholder() {
1786
2401
  return provider.keyPlaceholder ?? "sk-...";
@@ -1800,11 +2415,11 @@ var tui = async (api) => {
1800
2415
  setBalanceRefresh((v) => v + 1);
1801
2416
  if (key) {
1802
2417
  api.ui.toast({
1803
- message: zh ? "API Key \u5DF2\u4FDD\u5B58\uFF0C\u6B63\u5728\u67E5\u8BE2\u4F59\u989D..." : "API Key saved, fetching balance..."
2418
+ message: t("keySaved")
1804
2419
  });
1805
2420
  } else {
1806
2421
  api.ui.toast({
1807
- message: zh ? "API Key \u5DF2\u6E05\u9664" : "API Key cleared"
2422
+ message: t("keyCleared")
1808
2423
  });
1809
2424
  }
1810
2425
  dialog?.clear();
@@ -1829,6 +2444,7 @@ var tui = async (api) => {
1829
2444
  }));
1830
2445
  },
1831
2446
  onSelect: (opt) => {
2447
+ const t = createT(() => langCode());
1832
2448
  const sym = CURRENCIES[opt.value] ?? "$";
1833
2449
  const defRate = DEFAULT_RATES[opt.value] ?? 1;
1834
2450
  api.kv.set(`${KV_PREFIX}.currency`, sym);
@@ -1838,7 +2454,11 @@ var tui = async (api) => {
1838
2454
  signals.setCurrencySymbol(sym);
1839
2455
  signals.setExchangeRate(defRate);
1840
2456
  api.ui.toast({
1841
- message: `Currency: ${opt.value} (${sym}), rate: ${defRate}`
2457
+ message: t("currencySet", {
2458
+ v: opt.value,
2459
+ s: sym,
2460
+ r: defRate
2461
+ })
1842
2462
  });
1843
2463
  dialog?.clear();
1844
2464
  }
@@ -1855,21 +2475,24 @@ var tui = async (api) => {
1855
2475
  dialog?.replace(() => _$createComponent(api.ui.DialogPrompt, {
1856
2476
  title: "Exchange Rate",
1857
2477
  description: () => (() => {
1858
- var _el$94 = _$createElement("text");
1859
- _$insertNode(_el$94, _$createTextNode(`Enter the exchange rate from USD to your currency (e.g. 7.2 for CNY)`));
1860
- return _el$94;
2478
+ var _el$112 = _$createElement("text");
2479
+ _$insertNode(_el$112, _$createTextNode(`Enter the exchange rate from USD to your currency (e.g. 7.2 for CNY)`));
2480
+ return _el$112;
1861
2481
  })(),
1862
2482
  placeholder: "1.0",
1863
2483
  get value() {
1864
2484
  return String(api.kv.get(`${KV_PREFIX}.rate`, 1));
1865
2485
  },
1866
2486
  onConfirm: (val) => {
2487
+ const t = createT(() => langCode());
1867
2488
  const n = parseFloat(val);
1868
2489
  if (n > 0) {
1869
2490
  api.kv.set(`${KV_PREFIX}.rate`, n);
1870
2491
  signals.setExchangeRate(n);
1871
2492
  api.ui.toast({
1872
- message: `Exchange rate set to ${n}`
2493
+ message: t("rateSet", {
2494
+ r: n
2495
+ })
1873
2496
  });
1874
2497
  }
1875
2498
  dialog?.clear();
@@ -1884,40 +2507,59 @@ var tui = async (api) => {
1884
2507
  name: "cache-section"
1885
2508
  },
1886
2509
  onSelect: (dialog) => {
2510
+ const t = createT(() => langCode());
1887
2511
  const detailOn = Boolean(api.kv.get(`${KV_PREFIX}.section.detail`, true));
1888
2512
  const modelOn = Boolean(api.kv.get(`${KV_PREFIX}.section.model`, true));
1889
2513
  const distOn = Boolean(api.kv.get(`${KV_PREFIX}.section.dist`, true));
1890
2514
  const skillsOn = Boolean(api.kv.get(`${KV_PREFIX}.section.skills`, true));
1891
2515
  const balanceOn = Boolean(api.kv.get(`${KV_PREFIX}.section.balance`, true));
2516
+ const bottomOn = Boolean(api.kv.get(`${KV_PREFIX}.section.bottom`, true));
1892
2517
  const borderOn = Boolean(api.kv.get(`${KV_PREFIX}.border`, true));
2518
+ const labels = {
2519
+ detail: t("secDetail"),
2520
+ model: t("secModel"),
2521
+ dist: t("distTitle"),
2522
+ skills: t("secSkills"),
2523
+ balance: t("secBalance"),
2524
+ bottom: t("secBottom"),
2525
+ border: t("secBorder")
2526
+ };
2527
+ const optTitle = (label, on) => `${visualPadEnd(label, 15)}[${on ? "ON" : "OFF"}]`;
1893
2528
  dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
1894
- title: "Toggle Section",
1895
- options: [{
1896
- title: `Token Detail [${detailOn ? "ON" : "OFF"}]`,
1897
- value: "detail"
1898
- }, {
1899
- title: `Model & Pricing [${modelOn ? "ON" : "OFF"}]`,
1900
- value: "model"
1901
- }, {
1902
- title: `Token Dist. [${distOn ? "ON" : "OFF"}]`,
1903
- value: "dist"
1904
- }, {
1905
- title: `Loaded Skills [${skillsOn ? "ON" : "OFF"}]`,
1906
- value: "skills"
1907
- }, {
1908
- title: `Balance [${balanceOn ? "ON" : "OFF"}]`,
1909
- value: "balance"
1910
- }, {
1911
- title: `Panel Border [${borderOn ? "ON" : "OFF"}]`,
1912
- value: "border"
1913
- }],
2529
+ get title() {
2530
+ return t("secToggle");
2531
+ },
2532
+ get options() {
2533
+ return [{
2534
+ title: optTitle(labels.detail, detailOn),
2535
+ value: "detail"
2536
+ }, {
2537
+ title: optTitle(labels.model, modelOn),
2538
+ value: "model"
2539
+ }, {
2540
+ title: optTitle(labels.dist, distOn),
2541
+ value: "dist"
2542
+ }, {
2543
+ title: optTitle(labels.skills, skillsOn),
2544
+ value: "skills"
2545
+ }, {
2546
+ title: optTitle(labels.balance, balanceOn),
2547
+ value: "balance"
2548
+ }, {
2549
+ title: optTitle(labels.bottom, bottomOn),
2550
+ value: "bottom"
2551
+ }, {
2552
+ title: optTitle(labels.border, borderOn),
2553
+ value: "border"
2554
+ }];
2555
+ },
1914
2556
  onSelect: (opt) => {
1915
2557
  if (opt.value === "border") {
1916
2558
  const cur = Boolean(api.kv.get(`${KV_PREFIX}.border`, true));
1917
2559
  api.kv.set(`${KV_PREFIX}.border`, !cur);
1918
2560
  signals.setBorderVisible(!cur);
1919
2561
  api.ui.toast({
1920
- message: `Panel border ${!cur ? "shown" : "hidden"}`
2562
+ message: !cur ? t("borderShown") : t("borderHidden")
1921
2563
  });
1922
2564
  } else {
1923
2565
  const key = `${KV_PREFIX}.section.${opt.value}`;
@@ -1928,8 +2570,12 @@ var tui = async (api) => {
1928
2570
  if (opt.value === "dist") signals.setSectionDist(!cur);
1929
2571
  if (opt.value === "skills") signals.setSectionSkills(!cur);
1930
2572
  if (opt.value === "balance") signals.setSectionBalance(!cur);
2573
+ if (opt.value === "bottom") signals.setSectionBottom(!cur);
2574
+ const name = labels[opt.value] ?? opt.value;
1931
2575
  api.ui.toast({
1932
- message: `${opt.value} section ${!cur ? "shown" : "hidden"}`
2576
+ message: t(!cur ? "sectionShown" : "sectionHidden", {
2577
+ s: name
2578
+ })
1933
2579
  });
1934
2580
  }
1935
2581
  dialog?.clear();
@@ -1944,6 +2590,7 @@ var tui = async (api) => {
1944
2590
  name: "cache-config"
1945
2591
  },
1946
2592
  onSelect: (dialog) => {
2593
+ const t = createT(() => langCode());
1947
2594
  const sym = api.kv.get(`${KV_PREFIX}.currency`) ?? "$";
1948
2595
  const rate = api.kv.get(`${KV_PREFIX}.rate`) ?? 1;
1949
2596
  const detail = Boolean(api.kv.get(`${KV_PREFIX}.section.detail`, true));
@@ -1951,9 +2598,20 @@ var tui = async (api) => {
1951
2598
  const dist = Boolean(api.kv.get(`${KV_PREFIX}.section.dist`, true));
1952
2599
  const skills = Boolean(api.kv.get(`${KV_PREFIX}.section.skills`, true));
1953
2600
  const balance = Boolean(api.kv.get(`${KV_PREFIX}.section.balance`, true));
2601
+ const bottom = Boolean(api.kv.get(`${KV_PREFIX}.section.bottom`, true));
2602
+ const on = (v) => v ? "ON" : "OFF";
1954
2603
  api.ui.toast({
1955
- title: "Cache Panel Config",
1956
- 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"}`,
2604
+ title: t("panelConfigTitle"),
2605
+ message: t("panelConfigMsg", {
2606
+ c: sym,
2607
+ r: rate,
2608
+ d: on(detail),
2609
+ m: on(model),
2610
+ t: on(dist),
2611
+ k: on(skills),
2612
+ b: on(balance),
2613
+ f: on(bottom)
2614
+ }),
1957
2615
  duration: 8e3
1958
2616
  });
1959
2617
  dialog?.clear();
@@ -1966,22 +2624,24 @@ var tui = async (api) => {
1966
2624
  name: "cache-lang"
1967
2625
  },
1968
2626
  onSelect: (dialog) => {
1969
- const cur = langZH();
2627
+ const t = createT(() => langCode());
2628
+ const cur = langCode();
1970
2629
  dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
1971
- title: "Display Language",
1972
- options: [{
1973
- title: `\u4E2D\u6587 ${cur ? "\u2713" : ""}`,
1974
- value: "zh"
1975
- }, {
1976
- title: `English ${cur ? "" : "\u2713"}`,
1977
- value: "en"
1978
- }],
2630
+ get title() {
2631
+ return t("langTitle");
2632
+ },
2633
+ get options() {
2634
+ return LANG_META.map((m) => ({
2635
+ title: `${visualPadEnd(m.label, 9)}${cur === m.code ? "\u2713" : ""}`,
2636
+ value: m.code
2637
+ }));
2638
+ },
1979
2639
  onSelect: (opt) => {
1980
- const zh = opt.value === "zh";
1981
- api.kv.set(`${KV_PREFIX}.lang`, opt.value);
1982
- setLangZH(zh);
2640
+ const code = opt.value;
2641
+ api.kv.set(`${KV_PREFIX}.lang`, code);
2642
+ setLangCode(code);
1983
2643
  api.ui.toast({
1984
- message: zh ? "\u8BED\u8A00\u5DF2\u5207\u6362\u4E3A\u4E2D\u6587" : "Switched to English"
2644
+ message: t("langSwitched")
1985
2645
  });
1986
2646
  dialog?.clear();
1987
2647
  }
@@ -1995,12 +2655,14 @@ var tui = async (api) => {
1995
2655
  name: "cache-balance"
1996
2656
  },
1997
2657
  onSelect: (dialog) => {
1998
- const zh = langZH();
2658
+ const t = createT(() => langCode());
1999
2659
  const current = signals.balanceProviderId();
2000
2660
  const auto = signals.autoBalance();
2001
- const autoLabel = auto ? zh ? "\u81EA\u52A8\u5207\u6362\u63D0\u4F9B\u5546 [\u5F00]" : "Auto-switch provider [ON]" : zh ? "\u81EA\u52A8\u5207\u6362\u63D0\u4F9B\u5546 [\u5173]" : "Auto-switch provider [OFF]";
2661
+ const autoLabel = `${t("autoSwitchOpt")} [${auto ? "ON" : "OFF"}]`;
2002
2662
  dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
2003
- title: zh ? "\u4F59\u989D\u63D0\u4F9B\u5546 / \u81EA\u52A8\u5207\u6362" : "Balance Provider / Auto-switch",
2663
+ get title() {
2664
+ return t("balProvTitle");
2665
+ },
2004
2666
  get options() {
2005
2667
  return [{
2006
2668
  title: autoLabel,
@@ -2016,7 +2678,7 @@ var tui = async (api) => {
2016
2678
  api.kv.set(`${KV_PREFIX}.balance.auto`, next);
2017
2679
  signals.setAutoBalance(next);
2018
2680
  api.ui.toast({
2019
- message: zh ? `\u81EA\u52A8\u5207\u6362\u4F59\u989D\u63D0\u4F9B\u5546: ${next ? "\u5F00" : "\u5173"}` : `Auto-switch balance provider: ${next ? "ON" : "OFF"}`
2681
+ message: next ? t("autoSwitchOn") : t("autoSwitchOff")
2020
2682
  });
2021
2683
  dialog?.clear();
2022
2684
  } else {
@@ -2025,13 +2687,16 @@ var tui = async (api) => {
2025
2687
  api.kv.set(`${KV_PREFIX}.balance.auto`, false);
2026
2688
  signals.setBalanceProviderId(provider.id);
2027
2689
  signals.setAutoBalance(false);
2690
+ signals.setBalanceUnsupported(false);
2028
2691
  signals.setBalanceRefresh(signals.balanceRefresh() + 1);
2029
2692
  const hasKey = !!api.kv.get(`${KV_PREFIX}.balance.${provider.id}.key`, "");
2030
2693
  if (!hasKey) {
2031
2694
  promptBalanceKey(dialog, provider);
2032
2695
  } else {
2033
2696
  api.ui.toast({
2034
- message: zh ? `\u4F59\u989D\u63D0\u4F9B\u5546: ${provider.name}\uFF08\u81EA\u52A8\u5207\u6362\u5DF2\u5173\u95ED\uFF09` : `Balance provider: ${provider.name} (auto-switch off)`
2697
+ message: t("providerManual", {
2698
+ p: provider.name
2699
+ })
2035
2700
  });
2036
2701
  dialog?.clear();
2037
2702
  }
@@ -2047,9 +2712,11 @@ var tui = async (api) => {
2047
2712
  name: "cache-balance-key"
2048
2713
  },
2049
2714
  onSelect: (dialog) => {
2050
- const zh = langZH();
2715
+ const t = createT(() => langCode());
2051
2716
  dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
2052
- title: zh ? "\u9009\u62E9\u4F59\u989D\u63D0\u4F9B\u5546" : "Select Balance Provider",
2717
+ get title() {
2718
+ return t("balSelectTitle");
2719
+ },
2053
2720
  get options() {
2054
2721
  return balanceProviders.map((p) => ({
2055
2722
  title: providerOptionTitle(p),
@@ -2075,10 +2742,11 @@ var tui = async (api) => {
2075
2742
  name: "cache-debug-skills"
2076
2743
  },
2077
2744
  onSelect: () => {
2745
+ const t = createT(() => langCode());
2078
2746
  const rt = api.route.current;
2079
2747
  if (rt.name !== "session" || !rt.params) {
2080
2748
  api.ui.toast({
2081
- message: "Please run this command inside a session",
2749
+ message: t("runInSession"),
2082
2750
  variant: "warning"
2083
2751
  });
2084
2752
  return;
@@ -2096,9 +2764,9 @@ var tui = async (api) => {
2096
2764
  }
2097
2765
  for (const p of parts) {
2098
2766
  if (p.type === "tool") {
2099
- const t = String(p.tool ?? "?");
2100
- byTool[t] = (byTool[t] ?? 0) + 1;
2101
- if (t === "skill") {
2767
+ const t2 = String(p.tool ?? "?");
2768
+ byTool[t2] = (byTool[t2] ?? 0) + 1;
2769
+ if (t2 === "skill") {
2102
2770
  const meta = p.state?.metadata;
2103
2771
  const rootMeta = p.metadata;
2104
2772
  skillParts.push(`state.metadata=${JSON.stringify(meta)} | root.metadata=${JSON.stringify(rootMeta)} | state.title="${p.state?.title}" | state.output[:80]="${String(p.state?.output ?? "").slice(0, 80)}"`);
@@ -2167,7 +2835,7 @@ var tui = async (api) => {
2167
2835
  return true;
2168
2836
  });
2169
2837
  if (unique.length > 0) {
2170
- const zh = langZH();
2838
+ const t = createT(() => langCode());
2171
2839
  const currentSid = signals.overrideSessionId() ?? api.kv.get(`${KV_PREFIX}.session`, "");
2172
2840
  const options = unique.map((c, i) => ({
2173
2841
  title: `${i + 1}. ${c.title}`,
@@ -2175,7 +2843,7 @@ var tui = async (api) => {
2175
2843
  description: c.description
2176
2844
  }));
2177
2845
  const backValue = "__main__";
2178
- const backTitle = `\u2500 ${zh ? "\u56DE\u5230\u4E3B\u4F1A\u8BDD" : "Back to Main"}`;
2846
+ const backTitle = `\u2500 ${t("backToMainTitle")}`;
2179
2847
  options.unshift({
2180
2848
  title: backTitle,
2181
2849
  value: backValue,
@@ -2188,7 +2856,9 @@ var tui = async (api) => {
2188
2856
  });
2189
2857
  const currentIdx = currentSid ? options.findIndex((o) => o.value === currentSid) : -1;
2190
2858
  dialog?.replace(() => _$createComponent(api.ui.DialogSelect, {
2191
- title: zh ? "\u9009\u62E9\u5B50\u4EE3\u7406" : "Select Sub-Agent",
2859
+ get title() {
2860
+ return t("subSelectTitle");
2861
+ },
2192
2862
  options,
2193
2863
  get current() {
2194
2864
  return currentIdx >= 0 ? options[currentIdx].value : void 0;
@@ -2198,28 +2868,30 @@ var tui = async (api) => {
2198
2868
  signals.setOverrideSessionId(void 0);
2199
2869
  api.kv.set(`${KV_PREFIX}.session`, "");
2200
2870
  api.ui.toast({
2201
- message: zh ? "\u5DF2\u5207\u56DE\u4E3B\u4F1A\u8BDD" : "Switched to main session"
2871
+ message: t("backToMain")
2202
2872
  });
2203
2873
  } else {
2204
2874
  signals.setOverrideSessionId(opt.value);
2205
2875
  api.kv.set(`${KV_PREFIX}.session`, opt.value);
2206
2876
  api.ui.toast({
2207
- message: (zh ? "\u5DF2\u5207\u6362\u81F3\u5B50\u4EE3\u7406: " : "Showing sub-agent: ") + opt.value.slice(0, 24) + "\u2026"
2877
+ message: t("subAgentSwitched", {
2878
+ s: opt.value.slice(0, 24) + "\u2026"
2879
+ })
2208
2880
  });
2209
2881
  }
2210
2882
  dialog?.clear();
2211
2883
  }
2212
2884
  }));
2213
2885
  } else {
2214
- const zh = langZH();
2886
+ const t = createT(() => langCode());
2215
2887
  dialog?.replace(() => _$createComponent(api.ui.DialogPrompt, {
2216
2888
  get title() {
2217
- return signals.overrideSessionId() ? zh ? "\u5207\u6362\u5B50\u4EE3\u7406" : "Switch Sub" : zh ? "\u67E5\u770B\u5B50\u4EE3\u7406\u7F13\u5B58" : "View Sub Cache";
2889
+ return _$memo(() => !!signals.overrideSessionId())() ? t("subSwitchTitle") : t("subViewTitle");
2218
2890
  },
2219
2891
  description: () => (() => {
2220
- var _el$96 = _$createElement("text");
2221
- _$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");
2222
- return _el$96;
2892
+ var _el$114 = _$createElement("text");
2893
+ _$insert(_el$114, () => t("subNoFound"));
2894
+ return _el$114;
2223
2895
  })(),
2224
2896
  placeholder: "ses_...",
2225
2897
  get value() {
@@ -2231,7 +2903,9 @@ var tui = async (api) => {
2231
2903
  signals.setOverrideSessionId(sid);
2232
2904
  api.kv.set(`${KV_PREFIX}.session`, sid);
2233
2905
  api.ui.toast({
2234
- message: (langZH() ? "\u5DF2\u5207\u6362\u81F3\u5B50\u4EE3\u7406: " : "Showing sub-agent: ") + sid.slice(0, 24) + "\u2026"
2906
+ message: t("subAgentSwitched", {
2907
+ s: sid.slice(0, 24) + "\u2026"
2908
+ })
2235
2909
  });
2236
2910
  }
2237
2911
  dialog?.clear();
@@ -2248,10 +2922,11 @@ var tui = async (api) => {
2248
2922
  name: "cache-session-back"
2249
2923
  },
2250
2924
  onSelect: (dialog) => {
2925
+ const t = createT(() => langCode());
2251
2926
  signals.setOverrideSessionId(void 0);
2252
2927
  api.kv.set(`${KV_PREFIX}.session`, "");
2253
2928
  api.ui.toast({
2254
- message: langZH() ? "\u5DF2\u5207\u56DE\u4E3B\u4F1A\u8BDD" : "Switched to main session"
2929
+ message: t("backToMain")
2255
2930
  });
2256
2931
  dialog?.clear();
2257
2932
  }