@zhushanwen/pi-statusline 0.4.6 → 0.4.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-statusline",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "Pi statusline extension — shows context usage, token speed, and provider quota in the footer.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -22,7 +22,7 @@
22
22
  "index.ts"
23
23
  ],
24
24
  "dependencies": {
25
- "@zhushanwen/pi-quota-providers": "0.5.0"
25
+ "@zhushanwen/pi-quota-providers": "0.5.1"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@earendil-works/pi-ai": "*",
@@ -17,6 +17,7 @@ import {
17
17
  fmtDuration,
18
18
  fmtResetSec,
19
19
  fmtTokens,
20
+ formatCacheRatioPart,
20
21
  formatSpeedPart,
21
22
  formatWinCol,
22
23
  normalizeRows,
@@ -607,3 +608,27 @@ describe("formatSpeedPart", () => {
607
608
  expect(formatSpeedPart({ current: -1, day: -1 }, plainPallet)).toBe("");
608
609
  });
609
610
  });
611
+
612
+ // 8. formatCacheRatioPart — buildLine2 缓存命中率部分
613
+ // ════════════════════════════════════════════════════════
614
+
615
+ describe("formatCacheRatioPart", () => {
616
+ it("current + day 都有", () => {
617
+ const result = formatCacheRatioPart({ current: 85, day: 72 }, plainPallet);
618
+ expect(result).toBe("│ cache 85% · day 72%");
619
+ });
620
+
621
+ it("只有 current", () => {
622
+ const result = formatCacheRatioPart({ current: 50, day: null }, plainPallet);
623
+ expect(result).toBe("│ cache 50%");
624
+ });
625
+
626
+ it("只有 day", () => {
627
+ const result = formatCacheRatioPart({ current: null, day: 30 }, plainPallet);
628
+ expect(result).toBe("│ cache day 30%");
629
+ });
630
+
631
+ it("都为 null 时返回空串", () => {
632
+ expect(formatCacheRatioPart({ current: null, day: null }, plainPallet)).toBe("");
633
+ });
634
+ });
package/src/format.ts CHANGED
@@ -100,6 +100,21 @@ export function formatSpeedPart(sp: SpeedLike, p: PlainPallet): string {
100
100
  return parts.length ? `│ ${p.d("speed")} ${parts.join(" · ")}` : "";
101
101
  }
102
102
 
103
+ // ── 缓存命中率渲染 ─────────────────────────────────────
104
+
105
+ export interface CacheRatioLike {
106
+ current: number | null;
107
+ day: number | null;
108
+ }
109
+
110
+ /** 渲染缓存命中率部分:cache 85% · day 72%(无数据返回空串) */
111
+ export function formatCacheRatioPart(cr: CacheRatioLike, p: PlainPallet): string {
112
+ const parts: string[] = [];
113
+ if (cr.current !== null) parts.push(`${p.g(`${cr.current}`)}${p.d("%")}`);
114
+ if (cr.day !== null) parts.push(`${p.d("day")} ${p.g(`${cr.day}`)}${p.d("%")}`);
115
+ return parts.length ? `│ ${p.d("cache")} ${parts.join(" · ")}` : "";
116
+ }
117
+
103
118
  // ── 路径工具 ─────────────────────────────────────────
104
119
 
105
120
  /** 把路径切成段(按系统分隔符) */
