From f5d4e9cb194e9e7c3d1ef54f83e9e32e5238dc0a Mon Sep 17 00:00:00 2001 From: Steve Dudenhoeffer Date: Wed, 22 Jul 2026 13:13:17 -0400 Subject: [PATCH 1/2] Add "Scan a packet" to the editor's Plants mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #102 shipped the seed-packet scan UI in the Plants catalog; Steve's call was to also surface it in the editor's Plants mode, so you can add a variety mid-planting without leaving the garden. Adds a capability-gated "πŸ“· Scan packet" entry: - in the shared PlantPlacementTools cluster (so it appears in both the desktop focus toolbar and the mobile Plants strip when a plantable bed is focused), and - in the mobile Plants-mode hint shown before a bed is focused, both opening the same ScanPacketModal (gated on `capabilities.vision`, so it's never a dead button). Vite hoists the now-two-importer modal into its own shared chunk, so nothing is duplicated. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ --- web/src/pages/GardenEditorPage.tsx | 32 +++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/web/src/pages/GardenEditorPage.tsx b/web/src/pages/GardenEditorPage.tsx index 1457f5d..29afb38 100644 --- a/web/src/pages/GardenEditorPage.tsx +++ b/web/src/pages/GardenEditorPage.tsx @@ -13,6 +13,7 @@ import { PlantPicker } from '@/editor/PlantPicker' import { Palette } from '@/editor/Palette' import { SeedTray } from '@/editor/SeedTray' import { RecentPlants } from '@/editor/RecentPlants' +import { ScanPacketModal } from '@/components/plants/ScanPacketModal' import { ClearBedModal } from '@/editor/ClearBedModal' import { EditorHint } from '@/editor/EditorHint' import { SeasonBanner, SeasonPicker } from '@/editor/SeasonPicker' @@ -108,6 +109,7 @@ export function GardenEditorPage() { const [picker, setPicker] = useState<'place' | 'change' | null>(null) const [sharing, setSharing] = useState(false) const [clearing, setClearing] = useState(false) + const [scanning, setScanning] = useState(false) const nudgeTimer = useRef(null) const nudgeFire = useRef<(() => void) | null>(null) @@ -632,6 +634,8 @@ export function GardenEditorPage() { onClear={() => setClearing(true)} onFill={fillBed} filling={fillObject.isPending} + canScan={!!capabilities.data?.vision} + onScan={() => setScanning(true)} /> ) : ( Not plantable @@ -706,6 +710,8 @@ export function GardenEditorPage() { onClear={() => setClearing(true)} onFill={fillBed} filling={fillObject.isPending} + canScan={!!capabilities.data?.vision} + onScan={() => setScanning(true)} /> + )} + ))} )} @@ -741,6 +758,8 @@ export function GardenEditorPage() { {sharing && setSharing(false)} />} + {scanning && setScanning(false)} />} + {clearing && focusedObject && ( void onFill: (layout: FillLayout) => void filling: boolean + // Scanning a packet adds a variety to the catalog mid-planting; only offered + // where a vision model is configured. + canScan: boolean + onScan: () => void }) { return ( <> @@ -791,6 +816,11 @@ function PlantPlacementTools({ onRemove={onRemove} onOpenPicker={onOpenPicker} /> + {canScan && ( + + )} {armedPlant && } {armedPlant && (