From 9f81d3752c104d1eb917ec80ba43f71af97d9254 Mon Sep 17 00:00:00 2001 From: cynthialong0-0 Date: Thu, 14 May 2026 14:54:56 +0000 Subject: [PATCH] improve --- packages/cli/src/ui/AppContainer.test.tsx | 47 ++++++++++++++++++++++- packages/cli/src/ui/AppContainer.tsx | 21 ++++++---- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/ui/AppContainer.test.tsx b/packages/cli/src/ui/AppContainer.test.tsx index ff87c64e39..382aa6aa66 100644 --- a/packages/cli/src/ui/AppContainer.test.tsx +++ b/packages/cli/src/ui/AppContainer.test.tsx @@ -2128,9 +2128,10 @@ describe('AppContainer State Management', () => { describe('CTRL+C', () => { it('should not trigger quit flow when input buffer has text', async () => { + const setText = vi.fn(); mockedUseTextBuffer.mockReturnValue({ text: 'hello world', - setText: vi.fn(), + setText, lines: ['hello world'], cursor: [0, 11], handleInput: vi.fn().mockReturnValue(false), @@ -2144,6 +2145,50 @@ describe('AppContainer State Management', () => { unmount(); }); + it('should clear buffer and reset quit count when Ctrl+C is pressed with text', async () => { + const setText = vi.fn(); + const bufferWithText = { + text: 'hello world', + setText, + lines: ['hello world'], + cursor: [0, 11], + handleInput: vi.fn().mockReturnValue(false), + }; + const bufferEmpty = { + text: '', + setText, + lines: [''], + cursor: [0, 0], + handleInput: vi.fn().mockReturnValue(false), + }; + + // First press with text + mockedUseTextBuffer.mockReturnValue(bufferWithText); + await setupKeypressTest(); + pressKey('\x03'); // Ctrl+C + + expect(mockCancelOngoingRequest).not.toHaveBeenCalled(); + expect(mockHandleSlashCommand).not.toHaveBeenCalled(); + + // Second press with empty buffer (simulating clear happened) + // Since we reset the count, this should be treated as the "first" press for quit flow + mockedUseTextBuffer.mockReturnValue(bufferEmpty); + pressKey('\x03'); // Ctrl+C + expect(mockCancelOngoingRequest).toHaveBeenCalledTimes(1); + expect(mockHandleSlashCommand).not.toHaveBeenCalled(); + + // Third press with empty buffer should finally quit + pressKey('\x03'); // Ctrl+C + expect(mockCancelOngoingRequest).toHaveBeenCalledTimes(2); + expect(mockHandleSlashCommand).toHaveBeenCalledWith( + '/quit', + undefined, + undefined, + false, + ); + unmount(); + }); + it('should cancel ongoing request on first press', async () => { mockedUseGeminiStream.mockReturnValue({ ...DEFAULT_GEMINI_STREAM_MOCK, diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index 27597d5963..9bb6a6fdeb 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -1835,16 +1835,24 @@ Logging in with Google... Restarting Gemini CLI to continue. if ( keyMatchers[Command.QUIT](key) && - keyMatchers[Command.CLEAR_INPUT](key) && isInputActive && streamingState === StreamingState.Idle && - buffer.text.length > 0 + bufferRef.current.text.length > 0 ) { - // Let InputPrompt handle Ctrl+C as edit.clear when there is text. - // This preserves documented behavior and allows prompt-level state - // (completions/history) to reset correctly. + // Prevent quit flow when there is text. resetCtrlCPress(); - return false; + + if (keyMatchers[Command.CLEAR_INPUT](key)) { + // Let InputPrompt handle it as edit.clear. + // This preserves documented behavior and allows prompt-level state + // (completions/history) to reset correctly. + return false; + } + + // If QUIT is not also CLEAR_INPUT, we should still clear the buffer + // to match user expectations for Ctrl+C (or equivalent). + bufferRef.current.setText(''); + return true; } if (keyMatchers[Command.QUIT](key)) { @@ -2081,7 +2089,6 @@ Logging in with Google... Restarting Gemini CLI to continue. mouseMode, isInputActive, streamingState, - buffer, resetCtrlCPress, ], );