diff --git a/app/ipcHandlers.js b/app/ipcHandlers.js index 36a6ade..f4a3f4c 100644 --- a/app/ipcHandlers.js +++ b/app/ipcHandlers.js @@ -3,7 +3,7 @@ const fs = require("fs"); const path = require("path"); const { logger, logDir } = require("../server/logger"); const { configFeatures, featuresPath } = require("../config"); -const { getThemes } = require("./themeManager"); +const { getThemes, reloadThemes } = require("./themeManager"); const config = require("../config"); const { validateDownloadPath, getDefaultDownloadPath } = require("./pathValidator"); @@ -57,7 +57,7 @@ function registerIpcHandlers(getMainWindow) { getMainWindow()?.setProgressBar(percent / 100); }); - // Contrôles fenêtre (custom top bar) + // TOPBAR ACTION ipcMain.on("window-minimize", () => getMainWindow()?.minimize()); ipcMain.on("window-maximize", () => { const win = getMainWindow(); @@ -66,7 +66,7 @@ function registerIpcHandlers(getMainWindow) { }); ipcMain.on("window-close", () => getMainWindow()?.close()); - // Actions custom + ipcMain.on("open-devtools", () => getMainWindow()?.webContents.openDevTools({ mode: "detach" }) ); @@ -79,9 +79,16 @@ function registerIpcHandlers(getMainWindow) { ); ipcMain.on("open-config", () => shell.openPath(configFolderPath)); + + + // THEME + ipcMain.handle("get-themes", () => getThemes()); + ipcMain.on("open-theme", () => shell.openPath(themeFolderPath)); - ipcMain.handle("get-themes", () => getThemes()); + ipcMain.handle("reload-themes", async () => { + return await reloadThemes(); + }); // Modification des features ipcMain.handle("set-feature", (event, { key, value }) => { diff --git a/app/themeManager.js b/app/themeManager.js index 51acd5a..9a997ee 100644 --- a/app/themeManager.js +++ b/app/themeManager.js @@ -178,4 +178,11 @@ function getThemes() { return cachedThemes ?? []; } -module.exports = { initThemes, getThemes }; \ No newline at end of file +async function reloadThemes() { + const themes = await loadThemesFromFolder(); + cachedThemes = themes.sort((a, b) => getThemeOrder(a.id) - getThemeOrder(b.id)); + logger.info(`Themes reloaded: ${cachedThemes.length} theme(s)`); + return cachedThemes; +} + +module.exports = { initThemes, getThemes , reloadThemes }; \ No newline at end of file diff --git a/preload.js b/preload.js index a623352..3f7e4fa 100644 --- a/preload.js +++ b/preload.js @@ -9,6 +9,7 @@ contextBridge.exposeInMainWorld("electronAPI", { getVersion: () => ipcRenderer.invoke("version"), getValidatedDownloadPath: (path) => ipcRenderer.invoke("validate-download-path", path), getThemes: () => ipcRenderer.invoke("get-themes"), + reloadThemes: () => ipcRenderer.invoke("reload-themes") }); // Contrôles de fenêtre et outils custom pour la topbar diff --git a/public/index.html b/public/index.html index b2ababd..9581bb2 100644 --- a/public/index.html +++ b/public/index.html @@ -54,12 +54,18 @@
-
+
Thème +
Choose the theme of the application
diff --git a/public/script/customthemes.js b/public/script/customthemes.js index ffd5209..76750da 100644 --- a/public/script/customthemes.js +++ b/public/script/customthemes.js @@ -108,4 +108,18 @@ themeSelect.addEventListener("change", (e) => { } }); +async function refreshThemes() { + loadedThemes = await window.electronAPI.reloadThemes(); + populateThemeSelect(loadedThemes); + + const features = await window.electronAPI.getFeatures(); + const savedId = features.theme; + const theme = loadedThemes.find(t => t.id === savedId) || loadedThemes[0]; + + themeSelect.value = theme.id; + applyTheme(theme); +} + +window.refreshThemes = refreshThemes; + initThemes(); \ No newline at end of file diff --git a/public/script/settingsPanel.js b/public/script/settingsPanel.js index 40080fd..cb6e0f9 100644 --- a/public/script/settingsPanel.js +++ b/public/script/settingsPanel.js @@ -39,6 +39,16 @@ document.getElementById("open-json-btn").addEventListener("click", () => { window.topbarAPI.openConfig(); // ton IPC existant }); +// Refresh Themes +document.getElementById("refresh-themes-btn").addEventListener("click", function() { + this.classList.add("spinning"); + window.refreshThemes(); + + setTimeout(() => { + this.classList.remove("spinning"); + }, 600); +}); + /* clic sur l'overlay = fermer */ settingsPanel.addEventListener("click", closePanel); diff --git a/public/styles/layout/settingsPanel.css b/public/styles/layout/settingsPanel.css index 0a77622..feea711 100644 --- a/public/styles/layout/settingsPanel.css +++ b/public/styles/layout/settingsPanel.css @@ -232,6 +232,33 @@ details summary { opacity: 1; } +/* REFRESH THEMES BUTTON */ +.refresh-themes-btn { + padding: 6px 8px; + border-radius: 6px; + border: 1px solid var(--form-input-border-color); + background-color: var(--form-input-bg-color); + color: var(--form-input-text-color); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; +} + +.refresh-themes-btn:hover { + background-color: var(--form-button-bg-hover-color); +} + +.refresh-themes-btn.spinning svg { + animation: spin 0.6s ease-in-out; +} + +.refresh-themes-btn svg { + color: currentColor; + transition: transform 0.6s ease-in-out; +} + /* ANIMATION */ @keyframes popIn { from { transform: scale(0.95); opacity: 0; } @@ -241,4 +268,9 @@ details summary { @keyframes slideIn { from { transform: translateX(20px); opacity: 0; } to { transform: translateX(0); opacity: 1; } +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } } \ No newline at end of file