Tidy: store never writes an undefined ghost; plant card toggle is the face, not the card
Build image / build-and-push (push) Successful in 11s

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-08-22 19:14:08 -04:00
co-authored by Claude Fable 5
parent 52b2c09a9e
commit 5a9ae58a2a
2 changed files with 22 additions and 27 deletions
+8 -14
View File
@@ -49,20 +49,14 @@ export function PlantCard({
return (
<div
role="button"
tabIndex={0}
className={cn('panel relative transition-shadow hover:[box-shadow:var(--shadow-md)]', open && 'border-accent-400')}
>
{/* The whole face is the toggle; the expanded area below has its own controls. */}
<button
type="button"
aria-expanded={open}
onClick={() => setOpen((v) => !v)}
onKeyDown={(e) => {
if (e.target === e.currentTarget && (e.key === 'Enter' || e.key === ' ')) {
e.preventDefault()
setOpen((v) => !v)
}
}}
className={cn(
'panel relative cursor-pointer px-[18px] py-4 transition-shadow hover:[box-shadow:var(--shadow-md)]',
open && 'border-accent-400',
)}
className="block w-full cursor-pointer rounded-[inherit] px-[18px] py-4 text-left"
>
<div className="flex items-center gap-3">
<Monogram color={plant.color} letters={letters} />
@@ -75,10 +69,10 @@ export function PlantCard({
{builtin && <Tag tone="neutral" className="ml-auto flex-none">built-in</Tag>}
</div>
<div className="mt-2.5 text-[12.5px] text-ink-soft">{lotText}</div>
</button>
{open && (
// Stop clicks inside the expanded area from toggling the card.
<div className="mt-2.5 flex flex-col gap-2" onClick={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()}>
<div className="-mt-1.5 flex flex-col gap-2 px-[18px] pb-4">
{lots.map((lot) => (
<LotCard key={lot.id} lot={lot} onEdit={() => onEditLot(lot)} onDelete={() => onDeleteLot(lot)} />
))}
+2 -1
View File
@@ -101,7 +101,8 @@ export const useEditorStore = create<EditorState>((set) => ({
...TRANSIENT,
setSel: (sel) => set({ sel }),
setFocus: (id) => set({ focusId: id }),
setArmedKind: (kind) => set({ armedKind: kind, ghost: kind ? undefined : null } as Partial<EditorState>),
// Disarming drops the ghost too; arming keeps whatever the cursor last set.
setArmedKind: (kind) => set((s) => ({ armedKind: kind, ghost: kind ? s.ghost : null })),
setArmedPlant: (plant, lotId = null) => set({ armedPlant: plant, armedLotId: plant ? lotId : null }),
setArmedLotId: (lotId) => set({ armedLotId: lotId }),
setGhost: (ghost) => set({ ghost }),