From 870c52b4a6eaf09e2f2f3010234acff3c4b8bf5b Mon Sep 17 00:00:00 2001 From: Andrew Zambazos Date: Thu, 31 Jul 2025 18:21:50 +1200 Subject: [PATCH] Enhance theme customization and preview in settings Added support for a dynamic 'Custom' theme button that appears when user settings diverge from predefined themes. Improved theme state management by tracking the active theme name in localStorage, updating button states, and refining the preview to better reflect customizations. Also updated the settings page layout and styles for better user experience and fixed the preview to always show the custom title. --- GPU-FIX-README.md | 0 renderer/customization.js | 167 +++++++++++++++++++++++++++++++++++++- renderer/settings.css | 4 +- renderer/settings.html | 32 +++++++- site-history.json | 1 + 5 files changed, 196 insertions(+), 8 deletions(-) create mode 100644 GPU-FIX-README.md diff --git a/GPU-FIX-README.md b/GPU-FIX-README.md new file mode 100644 index 0000000..e69de29 diff --git a/renderer/customization.js b/renderer/customization.js index 74c9aec..eaf1c9d 100644 --- a/renderer/customization.js +++ b/renderer/customization.js @@ -191,13 +191,16 @@ class BrowserCustomizer { }; this.currentTheme = this.loadTheme(); + this.activeThemeName = this.loadActiveThemeName(); this.init(); } init() { this.setupEventListeners(); this.loadCurrentTheme(); + this.restoreActiveThemeButton(); this.updatePreview(); + this.updateCustomThemeButton(); } setupEventListeners() { @@ -224,9 +227,20 @@ class BrowserCustomizer { document.querySelectorAll('input[name="layout"]').forEach(input => { input.addEventListener('change', (e) => { this.currentTheme.layout = e.target.value; + + // Clear active theme name since this is now a custom theme + this.activeThemeName = 'custom'; + this.saveActiveThemeName('custom'); + + // Remove active class from all theme buttons + document.querySelectorAll('.theme-btn').forEach(btn => { + btn.classList.remove('active'); + }); + this.saveTheme(); this.updatePreview(); this.applyThemeToPages(); + this.updateCustomThemeButton(); }); }); @@ -235,9 +249,20 @@ class BrowserCustomizer { if (showLogoInput) { showLogoInput.addEventListener('change', (e) => { this.currentTheme.showLogo = e.target.checked; + + // Clear active theme name since this is now a custom theme + this.activeThemeName = 'custom'; + this.saveActiveThemeName('custom'); + + // Remove active class from all theme buttons + document.querySelectorAll('.theme-btn').forEach(btn => { + btn.classList.remove('active'); + }); + this.saveTheme(); this.updatePreview(); this.applyThemeToPages(); + this.updateCustomThemeButton(); }); } @@ -245,9 +270,20 @@ class BrowserCustomizer { if (customTitleInput) { customTitleInput.addEventListener('input', (e) => { this.currentTheme.customTitle = e.target.value || 'Nebula Browser'; + + // Clear active theme name since this is now a custom theme + this.activeThemeName = 'custom'; + this.saveActiveThemeName('custom'); + + // Remove active class from all theme buttons + document.querySelectorAll('.theme-btn').forEach(btn => { + btn.classList.remove('active'); + }); + this.saveTheme(); this.updatePreview(); this.applyThemeToPages(); + this.updateCustomThemeButton(); }); } @@ -301,21 +337,40 @@ class BrowserCustomizer { this.currentTheme.gradient = `linear-gradient(145deg, ${this.currentTheme.colors.bg} 0%, ${this.currentTheme.colors.darkPurple} 100%)`; } + // Clear active theme name since this is now a custom theme + this.activeThemeName = 'custom'; + this.saveActiveThemeName('custom'); + + // Remove active class from all theme buttons + document.querySelectorAll('.theme-btn').forEach(btn => { + btn.classList.remove('active'); + }); + this.saveTheme(); this.updatePreview(); this.applyThemeToPages(); + this.updateCustomThemeButton(); } } applyPredefinedTheme(themeName) { - if (this.predefinedThemes[themeName]) { + if (themeName === 'custom') { + // For custom theme, just activate the button but don't change the current theme + this.activeThemeName = 'custom'; + this.saveActiveThemeName('custom'); + this.updateThemeButtons('custom'); + this.updateCustomThemeButton(); + } else if (this.predefinedThemes[themeName]) { this.currentTheme = { ...this.predefinedThemes[themeName] }; + this.activeThemeName = themeName; this.saveTheme(); + this.saveActiveThemeName(themeName); this.loadCurrentTheme(); this.updatePreview(); this.applyThemeToCurrentPage(); this.applyThemeToPages(); this.updateThemeButtons(themeName); + this.updateCustomThemeButton(); } } @@ -328,6 +383,37 @@ class BrowserCustomizer { }); } + updateCustomThemeButton() { + const customBtn = document.getElementById('theme-custom'); + if (!customBtn) return; + + // Check if current theme matches any predefined theme + const matchingTheme = this.detectMatchingPredefinedTheme(); + const isCustomTheme = !matchingTheme; + + if (isCustomTheme) { + customBtn.style.display = 'flex'; + // Update the preview to show current colors + const preview = customBtn.querySelector('.theme-preview'); + if (preview && this.currentTheme) { + preview.style.background = this.currentTheme.gradient || + `linear-gradient(145deg, ${this.currentTheme.colors.bg}, ${this.currentTheme.colors.darkPurple})`; + } + // Set active theme name to custom if it's not already set to a predefined theme + if (this.activeThemeName !== 'custom') { + this.activeThemeName = 'custom'; + this.saveActiveThemeName('custom'); + } + } else { + customBtn.style.display = 'none'; + // If we found a matching predefined theme, update activeThemeName if it was set to custom + if (this.activeThemeName === 'custom') { + this.activeThemeName = matchingTheme; + this.saveActiveThemeName(matchingTheme); + } + } + } + loadCurrentTheme() { // Update color inputs document.getElementById('bg-color').value = this.currentTheme.colors.bg; @@ -349,12 +435,25 @@ class BrowserCustomizer { const preview = document.getElementById('preview-container'); const previewHome = preview.querySelector('.preview-home'); const previewLogo = preview.querySelector('.preview-logo'); + const previewText = preview.querySelector('.preview-text'); // Apply colors to preview previewHome.style.background = this.currentTheme.gradient; - previewLogo.style.color = this.currentTheme.colors.primary; - previewLogo.textContent = this.currentTheme.showLogo ? - `🌌 ${this.currentTheme.customTitle}` : this.currentTheme.customTitle; + + // Handle logo visibility + if (this.currentTheme.showLogo) { + previewLogo.style.display = 'block'; + previewLogo.style.color = this.currentTheme.colors.primary; + previewLogo.textContent = '🌌'; + } else { + previewLogo.style.display = 'none'; + } + + // Always show preview text with custom title + if (previewText) { + previewText.style.color = this.currentTheme.colors.primary; + previewText.textContent = this.currentTheme.customTitle; + } // Update CSS custom properties for live preview this.applyThemeToCurrentPage(); @@ -473,7 +572,9 @@ class BrowserCustomizer { resetToDefault() { if (confirm('Are you sure you want to reset to the default theme? This will lose your current customizations.')) { this.currentTheme = { ...this.defaultTheme }; + this.activeThemeName = 'default'; this.saveTheme(); + this.saveActiveThemeName('default'); this.loadCurrentTheme(); this.updatePreview(); this.applyThemeToCurrentPage(); @@ -492,6 +593,64 @@ class BrowserCustomizer { return savedTheme ? JSON.parse(savedTheme) : { ...this.defaultTheme }; } + saveActiveThemeName(themeName) { + localStorage.setItem('activeThemeName', themeName); + } + + loadActiveThemeName() { + return localStorage.getItem('activeThemeName') || 'default'; + } + + restoreActiveThemeButton() { + // First, remove active class from all buttons + document.querySelectorAll('.theme-btn').forEach(btn => { + btn.classList.remove('active'); + }); + + // If no active theme name is saved, try to detect which predefined theme matches current theme + if (!this.activeThemeName) { + this.activeThemeName = this.detectMatchingPredefinedTheme(); + if (this.activeThemeName) { + this.saveActiveThemeName(this.activeThemeName); + } else { + // If no predefined theme matches, this is a custom theme + this.activeThemeName = 'custom'; + this.saveActiveThemeName('custom'); + } + } + + // Update the custom theme button visibility + this.updateCustomThemeButton(); + + // Then, add active class to the currently active theme button + const activeBtn = document.querySelector(`[data-theme="${this.activeThemeName}"]`); + if (activeBtn) { + activeBtn.classList.add('active'); + } + } + + detectMatchingPredefinedTheme() { + // Check if current theme matches any predefined theme + for (const [themeName, themeData] of Object.entries(this.predefinedThemes)) { + if (this.themesMatch(this.currentTheme, themeData)) { + return themeName; + } + } + return null; + } + + themesMatch(theme1, theme2) { + // Compare essential properties to determine if themes match + return theme1.colors.bg === theme2.colors.bg && + theme1.colors.darkPurple === theme2.colors.darkPurple && + theme1.colors.primary === theme2.colors.primary && + theme1.colors.accent === theme2.colors.accent && + theme1.colors.text === theme2.colors.text && + theme1.layout === theme2.layout && + theme1.showLogo === theme2.showLogo && + theme1.customTitle === theme2.customTitle; + } + getCustomThemes() { const customThemes = localStorage.getItem('customThemes'); return customThemes ? JSON.parse(customThemes) : {}; diff --git a/renderer/settings.css b/renderer/settings.css index ccbb253..c5d83a5 100644 --- a/renderer/settings.css +++ b/renderer/settings.css @@ -16,7 +16,7 @@ } body { - background-color: var(--bg); + background: linear-gradient(145deg, var(--bg) 0%, var(--dark-purple) 100%); color: var(--text); font-family: 'InterVariable', sans-serif; margin: 0; @@ -24,7 +24,7 @@ body { display: flex; justify-content: center; align-items: flex-start; - height: 100vh; + min-height: 100vh; } .container { diff --git a/renderer/settings.html b/renderer/settings.html index e8396fb..6f84d6b 100644 --- a/renderer/settings.html +++ b/renderer/settings.html @@ -55,6 +55,22 @@ background: rgba(123, 46, 255, 0.1); } + .custom-theme-btn { + border: 2px dashed rgba(255, 255, 255, 0.3) !important; + opacity: 0.9; + } + + .custom-theme-btn:hover { + border-color: var(--accent) !important; + opacity: 1; + } + + .custom-theme-btn.active { + border-color: var(--primary) !important; + background: rgba(123, 46, 255, 0.15) !important; + opacity: 1; + } + .theme-preview { width: 60px; height: 40px; @@ -195,6 +211,13 @@ color: var(--primary); } + .preview-text { + font-size: 1.2rem; + font-weight: 600; + color: var(--primary); + margin-top: 5px; + } + .preview-search { width: 60%; height: 40px; @@ -237,7 +260,7 @@

Theme Presets

- @@ -285,6 +308,10 @@
Lavender Fields +
@@ -366,7 +393,8 @@

Preview

- + +
Nebula
diff --git a/site-history.json b/site-history.json index b19de29..5c64c1e 100644 --- a/site-history.json +++ b/site-history.json @@ -1,4 +1,5 @@ [ + "file:///X:/Projects/Code/NebulaBrowser/renderer/index.html", "file:///Users/andrewzambazos/Repositories/NebulaBrowser/renderer/index.html", "https://inscribe.zambazosmedia.group/renderer/editor.html", "https://inscribe.zambazosmedia.group/",