From 81e8cb81a56905b898a6f14fa545a74272ee2470 Mon Sep 17 00:00:00 2001 From: Simon Klee Date: Sun, 26 Apr 2026 21:44:20 +0200 Subject: [PATCH] fix(tui): startup rejection handling Propagate renderer startup failures from the TUI promise instead of leaving opencode hanging, and destroy any partially initialized renderer before rejecting to restore terminal state. --- packages/opencode/src/cli/cmd/tui/app.tsx | 148 ++++++++++++---------- 1 file changed, 78 insertions(+), 70 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 833c8dc8c3..3644cafa6a 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -117,11 +117,15 @@ export function tui(input: { headers?: RequestInit["headers"] events?: EventSource }) { - // promise to prevent immediate exit - // oxlint-disable-next-line no-async-promise-executor -- intentional: async executor used for sequential setup before resolve - return new Promise(async (resolve) => { + return new Promise((resolve, reject) => { const unguard = win32InstallCtrlCGuard() - win32DisableProcessedInput() + let renderer: Awaited> | undefined + const fail = (error: unknown) => { + renderer?.destroy() + renderer = undefined + unguard?.() + reject(error) + } const onExit = async () => { unguard?.() @@ -132,73 +136,77 @@ export function tui(input: { await TuiPluginRuntime.dispose() } - const renderer = await createCliRenderer(rendererConfig(input.config)) - const mode = (await renderer.waitForThemeMode(1000)) ?? "dark" + void (async () => { + win32DisableProcessedInput() - await render(() => { - return ( - ( - - )} - > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) - }, renderer) + renderer = await createCliRenderer(rendererConfig(input.config)) + const mode = (await renderer.waitForThemeMode(1000)) ?? "dark" + + await render(() => { + return ( + ( + + )} + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) + }, renderer) + })().catch(fail) }) }