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.
This commit is contained in:
parent
1e514693f1
commit
ab2b5c64f4
3 changed files with 76 additions and 6 deletions
|
|
@ -8066,6 +8066,9 @@
|
||||||
<div class="artist-name" id="artist-name">Unknown Artist</div>
|
<div class="artist-name" id="artist-name">Unknown Artist</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mini-player-controls">
|
<div class="mini-player-controls">
|
||||||
|
<button class="mini-toggle-btn" id="mini-shuffle-btn" title="Shuffle">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="13" height="13"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/><line x1="4" y1="4" x2="9" y2="9"/></svg>
|
||||||
|
</button>
|
||||||
<button class="mini-nav-btn" id="mini-prev-btn" title="Previous" disabled>
|
<button class="mini-nav-btn" id="mini-prev-btn" title="Previous" disabled>
|
||||||
<svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg>
|
<svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -8076,6 +8079,10 @@
|
||||||
<button class="mini-nav-btn" id="mini-next-btn" title="Next" disabled>
|
<button class="mini-nav-btn" id="mini-next-btn" title="Next" disabled>
|
||||||
<svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
|
<svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="mini-toggle-btn" id="mini-repeat-btn" title="Repeat">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="13" height="13"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
|
||||||
|
<span class="mini-repeat-one" id="mini-repeat-one-badge" style="display:none">1</span>
|
||||||
|
</button>
|
||||||
<button class="stop-button" id="stop-button" disabled>
|
<button class="stop-button" id="stop-button" disabled>
|
||||||
<svg viewBox="0 0 24 24" fill="currentColor" width="10" height="10"><rect x="6" y="6" width="12" height="12" rx="1.5"/></svg>
|
<svg viewBox="0 0 24 24" fill="currentColor" width="10" height="10"><rect x="6" y="6" width="12" height="12" rx="1.5"/></svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,12 @@ function initializeMediaPlayer() {
|
||||||
if (miniPrevBtn) miniPrevBtn.addEventListener('click', (e) => { e.stopPropagation(); playPreviousInQueue(); });
|
if (miniPrevBtn) miniPrevBtn.addEventListener('click', (e) => { e.stopPropagation(); playPreviousInQueue(); });
|
||||||
if (miniNextBtn) miniNextBtn.addEventListener('click', (e) => { e.stopPropagation(); playNextInQueue(); });
|
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)
|
// Restore a previously-saved queue (does not auto-play)
|
||||||
npRestoreQueue();
|
npRestoreQueue();
|
||||||
}
|
}
|
||||||
|
|
@ -2064,15 +2070,32 @@ function updateNpMuteIcon() {
|
||||||
if (muteBtn) muteBtn.classList.toggle('muted', npMuted);
|
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() {
|
function handleNpShuffle() {
|
||||||
npShuffleOn = !npShuffleOn;
|
npShuffleOn = !npShuffleOn;
|
||||||
const btn = document.getElementById('np-shuffle-btn');
|
syncShuffleRepeatUI();
|
||||||
if (btn) btn.classList.toggle('active', npShuffleOn);
|
|
||||||
updateNpPrevNextButtons();
|
updateNpPrevNextButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNpRepeat() {
|
function handleNpRepeat() {
|
||||||
const badge = document.getElementById('np-repeat-one-badge');
|
|
||||||
if (npRepeatMode === 'off') {
|
if (npRepeatMode === 'off') {
|
||||||
npRepeatMode = 'all';
|
npRepeatMode = 'all';
|
||||||
if (audioPlayer) audioPlayer.loop = false;
|
if (audioPlayer) audioPlayer.loop = false;
|
||||||
|
|
@ -2083,9 +2106,7 @@ function handleNpRepeat() {
|
||||||
npRepeatMode = 'off';
|
npRepeatMode = 'off';
|
||||||
if (audioPlayer) audioPlayer.loop = false;
|
if (audioPlayer) audioPlayer.loop = false;
|
||||||
}
|
}
|
||||||
const btn = document.getElementById('np-repeat-btn');
|
syncShuffleRepeatUI();
|
||||||
if (btn) btn.classList.toggle('active', npRepeatMode !== 'off');
|
|
||||||
if (badge) badge.classList.toggle('hidden', npRepeatMode !== 'one');
|
|
||||||
updateNpPrevNextButtons();
|
updateNpPrevNextButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -693,6 +693,48 @@ body {
|
||||||
cursor: not-allowed;
|
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 */
|
/* Controls cluster in the mini player */
|
||||||
.mini-player-controls {
|
.mini-player-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue