.preview was width:100% inside max-height:50vh container — tall photos
were clipped. Now: max-width:100%, max-height:50vh, width/height auto,
object-fit:contain, flex-centered in the wrap.
Added e2e regression test: loads a photo, verifies the canvas bounding
box fits the viewport, preserves aspect ratio, and is not clipped by
the container.
reduceAxisState 'rotate' now changes state.rect.angle and re-derives
state.axis via axisFromRect(newRect). The rectangle rotates as a whole;
the axis for Measure is derived from the rotated rect's long side.
2 new regression tests: rect.angle changes on rotate, axis is
re-derived from rotated rect. 109/109 tests green.
Spec CAPTURE.md §Browsers: Phone iOS Safari / Android Chrome, PC
Chrome / Edge / Firefox, others hard stop.
- browserGate.js: UA detection + allowlist (pure, DOM-free)
- +layout.svelte: renders hard-stop panel when unsupported
- iPadOS 13+ reports as Macintosh — added Safari rule (Version/ +
Safari/ without Chrome/) so iPad Safari is not blocked
- 27 unit tests incl. iPad Safari regression
- 107/107 full suite green
Root cause: auto selected the key with MORE kept pixels, which always
picked the green key (it keeps 100% of dark non-green backgrounds).
The black key correctly isolates the slab on dark surfaces.
Fix:
- auto now prefers the key with FEWER kept pixels (removed more bg)
when both are non-empty
- green key has a greenness pre-filter: non-greenish pixels pass
through (kept) so the black key can handle them, instead of the
green key keeping 100% of a dark surface
- added 3 regression tests: dark-bg auto selection, green-sheet auto
selection, green-key passthrough on non-green surfaces
106/106 unit tests green.
Spec CAPTURE.md §Mask lists exactly two controls: Sheet and Sensitivity. Edge offset and feather nudge buttons were extra. Removed from the UI; core params (edgeOffset, feather) remain in maskState.js, still unit-tested.