package/src/index.ts CHANGED
@@ -19,8 +19,10 @@ import type { ExtensionAPI, ExtensionContext, ReadonlyFooterDataProvider, Theme
19
19
  import { truncateToWidth } from "@mariozechner/pi-tui";
20
20
  import {
21
21
  buildRuntimeProviders,
22
+ type CacheRatioData,
22
23
  readCache,
23
24
  type SpeedData,
25
+ trackCacheRatio,
24
26
  trackSpeed,
25
27
  triggerUpdate,
26
28
  } from "@zhushanwen/pi-quota-providers";
@@ -31,6 +33,7 @@ import {
31
33
  fmtCount,
32
34
  fmtDuration,
33
35
  fmtTokens,
36
+ formatCacheRatioPart,
34
37
  formatSpeedPart,
35
38
  MIN_PAD,
36
39
  MS_PER_SEC,
@@ -133,6 +136,7 @@ interface StatuslineRuntimeState {
133
136
  lastLlmTime: number;
134
137
  assistantStart: number;
135
138
  speed: SpeedData;
139
+ cacheRatio: CacheRatioData;
136
140
  lastRunUpdate: number;
137
141
  isAgentBusy: boolean;
138
142
  thinkingLevel: string;
@@ -142,6 +146,7 @@ interface StatuslineRuntimeState {
142
146
  usedPct: number;
143
147
  contextTokens: number;
144
148
  contextWindow: number;
149
+ sessionName: string | undefined;
145
150
  }
146
151
 
147
152
  function makeInitialState(): StatuslineRuntimeState {
@@ -150,6 +155,7 @@ function makeInitialState(): StatuslineRuntimeState {
150
155
  lastLlmTime: 0,
151
156
  assistantStart: 0,
152
157
  speed: { current: 0, day: 0, d7: 0, d30: 0 },
158
+ cacheRatio: { current: null, day: null },
153
159
  lastRunUpdate: 0,
154
160
  isAgentBusy: false,
155
161
  thinkingLevel: "",
@@ -159,6 +165,7 @@ function makeInitialState(): StatuslineRuntimeState {
159
165
  usedPct: 0,
160
166
  contextTokens: 0,
161
167
  contextWindow: 0,
168
+ sessionName: undefined,
162
169
  };
163
170
  }
164
171
 
@@ -177,6 +184,7 @@ function registerSessionLifecycle(pi: ExtensionAPI): void {
177
184
  Object.assign(state, makeInitialState(), {
178
185
  sessionStart: Date.now(),
179
186
  thinkingLevel: pi.getThinkingLevel(),
187
+ sessionName: ctx.sessionManager.getSessionName() ?? undefined,
180
188
  });
181
189
  refreshTotals(state, ctx);
182
190
  initFooter(ctx, state, tuiRef);
@@ -201,6 +209,10 @@ function registerSessionLifecycle(pi: ExtensionAPI): void {
201
209
  return;
202
210
  }
203
211
  state.speed = trackSpeed(msg.usage.output, dur, ctx.model?.id ?? "");
212
+ state.cacheRatio = trackCacheRatio(
213
+ { input: msg.usage.input, cacheRead: msg.usage.cacheRead ?? 0, cacheWrite: msg.usage.cacheWrite ?? 0 },
214
+ ctx.model?.id ?? "",
215
+ );
204
216
  state.totalInp += msg.usage.input;
205
217
  state.totalOut += msg.usage.output;
206
218
  state.totalCost += msg.usage.cost.total;
@@ -221,7 +233,10 @@ function registerSessionLifecycle(pi: ExtensionAPI): void {
221
233
  });
222
234
  pi.on("session_tree", async (_event: unknown, ctx: ExtensionContext) => {
223
235
  // 切换分支后重建状态栏数据
224
- Object.assign(state, makeInitialState(), { sessionStart: Date.now() });
236
+ Object.assign(state, makeInitialState(), {
237
+ sessionStart: Date.now(),
238
+ sessionName: ctx.sessionManager.getSessionName() ?? undefined,
239
+ });
225
240
  refreshTotals(state, ctx);
226
241
  triggerUpdate();
227
242
  });
@@ -312,7 +327,10 @@ function buildLine2(ctx: ExtensionContext, st: StatuslineRuntimeState, p: Pallet
312
327
  const speedPart = formatSpeedPart(st.speed, p);
313
328
  const speedPrefix = speedPart ? ` ${speedPart}` : "";
314
329
 
315
- return `${p.d(provider)}/${p.a(modelId)}${tlPart}${speedPrefix}`;
330
+ const cachePart = formatCacheRatioPart(st.cacheRatio, p);
331
+ const cachePrefix = cachePart ? ` ${cachePart}` : "";
332
+
333
+ return `${p.d(provider)}/${p.a(modelId)}${tlPart}${speedPrefix}${cachePrefix}`;
316
334
  }
317
335
 
318
336
  function buildLine3(
@@ -341,12 +359,14 @@ function buildLine3(
341
359
  }
342
360
 
343
361
  const sid = tailSessionId(ctx.sessionManager.getSessionFile(), SESSION_ID_TAIL);
362
+ const namePart = st.sessionName ? p.a(st.sessionName) : "";
344
363
 
345
364
  const parts: string[] = [ctxStr];
346
365
  if (tp.length) parts.push(tp.join(` ${DOT} `));
347
366
  if (st.totalInp > 0 || st.totalOut > 0) {
348
367
  parts.push(`${p.d("↑↓")} ${p.v(fmtCount(st.totalInp))}/${p.v(fmtCount(st.totalOut))}`);
349
368
  }
369
+ if (namePart) parts.push(namePart);
350
370
  if (sid) parts.push(p.m(sid));
351
371
  return parts.join(` ${SEP} `);
352
372
  }