Polish: imperial, clear-bed, keyboard nudging, empty states (#18) #37

Merged
steve merged 2 commits from phase-7-polish into main 2026-07-19 04:44:26 +00:00
2 Commits
Author SHA1 Message Date
steveandClaude Opus 4.8 c4bc5e9f47 Address Gadfly review on #18: clear-bed + nudge robustness
Build image / build-and-push (push) Successful in 8s
- useClearObject: Promise.allSettled + invalidate in onSettled (not onSuccess),
  so a partial failure still reconciles the cache with the server instead of
  losing the already-removed rows; reports how many failed.
- Keyboard nudge: the keydown effect is now mounted once and reads live values
  from a ref, so a data refetch can't re-subscribe and cancel a pending
  debounced commit. The pending move is flushed on unmount instead of dropped,
  a fire only commits if its live value is still present (a drag that cleared it
  already committed its own PATCH), and nudges are ignored while a pointer drag
  is active — closing the nudge/drag race.
- usePageTitle: dropped the restore-on-unmount, which raced and could clobber the
  incoming route's title on a fast navigation.
- ClearBedModal disables confirm on an empty plop list.
- Extracted EditorHint (empty-state overlays) and objectDisplayName (harmonized
  the name fallback across the toolbar and clear dialog).

Skipped (false positive): "nudge clamp ignores rotation" — the clamp is to the
object's LOCAL bounds (±w/2, ±h/2), exactly what the server enforces, so a plop
can't escape a rotated bed.

tsc --noEmit clean; 24/24 vitest; production build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
2026-07-19 00:42:51 -04:00
steveandClaude Opus 4.8 73fa34cb81 Polish: clear-bed, keyboard nudging, empty states, titles, 404 (#18)
Build image / build-and-push (push) Successful in 7s
Gadfly review (reusable) / review (pull_request) Successful in 12m58s
Adversarial Review (Gadfly) / review (pull_request) Successful in 12m58s
- Clear bed: a "Clear (N)" action on a focused plantable object → confirm modal
  (ClearBedModal) → soft-removes every active plop (useClearObject loops PATCHes,
  invalidates once); rows kept with removed_at.
- Keyboard nudging (desktop): arrows move the selected object/plop 1cm, Shift
  10cm; the PATCH is debounced ~400ms on key-idle. Plops nudge in the object's
  local frame and clamp to its bounds; ignored while typing in a field.
- Empty states: hint to place a first bed on an empty field, and to add a plant
  in an empty focused bed (both non-interactive overlays).
- Paper cuts: per-route document titles (usePageTitle on every page), an emoji
  favicon, and a 404 catch-all route (NotFound) inside the app shell.
- Mobile: plop tap targets stay ≥~44px at low zoom via a transparent hit circle.
- Ownership check hoisted to the authoritative ownerId==me so the nudge handler
  can gate on canEdit before the loading early-returns.

Imperial coverage verified across inspectors/forms/cards/picker (all via the
unit-aware helpers); conversion helpers stay in lib/units.ts (their home since #8).

tsc --noEmit clean; 24/24 vitest; production build green.

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