feat: production deployment — Dockerfile, docker-compose, SPA serving, classic bot fights
- Add Dockerfile (multi-stage: build frontend + server, serve from single container) - Add docker-compose.yml for Portainer stack deployment - Server serves frontend SPA in production (static assets + SPA fallback) - Auto-run migrations and seed mock bots on server startup - DB path configurable via DB_PATH env var - Add "Fight a Classic Bot" button for instant mock bot matches - FIGHT button queues for real AI opponents Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
120250c01a
commit
6bf9fe27b3
@@ -2,6 +2,8 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import PixelGlove from '../components/PixelGlove.vue'
|
||||
import SpritePreview from '../components/SpritePreview.vue'
|
||||
import HumanPreview from '../components/HumanPreview.vue'
|
||||
|
||||
interface FightResult {
|
||||
id: string
|
||||
@@ -12,6 +14,20 @@ interface FightResult {
|
||||
totalRounds: number
|
||||
}
|
||||
|
||||
// Decorative crowd characters scattered around the page
|
||||
const crowd = [
|
||||
{ seed: 'hero_lobster', arch: 'lobster', tier: 3, type: 'bot' as const, x: 3, y: 18, size: 56, wr: 0.9, delay: 0 },
|
||||
{ seed: 'hero_lobster', arch: 'lobster', tier: 3, type: 'human' as const, x: 8, y: 26, size: 44, wr: 0.9, delay: 0.2 },
|
||||
{ seed: 'cool_ninja', arch: 'ninja', tier: 4, type: 'bot' as const, x: 88, y: 15, size: 52, wr: 0.7, delay: 0.5 },
|
||||
{ seed: 'cool_ninja', arch: 'ninja', tier: 4, type: 'human' as const, x: 92, y: 22, size: 40, wr: 0.7, delay: 0.7 },
|
||||
{ seed: 'sad_potato', arch: 'potato', tier: 1, type: 'bot' as const, x: 5, y: 72, size: 48, wr: 0.1, delay: 1.0 },
|
||||
{ seed: 'sad_potato', arch: 'potato', tier: 1, type: 'human' as const, x: 10, y: 78, size: 38, wr: 0.1, delay: 1.2 },
|
||||
{ seed: 'blazin_dragon', arch: 'dragon', tier: 5, type: 'bot' as const, x: 85, y: 68, size: 60, wr: 0.95, delay: 0.3 },
|
||||
{ seed: 'blazin_dragon', arch: 'dragon', tier: 5, type: 'human' as const, x: 90, y: 76, size: 46, wr: 0.95, delay: 0.6 },
|
||||
{ seed: 'lil_duck', arch: 'rubber_duck', tier: 0, type: 'bot' as const, x: 92, y: 45, size: 40, wr: 0.3, delay: 1.5 },
|
||||
{ seed: 'chef_boy', arch: 'chef', tier: 2, type: 'bot' as const, x: 2, y: 48, size: 44, wr: 0.5, delay: 0.8 },
|
||||
]
|
||||
|
||||
const tagline = ref('')
|
||||
const fullTagline = 'A safe place to hash it out.'
|
||||
const isTypingDone = ref(false)
|
||||
@@ -39,8 +55,41 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-[calc(100vh-4rem)] flex flex-col items-center justify-center px-6 overflow-hidden">
|
||||
<div class="max-w-4xl w-full text-center slide-up">
|
||||
<div class="h-[calc(100vh-4rem)] flex flex-col items-center justify-center px-6 overflow-hidden relative">
|
||||
<!-- Crowd characters floating around edges -->
|
||||
<template v-for="(c, i) in crowd" :key="i">
|
||||
<SpritePreview
|
||||
v-if="c.type === 'bot'"
|
||||
:seed="c.seed"
|
||||
:archetype="c.arch"
|
||||
:tier="c.tier"
|
||||
:size="c.size"
|
||||
class="absolute pointer-events-none select-none crowd-bob"
|
||||
:style="{
|
||||
left: c.x + '%',
|
||||
top: c.y + '%',
|
||||
opacity: 0.35,
|
||||
animationDelay: c.delay + 's',
|
||||
}"
|
||||
/>
|
||||
<HumanPreview
|
||||
v-else
|
||||
:seed="c.seed"
|
||||
:archetype="c.arch"
|
||||
:size="c.size"
|
||||
:win-rate="c.wr"
|
||||
anim="idle"
|
||||
class="absolute pointer-events-none select-none crowd-bob"
|
||||
:style="{
|
||||
left: c.x + '%',
|
||||
top: c.y + '%',
|
||||
opacity: 0.3,
|
||||
animationDelay: c.delay + 's',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div class="max-w-4xl w-full text-center slide-up relative z-10">
|
||||
|
||||
<!-- BIG NEON TITLE -->
|
||||
<div class="mb-6">
|
||||
@@ -130,3 +179,14 @@ onMounted(async () => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.crowd-bob {
|
||||
animation: crowdBob 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes crowdBob {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-6px); }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user