/** * @license * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { Box, Text } from 'ink'; import { theme } from '../semantic-colors.js'; import { useConfig } from '../contexts/ConfigContext.js'; import { ToolConfirmationMessage } from './messages/ToolConfirmationMessage.js'; import { ToolStatusIndicator, ToolInfo } from './messages/ToolShared.js'; import { useUIState } from '../contexts/UIStateContext.js'; import type { ConfirmingToolState } from '../hooks/useConfirmingTool.js'; import { StickyHeader } from './StickyHeader.js'; import type { SerializableConfirmationDetails } from '@google/gemini-cli-core'; import { useUIActions } from '../contexts/UIActionsContext.js'; function getConfirmationHeader( details: SerializableConfirmationDetails | undefined, ): string { const headers: Partial< Record > = { ask_user: 'Answer Questions', exit_plan_mode: 'Ready to start implementation?', }; if (!details?.type) { return 'Action Required'; } return headers[details.type] ?? 'Action Required'; } interface ToolConfirmationQueueProps { confirmingTool: ConfirmingToolState; } export const ToolConfirmationQueue: React.FC = ({ confirmingTool, }) => { const config = useConfig(); const { getPreferredEditor } = useUIActions(); const { mainAreaWidth, terminalHeight, constrainHeight, availableTerminalHeight: uiAvailableHeight, } = useUIState(); const { tool, index, total } = confirmingTool; // Safety check: ToolConfirmationMessage requires confirmationDetails if (!tool.confirmationDetails) return null; // Render up to 100% of the available terminal height // to maximize space for diffs and other content. const maxHeight = uiAvailableHeight !== undefined ? Math.max(uiAvailableHeight, 4) : Math.floor(terminalHeight * 0.5); const isRoutine = tool.confirmationDetails?.type === 'ask_user' || tool.confirmationDetails?.type === 'exit_plan_mode'; const borderColor = isRoutine ? theme.status.success : theme.status.warning; const hideToolIdentity = isRoutine; // ToolConfirmationMessage needs to know the height available for its OWN content. // We subtract the lines used by the Queue wrapper: // - 2 lines for the rounded border (top/bottom) // - 2 lines for the Header (text + margin) // - 2 lines for Tool Identity (text + margin) if shown const availableContentHeight = constrainHeight ? Math.max(maxHeight - (hideToolIdentity ? 4 : 6), 4) : undefined; const content = ( {/* Header */} {getConfirmationHeader(tool.confirmationDetails)} {total > 1 && ( {index} of {total} )} {!hideToolIdentity && ( )} {/* Interactive Area */} {/* Note: We force isFocused={true} because if this component is rendered, it effectively acts as a modal over the shell/composer. */} ); return content; };