mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-16 00:51:25 -07:00
refactor(ui): extract QueuedMessageDisplay into separate component (#8374)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
This commit is contained in:
@@ -65,6 +65,21 @@ vi.mock('./ShowMoreLines.js', () => ({
|
||||
ShowMoreLines: () => <Text>ShowMoreLines</Text>,
|
||||
}));
|
||||
|
||||
vi.mock('./QueuedMessageDisplay.js', () => ({
|
||||
QueuedMessageDisplay: ({ messageQueue }: { messageQueue: string[] }) => {
|
||||
if (messageQueue.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{messageQueue.map((message, index) => (
|
||||
<Text key={index}>{message}</Text>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock contexts
|
||||
vi.mock('../contexts/OverflowContext.js', () => ({
|
||||
OverflowProvider: ({ children }: { children: React.ReactNode }) => children,
|
||||
@@ -289,36 +304,17 @@ describe('Composer', () => {
|
||||
expect(output).toContain('Third queued message');
|
||||
});
|
||||
|
||||
it('shows overflow indicator when more than 3 messages are queued', () => {
|
||||
const uiState = createMockUIState({
|
||||
messageQueue: [
|
||||
'Message 1',
|
||||
'Message 2',
|
||||
'Message 3',
|
||||
'Message 4',
|
||||
'Message 5',
|
||||
],
|
||||
});
|
||||
|
||||
const { lastFrame } = renderComposer(uiState);
|
||||
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('Message 1');
|
||||
expect(output).toContain('Message 2');
|
||||
expect(output).toContain('Message 3');
|
||||
expect(output).toContain('... (+2 more)');
|
||||
});
|
||||
|
||||
it('does not display message queue section when empty', () => {
|
||||
it('renders QueuedMessageDisplay with empty message queue', () => {
|
||||
const uiState = createMockUIState({
|
||||
messageQueue: [],
|
||||
});
|
||||
|
||||
const { lastFrame } = renderComposer(uiState);
|
||||
|
||||
// Should not contain queued message indicators
|
||||
// The component should render but return null for empty queue
|
||||
// This test verifies that the component receives the correct prop
|
||||
const output = lastFrame();
|
||||
expect(output).not.toContain('more)');
|
||||
expect(output).toContain('InputPrompt'); // Verify basic Composer rendering
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { DetailedMessagesDisplay } from './DetailedMessagesDisplay.js';
|
||||
import { InputPrompt, calculatePromptWidths } from './InputPrompt.js';
|
||||
import { Footer, type FooterProps } from './Footer.js';
|
||||
import { ShowMoreLines } from './ShowMoreLines.js';
|
||||
import { QueuedMessageDisplay } from './QueuedMessageDisplay.js';
|
||||
import { OverflowProvider } from '../contexts/OverflowContext.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { isNarrowWidth } from '../utils/isNarrowWidth.js';
|
||||
@@ -27,8 +28,6 @@ import { ApprovalMode } from '@google/gemini-cli-core';
|
||||
import { StreamingState } from '../types.js';
|
||||
import { ConfigInitDisplay } from '../components/ConfigInitDisplay.js';
|
||||
|
||||
const MAX_DISPLAYED_QUEUED_MESSAGES = 3;
|
||||
|
||||
export const Composer = () => {
|
||||
const config = useConfig();
|
||||
const settings = useSettings();
|
||||
@@ -89,33 +88,7 @@ export const Composer = () => {
|
||||
|
||||
{!uiState.isConfigInitialized && <ConfigInitDisplay />}
|
||||
|
||||
{uiState.messageQueue.length > 0 && (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
{uiState.messageQueue
|
||||
.slice(0, MAX_DISPLAYED_QUEUED_MESSAGES)
|
||||
.map((message, index) => {
|
||||
const preview = message.replace(/\s+/g, ' ');
|
||||
|
||||
return (
|
||||
<Box key={index} paddingLeft={2} width="100%">
|
||||
<Text dimColor wrap="truncate">
|
||||
{preview}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
{uiState.messageQueue.length > MAX_DISPLAYED_QUEUED_MESSAGES && (
|
||||
<Box paddingLeft={2}>
|
||||
<Text dimColor>
|
||||
... (+
|
||||
{uiState.messageQueue.length -
|
||||
MAX_DISPLAYED_QUEUED_MESSAGES}{' '}
|
||||
more)
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
<QueuedMessageDisplay messageQueue={uiState.messageQueue} />
|
||||
|
||||
<Box
|
||||
marginTop={1}
|
||||
|
||||
76
packages/cli/src/ui/components/QueuedMessageDisplay.test.tsx
Normal file
76
packages/cli/src/ui/components/QueuedMessageDisplay.test.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render } from 'ink-testing-library';
|
||||
import { QueuedMessageDisplay } from './QueuedMessageDisplay.js';
|
||||
|
||||
describe('QueuedMessageDisplay', () => {
|
||||
it('renders nothing when message queue is empty', () => {
|
||||
const { lastFrame } = render(<QueuedMessageDisplay messageQueue={[]} />);
|
||||
|
||||
expect(lastFrame()).toBe('');
|
||||
});
|
||||
|
||||
it('displays single queued message', () => {
|
||||
const { lastFrame } = render(
|
||||
<QueuedMessageDisplay messageQueue={['First message']} />,
|
||||
);
|
||||
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('First message');
|
||||
});
|
||||
|
||||
it('displays multiple queued messages', () => {
|
||||
const messageQueue = [
|
||||
'First queued message',
|
||||
'Second queued message',
|
||||
'Third queued message',
|
||||
];
|
||||
|
||||
const { lastFrame } = render(
|
||||
<QueuedMessageDisplay messageQueue={messageQueue} />,
|
||||
);
|
||||
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('First queued message');
|
||||
expect(output).toContain('Second queued message');
|
||||
expect(output).toContain('Third queued message');
|
||||
});
|
||||
|
||||
it('shows overflow indicator when more than 3 messages are queued', () => {
|
||||
const messageQueue = [
|
||||
'Message 1',
|
||||
'Message 2',
|
||||
'Message 3',
|
||||
'Message 4',
|
||||
'Message 5',
|
||||
];
|
||||
|
||||
const { lastFrame } = render(
|
||||
<QueuedMessageDisplay messageQueue={messageQueue} />,
|
||||
);
|
||||
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('Message 1');
|
||||
expect(output).toContain('Message 2');
|
||||
expect(output).toContain('Message 3');
|
||||
expect(output).toContain('... (+2 more)');
|
||||
expect(output).not.toContain('Message 4');
|
||||
expect(output).not.toContain('Message 5');
|
||||
});
|
||||
|
||||
it('normalizes whitespace in messages', () => {
|
||||
const messageQueue = ['Message with\tmultiple\n whitespace'];
|
||||
|
||||
const { lastFrame } = render(
|
||||
<QueuedMessageDisplay messageQueue={messageQueue} />,
|
||||
);
|
||||
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('Message with multiple whitespace');
|
||||
});
|
||||
});
|
||||
47
packages/cli/src/ui/components/QueuedMessageDisplay.tsx
Normal file
47
packages/cli/src/ui/components/QueuedMessageDisplay.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Box, Text } from 'ink';
|
||||
|
||||
const MAX_DISPLAYED_QUEUED_MESSAGES = 3;
|
||||
|
||||
export interface QueuedMessageDisplayProps {
|
||||
messageQueue: string[];
|
||||
}
|
||||
|
||||
export const QueuedMessageDisplay = ({
|
||||
messageQueue,
|
||||
}: QueuedMessageDisplayProps) => {
|
||||
if (messageQueue.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
{messageQueue
|
||||
.slice(0, MAX_DISPLAYED_QUEUED_MESSAGES)
|
||||
.map((message, index) => {
|
||||
const preview = message.replace(/\s+/g, ' ');
|
||||
|
||||
return (
|
||||
<Box key={index} paddingLeft={2} width="100%">
|
||||
<Text dimColor wrap="truncate">
|
||||
{preview}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
{messageQueue.length > MAX_DISPLAYED_QUEUED_MESSAGES && (
|
||||
<Box paddingLeft={2}>
|
||||
<Text dimColor>
|
||||
... (+
|
||||
{messageQueue.length - MAX_DISPLAYED_QUEUED_MESSAGES} more)
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user