opencode-visual-cache 1.7.0-beta.4 → 1.7.0-beta.5

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.
@@ -1 +1 @@
1
- export declare const PLUGIN_VERSION = "1.7.0-beta.4";
1
+ export declare const PLUGIN_VERSION = "1.7.0-beta.5";
package/dist/_version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // auto-generated
2
- export const PLUGIN_VERSION = "1.7.0-beta.4";
2
+ export const PLUGIN_VERSION = "1.7.0-beta.5";
package/dist/tui.js CHANGED
@@ -705,7 +705,7 @@ import { createElement as _$createElement } from "@opentui/solid";
705
705
  import { createMemo, createSignal, createEffect, onMount, onCleanup, Show, untrack } from "solid-js";
706
706
 
707
707
  // src/_version.ts
708
- var PLUGIN_VERSION = "1.7.0-beta.4";
708
+ var PLUGIN_VERSION = "1.7.0-beta.5";
709
709
 
710
710
  // src/panel/TokenCachePanel.tsx
711
711
  var MIN_PANEL_WIDTH = 20;
package/dist/v2/index.js CHANGED
@@ -154,7 +154,7 @@ const mod = {
154
154
  // 底部状态栏(完整口径与 V1 一致;余额读共享 signals.balanceState)
155
155
  context.ui.slot({
156
156
  append: "prompt.footer.status",
157
- render: (props) => (_jsx(StatusView, { context: context, signals: signals, sessionID: String(props.sessionID ?? "") })),
157
+ render: (props) => (_jsx(StatusView, { context: context, api: api, signals: signals, sessionID: String(props.sessionID ?? "") })),
158
158
  });
159
159
  // 偏好持久化(实验:storage.store 用法验证)
160
160
  context.storage.store("opencode-visual-cache.panel", { initial: { collapsed: false } });
@@ -1,14 +1,16 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
2
  import type { Context } from "./types";
3
- import type { PanelSignals } from "../panel/panel-api";
3
+ import type { PanelApi, PanelSignals } from "../panel/panel-api";
4
4
  /**
5
5
  * 底部状态栏(prompt.footer.status)——对齐 V1 BottomStatusBar 统计段口径:
6
6
  * 单条命中率(最后一条有 token 的 assistant 消息)+ 趋势 + Tokens 总量 + 余额。
7
7
  * 余额读共享 signals.balanceState(PluginRoot 驱动轮询);provider 不支持余额时隐藏余额段(对齐 V1)。
8
8
  * 颜色经 mapTheme(V1 形状)——与侧边栏命中率颜色同源,保证两处一致。
9
+ * 显隐受 signals.sectionBottom 控制(/cache-section 切换),启动时从 kv 恢复偏好(对齐 V1)。
9
10
  */
10
11
  export declare function StatusView(props: {
11
12
  context: Context;
13
+ api: PanelApi;
12
14
  signals: PanelSignals;
13
15
  sessionID: string;
14
16
  }): import("solid-js").JSX.Element;
package/dist/v2/status.js CHANGED
@@ -1,18 +1,28 @@
1
1
  import { jsx as _jsx } from "@opentui/solid/jsx-runtime";
2
2
  /** @jsxImportSource @opentui/solid */
3
- import { createMemo, For, Show } from "solid-js";
3
+ import { createMemo, For, onMount, Show } from "solid-js";
4
4
  import { collectLastHitRate } from "./data";
5
5
  import { desaturateTo, fmtCompact, formatBalanceText, MAX_SAT, FALLBACK, visualWidth } from "../core";
6
6
  import { createT } from "../i18n";
7
7
  import { mapTheme } from "./theme";
8
+ const KV_PREFIX = "cache_panel";
8
9
  /**
9
10
  * 底部状态栏(prompt.footer.status)——对齐 V1 BottomStatusBar 统计段口径:
10
11
  * 单条命中率(最后一条有 token 的 assistant 消息)+ 趋势 + Tokens 总量 + 余额。
11
12
  * 余额读共享 signals.balanceState(PluginRoot 驱动轮询);provider 不支持余额时隐藏余额段(对齐 V1)。
12
13
  * 颜色经 mapTheme(V1 形状)——与侧边栏命中率颜色同源,保证两处一致。
14
+ * 显隐受 signals.sectionBottom 控制(/cache-section 切换),启动时从 kv 恢复偏好(对齐 V1)。
13
15
  */
14
16
  export function StatusView(props) {
15
17
  const t = createT(() => props.signals.langCode());
18
+ // 恢复显隐偏好(默认显示;关闭时隐藏统计段,与宿主默认 hint 行一致)
19
+ onMount(() => {
20
+ try {
21
+ const v = props.api.kv.get(`${KV_PREFIX}.section.bottom`, true);
22
+ props.signals.setSectionBottom(v !== false);
23
+ }
24
+ catch { }
25
+ });
16
26
  // 与侧边栏 TokenCachePanel 同一颜色来源(mapTheme → desaturateTo)
17
27
  const pal = createMemo(() => {
18
28
  const th = mapTheme(props.context.theme);
@@ -79,5 +89,5 @@ export function StatusView(props) {
79
89
  w += visualWidth(sg.text);
80
90
  return w;
81
91
  });
82
- return (_jsx(Show, { when: segsW() > 0, children: _jsx("text", { children: _jsx(For, { each: segs(), children: (sg) => _jsx("span", { style: { fg: sg.color }, children: sg.text }) }) }) }));
92
+ return (_jsx(Show, { when: segsW() > 0 && stats().hitRate >= 0 && props.signals.sectionBottom(), children: _jsx("text", { children: _jsx(For, { each: segs(), children: (sg) => _jsx("span", { style: { fg: sg.color }, children: sg.text }) }) }) }));
83
93
  }
package/dist/v2.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/v2/index.tsx
2
2
  import { createComponent as _$createComponent3 } from "@opentui/solid";
3
- import { createSignal as createSignal2, createEffect as createEffect2, onMount as onMount2, onCleanup as onCleanup2, untrack as untrack2 } from "solid-js";
3
+ import { createSignal as createSignal2, createEffect as createEffect2, onMount as onMount3, onCleanup as onCleanup2, untrack as untrack2 } from "solid-js";
4
4
 
5
5
  // src/v2/v2-panel-api.ts
6
6
  function createPanelApi(context) {
@@ -843,7 +843,7 @@ function estimateTokens(text) {
843
843
  }
844
844
 
845
845
  // src/_version.ts
846
- var PLUGIN_VERSION = "1.7.0-beta.4";
846
+ var PLUGIN_VERSION = "1.7.0-beta.5";
847
847
 
848
848
  // src/panel/TokenCachePanel.tsx
849
849
  var MIN_PANEL_WIDTH = 20;
@@ -985,7 +985,7 @@ function TokenCachePanel(props) {
985
985
  lastMainSid = sid;
986
986
  if (props.signals.overrideSessionId()) {
987
987
  props.signals.setOverrideSessionId(void 0);
988
- props.api.kv.set(`${KV_PREFIX3}.session`, "");
988
+ props.api.kv.set(`${KV_PREFIX4}.session`, "");
989
989
  }
990
990
  }
991
991
  });
@@ -1217,7 +1217,7 @@ function TokenCachePanel(props) {
1217
1217
  });
