--- phase: 16-store-modularization plan: 03 type: execute --- Extract remaining store slices into focused modules using Zustand slice pattern. Purpose: Break up the remaining monolithic store into logical slices (groups, clipboard, persistence, providers) that can be developed and tested independently while maintaining a single unified store. Output: Zustand slices for groups, clipboard, persistence, and provider settings, composed into the main store. ~/.claude/get-shit-done/workflows/execute-phase.md ~/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/16-store-modularization/16-01-PLAN.md @.planning/phases/16-store-modularization/16-02-PLAN.md @src/store/workflowStore.ts **Dependencies from 16-01 and 16-02:** - localStorage utilities extracted to src/store/utils/localStorage.ts - Node defaults extracted to src/store/utils/nodeDefaults.ts - Execution logic extracted to src/store/execution/ **Remaining Store Sections:** After 16-01 and 16-02, the store still contains: - Group operations (~200 lines): createGroup, deleteGroup, addNodesToGroup, etc. - Clipboard operations (~100 lines): copySelectedNodes, pasteNodes, clearClipboard - Persistence operations (~300 lines): saveWorkflow, loadWorkflow, auto-save - Provider settings (~100 lines): updateProviderSettings, updateProviderApiKey - Core node/edge operations (~200 lines): These stay in main store - UI state (~50 lines): modal count, quickstart - stays in main store Task 1: Create group operations slice src/store/slices/groupSlice.ts Create src/store/slices/groupSlice.ts using Zustand slice pattern: 1. Define GroupSlice interface: ```typescript interface GroupSlice { groups: Record; createGroup: (nodeIds: string[]) => string; deleteGroup: (groupId: string) => void; addNodesToGroup: (nodeIds: string[], groupId: string) => void; removeNodesFromGroup: (nodeIds: string[]) => void; updateGroup: (groupId: string, updates: Partial) => void; toggleGroupLock: (groupId: string) => void; moveGroupNodes: (groupId: string, delta: { x: number; y: number }) => void; setNodeGroupId: (nodeId: string, groupId: string | undefined) => void; } ``` 2. Create slice creator function: ```typescript export const createGroupSlice: StateCreator< WorkflowStore, [], [], GroupSlice > = (set, get) => ({ groups: {}, createGroup: (nodeIds) => { ... }, // ... other methods }); ``` 3. Import GROUP_COLORS and GROUP_COLOR_ORDER from utils/nodeDefaults 4. Import defaultNodeDimensions for bounding box calculation 5. Manage groupIdCounter within the slice The slice needs access to nodes for createGroup bounding box calculation, which it gets via get(). npm run build succeeds, slice compiles without errors Group slice created with all group operations Task 2: Create persistence slice src/store/slices/persistenceSlice.ts Create src/store/slices/persistenceSlice.ts: 1. Define PersistenceSlice interface: ```typescript interface PersistenceSlice { // State workflowId: string | null; workflowName: string | null; saveDirectoryPath: string | null; generationsPath: string | null; lastSavedAt: number | null; hasUnsavedChanges: boolean; autoSaveEnabled: boolean; isSaving: boolean; useExternalImageStorage: boolean; // Actions setWorkflowMetadata: (id: string, name: string, path: string, generationsPath?: string | null) => void; setWorkflowName: (name: string) => void; setGenerationsPath: (path: string | null) => void; setAutoSaveEnabled: (enabled: boolean) => void; setUseExternalImageStorage: (enabled: boolean) => void; markAsUnsaved: () => void; saveToFile: () => Promise; initializeAutoSave: () => void; cleanupAutoSave: () => void; saveWorkflow: (name?: string) => void; loadWorkflow: (workflow: WorkflowFile, workflowPath?: string) => Promise; clearWorkflow: () => void; } ``` 2. Move autoSaveIntervalId to slice file scope 3. Import localStorage helpers from utils/localStorage 4. Import externalizeWorkflowImages, hydrateWorkflowImages from @/utils/imageStorage 5. Keep nodeIdCounter and groupIdCounter management in main store (loadWorkflow needs to update them) Note: loadWorkflow also needs to reset execution state (isRunning, currentNodeId) which stays in main store. The slice's loadWorkflow will need to call through to main store's set(). npm run build succeeds, slice compiles without errors Persistence slice created with save/load/auto-save logic Task 3: Compose slices into main store src/store/workflowStore.ts Refactor workflowStore.ts to compose the slices: 1. Import slices: ```typescript import { createGroupSlice, GroupSlice } from './slices/groupSlice'; import { createPersistenceSlice, PersistenceSlice } from './slices/persistenceSlice'; ``` 2. Define combined store type: ```typescript type WorkflowStore = CoreSlice & GroupSlice & PersistenceSlice & ExecutionSlice; ``` 3. Update store creation using spread: ```typescript export const useWorkflowStore = create()((...args) => ({ // Core state and actions inline nodes: [], edges: [], // ... // Spread slices ...createGroupSlice(...args), ...createPersistenceSlice(...args), })); ``` 4. Keep in main store (CoreSlice): - nodes, edges, edgeStyle state - Node CRUD: addNode, updateNodeData, removeNode, onNodesChange - Edge operations: onEdgesChange, onConnect, addEdgeWithType, removeEdge, toggleEdgePause - getNodeById, getConnectedInputs, validateWorkflow - UI state: openModalCount, isModalOpen, showQuickstart - Execution state: isRunning, currentNodeId, pausedAtNodeId - executeWorkflow, regenerateNode, stopWorkflow (uses extracted execution modules) - globalImageHistory, incurredCost state and actions - Provider settings state and actions - Model search dialog state 5. Export backward-compatible aliases (GROUP_COLORS, saveNanoBananaDefaults, etc.) Target: Main store file reduced from ~2800 to ~1500 lines. npm run build succeeds, npm test passes all tests, manual test of group creation and save/load works Store composed from slices, main file significantly reduced, all functionality preserved Task 4: Add unit tests for slices src/store/slices/__tests__/groupSlice.test.ts, src/store/slices/__tests__/persistenceSlice.test.ts Create comprehensive tests for the extracted slices: 1. Create src/store/slices/__tests__/groupSlice.test.ts: - Test createGroup calculates correct bounding box from node positions - Test createGroup assigns unique colors to groups - Test createGroup sets groupId on all provided nodes - Test deleteGroup removes group and clears groupId from nodes - Test addNodesToGroup updates nodes' groupId - Test removeNodesFromGroup clears groupId - Test toggleGroupLock toggles the locked state - Test moveGroupNodes updates position of all nodes in group - Use Zustand's testing pattern: create isolated store instance per test 2. Create src/store/slices/__tests__/persistenceSlice.test.ts: - Test setWorkflowMetadata sets all metadata fields - Test setWorkflowName marks as unsaved - Test markAsUnsaved sets hasUnsavedChanges to true - Test saveToFile calls fetch with correct payload (mock fetch) - Test saveToFile updates lastSavedAt and hasUnsavedChanges on success - Test loadWorkflow hydrates nodes/edges and resets execution state - Test loadWorkflow migrates legacy nanoBanana nodes - Test clearWorkflow resets all state - Test initializeAutoSave/cleanupAutoSave manage interval - Mock fetch, localStorage, and imageStorage utilities Testing pattern for Zustand slices: ```typescript const createTestStore = () => create()((...args) => ({ ...createGroupSlice(...args), // minimal mock of other state needed nodes: [], edges: [], })); ``` npm test passes with new slice tests, coverage for group and persistence slices Unit tests added for both slices, all tests pass, slice functionality verified Before declaring plan complete: - [ ] `npm run build` succeeds without errors - [ ] `npm test` passes all tests (existing + new slice tests) - [ ] workflowStore.ts reduced from ~2800 to ~1500 lines - [ ] Group operations work (create, lock, delete) - [ ] Save/load workflow works - [ ] Auto-save works - [ ] All exports maintained for backward compatibility - [ ] groupSlice has test coverage for all operations - [ ] persistenceSlice has test coverage for save/load/auto-save - All tasks completed - All verification checks pass - Store properly composed from slices - Each slice is focused and cohesive - All slices have comprehensive test coverage - No regression in any store functionality - Phase 16 complete: Store Modularization achieved After completion, create `.planning/phases/16-store-modularization/16-03-SUMMARY.md`