import { create } from "zustand"; import { prefs } from "../lib/prefs"; const KEY = "gr-canvas-library"; interface LibraryState { /** Def types pinned to the top of the library, in display order. */ pinned: string[]; /** Def types hidden from the library. */ hidden: string[]; /** Collapsed section keys. */ collapsed: string[]; showHidden: boolean; /** Whether the full node tree is unfolded under the favourites in the sidebar. */ allOpen: boolean; setAllOpen: (v: boolean) => void; pin: (type: string) => void; unpin: (type: string) => void; movePinned: (type: string, toIndex: number) => void; hide: (type: string) => void; unhide: (type: string) => void; toggleSection: (key: string) => void; setShowHidden: (v: boolean) => void; /** Drops prefs for defs that no longer exist (e.g. a removed Space). */ forget: (predicate: (type: string) => boolean) => void; reset: () => void; } /** First visit: a couple of favourites so the sidebar is not empty. */ const DEFAULT_PINNED = ["in:text", "in:image"]; function load(): Pick { try { const raw = prefs.get(KEY); if (raw) { const p = JSON.parse(raw); return { pinned: p.pinned ?? [], hidden: p.hidden ?? [], collapsed: p.collapsed ?? [], allOpen: !!p.allOpen }; } } catch { /* ignore */ } return { pinned: DEFAULT_PINNED, hidden: [], collapsed: [], allOpen: false }; } const without = (xs: string[], x: string) => xs.filter((y) => y !== x); export const useLibraryStore = create((set) => ({ ...load(), showHidden: false, setAllOpen: (allOpen) => set({ allOpen }), pin: (t) => set((s) => ({ pinned: [...without(s.pinned, t), t], hidden: without(s.hidden, t) })), unpin: (t) => set((s) => ({ pinned: without(s.pinned, t) })), movePinned: (t, to) => set((s) => { const from = s.pinned.indexOf(t); if (from < 0) return {}; const next = without(s.pinned, t); next.splice(Math.max(0, Math.min(to > from ? to - 1 : to, next.length)), 0, t); return { pinned: next }; }), hide: (t) => set((s) => ({ hidden: [...without(s.hidden, t), t], pinned: without(s.pinned, t) })), unhide: (t) => set((s) => ({ hidden: without(s.hidden, t) })), toggleSection: (k) => set((s) => ({ collapsed: s.collapsed.includes(k) ? without(s.collapsed, k) : [...s.collapsed, k] })), setShowHidden: (v) => set({ showHidden: v }), forget: (pred) => set((s) => ({ pinned: s.pinned.filter((t) => !pred(t)), hidden: s.hidden.filter((t) => !pred(t)) })), reset: () => set({ pinned: [], hidden: [], collapsed: [] }), })); /** Re-reads prefs (after state.json was loaded). */ export const rehydrateLibrary = () => useLibraryStore.setState(load()); useLibraryStore.subscribe((s) => { try { prefs.set(KEY, JSON.stringify({ pinned: s.pinned, hidden: s.hidden, collapsed: s.collapsed, allOpen: s.allOpen })); } catch { /* ignore */ } }); export const isPinned = (t: string) => useLibraryStore.getState().pinned.includes(t);