OrderDraftStore persists per-game command drafts in Cache; the sidebar Order tab renders the list with a per-row delete control. The layout passes a `historyMode` prop through Sidebar / BottomTabs as a constant `false`, so Phase 26 only flips the source. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7.2 KiB
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.
The Order entry is hidden when the layout's historyMode flag is
true. Phase 12 plumbs the flag end-to-end as a prop —
+layout.svelte passes a constant false to Sidebar, which
forwards hideOrder to its TabBar; the same flag goes to
BottomTabs so the mobile Order button is also suppressed. A
?sidebar=order URL seed that arrives while the flag is true falls
back to inspector, and an $effect on the sidebar resets
activeTab away from order if the flag flips on mid-session.
Phase 26 introduces lib/history-mode.ts and replaces the constant
with the live signal; the order draft survives the toggle because
OrderDraftStore lives one level above the sidebar in the layout
hierarchy. See order-composer.md for the
draft-store side of the flow.
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.
- 768–1024 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.