Files
galaxy-game/ui/docs/navigation.md
T
Ilia Denisov fc371c7fe1 ui/phase-10: in-game shell with view-replacement skeleton
Wraps every in-game route under `/games/:id/*` in a responsive shell
with a header (race / turn placeholders, view-menu dropdown or mobile
hamburger, account menu), a three-tab sidebar (Calculator, Inspector,
Order), an active-view slot, and a mobile-only bottom-tabs row
`[Map, Calc, Order, More]`. Every view in the IA section
(`map`, `table/:entity`, `report`, `battle/:battleId?`, `mail`,
`designer/{ship-class,science}/:id?`) ships as a thin SvelteKit route
that mounts a `lib/active-view/<name>.svelte` stub rendering a
localised `coming soon` body. The lobby's `gotoGame` path now actually
lands on a rendered shell instead of a 404.

The "view router" mentioned in the plan is implemented as the file
system plus two-line route wrappers — no separate dispatch component.
Sidebar tab state lives as a `$state` rune inside `sidebar.svelte`,
which sits in the layout that SvelteKit keeps mounted across child
route swaps, so tab choice survives every active-view navigation for
free. A `?sidebar=calc|inspector|order` URL param seeds the initial
tab on first mount; the mobile bottom-tabs use a layout-owned
`mobileTool` rune with a URL-gated `effectiveTool` derivation so the
Calc / Order tool overlay only applies on `/map` and naturally drops
when the user navigates elsewhere.

Tablet ships with a click-toggle drawer for the sidebar rather than
the IA section's swipe-from-right gesture; the structural breakpoint
satisfies Phase 10's acceptance criterion and Phase 35 polish lands
the swipe. The mobile More drawer mirrors the header view-menu
content; the IA's narrower More list (Mail, Battle, Tables, History,
Settings, Logout) is also a Phase 35 polish target once History
exists.

Topic doc `ui/docs/navigation.md` captures the active-view model, the
sidebar state-preservation rule, the `?sidebar=` and `mobileTool`
conventions, and the transient map-overlay back-stack concept (with
the implementation deferred to Phase 34 alongside its first user).
i18n catalogues for `en` and `ru` add the full `game.shell.*`,
`game.view.*`, `game.sidebar.*`, `game.bottom_tabs.*` namespaces.

Tests: Vitest covers the header view-menu (every IA destination
including the Tables sub-list), the account-menu Logout / Language
wiring, the sidebar default tab / switching / `?sidebar=` seed /
close button, and every active-view stub. Playwright e2e boots an
authenticated session via `__galaxyDebug.setDeviceSessionId` (no
gateway calls — the shell makes none in Phase 10), exercises every
view through both the desktop dropdown and the mobile More drawer,
verifies sidebar tab survival across navigation, and uses
`setViewportSize` to validate the breakpoint switches at 768 px and
1024 px.

Phase 10 status stays `pending` in `ui/PLAN.md` until the local-ci
run lands green; flipping to `done` follows in the next commit per
the per-stage CI gate in `CLAUDE.md`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 20:15:49 +02:00

6.4 KiB
Raw Blame History

In-game shell — navigation model

This doc covers the chrome that wraps every in-game view: the responsive layout shell, the active-view router built on SvelteKit's file-system routes, the sidebar with three tools and its state-preservation rule, and the mobile bottom-tabs. The user-facing spec — view list, breakpoint diagrams, history-mode plans — lives in ../PLAN.md, section Information Architecture and Navigation. This doc is the source of truth for how those rules are implemented.

Active-view model

The client renders one active view at a time. Every active view is a SvelteKit route under routes/games/[id]/; the route file is a two-line wrapper that mounts the matching content component from src/lib/active-view/<name>.svelte. The "view router" mentioned in the plan is the file system plus those wrappers — there is no separate dispatch component.

URL Active view component Phase that fills it
/games/:id/map lib/active-view/map.svelte Phase 11
/games/:id/table/:entity lib/active-view/table.svelte Phase 11 / 17 / 19 / 22
/games/:id/report lib/active-view/report.svelte Phase 23
/games/:id/battle/:battleId? lib/active-view/battle.svelte Phase 27
/games/:id/mail lib/active-view/mail.svelte Phase 28
/games/:id/designer/ship-class/:id? lib/active-view/designer-ship-class.svelte Phase 17 / 18
/games/:id/designer/science/:id? lib/active-view/designer-science.svelte Phase 21

