Now Playing: fix squashed stop button + queue persistence + crafted entrance
- Stop button fix: my round .np-btn { width/height 46px; border-radius:50% }
override was also hitting .np-btn-stop (it carries both classes), squashing
the 'Stop' text pill into a tiny circle. Exempted .np-btn.np-btn-stop back to
an auto-width pill.
- Queue persistence: npPersistQueue() (called from renderNpQueue, the single
mutation hook) saves the queue to localStorage; npRestoreQueue() on init
repopulates the panel on reload WITHOUT auto-playing (index reset to -1).
Queue no longer vanishes on refresh.
- Crafted entrance: controls stagger-fade/rise in when the modal opens
(npRiseIn keyframe, delays cascading util->progress->controls->volume->
upnext). Art container excluded so its transform stays free for the
play-scale.
Frontend-only; Boulder verifying live.
This commit is contained in:
parent
ccfb3fb042
commit
a8985b317f
2 changed files with 63 additions and 0 deletions
|
|
@ -55,6 +55,9 @@ function initializeMediaPlayer() {
|
|||
const miniNextBtn = document.getElementById('mini-next-btn');
|
||||
if (miniPrevBtn) miniPrevBtn.addEventListener('click', (e) => { e.stopPropagation(); playPreviousInQueue(); });
|
||||
if (miniNextBtn) miniNextBtn.addEventListener('click', (e) => { e.stopPropagation(); playNextInQueue(); });
|
||||
|
||||
// Restore a previously-saved queue (does not auto-play)
|
||||
npRestoreQueue();
|
||||
}
|
||||
|
||||
function toggleMediaPlayerExpansion() {
|
||||
|
|
@ -2283,6 +2286,38 @@ function renderNpQueue() {
|
|||
});
|
||||
|
||||
npUpdateUpNext();
|
||||
npPersistQueue();
|
||||
}
|
||||
|
||||
// ── Queue persistence across page reloads (localStorage) ──
|
||||
const NP_QUEUE_STORAGE_KEY = 'soulsync-np-queue';
|
||||
|
||||
function npPersistQueue() {
|
||||
try {
|
||||
if (!npQueue.length) { localStorage.removeItem(NP_QUEUE_STORAGE_KEY); return; }
|
||||
localStorage.setItem(NP_QUEUE_STORAGE_KEY, JSON.stringify({
|
||||
queue: npQueue,
|
||||
index: npQueueIndex,
|
||||
savedAt: Date.now(),
|
||||
}));
|
||||
} catch (e) { /* quota / disabled storage — non-fatal */ }
|
||||
}
|
||||
|
||||
// Restore the saved queue into the panel WITHOUT auto-playing (the user
|
||||
// reloaded; resume playback is their choice via clicking a row).
|
||||
function npRestoreQueue() {
|
||||
try {
|
||||
const raw = localStorage.getItem(NP_QUEUE_STORAGE_KEY);
|
||||
if (!raw) return;
|
||||
const data = JSON.parse(raw);
|
||||
if (data && Array.isArray(data.queue) && data.queue.length) {
|
||||
npQueue = data.queue;
|
||||
// Don't claim a track is "playing" on a fresh load — nothing is.
|
||||
npQueueIndex = -1;
|
||||
renderNpQueue();
|
||||
updateNpPrevNextButtons();
|
||||
}
|
||||
} catch (e) { /* corrupt entry — ignore */ }
|
||||
}
|
||||
|
||||
// ── Queue drag-to-reorder ──
|
||||
|
|
|
|||
|
|
@ -48673,6 +48673,22 @@ textarea.enhanced-meta-field-input {
|
|||
.np-btn-shuffle.active, .np-btn-repeat.active { color: rgb(var(--accent-light-rgb)) !important; }
|
||||
.np-btn-shuffle.active::after, .np-btn-repeat.active::after { background: rgb(var(--accent-light-rgb)) !important; }
|
||||
|
||||
/* Stop is a text pill, NOT a circular transport button — exempt it from the
|
||||
round .np-btn sizing above (which was squashing "Stop" into a tiny circle). */
|
||||
.np-btn.np-btn-stop {
|
||||
width: auto !important; height: auto !important;
|
||||
border-radius: 999px !important;
|
||||
padding: 8px 20px !important;
|
||||
background: rgba(255,255,255,0.04) !important;
|
||||
border: 1px solid rgba(255,255,255,0.08) !important;
|
||||
}
|
||||
.np-btn.np-btn-stop:hover {
|
||||
background: rgba(255,60,60,0.1) !important;
|
||||
border-color: rgba(255,60,60,0.25) !important;
|
||||
color: rgba(255,90,90,0.9) !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
/* ── Visualizer bars: accent gradient ── */
|
||||
.np-viz-bar {
|
||||
background: linear-gradient(180deg, rgb(var(--accent-light-rgb)), rgba(var(--accent-rgb),0.35)) !important;
|
||||
|
|
@ -48690,6 +48706,18 @@ textarea.enhanced-meta-field-input {
|
|||
.np-close-btn:hover { color: #fff !important; background: rgba(255,255,255,0.1) !important; }
|
||||
.np-lyrics-line.active { color: rgb(var(--accent-light-rgb)) !important; font-weight: 700 !important; }
|
||||
|
||||
/* ── Crafted entrance: controls stagger-fade in when modal opens ──
|
||||
(Art container is intentionally excluded — its transform is reserved for the
|
||||
play-scale; it gets its own scale-in via the box transition.) */
|
||||
.np-modal-overlay:not(.hidden) .np-track-info { animation: npRiseIn 0.55s cubic-bezier(0.16,1,0.3,1) 0.06s both; }
|
||||
.np-modal-overlay:not(.hidden) .np-right > * { animation: npRiseIn 0.5s cubic-bezier(0.16,1,0.3,1) both; }
|
||||
.np-modal-overlay:not(.hidden) .np-util-row { animation-delay: 0.08s; }
|
||||
.np-modal-overlay:not(.hidden) .np-progress-section { animation-delay: 0.12s; }
|
||||
.np-modal-overlay:not(.hidden) .np-controls-row { animation-delay: 0.16s; }
|
||||
.np-modal-overlay:not(.hidden) .np-volume-row { animation-delay: 0.20s; }
|
||||
.np-modal-overlay:not(.hidden) .np-upnext { animation-delay: 0.24s; }
|
||||
@keyframes npRiseIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
/* ── Click-art → music-synced visualizer takeover (Plexamp-style) ── */
|
||||
.np-album-art-container { cursor: pointer; }
|
||||
.np-art-hint {
|
||||
|
|
|
|||
Loading…
Reference in a new issue