mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-08 20:30:53 -07:00
ui(polish) blend background color with theme (#18802)
This commit is contained in:
@@ -16,7 +16,11 @@ import type { UIState } from '../contexts/UIStateContext.js';
|
||||
vi.mock('../themes/theme-manager.js', () => ({
|
||||
themeManager: {
|
||||
getActiveTheme: vi.fn(),
|
||||
setTerminalBackground: vi.fn(),
|
||||
getAllThemes: vi.fn(() => []),
|
||||
setActiveTheme: vi.fn(),
|
||||
},
|
||||
DEFAULT_THEME: { name: 'Default' },
|
||||
}));
|
||||
|
||||
vi.mock('../themes/holiday.js', () => ({
|
||||
|
||||
@@ -15,6 +15,7 @@ const mockWrite = vi.fn();
|
||||
const mockSubscribe = vi.fn();
|
||||
const mockUnsubscribe = vi.fn();
|
||||
const mockHandleThemeSelect = vi.fn();
|
||||
const mockQueryTerminalBackground = vi.fn();
|
||||
|
||||
vi.mock('ink', async () => ({
|
||||
useStdout: () => ({
|
||||
@@ -28,6 +29,7 @@ vi.mock('../contexts/TerminalContext.js', () => ({
|
||||
useTerminalContext: () => ({
|
||||
subscribe: mockSubscribe,
|
||||
unsubscribe: mockUnsubscribe,
|
||||
queryTerminalBackground: mockQueryTerminalBackground,
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -52,6 +54,7 @@ vi.mock('../themes/theme-manager.js', async () => {
|
||||
themeManager: {
|
||||
isDefaultTheme: (name: string) =>
|
||||
name === 'default' || name === 'default-light',
|
||||
setTerminalBackground: vi.fn(),
|
||||
},
|
||||
DEFAULT_THEME: { name: 'default' },
|
||||
};
|
||||
@@ -78,6 +81,7 @@ describe('useTerminalTheme', () => {
|
||||
mockSubscribe.mockClear();
|
||||
mockUnsubscribe.mockClear();
|
||||
mockHandleThemeSelect.mockClear();
|
||||
mockQueryTerminalBackground.mockClear();
|
||||
// Reset any settings modifications
|
||||
mockSettings.merged.ui.autoThemeSwitching = true;
|
||||
mockSettings.merged.ui.theme = 'default';
|
||||
@@ -89,37 +93,37 @@ describe('useTerminalTheme', () => {
|
||||
});
|
||||
|
||||
it('should subscribe to terminal background events on mount', () => {
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config));
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config, vi.fn()));
|
||||
expect(mockSubscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should unsubscribe on unmount', () => {
|
||||
const { unmount } = renderHook(() =>
|
||||
useTerminalTheme(mockHandleThemeSelect, config),
|
||||
useTerminalTheme(mockHandleThemeSelect, config, vi.fn()),
|
||||
);
|
||||
unmount();
|
||||
expect(mockUnsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should poll for terminal background', () => {
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config));
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config, vi.fn()));
|
||||
|
||||
// Fast-forward time (1 minute)
|
||||
vi.advanceTimersByTime(60000);
|
||||
expect(mockWrite).toHaveBeenCalledWith('\x1b]11;?\x1b\\');
|
||||
expect(mockQueryTerminalBackground).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not poll if terminal background is undefined at startup', () => {
|
||||
config.getTerminalBackground = vi.fn().mockReturnValue(undefined);
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config));
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config, vi.fn()));
|
||||
|
||||
// Poll should not happen
|
||||
vi.advanceTimersByTime(60000);
|
||||
expect(mockWrite).not.toHaveBeenCalled();
|
||||
expect(mockQueryTerminalBackground).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should switch to light theme when background is light', () => {
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config));
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config, vi.fn()));
|
||||
|
||||
const handler = mockSubscribe.mock.calls[0][0];
|
||||
|
||||
@@ -137,7 +141,7 @@ describe('useTerminalTheme', () => {
|
||||
// Start with light theme
|
||||
mockSettings.merged.ui.theme = 'default-light';
|
||||
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config));
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config, vi.fn()));
|
||||
|
||||
const handler = mockSubscribe.mock.calls[0][0];
|
||||
|
||||
@@ -156,11 +160,11 @@ describe('useTerminalTheme', () => {
|
||||
|
||||
it('should not switch theme if autoThemeSwitching is disabled', () => {
|
||||
mockSettings.merged.ui.autoThemeSwitching = false;
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config));
|
||||
renderHook(() => useTerminalTheme(mockHandleThemeSelect, config, vi.fn()));
|
||||
|
||||
// Poll should not happen
|
||||
vi.advanceTimersByTime(60000);
|
||||
expect(mockWrite).not.toHaveBeenCalled();
|
||||
expect(mockQueryTerminalBackground).not.toHaveBeenCalled();
|
||||
|
||||
mockSettings.merged.ui.autoThemeSwitching = true;
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useStdout } from 'ink';
|
||||
import {
|
||||
getLuminance,
|
||||
parseColor,
|
||||
@@ -22,10 +21,11 @@ import type { UIActions } from '../contexts/UIActionsContext.js';
|
||||
export function useTerminalTheme(
|
||||
handleThemeSelect: UIActions['handleThemeSelect'],
|
||||
config: Config,
|
||||
refreshStatic: () => void,
|
||||
) {
|
||||
const { stdout } = useStdout();
|
||||
const settings = useSettings();
|
||||
const { subscribe, unsubscribe } = useTerminalContext();
|
||||
const { subscribe, unsubscribe, queryTerminalBackground } =
|
||||
useTerminalContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (settings.merged.ui.autoThemeSwitching === false) {
|
||||
@@ -44,7 +44,7 @@ export function useTerminalTheme(
|
||||
return;
|
||||
}
|
||||
|
||||
stdout.write('\x1b]11;?\x1b\\');
|
||||
void queryTerminalBackground();
|
||||
}, settings.merged.ui.terminalBackgroundPollingInterval * 1000);
|
||||
|
||||
const handleTerminalBackground = (colorStr: string) => {
|
||||
@@ -58,6 +58,8 @@ export function useTerminalTheme(
|
||||
const hexColor = parseColor(match[1], match[2], match[3]);
|
||||
const luminance = getLuminance(hexColor);
|
||||
config.setTerminalBackground(hexColor);
|
||||
themeManager.setTerminalBackground(hexColor);
|
||||
refreshStatic();
|
||||
|
||||
const currentThemeName = settings.merged.ui.theme;
|
||||
|
||||
@@ -69,7 +71,7 @@ export function useTerminalTheme(
|
||||
);
|
||||
|
||||
if (newTheme) {
|
||||
handleThemeSelect(newTheme, SettingScope.User);
|
||||
void handleThemeSelect(newTheme, SettingScope.User);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -83,10 +85,11 @@ export function useTerminalTheme(
|
||||
settings.merged.ui.theme,
|
||||
settings.merged.ui.autoThemeSwitching,
|
||||
settings.merged.ui.terminalBackgroundPollingInterval,
|
||||
stdout,
|
||||
config,
|
||||
handleThemeSelect,
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
queryTerminalBackground,
|
||||
refreshStatic,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -13,12 +13,16 @@ import type {
|
||||
import { MessageType } from '../types.js';
|
||||
import process from 'node:process';
|
||||
import type { UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||
import { useTerminalContext } from '../contexts/TerminalContext.js';
|
||||
|
||||
interface UseThemeCommandReturn {
|
||||
isThemeDialogOpen: boolean;
|
||||
openThemeDialog: () => void;
|
||||
closeThemeDialog: () => void;
|
||||
handleThemeSelect: (themeName: string, scope: LoadableSettingScope) => void;
|
||||
handleThemeSelect: (
|
||||
themeName: string,
|
||||
scope: LoadableSettingScope,
|
||||
) => Promise<void>;
|
||||
handleThemeHighlight: (themeName: string | undefined) => void;
|
||||
}
|
||||
|
||||
@@ -30,8 +34,9 @@ export const useThemeCommand = (
|
||||
): UseThemeCommandReturn => {
|
||||
const [isThemeDialogOpen, setIsThemeDialogOpen] =
|
||||
useState(!!initialThemeError);
|
||||
const { queryTerminalBackground } = useTerminalContext();
|
||||
|
||||
const openThemeDialog = useCallback(() => {
|
||||
const openThemeDialog = useCallback(async () => {
|
||||
if (process.env['NO_COLOR']) {
|
||||
addItem(
|
||||
{
|
||||
@@ -42,8 +47,14 @@ export const useThemeCommand = (
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we have an up to date terminal background color when opening the
|
||||
// theme dialog as the user may have just changed it before opening the
|
||||
// dialog.
|
||||
await queryTerminalBackground();
|
||||
|
||||
setIsThemeDialogOpen(true);
|
||||
}, [addItem]);
|
||||
}, [addItem, queryTerminalBackground]);
|
||||
|
||||
const applyTheme = useCallback(
|
||||
(themeName: string | undefined) => {
|
||||
@@ -72,7 +83,7 @@ export const useThemeCommand = (
|
||||
}, [applyTheme, loadedSettings]);
|
||||
|
||||
const handleThemeSelect = useCallback(
|
||||
(themeName: string, scope: LoadableSettingScope) => {
|
||||
async (themeName: string, scope: LoadableSettingScope) => {
|
||||
try {
|
||||
const mergedCustomThemes = {
|
||||
...(loadedSettings.user.settings.ui?.customThemes || {}),
|
||||
|
||||
Reference in New Issue
Block a user