/**
* @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}
);
}