From d9bd5c1d7f31c2a548ecbb650423997aeb2ef8fa Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Mon, 29 Jun 2026 16:48:34 -0700 Subject: [PATCH] =?UTF-8?q?discovery:=20elevate=20the=20wave=20dial=20?= =?UTF-8?q?=E2=80=94=20full=20width=20+=20glow,=20luminous=20fill,=20pulsi?= =?UTF-8?q?ng=20orb,=20colour=20aura?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webui/index.html | 10 ++++++++- webui/static/discover.js | 35 ++++++++++++++++++++++--------- webui/static/style.css | 45 +++++++++++++++++++++++++++------------- 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/webui/index.html b/webui/index.html index 78d90660..53cc9fc8 100644 --- a/webui/index.html +++ b/webui/index.html @@ -3156,8 +3156,16 @@ Balanced
+
diff --git a/webui/static/discover.js b/webui/static/discover.js index 6c8d7107..6d745212 100644 --- a/webui/static/discover.js +++ b/webui/static/discover.js @@ -36,9 +36,10 @@ function _advState(v) { return 'Deep cuts only'; } // green (120°) → red (0°) through the warm spectrum (yellow, orange) as the orb moves right. -function _advColor(v, light) { +function _advColor(v, light, alpha) { const hue = 120 * (1 - Math.max(0, Math.min(1, v))); - return `hsl(${hue.toFixed(0)}, 85%, ${light || 55}%)`; + const l = light || 55; + return alpha != null ? `hsla(${hue.toFixed(0)}, 85%, ${l}%, ${alpha})` : `hsl(${hue.toFixed(0)}, 85%, ${l}%)`; } // Wave height (viewBox units, centre 40) at position u (0..1) for adventurousness v. function _advWaveY(u, v) { @@ -57,14 +58,16 @@ function _advDraw() { if (track.offsetParent !== null) { // skip the work while the Discover page is hidden const v = _advWave.value; _advWave.phase += 0.03 + v * 0.07; // waves faster the more adventurous - let d = ''; + let line = ''; const N = 90; for (let i = 0; i <= N; i++) { const u = i / N; - d += (i === 0 ? 'M ' : ' L ') + (u * 1000).toFixed(1) + ' ' + _advWaveY(u, v).toFixed(1); + line += (i === 0 ? 'M ' : ' L ') + (u * 1000).toFixed(1) + ' ' + _advWaveY(u, v).toFixed(1); } - path.setAttribute('d', d); - path.setAttribute('stroke', _advColor(v)); + path.setAttribute('d', line); + // the luminous filled area = the wave closed down to the baseline + const area = document.getElementById('adv-wave-area'); + if (area) area.setAttribute('d', line + ' L 1000 80 L 0 80 Z'); const orb = document.getElementById('adv-wave-orb'); if (orb) orb.style.top = (_advWaveY(v, v) / 80 * 100).toFixed(2) + '%'; // orb rides the wave } @@ -73,15 +76,27 @@ function _advDraw() { function _advApply(v) { v = Math.max(0, Math.min(1, v)); _advWave.value = v; + const c = _advColor(v, 55); + const cBright = _advColor(v, 62); + // Line stroke + its colour glow (set on change, not every frame, so the rAF stays cheap). + const path = document.getElementById('adv-wave-path'); + if (path) { path.setAttribute('stroke', c); path.style.filter = `drop-shadow(0 0 7px ${c})`; } + const fillTop = document.getElementById('adv-wave-fill-top'); + if (fillTop) fillTop.setAttribute('stop-color', c); // luminous filled area const orb = document.getElementById('adv-wave-orb'); if (orb) { orb.style.left = (v * 100).toFixed(2) + '%'; - const c = _advColor(v, 58); - orb.style.background = c; - orb.style.boxShadow = `0 0 18px 2px ${c}, 0 0 0 5px rgba(255,255,255,0.08), inset 0 0 0 2px rgba(255,255,255,0.55)`; + orb.style.color = c; // currentColor for the pulsing ring + orb.style.background = cBright; + orb.style.boxShadow = `0 0 22px 3px ${c}, 0 0 0 6px rgba(255,255,255,0.08), inset 0 0 0 2px rgba(255,255,255,0.6)`; + } + const aura = document.getElementById('adv-wave-aura'); // colour wash that follows the orb + if (aura) { + aura.style.left = (v * 100).toFixed(2) + '%'; + aura.style.background = `radial-gradient(circle, ${_advColor(v, 50, 0.42)} 0%, transparent 68%)`; } const stateEl = document.getElementById('adv-wave-state'); - if (stateEl) { stateEl.textContent = _advState(v); stateEl.style.color = _advColor(v, 62); } + if (stateEl) { stateEl.textContent = _advState(v); stateEl.style.color = cBright; } } let _advCommitTimer = null; function _advCommit(v) { diff --git a/webui/static/style.css b/webui/static/style.css index 91017d4d..59ea721c 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -35168,35 +35168,52 @@ div.artist-hero-badge { and wilder + redder at the right (obscure). The path + colours are drawn each frame in JS (rAF). Syncs with Settings → Discovery. */ .adv-wave { - max-width: 880px; - margin: 0 0 26px; - padding: 16px 22px 14px; - border-radius: 16px; - background: linear-gradient(135deg, rgba(255,255,255,0.035), rgba(255,255,255,0.01)); + position: relative; + margin: 0 0 28px; + padding: 18px 26px 16px; + border-radius: 18px; + background: linear-gradient(135deg, rgba(255,255,255,0.04), rgba(255,255,255,0.012)); border: 1px solid rgba(255,255,255,0.07); - box-shadow: 0 8px 28px rgba(0,0,0,0.25); + box-shadow: 0 10px 34px rgba(0,0,0,0.30); + overflow: hidden; /* contain the colour aura wash */ } -.adv-wave-head { display: flex; align-items: baseline; gap: 12px; } -.adv-wave-title { font-size: 14px; font-weight: 700; color: #fff; } +.adv-wave-head { display: flex; align-items: baseline; gap: 12px; position: relative; z-index: 2; } +.adv-wave-title { font-size: 15px; font-weight: 800; color: #fff; letter-spacing: 0.2px; } .adv-wave-state { font-size: 12px; font-weight: 700; transition: color 0.25s; } .adv-wave-track { - position: relative; height: 84px; margin: 4px 0 2px; + position: relative; height: 104px; margin: 6px 0 2px; cursor: grab; touch-action: none; } .adv-wave-track:active { cursor: grabbing; } -.adv-wave-svg { width: 100%; height: 100%; display: block; overflow: visible; } -.adv-wave-orb { +.adv-wave-aura { position: absolute; top: 50%; left: 30%; - width: 20px; height: 20px; border-radius: 50%; + width: 360px; height: 230px; transform: translate(-50%, -50%); + pointer-events: none; filter: blur(14px); z-index: 0; + will-change: left, background; +} +.adv-wave-svg { width: 100%; height: 100%; display: block; overflow: visible; position: relative; z-index: 1; } +.adv-wave-orb { + position: absolute; top: 50%; left: 30%; z-index: 2; + width: 22px; height: 22px; border-radius: 50%; transform: translate(-50%, -50%); - background: #1DB954; - box-shadow: 0 0 18px 2px #1DB954, 0 0 0 5px rgba(255,255,255,0.08), inset 0 0 0 2px rgba(255,255,255,0.55); + background: #1DB954; color: #1DB954; + box-shadow: 0 0 20px 3px #1DB954, 0 0 0 6px rgba(255,255,255,0.08), inset 0 0 0 2px rgba(255,255,255,0.6); pointer-events: none; /* the track captures the pointer; the orb is purely visual */ will-change: left, top; } +.adv-wave-orb::after { + content: ''; position: absolute; inset: -7px; border-radius: 50%; + border: 2px solid currentColor; opacity: 0.5; + animation: adv-orb-pulse 2.4s ease-out infinite; +} +@keyframes adv-orb-pulse { + 0% { transform: scale(0.7); opacity: 0.55; } + 100% { transform: scale(1.95); opacity: 0; } +} .adv-wave-ends { display: flex; justify-content: space-between; font-size: 11px; font-weight: 500; color: rgba(255,255,255,0.35); + position: relative; z-index: 2; } /* Watchlist toggle on recommended-artist .ya-cards — corner pill, appears on hover,