Field editor: palette, select/move/resize/rotate, inspector, optimistic sync #11

Closed
opened 2026-07-18 18:17:30 +00:00 by steve · 0 comments
Owner

Part of epic #20 · Design: DESIGN.md

Context

This turns the canvas foundation (#9) into the real field editor: load a garden from the server, drag object kinds in from a palette, and select/move/resize/rotate them with changes synced optimistically. After this issue, the "landscaping UI" half of pansy's vision works end-to-end; plops inside beds come in #15.

Scope

  • Wire GardenCanvas into /gardens/:id via react-query on GET /gardens/:id/full; delete the mock-data demo route.
  • Palette (web/src/editor/Palette.tsx): the seven object kinds with icons + sensible default sizes (bed 120×240 cm, grow bag ⌀40, container ⌀60, in-ground 200×200, tree ⌀300 canopy, path 100×300, structure 200×200). Desktop: drag from palette onto the canvas. Mobile: tap a kind → tap the canvas to place. POST on placement.
  • Selection: click/tap an object → selection outline + handles; click empty space clears. Drag body to move; corner handles resize (center-compensated so the opposite edge stays put); a rotate handle above the object snaps to 15° with Shift/long-press for free rotation; Delete/Backspace (with confirm for plantable objects that contain plantings).
  • Inspector (web/src/editor/Inspector.tsx): desktop right panel / mobile bottom sheet for the selected object — name, kind (read-only), dimensions + position (unit-aware inputs), rotation, color override, notes, plantable toggle, delete.
  • Optimistic sync: local state moves instantly during gestures; one PATCH on gesture end carrying version; on 409, roll back to the server's current row, refetch, and toast "updated elsewhere". Queue-depth 1 per object (drop intermediate states).
  • ?focus=objectId param: on load, zoomToFit that object (groundwork for #15's focus mode).
  • z-order: paths/structures render under beds; explicit z_index respected; no UI for reordering yet (Inspector numeric input is fine).

Out of scope

  • Plops/focus interactions (#15). Read-only viewer mode (#17). Undo/redo (post-v1). Multi-select.

Key files & patterns

  • Gestures/viewport from #9; API + 409 envelope from #7/#10; component map in DESIGN.md § Frontend layout.

Dependencies

Blocked by #8, #9, #10.

Acceptance criteria

  • Desktop: drag a bed from the palette, move/resize/rotate it, set its name in the inspector, reload — everything persisted.
  • Phone: place and move an object with touch; inspector opens as a bottom sheet; pinch-zoom still works while an object is selected.
  • Open the same garden in two browsers, move the same object in both → the loser gets a rollback + toast, no silent divergence.
  • Network tab shows one PATCH per completed gesture, not a stream.
Part of epic #20 · Design: [DESIGN.md](https://gitea.stevedudenhoeffer.com/steve/pansy/src/branch/main/DESIGN.md) ## Context This turns the canvas foundation (#9) into the real field editor: load a garden from the server, drag object kinds in from a palette, and select/move/resize/rotate them with changes synced optimistically. After this issue, the "landscaping UI" half of pansy's vision works end-to-end; plops inside beds come in #15. ## Scope - [ ] Wire `GardenCanvas` into `/gardens/:id` via react-query on `GET /gardens/:id/full`; delete the mock-data demo route. - [ ] `Palette` (`web/src/editor/Palette.tsx`): the seven object kinds with icons + sensible default sizes (bed 120×240 cm, grow bag ⌀40, container ⌀60, in-ground 200×200, tree ⌀300 canopy, path 100×300, structure 200×200). Desktop: drag from palette onto the canvas. Mobile: tap a kind → tap the canvas to place. POST on placement. - [ ] Selection: click/tap an object → selection outline + handles; click empty space clears. Drag body to move; corner handles resize (center-compensated so the opposite edge stays put); a rotate handle above the object snaps to 15° with Shift/long-press for free rotation; Delete/Backspace (with confirm for plantable objects that contain plantings). - [ ] `Inspector` (`web/src/editor/Inspector.tsx`): desktop right panel / mobile bottom sheet for the selected object — name, kind (read-only), dimensions + position (unit-aware inputs), rotation, color override, notes, plantable toggle, delete. - [ ] Optimistic sync: local state moves instantly during gestures; **one PATCH on gesture end** carrying `version`; on 409, roll back to the server's `current` row, refetch, and toast "updated elsewhere". Queue-depth 1 per object (drop intermediate states). - [ ] `?focus=objectId` param: on load, `zoomToFit` that object (groundwork for #15's focus mode). - [ ] z-order: paths/structures render under beds; explicit `z_index` respected; no UI for reordering yet (Inspector numeric input is fine). ## Out of scope - Plops/focus interactions (#15). Read-only viewer mode (#17). Undo/redo (post-v1). Multi-select. ## Key files & patterns - Gestures/viewport from #9; API + 409 envelope from #7/#10; component map in `DESIGN.md` § Frontend layout. ## Dependencies Blocked by #8, #9, #10. ## Acceptance criteria - Desktop: drag a bed from the palette, move/resize/rotate it, set its name in the inspector, reload — everything persisted. - Phone: place and move an object with touch; inspector opens as a bottom sheet; pinch-zoom still works while an object is selected. - Open the same garden in two browsers, move the same object in both → the loser gets a rollback + toast, no silent divergence. - Network tab shows one PATCH per completed gesture, not a stream.
steve added the frontenddrawing labels 2026-07-18 18:17:30 +00:00
steve closed this issue 2026-07-19 01:01:39 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: steve/pansy#11