| 1 | // overlays owns the transient surfaces layered over the workspace — the command |
| 2 | // palette, the settings panel's open target/focus, the shortcuts / heartbeat / |
| 3 | // topic-export dialogs, sidebar search, the startup splash, and the onboarding |
| 4 | // gate — plus two imperative coordination signals (dismiss-all overlays, and |
| 5 | // sidebar-search focus) that callers bump to trigger an effect downstream. |
| 6 | // |
| 7 | // None of this is persisted (the splash uses sessionStorage internally via |
| 8 | // shouldShowStartupSplash). Every atom initializes to the same value the prior |
| 9 | // App-local useState used, and setters mirror Dispatch<SetStateAction<T>>, so |
| 10 | // the migrated call sites — including functional toggles/bumps — are drop-in and |
| 11 | // behavior is unchanged. |
| 12 | |
| 13 | import type { Dispatch, SetStateAction } from "react"; |
| 14 | import { create } from "zustand"; |
| 15 | |
| 16 | import type { SettingsInitialFocus } from "../components/SettingsPanel"; |
| 17 | import { shouldShowStartupSplash } from "../components/StartupSplash"; |
| 18 | import type { ExtensionActionView, SessionMeta, SettingsTab } from "../lib/types"; |
| 19 | |
| 20 | import { applySetState } from "./setState"; |
| 21 | |
| 22 | export type OverlayState = { |
| 23 | settingsTarget: SettingsTab | null; |
| 24 | settingsFocus: SettingsInitialFocus | null; |
| 25 | paletteOpen: boolean; |
| 26 | paletteSessions: SessionMeta[]; |
| 27 | // Extension actions snapshotted when the palette opens (same staleness |
| 28 | // contract as paletteSessions). |
| 29 | paletteExtensionActions: ExtensionActionView[]; |
| 30 | shortcutsOpen: boolean; |
| 31 | heartbeatOpen: boolean; |
| 32 | topicExportOpen: boolean; |
| 33 | sidebarSearchOpen: boolean; |
| 34 | sidebarSearchFocusSignal: number; |
| 35 | transientOverlayDismissSignal: number; |
| 36 | startupSplashVisible: boolean; |
| 37 | needsOnboarding: boolean | null; |
| 38 | setSettingsTarget: Dispatch<SetStateAction<SettingsTab | null>>; |
| 39 | setSettingsFocus: Dispatch<SetStateAction<SettingsInitialFocus | null>>; |
| 40 | setPaletteOpen: Dispatch<SetStateAction<boolean>>; |
| 41 | setPaletteSessions: Dispatch<SetStateAction<SessionMeta[]>>; |
| 42 | setPaletteExtensionActions: Dispatch<SetStateAction<ExtensionActionView[]>>; |
| 43 | setShortcutsOpen: Dispatch<SetStateAction<boolean>>; |
| 44 | setHeartbeatOpen: Dispatch<SetStateAction<boolean>>; |
| 45 | setTopicExportOpen: Dispatch<SetStateAction<boolean>>; |
| 46 | setSidebarSearchOpen: Dispatch<SetStateAction<boolean>>; |
| 47 | setSidebarSearchFocusSignal: Dispatch<SetStateAction<number>>; |
| 48 | setTransientOverlayDismissSignal: Dispatch<SetStateAction<number>>; |
| 49 | setStartupSplashVisible: Dispatch<SetStateAction<boolean>>; |
| 50 | setNeedsOnboarding: Dispatch<SetStateAction<boolean | null>>; |
| 51 | }; |
| 52 | |
| 53 | export const useOverlayStore = create<OverlayState>((set) => ({ |
| 54 | settingsTarget: null, |
| 55 | settingsFocus: null, |
| 56 | paletteOpen: false, |
| 57 | paletteSessions: [], |
| 58 | paletteExtensionActions: [], |
| 59 | shortcutsOpen: false, |
| 60 | heartbeatOpen: false, |
| 61 | topicExportOpen: false, |
| 62 | sidebarSearchOpen: false, |
| 63 | sidebarSearchFocusSignal: 0, |
| 64 | transientOverlayDismissSignal: 0, |
| 65 | startupSplashVisible: shouldShowStartupSplash(), |
| 66 | needsOnboarding: null, |
| 67 | setSettingsTarget: (update) => set((s) => ({ settingsTarget: applySetState(s.settingsTarget, update) })), |
| 68 | setSettingsFocus: (update) => set((s) => ({ settingsFocus: applySetState(s.settingsFocus, update) })), |
| 69 | setPaletteOpen: (update) => set((s) => ({ paletteOpen: applySetState(s.paletteOpen, update) })), |
| 70 | setPaletteSessions: (update) => set((s) => ({ paletteSessions: applySetState(s.paletteSessions, update) })), |
| 71 | setPaletteExtensionActions: (update) => set((s) => ({ paletteExtensionActions: applySetState(s.paletteExtensionActions, update) })), |
| 72 | setShortcutsOpen: (update) => set((s) => ({ shortcutsOpen: applySetState(s.shortcutsOpen, update) })), |
| 73 | setHeartbeatOpen: (update) => set((s) => ({ heartbeatOpen: applySetState(s.heartbeatOpen, update) })), |
| 74 | setTopicExportOpen: (update) => set((s) => ({ topicExportOpen: applySetState(s.topicExportOpen, update) })), |
| 75 | setSidebarSearchOpen: (update) => set((s) => ({ sidebarSearchOpen: applySetState(s.sidebarSearchOpen, update) })), |
| 76 | setSidebarSearchFocusSignal: (update) => set((s) => ({ sidebarSearchFocusSignal: applySetState(s.sidebarSearchFocusSignal, update) })), |
| 77 | setTransientOverlayDismissSignal: (update) => set((s) => ({ transientOverlayDismissSignal: applySetState(s.transientOverlayDismissSignal, update) })), |
| 78 | setStartupSplashVisible: (update) => set((s) => ({ startupSplashVisible: applySetState(s.startupSplashVisible, update) })), |
| 79 | setNeedsOnboarding: (update) => set((s) => ({ needsOnboarding: applySetState(s.needsOnboarding, update) })), |
| 80 | })); |
| 81 |