From 0aef1c5fe243fb19d4ec33e49af5da0e8e84fc39 Mon Sep 17 00:00:00 2001 From: Lotus CI Date: Wed, 1 Jul 2026 23:21:50 -0400 Subject: [PATCH] feat(lotus-audio-inject): one soundboard clip at a time (replace mode) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit playInjectedClip now stops any in-flight clip (via the existing idempotent cleanup) before starting a new one, so rapid taps replace rather than overlap/stack — no track leak. The host also debounces the button. Co-Authored-By: Claude Fable 5 --- src/lotus/lotusAudioInject.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lotus/lotusAudioInject.ts b/src/lotus/lotusAudioInject.ts index 998392e0..d5fe59ed 100644 --- a/src/lotus/lotusAudioInject.ts +++ b/src/lotus/lotusAudioInject.ts @@ -101,6 +101,13 @@ async function playInjectedClip( return; } + // Max ONE clip at a time (replace mode): stop any in-flight clip before + // starting a new one, so clips can't overlap or be spammed. Runs + // synchronously before the first await, and each cleanup() is idempotent and + // removes itself from activeClips (so no track leak). The host also debounces + // the button; together they cover the brief fetch window. + for (const abort of [...activeClips]) abort(); + const resp = await fetch(url, { credentials: "omit", mode: "cors" }); if (!resp.ok) throw new Error(`fetch ${url} -> ${resp.status}`); const arrayBuffer = await resp.arrayBuffer();