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:
1 parent
e6b5e0cedb
commit
a5fe8cb3fa
376 files changed
+18367
-22656
No files matched your search
@@ -701,8 +701,20 @@ public class BatchRunner {
|
||||
try {
|
||||
Path path = Path.of(deckFilePath);
|
||||
if (!Files.exists(path)) {
|
||||
System.err.println("[COACH] Deck file not found: " + deckFilePath);
|
||||
return names;
|
||||
// Try adding .dck extension
|
||||
Path withExt = Path.of(deckFilePath + ".dck");
|
||||
if (Files.exists(withExt)) {
|
||||
path = withExt;
|
||||
} else {
|
||||
// Try resolving against Forge commander decks directory
|
||||
Path resolved = resolveForgeDecksPath(deckFilePath);
|
||||
if (resolved != null && Files.exists(resolved)) {
|
||||
path = resolved;
|
||||
} else {
|
||||
System.err.println("[COACH] Deck file not found: " + deckFilePath);
|
||||
return names;
|
||||
}
|
||||
}
|
||||
}
|
||||
List<String> lines = Files.readAllLines(path);
|
||||
String section = "Main";
|
||||
@@ -737,6 +749,36 @@ public class BatchRunner {
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a deck name to its full path in the Forge commander decks directory.
|
||||
* Checks %APPDATA%/Forge/decks/commander/ on Windows.
|
||||
*/
|
||||
private Path resolveForgeDecksPath(String deckName) {
|
||||
try {
|
||||
String appdata = System.getenv("APPDATA");
|
||||
if (appdata == null || appdata.isEmpty()) return null;
|
||||
Path decksDir = Path.of(appdata, "Forge", "decks", "commander");
|
||||
if (!Files.isDirectory(decksDir)) return null;
|
||||
|
||||
// Try with .dck extension
|
||||
String name = deckName;
|
||||
if (!name.endsWith(".dck")) name = name + ".dck";
|
||||
Path candidate = decksDir.resolve(name);
|
||||
if (Files.exists(candidate)) return candidate;
|
||||
|
||||
// Case-insensitive fallback
|
||||
String lowerName = name.toLowerCase();
|
||||
try (var stream = Files.list(decksDir)) {
|
||||
return stream
|
||||
.filter(p -> p.getFileName().toString().toLowerCase().equals(lowerName))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build per-card game stats from verbose Forge log output.
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user