From c11fd4068120c783387b5229b92882a88b65a576 Mon Sep 17 00:00:00 2001 From: MasterAcnolo <68693319+MasterAcnolo@users.noreply.github.com> Date: Sat, 20 Dec 2025 09:53:17 +0100 Subject: [PATCH] feat: Implement topbar with custom window controls and actions --- main.js | 32 ++++++++ preload.js | 11 +++ public/assets/icon/close.svg | 1 + public/assets/icon/maximize.svg | 1 + public/assets/icon/minimize.svg | 1 + public/index.html | 15 ++++ public/script/topbar.js | 24 ++++++ public/styles/components/themebutton.css | 2 +- public/styles/layout/topbar.css | 95 ++++++++++++++++++++++++ 9 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 public/assets/icon/close.svg create mode 100644 public/assets/icon/maximize.svg create mode 100644 public/assets/icon/minimize.svg create mode 100644 public/script/topbar.js create mode 100644 public/styles/layout/topbar.css diff --git a/main.js b/main.js index b6b7d41..7e697e8 100644 --- a/main.js +++ b/main.js @@ -77,6 +77,7 @@ async function createMainWindow() { height: 800, minWidth: 750, minHeight: 800, + frame:false, webPreferences: { nodeIntegration: false, contextIsolation: true, @@ -118,6 +119,37 @@ ipcMain.on("set-progress", (event, percent) => { if (mainWindow) mainWindow.setProgressBar(percent / 100); // Electron attend 0 → 1 }); +// Topbar window controls +ipcMain.on("window-minimize", () => { + if (mainWindow) mainWindow.minimize(); +}); +ipcMain.on("window-maximize", () => { + if (mainWindow) { + if (mainWindow.isMaximized()) { + mainWindow.unmaximize(); + } else { + mainWindow.maximize(); + } + } +}); +ipcMain.on("window-close", () => { + if (mainWindow) mainWindow.close(); +}); + +// Topbar custom actions +ipcMain.on("open-devtools", () => { + if (mainWindow) mainWindow.webContents.openDevTools({ mode: 'detach' }); +}); +ipcMain.on("open-logs", () => { + if (logsFolderPath) shell.openPath(logsFolderPath); +}); +ipcMain.on("open-website", () => { + shell.openExternal("https://masteracnolo.github.io/FreedomLoader/index.html"); +}); +ipcMain.on("open-wiki", () => { + shell.openExternal("https://masteracnolo.github.io/FreedomLoader/pages/wiki.html"); +}); + // Menu function setupMenu() { const menuTemplate = [ diff --git a/preload.js b/preload.js index a1f3787..2f0e8e8 100644 --- a/preload.js +++ b/preload.js @@ -5,3 +5,14 @@ contextBridge.exposeInMainWorld("electronAPI", { selectDownloadFolder: () => ipcRenderer.invoke("select-download-folder"), setProgress: (percent) => ipcRenderer.send("set-progress", percent) }); + +// Contrôles de fenêtre et outils custom pour la topbar +contextBridge.exposeInMainWorld("topbarAPI", { + minimize: () => ipcRenderer.send("window-minimize"), + maximize: () => ipcRenderer.send("window-maximize"), + close: () => ipcRenderer.send("window-close"), + openDevTools: () => ipcRenderer.send("open-devtools"), + openLogs: () => ipcRenderer.send("open-logs"), + openWebsite: () => ipcRenderer.send("open-website"), + openWiki: () => ipcRenderer.send("open-wiki") +}); diff --git a/public/assets/icon/close.svg b/public/assets/icon/close.svg new file mode 100644 index 0000000..1d747f8 --- /dev/null +++ b/public/assets/icon/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/icon/maximize.svg b/public/assets/icon/maximize.svg new file mode 100644 index 0000000..24dc937 --- /dev/null +++ b/public/assets/icon/maximize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/icon/minimize.svg b/public/assets/icon/minimize.svg new file mode 100644 index 0000000..f5d42a4 --- /dev/null +++ b/public/assets/icon/minimize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/index.html b/public/index.html index f0eafd9..d68b91f 100644 --- a/public/index.html +++ b/public/index.html @@ -12,9 +12,23 @@ + +
+
+ + + + +
+
+ + + +
+
@@ -86,6 +100,7 @@ + \ No newline at end of file diff --git a/public/script/topbar.js b/public/script/topbar.js new file mode 100644 index 0000000..0225ea8 --- /dev/null +++ b/public/script/topbar.js @@ -0,0 +1,24 @@ +// Ajout des listeners pour la topbar +function setupTopbarListeners() { + document.addEventListener('DOMContentLoaded', () => { + const { topbarAPI } = window; + if (!topbarAPI) return; + const minBtn = document.getElementById('minimize-btn'); + const maxBtn = document.getElementById('maximize-btn'); + const closeBtn = document.getElementById('close-btn'); + const devtoolsBtn = document.getElementById('devtools-btn'); + const logsBtn = document.getElementById('logs-btn'); + const websiteBtn = document.getElementById('website-btn'); + const wikiBtn = document.getElementById('wiki-btn'); + + if (minBtn) minBtn.onclick = () => topbarAPI.minimize(); + if (maxBtn) maxBtn.onclick = () => topbarAPI.maximize(); + if (closeBtn) closeBtn.onclick = () => topbarAPI.close(); + if (devtoolsBtn) devtoolsBtn.onclick = () => topbarAPI.openDevTools(); + if (logsBtn) logsBtn.onclick = () => topbarAPI.openLogs(); + if (websiteBtn) websiteBtn.onclick = () => topbarAPI.openWebsite(); + if (wikiBtn) wikiBtn.onclick = () => topbarAPI.openWiki(); + }); +} + +setupTopbarListeners(); diff --git a/public/styles/components/themebutton.css b/public/styles/components/themebutton.css index 84693e6..d80e387 100644 --- a/public/styles/components/themebutton.css +++ b/public/styles/components/themebutton.css @@ -1,6 +1,6 @@ .theme-switcher { position: absolute; - top: 10px; + top: 60px; right: 10px; background: #242424; padding: 8px 12px; diff --git a/public/styles/layout/topbar.css b/public/styles/layout/topbar.css new file mode 100644 index 0000000..bcae7ad --- /dev/null +++ b/public/styles/layout/topbar.css @@ -0,0 +1,95 @@ +.topbar { + display: flex; + align-items: center; + justify-content: space-between; + + height: 48px; + width: 100vw; + + background: #1f1f1f98; + color: #eaeaea; + + -webkit-app-region: drag; + user-select: none; + +} + +/* LEFT - custom buttons */ +.custom-controls { + display: flex; + align-items: center; + gap: 6px; + padding-left: 12px; +} + +/* RIGHT - window buttons */ +.window-controls { + display: flex; + align-items: center; +} + +/* GLOBAL BUTTON RESET */ +.topbar button { + border: none; + background: transparent; + color: inherit; + cursor: pointer; + + -webkit-app-region: no-drag; + + display: flex; + align-items: center; + justify-content: center; + + transition: background 0.15s ease, transform 0.1s ease; + +} + +/* Custom buttons (Logs / Wiki / etc.) */ +.custom-controls button { + height: 32px; + padding: 0 14px; + + font-size: 15px; + border-radius: 8px; +} + +.custom-controls button:hover { + background: rgba(255, 255, 255, 0.12); +} + +/* Window buttons (Windows style) */ +.window-controls button { + width: 46px; + height: 48px; + + font-size: 12px; +} + +.window-controls button:hover { + background: rgba(255, 255, 255, 0.1); +} + +.window-controls img { + width: 27px; + height: 27px; + pointer-events: none; + filter: invert(100%); +} + + +#close-btn:hover { + background: #e81123; + color: #fff; +} + +/* Active effect */ +.topbar button:active { + background: rgba(255, 255, 255, 0.18); + transform: scale(0.9); +} + +#close-btn:active { + background: #c50f1f; + transform: scale(0.9); +} \ No newline at end of file