Commit Graph
7 Commits
Author SHA1 Message Date
Claude 9a3af808d9 fix: hide the settled guides copy for the whole gesture, and prove it (#521)
Code review r1 was right that AC5's evidence was empty: the smoke never
forced a settled render during a gesture, so the settled copy of
`.hp-editor-only-layer` was empty at every point it looked, and
`groups() === 1` held whether the copy was hidden or not.

Measuring the case the reviewer named turned up more than a weak assertion.
Ownership of the layer alternates on its own — every settled render ends in
`updated()` → `_commitLiveEditor()`, which empties the live root — so a
settled render mid-gesture takes the guides back and draws them itself, from
the same live `_alignPoint`. That much needs no suppression. But the copy it
leaves behind stays in the settled scene, and the NEXT live paint adds a
second one: measured two `.alignline` on one alignment, the settled one a
grid step behind the marker. So the suppression stays, and now it stays with
a witness.

The smoke counts what is visible, not what is in the DOM: the hidden copy is
still a node, and counting nodes is how this check could have looked green
while showing the user two lines. Its device scenario now drives the whole
handover — force an unrelated settled render mid-drag (`_hdrH`, the same
header-height observer that masked the defect in the S2 measurements), assert
the render actually happened, that the layer went back to the settled scene
with the live point on it, and that one real move later the live painter owns
it again — exactly one visible guide at every step.

Mutant `live-editor-keeps-the-settled-guides-visible` puts the suppression
back under the plan branch, as it was before this issue, and the smoke goes
red on `nextMoveTakesTheLayerBack`.

Issue: #521
User-Visible: no
2026-09-10 23:25:46 +03:00
Claude 53585b451b fix: alignment guides follow the live gesture again (#521)
#451 moved every editor gesture onto the live painter, and the guides stayed
behind in the settled scene. While a gesture runs, the settled scene is not
re-rendered at all, so the guides did not follow the marker in the device
editor, the shape in the backdrop editor, or the cursor while a contour is
drawn in the plan editor. Measured with real pointer events on the demo stand
against `origin/dev`, after waiting for the editor chrome to settle: three
gestures, each exactly on another object's axis, 0 settled render cycles,
`.alignline` 0 and no `.alignguides` group in all three.

The report called it two breaks. It is one — the layer — plus one thing that
would have broken the repair: `_alignPoint` read `_pos`, which during a live
gesture answers from the snapshot of the last settled render. Over one drag:
live 254.17 → 220.83 while `_pos` stayed at 254.17, eight grid steps behind,
so a restored layer would have drawn the guide at the marker's old place.

The live template now paints the guides in all three modes (the device editor
had no template at all — `paintDevice` only moves the marker element), and
`_alignPoint` takes the live position. The settled copy of
`.hp-editor-only-layer` is made transparent for the duration of any editor
gesture, not only in plan mode: two guides, one of them stale, is what the
user would otherwise see when an unrelated settled render lands mid-gesture.
`_renderAlignGuides` on the card becomes soft — a gesture that starts while
the editor runtime is still loading must cost nothing, and an exception inside
a `requestAnimationFrame` paint would take the whole gesture with it.

The witness is rewritten around the defect that hid this for two stable
releases: the old smoke assigned `_deviceDrag`/`_decorDraft` wholesale, and an
assignment with `oldValue == null` does not route to the live path — it
verified a state a real gesture never reaches. Every scenario now drives real
`PointerEvent`s, waits for silence first (the `_hdrH` settling window right
after entering a mode hands out settled frames that make even the broken code
draw a guide), and asserts zero settled cycles during the movements plus
exactly one `.alignguides` group. #400's exclusion is checked without touching
the drag state: the dragged marker must simply be absent from the candidates.
On `origin/dev` the smoke fails on nine of its facts; a witness that stays
green before the fix was the actual bug here.

Mutants: `live-editor-devices-drops-align-guides`,
`live-editor-decor-drops-align-guides`, `live-editor-plan-drops-align-guides`,
`align-point-reads-frozen-snapshot` — one per AC, all guarded by the smoke.
`test/smoke-harness-contract.test.mjs` pins that the smoke cannot go back to
fabricating gesture state.

Issue: #521
User-Visible: yes
2026-09-10 22:20:18 +03:00
Codex fe3b85c06b fix: the corner handle wins on small furniture, guides exclude the real drag (#400)
(1) Corner and edge handles carry the same hit radius (1.8 % of the
view), so on furniture narrower than 4·hr — a 40 cm cabinet — the two
circles overlap and whichever is painted last takes the tap. Edges were
painted last. Corners are now, because a side handle scales one axis
while a corner scales both, and the object is small exactly when
proportional resize matters most. The visible beads are unchanged.

The audit called this 'proportional resize becomes unavailable'; the
measurement says otherwise and the spec records the correction: the
corner centre lies outside the edge circle, so the corner was reachable
— its area was halved, not lost. A polish, not a bug, and worth fixing
because it is one line of ordering.

(2) Alignment guides in the devices mode excluded the dragged marker by
_drag, which has been null there since #74 moved device dragging into
_deviceDrag. So the marker being moved was among its own candidates.
Nothing looked wrong because a point always matches itself within
tolerance — the guide was drawn from the marker to itself, visually
identical to an honest one, and the smoke asserted only guides() >= 1.
The smoke now compares the candidate lists with and without the drag and
demands exactly one removed entry.

(3) The 38 settings-help strings stay in the initial chunk, and that is
now a recorded decision rather than an oversight: measured 2 654 B gzip,
0.9 % of the ceiling, against splitting a synchronous dictionary in two,
a second request on first hint, and a second source for the key type
derived from en.json (#391). docs/ARCHITECTURE.md says so, with the
number that would justify revisiting it.

Both mutants run by hand: reverting the paint order reddens the 40 cm
probe while the 160 cm one stays green; restoring _drag reddens the
guides smoke.

User-Visible: yes
Issue: #400
2026-08-31 07:56:27 +03:00
Matysh a6b0850a0c test: update device alignment guide fixture
Issue: #74
User-Visible: no
2026-08-30 16:24:48 +03:00
Matysh 94b298962a v1.48.0: the canvas is always square, the plan is centred inside it
A space carried an aspect ratio, and coordinates were normalised against it: x
by the width, y by the height. Every geometric question therefore depended on a
per-space number, and picking a canvas orientation was a decision the user had
no reason to make. The render space is now NORM_W x NORM_W and a plan image is
fitted into it by its OWN ratio, centred — wide plans get margins above and
below, tall ones at the sides.

Migration (geometry_migration.py, pure and unit-tested) runs once at setup under
the write lock. Nothing about a drawing changes: the old box is padded out to a
square and every coordinate re-expressed against it — rooms as rects and
polygons, openings and their lengths, decor, view_box, and the marker positions
in the separate layout store. In render units it is a uniform scale plus an
offset, so angles and proportions are exact. cell_cm is scaled for tall plans,
because the grid pitch is a fraction of the width: without it a wall would
measure less than it does.

 is now dropped by the schema rather than accepted — a stale tab sending
it would be sending coordinates from the old normalisation too, and honouring
the field would not make them right.

The demo fixture was migrated with the same transform, so the smokes exercise
the new geometry rather than a square-native fake; six of them needed their
render-space helpers updated and one its click coordinates.

Not released — dev only, per the owner's instruction.
2026-07-28 22:20:59 +03:00
Matysh 41b20e1901 test v1.43.2: smokes that can fail, in CI, and an honest TESTING.md
T1: demo/serve.mjs exports check/checkAll/finish — all 48 smokes now
assert named facts and exit non-zero on a mismatch or an uncaught
in-card exception (verified by breaking the kiosk guard on purpose).
Informational values were frozen from a v1.43.1 run and cross-read
against the source; timings assert budgets, not exact numbers.

T2: new CI job 'smoke' gated on 'frontend', builds a FRESH bundle
before running (the committed demo/srv/assets copy is a snapshot) and
uploads per-file logs on failure.

T3: [auto] now means 'a named failing check exists' and each line names
it (43 lines); 72 aspirational markers honestly downgraded to [manual].
Fixed the 'ZERO edit buttons' contradiction (wrong since v1.30.1) and
the opening-click line (true again since v1.43.1).

Three smokes carried pre-v1.39.0/v1.25 expectations and were testing
old behaviour: tap defaults for lights, card-wide tap action, label drag
requiring plan mode.

DEVELOPMENT.md documents the harness contract.
2026-07-27 11:20:31 +03:00
Matysh df9e158efb feat v1.40.0: smart guides — alignment helper in every editor
- alignGuides/segmentAngle/is45 pure helpers (+2 unit tests, 108);
  per-context candidates (room vertices + path pts / other icons /
  decor endpoints+corners / other room cards), nearest per axis, max
  two guides, dashed accent lines with a source dot
- cursor badge shows length + angle, green on 45deg multiples
- indication only, no magnetism (owner's decision); guides live in
  plan/devices/decor, never in View
- smoke_align_guides.mjs (9 checks); TESTING/CHANGELOG same-commit
2026-07-23 17:38:33 +03:00