/games/:id (no trailing view) redirects to /games/:id/map. The optional :id? segments on the designer routes match SvelteKit's [[id]] syntax — they accept both the new-draft and editing URLs; later phases read the param when wiring real content.

The entity slug on the table route is kebab-case (planets, ship-classes, ship-groups, fleets, sciences, races); the table stub maps it to the matching game.view.table.<snake> i18n key.

Sidebar tools and state preservation

The desktop sidebar hosts three tools:

Tool Component Phase that fills it
Calculator lib/sidebar/calculator-tab.svelte Phase 30
Inspector lib/sidebar/inspector-tab.svelte Phase 13 / 19
Order lib/sidebar/order-tab.svelte Phase 12 / 14

The sidebar's selected-tab state is a $state rune inside lib/sidebar/sidebar.svelte. The component is mounted by the layout at routes/games/[id]/+layout.svelte, and SvelteKit keeps that layout instance alive while the user navigates between child routes (/games/:id/map/games/:id/report → …). The rune therefore survives every active-view switch automatically, with no URL coupling needed.

A ?sidebar=calc|calculator|inspector|order URL param is read once on mount and seeds the initial tab. Later phases that want to land the user on a particular tool (for example, Phase 14's first end-to-end command flow) can set it on navigation.

Layout breakpoints

Three discrete CSS modes matched to the IA section diagrams:

  • ≥ 1024 px (desktop) — the sidebar sits beside the active view and is always rendered. The header view-menu trigger uses the dropdown icon (▾). Bottom-tabs and the tablet sidebar-toggle are CSS-hidden.
  • 7681024 px (tablet) — the sidebar collapses behind a click toggle in the header right corner. Tapping the toggle slides the sidebar in as a fixed overlay above the active view; a close button on the sidebar dismisses it. The full swipe-from-right gesture in the IA section is deferred to Phase 35 polish — the click toggle satisfies the "layout switches at 768 px" acceptance criterion on Phase 10.
  • < 768 px (mobile) — the sidebar is hidden entirely and the bottom-tabs row appears at the bottom of the viewport. The view-menu trigger swaps to a hamburger icon (☰) that opens the drop-down as a full-width drawer below the header.

Inspector is intentionally unreachable on mobile in Phase 10. Per the IA section the mobile inspector is a bottom-sheet raised by tapping a map object, and that mechanism waits for Phase 13.

Mobile bottom-tabs and tool overlay

The bottom-tabs row is [Map, Calc, Order, More]. Map navigates to /games/:id/map and clears any tool overlay. Calc and Order navigate to /games/:id/map too — but they also flip the layout's mobileTool state to calc / order, which the layout uses to swap the active-view slot for the Calculator / Order tool component.

The tool overlay only applies when the URL is /map. Navigating to any other view through the More drawer or the header view-menu makes the layout's derived effectiveTool collapse back to map, so the user always sees the URL's active view rather than a stale overlay. The next time the user taps a Calc or Order bottom-tab, the navigation re-routes them to /map and re-applies the overlay.

The More button opens a drawer that mirrors the header view-menu content. The IA section's narrower "More" list (Mail, Battle log, Tables, History, Settings, Logout) is the polish target for Phase 35 — Phase 10 keeps a single source of truth for destinations.

Transient map overlays

Some views can push a transient overlay onto /map with a back affordance — for example, the ship-class designer pushes a range-preview overlay onto the map. The transient overlay clears when the user navigates to any other view via the header or the bottom-tabs.

Phase 10 documents this concept but does not implement the back-stack mechanism. Phase 34 lands the back-stack alongside its first user (multi-turn projection, range circles in the ship-class designer).

Auth gate

The root +layout.svelte redirects anonymous → /login for any non-/__debug/ path; the in-game shell inherits that gate without any extra check. When a session is revoked while the user is in the shell, the same redirect fires through the existing revocation watcher.