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

128 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`](../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.