fix: sync title bar overlay colors with app theme

The Windows title bar overlay (minimize/maximize/close buttons) was
hardcoded to dark colors and did not update when switching themes.

- Add titleBarTheme helper that maps AppearanceTheme to overlay and
  background colors, using nativeTheme for the system preference
- Call applyTitleBarTheme from the setTheme IPC handler so the overlay
  updates immediately on theme change
- Apply persisted theme on startup so the overlay matches from launch

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-24 19:31:49 +01:00
co-authored by Copilot
parent 973e5eb25c
commit 75934a161a
3 changed files with 49 additions and 3 deletions
+5
View File
@@ -3,6 +3,7 @@ import { app, BrowserWindow } from 'electron';
import { registerIpcHandlers } from '@main/ipc/registerIpcHandlers';
import { EryxAppService } from '@main/EryxAppService';
import { createMainWindow } from '@main/windows/createMainWindow';
import { applyTitleBarTheme } from '@main/windows/titleBarTheme';
let mainWindow: BrowserWindow | undefined;
let appService: EryxAppService | undefined;
@@ -13,6 +14,10 @@ async function bootstrap(): Promise<void> {
mainWindow = createMainWindow();
registerIpcHandlers(mainWindow, appService);
// Apply persisted theme to the title bar overlay
const workspace = await appService.loadWorkspace();
applyTitleBarTheme(mainWindow, workspace.settings.theme);
if (!app.isPackaged) {
mainWindow.webContents.openDevTools({ mode: 'detach' });
}
+6 -3
View File
@@ -19,6 +19,7 @@ import type { QuerySessionsInput } from '@shared/domain/sessionLibrary';
import type { AppearanceTheme } from '@shared/domain/tooling';
import { EryxAppService } from '@main/EryxAppService';
import { applyTitleBarTheme } from '@main/windows/titleBarTheme';
export function registerIpcHandlers(window: BrowserWindow, service: EryxAppService): void {
ipcMain.handle(ipcChannels.describeSidecarCapabilities, () => service.describeSidecarCapabilities());
@@ -34,9 +35,11 @@ export function registerIpcHandlers(window: BrowserWindow, service: EryxAppServi
ipcMain.handle(ipcChannels.setPatternFavorite, (_event, input: SetPatternFavoriteInput) =>
service.setPatternFavorite(input.patternId, input.isFavorite),
);
ipcMain.handle(ipcChannels.setTheme, (_event, theme: AppearanceTheme) =>
service.setTheme(theme),
);
ipcMain.handle(ipcChannels.setTheme, async (_event, theme: AppearanceTheme) => {
const result = await service.setTheme(theme);
applyTitleBarTheme(window, theme);
return result;
});
ipcMain.handle(ipcChannels.saveMcpServer, (_event, input: SaveMcpServerInput) =>
service.saveMcpServer(input.server),
);
+38
View File
@@ -0,0 +1,38 @@
import { nativeTheme, type BrowserWindow } from 'electron';
import type { AppearanceTheme } from '@shared/domain/tooling';
interface TitleBarColors {
backgroundColor: string;
overlay: { color: string; symbolColor: string };
}
const darkColors: TitleBarColors = {
backgroundColor: '#09090b',
overlay: { color: '#09090b', symbolColor: '#a1a1aa' },
};
const lightColors: TitleBarColors = {
backgroundColor: '#ffffff',
overlay: { color: '#ffffff', symbolColor: '#52525b' },
};
function resolveEffectiveTheme(theme: AppearanceTheme): 'dark' | 'light' {
if (theme === 'light') return 'light';
if (theme === 'dark') return 'dark';
return nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
}
function colorsForTheme(theme: AppearanceTheme): TitleBarColors {
return resolveEffectiveTheme(theme) === 'light' ? lightColors : darkColors;
}
export function applyTitleBarTheme(window: BrowserWindow, theme: AppearanceTheme): void {
const { backgroundColor, overlay } = colorsForTheme(theme);
window.setBackgroundColor(backgroundColor);
// setTitleBarOverlay is only available on Windows (and some Linux WMs).
if (typeof window.setTitleBarOverlay === 'function') {
window.setTitleBarOverlay(overlay);
}
}