Skip to content

Commit 9be7c49

Browse files
committed
fix(init): remove outro screen and press-any-key dismissal
Remove the OutroScreen component and the outroDismissed promise that blocked teardown until the user pressed a key. The wizard now exits immediately on completion/failure — the post-dispose report in scrollback already shows the outcome summary.
1 parent dd47734 commit 9be7c49

2 files changed

Lines changed: 3 additions & 107 deletions

File tree

src/lib/init/ui/ink-app.tsx

Lines changed: 3 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ import type {
4949
LearnState,
5050
LogEntry,
5151
LogSeverity,
52-
OutroState,
5352
SpinnerState,
5453
StepEntry,
5554
WizardStore,
@@ -117,10 +116,6 @@ export function App({ store }: AppProps): React.ReactNode {
117116
const contentHeight = Math.max(5, rows - 3);
118117

119118
useInput((input, key) => {
120-
if (snapshot.outroState) {
121-
snapshot.requestCancel?.();
122-
return;
123-
}
124119
if (key.ctrl && input === "c" && !snapshot.prompt) {
125120
snapshot.requestCancel?.();
126121
return;
@@ -153,9 +148,6 @@ export function App({ store }: AppProps): React.ReactNode {
153148
);
154149

155150
const hints: KeyHint[] = useMemo(() => {
156-
if (snapshot.outroState) {
157-
return [{ label: "any key", action: "exit" }];
158-
}
159151
const h: KeyHint[] = [{ label: "\u2190\u2192", action: "switch tab" }];
160152
if (statusMessages.length > STATUS_COLLAPSED_COUNT) {
161153
h.push({ label: "s", action: "toggle status" });
@@ -176,7 +168,6 @@ export function App({ store }: AppProps): React.ReactNode {
176168
}, [
177169
statusMessages.length,
178170
snapshot.prompt,
179-
snapshot.outroState,
180171
activeTab,
181172
snapshot.filesRead.length,
182173
]);
@@ -191,13 +182,7 @@ export function App({ store }: AppProps): React.ReactNode {
191182
flexShrink={1}
192183
overflow="hidden"
193184
>
194-
{snapshot.outroState ? (
195-
<OutroScreen
196-
outro={snapshot.outroState}
197-
summary={snapshot.summary}
198-
/>
199-
) : null}
200-
{!snapshot.outroState && activeTab === 0 ? (
185+
{activeTab === 0 ? (
201186
<StatusScreen
202187
bannerRows={snapshot.bannerRows}
203188
learnState={snapshot.learnState}
@@ -210,14 +195,13 @@ export function App({ store }: AppProps): React.ReactNode {
210195
tipIndex={snapshot.tipIndex}
211196
width={width - 2}
212197
/>
213-
) : null}
214-
{!snapshot.outroState && activeTab !== 0 ? (
198+
) : (
215199
<FilesScreen
216200
filesRead={snapshot.filesRead}
217201
hasActivePrompt={snapshot.prompt !== null}
218202
terminalRows={rows}
219203
/>
220-
) : null}
204+
)}
221205
</Box>
222206

223207
{snapshot.overlay ? (
@@ -524,64 +508,6 @@ function FilesScreen({
524508

525509
// ──────────────────────────── Outro Screen ────────────────────────────
526510

527-
function OutroScreen({
528-
outro,
529-
summary,
530-
}: {
531-
outro: NonNullable<OutroState>;
532-
summary: WizardSummary | null;
533-
}): React.ReactNode {
534-
const isSuccess = outro.kind === "success";
535-
const isError = outro.kind === "error";
536-
537-
let icon: string;
538-
let iconColor: string;
539-
if (isSuccess) {
540-
icon = "✔";
541-
iconColor = COLOR_SUCCESS;
542-
} else if (isError) {
543-
icon = "✖";
544-
iconColor = COLOR_ERROR;
545-
} else {
546-
icon = "■";
547-
iconColor = COLOR_WARN;
548-
}
549-
550-
const showErrors = isError && "errors" in outro && outro.errors.length > 0;
551-
const showSummary = isSuccess && summary !== null;
552-
553-
return (
554-
<Box flexDirection="column" flexGrow={1} paddingTop={1} paddingX={1}>
555-
<Box gap={1}>
556-
<Text bold color={iconColor}>
557-
{icon}
558-
</Text>
559-
<Text bold>{outro.message}</Text>
560-
</Box>
561-
{showErrors ? (
562-
<Box flexDirection="column" marginTop={1} paddingLeft={3}>
563-
{"errors" in outro
564-
? outro.errors.map((err, i) => (
565-
// biome-ignore lint/suspicious/noArrayIndexKey: positional error lines
566-
<Text color={COLOR_ERROR} key={i}>
567-
{err}
568-
</Text>
569-
))
570-
: null}
571-
</Box>
572-
) : null}
573-
{showSummary ? (
574-
<Box flexDirection="column" marginTop={1}>
575-
<SummaryPanel summary={summary} />
576-
</Box>
577-
) : null}
578-
<Box marginTop={1}>
579-
<Text dimColor>Press any key to exit</Text>
580-
</Box>
581-
</Box>
582-
);
583-
}
584-
585511
// ──────────────────────────── Overlay ─────────────────────────────────
586512

587513
function OverlayPanel({

src/lib/init/ui/ink-ui.ts

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,6 @@ export class InkUI implements WizardUI {
380380
* `[Symbol.asyncDispose]` awaits this so the `using` block keeps the
381381
* UI alive until the user has seen and acknowledged the final screen.
382382
*/
383-
private outroDismissed:
384-
| { promise: Promise<void>; resolve: () => void }
385-
| undefined;
386383

387384
constructor(
388385
instance: InkInstance,
@@ -419,22 +416,13 @@ export class InkUI implements WizardUI {
419416
outro(message: string): void {
420417
const clean = stripAnsi(message);
421418
this.appendLog("success", clean);
422-
this.store.setOutro({ kind: "success", message: clean });
423419
this.outroMessage = clean;
424-
this.createOutroDismissed();
425420
}
426421

427422
cancel(message: string): void {
428423
const clean = stripAnsi(message);
429424
this.appendLog("error", clean);
430-
const errors = this.store
431-
.getSnapshot()
432-
.logs.filter((e) => e.severity === "error" && e.text !== clean)
433-
.slice(-5)
434-
.map((e) => e.text);
435-
this.store.setOutro({ kind: "error", message: clean, errors });
436425
this.failureMessage = clean;
437-
this.createOutroDismissed();
438426
}
439427

440428
summary(summary: WizardSummary): void {
@@ -613,9 +601,6 @@ export class InkUI implements WizardUI {
613601
// ── Disposal ──────────────────────────────────────────────────────
614602

615603
[Symbol.asyncDispose](): Promise<void> {
616-
if (this.outroDismissed) {
617-
return this.outroDismissed.promise.then(() => this.tearDown());
618-
}
619604
this.tearDown();
620605
return Promise.resolve();
621606
}
@@ -733,13 +718,6 @@ export class InkUI implements WizardUI {
733718
* no-op (the `cancelRequested` flag short-circuits).
734719
*/
735720
requestCancel(): void {
736-
// Outro path — the user pressed a key on the final screen.
737-
// Resolve the dismiss promise so `[Symbol.asyncDispose]` can
738-
// proceed with teardown through the normal `using` exit path.
739-
if (this.outroDismissed) {
740-
this.outroDismissed.resolve();
741-
return;
742-
}
743721
const promptCancel = this.activePromptCancel;
744722
if (promptCancel) {
745723
// Prompt path — let the runner unwind via WizardCancelledError.
@@ -764,14 +742,6 @@ export class InkUI implements WizardUI {
764742
setImmediate(() => process.exit(130));
765743
}
766744

767-
private createOutroDismissed(): void {
768-
let resolve!: () => void;
769-
const promise = new Promise<void>((r) => {
770-
resolve = r;
771-
});
772-
this.outroDismissed = { promise, resolve };
773-
}
774-
775745
/**
776746
* Build a compact final summary echoed to stderr after Ink
777747
* unmounts. Ink's inline rendering means the run's log lines are

0 commit comments

Comments
 (0)