summaryrefslogtreecommitdiff
path: root/packages/cli
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli')
-rw-r--r--packages/cli/src/ui/App.tsx7
-rw-r--r--packages/cli/src/ui/components/LoadingIndicator.test.tsx52
-rw-r--r--packages/cli/src/ui/components/LoadingIndicator.tsx42
-rw-r--r--packages/cli/src/ui/hooks/useGeminiStream.ts6
4 files changed, 90 insertions, 17 deletions
diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx
index 52c286dc..9a4ecbd3 100644
--- a/packages/cli/src/ui/App.tsx
+++ b/packages/cli/src/ui/App.tsx
@@ -300,6 +300,7 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
submitQuery,
initError,
pendingHistoryItems: pendingGeminiHistoryItems,
+ thought,
} = useGeminiStream(
config.getGeminiClient(),
history,
@@ -542,6 +543,12 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
) : (
<>
<LoadingIndicator
+ thought={
+ streamingState === StreamingState.WaitingForConfirmation ||
+ config.getAccessibility()?.disableLoadingPhrases
+ ? undefined
+ : thought
+ }
currentLoadingPhrase={
config.getAccessibility()?.disableLoadingPhrases
? undefined
diff --git a/packages/cli/src/ui/components/LoadingIndicator.test.tsx b/packages/cli/src/ui/components/LoadingIndicator.test.tsx
index 3d31818b..ba161062 100644
--- a/packages/cli/src/ui/components/LoadingIndicator.test.tsx
+++ b/packages/cli/src/ui/components/LoadingIndicator.test.tsx
@@ -159,4 +159,56 @@ describe('<LoadingIndicator />', () => {
);
expect(lastFrame()).toBe('');
});
+
+ it('should display fallback phrase if thought is empty', () => {
+ const props = {
+ thought: null,
+ currentLoadingPhrase: 'Loading...',
+ elapsedTime: 5,
+ };
+ const { lastFrame } = renderWithContext(
+ <LoadingIndicator {...props} />,
+ StreamingState.Responding,
+ );
+ const output = lastFrame();
+ expect(output).toContain('Loading...');
+ });
+
+ it('should display the subject of a thought', () => {
+ const props = {
+ thought: {
+ subject: 'Thinking about something...',
+ description: 'and other stuff.',
+ },
+ elapsedTime: 5,
+ };
+ const { lastFrame } = renderWithContext(
+ <LoadingIndicator {...props} />,
+ StreamingState.Responding,
+ );
+ const output = lastFrame();
+ expect(output).toBeDefined();
+ if (output) {
+ expect(output).toContain('Thinking about something...');
+ expect(output).not.toContain('and other stuff.');
+ }
+ });
+
+ it('should prioritize thought.subject over currentLoadingPhrase', () => {
+ const props = {
+ thought: {
+ subject: 'This should be displayed',
+ description: 'A description',
+ },
+ currentLoadingPhrase: 'This should not be displayed',
+ elapsedTime: 5,
+ };
+ const { lastFrame } = renderWithContext(
+ <LoadingIndicator {...props} />,
+ StreamingState.Responding,
+ );
+ const output = lastFrame();
+ expect(output).toContain('This should be displayed');
+ expect(output).not.toContain('This should not be displayed');
+ });
});
diff --git a/packages/cli/src/ui/components/LoadingIndicator.tsx b/packages/cli/src/ui/components/LoadingIndicator.tsx
index 61b74b89..855894e6 100644
--- a/packages/cli/src/ui/components/LoadingIndicator.tsx
+++ b/packages/cli/src/ui/components/LoadingIndicator.tsx
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ThoughtSummary } from '@gemini-cli/core';
import React from 'react';
import { Box, Text } from 'ink';
import { Colors } from '../colors.js';
@@ -15,12 +16,14 @@ interface LoadingIndicatorProps {
currentLoadingPhrase?: string;
elapsedTime: number;
rightContent?: React.ReactNode;
+ thought?: ThoughtSummary | null;
}
export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
currentLoadingPhrase,
elapsedTime,
rightContent,
+ thought,
}) => {
const streamingState = useStreamingContext();
@@ -28,25 +31,30 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
return null;
}
+ const primaryText = thought?.subject || currentLoadingPhrase;
+
return (
- <Box marginTop={1} paddingLeft={0}>
- <Box marginRight={1}>
- <GeminiRespondingSpinner
- nonRespondingDisplay={
- streamingState === StreamingState.WaitingForConfirmation ? '⠏' : ''
- }
- />
+ <Box marginTop={1} paddingLeft={0} flexDirection="column">
+ {/* Main loading line */}
+ <Box>
+ <Box marginRight={1}>
+ <GeminiRespondingSpinner
+ nonRespondingDisplay={
+ streamingState === StreamingState.WaitingForConfirmation
+ ? '⠏'
+ : ''
+ }
+ />
+ </Box>
+ {primaryText && <Text color={Colors.AccentPurple}>{primaryText}</Text>}
+ <Text color={Colors.Gray}>
+ {streamingState === StreamingState.WaitingForConfirmation
+ ? ''
+ : ` (esc to cancel, ${elapsedTime}s)`}
+ </Text>
+ <Box flexGrow={1}>{/* Spacer */}</Box>
+ {rightContent && <Box>{rightContent}</Box>}
</Box>
- {currentLoadingPhrase && (
- <Text color={Colors.AccentPurple}>{currentLoadingPhrase}</Text>
- )}
- <Text color={Colors.Gray}>
- {streamingState === StreamingState.WaitingForConfirmation
- ? ''
- : ` (esc to cancel, ${elapsedTime}s)`}
- </Text>
- <Box flexGrow={1}>{/* Spacer */}</Box>
- {rightContent && <Box>{rightContent}</Box>}
</Box>
);
};
diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts
index bff38a2b..51d32506 100644
--- a/packages/cli/src/ui/hooks/useGeminiStream.ts
+++ b/packages/cli/src/ui/hooks/useGeminiStream.ts
@@ -21,6 +21,7 @@ import {
logUserPrompt,
GitService,
EditorType,
+ ThoughtSummary,
} from '@gemini-cli/core';
import { type Part, type PartListUnion } from '@google/genai';
import {
@@ -90,6 +91,7 @@ export const useGeminiStream = (
const [initError, setInitError] = useState<string | null>(null);
const abortControllerRef = useRef<AbortController | null>(null);
const [isResponding, setIsResponding] = useState<boolean>(false);
+ const [thought, setThought] = useState<ThoughtSummary | null>(null);
const [pendingHistoryItemRef, setPendingHistoryItem] =
useStateAndRef<HistoryItemWithoutId | null>(null);
const logger = useLogger();
@@ -393,6 +395,9 @@ export const useGeminiStream = (
const toolCallRequests: ToolCallRequestInfo[] = [];
for await (const event of stream) {
switch (event.type) {
+ case ServerGeminiEventType.Thought:
+ setThought(event.value);
+ break;
case ServerGeminiEventType.Content:
geminiMessageBuffer = handleContentEvent(
event.value,
@@ -730,5 +735,6 @@ export const useGeminiStream = (
submitQuery,
initError,
pendingHistoryItems,
+ thought,
};
};