Add visual cues for nightly version (#3701)

Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
Miguel Solorio 2025-07-11 13:43:57 -07:00 committed by GitHub
parent 4197f30278
commit 448838dea8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 56 additions and 7 deletions

View File

@ -26,6 +26,7 @@ import { getUserStartupWarnings } from './utils/userStartupWarnings.js';
import { runNonInteractive } from './nonInteractiveCli.js';
import { loadExtensions, Extension } from './config/extension.js';
import { cleanupCheckpoints } from './utils/cleanup.js';
import { getCliVersion } from './utils/version.js';
import {
ApprovalMode,
Config,
@ -183,6 +184,7 @@ export async function main() {
// Render UI, passing necessary config values. Check that there is no command line question.
if (process.stdin.isTTY && input?.length === 0) {
const version = await getCliVersion();
setWindowTitle(basename(workspaceRoot), settings);
render(
<React.StrictMode>
@ -190,6 +192,7 @@ export async function main() {
config={config}
settings={settings}
startupWarnings={startupWarnings}
version={version}
/>
</React.StrictMode>,
{ exitOnCtrlC: false },

View File

@ -186,6 +186,7 @@ vi.mock('./components/Tips.js', () => ({
describe('App UI', () => {
let mockConfig: MockServerConfig;
let mockSettings: LoadedSettings;
let mockVersion: string;
let currentUnmount: (() => void) | undefined;
const createMockSettings = (
@ -229,6 +230,7 @@ describe('App UI', () => {
cwd: '/tmp',
model: 'model',
}) as unknown as MockServerConfig;
mockVersion = '0.0.0-test';
// Ensure the getShowMemoryUsage mock function is specifically set up if not covered by constructor mock
if (!mockConfig.getShowMemoryUsage) {
@ -258,6 +260,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -274,6 +277,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -293,6 +297,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -315,6 +320,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -334,6 +340,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -353,6 +360,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -372,6 +380,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -392,6 +401,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -404,6 +414,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -422,6 +433,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -440,6 +452,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -469,6 +482,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;
@ -483,6 +497,7 @@ describe('App UI', () => {
<App
config={mockConfig as unknown as ServerConfig}
settings={mockSettings}
version={mockVersion}
/>,
);
currentUnmount = unmount;

View File

@ -84,6 +84,7 @@ interface AppProps {
config: Config;
settings: LoadedSettings;
startupWarnings?: string[];
version: string;
}
export const AppWrapper = (props: AppProps) => (
@ -92,10 +93,11 @@ export const AppWrapper = (props: AppProps) => (
</SessionStatsProvider>
);
const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
useBracketedPaste();
const [updateMessage, setUpdateMessage] = useState<string | null>(null);
const { stdout } = useStdout();
const nightly = version.includes('nightly');
useEffect(() => {
checkForUpdates().then(setUpdateMessage);
@ -673,7 +675,11 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
key={staticKey}
items={[
<Box flexDirection="column" key="header">
<Header terminalWidth={terminalWidth} />
<Header
terminalWidth={terminalWidth}
version={version}
nightly={nightly}
/>
{!settings.merged.hideTips && <Tips config={config} />}
</Box>,
...history.map((h) => (
@ -931,6 +937,7 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => {
config.getDebugMode() || config.getShowMemoryUsage()
}
promptTokenCount={sessionStats.lastPromptTokenCount}
nightly={nightly}
/>
</Box>
</Box>

View File

@ -10,6 +10,7 @@ import { Colors } from '../colors.js';
import { shortenPath, tildeifyPath, tokenLimit } from '@google/gemini-cli-core';
import { ConsoleSummaryDisplay } from './ConsoleSummaryDisplay.js';
import process from 'node:process';
import Gradient from 'ink-gradient';
import { MemoryUsageDisplay } from './MemoryUsageDisplay.js';
interface FooterProps {
@ -23,6 +24,7 @@ interface FooterProps {
showErrorDetails: boolean;
showMemoryUsage?: boolean;
promptTokenCount: number;
nightly: boolean;
}
export const Footer: React.FC<FooterProps> = ({
@ -36,6 +38,7 @@ export const Footer: React.FC<FooterProps> = ({
showErrorDetails,
showMemoryUsage,
promptTokenCount,
nightly,
}) => {
const limit = tokenLimit(model);
const percentage = promptTokenCount / limit;
@ -43,10 +46,19 @@ export const Footer: React.FC<FooterProps> = ({
return (
<Box marginTop={1} justifyContent="space-between" width="100%">
<Box>
<Text color={Colors.LightBlue}>
{shortenPath(tildeifyPath(targetDir), 70)}
{branchName && <Text color={Colors.Gray}> ({branchName}*)</Text>}
</Text>
{nightly ? (
<Gradient colors={Colors.GradientColors}>
<Text>
{shortenPath(tildeifyPath(targetDir), 70)}
{branchName && <Text> ({branchName}*)</Text>}
</Text>
</Gradient>
) : (
<Text color={Colors.LightBlue}>
{shortenPath(tildeifyPath(targetDir), 70)}
{branchName && <Text color={Colors.Gray}> ({branchName}*)</Text>}
</Text>
)}
{debugMode && (
<Text color={Colors.AccentRed}>
{' ' + (debugMessage || '--debug')}

View File

@ -14,11 +14,15 @@ import { getAsciiArtWidth } from '../utils/textUtils.js';
interface HeaderProps {
customAsciiArt?: string; // For user-defined ASCII art
terminalWidth: number; // For responsive logo
version: string;
nightly: boolean;
}
export const Header: React.FC<HeaderProps> = ({
customAsciiArt,
terminalWidth,
version,
nightly,
}) => {
let displayTitle;
const widthOfLongLogo = getAsciiArtWidth(longAsciiLogo);
@ -38,6 +42,7 @@ export const Header: React.FC<HeaderProps> = ({
alignItems="flex-start"
width={artWidth}
flexShrink={0}
flexDirection="column"
>
{Colors.GradientColors ? (
<Gradient colors={Colors.GradientColors}>
@ -46,6 +51,13 @@ export const Header: React.FC<HeaderProps> = ({
) : (
<Text>{displayTitle}</Text>
)}
{nightly && (
<Box width="100%" flexDirection="row" justifyContent="flex-end">
<Gradient colors={Colors.GradientColors}>
<Text>v{version}</Text>
</Gradient>
</Box>
)}
</Box>
);
};