fix: move BETA badge after green dot in navbar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-09 15:36:24 +00:00
co-authored by Claude Opus 4.6
parent 226d242552
commit df70f5f093
5 changed files with 46 additions and 19 deletions
+12 -2
View File
@@ -451,9 +451,13 @@ async function _doReplay() {
await sleep(insanityMode.value ? 50 : 300) await sleep(insanityMode.value ? 50 : 300)
} }
// Hide human player's typed answers from replay
const replayIsAHuman = props.fight.botA?.archetype === 'human'
const replayIsBHuman = props.fight.botB?.archetype === 'human'
// 2. Bot A: ensure audio ready, then show log + bubble + mouth + voice all at once // 2. Bot A: ensure audio ready, then show log + bubble + mouth + voice all at once
const isCodeRound = isCodeAnswer(round.challengeType, round.botAResponse || '') const isCodeRound = isCodeAnswer(round.challengeType, round.botAResponse || '')
if (round.botAResponse) { if (round.botAResponse && !replayIsAHuman) {
if (doTTS && !isCodeRound) await awaitAnswerReady(props.fight.botA!.name, round.botAResponse) if (doTTS && !isCodeRound) await awaitAnswerReady(props.fight.botA!.name, round.botAResponse)
scene?.stopShowboating('a') scene?.stopShowboating('a')
logItems.value.push({ type: 'responseA', round: round.roundNumber, text: `${props.fight.botA?.name}: ${round.botAResponse || '[NO RESPONSE]'}`, color: 'neon-cyan' }) logItems.value.push({ type: 'responseA', round: round.roundNumber, text: `${props.fight.botA?.name}: ${round.botAResponse || '[NO RESPONSE]'}`, color: 'neon-cyan' })
@@ -473,10 +477,13 @@ async function _doReplay() {
if (!doTTS && !insanityMode.value) await sleep(400) if (!doTTS && !insanityMode.value) await sleep(400)
scene?.stopTalking('a') scene?.stopTalking('a')
scene?.hideSpeechBubble('a') scene?.hideSpeechBubble('a')
} else if (replayIsAHuman) {
scene?.stopShowboating('a')
await sleep(insanityMode.value ? 50 : 200)
} }
// 3. Bot B: ensure audio ready, then show log + bubble + mouth + voice all at once // 3. Bot B: ensure audio ready, then show log + bubble + mouth + voice all at once
if (round.botBResponse) { if (round.botBResponse && !replayIsBHuman) {
if (doTTS && !isCodeRound) await awaitAnswerReady(props.fight.botB!.name, round.botBResponse) if (doTTS && !isCodeRound) await awaitAnswerReady(props.fight.botB!.name, round.botBResponse)
scene?.stopShowboating('b') scene?.stopShowboating('b')
logItems.value.push({ type: 'responseB', round: round.roundNumber, text: `${props.fight.botB?.name}: ${round.botBResponse || '[NO RESPONSE]'}`, color: 'neon-pink' }) logItems.value.push({ type: 'responseB', round: round.roundNumber, text: `${props.fight.botB?.name}: ${round.botBResponse || '[NO RESPONSE]'}`, color: 'neon-pink' })
@@ -497,6 +504,9 @@ async function _doReplay() {
if (!doTTS && !insanityMode.value) await sleep(400) if (!doTTS && !insanityMode.value) await sleep(400)
scene?.stopTalking('b') scene?.stopTalking('b')
scene?.hideSpeechBubble('b') scene?.hideSpeechBubble('b')
} else if (replayIsBHuman) {
scene?.stopShowboating('b')
await sleep(insanityMode.value ? 50 : 200)
} }
// Stop all showboating before fight animation // Stop all showboating before fight animation
+1 -1
View File
@@ -39,12 +39,12 @@ const bottomNav = [
<span class="font-display font-black text-neon-pink text-lg tracking-widest glow-pink"> <span class="font-display font-black text-neon-pink text-lg tracking-widest glow-pink">
BOTFIGHTS BOTFIGHTS
</span> </span>
<span class="font-glitch text-[9px] text-neon-cyan/80 tracking-[0.2em] -rotate-12 -ml-1 -mt-2 select-none">BETA</span>
<span <span
class="w-2 h-2 rounded-full transition-colors" class="w-2 h-2 rounded-full transition-colors"
:class="isOnline ? 'bg-green-400' : 'bg-red-400 animate-pulse'" :class="isOnline ? 'bg-green-400' : 'bg-red-400 animate-pulse'"
:title="isOnline ? 'Online' : 'Offline'" :title="isOnline ? 'Online' : 'Offline'"
/> />
<span class="font-glitch text-[9px] text-neon-cyan/80 tracking-[0.2em] -rotate-12 -ml-0.5 select-none">BETA</span>
</RouterLink> </RouterLink>
<div class="hidden md:flex items-center gap-6"> <div class="hidden md:flex items-center gap-6">
+5 -1
View File
@@ -1222,7 +1222,11 @@ export async function createFightScene(config: FightSceneConfig) {
stopShowboating(side: 'a' | 'b') { showboatSystem.stopShowboating(side) }, stopShowboating(side: 'a' | 'b') { showboatSystem.stopShowboating(side) },
async playEntrance() { async playEntrance() {
return entranceSystem.playEntrance() // Timeout entrance to prevent hanging on mobile (max 8s)
await Promise.race([
entranceSystem.playEntrance(),
new Promise<void>(r => setTimeout(r, 8000)),
])
}, },
// Physical contact delegates // Physical contact delegates
+20 -11
View File
@@ -524,21 +524,30 @@ async function playEntrance() {
while (idxB === idxA && attempts < 5) { idxB = pickTierEntrance(botB.tier); attempts++ } while (idxB === idxA && attempts < 5) { idxB = pickTierEntrance(botB.tier); attempts++ }
// Play entrances with slight stagger — creator always gets special entrance // Play entrances with slight stagger — creator always gets special entrance
// Wrap each in try/catch so one failing doesn't prevent the other
announceDeepIntro() announceDeepIntro()
if (botA.archetype === 'the_creator') { try {
await creatorEntrance(fA, HOME_A, true) if (botA.archetype === 'the_creator') {
} else { await creatorEntrance(fA, HOME_A, true)
await entrances[idxA](fA, HOME_A, true) } else {
await entrances[idxA](fA, HOME_A, true)
}
} catch (e) {
console.warn('[Entrance] Fighter A entrance failed:', e)
} }
await k.wait(0.3) await k.wait(0.3)
if (botB.archetype === 'the_creator') { try {
await creatorEntrance(fB, HOME_B, false) if (botB.archetype === 'the_creator') {
} else { await creatorEntrance(fB, HOME_B, false)
await entrances[idxB](fB, HOME_B, false) } else {
await entrances[idxB](fB, HOME_B, false)
}
} catch (e) {
console.warn('[Entrance] Fighter B entrance failed:', e)
} }
await k.wait(0.2) await k.wait(0.2)
// Safety: ensure both fighters are visible and at home positions // Safety: ALWAYS ensure both fighters are visible and at home positions
fA.pos.x = HOME_A fA.pos.x = HOME_A
fA.pos.y = GROUND_Y - 6 fA.pos.y = GROUND_Y - 6
fA.opacity = 1 fA.opacity = 1
@@ -547,8 +556,8 @@ async function playEntrance() {
fB.opacity = 1 fB.opacity = 1
// Both idle at home positions // Both idle at home positions
fA.play('idle') try { fA.play('idle') } catch { /* sprite may not have anim */ }
fB.play('idle') try { fB.play('idle') } catch { /* sprite may not have anim */ }
} }
return { playEntrance } return { playEntrance }
+8 -4
View File
@@ -248,13 +248,17 @@ async function handleRoundEnd(data: any) {
const cLabel = currentChallengeInfo.value?.label || challengeLabel(currentChallengeInfo.value?.type || '') const cLabel = currentChallengeInfo.value?.label || challengeLabel(currentChallengeInfo.value?.type || '')
// Don't show the human player's typed answer in the battle log
const isAHuman = myBotId.value === fd.botA.id && isHumanFight.value
const isBHuman = myBotId.value === fd.botB.id && isHumanFight.value
liveLogItems.value.push( liveLogItems.value.push(
{ type: 'header', round, text: `ROUND ${round}: ${cLabel}`, color: 'neon-purple' }, { type: 'header', round, text: `ROUND ${round}: ${cLabel}`, color: 'neon-purple' },
) )
if (result.botAResponse) { if (result.botAResponse && !isAHuman) {
liveLogItems.value.push({ type: 'responseA', round, text: `${fd.botA.name}: ${result.botAResponse}`, color: 'neon-cyan' }) liveLogItems.value.push({ type: 'responseA', round, text: `${fd.botA.name}: ${result.botAResponse}`, color: 'neon-cyan' })
} }
if (result.botBResponse) { if (result.botBResponse && !isBHuman) {
liveLogItems.value.push({ type: 'responseB', round, text: `${fd.botB.name}: ${result.botBResponse}`, color: 'neon-pink' }) liveLogItems.value.push({ type: 'responseB', round, text: `${fd.botB.name}: ${result.botBResponse}`, color: 'neon-pink' })
} }
if (result.narration) { if (result.narration) {
@@ -269,8 +273,8 @@ async function handleRoundEnd(data: any) {
if (liveScene) { if (liveScene) {
fanfareRound(round) fanfareRound(round)
if (result.botAResponse) liveScene.showSpeechBubble('a', result.botAResponse, 3) if (result.botAResponse && !isAHuman) liveScene.showSpeechBubble('a', result.botAResponse, 3)
if (result.botBResponse) { if (result.botBResponse && !isBHuman) {
setTimeout(() => { setTimeout(() => {
if (liveScene && result.botBResponse) liveScene.showSpeechBubble('b', result.botBResponse, 2.5) if (liveScene && result.botBResponse) liveScene.showSpeechBubble('b', result.botBResponse, 2.5)
}, 300) }, 300)