feat: remove mock fight button for production, cleanup fight UI

- Remove "Fight a Classic Bot" button (mock endpoints blocked in prod)
- Simplify speech bubble rendering (remove glow/accent layers)
- Sound and FightViewer improvements
- BotProfilePage enhancements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-07 11:59:10 +00:00
co-authored by Claude Opus 4.6
parent bb9a966d4e
commit 56785cfdea
5 changed files with 155 additions and 135 deletions
+54 -3
View File
@@ -281,22 +281,45 @@ const tierClass = (t: number) => `tier-${t}`
<template v-else>
<!-- Header with human + bot character -->
<div class="text-center mb-5">
<div class="flex items-end justify-between mb-3">
<div class="flex items-end justify-between mb-3 relative">
<HumanPreview
:seed="stats.avatarSeed || stats.name"
:archetype="stats.archetype || 'standard'"
:size="200"
:win-rate="(stats.winRate || 0) / 100"
anim="idle"
class="drop-shadow-[0_0_12px_rgba(0,0,0,0.6)] shrink-0"
class="drop-shadow-[0_0_12px_rgba(0,0,0,0.6)] shrink-0 relative z-10"
/>
<!-- Wire from gamepad to bot with electricity -->
<svg class="absolute bottom-8 left-0 w-full h-24 z-0 pointer-events-none" preserveAspectRatio="none">
<defs>
<linearGradient id="wire-grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#444455"/>
<stop offset="50%" stop-color="#555566"/>
<stop offset="100%" stop-color="#444455"/>
</linearGradient>
<filter id="elec-glow">
<feGaussianBlur stdDeviation="3" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<!-- Main wire -->
<path d="M 30% 85% Q 45% 40%, 55% 55% T 78% 30%" fill="none" stroke="url(#wire-grad)" stroke-width="3" stroke-linecap="round"/>
<!-- Electricity pulse 1 -->
<path class="elec-pulse-1" d="M 30% 85% Q 45% 40%, 55% 55% T 78% 30%" fill="none" stroke="#00f0ff" stroke-width="1.5" stroke-linecap="round" filter="url(#elec-glow)" stroke-dasharray="8 20" opacity="0.8"/>
<!-- Electricity pulse 2 (reverse) -->
<path class="elec-pulse-2" d="M 30% 85% Q 45% 40%, 55% 55% T 78% 30%" fill="none" stroke="#ff2d7b" stroke-width="1" stroke-linecap="round" filter="url(#elec-glow)" stroke-dasharray="5 25" opacity="0.6"/>
<!-- Spark nodes -->
<circle class="elec-spark-1" cx="45%" cy="55%" r="2" fill="#00f0ff" filter="url(#elec-glow)" opacity="0"/>
<circle class="elec-spark-2" cx="62%" cy="45%" r="2" fill="#ff2d7b" filter="url(#elec-glow)" opacity="0"/>
</svg>
<SpritePreview
:seed="stats.avatarSeed || stats.name"
:archetype="stats.archetype"
:tier="stats.tier"
:size="160"
:customization="stats.customization || undefined"
class="drop-shadow-[0_0_20px_var(--glow)] mx-auto"
class="drop-shadow-[0_0_20px_var(--glow)] mx-auto relative z-10"
:style="{ '--glow': stats.tierColor + '80' } as any"
/>
</div>
@@ -531,3 +554,31 @@ const tierClass = (t: number) => `tier-${t}`
</div>
</div>
</template>
<style scoped>
/* Electricity pulses along wire */
.elec-pulse-1 {
animation: elec-flow 1.2s linear infinite;
}
.elec-pulse-2 {
animation: elec-flow 0.9s linear infinite reverse;
}
@keyframes elec-flow {
0% { stroke-dashoffset: 0; opacity: 0.9; }
50% { opacity: 0.4; }
100% { stroke-dashoffset: -56; opacity: 0.9; }
}
/* Spark flashes at wire midpoints */
.elec-spark-1 {
animation: spark-flash 0.8s ease-in-out infinite;
}
.elec-spark-2 {
animation: spark-flash 1.1s ease-in-out infinite 0.4s;
}
@keyframes spark-flash {
0%, 70%, 100% { opacity: 0; r: 1; }
75% { opacity: 1; r: 4; }
85% { opacity: 0.6; r: 2; }
}
</style>
+1 -43
View File
@@ -11,7 +11,6 @@ const { pubkey, bot, profilePicUrl, isLoggedIn, hasExtension, isLoading, login,
const step = ref<string>('login')
const error = ref('')
const isJoining = ref(false)
const isFightingClassic = ref(false)
const queueCount = ref(0)
let pollHandle: ReturnType<typeof setInterval> | null = null
@@ -174,25 +173,6 @@ async function fight() {
isJoining.value = false
}
async function fightClassicBot() {
if (!bot.value || isFightingClassic.value) return
isFightingClassic.value = true
error.value = ''
try {
const res = await fetch(`/api/fights/mock/${bot.value.id}`, { method: 'POST' })
if (res.ok) {
const data = await res.json()
router.push(`/arena/${data.fightId}`)
} else {
const data = await res.json()
error.value = data.error || 'Failed to start fight.'
}
} catch {
error.value = 'Network error.'
}
isFightingClassic.value = false
}
function handleSignOut() {
logout()
step.value = 'login'
@@ -533,7 +513,7 @@ function handleSignOut() {
font-display font-black text-2xl tracking-[0.2em]
hover:bg-neon-pink/20 transition-all neon-border-pink
disabled:opacity-30 disabled:cursor-not-allowed"
:disabled="isJoining || isFightingClassic"
:disabled="isJoining"
@click="fight"
>
{{ isJoining ? 'MATCHING...' : 'FIGHT' }}
@@ -542,28 +522,6 @@ function handleSignOut() {
Queue up against a real AI bot
</p>
<!-- Classic bot button instant mock fight -->
<button
class="w-full py-4 bg-neon-purple/10 border-2 border-neon-purple/50 text-neon-purple
font-display font-black text-base tracking-[0.15em]
hover:bg-neon-purple/20 hover:border-neon-purple transition-all
disabled:opacity-30 disabled:cursor-not-allowed
flex items-center justify-center gap-3"
:disabled="isJoining || isFightingClassic"
@click="fightClassicBot"
>
<svg class="w-6 h-6 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="4" y="4" width="16" height="12" rx="2" />
<circle cx="9" cy="10" r="1.5" fill="currentColor" stroke="none" />
<circle cx="15" cy="10" r="1.5" fill="currentColor" stroke="none" />
<line x1="8" y1="20" x2="8" y2="16" />
<line x1="16" y1="20" x2="16" y2="16" />
</svg>
{{ isFightingClassic ? 'STARTING...' : 'FIGHT A CLASSIC BOT' }}
</button>
<p class="font-mono text-[10px] text-text-muted text-center -mt-1">
Instant match against a house bot
</p>
</div>
<!-- Quick links -->