diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index c4b39696c..3c6f829c9 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -1208,8 +1208,7 @@ export const AppContainer = (props: AppContainerProps) => { isSubagentCreateDialogOpen || isAgentsManagerDialogOpen || isApprovalModeDialogOpen || - isResumeDialogOpen || - isFeedbackDialogOpen; + isResumeDialogOpen; const pendingHistoryItems = useMemo( () => [...pendingSlashCommandHistoryItems, ...pendingGeminiHistoryItems], diff --git a/packages/cli/src/ui/FeedbackDialog.tsx b/packages/cli/src/ui/FeedbackDialog.tsx index 187cde7d6..3984664d6 100644 --- a/packages/cli/src/ui/FeedbackDialog.tsx +++ b/packages/cli/src/ui/FeedbackDialog.tsx @@ -5,31 +5,39 @@ import { useUIActions } from './contexts/UIActionsContext.js'; import { useUIState } from './contexts/UIStateContext.js'; import { useKeypress } from './hooks/useKeypress.js'; +const FEEDBACK_OPTIONS = { + GOOD: 1, + BAD: 2, + NOT_SURE: 3, +} as const; + +const FEEDBACK_OPTION_KEYS = { + [FEEDBACK_OPTIONS.GOOD]: '1', + [FEEDBACK_OPTIONS.BAD]: '2', + [FEEDBACK_OPTIONS.NOT_SURE]: '3', +} as const; + +export const FEEDBACK_DIALOG_KEYS = ['1', '2', '3'] as const; + export const FeedbackDialog: React.FC = () => { const uiState = useUIState(); const uiActions = useUIActions(); useKeypress( (key) => { - if (key.name === 'escape') { - uiActions.closeFeedbackDialog(); - } else if (key.name === '1') { - uiActions.submitFeedback(1); - } else if (key.name === '2') { - uiActions.submitFeedback(2); - } else if (key.name === '3') { - uiActions.submitFeedback(3); - } else if (key.name === '0') { - uiActions.closeFeedbackDialog(); + if (key.name === FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.GOOD]) { + uiActions.submitFeedback(FEEDBACK_OPTIONS.GOOD); + } else if (key.name === FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.BAD]) { + uiActions.submitFeedback(FEEDBACK_OPTIONS.BAD); + } else if (key.name === FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.NOT_SURE]) { + uiActions.submitFeedback(FEEDBACK_OPTIONS.NOT_SURE); } + + uiActions.closeFeedbackDialog(); }, { isActive: uiState.isFeedbackDialogOpen }, ); - if (!uiState.isFeedbackDialogOpen) { - return null; - } - return ( @@ -37,13 +45,17 @@ export const FeedbackDialog: React.FC = () => { {t('How is Qwen doing this session? (optional)')} - 1: + + {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.GOOD]}:{' '} + {t('Good')} - 2: + {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.BAD]}: {t('Bad')} - 3: + + {FEEDBACK_OPTION_KEYS[FEEDBACK_OPTIONS.NOT_SURE]}:{' '} + {t('Not Sure Yet')} diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx index 1b51227a1..9052e4f4d 100644 --- a/packages/cli/src/ui/components/Composer.tsx +++ b/packages/cli/src/ui/components/Composer.tsx @@ -26,6 +26,7 @@ import { useSettings } from '../contexts/SettingsContext.js'; import { ApprovalMode } from '@qwen-code/qwen-code-core'; import { StreamingState } from '../types.js'; import { ConfigInitDisplay } from '../components/ConfigInitDisplay.js'; +import { FeedbackDialog } from '../FeedbackDialog.js'; import { t } from '../../i18n/index.js'; export const Composer = () => { @@ -134,6 +135,8 @@ export const Composer = () => { )} + {uiState.isFeedbackDialogOpen && } + {uiState.isInputActive && ( ; - } - return null; }; diff --git a/packages/cli/src/ui/components/InputPrompt.test.tsx b/packages/cli/src/ui/components/InputPrompt.test.tsx index cf8b9685c..c34578294 100644 --- a/packages/cli/src/ui/components/InputPrompt.test.tsx +++ b/packages/cli/src/ui/components/InputPrompt.test.tsx @@ -33,6 +33,9 @@ vi.mock('../hooks/useCommandCompletion.js'); vi.mock('../hooks/useInputHistory.js'); vi.mock('../hooks/useReverseSearchCompletion.js'); vi.mock('../utils/clipboardUtils.js'); +vi.mock('../contexts/UIStateContext.js', () => ({ + useUIState: vi.fn(() => ({ isFeedbackDialogOpen: false })), +})); const mockSlashCommands: SlashCommand[] = [ { diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx index 7d1742505..1f7ed099a 100644 --- a/packages/cli/src/ui/components/InputPrompt.tsx +++ b/packages/cli/src/ui/components/InputPrompt.tsx @@ -36,6 +36,8 @@ import { import * as path from 'node:path'; import { SCREEN_READER_USER_PREFIX } from '../textConstants.js'; import { useShellFocusState } from '../contexts/ShellFocusContext.js'; +import { useUIState } from '../contexts/UIStateContext.js'; +import { FEEDBACK_DIALOG_KEYS } from '../FeedbackDialog.js'; export interface InputPromptProps { buffer: TextBuffer; onSubmit: (value: string) => void; @@ -100,6 +102,7 @@ export const InputPrompt: React.FC = ({ isEmbeddedShellFocused, }) => { const isShellFocused = useShellFocusState(); + const uiState = useUIState(); const [justNavigatedHistory, setJustNavigatedHistory] = useState(false); const [escPressCount, setEscPressCount] = useState(0); const [showEscapePrompt, setShowEscapePrompt] = useState(false); @@ -326,6 +329,14 @@ export const InputPrompt: React.FC = ({ return; } + // Intercept feedback dialog option keys (1, 2, 3) when dialog is open + if ( + uiState.isFeedbackDialogOpen && + (FEEDBACK_DIALOG_KEYS as readonly string[]).includes(key.name) + ) { + return; + } + // Reset ESC count and hide prompt on any non-ESC key if (key.name !== 'escape') { if (escPressCount > 0 || showEscapePrompt) { @@ -670,6 +681,7 @@ export const InputPrompt: React.FC = ({ recentPasteTime, commandSearchActive, commandSearchCompletion, + uiState, ], );