1218
1218
  setLastHasDist(true);
1219
1219
  try {
1220
- props.api.kv.set(`${KV_PREFIX3}.dist_snapshot`, {
1220
+ props.api.kv.set(`${KV_PREFIX4}.dist_snapshot`, {
1221
1221
  ...d.dist
1222
1222
  });
1223
1223
  } catch {
@@ -1225,53 +1225,53 @@ function TokenCachePanel(props) {
1225
1225
  }
1226
1226
  });
1227
1227
  const [partVersion, setPartVersion] = createSignal(0);
1228
- const KV_PREFIX3 = "cache_panel";
1228
+ const KV_PREFIX4 = "cache_panel";
1229
1229
  const persistFold = (key, val) => {
1230
1230
  try {
1231
- props.api.kv.set(`${KV_PREFIX3}.${key}`, val);
1231
+ props.api.kv.set(`${KV_PREFIX4}.${key}`, val);
1232
1232
  } catch {
1233
1233
  }
1234
1234
  };
1235
1235
  onMount(() => {
1236
1236
  setPanelWidth(DEFAULT_PANEL_WIDTH);
1237
1237
  try {
1238
- setOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.open`, false)));
1239
- setDetailOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.detail`, true)));
1240
- setModelOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.model`, true)));
1241
- setDistOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.dist`, false)));
1242
- setSkillsOpen(Boolean(props.api.kv.get(`${KV_PREFIX3}.skills`, true)));
1238
+ setOpen(Boolean(props.api.kv.get(`${KV_PREFIX4}.open`, false)));
1239
+ setDetailOpen(Boolean(props.api.kv.get(`${KV_PREFIX4}.detail`, true)));
1240
+ setModelOpen(Boolean(props.api.kv.get(`${KV_PREFIX4}.model`, true)));
1241
+ setDistOpen(Boolean(props.api.kv.get(`${KV_PREFIX4}.dist`, false)));
1242
+ setSkillsOpen(Boolean(props.api.kv.get(`${KV_PREFIX4}.skills`, true)));
1243
1243
  } catch {
1244
1244
  }
