diff --git a/ui/src/routes/+layout.svelte b/ui/src/routes/+layout.svelte index 304c00c..a27e721 100644 --- a/ui/src/routes/+layout.svelte +++ b/ui/src/routes/+layout.svelte @@ -2,7 +2,7 @@ import '../app.css'; import { page, navigating } from '$app/state'; import { goto } from '$app/navigation'; - import { setContext } from 'svelte'; + import { setContext, untrack } from 'svelte'; import type { Snippet } from 'svelte'; import type { LayoutData } from './$types'; import { audioStore } from '$lib/audio.svelte'; @@ -156,15 +156,23 @@ }); // Handle toggle requests from AudioPlayer controller. + // IMPORTANT: isPlaying must be read inside untrack() so the effect only + // re-runs when toggleRequest increments, not every time isPlaying changes. + // Without untrack the effect subscribes to both toggleRequest AND isPlaying, + // causing an infinite play/pause loop: play() fires onplay → isPlaying=true + // → effect re-runs → sees isPlaying=true → calls pause() → onpause fires + // → isPlaying=false → effect re-runs → calls play() → … $effect(() => { // Read toggleRequest to subscribe; ignore value 0 (initial). const _req = audioStore.toggleRequest; if (!audioEl || _req === 0) return; - if (audioStore.isPlaying) { - audioEl.pause(); - } else { - audioEl.play().catch(() => {}); - } + untrack(() => { + if (audioStore.isPlaying) { + audioEl!.pause(); + } else { + audioEl!.play().catch(() => {}); + } + }); }); // Handle seek requests from AudioPlayer controller.