Stabilize inline thinking box during streaming

This commit is contained in:
Dmitry Lyalin
2026-01-31 11:07:06 -05:00
parent 66bbfa0dd4
commit 8841361a6f
3 changed files with 58 additions and 35 deletions
@@ -70,6 +70,9 @@ export const HistoryItemDisplay: React.FC<HistoryItemDisplayProps> = ({
<ThinkingMessage <ThinkingMessage
thoughts={itemForDisplay.thoughts} thoughts={itemForDisplay.thoughts}
terminalWidth={terminalWidth} terminalWidth={terminalWidth}
availableTerminalHeight={
isPending ? availableTerminalHeight : undefined
}
/> />
)} )}
{itemForDisplay.type === 'user' && ( {itemForDisplay.type === 'user' && (
@@ -12,15 +12,19 @@ import { Box, Text } from 'ink';
import type React from 'react'; import type React from 'react';
// Mock dependencies // Mock dependencies
vi.mock('../contexts/SettingsContext.js', () => ({ vi.mock('../contexts/SettingsContext.js', async () => {
useSettings: () => ({ const actual = await vi.importActual('../contexts/SettingsContext.js');
merged: { return {
ui: { ...actual,
showInlineThinking: false, useSettings: () => ({
merged: {
ui: {
showInlineThinking: false,
},
}, },
}, }),
}), };
})); });
vi.mock('../contexts/AppContext.js', async () => { vi.mock('../contexts/AppContext.js', async () => {
const actual = await vi.importActual('../contexts/AppContext.js'); const actual = await vi.importActual('../contexts/AppContext.js');
@@ -7,40 +7,56 @@
import type React from 'react'; import type React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import type { ThoughtSummary } from '@google/gemini-cli-core'; import type { ThoughtSummary } from '@google/gemini-cli-core';
import { MaxSizedBox, MINIMUM_MAX_HEIGHT } from '../shared/MaxSizedBox.js';
interface ThinkingMessageProps { interface ThinkingMessageProps {
thoughts: ThoughtSummary[]; thoughts: ThoughtSummary[];
terminalWidth: number; terminalWidth: number;
availableTerminalHeight?: number;
} }
export const ThinkingMessage: React.FC<ThinkingMessageProps> = ({ export const ThinkingMessage: React.FC<ThinkingMessageProps> = ({
thoughts, thoughts,
terminalWidth, terminalWidth,
}) => ( availableTerminalHeight,
<Box }) => {
borderStyle="round" const contentMaxHeight =
borderColor="magenta" availableTerminalHeight !== undefined
width={terminalWidth} ? Math.max(availableTerminalHeight - 4, MINIMUM_MAX_HEIGHT)
paddingX={1} : undefined;
marginBottom={1}
flexDirection="column" return (
> <Box
<Box> borderStyle="round"
<Text color="magenta"> </Text> borderColor="magenta"
<Text bold color="magenta"> width={terminalWidth}
Thinking paddingX={1}
</Text> marginBottom={1}
<Text dimColor> ({thoughts.length})</Text> flexDirection="column"
</Box> >
{thoughts.map((thought, index) => ( <Box>
<Box key={index} marginTop={1} flexDirection="column"> <Text color="magenta"> </Text>
{thought.subject && ( <Text bold color="magenta">
<Text bold color="magenta"> Thinking
{thought.subject} </Text>
</Text> <Text dimColor> ({thoughts.length})</Text>
)}
<Text>{thought.description || ' '}</Text>
</Box> </Box>
))} <MaxSizedBox
</Box> maxHeight={contentMaxHeight}
); maxWidth={terminalWidth - 2}
overflowDirection="top"
>
{thoughts.map((thought, index) => (
<Box key={index} marginTop={1} flexDirection="column">
{thought.subject && (
<Text bold color="magenta">
{thought.subject}
</Text>
)}
<Text>{thought.description || ' '}</Text>
</Box>
))}
</MaxSizedBox>
</Box>
);
};