Commit Graph

11 Commits

Author SHA1 Message Date
Ilia Denisov 209f8508cd feat(ui): F8-11 — battles table under table submenu (#54)
Tests · UI / test (push) Successful in 2m53s
Tests · UI / test (pull_request) Successful in 3m0s
Adds a sortable battles list as a new entity under the existing
`view → table` submenu (entity slug `battles`), replacing the
standalone top-level `battle log` shortcut which always opened a
"battle not found" placeholder. The single-battle viewer stays put
and is reached only by clicking a row (or a battle marker on the
map), identical to the existing `section-battles.svelte` flow.

Columns are planet (via the shared `planetLabel` helper) and shots
(the per-battle action count carried by `BattleSummary`), sortable
both ways with shots-desc default. No backend / FBS / map changes:
the wire payload is unchanged. Participants / observers / total
mass require the full BattleReport and were intentionally dropped
to avoid N round trips per menu open.

The top-level `battle log` item is removed from `header/view-menu`
and `sidebar/bottom-tabs` (and their stale comment blocks updated);
the now-orphan `game.view.battle` i18n key is dropped from both
locales.
2026-05-27 22:12:51 +02:00
Ilia Denisov 4a23c357e5 feat(ui): F8-05 — game-mode chrome cleanup + inspector compact rows (#48)
Tests · UI / test (push) Waiting to run
Tests · UI / test (pull_request) Waiting to run
Drains six F8 polish items (parent #43) in one feature:

а) Chrome cleanup
- п.6 — remove the AccountMenu (settings/sessions/theme/language/logout
  ∼ rudimentary in-game) and replace it with a single icon-button
  light/dark theme toggle. The toggle flips an in-memory `theme.override`;
  game-shell unmount calls `theme.clearOverride()` so the lobby (and
  any re-entry) re-projects the persisted lobby choice.
- п.8 — remove the wrap-scrolling radio from the map gear popover. The
  per-game `wrapMode` store and the renderer's no-wrap path stay in
  place for a future engine-side topology feature; only the UI surface
  is dropped (wrap is a server-side concept, not a per-session UI
  affordance).

б) Inspector compact rows (single idiom: select + ✓ apply / ✗ cancel,
or contextual edit/remove/add)
- п.13 — planet name is now click-to-edit: clicking the name opens an
  inline `<input>` + ✓ confirm icon; Escape cancels; the explicit
  Rename action button and Cancel button are gone.
- п.14 — production becomes one row: primary `<select>` picks
  industry/materials/research/ship, conditional secondary `<select>`
  picks the target (tech / science / ship class) for research and
  ship contexts. Apply is gated until row state differs from the
  planet's current effective production; auto-submit-on-click is
  replaced by the apply-gate.
- п.16 — cargo routes collapse to one row: a single dropdown
  (COL/CAP/MAT/EMP plus a placeholder that absorbs the old section
  title) and contextual action buttons (add / edit + remove) to the
  right. After a successful pick or remove the dropdown stays on the
  type the user just acted on.
- п.32 — stationed ship groups hoist the race column into a dropdown
  above the table. The dropdown seeds with the player's own race when
  local groups are stationed here, otherwise the first race
  alphabetically; rendered only when more than one race is in orbit.
  The race column is dropped in both single- and multi-race modes —
  the dropdown's value already names the active race.

Tests: unit and Playwright e2e updated for every changed test-id and
flow; new coverage added for `theme.override`, the in-game toggle, the
apply-gate behaviour, and the stationed-race dropdown. i18n keys for
the removed menu items, the wrap radios, the cargo title, and the
explicit `rename.cancel` are dropped from both locales; new
`game.shell.theme_toggle.*`, `production.main/target.*`,
`production.apply/cancel`, `cargo.placeholder`, and
`ship_groups.race_filter.aria` keys land.

Docs synced: `docs/FUNCTIONAL.md` §6.7 + `docs/FUNCTIONAL_ru.md`
mirror drop the torus / no-wrap radio mention; `ui/docs/design-system.md`
documents the lobby-owned persisted picker + the in-game ephemeral
override channel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 13:38:42 +02:00
Ilia Denisov 4e0058d46c test(ui): migrate suite to the app-shell (state-driven navigation)
- Unit: repoint moved screen imports (lib/screens, lib/game), mock
  $lib/app-nav (appScreen/activeView) instead of $app/navigation, drop the
  removed gameId props, assert screen/view selection.
- e2e: add a dev-only window.__galaxyNav affordance; specs enter a game via
  enterGame(...) instead of a /games/:id URL; URL assertions become content
  assertions (the URL stays /game/); reload uses waitUntil:"commit" (shallow
  routing) and mocks /rpc on game entry.
- Remove the obsolete report scroll-restore test (it relied on a SvelteKit
  route Snapshot that no longer exists); update the missing-membership test
  to the new lobby-redirect+toast behaviour. Fix a stale report.svelte
  docstring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:49:35 +02:00
Ilia Denisov 9ae7b88b89 feat(ui): Phase 30 ship-class calculator with goal-seek and reach circles
Tests · UI / test (push) Successful in 2m14s
Tests · Go / test (push) Successful in 2m25s
Fuse the standalone ship-class designer (Phases 17/18) into a sidebar calculator: live mass/speed/attack/defence/bombing results, a planet build-rate readout, single-target goal-seek, a modernization-cost mode, and auto reach circles on the map for the selected planet.

pkg/calc becomes the single source for the new math (no mirroring): extract BombingPower from the engine model and the per-turn ship-production loop from controller.ProduceShip into pkg/calc (engine now delegates), and add inverse goal-seek solvers in pkg/calc/solve.go. Thin-bridge the combat, planet-build, and solver functions through ui/core/calc + ui/wasm and rebuild core.wasm.

Remove the standalone designer view/route; the ship-classes table and the view/bottom menus open the calculator via a shared request store.

Docs: rewrite ui/PLAN.md Phase 30, adjust Phase 34 (realistic forecast + CAP/COL ownership), add ui/docs/calculator-ux.md, extend calc-bridge.md, fix navigation.md; remove ui/CALCULATOR.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:04:07 +02:00
Ilia Denisov 2d17760a5e ui/phase-26: history mode (turn navigator + read-only banner)
Split GameStateStore into currentTurn (server's latest) and viewedTurn
(displayed snapshot) so history excursions don't corrupt the resume
bookmark or the live-turn bound. Add viewTurn / returnToCurrent /
historyMode rune, plus a game-history cache namespace that stores
past-turn reports for fast re-entry. OrderDraftStore.bindClient takes
a getHistoryMode getter and short-circuits add / remove / move while
the user is viewing a past turn; RenderedReportSource skips the order
overlay in the same case. Header replaces the static "turn N" with a
clickable triplet (TurnNavigator), the layout mounts HistoryBanner
under the header, and visibility-refresh is a no-op while history is
active.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 00:13:19 +02:00
Ilia Denisov 676556db4e ui/phase-19: ship-group decoder + map binding + selection store
Wires Phase 19's data and rendering layers without yet adding the
inspector UI:

  - game-state.ts grows ReportLocalShipGroup / ReportOtherShipGroup
    / ReportIncomingShipGroup / ReportUnidentifiedShipGroup /
    ReportLocalFleet types and walks the matching FlatBuffers
    vectors (LocalGroup, OtherGroup, IncomingGroup,
    UnidentifiedGroup, LocalFleet) inside decodeReport. The Tech
    map is folded into the fixed-shape ShipGroupTech struct;
    cargo strings normalise to the closed CargoLoadType | "NONE"
    union; UUIDs come back as canonical 36-char strings.
  - synthetic-report.ts mirrors the new fields so the DEV-only
    lobby loader can feed JSON produced by legacy-report-to-json
    straight into the live UI surface.
  - selection.svelte.ts widens its discriminated union with a
    `kind: "shipGroup"` branch carrying a ShipGroupRef
    (local UUID / other / incoming / unidentified by index).
  - world.ts adds Style.strokeDashPx and render.ts.drawLine
    honours it via manual segmentation (PixiJS v8 has no native
    dash API). Ignored on points and circles.
  - state-binding.ts now returns { world, hitLookup }: the
    hit-lookup map keys every primitive id back to a concrete
    HitTarget so the click handler can dispatch to selectPlanet
    or selectShipGroup. Ship-group primitives live in a separate
    ship-groups.ts that emits one point per local / other /
    unidentified group, plus a dashed origin→destination line +
    clickable point per incoming group. Position is interpolated
    along the trajectory for in-hyperspace groups.
  - map.svelte threads the hitLookup into handleMapClick.

Vitest:
  - tests/helpers/empty-ship-groups.ts exposes EMPTY_SHIP_GROUPS
    so existing fixtures can spread the new five empty arrays
    without enumerating every field.
  - state-binding-groups.test.ts covers each group variant's
    primitive geometry and lookup correctness.
  - All previously-existing fixture builders pick up the spread
    so GameReport stays a complete object.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 13:23:56 +02:00
Ilia Denisov e4dc0ce029 ui/phase-18: ship-class calc bridge with live designer preview
Wires pkg/calc/ship.go into the WASM Core boundary as seven thin
wrappers (DriveEffective, EmptyMass, WeaponsBlockMass, FullMass,
Speed, CargoCapacity, CarryingMass). The ship-class designer reads
Core through a new CORE_CONTEXT_KEY populated by the in-game layout
and renders a five-row preview pane (mass, full-load mass, max
speed, range at full load, cargo capacity) that updates reactively
on every form edit and on the player's localPlayer{Drive,Weapons,
Shields,Cargo} tech levels — three of which are now decoded from
the report's Player block alongside the existing localPlayerDrive.

CarryingMass is the seventh wrapper added to the original six-function
list so that "full-load mass" composes through pkg/calc/ functions
without putting math in TypeScript.
2026-05-09 23:14:40 +02:00
Ilia Denisov 7c8b5aeb23 ui/phase-16: cargo routes inspector + map pick foundation
Add per-planet cargo routes (COL/CAP/MAT/EMP) to the inspector with
a renderer-driven destination picker (faded out-of-reach planets,
cursor-line anchor, hover-highlight) and per-route arrows on the
map. The pick-mode primitives are exposed via `MapPickService` so
ship-group dispatch in Phase 19/20 can reuse the same surface.

Pass A — generic map foundation:
- hit-test now sizes the click zone to `pointRadiusPx + slopPx` so
  the visible disc is always part of the target.
- `RendererHandle` gains `onPointerMove`, `onHoverChange`,
  `setPickMode`, `getPickState`, `getPrimitiveAlpha`,
  `setExtraPrimitives`, `getPrimitives`. The click dispatcher is
  centralised: pick-mode swallows clicks atomically so the standard
  selection consumers do not race against teardown.
- `MapPickService` (`lib/map-pick.svelte.ts`) wraps the renderer
  contract in a promise-shaped `pick(...)`. The in-game shell
  layout owns the service so sidebar and bottom-sheet inspectors
  see the same instance.
- Debug-surface registry exposes `getMapPrimitives`,
  `getMapPickState`, `getMapCamera` to e2e specs without spawning a
  separate debug page after navigation.

Pass B — cargo-route feature:
- `CargoLoadType`, `setCargoRoute`, `removeCargoRoute` typed
  variants with `(source, loadType)` collapse rule on the order
  draft; round-trip through the FBS encoder/decoder.
- `GameReport` decodes `routes` and the local player's drive tech
  for the inline reach formula (40 × drive). `applyOrderOverlay`
  upserts/drops route entries for valid/submitting/applied
  commands.
- `lib/inspectors/planet/cargo-routes.svelte` renders the
  four-slot section. `Add` / `Edit` call `MapPickService.pick`,
  `Remove` emits `removeCargoRoute`.
- `map/cargo-routes.ts` builds shaft + arrowhead primitives per
  cargo type; the map view pushes them through
  `setExtraPrimitives` so the renderer never re-inits Pixi on
  route mutations (Pixi 8 doesn't support that on a reused
  canvas).

Docs:
- `docs/cargo-routes-ux.md` covers engine semantics + UI map.
- `docs/renderer.md` documents pick mode and the debug surface.
- `docs/calc-bridge.md` records the Phase 16 reach waiver.
- `PLAN.md` rewrites Phase 16 to reflect the foundation + feature
  split and the decisions baked in (map-driven picker, inline
  reach, optimistic overlay via `setExtraPrimitives`).

Tests:
- `tests/map-pick-mode.test.ts` — pure overlay-spec helper.
- `tests/map-cargo-routes.test.ts` — `buildCargoRouteLines`.
- `tests/inspector-planet-cargo-routes.test.ts` — slot rendering,
  picker invocation, collapse, cancel, remove.
- Extensions to `order-draft`, `submit`, `order-load`,
  `order-overlay`, `state-binding`, `inspector-planet`,
  `inspector-overlay`, `game-shell-sidebar`, `game-shell-header`.
- `tests/e2e/cargo-routes.spec.ts` — Playwright happy path: add
  COL, add CAP, remove COL, asserting both the inspector and the
  arrow count via `__galaxyDebug.getMapPrimitives()`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 20:01:34 +02:00
Ilia Denisov 915b4372dd ui/phase-15: planet inspector production controls + order-draft collapse
Adds the second end-to-end command (`setProductionType`) with a
collapse-by-`planetNumber` rule on the order draft, the segmented
production-controls component on the planet inspector, the FBS
encoder/decoder pair for `CommandPlanetProduce`, and the
`localShipClass` projection on `GameReport`. Forecast number is
deferred and tracked in the new `ui/docs/calc-bridge.md`.
2026-05-09 15:54:30 +02:00
Ilia Denisov 229c43beb5 ui/phase-14: auto-sync order draft + always GET on boot + header headline
Replaces the manual Submit button with an auto-sync pipeline driven
by `OrderDraftStore`: every successful add / remove / move
coalesces a `submitOrder` call so the engine always mirrors the
local draft. Removing the last command sends an empty cmd[] PUT —
the engine, repo, and rest model now accept that as a valid
"player cleared their draft" state.

`hydrateFromServer` is now invoked unconditionally on game boot so
a fresh device picks up the player's stored order, and the local
cache is overwritten by the server's view (server is the source of
truth).

Header replaces the static "race ?" + turn counter with a single
headline string `<race> @ <game>, turn <n>`, sourced from the
engine's Report.race + the lobby's GameSummary.gameName + the live
turn number, with a `?` fallback while any piece is loading.

Tests:
- engine: empty PUT round-trips, repo round-trips empty Commands
- order-draft: auto-sync sends full draft on every mutation,
  rejected response surfaces error sync status, rapid mutations
  coalesce, server hydration overwrites cache
- order-tab: per-row status flips through the auto-sync lifecycle,
  remove → empty cmd[] PUT, rejected → retry button
- inspector overlay: applied + valid + submitting all participate
  in the optimistic projection
- header: live race / game / turn rendering with fall-back

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:34:10 +02:00
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