From d28e3eb95089454d85c3cbd0cd67f772727e5ad0 Mon Sep 17 00:00:00 2001 From: Vu Anh Nguyen Date: Thu, 26 Mar 2026 16:50:03 +0700 Subject: [PATCH] fix(ask): honor app interrupt in prompt editor --- .../src/modes/components/hook-editor.ts | 4 +-- .../coding-agent/test/hook-editor.test.ts | 28 +++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/src/modes/components/hook-editor.ts b/packages/coding-agent/src/modes/components/hook-editor.ts index dd9c76215..87f9d6373 100644 --- a/packages/coding-agent/src/modes/components/hook-editor.ts +++ b/packages/coding-agent/src/modes/components/hook-editor.ts @@ -80,8 +80,8 @@ export class HookEditorComponent extends Container { /** Prompt-style: raw Enter submits; Editor owns newline-producing sequences. */ #handlePromptStyleInput(keyData: string): void { - // Escape to cancel - if (matchesKey(keyData, "escape") || matchesKey(keyData, "esc")) { + // Prompt-style keeps Escape as an explicit cancel key and also honors app.interrupt remaps. + if (matchesKey(keyData, "escape") || matchesKey(keyData, "esc") || matchesAppInterrupt(keyData)) { this.#onCancelCallback(); return; } diff --git a/packages/coding-agent/test/hook-editor.test.ts b/packages/coding-agent/test/hook-editor.test.ts index 53f80029b..09a663f70 100644 --- a/packages/coding-agent/test/hook-editor.test.ts +++ b/packages/coding-agent/test/hook-editor.test.ts @@ -1,9 +1,10 @@ -import { beforeAll, describe, expect, it, vi } from "bun:test"; +import { afterEach, beforeAll, describe, expect, it, vi } from "bun:test"; +import { KeybindingsManager } from "@oh-my-pi/pi-coding-agent/config/keybindings"; import { HookEditorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/hook-editor"; import { ExtensionUiController } from "@oh-my-pi/pi-coding-agent/modes/controllers/extension-ui-controller"; import { getThemeByName, setThemeInstance } from "@oh-my-pi/pi-coding-agent/modes/theme/theme"; import type { InteractiveModeContext } from "@oh-my-pi/pi-coding-agent/modes/types"; -import type { TUI } from "@oh-my-pi/pi-tui"; +import { setKeybindings, type TUI } from "@oh-my-pi/pi-tui"; beforeAll(async () => { const theme = await getThemeByName("dark"); @@ -13,6 +14,11 @@ beforeAll(async () => { setThemeInstance(theme); }); +afterEach(() => { + setKeybindings(KeybindingsManager.inMemory()); + vi.restoreAllMocks(); +}); + function createTui(): TUI { return { requestRender: vi.fn(), @@ -260,6 +266,24 @@ describe("HookEditorComponent prompt-style mode", () => { expect(onCancel).toHaveBeenCalledTimes(1); expect(onSubmit).not.toHaveBeenCalled(); }); + + it("cancels on app.interrupt in prompt-style mode even when remapped", () => { + setKeybindings( + KeybindingsManager.inMemory({ + "app.interrupt": "ctrl+c", + }), + ); + const onSubmit = vi.fn(); + const onCancel = vi.fn(); + const component = new HookEditorComponent(createTui(), "Prompt", "draft", onSubmit, onCancel, { + promptStyle: true, + }); + + component.handleInput("\x03"); + + expect(onCancel).toHaveBeenCalledTimes(1); + expect(onSubmit).not.toHaveBeenCalled(); + }); }); describe("ExtensionUiController hook editor abort", () => {