feat: #65 systeme de themes — 15 themes x dark/light
CI / lint (push) Failing after 2s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Failing after -8s

- themes.js: 15 palettes (Defaut ObsiGate, GitHub, Linear, Spotify, Notion,
  Discord, Dracula, One Dark, Tokyo Night, Nord, Night Owl, Anthracite,
  Cyberpunk, Navy Elegance, Tons Terreux)
- Chaque theme a un mode sombre ET clair
- UI: grille de themes dans Configuration → Themes
- Toggle Sombre/Clair par theme
- Persistance localStorage (obsigate-theme + obsigate-theme-mode)
- Preview visuel: barre de couleur + accent bouton
- initThemes() appele au startup dans sync.js
- Defaut ObsiGate conserve le theme actuel
This commit is contained in:
2026-06-15 23:20:35 -04:00
parent 671befe610
commit d1841a057d
5 changed files with 723 additions and 0 deletions
+67
View File
@@ -641,6 +641,9 @@ function initConfigModal() {
// Load saved frontend config on startup
applyFrontendConfig();
// Init theme picker
initThemePicker();
}
function closeConfigModal() {
@@ -1382,5 +1385,69 @@ export {
initEditorPocBtn,
loadAbout as initAboutSection,
loadHiddenFilesSettings as initHiddenFilesConfig,
initThemePicker,
switchSidebarTab,
};
// ── Theme Picker ─────────────────────────────────────────────────────
function initThemePicker() {
import('./themes.js').then(function(mod) {
var THEMES = mod.THEMES;
var applyTheme = mod.applyTheme;
var getCurrentTheme = mod.getCurrentTheme;
var getCurrentMode = mod.getCurrentMode;
var grid = document.getElementById('theme-grid');
if (!grid) return;
var themeKeys = Object.keys(THEMES);
function renderCards() {
var current = getCurrentTheme();
var mode = getCurrentMode();
grid.innerHTML = '';
themeKeys.forEach(function(key) {
var t = THEMES[key];
var v = t.modes[mode];
if (!v) return;
var card = document.createElement('div');
card.className = 'theme-card' + (key === current ? ' active' : '');
card.innerHTML =
'<div class="theme-card-preview">' +
'<div class="theme-card-preview-bar" style="background:' + (v['--bg-sidebar'] || v['--bg-primary']) + '"></div>' +
'<div class="theme-card-preview-body" style="background:' + (v['--bg-primary'] || '#000') + '">' +
'<div class="theme-card-preview-btn" style="background:' + (v['--accent'] || '#58a6ff') + '"></div>' +
'</div>' +
'</div>' +
'<div class="theme-card-name">' + t.name + '</div>' +
'<div class="theme-card-desc">' + t.desc + '</div>';
card.addEventListener('click', function() {
applyTheme(key, mode);
renderCards();
});
grid.appendChild(card);
});
// Mode toggle
var toggle = document.createElement('div');
toggle.className = 'theme-mode-toggle';
var dBtn = elBtn('Sombre', 'dark', mode);
var lBtn = elBtn('Clair', 'light', mode);
toggle.appendChild(dBtn);
toggle.appendChild(lBtn);
grid.appendChild(toggle);
}
function elBtn(label, m, currentMode) {
var btn = document.createElement('button');
btn.className = 'theme-mode-btn' + (m === currentMode ? ' active' : '');
btn.textContent = label;
btn.addEventListener('click', function() {
applyTheme(getCurrentTheme(), m);
renderCards();
});
return btn;
}
renderCards();
});
}