/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { useState } from 'react'; import { Box, Text, useIsScreenReaderEnabled } from 'ink'; import { LoadingIndicator } from './LoadingIndicator.js'; import { StatusDisplay } from './StatusDisplay.js'; import { ApprovalModeIndicator } from './ApprovalModeIndicator.js'; import { ShellModeIndicator } from './ShellModeIndicator.js'; import { DetailedMessagesDisplay } from './DetailedMessagesDisplay.js'; import { RawMarkdownIndicator } from './RawMarkdownIndicator.js'; import { ShortcutsHint } from './ShortcutsHint.js'; import { ShortcutsHelp } from './ShortcutsHelp.js'; import { InputPrompt } from './InputPrompt.js'; import { Footer } from './Footer.js'; import { ShowMoreLines } from './ShowMoreLines.js'; import { QueuedMessageDisplay } from './QueuedMessageDisplay.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, ToolCallStatus } from '../types.js'; import { ConfigInitDisplay } from '../components/ConfigInitDisplay.js'; import { TodoTray } from './messages/Todo.js'; import { theme } from '../semantic-colors.js'; 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 terminalWidth = process.stdout.columns; const isNarrow = isNarrowWidth(terminalWidth); const debugConsoleMaxHeight = Math.floor(Math.max(terminalWidth * 0.2, 5)); const [suggestionsVisible, setSuggestionsVisible] = useState(false); const isAlternateBuffer = useAlternateBuffer(); const { showApprovalModeIndicator } = uiState; const suggestionsPosition = isAlternateBuffer ? 'above' : 'below'; const hideContextSummary = suggestionsVisible && suggestionsPosition === 'above'; const hasPendingToolConfirmation = (uiState.pendingHistoryItems ?? []).some( (item) => item.type === 'tool_group' && item.tools.some((tool) => tool.status === ToolCallStatus.Confirming), ); const hasPendingActionRequired = hasPendingToolConfirmation || Boolean(uiState.commandConfirmationRequest) || Boolean(uiState.authConsentRequest) || (uiState.confirmUpdateExtensionRequests?.length ?? 0) > 0 || Boolean(uiState.loopDetectionConfirmationRequest) || Boolean(uiState.proQuotaRequest) || Boolean(uiState.validationRequest) || Boolean(uiState.customDialog); const isActivelyStreaming = uiState.streamingState === StreamingState.Responding; const showLoadingIndicator = (!uiState.embeddedShellFocused || uiState.isBackgroundShellVisible) && (uiState.streamingState === StreamingState.Responding || uiState.streamingState === StreamingState.WaitingForConfirmation) && !hasPendingActionRequired; const showApprovalIndicator = !uiState.shellModeActive; const showRawMarkdownIndicator = !uiState.renderMarkdown; const showEscToCancelHint = isActivelyStreaming && (!uiState.embeddedShellFocused || uiState.isBackgroundShellVisible) && !hasPendingActionRequired && uiState.streamingState !== StreamingState.WaitingForConfirmation; return ( {(!uiState.slashCommands || !uiState.isConfigInitialized || uiState.isResuming) && ( )} {showEscToCancelHint && ( esc to cancel )} {showLoadingIndicator && ( )} {!hasPendingActionRequired && } {uiState.shortcutsHelpVisible && } {!isActivelyStreaming && ( {showApprovalIndicator && ( )} {uiState.shellModeActive && ( )} {showRawMarkdownIndicator && ( )} )} {!isActivelyStreaming && ( )} {uiState.showErrorDetails && ( )} {uiState.isInputActive && ( )} {!settings.merged.ui.hideFooter && !isScreenReaderEnabled &&