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();