diff --git a/Cargo.lock b/Cargo.lock index f93baa4..d730ed1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5038,7 +5038,7 @@ dependencies = [ [[package]] name = "widgetsack" -version = "0.0.50" +version = "0.0.51" dependencies = [ "fontdb", "futures-util", diff --git a/client/src/lib/widgets/meters/NowPlaying.test.tsx b/client/src/lib/widgets/meters/NowPlaying.test.tsx index eb96e06..700527f 100644 --- a/client/src/lib/widgets/meters/NowPlaying.test.tsx +++ b/client/src/lib/widgets/meters/NowPlaying.test.tsx @@ -74,6 +74,26 @@ const session = (title: string, art: string | null): SessionRecord => ({ last_model_update: { Model: { playback: null, timeline: null, media: null, source: '' } } }); +// A live session with NO current track: present (so hasSession is true) but its media update is gone, +// so there's no title and no album art. Models a player that tears down / recreates its SMTC session on +// pause — the prior track can't be carried forward (mergeMediaForward has nothing for the new id), so +// the meter sees a session with nothing to show. The cover must leave WITH the (now-empty) title. +const tracklessSession = (): SessionRecord => ({ + session_id: 2, + source: 'spotify.exe', + timestamp_created: null, + timestamp_updated: null, + last_media_update: null, + last_model_update: { + Model: { + playback: { auto_repeat: 'None', rate: 1, shuffle: false, status: 'Paused', type: 'Music' }, + timeline: null, + media: null, + source: 'spotify.exe' + } + } +}); + // Render NowPlaying (props-driven — the NowPlayingHost container does the store wiring in the app) // showing a track whose cover has loaded (the visible, full-colour layer). async function renderWithLoadedCover() { @@ -500,6 +520,20 @@ describe('NowPlaying — art clearing', () => { expect(container.querySelector('[data-part="title"]')).toBeNull(); }); + it('drops the cover at once when the session loses its track — not held (expanding) on the no-art grace', async () => { + const { container, rerender } = await renderWithLoadedCover(); + expect(container.querySelectorAll('.np-thumb').length).toBe(1); + // The active session now carries no track (no media → no title, no art). The title disappears at + // once (it's read straight from props), so the cover must too — otherwise it lingers on the no-art + // grace, expanding to fill the box where the title/artist were before finally fading (the reported + // "cover expands for a short while then disappears" on pause). This differs from a genuine art-lag + // (a track IS present, its cover just hasn't arrived) below, where keeping the cover is correct. + rerender(); + expect(container.querySelectorAll('.np-thumb').length).toBe(0); + // The artist row is gone with the track — the cover did not outlive it. + expect(container.querySelector('[data-part="artist"]')).toBeNull(); + }); + it('keeps the stale cover during the grace window, then fades it out for a genuinely art-less track', async () => { const { container, rerender } = await renderWithLoadedCover(); vi.useFakeTimers(); diff --git a/client/src/lib/widgets/meters/NowPlaying.tsx b/client/src/lib/widgets/meters/NowPlaying.tsx index a263904..d8875e4 100644 Binary files a/client/src/lib/widgets/meters/NowPlaying.tsx and b/client/src/lib/widgets/meters/NowPlaying.tsx differ diff --git a/widgetsack/Cargo.toml b/widgetsack/Cargo.toml index 30571dd..978e0c8 100644 --- a/widgetsack/Cargo.toml +++ b/widgetsack/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "widgetsack" # Keep in lockstep with tauri.conf.json's version (the bundle/updater source of truth). -version = "0.0.50" +version = "0.0.51" description = "widgetsack desktop widget platform" authors = ["you"] license = "MIT OR Apache-2.0" diff --git a/widgetsack/tauri.conf.json b/widgetsack/tauri.conf.json index 1577a10..5690e9e 100644 --- a/widgetsack/tauri.conf.json +++ b/widgetsack/tauri.conf.json @@ -50,7 +50,7 @@ "createUpdaterArtifacts": false }, "productName": "widgetsack", - "version": "0.0.50", + "version": "0.0.51", "identifier": "io.github.gyng", "plugins": {}, "app": {