feat(ui): refine /colors layout and add colorized names and vertical gradients

This commit is contained in:
Keith Guerin
2026-02-26 13:15:15 -08:00
parent 8ced1bcf38
commit b67b103f45
2 changed files with 35 additions and 40 deletions
@@ -70,9 +70,6 @@ describe('ColorsDisplay', () => {
expect(output).toContain('/colors - Theme Colors Demo'); expect(output).toContain('/colors - Theme Colors Demo');
expect(output).toContain('visualize how colors are used'); expect(output).toContain('visualize how colors are used');
// Check for Background section
expect(output).toContain('Background Colors');
// Check for active theme name // Check for active theme name
expect(output).toContain('Test Theme'); expect(output).toContain('Test Theme');
@@ -87,7 +84,7 @@ describe('ColorsDisplay', () => {
// Check for some descriptions // Check for some descriptions
expect(output).toContain('Primary text color'); expect(output).toContain('Primary text color');
expect(output).toContain('Standard border color'); expect(output).toContain('Standard border color');
expect(output).toContain('Main terminal background color');
unmount(); unmount();
}); });
}); });
@@ -6,15 +6,17 @@
import type React from 'react'; import type React from 'react';
import { Box, Text } from 'ink'; import { Box, Text } from 'ink';
import Gradient from 'ink-gradient';
import { themeManager } from '../themes/theme-manager.js'; import { themeManager } from '../themes/theme-manager.js';
import { theme } from '../semantic-colors.js'; import { theme } from '../semantic-colors.js';
const COLOR_DESCRIPTIONS: Record<string, string> = { const COLOR_DESCRIPTIONS: Record<string, string> = {
'text.primary': 'Primary text color', 'text.primary': 'Primary text color (uses terminal default if blank)',
'text.secondary': 'Secondary/dimmed text color', 'text.secondary': 'Secondary/dimmed text color',
'text.link': 'Hyperlink and highlighting color', 'text.link': 'Hyperlink and highlighting color',
'text.accent': 'Accent color for emphasis', 'text.accent': 'Accent color for emphasis',
'text.response': 'Color for model response text', 'text.response':
'Color for model response text (uses terminal default if blank)',
'background.primary': 'Main terminal background color', 'background.primary': 'Main terminal background color',
'background.message': 'Subtle background for message blocks', 'background.message': 'Subtle background for message blocks',
'background.input': 'Background for the input prompt', 'background.input': 'Background for the input prompt',
@@ -49,12 +51,13 @@ interface BackgroundColorRow {
value: string; value: string;
} }
type ColorRow = StandardColorRow | GradientColorRow | BackgroundColorRow;
export const ColorsDisplay: React.FC = () => { export const ColorsDisplay: React.FC = () => {
const semanticColors = themeManager.getSemanticColors(); const semanticColors = themeManager.getSemanticColors();
const activeTheme = themeManager.getActiveTheme(); const activeTheme = themeManager.getActiveTheme();
const standardRows: StandardColorRow[] = []; const allRows: ColorRow[] = [];
const backgroundRows: BackgroundColorRow[] = [];
const gradientRow: GradientColorRow | null = const gradientRow: GradientColorRow | null =
semanticColors.ui.gradient && semanticColors.ui.gradient.length > 0 semanticColors.ui.gradient && semanticColors.ui.gradient.length > 0
? { ? {
@@ -67,7 +70,7 @@ export const ColorsDisplay: React.FC = () => {
// Flatten and categorize the SemanticColors object // Flatten and categorize the SemanticColors object
for (const [category, subColors] of Object.entries(semanticColors)) { for (const [category, subColors] of Object.entries(semanticColors)) {
if (category === 'ui' && 'gradient' in subColors) { if (category === 'ui' && 'gradient' in subColors) {
// Handled separately // Handled separately or later
continue; continue;
} }
@@ -82,13 +85,13 @@ export const ColorsDisplay: React.FC = () => {
for (const [diffName, diffValue] of Object.entries(value)) { for (const [diffName, diffValue] of Object.entries(value)) {
if (typeof diffValue === 'string') { if (typeof diffValue === 'string') {
if (category === 'background') { if (category === 'background') {
backgroundRows.push({ allRows.push({
type: 'background', type: 'background',
name: `${fullName}.${diffName}`, name: `${fullName}.${diffName}`,
value: diffValue, value: diffValue,
}); });
} else { } else {
standardRows.push({ allRows.push({
type: 'standard', type: 'standard',
name: `${fullName}.${diffName}`, name: `${fullName}.${diffName}`,
value: diffValue, value: diffValue,
@@ -98,13 +101,13 @@ export const ColorsDisplay: React.FC = () => {
} }
} else if (typeof value === 'string') { } else if (typeof value === 'string') {
if (category === 'background') { if (category === 'background') {
backgroundRows.push({ allRows.push({
type: 'background', type: 'background',
name: fullName, name: fullName,
value, value,
}); });
} else { } else {
standardRows.push({ allRows.push({
type: 'standard', type: 'standard',
name: fullName, name: fullName,
value, value,
@@ -114,6 +117,11 @@ export const ColorsDisplay: React.FC = () => {
} }
} }
// Add gradient row if it exists
if (gradientRow) {
allRows.push(gradientRow);
}
return ( return (
<Box flexDirection="column" paddingX={1} marginY={1}> <Box flexDirection="column" paddingX={1} marginY={1}>
<Box marginBottom={1} flexDirection="column"> <Box marginBottom={1} flexDirection="column">
@@ -162,26 +170,14 @@ export const ColorsDisplay: React.FC = () => {
</Box> </Box>
</Box> </Box>
{/* Standard Section */} {/* All Rows */}
<Box flexDirection="column" marginBottom={1}> <Box flexDirection="column">
{standardRows.map((row) => renderStandardRow(row))} {allRows.map((row) => {
</Box> if (row.type === 'standard') return renderStandardRow(row);
if (row.type === 'gradient') return renderGradientRow(row);
{/* Gradient Section */} if (row.type === 'background') return renderBackgroundRow(row);
{gradientRow && ( return null;
<Box flexDirection="column" marginBottom={1}> })}
{renderGradientRow(gradientRow)}
</Box>
)}
{/* Background Section */}
<Box flexDirection="column" marginTop={1}>
<Box marginBottom={1}>
<Text bold color={theme.text.accent}>
Background Colors
</Text>
</Box>
{backgroundRows.map((row) => renderBackgroundRow(row))}
</Box> </Box>
</Box> </Box>
); );
@@ -190,14 +186,15 @@ export const ColorsDisplay: React.FC = () => {
function renderStandardRow({ name, value }: StandardColorRow) { function renderStandardRow({ name, value }: StandardColorRow) {
const description = COLOR_DESCRIPTIONS[name] || ''; const description = COLOR_DESCRIPTIONS[name] || '';
const isHex = value.startsWith('#'); const isHex = value.startsWith('#');
const displayColor = isHex ? value : theme.text.primary;
return ( return (
<Box key={name} flexDirection="row" paddingX={1}> <Box key={name} flexDirection="row" paddingX={1}>
<Box width="15%"> <Box width="15%">
<Text color={isHex ? value : theme.text.primary}>{value}</Text> <Text color={displayColor}>{value || '(blank)'}</Text>
</Box> </Box>
<Box width="30%"> <Box width="30%">
<Text color={theme.text.primary}>{name}</Text> <Text color={displayColor}>{name}</Text>
</Box> </Box>
<Box flexGrow={1}> <Box flexGrow={1}>
<Text color={theme.text.secondary}>{description}</Text> <Text color={theme.text.secondary}>{description}</Text>
@@ -211,16 +208,17 @@ function renderGradientRow({ name, value }: GradientColorRow) {
return ( return (
<Box key={name} flexDirection="row" paddingX={1}> <Box key={name} flexDirection="row" paddingX={1}>
<Box width="15%" flexDirection="row"> <Box width="15%" flexDirection="column">
{value.map((c, i) => ( {value.map((c, i) => (
<Text key={i} color={c}> <Text key={i} color={c}>
{c} {c}
{i < value.length - 1 ? ', ' : ''}
</Text> </Text>
))} ))}
</Box> </Box>
<Box width="30%"> <Box width="30%">
<Text color={theme.text.primary}>{name}</Text> <Gradient colors={value}>
<Text>{name}</Text>
</Gradient>
</Box> </Box>
<Box flexGrow={1}> <Box flexGrow={1}>
<Text color={theme.text.secondary}>{description}</Text> <Text color={theme.text.secondary}>{description}</Text>
@@ -233,7 +231,7 @@ function renderBackgroundRow({ name, value }: BackgroundColorRow) {
const description = COLOR_DESCRIPTIONS[name] || ''; const description = COLOR_DESCRIPTIONS[name] || '';
return ( return (
<Box key={name} flexDirection="row" paddingX={1} marginBottom={1}> <Box key={name} flexDirection="row" paddingX={1} marginY={1}>
<Box <Box
width="15%" width="15%"
backgroundColor={value} backgroundColor={value}
@@ -245,7 +243,7 @@ function renderBackgroundRow({ name, value }: BackgroundColorRow) {
</Text> </Text>
</Box> </Box>
<Box width="30%" paddingLeft={1}> <Box width="30%" paddingLeft={1}>
<Text color={theme.text.primary}>{name}</Text> <Text color={value}>{name}</Text>
</Box> </Box>
<Box flexGrow={1}> <Box flexGrow={1}>
<Text color={theme.text.secondary}>{description}</Text> <Text color={theme.text.secondary}>{description}</Text>