You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

7.4 KiB

phase plan type
16-store-modularization 3 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.

<execution_context> ~/.claude/get-shit-done/workflows/execute-phase.md ~/.claude/get-shit-done/templates/summary.md </execution_context>

@.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:

    interface GroupSlice {
      groups: Record<string, NodeGroup>;
      createGroup: (nodeIds: string[]) => string;
      deleteGroup: (groupId: string) => void;
      addNodesToGroup: (nodeIds: string[], groupId: string) => void;
      removeNodesFromGroup: (nodeIds: string[]) => void;
      updateGroup: (groupId: string, updates: Partial<NodeGroup>) => 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:

    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:

    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<boolean>;
      initializeAutoSave: () => void;
      cleanupAutoSave: () => void;
      saveWorkflow: (name?: string) => void;
      loadWorkflow: (workflow: WorkflowFile, workflowPath?: string) => Promise<void>;
      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:

    import { createGroupSlice, GroupSlice } from './slices/groupSlice';
    import { createPersistenceSlice, PersistenceSlice } from './slices/persistenceSlice';
    
  2. Define combined store type:

    type WorkflowStore = CoreSlice & GroupSlice & PersistenceSlice & ExecutionSlice;
    
  3. Update store creation using spread:

    export const useWorkflowStore = create<WorkflowStore>()((...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

Before declaring plan complete: - [ ] `npm run build` succeeds without errors - [ ] `npm test` passes all 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

<success_criteria>

  • All tasks completed
  • All verification checks pass
  • Store properly composed from slices
  • Each slice is focused and cohesive
  • No regression in any store functionality
  • Phase 16 complete: Store Modularization achieved </success_criteria>
After completion, create `.planning/phases/16-store-modularization/16-03-SUMMARY.md`