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