opencode-visual-cache 1.1.0 → 1.1.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.
package/README.md CHANGED
@@ -45,7 +45,7 @@
45
45
  在 OpenCode 中按 **`Ctrl + P`** 打开命令面板,搜索 **`install plugin`**,输入:
46
46
 
47
47
  ```
48
- opencode-visual-cache
48
+ opencode-visual-cache@latest
49
49
  ```
50
50
 
51
51
  回车即可完成安装与配置。
@@ -55,7 +55,7 @@ opencode-visual-cache
55
55
  **1. 安装插件**
56
56
 
57
57
  ```bash
58
- npm install -g opencode-visual-cache
58
+ npm install -g opencode-visual-cache@latest
59
59
  ```
60
60
 
61
61
  **2. 配置 TUI 插件**
@@ -65,7 +65,7 @@ npm install -g opencode-visual-cache
65
65
  ```jsonc
66
66
  {
67
67
  "$schema": "https://opencode.ai/tui.json",
68
- "plugin": ["opencode-visual-cache"]
68
+ "plugin": ["opencode-visual-cache@latest"]
69
69
  }
70
70
  ```
71
71
 
@@ -75,6 +75,32 @@ npm install -g opencode-visual-cache
75
75
 
76
76
  ---
77
77
 
78
+ ## 更新
79
+
80
+ 由于 [OpenCode 已知问题 #6774](https://github.com/anomalyco/opencode/issues/6774),插件缓存会锁死在首次安装时的版本,不会自动检测 npm 上的新版本。
81
+
82
+ 更新步骤:
83
+
84
+ **1. 清除 OpenCode 插件缓存**
85
+
86
+ ```powershell
87
+ # Windows
88
+ Remove-Item -Recurse -Force "$env:USERPROFILE\.cache\opencode\packages\opencode-visual-cache@latest"
89
+ ```
90
+
91
+ ```bash
92
+ # macOS / Linux
93
+ rm -rf ~/.cache/opencode/packages/opencode-visual-cache@latest
94
+ ```
95
+
96
+ **2. 重新安装插件**
97
+
98
+ 在 OpenCode 中按 **`Ctrl + P`** → `install plugin` → `opencode-visual-cache@latest` → 回车
99
+
100
+ **3. 重启 OpenCode**
101
+
102
+ ---
103
+
78
104
  ## 调试
79
105
 
80
106
  强制英文(Windows PowerShell):
package/README_EN.md CHANGED
@@ -46,7 +46,7 @@
46
46
  Press **`Ctrl + P`** in OpenCode to open the command palette, search **`install plugin`**, then type:
47
47
 
48
48
  ```
49
- opencode-visual-cache
49
+ opencode-visual-cache@latest
50
50
  ```
51
51
 
52
52
  Press Enter to install and configure automatically.
@@ -56,7 +56,7 @@ Press Enter to install and configure automatically.
56
56
  **1. Install the plugin**
57
57
 
58
58
  ```bash
59
- npm install -g opencode-visual-cache
59
+ npm install -g opencode-visual-cache@latest
60
60
  ```
61
61
 
62
62
  **2. Configure TUI plugin**
@@ -66,7 +66,7 @@ Create or edit `~/.config/opencode/tui.jsonc`:
66
66
  ```jsonc
67
67
  {
68
68
  "$schema": "https://opencode.ai/tui.json",
69
- "plugin": ["opencode-visual-cache"]
69
+ "plugin": ["opencode-visual-cache@latest"]
70
70
  }
71
71
  ```
72
72
 
@@ -76,6 +76,32 @@ Open any session — the cache stats panel appears in the sidebar.
76
76
 
77
77
  ---
78
78
 
79
+ ## Update
80
+
81
+ Due to a [known OpenCode issue #6774](https://github.com/anomalyco/opencode/issues/6774), the plugin cache locks to the version installed at first setup and does **not** auto-detect newer releases on npm.
82
+
83
+ To update:
84
+
85
+ **1. Clear the OpenCode plugin cache**
86
+
87
+ ```powershell
88
+ # Windows
89
+ Remove-Item -Recurse -Force "$env:USERPROFILE\.cache\opencode\packages\opencode-visual-cache@latest"
90
+ ```
91
+
92
+ ```bash
93
+ # macOS / Linux
94
+ rm -rf ~/.cache/opencode/packages/opencode-visual-cache@latest
95
+ ```
96
+
97
+ **2. Re-install the plugin**
98
+
99
+ Press **`Ctrl + P`** in OpenCode → `install plugin` → `opencode-visual-cache@latest` → Enter
100
+
101
+ **3. Restart OpenCode**
102
+
103
+ ---
104
+
79
105
  ## Debug
80
106
 
81
107
  Force English:
@@ -0,0 +1 @@
1
+ export declare const PLUGIN_VERSION = "1.1.5";
@@ -0,0 +1,2 @@
1
+ // auto-generated
2
+ export const PLUGIN_VERSION = "1.1.5";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@opentui/solid/jsx-runtime";
2
2
  import { createMemo, createSignal, Show } from "solid-js";
3
+ import { PLUGIN_VERSION } from "./_version";
3
4
  // ── terminal-width helpers ────────────────────────────────────────
4
5
  // CJK characters occupy 2 terminal columns; padEnd/padStart count
5
6
  // string length (=1 per char), which breaks alignment with mixed text.
@@ -331,7 +332,7 @@ function TokenCachePanel(props) {
331
332
  // 通过 ref 获取 Renderable 实例宽度
332
333
  const w = boxEl ? Math.max(MIN_PANEL_WIDTH, boxEl.width) : DEFAULT_PANEL_WIDTH;
333
334
  setPanelWidth((prev) => (prev === w ? prev : w));
334
- }, children: [_jsxs("text", { onMouseUp: () => setOpen((o) => !o), children: [_jsx("span", { style: { fg: pal().muted }, children: open() ? "\u25bc " : "\u25b6 " }), _jsx("span", { style: { fg: pal().primary }, children: _jsx("b", { children: T.title }) }), _jsxs(Show, { when: !open() && data().hasData, children: [_jsxs(Show, { when: data().hasTrendData, children: [_jsx("span", { children: " ".repeat(Math.max(1, panelWidth() - GUTTER - HEADER_PREFIX - visualWidth(T.title) - visualWidth(pct() + " " + T.hitFolded + " " + trendLabel(data().trend)))) }), _jsxs("span", { style: { fg: hitColor() }, children: [pct(), " ", T.hitFolded] }), _jsxs("span", { style: { fg: data().trend !== 0 ? (data().trend > 0 ? pal().success : pal().error) : pal().text }, children: [" ", trendLabel(data().trend)] })] }), _jsxs(Show, { when: !data().hasTrendData, children: [_jsx("span", { children: " ".repeat(Math.max(1, panelWidth() - GUTTER - HEADER_PREFIX - visualWidth(T.title) - visualWidth(pct() + " " + T.hitFolded))) }), _jsxs("span", { style: { fg: hitColor() }, children: [pct(), " ", T.hitFolded] })] })] })] }), _jsx(Show, { when: open(), children: _jsxs(Show, { when: data().hasData, fallback: _jsxs(_Fragment, { children: [_jsx("text", { fg: pal().muted, children: sep() }), _jsxs("text", { children: [_jsx("span", { style: { fg: pal().muted }, children: "> " }), _jsx("span", { style: { fg: pal().muted }, children: T.noData })] })] }), children: [_jsx("text", { fg: pal().muted, children: sep() }), _jsxs("text", { children: [_jsxs("span", { style: { fg: pal().text }, children: [T.hit, " "] }), _jsxs("span", { style: { fg: hitColor() }, children: ["[", bar(), "] "] }), _jsx("span", { style: { fg: pal().text }, children: pct() }), _jsx(Show, { when: data().hasTrendData, children: _jsxs("span", { style: { fg: data().trend !== 0 ? (data().trend > 0 ? pal().success : pal().error) : pal().text }, children: [" ", trendLabel(data().trend)] }) })] }), _jsx("text", { fg: pal().muted, children: justify(T.totalHit, data().sessionHitRate.toFixed(1) + "%") }), _jsx(Show, { when: data().read > 0, children: _jsx("text", { fg: pal().muted, children: justify(T.read, fmt(data().read), T.tok) }) }), _jsx(Show, { when: data().write > 0, children: _jsx("text", { fg: pal().muted, children: justify(T.write, fmt(data().write), T.tok) }) }), _jsx("text", { fg: pal().muted, children: justify(T.miss, fmt(data().freshInput), T.tok) }), _jsx("text", { fg: pal().muted, children: justify(T.out, fmt(data().output), T.tok) }), _jsx("text", { fg: pal().muted, children: sep() }), _jsx("text", { fg: pal().text, children: justify(T.cost, fmtCost(data().cost)) }), _jsx(Show, { when: data().saved > 0, children: _jsxs("text", { children: [_jsx("span", { style: { fg: pal().muted }, children: T.saved }), _jsx("span", { children: " ".repeat(Math.max(1, panelWidth() - GUTTER - visualWidth(T.saved) - visualWidth("~" + fmtCost(data().saved)))) }), _jsxs("span", { style: { fg: pal().success }, children: ["~", fmtCost(data().saved)] })] }) }), _jsx(Show, { when: data().providerName, children: _jsx("text", { fg: pal().muted, children: justify(T.provider, data().providerName) }) }), _jsx("text", { fg: pal().muted, children: justify(T.model, data().model) }), _jsxs(Show, { when: data().hasPricing, children: [_jsx("text", { fg: pal().muted, children: justify(T.rate, "$" + data().inputRate.toFixed(2) + "/M " + T.inputRate) }), _jsx(Show, { when: data().cacheReadRate > 0, children: _jsx("text", { fg: pal().muted, children: justify("", "$" + data().cacheReadRate.toFixed(2) + "/M " + T.cacheRate) }) }), _jsx(Show, { when: data().cacheWriteRate > 0, children: _jsx("text", { fg: pal().muted, children: justify("", "$" + data().cacheWriteRate.toFixed(2) + "/M " + T.writeRate) }) })] })] }) })] }));
335
+ }, children: [_jsxs("text", { onMouseUp: () => setOpen((o) => !o), children: [_jsx("span", { style: { fg: pal().muted }, children: open() ? "\u25bc " : "\u25b6 " }), _jsxs("span", { style: { fg: pal().primary }, children: [_jsx("b", { children: T.title }), _jsx(Show, { when: open(), children: _jsxs("span", { style: { fg: pal().muted }, children: [" (v", PLUGIN_VERSION, ")"] }) })] }), _jsxs(Show, { when: !open() && data().hasData, children: [_jsxs(Show, { when: data().hasTrendData, children: [_jsx("span", { children: " ".repeat(Math.max(1, panelWidth() - GUTTER - HEADER_PREFIX - visualWidth(T.title) - visualWidth(pct() + " " + T.hitFolded + " " + trendLabel(data().trend)))) }), _jsxs("span", { style: { fg: hitColor() }, children: [pct(), " ", T.hitFolded] }), _jsxs("span", { style: { fg: data().trend !== 0 ? (data().trend > 0 ? pal().success : pal().error) : pal().text }, children: [" ", trendLabel(data().trend)] })] }), _jsxs(Show, { when: !data().hasTrendData, children: [_jsx("span", { children: " ".repeat(Math.max(1, panelWidth() - GUTTER - HEADER_PREFIX - visualWidth(T.title) - visualWidth(pct() + " " + T.hitFolded))) }), _jsxs("span", { style: { fg: hitColor() }, children: [pct(), " ", T.hitFolded] })] })] })] }), _jsx(Show, { when: open(), children: _jsxs(Show, { when: data().hasData, fallback: _jsxs(_Fragment, { children: [_jsx("text", { fg: pal().muted, children: sep() }), _jsxs("text", { children: [_jsx("span", { style: { fg: pal().muted }, children: "> " }), _jsx("span", { style: { fg: pal().muted }, children: T.noData })] })] }), children: [_jsx("text", { fg: pal().muted, children: sep() }), _jsxs("text", { children: [_jsxs("span", { style: { fg: pal().text }, children: [T.hit, " "] }), _jsxs("span", { style: { fg: hitColor() }, children: ["[", bar(), "] "] }), _jsx("span", { style: { fg: pal().text }, children: pct() }), _jsx(Show, { when: data().hasTrendData, children: _jsxs("span", { style: { fg: data().trend !== 0 ? (data().trend > 0 ? pal().success : pal().error) : pal().text }, children: [" ", trendLabel(data().trend)] }) })] }), _jsx("text", { fg: pal().muted, children: justify(T.totalHit, data().sessionHitRate.toFixed(1) + "%") }), _jsx(Show, { when: data().read > 0, children: _jsx("text", { fg: pal().muted, children: justify(T.read, fmt(data().read), T.tok) }) }), _jsx(Show, { when: data().write > 0, children: _jsx("text", { fg: pal().muted, children: justify(T.write, fmt(data().write), T.tok) }) }), _jsx("text", { fg: pal().muted, children: justify(T.miss, fmt(data().freshInput), T.tok) }), _jsx("text", { fg: pal().muted, children: justify(T.out, fmt(data().output), T.tok) }), _jsx("text", { fg: pal().muted, children: sep() }), _jsx("text", { fg: pal().text, children: justify(T.cost, fmtCost(data().cost)) }), _jsx(Show, { when: data().saved > 0, children: _jsxs("text", { children: [_jsx("span", { style: { fg: pal().muted }, children: T.saved }), _jsx("span", { children: " ".repeat(Math.max(1, panelWidth() - GUTTER - visualWidth(T.saved) - visualWidth("~" + fmtCost(data().saved)))) }), _jsxs("span", { style: { fg: pal().success }, children: ["~", fmtCost(data().saved)] })] }) }), _jsx(Show, { when: data().providerName, children: _jsx("text", { fg: pal().muted, children: justify(T.provider, data().providerName) }) }), _jsx("text", { fg: pal().muted, children: justify(T.model, data().model) }), _jsxs(Show, { when: data().hasPricing, children: [_jsx("text", { fg: pal().muted, children: justify(T.rate, "$" + data().inputRate.toFixed(2) + "/M " + T.inputRate) }), _jsx(Show, { when: data().cacheReadRate > 0, children: _jsx("text", { fg: pal().muted, children: justify("", "$" + data().cacheReadRate.toFixed(2) + "/M " + T.cacheRate) }) }), _jsx(Show, { when: data().cacheWriteRate > 0, children: _jsx("text", { fg: pal().muted, children: justify("", "$" + data().cacheWriteRate.toFixed(2) + "/M " + T.writeRate) }) })] })] }) })] }));
335
336
  }
