fix: mobile fight playback — prevent sprite loading hangs, TTS stalls, unlock audio from gesture

- Unified speakAsync/speakAsyncWithRate into _speakAsyncCore with:
  - 500ms startup check: bail immediately if speech won't start (mobile/no gesture)
  - 8s safety timeout (down from 15s) to prevent blocking
  - iOS-safe keepalive: only do Chrome pause/resume workaround on desktop Chrome
  - Immediate bail when no voices loaded
- Sprite loading: 5s timeout per sprite prevents mobile hangs from stuck Image decodes
- Scene creation: 10s timeout in FightViewer so overlay/voice flow continues even if
  canvas fails on mobile
- playRound: bail gracefully if fighter sprites missing instead of crashing
- Global audio unlock: first touch/click on site unlocks AudioContext + SpeechSynthesis
- Practice button pre-unlocks audio while still in user gesture context

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-08 15:54:58 +00:00
co-authored by Claude Opus 4.6
parent 9287fd1783
commit 6561ef5d15
5 changed files with 97 additions and 65 deletions
+13
View File
@@ -1,6 +1,19 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { RouterView } from 'vue-router'
import NavBar from './components/NavBar.vue'
import { ensureAudioContext } from './game/sounds'
// Unlock AudioContext + SpeechSynthesis on first user interaction (mobile requires gesture)
onMounted(() => {
const unlock = () => {
ensureAudioContext()
document.removeEventListener('touchstart', unlock)
document.removeEventListener('click', unlock)
}
document.addEventListener('touchstart', unlock, { once: true, passive: true })
document.addEventListener('click', unlock, { once: true })
})
</script>
<template>