Add configurable grid + snap to the layout system
Build image / build-and-push (push) Successful in 15s
Gadfly review (reusable) / review (pull_request) Successful in 10m8s
Adversarial Review (Gadfly) / review (pull_request) Successful in 10m9s

Two independent grids, each with a size and a snap toggle:

- Garden grid (owner-set, in the garden settings form): drives the
  editor's visual grid and, when snapping is on, snaps objects on
  place/move and snaps their dimensions to whole grid steps on resize.
- Bed grid (per plantable object, in the bed Inspector): when snapping
  is on, plants snap to the bed's grid on place/move, and the grid is
  drawn inside the focused bed. Plant radius stays free.

Snapping defaults off on both, so existing gardens keep free placement.
Grid spacing shares the [1cm, 100m] range and defaults to 1m (garden) /
30cm (bed); 0 is treated as unset and re-defaulted.

Backend: migration 0005 adds grid_size_cm/snap_to_grid to gardens and
garden_objects, threaded through domain/store/service/api with service +
api round-trip tests. Frontend: new geometry snap helpers (unit-tested),
schema/type plumbing, canvas + overlay snapping, and the two forms.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
This commit is contained in:
2026-07-19 02:51:44 -04:00
co-authored by Claude Opus 4.8
parent 9968c06243
commit 3f4d5627d8
23 changed files with 553 additions and 64 deletions
+12 -4
View File
@@ -127,9 +127,13 @@ type Garden struct {
HeightCM float64 `json:"heightCm"`
UnitPref string `json:"unitPref"`
Notes string `json:"notes"`
Version int64 `json:"version"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
// GridSizeCM is the garden-scale grid spacing (cm) the editor draws its grid
// from; SnapToGrid, when true, snaps objects to that grid on place/move/resize.
GridSizeCM float64 `json:"gridSizeCm"`
SnapToGrid bool `json:"snapToGrid"`
Version int64 `json:"version"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
// MyRole is the requesting actor's effective role on this garden ("owner",
// "editor", or "viewer"). Computed by the service, never persisted.
@@ -174,7 +178,11 @@ type GardenObject struct {
Plantable bool `json:"plantable"`
Color *string `json:"color,omitempty"`
Props *string `json:"props,omitempty"` // kind-specific JSON
Notes string `json:"notes"`
// GridSizeCM is this object's local grid spacing (cm); when SnapToGrid is true
// and the object is a plantable bed, plants snap to it inside the bed.
GridSizeCM float64 `json:"gridSizeCm"`
SnapToGrid bool `json:"snapToGrid"`
Notes string `json:"notes"`
Version int64 `json:"version"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`