diff options
| author | Luke Parker <[email protected]> | 2026-03-12 16:38:56 +1000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-12 16:38:56 +1000 |
| commit | d481f64bdeaca91226e66c0e7888c7a10ba631f7 (patch) | |
| tree | 8700aaad0363342015ffc49cb962620186e1f7f0 /packages/desktop-electron/src | |
| parent | 54e7baa6cfff86627e5555842560b4a20e4be424 (diff) | |
| download | opencode-d481f64bdeaca91226e66c0e7888c7a10ba631f7.tar.gz opencode-d481f64bdeaca91226e66c0e7888c7a10ba631f7.zip | |
fix(electron): theme Windows titlebar overlay (#16843)
Co-authored-by: Brendan Allan <[email protected]>
Diffstat (limited to 'packages/desktop-electron/src')
| -rw-r--r-- | packages/desktop-electron/src/main/ipc.ts | 8 | ||||
| -rw-r--r-- | packages/desktop-electron/src/main/windows.ts | 35 | ||||
| -rw-r--r-- | packages/desktop-electron/src/preload/index.ts | 1 | ||||
| -rw-r--r-- | packages/desktop-electron/src/preload/types.ts | 4 |
4 files changed, 36 insertions, 12 deletions
diff --git a/packages/desktop-electron/src/main/ipc.ts b/packages/desktop-electron/src/main/ipc.ts index bbb5379bb..c0d8773c2 100644 --- a/packages/desktop-electron/src/main/ipc.ts +++ b/packages/desktop-electron/src/main/ipc.ts @@ -2,8 +2,9 @@ import { execFile } from "node:child_process" import { BrowserWindow, Notification, app, clipboard, dialog, ipcMain, shell } from "electron" import type { IpcMainEvent, IpcMainInvokeEvent } from "electron" -import type { InitStep, ServerReadyData, SqliteMigrationProgress, WslConfig } from "../preload/types" +import type { InitStep, ServerReadyData, SqliteMigrationProgress, TitlebarTheme, WslConfig } from "../preload/types" import { getStore } from "./store" +import { setTitlebar } from "./windows" type Deps = { killSidecar: () => void @@ -161,6 +162,11 @@ export function registerIpcHandlers(deps: Deps) { ipcMain.handle("get-zoom-factor", (event: IpcMainInvokeEvent) => event.sender.getZoomFactor()) ipcMain.handle("set-zoom-factor", (event: IpcMainInvokeEvent, factor: number) => event.sender.setZoomFactor(factor)) + ipcMain.handle("set-titlebar", (event: IpcMainInvokeEvent, theme: TitlebarTheme) => { + const win = BrowserWindow.fromWebContents(event.sender) + if (!win) return + setTitlebar(win, theme) + }) } export function sendSqliteMigrationProgress(win: BrowserWindow, progress: SqliteMigrationProgress) { diff --git a/packages/desktop-electron/src/main/windows.ts b/packages/desktop-electron/src/main/windows.ts index 9178457f8..d4ec5ac79 100644 --- a/packages/desktop-electron/src/main/windows.ts +++ b/packages/desktop-electron/src/main/windows.ts @@ -1,7 +1,8 @@ import windowState from "electron-window-state" -import { app, BrowserWindow, nativeImage } from "electron" +import { app, BrowserWindow, nativeImage, nativeTheme } from "electron" import { dirname, join } from "node:path" import { fileURLToPath } from "node:url" +import type { TitlebarTheme } from "../preload/types" type Globals = { updaterEnabled: boolean @@ -20,6 +21,24 @@ function iconPath() { return join(iconsDir(), `icon.${ext}`) } +function tone() { + return nativeTheme.shouldUseDarkColors ? "dark" : "light" +} + +function overlay(theme: Partial<TitlebarTheme> = {}) { + const mode = theme.mode ?? tone() + return { + color: "#00000000", + symbolColor: mode === "dark" ? "white" : "black", + height: 40, + } +} + +export function setTitlebar(win: BrowserWindow, theme: Partial<TitlebarTheme> = {}) { + if (process.platform !== "win32") return + win.setTitleBarOverlay(overlay(theme)) +} + export function setDockIcon() { if (process.platform !== "darwin") return app.dock?.setIcon(nativeImage.createFromPath(join(iconsDir(), "[email protected]"))) @@ -31,6 +50,7 @@ export function createMainWindow(globals: Globals) { defaultHeight: 800, }) + const mode = tone() const win = new BrowserWindow({ x: state.x, y: state.y, @@ -49,11 +69,7 @@ export function createMainWindow(globals: Globals) { ? { frame: false, titleBarStyle: "hidden" as const, - titleBarOverlay: { - color: "transparent", - symbolColor: "#999", - height: 40, - }, + titleBarOverlay: overlay({ mode }), } : {}), webPreferences: { @@ -71,6 +87,7 @@ export function createMainWindow(globals: Globals) { } export function createLoadingWindow(globals: Globals) { + const mode = tone() const win = new BrowserWindow({ width: 640, height: 480, @@ -83,11 +100,7 @@ export function createLoadingWindow(globals: Globals) { ? { frame: false, titleBarStyle: "hidden" as const, - titleBarOverlay: { - color: "transparent", - symbolColor: "#999", - height: 40, - }, + titleBarOverlay: overlay({ mode }), } : {}), webPreferences: { diff --git a/packages/desktop-electron/src/preload/index.ts b/packages/desktop-electron/src/preload/index.ts index a6520ab42..c1ed9afd2 100644 --- a/packages/desktop-electron/src/preload/index.ts +++ b/packages/desktop-electron/src/preload/index.ts @@ -57,6 +57,7 @@ const api: ElectronAPI = { relaunch: () => ipcRenderer.send("relaunch"), getZoomFactor: () => ipcRenderer.invoke("get-zoom-factor"), setZoomFactor: (factor) => ipcRenderer.invoke("set-zoom-factor", factor), + setTitlebar: (theme) => ipcRenderer.invoke("set-titlebar", theme), loadingWindowComplete: () => ipcRenderer.send("loading-window-complete"), runUpdater: (alertOnFail) => ipcRenderer.invoke("run-updater", alertOnFail), checkUpdate: () => ipcRenderer.invoke("check-update"), diff --git a/packages/desktop-electron/src/preload/types.ts b/packages/desktop-electron/src/preload/types.ts index af5410f5f..43bdf1e6c 100644 --- a/packages/desktop-electron/src/preload/types.ts +++ b/packages/desktop-electron/src/preload/types.ts @@ -10,6 +10,9 @@ export type SqliteMigrationProgress = { type: "InProgress"; value: number } | { export type WslConfig = { enabled: boolean } export type LinuxDisplayBackend = "wayland" | "auto" +export type TitlebarTheme = { + mode: "light" | "dark" +} export type ElectronAPI = { killSidecar: () => Promise<void> @@ -57,6 +60,7 @@ export type ElectronAPI = { relaunch: () => void getZoomFactor: () => Promise<number> setZoomFactor: (factor: number) => Promise<void> + setTitlebar: (theme: TitlebarTheme) => Promise<void> loadingWindowComplete: () => void runUpdater: (alertOnFail: boolean) => Promise<void> checkUpdate: () => Promise<{ updateAvailable: boolean; version?: string }> |
