feat(ui): F8-12 — map polish (zoom invariance, labels, selection, soft radius) (#55)
Tests · UI / test (push) Waiting to run
Tests · UI / test (pull_request) Failing after 5m16s

* Honest pixel-space sizing for `pointRadiusPx` / `strokeWidthPx`: the
  renderer divides by the current camera scale on every
  `viewport.zoomed` so thin lines / small markers stay the same on-screen
  size at any zoom.
* Known-size planets switch to `pointRadiusWorld`, softened against the
  reference scale by `PLANET_SIZE_ZOOM_ALPHA = 0.33`; unidentified
  planets pin to a 3-px disc.
* New planet label layer renders a two-line `name / #N` legend under
  each planet (`#N` only for unidentified or when the new `planetNames`
  toggle is off). Selection now paints an inverse-fill frame around the
  selected planet's label plus an outline on the disc; the old
  selection-ring primitive is retired.
* Bombing markers swap the separate CirclePrim for a planet-outline
  overlay (damaged / wiped colour); the report deep-link moves to a
  "view bombing report" link in the planet inspector.
* Docs + tests follow: `renderer.md` reflects the new sizing contract +
  label / outline layers, vitest covers the sizing math, label
  formatting, and the new toggle, and the map-toggles e2e adds a
  persistence case for `planetNames`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-27 23:51:16 +02:00
parent ba93a9092e
commit 680ebac919
30 changed files with 1240 additions and 322 deletions
+3 -21
View File
@@ -110,7 +110,7 @@ describe("buildBattleAndBombingMarkers", () => {
expect(out.primitives).toHaveLength(0);
});
it("emits one yellow ring per damaged bombing and red per wiped", () => {
it("does not emit bombing primitives (F8-12 / #30) — the planet outline is drawn elsewhere", () => {
const report = makeReport({
planets: [
{
@@ -163,28 +163,12 @@ describe("buildBattleAndBombingMarkers", () => {
attackPower: 1,
wiped: false,
},
{
planetNumber: 2,
planet: "B",
owner: "X",
attacker: "Y",
production: "MAT",
industry: 0,
population: 0,
colonists: 0,
industryStockpile: 0,
materialsStockpile: 0,
attackPower: 1,
wiped: true,
},
],
});
const out = buildBattleAndBombingMarkers(report);
const rings = out.primitives.filter((p) => p.kind === "circle");
expect(rings).toHaveLength(2);
expect(rings[0].style.strokeColor).toBe(DARK_THEME.bombingDamaged);
expect(rings[1].style.strokeColor).toBe(DARK_THEME.bombingWiped);
expect(out.primitives.filter((p) => p.kind === "circle")).toHaveLength(0);
// `setPlanetOutlines` in the renderer paints the bombing accent.
});
it("paints markers with the supplied palette's colours", () => {
@@ -231,11 +215,9 @@ describe("buildBattleAndBombingMarkers", () => {
const out = buildBattleAndBombingMarkers(report, LIGHT_THEME);
const lines = out.primitives.filter((p) => p.kind === "line");
const rings = out.primitives.filter((p) => p.kind === "circle");
for (const l of lines) {
expect(l.style.strokeColor).toBe(LIGHT_THEME.battleMarker);
}
expect(rings[0].style.strokeColor).toBe(LIGHT_THEME.bombingWiped);
// The accents are deliberately distinct between the palettes.
expect(LIGHT_THEME.battleMarker).not.toBe(DARK_THEME.battleMarker);
expect(LIGHT_THEME.bombingWiped).not.toBe(DARK_THEME.bombingWiped);