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
@@ -22,7 +22,6 @@ TOC and the body iterate the same data.
-->
<script lang="ts">
import { onMount } from "svelte";
import { page } from "$app/state";
import ReportToc, {
type TocEntry,
@@ -71,8 +70,6 @@ TOC and the body iterate the same data.
{ slug: "unidentified-groups", titleKey: "game.report.section.unidentified_groups.title" },
];
const gameId = $derived(page.params.id ?? "");
let activeSlug = $state<string>(ENTRIES[0]?.slug ?? "");
let bodyEl: HTMLDivElement | null = $state(null);
@@ -116,7 +113,7 @@ TOC and the body iterate the same data.
</script>
<div class="report-view" data-testid="active-view-report">
<ReportToc entries={ENTRIES} {activeSlug} {gameId} />
<ReportToc entries={ENTRIES} {activeSlug} />
<div class="report-body" bind:this={bodyEl}>
<SectionGalaxySummary />