import { create } from 'zustand' import type { Viewport } from '@/lib/geometry' import type { Plant } from '@/lib/plants' import type { EditorPlanting } from '@/lib/plantings' import type { EditorObject } from './types' // Ephemeral editor state only (per DESIGN § State): the viewport, the current // selection, the object being live-edited mid-gesture, and the palette kind // armed for tap-to-place. All server state stays in react-query. export const MIN_SCALE = 0.05 // px per cm — fully zoomed out export const MAX_SCALE = 20 // px per cm — fully zoomed in interface EditorState { viewport: Viewport setViewport: (next: Viewport | ((prev: Viewport) => Viewport)) => void // The selected object OR plop (mutually exclusive; selecting one clears the // other). selectedId is a garden object; selectedPlantingId is a plop. selectedId: number | null select: (id: number | null) => void selectedPlantingId: number | null selectPlanting: (id: number | null) => void focusedObjectId: number | null setFocusedObject: (id: number | null) => void // Which rail tab is showing, or null when the rail is closed. Selecting an // object switches this to 'inspector' (see GardenEditorPage), so editing never // starts with a click on the rail itself. railTab: string | null setRailTab: (tab: string | null) => void // The plant armed for placing plops (set after the PlantPicker choice); stays // armed for repeat-placement until cleared (Escape / done). null = not placing. armedPlant: Plant | null setArmedPlant: (p: Plant | null) => void // During a move/resize/rotate, the object's live geometry is held here so the // canvas renders it instantly; the PATCH fires only on gesture end. liveObject: EditorObject | null setLiveObject: (o: EditorObject | null) => void // The same, for a plop being moved/resized. livePlanting: EditorPlanting | null setLivePlanting: (p: EditorPlanting | null) => void // The palette kind armed for tap-to-place (mobile-friendly; also set while // dragging a kind from the palette). null when nothing is armed. armedKind: string | null setArmedKind: (kind: string | null) => void // True while an object move/resize/rotate is in progress, so the viewport's // pan gesture stands down (both listen on the same svg). objectDragging: boolean setObjectDragging: (v: boolean) => void // Clear all transient (non-persisted) editor state at once — selection, focus, // armed plant/kind, and any in-flight live geometry — when entering a garden // view fresh (e.g. the public read-only page on mount). The viewport is left // alone; the canvas re-fits it from the loaded garden. resetTransient: () => void } export const useEditorStore = create((set) => ({ viewport: { tx: 0, ty: 0, scale: 1 }, setViewport: (next) => set((s) => ({ viewport: typeof next === 'function' ? next(s.viewport) : next })), selectedId: null, select: (id) => set({ selectedId: id, selectedPlantingId: null }), selectedPlantingId: null, selectPlanting: (id) => set({ selectedPlantingId: id, selectedId: null }), focusedObjectId: null, setFocusedObject: (id) => set({ focusedObjectId: id }), railTab: null, setRailTab: (tab) => set({ railTab: tab }), armedPlant: null, setArmedPlant: (p) => set({ armedPlant: p }), liveObject: null, setLiveObject: (o) => set({ liveObject: o }), livePlanting: null, setLivePlanting: (p) => set({ livePlanting: p }), armedKind: null, setArmedKind: (kind) => set({ armedKind: kind }), objectDragging: false, setObjectDragging: (v) => set({ objectDragging: v }), resetTransient: () => set({ selectedId: null, selectedPlantingId: null, focusedObjectId: null, armedPlant: null, armedKind: null, liveObject: null, livePlanting: null, }), }))