diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..9030888 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "enabledPlugins": { + "frontend-design@claude-plugins-official": true + } +} diff --git a/web/src/components/gardens/GardenFormModal.tsx b/web/src/components/gardens/GardenFormModal.tsx index e1798f5..70375be 100644 --- a/web/src/components/gardens/GardenFormModal.tsx +++ b/web/src/components/gardens/GardenFormModal.tsx @@ -9,12 +9,11 @@ import { errorMessage } from '@/lib/api' import { conflictGarden, useCreateGarden, useUpdateGarden, type Garden } from '@/lib/gardens' import { cmFromDisplay, - cmFromSpacing, dimensionUnitLabel, displayFromCm, + formatCm, isValidDimensionCm, - spacingFromCm, - spacingUnitLabel, + MIN_GARDEN_GRID_CM, type UnitPref, } from '@/lib/units' @@ -47,7 +46,7 @@ export function GardenFormModal({ garden, onClose }: { garden?: Garden; onClose: const [width, setWidth] = useState(() => dimString(garden?.widthCm, garden?.unitPref ?? 'metric')) const [height, setHeight] = useState(() => dimString(garden?.heightCm, garden?.unitPref ?? 'metric')) const [gridSize, setGridSize] = useState(() => - String(spacingFromCm(garden?.gridSizeCm ?? DEFAULT_GRID_CM, garden?.unitPref ?? 'metric')), + String(displayFromCm(garden?.gridSizeCm ?? DEFAULT_GRID_CM, garden?.unitPref ?? 'metric')), ) const [snapToGrid, setSnapToGrid] = useState(garden?.snapToGrid ?? false) const [notes, setNotes] = useState(garden?.notes ?? '') @@ -60,18 +59,23 @@ export function GardenFormModal({ garden, onClose }: { garden?: Garden; onClose: const v = parseFloat(s) return Number.isFinite(v) ? String(displayFromCm(cmFromDisplay(v, unit), next)) : s } - // The grid is entered at spacing scale (cm/in), so it converts with the - // spacing helpers, not the dimension ones. - const convertGrid = (s: string) => { - const v = parseFloat(s) - return Number.isFinite(v) ? String(spacingFromCm(cmFromSpacing(v, unit), next)) : s - } setWidth(convert(width)) setHeight(convert(height)) - setGridSize(convertGrid(gridSize)) + // The garden grid is a layout concern, so it lives at the same scale as the + // garden's own dimensions — same helpers, same unit label. (The *bed* grid in + // the object inspector is a plant-spacing concern and stays at cm/in.) + setGridSize(convert(gridSize)) setUnit(next) } + // Converted once per render and used by both the submit handler and the + // too-fine hint, so the two can never disagree about what was entered. + const gridSizeCm = cmFromDisplay(parseFloat(gridSize), unit) + // Soft floor: hint, don't refuse. A garden-scale grid this fine is usually + // someone reaching for plant spacing, which lives on the bed instead — but it + // is a legitimate choice for a very small garden, so the save still goes through. + const gridTooFine = Number.isFinite(gridSizeCm) && gridSizeCm > 0 && gridSizeCm < MIN_GARDEN_GRID_CM + async function onSubmit(e: FormEvent) { e.preventDefault() setFormError(null) @@ -90,7 +94,6 @@ export function GardenFormModal({ garden, onClose }: { garden?: Garden; onClose: setFormError('Width and height must be between 1 cm and 100 m.') return } - const gridSizeCm = cmFromSpacing(parseFloat(gridSize), unit) if (!isValidDimensionCm(gridSizeCm)) { setFormError('Grid size must be between 1 cm and 100 m.') return @@ -123,7 +126,7 @@ export function GardenFormModal({ garden, onClose }: { garden?: Garden; onClose: setUnit(current.unitPref) setWidth(dimString(current.widthCm, current.unitPref)) setHeight(dimString(current.heightCm, current.unitPref)) - setGridSize(String(spacingFromCm(current.gridSizeCm, current.unitPref))) + setGridSize(String(displayFromCm(current.gridSizeCm, current.unitPref))) setSnapToGrid(current.snapToGrid) setNotes(current.notes) setConflict('This garden changed elsewhere. The latest values are shown — review and save again.') @@ -175,28 +178,36 @@ export function GardenFormModal({ garden, onClose }: { garden?: Garden; onClose: /> -
-
- setGridSize(e.target.value)} - /> +
+
+
+ setGridSize(e.target.value)} + /> +
+
- + {gridTooFine && ( +

+ {formatCm(gridSizeCm, unit)} is very fine for a whole-garden grid. Plant spacing lives on each bed + (Bed grid in the inspector), not here. +

+ )}