summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/components/messages
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui/components/messages')
-rw-r--r--packages/cli/src/ui/components/messages/DiffRenderer.tsx7
-rw-r--r--packages/cli/src/ui/components/messages/ErrorMessage.tsx2
-rw-r--r--packages/cli/src/ui/components/messages/GeminiMessageContent.tsx33
-rw-r--r--packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx5
-rw-r--r--packages/cli/src/ui/components/messages/ToolGroupMessage.tsx16
-rw-r--r--packages/cli/src/ui/components/messages/ToolMessage.tsx4
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">