@pi-vault/pi-status 0.2.0 → 0.2.1
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 +44 -0
- package/README.md +47 -29
- package/package.json +5 -4
- package/src/core/snapshot.ts +75 -0
- package/src/index.ts +94 -98
- package/src/tui/render.ts +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@pi-vault/pi-status` are documented in this file.
|
|
4
|
+
|
|
5
|
+
## 0.2.1 - 2026-06-14
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Updated the Pi host baseline to the `0.79.x` package line and refreshed the packaged dependency set.
|
|
10
|
+
- Reworked the README around install, reload, `/statusline`, footer segments, and `pi-usage` integration so the published docs match current behavior.
|
|
11
|
+
- Added this changelog to the published package contents.
|
|
12
|
+
|
|
13
|
+
### Internal
|
|
14
|
+
|
|
15
|
+
- Refactored internal snapshot and runtime-state code without changing the public behavior of the extension.
|
|
16
|
+
- Exported `formatSegment` with full test coverage to harden segment rendering behavior.
|
|
17
|
+
|
|
18
|
+
## 0.2.0 - 2026-06-07
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Screenshots for the live footer and interactive `/statusline` editor.
|
|
23
|
+
- A usage runtime that integrates with `@pi-vault/pi-usage` for live limit-backed footer segments.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Upgraded usage-backed segments to the `@pi-vault/pi-usage@0.2.x` line.
|
|
28
|
+
- Consolidated the TUI implementation and theme plumbing used by the footer preview and `/statusline`.
|
|
29
|
+
- Refreshed the README to cover the shipped UI and configuration flow.
|
|
30
|
+
|
|
31
|
+
## 0.1.0 - 2026-06-02
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- Initial release of the Pi status line extension.
|
|
36
|
+
- A footer that can replace Pi's default footer with configurable status segments.
|
|
37
|
+
- The `/statusline` interactive editor for enabling, disabling, reordering, and previewing segments.
|
|
38
|
+
- Settings persistence through Pi's `settings.json` with project and global loading behavior.
|
|
39
|
+
- Segment support for model, reasoning level, project name, working directory, Git branch, run state, context metrics, token counts, session ID, usage limits, and extension statuses.
|
|
40
|
+
- Filtering controls for visible extension statuses.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Iterated on the `/statusline` UI to use sectioned rows, search, inline rendering, live preview, theme adaptation, and footer suppression while editing.
|
package/README.md
CHANGED
|
@@ -5,58 +5,63 @@
|
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
Replace Pi's default footer with a
|
|
8
|
+
Replace Pi's default footer with a compact status line that shows the session details you actually care about. The extension installs a live footer and adds `/statusline`, an interactive editor for choosing, ordering, and previewing footer segments.
|
|
9
9
|
|
|
10
|
-
Default
|
|
10
|
+
Default footer:
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
```text
|
|
13
|
+
model-with-reasoning · current-dir
|
|
14
|
+
```
|
|
13
15
|
|
|
14
|
-
##
|
|
16
|
+
## Screenshots
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
Default status line rendering:
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
Interactive configuration editor (`/statusline`):
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
## Install And Reload
|
|
27
|
+
|
|
28
|
+
Install the extension:
|
|
17
29
|
|
|
18
30
|
```bash
|
|
19
31
|
pi install npm:@pi-vault/pi-status
|
|
20
32
|
```
|
|
21
33
|
|
|
22
|
-
Optional: install `pi-usage` if you want
|
|
34
|
+
Optional: install `pi-usage` if you want the `five-hour-limit` and `weekly-limit` footer segments:
|
|
23
35
|
|
|
24
36
|
```bash
|
|
25
37
|
pi install npm:@pi-vault/pi-usage
|
|
26
38
|
```
|
|
27
39
|
|
|
28
|
-
|
|
40
|
+
Reload Pi after installing or upgrading:
|
|
29
41
|
|
|
30
42
|
```bash
|
|
31
43
|
/reload
|
|
32
44
|
```
|
|
33
45
|
|
|
34
|
-
##
|
|
46
|
+
## Use `/statusline`
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
Once installed, the footer updates automatically. Run `/statusline` inside Pi to open the interactive editor.
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Interactive configuration editor (`/statusline`):
|
|
41
|
-
|
|
42
|
-

|
|
43
|
-
|
|
44
|
-
## Use
|
|
45
|
-
|
|
46
|
-
Once installed, the footer updates automatically.
|
|
47
|
-
|
|
48
|
-
Use `/statusline` inside Pi to:
|
|
50
|
+
The editor lets you:
|
|
49
51
|
|
|
50
52
|
- turn footer items on or off
|
|
51
|
-
- reorder
|
|
53
|
+
- reorder enabled items with `Left` and `Right`
|
|
54
|
+
- search the segment list
|
|
52
55
|
- preview the result before saving
|
|
53
56
|
- control which extension status messages are shown
|
|
54
57
|
|
|
55
58
|
Changes are saved and reused the next time Pi starts.
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
During editing, the live footer is temporarily hidden so the inline UI can use the full width cleanly.
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
## Available Footer Items
|
|
63
|
+
|
|
64
|
+
You can compose the footer from these segment IDs:
|
|
60
65
|
|
|
61
66
|
- `model`
|
|
62
67
|
- `model-with-reasoning`
|
|
@@ -75,9 +80,11 @@ You can build your footer from these items:
|
|
|
75
80
|
- `weekly-limit`
|
|
76
81
|
- `extension-statuses`
|
|
77
82
|
|
|
78
|
-
`five-hour-limit` and `weekly-limit`
|
|
83
|
+
`five-hour-limit` and `weekly-limit` depend on standalone [`@pi-vault/pi-usage`](https://www.npmjs.com/package/@pi-vault/pi-usage). When `pi-usage` is not installed or has not responded yet, those segments are hidden from `/statusline` and omitted from the footer.
|
|
84
|
+
|
|
85
|
+
`extension-statuses` renders the visible extension status values reported by Pi extensions. `/statusline` also lets you hide individual status keys or switch to an allowlist.
|
|
79
86
|
|
|
80
|
-
## Common
|
|
87
|
+
## Common Examples
|
|
81
88
|
|
|
82
89
|
Keep it minimal:
|
|
83
90
|
|
|
@@ -91,19 +98,30 @@ Show more session detail:
|
|
|
91
98
|
model · run-state · git-branch · context-used · context-remaining · session-id
|
|
92
99
|
```
|
|
93
100
|
|
|
101
|
+
Usage-aware footer:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
model-with-reasoning · current-dir · five-hour-limit · weekly-limit
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Show extension activity too:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
model · current-dir · extension-statuses
|
|
111
|
+
```
|
|
112
|
+
|
|
94
113
|
## Compatibility
|
|
95
114
|
|
|
96
115
|
- Node.js `>=22.19`
|
|
97
116
|
- Pi host environment with `@earendil-works/pi-coding-agent` and `@earendil-works/pi-tui`
|
|
98
|
-
- Tested in this repo against `@earendil-works/pi-coding-agent@0.
|
|
117
|
+
- Tested in this repo against `@earendil-works/pi-coding-agent@0.79.3` and `@earendil-works/pi-tui@0.79.3`
|
|
99
118
|
|
|
100
|
-
## Development
|
|
119
|
+
## Development
|
|
101
120
|
|
|
102
121
|
```bash
|
|
103
122
|
pnpm install
|
|
104
123
|
pnpm check
|
|
105
|
-
pnpm pack
|
|
106
|
-
pi -e .
|
|
124
|
+
pnpm run pack:dry-run
|
|
107
125
|
```
|
|
108
126
|
|
|
109
127
|
## License
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-vault/pi-status",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pi extension that replaces the default status with a Codex-like status",
|
|
6
6
|
"author": "Lanh Hoang <lanhhoang@users.noreply.github.com>",
|
|
@@ -38,10 +38,11 @@
|
|
|
38
38
|
"files": [
|
|
39
39
|
"src",
|
|
40
40
|
"docs/assets",
|
|
41
|
+
"CHANGELOG.md",
|
|
41
42
|
"README.md"
|
|
42
43
|
],
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@pi-vault/pi-usage": "^0.
|
|
45
|
+
"@pi-vault/pi-usage": "^0.4.0"
|
|
45
46
|
},
|
|
46
47
|
"peerDependencies": {
|
|
47
48
|
"@earendil-works/pi-coding-agent": "*",
|
|
@@ -49,8 +50,8 @@
|
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@biomejs/biome": "^2.4.16",
|
|
52
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
53
|
-
"@earendil-works/pi-tui": "^0.
|
|
53
|
+
"@earendil-works/pi-coding-agent": "^0.79.3",
|
|
54
|
+
"@earendil-works/pi-tui": "^0.79.3",
|
|
54
55
|
"@types/node": "^25.9.1",
|
|
55
56
|
"typescript": "^6.0.3",
|
|
56
57
|
"vitest": "^4.1.7"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { FooterRenderInput, ModelLike, RunState } from "../tui/render.ts";
|
|
2
|
+
|
|
3
|
+
export type SnapshotInput = {
|
|
4
|
+
model?: ModelLike;
|
|
5
|
+
cwd: string;
|
|
6
|
+
thinkingLevel: string;
|
|
7
|
+
gitBranch: string | null;
|
|
8
|
+
isIdle: boolean;
|
|
9
|
+
hasPendingMessages: boolean;
|
|
10
|
+
contextUsage?: {
|
|
11
|
+
tokens?: number | null;
|
|
12
|
+
contextWindow?: number;
|
|
13
|
+
percent?: number | null;
|
|
14
|
+
};
|
|
15
|
+
branch: unknown[];
|
|
16
|
+
sessionId: string;
|
|
17
|
+
usageState?: FooterRenderInput["usageState"];
|
|
18
|
+
extensionStatuses: ReadonlyMap<string, string>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function aggregateBranchTotals(branch: unknown[]): {
|
|
22
|
+
input: number;
|
|
23
|
+
output: number;
|
|
24
|
+
totalTokens: number;
|
|
25
|
+
} {
|
|
26
|
+
const totals = { input: 0, output: 0, totalTokens: 0 };
|
|
27
|
+
|
|
28
|
+
for (const entry of branch ?? []) {
|
|
29
|
+
if (!entry || typeof entry !== "object") continue;
|
|
30
|
+
if ((entry as { type?: unknown }).type !== "message") continue;
|
|
31
|
+
const message = (
|
|
32
|
+
entry as {
|
|
33
|
+
message?: {
|
|
34
|
+
role?: unknown;
|
|
35
|
+
usage?: { input?: number; output?: number; totalTokens?: number };
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
).message;
|
|
39
|
+
if (message?.role !== "assistant") continue;
|
|
40
|
+
const usage = message.usage;
|
|
41
|
+
if (!usage) continue;
|
|
42
|
+
if (typeof usage.input === "number") totals.input += usage.input;
|
|
43
|
+
if (typeof usage.output === "number") totals.output += usage.output;
|
|
44
|
+
if (typeof usage.totalTokens === "number")
|
|
45
|
+
totals.totalTokens += usage.totalTokens;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return totals;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function deriveRunState(
|
|
52
|
+
isIdle: boolean,
|
|
53
|
+
hasPendingMessages: boolean,
|
|
54
|
+
): RunState {
|
|
55
|
+
if (!isIdle) return "busy";
|
|
56
|
+
if (hasPendingMessages) return "queued";
|
|
57
|
+
return "idle";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function buildSnapshot(
|
|
61
|
+
input: SnapshotInput,
|
|
62
|
+
): Omit<FooterRenderInput, "segments" | "filter"> {
|
|
63
|
+
return {
|
|
64
|
+
model: input.model,
|
|
65
|
+
cwd: input.cwd,
|
|
66
|
+
thinkingLevel: input.thinkingLevel,
|
|
67
|
+
gitBranch: input.gitBranch,
|
|
68
|
+
runState: deriveRunState(input.isIdle, input.hasPendingMessages),
|
|
69
|
+
contextUsage: input.contextUsage,
|
|
70
|
+
branchTotals: aggregateBranchTotals(input.branch),
|
|
71
|
+
sessionId: input.sessionId,
|
|
72
|
+
usageState: input.usageState,
|
|
73
|
+
extensionStatuses: input.extensionStatuses,
|
|
74
|
+
};
|
|
75
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,10 +2,8 @@ import type {
|
|
|
2
2
|
ExtensionAPI,
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
} from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
saveConfigToSettings,
|
|
8
|
-
} from "./core/config.ts";
|
|
5
|
+
import { loadConfig, saveConfigToSettings } from "./core/config.ts";
|
|
6
|
+
import { buildSnapshot } from "./core/snapshot.ts";
|
|
9
7
|
import { createUsageRuntime } from "./core/usage-runtime.ts";
|
|
10
8
|
import type { PiStatusConfig } from "./shared/types.ts";
|
|
11
9
|
import { createStatusLineEditor } from "./tui/editor.ts";
|
|
@@ -30,6 +28,24 @@ type FooterFactory = (
|
|
|
30
28
|
footerData: FooterDataLike,
|
|
31
29
|
) => FooterComponent;
|
|
32
30
|
|
|
31
|
+
type RuntimeState = {
|
|
32
|
+
config: PiStatusConfig;
|
|
33
|
+
ctx: ExtensionContext | undefined;
|
|
34
|
+
requestRender: (() => void) | undefined;
|
|
35
|
+
gitBranch: string | null;
|
|
36
|
+
extensionStatuses: Map<string, string>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function createRuntimeState(): RuntimeState {
|
|
40
|
+
return {
|
|
41
|
+
config: loadConfig().config,
|
|
42
|
+
ctx: undefined,
|
|
43
|
+
requestRender: undefined,
|
|
44
|
+
gitBranch: null,
|
|
45
|
+
extensionStatuses: new Map(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
const EMPTY_FOOTER_FACTORY: FooterFactory = () => ({
|
|
34
50
|
render(): string[] {
|
|
35
51
|
return [];
|
|
@@ -41,91 +57,64 @@ const EMPTY_FOOTER_FACTORY: FooterFactory = () => ({
|
|
|
41
57
|
function isLiveTheme(value: unknown): boolean {
|
|
42
58
|
if (!value || typeof value !== "object") return false;
|
|
43
59
|
const candidate = value as { fg?: unknown; bold?: unknown };
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
function aggregateBranchTotals(ctx: ExtensionContext): {
|
|
48
|
-
input: number;
|
|
49
|
-
output: number;
|
|
50
|
-
totalTokens: number;
|
|
51
|
-
} {
|
|
52
|
-
const totals = { input: 0, output: 0, totalTokens: 0 };
|
|
53
|
-
const branch = ctx.sessionManager.getBranch() as unknown[];
|
|
54
|
-
|
|
55
|
-
for (const entry of branch ?? []) {
|
|
56
|
-
if (!entry || typeof entry !== "object") continue;
|
|
57
|
-
if ((entry as { type?: unknown }).type !== "message") continue;
|
|
58
|
-
const message = (
|
|
59
|
-
entry as {
|
|
60
|
-
message?: {
|
|
61
|
-
role?: unknown;
|
|
62
|
-
usage?: { input?: number; output?: number; totalTokens?: number };
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
).message;
|
|
66
|
-
if (message?.role !== "assistant") continue;
|
|
67
|
-
const usage = message.usage;
|
|
68
|
-
if (!usage) continue;
|
|
69
|
-
if (typeof usage.input === "number") totals.input += usage.input;
|
|
70
|
-
if (typeof usage.output === "number") totals.output += usage.output;
|
|
71
|
-
if (typeof usage.totalTokens === "number") totals.totalTokens += usage.totalTokens;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return totals;
|
|
60
|
+
return (
|
|
61
|
+
typeof candidate.fg === "function" && typeof candidate.bold === "function"
|
|
62
|
+
);
|
|
75
63
|
}
|
|
76
64
|
|
|
77
65
|
export default function createExtension(pi: ExtensionAPI): void {
|
|
78
|
-
|
|
79
|
-
let currentCtx: ExtensionContext | undefined;
|
|
80
|
-
let requestRender: (() => void) | undefined;
|
|
81
|
-
let lastGitBranch: string | null = null;
|
|
82
|
-
let lastExtensionStatuses = new Map<string, string>();
|
|
66
|
+
const state = createRuntimeState();
|
|
83
67
|
|
|
84
68
|
const usageRuntime = createUsageRuntime(pi);
|
|
85
69
|
|
|
86
70
|
function refreshRuntimeConfig(cwd?: string): void {
|
|
87
|
-
|
|
71
|
+
state.config = loadConfig(cwd ? { cwd } : undefined).config;
|
|
88
72
|
}
|
|
89
73
|
|
|
90
74
|
function installFooter(ctx: ExtensionContext): void {
|
|
91
75
|
if (!ctx.hasUI) return;
|
|
92
76
|
|
|
93
77
|
const factory: FooterFactory = (tui, theme, footerData) => {
|
|
94
|
-
requestRender = () => tui.requestRender?.();
|
|
95
|
-
usageRuntime.setOnChange(requestRender);
|
|
96
|
-
const unsubscribe = footerData.onBranchChange?.(() =>
|
|
78
|
+
state.requestRender = () => tui.requestRender?.();
|
|
79
|
+
usageRuntime.setOnChange(state.requestRender);
|
|
80
|
+
const unsubscribe = footerData.onBranchChange?.(() =>
|
|
81
|
+
tui.requestRender?.(),
|
|
82
|
+
);
|
|
97
83
|
|
|
98
84
|
return {
|
|
99
85
|
dispose() {
|
|
100
86
|
unsubscribe?.();
|
|
101
|
-
if (requestRender === tui.requestRender)
|
|
102
|
-
|
|
87
|
+
if (state.requestRender === tui.requestRender)
|
|
88
|
+
state.requestRender = undefined;
|
|
89
|
+
usageRuntime.setOnChange(state.requestRender);
|
|
103
90
|
},
|
|
104
91
|
invalidate() {
|
|
105
|
-
requestRender?.();
|
|
92
|
+
state.requestRender?.();
|
|
106
93
|
},
|
|
107
94
|
render(width: number) {
|
|
108
|
-
const activeCtx =
|
|
109
|
-
|
|
110
|
-
|
|
95
|
+
const activeCtx = state.ctx ?? ctx;
|
|
96
|
+
state.gitBranch = footerData.getGitBranch();
|
|
97
|
+
state.extensionStatuses = new Map(
|
|
98
|
+
footerData.getExtensionStatuses().entries(),
|
|
99
|
+
);
|
|
100
|
+
const snapshot = buildSnapshot({
|
|
101
|
+
model: activeCtx.model,
|
|
102
|
+
cwd: activeCtx.cwd,
|
|
103
|
+
thinkingLevel: String(pi.getThinkingLevel()),
|
|
104
|
+
gitBranch: state.gitBranch,
|
|
105
|
+
isIdle: activeCtx.isIdle(),
|
|
106
|
+
hasPendingMessages: activeCtx.hasPendingMessages(),
|
|
107
|
+
contextUsage: activeCtx.getContextUsage(),
|
|
108
|
+
branch: activeCtx.sessionManager.getBranch() as unknown[],
|
|
109
|
+
sessionId: activeCtx.sessionManager.getSessionId(),
|
|
110
|
+
usageState: usageRuntime.getState(),
|
|
111
|
+
extensionStatuses: state.extensionStatuses,
|
|
112
|
+
});
|
|
111
113
|
const line = buildFooterLine(
|
|
112
114
|
{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
gitBranch: lastGitBranch,
|
|
117
|
-
runState: !activeCtx.isIdle()
|
|
118
|
-
? "busy"
|
|
119
|
-
: activeCtx.hasPendingMessages()
|
|
120
|
-
? "queued"
|
|
121
|
-
: "idle",
|
|
122
|
-
contextUsage: activeCtx.getContextUsage(),
|
|
123
|
-
branchTotals: aggregateBranchTotals(activeCtx),
|
|
124
|
-
sessionId: activeCtx.sessionManager.getSessionId(),
|
|
125
|
-
usageState: usageRuntime.getState(),
|
|
126
|
-
extensionStatuses: lastExtensionStatuses,
|
|
127
|
-
filter: runtimeConfig.filter,
|
|
128
|
-
segments: runtimeConfig.segments,
|
|
115
|
+
...snapshot,
|
|
116
|
+
filter: state.config.filter,
|
|
117
|
+
segments: state.config.segments,
|
|
129
118
|
},
|
|
130
119
|
theme,
|
|
131
120
|
width,
|
|
@@ -143,9 +132,9 @@ export default function createExtension(pi: ExtensionAPI): void {
|
|
|
143
132
|
}
|
|
144
133
|
|
|
145
134
|
function refresh(ctx: ExtensionContext): void {
|
|
146
|
-
|
|
135
|
+
state.ctx = ctx;
|
|
147
136
|
refreshRuntimeConfig(ctx.cwd);
|
|
148
|
-
requestRender?.();
|
|
137
|
+
state.requestRender?.();
|
|
149
138
|
}
|
|
150
139
|
|
|
151
140
|
pi.registerCommand("statusline", {
|
|
@@ -156,39 +145,43 @@ export default function createExtension(pi: ExtensionAPI): void {
|
|
|
156
145
|
return;
|
|
157
146
|
}
|
|
158
147
|
|
|
159
|
-
const discovered = [...
|
|
148
|
+
const discovered = [...state.extensionStatuses.keys()].sort((a, b) =>
|
|
149
|
+
a.localeCompare(b),
|
|
150
|
+
);
|
|
160
151
|
|
|
161
152
|
let result: PiStatusConfig | null = null;
|
|
162
153
|
try {
|
|
163
154
|
installEmptyFooter(ctx);
|
|
164
|
-
result = await ctx.ui.custom<PiStatusConfig | null>(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
155
|
+
result = await ctx.ui.custom<PiStatusConfig | null>(
|
|
156
|
+
(tui, theme, _keys, done) => {
|
|
157
|
+
const activeCtx = state.ctx ?? ctx;
|
|
158
|
+
const menuTheme: StatusLineTheme = isLiveTheme(theme)
|
|
159
|
+
? fromPiTheme(theme)
|
|
160
|
+
: noTheme;
|
|
161
|
+
const snapshot = buildSnapshot({
|
|
171
162
|
model: activeCtx.model,
|
|
172
163
|
cwd: activeCtx.cwd,
|
|
173
164
|
thinkingLevel: String(pi.getThinkingLevel()),
|
|
174
|
-
gitBranch:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
: activeCtx.hasPendingMessages()
|
|
178
|
-
? "queued"
|
|
179
|
-
: "idle",
|
|
165
|
+
gitBranch: state.gitBranch,
|
|
166
|
+
isIdle: activeCtx.isIdle(),
|
|
167
|
+
hasPendingMessages: activeCtx.hasPendingMessages(),
|
|
180
168
|
contextUsage: activeCtx.getContextUsage(),
|
|
181
|
-
|
|
169
|
+
branch: activeCtx.sessionManager.getBranch() as unknown[],
|
|
182
170
|
sessionId: activeCtx.sessionManager.getSessionId(),
|
|
183
171
|
usageState: usageRuntime.getState(),
|
|
184
|
-
extensionStatuses:
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
172
|
+
extensionStatuses: state.extensionStatuses,
|
|
173
|
+
});
|
|
174
|
+
return createStatusLineEditor({
|
|
175
|
+
config: state.config,
|
|
176
|
+
discoveredStatuses: discovered,
|
|
177
|
+
previewInput: snapshot,
|
|
178
|
+
theme: menuTheme,
|
|
179
|
+
done,
|
|
180
|
+
requestRender: () => tui.requestRender?.(),
|
|
181
|
+
usageAvailable: usageRuntime.getAvailable(),
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
);
|
|
192
185
|
} finally {
|
|
193
186
|
installFooter(ctx);
|
|
194
187
|
}
|
|
@@ -197,10 +190,13 @@ export default function createExtension(pi: ExtensionAPI): void {
|
|
|
197
190
|
|
|
198
191
|
try {
|
|
199
192
|
saveConfigToSettings(result, { cwd: ctx.cwd });
|
|
200
|
-
|
|
201
|
-
requestRender?.();
|
|
193
|
+
state.config = result;
|
|
194
|
+
state.requestRender?.();
|
|
202
195
|
} catch (error) {
|
|
203
|
-
const message =
|
|
196
|
+
const message =
|
|
197
|
+
error instanceof Error
|
|
198
|
+
? error.message
|
|
199
|
+
: "Failed to save statusline settings";
|
|
204
200
|
ctx.ui.notify(message, "warning");
|
|
205
201
|
}
|
|
206
202
|
},
|
|
@@ -209,13 +205,13 @@ export default function createExtension(pi: ExtensionAPI): void {
|
|
|
209
205
|
pi.on("session_start", (_event, ctx) => {
|
|
210
206
|
usageRuntime.requestCurrent();
|
|
211
207
|
refreshRuntimeConfig(ctx.cwd);
|
|
212
|
-
|
|
208
|
+
state.ctx = ctx;
|
|
213
209
|
installFooter(ctx);
|
|
214
210
|
});
|
|
215
211
|
|
|
216
212
|
pi.on("session_tree", (_event, ctx) => {
|
|
217
213
|
refreshRuntimeConfig(ctx.cwd);
|
|
218
|
-
|
|
214
|
+
state.ctx = ctx;
|
|
219
215
|
installFooter(ctx);
|
|
220
216
|
});
|
|
221
217
|
|
|
@@ -228,8 +224,8 @@ export default function createExtension(pi: ExtensionAPI): void {
|
|
|
228
224
|
});
|
|
229
225
|
|
|
230
226
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
231
|
-
|
|
232
|
-
requestRender = undefined;
|
|
227
|
+
state.ctx = undefined;
|
|
228
|
+
state.requestRender = undefined;
|
|
233
229
|
usageRuntime.setOnChange(undefined);
|
|
234
230
|
if (ctx.hasUI) ctx.ui.setFooter(undefined);
|
|
235
231
|
});
|
package/src/tui/render.ts
CHANGED