v23: React + TypeScript SPA migration

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
This commit is contained in:
KoalaTrapLord committed 2026-03-12 01:12:02 +00:00
1 parent e6b5e0cedb
commit a5fe8cb3fa
376 files changed
+18367 -22656

No files matched your search

+69 -28
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Commander AI Lab — Auto Deck Generator</title>
<title>Commander AI Lab — Auto Deck Generator V3</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" />
@@ -39,13 +39,37 @@
</div>
<div class="dg-commander-selected" id="dg-commander-selected" style="display:none">
<div class="dg-commander-card">
<button class="dg-commander-clear" id="dg-commander-clear" title="Clear commander">&times;</button>
<img class="dg-commander-img" id="dg-commander-img" src="" alt="" />
<div class="dg-commander-info">
<div class="dg-commander-name" id="dg-commander-name"></div>
<div class="dg-commander-type" id="dg-commander-type"></div>
<div class="dg-commander-colors" id="dg-commander-colors"></div>
</div>
<button class="dg-commander-clear" id="dg-commander-clear" title="Clear commander">&times;</button>
</div>
</div>
</div>
</div>
<!-- Strategy & Bracket -->
<div class="dg-panel">
<div class="dg-panel-header">
<span class="dg-panel-title">Strategy &amp; Power</span>
</div>
<div class="dg-panel-body">
<input
type="text"
class="dg-input"
id="dg-strategy"
placeholder="e.g. zombie tokens, voltron, spellslinger..."
/>
<div class="dg-bracket-row">
<label class="dg-label">Target Bracket</label>
<div class="dg-bracket-btns" id="dg-bracket-btns">
<button class="dg-bracket-btn" data-bracket="1" title="Casual — precon level">B1</button>
<button class="dg-bracket-btn" data-bracket="2" title="Upgraded Casual">B2</button>
<button class="dg-bracket-btn dg-bracket-active" data-bracket="3" title="High Power">B3</button>
<button class="dg-bracket-btn" data-bracket="4" title="cEDH — maximum power">B4</button>
</div>
</div>
</div>
@@ -66,27 +90,25 @@
</div>
</div>
<!-- Source Toggles -->
<!-- Budget -->
<div class="dg-panel">
<div class="dg-panel-header">
<span class="dg-panel-title">Template Sources</span>
<span class="dg-panel-title">Budget</span>
</div>
<div class="dg-panel-body">
<div class="dg-source-toggles" id="dg-source-toggles">
<!-- Populated by JS from /api/deck-generator/config -->
</div>
</div>
</div>
<!-- Card Type Ratios -->
<div class="dg-panel">
<div class="dg-panel-header">
<span class="dg-panel-title">Card Type Targets</span>
<span class="dg-panel-hint" id="dg-total-hint">100 / 100</span>
</div>
<div class="dg-panel-body">
<div class="dg-ratio-list" id="dg-ratio-list">
<!-- Populated by JS from config defaults -->
<div class="dg-budget-row">
<input
type="number"
class="dg-input dg-input-narrow"
id="dg-budget"
placeholder="No limit"
min="0"
step="25"
/>
<select class="dg-select" id="dg-budget-mode">
<option value="total">Total Deck $</option>
<option value="per_card">Per Card Max $</option>
</select>
</div>
</div>
</div>
@@ -98,22 +120,29 @@
</div>
<div class="dg-panel-body">
<label class="dg-toggle-row">
<span class="dg-toggle-label">Only cards I own</span>
<input type="checkbox" class="dg-toggle-check" id="dg-only-owned" />
<span class="dg-toggle-label">Use my collection</span>
<input type="checkbox" class="dg-toggle-check" id="dg-use-collection" checked />
<span class="dg-toggle-track"><span class="dg-toggle-thumb"></span></span>
</label>
<label class="dg-toggle-row">
<span class="dg-toggle-label">Allow proxies (from sources)</span>
<input type="checkbox" class="dg-toggle-check" id="dg-allow-proxies" checked />
<span class="dg-toggle-label">Smart Substitution</span>
<input type="checkbox" class="dg-toggle-check" id="dg-substitution" checked />
<span class="dg-toggle-track"><span class="dg-toggle-thumb"></span></span>
</label>
<div class="dg-model-row">
<label class="dg-label">AI Model</label>
<select class="dg-select" id="dg-model">
<option value="sonar">Sonar (fast, ~$0.004)</option>
<option value="sonar-pro">Sonar Pro (deep, ~$0.04)</option>
</select>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="dg-actions">
<button class="dg-btn dg-btn-preview" id="dg-preview-btn" disabled>
<span class="dg-btn-icon">👁</span> Preview Deck
<span class="dg-btn-icon">⚡</span> Generate Deck
</button>
</div>
@@ -124,26 +153,32 @@
<!-- Empty State -->
<div class="dg-empty-state" id="dg-empty-state">
<div class="dg-empty-icon">⚡</div>
<div class="dg-empty-title">Configure &amp; Preview</div>
<div class="dg-empty-text">Select a commander and click "Preview Deck" to generate a deck from your collection.</div>
<div class="dg-empty-title">V3 AI Deck Generator</div>
<div class="dg-empty-text">Select a commander, set your bracket and strategy, then click "Generate Deck" to build a complete 100-card deck using Perplexity AI with structured output.</div>
</div>
<!-- Loading State -->
<div class="dg-loading" id="dg-loading" style="display:none">
<div class="dg-spinner"></div>
<div class="dg-loading-text">Generating deck...</div>
<div class="dg-loading-text" id="dg-loading-text">Generating deck via Perplexity AI...</div>
</div>
<!-- Results -->
<div class="dg-results" id="dg-results" style="display:none">
<!-- Strategy & Bracket Summary -->
<div class="dg-strategy-bar" id="dg-strategy-bar"></div>
<!-- Stats Bar -->
<div class="dg-stats-bar" id="dg-stats-bar"></div>
<!-- Substitution Summary -->
<div class="dg-sub-summary" id="dg-sub-summary" style="display:none"></div>
<!-- Card Groups -->
<div class="dg-card-groups" id="dg-card-groups"></div>
<!-- Save Bar -->
<!-- Export & Save Bar -->
<div class="dg-save-bar" id="dg-save-bar">
<button class="dg-btn dg-btn-save" id="dg-save-btn">
<span class="dg-btn-icon">💾</span> Save to Deck Builder
@@ -151,6 +186,12 @@
<button class="dg-btn dg-btn-ghost" id="dg-regenerate-btn">
<span class="dg-btn-icon">🔄</span> Regenerate
</button>
<div class="dg-export-group">
<button class="dg-btn dg-btn-export" id="dg-export-csv">📊 CSV</button>
<button class="dg-btn dg-btn-export" id="dg-export-dck">🎮 Forge .dck</button>
<button class="dg-btn dg-btn-export" id="dg-export-moxfield">📋 Moxfield</button>
<button class="dg-btn dg-btn-export" id="dg-export-shopping">🛒 Shopping List</button>
</div>
</div>
</div>
</section>