From c4bc5e9f47f2716458c00ac588f8282dab370f3f Mon Sep 17 00:00:00 2001 From: Steve Dudenhoeffer Date: Sun, 19 Jul 2026 00:42:51 -0400 Subject: [PATCH] Address Gadfly review on #18: clear-bed + nudge robustness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi --- web/src/editor/ClearBedModal.tsx | 2 +- web/src/editor/EditorHint.tsx | 18 +++++++ web/src/editor/kinds.ts | 5 ++ web/src/lib/objects.ts | 14 ++++-- web/src/lib/usePageTitle.ts | 9 ++-- web/src/pages/GardenEditorPage.tsx | 75 ++++++++++++++++++------------ 6 files changed, 82 insertions(+), 41 deletions(-) create mode 100644 web/src/editor/EditorHint.tsx diff --git a/web/src/editor/ClearBedModal.tsx b/web/src/editor/ClearBedModal.tsx index 76fd2fc..ed5665e 100644 --- a/web/src/editor/ClearBedModal.tsx +++ b/web/src/editor/ClearBedModal.tsx @@ -31,7 +31,7 @@ export function ClearBedModal({ - - {focusedObject.name || kindDef(focusedObject.kind)?.label || 'Object'} - + {objectDisplayName(focusedObject)} {canEdit && (!focusedObject.plantable ? ( Not plantable @@ -273,18 +294,10 @@ export function GardenEditorPage() { {/* Empty-state hints (non-interactive overlays). */} {canEdit && focusedObjectId == null && objects.length === 0 && ( -
-

- Pick a shape from the palette, then tap the field to place your first bed. -

-
+ Pick a shape from the palette, then tap the field to place your first bed. )} {canEdit && focusedObject?.plantable && focusedPlops.length === 0 && !armedPlant && ( -
-

- Tap “+ Add plant”, then tap inside the bed to place plops. -

-
+ Tap “+ Add plant”, then tap inside the bed to place plops. )} @@ -330,7 +343,7 @@ export function GardenEditorPage() { {clearing && focusedObject && ( ({ id: p.id, version: p.version }))} gardenId={gid} onClose={() => setClearing(false)}