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>
This commit is contained in:
Ilia Denisov
2026-05-21 21:24:40 +02:00
parent 3ea29cf8b5
commit b1b87c8521
17 changed files with 343 additions and 9 deletions
+1
View File
@@ -49,6 +49,7 @@ on the JS-side `globalThis.galaxyCore` (registered in
| `shieldsForDefence` | `calc.ShieldsForDefence(targetDefence, sTech, restMass)` | `number\|null` | calculator goal-seek (defence → shields) |
| `cargoForEmptyMass` | `calc.CargoForEmptyMass(targetEmptyMass, restMass)` | `number\|null` | calculator goal-seek (mass → cargo) |
| `loadForFullMass` | `calc.LoadForFullMass(targetFullMass, emptyMass, cTech)` | `number\|null` | calculator goal-seek (loaded mass → load)|
| `ceil3` | `calc.Ceil3(value)` (`pkg/calc/number.go`) | `number` | calculator display rounding (round up to 3 dp) |
`BombingPower` and the per-turn build loop are no longer engine-only:
Phase 30 extracted `BombingPower` from
+23 -3
View File
@@ -28,8 +28,11 @@ in as a per-ship result rather than a separate mode.
icon once overridden; clicking it resets to the default.
2. **Calculator area** — derived results: empty/loaded mass, empty/
loaded speed, attack, defence, bombing (per ship), cargo capacity.
A load toggle (empty / full / custom) sets the cargo load that the
loaded-column results use.
A load toggle (empty / full / custom) sets the cargo load (in cargo
units) that the loaded-column results use. At **full** the toggle
shows the ship's cargo capacity; a **custom** load over that capacity
is flagged as an error. With a zero cargo block there is no hold, so
the load is pinned to empty and the toggle is disabled.
3. **Planet area** — when an own planet is selected on the map, shows
its MAT (overridable) and the single-turn build rate (ships per turn,
turns per ship). The realistic multi-turn forecast with CAP/COL
@@ -61,7 +64,24 @@ appears once a value is pinned, click to release):
solved block that fails the value rules — leaves the locked cell in a
red error state and does not apply. Inverse solving lives in
`pkg/calc/solve.go`; the bisection for defence → shields is the only
non-analytic case.
non-analytic case. Locking a speed is disabled when the drive block is
zero (a deliberately immobile ship has no speed to back-solve).
## Validation and display
Every numeric input is validated independently and an offending one gets
a red border and a hover/tap tooltip with the reason: no value may be
negative, the five blocks follow the engine value rules
(`pkg/calc/validator.go`, surfaced per-field by
`shipClassFieldErrors`), and a custom load may not exceed cargo capacity.
Every displayed number — the derived results and the goal-seek
back-solved input — is rounded **up** to three decimals through the
shared `pkg/calc/number.go.Ceil3` (bridged as `core.ceil3`), so a value
is never shown lower than it is (a speed of 5.0003 reads 5.001). The
engine keeps its own round-to-nearest `util.Fixed*`; `Ceil3` is a
display-only helper that lives in `pkg/calc` so the UI and Go share one
implementation.
## Create / load / delete