From 016b9bdd4869341169b3b09f1c374845d6e30a9f Mon Sep 17 00:00:00 2001 From: rope Date: Sun, 16 Aug 2026 13:45:15 +0100 Subject: [PATCH] spotify-player: inset the special workspace window; keep cava on with playback Co-Authored-By: Claude Opus 5 --- settings/hyprland.nix | 14 ++++++++++++++ settings/quickshell.nix | 22 ++++++++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/settings/hyprland.nix b/settings/hyprland.nix index 5160fe6..f0c7657 100644 --- a/settings/hyprland.nix +++ b/settings/hyprland.nix @@ -333,6 +333,20 @@ in workspace = "special:music silent", }) + -- Inset it rather than filling the screen, so the workspace + -- underneath still frames it. Floating is what makes a percentage + -- size stick: a tiled window is sized by the layout, which would + -- just expand it back to the full area. Matched on title for the + -- same reason as above, and because matching on the special + -- workspace would depend on the assignment rule running first. + hl.window_rule({ + name = "spotify-player-inset", + match = { title = "^spotify-player$" }, + float = true, + size = "80% 80%", + center = true, + }) + -- Games stall on other workspaces: no frame callbacks = no render = -- game loop blocks. Keep them ticking in the background at -- misc:render_unfocused_fps (15). diff --git a/settings/quickshell.nix b/settings/quickshell.nix index f1aab08..25a6857 100644 --- a/settings/quickshell.nix +++ b/settings/quickshell.nix @@ -6191,16 +6191,21 @@ in return Math.floor(s / 60) + ":" + ("0" + Math.floor(s % 60)).slice(-2); } - // Live spectrum for the media cards' rings. cava runs only - // while this popup is open AND something is playing, so it - // isn't capturing audio in the background. levels is a - // barCount-long array of 0-100 ints, one frame per tick. + // Live spectrum for the media cards' rings. Tied to playback + // rather than to the popup: gating on `open` restarted cava + // on every open, and its bars ramp up from flat, so the + // ring visibly booted each time instead of being mid-song + // when it appeared. The cost is cava capturing while the + // popup is closed, but only ever while something is + // actually playing. levels is a barCount-long array of + // 0-100 ints, one frame per tick. readonly property int barCount: 24 property var levels: [] Process { id: cavaProc - running: calPopup.open && calPopup.players.length > 0 + running: calPopup.players.some( + p => p.playbackState === MprisPlaybackState.Playing) command: [Commands.cava, "-p", Commands.cavaConfig] stdout: SplitParser { onRead: data => { @@ -6215,9 +6220,10 @@ in } } - // Bars decay to flat when cava stops, so a paused card - // doesn't keep the last frame frozen around the disc. - onOpenChanged: if (!open) levels = [] + // Clear on the last frame of playback, not on close: cava + // now outlives the popup, so wiping on close would throw + // away the live frames it is still producing. + onPlayersChanged: if (!cavaProc.running) levels = [] Row { id: calRow