diff options
Diffstat (limited to 'packages/cli/src/ui/components/messages')
| -rw-r--r-- | packages/cli/src/ui/components/messages/ToolMessage.test.tsx | 40 | ||||
| -rw-r--r-- | packages/cli/src/ui/components/messages/ToolMessage.tsx | 73 |
2 files changed, 60 insertions, 53 deletions
diff --git a/packages/cli/src/ui/components/messages/ToolMessage.test.tsx b/packages/cli/src/ui/components/messages/ToolMessage.test.tsx index 10380ad4..a40ca31b 100644 --- a/packages/cli/src/ui/components/messages/ToolMessage.test.tsx +++ b/packages/cli/src/ui/components/messages/ToolMessage.test.tsx @@ -4,15 +4,29 @@ * SPDX-License-Identifier: Apache-2.0 */ +import React from 'react'; import { render } from 'ink-testing-library'; import { ToolMessage, ToolMessageProps } from './ToolMessage.js'; import { StreamingState, ToolCallStatus } from '../../types.js'; import { Text } from 'ink'; -import { StreamingContext } from '../../contexts/StreamingContext.js'; +import { + StreamingContext, + StreamingContextType, +} from '../../contexts/StreamingContext.js'; // Mock child components or utilities if they are complex or have side effects -vi.mock('ink-spinner', () => ({ - default: () => <Text>MockSpinner</Text>, +vi.mock('../GeminiRespondingSpinner.js', () => ({ + GeminiRespondingSpinner: ({ + nonRespondingDisplay, + }: { + nonRespondingDisplay?: string; + }) => { + const { streamingState } = React.useContext(StreamingContext)!; + if (streamingState === StreamingState.Responding) { + return <Text>MockRespondingSpinner</Text>; + } + return nonRespondingDisplay ? <Text>{nonRespondingDisplay}</Text> : null; + }, })); vi.mock('./DiffRenderer.js', () => ({ DiffRenderer: function MockDiffRenderer({ @@ -33,12 +47,14 @@ vi.mock('../../utils/MarkdownDisplay.js', () => ({ const renderWithContext = ( ui: React.ReactElement, streamingState: StreamingState, -) => - render( - <StreamingContext.Provider value={{ streamingState }}> +) => { + const contextValue: StreamingContextType = { streamingState }; + return render( + <StreamingContext.Provider value={contextValue}> {ui} </StreamingContext.Provider>, ); +}; describe('<ToolMessage />', () => { const baseProps: ToolMessageProps = { @@ -110,8 +126,8 @@ describe('<ToolMessage />', () => { <ToolMessage {...baseProps} status={ToolCallStatus.Executing} />, StreamingState.Idle, ); - expect(lastFrame()).toContain('⠇'); - expect(lastFrame()).not.toContain('MockSpinner'); + expect(lastFrame()).toContain('⊷'); + expect(lastFrame()).not.toContain('MockRespondingSpinner'); expect(lastFrame()).not.toContain('✔'); }); @@ -120,17 +136,17 @@ describe('<ToolMessage />', () => { <ToolMessage {...baseProps} status={ToolCallStatus.Executing} />, StreamingState.WaitingForConfirmation, ); - expect(lastFrame()).toContain('⠇'); - expect(lastFrame()).not.toContain('MockSpinner'); + expect(lastFrame()).toContain('⊷'); + expect(lastFrame()).not.toContain('MockRespondingSpinner'); expect(lastFrame()).not.toContain('✔'); }); - it('shows MockSpinner for Executing status when streamingState is Responding', () => { + it('shows MockRespondingSpinner for Executing status when streamingState is Responding', () => { const { lastFrame } = renderWithContext( <ToolMessage {...baseProps} status={ToolCallStatus.Executing} />, StreamingState.Responding, // Simulate app still responding ); - expect(lastFrame()).toContain('MockSpinner'); + expect(lastFrame()).toContain('MockRespondingSpinner'); expect(lastFrame()).not.toContain('✔'); }); }); diff --git a/packages/cli/src/ui/components/messages/ToolMessage.tsx b/packages/cli/src/ui/components/messages/ToolMessage.tsx index c8b61297..922f59d0 100644 --- a/packages/cli/src/ui/components/messages/ToolMessage.tsx +++ b/packages/cli/src/ui/components/messages/ToolMessage.tsx @@ -6,16 +6,11 @@ import React from 'react'; import { Box, Text } from 'ink'; -import Spinner from 'ink-spinner'; -import { - IndividualToolCallDisplay, - StreamingState, - ToolCallStatus, -} from '../../types.js'; +import { IndividualToolCallDisplay, ToolCallStatus } from '../../types.js'; import { DiffRenderer } from './DiffRenderer.js'; import { Colors } from '../../colors.js'; import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js'; -import { useStreamingContext } from '../../contexts/StreamingContext.js'; +import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js'; const STATIC_HEIGHT = 1; const RESERVED_LINE_COUNT = 5; // for tool name, status, padding etc. @@ -61,7 +56,6 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({ return ( <Box paddingX={1} paddingY={0} flexDirection="column"> <Box minHeight={1}> - {/* Status Indicator */} <ToolStatusIndicator status={status} /> <ToolInfo name={name} @@ -107,41 +101,38 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({ type ToolStatusIndicatorProps = { status: ToolCallStatus; }; + const ToolStatusIndicator: React.FC<ToolStatusIndicatorProps> = ({ status, -}) => { - const { streamingState } = useStreamingContext(); - return ( - <Box minWidth={STATUS_INDICATOR_WIDTH}> - {status === ToolCallStatus.Pending && ( - <Text color={Colors.AccentGreen}>o</Text> - )} - {status === ToolCallStatus.Executing && - (streamingState === StreamingState.Responding ? ( - <Spinner type="toggle" /> - ) : ( - // Paused spinner to avoid flicker. - <Text>⠇</Text> - ))} - {status === ToolCallStatus.Success && ( - <Text color={Colors.AccentGreen}>✔</Text> - )} - {status === ToolCallStatus.Confirming && ( - <Text color={Colors.AccentYellow}>?</Text> - )} - {status === ToolCallStatus.Canceled && ( - <Text color={Colors.AccentYellow} bold> - - - </Text> - )} - {status === ToolCallStatus.Error && ( - <Text color={Colors.AccentRed} bold> - x - </Text> - )} - </Box> - ); -}; +}) => ( + <Box minWidth={STATUS_INDICATOR_WIDTH}> + {status === ToolCallStatus.Pending && ( + <Text color={Colors.AccentGreen}>o</Text> + )} + {status === ToolCallStatus.Executing && ( + <GeminiRespondingSpinner + spinnerType="toggle" + nonRespondingDisplay={'⊷'} + /> + )} + {status === ToolCallStatus.Success && ( + <Text color={Colors.AccentGreen}>✔</Text> + )} + {status === ToolCallStatus.Confirming && ( + <Text color={Colors.AccentYellow}>?</Text> + )} + {status === ToolCallStatus.Canceled && ( + <Text color={Colors.AccentYellow} bold> + - + </Text> + )} + {status === ToolCallStatus.Error && ( + <Text color={Colors.AccentRed} bold> + x + </Text> + )} + </Box> +); type ToolInfo = { name: string; |
