- Add full CSS for precon grid: responsive card grid, hover effects,
install button states, color pips, dark theme styling
- Build commander art_crop image URLs from set_code/number during
precon download (Scryfall direct image redirect, no API call needed)
- Batch-fetch color_identity from Scryfall /cards/collection endpoint
(2 requests for ~145 unique commanders) and store in precon index
- Widen precon modal to 900px for grid layout
- Auto-invalidate precon cache when enrichment fields are missing
- JS renderer now shows commander art crop + card body layout
- Bump cache-busting version to v=4
Browser was serving the old Base64-encoded ai-lab.js from cache.
Adding ?v=3 query parameter forces the browser to fetch the new
decoded JavaScript file.
The entire ai-lab.js file (139KB) was stored as a single Base64-encoded
string instead of actual JavaScript. The browser loaded it as a script
but couldn't parse it — AiLab was never defined, so the batch sim panel
never rendered.
Decoded the file back to its proper 2531-line JavaScript source. The
file already includes auto-initialization at the bottom:
if (document.readyState === 'loading')
document.addEventListener('DOMContentLoaded', AiLab.init);
else AiLab.init();
Also removed the now-redundant DOMContentLoaded listener from
index.html since the JS file handles init itself.
The ai-lab.js IIFE defines the AiLab global with an init() method that
calls renderPanel(), detectBackend(), loadAvailableDecks(),
loadMetaCommanders(), loadPrecons(), and bindEvents() — but nothing
ever invoked it. Added a DOMContentLoaded listener after the script tag
in index.html to trigger initialization.
- Added _fill_basic_lands() post-processing for V1 and V3 generators
- Strips LLM-provided basics, then distributes evenly across commander colors
- Always hits exactly 100 cards regardless of LLM output
- Frontend group counts now sum card.count instead of entry count
- Cards with count > 1 show '5x Forest' style prefix
- Colorless commanders get Wastes
Previously the coach required running simulations first to generate a
deck-reports/*.json file. Now if no simulation report is found, the
endpoint builds a lightweight DeckReport on the fly from the deck_cards
table — pulling card names, types, CMC, and color identity directly from
the DB. The LLM can still analyze composition and give advice, just
without simulation-derived performance metrics (impactScore, etc.).
Also removes the '[no report]' tag from the dropdown since all decks
can now be coached.
Backend returns [{deck_id, deck_name, commander, has_report, ...}] directly,
not {decks: [...]}. Frontend was reading data.decks which was always undefined.
Now properly unwraps the array, shows deck name + commander, slugifies name
for coach session deck_id, and marks decks without reports.