pi-one-ui 0.1.1 → 0.2.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/README.md +66 -39
- package/extensions/{shell → app/commands}/settings-previews.ts +7 -7
- package/extensions/app/config/renderer.ts +13 -63
- package/extensions/app/config/shell.ts +24 -138
- package/extensions/app/config/store.ts +290 -0
- package/extensions/app/host/pi-extension-port.ts +25 -0
- package/extensions/app/host/pi-ui-port.ts +52 -0
- package/extensions/app/host/tui-capabilities.ts +27 -0
- package/extensions/app/overlay/input-router.ts +63 -0
- package/extensions/app/overlay/overlay-manager.ts +39 -0
- package/extensions/{shell → app/overlay}/selector-border.ts +3 -3
- package/extensions/app/overlay/selector-controller.ts +104 -0
- package/extensions/app/ownership/layout-registry.ts +65 -0
- package/extensions/app/ownership.ts +21 -7
- package/extensions/app/panel.ts +261 -140
- package/extensions/app/runtime/event-coordinator.ts +84 -0
- package/extensions/app/runtime/render-scheduler.ts +56 -0
- package/extensions/app/runtime/runtime-state.ts +83 -0
- package/extensions/app/runtime/tui-runtime.ts +546 -0
- package/extensions/features/{context.ts → context-inspector/index.ts} +30 -23
- package/extensions/features/{shell/flush-docked-bash.ts → flush-docked-bash.ts} +1 -4
- package/extensions/index.ts +2 -48
- package/extensions/layouts/context/index.ts +188 -0
- package/extensions/{shell → layouts/context/message}/user-message-styles.ts +2 -2
- package/extensions/{shell → layouts/context/message}/user-message.ts +2 -2
- package/extensions/{transcript → layouts/context}/renderer/compact-mode.ts +17 -13
- package/extensions/layouts/context/renderer/context-refresh.ts +79 -0
- package/extensions/{transcript → layouts/context}/renderer/default-mode.ts +3 -3
- package/extensions/{transcript → layouts/context}/renderer/index.ts +14 -14
- package/extensions/{transcript → layouts/context}/renderer/mouse/hover.ts +2 -2
- package/extensions/{transcript → layouts/context}/renderer/mouse/interaction.ts +26 -7
- package/extensions/{transcript → layouts/context}/renderer/mouse/layout.ts +1 -1
- package/extensions/{transcript → layouts/context}/renderer/mouse/packets.ts +3 -3
- package/extensions/{transcript → layouts/context}/renderer/mouse/scroll.ts +12 -7
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-component.ts +2 -2
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-edit-render.ts +1 -1
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-highlight.ts +1 -1
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-layout.ts +1 -1
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-limits.ts +1 -1
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-palette.ts +1 -1
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-presentation.ts +1 -1
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-renderer.ts +1 -1
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-write-render.ts +1 -1
- package/extensions/{transcript → layouts/context}/renderer/tool/diff/index.ts +2 -2
- package/extensions/{transcript → layouts/context}/renderer/tool/grouping.ts +4 -4
- package/extensions/{transcript → layouts/context}/renderer/tool/message-display.ts +6 -4
- package/extensions/{transcript → layouts/context}/renderer/tool/names.ts +2 -2
- package/extensions/{transcript → layouts/context}/renderer/tool/result.ts +3 -3
- package/extensions/{features/agent-summary → layouts/context/summary}/core.ts +1 -1
- package/extensions/{features → layouts/context/thinking}/compact-thinking.ts +7 -7
- package/extensions/{shell → layouts/editor}/accent-rail-editor.ts +8 -8
- package/extensions/{shell → layouts/editor}/completion-menu.ts +3 -6
- package/extensions/layouts/editor/controller.ts +613 -0
- package/extensions/{shell → layouts/editor}/editor-metadata-format.ts +6 -3
- package/extensions/layouts/editor/factory.ts +159 -0
- package/extensions/layouts/editor/index.ts +16 -0
- package/extensions/{shell → layouts/editor}/minimalist-editor.ts +4 -4
- package/extensions/layouts/editor/ownership.ts +103 -0
- package/extensions/{shell → layouts/editor}/ui.ts +2 -2
- package/extensions/layouts/footer/controller.ts +392 -0
- package/extensions/layouts/footer/data.ts +41 -0
- package/extensions/{shell → layouts/footer}/extension-status.ts +2 -2
- package/extensions/{shell → layouts/footer}/footer-layout.ts +1 -1
- package/extensions/{shell → layouts/footer}/footer.ts +11 -8
- package/extensions/layouts/footer/index.ts +24 -0
- package/extensions/layouts/header/index.ts +5 -0
- package/extensions/{features/shell → layouts/header}/startup-header.ts +105 -19
- package/extensions/layouts/working-line/controller.ts +291 -0
- package/extensions/{shell → layouts/working-line}/interaction-summary.ts +3 -3
- package/extensions/{shell → layouts/working-line}/working-line-spinners.ts +1 -1
- package/extensions/{shell → layouts/working-line}/working-line.ts +4 -4
- package/extensions/{shell → services}/live-context.ts +29 -1
- package/extensions/services/project-refresh.ts +374 -0
- package/extensions/{shell → services}/project-state.ts +5 -5
- package/extensions/{shell/state.ts → services/session-state.ts} +75 -5
- package/extensions/{shell → shared}/format.ts +4 -4
- package/extensions/tools/component-tree.ts +6 -6
- package/package.json +4 -4
- package/extensions/shell/index.ts +0 -1659
- package/extensions/shell/project-refresh.ts +0 -130
- package/extensions/shell/settings-command.ts +0 -2144
- package/extensions/transcript/index.ts +0 -65
- package/extensions/transcript/renderer/transcript-refresh.ts +0 -79
- /package/extensions/{shell → app/ownership}/prototype-patch-registry.ts +0 -0
- /package/extensions/{shell → app/runtime}/session-lifecycle.ts +0 -0
- /package/extensions/features/{shell/aliases.ts → aliases.ts} +0 -0
- /package/extensions/features/{shell → legacy}/working-message.ts +0 -0
- /package/extensions/features/{reference → session-reference}/index.ts +0 -0
- /package/extensions/features/{reference → session-reference}/session.ts +0 -0
- /package/extensions/features/{reference/subagent.ts → subagent-autocomplete.ts} +0 -0
- /package/extensions/{shell → layouts/context/message}/user-message-osc.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/markdown-enhance.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/diff/ATTRIBUTION.md +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/diff/ansi-utils.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-header.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-inline.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-parse.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/diff/diff-text.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/diff/line-width-safety.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/diff/shiki-highlight.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/diff/write-execution.ts +0 -0
- /package/extensions/{transcript → layouts/context}/renderer/tool/show-more-hint.ts +0 -0
- /package/extensions/{features/agent-summary → layouts/context/summary}/index.ts +0 -0
- /package/extensions/{shell → layouts/editor}/accent-rail-layout-patch.ts +0 -0
- /package/extensions/{shell → layouts/editor}/editor-transfer.ts +0 -0
- /package/extensions/{shell → layouts/footer}/footer-format.ts +0 -0
- /package/extensions/{shell → layouts/working-line}/working-line-messages.ts +0 -0
- /package/extensions/{shell/git.ts → services/git-data.ts} +0 -0
- /package/extensions/{shell/package-version.ts → services/package-data.ts} +0 -0
- /package/extensions/{shell/runtime.ts → services/runtime-data.ts} +0 -0
- /package/extensions/{shell → services}/telemetry.ts +0 -0
- /package/extensions/{shell → shared}/icons.ts +0 -0
- /package/extensions/{shell → shared}/style.ts +0 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import {
|
|
3
|
+
hasUnsupportedComponentStyle,
|
|
4
|
+
type FooterComponentConfig,
|
|
5
|
+
type PolishedTuiConfig,
|
|
6
|
+
} from "../../app/config/shell.ts";
|
|
7
|
+
import type { SessionLifecycle } from "../../app/runtime/session-lifecycle.ts";
|
|
8
|
+
import { buildSessionDurationLabel } from "../../shared/format.ts";
|
|
9
|
+
import type { LiveContextOverride } from "../../services/live-context.ts";
|
|
10
|
+
import type { FooterState } from "../../services/session-state.ts";
|
|
11
|
+
import { installFooter, installHiddenFooter } from "./footer.ts";
|
|
12
|
+
import { activeFooterReferences } from "./data.ts";
|
|
13
|
+
|
|
14
|
+
const ZENTUI_FOOTER_OWNER = Symbol.for("pi-zentui.footer-owner");
|
|
15
|
+
|
|
16
|
+
type FooterKind = "starship" | "hidden";
|
|
17
|
+
|
|
18
|
+
export type FooterControllerContext = {
|
|
19
|
+
readonly getConfig: () => PolishedTuiConfig;
|
|
20
|
+
readonly saveComponent: (
|
|
21
|
+
patch: Partial<FooterComponentConfig>,
|
|
22
|
+
) => PolishedTuiConfig;
|
|
23
|
+
readonly state: FooterState;
|
|
24
|
+
readonly sessionLifecycle: SessionLifecycle;
|
|
25
|
+
readonly refresh: () => void;
|
|
26
|
+
readonly scheduleProjectRefresh: (ctx: ExtensionContext) => void;
|
|
27
|
+
readonly getLiveContext: () => LiveContextOverride | undefined;
|
|
28
|
+
readonly onProjectRequirementChanged: (
|
|
29
|
+
ctx: ExtensionContext,
|
|
30
|
+
force?: boolean,
|
|
31
|
+
) => void;
|
|
32
|
+
readonly onModelLabelChanged: (ctx: ExtensionContext) => void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Owns Footer installation, ownership markers, status callbacks, and clocks.
|
|
37
|
+
*/
|
|
38
|
+
export class FooterLayoutController {
|
|
39
|
+
private readonly context: FooterControllerContext;
|
|
40
|
+
private installedKind: FooterKind | undefined;
|
|
41
|
+
private installedToken: symbol | undefined;
|
|
42
|
+
private requestFooterRender: (() => void) | undefined;
|
|
43
|
+
private getActiveExtensionStatuses: () => ReadonlyMap<string, string> = () =>
|
|
44
|
+
new Map();
|
|
45
|
+
private stopSessionTimer: () => void = () => {};
|
|
46
|
+
private sessionTimerRequirements = "";
|
|
47
|
+
private lastDurationLabel = "";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates a Footer controller with shared state and project selectors.
|
|
51
|
+
*
|
|
52
|
+
* @param context Runtime services consumed by Footer installation.
|
|
53
|
+
*/
|
|
54
|
+
constructor(context: FooterControllerContext) {
|
|
55
|
+
this.context = context;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Installs the configured Footer and starts required session data updates.
|
|
60
|
+
*
|
|
61
|
+
* @param ctx Active Pi extension context.
|
|
62
|
+
*/
|
|
63
|
+
install(ctx: ExtensionContext): void {
|
|
64
|
+
if (ctx.mode !== "tui" && ctx.mode !== undefined) return;
|
|
65
|
+
const staleOwner = this.footerOwner(ctx);
|
|
66
|
+
if (typeof staleOwner === "symbol") this.installedToken = staleOwner;
|
|
67
|
+
this.reconcile(ctx);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Applies a Footer configuration patch and reconciles its host seam.
|
|
72
|
+
*
|
|
73
|
+
* @param patch Footer configuration changes to persist.
|
|
74
|
+
* @param ctx Active Pi extension context.
|
|
75
|
+
*/
|
|
76
|
+
setComponent(
|
|
77
|
+
patch: Partial<FooterComponentConfig>,
|
|
78
|
+
ctx: ExtensionContext,
|
|
79
|
+
): void {
|
|
80
|
+
const previousStyle = this.context.getConfig().components.footer.style;
|
|
81
|
+
const nextConfig = this.context.saveComponent(patch);
|
|
82
|
+
const styleChanged = nextConfig.components.footer.style !== previousStyle;
|
|
83
|
+
if (patch.style !== undefined) this.reconcile(ctx);
|
|
84
|
+
if (patch.modelLabel !== undefined) this.context.onModelLabelChanged(ctx);
|
|
85
|
+
this.context.onProjectRequirementChanged(ctx, styleChanged);
|
|
86
|
+
this.reconcileSessionTimer();
|
|
87
|
+
this.context.refresh();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Reconciles native, Starship, and hidden Footer ownership.
|
|
92
|
+
*
|
|
93
|
+
* @param ctx Active Pi extension context.
|
|
94
|
+
*/
|
|
95
|
+
reconcile(ctx: ExtensionContext): void {
|
|
96
|
+
switch (this.effectiveFooterStyle()) {
|
|
97
|
+
case "starship":
|
|
98
|
+
this.installStarship(ctx);
|
|
99
|
+
break;
|
|
100
|
+
case "hidden":
|
|
101
|
+
this.installHidden(ctx);
|
|
102
|
+
break;
|
|
103
|
+
case "native":
|
|
104
|
+
this.uninstall(ctx);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
this.reconcileSessionTimer();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Removes the owned Footer while leaving third-party ownership untouched.
|
|
112
|
+
*
|
|
113
|
+
* @param ctx Active Pi extension context.
|
|
114
|
+
* @param forceLocalCleanup Whether shutdown should clear local bookkeeping.
|
|
115
|
+
*/
|
|
116
|
+
uninstall(ctx: ExtensionContext, forceLocalCleanup = false): void {
|
|
117
|
+
const token = this.ownsStatusLine(ctx) ? this.installedToken : undefined;
|
|
118
|
+
if (!token) return;
|
|
119
|
+
try {
|
|
120
|
+
ctx.ui.setFooter(undefined);
|
|
121
|
+
} catch {
|
|
122
|
+
if (!forceLocalCleanup) return;
|
|
123
|
+
}
|
|
124
|
+
this.clearOwnership(ctx, token);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Stops Footer timers and clears host ownership during session shutdown.
|
|
129
|
+
*
|
|
130
|
+
* @param ctx Active Pi extension context, when available.
|
|
131
|
+
*/
|
|
132
|
+
cleanup(ctx?: ExtensionContext): void {
|
|
133
|
+
this.stopSessionTimer();
|
|
134
|
+
if (ctx && this.isTuiContext(ctx)) this.uninstall(ctx, true);
|
|
135
|
+
this.installedKind = undefined;
|
|
136
|
+
this.installedToken = undefined;
|
|
137
|
+
this.requestFooterRender = undefined;
|
|
138
|
+
this.getActiveExtensionStatuses = () => new Map();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Reconciles the session timer after a Footer configuration update.
|
|
143
|
+
*/
|
|
144
|
+
reconcileSessionTimer(): void {
|
|
145
|
+
const references = this.installedFooterReferences();
|
|
146
|
+
const needsTime = references.has("time");
|
|
147
|
+
const needsDuration = references.has("session_duration");
|
|
148
|
+
const requirements =
|
|
149
|
+
needsTime || needsDuration ? `${needsTime}:${needsDuration}` : "";
|
|
150
|
+
if (
|
|
151
|
+
!this.context.sessionLifecycle.isCurrent() ||
|
|
152
|
+
this.installedKind !== "starship" ||
|
|
153
|
+
!requirements
|
|
154
|
+
) {
|
|
155
|
+
this.stopSessionTimer();
|
|
156
|
+
this.sessionTimerRequirements = "";
|
|
157
|
+
this.lastDurationLabel = "";
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (requirements === this.sessionTimerRequirements) return;
|
|
161
|
+
this.stopSessionTimer();
|
|
162
|
+
this.sessionTimerRequirements = requirements;
|
|
163
|
+
this.lastDurationLabel = "";
|
|
164
|
+
const timer = setInterval(() => {
|
|
165
|
+
if (!this.context.sessionLifecycle.isCurrent()) return;
|
|
166
|
+
if (needsTime) {
|
|
167
|
+
this.context.refresh();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const label = this.context.state.sessionStartEpoch
|
|
171
|
+
? buildSessionDurationLabel(this.context.state.sessionStartEpoch)
|
|
172
|
+
: "";
|
|
173
|
+
if (label === this.lastDurationLabel) return;
|
|
174
|
+
this.lastDurationLabel = label;
|
|
175
|
+
this.context.refresh();
|
|
176
|
+
}, 1000);
|
|
177
|
+
this.stopSessionTimer = () => {
|
|
178
|
+
clearInterval(timer);
|
|
179
|
+
this.sessionTimerRequirements = "";
|
|
180
|
+
this.stopSessionTimer = () => {};
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Returns whether the owned Starship Footer needs project data.
|
|
186
|
+
*
|
|
187
|
+
* @returns Whether project refreshes are required.
|
|
188
|
+
*/
|
|
189
|
+
needsProjectRefresh(): boolean {
|
|
190
|
+
return (
|
|
191
|
+
this.installedKind === "starship" && this.installedToken !== undefined
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Returns the active extension status getter supplied by the Footer.
|
|
197
|
+
*
|
|
198
|
+
* @returns Read-only extension status map.
|
|
199
|
+
*/
|
|
200
|
+
getExtensionStatuses(): ReadonlyMap<string, string> {
|
|
201
|
+
return this.getActiveExtensionStatuses();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Returns the format data dependencies of the owned Footer.
|
|
206
|
+
*
|
|
207
|
+
* @returns Active Footer dependency names.
|
|
208
|
+
*/
|
|
209
|
+
installedFooterReferences(): Set<string> {
|
|
210
|
+
return this.installedKind === "starship" && this.installedToken
|
|
211
|
+
? activeFooterReferences(this.context.getConfig())
|
|
212
|
+
: new Set<string>();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Requests a redraw from the mounted Footer component.
|
|
217
|
+
*/
|
|
218
|
+
requestRender(): void {
|
|
219
|
+
this.requestFooterRender?.();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Reports whether a context is an interactive TUI context.
|
|
224
|
+
*
|
|
225
|
+
* @param ctx Candidate Pi extension context.
|
|
226
|
+
* @returns Whether Footer host APIs are available.
|
|
227
|
+
*/
|
|
228
|
+
private isTuiContext(ctx: ExtensionContext): boolean {
|
|
229
|
+
return ctx.hasUI && (ctx.mode === undefined || ctx.mode === "tui");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Returns the currently marked Footer owner on Pi's UI object.
|
|
234
|
+
*
|
|
235
|
+
* @param ctx Active Pi extension context.
|
|
236
|
+
* @returns Existing owner token, when marked.
|
|
237
|
+
*/
|
|
238
|
+
private footerOwner(ctx: ExtensionContext): unknown {
|
|
239
|
+
return (ctx.ui as unknown as Record<PropertyKey, unknown>)[
|
|
240
|
+
ZENTUI_FOOTER_OWNER
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Tests whether this controller still owns the host Footer marker.
|
|
246
|
+
*
|
|
247
|
+
* @param ctx Active Pi extension context.
|
|
248
|
+
* @returns Whether the local owner token remains installed.
|
|
249
|
+
*/
|
|
250
|
+
private ownsStatusLine(ctx: ExtensionContext): boolean {
|
|
251
|
+
return (
|
|
252
|
+
this.installedToken !== undefined &&
|
|
253
|
+
this.footerOwner(ctx) === this.installedToken
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Writes or removes the host Footer ownership marker.
|
|
259
|
+
*
|
|
260
|
+
* @param ctx Active Pi extension context.
|
|
261
|
+
* @param token Owner token, or undefined to remove it.
|
|
262
|
+
*/
|
|
263
|
+
private setOwnership(ctx: ExtensionContext, token: symbol | undefined): void {
|
|
264
|
+
const ui = ctx.ui as unknown as Record<PropertyKey, unknown>;
|
|
265
|
+
try {
|
|
266
|
+
if (token) ui[ZENTUI_FOOTER_OWNER] = token;
|
|
267
|
+
else delete ui[ZENTUI_FOOTER_OWNER];
|
|
268
|
+
} catch {
|
|
269
|
+
// Failure to mark ownership prevents destructive native restoration.
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Clears local Footer state after the host has released an owned Footer.
|
|
275
|
+
*
|
|
276
|
+
* @param ctx Active Pi extension context.
|
|
277
|
+
* @param token Owner token being released.
|
|
278
|
+
*/
|
|
279
|
+
private clearOwnership(ctx: ExtensionContext, token: symbol): void {
|
|
280
|
+
if (this.installedToken !== token) return;
|
|
281
|
+
this.installedKind = undefined;
|
|
282
|
+
this.installedToken = undefined;
|
|
283
|
+
if (this.footerOwner(ctx) === token) this.setOwnership(ctx, undefined);
|
|
284
|
+
this.requestFooterRender = undefined;
|
|
285
|
+
this.getActiveExtensionStatuses = () => new Map();
|
|
286
|
+
this.stopSessionTimer();
|
|
287
|
+
if (this.context.sessionLifecycle.isCurrent())
|
|
288
|
+
this.context.onProjectRequirementChanged(ctx, true);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Installs the Starship Footer with transactional local bookkeeping.
|
|
293
|
+
*
|
|
294
|
+
* @param ctx Active Pi extension context.
|
|
295
|
+
*/
|
|
296
|
+
private installStarship(ctx: ExtensionContext): void {
|
|
297
|
+
if (this.installedKind === "starship" && this.ownsStatusLine(ctx)) return;
|
|
298
|
+
const token = Symbol("pi-one-ui-starship-footer");
|
|
299
|
+
const previous = this.snapshotBookkeeping(ctx);
|
|
300
|
+
try {
|
|
301
|
+
installFooter(ctx, this.context.state, this.context.getConfig, {
|
|
302
|
+
setRequestRender: (requestRender) => {
|
|
303
|
+
this.requestFooterRender = requestRender;
|
|
304
|
+
},
|
|
305
|
+
scheduleProjectRefresh: (refreshContext) =>
|
|
306
|
+
this.context.scheduleProjectRefresh(refreshContext),
|
|
307
|
+
setExtensionStatusesGetter: (getter) => {
|
|
308
|
+
this.getActiveExtensionStatuses = getter ?? (() => new Map());
|
|
309
|
+
},
|
|
310
|
+
getLiveContext: () => this.context.getLiveContext(),
|
|
311
|
+
onDispose: () => this.clearOwnership(ctx, token),
|
|
312
|
+
});
|
|
313
|
+
this.installedKind = "starship";
|
|
314
|
+
this.installedToken = token;
|
|
315
|
+
this.setOwnership(ctx, token);
|
|
316
|
+
this.context.refresh();
|
|
317
|
+
} catch {
|
|
318
|
+
this.restoreFailedInstallation(ctx, token, previous);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Installs the hidden Footer while retaining ownership cleanup semantics.
|
|
324
|
+
*
|
|
325
|
+
* @param ctx Active Pi extension context.
|
|
326
|
+
*/
|
|
327
|
+
private installHidden(ctx: ExtensionContext): void {
|
|
328
|
+
if (this.installedKind === "hidden" && this.ownsStatusLine(ctx)) return;
|
|
329
|
+
const token = Symbol("pi-one-ui-hidden-footer");
|
|
330
|
+
const previous = this.snapshotBookkeeping(ctx);
|
|
331
|
+
try {
|
|
332
|
+
installHiddenFooter(ctx, () => this.clearOwnership(ctx, token));
|
|
333
|
+
this.installedKind = "hidden";
|
|
334
|
+
this.installedToken = token;
|
|
335
|
+
this.setOwnership(ctx, token);
|
|
336
|
+
this.requestFooterRender = undefined;
|
|
337
|
+
this.getActiveExtensionStatuses = () => new Map();
|
|
338
|
+
this.stopSessionTimer();
|
|
339
|
+
} catch {
|
|
340
|
+
this.restoreFailedInstallation(ctx, token, previous);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Snapshots callbacks needed to retain a live Footer after a failed swap.
|
|
346
|
+
*
|
|
347
|
+
* @param ctx Active Pi extension context.
|
|
348
|
+
* @returns Previous Footer bookkeeping.
|
|
349
|
+
*/
|
|
350
|
+
private snapshotBookkeeping(ctx: ExtensionContext) {
|
|
351
|
+
return {
|
|
352
|
+
token: this.ownsStatusLine(ctx) ? this.installedToken : undefined,
|
|
353
|
+
requestRender: this.requestFooterRender,
|
|
354
|
+
getExtensionStatuses: this.getActiveExtensionStatuses,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Restores predecessor callbacks after a non-transactional Footer failure.
|
|
360
|
+
*
|
|
361
|
+
* @param ctx Active Pi extension context.
|
|
362
|
+
* @param token Failed replacement token.
|
|
363
|
+
* @param previous Predecessor bookkeeping snapshot.
|
|
364
|
+
*/
|
|
365
|
+
private restoreFailedInstallation(
|
|
366
|
+
ctx: ExtensionContext,
|
|
367
|
+
token: symbol,
|
|
368
|
+
previous: ReturnType<FooterLayoutController["snapshotBookkeeping"]>,
|
|
369
|
+
): void {
|
|
370
|
+
if (
|
|
371
|
+
previous.token !== undefined &&
|
|
372
|
+
this.installedToken === previous.token &&
|
|
373
|
+
this.footerOwner(ctx) === previous.token
|
|
374
|
+
) {
|
|
375
|
+
this.requestFooterRender = previous.requestRender;
|
|
376
|
+
this.getActiveExtensionStatuses = previous.getExtensionStatuses;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
this.clearOwnership(ctx, token);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Resolves the configured Footer style with unsupported-style fallback.
|
|
384
|
+
*
|
|
385
|
+
* @returns Effective Footer style.
|
|
386
|
+
*/
|
|
387
|
+
private effectiveFooterStyle(): PolishedTuiConfig["components"]["footer"]["style"] {
|
|
388
|
+
return hasUnsupportedComponentStyle(this.context.getConfig(), "footer")
|
|
389
|
+
? "native"
|
|
390
|
+
: this.context.getConfig().components.footer.style;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FOOTER_FORMAT_ALIASES,
|
|
3
|
+
type ZentuiConfig,
|
|
4
|
+
} from "../../app/config/shell.ts";
|
|
5
|
+
import {
|
|
6
|
+
collectFooterFormatReferences,
|
|
7
|
+
parseFooterFormat,
|
|
8
|
+
} from "./footer-format.ts";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the footer data dependencies referenced by the active formats.
|
|
12
|
+
*
|
|
13
|
+
* @param config Current normalized plugin configuration.
|
|
14
|
+
* @returns Names of project, session, runtime, and clock data dependencies.
|
|
15
|
+
*/
|
|
16
|
+
export function activeFooterReferences(config: ZentuiConfig): Set<string> {
|
|
17
|
+
const starship = config.components.footer.styles.starship;
|
|
18
|
+
const references = starship.format
|
|
19
|
+
? collectFooterFormatReferences(
|
|
20
|
+
parseFooterFormat(starship.format),
|
|
21
|
+
FOOTER_FORMAT_ALIASES,
|
|
22
|
+
)
|
|
23
|
+
: new Set<string>([
|
|
24
|
+
...(starship.segments.sessionName ? ["session_name"] : []),
|
|
25
|
+
...(starship.segments.runtime ? ["runtime"] : []),
|
|
26
|
+
...(starship.segments.gitCommit ? ["git_commit"] : []),
|
|
27
|
+
...(starship.segments.gitMetrics ? ["git_metrics"] : []),
|
|
28
|
+
...(starship.segments.packageVersion ? ["package"] : []),
|
|
29
|
+
...(starship.segments.sessionDuration ? ["session_duration"] : []),
|
|
30
|
+
...(starship.segments.time ? ["time"] : []),
|
|
31
|
+
]);
|
|
32
|
+
if (starship.responsive) {
|
|
33
|
+
for (const name of collectFooterFormatReferences(
|
|
34
|
+
parseFooterFormat(starship.compactFormat),
|
|
35
|
+
FOOTER_FORMAT_ALIASES,
|
|
36
|
+
)) {
|
|
37
|
+
references.add(name);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return references;
|
|
41
|
+
}
|
|
@@ -3,12 +3,12 @@ import type {
|
|
|
3
3
|
ExtensionStatusColorMode,
|
|
4
4
|
ExtensionStatusPlacement,
|
|
5
5
|
ZentuiConfig,
|
|
6
|
-
} from "
|
|
6
|
+
} from "../../app/config/shell.ts";
|
|
7
7
|
import {
|
|
8
8
|
getExtensionStatusColorMode,
|
|
9
9
|
getExtensionStatusPlacement,
|
|
10
10
|
isExtensionStatusPlacement,
|
|
11
|
-
} from "
|
|
11
|
+
} from "../../app/config/shell.ts";
|
|
12
12
|
|
|
13
13
|
export type ExtensionStatusSegment = {
|
|
14
14
|
key: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
2
|
-
import type { CompactFooterMaxLines } from "
|
|
2
|
+
import type { CompactFooterMaxLines } from "../../app/config/shell.ts";
|
|
3
3
|
import type { CompactBoundaryKind } from "./footer-format.ts";
|
|
4
4
|
|
|
5
5
|
export type FooterZones = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
-
import type { SeparatorStyle, ZentuiConfig } from "
|
|
4
|
-
import { FOOTER_FORMAT_ALIASES } from "
|
|
5
|
-
import { sanitizeEditorMetadataText } from "
|
|
3
|
+
import type { SeparatorStyle, ZentuiConfig } from "../../app/config/shell.ts";
|
|
4
|
+
import { FOOTER_FORMAT_ALIASES } from "../../app/config/shell.ts";
|
|
5
|
+
import { sanitizeEditorMetadataText } from "../editor/editor-metadata-format.ts";
|
|
6
6
|
import {
|
|
7
7
|
collectExtensionStatusSegments,
|
|
8
8
|
type ExtensionStatusSegment,
|
|
@@ -35,11 +35,14 @@ import {
|
|
|
35
35
|
formatRuntimeSegment,
|
|
36
36
|
formatTimeLabel,
|
|
37
37
|
formatUsernameHostLabel,
|
|
38
|
-
} from "
|
|
39
|
-
import { resolveRuntimeSymbol } from "
|
|
40
|
-
import type { LiveContextOverride } from "
|
|
41
|
-
import {
|
|
42
|
-
|
|
38
|
+
} from "../../shared/format.ts";
|
|
39
|
+
import { resolveRuntimeSymbol } from "../../shared/icons.ts";
|
|
40
|
+
import type { LiveContextOverride } from "../../services/live-context.ts";
|
|
41
|
+
import {
|
|
42
|
+
type FooterState,
|
|
43
|
+
modelLabelFor,
|
|
44
|
+
} from "../../services/session-state.ts";
|
|
45
|
+
import { renderStyleForSource } from "../../shared/style.ts";
|
|
43
46
|
|
|
44
47
|
const separatorText: Record<SeparatorStyle, string> = {
|
|
45
48
|
pipe: " | ",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export {
|
|
2
|
+
FooterLayoutController,
|
|
3
|
+
type FooterControllerContext,
|
|
4
|
+
} from "./controller.ts";
|
|
5
|
+
export { activeFooterReferences } from "./data.ts";
|
|
6
|
+
export { installFooter, installHiddenFooter } from "./footer.ts";
|
|
7
|
+
export {
|
|
8
|
+
collectExtensionStatusSegments,
|
|
9
|
+
sanitizeExtensionStatusOriginalText,
|
|
10
|
+
sanitizeExtensionStatusText,
|
|
11
|
+
} from "./extension-status.ts";
|
|
12
|
+
export {
|
|
13
|
+
collectFooterFormatReferences,
|
|
14
|
+
compileCompactFormat,
|
|
15
|
+
parseFooterFormat,
|
|
16
|
+
renderFormatSplit,
|
|
17
|
+
renderFormatTokens,
|
|
18
|
+
} from "./footer-format.ts";
|
|
19
|
+
export {
|
|
20
|
+
createInitialState,
|
|
21
|
+
modelLabelFor,
|
|
22
|
+
syncState,
|
|
23
|
+
} from "../../services/session-state.ts";
|
|
24
|
+
export type { FooterState } from "../../services/session-state.ts";
|