From 921ddf47a8d85542b36513e8a7aa1c63e68afaba Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sun, 29 Mar 2026 09:26:18 +0700 Subject: [PATCH] refactor(desktop): move title bar overlay logic to window-manager and add setTitleBarTheme IPC --- packages/desktop/src/main.ts | 24 ++---------- packages/desktop/src/preload.ts | 2 + .../desktop/src/window/window-manager.test.ts | 33 +++++++++++++++- packages/desktop/src/window/window-manager.ts | 38 ++++++++++++++++++- 4 files changed, 75 insertions(+), 22 deletions(-) diff --git a/packages/desktop/src/main.ts b/packages/desktop/src/main.ts index f652a4eb7..59012d63b 100644 --- a/packages/desktop/src/main.ts +++ b/packages/desktop/src/main.ts @@ -4,7 +4,7 @@ log.initialize({ spyRendererConsole: true }); import path from "node:path"; import { pathToFileURL } from "node:url"; import { existsSync } from "node:fs"; -import { app, BrowserWindow, nativeImage, nativeTheme, net, protocol } from "electron"; +import { app, BrowserWindow, nativeImage, net, protocol } from "electron"; import { registerDaemonManager } from "./daemon/daemon-manager.js"; import { parseCliPassthroughArgsFromArgv, @@ -13,6 +13,8 @@ import { import { closeAllTransportSessions } from "./daemon/local-transport.js"; import { registerWindowManager, + getTitleBarOverlayOptions, + resolveSystemWindowTheme, setupWindowResizeEvents, setupDragDropPrevention, } from "./window/window-manager.js"; @@ -32,17 +34,6 @@ protocol.registerSchemesAsPrivileged([ { scheme: APP_SCHEME, privileges: { standard: true, secure: true, supportFetchAPI: true } }, ]); -// --------------------------------------------------------------------------- -// Window chrome -// --------------------------------------------------------------------------- - -function getTitleBarOverlayColors(): Electron.TitleBarOverlayOptions { - if (nativeTheme.shouldUseDarkColors) { - return { color: "#18181c", symbolColor: "#e4e4e7", height: 48 }; - } - return { color: "#ffffff", symbolColor: "#09090b", height: 48 }; -} - // --------------------------------------------------------------------------- // Window creation // --------------------------------------------------------------------------- @@ -107,7 +98,7 @@ async function createMainWindow(): Promise { ...(isMac ? { trafficLightPosition: { x: 16, y: 14 } } : { - titleBarOverlay: getTitleBarOverlayColors(), + titleBarOverlay: getTitleBarOverlayOptions(resolveSystemWindowTheme()), autoHideMenuBar: true, }), webPreferences: { @@ -119,13 +110,6 @@ async function createMainWindow(): Promise { setupWindowResizeEvents(mainWindow); setupDragDropPrevention(mainWindow); - - if (!isMac) { - nativeTheme.on("updated", () => { - mainWindow.setTitleBarOverlay(getTitleBarOverlayColors()); - }); - } - mainWindow.once("ready-to-show", () => { mainWindow.show(); }); diff --git a/packages/desktop/src/preload.ts b/packages/desktop/src/preload.ts index 54b429362..a1a6650d8 100644 --- a/packages/desktop/src/preload.ts +++ b/packages/desktop/src/preload.ts @@ -26,6 +26,8 @@ contextBridge.exposeInMainWorld("paseoDesktop", { endMove: () => ipcRenderer.send("paseo:window:endMove"), toggleMaximize: () => ipcRenderer.invoke("paseo:window:toggleMaximize"), isFullscreen: () => ipcRenderer.invoke("paseo:window:isFullscreen"), + setTitleBarTheme: (theme: "light" | "dark") => + ipcRenderer.invoke("paseo:window:setTitleBarTheme", theme), onResized: (handler: EventHandler): (() => void) => { const listener = (_ipcEvent: Electron.IpcRendererEvent, payload: unknown) => { handler(payload); diff --git a/packages/desktop/src/window/window-manager.test.ts b/packages/desktop/src/window/window-manager.test.ts index b5676f300..41e227b62 100644 --- a/packages/desktop/src/window/window-manager.test.ts +++ b/packages/desktop/src/window/window-manager.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { readBadgeCount } from "./window-manager"; +import { getTitleBarOverlayOptions, readBadgeCount, readWindowTheme } from "./window-manager"; describe("window-manager", () => { describe("readBadgeCount", () => { @@ -20,4 +20,35 @@ describe("window-manager", () => { expect(readBadgeCount({ count: 2 })).toBe(0); }); }); + + describe("readWindowTheme", () => { + it("accepts supported title bar themes", () => { + expect(readWindowTheme("light")).toBe("light"); + expect(readWindowTheme("dark")).toBe("dark"); + }); + + it("rejects invalid title bar themes", () => { + expect(readWindowTheme(undefined)).toBeNull(); + expect(readWindowTheme("auto")).toBeNull(); + expect(readWindowTheme("system")).toBeNull(); + }); + }); + + describe("getTitleBarOverlayOptions", () => { + it("returns light title bar overlay colors", () => { + expect(getTitleBarOverlayOptions("light")).toEqual({ + color: "#ffffff", + symbolColor: "#09090b", + height: 48, + }); + }); + + it("returns dark title bar overlay colors", () => { + expect(getTitleBarOverlayOptions("dark")).toEqual({ + color: "#18181c", + symbolColor: "#e4e4e7", + height: 48, + }); + }); + }); }); diff --git a/packages/desktop/src/window/window-manager.ts b/packages/desktop/src/window/window-manager.ts index a90d91ecd..0eb3c3801 100644 --- a/packages/desktop/src/window/window-manager.ts +++ b/packages/desktop/src/window/window-manager.ts @@ -1,4 +1,4 @@ -import { app, BrowserWindow, ipcMain } from "electron"; +import { app, BrowserWindow, ipcMain, nativeTheme } from "electron"; import { createWindowMoveState, readWindowMovePayload, @@ -14,6 +14,28 @@ export function readBadgeCount(input: unknown): number { return input; } +export type WindowTheme = "light" | "dark"; + +export function readWindowTheme(input: unknown): WindowTheme | null { + if (input === "light" || input === "dark") { + return input; + } + + return null; +} + +export function resolveSystemWindowTheme(): WindowTheme { + return nativeTheme.shouldUseDarkColors ? "dark" : "light"; +} + +export function getTitleBarOverlayOptions(theme: WindowTheme): Electron.TitleBarOverlayOptions { + if (theme === "dark") { + return { color: "#18181c", symbolColor: "#e4e4e7", height: 48 }; + } + + return { color: "#ffffff", symbolColor: "#09090b", height: 48 }; +} + export function registerWindowManager(): void { // --------------------------------------------------------------------------- // Manual window dragging (replaces flaky CSS -webkit-app-region: drag). @@ -101,6 +123,20 @@ export function registerWindowManager(): void { } } }); + + ipcMain.handle("paseo:window:setTitleBarTheme", (event, theme?: unknown) => { + const win = BrowserWindow.fromWebContents(event.sender); + if (!win || process.platform === "darwin") { + return; + } + + const nextTheme = readWindowTheme(theme); + if (!nextTheme) { + return; + } + + win.setTitleBarOverlay(getTitleBarOverlayOptions(nextTheme)); + }); } export function setupWindowResizeEvents(win: BrowserWindow): void {