diff --git a/packages/cli/src/test-utils/render.tsx b/packages/cli/src/test-utils/render.tsx index 6908fd36fb..21d5ecf4f9 100644 --- a/packages/cli/src/test-utils/render.tsx +++ b/packages/cli/src/test-utils/render.tsx @@ -604,6 +604,7 @@ const mockUIActions: UIActions = { revealCleanUiDetailsTemporarily: vi.fn(), handleWarning: vi.fn(), setEmbeddedShellFocused: vi.fn(), + setActivePtyId: vi.fn(), dismissBackgroundShell: vi.fn(), setActiveBackgroundShellPid: vi.fn(), setIsBackgroundShellListOpen: vi.fn(), diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index d42cad8495..eb30cb7a55 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -1109,6 +1109,7 @@ Logging in with Google... Restarting Gemini CLI to continue. backgroundShells, dismissBackgroundShell, retryStatus, + setActivePtyId, } = useGeminiStream( config.getGeminiClient(), historyManager.history, @@ -2509,6 +2510,7 @@ Logging in with Google... Restarting Gemini CLI to continue. revealCleanUiDetailsTemporarily, handleWarning, setEmbeddedShellFocused, + setActivePtyId, dismissBackgroundShell, setActiveBackgroundShellPid, setIsBackgroundShellListOpen, @@ -2601,6 +2603,7 @@ Logging in with Google... Restarting Gemini CLI to continue. revealCleanUiDetailsTemporarily, handleWarning, setEmbeddedShellFocused, + setActivePtyId, dismissBackgroundShell, setActiveBackgroundShellPid, setIsBackgroundShellListOpen, diff --git a/packages/cli/src/ui/components/messages/ShellToolMessage.tsx b/packages/cli/src/ui/components/messages/ShellToolMessage.tsx index 8e760b28e7..a7758c3390 100644 --- a/packages/cli/src/ui/components/messages/ShellToolMessage.tsx +++ b/packages/cli/src/ui/components/messages/ShellToolMessage.tsx @@ -78,7 +78,7 @@ export const ShellToolMessage: React.FC = ({ embeddedShellFocused, ); - const { setEmbeddedShellFocused } = useUIActions(); + const { setEmbeddedShellFocused, setActivePtyId } = useUIActions(); const wasFocusedRef = React.useRef(false); React.useEffect(() => { @@ -102,6 +102,7 @@ export const ShellToolMessage: React.FC = ({ const handleFocus = () => { if (isThisShellFocusable) { + setActivePtyId(ptyId ?? null); setEmbeddedShellFocused(true); } }; diff --git a/packages/cli/src/ui/components/shared/Scrollable.tsx b/packages/cli/src/ui/components/shared/Scrollable.tsx index 50591b6f96..ba6699254a 100644 --- a/packages/cli/src/ui/components/shared/Scrollable.tsx +++ b/packages/cli/src/ui/components/shared/Scrollable.tsx @@ -209,7 +209,7 @@ export const Scrollable: React.FC = ({ width={width ?? maxWidth} height={height} flexDirection="column" - overflowY="scroll" + overflowY={hasFocus ? 'scroll' : 'hidden'} overflowX="hidden" scrollTop={scrollTop} flexGrow={flexGrow} diff --git a/packages/cli/src/ui/contexts/UIActionsContext.tsx b/packages/cli/src/ui/contexts/UIActionsContext.tsx index 988837df4d..0c850c8717 100644 --- a/packages/cli/src/ui/contexts/UIActionsContext.tsx +++ b/packages/cli/src/ui/contexts/UIActionsContext.tsx @@ -80,6 +80,7 @@ export interface UIActions { revealCleanUiDetailsTemporarily: (durationMs?: number) => void; handleWarning: (message: string) => void; setEmbeddedShellFocused: (value: boolean) => void; + setActivePtyId: (pid: number | null) => void; dismissBackgroundShell: (pid: number) => void; setActiveBackgroundShellPid: (pid: number) => void; setIsBackgroundShellListOpen: (isOpen: boolean) => void; diff --git a/packages/cli/src/ui/hooks/shellCommandProcessor.ts b/packages/cli/src/ui/hooks/shellCommandProcessor.ts index 364b395876..a3698ebf4e 100644 --- a/packages/cli/src/ui/hooks/shellCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/shellCommandProcessor.ts @@ -152,6 +152,13 @@ export const useShellCommandProcessor = ( [m], ); + const setActivePtyId = useCallback( + (pid: number | null) => { + dispatch({ type: 'SET_ACTIVE_PTY', pid }); + }, + [dispatch], + ); + const toggleBackgroundShell = useCallback(() => { if (state.backgroundShells.size > 0) { const willBeVisible = !state.isBackgroundShellVisible; @@ -550,5 +557,6 @@ export const useShellCommandProcessor = ( registerBackgroundShell, dismissBackgroundShell, backgroundShells: state.backgroundShells, + setActivePtyId, }; }; diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts index 36374a5e20..e3328b4795 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.ts +++ b/packages/cli/src/ui/hooks/useGeminiStream.ts @@ -204,9 +204,8 @@ export const useGeminiStream = ( consumeUserHint?: () => string | null, ) => { const [initError, setInitError] = useState(null); - const [retryStatus, setRetryStatus] = useState( - null, - ); + const [modelRetryStatus, setModelRetryStatus] = + useState(null); const isLowErrorVerbosity = settings.merged.ui?.errorVerbosity !== 'full'; const suppressedToolErrorCountRef = useRef(0); const suppressedToolErrorNoteShownRef = useRef(false); @@ -242,7 +241,7 @@ export const useGeminiStream = ( useEffect(() => { const handleRetryAttempt = (payload: RetryAttemptPayload) => { - setRetryStatus(payload); + setModelRetryStatus(payload); }; coreEvents.on(CoreEvent.RetryAttempt, handleRetryAttempt); return () => { @@ -338,6 +337,7 @@ export const useGeminiStream = ( registerBackgroundShell, dismissBackgroundShell, backgroundShells, + setActivePtyId, } = useShellCommandProcessor( addItem, setPendingHistoryItem, @@ -564,7 +564,7 @@ export const useGeminiStream = ( useEffect(() => { if (!isResponding) { - setRetryStatus(null); + setModelRetryStatus(null); } }, [isResponding]); @@ -844,7 +844,7 @@ export const useGeminiStream = ( currentGeminiMessageBuffer: string, userMessageTimestamp: number, ): string => { - setRetryStatus(null); + setModelRetryStatus(null); if (turnCancelledRef.current) { // Prevents additional output after a user initiated cancel. return ''; @@ -1897,6 +1897,7 @@ export const useGeminiStream = ( backgroundCurrentShell, backgroundShells, dismissBackgroundShell, - retryStatus, + retryStatus: modelRetryStatus, + setActivePtyId, }; };