Feat: Add theme reloading functionality with refresh button in settings panel

This commit is contained in:
MasterAcnolo
2026-04-12 09:32:15 +02:00
parent ebb48f4f8b
commit d05b9e2511
7 changed files with 83 additions and 6 deletions

View File

@@ -3,7 +3,7 @@ const fs = require("fs");
const path = require("path"); const path = require("path");
const { logger, logDir } = require("../server/logger"); const { logger, logDir } = require("../server/logger");
const { configFeatures, featuresPath } = require("../config"); const { configFeatures, featuresPath } = require("../config");
const { getThemes } = require("./themeManager"); const { getThemes, reloadThemes } = require("./themeManager");
const config = require("../config"); const config = require("../config");
const { validateDownloadPath, getDefaultDownloadPath } = require("./pathValidator"); const { validateDownloadPath, getDefaultDownloadPath } = require("./pathValidator");
@@ -57,7 +57,7 @@ function registerIpcHandlers(getMainWindow) {
getMainWindow()?.setProgressBar(percent / 100); getMainWindow()?.setProgressBar(percent / 100);
}); });
// Contrôles fenêtre (custom top bar) // TOPBAR ACTION
ipcMain.on("window-minimize", () => getMainWindow()?.minimize()); ipcMain.on("window-minimize", () => getMainWindow()?.minimize());
ipcMain.on("window-maximize", () => { ipcMain.on("window-maximize", () => {
const win = getMainWindow(); const win = getMainWindow();
@@ -66,7 +66,7 @@ function registerIpcHandlers(getMainWindow) {
}); });
ipcMain.on("window-close", () => getMainWindow()?.close()); ipcMain.on("window-close", () => getMainWindow()?.close());
// Actions custom
ipcMain.on("open-devtools", () => ipcMain.on("open-devtools", () =>
getMainWindow()?.webContents.openDevTools({ mode: "detach" }) getMainWindow()?.webContents.openDevTools({ mode: "detach" })
); );
@@ -79,9 +79,16 @@ function registerIpcHandlers(getMainWindow) {
); );
ipcMain.on("open-config", () => shell.openPath(configFolderPath)); ipcMain.on("open-config", () => shell.openPath(configFolderPath));
// THEME
ipcMain.handle("get-themes", () => getThemes());
ipcMain.on("open-theme", () => shell.openPath(themeFolderPath)); ipcMain.on("open-theme", () => shell.openPath(themeFolderPath));
ipcMain.handle("get-themes", () => getThemes()); ipcMain.handle("reload-themes", async () => {
return await reloadThemes();
});
// Modification des features // Modification des features
ipcMain.handle("set-feature", (event, { key, value }) => { ipcMain.handle("set-feature", (event, { key, value }) => {

View File

@@ -178,4 +178,11 @@ function getThemes() {
return cachedThemes ?? []; return cachedThemes ?? [];
} }
module.exports = { initThemes, getThemes }; 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 };

View File

@@ -9,6 +9,7 @@ contextBridge.exposeInMainWorld("electronAPI", {
getVersion: () => ipcRenderer.invoke("version"), getVersion: () => ipcRenderer.invoke("version"),
getValidatedDownloadPath: (path) => ipcRenderer.invoke("validate-download-path", path), getValidatedDownloadPath: (path) => ipcRenderer.invoke("validate-download-path", path),
getThemes: () => ipcRenderer.invoke("get-themes"), getThemes: () => ipcRenderer.invoke("get-themes"),
reloadThemes: () => ipcRenderer.invoke("reload-themes")
}); });
// Contrôles de fenêtre et outils custom pour la topbar // Contrôles de fenêtre et outils custom pour la topbar

View File

@@ -54,12 +54,18 @@
<div class="setting-item" id="theme" style="margin-left: 32px;"> <div class="setting-item" id="theme" style="margin-left: 32px;">
<div class="setting-info"> <div class="setting-info">
<div style="display: flex;"> <div style="display: flex; align-items: center; gap: 10px;">
<span class="setting-title">Thème</span> <span class="setting-title">Thème</span>
<select id="themeSelect" data-key="theme" aria-label="Choisir le thème"> <select id="themeSelect" data-key="theme" aria-label="Choisir le thème">
</select> </select>
<button id="refresh-themes-btn" title="Refresh themes" class="refresh-themes-btn">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 4v6h6M23 20v-6h-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div> </div>
<small>Choose the theme of the application</small> <small>Choose the theme of the application</small>
</div> </div>

View File

@@ -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(); initThemes();

View File

@@ -39,6 +39,16 @@ document.getElementById("open-json-btn").addEventListener("click", () => {
window.topbarAPI.openConfig(); // ton IPC existant 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 */ /* clic sur l'overlay = fermer */
settingsPanel.addEventListener("click", closePanel); settingsPanel.addEventListener("click", closePanel);

View File

@@ -232,6 +232,33 @@ details summary {
opacity: 1; 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 */ /* ANIMATION */
@keyframes popIn { @keyframes popIn {
from { transform: scale(0.95); opacity: 0; } from { transform: scale(0.95); opacity: 0; }
@@ -241,4 +268,9 @@ details summary {
@keyframes slideIn { @keyframes slideIn {
from { transform: translateX(20px); opacity: 0; } from { transform: translateX(20px); opacity: 0; }
to { transform: translateX(0); opacity: 1; } to { transform: translateX(0); opacity: 1; }
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
} }