diff --git a/src/lib/components/GlobalPlayer.svelte b/src/lib/components/GlobalPlayer.svelte index 2c9de73..9e30ae6 100644 --- a/src/lib/components/GlobalPlayer.svelte +++ b/src/lib/components/GlobalPlayer.svelte @@ -14,6 +14,7 @@ nowPlayingLabel, prev, removeTrack, + schedulePersistNow, setUiOpen, setVolume, toggleShuffle, @@ -151,6 +152,11 @@ $effect(() => { media.setTrack(snap.currentTrack); + + // Persist queue/settings/UI state (throttled) from within a component-scoped effect + // to avoid orphaned module-level `$effect`. + schedulePersistNow(); + if (!audioEl) return; audioEl.volume = snap.volume; diff --git a/src/lib/player/player.svelte.ts b/src/lib/player/player.svelte.ts index d34e0fb..3b76484 100644 --- a/src/lib/player/player.svelte.ts +++ b/src/lib/player/player.svelte.ts @@ -154,12 +154,19 @@ const snapshot = $derived({ uiOpen, }); -/** Persist on changes (best effort, throttled). */ +/** + * Persistence + * + * NOTE: Module-level `$effect` is not allowed (it becomes an "orphaned effect"). + * Instead, the GlobalPlayer component (or any single always-mounted component) + * should call `schedulePersistNow()` inside its own `$effect`. + */ const schedulePersist = createPersistScheduler(250); -$effect(() => { + +export function schedulePersistNow(): void { if (!browser) return; schedulePersist(persistableState()); -}); +} /** --- Public reads --- */