Add particle background toggle & optimize accent color caching

This commit is contained in:
Broque Thomas 2026-03-13 16:05:22 -07:00
parent 2c6f2adce1
commit f9bdabc0e4
3 changed files with 58 additions and 5 deletions

View file

@ -4195,6 +4195,13 @@
</select> </select>
<small style="color: #888;">Audio-reactive visualizer on the sidebar edge when music is playing</small> <small style="color: #888;">Audio-reactive visualizer on the sidebar edge when music is playing</small>
</div> </div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="particles-enabled" checked>
Background Particles
</label>
<small style="color: #888;">Animated particle effects behind each page. Disable to reduce GPU usage.</small>
</div>
</div> </div>
<!-- Database Settings --> <!-- Database Settings -->

View file

@ -21,9 +21,15 @@
// ── Helpers ── // ── Helpers ──
let _cachedAccent = '29, 185, 84';
let _accentCheckFrame = 0;
function getAccentRGB() { function getAccentRGB() {
const s = getComputedStyle(document.documentElement).getPropertyValue('--accent-rgb').trim(); // Only re-read CSS variable every 60 frames (~1s) to avoid getComputedStyle overhead
return s || '29, 185, 84'; if (_accentCheckFrame++ % 60 === 0) {
const s = getComputedStyle(document.documentElement).getPropertyValue('--accent-rgb').trim();
if (s) _cachedAccent = s;
}
return _cachedAccent;
} }
// Shift an "r, g, b" accent string by a hue offset (degrees), cached per base color // Shift an "r, g, b" accent string by a hue offset (degrees), cached per base color
@ -2286,8 +2292,9 @@
stop stop
}; };
// Auto-start for initial page // Auto-start for initial page (respect particles toggle)
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (window._particlesEnabled === false) return;
const activePage = document.querySelector('.page.active'); const activePage = document.querySelector('.page.active');
if (activePage) { if (activePage) {
const pageId = activePage.id.replace('-page', ''); const pageId = activePage.id.replace('-page', '');

View file

@ -706,6 +706,23 @@ function applyAccentColor(hex) {
if (swatch) swatch.style.background = hex; if (swatch) swatch.style.background = hex;
} }
function applyParticlesSetting(enabled) {
const canvas = document.getElementById('page-particles-canvas');
if (canvas) canvas.style.display = enabled ? '' : 'none';
if (window.pageParticles) {
if (enabled) {
const activePage = document.querySelector('.page.active');
if (activePage) {
window.pageParticles.setPage(activePage.id.replace('-page', ''));
}
} else {
window.pageParticles.stop();
}
}
window._particlesEnabled = enabled;
localStorage.setItem('soulsync-particles', String(enabled));
}
function initAccentColorListeners() { function initAccentColorListeners() {
const presetSelect = document.getElementById('accent-preset'); const presetSelect = document.getElementById('accent-preset');
const customGroup = document.getElementById('custom-color-group'); const customGroup = document.getElementById('custom-color-group');
@ -728,12 +745,27 @@ function initAccentColorListeners() {
applyAccentColor(customPicker.value); applyAccentColor(customPicker.value);
}); });
} }
// Particles toggle — apply immediately on change
const particlesCheckbox = document.getElementById('particles-enabled');
if (particlesCheckbox) {
particlesCheckbox.addEventListener('change', () => {
applyParticlesSetting(particlesCheckbox.checked);
});
}
} }
// Bootstrap accent from localStorage instantly (prevents default-color flash) // Bootstrap accent from localStorage instantly (prevents default-color flash)
(function() { (function() {
const saved = localStorage.getItem('soulsync-accent'); const saved = localStorage.getItem('soulsync-accent');
if (saved) applyAccentColor(saved); if (saved) applyAccentColor(saved);
// Bootstrap particles setting from localStorage
const particlesSaved = localStorage.getItem('soulsync-particles');
if (particlesSaved === 'false') {
window._particlesEnabled = false;
const canvas = document.getElementById('page-particles-canvas');
if (canvas) canvas.style.display = 'none';
}
})(); })();
// ── Profile System ───────────────────────────────────────────── // ── Profile System ─────────────────────────────────────────────
@ -1819,7 +1851,7 @@ function navigateToPage(pageId) {
loadPageData(pageId); loadPageData(pageId);
// Update page background particles // Update page background particles
if (window.pageParticles) window.pageParticles.setPage(pageId); if (window.pageParticles && window._particlesEnabled !== false) window.pageParticles.setPage(pageId);
} }
// REPLACE your old loadPageData function with this one: // REPLACE your old loadPageData function with this one:
@ -4641,6 +4673,12 @@ async function loadSettingsData() {
if (vizSelect) vizSelect.value = vizType; if (vizSelect) vizSelect.value = vizType;
sidebarVisualizerType = vizType; sidebarVisualizerType = vizType;
// Background particles toggle
const particlesEnabled = settings.ui_appearance?.particles_enabled !== false; // default true
const particlesCheckbox = document.getElementById('particles-enabled');
if (particlesCheckbox) particlesCheckbox.checked = particlesEnabled;
applyParticlesSetting(particlesEnabled);
// Populate Logging information (read-only) // Populate Logging information (read-only)
document.getElementById('log-level-display').textContent = settings.logging?.level || 'INFO'; document.getElementById('log-level-display').textContent = settings.logging?.level || 'INFO';
document.getElementById('log-path-display').textContent = settings.logging?.path || 'logs/app.log'; document.getElementById('log-path-display').textContent = settings.logging?.path || 'logs/app.log';
@ -5383,7 +5421,8 @@ async function saveSettings(quiet = false) {
ui_appearance: { ui_appearance: {
accent_preset: document.getElementById('accent-preset')?.value || '#1db954', accent_preset: document.getElementById('accent-preset')?.value || '#1db954',
accent_color: document.getElementById('accent-custom-color')?.value || '#1db954', accent_color: document.getElementById('accent-custom-color')?.value || '#1db954',
sidebar_visualizer: document.getElementById('sidebar-visualizer-type')?.value || 'bars' sidebar_visualizer: document.getElementById('sidebar-visualizer-type')?.value || 'bars',
particles_enabled: document.getElementById('particles-enabled')?.checked !== false
}, },
youtube: { youtube: {
cookies_browser: document.getElementById('youtube-cookies-browser').value, cookies_browser: document.getElementById('youtube-cookies-browser').value,