Files
galaxy-game/ui
Ilia Denisov cbf7f65916
Tests · UI / test (push) Has been cancelled
Tests · Integration / integration (pull_request) Successful in 1m45s
Tests · Go / test (pull_request) Successful in 2m3s
Tests · UI / test (pull_request) Successful in 2m28s
fix(ui): F8-06 calculator polish — unified spinner UX, lock-infeasible on (0, 1), dropdown reset-changes
Owner review on PR #61:

- п.9 (option B). Hide the native spinner on EVERY numeric input in
  the calculator (DWSC blocks, armament, tech, planet MAT, custom
  load, lock value, modernization target tech) and drive every step
  through ArrowUp / ArrowDown. The column widths stay stable and the
  inputs read consistently across the whole row. The ship blocks
  keep the smart (0 ↔ 1) jump on ArrowUp/ArrowDown; armament steps
  ±1 with a JS handler instead of relying on the native spinner.
  Other inputs step by their natural grain (±0.001 for tech / lock,
  ±0.01 for MAT / load).
- п.10. Tech-level labels (`tech-val`) and the planet MAT label
  (`mat-val`) now read through the same `Ceil3` formatter as the
  derived results, so plain-text numeric values share the report's
  3-decimal tabular formatting. The design-area component receives
  `formatNumber` as a prop; the resolved (goal-seek) cell uses the
  same formatter, so the read-only computed value matches the rest
  of the row.
- п.12. `computeCalculator` now validates the back-solved block
  against the same DWSC rule the live validator enforces (`0` or
  `≥ 1`). When the solver lands in the `(0, 1)` gap (e.g. attack
  0.5 / weaponsTech 1.5 → weapons 0.333…) the lock is flagged
  infeasible — the lock input flips red and the claimed block is
  NOT back-solved into the invalid range, so the design preview
  keeps reading the user's own typed values instead of silently
  showing a sub-1 block.
- new. Selecting an existing ship class from the name datalist now
  loads it immediately. `change` fires only on blur in Firefox,
  which is why the previous behaviour looked delayed; switching the
  load to `oninput` with an `InputEvent.inputType` check makes the
  load synchronous everywhere (datalist replacement carries
  `"insertReplacementText"` in Chromium / WebKit, `undefined` in
  Firefox; keyboard typing always carries a typing `inputType`).
  Before loading we compare the live blocks to the previously
  loaded class (or to the empty defaults) and, if they differ, ask
  through a `window.confirm`. On decline we revert the name field
  and leave the design untouched.

Tests: calculator-tab and calc-model gain six cases (armament
step, tech/MAT formatter labels, lock infeasible on (0, 1) for
both attack→weapons and emptyMass→cargo, lock-value Arrow step,
dropdown immediate load + confirm-blocks-load + confirm-allows-load),
all 779 vitest tests green. docs/calculator-ux.md follows the new
behaviour.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 18:02:56 +02:00
..
2026-05-23 16:55:02 +02:00
2026-05-07 07:18:55 +02:00

ui — Galaxy Cross-Platform Client

ui/ hosts the new cross-platform Galaxy client. A single TypeScript + Svelte source tree builds to five targets: web, web-mobile, standalone PC (mac/win/linux), iOS, and Android. A shared Go module (ui/core) carries envelope cryptography, the FlatBuffers codec, keypair management, and a thin bridge over pkg/calc/ for UI-side game math; it is compiled to WASM for the web targets, gomobile native libraries for mobile, and embedded directly in Wails on desktop. All network I/O lives on the TypeScript side via ConnectRPC, so the Go module is a pure compute boundary on every platform.

The web target is a finalized, installable PWA: a shared design-token system with light/dark themes, WCAG 2.2 AA accessibility, en/ru localisation with a persisted choice, a central error surface, and offline tolerance — see the topic docs below.

The legacy Fyne client under client/ is reference-only. Nothing in ui/ imports from it.

The strategic rationale (why Svelte, why PixiJS, why Go-as-WASM, why Wails+Capacitor) lives outside the repo at ~/.claude/plans/buzzing-questing-fountain.md. This README is a quick orientation; deeper design notes live under ui/docs/.

Targets

Target Wrapper Toolchain Status
web browser tab Vite + WASM implemented
web-mobile mobile browser Vite + WASM implemented
desktop (mac) Wails v2 Go + Wails CLI planned (see ROADMAP.md)
desktop (win) Wails v2 Go + Wails CLI planned (see ROADMAP.md)
desktop (linux) Wails v2 Go + Wails CLI planned (see ROADMAP.md)
iOS Capacitor gomobile + Xcode planned (see ROADMAP.md)
Android Capacitor gomobile + Gradle planned (see ROADMAP.md)

Layered architecture

  • TypeScript + Svelte 5 frontend, shared across all five targets, scaffolded with SvelteKit + Vite.
  • PixiJS v8 with dual WebGPU/WebGL backend for the world map renderer.
  • Go module ui/core/ as a compute-only library (canonical bytes, Ed25519 sign/verify, FlatBuffers codec, keypair, thin bridge to pkg/calc/) compiled to WASM, gomobile, and Wails-embedded native.
  • TypeScript-side Core interface with three adapters (WasmCore, WailsCore, CapacitorCore) selected at build time.
  • GalaxyClient on top of Core performs all network I/O via ConnectRPC (@connectrpc/connect-web) on every platform.
  • Per-platform storage: WebCrypto + IndexedDB on web, OS keychain
    • SQLite on desktop, iOS Keychain / Android Keystore + SQLite on mobile, all behind a single KeyStore and Cache TypeScript interface.
  • Single-URL app-shell navigation: the game UI is one route served at /game/; the screen (login / lobby / game) and the in-game view are in-memory state (lib/app-nav.svelte.ts), not URLs, so the address bar never changes. Browser Back/Forward move between screens via shallow routing without touching the URL — a model that also suits the bundled standalone targets (Wails / Capacitor) that have no URLs. One active view occupies the main area at a time; the sidebar holds a single tool (calculator, inspector, or order) with persistent state on switch. See docs/navigation.md.

