diff --git a/web/src/editor/SeedTray.tsx b/web/src/editor/SeedTray.tsx
index deebb51..cbfe6f1 100644
--- a/web/src/editor/SeedTray.tsx
+++ b/web/src/editor/SeedTray.tsx
@@ -5,17 +5,17 @@ import type { Plant } from '@/lib/plants'
/**
* The Seed Tray strip in the focus-mode toolbar: a row of the garden's
* kept-at-hand plants. Tap a chip to arm that plant for tap-to-place (the armed
- * chip is highlighted); ✕ removes it from the tray; the trailing + chip opens
- * the full catalog picker. See useSeedTray for persistence.
+ * chip is highlighted); ✕ removes it from the tray; the trailing "+ Plant" chip
+ * opens the full catalog picker. See useSeedTray for persistence.
*/
export function SeedTray({
- plants,
+ trayPlants,
armedPlantId,
onArm,
onRemove,
onOpenPicker,
}: {
- plants: Plant[]
+ trayPlants: Plant[]
armedPlantId: number | null
onArm: (plant: Plant) => void
onRemove: (id: number) => void
@@ -23,7 +23,7 @@ export function SeedTray({
}) {
return (
)
diff --git a/web/src/lib/seedTray.ts b/web/src/lib/seedTray.ts
index 9c01916..8db0347 100644
--- a/web/src/lib/seedTray.ts
+++ b/web/src/lib/seedTray.ts
@@ -16,7 +16,8 @@ const TRAY_MAX = 24 // a working set, not a catalog; caps pathological growth
function loadIds(gardenId: number): number[] {
try {
const v: unknown = JSON.parse(localStorage.getItem(KEY(gardenId)) ?? '[]')
- return Array.isArray(v) ? v.filter((n): n is number => typeof n === 'number') : []
+ const ids = Array.isArray(v) ? v.filter((n): n is number => typeof n === 'number') : []
+ return ids.slice(-TRAY_MAX) // honor the cap even if storage was hand-edited
} catch {
return []
}
@@ -30,17 +31,18 @@ function saveIds(gardenId: number, ids: number[]) {
}
}
-export interface SeedTray {
+export interface SeedTrayState {
/** Tray plants in insertion order, resolved against the live catalog. */
trayPlants: Plant[]
- /** Add a plant to the end of the tray (no-op if already present). */
+ /** Add a plant to the end of the tray (no-op if already present; when the tray
+ * is full the oldest entry is evicted). */
add: (plant: Plant) => void
/** Remove a plant from the tray. */
remove: (id: number) => void
}
/** Per-garden Seed Tray backed by localStorage and the plant catalog. */
-export function useSeedTray(gardenId: number): SeedTray {
+export function useSeedTray(gardenId: number): SeedTrayState {
const plants = usePlants()
const [ids, setIds] = useState(() => loadIds(gardenId))
diff --git a/web/src/pages/GardenEditorPage.tsx b/web/src/pages/GardenEditorPage.tsx
index c9f097b..77ef159 100644
--- a/web/src/pages/GardenEditorPage.tsx
+++ b/web/src/pages/GardenEditorPage.tsx
@@ -244,18 +244,26 @@ export function GardenEditorPage() {
setArmedPlant(plant)
}
+ // Removing the armed plant from the tray also disarms it, so placement doesn't
+ // silently continue for a plant the user just cleared out.
+ function removeFromTrayAndDisarm(id: number) {
+ removeFromTray(id)
+ if (armedPlant?.id === id) setArmedPlant(null)
+ }
+
// The picker hands back the full Plant (from the whole catalog), so use it
// directly rather than re-resolving against the garden's referenced-plant map —
// a not-yet-placed plant isn't in that map, which used to silently abort the
- // pick (nothing armed, picker left open).
+ // pick (nothing armed, picker left open). Picking (place OR change) makes sure
+ // the plant renders and drops it into this garden's tray for quick reuse.
function onPickPlant(plant: Plant) {
if (!canEdit) return // defense in depth: viewers can't reach the picker anyway
+ ensurePlant(plant)
addToTray(plant)
if (picker === 'change' && selectedPlop) {
- ensurePlant(plant)
updatePlanting.mutate({ id: selectedPlop.id, version: selectedPlop.version, plantId: plant.id })
} else {
- armPlant(plant) // 'place' mode: arm for repeat placement
+ setArmedPlant(plant) // 'place' mode: arm for repeat placement
}
setPicker(null)
}
@@ -284,30 +292,34 @@ export function GardenEditorPage() {
← {garden.name}
{objectDisplayName(focusedObject)}
- {canEdit && !focusedObject.plantable && Not plantable}
- {canEdit && focusedObject.plantable && (
- setPicker('place')}
- />
- )}
- {canEdit && focusedObject.plantable && armedPlant && (
-
- )}
- {canEdit && focusedObject.plantable && focusedPlops.length > 0 && (
-
- )}
+ {canEdit &&
+ (focusedObject.plantable ? (
+ <>
+ setPicker('place')}
+ />
+ {armedPlant && (
+
+ )}
+ {focusedPlops.length > 0 && (
+
+ )}
+ >
+ ) : (
+ Not plantable
+ ))}
)}
@@ -355,7 +367,7 @@ export function GardenEditorPage() {
setPicker(null)}
- onSelect={(p) => onPickPlant(p)}
+ onSelect={onPickPlant}
/>
)}