From 01971741e05a22e0402ac6c755ce0eced8f2930b Mon Sep 17 00:00:00 2001 From: Brandon Keiji Date: Fri, 23 May 2025 05:28:31 +0000 Subject: feat: add emphasis to tool confirmations (#502) --- .../cli/src/ui/components/messages/ToolMessage.tsx | 158 ++++++++++++++------- 1 file changed, 110 insertions(+), 48 deletions(-) (limited to 'packages/cli/src/ui/components/messages/ToolMessage.tsx') diff --git a/packages/cli/src/ui/components/messages/ToolMessage.tsx b/packages/cli/src/ui/components/messages/ToolMessage.tsx index 32b23b9e..32b3b7e8 100644 --- a/packages/cli/src/ui/components/messages/ToolMessage.tsx +++ b/packages/cli/src/ui/components/messages/ToolMessage.tsx @@ -12,8 +12,15 @@ import { DiffRenderer } from './DiffRenderer.js'; import { Colors } from '../../colors.js'; import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js'; +const STATIC_HEIGHT = 1; +const RESERVED_LINE_COUNT = 5; // for tool name, status, padding etc. +const STATUS_INDICATOR_WIDTH = 3; + +export type TextEmphasis = 'high' | 'medium' | 'low'; + export interface ToolMessageProps extends IndividualToolCallDisplay { availableTerminalHeight: number; + emphasis?: TextEmphasis; } export const ToolMessage: React.FC = ({ @@ -22,63 +29,45 @@ export const ToolMessage: React.FC = ({ resultDisplay, status, availableTerminalHeight, + emphasis = 'medium', }) => { - const statusIndicatorWidth = 3; - const hasResult = resultDisplay && resultDisplay.toString().trim().length > 0; - const staticHeight = /* Header */ 1; - - let displayableResult = resultDisplay; - let hiddenLines = 0; + const contentHeightEstimate = + availableTerminalHeight - STATIC_HEIGHT - RESERVED_LINE_COUNT; + const resultIsString = + typeof resultDisplay === 'string' && resultDisplay.trim().length > 0; + const lines = React.useMemo( + () => (resultIsString ? resultDisplay.split('\n') : []), + [resultIsString, resultDisplay], + ); // Truncate the overall string content if it's too long. // MarkdownRenderer will handle specific truncation for code blocks within this content. - if (typeof resultDisplay === 'string' && resultDisplay.length > 0) { - const lines = resultDisplay.split('\n'); - // Estimate available height for this specific tool message content area - // This is a rough estimate; ideally, we'd have a more precise measurement. - const contentHeightEstimate = availableTerminalHeight - staticHeight - 5; // Subtracting lines for tool name, status, padding etc. - if (lines.length > contentHeightEstimate && contentHeightEstimate > 0) { - displayableResult = lines.slice(0, contentHeightEstimate).join('\n'); - hiddenLines = lines.length - contentHeightEstimate; - } - } + // Estimate available height for this specific tool message content area + // This is a rough estimate; ideally, we'd have a more precise measurement. + const displayableResult = React.useMemo( + () => + resultIsString + ? lines.slice(0, contentHeightEstimate).join('\n') + : resultDisplay, + [lines, resultIsString, contentHeightEstimate, resultDisplay], + ); + const hiddenLines = lines.length - contentHeightEstimate; return ( {/* Status Indicator */} - - {(status === ToolCallStatus.Pending || - status === ToolCallStatus.Executing) && } - {status === ToolCallStatus.Success && ( - - )} - {status === ToolCallStatus.Confirming && ( - ? - )} - {status === ToolCallStatus.Canceled && ( - - - - - )} - {status === ToolCallStatus.Error && ( - - x - - )} - - - - {name}{' '} - {description} - - + + + {emphasis === 'high' && } - {hasResult && ( - + {displayableResult && ( + {typeof displayableResult === 'string' && ( @@ -89,7 +78,7 @@ export const ToolMessage: React.FC = ({ /> )} - {typeof displayableResult === 'object' && ( + {typeof displayableResult !== 'string' && ( = ({ ); }; + +type ToolStatusIndicator = { + 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.SubtleComment; + default: { + const exhaustiveCheck: never = emphasis; + return exhaustiveCheck; + } + } + }, [emphasis]); + return ( + + + + {name} + {' '} + {description} + + + ); +}; + +const TrailingIndicator: React.FC = () => ( + +); -- cgit v1.2.3