fix: human vs AI fight bugs — CSP for TTS, invisible sprites, fight end sequence

- Allow huggingface.co in CSP connect-src (fixes Kokoro TTS model download)
- Add registerSW.js route (fixes PWA service worker 404)
- Add _resetPositions() safety after entrance (fixes invisible fighters)
- Fight end sequence works without canvas scene (KO/overlays/log always play)
- Pre-fight instructions in battle log for human players
- NIP-55 visibility sync and cleanup handlers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-09 17:00:20 +00:00
co-authored by Claude Opus 4.6
parent 63cc00fcb6
commit 150ce7447d
5 changed files with 142 additions and 54 deletions
+42
View File
@@ -33,6 +33,7 @@ const queueCount = ref(0)
const rankedQueueCount = ref(0)
const rankedEstWait = ref(15)
let pollHandle: ReturnType<typeof setInterval> | null = null
let nip55ReturnHandler: (() => void) | null = null
// Registration form
const selectedArchetype = ref('standard')
@@ -161,11 +162,13 @@ onMounted(async () => {
onBeforeRouteLeave(() => {
if (pollHandle) { clearInterval(pollHandle); pollHandle = null }
if (rateLimitTimer) { clearInterval(rateLimitTimer); rateLimitTimer = null }
if (nip55ReturnHandler) { document.removeEventListener('visibilitychange', nip55ReturnHandler); nip55ReturnHandler = null }
})
onUnmounted(() => {
if (pollHandle) clearInterval(pollHandle)
if (rateLimitTimer) { clearInterval(rateLimitTimer); rateLimitTimer = null }
if (nip55ReturnHandler) { document.removeEventListener('visibilitychange', nip55ReturnHandler); nip55ReturnHandler = null }
})
async function pollQueue() {
@@ -212,6 +215,45 @@ async function handleSignerLogin() {
// On Android, try NIP-55 intent to open Amber/Primal directly
if (hasAndroidSigner.value) {
error.value = 'Opening signer app...'
// Listen for return from signer app (user switches back to browser)
if (nip55ReturnHandler) document.removeEventListener('visibilitychange', nip55ReturnHandler)
nip55ReturnHandler = async () => {
if (document.visibilityState !== 'visible') return
document.removeEventListener('visibilitychange', nip55ReturnHandler!)
nip55ReturnHandler = null
// Check if signer redirected with callback params in the URL
try {
const nip55Result = await processNip55Return()
if (nip55Result) {
error.value = ''
if (nip55Result.bot) {
isHumanMode.value = !!nip55Result.bot.isHuman
step.value = 'ready'
} else {
step.value = 'choose-mode'
}
return
}
} catch {
// Callback params present but auth failed
error.value = 'Signer login failed. Try again.'
return
}
// No callback params — signer may have injected window.nostr
error.value = 'Connecting...'
const signerFound = await waitForSigner(2000)
if (signerFound) {
error.value = ''
handleLogin()
} else {
error.value = 'Signer did not return a signed event. Tap to try again.'
}
}
document.addEventListener('visibilitychange', nip55ReturnHandler)
initiateNip55Login()
return
}