Spaces:
Running
Running
File size: 3,064 Bytes
fb57ab3 0ac416b fb57ab3 0ac416b fb57ab3 0ac416b c9f1455 fb57ab3 0ac416b fb57ab3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | 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<LibraryState, "pinned" | "hidden" | "collapsed" | "allOpen"> {
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<LibraryState>((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);
|