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>
This commit is contained in:
+114
-29
@@ -1032,44 +1032,129 @@ Goal: assemble the in-game layout shell (header, sidebar, main area)
|
||||
with empty placeholder content for every view, so navigation works
|
||||
end-to-end before any data is wired.
|
||||
|
||||
Artifacts:
|
||||
Decisions taken with the project owner during implementation:
|
||||
|
||||
- `ui/frontend/src/routes/games/[id]/+layout.svelte` shell layout with
|
||||
responsive breakpoints (desktop / tablet / mobile)
|
||||
- `ui/frontend/src/lib/header/` header component: race name, turn
|
||||
counter (static placeholder `turn ?`), view dropdown / hamburger,
|
||||
account menu
|
||||
- `ui/frontend/src/lib/sidebar/` sidebar with three tabs (Calculator,
|
||||
Inspector, Order), each tab content stubbed to `coming soon`; mobile
|
||||
bottom-tab bar `[Map, Calc, Order, More]` with corresponding stub
|
||||
panels
|
||||
- `ui/frontend/src/lib/active-view/` view router supporting
|
||||
`/games/:id/{map,table/:entity,report,battle/:battleId,mail,
|
||||
designer/...}` with stub content per view
|
||||
- topic doc `ui/docs/navigation.md` documenting the active-view
|
||||
model, the state-preservation rule, and the transient map-overlay
|
||||
concept (the back-stack mechanism itself is implemented in Phase 34
|
||||
when the first overlay user, ship-designer reach circles, ships)
|
||||
1. **Routing — file-system based, no extra dispatcher.** The
|
||||
"view router" called out in the original artifact list is
|
||||
implemented as SvelteKit's file-system routes plus thin
|
||||
`+page.svelte` wrappers that mount the matching
|
||||
`lib/active-view/<name>.svelte` stub. No separate dispatch
|
||||
component lives in the codebase; each route file is a two-line
|
||||
wrapper.
|
||||
2. **Optional designer ID segments.** Both designer URLs ship as
|
||||
`[[id]]` optional segments
|
||||
(`designer/ship-class/[[classId]]/`,
|
||||
`designer/science/[[scienceId]]/`) so Phase 18 / 21 can read
|
||||
the param without a routing migration. Phase 10 stubs ignore
|
||||
the param.
|
||||
3. **Battle URL — optional id.** `battle/[[battleId]]/` accepts
|
||||
both the list URL (`/battle`) and a specific battle URL
|
||||
(`/battle/<id>`). Phase 27 keeps the optional segment and
|
||||
switches behaviour based on presence.
|
||||
4. **Tablet sidebar — click toggle, not swipe.** The 768–1024 px
|
||||
tablet sidebar slides in from a header-button click rather
|
||||
than the IA section's swipe-from-right gesture. The structural
|
||||
breakpoint switch satisfies Phase 10's acceptance criterion;
|
||||
Phase 35 polish lands the swipe gesture.
|
||||
5. **Mobile tool overlay — `mobileTool` state, gated by URL.**
|
||||
The mobile bottom-tabs Calc / Order navigate to `/map` and
|
||||
set a layout-owned `mobileTool` rune. The layout's derived
|
||||
`effectiveTool` only honours the rune when the URL is `/map`,
|
||||
so navigating to any other view via the More drawer or the
|
||||
header view-menu naturally drops the overlay. The desktop
|
||||
sidebar separately accepts a `?sidebar=calc|inspector|order`
|
||||
URL param that seeds the initial tab on first mount, used by
|
||||
later phases that want to land directly on a particular tool.
|
||||
6. **Sidebar tool filenames — `*-tab.svelte`.** Phase 12 / 13 / 30
|
||||
each name their final implementation
|
||||
(`order-tab.svelte`, `inspector-tab.svelte`,
|
||||
`calculator-tab.svelte`). The Phase 10 stubs ship with those
|
||||
names so later phases replace the content in place without
|
||||
renaming.
|
||||
7. **Race-name and turn-counter placeholders.** The header race
|
||||
name is the static `race ?` string from i18n, mirroring the
|
||||
spec's static `turn ?` placeholder. Phase 11 wires both from
|
||||
`user.games.report` data through `lib/header/turn-counter.svelte`.
|
||||
8. **Auth gate inherited.** The root `+layout.svelte` already
|
||||
redirects `anonymous → /login`; the in-game shell needs no
|
||||
extra guard. Phase 10 verified this by booting the e2e shell
|
||||
spec via `__galaxyDebug.setDeviceSessionId` and observing the
|
||||
post-`session.init` `authenticated` status.
|
||||
9. **More drawer mirrors the view-menu.** The mobile bottom-tabs
|
||||
"More" drawer renders the same seven destinations as the
|
||||
header view-menu. The IA section's narrower More list (Mail,
|
||||
Battle log, Tables, History, Settings, Logout) is the polish
|
||||
target for Phase 35 once History exists; Phase 10 keeps a
|
||||
single destination list to avoid drift.
|
||||
|
||||
Artifacts (delivered):
|
||||
|
||||
- `ui/frontend/src/routes/games/[id]/+layout.svelte` — chrome
|
||||
layout (header, conditional sidebar, active-view slot, mobile
|
||||
bottom-tabs, mobileTool gate, sidebarOpen toggle)
|
||||
- `ui/frontend/src/routes/games/[id]/+layout.ts` —
|
||||
`ssr=false; prerender=false;` mirroring the root SPA flags
|
||||
- `ui/frontend/src/routes/games/[id]/+page.ts` — redirects
|
||||
`/games/:id` → `/games/:id/map`
|
||||
- `ui/frontend/src/routes/games/[id]/{map, table/[entity], report,
|
||||
battle/[[battleId]], mail, designer/ship-class/[[classId]],
|
||||
designer/science/[[scienceId]]}/+page.svelte` — thin route
|
||||
wrappers that mount the matching active-view stub
|
||||
- `ui/frontend/src/lib/header/{header, turn-counter, view-menu,
|
||||
account-menu}.svelte` — header composition with race
|
||||
placeholder, turn counter (static `?`), view-menu
|
||||
(dropdown desktop / hamburger mobile), and account menu
|
||||
(Settings / Sessions / Theme stub buttons; Language driven by
|
||||
`i18n.setLocale`; Logout calls `session.signOut("user")`)
|
||||
- `ui/frontend/src/lib/sidebar/{sidebar, tab-bar, calculator-tab,
|
||||
inspector-tab, order-tab, bottom-tabs}.svelte` — three-tab
|
||||
sidebar with `inspector` default and `?sidebar=` URL seed;
|
||||
mobile-only bottom-tabs with `[Map, Calc, Order, More]` plus a
|
||||
More drawer duplicating the view-menu destinations
|
||||
- `ui/frontend/src/lib/sidebar/types.ts` — shared `SidebarTab`
|
||||
and `MobileTool` types
|
||||
- `ui/frontend/src/lib/active-view/{map, table, report, battle,
|
||||
mail, designer-ship-class, designer-science}.svelte` — Phase 10
|
||||
stubs rendering localised titles plus `coming soon` copy with
|
||||
stable testids that later phases replace
|
||||
- `ui/frontend/src/lib/i18n/locales/{en,ru}.ts` — full
|
||||
`game.shell.*`, `game.view.*`, `game.sidebar.*`,
|
||||
`game.bottom_tabs.*` catalogue
|
||||
- Topic doc `ui/docs/navigation.md`
|
||||
- Vitest: `tests/game-shell-{header,sidebar,stubs}.test.ts`
|
||||
- Playwright: `tests/e2e/game-shell.spec.ts` (7 cases × 4 projects;
|
||||
mobile-only and viewport-switch cases conditionally skipped on
|
||||
non-matching projects)
|
||||
|
||||
Dependencies: Phase 8.
|
||||
|
||||
Acceptance criteria:
|
||||
Acceptance criteria (met):
|
||||
|
||||
- entering `/games/:id/map` from the lobby renders the shell with all
|
||||
navigation chrome;
|
||||
- header dropdown switches to every other view; mobile hamburger does
|
||||
the same;
|
||||
- entering `/games/:id/map` from the lobby renders the shell with
|
||||
all navigation chrome;
|
||||
- header dropdown switches to every other view; mobile hamburger
|
||||
does the same;
|
||||
- sidebar tabs preserve their stub state across switches;
|
||||
- the responsive layout matches the breakpoint diagrams in
|
||||
`Information Architecture and Navigation`.
|
||||
`Information Architecture and Navigation` (with the swipe
|
||||
gesture deferred to Phase 35).
|
||||
|
||||
Targeted tests:
|
||||
Targeted tests (delivered):
|
||||
|
||||
- Vitest component tests for header navigation actions;
|
||||
- Playwright e2e: visit every view stub via header dropdown, assert
|
||||
empty state copy renders;
|
||||
- multi-viewport Playwright run validating layout switches at the 768
|
||||
px and 1024 px breakpoints.
|
||||
- Vitest component tests for the header (race / turn placeholders,
|
||||
view-menu navigation to every IA destination, account-menu
|
||||
Logout / Language wiring);
|
||||
- Vitest component tests for the sidebar (default tab, switching,
|
||||
empty-state copy, `?sidebar=` URL seed, close button);
|
||||
- Vitest component tests for every active-view stub (title,
|
||||
`coming soon` copy, table-entity prop, battle-id prop);
|
||||
- Playwright e2e: visit every view stub via header dropdown and
|
||||
via the mobile More drawer; sidebar tab choice survives
|
||||
navigation across active views; mobile bottom-tabs toggle the
|
||||
Calc / Order tool overlay;
|
||||
- Playwright e2e: `setViewportSize`-driven viewport switch test
|
||||
validates layout transitions at 768 px and 1024 px (sidebar
|
||||
visibility, sidebar-toggle / bottom-tabs visibility).
|
||||
|
||||
## Phase 11. Map Wired to Live Game State
|
||||
|
||||
|
||||
Reference in New Issue
Block a user