feat(ui): Phase 29 map visibility toggles
Tests · Go / test (push) Successful in 2m31s
Tests · UI / test (push) Failing after 8m7s

Adds the gear-icon popover on the map view with per-game persistence
of every category toggle plus the wrap-mode radio. Hide-by-id and
visibility-fog facilities land on the renderer so every flip applies
within one frame without a Pixi remount; the wrap-mode toggle keeps
its existing remount + camera-preserve path. A new server-side turn
force-resets every flag to defaults so a hidden category never makes
the player miss the next turn's news.

Also fixes the FligthDistance → FlightDistance typo in pkg/calc/race.go
(plus the single Go caller); the TS side keeps duplicating the formula
until a race-level WASM bridge lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-19 21:33:53 +02:00
parent 65c0fbb87d
commit 2bd1b54936
32 changed files with 3046 additions and 63 deletions
+60 -6
View File
@@ -269,25 +269,79 @@ resolver that translates `sourcePlanetNumber` to the underlying
current report). Inspector subsections call `service.pick(...)`
and react to the resolved id.
## Hidden primitives
`RendererHandle.setHiddenPrimitiveIds(ids)` replaces the current
hide-by-id set. Every primitive whose id sits in `ids` has its
per-copy `Graphics.visible` flipped to `false` and is skipped by
`hitAt`, so a click on its former area falls through to the next
visible primitive. An empty set restores everything. Repeated
calls are diff-free idempotent — `g.visible` assignments are
cheap.
The hide set is propagated to `hitTest` through a new optional
`hiddenIds` parameter so internal hit-test sites (pointer-move,
clicked dispatcher) stay in lock-step with the visible scene.
After `setExtraPrimitives` the hide set is re-applied so a
freshly-pushed extras layer (cargo-route overlay, pending-Send
tracks) does not silently un-hide a primitive whose id is in the
current set.
The Phase 29 map view (`src/lib/active-view/map.svelte`) computes
the set from the per-game `MapToggles` rune + the planet-cascade
rule and pushes it on every effect run; toggling a checkbox
flips visibility within one frame without a Pixi remount.
## Visibility fog
`RendererHandle.setVisibilityFog(circles)` draws (or removes) the
Phase 29 fog overlay. Each entry describes a circle around a
LOCAL planet where the player has scanner / visibility coverage:
- An empty list destroys the existing fog Graphics.
- A non-empty list creates one fog `Graphics` per torus copy.
Each fills the world rectangle with `FOG_COLOR` (two shades
lighter than the dark theme background) and "cuts" every
circle out of it via Pixi v8's `Graphics.cut()` path operator,
so overlapping circles compose into a union hole (no
even-odd-fill quirks). The fog is inserted at the bottom of
each copy's z-order so primitives paint on top.
- The fog never participates in hit-test. Planet glyphs sit on
top of fog, so clicks on visible planets work unchanged.
- Wrap mode is honoured for free — `applyMode` hides every
non-origin copy in `no-wrap`, so the fog inherits the same
behaviour because the fog Graphics is a child of each copy.
The map view recomputes the fog input only when the report or the
fog toggle changes — per-frame cost stays at zero.
## Debug surface
The DEV-only `__galaxyDebug` object (defined in
`routes/__debug/store/+page.svelte`) exposes
`getMapPrimitives()` and `getMapPickState()` so e2e specs can
assert the renderer's current state without scraping pixels:
`getMapPrimitives()`, `getMapPickState()`, `getMapCamera()`, and
`getMapFog()` so e2e specs can assert the renderer's current
state without scraping pixels:
- `getMapPrimitives()` returns a snapshot of every primitive in
the active world: id, kind, priority, current alpha
(post-overlay), and the explicit fill / stroke colour from its
`Style` (no theme fallback). Tests use this to count cargo
arrows or to verify dim state during pick mode.
(post-overlay), the explicit fill / stroke colour from its
`Style` (no theme fallback), and the Phase 29 `visible` flag
mirroring the renderer's hide set.
- `getMapPickState()` returns `{ active, sourcePlanetNumber,
reachableIds, hoveredId }` — the renderer's view of the
current pick session.
- `getMapCamera()` returns the current camera + viewport +
canvas-origin snapshot, used by Phase 29 e2e specs to assert
camera preservation across wrap-mode flips.
- `getMapFog()` returns the most recent visibility-fog input
(the list of circles last passed to `setVisibilityFog`).
Empty when the fog toggle is off.
The active map view registers providers on mount via
`registerMapPrimitivesProvider` / `registerMapPickStateProvider`
in `src/lib/debug-surface.svelte.ts`, deregisters on dispose, and
/ `registerMapCameraProvider` / `registerMapFogProvider` in
`src/lib/debug-surface.svelte.ts`, deregisters on dispose, and
the surface invokes them lazily on every read.
## Tests