summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui/components')
-rw-r--r--packages/cli/src/ui/components/Header.tsx2
-rw-r--r--packages/cli/src/ui/components/InputPrompt.tsx56
-rw-r--r--packages/cli/src/ui/components/messages/ToolGroupMessage.tsx2
-rw-r--r--packages/cli/src/ui/components/messages/ToolMessage.tsx144
4 files changed, 124 insertions, 80 deletions
diff --git a/packages/cli/src/ui/components/Header.tsx b/packages/cli/src/ui/components/Header.tsx
index d045706c..62649996 100644
--- a/packages/cli/src/ui/components/Header.tsx
+++ b/packages/cli/src/ui/components/Header.tsx
@@ -7,7 +7,7 @@
import React from 'react';
import { Box, Text } from 'ink';
import { UI_WIDTH, BOX_PADDING_X } from '../constants.js';
-import { shortenPath } from '../../utils/paths.js';
+import { shortenPath } from '@gemini-code/server';
interface HeaderProps {
cwd: string;
diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx
index b5d0b2b5..86e760ee 100644
--- a/packages/cli/src/ui/components/InputPrompt.tsx
+++ b/packages/cli/src/ui/components/InputPrompt.tsx
@@ -5,41 +5,43 @@
*/
import React from 'react';
-import { Box, Text } from 'ink';
+import { Box, useInput, useFocus } from 'ink';
import TextInput from 'ink-text-input';
-import { globalConfig } from '../../config/config.js';
interface InputPromptProps {
- query: string;
- setQuery: (value: string) => void;
onSubmit: (value: string) => void;
- isActive: boolean;
- forceKey?: number;
}
-export const InputPrompt: React.FC<InputPromptProps> = ({
- query,
- setQuery,
- onSubmit,
- isActive,
- forceKey,
-}) => {
- const model = globalConfig.getModel();
+export const InputPrompt: React.FC<InputPromptProps> = ({ onSubmit }) => {
+ const [value, setValue] = React.useState('');
+ const { isFocused } = useFocus({ autoFocus: true });
+
+ useInput(
+ (input, key) => {
+ if (key.return) {
+ if (value.trim()) {
+ onSubmit(value);
+ setValue('');
+ }
+ }
+ },
+ { isActive: isFocused },
+ );
return (
- <Box marginTop={1} borderStyle="round" borderColor={'white'} paddingX={1}>
- <Text color={'white'}>&gt; </Text>
- <Box flexGrow={1}>
- <TextInput
- key={forceKey?.toString()}
- value={query}
- onChange={setQuery}
- onSubmit={onSubmit}
- showCursor={true}
- focus={isActive}
- placeholder={`Ask Gemini (${model})... (try "/init" or "/help")`}
- />
- </Box>
+ <Box
+ borderStyle="round"
+ borderColor={isFocused ? 'blue' : 'gray'}
+ paddingX={1}
+ >
+ <TextInput
+ value={value}
+ onChange={setValue}
+ placeholder="Enter your message or use tools..."
+ onSubmit={() => {
+ /* Empty to prevent double submission */
+ }}
+ />
</Box>
);
};
diff --git a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
index b43d452b..0662e333 100644
--- a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
+++ b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
@@ -30,10 +30,12 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
<React.Fragment key={tool.callId}>
<ToolMessage
key={tool.callId} // Use callId as the key
+ callId={tool.callId} // Pass callId
name={tool.name}
description={tool.description}
resultDisplay={tool.resultDisplay}
status={tool.status}
+ confirmationDetails={tool.confirmationDetails} // Pass confirmationDetails
/>
{tool.status === ToolCallStatus.Confirming &&
tool.confirmationDetails && (
diff --git a/packages/cli/src/ui/components/messages/ToolMessage.tsx b/packages/cli/src/ui/components/messages/ToolMessage.tsx
index da93b18a..9c1dd36b 100644
--- a/packages/cli/src/ui/components/messages/ToolMessage.tsx
+++ b/packages/cli/src/ui/components/messages/ToolMessage.tsx
@@ -7,70 +7,110 @@
import React from 'react';
import { Box, Text } from 'ink';
import Spinner from 'ink-spinner';
-import { ToolCallStatus } from '../../types.js';
-import { ToolResultDisplay } from '../../../tools/tools.js';
+import {
+ IndividualToolCallDisplay,
+ ToolCallStatus,
+ ToolCallConfirmationDetails,
+ ToolEditConfirmationDetails,
+ ToolExecuteConfirmationDetails,
+} from '../../types.js';
import { DiffRenderer } from './DiffRenderer.js';
-import { MarkdownRenderer } from '../../utils/MarkdownRenderer.js';
+import { FileDiff, ToolResultDisplay } from '../../../tools/tools.js';
-interface ToolMessageProps {
- name: string;
- description: string;
- resultDisplay: ToolResultDisplay | undefined;
- status: ToolCallStatus;
-}
-
-export const ToolMessage: React.FC<ToolMessageProps> = ({
+export const ToolMessage: React.FC<IndividualToolCallDisplay> = ({
+ callId,
name,
description,
resultDisplay,
status,
+ confirmationDetails,
}) => {
- const statusIndicatorWidth = 3;
- const hasResult =
- (status === ToolCallStatus.Invoked || status === ToolCallStatus.Canceled) &&
- resultDisplay &&
- resultDisplay.toString().trim().length > 0;
+ // Explicitly type the props to help the type checker
+ const typedConfirmationDetails = confirmationDetails as
+ | ToolCallConfirmationDetails
+ | undefined;
+ const typedResultDisplay = resultDisplay as ToolResultDisplay | undefined;
+
+ let color = 'gray';
+ let prefix = '';
+ switch (status) {
+ case ToolCallStatus.Pending:
+ prefix = 'Pending:';
+ break;
+ case ToolCallStatus.Invoked:
+ prefix = 'Executing:';
+ break;
+ case ToolCallStatus.Confirming:
+ color = 'yellow';
+ prefix = 'Confirm:';
+ break;
+ case ToolCallStatus.Success:
+ color = 'green';
+ prefix = 'Success:';
+ break;
+ case ToolCallStatus.Error:
+ color = 'red';
+ prefix = 'Error:';
+ break;
+ default:
+ // Handle unexpected status if necessary, or just break
+ break;
+ }
+
+ const title = `${prefix} ${name}`;
return (
- <Box paddingX={1} paddingY={0} flexDirection="column">
- {/* Row for Status Indicator and Tool Info */}
- <Box minHeight={1}>
- {/* Status Indicator */}
- <Box minWidth={statusIndicatorWidth}>
- {status === ToolCallStatus.Pending && <Spinner type="dots" />}
- {status === ToolCallStatus.Invoked && <Text color="green">✔</Text>}
- {status === ToolCallStatus.Confirming && <Text color="blue">?</Text>}
- {status === ToolCallStatus.Canceled && (
- <Text color="red" bold>
- -
+ <Box key={callId} borderStyle="round" paddingX={1} flexDirection="column">
+ <Box>
+ {status === ToolCallStatus.Invoked && (
+ <Box marginRight={1}>
+ <Text color="blue">
+ <Spinner type="dots" />
</Text>
+ </Box>
+ )}
+ <Text bold color={color}>
+ {title}
+ </Text>
+ <Text color={color}>
+ {status === ToolCallStatus.Error && typedResultDisplay
+ ? `: ${typedResultDisplay}`
+ : ` - ${description}`}
+ </Text>
+ </Box>
+ {status === ToolCallStatus.Confirming && typedConfirmationDetails && (
+ <Box flexDirection="column" marginLeft={2}>
+ {/* Display diff for edit/write */}
+ {'fileDiff' in typedConfirmationDetails && (
+ <DiffRenderer
+ diffContent={
+ (typedConfirmationDetails as ToolEditConfirmationDetails)
+ .fileDiff
+ }
+ />
)}
+ {/* Display command for execute */}
+ {'command' in typedConfirmationDetails && (
+ <Text color="yellow">
+ Command:{' '}
+ {
+ (typedConfirmationDetails as ToolExecuteConfirmationDetails)
+ .command
+ }
+ </Text>
+ )}
+ {/* <ConfirmInput onConfirm={handleConfirm} isFocused={isFocused} /> */}
</Box>
- <Box>
- <Text
- color="blue"
- wrap="truncate-end"
- strikethrough={status === ToolCallStatus.Canceled}
- >
- <Text bold>{name}</Text> <Text color="gray">{description}</Text>
- </Text>
- </Box>
- </Box>
-
- {hasResult && (
- <Box paddingLeft={statusIndicatorWidth}>
- <Box flexShrink={1} flexDirection="row">
- <Text color="gray">↳ </Text>
- {/* Use default text color (white) or gray instead of dimColor */}
- {typeof resultDisplay === 'string' && (
- <Box flexDirection="column">
- {MarkdownRenderer.render(resultDisplay)}
- </Box>
- )}
- {typeof resultDisplay === 'object' && (
- <DiffRenderer diffContent={resultDisplay.fileDiff} />
- )}
- </Box>
+ )}
+ {status === ToolCallStatus.Success && typedResultDisplay && (
+ <Box flexDirection="column" marginLeft={2}>
+ {typeof typedResultDisplay === 'string' ? (
+ <Text>{typedResultDisplay}</Text>
+ ) : (
+ <DiffRenderer
+ diffContent={(typedResultDisplay as FileDiff).fileDiff}
+ />
+ )}
</Box>
)}
</Box>