Commit Graph

558 Commits

Author SHA1 Message Date
developer 6f2967024a Merge pull request 'feat(ui): map canvas follows light/dark theme; fix invisible gear control' (#42) from feature/issue-40-map-light-theme into development
Deploy · Dev / deploy (push) Successful in 1m58s
Tests · UI / test (push) Successful in 2m39s
2026-05-24 07:15:08 +00:00
Ilia Denisov f6e4a4f6bd feat(ui): map canvas follows light/dark theme; fix invisible gear control
Tests · UI / test (push) Waiting to run
Tests · UI / test (pull_request) Successful in 2m45s
The map view now selects a DARK_THEME or LIGHT_THEME palette from the
resolved app theme and threads it through every primitive builder, so
the canvas, planets, ship groups, cargo routes, battle/bombing markers,
fog, reach + selection rings, pending-Send tracks, and the pick overlay
all switch with the rest of the chrome. A theme flip remounts the
renderer preserving the camera — Pixi bakes the background at init and
every primitive bakes its colour at build, so a live re-tint is not
possible on the same instance.

This also fixes the reported bug: the gear-popover trigger and the
loading overlay hardcoded a dark navy background, so in light theme the
gear was invisible (dark icon on dark chip) until hover flipped it to a
white chip. Both now use the --color-surface-overlay token and read
correctly in both themes.

The light palette mirrors the dark one role-for-role, darkened /
saturated for contrast on a light background while keeping the incoming,
battle, and bombing accents vivid. The values are a first pass meant to
be refined during the F8 manual-QA loop.

Removes the now-dead "Phase 35" references from the code and lifts the
map-recoloring prohibition from the design-system / renderer docs; the
battle scene stays a fixed-palette data-viz surface.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 08:49:37 +02:00
owner d44ad9b6eb Merge pull request 'fix(gateway): verify client signature before payload_hash' (#41) from feature/issue-39-verification-order into development
Deploy · Dev / deploy (push) Successful in 48s
Tests · Go / test (push) Successful in 2m1s
Tests · Integration / integration (push) Successful in 1m40s
Reviewed-on: #41
2026-05-24 05:59:44 +00:00
Ilia Denisov 91e34a0929 fix(gateway): verify client signature before payload_hash
Tests · Go / test (push) Successful in 2m1s
Tests · Go / test (pull_request) Successful in 2m58s
Tests · Integration / integration (pull_request) Successful in 1m39s
ARCHITECTURE.md §15 "Verification order" specifies signature verification
(step 4) before payload_hash (step 5), but the authenticated-edge
decorator chain wrapped the payload-hash gate outside the signature gate,
so the hash was checked first. gateway/README.md and gateway/docs/flows.md
had drifted to match the code (hash-first), leaving ARCHITECTURE.md as the
lone source describing the intended order.

Swap the two decorators in server.go so the signature gate runs first, and
align README + flows.md to ARCHITECTURE.md. Signature-first is the
cryptographically sound order: the signature covers the payload_hash field,
so the request is authenticated before any of its content is processed.

Observable side effect: a request carrying a tampered payload_hash whose
signature was computed over the original hash is now rejected at the
signature gate (UNAUTHENTICATED "invalid request signature") instead of the
hash gate (INVALID_ARGUMENT). Security is unchanged — both refusals happen
before the payload is handled. The four payload-hash unit tests re-sign
over the tampered hash so they keep exercising the hash gate; the
cross-service integration test signs over the overridden hash and already
accepts both codes.

Refs #39

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 02:42:09 +02:00
developer f0857243e2 Merge pull request 'feat(ui): single-URL game app-shell (in-memory screens/views)' (#35) from feature/ui-app-shell into development
Deploy · Dev / deploy (push) Successful in 46s
Tests · UI / test (push) Successful in 2m59s
2026-05-23 20:18:09 +00:00
Ilia Denisov 1dadf08672 docs(ui): clarify lobby re-enter starts at the map, only refresh restores
Tests · UI / test (pull_request) Successful in 2m32s
The single-URL restore replays the saved screen/view on an in-place
refresh only. Re-entering a game from the lobby resets activeView to the
map (lobby calls activeView.reset() before appScreen.go("game")), and
browser Back / the return-to-lobby control exit to the lobby. Spell this
out so the refresh-restore is not mistaken for a per-re-enter restore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 22:14:15 +02:00
Ilia Denisov c1672224a6 fix(ui): pin the mobile game shell to the viewport
Tests · UI / test (push) Successful in 2m49s
The app-shell migration surfaced a mobile-only e2e failure: taps on the
bottom-tab bar, the map-toggles menu, and the planet sheet were
intercepted by sibling elements despite the targets being on top.

Root cause: `.game-shell` used `min-height: 100vh`, so sub-pixel content
overflowed the viewport and made the document scrollable. On mobile that
scroll toggles the browser's dynamic toolbar, which resizes the viewport
and every `position: fixed` overlay (their sizes derive from `100vh`)
mid-gesture — defeating Playwright's actionability hit-test, and making
the real controls jittery to tap.

Pin the shell with `position: fixed; inset: 0` on the mobile breakpoint
so it leaves document flow: the document can no longer scroll, the
toolbar stays put, the viewport and overlays stay stable, and the
active-view area remains the single internal scroll region. Desktop is
unchanged (the rule is scoped to max-width: 767.98px).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 21:40:06 +02:00
Ilia Denisov e31fb2c17a docs(ui): sync docs to the app-shell; fix stale nav comments
Tests · UI / test (push) Failing after 9m28s
Rewrite ui/docs (navigation, order-composer, auth-flow, pwa-strategy,
game-state + secondary topic docs) and ui/README for the single-URL
app-shell (in-memory screens/views, Back→lobby via shallow routing,
sessionStorage restore + validation, return-to-lobby). ui/PLAN.md gets a
Phase-10 supersede note (implemented; standalone-compatible). Fix stale
code comments (session-store auth gate, report-sections spec contract).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 21:04:11 +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 80545e9f9d feat(ui): app-shell behaviour — restore validation, return-to-lobby, push
- A restored game that no longer exists (cancelled/removed/revoked) drops to
  the lobby with a toast instead of the in-game error state: game-state
  exposes a `notFound` flag and the shell redirects via appScreen.go("lobby").
- Add a visible "return to lobby" control to the in-game header.
- Push/toast deep-links use activeView.select(...) (no URL); fix a latent
  visibility-listener double-install on in-place game switches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:11:54 +02:00
Ilia Denisov be7f06e163 feat(ui): screen-level history for the app-shell (Back → lobby)
Mirror the screen into browser history via SvelteKit shallow routing
(pushState/replaceState with page.state) so Back/Forward move between
screens while the URL stays at /game/. Overlays (game, lobby-create) push;
lobby/login replace. A popstate→page.state effect syncs the store back
without re-pushing (no loop); the boot stamp puts a restored overlay above
the load entry so Back falls through to lobby. In-game view switches never
touch history.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:07:03 +02:00
Ilia Denisov b6770d394c feat(ui): app-shell core — single-route dispatcher, route collapse, nav→state
Collapse the game UI to one route (`/`): a screen dispatcher renders
login/lobby/lobby-create/game from `appScreen`/`activeView` state instead of
URL routes. Move screen components to lib/screens & lib/game; the game shell
reads the game id from `appScreen.gameId` and re-inits per-game stores via an
$effect; in-game views render from `activeView`. Flip ~23 goto/href nav sites
to store mutations; drop the `?sidebar=` URL coupling. Auth gate is now
state-based. WIP: browser-history (Back→lobby), restore-validation, the
return-to-lobby button, push deep-links, and the test migration are follow-ups
on this branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:04:04 +02:00
Ilia Denisov 182beebcd6 feat(ui): app-nav state stores (app-shell foundation)
Add `appScreen` + `activeView` rune singletons with a shared sessionStorage
snapshot — the in-memory source of truth that replaces URL-based screen/view
routing for the single-URL app-shell. Not wired in yet (additive).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 19:45:27 +02:00
developer ae91037bc3 Merge pull request 'feat(deploy): single-origin path-based deployment + project site' (#34) from feature/deploy-single-origin into development
Deploy · Dev / deploy (push) Successful in 43s
Tests · Go / test (push) Successful in 2m3s
Build · Site / build (push) Successful in 9s
Tests · Integration / integration (push) Successful in 1m49s
Tests · UI / test (push) Successful in 2m36s
2026-05-23 17:24:25 +00:00
Ilia Denisov ec98639d49 fix(site): link to the game with target=_self to avoid VitePress SPA 404
Build · Site / build (push) Successful in 8s
Tests · Integration / integration (pull_request) Successful in 1m38s
Build · Site / build (pull_request) Successful in 10s
Tests · Go / test (pull_request) Failing after 3m0s
Tests · UI / test (pull_request) Successful in 2m35s
VitePress is a Vue SPA; a same-origin link to /game/ (a separate app, not
a VitePress page) was intercepted by its client router and rendered
VitePress's own 404 until a manual reload. Mark the game links (both
home pages and the nav item) target="_self" so the click is a real
browser navigation that the edge Caddy serves from the game bundle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 19:13:15 +02:00
Ilia Denisov 9cb5097f54 fix(ui): redirect app root to lobby/login; evict stale root service worker
Tests · UI / test (push) Has been cancelled
Build · Site / build (push) Successful in 8s
Tests · Integration / integration (pull_request) Successful in 1m42s
Build · Site / build (pull_request) Successful in 6s
Tests · UI / test (pull_request) Successful in 2m23s
Tests · Go / test (pull_request) Successful in 1m56s
- The app root ("/", i.e. /game/) rendered a dev "workspace skeleton"
  stub, and the layout guard only redirected anonymous users off it, so
  an authenticated visitor stayed on the stub. Redirect "/" to /lobby
  (authenticated) and /login (anonymous), and replace the stub with a
  minimal loading placeholder. Drop the obsolete landing-stub unit test
  (root redirect is covered by the auth-flow e2e).
- Ship a tombstone /service-worker.js on the project site so any old
  root-scoped PWA worker (from when the game lived at the origin root)
  unregisters itself instead of serving a stale cached page at the
  site origin. The game now registers its worker only under /game/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:53:16 +02:00
Ilia Denisov a453b74b04 test(ui): assert relative manifest start_url in the PWA spec
Tests · UI / test (push) Successful in 2m28s
Tests · Integration / integration (pull_request) Successful in 1m46s
Build · Site / build (pull_request) Successful in 10s
Tests · Go / test (pull_request) Successful in 2m1s
Tests · UI / test (pull_request) Successful in 2m46s
The single-origin manifest now uses relative URLs (`start_url: "./"`) so
it stays base-agnostic under `/` and `/game/`. Update the PWA spec to
assert the relative value instead of the old absolute `/`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:24:55 +02:00
Ilia Denisov 8565942392 feat(deploy): single-origin path-based deployment + project site
Build · Site / build (push) Successful in 8s
Tests · Go / test (push) Successful in 2m22s
Tests · UI / test (push) Failing after 2m42s
Serve the whole stack behind one host: site at /, game UI at /game/,
gateway REST at /api + /healthz, Connect at /rpc (prefix stripped by the
edge Caddy). The built artifact is domain-agnostic — the UI talks to the
gateway same-origin via relative URLs, so the same bundle runs under any
host with no rebuild and with CORS disabled.

- Rename the Connect proto service galaxy.gateway.v1.EdgeGateway ->
  edge.v1.Gateway; regenerate Go + TS; public path /rpc/edge.v1.Gateway.
- Move the game UI under base path /game (env BASE_PATH); make the
  manifest, service-worker scope, WASM loader, and all navigation
  base-aware via a withBase helper.
- Relative API + /rpc Connect prefix; Vite dev proxy mirrors the strip.
- Rewrite the edge Caddy (dev + prod) for path-based routing; empty CORS
  allow-lists (same-origin); single host.
- New VitePress project site (site/): i18n en/ru with switcher, LaTeX
  math, minimal monospace theme; built and served at /.
- dev-deploy compose/Makefile + CI (dev-deploy, prod-build, new
  site-build) build and seed the site; probes hit /, /game/, /healthz.
- Sync docs (ARCHITECTURE, gateway README/openapi, dev-deploy &
  local-dev READMEs, CLAUDE.md, ui/PLAN).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:19:07 +02:00
developer fa0df5183a Merge pull request 'chore: remove deprecated client' (#33) from chore/cleanup-code-and-repo into development
Deploy · Dev / deploy (push) Successful in 45s
Tests · Integration / integration (push) Successful in 1m37s
Tests · Go / test (push) Successful in 3m11s
Tests · UI / test (push) Successful in 2m34s
Reviewed-on: https://gitea.lan/developer/galaxy-game/pulls/33
2026-05-23 14:57:28 +00:00
Ilia Denisov 8e0a1c39c0 chore: remove deprecated client
Tests · UI / test (pull_request) Has been cancelled
Tests · Go / test (push) Successful in 2m25s
Tests · UI / test (push) Waiting to run
Tests · Integration / integration (pull_request) Successful in 1m44s
Tests · Go / test (pull_request) Successful in 2m2s
2026-05-23 16:55:02 +02:00
developer 780769b3c4 Merge pull request 'docs(ui): documentation finalization (F7)' (#32) from feature/ui-finalize-f7-docs into development 2026-05-22 14:07:02 +00:00
Ilia Denisov 53fb4f5f76 docs(ui): finalize docs sync — README finalized-web summary + topic links (F7)
The de-archaeology and the ui/docs index landed in the earlier reorg
(0 "Phase N" refs in ui/docs/; 24 topic docs linked). This finishes the
sync: ui/README.md gains a finalized-web-target summary and links the new
topic docs (design-system, a11y, error-state-ux, pwa-strategy).
docs/ARCHITECTURE.md and docs/FUNCTIONAL.md need no change — they cover
the backend/gateway/cross-service contracts with no stale UI statements;
the finalized UI is client-local UX documented under ui/docs/. Marks F7
done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 16:06:27 +02:00
developer 1dd8df9f6e Merge pull request 'feat(ui): installable offline PWA (F5)' (#31) from feature/ui-finalize-f5-pwa into development
Deploy · Dev / deploy (push) Successful in 46s
Tests · UI / test (push) Successful in 2m55s
2026-05-22 14:02:40 +00:00
Ilia Denisov 11f51944df fix(ui): register the service worker in production only
Tests · UI / test (push) Successful in 2m19s
Tests · UI / test (pull_request) Successful in 2m25s
SvelteKit's automatic SW registration also runs under `vite dev`, where
the worker intercepted/cached the dev-server e2e suite (42 failures).
Disable auto-registration (kit.serviceWorker.register: false) and
register the worker manually from the root layout guarded by `!dev`, so
`vite dev` and the e2e suite run worker-free while the production build —
and the PWA preview test — still install it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:56:32 +02:00
Ilia Denisov 04c7f6e68a feat(ui): installable offline PWA — service worker, manifest, icons (F5)
Tests · UI / test (push) Failing after 7m31s
Native SvelteKit service worker (src/service-worker.ts): a version-keyed
cache precaches the app shell + build artefacts (incl. core.wasm) +
static files; activate purges old caches; the gateway is never
intercepted; navigations fall back to the cached shell offline. Adds
static/manifest.webmanifest, a generated placeholder icon set
(scripts/gen-pwa-icons.mjs — dependency-free pure-Node PNG encoder), and
manifest / theme-color / apple-touch tags in app.html.

Gated by Playwright against a production preview (playwright.pwa.config.ts
+ tests/pwa/pwa.spec.ts via `pnpm test:pwa`, wired into ui-test):
manifest + installable icons, SW registration + a single version-keyed
cache, and offline shell load. Lighthouse is not used — its PWA category
was removed in v12.

Docs: ui/docs/pwa-strategy.md (+ index); F5 marked done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:46:42 +02:00
developer c066a8958e Merge pull request 'build(ui): build core.wasm in CI, stop committing the binary (F6)' (#30) from feature/ui-finalize-f6-wasm-ci into development
Deploy · Dev / deploy (push) Successful in 44s
Tests · UI / test (push) Successful in 2m18s
2026-05-22 12:37:24 +00:00
Ilia Denisov b729036778 build(ui): build core.wasm in CI, stop committing the binary (F6)
Tests · UI / test (push) Successful in 3m48s
Tests · UI / test (pull_request) Successful in 2m35s
core.wasm and wasm_exec.js are no longer tracked (untracked + gitignored).
A reusable composite action .gitea/actions/build-wasm installs TinyGo
(actions/cache'd) and runs `make -C ui wasm`; it runs in all three
frontend-building workflows — ui-test (before Playwright; Vitest uses the
fake Core and needs no build), dev-deploy, and prod-build. ui-test gains a
Go setup (TinyGo shells out to Go); the deploy workflows already had one.

Docs: ui/docs/wasm-toolchain.md, ui/README.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:29:33 +02:00
developer 9d3a652b6b Merge pull request 'feat(ui): error & state UX (F4)' (#29) from feature/ui-finalize-f4-error-ux into development
Deploy · Dev / deploy (push) Successful in 35s
Tests · UI / test (push) Successful in 2m19s
2026-05-22 11:53:16 +00:00
Ilia Denisov b07b8fb1c8 test(ui): cargo-routes counts the selection ring in the primitive total
Tests · UI / test (push) Has been cancelled
Tests · UI / test (pull_request) Successful in 2m4s
The F4 selection ring is a real map primitive. The cargo-route flow has
the source planet selected, so the total primitive count is 8 (7 + the
ring circle), not 7; the line count (3) is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:50:42 +02:00
Ilia Denisov 35e27c5aec fix(ui): bottom-sheet tap-outside only fires while the sheet is shown
Tests · UI / test (push) Has been cancelled
Tests · UI / test (pull_request) Failing after 2m53s
The planet/ship-group sheets stay mounted on desktop but are hidden by a
media query (`display: none`); the document-level tap-outside listener
fired regardless, so the first click after selecting a planet cleared the
selection — breaking every desktop inspector/select flow in CI. Guard the
handler on the sheet's computed display (`offsetParent` is unreliable for
`position: fixed`). The swipe handle is naturally inert when hidden.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:45:41 +02:00
Ilia Denisov 8dcaf1c6c6 feat(ui): error & state UX — error surface, view states, map selection, sheet gestures (F4)
Tests · UI / test (push) Waiting to run
Tests · UI / test (pull_request) Failing after 7m13s
- lib/error/: classify any caught error into a stable ErrorKind from the
  transport signal (HTTP status / Connect Code / fetch TypeError /
  navigator.onLine); map to translated error.* messages via reportError
  (sticky Retry toast for retryable kinds) or errorMessageKey (inline).
  Mail compose now surfaces the translated 403/error inline.
- lib/ui/view-state.svelte: shared loading/empty/error placeholder with
  the right live-region role + optional action; entity tables
  (races/sciences/ship-classes) migrated, rest adopt incrementally.
- map/selection-ring.ts: accent ring around the selected planet, fed into
  the map buildExtras alongside the reach circles.
- lib/ui/sheet-dismiss.ts: tap-outside + drag-handle swipe-down dismissal
  for the planet/ship-group bottom-sheets (hand-rolled pointer events).

Tests: error, view-state, selection-ring, sheet-dismiss (761 total).
Docs: ui/docs/error-state-ux.md (+ index); F4 marked done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:29:11 +02:00
developer 87d524fb89 Merge pull request 'feat(ui): localisation completeness — persistence + parity guards (F3)' (#28) from feature/ui-finalize-f3-i18n into development
Deploy · Dev / deploy (push) Successful in 38s
Tests · UI / test (push) Successful in 2m10s
2026-05-22 06:57:23 +00:00
Ilia Denisov 1e62837c68 feat(ui): locale persistence + i18n completeness guards (F3)
Tests · UI / test (push) Waiting to run
Tests · UI / test (pull_request) Successful in 2m11s
An audit found the client already i18n-first: one hard-coded UI string
(the battle-scene aria-label, now keyed) and en/ru already share an
identical 692-key set.

- Persist the locale: i18n.setLocale writes localStorage (galaxy-locale)
  and the store boots from stored > browser detection > default, so a
  language switch survives reloads.
- tests/i18n-completeness.test.ts: en/ru key-set parity, non-empty
  values, and locale persistence.
- Docs: ui/docs/i18n.md; mark F3 done in ui/PLAN-finalize.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:48:13 +02:00
developer c56050f5dd Merge pull request 'feat(ui): accessibility pass — WCAG 2.2 AA (F2)' (#27) from feature/ui-finalize-f2-a11y into development
Deploy · Dev / deploy (push) Successful in 35s
Tests · UI / test (push) Successful in 2m38s
2026-05-22 06:43:40 +00:00
Ilia Denisov 70f2973396 fix(ui): darken light-theme danger to meet AA contrast
Tests · UI / test (push) Has been cancelled
Tests · UI / test (pull_request) Successful in 2m33s
With the default theme now following the OS, Playwright renders the light
theme, where the previous light `--color-danger` (#c84d4d, ~3.9:1 on a
near-white surface) failed WCAG 1.4.3 on error text — caught by the axe
scan of the science designer's empty-name error. Darken light
`--color-danger` to #c0392b (~5.5:1 on white; white-on-danger fills stay
≥5:1). Dark theme unchanged.
2026-05-22 08:40:38 +02:00
Ilia Denisov e193f3ca88 feat(ui): default theme to system (follow OS light/dark)
Tests · UI / test (push) Has been cancelled
Tests · UI / test (pull_request) Failing after 2m7s
Light has been signed off, so the theme store's default choice is now
`system` (it was `dark` during the incremental migration). This matches
the app.html pre-paint guard, which already resolved an unset choice via
prefers-color-scheme — removing the brief boot-time mismatch where the
store re-pinned dark. Users still pin light/dark via the account-menu
picker. Updates the store default + its test and the design-system /
finalize-plan docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:36:17 +02:00
Ilia Denisov 642c5b7322 feat(ui): accessibility pass — WCAG 2.2 AA for login/lobby/shell (F2)
Tests · UI / test (push) Waiting to run
Tests · UI / test (pull_request) Successful in 2m9s
Add the a11y foundation and bring login, lobby, and the in-game shell to
WCAG 2.2 AA:

- Primitives: .sr-only + .skip-link (base.css), trapFocus (modal focus
  trap + restore) and restoreFocus (menu focus restore) actions, the
  --color-focus visible ring.
- In-game shell: skip link + focusable main landmark; WAI-ARIA sidebar
  tabs (roving tabindex, arrow/Home/End, tabpanel wiring); menu Escape +
  focus restore (account / view / turn-navigator / map-toggles /
  bottom-tabs); mail compose as a role=dialog modal with a focus trap.
- login / lobby / lobby-create: skip link + main landmark, field labels,
  role=alert / role=status live regions.
- Map canvas: aria-label naming it a visual overview, with its data
  reachable by keyboard via the sidebar inspector and tables (accessible
  alternative; in-canvas keyboard nav deferred).

Gates (chromium-desktop): tests/e2e/a11y-axe.spec.ts scans every
top-level view for WCAG 2.2 AA violations (zero); a11y-keyboard.spec.ts
covers the skip link, menu Escape+restore, and tab roving. Adds
@axe-core/playwright. Docs: ui/docs/a11y.md (+ index). Marks F1 and F2
done in ui/PLAN-finalize.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:25:14 +02:00
developer dcc655c7c4 Merge pull request 'feat(ui): visual design system — tokens + light/dark theming (F1)' (#26) from feature/ui-finalize-f1-tokens into development
Deploy · Dev / deploy (push) Successful in 37s
Tests · UI / test (push) Successful in 2m16s
2026-05-22 05:37:11 +00:00
Ilia Denisov 4ad96b0ef7 feat(ui): migrate all view bodies to design tokens (F1b)
Tests · UI / test (push) Successful in 2m11s
Tests · UI / test (pull_request) Successful in 2m7s
Tokenize every remaining component <style> — calculator, order tab,
inspectors, tables, report sections, lobby, auth, mail, battle viewer,
toasts, map overlays. A scripted pass handled the unambiguous core
palette (text/bg/surface/border/accent/danger/muted), the rest were
mapped to the semantic/grey tokens by role.

Remaining colour literals are the documented exceptions only: the
battle-scene SVG data-visualisation palette (fixed dark, like the WebGL
map canvas), overlay scrims (modal / map-canvas), and directional or
deliberate drop shadows. The default theme stays dark until light
coherence is signed off across the views.

Updates ui/docs/design-system.md (migration status + exceptions).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 07:24:02 +02:00
Ilia Denisov 973480d812 feat(ui): design tokens + light/dark theming, migrate in-game chrome (F1a)
Tests · UI / test (push) Successful in 2m4s
Introduce the shared design-token system under
ui/frontend/src/lib/theme/: tokens.css (dark default + light palette,
plus spacing/radii/typography scales), base.css global baseline
(document background, text, token focus ring, selection), and
theme.svelte.ts (system/light/dark choice, persisted to localStorage,
applied via data-theme on <html>). A pre-paint guard in app.html
resolves the theme before the app boots to avoid a flash, and the theme
picker is wired into the previously-disabled account-menu stub.

Migrate the always-visible in-game chrome to the tokens (header, account
menu, sidebar, tab-bar, bottom-tabs, shell background): dark renders as
before, light comes for free. The default stays dark during the
incremental migration; the remaining view bodies migrate in F1b.

Docs: ui/docs/design-system.md (+ index entry). Test: tests/theme.test.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 07:02:13 +02:00
developer 44ed0a90eb Merge pull request 'docs(ui): finalize MVP plan structure + de-archaeologize topic docs' (#25) from feature/ui-finalization-plan into development 2026-05-21 21:25:06 +00:00
Ilia Denisov a89048f6c5 docs(ui): finalize MVP plan structure and de-archaeologize topic docs
MVP web client (Phases 1-30) is complete; reorganize planning + living docs around that.

- PLAN.md kept as the staged MVP record (1-30) with a status block + pointers; removed the 31-36 stages, regression scenarios, and deferred-TODO section (moved out); fixed a stale cross-machine plan path.

- ui/PLAN-finalize.md (new): active web-finalization plan in 8 stages (visual system, a11y, i18n, error UX, PWA, build hygiene, docs, owner manual-QA loop); absorbs former Phases 33 and 35.

- ui/ROADMAP.md (new): post-MVP (Wails, Capacitor, realistic projection, acceptance + regression scenarios) and triaged deferred follow-ups.

- ui/docs/README.md (new): grouped topic-doc index.

- De-archaeologized all 20 ui/docs topic docs + ui/README.md + ui/core/README.md: stripped Phase-N build history, rewritten as current-state; deferred work now points at ROADMAP.md / PLAN-finalize.md. Docs-only; no code change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:17:51 +02:00
developer 51865b8cf4 Merge pull request 'Phase 30 — Ship Class Calculator (goal-seek, reach circles, planet build)' (#24) from feature/ui-calculator into development
Deploy · Dev / deploy (push) Successful in 38s
Tests · Go / test (push) Successful in 2m6s
Tests · Integration / integration (push) Successful in 1m43s
Tests · UI / test (push) Successful in 2m1s
2026-05-21 19:47:16 +00:00
Ilia Denisov 4d3cfd11a3 docs(ui): mark Phase 30 (ship-class calculator) done in PLAN.md
Tests · Integration / integration (pull_request) Successful in 1m47s
Tests · Go / test (pull_request) Successful in 2m10s
Tests · UI / test (pull_request) Successful in 1m59s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:40:45 +02:00
Ilia Denisov b1b87c8521 feat(ui-calculator): input validation, load caps, ceil display, modernization layout
Tests · Go / test (push) Successful in 2m26s
Tests · UI / test (push) Successful in 2m26s
- custom load capped at cargo capacity (error when exceeded); full load shows the cargo capacity; zero cargo pins load to empty and disables the toggle

- per-input red border + tooltip for every invalid value (blocks, techs, load, MAT, modernization target); no value may be negative; locking a speed is disabled when drive is zero

- display every computed number (results + goal-seek back-solved input) rounded up to 3 decimals via a shared pkg/calc Ceil3 bridged to wasm; engine keeps its own round-to-nearest util.Fixed*

- modernization total upgrade cost spans two columns (single line)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:24:40 +02:00
Ilia Denisov 3ea29cf8b5 fix(ui-calculator): keep calculator state long-lived; don't eject on planet click
Tests · UI / test (push) Successful in 1m59s
Move the calculator's inputs into a page-level calculatorState singleton so they survive the sidebar unmounting the tab on a tab switch (the inspector auto-opens on a planet click). ensureGame resets the design when the active game changes.

While on the calculator, a planet click no longer switches to the inspector — the calculator consumes the selection in its planet area / reach circles. Halve the reach-circle stroke width.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:29:08 +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
developer 00159ddf7c Merge pull request 'ci: per-job pnpm install dir to fix the host-runner setup-pnpm race' (#23) from feature/ci-pnpm-setup-isolation into development
Deploy · Dev / deploy (push) Successful in 40s
Tests · UI / test (push) Successful in 2m4s
2026-05-20 15:31:12 +00:00
Ilia Denisov b24d53b82f ci: install pnpm into a per-job dir to fix the host-runner setup race
Tests · UI / test (push) Waiting to run
Tests · UI / test (pull_request) Successful in 1m56s
`pnpm/action-setup@v4` defaults to installing pnpm in the shared
`~/setup-pnpm`. On the single host-mode runner $HOME is shared across
concurrent jobs, so when two pnpm jobs overlap (e.g. a post-merge
`dev-deploy` and `ui-test`, which sit in different concurrency groups)
their self-installers race and one fails with
`ENOTEMPTY ... rmdir '~/setup-pnpm/node_modules/.bin/store/v11/files'`
before the tests even run.

Point each step's `dest` at `${{ runner.temp }}/setup-pnpm` (a per-job
isolated directory) so concurrent jobs never share the install location.
The action still adds `dest` to PATH, so setup-node's pnpm cache and
later `pnpm` calls are unaffected; the pnpm package store stays shared
(safe — pnpm locks it). Applied to the three workflows that set up pnpm:
ui-test, dev-deploy, prod-build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 17:26:49 +02:00
developer 6572f5b59d Merge pull request 'fix(ui-map): inverse stencil mask for the visibility fog (Safari pan perf, stage 2)' (#22) from feature/ui-map-fog-inverse-mask into development
Deploy · Dev / deploy (push) Successful in 31s
Tests · UI / test (push) Successful in 1m58s
2026-05-20 15:02:03 +00:00