@jack-yang/pi-tps-meter 0.3.0 → 1.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.0] - 2026-11-04
4
+
5
+ First stable release; the behaviour is that of 0.3.0, reviewed before freezing it.
6
+
7
+ - Declare `@earendil-works/pi-tui` as a peer dependency: footer mode imports it at runtime (pi
8
+ supplies it, as it does `@earendil-works/pi-coding-agent`).
9
+ - Footer mode no longer deep-copies all of pi's settings on every frame to read the
10
+ auto-compaction flag; it re-reads the setting at most once a second.
11
+
3
12
  ## [0.3.0] - 2026-11-04
4
13
 
5
14
  - New default display `footer`: the meter sits at the right end of pi's footer line with the
package/README.md CHANGED
@@ -148,6 +148,7 @@ TTFT 从 `turn_start` 开始计时(在 pi 里,一个 turn 就是一次模型
148
148
  - toolcall delta 计入 TPS 和校准,因为 pi 的 `usage.output` 包含工具调用参数的 token;原版只统计文本和推理。TTFT 仍然只认文本和思考。
149
149
  - 权威 token 数直接用 `usage.output`:pi 的 output 已经包含推理 token,原版需要把 output 和 reasoning 相加。
150
150
  - 思考粒子流已移植(配色预设、渐变、FIFO 流动、配置项都和原版一致),并做了原版想做但受限于 opencode 做不到的一点:每个粒子单独着色,真正从亮到暗渐隐(原版受 opentui 限制,整串只能用一种颜色)。右对齐时粒子流放在读数左边、方向镜像,读数本身不会移动。
151
+ - 显示位置:原版在 opencode 输入框右侧的 slot 里;pi 没有对应的位置,默认放在 footer 目录行右端(包装 pi 自己的 footer,不替换),另有 `right` / `status` / `widget` 可选。
151
152
  - 颜色用 ANSI 基本色:红 31、黄 33、绿 32、青 36,标签用暗色;原版用的是 opencode 主题槽位。
152
153
 
153
154
  ## 已知限制
@@ -144,7 +144,7 @@ export function createTpsMeter(pi: ExtensionAPI, deps: Deps = {}): void {
144
144
  const m = entry.message
145
145
  if (m.stopReason === "error" || m.stopReason === "aborted") continue
146
146
  const physical = ctxRef.modelRegistry.find(m.provider, m.model)
147
- return physical ? { model: physical, thinkingLevel: (m as { thinkingLevel?: string }).thinkingLevel } : undefined
147
+ return physical ? { model: physical, thinkingLevel: m.thinkingLevel } : undefined
148
148
  }
149
149
  return undefined
150
150
  },
@@ -156,15 +156,12 @@ export function createTpsMeter(pi: ExtensionAPI, deps: Deps = {}): void {
156
156
  const registry = ctxRef?.modelRegistry
157
157
  const model = ctxRef?.model
158
158
  if (!registry || model?.provider !== provider || !registry.isUsingOAuth(model)) return false
159
- const p = registry.getProvider(provider) as { auth?: { oauth?: { isSubscription?: boolean } } } | undefined
160
- return p?.auth?.oauth?.isSubscription === true
159
+ return registry.getProvider(provider)?.auth.oauth?.isSubscription === true
161
160
  },
162
161
  },
163
162
  getContextUsage: () => ctxRef?.getContextUsage(),
164
163
  }
165
164
 
166
- // If pi's footer cannot be wrapped (an older or newer pi changed it), never leave the user
167
- // without a footer: restore pi's, fall back to the line below the editor, say so once.
168
165
  // If pi's footer cannot be wrapped (an older or newer pi changed it), never leave the user
169
166
  // without a footer: fall back to the line below the editor (leaving footer mode is what restores
170
167
  // pi's own footer, in render()) and say so once. A mount that is no longer current (the mode
@@ -176,6 +173,16 @@ export function createTpsMeter(pi: ExtensionAPI, deps: Deps = {}): void {
176
173
  render()
177
174
  }
178
175
 
176
+ // pi feeds its own footer the auto-compaction flag whenever it changes; ours has to read the
177
+ // setting. pi.getSettings() deep-copies all settings, and the footer renders on every frame, so
178
+ // the flag is re-read at most once a second (a toggle in /settings shows up within that).
179
+ let autoCompact = { value: true, readAt: Number.NEGATIVE_INFINITY }
180
+ const autoCompactEnabled = (): boolean => {
181
+ const now = Date.now()
182
+ if (now - autoCompact.readAt >= 1_000) autoCompact = { value: pi.getSettings().compaction?.enabled ?? true, readAt: now }
183
+ return autoCompact.value
184
+ }
185
+
179
186
  const mountFooter = (target: UI, epoch: number): void => {
180
187
  loadFooterKit().then((kit) => {
181
188
  if (epoch !== mountEpoch || ui !== target) return
@@ -187,8 +194,7 @@ export function createTpsMeter(pi: ExtensionAPI, deps: Deps = {}): void {
187
194
  return {
188
195
  render(width: number): string[] {
189
196
  try {
190
- // pi feeds this flag to its own footer instance; ours reads the same setting.
191
- inner.setAutoCompactEnabled(pi.getSettings().compaction?.enabled ?? true)
197
+ inner.setAutoCompactEnabled(autoCompactEnabled())
192
198
  const lines = inner.render(width)
193
199
  if (lines.length > 0 && shownLine) lines[0] = mergeFooterLine(lines[0], shownLine, width, fns)
194
200
  return lines
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@jack-yang/pi-tps-meter",
4
- "version": "0.3.0",
4
+ "version": "1.0.0",
5
5
  "description": "Live TPS / AVG / TTFT meter for the pi coding agent, calibrated against authoritative token counts (port of opencode-tps-meter)",
6
6
  "type": "module",
7
7
  "keywords": [
@@ -42,7 +42,8 @@
42
42
  "node": ">=22.19.0"
43
43
  },
44
44
  "peerDependencies": {
45
- "@earendil-works/pi-coding-agent": "*"
45
+ "@earendil-works/pi-coding-agent": "*",
46
+ "@earendil-works/pi-tui": "*"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@types/node": "^24.0.0",