Added isVerifyingWebhook state with spinner and disabled button during
webhook test. Improved error message to guide user on retry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feedbackTimer, timerHandle, and pollHandle are all cleared in
onUnmounted. Test confirms cleanup pattern works correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 silent catch handlers replaced with descriptive console.warn logging
across 6 frontend files. No silent error swallowing remains.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merged bot-setup and choose-connection into one actionable step.
Users now see mode picker, download button for the correct guide
(BOTFIGHTS-WEBHOOK.md or BOTFIGHTS-POLLING.md), and safety info
all on one screen instead of two filler steps with no actions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
buildNip98Token checked window.nostr before secretKeyHex, so
"Generate New Identity" would sign with the browser extension's key
after saving. Now explicit keys always take priority over extensions.
Also made setup flow mode-aware: webhook users get BOTFIGHTS-WEBHOOK.md,
polling users get BOTFIGHTS-POLLING.md with matching copy prompts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
When window.nostr isn't available (common on mobile Chrome where
extensions can't inject), fall back to NIP-55 nostrsigner: intent
URIs. This opens Amber/Primal directly to sign a NIP-98 event,
then redirects back with the signed event for JWT authentication.
- Build nostrsigner: URI with unsigned NIP-98 event + callback URL
- Process NIP-55 callback on page mount (extract signed event from URL)
- Auto-detect Android to show "SIGN IN WITH AMBER / PRIMAL" label
- Reduced window.nostr polling from 3s to 2s before NIP-55 fallback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Security:
- Move CREATOR_PUBKEY from hardcoded constant to BOTFIGHTS_CREATOR_PUBKEYS
env var. Shared isCreatorPubkey() in constants.ts used by auth, admin,
tournaments. Frontend checks authorization via API, not client-side.
Mobile fixes:
- Nostr signer: poll for window.nostr up to 3s (Amber injects late).
- TTS: auto-unlock AudioContext on first user interaction via
installAutoUnlock() on fight page mount.
UX:
- Add loading spinners to "I BUILD BOTS" and "I FIGHT MYSELF" buttons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Nostr signer: poll for window.nostr up to 3s on mobile (Amber injects
late). Both login() and handleSignerLogin() now wait before failing.
- Mobile TTS: install global one-time click/touch/keydown handler to
auto-unlock AudioContext when fight pages mount. Previously only
triggered by explicit sound toggle, so mobile TTS silently failed.
- Add loading spinners to "I BUILD BOTS" and "I FIGHT MYSELF" buttons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove .slice(0, 120) truncation from battle log entries and
.slice(0, 60) from speech bubble calls. Increase bubble limits
to 200 chars, 24 chars/line, 8 lines so responses display fully.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix invisible characters in human mode: init live scene BEFORE starting
challenge polling so entrance plays before first question appears
- Cap creative writing timer to 10s for multiple choice (just tapping buttons)
- Fix TTS reliability: precache priority phrases (Round 1-7, Fight!, K.O.)
all at once instead of in slow batches; prevent duplicate precache runs
- Add 10 vibe-coded narrations (~20% chance): "I was vibe coded into existence"
- Add 15 sweary narrations (~30% chance): raw unhinged fight commentary
- Add sweary draw and retro narrations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace insecure raw-pubkey auth with cryptographic NIP-98 signed
requests and server-issued JWT sessions. Logout now fully clears
all state including nsec. Add yellow "Use Nostr Signer" button
for Amber/NIP-07 remote signers.
- Server: JWT middleware (HMAC-SHA256, 24h expiry), NIP-98 verification
- Server: POST /api/auth/nostr/session endpoint
- Frontend: NIP-98 token builder + authFetch wrapper with JWT Bearer
- Frontend: All authenticated API calls use authFetch
- Security: logout clears JWT, pubkey, bot, nsec, and profile pic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Practice button to BotProfilePage for quick sparring
- Fix rate limiter bug: all rateLimit() instances shared one counter map,
causing global and per-route limits to corrupt each other. Each limiter
now gets its own isolated map.
- Replace 8-digit hex colors (#ffd70066) with rgba() in sprite rendering
for mobile browser compatibility (iOS Safari renders them as black boxes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows a one-time warning when users first click "Fight For Sats" letting
them know it's vibe coded and to only use small sats amounts. Acknowledged
state persists in localStorage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Track active fight IDs per bot in orchestrator (Set → Map)
- Return fightId in "already in fight" error responses (409)
- Frontend shows "REJOIN FIGHT" link instead of generic error
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Don't reset isJoining flags on successful navigation (component unmounts)
- Add onBeforeRouteLeave guard to clean up polling and rate limit timers
- Use handleError with rate limit countdown for all error paths
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Warn before navigating away from active fight (beforeRouteLeave)
- Re-poll challenge state when tab becomes visible (visibilitychange)
- Prevents silent forfeit on back-button and stale timer after tab switch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- BUG-1: Prevent double-tap by locking phase before async submit
- BUG-2: Submit timeout notification to server when timer expires
- BUG-3: Distinct "TIME'S UP!" visual vs "ANSWER SUBMITTED"
- BUG-4: Track consecutive poll failures, show connection lost banner
- BUG-5: Add A-D / 1-4 keyboard shortcuts for MC choices
- BUG-6: Use choice text as v-for key instead of array index
- BUG-7: Deadline-based timer (250ms tick) prevents drift
- BUG-8: Validate choice is in current choices before submit
- BUG-9: Submit empty timeout instead of random choice on expiry
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Creative challenges (roast_battle, creative_writing, meme_war,
code_golf, wrestling_match) now auto-generate multiple choice options
from per-type response pools: 1 good answer + 3 weaker distractors.
The free text input UI is commented out but preserved for future use.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix fight replay showing empty scene: validate rounds before playing,
retry loading fight data up to 5 times when rounds are missing
- Add COOP/COEP headers to production server for SharedArrayBuffer
(required by Kokoro TTS WASM threading on mobile)
- Add blob: to scriptSrc/workerSrc CSP for Web Worker support
- Fix mobile character cutoff: raise ground line to 0.82, add
MOBILE_SCALE_CAP (0.55) so fighters never shrink too small
- Add ₿ bitcoin chest badge to all fighter sprites (scales with tier)
- Add sats coin stack on bot profile page proportional to satsWon
- Seed dev tournament with 8 mock bots on startup (dev mode only)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All pages used h-[calc(100dvh-4rem)] which only subtracted the top
navbar but ignored the mobile tab bar's pb-14 bottom padding, causing
content to overflow and scroll. Changed all pages to h-full so they
fill the flex parent (main element) which already handles both the
navbar and tab bar spacing correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace any[] with GameObj[], fighter: any with Fighter in speech
bubbles, talking anims, showboat system, bow/fistbump/helpup, and
dizzy stars. Fix customization as any casts with ?? undefined.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move FightData, FightRound, FightBot, FightArenaInfo types from local
definitions in FightViewer.vue to shared fight/types.ts. Replace `any`
typing in HumanFightPage, FightPage, useFightPolling, and useFightCache
with proper typed interfaces. Fix null-safety guards exposed by typing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split FightPage.vue into useFightPolling (SSE, polling, reconnect)
and useHumanChallenge (timer, submission, cooldown) composables.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tier-selectable practice page at /practice. Fights against classic
bots with dampened Elo. Routes to human fight page or arena based
on bot type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Large tap-target multiple-choice buttons (min 48px), full-width text
area for creative responses, countdown timer with color shift
(green/yellow/red) and shake animation at <3s, timer progress bar,
correct/wrong feedback flash overlay.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TournamentListPage with status filters, TournamentPage with CSS grid
bracket display, lobby view for open tournaments, champion banner,
live polling for active tournaments. Routes at /tournaments and
/tournament/:id. NavBar link added.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FightPage.vue: store SSE listener refs in array, removeEventListener
on each before close() in disconnectSSE()
- FightScene.ts: add cleanupTimers Set with trackedInterval/trackedTimeout
helpers; replace key setInterval calls (projectile trails, entrance
flames, talking animation, exhaust effects, dimensional shift, hole
fade) with tracked versions; clear all on scene destroy()
- rate-limit.ts: export cleanupInterval handle for graceful shutdown
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Elo-bracket matchmaking: prefer ±200, widen by 100 every 15s of waiting.
Add pickRankedChallenge() that filters to creative/open-ended only, never
multiple choice. Show ranked queue status, wait estimate, and "HARDER
PROMPTS" notice on JoinBoutPage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add GET /api/bots/leaderboard?season=current endpoint. LeaderboardPage
now toggles between "This Season" and "All Time" views. Shows season
name, countdown timer, and top 3 placement badges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ZAP WINNER button in FightViewer after fight ends. Server endpoint
POST /api/payments/zap increments zapsReceived on winner bot. Show zap
count on bot profile page. Add zaps_received column with migration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetch full kind:0 metadata (display_name, about, banner, nip05) from
relays with a 5-min TTL cache. Show Nostr banner, display name, and
NIP-05 on bot profile pages. Expose ownerPubkey in stats API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BetHistory component shows personal bet history with fight links,
amounts, outcomes, and payouts. P&L summary shows total wagered, total
won, and net sats. Added to BotProfilePage for the bot owner's view.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BetPanel component with pick-winner buttons, preset amounts (21/100/500/
1000 sats), Cashu token paste, odds display from server, and payout
preview with 5% house cut. Wired into FightCardPage for scheduled fights.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>