feat(ui): app-shell core — single-route dispatcher, route collapse, nav→state

Collapse the game UI to one route (`/`): a screen dispatcher renders
login/lobby/lobby-create/game from `appScreen`/`activeView` state instead of
URL routes. Move screen components to lib/screens & lib/game; the game shell
reads the game id from `appScreen.gameId` and re-inits per-game stores via an
$effect; in-game views render from `activeView`. Flip ~23 goto/href nav sites
to store mutations; drop the `?sidebar=` URL coupling. Auth gate is now
state-based. WIP: browser-history (Back→lobby), restore-validation, the
return-to-lobby button, push deep-links, and the test migration are follow-ups
on this branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-23 20:04:04 +02:00
parent 182beebcd6
commit b6770d394c
30 changed files with 294 additions and 394 deletions
+1 -22
View File
@@ -2,10 +2,8 @@
import "$lib/theme/tokens.css";
import "$lib/theme/base.css";
import { onMount } from "svelte";
import { goto } from "$app/navigation";
import { page } from "$app/state";
import { dev } from "$app/environment";
import { appBase, withBase } from "$lib/paths";
import { withBase } from "$lib/paths";
import { i18n } from "$lib/i18n/index.svelte";
import { session } from "$lib/session-store.svelte";
import { eventStream } from "../api/events.svelte";
@@ -77,25 +75,6 @@
eventStream.stop();
streamSessionId = null;
}
// page.url.pathname includes the configured base path; strip it so
// the route comparisons below stay base-agnostic.
const pathname = page.url.pathname.slice(appBase.length);
// Debug-only routes under /__debug/* run their own bootstrap
// path against the storage primitives and must bypass the
// auth guard so Phase 6's Playwright spec can drive the
// keystore directly.
if (pathname.startsWith("/__debug/")) {
return;
}
if (session.status === "anonymous" && pathname !== "/login") {
void goto(withBase("/login"), { replaceState: true });
} else if (
session.status === "authenticated" &&
(pathname === "/login" || pathname === "/")
) {
void goto(withBase("/lobby"), { replaceState: true });
}
});
</script>