Skip to content

Commit 957bbce

Browse files
committed
fix(cli): use tmux-safe thinking indicator
1 parent 88638c1 commit 957bbce

File tree

1 file changed

+29
-5
lines changed

1 file changed

+29
-5
lines changed

packages/cli/src/ui/components/GeminiSpinner.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,22 @@
66

77
import type React from 'react';
88
import { useState, useEffect, useMemo } from 'react';
9-
import { Text, useIsScreenReaderEnabled } from 'ink';
9+
import { Box, Text, useIsScreenReaderEnabled } from 'ink';
1010
import { CliSpinner } from './CliSpinner.js';
1111
import type { SpinnerName } from 'cli-spinners';
1212
import { Colors } from '../colors.js';
1313
import tinygradient from 'tinygradient';
1414

1515
const COLOR_CYCLE_DURATION_MS = 4000;
16+
const SPINNER_UPDATE_INTERVAL_MS = 30;
17+
const TMUX_UPDATE_INTERVAL_MS = 750;
18+
const TMUX_FRAMES = ['.', '..', '...'] as const;
19+
20+
function isTmuxEnvironment(): boolean {
21+
return (
22+
!!process.env['TMUX'] || (process.env['TERM'] ?? '').startsWith('tmux')
23+
);
24+
}
1625

1726
interface GeminiSpinnerProps {
1827
spinnerType?: SpinnerName;
@@ -24,7 +33,9 @@ export const GeminiSpinner: React.FC<GeminiSpinnerProps> = ({
2433
altText,
2534
}) => {
2635
const isScreenReaderEnabled = useIsScreenReaderEnabled();
36+
const isTmux = isTmuxEnvironment();
2737
const [time, setTime] = useState(0);
38+
const [tmuxFrameIndex, setTmuxFrameIndex] = useState(0);
2839

2940
const googleGradient = useMemo(() => {
3041
const brandColors = [
@@ -43,18 +54,31 @@ export const GeminiSpinner: React.FC<GeminiSpinnerProps> = ({
4354
return;
4455
}
4556

46-
const interval = setInterval(() => {
47-
setTime((prevTime) => prevTime + 30);
48-
}, 30); // ~33fps for smooth color transitions
57+
const interval = setInterval(
58+
() => {
59+
if (isTmux) {
60+
setTmuxFrameIndex(
61+
(prevIndex) => (prevIndex + 1) % TMUX_FRAMES.length,
62+
);
63+
return;
64+
}
65+
setTime((prevTime) => prevTime + SPINNER_UPDATE_INTERVAL_MS);
66+
},
67+
isTmux ? TMUX_UPDATE_INTERVAL_MS : SPINNER_UPDATE_INTERVAL_MS,
68+
);
4969

5070
return () => clearInterval(interval);
51-
}, [isScreenReaderEnabled]);
71+
}, [isScreenReaderEnabled, isTmux]);
5272

5373
const progress = (time % COLOR_CYCLE_DURATION_MS) / COLOR_CYCLE_DURATION_MS;
5474
const currentColor = googleGradient.rgbAt(progress).toHexString();
5575

5676
return isScreenReaderEnabled ? (
5777
<Text>{altText}</Text>
78+
) : isTmux ? (
79+
<Box width={3}>
80+
<Text>{TMUX_FRAMES[tmuxFrameIndex]}</Text>
81+
</Box>
5882
) : (
5983
<Text color={currentColor}>
6084
<CliSpinner type={spinnerType} />

0 commit comments

Comments
 (0)