From ab2b5c64f406b4c3f8a9be5191c335c0b761f907 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Sat, 30 May 2026 14:57:04 -0700 Subject: [PATCH] Player: shuffle + repeat on the mini-player (parity with modal) The sidebar mini-player had prev/play/next/stop/expand but not the two set-and-forget controls you reach for without opening the full view. Added shuffle + repeat (3-mode, with a repeat-one badge) to the mini-controls. State stays in sync both ways: handleNpShuffle/handleNpRepeat now call a shared syncShuffleRepeatUI() that reflects state onto BOTH the modal and mini buttons, so toggling in either place updates the other. Mini buttons reuse the same handlers. Accent-active styling via --accent-light-rgb. JS clean; CSS balance consistent with HEAD. --- webui/index.html | 7 ++++++ webui/static/media-player.js | 33 ++++++++++++++++++++++------ webui/static/style.css | 42 ++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/webui/index.html b/webui/index.html index aa34bef0..bdef4cfa 100644 --- a/webui/index.html +++ b/webui/index.html @@ -8066,6 +8066,9 @@
Unknown Artist
+ @@ -8076,6 +8079,10 @@ + diff --git a/webui/static/media-player.js b/webui/static/media-player.js index bb3ccc8d..23072eab 100644 --- a/webui/static/media-player.js +++ b/webui/static/media-player.js @@ -61,6 +61,12 @@ function initializeMediaPlayer() { if (miniPrevBtn) miniPrevBtn.addEventListener('click', (e) => { e.stopPropagation(); playPreviousInQueue(); }); if (miniNextBtn) miniNextBtn.addEventListener('click', (e) => { e.stopPropagation(); playNextInQueue(); }); + // Mini shuffle / repeat — share the modal handlers (which now sync both UIs) + const miniShuffleBtn = document.getElementById('mini-shuffle-btn'); + const miniRepeatBtn = document.getElementById('mini-repeat-btn'); + if (miniShuffleBtn) miniShuffleBtn.addEventListener('click', (e) => { e.stopPropagation(); handleNpShuffle(); }); + if (miniRepeatBtn) miniRepeatBtn.addEventListener('click', (e) => { e.stopPropagation(); handleNpRepeat(); }); + // Restore a previously-saved queue (does not auto-play) npRestoreQueue(); } @@ -2064,15 +2070,32 @@ function updateNpMuteIcon() { if (muteBtn) muteBtn.classList.toggle('muted', npMuted); } +// Reflect shuffle/repeat state on BOTH the modal and mini-player buttons. +function syncShuffleRepeatUI() { + const npShuffle = document.getElementById('np-shuffle-btn'); + const miniShuffle = document.getElementById('mini-shuffle-btn'); + if (npShuffle) npShuffle.classList.toggle('active', npShuffleOn); + if (miniShuffle) miniShuffle.classList.toggle('active', npShuffleOn); + + const repeatOn = npRepeatMode !== 'off'; + const repeatOne = npRepeatMode === 'one'; + const npRepeat = document.getElementById('np-repeat-btn'); + const miniRepeat = document.getElementById('mini-repeat-btn'); + if (npRepeat) npRepeat.classList.toggle('active', repeatOn); + if (miniRepeat) miniRepeat.classList.toggle('active', repeatOn); + const npBadge = document.getElementById('np-repeat-one-badge'); + const miniBadge = document.getElementById('mini-repeat-one-badge'); + if (npBadge) npBadge.classList.toggle('hidden', !repeatOne); + if (miniBadge) miniBadge.style.display = repeatOne ? '' : 'none'; +} + function handleNpShuffle() { npShuffleOn = !npShuffleOn; - const btn = document.getElementById('np-shuffle-btn'); - if (btn) btn.classList.toggle('active', npShuffleOn); + syncShuffleRepeatUI(); updateNpPrevNextButtons(); } function handleNpRepeat() { - const badge = document.getElementById('np-repeat-one-badge'); if (npRepeatMode === 'off') { npRepeatMode = 'all'; if (audioPlayer) audioPlayer.loop = false; @@ -2083,9 +2106,7 @@ function handleNpRepeat() { npRepeatMode = 'off'; if (audioPlayer) audioPlayer.loop = false; } - const btn = document.getElementById('np-repeat-btn'); - if (btn) btn.classList.toggle('active', npRepeatMode !== 'off'); - if (badge) badge.classList.toggle('hidden', npRepeatMode !== 'one'); + syncShuffleRepeatUI(); updateNpPrevNextButtons(); } diff --git a/webui/static/style.css b/webui/static/style.css index e5d7a6b3..7ee3be99 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -693,6 +693,48 @@ body { cursor: not-allowed; } +/* Mini shuffle / repeat toggle buttons (parity with the modal). Smaller than + nav buttons; accent when active; repeat-one shows a tiny badge. */ +.mini-toggle-btn { + position: relative; + width: 22px; + height: 22px; + border-radius: 50%; + background: transparent; + border: none; + color: rgba(255, 255, 255, 0.38); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.18s ease, background 0.18s ease; + padding: 0; + flex-shrink: 0; +} +.mini-toggle-btn:hover { + color: rgba(255, 255, 255, 0.85); + background: rgba(255, 255, 255, 0.08); +} +.mini-toggle-btn.active { + color: rgb(var(--accent-light-rgb)); +} +.mini-repeat-one { + position: absolute; + bottom: -1px; + right: -1px; + font-size: 8px; + font-weight: 800; + line-height: 1; + background: rgb(var(--accent-light-rgb)); + color: #06140b; + border-radius: 50%; + width: 10px; + height: 10px; + display: flex; + align-items: center; + justify-content: center; +} + /* Controls cluster in the mini player */ .mini-player-controls { display: flex;