From 15a545a25cfe68b4642dd00cdaac4fd306344a32 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 1 Jun 2026 23:24:06 -0400 Subject: [PATCH] feat(web): two-pane now-playing on desktop with permanent queue panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The full player previously sat dead-center on wide viewports — cover + controls capped at max-w-md left the right two-thirds of the screen empty while the queue remained behind a drawer toggle. Split the layout at lg+: - Extract QueueList from QueueDrawer (header + count + scrollable list body + empty state); the drawer now wraps QueueList for its slide-in chrome, and the now-playing route embeds it directly. - now-playing: at lg+ render the player as a left section + a 384px (xl: 448px) aside with QueueList. Below lg the layout is unchanged single-column and the drawer toggle in the bottom row still opens it (lg:hidden on the toggle button keeps it out of the way once the panel is permanent). - QueueList owns the close X conditionally — drawer passes onClose and the bind:closeButtonRef for focus management; embedded panel omits both since it has no dismiss action. QueueTrackRow's drag-to-reorder, click-to-jump, and current-row highlight all carry over for free since they're owned by the row component. --- web/src/lib/components/QueueDrawer.svelte | 41 ++-------------- web/src/lib/components/QueueList.svelte | 57 +++++++++++++++++++++++ web/src/routes/now-playing/+page.svelte | 19 +++++++- 3 files changed, 77 insertions(+), 40 deletions(-) create mode 100644 web/src/lib/components/QueueList.svelte diff --git a/web/src/lib/components/QueueDrawer.svelte b/web/src/lib/components/QueueDrawer.svelte index 924b3f26..075c52f4 100644 --- a/web/src/lib/components/QueueDrawer.svelte +++ b/web/src/lib/components/QueueDrawer.svelte @@ -1,18 +1,10 @@ + +
+
+
+

Queue

+

+ {player.queue.length} {player.queue.length === 1 ? 'track' : 'tracks'} + {#if player.queue.length > 0} · {totalDurationLabel(player.queue)}{/if} +

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

No tracks queued.

+ {:else} + {#each player.queue as track, i (track.id)} + + {/each} + {/if} +
+
diff --git a/web/src/routes/now-playing/+page.svelte b/web/src/routes/now-playing/+page.svelte index ad963b81..03aa9841 100644 --- a/web/src/routes/now-playing/+page.svelte +++ b/web/src/routes/now-playing/+page.svelte @@ -16,6 +16,7 @@ import { FALLBACK_COVER, coverUrl } from '$lib/media/covers'; import { useSmoothPosition } from '$lib/player/smoothPosition.svelte'; import LikeButton from '$lib/components/LikeButton.svelte'; + import QueueList from '$lib/components/QueueList.svelte'; import { pageTitle } from '$lib/branding'; const current = $derived(player.current); @@ -89,7 +90,13 @@

{:else} -
+ +
+
{player.queue.length}
+
+
{/if}