diff --git a/electron/main.ts b/electron/main.ts index 89a4814..1d9bacf 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -5,6 +5,17 @@ import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const titleBarThemes = { + light: { + color: "#eef1ed", + symbolColor: "#17241d" + }, + dark: { + color: "#132019", + symbolColor: "#e9f3ec" + } +}; + async function createWindow() { const win = new BrowserWindow({ width: 1220, @@ -14,8 +25,7 @@ async function createWindow() { title: "EnvHelper", titleBarStyle: "hidden", titleBarOverlay: { - color: "#eef1ed", - symbolColor: "#17241d", + ...titleBarThemes.light, height: 34 }, autoHideMenuBar: true, @@ -53,6 +63,13 @@ ipcMain.handle("envhelper:open-file", async () => { }; }); +ipcMain.handle("envhelper:set-titlebar-theme", (event, theme: "light" | "dark") => { + BrowserWindow.fromWebContents(event.sender)?.setTitleBarOverlay({ + ...titleBarThemes[theme], + height: 34 + }); +}); + ipcMain.handle("envhelper:save-file", async (_event, content: string) => { const result = await dialog.showSaveDialog({ defaultPath: ".env", diff --git a/electron/preload.ts b/electron/preload.ts index cf5fa72..2e13e96 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -2,5 +2,6 @@ import { contextBridge, ipcRenderer } from "electron"; contextBridge.exposeInMainWorld("envHelper", { openFile: () => ipcRenderer.invoke("envhelper:open-file"), - saveFile: (content: string) => ipcRenderer.invoke("envhelper:save-file", content) + saveFile: (content: string) => ipcRenderer.invoke("envhelper:save-file", content), + setTitlebarTheme: (theme: "light" | "dark") => ipcRenderer.invoke("envhelper:set-titlebar-theme", theme) }); diff --git a/src/App.tsx b/src/App.tsx index 63938b1..f60d5c6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -271,6 +271,14 @@ function readTheme(): ThemeMode { return stored && themeLabels.includes(stored) ? stored : "system"; } +function getEffectiveTheme(themeMode: ThemeMode): "light" | "dark" { + if (themeMode !== "system") { + return themeMode; + } + + return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; +} + export default function App() { const [input, setInput] = useState(sampleEnv); const [loadedPath, setLoadedPath] = useState(null); @@ -286,6 +294,16 @@ export default function App() { useEffect(() => { document.documentElement.dataset.theme = themeMode; localStorage.setItem("envhelper-theme", themeMode); + + const updateTitlebar = () => { + void window.envHelper?.setTitlebarTheme(getEffectiveTheme(themeMode)); + }; + const media = window.matchMedia("(prefers-color-scheme: dark)"); + + updateTitlebar(); + media.addEventListener("change", updateTitlebar); + + return () => media.removeEventListener("change", updateTitlebar); }, [themeMode]); useEffect(() => { diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index d1c7072..6c2aa67 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -9,5 +9,6 @@ interface Window { envHelper?: { openFile: () => Promise; saveFile: (content: string) => Promise; + setTitlebarTheme: (theme: "light" | "dark") => Promise; }; }