Complete frontend rewrite from vanilla HTML/CSS/JS to React 19 + TypeScript + Tailwind CSS v4. Frontend (frontend/commander-ai-lab-ui/): - Vite + React 19 + TypeScript + Tailwind CSS v4 + React Router - 7 pages: BatchSim, Collection, Decks, AutoGen, Simulator, Coach, Training - Reusable components: Spinner, StatusBadge, CardImage, ManaSymbols, EmptyState - Full typed API client layer (8 modules matching all backend endpoints) - Recharts for mana curves, win rate charts, progress bars - Lucide icons throughout - Custom dark theme with MTG mana color tokens - Code splitting (recharts + react in separate chunks) Backend (lab_api.py): - SPA-aware static file serving with catch-all for client-side routing - Fallback to legacy ui/ folder if React dist not built - Updated startup log with SPA route list Build system: - .gitignore added (was missing - removes pyc, db, node_modules, etc.) - build-full-installer-v2.py: v23 with Node.js check + React build step - Vite build config with manual chunks for optimal loading Also includes: V3 deck generator fixes, coach improvements, DeepSeek sim modules
129 lines
6.5 KiB
HTML
129 lines
6.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Commander AI Lab — Simulator</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
|
<link rel="stylesheet" href="global.css" />
|
|
<link rel="stylesheet" href="simulator.css" />
|
|
</head>
|
|
<body>
|
|
<script src="nav.js"></script>
|
|
<div class="page">
|
|
<div class="page-header">
|
|
<h1>Deck Simulator</h1>
|
|
<p>Monte Carlo engine — test your deck against an AI training opponent</p>
|
|
</div>
|
|
|
|
<!-- Config Panel -->
|
|
<section class="panel">
|
|
<div class="sim-config-row">
|
|
<div class="form-group sim-field-grow">
|
|
<label class="form-label" for="sim-deck-select">Deck</label>
|
|
<select id="sim-deck-select" class="form-select">
|
|
<option value="">-- Select a deck --</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group sim-field-auto">
|
|
<label class="form-label" for="sim-opponent-select">Opponent</label>
|
|
<select id="sim-opponent-select" class="form-select">
|
|
<option value="heuristic">Heuristic AI</option>
|
|
<option value="deepseek">DeepSeek AI</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group sim-field-auto">
|
|
<label class="form-label" for="sim-num-games">Games</label>
|
|
<input type="number" id="sim-num-games" class="form-input sim-num-input" value="10" min="1" max="1000" />
|
|
</div>
|
|
<div class="form-group sim-field-auto sim-field-end">
|
|
<label class="form-checkbox">
|
|
<input type="checkbox" id="sim-record-logs" checked />
|
|
Record turn logs
|
|
</label>
|
|
</div>
|
|
<div class="form-group sim-field-auto sim-field-end">
|
|
<button id="sim-run-btn" class="btn btn-primary" disabled>Run Simulation</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DeepSeek Config Panel -->
|
|
<div id="deepseek-panel" class="deepseek-panel" style="display:none;">
|
|
<div class="deepseek-header">
|
|
<div class="deepseek-title">
|
|
<span class="deepseek-icon">🧠</span>
|
|
<span>DeepSeek AI Brain</span>
|
|
<span id="ds-status-badge" class="ds-badge ds-badge-off">Disconnected</span>
|
|
</div>
|
|
<button id="ds-connect-btn" class="btn btn-sm btn-outline">Connect</button>
|
|
</div>
|
|
<div class="deepseek-config-row">
|
|
<div class="form-group sim-field-grow">
|
|
<label class="form-label" for="ds-api-base">API Endpoint</label>
|
|
<input type="text" id="ds-api-base" class="form-input" value="http://192.168.0.122:1234" placeholder="http://192.168.0.122:1234" />
|
|
</div>
|
|
<div class="form-group sim-field-auto">
|
|
<label class="form-label" for="ds-model">Model</label>
|
|
<input type="text" id="ds-model" class="form-input" value="" placeholder="Auto-detect" style="width:200px" />
|
|
</div>
|
|
<div class="form-group sim-field-auto">
|
|
<label class="form-label" for="ds-temp">Temp</label>
|
|
<input type="number" id="ds-temp" class="form-input sim-num-input" value="0.3" min="0" max="2" step="0.1" />
|
|
</div>
|
|
<div class="form-group sim-field-auto">
|
|
<label class="form-label" for="ds-timeout">Timeout (s)</label>
|
|
<input type="number" id="ds-timeout" class="form-input sim-num-input" value="10" min="1" max="60" />
|
|
</div>
|
|
</div>
|
|
<div id="ds-stats-row" class="deepseek-stats-row" style="display:none;">
|
|
<span id="ds-stat-calls" class="ds-stat">Calls: 0</span>
|
|
<span id="ds-stat-cache" class="ds-stat">Cache: 0</span>
|
|
<span id="ds-stat-fallback" class="ds-stat">Fallbacks: 0</span>
|
|
<span id="ds-stat-latency" class="ds-stat">Avg: 0ms</span>
|
|
<span id="ds-stat-model" class="ds-stat"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Or paste decklist -->
|
|
<details class="sim-paste-section">
|
|
<summary>Or paste a decklist</summary>
|
|
<textarea id="sim-paste-decklist" class="form-textarea" rows="8" placeholder="1 Sol Ring 1 Cultivate 37 Forest ..."></textarea>
|
|
<div class="sim-paste-controls">
|
|
<input type="text" id="sim-paste-name" class="form-input" placeholder="Deck name" value="Custom Deck" />
|
|
<button id="sim-paste-run-btn" class="btn btn-primary">Run from Paste</button>
|
|
</div>
|
|
</details>
|
|
|
|
<!-- Progress bar -->
|
|
<div id="sim-progress" class="progress-container" style="display:none;">
|
|
<div class="flex justify-between mb-2">
|
|
<span id="sim-progress-text" class="progress-label">Running...</span>
|
|
<span id="sim-progress-count" class="text-sm text-muted text-mono">0 / 0</span>
|
|
</div>
|
|
<div class="progress-track">
|
|
<div id="sim-progress-bar" class="progress-fill"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Results -->
|
|
<section id="sim-results" style="display:none;">
|
|
<!-- Stats Summary -->
|
|
<div class="sim-stats-grid stat-grid" id="sim-stats-grid"></div>
|
|
|
|
<!-- Game List -->
|
|
<div class="panel sim-games-section">
|
|
<div class="panel-header">
|
|
<h2>Game-by-Game Results</h2>
|
|
</div>
|
|
<div id="sim-games-list" class="sim-games-list"></div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<script src="simulator.js"></script>
|
|
</body>
|
|
</html>
|