diff --git a/packages/cli/src/config/footerItems.ts b/packages/cli/src/config/footerItems.ts index b9ba040c9f..a0752de4e2 100644 --- a/packages/cli/src/config/footerItems.ts +++ b/packages/cli/src/config/footerItems.ts @@ -61,12 +61,6 @@ export const ALL_ITEMS = [ export type FooterItemId = (typeof ALL_ITEMS)[number]['id']; -export interface FooterItem { - id: string; - header: string; - description: string; -} - export const DEFAULT_ORDER = [ 'cwd', 'git-branch', diff --git a/packages/cli/src/ui/components/Footer.tsx b/packages/cli/src/ui/components/Footer.tsx index ea2d63de1f..c51c2e279a 100644 --- a/packages/cli/src/ui/components/Footer.tsx +++ b/packages/cli/src/ui/components/Footer.tsx @@ -101,6 +101,52 @@ const CorgiIndicator: React.FC = () => ( ); +export interface FooterRowItem { + key: string; + header: string; + element: React.ReactNode; +} + +const COLUMN_GAP = 3; + +export const FooterRow: React.FC<{ + items: FooterRowItem[]; + showLabels: boolean; +}> = ({ items, showLabels }) => { + const elements: React.ReactNode[] = []; + + items.forEach((item, idx) => { + if (idx > 0 && !showLabels) { + elements.push( + + · + , + ); + } + + elements.push( + + {showLabels && ( + + {item.header} + + )} + {item.element} + , + ); + }); + + return ( + + {elements} + + ); +}; + function isFooterItemId(id: string): id is FooterItemId { return ALL_ITEMS.some((i) => i.id === id); } @@ -251,21 +297,19 @@ export const Footer: React.FC = () => { break; } case 'context-remaining': { - if (!settings.merged.ui.footer.hideContextPercentage) { - addCol( - id, - header, - () => ( - - ), - 10, // "100% left" is 9 chars - ); - } + addCol( + id, + header, + () => ( + + ), + 10, // "100% left" is 9 chars + ); break; } case 'quota': { @@ -366,7 +410,6 @@ export const Footer: React.FC = () => { } // --- Width Fitting Logic --- - const COLUMN_GAP = 3; let currentWidth = 2; // Initial padding const columnsToRender: FooterColumn[] = []; let droppedAny = false; @@ -396,58 +439,26 @@ export const Footer: React.FC = () => { ); const excessSpace = Math.max(0, terminalWidth - totalBudgeted); - const finalElements: React.ReactNode[] = []; - - columnsToRender.forEach((col, idx) => { - if (idx > 0 && !showLabels) { - finalElements.push( - - · - , - ); - } - + const rowItems: FooterRowItem[] = columnsToRender.map((col) => { const maxWidth = col.id === 'cwd' ? 20 + excessSpace : col.width; - finalElements.push( - - {showLabels && ( - - {col.header} - - )} - {col.element(maxWidth)} - , - ); + return { + key: col.id, + header: col.header, + element: col.element(maxWidth), + }; }); if (droppedAny) { - if (!showLabels) { - finalElements.push( - - · - , - ); - } - finalElements.push( - - {showLabels && } - - - - , - ); + rowItems.push({ + key: 'ellipsis', + header: '', + element: , + }); } return ( - - {finalElements} + + ); }; diff --git a/packages/cli/src/ui/components/FooterConfigDialog.tsx b/packages/cli/src/ui/components/FooterConfigDialog.tsx index f887c8db0b..d2e779487a 100644 --- a/packages/cli/src/ui/components/FooterConfigDialog.tsx +++ b/packages/cli/src/ui/components/FooterConfigDialog.tsx @@ -14,6 +14,7 @@ import { keyMatchers, Command } from '../keyMatchers.js'; import { TextInput } from './shared/TextInput.js'; import { useFuzzyList } from '../hooks/useFuzzyList.js'; import { MemoryUsageDisplay } from './MemoryUsageDisplay.js'; +import { FooterRow, type FooterRowItem } from './Footer.js'; import { ALL_ITEMS, DEFAULT_ORDER, @@ -316,98 +317,47 @@ export const FooterConfigDialog: React.FC = ({ const getColor = (id: string, defaultColor?: string) => id === activeId ? 'white' : defaultColor || itemColor; - // Mock values for preview - const mockValues: Record< - string, - { header: string; data: React.ReactNode } - > = { - cwd: { - header: 'Path', - data: ~/project/path, - }, - 'git-branch': { - header: 'Branch', - data: main, - }, - 'sandbox-status': { - header: '/docs', - data: docker, - }, - 'model-name': { - header: '/model', - data: ( - gemini-2.5-pro - ), - }, - 'context-remaining': { - header: 'Context', - data: ( - 85% left - ), - }, - quota: { - header: '/stats', - data: daily 97%, - }, - 'memory-usage': { - header: 'Memory', - data: , - }, - 'session-id': { - header: 'Session', - data: 769992f9, - }, - 'code-changes': { - header: 'Diff', - data: ( - - - +12 - - - -4 - - ), - }, - 'token-count': { - header: 'Tokens', - data: ( - 1.5k tokens - ), - }, + // Mock data for preview (headers come from ALL_ITEMS) + const mockData: Record = { + cwd: ~/project/path, + 'git-branch': main, + 'sandbox-status': ( + docker + ), + 'model-name': ( + gemini-2.5-pro + ), + 'context-remaining': ( + 85% left + ), + quota: daily 97%, + 'memory-usage': , + 'session-id': ( + 769992f9 + ), + 'code-changes': ( + + + +12 + + + -4 + + ), + 'token-count': ( + 1.5k tokens + ), }; - const previewElements: React.ReactNode[] = []; + const rowItems: FooterRowItem[] = itemsToPreview + .filter((id: string) => mockData[id]) + .map((id: string) => ({ + key: id, + header: ALL_ITEMS.find((i) => i.id === id)?.header ?? id, + element: mockData[id], + })); - itemsToPreview.forEach((id: string, idx: number) => { - const mock = mockValues[id]; - if (!mock) return; - - if (idx > 0 && !showLabels) { - previewElements.push( - - · - , - ); - } - - previewElements.push( - - {showLabels && ( - - {mock.header} - - )} - {mock.data} - , - ); - }); - - return ( - - {previewElements} - - ); + return ; }, [orderedIds, selectedIds, activeId, isResetFocused, showLabels]); return (