ui/phase-23: turn-report view with twenty sections and TOC
Replaces the Phase 10 report stub with a scrollable orchestrator that renders every FBS array as a dedicated section (galaxy summary, votes, player status, my/foreign sciences, my/foreign ship classes, battles, bombings, approaching groups, my/foreign/uninhabited/unknown planets, ships in production, cargo routes, my fleets, my/foreign/unidentified ship groups). A sticky table of contents (a <select> on mobile), "back to map" affordance, IntersectionObserver-driven active-section highlight, and SvelteKit Snapshot-based scroll save/restore round out the view. GameReport gains six new fields (players, otherScience, otherShipClass, battleIds, bombings, shipProductions); decodeReport, the synthetic- report loader, the e2e fixture builder, and EMPTY_SHIP_GROUPS extend in lockstep. ~90 new i18n keys land in en + ru together. The legacy-report parser is extended to populate the new sections from the dg/gplus text formats (Your Sciences, <Race> Sciences, <Race> Ship Types, Bombings, Ships In Production). Ships-in-production prod_used is derived through a new pkg/calc.ShipBuildCost helper; the engine's controller.ProduceShip refactors to call the same helper without any behaviour change (engine tests stay unchanged and green). Battles remain in the parser's Skipped list — the legacy text carries no stable per-battle UUID. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+75
@@ -2506,6 +2506,81 @@ Targeted tests:
|
||||
- Playwright e2e: open the report, scroll to each section via anchor
|
||||
navigation, assert content present.
|
||||
|
||||
Decisions during stage:
|
||||
|
||||
1. **Component decomposition.** The orchestrator
|
||||
`lib/active-view/report.svelte` is one file; each of the twenty
|
||||
sections is its own component under
|
||||
`lib/active-view/report/section-<slug>.svelte`. Six distinct data
|
||||
shapes (kv-list, races-style grid, planets-style grid, sub-table-
|
||||
per-race, raw UUID list, fleet/group grids) sit too unevenly in one
|
||||
monolith; per-section components also map directly onto the Vitest
|
||||
targeted-test seam. No shared `<Section>` abstraction was extracted
|
||||
— CLAUDE.md "wait for the third real caller" still holds with one
|
||||
shape per section. Shared formatters live in `report/format.ts`.
|
||||
2. **`races` vs `players`.** A parallel
|
||||
`GameReport.players: ReportPlayer[]` was added (full roster, self
|
||||
row included, extinct rows kept with `extinct: true`). The Phase 22
|
||||
`races[]` (non-extinct, self excluded) stays untouched so no Phase
|
||||
22 surface had to change. Extinct races are shown in Player Status
|
||||
with a `RIP` marker; the orchestrator highlights the local row.
|
||||
3. **Scroll save / restore.** Wired through SvelteKit's `Snapshot`
|
||||
API on `routes/games/[id]/report/+page.svelte`. Captures
|
||||
`window.scrollY` (the in-game shell layout expands its
|
||||
`active-view-host` to fit content, so the document body is the real
|
||||
scroll container) and restores via a `requestAnimationFrame` poll
|
||||
that waits for `documentElement.scrollHeight` to catch up before
|
||||
calling `window.scrollTo`. The earlier plan to track the host's
|
||||
`scrollTop` did not survive contact with the layout's
|
||||
no-explicit-height contract; the change is contained to the route
|
||||
file. No new context plumbing was introduced.
|
||||
4. **Active-section highlight.** `IntersectionObserver` rooted on the
|
||||
viewport (`root: null`) with `rootMargin: "-30% 0px -60% 0px"`
|
||||
tracks which section sits in the upper third of the visible area
|
||||
and updates the TOC. Cheaper than a scroll handler and degrades
|
||||
gracefully where IO is not available.
|
||||
5. **Mobile TOC.** A sticky `<select>` at the top of the report body
|
||||
replaces the desktop anchor sidebar on viewports below 768 px. No
|
||||
new overlay primitive is introduced; the existing layout-owned
|
||||
bottom-tab bar stays unobstructed. Picking an option scrolls the
|
||||
chosen section into view.
|
||||
6. **Battles section.** Battle UUIDs render as inactive monospace
|
||||
`<span>` rows until Phase 27 lights up `/games/:id/battle/:battleId`.
|
||||
The earlier plan to link them now was reverted: a dead link is a
|
||||
worse experience than a plain identifier, and the rewire when
|
||||
Phase 27 lands is one line.
|
||||
7. **Foreign sciences / ship classes layout.** One sub-table per race
|
||||
with a `{race} sciences` / `{race} ship classes` sub-header. The
|
||||
`(race, name)` decoder sort produces stable groups; cross-race
|
||||
sorting is intentionally avoided (it would be semantically
|
||||
meaningless across races).
|
||||
8. **Bombings wiped state.** Wiped rows get a `.wiped` CSS class plus
|
||||
a dedicated `report-bombing-wiped-badge` element so the boolean is
|
||||
visually explicit and easy to assert in e2e.
|
||||
9. **Ships in production `prodUsed` derivation (Go side).** The legacy
|
||||
text reports do not carry the engine's per-turn `ProdUsed` field —
|
||||
only `Cost`, `Percent`, `Free`. The legacy parser derives an
|
||||
approximation as `ShipBuildCost(shipMass, material, resources) * percent`
|
||||
using a new shared helper `pkg/calc.ShipBuildCost`. The engine's
|
||||
`controller.ProduceShip` was refactored to call the same helper
|
||||
(behavior-preserving — engine tests stay unchanged and pass). The
|
||||
approximation is documented in
|
||||
`tools/local-dev/legacy-report/README.md`; live engine reports come
|
||||
over FBS and never flow through this parser.
|
||||
10. **Legacy parser scope.** Per user direction, the parser was
|
||||
extended to populate `LocalScience`, `OtherScience`,
|
||||
`OthersShipClass`, `Bombing`, and `ShipProduction` from their
|
||||
legacy text sections. Battles stay in the parser's Skipped list:
|
||||
the legacy text carries per-battle rosters with no stable UUID,
|
||||
and synthesising IDs would invent data Phase 27 would have to
|
||||
drop. `OtherGroup[]`, `UnidentifiedGroup[]`, and cargo routes
|
||||
remain skipped (no legacy section).
|
||||
11. **i18n namespace.** All Phase 23 strings live under
|
||||
`game.report.section.<slug>.*`; the duplicate-looking entries
|
||||
(sciences / ship classes columns) are deliberately separate from
|
||||
`game.table.*` so the two surfaces evolve independently. ≈90 new
|
||||
keys, en + ru in lockstep.
|
||||
|
||||
## Phase 24. Push Events — Turn-Ready
|
||||
|
||||
Status: pending.
|
||||
|
||||
Reference in New Issue
Block a user