From a27d453b13bb1a4e83b53d60318ad2671a04b9a9 Mon Sep 17 00:00:00 2001 From: AE <98699388+A-EDev@users.noreply.github.com> Date: Sun, 25 Oct 2026 21:49:54 +0200 Subject: [PATCH 1/2] feat(music): center the immersive cover when the side pane is hidden --- src/components/music/AmbientBackdrop.tsx | 59 +++++++++++++++--------- src/components/music/MusicOverlay.tsx | 4 +- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/components/music/AmbientBackdrop.tsx b/src/components/music/AmbientBackdrop.tsx index eddcd153..bbfb6cc2 100644 --- a/src/components/music/AmbientBackdrop.tsx +++ b/src/components/music/AmbientBackdrop.tsx @@ -1,4 +1,5 @@ import type { CSSProperties } from "react"; +import { AnimatePresence, motion } from "framer-motion"; import { IS_LINUX_RUNTIME } from "../../lib/platform"; import type { PlayerBackgroundStyle } from "../../lib/playerColors"; @@ -45,25 +46,33 @@ const ARTWORK_OPACITY: Partial> = { immersive: "opacity-[0.85]", }; +/** Where immersive's cover sits: across the top, on the start side, or behind a centred song. */ +type ImmersiveArtworkPlacement = "top" | "start" | "center"; + /** * Immersive's full-bleed cover (Android issue #954): it holds the top of the - * player, or the start side of a wide one, then melts into its own blurred - * continuation underneath. + * player, the start side of a wide one beside Up next or the lyrics, or the + * middle of a wide one with the side pane hidden, then melts into its own + * blurred continuation underneath. */ -const IMMERSIVE_MASK = { - top: "linear-gradient(to bottom, black 42%, transparent 85%)", - start: "linear-gradient(to right, black 50%, transparent 100%)", -} as const; +const IMMERSIVE_COVER: Record = { + top: { className: "left-0 w-full object-top", mask: "linear-gradient(to bottom, black 42%, transparent 85%)" }, + start: { className: "left-0 w-[62%] object-center", mask: "linear-gradient(to right, black 50%, transparent 100%)" }, + center: { + className: "left-[19%] w-[62%] object-center", + mask: "radial-gradient(ellipse 50% 50% at 50% 50%, black 45%, transparent 100%)", + }, +}; interface AmbientBackdropProps { /** The cover as the artwork card loads it, so both share one cached copy. */ imageSrc?: string | null; style?: PlayerBackgroundStyle; - /** Wide layouts put the immersive cover on the start side, where the controls are not. */ - artworkAtStart?: boolean; + /** Wide layouts put the immersive cover beside the side pane, or behind the song when it is hidden. */ + artworkPlacement?: ImmersiveArtworkPlacement; } -export function AmbientBackdrop({ imageSrc, style = "blur_gradient", artworkAtStart = false }: AmbientBackdropProps) { +export function AmbientBackdrop({ imageSrc, style = "blur_gradient", artworkPlacement = "top" }: AmbientBackdropProps) { const artworkOpacity = ARTWORK_OPACITY[style]; const wash = washFor(style); @@ -77,18 +86,26 @@ export function AmbientBackdrop({ imageSrc, style = "blur_gradient", artworkAtSt className={`absolute inset-0 h-full w-full object-cover ${artworkOpacity}`} /> )} - {style === "immersive" && imageSrc && ( - - )} + {/* The cover crossfades to its new place as the side pane opens or closes. */} + + {style === "immersive" && imageSrc && ( + + )} + {wash &&
}
); diff --git a/src/components/music/MusicOverlay.tsx b/src/components/music/MusicOverlay.tsx index a9084d85..3f4ea4fb 100644 --- a/src/components/music/MusicOverlay.tsx +++ b/src/components/music/MusicOverlay.tsx @@ -133,6 +133,8 @@ export function MusicOverlay() { const fullBleedLyrics = isLyrics && (!wide || lyricsExpanded); const sideTab: SidePaneTab = isLyrics ? "lyrics" : "queue"; const showPane = wide && paneVisible; + // Immersive's cover sits beside the side pane, and moves behind the song once the pane is hidden. + const artworkPlacement = !wide ? "top" : showPane && !fullBleedLyrics ? "start" : "center"; // Asking for the queue or the lyrics from elsewhere (a key, the dock) brings a hidden pane back. // It follows the view only, so hiding the pane is never undone by the pane's own state. @@ -197,7 +199,7 @@ export function MusicOverlay() { style={{ opacity: innerOpacity, scale: innerScale, transformOrigin: "50% 100%" }} className="relative flex min-h-0 flex-1 flex-col" > - + {/* TOP BAR */}
From 0df02d17ac01a814c857c520c18278e624b746bf Mon Sep 17 00:00:00 2001 From: AE <98699388+A-EDev@users.noreply.github.com> Date: Sun, 25 Oct 2026 21:49:54 +0200 Subject: [PATCH 2/2] docs(changelog): note the centered immersive cover --- changelog/v0.1.0-beta3.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/v0.1.0-beta3.txt b/changelog/v0.1.0-beta3.txt index 5e13bab9..8d869e04 100644 --- a/changelog/v0.1.0-beta3.txt +++ b/changelog/v0.1.0-beta3.txt @@ -32,7 +32,7 @@ MUSIC - The full player answers the same keys as Flow for Android: Space or K to play and pause, J, L and the arrow keys to seek, Up and Down for volume, M to mute, Shift+N and Shift+P to skip, 0 to 9 to jump through the song, S, R and Q for shuffle, repeat and the queue. Escape closes an open menu before the player. - The full player has its own volume button, the mouse wheel changes volume over either volume button, and Shift with the wheel seeks over the seek bar. Hovering Next shows the song that plays next, and the top shows what you are playing from. - The song menu in the player adds Start radio, Equalizer, Not interested and Don't recommend this artist. -- New Immersive Artwork background for the full player: the cover fills the top, or the left side of a wide window, and fades into the player. The blurred backgrounds now load the cover through the same copy as the artwork, so they no longer stay black when the cover is slow. +- New Immersive Artwork background for the full player: the cover fills the top, the left side of a wide window beside Up next or the lyrics, or the middle of one with that pane hidden, and fades into the player. The blurred backgrounds now load the cover through the same copy as the artwork, so they no longer stay black when the cover is slow. - "Watch video" in the player's song menu opens the song's official music video at the same moment and pauses the song. The video is matched the same way Flow for Android does, so live takes, remixes and audio uploads are skipped. - The full player's seek bar glides with the song instead of stepping, and dragging it seeks once when you let go instead of on every move. The page behind the full player stops drawing while it is covered. - Mood and genre pages keep their shelves with titles instead of one long grid, album cards start playing without loading the album first, and playlist cards show their song count instead of the word Playlist.