1245
1245
  const doRestore = () => {
1246
1246
  try {
1247
- const sym = props.api.kv.get(`${KV_PREFIX3}.currency`);
1248
- const rate = props.api.kv.get(`${KV_PREFIX3}.rate`);
1247
+ const sym = props.api.kv.get(`${KV_PREFIX4}.currency`);
1248
+ const rate = props.api.kv.get(`${KV_PREFIX4}.rate`);
1249
1249
  if (typeof sym === "string") setCurrencySymbol(sym);
1250
1250
  if (typeof rate === "number" && rate > 0) setExchangeRate(rate);
1251
- const balCur = props.api.kv.get(`${KV_PREFIX3}.balance_currency`);
1251
+ const balCur = props.api.kv.get(`${KV_PREFIX4}.balance_currency`);
1252
1252
  if (typeof balCur === "string") setBalanceCurrency(balCur);
1253
- const savedProvider = props.api.kv.get(`${KV_PREFIX3}.balance.provider`);
1253
+ const savedProvider = props.api.kv.get(`${KV_PREFIX4}.balance.provider`);
1254
1254
  if (typeof savedProvider === "string" && balanceProviders.some((p) => p.id === savedProvider)) {
1255
1255
  setBalanceProviderId(savedProvider);
1256
1256
  setBalanceUnsupported(false);
1257
1257
  }
1258
- const savedAuto = props.api.kv.get(`${KV_PREFIX3}.balance.auto`);
1258
+ const savedAuto = props.api.kv.get(`${KV_PREFIX4}.balance.auto`);
1259
1259
  if (typeof savedAuto === "boolean") setAutoBalance(savedAuto);
1260
- const legacyKey = props.api.kv.get(`${KV_PREFIX3}.ds_key`, "");
1260
+ const legacyKey = props.api.kv.get(`${KV_PREFIX4}.ds_key`, "");
1261
1261
  if (legacyKey) {
1262
- const dsKey = props.api.kv.get(`${KV_PREFIX3}.balance.deepseek.key`, "");
1263
- if (!dsKey) props.api.kv.set(`${KV_PREFIX3}.balance.deepseek.key`, legacyKey);
1264
- props.api.kv.set(`${KV_PREFIX3}.ds_key`, "");
1262
+ const dsKey = props.api.kv.get(`${KV_PREFIX4}.balance.deepseek.key`, "");
1263
+ if (!dsKey) props.api.kv.set(`${KV_PREFIX4}.balance.deepseek.key`, legacyKey);
1264
+ props.api.kv.set(`${KV_PREFIX4}.ds_key`, "");
1265
1265
  }
1266
1266
  props.signals.setBalanceRefresh(props.signals.balanceRefresh() + 1);
1267
- setSectionDetail(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.detail`, true)));
1268
- setSectionModel(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.model`, true)));
1269
- setSectionDist(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.dist`, true)));
1270
- setSectionSkills(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.skills`, true)));
1271
- setSectionBalance(Boolean(props.api.kv.get(`${KV_PREFIX3}.section.balance`, true)));
1272
- const bv = props.api.kv.get(`${KV_PREFIX3}.border`, true);
1267
+ setSectionDetail(Boolean(props.api.kv.get(`${KV_PREFIX4}.section.detail`, true)));
1268
+ setSectionModel(Boolean(props.api.kv.get(`${KV_PREFIX4}.section.model`, true)));
1269
+ setSectionDist(Boolean(props.api.kv.get(`${KV_PREFIX4}.section.dist`, true)));
1270
+ setSectionSkills(Boolean(props.api.kv.get(`${KV_PREFIX4}.section.skills`, true)));
1271
+ setSectionBalance(Boolean(props.api.kv.get(`${KV_PREFIX4}.section.balance`, true)));
1272
+ const bv = props.api.kv.get(`${KV_PREFIX4}.border`, true);
1273
1273
  setBorderVisible(bv !== false);
