From 34f28dd07749f7a22cb20e9cff1e5b413a98c882 Mon Sep 17 00:00:00 2001 From: kavin553 Date: Tue, 16 Jun 2026 14:20:39 +0530 Subject: [PATCH] feat(theme) : add live theme preview system --- public/css/style.css | 41 ++++++++++++++++ public/index.html | 9 ++++ public/js/app.js | 110 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 150 insertions(+), 10 deletions(-) diff --git a/public/css/style.css b/public/css/style.css index 9feb664..a4f6192 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -841,4 +841,45 @@ input, textarea, select { .toggle-password svg{ transition:.2s; } +.theme-preview-controls { + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + z-index: 9999; + + display: flex; + gap: 12px; + + padding: 12px 16px; + + background: rgba(0,0,0,0.6); + backdrop-filter: blur(12px); + + border: 1px solid rgba(255,255,255,0.1); + border-radius: 999px; +} + +.theme-preview-controls button { + border: none; + padding: 10px 18px; + border-radius: 999px; + cursor: pointer; + + font-weight: 600; + transition: all 0.25s ease; +} +#applyThemeBtn { + background: var(--accent); + color: white; +} + +#cancelThemeBtn { + background: rgba(255,255,255,0.08); + color: var(--text-primary); +} + +.theme-preview-controls button:hover { + transform: translateY(-2px); +} diff --git a/public/index.html b/public/index.html index 9478a5f..fd8480d 100644 --- a/public/index.html +++ b/public/index.html @@ -101,7 +101,16 @@

Loading...

Conn. + +
+ + +
diff --git a/public/js/app.js b/public/js/app.js index 4e56d47..47a3f2c 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -12,9 +12,14 @@ const profileUsername = isPublicProfile ? pathParts[1] : null; const urlParams = new URLSearchParams(window.location.search); const previewTheme = urlParams.get('previewTheme'); - if (previewTheme) { - document.body.className = `theme-${previewTheme}`; - } + +let activeTheme = null; +let originalTheme = null; + +if (previewTheme) { + activeTheme = previewTheme; + document.body.className = `theme-${previewTheme}`; +} function apiUrl(endpoint) { if (isPublicProfile) { @@ -64,6 +69,34 @@ let currentThemeColor = '168, 85, 247'; +// Theme State Management +let activeTheme = 'midnight'; +let previewThemeState = null; +function applyTheme(themeName, isPreview = false) { + if (!themeName) return; + + // Remove existing theme classes + document.body.className = document.body.className + .split(' ') + .filter(cls => !cls.startsWith('theme-')) + .join(' '); + + // Apply new theme + document.body.classList.add(`theme-${themeName}`); + + // Update particle colors + currentThemeColor = + THEME_COLORS[themeName] || '168, 85, 247'; + + // Store preview state + if (isPreview) { + previewThemeState = themeName; + } else { + activeTheme = themeName; + previewThemeState = null; + } +} + // ─── Load Settings & Apply Theme ─── async function loadSettings() { try { @@ -78,8 +111,8 @@ // Apply theme const theme = settings.selectedTheme || 'midnight'; if (!previewTheme) { - document.body.className = `theme-${theme}`; - } + applyTheme(theme); +} currentThemeColor = THEME_COLORS[theme] || '168, 85, 247'; // Update page title @@ -400,12 +433,69 @@ } }); // --- End Scroll Progress Indicator --- + function setupThemePreviewControls() { + const controls = document.getElementById('themePreviewControls'); + const applyBtn = document.getElementById('applyThemeBtn'); + const cancelBtn = document.getElementById('cancelThemeBtn'); + if (!controls) return; + + // Hide controls if no preview theme exists + if (!previewTheme) { + controls.style.display = 'none'; + return; + } + + controls.style.display = 'flex'; + + // APPLY THEME + applyBtn?.addEventListener('click', async () => { + try { + const res = await fetch('/api/settings/theme', { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + selectedTheme: previewTheme + }) + }); + + if (!res.ok) { + throw new Error('Failed to save theme'); + } + + originalTheme = `theme-${previewTheme}`; + + alert('Theme applied successfully!'); + } catch (err) { + console.error(err); + alert('Failed to apply theme'); + } + }); + + // CANCEL PREVIEW + cancelBtn?.addEventListener('click', () => { + document.body.className = originalTheme; + + const cleanUrl = + window.location.pathname; + + window.history.replaceState({}, '', cleanUrl); + + controls.style.display = 'none'; + }); +} // ─── Init ─── document.addEventListener('DOMContentLoaded', async () => { - await loadSettings(); - initParticles(); - renderProfile(); - renderLinks(); - }); + await loadSettings(); + + originalTheme = document.body.className; + + initParticles(); + renderProfile(); + renderLinks(); + + setupThemePreviewControls(); +}); })();