From d8ebc1b8c035b1f7ddbd1d5a0948580af1b58a9f Mon Sep 17 00:00:00 2001 From: Jan Svabenik Date: Tue, 26 May 2026 21:51:25 +0200 Subject: [PATCH] feat: click canvas to cycle viz / elapsed / remaining Three modes on canvas click: viz -> spectrum analyser (default) actual -> hh:mm:ss elapsed in white, ELAPSED label remaining -> -hh:mm:ss countdown in accent red, REMAINING label Co-Authored-By: Claude Sonnet 4.6 --- web/static/app.js | 51 +++++++++++++++++++++++++++++++++++++++++--- web/static/style.css | 1 + 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/web/static/app.js b/web/static/app.js index e9c46d6..a45b244 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -35,6 +35,12 @@ let rafId = null; let lastVizAt = 0; // timestamp of last received viz frame let winampPlaying = false; +// Canvas display mode — cycles on click: viz → actual → remaining → viz +const VIZ_MODES = ['viz', 'actual', 'remaining']; +let vizMode = 'viz'; +let currentPosition = 0; +let currentLength = 0; + // ── WebSocket ───────────────────────────────────────────────────────────────── function connect() { const proto = location.protocol === 'https:' ? 'wss' : 'ws'; @@ -94,6 +100,8 @@ function applyStatus(st) { } if (st.length > 0) { + currentPosition = st.position; + currentLength = st.length; progressFill.style.width = (st.position / st.length * 100).toFixed(1) + '%'; timeCurrent.textContent = fmtTime(st.position); timeLength.textContent = '-' + fmtTime(st.length - st.position); @@ -202,6 +210,10 @@ $('btn-close-killist').addEventListener('click', () => killistPanel.classList.add('hidden')); // ── Visualisation (Canvas) ──────────────────────────────────────────────────── +canvas.addEventListener('click', () => { + vizMode = VIZ_MODES[(VIZ_MODES.indexOf(vizMode) + 1) % VIZ_MODES.length]; +}); + function applyViz(bars) { if (!bars || bars.length === 0) return; lastBars = new Float32Array(bars); @@ -226,13 +238,37 @@ function renderFrame(ts = 0) { const h = cssH; if (w === 0 || h === 0) return; - // Check if real viz data is fresh (< 1.5 s old) and Winamp is playing. - const hasSignal = winampPlaying && (performance.now() - lastVizAt) < 1500; - // Background ctx2d.fillStyle = '#000'; ctx2d.fillRect(0, 0, w, h); + // ── Time display modes ────────────────────────────────────────────────────── + if (vizMode === 'actual' || vizMode === 'remaining') { + const isActual = vizMode === 'actual'; + const secs = isActual ? currentPosition : Math.max(0, currentLength - currentPosition); + const prefix = isActual ? '' : '-'; + const timeStr = prefix + fmtTimeLong(secs); + const label = isActual ? 'ELAPSED' : 'REMAINING'; + + ctx2d.textAlign = 'center'; + ctx2d.textBaseline = 'middle'; + + // Large time + ctx2d.font = `bold ${Math.round(h * 0.52)}px monospace`; + ctx2d.fillStyle = isActual ? '#e0e0e0' : '#e94560'; + ctx2d.fillText(timeStr, w / 2, h * 0.48); + + // Small label below + ctx2d.font = `${Math.round(h * 0.18)}px monospace`; + ctx2d.fillStyle = '#444'; + ctx2d.fillText(label, w / 2, h * 0.82); + return; + } + + // ── Spectrum mode ─────────────────────────────────────────────────────────── + // Check if real viz data is fresh (< 1.5 s old) and Winamp is playing. + const hasSignal = winampPlaying && (performance.now() - lastVizAt) < 1500; + const n = NUM_BARS; const gap = 1; const barW = Math.max(1, (w - gap * (n - 1)) / n); @@ -296,6 +332,15 @@ function fmtTime(secs) { return `${m}:${s}`; } +// Like fmtTime but always zero-pads to hh:mm:ss for the canvas display. +function fmtTimeLong(secs) { + secs = Math.floor(secs); + const h = Math.floor(secs / 3600); + const m = Math.floor((secs % 3600) / 60); + const s = secs % 60; + return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`; +} + function parseTime(str) { const [m, s] = (str || '0:00').split(':').map(Number); return m * 60 + (s || 0); diff --git a/web/static/style.css b/web/static/style.css index 89d2d1b..c3862a4 100644 --- a/web/static/style.css +++ b/web/static/style.css @@ -69,6 +69,7 @@ html, body { border-radius: var(--radius); background: #000; display: block; + cursor: pointer; } /* Progress */