From 112ecbb24f45565e12e44006fcd2f9057d2508eb Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Sat, 30 May 2026 15:15:58 -0700 Subject: [PATCH] Player: seek hover tooltip on the Now Playing progress bar The mockup had a seek tooltip (timestamp tracks the cursor over the progress bar) but it was never ported to the real player. Added it: mousemove computes the hovered fraction -> formatTime(duration*frac), positions the tip, shows on hover / hides on leave. Guarded when no duration. Frontend-only; JS + CSS clean. --- webui/index.html | 1 + webui/static/media-player.js | 17 +++++++++++++++++ webui/static/style.css | 10 ++++++++++ 3 files changed, 28 insertions(+) diff --git a/webui/index.html b/webui/index.html index ca63d727..92a982bd 100644 --- a/webui/index.html +++ b/webui/index.html @@ -7068,6 +7068,7 @@
+
0:00
0:00 diff --git a/webui/static/media-player.js b/webui/static/media-player.js index 8b488992..0eb2ee3e 100644 --- a/webui/static/media-player.js +++ b/webui/static/media-player.js @@ -1537,6 +1537,23 @@ function initExpandedPlayer() { npProgressBar.addEventListener('mousedown', () => { npProgressBar.dataset.seeking = 'true'; }); npProgressBar.addEventListener('mouseup', () => { delete npProgressBar.dataset.seeking; }); + // Seek hover tooltip — shows the timestamp the cursor is over. + const npSeekTip = document.getElementById('np-seek-tip'); + if (npSeekTip) { + npProgressBar.addEventListener('mousemove', (e) => { + if (!audioPlayer || !isFinite(audioPlayer.duration) || audioPlayer.duration <= 0) { + npSeekTip.classList.remove('visible'); + return; + } + const rect = npProgressBar.getBoundingClientRect(); + const frac = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)); + npSeekTip.textContent = formatTime(frac * audioPlayer.duration); + npSeekTip.style.left = (frac * 100) + '%'; + npSeekTip.classList.add('visible'); + }); + npProgressBar.addEventListener('mouseleave', () => npSeekTip.classList.remove('visible')); + } + // Progress bar (touch) npProgressBar.addEventListener('touchstart', () => { npProgressBar.dataset.seeking = 'true'; }, { passive: true }); npProgressBar.addEventListener('touchmove', (e) => { diff --git a/webui/static/style.css b/webui/static/style.css index 020a9264..8eef8d93 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -48705,6 +48705,16 @@ textarea.enhanced-meta-field-input { .np-progress-bar-container:hover .np-progress-fill { box-shadow: 0 0 12px rgba(var(--accent-rgb),0.6) !important; } .np-time-display { font-variant-numeric: tabular-nums !important; } +/* Seek hover tooltip — timestamp under the cursor on the progress bar. */ +.np-progress-bar-container { position: relative; } +.np-seek-tip { + position: absolute; bottom: 150%; transform: translateX(-50%); + background: #000; color: #fff; font-size: 11px; font-variant-numeric: tabular-nums; + padding: 3px 7px; border-radius: 6px; white-space: nowrap; pointer-events: none; + opacity: 0; transition: opacity 0.12s ease; box-shadow: 0 4px 12px rgba(0,0,0,0.5); z-index: 5; +} +.np-seek-tip.visible { opacity: 1; } + /* ── Transport: dominant gradient play button ── */ .np-controls-row { gap: 24px !important; } .np-btn {