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.
This commit is contained in:
2025-07-31 18:21:50 +12:00
parent 1a309d9244
commit 870c52b4a6
5 changed files with 196 additions and 8 deletions
View File
+162 -3
View File
@@ -191,13 +191,16 @@ class BrowserCustomizer {
}; };
this.currentTheme = this.loadTheme(); this.currentTheme = this.loadTheme();
this.activeThemeName = this.loadActiveThemeName();
this.init(); this.init();
} }
init() { init() {
this.setupEventListeners(); this.setupEventListeners();
this.loadCurrentTheme(); this.loadCurrentTheme();
this.restoreActiveThemeButton();
this.updatePreview(); this.updatePreview();
this.updateCustomThemeButton();
} }
setupEventListeners() { setupEventListeners() {
@@ -224,9 +227,20 @@ class BrowserCustomizer {
document.querySelectorAll('input[name="layout"]').forEach(input => { document.querySelectorAll('input[name="layout"]').forEach(input => {
input.addEventListener('change', (e) => { input.addEventListener('change', (e) => {
this.currentTheme.layout = e.target.value; 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.saveTheme();
this.updatePreview(); this.updatePreview();
this.applyThemeToPages(); this.applyThemeToPages();
this.updateCustomThemeButton();
}); });
}); });
@@ -235,9 +249,20 @@ class BrowserCustomizer {
if (showLogoInput) { if (showLogoInput) {
showLogoInput.addEventListener('change', (e) => { showLogoInput.addEventListener('change', (e) => {
this.currentTheme.showLogo = e.target.checked; 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.saveTheme();
this.updatePreview(); this.updatePreview();
this.applyThemeToPages(); this.applyThemeToPages();
this.updateCustomThemeButton();
}); });
} }
@@ -245,9 +270,20 @@ class BrowserCustomizer {
if (customTitleInput) { if (customTitleInput) {
customTitleInput.addEventListener('input', (e) => { customTitleInput.addEventListener('input', (e) => {
this.currentTheme.customTitle = e.target.value || 'Nebula Browser'; 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.saveTheme();
this.updatePreview(); this.updatePreview();
this.applyThemeToPages(); 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%)`; 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.saveTheme();
this.updatePreview(); this.updatePreview();
this.applyThemeToPages(); this.applyThemeToPages();
this.updateCustomThemeButton();
} }
} }
applyPredefinedTheme(themeName) { 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.currentTheme = { ...this.predefinedThemes[themeName] };
this.activeThemeName = themeName;
this.saveTheme(); this.saveTheme();
this.saveActiveThemeName(themeName);
this.loadCurrentTheme(); this.loadCurrentTheme();
this.updatePreview(); this.updatePreview();
this.applyThemeToCurrentPage(); this.applyThemeToCurrentPage();
this.applyThemeToPages(); this.applyThemeToPages();
this.updateThemeButtons(themeName); 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() { loadCurrentTheme() {
// Update color inputs // Update color inputs
document.getElementById('bg-color').value = this.currentTheme.colors.bg; document.getElementById('bg-color').value = this.currentTheme.colors.bg;
@@ -349,12 +435,25 @@ class BrowserCustomizer {
const preview = document.getElementById('preview-container'); const preview = document.getElementById('preview-container');
const previewHome = preview.querySelector('.preview-home'); const previewHome = preview.querySelector('.preview-home');
const previewLogo = preview.querySelector('.preview-logo'); const previewLogo = preview.querySelector('.preview-logo');
const previewText = preview.querySelector('.preview-text');
// Apply colors to preview // Apply colors to preview
previewHome.style.background = this.currentTheme.gradient; previewHome.style.background = this.currentTheme.gradient;
// Handle logo visibility
if (this.currentTheme.showLogo) {
previewLogo.style.display = 'block';
previewLogo.style.color = this.currentTheme.colors.primary; previewLogo.style.color = this.currentTheme.colors.primary;
previewLogo.textContent = this.currentTheme.showLogo ? previewLogo.textContent = '🌌';
`🌌 ${this.currentTheme.customTitle}` : this.currentTheme.customTitle; } 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 // Update CSS custom properties for live preview
this.applyThemeToCurrentPage(); this.applyThemeToCurrentPage();
@@ -473,7 +572,9 @@ class BrowserCustomizer {
resetToDefault() { resetToDefault() {
if (confirm('Are you sure you want to reset to the default theme? This will lose your current customizations.')) { if (confirm('Are you sure you want to reset to the default theme? This will lose your current customizations.')) {
this.currentTheme = { ...this.defaultTheme }; this.currentTheme = { ...this.defaultTheme };
this.activeThemeName = 'default';
this.saveTheme(); this.saveTheme();
this.saveActiveThemeName('default');
this.loadCurrentTheme(); this.loadCurrentTheme();
this.updatePreview(); this.updatePreview();
this.applyThemeToCurrentPage(); this.applyThemeToCurrentPage();
@@ -492,6 +593,64 @@ class BrowserCustomizer {
return savedTheme ? JSON.parse(savedTheme) : { ...this.defaultTheme }; 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() { getCustomThemes() {
const customThemes = localStorage.getItem('customThemes'); const customThemes = localStorage.getItem('customThemes');
return customThemes ? JSON.parse(customThemes) : {}; return customThemes ? JSON.parse(customThemes) : {};
+2 -2
View File
@@ -16,7 +16,7 @@
} }
body { body {
background-color: var(--bg); background: linear-gradient(145deg, var(--bg) 0%, var(--dark-purple) 100%);
color: var(--text); color: var(--text);
font-family: 'InterVariable', sans-serif; font-family: 'InterVariable', sans-serif;
margin: 0; margin: 0;
@@ -24,7 +24,7 @@ body {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: flex-start; align-items: flex-start;
height: 100vh; min-height: 100vh;
} }
.container { .container {
+30 -2
View File
@@ -55,6 +55,22 @@
background: rgba(123, 46, 255, 0.1); 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 { .theme-preview {
width: 60px; width: 60px;
height: 40px; height: 40px;
@@ -195,6 +211,13 @@
color: var(--primary); color: var(--primary);
} }
.preview-text {
font-size: 1.2rem;
font-weight: 600;
color: var(--primary);
margin-top: 5px;
}
.preview-search { .preview-search {
width: 60%; width: 60%;
height: 40px; height: 40px;
@@ -237,7 +260,7 @@
<div class="customization-group"> <div class="customization-group">
<h3>Theme Presets</h3> <h3>Theme Presets</h3>
<div class="theme-selector"> <div class="theme-selector">
<button id="theme-default" class="theme-btn active" data-theme="default"> <button id="theme-default" class="theme-btn" data-theme="default">
<div class="theme-preview" style="background: linear-gradient(145deg, #121418, #1B1035);"></div> <div class="theme-preview" style="background: linear-gradient(145deg, #121418, #1B1035);"></div>
<span>Default</span> <span>Default</span>
</button> </button>
@@ -285,6 +308,10 @@
<div class="theme-preview" style="background: linear-gradient(145deg, #f8f4ff, #e6d8ff);"></div> <div class="theme-preview" style="background: linear-gradient(145deg, #f8f4ff, #e6d8ff);"></div>
<span>Lavender Fields</span> <span>Lavender Fields</span>
</button> </button>
<button id="theme-custom" class="theme-btn custom-theme-btn" data-theme="custom" style="display: none;">
<div class="theme-preview" style="background: linear-gradient(145deg, var(--bg), var(--gradient-color));"></div>
<span>Custom</span>
</button>
</div> </div>
</div> </div>
@@ -366,7 +393,8 @@
<h3>Preview</h3> <h3>Preview</h3>
<div class="preview-container" id="preview-container"> <div class="preview-container" id="preview-container">
<div class="preview-home"> <div class="preview-home">
<div class="preview-logo">🌌 Nebula</div> <div class="preview-logo" id="preview-logo">../assets/images/Logos/Nebula-Logo.svg</div>
<div class="preview-text">Nebula</div>
<div class="preview-search"></div> <div class="preview-search"></div>
<div class="preview-bookmarks"> <div class="preview-bookmarks">
<div class="preview-bookmark"></div> <div class="preview-bookmark"></div>
+1
View File
@@ -1,4 +1,5 @@
[ [
"file:///X:/Projects/Code/NebulaBrowser/renderer/index.html",
"file:///Users/andrewzambazos/Repositories/NebulaBrowser/renderer/index.html", "file:///Users/andrewzambazos/Repositories/NebulaBrowser/renderer/index.html",
"https://inscribe.zambazosmedia.group/renderer/editor.html", "https://inscribe.zambazosmedia.group/renderer/editor.html",
"https://inscribe.zambazosmedia.group/", "https://inscribe.zambazosmedia.group/",