- Removed toolbar (Library title, search, upload button) from library
- Moved search box into the sidebar alongside Year/Genre filters
- Removed 'Filters' heading from sidebar
- Replaced 'Library' nav button in header with '+ Upload' button
- Genre filters now sorted by count (desc) instead of alphabetically
- Cleaned up unused CSS
- GameCard: removed 'Ready' badge from covers (keep '🪟 Win' for
Windows games only — it's informative since they can't be played)
- Library: sidebar with Year + Genre checkbox filter groups
- Each shows up to 5 items with game counts, 'Show all' button
- Active filters shown as removable chips above the grid
- Reset button clears all filters
- Filters reflected in URL hash (#/?genre=FPS&year=1996)
- GameDetail: year and genre tags are now clickable links to
#/?year=1996 or #/?genre=FPS, which opens the Library with that
filter pre-applied
- App.svelte: parses query params from hash and passes to Library
When a game archive contains SETUP.EXE, INSTALL.EXE, CONFIG.EXE or
CUSTOM.EXE (or .com/.bat variants), the upload now:
- Detects and stores the path in game metadata (setup_exe / has_setup)
- Generates a second .jsdos bundle with SETUP.EXE as autoexec:
- Standard mode: {gameId}.setup.jsdos (full game + setup autoexec)
- Sockdrive mode: .jsdos-setup/ directory alongside .jsdos/
- Both bundles share the same game files; only the autoexec differs
Frontend changes:
- Router strips query params from hash so ?setup=1 doesn't leak into id
- GameDetail shows a "Setup" button between Play and Edit (only when
game.has_setup is true), navigating to /play/{id}?setup=1
- Play.svelte detects ?setup=1, loads setupBundleUrl() instead, and
shows setup-specific overlay text
Persistence is handled automatically by js-dos v8 via the built-in
auto-save mechanism (ci.persist() → browser OPFS), so any config
changes made via SETUP.EXE survive across sessions.