Skip to content

Commit 8086afd

Browse files
committed
Fix bottom border color
1 parent 366f1df commit 8086afd

12 files changed

+506
-118
lines changed

packages/cli/src/ui/App.test.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,18 @@ describe('App', () => {
216216

217217
const stateWithConfirmingTool = {
218218
...mockUIState,
219-
pendingHistoryItems: [{ type: 'tool_group', tools: toolCalls }],
220-
pendingGeminiHistoryItems: [{ type: 'tool_group', tools: toolCalls }],
219+
pendingHistoryItems: [
220+
{
221+
type: 'tool_group',
222+
tools: toolCalls,
223+
},
224+
],
225+
pendingGeminiHistoryItems: [
226+
{
227+
type: 'tool_group',
228+
tools: toolCalls,
229+
},
230+
],
221231
} as UIState;
222232

223233
const configWithExperiment = makeFakeConfig();

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ interface HistoryItemDisplayProps {
4747
activeShellPtyId?: number | null;
4848
embeddedShellFocused?: boolean;
4949
availableTerminalHeightGemini?: number;
50+
borderColor?: string;
51+
borderDimColor?: boolean;
5052
}
5153

5254
export const HistoryItemDisplay: React.FC<HistoryItemDisplayProps> = ({
@@ -58,6 +60,8 @@ export const HistoryItemDisplay: React.FC<HistoryItemDisplayProps> = ({
5860
activeShellPtyId,
5961
embeddedShellFocused,
6062
availableTerminalHeightGemini,
63+
borderColor,
64+
borderDimColor,
6165
}) => {
6266
const settings = useSettings();
6367
const inlineThinkingMode = getInlineThinkingMode(settings);
@@ -181,6 +185,8 @@ export const HistoryItemDisplay: React.FC<HistoryItemDisplayProps> = ({
181185
embeddedShellFocused={embeddedShellFocused}
182186
borderTop={itemForDisplay.borderTop}
183187
borderBottom={itemForDisplay.borderBottom}
188+
borderColor={borderColor ?? ''}
189+
borderDimColor={borderDimColor ?? false}
184190
/>
185191
)}
186192
{itemForDisplay.type === 'compression' && (

packages/cli/src/ui/components/MainContent.test.tsx

Lines changed: 205 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import { renderWithProviders } from '../../test-utils/render.js';
88
import { waitFor } from '../../test-utils/async.js';
9-
import { MainContent } from './MainContent.js';
9+
import { MainContent, getToolGroupBorderAppearance } from './MainContent.js';
1010
import { describe, it, expect, vi, beforeEach } from 'vitest';
1111
import { Box, Text } from 'ink';
1212
import { act, useState, type JSX } from 'react';
@@ -18,6 +18,7 @@ import {
1818
type UIState,
1919
} from '../contexts/UIStateContext.js';
2020
import { CoreToolCallStatus } from '@google/gemini-cli-core';
21+
import { type IndividualToolCallDisplay } from '../types.js';
2122

2223
// Mock dependencies
2324
vi.mock('../contexts/SettingsContext.js', async () => {
@@ -76,6 +77,208 @@ vi.mock('./shared/ScrollableList.js', () => ({
7677
SCROLL_TO_ITEM_END: 0,
7778
}));
7879

80+
import { theme } from '../semantic-colors.js';
81+
import { type BackgroundShell } from '../hooks/shellReducer.js';
82+
83+
describe('getToolGroupBorderAppearance', () => {
84+
const mockBackgroundShells = new Map<number, BackgroundShell>();
85+
const activeShellPtyId = 123;
86+
87+
it('returns default empty values for non-tool_group items', () => {
88+
const item = { type: 'user' as const, text: 'Hello', id: 1 };
89+
const result = getToolGroupBorderAppearance(
90+
item,
91+
null,
92+
false,
93+
[],
94+
mockBackgroundShells,
95+
);
96+
expect(result).toEqual({ borderColor: '', borderDimColor: false });
97+
});
98+
99+
it('inspects only the last pending tool_group item if current has no tools', () => {
100+
const item = { type: 'tool_group' as const, tools: [], id: 1 };
101+
const pendingItems = [
102+
{
103+
type: 'tool_group' as const,
104+
tools: [
105+
{
106+
callId: '1',
107+
name: 'some_tool',
108+
description: '',
109+
status: CoreToolCallStatus.Executing,
110+
ptyId: undefined,
111+
resultDisplay: undefined,
112+
confirmationDetails: undefined,
113+
} as IndividualToolCallDisplay,
114+
],
115+
},
116+
{
117+
type: 'tool_group' as const,
118+
tools: [
119+
{
120+
callId: '2',
121+
name: 'other_tool',
122+
description: '',
123+
status: CoreToolCallStatus.Success,
124+
ptyId: undefined,
125+
resultDisplay: undefined,
126+
confirmationDetails: undefined,
127+
} as IndividualToolCallDisplay,
128+
],
129+
},
130+
];
131+
132+
// Only the last item (Success) should be inspected, so hasPending = false.
133+
// The previous item was Executing (pending) but it shouldn't be counted.
134+
const result = getToolGroupBorderAppearance(
135+
item,
136+
null,
137+
false,
138+
pendingItems,
139+
mockBackgroundShells,
140+
);
141+
expect(result).toEqual({
142+
borderColor: theme.border.default,
143+
borderDimColor: false,
144+
});
145+
});
146+
147+
it('returns default border for completed normal tools', () => {
148+
const item = {
149+
type: 'tool_group' as const,
150+
tools: [
151+
{
152+
callId: '1',
153+
name: 'some_tool',
154+
description: '',
155+
status: CoreToolCallStatus.Success,
156+
ptyId: undefined,
157+
resultDisplay: undefined,
158+
confirmationDetails: undefined,
159+
} as IndividualToolCallDisplay,
160+
],
161+
id: 1,
162+
};
163+
const result = getToolGroupBorderAppearance(
164+
item,
165+
null,
166+
false,
167+
[],
168+
mockBackgroundShells,
169+
);
170+
expect(result).toEqual({
171+
borderColor: theme.border.default,
172+
borderDimColor: false,
173+
});
174+
});
175+
176+
it('returns warning border for pending normal tools', () => {
177+
const item = {
178+
type: 'tool_group' as const,
179+
tools: [
180+
{
181+
callId: '1',
182+
name: 'some_tool',
183+
description: '',
184+
status: CoreToolCallStatus.Executing,
185+
ptyId: undefined,
186+
resultDisplay: undefined,
187+
confirmationDetails: undefined,
188+
} as IndividualToolCallDisplay,
189+
],
190+
id: 1,
191+
};
192+
const result = getToolGroupBorderAppearance(
193+
item,
194+
null,
195+
false,
196+
[],
197+
mockBackgroundShells,
198+
);
199+
expect(result).toEqual({
200+
borderColor: theme.status.warning,
201+
borderDimColor: true,
202+
});
203+
});
204+
205+
it('returns symbol border for executing shell commands', () => {
206+
const item = {
207+
type: 'tool_group' as const,
208+
tools: [
209+
{
210+
callId: '1',
211+
name: SHELL_COMMAND_NAME,
212+
description: '',
213+
status: CoreToolCallStatus.Executing,
214+
ptyId: activeShellPtyId,
215+
resultDisplay: undefined,
216+
confirmationDetails: undefined,
217+
} as IndividualToolCallDisplay,
218+
],
219+
id: 1,
220+
};
221+
// While executing shell commands, it's dim false, border symbol
222+
const result = getToolGroupBorderAppearance(
223+
item,
224+
activeShellPtyId,
225+
true,
226+
[],
227+
mockBackgroundShells,
228+
);
229+
expect(result).toEqual({
230+
borderColor: theme.ui.symbol,
231+
borderDimColor: false,
232+
});
233+
});
234+
235+
it('returns symbol border and dims color for background executing shell command when another shell is active', () => {
236+
const item = {
237+
type: 'tool_group' as const,
238+
tools: [
239+
{
240+
callId: '1',
241+
name: SHELL_COMMAND_NAME,
242+
description: '',
243+
status: CoreToolCallStatus.Executing,
244+
ptyId: 456, // Different ptyId, not active
245+
resultDisplay: undefined,
246+
confirmationDetails: undefined,
247+
} as IndividualToolCallDisplay,
248+
],
249+
id: 1,
250+
};
251+
const result = getToolGroupBorderAppearance(
252+
item,
253+
activeShellPtyId,
254+
false,
255+
[],
256+
mockBackgroundShells,
257+
);
258+
expect(result).toEqual({
259+
borderColor: theme.ui.symbol,
260+
borderDimColor: true,
261+
});
262+
});
263+
264+
it('handles empty tools with active shell turn (isCurrentlyInShellTurn)', () => {
265+
const item = { type: 'tool_group' as const, tools: [], id: 1 };
266+
267+
// active shell turn
268+
const result = getToolGroupBorderAppearance(
269+
item,
270+
activeShellPtyId,
271+
true,
272+
[],
273+
mockBackgroundShells,
274+
);
275+
// Since there are no tools to inspect, it falls back to empty pending, but isCurrentlyInShellTurn=true
276+
// so it counts as pending shell.
277+
expect(result.borderColor).toEqual(theme.ui.symbol);
278+
// It shouldn't be dim because there are no tools to say it isEmbeddedShellFocused = false
279+
});
280+
});
281+
79282
describe('MainContent', () => {
80283
const defaultMockUiState = {
81284
history: [
@@ -258,7 +461,7 @@ describe('MainContent', () => {
258461
history: [],
259462
pendingHistoryItems: [
260463
{
261-
type: 'tool_group' as const,
464+
type: 'tool_group',
262465
id: 1,
263466
tools: [
264467
{

0 commit comments

Comments
 (0)