diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx
index 9a12195a15..264ad96593 100644
--- a/packages/cli/src/ui/components/Composer.tsx
+++ b/packages/cli/src/ui/components/Composer.tsx
@@ -37,8 +37,6 @@ import { Footer } from './Footer.js';
import { ShowMoreLines } from './ShowMoreLines.js';
import { QueuedMessageDisplay } from './QueuedMessageDisplay.js';
import { OverflowProvider } from '../contexts/OverflowContext.js';
-import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
-import { HookStatusDisplay } from './HookStatusDisplay.js';
import { ConfigInitDisplay } from './ConfigInitDisplay.js';
import { TodoTray } from './messages/Todo.js';
@@ -279,7 +277,6 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
const showRow1 = showUiDetails || showRow1_MiniMode;
const showRow2 = showUiDetails || showRow2_MiniMode;
- const showMinimalInlineLoading = !showUiDetails && showLoadingIndicator;
const showMinimalBleedThroughRow = !showUiDetails && showRow2_MiniMode;
const renderAmbientNode = () => {
@@ -311,41 +308,47 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
};
const renderStatusNode = () => {
+ if (!hasUserHooks && !showLoadingIndicator) return null;
+
if (hasUserHooks) {
const activeHook = userHooks[0];
const hookIcon = activeHook?.eventName?.startsWith('After') ? '↩' : '↪';
+ const label = userHooks.length > 1 ? 'Executing Hooks' : 'Executing Hook';
+ const displayNames = userHooks.map((h) => {
+ let name = h.name;
+ if (h.index && h.total && h.total > 1) name += ` (${h.index}/${h.total})`;
+ return name;
+ });
+ const hookText = `${label}: ${displayNames.join(', ')}`;
- return (
-
-
-
- {showWit && uiState.currentWittyPhrase && (
-
- {uiState.currentWittyPhrase}
-
- )}
-
- );
- }
-
- if (showLoadingIndicator) {
return (
);
}
- return null;
+
+ return (
+
+ );
};
const statusNode = renderStatusNode();
@@ -355,22 +358,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
*/
const renderMinimalMetaRowContent = () => (
- {showMinimalInlineLoading && (
-
- )}
- {hasUserHooks && (
-
-
-
-
- )}
+ {renderStatusNode()}
{showMinimalBleedThroughRow && (
{miniMode_ShowApprovalMode && modeContentObj && (
diff --git a/packages/cli/src/ui/components/LoadingIndicator.test.tsx b/packages/cli/src/ui/components/LoadingIndicator.test.tsx
index 1aba99ebc1..e5e0f6bb91 100644
--- a/packages/cli/src/ui/components/LoadingIndicator.test.tsx
+++ b/packages/cli/src/ui/components/LoadingIndicator.test.tsx
@@ -448,4 +448,20 @@ describe('', () => {
unmount();
});
});
+
+ it('should use spinnerIcon when provided', async () => {
+ const props = {
+ currentLoadingPhrase: 'Confirm action',
+ elapsedTime: 10,
+ spinnerIcon: '?',
+ };
+ const { lastFrame, waitUntilReady } = renderWithContext(
+ ,
+ StreamingState.WaitingForConfirmation,
+ );
+ await waitUntilReady();
+ const output = lastFrame();
+ expect(output).toContain('?');
+ expect(output).not.toContain('⠏');
+ });
});
diff --git a/packages/cli/src/ui/components/LoadingIndicator.tsx b/packages/cli/src/ui/components/LoadingIndicator.tsx
index d0a030a807..a48451b26c 100644
--- a/packages/cli/src/ui/components/LoadingIndicator.tsx
+++ b/packages/cli/src/ui/components/LoadingIndicator.tsx
@@ -29,6 +29,8 @@ interface LoadingIndicatorProps {
thoughtLabel?: string;
showCancelAndTimer?: boolean;
forceRealStatusOnly?: boolean;
+ spinnerIcon?: string;
+ isHookActive?: boolean;
}
export const LoadingIndicator: React.FC = ({
@@ -42,6 +44,8 @@ export const LoadingIndicator: React.FC = ({
thoughtLabel,
showCancelAndTimer = true,
forceRealStatusOnly = false,
+ spinnerIcon,
+ isHookActive = false,
}) => {
const streamingState = useStreamingContext();
const { columns: terminalWidth } = useTerminalSize();
@@ -91,10 +95,12 @@ export const LoadingIndicator: React.FC = ({
{primaryText && (
@@ -133,10 +139,12 @@ export const LoadingIndicator: React.FC = ({
{primaryText && (