feat(ui): implement compact version tags in AppHeader

Long nightly and pre-release version strings (e.g., `0.42.0-nightly.20260428.g59b2dea0e`) were causing UI clutter and layout issues, especially on narrow terminals.

This change:
- Parses the version string to extract the base version and the first pre-release tag.
- Renders the tag in a compact format `[tag]` with dimmed color.
- Reduces the horizontal footprint of the header metadata.

Verified with new unit tests in `AppHeader.test.tsx` and manual inspection of Ink output snapshots.

cc @gundermanc

Label: bot-fix
This commit is contained in:
gemini-cli[bot]
2026-05-13 19:29:31 +00:00
parent 74e9079e5b
commit 41a12e13bb
2 changed files with 51 additions and 3 deletions
@@ -11,7 +11,7 @@ import {
import type { LoadedSettings } from '../../config/settings.js';
import { AppHeader } from './AppHeader.js';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { makeFakeConfig } from '@google/gemini-cli-core';
import { makeFakeConfig, type ContentGeneratorConfig } from '@google/gemini-cli-core';
import crypto from 'node:crypto';
import { _clearSessionBannersForTest } from '../hooks/useBanner.js';
@@ -252,7 +252,7 @@ describe('<AppHeader />', () => {
const mockConfig = makeFakeConfig();
vi.spyOn(mockConfig, 'getContentGeneratorConfig').mockReturnValue({
authType: undefined,
} as any); // eslint-disable-line @typescript-eslint/no-explicit-any
} as ContentGeneratorConfig);
const { lastFrame, waitUntilReady, unmount } = await renderWithProviders(
<AppHeader version="1.0.0" />,
@@ -289,4 +289,39 @@ describe('<AppHeader />', () => {
expect(lastFrame()).not.toContain('Tips');
unmount();
});
it('should render compact version tags for nightly builds', async () => {
const { lastFrame, unmount } = await renderWithProviders(
<AppHeader version="0.42.0-nightly.20260428.g59b2dea0e" />,
{},
);
expect(lastFrame()).toContain('v0.42.0');
expect(lastFrame()).toContain('[nightly]');
expect(lastFrame()).not.toContain('20260428');
unmount();
});
it('should render compact version tags for alpha builds', async () => {
const { lastFrame, unmount } = await renderWithProviders(
<AppHeader version="1.0.0-alpha.1" />,
{},
);
expect(lastFrame()).toContain('v1.0.0');
expect(lastFrame()).toContain('[alpha]');
unmount();
});
it('should render normal versions without tags', async () => {
const { lastFrame, unmount } = await renderWithProviders(
<AppHeader version="1.2.3" />,
{},
);
expect(lastFrame()).toContain('v1.2.3');
expect(lastFrame()).not.toContain('[');
expect(lastFrame()).not.toContain(']');
unmount();
});
});
+14 -1
View File
@@ -90,6 +90,13 @@ export const AppHeader = ({ version, showDetails = true }: AppHeaderProps) => {
}
}
// Parse version string (e.g., 0.42.0-nightly.20260428.g59b2dea0e)
// to extract the base version and the tag.
const versionParts = version.split('-');
const baseVersion = versionParts[0];
const tagPart = versionParts.slice(1).join('-');
const tag = tagPart ? tagPart.split('.')[0] : '';
// If the terminal is too narrow to fit the icon and metadata (especially long nightly versions)
// side-by-side, we switch to column mode to prevent wrapping.
const isNarrow = terminalWidth < NARROW_TERMINAL_BREAKPOINT;
@@ -114,7 +121,13 @@ export const AppHeader = ({ version, showDetails = true }: AppHeaderProps) => {
<Text bold color={theme.text.primary}>
Gemini CLI
</Text>
<Text color={theme.text.secondary}> v{version}</Text>
<Text color={theme.text.secondary}> v{baseVersion}</Text>
{tag && (
<Text color={theme.text.secondary} dimColor>
{' '}
[{tag}]
</Text>
)}
{updateInfo?.isUpdating && (
<Box marginLeft={2}>
<Text color={theme.text.secondary}>