fix(tui): dismiss /btw panel before cancelling compaction on Esc and Ctrl+C (#1811)

This commit is contained in:
liruifengv 2026-07-17 13:29:12 +08:00 committed by GitHub
parent 31449728b7
commit cec15e2188
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 110 additions and 15 deletions

View file

@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---
Fix Esc and Ctrl+C cancelling compaction instead of closing an open /btw panel.

View file

@ -129,15 +129,8 @@ export class EditorKeyboardController {
return;
}
if (host.state.appState.isCompacting) {
this.clearPendingExit();
if (this.clearEditorTextIfPresent()) return;
this.cancelCurrentCompaction();
return;
}
// The btw panel stacks above the transcript, so Ctrl+C cancels/closes it
// before touching an in-flight compaction or stream.
if (host.btwPanelController.cancelRunning()) {
this.clearPendingExit();
return;
@ -147,6 +140,15 @@ export class EditorKeyboardController {
return;
}
if (host.state.appState.isCompacting) {
this.clearPendingExit();
if (this.clearEditorTextIfPresent()) return;
this.cancelCurrentCompaction();
return;
}
if (host.state.appState.streamingPhase !== 'idle') {
this.clearPendingExit();
@ -184,12 +186,14 @@ export class EditorKeyboardController {
this.clearPendingUndoEsc();
return;
}
if (host.state.appState.isCompacting) {
this.cancelCurrentCompaction();
// The btw panel stacks above the transcript, so Esc dismisses it before
// touching an in-flight compaction or stream.
if (host.btwPanelController.closeOrCancel()) {
this.clearPendingUndoEsc();
return;
}
if (host.btwPanelController.closeOrCancel()) {
if (host.state.appState.isCompacting) {
this.cancelCurrentCompaction();
this.clearPendingUndoEsc();
return;
}

View file

@ -12,16 +12,24 @@ interface Harness {
readonly editor: Record<string, ((...args: never[]) => unknown) | undefined>;
readonly openUndoSelector: ReturnType<typeof vi.fn>;
readonly cancelRunningShellCommand: ReturnType<typeof vi.fn>;
readonly cancelCompaction: ReturnType<typeof vi.fn>;
readonly btwCancelRunning: ReturnType<typeof vi.fn>;
readonly btwCloseOrCancel: ReturnType<typeof vi.fn>;
}
function createHarness(options: { streamingPhase?: string; isCompacting?: boolean } = {}): Harness {
const editor: Record<string, ((...args: never[]) => unknown) | undefined> = {
setHistoryFilter: vi.fn() as unknown as (...args: never[]) => unknown,
setInputMode: vi.fn() as unknown as (...args: never[]) => unknown,
getText: vi.fn(() => '') as unknown as (...args: never[]) => unknown,
setText: vi.fn() as unknown as (...args: never[]) => unknown,
};
const openUndoSelector = vi.fn();
const cancelRunningShellCommand = vi.fn();
const session = { cancel: vi.fn(async () => {}) };
const cancelCompaction = vi.fn(async () => {});
const btwCancelRunning = vi.fn(() => false);
const btwCloseOrCancel = vi.fn(() => false);
const session = { cancel: vi.fn(async () => {}), cancelCompaction };
const host = {
state: {
@ -35,7 +43,7 @@ function createHarness(options: { streamingPhase?: string; isCompacting?: boolea
ui: { requestRender: vi.fn() },
},
session,
btwPanelController: { closeOrCancel: vi.fn(() => false) },
btwPanelController: { cancelRunning: btwCancelRunning, closeOrCancel: btwCloseOrCancel },
openUndoSelector,
cancelRunningShellCommand,
} as unknown as EditorKeyboardHost;
@ -46,7 +54,15 @@ function createHarness(options: { streamingPhase?: string; isCompacting?: boolea
);
controller.install();
return { host, editor, openUndoSelector, cancelRunningShellCommand };
return {
host,
editor,
openUndoSelector,
cancelRunningShellCommand,
cancelCompaction,
btwCancelRunning,
btwCloseOrCancel,
};
}
function pressEscape(editor: Harness['editor']): void {
@ -55,6 +71,12 @@ function pressEscape(editor: Harness['editor']): void {
(handler as () => void)();
}
function pressCtrlC(editor: Harness['editor']): void {
const handler = editor['onCtrlC'];
if (handler === undefined) throw new Error('onCtrlC handler not installed');
(handler as () => void)();
}
function pressNonEscape(editor: Harness['editor']): void {
const handler = editor['onNonEscapeInput'];
if (handler === undefined) throw new Error('onNonEscapeInput handler not installed');
@ -123,6 +145,70 @@ describe('EditorKeyboardController double-Esc undo', () => {
});
});
describe('EditorKeyboardController btw panel priority', () => {
it('Esc closes the btw panel first while compacting, without cancelling compaction', () => {
const { editor, btwCloseOrCancel, cancelCompaction } = createHarness({ isCompacting: true });
btwCloseOrCancel.mockReturnValue(true);
pressEscape(editor);
expect(btwCloseOrCancel).toHaveBeenCalledOnce();
expect(cancelCompaction).not.toHaveBeenCalled();
});
it('Esc cancels compaction on the next press once the btw panel is gone', () => {
const { editor, btwCloseOrCancel, cancelCompaction } = createHarness({ isCompacting: true });
btwCloseOrCancel.mockReturnValueOnce(true);
pressEscape(editor);
expect(cancelCompaction).not.toHaveBeenCalled();
pressEscape(editor);
expect(cancelCompaction).toHaveBeenCalledOnce();
});
it('Esc cancels compaction directly when no btw panel is open', () => {
const { editor, btwCloseOrCancel, cancelCompaction } = createHarness({ isCompacting: true });
pressEscape(editor);
expect(btwCloseOrCancel).toHaveBeenCalledOnce();
expect(cancelCompaction).toHaveBeenCalledOnce();
});
it('Ctrl+C cancels a running btw question first while compacting', () => {
const { editor, btwCancelRunning, cancelCompaction } = createHarness({ isCompacting: true });
btwCancelRunning.mockReturnValue(true);
pressCtrlC(editor);
expect(btwCancelRunning).toHaveBeenCalledOnce();
expect(cancelCompaction).not.toHaveBeenCalled();
});
it('Ctrl+C closes an idle btw panel while compacting, without cancelling compaction', () => {
const { editor, btwCloseOrCancel, cancelCompaction } = createHarness({ isCompacting: true });
btwCloseOrCancel.mockReturnValue(true);
pressCtrlC(editor);
expect(btwCloseOrCancel).toHaveBeenCalledOnce();
expect(cancelCompaction).not.toHaveBeenCalled();
});
it('Ctrl+C cancels compaction when no btw panel is open', () => {
const { editor, btwCancelRunning, btwCloseOrCancel, cancelCompaction } = createHarness({
isCompacting: true,
});
pressCtrlC(editor);
expect(btwCancelRunning).toHaveBeenCalledOnce();
expect(btwCloseOrCancel).toHaveBeenCalledOnce();
expect(cancelCompaction).toHaveBeenCalledOnce();
});
});
describe('EditorKeyboardController shell history recall', () => {
type Recall = (entry: string, direction: 1 | -1) => string | undefined;
type Mock = ReturnType<typeof vi.fn>;