1274
- const cachedDist = props.api.kv.get(`${KV_PREFIX3}.dist_snapshot`);
1274
+ const cachedDist = props.api.kv.get(`${KV_PREFIX4}.dist_snapshot`);
1275
1275
  if (cachedDist) {
1276
1276
  setLastDist(cachedDist);
1277
1277
  setLastHasDist(true);
@@ -2154,7 +2154,8 @@ import { effect as _$effect2 } from "@opentui/solid";
2154
2154
  import { insert as _$insert2 } from "@opentui/solid";
2155
2155
  import { createComponent as _$createComponent2 } from "@opentui/solid";
2156
2156
  import { createElement as _$createElement2 } from "@opentui/solid";
2157
- import { createMemo as createMemo2, For, Show as Show2 } from "solid-js";
2157
+ import { memo as _$memo2 } from "@opentui/solid";
2158
+ import { createMemo as createMemo2, For, onMount as onMount2, Show as Show2 } from "solid-js";
2158
2159
 
2159
2160
  // src/v2/data.ts
2160
2161
  function collectLastHitRate(context, sessionID) {
@@ -2207,8 +2208,16 @@ function mapTheme(theme) {
2207
2208
  }
2208
2209
 
2209
2210
  // src/v2/status.tsx
2211
+ var KV_PREFIX = "cache_panel";
2210
2212
  function StatusView(props) {
2211
2213
  const t = createT(() => props.signals.langCode());
2214
+ onMount2(() => {
2215
+ try {
2216
+ const v = props.api.kv.get(`${KV_PREFIX}.section.bottom`, true);
2217
+ props.signals.setSectionBottom(v !== false);
2218
+ } catch {
2219
+ }
2220
+ });
2212
2221
  const pal = createMemo2(() => {
2213
2222
  const th = mapTheme(props.context.theme);
2214
2223
  const sat = (k, fb) => desaturateTo(th[k], MAX_SAT, fb);
@@ -2288,7 +2297,7 @@ function StatusView(props) {
2288
2297
  });
2289
2298
  return _$createComponent2(Show2, {
2290
2299
  get when() {
2291
- return segsW() > 0;
2300
+ return _$memo2(() => !!(segsW() > 0 && stats().hitRate >= 0))() && props.signals.sectionBottom();
2292
2301
  },
2293
2302
  get children() {
2294
2303
  var _el$ = _$createElement2("text");
@@ -2311,7 +2320,7 @@ function StatusView(props) {
2311
2320
  }
2312
2321
 
2313
2322
  // src/v2/commands.ts
2314
- var KV_PREFIX = "cache_panel";
2323
+ var KV_PREFIX2 = "cache_panel";
2315
2324
  function extractToolParts(msg) {
2316
2325
  if (!Array.isArray(msg?.content)) return [];
2317
2326
  return msg.content.filter((p) => p?.type === "tool");
@@ -2340,13 +2349,13 @@ function currentSessionID(context) {
2340
2349
  function makeCommands(context, api, signals) {
2341
2350
  const t = () => createT(() => signals.langCode());
2342
2351
  const providerOptionTitle = (p, current) => {
2343
- const hasManual = !!api.kv.get(`${KV_PREFIX}.balance.${p.id}.key`, "");
2352
+ const hasManual = !!api.kv.get(`${KV_PREFIX2}.balance.${p.id}.key`, "");
2344
2353
  const hasAuto = !hasManual && !!findOpencodeKeyV2(context, p);
2345
2354
  const mark = hasManual ? t()("keyUser") : hasAuto ? t()("keyOpenCode") : t()("keyNotSet");
2346
2355
  return p.name + mark + (current && p.id === current ? " *" : "");
2347
2356
  };
2348
2357
  const promptBalanceKey = async (provider) => {
2349
- const current = api.kv.get(`${KV_PREFIX}.balance.${provider.id}.key`, "") ?? "";
2358
+ const current = api.kv.get(`${KV_PREFIX2}.balance.${provider.id}.key`, "") ?? "";
2350
2359
  const val = await context.ui.dialog.prompt({
2351
2360
  title: provider.name,
2352
2361
  message: t()("balKeyPrompt", { p: provider.name }),
@@ -2362,7 +2371,7 @@ function makeCommands(context, api, signals) {
2362
2371
  } else {
2363
2372
  key = input;
2364
2373
  }
2365
- await api.kv.set(`${KV_PREFIX}.balance.${provider.id}.key`, key);
2374
+ await api.kv.set(`${KV_PREFIX2}.balance.${provider.id}.key`, key);
2366
2375
  signals.setBalanceRefresh(signals.balanceRefresh() + 1);
2367
2376
  context.ui.toast.show({ message: key ? t()("keySaved") : t()("keyCleared") });
2368
2377
  };
@@ -2386,9 +2395,9 @@ function makeCommands(context, api, signals) {
2386
2395
  if (!opt) return;
2387
2396
  const sym = CURRENCIES[opt] ?? "$";
2388
2397
  const defRate = DEFAULT_RATES[opt] ?? 1;
2389
- await api.kv.set(`${KV_PREFIX}.currency`, sym);
2390
- await api.kv.set(`${KV_PREFIX}.rate`, defRate);
2391
- await api.kv.set(`${KV_PREFIX}.balance_currency`, opt);
2398
+ await api.kv.set(`${KV_PREFIX2}.currency`, sym);
2399
+ await api.kv.set(`${KV_PREFIX2}.rate`, defRate);
2400
+ await api.kv.set(`${KV_PREFIX2}.balance_currency`, opt);
2392
2401
  signals.setBalanceCurrency(opt);
2393
2402
  signals.setCurrencySymbol(sym);
2394
2403
  signals.setExchangeRate(defRate);
@@ -2412,7 +2421,7 @@ function makeCommands(context, api, signals) {
2412
2421
  if (val === void 0) return;
2413
2422
  const n = parseFloat(val);
2414
2423
  if (n > 0) {
2415
- await api.kv.set(`${KV_PREFIX}.rate`, n);
2424
+ await api.kv.set(`${KV_PREFIX2}.rate`, n);
2416
2425
  signals.setExchangeRate(n);
2417
2426
  context.ui.toast.show({ message: t()("rateSet", { r: n }) });
2418
2427
  }
@@ -2427,13 +2436,13 @@ function makeCommands(context, api, signals) {
2427
2436
  palette: true,
2428
2437
  slash: { name: "cache-section" },
2429
2438
  run: async () => {
2430
- const detailOn = Boolean(api.kv.get(`${KV_PREFIX}.section.detail`, true));
2431
- const modelOn = Boolean(api.kv.get(`${KV_PREFIX}.section.model`, true));
2432
- const distOn = Boolean(api.kv.get(`${KV_PREFIX}.section.dist`, true));
2433
- const skillsOn = Boolean(api.kv.get(`${KV_PREFIX}.section.skills`, true));
2434
- const balanceOn = Boolean(api.kv.get(`${KV_PREFIX}.section.balance`, true));
2435
- const bottomOn = Boolean(api.kv.get(`${KV_PREFIX}.section.bottom`, true));
2436
- const borderOn = Boolean(api.kv.get(`${KV_PREFIX}.border`, true));
2439
+ const detailOn = Boolean(api.kv.get(`${KV_PREFIX2}.section.detail`, true));
2440
+ const modelOn = Boolean(api.kv.get(`${KV_PREFIX2}.section.model`, true));
2441
+ const distOn = Boolean(api.kv.get(`${KV_PREFIX2}.section.dist`, true));
2442
+ const skillsOn = Boolean(api.kv.get(`${KV_PREFIX2}.section.skills`, true));
2443
+ const balanceOn = Boolean(api.kv.get(`${KV_PREFIX2}.section.balance`, true));
2444
+ const bottomOn = Boolean(api.kv.get(`${KV_PREFIX2}.section.bottom`, true));
2445
+ const borderOn = Boolean(api.kv.get(`${KV_PREFIX2}.border`, true));
2437
2446
  const labels = {
2438
2447
  detail: t()("secDetail"),
2439
2448
  model: t()("secModel"),
@@ -2458,12 +2467,12 @@ function makeCommands(context, api, signals) {
2458
2467
  });
2459
2468
  if (!opt) return;
2460
2469
  if (opt === "border") {
2461
- const cur = Boolean(api.kv.get(`${KV_PREFIX}.border`, true));
2462
- await api.kv.set(`${KV_PREFIX}.border`, !cur);
2470
+ const cur = Boolean(api.kv.get(`${KV_PREFIX2}.border`, true));
2471
+ await api.kv.set(`${KV_PREFIX2}.border`, !cur);
2463
2472
  signals.setBorderVisible(!cur);
2464
2473
  context.ui.toast.show({ message: !cur ? t()("borderShown") : t()("borderHidden") });
2465
2474
  } else {
2466
- const key = `${KV_PREFIX}.section.${opt}`;
2475
+ const key = `${KV_PREFIX2}.section.${opt}`;
2467
2476
  const cur = Boolean(api.kv.get(key, true));
2468
2477
  await api.kv.set(key, !cur);
2469
2478
  if (opt === "detail") signals.setSectionDetail(!cur);
@@ -2486,14 +2495,14 @@ function makeCommands(context, api, signals) {
2486
2495
  palette: true,
2487
2496
  slash: { name: "cache-config" },
2488
2497
  run: () => {
2489
- const sym = api.kv.get(`${KV_PREFIX}.currency`) ?? "$";
2490
- const rate = api.kv.get(`${KV_PREFIX}.rate`) ?? 1;
2491
- const detail = Boolean(api.kv.get(`${KV_PREFIX}.section.detail`, true));
2492
- const model = Boolean(api.kv.get(`${KV_PREFIX}.section.model`, true));
2493
- const dist = Boolean(api.kv.get(`${KV_PREFIX}.section.dist`, true));
2494
- const skills = Boolean(api.kv.get(`${KV_PREFIX}.section.skills`, true));
2495
- const balance = Boolean(api.kv.get(`${KV_PREFIX}.section.balance`, true));
2496
- const bottom = Boolean(api.kv.get(`${KV_PREFIX}.section.bottom`, true));
2498
+ const sym = api.kv.get(`${KV_PREFIX2}.currency`) ?? "$";
2499
+ const rate = api.kv.get(`${KV_PREFIX2}.rate`) ?? 1;
2500
+ const detail = Boolean(api.kv.get(`${KV_PREFIX2}.section.detail`, true));
2501
+ const model = Boolean(api.kv.get(`${KV_PREFIX2}.section.model`, true));
2502
+ const dist = Boolean(api.kv.get(`${KV_PREFIX2}.section.dist`, true));
2503
+ const skills = Boolean(api.kv.get(`${KV_PREFIX2}.section.skills`, true));
2504
+ const balance = Boolean(api.kv.get(`${KV_PREFIX2}.section.balance`, true));
2505
+ const bottom = Boolean(api.kv.get(`${KV_PREFIX2}.section.bottom`, true));
2497
2506
  const on = (v) => v ? "ON" : "OFF";
2498
2507
  context.ui.toast.show({
2499
2508
  title: t()("panelConfigTitle"),
@@ -2528,7 +2537,7 @@ function makeCommands(context, api, signals) {
2528
2537
  }))
2529
2538
  });
2530
2539
  if (!opt) return;
2531
- await api.kv.set(`${KV_PREFIX}.lang`, opt);
2540
+ await api.kv.set(`${KV_PREFIX2}.lang`, opt);
2532
2541
  signals.setLangCode(opt);
2533
2542
  context.ui.toast.show({ message: t()("langSwitched") });
2534
2543
  }
@@ -2558,18 +2567,18 @@ function makeCommands(context, api, signals) {
2558
2567
  if (!opt) return;
2559
2568
  if (opt === "__auto__") {
2560
2569
  const next = !auto;
2561
- await api.kv.set(`${KV_PREFIX}.balance.auto`, next);
2570
+ await api.kv.set(`${KV_PREFIX2}.balance.auto`, next);
2562
2571
  signals.setAutoBalance(next);
2563
2572
  context.ui.toast.show({ message: next ? t()("autoSwitchOn") : t()("autoSwitchOff") });
2564
2573
  } else {
2565
2574
  const provider = getBalanceProvider(opt);
2566
- await api.kv.set(`${KV_PREFIX}.balance.provider`, provider.id);
2567
- await api.kv.set(`${KV_PREFIX}.balance.auto`, false);
2575
+ await api.kv.set(`${KV_PREFIX2}.balance.provider`, provider.id);
2576
+ await api.kv.set(`${KV_PREFIX2}.balance.auto`, false);
2568
2577
  signals.setBalanceProviderId(provider.id);
2569
2578
  signals.setAutoBalance(false);
2570
2579
  signals.setBalanceUnsupported(false);
2571
2580
  signals.setBalanceRefresh(signals.balanceRefresh() + 1);
2572
- const hasKey = !!api.kv.get(`${KV_PREFIX}.balance.${provider.id}.key`, "");
2581
+ const hasKey = !!api.kv.get(`${KV_PREFIX2}.balance.${provider.id}.key`, "");
2573
2582
  if (!hasKey) {
2574
2583
  await promptBalanceKey(provider);
2575
2584
  } else {
@@ -2596,8 +2605,8 @@ function makeCommands(context, api, signals) {
2596
2605
  });
2597
2606
  if (!opt) return;
2598
2607
  const provider = getBalanceProvider(opt);
2599
- await api.kv.set(`${KV_PREFIX}.balance.provider`, provider.id);
2600
- await api.kv.set(`${KV_PREFIX}.balance.auto`, false);
2608
+ await api.kv.set(`${KV_PREFIX2}.balance.provider`, provider.id);
2609
+ await api.kv.set(`${KV_PREFIX2}.balance.auto`, false);
2601
2610
  signals.setBalanceProviderId(provider.id);
2602
2611
  signals.setAutoBalance(false);
2603
2612
  signals.setBalanceRefresh(signals.balanceRefresh() + 1);
@@ -2684,7 +2693,7 @@ function makeCommands(context, api, signals) {
2684
2693
  return true;
2685
2694
  });
2686
2695
  if (unique.length > 0) {
2687
- const currentSid = signals.overrideSessionId() ?? api.kv.get(`${KV_PREFIX}.session`, "");
2696
+ const currentSid = signals.overrideSessionId() ?? api.kv.get(`${KV_PREFIX2}.session`, "");
2688
2697
  const options = unique.map((c, i) => ({
2689
2698
  title: `${i + 1}. ${c.title}`,
2690
2699
  value: c.value,
@@ -2703,11 +2712,11 @@ function makeCommands(context, api, signals) {
2703
2712
  if (!opt) return;
2704
2713
  if (opt === backValue) {
2705
2714
  signals.setOverrideSessionId(void 0);
2706
- await api.kv.set(`${KV_PREFIX}.session`, "");
2715
+ await api.kv.set(`${KV_PREFIX2}.session`, "");
2707
2716
  context.ui.toast.show({ message: t()("backToMain") });
2708
2717
  } else {
2709
2718
  signals.setOverrideSessionId(opt);
2710
- await api.kv.set(`${KV_PREFIX}.session`, opt);
2719
+ await api.kv.set(`${KV_PREFIX2}.session`, opt);
2711
2720
  context.ui.toast.show({ message: t()("subAgentSwitched", { s: opt.slice(0, 24) + "\u2026" }) });
2712
2721
  }
2713
2722
  } else {
@@ -2720,7 +2729,7 @@ function makeCommands(context, api, signals) {
2720
2729
  const sid = val.trim();
2721
2730
  if (sid) {
2722
2731
  signals.setOverrideSessionId(sid);
2723
- await api.kv.set(`${KV_PREFIX}.session`, sid);
2732
+ await api.kv.set(`${KV_PREFIX2}.session`, sid);
2724
2733
  context.ui.toast.show({ message: t()("subAgentSwitched", { s: sid.slice(0, 24) + "\u2026" }) });
2725
2734
  }
2726
2735
  }
@@ -2736,7 +2745,7 @@ function makeCommands(context, api, signals) {
2736
2745
  slash: { name: "cache-session-back" },
2737
2746
  run: async () => {
2738
2747
  signals.setOverrideSessionId(void 0);
2739
- await api.kv.set(`${KV_PREFIX}.session`, "");
2748
+ await api.kv.set(`${KV_PREFIX2}.session`, "");
2740
2749
  context.ui.toast.show({ message: t()("backToMain") });
2741
2750
  }
2742
2751
  }
@@ -2744,7 +2753,7 @@ function makeCommands(context, api, signals) {
2744
2753
  }
2745
2754
 
2746
2755
  // src/v2/index.tsx
2747
- var KV_PREFIX2 = "cache_panel";
2756
+ var KV_PREFIX3 = "cache_panel";
2748
2757
  var BALANCE_POLL_MS = 5 * 60 * 1e3;
2749
2758
  var DEBUG_LANG = typeof process !== "undefined" ? process.env?.CACHE_TUI_LANG : void 0;
2750
2759
  var INIT_LANG = DEBUG_LANG !== void 0 && LANG_META.some((m) => m.code === DEBUG_LANG) ? DEBUG_LANG : detectLang();
@@ -2816,9 +2825,9 @@ function PluginRoot(props) {
2816
2825
  mode: "global",
2817
2826
  commands: makeCommands(props.context, props.api, props.signals)
2818
2827
  }));
2819
- onMount2(() => {
2828
+ onMount3(() => {
2820
2829
  try {
2821
- const saved = props.api.kv.get(`${KV_PREFIX2}.lang`);
2830
+ const saved = props.api.kv.get(`${KV_PREFIX3}.lang`);
2822
2831
  if (saved && LANG_META.some((m) => m.code === saved)) {
2823
2832
  props.signals.setLangCode(saved);
2824
2833
  }
@@ -2827,7 +2836,7 @@ function PluginRoot(props) {
2827
2836
  });
2828
2837
  const pollBalance = async () => {
2829
2838
  const provider = getBalanceProvider(props.signals.balanceProviderId());
2830
- const key = props.api.kv.get(`${KV_PREFIX2}.balance.${provider.id}.key`, "") || findOpencodeKeyV2(props.context, provider);
2839
+ const key = props.api.kv.get(`${KV_PREFIX3}.balance.${provider.id}.key`, "") || findOpencodeKeyV2(props.context, provider);
2831
2840
  const set = props.signals.setBalanceState;
2832
2841
  if (props.signals.balanceUnsupported()) {
2833
2842
  set({
@@ -2904,7 +2913,7 @@ function PluginRoot(props) {
2904
2913
  lastSlotSid = sid;
2905
2914
  if (props.signals.overrideSessionId()) {
2906
2915
  props.signals.setOverrideSessionId(void 0);
2907
- void props.api.kv.set(`${KV_PREFIX2}.session`, "");
2916
+ void props.api.kv.set(`${KV_PREFIX3}.session`, "");
2908
2917
  }
2909
2918
  }
2910
2919
  });
@@ -2943,6 +2952,7 @@ var mod = {
2943
2952
  append: "prompt.footer.status",
2944
2953
  render: (props) => _$createComponent3(StatusView, {
2945
2954
  context,
2955
+ api,
2946
2956
  signals,
2947
2957
  get sessionID() {
2948
2958
  return String(props.sessionID ?? "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-visual-cache",
3
- "version": "1.7.0-beta.4",
3
+ "version": "1.7.0-beta.5",
4
4
  "description": "OpenCode TUI plugin displaying real-time token cache hit rate in the sidebar",
5
5
  "type": "module",
6
6
  "types": "dist/index.d.ts",
package/src/_version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // auto-generated
2
- export const PLUGIN_VERSION="1.7.0-beta.4";
2
+ export const PLUGIN_VERSION="1.7.0-beta.5";
package/src/v2/index.tsx CHANGED
@@ -172,7 +172,7 @@ const mod: PluginModule & { server: () => Promise<Record<string, never>> } = {
172
172
  context.ui.slot({
173
173
  append: "prompt.footer.status",
174
174
  render: (props) => (
175
- <StatusView context={context} signals={signals} sessionID={String(props.sessionID ?? "")} />
175
+ <StatusView context={context} api={api} signals={signals} sessionID={String(props.sessionID ?? "")} />
176
176
  ),
177
177
  })
178
178
 
package/src/v2/status.tsx CHANGED
@@ -1,26 +1,38 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
2
 
3
- import { createMemo, For, Show } from "solid-js"
3
+ import { createMemo, For, onMount, Show } from "solid-js"
4
4
  import type { Context } from "./types"
5
- import type { PanelSignals } from "../panel/panel-api"
5
+ import type { PanelApi, PanelSignals } from "../panel/panel-api"
6
6
  import { collectLastHitRate } from "./data"
7
7
  import { desaturateTo, fmtCompact, formatBalanceText, MAX_SAT, FALLBACK, visualWidth } from "../core"
8
8
  import { createT } from "../i18n"
9
9
  import { mapTheme } from "./theme"
10
10
 
11
+ const KV_PREFIX = "cache_panel"
12
+
11
13
  /**
12
14
  * 底部状态栏(prompt.footer.status)——对齐 V1 BottomStatusBar 统计段口径:
13
15
  * 单条命中率(最后一条有 token 的 assistant 消息)+ 趋势 + Tokens 总量 + 余额。
14
16
  * 余额读共享 signals.balanceState(PluginRoot 驱动轮询);provider 不支持余额时隐藏余额段(对齐 V1)。
15
17
  * 颜色经 mapTheme(V1 形状)——与侧边栏命中率颜色同源,保证两处一致。
18
+ * 显隐受 signals.sectionBottom 控制(/cache-section 切换),启动时从 kv 恢复偏好(对齐 V1)。
16
19
  */
17
20
  export function StatusView(props: {
18
21
  context: Context
22
+ api: PanelApi
19
23
  signals: PanelSignals
20
24
  sessionID: string
21
25
  }) {
22
26
  const t = createT(() => props.signals.langCode())
23
27
 
28
+ // 恢复显隐偏好(默认显示;关闭时隐藏统计段,与宿主默认 hint 行一致)
29
+ onMount(() => {
30
+ try {
31
+ const v = props.api.kv.get<boolean>(`${KV_PREFIX}.section.bottom`, true)
32
+ props.signals.setSectionBottom(v !== false)
33
+ } catch {}
34
+ })
35
+
24
36
  // 与侧边栏 TokenCachePanel 同一颜色来源(mapTheme → desaturateTo)
25
37
  const pal = createMemo(() => {
26
38
  const th = mapTheme(props.context.theme) as unknown as Record<string, string>
@@ -87,7 +99,7 @@ export function StatusView(props: {
87
99
  })
88
100
 
89
101
  return (
90
- <Show when={segsW() > 0}>
102
+ <Show when={segsW() > 0 && stats().hitRate >= 0 && props.signals.sectionBottom()}>
91
103
  <text>
92
104
  <For each={segs()}>{(sg) => <span style={{ fg: sg.color }}>{sg.text}</span>}</For>
93
105
  </text>