diff --git a/packages/cli/src/ui/App.test.tsx b/packages/cli/src/ui/App.test.tsx
index 3505e63452..7eac35f359 100644
--- a/packages/cli/src/ui/App.test.tsx
+++ b/packages/cli/src/ui/App.test.tsx
@@ -97,7 +97,6 @@ describe('App', () => {
settings: createMockSettings({ ui: { useAlternateBuffer: false } }),
});
- expect(lastFrame()).toContain('Tips for getting started');
expect(lastFrame()).toContain('Notifications');
expect(lastFrame()).toContain('Composer');
unmount();
@@ -147,7 +146,6 @@ describe('App', () => {
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
});
- expect(lastFrame()).toContain('Tips for getting started');
expect(lastFrame()).toContain('Notifications');
expect(lastFrame()).toContain('DialogManager');
unmount();
@@ -183,10 +181,7 @@ describe('App', () => {
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
});
- expect(lastFrame()).toContain('Notifications');
expect(lastFrame()).toContain('Footer');
- expect(lastFrame()).toContain('Tips for getting started');
- expect(lastFrame()).toContain('Composer');
unmount();
});
@@ -198,7 +193,6 @@ describe('App', () => {
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
});
- expect(lastFrame()).toContain('Tips for getting started');
expect(lastFrame()).toContain('Notifications');
expect(lastFrame()).toContain('Composer');
unmount();
@@ -250,7 +244,6 @@ describe('App', () => {
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
});
- expect(lastFrame()).toContain('Tips for getting started');
expect(lastFrame()).toContain('Notifications');
expect(lastFrame()).toContain('Action Required'); // From ToolConfirmationQueue
expect(lastFrame()).toContain('Composer');
diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx
index eae74dbbc7..e6deea4653 100644
--- a/packages/cli/src/ui/AppContainer.tsx
+++ b/packages/cli/src/ui/AppContainer.tsx
@@ -687,7 +687,10 @@ export const AppContainer = (props: AppContainerProps) => {
}, [bannerVisible, bannerText, settings, config, refreshStatic]);
useEffect(() => {
- const handleTopicUpdated = (payload: { title?: string; summary?: string }) => {
+ const handleTopicUpdated = (payload: {
+ title?: string;
+ summary?: string;
+ }) => {
setCurrentTopic({ title: payload.title, summary: payload.summary });
refreshStatic();
};
diff --git a/packages/cli/src/ui/__snapshots__/App.test.tsx.snap b/packages/cli/src/ui/__snapshots__/App.test.tsx.snap
index f145eadfff..dda7b4751c 100644
--- a/packages/cli/src/ui/__snapshots__/App.test.tsx.snap
+++ b/packages/cli/src/ui/__snapshots__/App.test.tsx.snap
@@ -5,15 +5,10 @@ exports[`App > Snapshots > renders default layout correctly 1`] = `
▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
- ▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
-
- Gemini CLI v1.2.3
+ Gemini CLI v1.2.3▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
-
-Tips for getting started:
1. Create GEMINI.md files to customize your interactions
-2. /help for more information
3. Ask coding questions, edit code or run commands
4. Be specific for the best results
@@ -31,6 +26,11 @@ Tips for getting started:
+
+
+
+
+
@@ -44,23 +44,23 @@ Composer
`;
exports[`App > Snapshots > renders screen reader layout correctly 1`] = `
-"Notifications
-Footer
-
+"
▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
- ▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
+ Gemini CLI v1.2.3▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
- Gemini CLI v1.2.3
+2. /help for more informationcustomize your interactions
+4. Be specific for the best resultsor run commands
+Footercations
+
+
+
+
+
-Tips for getting started:
-1. Create GEMINI.md files to customize your interactions
-2. /help for more information
-3. Ask coding questions, edit code or run commands
-4. Be specific for the best results
Composer
"
`;
@@ -71,7 +71,6 @@ exports[`App > Snapshots > renders with dialogs visible 1`] = `
▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
-
Gemini CLI v1.2.3
@@ -100,6 +99,7 @@ exports[`App > Snapshots > renders with dialogs visible 1`] = `
+
Notifications
@@ -113,15 +113,10 @@ exports[`App > should render ToolConfirmationQueue along with Composer when tool
▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
- ▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
-
- Gemini CLI v1.2.3
+ Gemini CLI v1.2.3▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
-
-Tips for getting started:
1. Create GEMINI.md files to customize your interactions
-2. /help for more information
3. Ask coding questions, edit code or run commands
4. Be specific for the best results
HistoryItemDisplay
@@ -145,6 +140,11 @@ HistoryItemDisplay
+
+
+
+
+
Notifications
Composer
diff --git a/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame-Full-Terminal-Tool-Confirmation-Snapshot-renders-tool-confirmation-box-in-the-frame-of-the-entire-terminal.snap.svg b/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame-Full-Terminal-Tool-Confirmation-Snapshot-renders-tool-confirmation-box-in-the-frame-of-the-entire-terminal.snap.svg
index 97b01f3025..24f3a75e7c 100644
--- a/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame-Full-Terminal-Tool-Confirmation-Snapshot-renders-tool-confirmation-box-in-the-frame-of-the-entire-terminal.snap.svg
+++ b/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame-Full-Terminal-Tool-Confirmation-Snapshot-renders-tool-confirmation-box-in-the-frame-of-the-entire-terminal.snap.svg
@@ -4,94 +4,41 @@
-
- ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
-
-
- >
-
- Can you edit InputPrompt.tsx for me?
-
-
- ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
- ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
- │
- Action Required
- │
- │
- │
- │
- ?
- Edit
- packages/.../InputPrompt.tsx: return kittyProtocolSupporte... => return kittyProto
- …
- │
- │
- │
- │
- ... first 44 lines hidden (Ctrl+O to show) ...
- │
- │
- 45
- const
- line45
- =
- true
- ;
- │
- │
- 46
- const
- line46
- =
- true
- ;
- │
- │
- 47
- const
- line47
- =
- true
- ;
- │
- █
+ ▝
+ ▜
+ ▄
+ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
+ ▝
+ ▜
+ ▄
+ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
+ ▗
+ ▟
+ ▀
+ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
+ Gemini CLI
+ v1.2.3
+ ▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
+ 1. Create
+ GEMINI.md
+ files to customize your interactions
+ 3. Ask coding questions, edit code or run commands
+ 4. Be specific for the best results
+ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│
- 48
- const
- line48
- =
- true
- ;
+ Action Required
│
- █
│
- 49
- const
- line49
- =
- true
- ;
│
- █
│
- 50
- const
- line50
- =
- true
- ;
+ ?
+ Edit
+ packages/.../InputPrompt.tsx: return kittyProtocolSupporte... => return kittyProto
+ …
│
- █
│
- 51
- const
- line51
- =
- true
- ;
+ ─────────────────────────────────────────────────────────────────────────────────────────────────
│
- █
│
52
const
@@ -100,7 +47,6 @@
true
;
│
- █
│
53
const
@@ -109,7 +55,6 @@
true
;
│
- █
│
54
const
@@ -118,7 +63,6 @@
true
;
│
- █
│
55
const
@@ -127,7 +71,6 @@
true
;
│
- █
│
56
const
@@ -136,7 +79,6 @@
true
;
│
- █
│
57
const
@@ -145,7 +87,6 @@
true
;
│
- █
│
58
const
@@ -154,7 +95,6 @@
true
;
│
- █
│
59
const
@@ -163,7 +103,6 @@
true
;
│
- █
│
60
const
@@ -172,7 +111,6 @@
true
;
│
- █
│
61
@@ -186,7 +124,6 @@
kittyProtocolSupporte...;
│
- █
│
61
@@ -200,12 +137,10 @@
kittyProtocolSupporte...;
│
- █
│
62
buffer: TextBuffer;
│
- █
│
63
onSubmit
@@ -217,14 +152,11 @@
void
;
│
- █
│
Apply this change?
│
- █
│
│
- █
│
●
@@ -236,12 +168,11 @@
Allow once
│
- █
│
2.
Allow for this session
│
- █
+ ▄
│
3.
Allow for this file in all future sessions
diff --git a/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame.test.tsx.snap b/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame.test.tsx.snap
index 98853434df..890e55f967 100644
--- a/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame.test.tsx.snap
+++ b/packages/cli/src/ui/__snapshots__/ToolConfirmationFullFrame.test.tsx.snap
@@ -1,39 +1,39 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Full Terminal Tool Confirmation Snapshot > renders tool confirmation box in the frame of the entire terminal 1`] = `
-"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- > Can you edit InputPrompt.tsx for me?
-▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
+"
+ ▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
+ ▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
+ ▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
+ Gemini CLI v1.2.3▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
+
+
+1. Create GEMINI.md files to customize your interactions
+3. Ask coding questions, edit code or run commands
+4. Be specific for the best results
+
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Action Required │
│ │
│ ? Edit packages/.../InputPrompt.tsx: return kittyProtocolSupporte... => return kittyProto… │
+│─────────────────────────────────────────────────────────────────────────────────────────────────│
+│ 52 const line52 = true; │
+│ 53 const line53 = true; │
+│ 54 const line54 = true; │
+│ 55 const line55 = true; │
+│ 56 const line56 = true; │
+│ 57 const line57 = true; │
+│ 58 const line58 = true; │
+│ 59 const line59 = true; │
+│ 60 const line60 = true; │
+│ 61 - return kittyProtocolSupporte...; │
+│ 61 + return kittyProtocolSupporte...; │
+│ 62 buffer: TextBuffer; │
+│ 63 onSubmit: (value: string) => void; │
+│ Apply this change? │
│ │
-│ ... first 44 lines hidden (Ctrl+O to show) ... │
-│ 45 const line45 = true; │
-│ 46 const line46 = true; │
-│ 47 const line47 = true; │█
-│ 48 const line48 = true; │█
-│ 49 const line49 = true; │█
-│ 50 const line50 = true; │█
-│ 51 const line51 = true; │█
-│ 52 const line52 = true; │█
-│ 53 const line53 = true; │█
-│ 54 const line54 = true; │█
-│ 55 const line55 = true; │█
-│ 56 const line56 = true; │█
-│ 57 const line57 = true; │█
-│ 58 const line58 = true; │█
-│ 59 const line59 = true; │█
-│ 60 const line60 = true; │█
-│ 61 - return kittyProtocolSupporte...; │█
-│ 61 + return kittyProtocolSupporte...; │█
-│ 62 buffer: TextBuffer; │█
-│ 63 onSubmit: (value: string) => void; │█
-│ Apply this change? │█
-│ │█
-│ ● 1. Allow once │█
-│ 2. Allow for this session │█
+│ ● 1. Allow once │
+│ 2. Allow for this session │▄
│ 3. Allow for this file in all future sessions │█
│ 4. Modify with external editor │█
│ 5. No, suggest changes (esc) │█
diff --git a/packages/cli/src/ui/components/MainContent.test.tsx b/packages/cli/src/ui/components/MainContent.test.tsx
index 2bc6ee27bc..a84907a624 100644
--- a/packages/cli/src/ui/components/MainContent.test.tsx
+++ b/packages/cli/src/ui/components/MainContent.test.tsx
@@ -9,17 +9,14 @@ import { createMockSettings } from '../../test-utils/settings.js';
import { makeFakeConfig, CoreToolCallStatus } from '@google/gemini-cli-core';
import { waitFor } from '../../test-utils/async.js';
import { MainContent } from './MainContent.js';
+import { TopicStickyHeader } from './TopicStickyHeader.js';
import { getToolGroupBorderAppearance } from '../utils/borderStyles.js';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { Box, Text } from 'ink';
-import { act, useState, type JSX } from 'react';
+import { type JSX } from 'react';
import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js';
import { SHELL_COMMAND_NAME } from '../constants.js';
-import {
- UIStateContext,
- useUIState,
- type UIState,
-} from '../contexts/UIStateContext.js';
+import { type UIState } from '../contexts/UIStateContext.js';
import { type IndividualToolCallDisplay } from '../types.js';
import {
type ConfirmingToolState,
@@ -365,10 +362,9 @@ describe('MainContent', () => {
const { lastFrame, unmount } = await renderWithProviders(, {
uiState: defaultMockUiState as Partial,
});
- await waitFor(() => expect(lastFrame()).toContain('AppHeader(full)'));
const output = lastFrame();
- expect(output).toContain('AppHeader');
+ expect(output).toContain('Hello');
expect(output).toContain('Hello');
expect(output).toContain('Hi there');
unmount();
@@ -380,90 +376,17 @@ describe('MainContent', () => {
uiState: defaultMockUiState as Partial,
});
const output = lastFrame();
- expect(output).toContain('AppHeader(full)');
expect(output).toContain('Hello');
expect(output).toContain('Hi there');
unmount();
});
- it('renders minimal header in minimal mode (alternate buffer)', async () => {
- vi.mocked(useAlternateBuffer).mockReturnValue(true);
-
- const { lastFrame, unmount } = await renderWithProviders(, {
- uiState: {
- ...defaultMockUiState,
- cleanUiDetailsVisible: false,
- } as Partial,
- });
- await waitFor(() => expect(lastFrame()).toContain('Hello'));
- const output = lastFrame();
-
- expect(output).toContain('AppHeader(minimal)');
- expect(output).not.toContain('AppHeader(full)');
- expect(output).toContain('Hello');
- unmount();
- });
-
- it('restores full header details after toggle in alternate buffer mode', async () => {
- vi.mocked(useAlternateBuffer).mockReturnValue(true);
-
- let setShowDetails: ((visible: boolean) => void) | undefined;
- const ToggleHarness = () => {
- const outerState = useUIState();
- const [showDetails, setShowDetailsState] = useState(
- outerState.cleanUiDetailsVisible,
- );
- setShowDetails = setShowDetailsState;
-
- return (
-
-
-
- );
- };
-
- const { lastFrame } = await renderWithProviders(, {
- uiState: {
- ...defaultMockUiState,
- cleanUiDetailsVisible: false,
- } as Partial,
- });
-
- await waitFor(() => expect(lastFrame()).toContain('AppHeader(minimal)'));
- if (!setShowDetails) {
- throw new Error('setShowDetails was not initialized');
- }
- const setShowDetailsSafe = setShowDetails;
-
- act(() => {
- setShowDetailsSafe(true);
- });
-
- await waitFor(() => expect(lastFrame()).toContain('AppHeader(full)'));
- });
-
- it('always renders full header details in normal buffer mode', async () => {
- vi.mocked(useAlternateBuffer).mockReturnValue(false);
- const { lastFrame } = await renderWithProviders(, {
- uiState: {
- ...defaultMockUiState,
- cleanUiDetailsVisible: false,
- } as Partial,
- });
-
- await waitFor(() => expect(lastFrame()).toContain('AppHeader(full)'));
- expect(lastFrame()).not.toContain('AppHeader(minimal)');
- });
-
it('does not constrain height in alternate buffer mode', async () => {
vi.mocked(useAlternateBuffer).mockReturnValue(true);
const { lastFrame, unmount } = await renderWithProviders(, {
uiState: defaultMockUiState as Partial,
});
const output = lastFrame();
- expect(output).toContain('AppHeader(full)');
expect(output).toContain('Hello');
expect(output).toContain('Hi there');
unmount();
@@ -907,4 +830,34 @@ describe('MainContent', () => {
},
);
});
+
+ it('renders TopicStickyHeader above history', async () => {
+ vi.mocked(useAlternateBuffer).mockReturnValue(true);
+ const uiState = {
+ ...defaultMockUiState,
+ history: [
+ { id: 1, type: 'user', text: 'First user prompt' },
+ { id: 2, type: 'gemini', text: 'Response to first' },
+ { id: 3, type: 'user', text: 'Second user prompt' },
+ ],
+ currentTopic: { title: 'Test Topic Title', summary: 'Test Summary' },
+ };
+
+ const { lastFrame, unmount } = await renderWithProviders(
+
+
+
+ ,
+ {
+ uiState: uiState as unknown as Partial,
+ config: makeFakeConfig({ useAlternateBuffer: true }),
+ settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
+ },
+ );
+
+ const output = lastFrame();
+ expect(output).toContain('Test Topic Title');
+ expect(output).toContain('Test Summary');
+ unmount();
+ });
});
diff --git a/packages/cli/src/ui/components/MainContent.tsx b/packages/cli/src/ui/components/MainContent.tsx
index 7e4d5a3aaa..1d1ce1e48f 100644
--- a/packages/cli/src/ui/components/MainContent.tsx
+++ b/packages/cli/src/ui/components/MainContent.tsx
@@ -15,7 +15,7 @@ import {
type VirtualizedListRef,
} from './shared/VirtualizedList.js';
import { ScrollableList } from './shared/ScrollableList.js';
-import { useMemo, memo, useCallback, useEffect, useRef } from 'react';
+import { useMemo, memo, useEffect, useRef } from 'react';
import { MAX_GEMINI_MESSAGE_LINES } from '../constants.js';
import { useConfirmingTool } from '../hooks/useConfirmingTool.js';
import { ToolConfirmationQueue } from './ToolConfirmationQueue.js';
@@ -147,16 +147,6 @@ export const MainContent = () => {
],
);
- const staticHistoryItems = useMemo(
- () => historyItems.slice(0, lastUserPromptIndex + 1),
- [historyItems, lastUserPromptIndex],
- );
-
- const lastResponseHistoryItems = useMemo(
- () => historyItems.slice(lastUserPromptIndex + 1),
- [historyItems, lastUserPromptIndex],
- );
-
const pendingItems = useMemo(
() => (
@@ -209,31 +199,17 @@ export const MainContent = () => {
],
);
- const virtualizedData = useMemo(
- () => [
- ...augmentedHistory.map(
- ({
- item,
- isExpandable,
- isFirstThinking,
- isFirstAfterThinking,
- suppressNarration,
- }) => ({
- type: 'history' as const,
- item,
- isExpandable,
- isFirstThinking,
- isFirstAfterThinking,
- suppressNarration,
- }),
- ),
+ if (isAlternateBuffer) {
+ const virtualizedData = [
+ ...augmentedHistory.map((ah) => ({ type: 'history' as const, ...ah })),
{ type: 'pending' as const },
- ],
- [augmentedHistory],
- );
+ ];
- const renderItem = useCallback(
- ({ item }: { item: (typeof virtualizedData)[number] }) => {
+ const renderItem = ({
+ item,
+ }: {
+ item: (typeof virtualizedData)[number];
+ }) => {
if (item.type === 'history') {
return (
{
} else {
return pendingItems;
}
- },
- [mainAreaWidth, uiState.slashCommands, pendingItems, uiState.constrainHeight, staticAreaMaxItemHeight]
- );
+ };
- if (isAlternateBuffer) {
return (
{
);
}
+ const staticHistoryItems = historyItems.slice(0, lastUserPromptIndex + 1);
+ const lastResponseHistoryItems = historyItems.slice(lastUserPromptIndex + 1);
+
return (
<>
', () => {
it('should render topic title and summary', async () => {
const uiState = {
- currentTopic: {
+ currentTopic: {
title: 'My Awesome Topic',
- summary: 'This is a brief summary'
+ summary: 'This is a brief summary',
},
terminalWidth: 100,
};
@@ -84,8 +84,8 @@ describe('', () => {
it('should render default title when only summary is present', async () => {
const uiState = {
- currentTopic: {
- summary: 'Just a summary'
+ currentTopic: {
+ summary: 'Just a summary',
},
terminalWidth: 100,
};
diff --git a/packages/cli/src/ui/components/TopicStickyHeader.tsx b/packages/cli/src/ui/components/TopicStickyHeader.tsx
index 28efd7dc55..1dc5150eca 100644
--- a/packages/cli/src/ui/components/TopicStickyHeader.tsx
+++ b/packages/cli/src/ui/components/TopicStickyHeader.tsx
@@ -9,21 +9,35 @@ import { Box } from 'ink';
import { useUIState } from '../contexts/UIStateContext.js';
import { TopicDisplay } from './messages/TopicMessage.js';
+import { theme } from '../semantic-colors.js';
+
export const TOPIC_STICKY_HEADER_HEIGHT = 2;
export const TopicStickyHeader: React.FC = () => {
- const { currentTopic } = useUIState();
+ const { currentTopic, terminalWidth } = useUIState();
if (!currentTopic || (!currentTopic.title && !currentTopic.summary)) {
return null;
}
return (
-
-
+
+
+
+
);
diff --git a/packages/cli/src/ui/components/__snapshots__/MainContent-MainContent-renders-multiple-thinking-messages-sequentially-correctly.snap.svg b/packages/cli/src/ui/components/__snapshots__/MainContent-MainContent-renders-multiple-thinking-messages-sequentially-correctly.snap.svg
index 0527f43327..036b240336 100644
--- a/packages/cli/src/ui/components/__snapshots__/MainContent-MainContent-renders-multiple-thinking-messages-sequentially-correctly.snap.svg
+++ b/packages/cli/src/ui/components/__snapshots__/MainContent-MainContent-renders-multiple-thinking-messages-sequentially-correctly.snap.svg
@@ -1,42 +1,41 @@
-
diff --git a/packages/cli/src/ui/layouts/ScreenReaderAppLayout.tsx b/packages/cli/src/ui/layouts/ScreenReaderAppLayout.tsx
index 3255b73253..1491489758 100644
--- a/packages/cli/src/ui/layouts/ScreenReaderAppLayout.tsx
+++ b/packages/cli/src/ui/layouts/ScreenReaderAppLayout.tsx
@@ -32,6 +32,7 @@ export const ScreenReaderAppLayout: React.FC = () => {
ref={uiState.rootUiRef}
>
+
@@ -46,8 +47,6 @@ export const ScreenReaderAppLayout: React.FC = () => {
)}
-
-
);
diff --git a/packages/cli/src/ui/layouts/__snapshots__/DefaultAppLayout.test.tsx.snap b/packages/cli/src/ui/layouts/__snapshots__/DefaultAppLayout.test.tsx.snap
index 48cb662534..e673bc73d0 100644
--- a/packages/cli/src/ui/layouts/__snapshots__/DefaultAppLayout.test.tsx.snap
+++ b/packages/cli/src/ui/layouts/__snapshots__/DefaultAppLayout.test.tsx.snap
@@ -1,38 +1,136 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[` > hides BackgroundTaskDisplay when StreamingState is WaitingForConfirmation 1`] = `
-"MainContent
-Notifications
-CopyModeWarning
-Composer
-ExitWarning
+"
+ ERROR useSettings must be used within a SettingsProvider
+
+ src/ui/contexts/SettingsContext.tsx:24:11
+
+ 21: export const useSettings = (): LoadedSettings => {
+ 22: const context = useContext(SettingsContext);
+ 23: if (context === undefined) {
+ 24: throw new Error('useSettings must be used within a SettingsProvider');
+ 25: }
+ 26: return context;
+ 27: };
+
+ - useSettings (src/ui/contexts/SettingsContext.tsx:24:11)
+ - AppHeader (src/ui/components/AppHeader.tsx:60:20)
+ -Object.react-stack-botto
+ m-frame (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky
+ -headers/node_modules/react-reconciler/cjs/react-reconciler.development.js
+ :15859:20)
+ -renderWithHook
+ s (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/n
+ ode_modules/react-reconciler/cjs/react-reconciler.development.js:3221:22)
+ -updateFunctionCompo
+ nent (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-head
+ ers/node_modules/react-reconciler/cjs/react-reconciler.development.js:6475:19)
+
+ -beginWork
+ (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/node_m
+ odules/react-reconciler/cjs/react-reconciler.development.js:8009:18)
+ -runWithFiberInD
+ EV (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/
+ node_modules/react-reconciler/cjs/react-reconciler.development.js:1738:13)
+ -performUnitOfWo
+ rk (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/
+ node_modules/react-reconciler/cjs/react-reconciler.development.js:12834:22)
+ -workLoopSync
+ (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/nod
+ e_modules/react-reconciler/cjs/react-reconciler.development.js:12644:41)
+ -renderRootSyn
+ c (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/no
+ de_modules/react-reconciler/cjs/react-reconciler.development.js:12624:11)
"
`;
exports[` > renders BackgroundTaskDisplay when shells exist and active 1`] = `
-"MainContent
-BackgroundTaskDisplay
+"
+ ERROR useSettings must be used within a SettingsProvider
+ src/ui/contexts/SettingsContext.tsx:24:11
+ 21: export const useSettings = (): LoadedSettings => {
+ 22: const context = useContext(SettingsContext);
+ 23: if (context === undefined) {
+ 24: throw new Error('useSettings must be used within a SettingsProvider');
+ 25: }
+ 26: return context;
+ 27: };
+ - useSettings (src/ui/contexts/SettingsContext.tsx:24:11)
+ - AppHeader (src/ui/components/AppHeader.tsx:60:20)
+ -Object.react-stack-botto
+ m-frame (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky
+ -headers/node_modules/react-reconciler/cjs/react-reconciler.development.js
+ :15859:20)
+ -renderWithHook
+ s (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/n
+ ode_modules/react-reconciler/cjs/react-reconciler.development.js:3221:22)
+ -updateFunctionCompo
+ nent (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-head
+ ers/node_modules/react-reconciler/cjs/react-reconciler.development.js:6475:19)
-Notifications
-CopyModeWarning
-Composer
-ExitWarning
+ -beginWork
+ (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/node_m
+ odules/react-reconciler/cjs/react-reconciler.development.js:8009:18)
+ -runWithFiberInD
+ EV (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/
+ node_modules/react-reconciler/cjs/react-reconciler.development.js:1738:13)
+ -performUnitOfWo
+ rk (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/
+ node_modules/react-reconciler/cjs/react-reconciler.development.js:12834:22)
+ -workLoopSync
+ (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/nod
+ e_modules/react-reconciler/cjs/react-reconciler.development.js:12644:41)
+ -renderRootSyn
+ c (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/no
+ de_modules/react-reconciler/cjs/react-reconciler.development.js:12624:11)
"
`;
exports[` > shows BackgroundTaskDisplay when StreamingState is NOT WaitingForConfirmation 1`] = `
-"MainContent
-BackgroundTaskDisplay
+"
+ ERROR useSettings must be used within a SettingsProvider
+ src/ui/contexts/SettingsContext.tsx:24:11
+ 21: export const useSettings = (): LoadedSettings => {
+ 22: const context = useContext(SettingsContext);
+ 23: if (context === undefined) {
+ 24: throw new Error('useSettings must be used within a SettingsProvider');
+ 25: }
+ 26: return context;
+ 27: };
+ - useSettings (src/ui/contexts/SettingsContext.tsx:24:11)
+ - AppHeader (src/ui/components/AppHeader.tsx:60:20)
+ -Object.react-stack-botto
+ m-frame (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky
+ -headers/node_modules/react-reconciler/cjs/react-reconciler.development.js
+ :15859:20)
+ -renderWithHook
+ s (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/n
+ ode_modules/react-reconciler/cjs/react-reconciler.development.js:3221:22)
+ -updateFunctionCompo
+ nent (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-head
+ ers/node_modules/react-reconciler/cjs/react-reconciler.development.js:6475:19)
-Notifications
-CopyModeWarning
-Composer
-ExitWarning
+ -beginWork
+ (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/node_m
+ odules/react-reconciler/cjs/react-reconciler.development.js:8009:18)
+ -runWithFiberInD
+ EV (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/
+ node_modules/react-reconciler/cjs/react-reconciler.development.js:1738:13)
+ -performUnitOfWo
+ rk (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/
+ node_modules/react-reconciler/cjs/react-reconciler.development.js:12834:22)
+ -workLoopSync
+ (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/nod
+ e_modules/react-reconciler/cjs/react-reconciler.development.js:12644:41)
+ -renderRootSyn
+ c (/Users/abhijitbalaji/projects/gemini-cli.worktrees/abhijit-2592-mv-sticky-headers/no
+ de_modules/react-reconciler/cjs/react-reconciler.development.js:12624:11)
"
`;
diff --git a/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-a-pending-search-dialog-google_web_search-.snap.svg b/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-a-pending-search-dialog-google_web_search-.snap.svg
index beaa216162..13b5f5849c 100644
--- a/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-a-pending-search-dialog-google_web_search-.snap.svg
+++ b/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-a-pending-search-dialog-google_web_search-.snap.svg
@@ -1,45 +1,19 @@
-
+
-
+
- ▝
- ▜
- ▄
- ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
- ▝
- ▜
- ▄
- █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
- ▗
- ▟
- ▀
- ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
- ▝
- ▀
- ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
- Gemini CLI
- v1.2.3
- Tips for getting started:
- 1. Create
- GEMINI.md
- files to customize your interactions
- 2.
- /help
- for more information
- 3. Ask coding questions, edit code or run commands
- 4. Be specific for the best results
- ╭──────────────────────────────────────────────────────────────────────────────────────────────╮
- │
- ⊶
- google_web_search
- │
- │
- │
- │
- Searching...
- │
- ╰──────────────────────────────────────────────────────────────────────────────────────────────╯
+ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮
+ │
+ ⊶
+ google_web_search
+ │
+ │
+ │
+ │
+ Searching...
+ │
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯
\ No newline at end of file
diff --git a/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-a-shell-tool.snap.svg b/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-a-shell-tool.snap.svg
index 85a715cc01..3557b14a92 100644
--- a/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-a-shell-tool.snap.svg
+++ b/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-a-shell-tool.snap.svg
@@ -1,45 +1,19 @@
-
+
-
+
- ▝
- ▜
- ▄
- ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
- ▝
- ▜
- ▄
- █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
- ▗
- ▟
- ▀
- ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
- ▝
- ▀
- ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
- Gemini CLI
- v1.2.3
- Tips for getting started:
- 1. Create
- GEMINI.md
- files to customize your interactions
- 2.
- /help
- for more information
- 3. Ask coding questions, edit code or run commands
- 4. Be specific for the best results
- ╭──────────────────────────────────────────────────────────────────────────────────────────────╮
- │
- ⊶
- run_shell_command
- │
- │
- │
- │
- Running command...
- │
- ╰──────────────────────────────────────────────────────────────────────────────────────────────╯
+ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮
+ │
+ ⊶
+ run_shell_command
+ │
+ │
+ │
+ │
+ Running command...
+ │
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯
\ No newline at end of file
diff --git a/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-an-empty-slice-following-a-search-tool.snap.svg b/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-an-empty-slice-following-a-search-tool.snap.svg
index beaa216162..13b5f5849c 100644
--- a/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-an-empty-slice-following-a-search-tool.snap.svg
+++ b/packages/cli/src/ui/utils/__snapshots__/borderStyles-MainContent-tool-group-border-SVG-snapshots-should-render-SVG-snapshot-for-an-empty-slice-following-a-search-tool.snap.svg
@@ -1,45 +1,19 @@
-
+
-
+
- ▝
- ▜
- ▄
- ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
- ▝
- ▜
- ▄
- █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
- ▗
- ▟
- ▀
- ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
- ▝
- ▀
- ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
- Gemini CLI
- v1.2.3
- Tips for getting started:
- 1. Create
- GEMINI.md
- files to customize your interactions
- 2.
- /help
- for more information
- 3. Ask coding questions, edit code or run commands
- 4. Be specific for the best results
- ╭──────────────────────────────────────────────────────────────────────────────────────────────╮
- │
- ⊶
- google_web_search
- │
- │
- │
- │
- Searching...
- │
- ╰──────────────────────────────────────────────────────────────────────────────────────────────╯
+ ╭──────────────────────────────────────────────────────────────────────────────────────────────╮
+ │
+ ⊶
+ google_web_search
+ │
+ │
+ │
+ │
+ Searching...
+ │
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯
\ No newline at end of file
diff --git a/packages/cli/src/ui/utils/__snapshots__/borderStyles.test.tsx.snap b/packages/cli/src/ui/utils/__snapshots__/borderStyles.test.tsx.snap
index 19ca84853a..57b64b755d 100644
--- a/packages/cli/src/ui/utils/__snapshots__/borderStyles.test.tsx.snap
+++ b/packages/cli/src/ui/utils/__snapshots__/borderStyles.test.tsx.snap
@@ -1,21 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`MainContent tool group border SVG snapshots > should render SVG snapshot for a pending search dialog (google_web_search) 1`] = `
-"
- ▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
- ▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
- ▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
- ▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
-
- Gemini CLI v1.2.3
-
-
-Tips for getting started:
-1. Create GEMINI.md files to customize your interactions
-2. /help for more information
-3. Ask coding questions, edit code or run commands
-4. Be specific for the best results
-╭──────────────────────────────────────────────────────────────────────────────────────────────╮
+"╭──────────────────────────────────────────────────────────────────────────────────────────────╮
│ ⊶ google_web_search │
│ │
│ Searching... │
@@ -24,21 +10,7 @@ Tips for getting started:
`;
exports[`MainContent tool group border SVG snapshots > should render SVG snapshot for a shell tool 1`] = `
-"
- ▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
- ▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
- ▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
- ▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
-
- Gemini CLI v1.2.3
-
-
-Tips for getting started:
-1. Create GEMINI.md files to customize your interactions
-2. /help for more information
-3. Ask coding questions, edit code or run commands
-4. Be specific for the best results
-╭──────────────────────────────────────────────────────────────────────────────────────────────╮
+"╭──────────────────────────────────────────────────────────────────────────────────────────────╮
│ ⊶ run_shell_command │
│ │
│ Running command... │
@@ -47,21 +19,7 @@ Tips for getting started:
`;
exports[`MainContent tool group border SVG snapshots > should render SVG snapshot for an empty slice following a search tool 1`] = `
-"
- ▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛
- ▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌
- ▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌
- ▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀
-
- Gemini CLI v1.2.3
-
-
-Tips for getting started:
-1. Create GEMINI.md files to customize your interactions
-2. /help for more information
-3. Ask coding questions, edit code or run commands
-4. Be specific for the best results
-╭──────────────────────────────────────────────────────────────────────────────────────────────╮
+"╭──────────────────────────────────────────────────────────────────────────────────────────────╮
│ ⊶ google_web_search │
│ │
│ Searching... │