diff --git a/docs/cli/settings.md b/docs/cli/settings.md
index 83b92fb3c8..54dcec335e 100644
--- a/docs/cli/settings.md
+++ b/docs/cli/settings.md
@@ -60,6 +60,7 @@ they appear in the UI.
| Use Alternate Screen Buffer | `ui.useAlternateBuffer` | Use an alternate screen buffer for the UI, preserving shell history. | `false` |
| Use Background Color | `ui.useBackgroundColor` | Whether to use background colors in the UI. | `true` |
| Incremental Rendering | `ui.incrementalRendering` | Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled. | `true` |
+| Show Spinner | `ui.showSpinner` | Show the spinner during operations. | `true` |
| Enable Loading Phrases | `ui.accessibility.enableLoadingPhrases` | Enable loading phrases during operations. | `true` |
| Screen Reader Mode | `ui.accessibility.screenReader` | Render output in plain-text to be more screen reader accessible | `false` |
diff --git a/docs/get-started/configuration.md b/docs/get-started/configuration.md
index d7885df084..fac2845d60 100644
--- a/docs/get-started/configuration.md
+++ b/docs/get-started/configuration.md
@@ -261,6 +261,10 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `true`
- **Requires restart:** Yes
+- **`ui.showSpinner`** (boolean):
+ - **Description:** Show the spinner during operations.
+ - **Default:** `true`
+
- **`ui.customWittyPhrases`** (array):
- **Description:** Custom witty phrases to display during loading. When
provided, the CLI cycles through these instead of the defaults.
diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts
index 6a881fdeff..7ad6673768 100644
--- a/packages/cli/src/config/settingsSchema.ts
+++ b/packages/cli/src/config/settingsSchema.ts
@@ -555,6 +555,15 @@ const SETTINGS_SCHEMA = {
'Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled.',
showInDialog: true,
},
+ showSpinner: {
+ type: 'boolean',
+ label: 'Show Spinner',
+ category: 'UI',
+ requiresRestart: false,
+ default: true,
+ description: 'Show the spinner during operations.',
+ showInDialog: true,
+ },
customWittyPhrases: {
type: 'array',
label: 'Custom Witty Phrases',
diff --git a/packages/cli/src/ui/components/CliSpinner.test.tsx b/packages/cli/src/ui/components/CliSpinner.test.tsx
index bbea23ab5d..76522c41c1 100644
--- a/packages/cli/src/ui/components/CliSpinner.test.tsx
+++ b/packages/cli/src/ui/components/CliSpinner.test.tsx
@@ -4,7 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { render } from '../../test-utils/render.js';
+import {
+ renderWithProviders,
+ createMockSettings,
+} from '../../test-utils/render.js';
import { CliSpinner } from './CliSpinner.js';
import { debugState } from '../debug.js';
import { describe, it, expect, beforeEach } from 'vitest';
@@ -16,9 +19,15 @@ describe('', () => {
it('should increment debugNumAnimatedComponents on mount and decrement on unmount', () => {
expect(debugState.debugNumAnimatedComponents).toBe(0);
- const { unmount } = render();
+ const { unmount } = renderWithProviders();
expect(debugState.debugNumAnimatedComponents).toBe(1);
unmount();
expect(debugState.debugNumAnimatedComponents).toBe(0);
});
+
+ it('should not render when showSpinner is false', () => {
+ const settings = createMockSettings({ ui: { showSpinner: false } });
+ const { lastFrame } = renderWithProviders(, { settings });
+ expect(lastFrame()).toBe('');
+ });
});
diff --git a/packages/cli/src/ui/components/CliSpinner.tsx b/packages/cli/src/ui/components/CliSpinner.tsx
index 6795bf2670..66cb7a0281 100644
--- a/packages/cli/src/ui/components/CliSpinner.tsx
+++ b/packages/cli/src/ui/components/CliSpinner.tsx
@@ -7,16 +7,27 @@
import Spinner from 'ink-spinner';
import { type ComponentProps, useEffect } from 'react';
import { debugState } from '../debug.js';
+import { useSettings } from '../contexts/SettingsContext.js';
export type SpinnerProps = ComponentProps;
export const CliSpinner = (props: SpinnerProps) => {
+ const settings = useSettings();
+ const shouldShow = settings.merged.ui?.showSpinner !== false;
+
useEffect(() => {
- debugState.debugNumAnimatedComponents++;
- return () => {
- debugState.debugNumAnimatedComponents--;
- };
- }, []);
+ if (shouldShow) {
+ debugState.debugNumAnimatedComponents++;
+ return () => {
+ debugState.debugNumAnimatedComponents--;
+ };
+ }
+ return undefined;
+ }, [shouldShow]);
+
+ if (!shouldShow) {
+ return null;
+ }
return ;
};
diff --git a/packages/cli/src/ui/components/messages/CompressionMessage.test.tsx b/packages/cli/src/ui/components/messages/CompressionMessage.test.tsx
index 88c3fb2197..622daa834d 100644
--- a/packages/cli/src/ui/components/messages/CompressionMessage.test.tsx
+++ b/packages/cli/src/ui/components/messages/CompressionMessage.test.tsx
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { render } from '../../../test-utils/render.js';
+import { renderWithProviders } from '../../../test-utils/render.js';
import type { CompressionDisplayProps } from './CompressionMessage.js';
import { CompressionMessage } from './CompressionMessage.js';
import { CompressionStatus } from '@google/gemini-cli-core';
@@ -27,7 +27,9 @@ describe('', () => {
describe('pending state', () => {
it('renders pending message when compression is in progress', () => {
const props = createCompressionProps({ isPending: true });
- const { lastFrame, unmount } = render();
+ const { lastFrame, unmount } = renderWithProviders(
+ ,
+ );
const output = lastFrame();
expect(output).toContain('Compressing chat history');
@@ -43,7 +45,9 @@ describe('', () => {
newTokenCount: 50,
compressionStatus: CompressionStatus.COMPRESSED,
});
- const { lastFrame, unmount } = render();
+ const { lastFrame, unmount } = renderWithProviders(
+ ,
+ );
const output = lastFrame();
expect(output).toContain('✦');
@@ -66,7 +70,7 @@ describe('', () => {
newTokenCount: newTokens,
compressionStatus: CompressionStatus.COMPRESSED,
});
- const { lastFrame, unmount } = render(
+ const { lastFrame, unmount } = renderWithProviders(
,
);
const output = lastFrame();
@@ -91,7 +95,9 @@ describe('', () => {
compressionStatus:
CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
});
- const { lastFrame, unmount } = render();
+ const { lastFrame, unmount } = renderWithProviders(
+ ,
+ );
const output = lastFrame();
expect(output).toContain('✦');
@@ -109,7 +115,9 @@ describe('', () => {
compressionStatus:
CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
});
- const { lastFrame, unmount } = render();
+ const { lastFrame, unmount } = renderWithProviders(
+ ,
+ );
const output = lastFrame();
expect(output).toContain(
@@ -146,7 +154,7 @@ describe('', () => {
newTokenCount: newTokens,
compressionStatus: CompressionStatus.COMPRESSED,
});
- const { lastFrame, unmount } = render(
+ const { lastFrame, unmount } = renderWithProviders(
,
);
const output = lastFrame();
@@ -171,7 +179,7 @@ describe('', () => {
compressionStatus:
CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
});
- const { lastFrame, unmount } = render(
+ const { lastFrame, unmount } = renderWithProviders(
,
);
const output = lastFrame();
@@ -199,7 +207,7 @@ describe('', () => {
compressionStatus:
CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
});
- const { lastFrame, unmount } = render(
+ const { lastFrame, unmount } = renderWithProviders(
,
);
const output = lastFrame();
@@ -218,7 +226,9 @@ describe('', () => {
isPending: false,
compressionStatus: CompressionStatus.COMPRESSION_FAILED_EMPTY_SUMMARY,
});
- const { lastFrame, unmount } = render();
+ const { lastFrame, unmount } = renderWithProviders(
+ ,
+ );
const output = lastFrame();
expect(output).toContain('✦');
@@ -234,7 +244,9 @@ describe('', () => {
compressionStatus:
CompressionStatus.COMPRESSION_FAILED_TOKEN_COUNT_ERROR,
});
- const { lastFrame, unmount } = render();
+ const { lastFrame, unmount } = renderWithProviders(
+ ,
+ );
const output = lastFrame();
expect(output).toContain(
diff --git a/schemas/settings.schema.json b/schemas/settings.schema.json
index bd0b406eaf..fdaf50e933 100644
--- a/schemas/settings.schema.json
+++ b/schemas/settings.schema.json
@@ -323,6 +323,13 @@
"default": true,
"type": "boolean"
},
+ "showSpinner": {
+ "title": "Show Spinner",
+ "description": "Show the spinner during operations.",
+ "markdownDescription": "Show the spinner during operations.\n\n- Category: `UI`\n- Requires restart: `no`\n- Default: `true`",
+ "default": true,
+ "type": "boolean"
+ },
"customWittyPhrases": {
"title": "Custom Witty Phrases",
"description": "Custom witty phrases to display during loading. When provided, the CLI cycles through these instead of the defaults.",