diff --git a/packages/cli/src/ui/__snapshots__/App.test.tsx.snap b/packages/cli/src/ui/__snapshots__/App.test.tsx.snap index 450da8362e..8b3308803d 100644 --- a/packages/cli/src/ui/__snapshots__/App.test.tsx.snap +++ b/packages/cli/src/ui/__snapshots__/App.test.tsx.snap @@ -2,20 +2,20 @@ exports[`App > Snapshots > renders default layout correctly 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v1.2.3 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results + + + + @@ -47,34 +47,29 @@ exports[`App > Snapshots > renders screen reader layout correctly 1`] = ` "Notifications Footer - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v1.2.3 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. -Composer -" +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results +Composer" `; exports[`App > Snapshots > renders with dialogs visible 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v1.2.3 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + + + + @@ -110,34 +105,34 @@ DialogManager exports[`App > should render ToolConfirmationQueue along with Composer when tool is confirming and experiment is on 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v1.2.3 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results HistoryItemDisplay -╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ Action Required │ -│ │ -│ ? ls list directory │ -│ │ -│ ls │ -│ Allow execution of: 'ls'? │ -│ │ -│ ● 1. Allow once │ -│ 2. Allow for this session │ -│ 3. No, suggest changes (esc) │ -│ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ Action Required │ +│ │ +│ ? ls list directory │ +│ │ +│ ls │ +│ Allow execution of: 'ls'? │ +│ │ +│ ● 1. Allow once │ +│ 2. Allow for this session │ +│ 3. No, suggest changes (esc) │ +│ │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + + + + diff --git a/packages/cli/src/ui/components/AppHeader.tsx b/packages/cli/src/ui/components/AppHeader.tsx index ad5e2f67d2..aaa16e86a5 100644 --- a/packages/cli/src/ui/components/AppHeader.tsx +++ b/packages/cli/src/ui/components/AppHeader.tsx @@ -4,55 +4,140 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Box } from 'ink'; -import { Header } from './Header.js'; +import { Box, Text } from 'ink'; +import { useEffect, useMemo, useState } from 'react'; +import { UserAccountManager, AuthType } from '@google/gemini-cli-core'; import { Tips } from './Tips.js'; -import { UserIdentity } from './UserIdentity.js'; import { useSettings } from '../contexts/SettingsContext.js'; import { useConfig } from '../contexts/ConfigContext.js'; import { useUIState } from '../contexts/UIStateContext.js'; import { Banner } from './Banner.js'; import { useBanner } from '../hooks/useBanner.js'; import { useTips } from '../hooks/useTips.js'; +import { theme } from '../semantic-colors.js'; +import { ThemedGradient } from './ThemedGradient.js'; +import { CliSpinner } from './CliSpinner.js'; interface AppHeaderProps { version: string; showDetails?: boolean; } +const ICON = `▝▜▄ + ▝▜▄ + ▗▟▀ +▝▀ `; + export const AppHeader = ({ version, showDetails = true }: AppHeaderProps) => { const settings = useSettings(); const config = useConfig(); - const { nightly, terminalWidth, bannerData, bannerVisible } = useUIState(); + const { terminalWidth, bannerData, bannerVisible, updateInfo } = useUIState(); const { bannerText } = useBanner(bannerData); const { showTips } = useTips(); + const authType = config.getContentGeneratorConfig()?.authType; + const [email, setEmail] = useState(); + + useEffect(() => { + if (authType) { + const userAccountManager = new UserAccountManager(); + // Even though the current implementation of getCachedGoogleAccount is sync, + // it performs file I/O. Moving it to useEffect ensures it doesn't block the render cycle. + setEmail(userAccountManager.getCachedGoogleAccount() ?? undefined); + } + }, [authType]); + + const tierName = useMemo(() => config.getUserTierName(), [config]); + + const showHeader = !( + settings.merged.ui.hideBanner || config.getScreenReader() + ); + if (!showDetails) { return ( -
+ {showHeader && ( + + + {ICON} + + + + + Gemini CLI + + v{version} + + + + )} ); } return ( - {!(settings.merged.ui.hideBanner || config.getScreenReader()) && ( - <> -
- {bannerVisible && bannerText && ( - - )} - + {showHeader && ( + + + {ICON} + + + {/* Line 1: Gemini CLI vVersion [Updating] */} + + + Gemini CLI + + v{version} + {updateInfo && ( + + + Updating + + + )} + + + {/* Line 2: Blank */} + + + {/* Line 3: User Email /auth */} + + + {authType === AuthType.LOGIN_WITH_GOOGLE ? ( + {email ?? 'Logged in with Google'} + ) : ( + `Authenticated with ${authType}` + )} + + /auth + + + {/* Line 4: Tier Name /upgrade */} + + + {tierName ?? 'Gemini Code Assist for individuals'} + + /upgrade + + + )} - {settings.merged.ui.showUserIdentity !== false && ( - + + {bannerVisible && bannerText && ( + )} + {!(settings.merged.ui.hideTips || config.getScreenReader()) && showTips && } diff --git a/packages/cli/src/ui/components/Tips.test.tsx b/packages/cli/src/ui/components/Tips.test.tsx index 06b4760834..1730ae81fd 100644 --- a/packages/cli/src/ui/components/Tips.test.tsx +++ b/packages/cli/src/ui/components/Tips.test.tsx @@ -6,27 +6,23 @@ import { render } from '../../test-utils/render.js'; import { Tips } from './Tips.js'; -import { describe, it, expect, vi } from 'vitest'; +import { describe, it, expect } from 'vitest'; import type { Config } from '@google/gemini-cli-core'; describe('Tips', () => { - it.each([ - [0, '3. Create GEMINI.md files'], - [5, '3. /help for more information'], - ])( - 'renders correct tips when file count is %i', - async (count, expectedText) => { - const config = { - getGeminiMdFileCount: vi.fn().mockReturnValue(count), - } as unknown as Config; + it('renders correct tips', async () => { + const config = {} as unknown as Config; - const { lastFrame, waitUntilReady, unmount } = render( - , - ); - await waitUntilReady(); - const output = lastFrame(); - expect(output).toContain(expectedText); - unmount(); - }, - ); + const { lastFrame, waitUntilReady, unmount } = render( + , + ); + await waitUntilReady(); + const output = lastFrame(); + expect(output).toContain('1. /help for more information'); + expect(output).toContain( + '2. Ask coding questions, edit code or run commands', + ); + expect(output).toContain('3. Be specific for the best results'); + unmount(); + }); }); diff --git a/packages/cli/src/ui/components/Tips.tsx b/packages/cli/src/ui/components/Tips.tsx index 576b8494c5..94ff8dee6e 100644 --- a/packages/cli/src/ui/components/Tips.tsx +++ b/packages/cli/src/ui/components/Tips.tsx @@ -13,33 +13,15 @@ interface TipsProps { config: Config; } -export const Tips: React.FC = ({ config }) => { - const geminiMdFileCount = config.getGeminiMdFileCount(); - return ( - - Tips for getting started: - - 1. Ask questions, edit files, or run commands. - - - 2. Be specific for the best results. - - {geminiMdFileCount === 0 && ( - - 3. Create{' '} - - GEMINI.md - {' '} - files to customize your interactions with Gemini. - - )} - - {geminiMdFileCount === 0 ? '4.' : '3.'}{' '} - - /help - {' '} - for more information. - - - ); -}; +export const Tips: React.FC = () => ( + + Tips for getting started: + + 1. /help for more information + + + 2. Ask coding questions, edit code or run commands + + 3. Be specific for the best results + +); diff --git a/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap index 18e75b75e2..a2ca66d9f5 100644 --- a/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap @@ -2,20 +2,16 @@ exports[`AlternateBufferQuittingDisplay > renders with a tool awaiting confirmation > with_confirming_tool 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v0.10.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results Action Required (was prompted): @@ -25,20 +21,16 @@ Action Required (was prompted): exports[`AlternateBufferQuittingDisplay > renders with active and pending tool messages > with_history_and_pending 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v0.10.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results ╭──────────────────────────────────────────────────────────────────────────╮ │ ✓ tool1 Description for tool 1 │ │ │ @@ -46,45 +38,35 @@ Tips for getting started: ╭──────────────────────────────────────────────────────────────────────────╮ │ ✓ tool2 Description for tool 2 │ │ │ -╰──────────────────────────────────────────────────────────────────────────╯ -" +╰──────────────────────────────────────────────────────────────────────────╯" `; exports[`AlternateBufferQuittingDisplay > renders with empty history and no pending items > empty 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v0.10.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. -" +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results" `; exports[`AlternateBufferQuittingDisplay > renders with history but no pending items > with_history_no_pending 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v0.10.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results ╭──────────────────────────────────────────────────────────────────────────╮ │ ✓ tool1 Description for tool 1 │ │ │ @@ -92,47 +74,37 @@ Tips for getting started: ╭──────────────────────────────────────────────────────────────────────────╮ │ ✓ tool2 Description for tool 2 │ │ │ -╰──────────────────────────────────────────────────────────────────────────╯ -" +╰──────────────────────────────────────────────────────────────────────────╯" `; exports[`AlternateBufferQuittingDisplay > renders with pending items but no history > with_pending_no_history 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v0.10.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. -" +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results" `; exports[`AlternateBufferQuittingDisplay > renders with user and gemini messages > with_user_gemini_messages 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v0.10.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ - > Hello Gemini + > Hello Gemini ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ✦ Hello User! " diff --git a/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap index 324274fddd..b3866162ad 100644 --- a/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap @@ -2,82 +2,62 @@ exports[` > should not render the banner when no flags are set 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v1.0.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. -" +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results" `; exports[` > should not render the default banner if shown count is 5 or more 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v1.0.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + 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. -4. /help for more information. -" +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results" `; exports[` > should render the banner with default text 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v1.0.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + +╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ This is the default banner │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ This is the default banner │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ 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. -4. /help for more information. -" +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results" `; exports[` > should render the banner with warning text 1`] = ` " - ███ █████████ -░░░███ ███░░░░░███ - ░░░███ ███ ░░░ - ░░░███░███ - ███░ ░███ █████ - ███░ ░░███ ░░███ - ███░ ░░█████████ -░░░ ░░░░░░░░░ + ▝▜▄ Gemini CLI v1.0.0 + ▝▜▄ + ▗▟▀ Authenticated with undefined /auth + ▝▀ Gemini Code Assist for individuals /upgrade + +╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ There are capacity issues │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ There are capacity issues │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ 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. -4. /help for more information. -" +1. /help for more information +2. Ask coding questions, edit code or run commands +3. Be specific for the best results" `; diff --git a/packages/cli/src/ui/components/shared/HorizontalLine.tsx b/packages/cli/src/ui/components/shared/HorizontalLine.tsx index 92935617a7..f2463c404e 100644 --- a/packages/cli/src/ui/components/shared/HorizontalLine.tsx +++ b/packages/cli/src/ui/components/shared/HorizontalLine.tsx @@ -8,20 +8,57 @@ import type React from 'react'; import { Box } from 'ink'; import { theme } from '../../semantic-colors.js'; +export type LinePosition = 'top' | 'center' | 'bottom'; + interface HorizontalLineProps { color?: string; + width?: number | string; + position?: LinePosition; } +const overlineStyle = { + top: '‾', + bottom: '', + left: '', + right: '', + topLeft: '', + topRight: '', + bottomLeft: '', + bottomRight: '', +}; + +const underlineStyle = { + top: '_', + bottom: '', + left: '', + right: '', + topLeft: '', + topRight: '', + bottomLeft: '', + bottomRight: '', +}; + export const HorizontalLine: React.FC = ({ color = theme.border.default, -}) => ( - -); + width = '100%', + position = 'center', +}) => { + const borderStyle = + position === 'top' + ? overlineStyle + : position === 'bottom' + ? underlineStyle + : 'single'; + + return ( + + ); +};