pi-open-tui 0.2.9 → 0.2.11
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 +105 -99
- package/extensions/open-tui/config.ts +141 -129
- package/extensions/open-tui/editor.ts +100 -100
- package/extensions/open-tui/footer.ts +67 -20
- package/extensions/open-tui/git.ts +5 -2
- package/extensions/open-tui/icons.ts +4 -1
- package/extensions/open-tui/index.ts +286 -273
- package/extensions/open-tui/settings-command.ts +361 -289
- package/extensions/open-tui/state.ts +90 -90
- package/extensions/open-tui/utils.ts +38 -6
- package/package.json +66 -66
|
@@ -1,273 +1,286 @@
|
|
|
1
|
-
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { type OpenTuiConfig, DEFAULT_CONFIG, ensureConfigExists, loadConfig, saveConfig } from "./config.ts";
|
|
3
|
-
import { installEditor } from "./editor.ts";
|
|
4
|
-
import { installFooter } from "./footer.ts";
|
|
5
|
-
import { installHeader } from "./header.ts";
|
|
6
|
-
import { readGitStatus } from "./git.ts";
|
|
7
|
-
import { readRuntimeInfo } from "./runtime.ts";
|
|
8
|
-
import { SessionLifecycle } from "./session-lifecycle.ts";
|
|
9
|
-
import { registerSettingsCommand } from "./settings-command.ts";
|
|
10
|
-
import { formatTurnTelemetry, TurnTelemetryTracker } from "./telemetry.ts";
|
|
11
|
-
import {
|
|
12
|
-
createInitialState,
|
|
13
|
-
getModelMeta,
|
|
14
|
-
invalidateUsageCache,
|
|
15
|
-
type FooterState,
|
|
16
|
-
} from "./state.ts";
|
|
17
|
-
|
|
18
|
-
function isInteractiveLaunch(): boolean {
|
|
19
|
-
if (!process.stdout.isTTY) return false;
|
|
20
|
-
const args = process.argv.slice(2);
|
|
21
|
-
const nonInteractiveFlags = ["-p", "--print", "--help", "-h", "--version", "-v", "--list-models", "--export"];
|
|
22
|
-
for (const arg of args) {
|
|
23
|
-
if (nonInteractiveFlags.includes(arg)) return false;
|
|
24
|
-
if (arg.startsWith("--mode")) return false;
|
|
25
|
-
}
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function clearVisibleScreen(): void {
|
|
30
|
-
if (process.stdout.isTTY) {
|
|
31
|
-
process.stdout.write("\x1b[2J\x1b[H");
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function isTuiContext(ctx: ExtensionContext): boolean {
|
|
36
|
-
try {
|
|
37
|
-
const mode = (ctx as ExtensionContext & { mode?: string }).mode;
|
|
38
|
-
return ctx.hasUI && (mode === undefined || mode === "tui");
|
|
39
|
-
} catch {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export default function (pi: ExtensionAPI) {
|
|
45
|
-
const sessionLifecycle = new SessionLifecycle();
|
|
46
|
-
const state: FooterState = createInitialState();
|
|
47
|
-
const turnTelemetry = new TurnTelemetryTracker();
|
|
48
|
-
|
|
49
|
-
let config: OpenTuiConfig = structuredClone(DEFAULT_CONFIG);
|
|
50
|
-
let active = false;
|
|
51
|
-
let lastCtx: ExtensionContext | undefined;
|
|
52
|
-
let requestFooterRender: (() => void) | undefined;
|
|
53
|
-
let workingTimer: ReturnType<typeof setInterval> | undefined;
|
|
54
|
-
let cleanupHeader: (() => void) | undefined;
|
|
55
|
-
let cleanupFooter: (() => void) | undefined;
|
|
56
|
-
let cleanupEditor: (() => void) | undefined;
|
|
57
|
-
|
|
58
|
-
const getThinkingLevel = () => (sessionLifecycle.isCurrent() ? pi.getThinkingLevel() : "off");
|
|
59
|
-
|
|
60
|
-
const applyUi = (ctx: ExtensionContext) => {
|
|
61
|
-
if (!isTuiContext(ctx)) return;
|
|
62
|
-
if (!config.enabled) {
|
|
63
|
-
uninstallUi(ctx);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (!active) {
|
|
67
|
-
cleanupHeader = installHeader(pi, ctx);
|
|
68
|
-
cleanupFooter = installFooter(
|
|
69
|
-
ctx,
|
|
70
|
-
() => state,
|
|
71
|
-
() => config,
|
|
72
|
-
() => getModelMeta(ctx, getThinkingLevel),
|
|
73
|
-
{
|
|
74
|
-
setRequestRender: (fn) => {
|
|
75
|
-
requestFooterRender = fn ?? undefined;
|
|
76
|
-
},
|
|
77
|
-
scheduleGitRefresh: () => {
|
|
78
|
-
void scheduleGitRefresh(ctx);
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
);
|
|
82
|
-
cleanupEditor = installEditor(pi, ctx);
|
|
83
|
-
active = true;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const uninstallUi = (ctx: ExtensionContext) => {
|
|
88
|
-
if (!isTuiContext(ctx)) return;
|
|
89
|
-
if (active) {
|
|
90
|
-
cleanupHeader?.();
|
|
91
|
-
cleanupFooter?.();
|
|
92
|
-
cleanupEditor?.();
|
|
93
|
-
cleanupHeader = undefined;
|
|
94
|
-
cleanupFooter = undefined;
|
|
95
|
-
cleanupEditor = undefined;
|
|
96
|
-
requestFooterRender = undefined;
|
|
97
|
-
active = false;
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const scheduleGitRefresh = async (ctx: ExtensionContext) => {
|
|
102
|
-
if (!sessionLifecycle.isCurrent()) return;
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (!sessionLifecycle.isCurrent()) return;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
refreshInteractiveState(ctx);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
pi.on("
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type OpenTuiConfig, DEFAULT_CONFIG, ensureConfigExists, loadConfig, saveConfig } from "./config.ts";
|
|
3
|
+
import { installEditor } from "./editor.ts";
|
|
4
|
+
import { installFooter } from "./footer.ts";
|
|
5
|
+
import { installHeader } from "./header.ts";
|
|
6
|
+
import { emptyGitStatus, readGitStatus } from "./git.ts";
|
|
7
|
+
import { readRuntimeInfo } from "./runtime.ts";
|
|
8
|
+
import { SessionLifecycle } from "./session-lifecycle.ts";
|
|
9
|
+
import { registerSettingsCommand } from "./settings-command.ts";
|
|
10
|
+
import { formatTurnTelemetry, TurnTelemetryTracker } from "./telemetry.ts";
|
|
11
|
+
import {
|
|
12
|
+
createInitialState,
|
|
13
|
+
getModelMeta,
|
|
14
|
+
invalidateUsageCache,
|
|
15
|
+
type FooterState,
|
|
16
|
+
} from "./state.ts";
|
|
17
|
+
|
|
18
|
+
function isInteractiveLaunch(): boolean {
|
|
19
|
+
if (!process.stdout.isTTY) return false;
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
const nonInteractiveFlags = ["-p", "--print", "--help", "-h", "--version", "-v", "--list-models", "--export"];
|
|
22
|
+
for (const arg of args) {
|
|
23
|
+
if (nonInteractiveFlags.includes(arg)) return false;
|
|
24
|
+
if (arg.startsWith("--mode")) return false;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function clearVisibleScreen(): void {
|
|
30
|
+
if (process.stdout.isTTY) {
|
|
31
|
+
process.stdout.write("\x1b[2J\x1b[H");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isTuiContext(ctx: ExtensionContext): boolean {
|
|
36
|
+
try {
|
|
37
|
+
const mode = (ctx as ExtensionContext & { mode?: string }).mode;
|
|
38
|
+
return ctx.hasUI && (mode === undefined || mode === "tui");
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default function (pi: ExtensionAPI) {
|
|
45
|
+
const sessionLifecycle = new SessionLifecycle();
|
|
46
|
+
const state: FooterState = createInitialState();
|
|
47
|
+
const turnTelemetry = new TurnTelemetryTracker();
|
|
48
|
+
|
|
49
|
+
let config: OpenTuiConfig = structuredClone(DEFAULT_CONFIG);
|
|
50
|
+
let active = false;
|
|
51
|
+
let lastCtx: ExtensionContext | undefined;
|
|
52
|
+
let requestFooterRender: (() => void) | undefined;
|
|
53
|
+
let workingTimer: ReturnType<typeof setInterval> | undefined;
|
|
54
|
+
let cleanupHeader: (() => void) | undefined;
|
|
55
|
+
let cleanupFooter: (() => void) | undefined;
|
|
56
|
+
let cleanupEditor: (() => void) | undefined;
|
|
57
|
+
|
|
58
|
+
const getThinkingLevel = () => (sessionLifecycle.isCurrent() ? pi.getThinkingLevel() : "off");
|
|
59
|
+
|
|
60
|
+
const applyUi = (ctx: ExtensionContext) => {
|
|
61
|
+
if (!isTuiContext(ctx)) return;
|
|
62
|
+
if (!config.enabled) {
|
|
63
|
+
uninstallUi(ctx);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!active) {
|
|
67
|
+
cleanupHeader = installHeader(pi, ctx);
|
|
68
|
+
cleanupFooter = installFooter(
|
|
69
|
+
ctx,
|
|
70
|
+
() => state,
|
|
71
|
+
() => config,
|
|
72
|
+
() => getModelMeta(ctx, getThinkingLevel),
|
|
73
|
+
{
|
|
74
|
+
setRequestRender: (fn) => {
|
|
75
|
+
requestFooterRender = fn ?? undefined;
|
|
76
|
+
},
|
|
77
|
+
scheduleGitRefresh: () => {
|
|
78
|
+
void scheduleGitRefresh(ctx);
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
cleanupEditor = installEditor(pi, ctx);
|
|
83
|
+
active = true;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const uninstallUi = (ctx: ExtensionContext) => {
|
|
88
|
+
if (!isTuiContext(ctx)) return;
|
|
89
|
+
if (active) {
|
|
90
|
+
cleanupHeader?.();
|
|
91
|
+
cleanupFooter?.();
|
|
92
|
+
cleanupEditor?.();
|
|
93
|
+
cleanupHeader = undefined;
|
|
94
|
+
cleanupFooter = undefined;
|
|
95
|
+
cleanupEditor = undefined;
|
|
96
|
+
requestFooterRender = undefined;
|
|
97
|
+
active = false;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const scheduleGitRefresh = async (ctx: ExtensionContext) => {
|
|
102
|
+
if (!sessionLifecycle.isCurrent()) return;
|
|
103
|
+
const segs = config.footerSegments;
|
|
104
|
+
if (!segs.gitBranch && !segs.gitStatus && !segs.gitCommit) {
|
|
105
|
+
state.git = emptyGitStatus();
|
|
106
|
+
requestFooterRender?.();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const generation = sessionLifecycle.currentGeneration();
|
|
110
|
+
const cwd = ctx.cwd;
|
|
111
|
+
const git = await readGitStatus(cwd, {
|
|
112
|
+
readCommit: true,
|
|
113
|
+
readTag: segs.gitCommit,
|
|
114
|
+
readCounts: segs.gitStatus,
|
|
115
|
+
});
|
|
116
|
+
if (!sessionLifecycle.isCurrent(generation)) return;
|
|
117
|
+
state.git = git;
|
|
118
|
+
requestFooterRender?.();
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const refreshRuntime = async (ctx: ExtensionContext) => {
|
|
122
|
+
if (!sessionLifecycle.isCurrent()) return;
|
|
123
|
+
const generation = sessionLifecycle.currentGeneration();
|
|
124
|
+
const cwd = ctx.cwd;
|
|
125
|
+
const runtime = await readRuntimeInfo(cwd);
|
|
126
|
+
if (!sessionLifecycle.isCurrent(generation)) return;
|
|
127
|
+
state.runtime = runtime;
|
|
128
|
+
requestFooterRender?.();
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const refreshInteractiveState = (ctx: ExtensionContext, project = false) => {
|
|
132
|
+
if (!sessionLifecycle.isCurrent() || !ctx.hasUI) return;
|
|
133
|
+
if (project) {
|
|
134
|
+
void scheduleGitRefresh(ctx);
|
|
135
|
+
void refreshRuntime(ctx);
|
|
136
|
+
}
|
|
137
|
+
requestFooterRender?.();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const startWorkingTimer = () => {
|
|
141
|
+
stopWorkingTimer();
|
|
142
|
+
const tick = () => {
|
|
143
|
+
if (!sessionLifecycle.isCurrent() || !active) return;
|
|
144
|
+
requestFooterRender?.();
|
|
145
|
+
};
|
|
146
|
+
tick();
|
|
147
|
+
workingTimer = setInterval(tick, 250);
|
|
148
|
+
workingTimer.unref?.();
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const stopWorkingTimer = () => {
|
|
152
|
+
if (workingTimer) {
|
|
153
|
+
clearInterval(workingTimer);
|
|
154
|
+
workingTimer = undefined;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
159
|
+
sessionLifecycle.start();
|
|
160
|
+
lastCtx = ctx;
|
|
161
|
+
state.sessionStartEpoch = Date.now();
|
|
162
|
+
state.workingSince = undefined;
|
|
163
|
+
state.lastDoneIn = undefined;
|
|
164
|
+
invalidateUsageCache();
|
|
165
|
+
|
|
166
|
+
ensureConfigExists();
|
|
167
|
+
config = loadConfig((msg, level) => ctx.ui.notify(msg, level));
|
|
168
|
+
|
|
169
|
+
if (isInteractiveLaunch() && config.enabled) {
|
|
170
|
+
clearVisibleScreen();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
applyUi(ctx);
|
|
174
|
+
|
|
175
|
+
refreshInteractiveState(ctx, true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
179
|
+
sessionLifecycle.shutdown();
|
|
180
|
+
stopWorkingTimer();
|
|
181
|
+
if (active) {
|
|
182
|
+
uninstallUi(ctx);
|
|
183
|
+
}
|
|
184
|
+
lastCtx = undefined;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
pi.on("agent_start", (event, _ctx) => {
|
|
188
|
+
turnTelemetry.handle(event);
|
|
189
|
+
if (!sessionLifecycle.isCurrent()) return;
|
|
190
|
+
state.workingSince = Date.now();
|
|
191
|
+
state.lastDoneIn = undefined;
|
|
192
|
+
startWorkingTimer();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
pi.on("agent_end", (_event, _ctx) => {
|
|
196
|
+
if (!sessionLifecycle.isCurrent()) return;
|
|
197
|
+
stopWorkingTimer();
|
|
198
|
+
if (state.workingSince !== undefined) {
|
|
199
|
+
state.lastDoneIn = Date.now() - state.workingSince;
|
|
200
|
+
state.workingSince = undefined;
|
|
201
|
+
}
|
|
202
|
+
requestFooterRender?.();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
pi.on("turn_start", (event) => {
|
|
206
|
+
turnTelemetry.handle(event);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
pi.on("message_start", (event) => {
|
|
210
|
+
turnTelemetry.handle(event);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
pi.on("message_update", (event) => {
|
|
214
|
+
turnTelemetry.handle(event);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
pi.on("tool_execution_start", (event) => {
|
|
218
|
+
turnTelemetry.handle(event);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
pi.on("turn_end", (event) => {
|
|
222
|
+
turnTelemetry.handle(event);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
pi.on("agent_settled", (event, ctx) => {
|
|
226
|
+
const telemetry = turnTelemetry.handle(event);
|
|
227
|
+
if (telemetry && config.enabled && config.telemetry.enabled && isTuiContext(ctx)) {
|
|
228
|
+
const message = formatTurnTelemetry(telemetry, ctx.ui.theme, config.telemetry, config.icons.mode);
|
|
229
|
+
if (message) ctx.ui.notify(message, "info");
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
pi.on("model_select", (_event, ctx) => {
|
|
234
|
+
refreshInteractiveState(ctx);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
pi.on("thinking_level_select", (_event, ctx) => {
|
|
238
|
+
refreshInteractiveState(ctx);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
pi.on("message_end", (event, ctx) => {
|
|
242
|
+
turnTelemetry.handle(event);
|
|
243
|
+
if (!sessionLifecycle.isCurrent()) return;
|
|
244
|
+
invalidateUsageCache();
|
|
245
|
+
refreshInteractiveState(ctx);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
pi.on("tool_execution_end", (_event, ctx) => {
|
|
249
|
+
refreshInteractiveState(ctx);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
pi.on("session_compact", (_event, ctx) => {
|
|
253
|
+
if (!sessionLifecycle.isCurrent()) return;
|
|
254
|
+
invalidateUsageCache();
|
|
255
|
+
refreshInteractiveState(ctx);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
pi.on("session_tree", (_event, ctx) => {
|
|
259
|
+
if (!sessionLifecycle.isCurrent()) return;
|
|
260
|
+
invalidateUsageCache();
|
|
261
|
+
refreshInteractiveState(ctx);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
registerSettingsCommand(pi, {
|
|
265
|
+
getConfig: () => config,
|
|
266
|
+
onConfigChanged: (newConfig) => {
|
|
267
|
+
const wasEnabled = config.enabled;
|
|
268
|
+
saveConfig(newConfig);
|
|
269
|
+
config = newConfig;
|
|
270
|
+
if (lastCtx && wasEnabled !== newConfig.enabled) {
|
|
271
|
+
if (newConfig.enabled) {
|
|
272
|
+
applyUi(lastCtx);
|
|
273
|
+
} else {
|
|
274
|
+
uninstallUi(lastCtx);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const gitNeeded = newConfig.footerSegments.gitBranch || newConfig.footerSegments.gitStatus || newConfig.footerSegments.gitCommit;
|
|
278
|
+
if (lastCtx && gitNeeded) {
|
|
279
|
+
void scheduleGitRefresh(lastCtx);
|
|
280
|
+
} else {
|
|
281
|
+
state.git = emptyGitStatus();
|
|
282
|
+
}
|
|
283
|
+
requestFooterRender?.();
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
}
|