/** * @license * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ <<<<<<< HEAD import { Box, useIsScreenReaderEnabled } from 'ink'; import { useState, useEffect } from 'react'; import { useConfig } from '../contexts/ConfigContext.js'; import { useSettings } from '../contexts/SettingsContext.js'; import { useUIState } from '../contexts/UIStateContext.js'; import { useUIActions } from '../contexts/UIActionsContext.js'; import { useVimMode } from '../contexts/VimModeContext.js'; import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js'; import { useTerminalSize } from '../hooks/useTerminalSize.js'; import { isNarrowWidth } from '../utils/isNarrowWidth.js'; ======= import { useState, useEffect, useMemo } from 'react'; import { Box, useIsScreenReaderEnabled } from 'ink'; import { CoreToolCallStatus } from '@google/gemini-cli-core'; import { LoadingIndicator } from './LoadingIndicator.js'; import { StatusDisplay } from './StatusDisplay.js'; >>>>>>> 96b0876e6 (feat(cli): unify session modes in footer and reorganize composer layout) import { ToastDisplay, shouldShowToast } from './ToastDisplay.js'; import { DetailedMessagesDisplay } from './DetailedMessagesDisplay.js'; import { ShortcutsHelp } from './ShortcutsHelp.js'; import { ShortcutsHint } from './ShortcutsHint.js'; import { InputPrompt } from './InputPrompt.js'; import { Footer } from './Footer.js'; import { StatusRow } from './StatusRow.js'; import { ShowMoreLines } from './ShowMoreLines.js'; import { QueuedMessageDisplay } from './QueuedMessageDisplay.js'; import { ContextUsageDisplay } from './ContextUsageDisplay.js'; import { HorizontalLine } from './shared/HorizontalLine.js'; import { OverflowProvider } from '../contexts/OverflowContext.js'; import { isNarrowWidth } from '../utils/isNarrowWidth.js'; import { useUIState } from '../contexts/UIStateContext.js'; import { useUIActions } from '../contexts/UIActionsContext.js'; import { useVimMode } from '../contexts/VimModeContext.js'; import { useConfig } from '../contexts/ConfigContext.js'; import { useSettings } from '../contexts/SettingsContext.js'; import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js'; import { StreamingState, type HistoryItemToolGroup } from '../types.js'; import { ConfigInitDisplay } from '../components/ConfigInitDisplay.js'; import { TodoTray } from './messages/Todo.js'; <<<<<<< HEAD import { useComposerStatus } from '../hooks/useComposerStatus.js'; ======= import { getInlineThinkingMode } from '../utils/inlineThinkingMode.js'; import { isContextUsageHigh } from '../utils/contextUsage.js'; >>>>>>> 96b0876e6 (feat(cli): unify session modes in footer and reorganize composer layout) export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { const config = useConfig(); const settings = useSettings(); const isScreenReaderEnabled = useIsScreenReaderEnabled(); const uiState = useUIState(); const uiActions = useUIActions(); const { vimEnabled, vimMode } = useVimMode(); const inlineThinkingMode = getInlineThinkingMode(settings); const terminalWidth = uiState.terminalWidth; const isNarrow = isNarrowWidth(terminalWidth); const debugConsoleMaxHeight = Math.floor(Math.max(terminalWidth * 0.2, 5)); const [suggestionsVisible, setSuggestionsVisible] = useState(false); const isAlternateBuffer = useAlternateBuffer(); <<<<<<< HEAD ======= const { showApprovalModeIndicator } = uiState; >>>>>>> 96b0876e6 (feat(cli): unify session modes in footer and reorganize composer layout) const showUiDetails = uiState.cleanUiDetailsVisible; const suggestionsPosition = isAlternateBuffer ? 'above' : 'below'; const hideContextSummary = suggestionsVisible && suggestionsPosition === 'above'; const { hasPendingActionRequired, shouldCollapseDuringApproval } = useComposerStatus(); const isPassiveShortcutsHelpState = uiState.isInputActive && uiState.streamingState === 'idle' && !hasPendingActionRequired; const { setShortcutsHelpVisible } = uiActions; useEffect(() => { if (uiState.shortcutsHelpVisible && !isPassiveShortcutsHelpState) { setShortcutsHelpVisible(false); } }, [ uiState.shortcutsHelpVisible, isPassiveShortcutsHelpState, setShortcutsHelpVisible, ]); <<<<<<< HEAD const showShortcutsHelp = uiState.shortcutsHelpVisible && uiState.streamingState === 'idle' && !hasPendingActionRequired; ======= const hideUiDetailsForSuggestions = suggestionsVisible && suggestionsPosition === 'above'; const hideShortcutsHintForSuggestions = hideUiDetailsForSuggestions; const isModelIdle = uiState.streamingState === StreamingState.Idle; const isModelResponding = uiState.streamingState === StreamingState.Responding; const isBufferEmpty = uiState.buffer.text.length === 0; const canShowShortcutsHint = (isModelIdle || isModelResponding) && isBufferEmpty && !hasPendingActionRequired; const [showShortcutsHintDebounced, setShowShortcutsHintDebounced] = useState(canShowShortcutsHint); useEffect(() => { if (!canShowShortcutsHint) { setShowShortcutsHintDebounced(false); return; } const timeout = setTimeout(() => { setShowShortcutsHintDebounced(true); }, 200); return () => clearTimeout(timeout); }, [canShowShortcutsHint]); /** * Use the setting if provided, otherwise default to true for the new UX. * This allows tests to override the collapse behavior. */ const shouldCollapseDuringApproval = settings.merged.ui.collapseDrawerDuringApproval !== false; >>>>>>> 96b0876e6 (feat(cli): unify session modes in footer and reorganize composer layout) if (hasPendingActionRequired && shouldCollapseDuringApproval) { return null; } const showShortcutsHelp = uiState.shortcutsHelpVisible && uiState.streamingState === StreamingState.Idle && !hasPendingActionRequired; const hasToast = shouldShowToast(uiState); <<<<<<< HEAD const hideUiDetailsForSuggestions = suggestionsVisible && suggestionsPosition === 'above'; // Mini Mode VIP Flags (Pure Content Triggers) const showMinimalToast = hasToast; ======= const showLoadingIndicator = (!uiState.embeddedShellFocused || uiState.isBackgroundShellVisible) && uiState.streamingState === StreamingState.Responding && !hasPendingActionRequired; const hasMinimalStatusBleedThrough = shouldShowToast(uiState); const showMinimalContextBleedThrough = !settings.merged.ui.footer.hideContextPercentage && isContextUsageHigh( uiState.sessionStats.lastPromptTokenCount, typeof uiState.currentModel === 'string' ? uiState.currentModel : undefined, ); const shouldReserveSpaceForShortcutsHint = settings.merged.ui.showShortcutsHint && !hideShortcutsHintForSuggestions && !hasPendingActionRequired; const showShortcutsHint = shouldReserveSpaceForShortcutsHint && showShortcutsHintDebounced; const showMinimalInlineLoading = !showUiDetails && showLoadingIndicator; const showMinimalBleedThroughRow = !showUiDetails && (hasMinimalStatusBleedThrough || showMinimalContextBleedThrough); const showMinimalMetaRow = !showUiDetails && (showMinimalInlineLoading || showMinimalBleedThroughRow || shouldReserveSpaceForShortcutsHint); const loadingPhrases = settings.merged.ui.loadingPhrases; const showTips = loadingPhrases === 'tips' || loadingPhrases === 'all'; const showWit = loadingPhrases === 'witty' || loadingPhrases === 'all'; >>>>>>> 96b0876e6 (feat(cli): unify session modes in footer and reorganize composer layout) return ( {(!uiState.slashCommands || !uiState.isConfigInitialized || uiState.isResuming) && ( )} {showUiDetails && ( )} {showUiDetails && } <<<<<<< HEAD {showShortcutsHelp && } {(showUiDetails || showMinimalToast) && ( )} ======= {showUiDetails && (hasToast ? : null)} {showUiDetails && showShortcutsHint && } {showMinimalMetaRow && ( {showMinimalInlineLoading && ( )} {hasMinimalStatusBleedThrough && ( )} {(showMinimalContextBleedThrough || shouldReserveSpaceForShortcutsHint) && ( {showMinimalContextBleedThrough && ( )} {showShortcutsHint && } )} )} {showShortcutsHelp && } {showUiDetails && } {showUiDetails && ( {showLoadingIndicator && ( )} {!showLoadingIndicator && ( )} )} >>>>>>> 96b0876e6 (feat(cli): unify session modes in footer and reorganize composer layout) {showUiDetails && uiState.showErrorDetails && ( )} {uiState.isInputActive && ( )} {showUiDetails && !settings.merged.ui.hideFooter && !isScreenReaderEnabled &&