From cde74b596539c9a12e0ef00485e2dbc5510bbd17 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 22 Jul 2026 22:57:23 -0400 Subject: [PATCH] =?UTF-8?q?feat(player):=20web=20queue=20auto-follow=20+?= =?UTF-8?q?=20jump-to-current=20pill=20+=20clear-queue=20=E2=80=94=20#1944?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QueueList now follows the now-playing row as the track auto-advances (only while it's in view), centers it on open, and surfaces a 'Jump to current' pill once the user scrolls it off-screen. Header gains a clear-queue action backed by a new store clearQueue() that empties the queue and stops playback. Co-Authored-By: Claude Opus 4.8 (1M context) --- web/src/lib/components/QueueDrawer.test.ts | 6 +- web/src/lib/components/QueueList.svelte | 117 ++++++++++++++++----- web/src/lib/player/store.svelte.ts | 15 +++ 3 files changed, 107 insertions(+), 31 deletions(-) diff --git a/web/src/lib/components/QueueDrawer.test.ts b/web/src/lib/components/QueueDrawer.test.ts index ec82c755..c9b81cfa 100644 --- a/web/src/lib/components/QueueDrawer.test.ts +++ b/web/src/lib/components/QueueDrawer.test.ts @@ -25,10 +25,12 @@ vi.mock('$lib/player/store.svelte', () => ({ get queueDrawerOpen() { return openValue; } }, // QueueTrackRow imports these from the store; provide stubs so its - // module-load doesn't break when QueueDrawer renders rows. + // module-load doesn't break when QueueDrawer renders rows. QueueList + // imports clearQueue for its header action. playFromQueueIndex: vi.fn(), removeFromQueue: vi.fn(), - moveQueueItem: vi.fn() + moveQueueItem: vi.fn(), + clearQueue: vi.fn() })); import QueueDrawer from './QueueDrawer.svelte'; diff --git a/web/src/lib/components/QueueList.svelte b/web/src/lib/components/QueueList.svelte index 1fb2dd48..cc223183 100644 --- a/web/src/lib/components/QueueList.svelte +++ b/web/src/lib/components/QueueList.svelte @@ -1,7 +1,7 @@ -
+

Queue

@@ -54,20 +87,33 @@ {#if player.queue.length > 0} · {totalDurationLabel(player.queue)}{/if}

- {#if onClose} - - {/if} +
+ {#if player.queue.length > 0} + + {/if} + {#if onClose} + + {/if} +
-
+
{#if player.queue.length === 0}

No tracks queued.

{:else} @@ -76,4 +122,17 @@ {/each} {/if}
+ + {#if active && player.queue.length > 0 && !currentInView} + + {/if}
diff --git a/web/src/lib/player/store.svelte.ts b/web/src/lib/player/store.svelte.ts index 1e41432b..031f8c70 100644 --- a/web/src/lib/player/store.svelte.ts +++ b/web/src/lib/player/store.svelte.ts @@ -505,6 +505,21 @@ export function removeFromQueue(idx: number): void { _error = null; } +// Clear the whole queue and stop playback — mirrors removeFromQueue's +// empty-queue branch. Also drops the radio/system source + self-heal closure +// so the emptied player doesn't try to refill from a now-irrelevant source. +export function clearQueue(): void { + _queue = []; + _index = 0; + _state = 'idle'; + _position = 0; + _duration = 0; + _error = null; + _radioSeedId = null; + _queueSource = null; + _queueRefetch = null; +} + export function playFromQueueIndex(idx: number): void { if (idx < 0 || idx >= _queue.length) return; _radioSeedId = null;