Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/cli/src/ui/components/GeminiRespondingSpinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { StreamingState } from '../types.js';
import {
SCREEN_READER_LOADING,
SCREEN_READER_RESPONDING,
SCREEN_READER_WAITING_FOR_CONFIRMATION,
} from '../textConstants.js';
import { theme } from '../semantic-colors.js';

Expand All @@ -37,6 +38,13 @@ export const GeminiRespondingSpinner: React.FC<
altText={SCREEN_READER_RESPONDING}
/>
);
} else if (streamingState === StreamingState.WaitingForConfirmation) {
return (
<GeminiSpinner
spinnerType={spinnerType}
altText={SCREEN_READER_WAITING_FOR_CONFIRMATION}
/>
);
} else if (nonRespondingDisplay) {
return isScreenReaderEnabled ? (
<Text>{SCREEN_READER_LOADING}</Text>
Expand Down
19 changes: 8 additions & 11 deletions packages/cli/src/ui/components/LoadingIndicator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,13 @@ import * as useTerminalSize from '../hooks/useTerminalSize.js';

// Mock GeminiRespondingSpinner
vi.mock('./GeminiRespondingSpinner.js', () => ({
GeminiRespondingSpinner: ({
nonRespondingDisplay,
}: {
nonRespondingDisplay?: string;
}) => {
GeminiRespondingSpinner: () => {
const streamingState = React.useContext(StreamingContext)!;
if (streamingState === StreamingState.Responding) {
if (
streamingState === StreamingState.Responding ||
streamingState === StreamingState.WaitingForConfirmation
) {
return <Text>MockRespondingSpinner</Text>;
} else if (nonRespondingDisplay) {
return <Text>{nonRespondingDisplay}</Text>;
}
return null;
},
Expand Down Expand Up @@ -76,7 +73,7 @@ describe('<LoadingIndicator />', () => {
expect(output).toContain('(esc to cancel, 5s)');
});

it('should render spinner (static), phrase but no time/cancel when streamingState is WaitingForConfirmation', () => {
it('should render animated spinner, phrase but no time/cancel when streamingState is WaitingForConfirmation', () => {
const props = {
currentLoadingPhrase: 'Confirm action',
elapsedTime: 10,
Expand All @@ -86,7 +83,7 @@ describe('<LoadingIndicator />', () => {
StreamingState.WaitingForConfirmation,
);
const output = lastFrame();
expect(output).toContain(''); // Static char for WaitingForConfirmation
expect(output).toContain('MockRespondingSpinner'); // Animated spinner for WaitingForConfirmation
expect(output).toContain('Confirm action');
expect(output).not.toContain('(esc to cancel)');
expect(output).not.toContain(', 10s');
Expand Down Expand Up @@ -172,7 +169,7 @@ describe('<LoadingIndicator />', () => {
</StreamingContext.Provider>,
);
output = lastFrame();
expect(output).toContain('');
expect(output).toContain('MockRespondingSpinner');
expect(output).toContain('Please Confirm');
expect(output).not.toContain('(esc to cancel)');
expect(output).not.toContain(', 15s');
Expand Down
8 changes: 1 addition & 7 deletions packages/cli/src/ui/components/LoadingIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,7 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
>
<Box>
<Box marginRight={1}>
<GeminiRespondingSpinner
nonRespondingDisplay={
streamingState === StreamingState.WaitingForConfirmation
? '⠏'
: ''
}
/>
<GeminiRespondingSpinner />
</Box>
{primaryText && (
<Text color={theme.text.accent} wrap="truncate-end">
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/ui/textConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ export const SCREEN_READER_MODEL_PREFIX = 'Model: ';
export const SCREEN_READER_LOADING = 'loading';

export const SCREEN_READER_RESPONDING = 'responding';

export const SCREEN_READER_WAITING_FOR_CONFIRMATION =
'waiting for confirmation';