From 448838dea83cefc75904e36982986232b6e5414f Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 11 Jul 2025 13:43:57 -0700 Subject: [PATCH] Add visual cues for nightly version (#3701) Co-authored-by: Jacob Richman --- packages/cli/src/gemini.tsx | 3 +++ packages/cli/src/ui/App.test.tsx | 15 +++++++++++++++ packages/cli/src/ui/App.tsx | 13 ++++++++++--- packages/cli/src/ui/components/Footer.tsx | 20 ++++++++++++++++---- packages/cli/src/ui/components/Header.tsx | 12 ++++++++++++ 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/gemini.tsx b/packages/cli/src/gemini.tsx index 23990f6d..74091874 100644 --- a/packages/cli/src/gemini.tsx +++ b/packages/cli/src/gemini.tsx @@ -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( @@ -190,6 +192,7 @@ export async function main() { config={config} settings={settings} startupWarnings={startupWarnings} + version={version} /> , { exitOnCtrlC: false }, diff --git a/packages/cli/src/ui/App.test.tsx b/packages/cli/src/ui/App.test.tsx index 32f13329..fffea64d 100644 --- a/packages/cli/src/ui/App.test.tsx +++ b/packages/cli/src/ui/App.test.tsx @@ -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', () => { , ); currentUnmount = unmount; @@ -274,6 +277,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -293,6 +297,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -315,6 +320,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -334,6 +340,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -353,6 +360,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -372,6 +380,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -392,6 +401,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -404,6 +414,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -422,6 +433,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -440,6 +452,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -469,6 +482,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; @@ -483,6 +497,7 @@ describe('App UI', () => { , ); currentUnmount = unmount; diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index 4e2c7242..519a4b53 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -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) => ( ); -const App = ({ config, settings, startupWarnings = [] }: AppProps) => { +const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { useBracketedPaste(); const [updateMessage, setUpdateMessage] = useState(null); const { stdout } = useStdout(); + const nightly = version.includes('nightly'); useEffect(() => { checkForUpdates().then(setUpdateMessage); @@ -315,7 +317,7 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => { ⚡ To continue accessing the ${currentModel} model today, consider using /auth to switch to using a paid API key from AI Studio at https://aistudio.google.com/apikey`; } else { // Default fallback message for other cases (like consecutive 429s) - message = `⚡ Automatically switching from ${currentModel} to ${fallbackModel} for faster responses for the remainder of this session. + message = `⚡ Automatically switching from ${currentModel} to ${fallbackModel} for faster responses for the remainder of this session. ⚡ Possible reasons for this are that you have received multiple consecutive capacity errors or you have reached your daily ${currentModel} quota limit ⚡ To increase your limits, upgrade to a Gemini Code Assist Standard or Enterprise plan with higher limits at https://goo.gle/set-up-gemini-code-assist ⚡ Or you can utilize a Gemini API Key. See: https://goo.gle/gemini-cli-docs-auth#gemini-api-key @@ -673,7 +675,11 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => { key={staticKey} items={[ -
+
{!settings.merged.hideTips && } , ...history.map((h) => ( @@ -931,6 +937,7 @@ const App = ({ config, settings, startupWarnings = [] }: AppProps) => { config.getDebugMode() || config.getShowMemoryUsage() } promptTokenCount={sessionStats.lastPromptTokenCount} + nightly={nightly} /> diff --git a/packages/cli/src/ui/components/Footer.tsx b/packages/cli/src/ui/components/Footer.tsx index 48f37ee8..95904cd9 100644 --- a/packages/cli/src/ui/components/Footer.tsx +++ b/packages/cli/src/ui/components/Footer.tsx @@ -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 = ({ @@ -36,6 +38,7 @@ export const Footer: React.FC = ({ showErrorDetails, showMemoryUsage, promptTokenCount, + nightly, }) => { const limit = tokenLimit(model); const percentage = promptTokenCount / limit; @@ -43,10 +46,19 @@ export const Footer: React.FC = ({ return ( - - {shortenPath(tildeifyPath(targetDir), 70)} - {branchName && ({branchName}*)} - + {nightly ? ( + + + {shortenPath(tildeifyPath(targetDir), 70)} + {branchName && ({branchName}*)} + + + ) : ( + + {shortenPath(tildeifyPath(targetDir), 70)} + {branchName && ({branchName}*)} + + )} {debugMode && ( {' ' + (debugMessage || '--debug')} diff --git a/packages/cli/src/ui/components/Header.tsx b/packages/cli/src/ui/components/Header.tsx index 375faf07..b99382e0 100644 --- a/packages/cli/src/ui/components/Header.tsx +++ b/packages/cli/src/ui/components/Header.tsx @@ -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 = ({ customAsciiArt, terminalWidth, + version, + nightly, }) => { let displayTitle; const widthOfLongLogo = getAsciiArtWidth(longAsciiLogo); @@ -38,6 +42,7 @@ export const Header: React.FC = ({ alignItems="flex-start" width={artWidth} flexShrink={0} + flexDirection="column" > {Colors.GradientColors ? ( @@ -46,6 +51,13 @@ export const Header: React.FC = ({ ) : ( {displayTitle} )} + {nightly && ( + + + v{version} + + + )} ); };