- Removing the armed plant from the tray now disarms it, so placement
doesn't continue for a plant that's no longer shown.
- loadIds honors TRAY_MAX even if localStorage was hand-edited.
- Consolidate the repeated `canEdit && plantable` toolbar guards into one
nested conditional (3-model finding).
- Rename the hook's SeedTray interface to SeedTrayState (avoids colliding
with the SeedTray component) and the component prop plants → trayPlants.
- Hoist ensurePlant to one call in onPickPlant; pass onPickPlant directly as
onSelect; use ASCII "+" for the tray chip; document the FIFO eviction.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
The picker offers the full catalog, but onPickPlant resolved the choice
against the garden's *referenced* plants (the /full payload only carries
ListReferencedPlants). A not-yet-placed plant wasn't in that map, so the
handler returned early — before arming and before closing the picker, so
nothing happened and the picker stayed open. Use the full Plant the picker
already hands back, and splice it into the /full cache so its plops render
with an icon/color before the next refetch (useEnsurePlantInFull).
Add a per-garden Seed Tray: a curated row of plant chips in the focus
toolbar. Tap a chip to arm that plant for tap-to-place; picking from the
catalog adds it; ✕ removes it. Persisted in localStorage, mirroring the
PlantPicker's recent-plants mechanism.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi