From a779d44b38438d8628cf64af76d480cb2b56b51f Mon Sep 17 00:00:00 2001 From: Mingholy Date: Tue, 14 Oct 2025 15:55:38 +0800 Subject: [PATCH] fix: unable to quit when auth dialog is opened (#804) --- packages/cli/src/ui/App.tsx | 17 ++++++++++++++++- .../ui/components/QwenOAuthProgress.test.tsx | 4 ++-- .../cli/src/ui/components/QwenOAuthProgress.tsx | 8 +++++--- packages/cli/src/ui/hooks/useDialogClose.ts | 10 ---------- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index 26090018..1597c765 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -913,7 +913,21 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { return; } - // 1. Close other dialogs (highest priority) + /** + * For AuthDialog it is required to complete the authentication process, + * otherwise user cannot proceed to the next step. + * So a quit on AuthDialog should go with normal two press quit + * and without quit-confirm dialog. + */ + if (isAuthDialogOpen) { + setPressedOnce(true); + timerRef.current = setTimeout(() => { + setPressedOnce(false); + }, 500); + return; + } + + //1. Close other dialogs (highest priority) if (closeAnyOpenDialog()) { return; // Dialog closed, end processing } @@ -934,6 +948,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { handleSlashCommand('/quit-confirm'); }, [ + isAuthDialogOpen, handleSlashCommand, quitConfirmationRequest, closeAnyOpenDialog, diff --git a/packages/cli/src/ui/components/QwenOAuthProgress.test.tsx b/packages/cli/src/ui/components/QwenOAuthProgress.test.tsx index 470e326b..b9d5201f 100644 --- a/packages/cli/src/ui/components/QwenOAuthProgress.test.tsx +++ b/packages/cli/src/ui/components/QwenOAuthProgress.test.tsx @@ -81,7 +81,7 @@ describe('QwenOAuthProgress', () => { const output = lastFrame(); expect(output).toContain('MockSpinner(dots)'); expect(output).toContain('Waiting for Qwen OAuth authentication...'); - expect(output).toContain('(Press ESC to cancel)'); + expect(output).toContain('(Press ESC or CTRL+C to cancel)'); }); it('should render loading state with gray border', () => { @@ -105,7 +105,7 @@ describe('QwenOAuthProgress', () => { expect(output).toContain('MockSpinner(dots)'); expect(output).toContain('Waiting for authorization'); expect(output).toContain('Time remaining: 5:00'); - expect(output).toContain('(Press ESC to cancel)'); + expect(output).toContain('(Press ESC or CTRL+C to cancel)'); }); it('should display correct URL in Static component when QR code is generated', async () => { diff --git a/packages/cli/src/ui/components/QwenOAuthProgress.tsx b/packages/cli/src/ui/components/QwenOAuthProgress.tsx index 8fe6b47f..5271a67f 100644 --- a/packages/cli/src/ui/components/QwenOAuthProgress.tsx +++ b/packages/cli/src/ui/components/QwenOAuthProgress.tsx @@ -110,7 +110,7 @@ function StatusDisplay({ Time remaining: {formatTime(timeRemaining)} - (Press ESC to cancel) + (Press ESC or CTRL+C to cancel) ); @@ -132,7 +132,7 @@ export function QwenOAuthProgress({ if (authStatus === 'timeout') { // Any key press in timeout state should trigger cancel to return to auth dialog onCancel(); - } else if (key.escape) { + } else if (key.escape || (key.ctrl && input === 'c')) { onCancel(); } }); @@ -250,7 +250,9 @@ export function QwenOAuthProgress({ Time remaining: {Math.floor(timeRemaining / 60)}: {(timeRemaining % 60).toString().padStart(2, '0')} - (Press ESC to cancel) + + (Press ESC or CTRL+C to cancel) + ); diff --git a/packages/cli/src/ui/hooks/useDialogClose.ts b/packages/cli/src/ui/hooks/useDialogClose.ts index 1711210a..504f2f2e 100644 --- a/packages/cli/src/ui/hooks/useDialogClose.ts +++ b/packages/cli/src/ui/hooks/useDialogClose.ts @@ -61,16 +61,6 @@ export function useDialogClose(options: DialogCloseOptions) { return true; } - if (options.isAuthDialogOpen) { - // Mimic ESC behavior: only close if already authenticated (same as AuthDialog ESC logic) - if (options.selectedAuthType !== undefined) { - // Note: We don't await this since we want non-blocking behavior like ESC - void options.handleAuthSelect(undefined, SettingScope.User); - } - // Note: AuthDialog prevents ESC exit if not authenticated, we follow same logic - return true; - } - if (options.isEditorDialogOpen) { // Mimic ESC behavior: call onExit() directly options.exitEditorDialog();