chore: reduce duplication

This commit is contained in:
Jack Wotherspoon
2026-02-16 15:27:48 -05:00
committed by Keith Guerin
parent c51925b58a
commit 4412314dbe
3 changed files with 111 additions and 156 deletions

View File

@@ -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',

View File

@@ -101,6 +101,52 @@ const CorgiIndicator: React.FC = () => (
</Text>
);
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(
<Box key={`sep-${item.key}`} height={1}>
<Text color={theme.ui.comment}> · </Text>
</Box>,
);
}
elements.push(
<Box key={item.key} flexDirection="column">
{showLabels && (
<Box height={1}>
<Text color={theme.ui.comment}>{item.header}</Text>
</Box>
)}
<Box height={1}>{item.element}</Box>
</Box>,
);
});
return (
<Box
flexDirection="row"
flexWrap="nowrap"
columnGap={showLabels ? COLUMN_GAP : 0}
>
{elements}
</Box>
);
};
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,
() => (
<ContextUsageDisplay
promptTokenCount={promptTokenCount}
model={model}
terminalWidth={terminalWidth}
color={itemColor}
/>
),
10, // "100% left" is 9 chars
);
}
addCol(
id,
header,
() => (
<ContextUsageDisplay
promptTokenCount={promptTokenCount}
model={model}
terminalWidth={terminalWidth}
color={itemColor}
/>
),
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(
<Box key={`sep-${col.id}`} height={1}>
<Text color={theme.ui.comment}> · </Text>
</Box>,
);
}
const rowItems: FooterRowItem[] = columnsToRender.map((col) => {
const maxWidth = col.id === 'cwd' ? 20 + excessSpace : col.width;
finalElements.push(
<Box key={col.id} flexDirection="column">
{showLabels && (
<Box height={1}>
<Text color={theme.ui.comment}>{col.header}</Text>
</Box>
)}
<Box height={1}>{col.element(maxWidth)}</Box>
</Box>,
);
return {
key: col.id,
header: col.header,
element: col.element(maxWidth),
};
});
if (droppedAny) {
if (!showLabels) {
finalElements.push(
<Box key="sep-ellipsis" height={1}>
<Text color={theme.ui.comment}> · </Text>
</Box>,
);
}
finalElements.push(
<Box key="ellipsis" flexDirection="column">
{showLabels && <Box height={1} />}
<Box height={1}>
<Text color={theme.ui.comment}>…</Text>
</Box>
</Box>,
);
rowItems.push({
key: 'ellipsis',
header: '',
element: <Text color={theme.ui.comment}></Text>,
});
}
return (
<Box
width={terminalWidth}
flexDirection="row"
paddingX={1}
overflow="hidden"
flexWrap="nowrap"
columnGap={showLabels ? COLUMN_GAP : 0}
>
{finalElements}
<Box width={terminalWidth} paddingX={1} overflow="hidden" flexWrap="nowrap">
<FooterRow items={rowItems} showLabels={showLabels} />
</Box>
);
};

View File

@@ -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<FooterConfigDialogProps> = ({
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: <Text color={getColor('cwd', itemColor)}>~/project/path</Text>,
},
'git-branch': {
header: 'Branch',
data: <Text color={getColor('git-branch', itemColor)}>main</Text>,
},
'sandbox-status': {
header: '/docs',
data: <Text color={getColor('sandbox-status', 'green')}>docker</Text>,
},
'model-name': {
header: '/model',
data: (
<Text color={getColor('model-name', itemColor)}>gemini-2.5-pro</Text>
),
},
'context-remaining': {
header: 'Context',
data: (
<Text color={getColor('context-remaining', itemColor)}>85% left</Text>
),
},
quota: {
header: '/stats',
data: <Text color={getColor('quota', itemColor)}>daily 97%</Text>,
},
'memory-usage': {
header: 'Memory',
data: <MemoryUsageDisplay color={itemColor} />,
},
'session-id': {
header: 'Session',
data: <Text color={getColor('session-id', itemColor)}>769992f9</Text>,
},
'code-changes': {
header: 'Diff',
data: (
<Box flexDirection="row">
<Text color={getColor('code-changes', theme.status.success)}>
+12
</Text>
<Text color={getColor('code-changes')}> </Text>
<Text color={getColor('code-changes', theme.status.error)}>-4</Text>
</Box>
),
},
'token-count': {
header: 'Tokens',
data: (
<Text color={getColor('token-count', itemColor)}>1.5k tokens</Text>
),
},
// Mock data for preview (headers come from ALL_ITEMS)
const mockData: Record<string, React.ReactNode> = {
cwd: <Text color={getColor('cwd', itemColor)}>~/project/path</Text>,
'git-branch': <Text color={getColor('git-branch', itemColor)}>main</Text>,
'sandbox-status': (
<Text color={getColor('sandbox-status', 'green')}>docker</Text>
),
'model-name': (
<Text color={getColor('model-name', itemColor)}>gemini-2.5-pro</Text>
),
'context-remaining': (
<Text color={getColor('context-remaining', itemColor)}>85% left</Text>
),
quota: <Text color={getColor('quota', itemColor)}>daily 97%</Text>,
'memory-usage': <MemoryUsageDisplay color={itemColor} />,
'session-id': (
<Text color={getColor('session-id', itemColor)}>769992f9</Text>
),
'code-changes': (
<Box flexDirection="row">
<Text color={getColor('code-changes', theme.status.success)}>
+12
</Text>
<Text color={getColor('code-changes')}> </Text>
<Text color={getColor('code-changes', theme.status.error)}>-4</Text>
</Box>
),
'token-count': (
<Text color={getColor('token-count', itemColor)}>1.5k tokens</Text>
),
};
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(
<Box key={`sep-${id}`} height={1}>
<Text color={theme.ui.comment}> · </Text>
</Box>,
);
}
previewElements.push(
<Box key={id} flexDirection="column">
{showLabels && (
<Box height={1}>
<Text color={theme.ui.comment}>{mock.header}</Text>
</Box>
)}
<Box height={1}>{mock.data}</Box>
</Box>,
);
});
return (
<Box flexDirection="row" flexWrap="nowrap" columnGap={showLabels ? 3 : 0}>
{previewElements}
</Box>
);
return <FooterRow items={rowItems} showLabels={showLabels} />;
}, [orderedIds, selectedIds, activeId, isResetFocused, showLabels]);
return (