diff --git a/electron/main.ts b/electron/main.ts index 1d9bacf..875afbd 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -5,17 +5,6 @@ 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, @@ -23,11 +12,7 @@ async function createWindow() { minWidth: 980, minHeight: 680, title: "EnvHelper", - titleBarStyle: "hidden", - titleBarOverlay: { - ...titleBarThemes.light, - height: 34 - }, + frame: false, autoHideMenuBar: true, backgroundColor: "#f6f7f4", webPreferences: { @@ -63,11 +48,27 @@ 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:window-minimize", (event) => { + BrowserWindow.fromWebContents(event.sender)?.minimize(); +}); + +ipcMain.handle("envhelper:window-toggle-maximize", (event) => { + const win = BrowserWindow.fromWebContents(event.sender); + if (!win) { + return false; + } + + if (win.isMaximized()) { + win.unmaximize(); + return false; + } + + win.maximize(); + return true; +}); + +ipcMain.handle("envhelper:window-close", (event) => { + BrowserWindow.fromWebContents(event.sender)?.close(); }); ipcMain.handle("envhelper:save-file", async (_event, content: string) => { diff --git a/electron/preload.ts b/electron/preload.ts index 2e13e96..9b7b055 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -3,5 +3,7 @@ import { contextBridge, ipcRenderer } from "electron"; contextBridge.exposeInMainWorld("envHelper", { openFile: () => ipcRenderer.invoke("envhelper:open-file"), saveFile: (content: string) => ipcRenderer.invoke("envhelper:save-file", content), - setTitlebarTheme: (theme: "light" | "dark") => ipcRenderer.invoke("envhelper:set-titlebar-theme", theme) + minimizeWindow: () => ipcRenderer.invoke("envhelper:window-minimize"), + toggleMaximizeWindow: () => ipcRenderer.invoke("envhelper:window-toggle-maximize"), + closeWindow: () => ipcRenderer.invoke("envhelper:window-close") }); diff --git a/src/App.tsx b/src/App.tsx index f60d5c6..3f282f1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,8 @@ import { FileDown, FileInput, Languages, + Maximize2, + Minus, Plus, RefreshCcw, Settings, @@ -271,14 +273,6 @@ 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); @@ -294,16 +288,6 @@ 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(() => { @@ -351,6 +335,23 @@ export default function App() {
EH
EnvHelper +
+ + + +
diff --git a/src/styles.css b/src/styles.css index fdfd955..4872b21 100644 --- a/src/styles.css +++ b/src/styles.css @@ -149,13 +149,12 @@ textarea:focus { .titlebar { -webkit-app-region: drag; align-items: center; - background: color-mix(in srgb, var(--surface) 88%, var(--bg)); + background: color-mix(in srgb, var(--surface) 92%, var(--bg)); border: 1px solid var(--border); border-radius: 8px; display: flex; justify-content: space-between; overflow: hidden; - padding-right: 138px; } .titlebarBrand { @@ -181,6 +180,42 @@ textarea:focus { width: 24px; } +.windowControls { + -webkit-app-region: no-drag; + align-items: stretch; + display: flex; + height: 100%; + padding: 3px; +} + +.windowControls button { + background: transparent; + border: 1px solid transparent; + border-radius: 6px; + color: var(--muted); + min-height: 0; + min-width: 38px; + padding: 0; +} + +.windowControls button:hover { + background: var(--surface-subtle); + border-color: var(--border); + color: var(--text); + transform: none; +} + +.windowControls button:active { + background: var(--accent-soft); + color: var(--accent-strong); +} + +.windowControls .closeButton:hover { + background: var(--danger); + border-color: var(--danger); + color: #fff; +} + .toolbar { align-items: center; background: var(--surface); diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 6c2aa67..668dc2d 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -9,6 +9,8 @@ interface Window { envHelper?: { openFile: () => Promise; saveFile: (content: string) => Promise; - setTitlebarTheme: (theme: "light" | "dark") => Promise; + minimizeWindow: () => Promise; + toggleMaximizeWindow: () => Promise; + closeWindow: () => Promise; }; }