From 686998c1c814ef050281493f3489914d5b992598 Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Mon, 16 Feb 2026 12:54:39 -0500 Subject: [PATCH] chore: refactor based on feedback --- packages/cli/src/config/footerItems.ts | 11 + packages/cli/src/config/settingsSchema.ts | 10 + .../components/ContextUsageDisplay.test.tsx | 22 +- .../src/ui/components/ContextUsageDisplay.tsx | 6 +- .../cli/src/ui/components/Footer.test.tsx | 1174 +++++++---------- packages/cli/src/ui/components/Footer.tsx | 472 ++++--- .../ui/components/FooterConfigDialog.test.tsx | 14 +- .../src/ui/components/FooterConfigDialog.tsx | 176 ++- .../src/ui/components/MemoryUsageDisplay.tsx | 14 +- .../__snapshots__/Footer.test.tsx.snap | 28 +- .../FooterConfigDialog.test.tsx.snap | 4 +- 11 files changed, 918 insertions(+), 1013 deletions(-) diff --git a/packages/cli/src/config/footerItems.ts b/packages/cli/src/config/footerItems.ts index 2a24bb1bc9..c9de4790e4 100644 --- a/packages/cli/src/config/footerItems.ts +++ b/packages/cli/src/config/footerItems.ts @@ -9,60 +9,70 @@ import type { MergedSettings } from './settings.js'; export const ALL_ITEMS = [ { id: 'cwd', + header: 'Path', label: 'cwd', description: 'Current directory path', defaultEnabled: true, }, { id: 'git-branch', + header: 'Branch', label: 'git-branch', description: 'Current git branch name', defaultEnabled: true, }, { id: 'sandbox-status', + header: '/docs', label: 'sandbox-status', description: 'Sandbox type and trust indicator', defaultEnabled: true, }, { id: 'model-name', + header: '/model', label: 'model-name', description: 'Current model identifier', defaultEnabled: true, }, { id: 'context-remaining', + header: 'Context', label: 'context-remaining', description: 'Percentage of context window remaining', defaultEnabled: false, }, { id: 'quota', + header: '/stats', label: 'quota', description: 'Remaining usage on daily limit', defaultEnabled: true, }, { id: 'memory-usage', + header: 'Memory', label: 'memory-usage', description: 'Node.js heap memory usage', defaultEnabled: false, }, { id: 'session-id', + header: 'Session', label: 'session-id', description: 'Unique identifier for the current session', defaultEnabled: false, }, { id: 'code-changes', + header: 'Diff', label: 'code-changes', description: 'Lines added/removed in the session', defaultEnabled: true, }, { id: 'token-count', + header: 'Tokens', label: 'token-count', description: 'Total tokens used in the session', defaultEnabled: false, @@ -73,6 +83,7 @@ export type FooterItemId = (typeof ALL_ITEMS)[number]['id']; export interface FooterItem { id: string; + header: string; label: string; description: string; defaultEnabled: boolean; diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index 6ef47208a8..ccca95b147 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -582,6 +582,16 @@ const SETTINGS_SCHEMA = { showInDialog: false, items: { type: 'string' }, }, + showLabels: { + type: 'boolean', + label: 'Show Footer Labels', + category: 'UI', + requiresRestart: false, + default: true, + description: + 'Display a second line above the footer items with descriptive headers (e.g., /model).', + showInDialog: false, + }, hideCWD: { type: 'boolean', label: 'Hide CWD', diff --git a/packages/cli/src/ui/components/ContextUsageDisplay.test.tsx b/packages/cli/src/ui/components/ContextUsageDisplay.test.tsx index ae272d6145..06c1ef1067 100644 --- a/packages/cli/src/ui/components/ContextUsageDisplay.test.tsx +++ b/packages/cli/src/ui/components/ContextUsageDisplay.test.tsx @@ -27,46 +27,40 @@ vi.mock('../../config/settings.js', () => ({ })); describe('ContextUsageDisplay', () => { - it('renders correct percentage left', async () => { - const { lastFrame, waitUntilReady, unmount } = render( + it('renders correct percentage left', () => { + const { lastFrame } = render( , ); - await waitUntilReady(); const output = lastFrame(); - expect(output).toContain('50% context left'); - unmount(); + expect(output).toContain('50% left'); }); - it('renders short label when terminal width is small', async () => { - const { lastFrame, waitUntilReady, unmount } = render( + it('renders short label when terminal width is small', () => { + const { lastFrame } = render( , ); - await waitUntilReady(); const output = lastFrame(); expect(output).toContain('80%'); expect(output).not.toContain('context left'); - unmount(); }); - it('renders 0% when full', async () => { - const { lastFrame, waitUntilReady, unmount } = render( + it('renders 0% when full', () => { + const { lastFrame } = render( , ); - await waitUntilReady(); const output = lastFrame(); - expect(output).toContain('0% context left'); - unmount(); + expect(output).toContain('0% left'); }); }); diff --git a/packages/cli/src/ui/components/ContextUsageDisplay.tsx b/packages/cli/src/ui/components/ContextUsageDisplay.tsx index 1c1d24cc2d..293edd9e3e 100644 --- a/packages/cli/src/ui/components/ContextUsageDisplay.tsx +++ b/packages/cli/src/ui/components/ContextUsageDisplay.tsx @@ -12,18 +12,20 @@ export const ContextUsageDisplay = ({ promptTokenCount, model, terminalWidth, + color = theme.text.primary, }: { promptTokenCount: number; model: string; terminalWidth: number; + color?: string; }) => { const percentage = getContextUsagePercentage(promptTokenCount, model); const percentageLeft = ((1 - percentage) * 100).toFixed(0); - const label = terminalWidth < 100 ? '%' : '% context left'; + const label = terminalWidth < 100 ? '%' : '% left'; return ( - + {percentageLeft} {label} diff --git a/packages/cli/src/ui/components/Footer.test.tsx b/packages/cli/src/ui/components/Footer.test.tsx index 1f71290b60..6d698055d2 100644 --- a/packages/cli/src/ui/components/Footer.test.tsx +++ b/packages/cli/src/ui/components/Footer.test.tsx @@ -4,47 +4,23 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { describe, it, expect } from 'vitest'; import { renderWithProviders } from '../../test-utils/render.js'; -import { createMockSettings } from '../../test-utils/settings.js'; import { Footer } from './Footer.js'; -import { - makeFakeConfig, - tildeifyPath, - ToolCallDecision, -} from '@google/gemini-cli-core'; -import type { SessionStatsState } from '../contexts/SessionContext.js'; -import type { UIState } from '../contexts/UIStateContext.js'; +import { createMockSettings } from '../../test-utils/settings.js'; -vi.mock('@google/gemini-cli-core', async (importOriginal) => { - const original = - await importOriginal(); - return { - ...original, - shortenPath: (p: string, len: number) => { - if (p.length > len) { - return '...' + p.slice(p.length - len + 3); - } - return p; - }, - }; -}); - -const defaultProps = { - model: 'gemini-pro', - targetDir: - '/Users/test/project/foo/bar/and/some/more/directories/to/make/it/long', - branchName: 'main', -}; - -const mockSessionStats: SessionStatsState = { - sessionId: 'test-session', +const customMockSessionStats = { + sessionId: 'test-session-id', sessionStartTime: new Date(), - lastPromptTokenCount: 0, promptCount: 0, + lastPromptTokenCount: 150000, metrics: { - models: {}, + files: { + totalLinesAdded: 12, + totalLinesRemoved: 4, + }, tools: { + count: 0, totalCalls: 0, totalSuccess: 0, totalFail: 0, @@ -53,776 +29,580 @@ const mockSessionStats: SessionStatsState = { accept: 0, reject: 0, modify: 0, - [ToolCallDecision.AUTO_ACCEPT]: 0, + auto_accept: 0, }, byName: {}, + latency: { avg: 0, max: 0, min: 0 }, }, - files: { - totalLinesAdded: 0, - totalLinesRemoved: 0, + models: { + 'gemini-pro': { + api: { + totalRequests: 0, + totalErrors: 0, + totalLatencyMs: 0, + }, + tokens: { + input: 0, + prompt: 0, + candidates: 0, + total: 1500, + cached: 0, + thoughts: 0, + tool: 0, + }, + }, }, }, }; +const defaultProps = { + model: 'gemini-pro', + targetDir: '/long/path/to/some/deeply/nested/directories/to/make/it/long', + debugMode: false, + branchName: 'main', + errorCount: 0, +}; + describe('