Touch ergonomics: bigger handles + on-screen nudge pad (#104) #117

Merged
steve merged 2 commits from feat/touch-ergonomics into main 2026-07-22 14:20:24 +00:00
2 Commits
Author SHA1 Message Date
steveandClaude Opus 4.8 b0e11bce17 Address touch review: one coarse-pointer signal + stable nudge callbacks
Build image / build-and-push (push) Successful in 10s
Gadfly on #104:
- The handles keyed off `pointer: coarse` but the NudgePad off `md:hidden`
  (viewport), so a large touchscreen or a narrow mouse window got them
  disagreeing. Extracted one `isCoarsePointer` in shared.ts that both use —
  the pad now shows on a coarse pointer, same as the bigger handles.
- Wrapped commitLater/nudgeSelected in useCallback([]) — stable identity, so
  NudgePad doesn't re-render each parent render, and the mount-once keydown
  effect capturing nudgeSelected is now explicitly safe (a comment spells out
  the refs-only invariant that makes the empty-deps capture correct).
- isCoarsePointer's optional-chained matchMedia keeps it false (mouse
  defaults) under test/SSR, addressing the constants-file testability note.

tsc + build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
2026-07-22 10:19:53 -04:00
steveandClaude Opus 4.8 1323a03acd Touch ergonomics: bigger handles + on-screen nudge pad (#104)
Build image / build-and-push (push) Successful in 10s
Gadfly review (reusable) / review (pull_request) Successful in 10m11s
Adversarial Review (Gadfly) / review (pull_request) Successful in 10m12s
Two touch gaps the audit named:

- Resize/rotate handles were a fixed 12px — fine for a mouse, hard for a
  fingertip. HANDLE_PX is now 22px on a coarse pointer (touch), 12px
  otherwise. Read once at load.
- Fine positioning was keyboard-only (arrow-nudge), which a phone can't
  reach and a drag can't do at single-cm precision. Added an on-screen
  NudgePad — a ↑←→↓ d-pad (~40px targets) shown while something's selected
  on a touch layout (md:hidden).

To share behaviour without duplicating the intricate part, extracted
nudgeSelected(dx, dy) from the keyboard handler — the live-geometry update
+ one debounced PATCH (so a burst of nudges from either surface commits
once) + the plop-bounds clamp. The keyboard handler and the pad both call
it. Verified live: the pad moves a selected bed 1cm/tap on mobile, and the
keyboard arrows still nudge on desktop after the refactor.

(The rail-vs-toast layering the issue also lists was resolved by #101 —
the rail is now an in-flow peek, not a fixed sheet the toast could cover.)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
2026-07-22 10:13:23 -04:00