diff --git a/src/cli/components/InputRich.tsx b/src/cli/components/InputRich.tsx index 5686b0b..063a16b 100644 --- a/src/cli/components/InputRich.tsx +++ b/src/cli/components/InputRich.tsx @@ -20,6 +20,8 @@ const appVersion = getVersion(); // Only show token count when it exceeds this threshold const COUNTER_VISIBLE_THRESHOLD = 1000; +// Window for double-escape to clear input +const ESC_CLEAR_WINDOW_MS = 2500; export function Input({ visible = true, @@ -164,12 +166,12 @@ export function Input({ setEscapePressed(false); if (escapeTimerRef.current) clearTimeout(escapeTimerRef.current); } else { - // First escape - start 1-second timer + // First escape - start timer to allow double-escape to clear setEscapePressed(true); if (escapeTimerRef.current) clearTimeout(escapeTimerRef.current); escapeTimerRef.current = setTimeout(() => { setEscapePressed(false); - }, 1000); + }, ESC_CLEAR_WINDOW_MS); } } } diff --git a/src/cli/components/PasteAwareTextInput.tsx b/src/cli/components/PasteAwareTextInput.tsx index ae8d826..ed31728 100644 --- a/src/cli/components/PasteAwareTextInput.tsx +++ b/src/cli/components/PasteAwareTextInput.tsx @@ -333,6 +333,22 @@ export function PasteAwareTextInput({ }, [internal_eventEmitter]); const handleChange = (newValue: string) => { + // Drop lone escape characters that Ink's text input would otherwise insert; + // they are used as control keys for double-escape handling and should not + // mutate the input value. + const sanitizedValue = newValue.replaceAll("\u001b", ""); + if (sanitizedValue !== newValue) { + // Keep caret in bounds after stripping control chars + const nextCaret = Math.min(caretOffsetRef.current, sanitizedValue.length); + setNudgeCursorOffset(nextCaret); + caretOffsetRef.current = nextCaret; + newValue = sanitizedValue; + // If nothing actually changed after stripping, bail out early + if (sanitizedValue === displayValue) { + return; + } + } + // Heuristic: detect large additions that look like pastes const addedLen = newValue.length - displayValue.length; const lineDelta = countLines(newValue) - countLines(displayValue);