ui(polish) blend background color with theme (#18802)

This commit is contained in:
Jacob Richman
2026-02-12 11:56:07 -08:00
committed by GitHub
parent db00c5abf3
commit 207ac6f2dc
20 changed files with 432 additions and 240 deletions

View File

@@ -1549,7 +1549,6 @@ describe('InputPrompt', () => {
{ color: 'black', name: 'black' },
{ color: '#000000', name: '#000000' },
{ color: '#000', name: '#000' },
{ color: undefined, name: 'default (black)' },
{ color: 'white', name: 'white' },
{ color: '#ffffff', name: '#ffffff' },
{ color: '#fff', name: '#fff' },
@@ -1619,6 +1618,11 @@ describe('InputPrompt', () => {
const { stdout, unmount } = renderWithProviders(
<InputPrompt {...props} />,
{
uiState: {
terminalBackgroundColor: 'black',
} as Partial<UIState>,
},
);
await waitFor(() => {

View File

@@ -222,7 +222,6 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
terminalWidth,
activePtyId,
history,
terminalBackgroundColor,
backgroundShells,
backgroundShellHeight,
shortcutsHelpVisible,
@@ -1352,7 +1351,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
const useBackgroundColor = config.getUseBackgroundColor();
const isLowColor = isLowColorDepth();
const terminalBg = terminalBackgroundColor || 'black';
const terminalBg = theme.background.primary || 'black';
// We should fallback to lines if the background color is disabled OR if it is
// enabled but we are in a low color depth terminal where we don't have a safe

View File

@@ -9,7 +9,7 @@ import { useCallback, useState } from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { themeManager, DEFAULT_THEME } from '../themes/theme-manager.js';
import { pickDefaultThemeName } from '../themes/theme.js';
import { pickDefaultThemeName, type Theme } from '../themes/theme.js';
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
import { DiffRenderer } from './messages/DiffRenderer.js';
import { colorizeCode } from '../utils/CodeColorizer.js';
@@ -27,7 +27,10 @@ import { useUIState } from '../contexts/UIStateContext.js';
interface ThemeDialogProps {
/** Callback function when a theme is selected */
onSelect: (themeName: string, scope: LoadableSettingScope) => void;
onSelect: (
themeName: string,
scope: LoadableSettingScope,
) => void | Promise<void>;
/** Callback function when the dialog is cancelled */
onCancel: () => void;
@@ -40,24 +43,21 @@ interface ThemeDialogProps {
terminalWidth: number;
}
import {
getThemeTypeFromBackgroundColor,
resolveColor,
} from '../themes/color-utils.js';
import { resolveColor } from '../themes/color-utils.js';
function generateThemeItem(
name: string,
typeDisplay: string,
themeType: string,
themeBackground: string | undefined,
fullTheme: Theme | undefined,
terminalBackgroundColor: string | undefined,
terminalThemeType: 'light' | 'dark' | undefined,
) {
const isCompatible =
themeType === 'custom' ||
terminalThemeType === undefined ||
themeType === 'ansi' ||
themeType === terminalThemeType;
const isCompatible = fullTheme
? themeManager.isThemeCompatible(fullTheme, terminalBackgroundColor)
: true;
const themeBackground = fullTheme
? resolveColor(fullTheme.colors.Background)
: undefined;
const isBackgroundMatch =
terminalBackgroundColor &&
@@ -111,26 +111,17 @@ export function ThemeDialog({
const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
const terminalThemeType = getThemeTypeFromBackgroundColor(
terminalBackgroundColor,
);
// Generate theme items
const themeItems = themeManager
.getAvailableThemes()
.map((theme) => {
const fullTheme = themeManager.getTheme(theme.name);
const themeBackground = fullTheme
? resolveColor(fullTheme.colors.Background)
: undefined;
return generateThemeItem(
theme.name,
capitalize(theme.type),
theme.type,
themeBackground,
fullTheme,
terminalBackgroundColor,
terminalThemeType,
);
})
.sort((a, b) => {
@@ -149,8 +140,8 @@ export function ThemeDialog({
const safeInitialThemeIndex = initialThemeIndex >= 0 ? initialThemeIndex : 0;
const handleThemeSelect = useCallback(
(themeName: string) => {
onSelect(themeName, selectedScope);
async (themeName: string) => {
await onSelect(themeName, selectedScope);
refreshStatic();
},
[onSelect, selectedScope, refreshStatic],
@@ -166,8 +157,8 @@ export function ThemeDialog({
}, []);
const handleScopeSelect = useCallback(
(scope: LoadableSettingScope) => {
onSelect(highlightedThemeName, scope);
async (scope: LoadableSettingScope) => {
await onSelect(highlightedThemeName, scope);
refreshStatic();
},
[onSelect, highlightedThemeName, refreshStatic],

View File

@@ -90,18 +90,18 @@ exports[`ThemeDialog Snapshots > should render correctly in theme selection mode
│ │
│ > Select Theme Preview │
│ ▲ ┌────────────────────────────────────────────────────────────┐ │
1. ANSI Dark │ │ │
│ 2. ANSI Light Light │ 1 # function │ │
│ 3. Atom One Dark │ 2 def fibonacci(n): │ │
│ 4. Ayu Dark │ 3 a, b = 0, 1 │ │
│ 5. Ayu Light Light │ 4 for _ in range(n): │ │
6. Default Dark │ 5 a, b = b, a + b │ │
│ 7. Default Light Light │ 6 return a │ │
│ 8. Dracula Dark │ │ │
│ 9. GitHub Dark │ 1 - print("Hello, " + name) │ │
│ 10. GitHub Light Light │ 1 + print(f"Hello, {name}!") │ │
│ 11. Google Code Light │ │ │
│ 12. Holiday Dark └────────────────────────────────────────────────────────────┘ │
1. ANSI Dark (Matches terminal) │ │ │
│ 2. Atom One Dark │ 1 # function │ │
│ 3. Ayu Dark │ 2 def fibonacci(n): │ │
│ 4. Default Dark │ 3 a, b = 0, 1 │ │
│ 5. Dracula Dark │ 4 for _ in range(n): │ │
6. GitHub Dark │ 5 a, b = b, a + b │ │
│ 7. Holiday Dark │ 6 return a │ │
│ 8. Shades Of Purple Dark │ │ │
│ 9. ANSI Light Light (Incompatible) │ 1 - print("Hello, " + name) │ │
│ 10. Ayu Light Light (Incompatible) │ 1 + print(f"Hello, {name}!") │ │
│ 11. Default Light Light (Incompatible) │ │ │
│ 12. GitHub Light Light (Incompatible) └────────────────────────────────────────────────────────────┘ │
│ ▼ │
│ │
│ (Use Enter to select, Tab to configure scope, Esc to close) │

View File

@@ -8,6 +8,7 @@ import type React from 'react';
import { useMemo } from 'react';
import { Box, Text, useIsScreenReaderEnabled } from 'ink';
import { useUIState } from '../../contexts/UIStateContext.js';
import { theme } from '../../semantic-colors.js';
import {
interpolateColor,
resolveColor,
@@ -52,8 +53,8 @@ const HalfLinePaddedBoxInternal: React.FC<HalfLinePaddedBoxProps> = ({
backgroundOpacity,
children,
}) => {
const { terminalWidth, terminalBackgroundColor } = useUIState();
const terminalBg = terminalBackgroundColor || 'black';
const { terminalWidth } = useUIState();
const terminalBg = theme.background.primary || 'black';
const isLowColor = isLowColorDepth();