Repository Public
1import { create } from 'zustand';23/** Purely local UI state. Only the sidebar width preference is persisted (localStorage, no secrets). */4interface UiState {5 sidebarCollapsed: boolean;6 paletteOpen: boolean;7 notificationsOpen: boolean;8 newMission: { open: boolean; projectId: string | null };9 set: (p: Partial<Pick<UiState, 'sidebarCollapsed' | 'paletteOpen' | 'notificationsOpen'>>) => void;10 openNewMission: (projectId?: string | null) => void;11 closeNewMission: () => void;12}13export const LAYOUT_KEY = 'aos.layout';14function loadCollapsed(): boolean {15 try {16 return typeof window !== 'undefined' && JSON.parse(localStorage.getItem(LAYOUT_KEY) ?? '{}').sidebarCollapsed === true;17 } catch {18 return false;19 }20}21export const useUi = create<UiState>((set) => ({22 sidebarCollapsed: loadCollapsed(),23 paletteOpen: false,24 notificationsOpen: false,25 newMission: { open: false, projectId: null },26 set: (p) => {27 set(p);28 if (p.sidebarCollapsed !== undefined) {29 try {30 localStorage.setItem(LAYOUT_KEY, JSON.stringify({ sidebarCollapsed: p.sidebarCollapsed }));31 } catch {32 /* storage unavailable */33 }34 }35 },36 openNewMission: (projectId = null) => set({ newMission: { open: true, projectId } }),37 closeNewMission: () => set((s) => ({ newMission: { ...s.newMission, open: false } })),38}));39