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);