@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 +2 -2
- package/src/__tests__/format.test.ts +25 -0
- package/src/format.ts +15 -0
- package/src/index.ts +22 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-statusline",
|
|
3
|
-
"version": "0.4.
|
|
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.
|
|
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(), {
|
|
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
|
-
|
|
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
|
}
|