diff --git a/components/terminal/runtime/createXTermRuntime.ts b/components/terminal/runtime/createXTermRuntime.ts index 13f7468c8..f57d726cb 100644 --- a/components/terminal/runtime/createXTermRuntime.ts +++ b/components/terminal/runtime/createXTermRuntime.ts @@ -79,6 +79,7 @@ import { createTerminalInterruptTrace, logTerminalInterruptTrace, } from "./terminalInterruptDiagnostics"; +import { clearTerminalInputStateForInterrupt } from "./terminalInterruptInputState"; import { getFlowControllerForTerm } from "./terminalSessionAttachment"; import { prioritizeTerminalInput } from "./terminalOutputPipeline"; import { @@ -822,6 +823,11 @@ export const createXTermRuntime = (ctx: CreateXTermRuntimeContext): XTermRuntime logTerminalInterruptTrace("renderer-keydown-send", interruptTrace, { priority, }); + clearTerminalInputStateForInterrupt({ + commandBufferRef: ctx.commandBufferRef, + serialLineBufferRef: ctx.serialLineBufferRef, + onAutocompleteInput: ctx.onAutocompleteInput, + }); if (ctx.terminalBackend.interruptSession) { ctx.terminalBackend.interruptSession(id, interruptTrace); } else { diff --git a/components/terminal/runtime/terminalInterruptInputState.test.ts b/components/terminal/runtime/terminalInterruptInputState.test.ts new file mode 100644 index 000000000..fd7d1042e --- /dev/null +++ b/components/terminal/runtime/terminalInterruptInputState.test.ts @@ -0,0 +1,29 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { clearTerminalInputStateForInterrupt } from "./terminalInterruptInputState"; + +test("interrupt input state clearing matches the normal Ctrl+C input bookkeeping", () => { + const commandBufferRef = { current: "sudo apt" }; + const serialLineBufferRef = { current: "pending serial input" }; + const autocompleteInputs: string[] = []; + + clearTerminalInputStateForInterrupt({ + commandBufferRef, + serialLineBufferRef, + onAutocompleteInput: (data) => autocompleteInputs.push(data), + }); + + assert.equal(commandBufferRef.current, ""); + assert.equal(serialLineBufferRef.current, ""); + assert.deepEqual(autocompleteInputs, ["\x03"]); +}); + +test("interrupt input state clearing tolerates terminals without serial line mode", () => { + const commandBufferRef = { current: "echo pending" }; + + assert.doesNotThrow(() => { + clearTerminalInputStateForInterrupt({ commandBufferRef }); + }); + assert.equal(commandBufferRef.current, ""); +}); diff --git a/components/terminal/runtime/terminalInterruptInputState.ts b/components/terminal/runtime/terminalInterruptInputState.ts new file mode 100644 index 000000000..cc49bf2cc --- /dev/null +++ b/components/terminal/runtime/terminalInterruptInputState.ts @@ -0,0 +1,21 @@ +type StringRef = { + current: string; +}; + +type InterruptInputStateOptions = { + commandBufferRef: StringRef; + serialLineBufferRef?: StringRef; + onAutocompleteInput?: (data: string) => void; +}; + +export function clearTerminalInputStateForInterrupt({ + commandBufferRef, + serialLineBufferRef, + onAutocompleteInput, +}: InterruptInputStateOptions): void { + commandBufferRef.current = ""; + if (serialLineBufferRef) { + serialLineBufferRef.current = ""; + } + onAutocompleteInput?.("\x03"); +}