Compact mode.

This commit is contained in:
jacob314
2026-01-09 08:44:16 -08:00
parent 0144b08231
commit 0f5bcfad1e
8 changed files with 92 additions and 74 deletions
+2 -2
View File
@@ -528,8 +528,8 @@ const SETTINGS_SCHEMA = {
type: 'boolean',
label: 'Compact UI',
category: 'UI',
requiresRestart: false,
default: false,
requiresRestart: true,
default: true,
description: 'Enable a more compact UI layout.',
showInDialog: true,
},
@@ -4,7 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { renderWithProviders } from '../../test-utils/render.js';
import {
renderWithProviders,
createMockSettings,
} from '../../test-utils/render.js';
import { AppHeader } from './AppHeader.js';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { makeFakeConfig } from '@google/gemini-cli-core';
@@ -200,4 +203,34 @@ describe('<AppHeader />', () => {
expect(lastFrame()).not.toContain('First line\\nSecond line');
unmount();
});
it('should render in compact mode', () => {
const mockConfig = makeFakeConfig();
const uiState = {
history: [],
bannerData: {
defaultText: '',
warningText: '',
},
nightly: true,
};
const { lastFrame, unmount } = renderWithProviders(
<AppHeader version="1.0.0" />,
{
config: mockConfig,
uiState,
settings: createMockSettings({
ui: {
compact: true,
},
}),
},
);
expect(lastFrame()).toContain('Tips for getting started:');
expect(lastFrame()).toContain('v1.0.0');
expect(lastFrame()).toMatchSnapshot();
unmount();
});
});
+28 -14
View File
@@ -21,25 +21,39 @@ export const AppHeader = ({ version }: AppHeaderProps) => {
const settings = useSettings();
const config = useConfig();
const { nightly, mainAreaWidth, bannerData, bannerVisible } = useUIState();
const compact = settings.merged.ui?.compact;
const { bannerText } = useBanner(bannerData, config);
return (
const showHeader = !(
settings.merged.ui?.hideBanner || config.getScreenReader()
);
const showTips = !(settings.merged.ui?.hideTips || config.getScreenReader());
const headerContent = showHeader && (
<Box flexDirection="column">
{!(settings.merged.ui?.hideBanner || config.getScreenReader()) && (
<>
<Header version={version} nightly={nightly} />
{bannerVisible && bannerText && (
<Banner
width={mainAreaWidth}
bannerText={bannerText}
isWarning={bannerData.warningText !== ''}
/>
)}
</>
<Header version={version} nightly={nightly} />
{bannerVisible && bannerText && (
<Banner
width={mainAreaWidth}
bannerText={bannerText}
isWarning={bannerData.warningText !== ''}
/>
)}
{!(settings.merged.ui?.hideTips || config.getScreenReader()) && (
<Tips config={config} />
</Box>
);
const tipsContent = showTips && <Tips config={config} />;
return (
<Box
flexDirection={compact ? 'row' : 'column'}
marginTop={compact ? 1 : 0}
marginBottom={0}
>
{headerContent}
{tipsContent && (
<Box marginLeft={compact && showHeader ? 4 : 0}>{tipsContent}</Box>
)}
</Box>
);
@@ -59,7 +59,7 @@ describe('<Header />', () => {
renderWithProviders(<Header version="1.0.0" nightly={false} />);
expect(Text).toHaveBeenCalledWith(
expect.objectContaining({
children: longAsciiLogo,
children: longAsciiLogo.trim(),
}),
undefined,
);
@@ -75,7 +75,7 @@ describe('<Header />', () => {
renderWithProviders(<Header version="1.0.0" nightly={false} />);
expect(Text).toHaveBeenCalledWith(
expect.objectContaining({
children: longAsciiLogoIde,
children: longAsciiLogoIde.trim(),
}),
undefined,
);
@@ -138,13 +138,13 @@ describe('<Header />', () => {
});
expect(Text).toHaveBeenCalledWith(
expect.objectContaining({
children: longAsciiLogoCompact,
children: longAsciiLogoCompact.trim(),
}),
undefined,
);
});
it('renders the version to the right in compact mode when nightly is true', () => {
it('renders the version under the logo in compact mode when nightly is true', () => {
vi.spyOn(useTerminalSize, 'useTerminalSize').mockReturnValue({
columns: 120,
rows: 20,
@@ -160,7 +160,7 @@ describe('<Header />', () => {
},
);
expect(lastFrame()).toContain('v1.0.0');
// In compact mode, logo and version are in the same Box with flexDirection="row"
// In compact mode, logo and version are in the same Box with flexDirection="column"
});
it('renders with no gradient when theme.ui.gradient is undefined', async () => {
+2 -18
View File
@@ -70,27 +70,11 @@ export const Header: React.FC<HeaderProps> = ({
displayTitle = tinyLogo;
}
displayTitle = displayTitle.trim();
const artWidth = getAsciiArtWidth(displayTitle);
const title = useSnowfall(displayTitle);
if (compact) {
return (
<Box
alignItems="flex-end"
flexDirection="row"
flexShrink={0}
marginBottom={1}
>
<ThemedGradient>{title}</ThemedGradient>
{nightly && (
<Box paddingLeft={2}>
<ThemedGradient>v{version}</ThemedGradient>
</Box>
)}
</Box>
);
}
return (
<Box
alignItems="flex-start"
+1 -1
View File
@@ -17,7 +17,7 @@ export const Tips: React.FC<TipsProps> = ({ config }) => {
const geminiMdFileCount = config.getGeminiMdFileCount();
return (
<Box flexDirection="column">
<Text color={theme.text.primary}>Tips for getting started:</Text>
<Text color={theme.text.accent}>Tips for getting started:</Text>
<Text color={theme.text.primary}>
1. Ask questions, edit files, or run commands.
</Text>
@@ -1,8 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`AlternateBufferQuittingDisplay > renders with active and pending tool messages > with_history_and_pending 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -10,7 +9,6 @@ exports[`AlternateBufferQuittingDisplay > renders with active and pending tool m
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -31,8 +29,7 @@ Tips for getting started:
`;
exports[`AlternateBufferQuittingDisplay > renders with empty history and no pending items > empty 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -40,7 +37,6 @@ exports[`AlternateBufferQuittingDisplay > renders with empty history and no pend
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -49,8 +45,7 @@ Tips for getting started:
`;
exports[`AlternateBufferQuittingDisplay > renders with history but no pending items > with_history_no_pending 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -58,7 +53,6 @@ exports[`AlternateBufferQuittingDisplay > renders with history but no pending it
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -75,8 +69,7 @@ Tips for getting started:
`;
exports[`AlternateBufferQuittingDisplay > renders with pending items but no history > with_pending_no_history 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -84,7 +77,6 @@ exports[`AlternateBufferQuittingDisplay > renders with pending items but no hist
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -97,8 +89,7 @@ Tips for getting started:
`;
exports[`AlternateBufferQuittingDisplay > renders with user and gemini messages > with_user_gemini_messages 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -106,7 +97,6 @@ exports[`AlternateBufferQuittingDisplay > renders with user and gemini messages
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -1,8 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<AppHeader /> > should not render the banner when no flags are set 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -10,7 +9,6 @@ exports[`<AppHeader /> > should not render the banner when no flags are set 1`]
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -19,8 +17,7 @@ Tips for getting started:
`;
exports[`<AppHeader /> > should not render the banner when previewFeatures is enabled 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -28,7 +25,6 @@ exports[`<AppHeader /> > should not render the banner when previewFeatures is en
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -37,8 +33,7 @@ Tips for getting started:
`;
exports[`<AppHeader /> > should not render the default banner if shown count is 5 or more 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -46,7 +41,6 @@ exports[`<AppHeader /> > should not render the default banner if shown count is
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -54,9 +48,17 @@ Tips for getting started:
4. /help for more information."
`;
exports[`<AppHeader /> > should render the banner when previewFeatures is disabled 1`] = `
exports[`<AppHeader /> > should render in compact mode 1`] = `
"
███ █████████
▝▜▄ ▗█▀▀▜▙▝█▛▀▀▌▜██▖▟██▘▜█▘▜██▖▝█▛▝█▛ Tips for getting started:
▝▜▄ █▌ █▙▟ ▐█▝█▛▐█ ▐█ ▐█▝█▖█▌ █▌ 1. Ask questions, edit files, or run commands.
▗▟▀ ▜▙ ▝█▛ █▌▝ ▖▐█ ▐█ ▐█ ▐█ ▝██▌ █▌ 2. Be specific for the best results.
▝▀ ▀▀▀▀▘▝▀▀▀▀▘▀▀▘ ▀▀▘▀▀▘▀▀▘ ▝▀▀▝▀▀ 3. Create GEMINI.md files to customize your interactions with Gemini.
v1.0.0 4. /help for more information."
`;
exports[`<AppHeader /> > should render the banner when previewFeatures is disabled 1`] = `
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -64,7 +66,6 @@ exports[`<AppHeader /> > should render the banner when previewFeatures is disabl
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ This is the default banner │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -76,8 +77,7 @@ Tips for getting started:
`;
exports[`<AppHeader /> > should render the banner with default text 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -85,7 +85,6 @@ exports[`<AppHeader /> > should render the banner with default text 1`] = `
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ This is the default banner │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -97,8 +96,7 @@ Tips for getting started:
`;
exports[`<AppHeader /> > should render the banner with warning text 1`] = `
"
███ █████████
"███ █████████
░░░███ ███░░░░░███
░░░███ ███ ░░░
░░░███░███
@@ -106,7 +104,6 @@ exports[`<AppHeader /> > should render the banner with warning text 1`] = `
███░ ░░███ ░░███
███░ ░░█████████
░░░ ░░░░░░░░░
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ There are capacity issues │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