From 084a3bb48647606d4acaaa67f65400a38d983ce7 Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Thu, 16 Oct 2025 14:30:49 +0200 Subject: [PATCH] feat: implement global settings system with localStorage persistence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates a new extendable settings store for application-wide preferences that persist across browser sessions using Zustand's persist middleware. Architecture: - New settingsStore.ts using Zustand persist middleware - Automatic localStorage synchronization - Versioned schema (v1) for future migrations - Clean, typed interface for easy extensibility Changes: - Created settingsStore for persistent global settings - Migrated autoZoomEnabled from searchStore to settingsStore - Updated GraphEditor to use settings store for auto-zoom - Updated LeftPanel to use settings store for toggle control - searchStore now focuses on ephemeral filter/search state Settings Persistence: - Storage key: 'constellation-settings' - Automatically saves on any setting change - Restores settings on page reload - Same proven pattern as panelStore Future Extensibility: The settings store is designed to easily accommodate new settings: - Theme preferences (light/dark mode) - Default view options - User interface preferences - Any application-wide persistent settings Benefits: - User preferences persist across sessions - Clean separation between ephemeral and persistent state - Type-safe settings management - Easy to extend for future features 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/components/Editor/GraphEditor.tsx | 5 +++- src/components/Panels/LeftPanel.tsx | 6 ++-- src/stores/searchStore.ts | 9 ------ src/stores/settingsStore.ts | 40 +++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 src/stores/settingsStore.ts diff --git a/src/components/Editor/GraphEditor.tsx b/src/components/Editor/GraphEditor.tsx index 27b1822..8cbde57 100644 --- a/src/components/Editor/GraphEditor.tsx +++ b/src/components/Editor/GraphEditor.tsx @@ -25,6 +25,7 @@ import { useGraphWithHistory } from "../../hooks/useGraphWithHistory"; import { useDocumentHistory } from "../../hooks/useDocumentHistory"; import { useEditorStore } from "../../stores/editorStore"; import { useSearchStore } from "../../stores/searchStore"; +import { useSettingsStore } from "../../stores/settingsStore"; import { useActiveDocument } from "../../stores/workspace/useActiveDocument"; import { useWorkspaceStore } from "../../stores/workspaceStore"; import { useCreateDocument } from "../../hooks/useCreateDocument"; @@ -108,9 +109,11 @@ const GraphEditor = ({ onNodeSelect, onEdgeSelect, onAddNodeRequest, onExportReq searchText, visibleActorTypes, visibleRelationTypes, - autoZoomEnabled, } = useSearchStore(); + // Settings for auto-zoom + const { autoZoomEnabled } = useSettingsStore(); + // Track previous document ID to save viewport before switching const prevDocumentIdRef = useRef(null); diff --git a/src/components/Panels/LeftPanel.tsx b/src/components/Panels/LeftPanel.tsx index 10a6c57..1c2d996 100644 --- a/src/components/Panels/LeftPanel.tsx +++ b/src/components/Panels/LeftPanel.tsx @@ -13,6 +13,7 @@ import { usePanelStore } from '../../stores/panelStore'; import { useGraphWithHistory } from '../../hooks/useGraphWithHistory'; import { useEditorStore } from '../../stores/editorStore'; import { useSearchStore } from '../../stores/searchStore'; +import { useSettingsStore } from '../../stores/settingsStore'; import { createNode } from '../../utils/nodeUtils'; import { getIconComponent } from '../../utils/iconUtils'; import { getContrastColor } from '../../utils/colorUtils'; @@ -87,12 +88,13 @@ const LeftPanel = forwardRef(({ onDeselectAll, onA setActorTypeVisible, visibleRelationTypes, setRelationTypeVisible, - autoZoomEnabled, - setAutoZoomEnabled, clearFilters, hasActiveFilters, } = useSearchStore(); + // Settings + const { autoZoomEnabled, setAutoZoomEnabled } = useSettingsStore(); + // Initialize filter state when node/edge types change useEffect(() => { nodeTypes.forEach((nodeType) => { diff --git a/src/stores/searchStore.ts b/src/stores/searchStore.ts index 4d53ae2..7211843 100644 --- a/src/stores/searchStore.ts +++ b/src/stores/searchStore.ts @@ -7,7 +7,6 @@ import { create } from 'zustand'; * - Search text for filtering both actors (by label, description, or type) and relations (by label or type) * - Filter by actor types (show/hide specific node types) * - Filter by relation types (show/hide specific edge types) - * - Auto-zoom to filtered results (optional, enabled by default) * - Results tracking */ @@ -28,10 +27,6 @@ interface SearchStore { toggleRelationType: (typeId: string) => void; setAllRelationTypesVisible: (visible: boolean) => void; - // Auto-zoom to filtered results - autoZoomEnabled: boolean; - setAutoZoomEnabled: (enabled: boolean) => void; - // Clear all filters clearFilters: () => void; @@ -43,7 +38,6 @@ export const useSearchStore = create((set, get) => ({ searchText: '', visibleActorTypes: {}, visibleRelationTypes: {}, - autoZoomEnabled: true, setSearchText: (text: string) => set({ searchText: text }), @@ -98,9 +92,6 @@ export const useSearchStore = create((set, get) => ({ return { visibleRelationTypes: updated }; }), - setAutoZoomEnabled: (enabled: boolean) => - set({ autoZoomEnabled: enabled }), - clearFilters: () => set((state) => { // Reset all actor types to visible diff --git a/src/stores/settingsStore.ts b/src/stores/settingsStore.ts new file mode 100644 index 0000000..fb909ed --- /dev/null +++ b/src/stores/settingsStore.ts @@ -0,0 +1,40 @@ +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; + +/** + * Settings Store - Global application settings with localStorage persistence + * + * Features: + * - Search and filter preferences + * - UI behavior settings + * - Auto-save to localStorage + * - Extendable for future settings + */ + +interface SettingsState { + // Search & Filter Settings + autoZoomEnabled: boolean; + setAutoZoomEnabled: (enabled: boolean) => void; + + // Future settings can be added here + // Example: + // theme: 'light' | 'dark'; + // setTheme: (theme: 'light' | 'dark') => void; +} + +export const useSettingsStore = create()( + persist( + (set) => ({ + // Search & Filter Settings + autoZoomEnabled: true, + setAutoZoomEnabled: (enabled: boolean) => + set({ autoZoomEnabled: enabled }), + + // Future settings implementations go here + }), + { + name: 'constellation-settings', // localStorage key + version: 1, // For future migrations if needed + } + ) +);