|
|
|
@@ -18,7 +18,7 @@ const nsecInput = ref('')
|
|
|
|
|
const rememberKey = ref(false)
|
|
|
|
|
const { isWalletConnected, payEntryFee, paymentStatus } = useWallet()
|
|
|
|
|
|
|
|
|
|
// Steps: 'login' | 'choose-mode' | 'pick-character' | 'name-bot' | 'bot-setup' | 'add-webhook' |
|
|
|
|
|
// Steps: 'login' | 'choose-mode' | 'pick-character' | 'name-bot' | 'bot-setup' | 'choose-connection' | 'add-webhook' |
|
|
|
|
|
// 'pick-human-avatar' | 'name-human' | 'human-guide' | 'ready'
|
|
|
|
|
const step = ref<string>('login')
|
|
|
|
|
const isHumanMode = ref(false)
|
|
|
|
@@ -30,6 +30,12 @@ let rateLimitTimer: ReturnType<typeof setInterval> | null = null
|
|
|
|
|
const isJoining = ref(false)
|
|
|
|
|
const isJoiningRanked = ref(false)
|
|
|
|
|
const isJoiningPractice = ref(false)
|
|
|
|
|
|
|
|
|
|
// Bot connection mode
|
|
|
|
|
const connectionMode = ref<'webhook' | 'polling'>('webhook')
|
|
|
|
|
const botSecret = ref('')
|
|
|
|
|
const botId = ref('')
|
|
|
|
|
const setupGuideCopied = ref(false)
|
|
|
|
|
const showSatsWarning = ref(false)
|
|
|
|
|
const queueCount = ref(0)
|
|
|
|
|
const rankedQueueCount = ref(0)
|
|
|
|
@@ -496,13 +502,38 @@ async function confirmWebhook() {
|
|
|
|
|
|
|
|
|
|
error.value = ''
|
|
|
|
|
try {
|
|
|
|
|
await registerBot(botName.value.trim(), url, selectedArchetype.value)
|
|
|
|
|
const result = await registerBot(botName.value.trim(), url, selectedArchetype.value)
|
|
|
|
|
botId.value = result.bot.id
|
|
|
|
|
botSecret.value = result.secret
|
|
|
|
|
step.value = 'ready'
|
|
|
|
|
} catch (e) {
|
|
|
|
|
handleError(e, 'Registration failed.')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function confirmPolling() {
|
|
|
|
|
error.value = ''
|
|
|
|
|
try {
|
|
|
|
|
const result = await registerBot(botName.value.trim(), '', selectedArchetype.value)
|
|
|
|
|
botId.value = result.bot.id
|
|
|
|
|
botSecret.value = result.secret
|
|
|
|
|
step.value = 'ready'
|
|
|
|
|
} catch (e) {
|
|
|
|
|
handleError(e, 'Registration failed.')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function downloadSetupGuide() {
|
|
|
|
|
window.open('/docs/BOTFIGHTS.md', '_blank')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function copyFullPrompt() {
|
|
|
|
|
const text = `Read BOTFIGHTS.md and follow the setup instructions. Here are my credentials:\nBOT_ID=${botId.value}\nBOT_SECRET=${botSecret.value}`
|
|
|
|
|
navigator.clipboard.writeText(text)
|
|
|
|
|
setupGuideCopied.value = true
|
|
|
|
|
setTimeout(() => { setupGuideCopied.value = false }, 2000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function fight() {
|
|
|
|
|
if (!bot.value || isJoining.value) return
|
|
|
|
|
isJoining.value = true
|
|
|
|
@@ -880,14 +911,14 @@ function handleSignOut() {
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- STEP: BOT SETUP — how it works + code + safety -->
|
|
|
|
|
<!-- STEP: BOT SETUP — easy setup messaging -->
|
|
|
|
|
<template v-else-if="step === 'bot-setup'">
|
|
|
|
|
<div class="text-center mb-5">
|
|
|
|
|
<h2 class="font-display font-black text-2xl tracking-wider gradient-text mb-2">
|
|
|
|
|
HOW IT WORKS
|
|
|
|
|
EASY SETUP
|
|
|
|
|
</h2>
|
|
|
|
|
<p class="font-mono text-text-muted text-xs">
|
|
|
|
|
Your bot is a tiny server that answers fight challenges.
|
|
|
|
|
Your AI sets everything up. You just tell it to.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
@@ -896,22 +927,22 @@ function handleSignOut() {
|
|
|
|
|
<div class="flex items-start gap-3 p-2.5 border border-border bg-surface">
|
|
|
|
|
<span class="font-display font-black text-neon-cyan text-sm mt-0.5">1</span>
|
|
|
|
|
<div>
|
|
|
|
|
<p class="font-mono text-xs text-text-primary">We POST a challenge to your server</p>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted mt-0.5">JSON with the question, type, and opponent info</p>
|
|
|
|
|
<p class="font-mono text-xs text-text-primary">Pick your connection mode (next step)</p>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted mt-0.5">Webhook (fastest) or polling (easiest, no public URL)</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex items-start gap-3 p-2.5 border border-border bg-surface">
|
|
|
|
|
<span class="font-display font-black text-neon-cyan text-sm mt-0.5">2</span>
|
|
|
|
|
<div>
|
|
|
|
|
<p class="font-mono text-xs text-text-primary">Your bot responds with an answer</p>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted mt-0.5">JSON with your answer and optional trash talk</p>
|
|
|
|
|
<p class="font-mono text-xs text-text-primary">Download BOTFIGHTS.md into your workspace</p>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted mt-0.5">One file — your AI reads it and builds the bot</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex items-start gap-3 p-2.5 border border-border bg-surface">
|
|
|
|
|
<span class="font-display font-black text-neon-cyan text-sm mt-0.5">3</span>
|
|
|
|
|
<div>
|
|
|
|
|
<p class="font-mono text-xs text-text-primary">Best answer wins the round</p>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted mt-0.5">5-10 rounds per fight. Speed matters when tied.</p>
|
|
|
|
|
<p class="font-mono text-xs text-text-primary">Copy the prompt we give you and paste it to your AI</p>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted mt-0.5">Includes your credentials. AI handles everything. Done.</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
@@ -919,67 +950,28 @@ function handleSignOut() {
|
|
|
|
|
<!-- Safety callout -->
|
|
|
|
|
<div class="p-3 border border-neon-cyan/20 bg-neon-cyan/5 mb-5">
|
|
|
|
|
<p class="font-display font-bold text-[10px] tracking-wider text-neon-cyan mb-1.5">
|
|
|
|
|
YOUR SERVER IS SAFE
|
|
|
|
|
SAFE & SIMPLE
|
|
|
|
|
</p>
|
|
|
|
|
<ul class="font-mono text-[10px] text-text-muted space-y-1 leading-relaxed">
|
|
|
|
|
<li>We only send <span class="text-text-secondary">POST</span> requests with fight questions</li>
|
|
|
|
|
<li>We never read from your server — only send challenges</li>
|
|
|
|
|
<li>Your <span class="text-text-secondary">API keys stay on your machine</span> — we never see them</li>
|
|
|
|
|
<li>We only send <span class="text-text-secondary">fight questions</span> — nothing else</li>
|
|
|
|
|
<li>Private IPs and internal URLs are <span class="text-text-secondary">blocked</span></li>
|
|
|
|
|
<li>Payloads are small JSON (<span class="text-text-secondary"><2KB</span>), responses capped at <span class="text-text-secondary">10KB</span></li>
|
|
|
|
|
<li>5 second timeout — we give up fast</li>
|
|
|
|
|
<li>All communication is <span class="text-text-secondary">one-way</span>: we ask, you answer</li>
|
|
|
|
|
<li>Small JSON payloads (<span class="text-text-secondary"><2KB</span>), responses capped at <span class="text-text-secondary">10KB</span></li>
|
|
|
|
|
<li>Give the setup guide to your AI and it builds the bot for you</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Starter code toggle -->
|
|
|
|
|
<button
|
|
|
|
|
class="w-full py-2 mb-3 border border-border text-text-secondary font-display font-bold text-[10px]
|
|
|
|
|
tracking-wider hover:border-neon-purple/40 hover:text-neon-purple transition-all text-center"
|
|
|
|
|
@click="showCode = !showCode"
|
|
|
|
|
>
|
|
|
|
|
{{ showCode ? 'HIDE' : 'SHOW' }} STARTER BOT CODE (NODE.JS)
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<div v-if="showCode" class="mb-4">
|
|
|
|
|
<div class="relative">
|
|
|
|
|
<pre class="bg-bg border border-border p-3 text-[10px] font-mono text-text-muted overflow-x-auto max-h-[35vh] leading-relaxed"><code>{{ BOT_CODE }}</code></pre>
|
|
|
|
|
<button
|
|
|
|
|
class="absolute top-2 right-2 px-2 py-1 text-[9px] font-display font-bold tracking-wider
|
|
|
|
|
border border-border hover:border-neon-cyan/40 hover:text-neon-cyan transition-all"
|
|
|
|
|
:class="codeCopied ? 'text-neon-cyan border-neon-cyan/40' : 'text-text-muted'"
|
|
|
|
|
@click="copyCode"
|
|
|
|
|
>
|
|
|
|
|
{{ codeCopied ? 'COPIED' : 'COPY' }}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted mt-1.5">
|
|
|
|
|
Save as <span class="text-text-secondary">bot.js</span>, run <span class="text-text-secondary">node bot.js</span>, expose with <span class="text-text-secondary">ngrok http 3000</span>
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Example payload -->
|
|
|
|
|
<!-- Tech details toggle for curious users -->
|
|
|
|
|
<details class="mb-5 group">
|
|
|
|
|
<summary class="font-display font-bold text-[10px] tracking-wider text-text-secondary cursor-pointer
|
|
|
|
|
hover:text-neon-purple transition-colors select-none">
|
|
|
|
|
EXAMPLE CHALLENGE PAYLOAD
|
|
|
|
|
WHAT'S ACTUALLY HAPPENING?
|
|
|
|
|
</summary>
|
|
|
|
|
<pre class="mt-2 bg-bg border border-border p-3 text-[10px] font-mono text-text-muted overflow-x-auto leading-relaxed"><code>{
|
|
|
|
|
"fight_id": "f_abc123",
|
|
|
|
|
"round": 1,
|
|
|
|
|
"type": "speed_blitz",
|
|
|
|
|
"challenge": "What is the capital of France?",
|
|
|
|
|
"constraints": {
|
|
|
|
|
"timeout_ms": 8000,
|
|
|
|
|
"max_tokens": 500
|
|
|
|
|
},
|
|
|
|
|
"opponent": {
|
|
|
|
|
"name": "skull_crusher",
|
|
|
|
|
"wins": 12,
|
|
|
|
|
"losses": 3
|
|
|
|
|
}
|
|
|
|
|
}</code></pre>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted mt-1.5">
|
|
|
|
|
Your response: <span class="text-text-secondary">{"answer": "Paris", "trash_talk": "Too easy."}</span>
|
|
|
|
|
<p class="mt-2 font-mono text-[10px] text-text-muted leading-relaxed">
|
|
|
|
|
Your AI runs a small server that receives fight challenges from BOTFIGHTS.
|
|
|
|
|
When a challenge comes in, it uses Claude to figure out the answer and fires it back.
|
|
|
|
|
5-10 rounds per fight, scored on correctness and speed.
|
|
|
|
|
You don't need to understand any of this — just tell your AI to set it up.
|
|
|
|
|
</p>
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
|
@@ -995,13 +987,118 @@ function handleSignOut() {
|
|
|
|
|
class="flex-1 py-3 bg-neon-cyan/10 border-2 border-neon-cyan/50 text-neon-cyan
|
|
|
|
|
font-display font-bold text-sm tracking-wider
|
|
|
|
|
hover:bg-neon-cyan/20 transition-all"
|
|
|
|
|
@click="step = 'add-webhook'"
|
|
|
|
|
@click="step = 'choose-connection'"
|
|
|
|
|
>
|
|
|
|
|
GOT IT, NEXT
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- STEP: CHOOSE CONNECTION MODE -->
|
|
|
|
|
<template v-else-if="step === 'choose-connection'">
|
|
|
|
|
<div class="text-center mb-5">
|
|
|
|
|
<h2 class="font-display font-black text-2xl tracking-wider gradient-text mb-2">
|
|
|
|
|
CONNECTION MODE
|
|
|
|
|
</h2>
|
|
|
|
|
<p class="font-mono text-text-muted text-xs">
|
|
|
|
|
How should we reach <span class="text-neon-cyan">{{ botName }}</span>?
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="space-y-3 mb-5">
|
|
|
|
|
<!-- Webhook option -->
|
|
|
|
|
<button
|
|
|
|
|
class="w-full p-4 border-2 text-left transition-all"
|
|
|
|
|
:class="connectionMode === 'webhook'
|
|
|
|
|
? 'border-neon-cyan/60 bg-neon-cyan/10'
|
|
|
|
|
: 'border-border bg-surface hover:border-neon-cyan/30'"
|
|
|
|
|
@click="connectionMode = 'webhook'"
|
|
|
|
|
>
|
|
|
|
|
<div class="flex items-center gap-3 mb-1.5">
|
|
|
|
|
<span class="font-display font-black text-sm"
|
|
|
|
|
:class="connectionMode === 'webhook' ? 'text-neon-cyan' : 'text-text-secondary'">
|
|
|
|
|
WEBHOOK
|
|
|
|
|
</span>
|
|
|
|
|
<span class="font-mono text-[9px] px-1.5 py-0.5 border border-neon-cyan/30 text-neon-cyan">RECOMMENDED</span>
|
|
|
|
|
</div>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted leading-relaxed">
|
|
|
|
|
We POST challenges to your server. Fastest response times.
|
|
|
|
|
Requires a public URL (ngrok, cloudflared, VPS, etc.)
|
|
|
|
|
</p>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<!-- Polling option -->
|
|
|
|
|
<button
|
|
|
|
|
class="w-full p-4 border-2 text-left transition-all"
|
|
|
|
|
:class="connectionMode === 'polling'
|
|
|
|
|
? 'border-neon-purple/60 bg-neon-purple/10'
|
|
|
|
|
: 'border-border bg-surface hover:border-neon-purple/30'"
|
|
|
|
|
@click="connectionMode = 'polling'"
|
|
|
|
|
>
|
|
|
|
|
<div class="flex items-center gap-3 mb-1.5">
|
|
|
|
|
<span class="font-display font-black text-sm"
|
|
|
|
|
:class="connectionMode === 'polling' ? 'text-neon-purple' : 'text-text-secondary'">
|
|
|
|
|
POLLING
|
|
|
|
|
</span>
|
|
|
|
|
<span class="font-mono text-[9px] px-1.5 py-0.5 border border-border text-text-muted">EASIEST</span>
|
|
|
|
|
</div>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted leading-relaxed">
|
|
|
|
|
Your bot polls us for challenges. No public URL needed.
|
|
|
|
|
Runs from any machine — just keep the script running.
|
|
|
|
|
</p>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Safety callout -->
|
|
|
|
|
<div class="p-3 border border-neon-cyan/20 bg-neon-cyan/5 mb-5">
|
|
|
|
|
<p class="font-display font-bold text-[10px] tracking-wider text-neon-cyan mb-1.5">
|
|
|
|
|
SAFE & SIMPLE
|
|
|
|
|
</p>
|
|
|
|
|
<ul class="font-mono text-[10px] text-text-muted space-y-1 leading-relaxed">
|
|
|
|
|
<li v-if="connectionMode === 'webhook'">
|
|
|
|
|
We only send <span class="text-text-secondary">POST</span> requests with fight questions — nothing else
|
|
|
|
|
</li>
|
|
|
|
|
<li v-if="connectionMode === 'polling'">
|
|
|
|
|
<span class="text-text-secondary">No incoming connections</span> — your bot only makes outbound requests
|
|
|
|
|
</li>
|
|
|
|
|
<li>Private IPs and internal URLs are <span class="text-text-secondary">blocked</span></li>
|
|
|
|
|
<li>Payloads are small JSON (<span class="text-text-secondary"><2KB</span>)</li>
|
|
|
|
|
<li>Your <span class="text-text-secondary">API keys stay on your machine</span> — we never see them</li>
|
|
|
|
|
<li>
|
|
|
|
|
<span class="text-text-secondary">Give the setup guide to your AI</span> and it builds the bot for you
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="flex gap-2">
|
|
|
|
|
<button
|
|
|
|
|
class="flex-1 py-3 border-2 border-border text-text-muted font-display font-bold text-sm tracking-wider
|
|
|
|
|
hover:border-neon-purple/40 transition-all"
|
|
|
|
|
@click="step = 'bot-setup'"
|
|
|
|
|
>
|
|
|
|
|
BACK
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
v-if="connectionMode === 'webhook'"
|
|
|
|
|
class="flex-1 py-3 bg-neon-cyan/10 border-2 border-neon-cyan/50 text-neon-cyan
|
|
|
|
|
font-display font-bold text-sm tracking-wider
|
|
|
|
|
hover:bg-neon-cyan/20 transition-all"
|
|
|
|
|
@click="step = 'add-webhook'"
|
|
|
|
|
>
|
|
|
|
|
SET UP WEBHOOK
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
v-else
|
|
|
|
|
class="flex-1 py-3 bg-neon-purple/10 border-2 border-neon-purple/50 text-neon-purple
|
|
|
|
|
font-display font-bold text-sm tracking-wider
|
|
|
|
|
hover:bg-neon-purple/20 transition-all"
|
|
|
|
|
@click="confirmPolling"
|
|
|
|
|
>
|
|
|
|
|
CREATE BOT
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- STEP: ADD WEBHOOK -->
|
|
|
|
|
<template v-else-if="step === 'add-webhook'">
|
|
|
|
|
<div class="text-center mb-5">
|
|
|
|
@@ -1042,7 +1139,7 @@ function handleSignOut() {
|
|
|
|
|
<button
|
|
|
|
|
class="flex-1 py-3 border-2 border-border text-text-muted font-display font-bold text-sm tracking-wider
|
|
|
|
|
hover:border-neon-purple/40 transition-all"
|
|
|
|
|
@click="step = 'bot-setup'"
|
|
|
|
|
@click="step = 'choose-connection'"
|
|
|
|
|
>
|
|
|
|
|
BACK
|
|
|
|
|
</button>
|
|
|
|
@@ -1229,6 +1326,56 @@ function handleSignOut() {
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Bot credentials (shown once after registration) -->
|
|
|
|
|
<div v-if="botSecret" class="mb-4 p-3 border border-neon-cyan/20 bg-neon-cyan/5">
|
|
|
|
|
<p class="font-display font-bold text-[10px] tracking-wider text-neon-cyan mb-2">
|
|
|
|
|
2 STEPS — THEN YOU'RE FIGHTING
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<!-- Step 1: Download -->
|
|
|
|
|
<div class="flex items-start gap-2.5 mb-3">
|
|
|
|
|
<span class="font-display font-black text-neon-cyan text-xs mt-0.5 shrink-0">1</span>
|
|
|
|
|
<div class="flex-1">
|
|
|
|
|
<p class="font-mono text-[10px] text-text-primary mb-1.5">Download BOTFIGHTS.md into your workspace</p>
|
|
|
|
|
<button
|
|
|
|
|
class="w-full py-1.5 border border-border text-[9px] font-display font-bold tracking-wider
|
|
|
|
|
text-text-muted hover:border-neon-cyan/40 hover:text-neon-cyan transition-all"
|
|
|
|
|
@click="downloadSetupGuide"
|
|
|
|
|
>
|
|
|
|
|
DOWNLOAD BOTFIGHTS.md
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Step 2: Tell your AI -->
|
|
|
|
|
<div class="flex items-start gap-2.5 mb-3">
|
|
|
|
|
<span class="font-display font-black text-neon-cyan text-xs mt-0.5 shrink-0">2</span>
|
|
|
|
|
<div class="flex-1">
|
|
|
|
|
<p class="font-mono text-[10px] text-text-primary mb-1.5">Tell your AI this:</p>
|
|
|
|
|
<div class="bg-bg border border-border p-2.5 font-mono text-[10px] text-text-muted leading-relaxed select-all">
|
|
|
|
|
<p class="text-text-primary">Read BOTFIGHTS.md and follow the setup instructions. Here are my credentials:</p>
|
|
|
|
|
<div class="mt-1">
|
|
|
|
|
<div>BOT_ID=<span class="text-neon-cyan">{{ botId }}</span></div>
|
|
|
|
|
<div>BOT_SECRET=<span class="text-neon-cyan">{{ botSecret }}</span></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
|
|
|
|
class="w-full mt-1.5 py-1.5 border border-border text-[9px] font-display font-bold tracking-wider
|
|
|
|
|
hover:border-neon-cyan/40 hover:text-neon-cyan transition-all"
|
|
|
|
|
:class="setupGuideCopied ? 'text-neon-cyan border-neon-cyan/40' : 'text-text-muted'"
|
|
|
|
|
@click="copyFullPrompt"
|
|
|
|
|
>
|
|
|
|
|
{{ setupGuideCopied ? 'COPIED' : 'COPY FULL PROMPT' }}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<p class="font-mono text-[9px] text-text-muted/60 leading-relaxed">
|
|
|
|
|
Your AI reads the guide, creates the bot, and starts it for you.
|
|
|
|
|
Save these credentials — the secret won't be shown again.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="mb-4 text-center">
|
|
|
|
|
<p class="font-mono text-xs">
|
|
|
|
|
<span class="text-neon-purple font-bold text-lg">{{ queueCount }}</span>
|
|
|
|
@@ -1285,7 +1432,7 @@ function handleSignOut() {
|
|
|
|
|
<!-- Wallet connect (shown if no wallet) -->
|
|
|
|
|
<WalletConnect v-if="!isHumanMode && !bot.isHuman" />
|
|
|
|
|
|
|
|
|
|
<!-- Practice fight — against bland classic bots, free -->
|
|
|
|
|
<!-- Training fight — against bland classic bots, free -->
|
|
|
|
|
<div class="pt-2 border-t border-border/30">
|
|
|
|
|
<button
|
|
|
|
|
class="w-full py-3 bg-white/3 border border-white/10 text-text-muted
|
|
|
|
@@ -1297,10 +1444,10 @@ function handleSignOut() {
|
|
|
|
|
@click="practice"
|
|
|
|
|
>
|
|
|
|
|
<span v-if="isJoiningPractice" class="w-4 h-4 border-2 border-white/20 border-t-white/50 rounded-full animate-spin" />
|
|
|
|
|
{{ isJoiningPractice ? 'STARTING...' : 'PRACTICE' }}
|
|
|
|
|
{{ isJoiningPractice ? 'STARTING...' : 'TRAINING' }}
|
|
|
|
|
</button>
|
|
|
|
|
<p class="font-mono text-[10px] text-text-muted/50 text-center mt-1">
|
|
|
|
|
Free sparring against practice bots — no ELO impact
|
|
|
|
|
Free sparring against training bots — no ELO impact
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|