From a5e8d6c216e2c449407c857abb28fbf797b7e12c Mon Sep 17 00:00:00 2001 From: William Valentin Date: Mon, 16 Feb 2026 11:36:14 -0800 Subject: [PATCH] fix(tui): cancel prompts on raw Esc input in minimal mode --- src/frontends/tui/minimal.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/frontends/tui/minimal.ts b/src/frontends/tui/minimal.ts index 4b632b0..eba565e 100644 --- a/src/frontends/tui/minimal.ts +++ b/src/frontends/tui/minimal.ts @@ -217,11 +217,31 @@ export class MinimalTui { } let settled = false; + const stdin = process.stdin as NodeJS.ReadStream & { + isRaw?: boolean; + setRawMode?: (mode: boolean) => void; + }; + const wasRaw = Boolean(stdin.isRaw); + let enabledRawForPrompt = false; + let dataListener: ((chunk: Buffer) => void) | null = null; + + if (stdin.isTTY && stdin.setRawMode && !wasRaw) { + stdin.setRawMode(true); + enabledRawForPrompt = true; + } + const finish = (answer: string) => { if (settled) { return; } settled = true; + if (dataListener) { + stdin.removeListener('data', dataListener); + dataListener = null; + } + if (enabledRawForPrompt && stdin.setRawMode) { + stdin.setRawMode(false); + } this.activePromptCancel = null; this.rl?.removeListener('close', onClose); resolve(answer); @@ -229,6 +249,12 @@ export class MinimalTui { const onClose = () => finish(''); this.rl.once('close', onClose); + dataListener = (chunk: Buffer) => { + if (chunk.length === 1 && chunk[0] === 0x1b) { + this.activePromptCancel?.(); + } + }; + stdin.on('data', dataListener); this.activePromptCancel = () => { if (!this.rl) { finish('');