Repository layout

ui/
├── PLAN.md                 staged implementation plan
├── ROADMAP.md              planned desktop / mobile / multi-turn features
├── PLAN-finalize.md        PWA, accessibility, localisation, error UX
├── Makefile                wasm / ts-protos / web / mobile / desktop targets
├── README.md               this file
├── buf.gen.yaml            local-plugin TS Protobuf-ES generator
├── docs/                    topic-based design notes
│   ├── auth-flow.md         email-code login, session store, revocation
│   ├── i18n.md              translation primitive, native-name picker, extensibility
│   ├── order-composer.md    order draft model, persistence, history-mode wiring
│   ├── storage.md           web KeyStore/Cache, IDB schema, baseline
│   ├── testing.md           per-PR / release test tiers
│   └── wasm-toolchain.md    TinyGo build, JSDOM loading, bundle budget
├── core/                    ui/core Go module (canonical bytes, keypair)
├── wasm/                    TinyGo entry point exposing Core to JS
├── mobile-bridge/           gomobile bindings (planned — see ROADMAP.md)
├── desktop/                 Wails project (planned — see ROADMAP.md)
├── mobile/                  Capacitor project (planned — see ROADMAP.md)
└── frontend/                SvelteKit / Vite source
    ├── src/api/             GalaxyClient + typed Connect client + auth + session
    ├── src/lib/             app-shell nav + screens + game shell, env config, session store, stores
    ├── src/platform/core/   Core interface + WasmCore adapter
    ├── src/platform/store/  KeyStore/Cache interfaces + web adapter
    ├── src/proto/           generated Protobuf-ES + Connect descriptors + FlatBuffers TS bindings
    ├── src/routes/          single-URL app-shell: `/game/` dispatcher (+page.svelte) + `/__debug/*`
    └── static/              core.wasm + wasm_exec.js (built by `make wasm` / CI; gitignored)

Linked topic docs:

  • docs/navigation.md — single-URL app-shell, screens and views as in-memory state, screen history, sidebar tools.
  • docs/auth-flow.md — email-code login, session store state machine, revocation watcher.
  • docs/lobby.md — lobby UI sections, application / invite lifecycle, create-game form defaults.
  • docs/i18n.md — translation primitive, native-name language picker, recipe for adding a new locale.
  • docs/storage.md — web KeyStore/Cache, IndexedDB schema, browser baseline.
  • docs/order-composer.md — local order draft store, persistence, history-mode wiring.
  • docs/wasm-toolchain.md — TinyGo build, loading recipe, bundle size budget.
  • docs/design-system.md — design tokens, light/dark theming, component conventions.
  • docs/a11y.md — WCAG 2.2 AA approach, axe + keyboard gates, shared a11y primitives.
  • docs/error-state-ux.md — central error surface, shared loading/empty/error states, selection marker, sheet dismissal.
  • docs/pwa-strategy.md — installable, offline PWA: service worker, manifest, icons.
  • docs/testing.md — Tier 1 per-PR + Tier 2 release test tiers.

Build pipeline

Every cross-target build flows through make at this level. Native targets are placeholders until their platform work lands; running make with no arguments prints the current placeholder map.

make web             Vite production build
make wasm            TinyGo → core.wasm
make ts-protos       Connect-ES + Protobuf-ES gen
make fbs-ts          FlatBuffers TS bindings via flatc
make gomobile        gomobile bind → ios + android   (planned — see ROADMAP.md)
make desktop-mac     Wails build for darwin           (planned — see ROADMAP.md)
make desktop-win     Wails build for windows          (planned — see ROADMAP.md)
make desktop-linux   Wails build for linux            (planned — see ROADMAP.md)
make ios             Capacitor + xcodebuild           (planned — see ROADMAP.md)
make android         Capacitor + gradle               (planned — see ROADMAP.md)
make all             every target above

Local development

For UI work against a real stack, the tools/local-dev/ docker compose brings up postgres + redis + mailpit + backend + gateway in one command, and ui/frontend/.env.development is already wired to talk to it:

make -C tools/local-dev up        # build + start, wait for healthy
pnpm -C ui/frontend dev           # Vite on the host
# UI:     http://localhost:5173
# Mailpit: http://localhost:8025

The stack accepts a fixed dev code (123456) in addition to the real Mailpit-delivered one. Full runbook in ../tools/local-dev/README.md.

For testing the production-shaped surface — Caddy in front of the gateway, statically served UI bundle, real https://*.galaxy.lan hostnames — use the long-lived dev environment at ../tools/dev-deploy/. It is redeployed by Gitea Actions on every merge into development.

Topic docs

Topic docs live under ui/docs/ (testing tiers, WASM toolchain, navigation shell, renderer internals, sync protocol, auth flow, and so on).

Cross-references

  • PLAN.md — staged implementation plan (historical record of completed work).
  • ROADMAP.md — planned desktop / mobile / multi-turn projection features.
  • PLAN-finalize.md — PWA, accessibility, localisation, error UX finalization work.
  • ../docs/ARCHITECTURE.md — platform architecture and the transport security model (§15) the client envelope contract derives from.
  • ../docs/FUNCTIONAL.md — per-domain user stories that drive the UI flows.
  • ../docs/TESTING.md — project-wide testing layers; UI-specific test tiers (Vitest, Playwright) live in ui/docs/testing.md.