336
337
  // ---------------------------------------------------------------------------
337
338
  // Plugin entry
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-visual-cache",
3
- "version": "1.1.0",
3
+ "version": "1.1.5",
4
4
  "description": "OpenCode TUI plugin displaying real-time token cache hit rate in the sidebar",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,6 +30,7 @@
30
30
  "scripts": {
31
31
  "build": "tsc",
32
32
  "typecheck": "tsc --noEmit",
33
+ "version": "node -e \"require('fs').writeFileSync('src/_version.ts','// auto-generated\\nexport const PLUGIN_VERSION='+JSON.stringify(require('./package.json').version)+';\\n')\"",
33
34
  "prepublishOnly": "tsc"
34
35
  },
35
36
  "keywords": [
@@ -0,0 +1,2 @@
1
+ // auto-generated
2
+ export const PLUGIN_VERSION="1.1.5";
package/src/index.tsx CHANGED
@@ -11,6 +11,7 @@ import type {
11
11
  } from "@opencode-ai/plugin/tui"
12
12
  import type { AssistantMessage, Message } from "@opencode-ai/sdk"
13
13
  import { createMemo, createSignal, Show } from "solid-js"
14
+ import { PLUGIN_VERSION } from "./_version"
14
15
 
15
16
  // ---------------------------------------------------------------------------
16
17
  // Helpers
@@ -223,6 +224,7 @@ const PCT_FIXED_WIDTH = 5 // "XX.X%" 固定 5 字符宽度
223
224
  const HEADER_PREFIX = 2 // 折叠态标题行:▶/▼ 图标 + 后面的空格
224
225
  const UNIT_GAP = 1 // 计量单位前的空格(如 "tok")
225
226
 
227
+
226
228
  function TokenCachePanel(props: {
227
229
  theme: TuiThemeCurrent
228
230
  api: TuiPluginApi
@@ -381,7 +383,12 @@ function TokenCachePanel(props: {
381
383
  {/* collapsible header */}
382
384
  <text onMouseUp={() => setOpen((o) => !o)}>
383
385
  <span style={{ fg: pal().muted }}>{open() ? "\u25bc " : "\u25b6 "}</span>
384
- <span style={{ fg: pal().primary }}><b>{T.title}</b></span>
386
+ <span style={{ fg: pal().primary }}>
387
+ <b>{T.title}</b>
388
+ <Show when={open()}>
389
+ <span style={{ fg: pal().muted }}> (v{PLUGIN_VERSION})</span>
390
+ </Show>
391
+ </span>
385
392
  <Show when={!open() && data().hasData}>
386
393
  <Show when={data().hasTrendData}>
387
394
  <span>