/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import React from 'react'; import { Box, Text } from 'ink'; import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js'; import { DiffRenderer } from './DiffRenderer.js'; import { Colors } from '../../colors.js'; import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js'; import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js'; import { MaxSizedBox } from '../shared/MaxSizedBox.js'; const STATIC_HEIGHT = 1; const RESERVED_LINE_COUNT = 5; // for tool name, status, padding etc. const STATUS_INDICATOR_WIDTH = 3; const MIN_LINES_SHOWN = 2; // show at least this many lines export type TextEmphasis = 'high' | 'medium' | 'low'; export interface ToolMessageProps extends IndividualToolCallDisplay { availableTerminalHeight?: number; terminalWidth: number; emphasis?: TextEmphasis; renderOutputAsMarkdown?: boolean; } export const ToolMessage: React.FC = ({ name, description, resultDisplay, status, availableTerminalHeight, terminalWidth, emphasis = 'medium', renderOutputAsMarkdown = true, }) => { const availableHeight = availableTerminalHeight ? Math.max( availableTerminalHeight - STATIC_HEIGHT - RESERVED_LINE_COUNT, MIN_LINES_SHOWN + 1, // enforce minimum lines shown ) : undefined; const childWidth = terminalWidth - 3; // account for padding. return ( {emphasis === 'high' && } {resultDisplay && ( {typeof resultDisplay === 'string' && renderOutputAsMarkdown && ( )} {typeof resultDisplay === 'string' && !renderOutputAsMarkdown && ( {resultDisplay} )} {typeof resultDisplay !== 'string' && ( )} )} ); }; type ToolStatusIndicatorProps = { status: ToolCallStatus; }; const ToolStatusIndicator: React.FC = ({ status, }) => ( {status === ToolCallStatus.Pending && ( o )} {status === ToolCallStatus.Executing && ( )} {status === ToolCallStatus.Success && ( )} {status === ToolCallStatus.Confirming && ( ? )} {status === ToolCallStatus.Canceled && ( - )} {status === ToolCallStatus.Error && ( x )} ); type ToolInfo = { name: string; description: string; status: ToolCallStatus; emphasis: TextEmphasis; }; const ToolInfo: React.FC = ({ name, description, status, emphasis, }) => { const nameColor = React.useMemo(() => { switch (emphasis) { case 'high': return Colors.Foreground; case 'medium': return Colors.Foreground; case 'low': return Colors.Gray; default: { const exhaustiveCheck: never = emphasis; return exhaustiveCheck; } } }, [emphasis]); return ( {name} {' '} {description} ); }; const TrailingIndicator: React.FC = () => ( {' '} ← );