diff options
Diffstat (limited to 'packages/cli/src/ui/components/messages')
6 files changed, 58 insertions, 9 deletions
diff --git a/packages/cli/src/ui/components/messages/DiffRenderer.tsx b/packages/cli/src/ui/components/messages/DiffRenderer.tsx index eb3133c3..4d196e6d 100644 --- a/packages/cli/src/ui/components/messages/DiffRenderer.tsx +++ b/packages/cli/src/ui/components/messages/DiffRenderer.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { Box, Text } from 'ink'; import { Colors } from '../../colors.js'; +import crypto from 'crypto'; interface DiffLine { type: 'add' | 'del' | 'context' | 'hunk' | 'other'; @@ -94,6 +95,7 @@ const DEFAULT_TAB_WIDTH = 4; // Spaces per tab for normalization export const DiffRenderer: React.FC<DiffRendererProps> = ({ diffContent, + filename, tabWidth = DEFAULT_TAB_WIDTH, }) => { if (!diffContent || typeof diffContent !== 'string') { @@ -137,8 +139,11 @@ export const DiffRenderer: React.FC<DiffRendererProps> = ({ } // --- End Modification --- + const key = filename + ? `diff-box-${filename}` + : `diff-box-${crypto.createHash('sha1').update(diffContent).digest('hex')}`; return ( - <Box flexDirection="column"> + <Box flexDirection="column" key={key}> {/* Iterate over the lines that should be displayed (already normalized) */} {displayableLines.map((line, index) => { const key = `diff-line-${index}`; diff --git a/packages/cli/src/ui/components/messages/ErrorMessage.tsx b/packages/cli/src/ui/components/messages/ErrorMessage.tsx index 22d82465..edbea435 100644 --- a/packages/cli/src/ui/components/messages/ErrorMessage.tsx +++ b/packages/cli/src/ui/components/messages/ErrorMessage.tsx @@ -17,7 +17,7 @@ export const ErrorMessage: React.FC<ErrorMessageProps> = ({ text }) => { const prefixWidth = prefix.length; return ( - <Box flexDirection="row"> + <Box flexDirection="row" marginBottom={1}> <Box width={prefixWidth}> <Text color={Colors.AccentRed}>{prefix}</Text> </Box> diff --git a/packages/cli/src/ui/components/messages/GeminiMessageContent.tsx b/packages/cli/src/ui/components/messages/GeminiMessageContent.tsx new file mode 100644 index 00000000..fb025231 --- /dev/null +++ b/packages/cli/src/ui/components/messages/GeminiMessageContent.tsx @@ -0,0 +1,33 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { Box } from 'ink'; +import { MarkdownRenderer } from '../../utils/MarkdownRenderer.js'; + +interface GeminiMessageContentProps { + text: string; +} + +/* + * Gemini message content is a semi-hacked component. The intention is to represent a partial + * of GeminiMessage and is only used when a response gets too long. In that instance messages + * are split into multiple GeminiMessageContent's to enable the root <Static> component in + * App.tsx to be as performant as humanly possible. + */ +export const GeminiMessageContent: React.FC<GeminiMessageContentProps> = ({ + text, +}) => { + const originalPrefix = '✦ '; + const prefixWidth = originalPrefix.length; + const renderedBlocks = MarkdownRenderer.render(text); + + return ( + <Box flexDirection="column" paddingLeft={prefixWidth}> + {renderedBlocks} + </Box> + ); +}; diff --git a/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx b/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx index 748c7d1c..7099537e 100644 --- a/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx +++ b/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { Box, Text, useInput } from 'ink'; import { PartListUnion } from '@google/genai'; import { DiffRenderer } from './DiffRenderer.js'; -import { UI_WIDTH } from '../../constants.js'; import { Colors } from '../../colors.js'; import { ToolCallConfirmationDetails, @@ -88,7 +87,7 @@ export const ToolConfirmationMessage: React.FC< value: ToolConfirmationOutcome.ProceedOnce, }, { - label: `Yes, allow always for ${executionProps.rootCommand} ...`, + label: `Yes, allow always "${executionProps.rootCommand} ..."`, value: ToolConfirmationOutcome.ProceedAlways, }, { label: 'No (esc)', value: ToolConfirmationOutcome.Cancel }, @@ -96,7 +95,7 @@ export const ToolConfirmationMessage: React.FC< } return ( - <Box flexDirection="column" padding={1} minWidth={UI_WIDTH}> + <Box flexDirection="column" padding={1} minWidth="90%"> {/* Body Content (Diff Renderer or Command Info) */} {/* No separate context display here anymore for edits */} <Box flexGrow={1} flexShrink={1} overflow="hidden" marginBottom={1}> diff --git a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx index 0675411f..2d4982c2 100644 --- a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx +++ b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx @@ -13,12 +13,14 @@ import { ToolConfirmationMessage } from './ToolConfirmationMessage.js'; import { Colors } from '../../colors.js'; interface ToolGroupMessageProps { + groupId: number; toolCalls: IndividualToolCallDisplay[]; onSubmit: (value: PartListUnion) => void; } // Main component renders the border and maps the tools using ToolMessage export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({ + groupId, toolCalls, onSubmit, }) => { @@ -29,13 +31,23 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({ return ( <Box + key={groupId} flexDirection="column" borderStyle="round" + /* + This width constraint is highly important and protects us from an Ink rendering bug. + Since the ToolGroup can typically change rendering states frequently, it can cause + Ink to render the border of the box incorrectly and span multiple lines and even + cause tearing. + */ + width="100%" + marginLeft={1} borderDimColor={hasPending} borderColor={borderColor} + marginBottom={1} > {toolCalls.map((tool) => ( - <React.Fragment key={tool.callId}> + <Box key={groupId + '-' + tool.callId} flexDirection="column"> <ToolMessage key={tool.callId} // Use callId as the key callId={tool.callId} // Pass callId @@ -52,7 +64,7 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({ onSubmit={onSubmit} ></ToolConfirmationMessage> )} - </React.Fragment> + </Box> ))} {/* Optional: Add padding below the last item if needed, though ToolMessage already has some vertical space implicitly */} diff --git a/packages/cli/src/ui/components/messages/ToolMessage.tsx b/packages/cli/src/ui/components/messages/ToolMessage.tsx index ab590f53..4d5fca37 100644 --- a/packages/cli/src/ui/components/messages/ToolMessage.tsx +++ b/packages/cli/src/ui/components/messages/ToolMessage.tsx @@ -54,8 +54,8 @@ export const ToolMessage: React.FC<IndividualToolCallDisplay> = ({ </Box> </Box> {hasResult && ( - <Box paddingLeft={statusIndicatorWidth}> - <Box flexShrink={1} flexDirection="row"> + <Box paddingLeft={statusIndicatorWidth} width="100%"> + <Box flexDirection="row"> {/* Use default text color (white) or gray instead of dimColor */} {typeof resultDisplay === 'string' && ( <Box flexDirection="column"> |
