mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 01:51:20 -07:00
refactor(ui): implement recursive color flattening and improve contrast in colors visualizer
This commit is contained in:
@@ -36,6 +36,22 @@ interface ColorsDisplayProps {
|
||||
activeTheme: Theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a contrasting text color (black or white) based on the background color's luminance.
|
||||
*/
|
||||
function getContrastingTextColor(hex: string): string {
|
||||
if (!hex || !hex.startsWith('#') || hex.length < 7) {
|
||||
// Fallback for invalid hex codes or named colors
|
||||
return theme.text.primary;
|
||||
}
|
||||
const r = parseInt(hex.slice(1, 3), 16);
|
||||
const g = parseInt(hex.slice(3, 5), 16);
|
||||
const b = parseInt(hex.slice(5, 7), 16);
|
||||
// Using YIQ formula to determine luminance
|
||||
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
return yiq >= 128 ? '#000000' : '#FFFFFF';
|
||||
}
|
||||
|
||||
export const ColorsDisplay: React.FC<ColorsDisplayProps> = ({
|
||||
activeTheme,
|
||||
}) => {
|
||||
@@ -53,54 +69,40 @@ export const ColorsDisplay: React.FC<ColorsDisplayProps> = ({
|
||||
};
|
||||
}
|
||||
|
||||
// Flatten and categorize the SemanticColors object
|
||||
for (const [category, subColors] of Object.entries(semanticColors)) {
|
||||
if (category === 'ui' && 'gradient' in subColors) {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Recursively flattens the semanticColors object.
|
||||
*/
|
||||
const flattenColors = (obj: object, path: string = '') => {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === undefined || value === null) continue;
|
||||
const newPath = path ? `${path}.${key}` : key;
|
||||
|
||||
for (const [name, value] of Object.entries(subColors)) {
|
||||
const fullName = `${category}.${name}`;
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
if (key === 'gradient' && Array.isArray(value)) {
|
||||
// Gradient handled separately
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||
for (const [diffName, diffValue] of Object.entries(value)) {
|
||||
if (typeof diffValue === 'string') {
|
||||
if (category === 'background') {
|
||||
backgroundRows.push({
|
||||
type: 'background',
|
||||
name: `${fullName}.${diffName}`,
|
||||
value: diffValue,
|
||||
});
|
||||
} else {
|
||||
standardRows.push({
|
||||
type: 'standard',
|
||||
name: `${fullName}.${diffName}`,
|
||||
value: diffValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
flattenColors(value, newPath);
|
||||
} else if (typeof value === 'string') {
|
||||
if (category === 'background') {
|
||||
if (newPath.startsWith('background.')) {
|
||||
backgroundRows.push({
|
||||
type: 'background',
|
||||
name: fullName,
|
||||
name: newPath,
|
||||
value,
|
||||
});
|
||||
} else {
|
||||
standardRows.push({
|
||||
type: 'standard',
|
||||
name: fullName,
|
||||
name: newPath,
|
||||
value,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
flattenColors(semanticColors);
|
||||
|
||||
// Final order: Backgrounds first, then Standards, then Gradient
|
||||
const allRows: ColorRow[] = [
|
||||
@@ -218,7 +220,7 @@ function renderBackgroundRow({ name, value }: BackgroundColorRow) {
|
||||
justifyContent="center"
|
||||
paddingX={1}
|
||||
>
|
||||
<Text color={theme.text.primary} bold wrap="truncate">
|
||||
<Text color={getContrastingTextColor(value)} bold wrap="truncate">
|
||||
{value || 'default'}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
@@ -44,6 +44,9 @@ exports[`Initial Theme Selection > should default to a dark theme when terminal
|
||||
│ │ (blank) text.response │ │
|
||||
│ │ #3d3f51 border.default │ │
|
||||
│ │ #89B4FA border.focused │ │
|
||||
│ │ #6C7086 ui.comment │ │
|
||||
│ │ #89DCEB ui.symbol │ │
|
||||
│ │ #3d3f51 ui.dark │ │
|
||||
│ │ #F38BA8 status.error │ │
|
||||
│ │ #A6E3A1 status.success │ │
|
||||
│ │ #F9E2AF status.warning │ │
|
||||
@@ -102,6 +105,9 @@ exports[`Initial Theme Selection > should default to a light theme when terminal
|
||||
│ │ (blank) text.response │ │
|
||||
│ │ #d2d6dc border.default │ │
|
||||
│ │ #3B82F6 border.focused │ │
|
||||
│ │ #97a0b0 ui.comment │ │
|
||||
│ │ #06B6D4 ui.symbol │ │
|
||||
│ │ #d2d6dc ui.dark │ │
|
||||
│ │ #DD4C4C status.error │ │
|
||||
│ │ #3CA84B status.success │ │
|
||||
│ │ #D5A40A status.warning │ │
|
||||
@@ -160,6 +166,9 @@ exports[`Initial Theme Selection > should use the theme from settings even if te
|
||||
│ │ (blank) text.response │ │
|
||||
│ │ #3d3f51 border.default │ │
|
||||
│ │ #89B4FA border.focused │ │
|
||||
│ │ #6C7086 ui.comment │ │
|
||||
│ │ #89DCEB ui.symbol │ │
|
||||
│ │ #3d3f51 ui.dark │ │
|
||||
│ │ #F38BA8 status.error │ │
|
||||
│ │ #A6E3A1 status.success │ │
|
||||
│ │ #F9E2AF status.warning │ │
|
||||
@@ -232,6 +241,9 @@ exports[`ThemeDialog Snapshots > should render correctly in theme selection mode
|
||||
│ │ (blank) text.response │ │
|
||||
│ │ #3d3f51 border.default │ │
|
||||
│ │ #89B4FA border.focused │ │
|
||||
│ │ #6C7086 ui.comment │ │
|
||||
│ │ #6C7086 ui.symbol │ │
|
||||
│ │ #3d3f51 ui.dark │ │
|
||||
│ │ #F38BA8 status.error │ │
|
||||
│ │ #A6E3A1 status.success │ │
|
||||
│ │ #F9E2AF status.warning │ │
|
||||
|
||||
Reference in New Issue
Block a user