/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import React from 'react'; import { Box, Text, useInput } from 'ink'; import SelectInput from 'ink-select-input'; import { PartListUnion } from '@google/genai'; import { DiffRenderer } from './DiffRenderer.js'; import { UI_WIDTH } from '../../constants.js'; import { Colors } from '../../colors.js'; import { ToolCallConfirmationDetails, ToolEditConfirmationDetails, ToolConfirmationOutcome, ToolExecuteConfirmationDetails, } from '@gemini-code/server'; export interface ToolConfirmationMessageProps { confirmationDetails: ToolCallConfirmationDetails; onSubmit: (value: PartListUnion) => void; } function isEditDetails( props: ToolCallConfirmationDetails, ): props is ToolEditConfirmationDetails { return (props as ToolEditConfirmationDetails).fileName !== undefined; } interface InternalOption { label: string; value: ToolConfirmationOutcome; } export const ToolConfirmationMessage: React.FC< ToolConfirmationMessageProps > = ({ confirmationDetails }) => { const { onConfirm } = confirmationDetails; useInput((_, key) => { if (key.escape) { onConfirm(ToolConfirmationOutcome.Cancel); } }); const handleSelect = (item: InternalOption) => { onConfirm(item.value); }; let bodyContent: React.ReactNode | null = null; // Removed contextDisplay here let question: string; const options: InternalOption[] = []; if (isEditDetails(confirmationDetails)) { // Body content is now the DiffRenderer, passing filename to it // The bordered box is removed from here and handled within DiffRenderer bodyContent = ; question = `Apply this change?`; options.push( { label: 'Yes', value: ToolConfirmationOutcome.ProceedOnce, }, { label: 'Yes (always allow)', value: ToolConfirmationOutcome.ProceedAlways, }, { label: 'No (esc)', value: ToolConfirmationOutcome.Cancel }, ); } else { const executionProps = confirmationDetails as ToolExecuteConfirmationDetails; // For execution, we still need context display and description const commandDisplay = ( {executionProps.command} ); // Combine command and description into bodyContent for layout consistency bodyContent = ( {commandDisplay} ); question = `Allow execution?`; const alwaysLabel = `Yes (always allow '${executionProps.rootCommand}' commands)`; options.push( { label: 'Yes', value: ToolConfirmationOutcome.ProceedOnce, }, { label: alwaysLabel, value: ToolConfirmationOutcome.ProceedAlways, }, { label: 'No (esc)', value: ToolConfirmationOutcome.Cancel }, ); } return ( {/* Body Content (Diff Renderer or Command Info) */} {/* No separate context display here anymore for edits */} {bodyContent} {/* Confirmation Question */} {question} {/* Select Input for Options */} ); }; function Indicator({ isSelected = false }): React.JSX.Element { return ( {isSelected ? '◉ ' : '○ '} ); } export type ItemProps = { readonly isSelected?: boolean; readonly label: string; }; function Item({ isSelected = false, label }: ItemProps): React.JSX.Element { return ( {label} ); }