Documentation
v1.0.0
Core Primitive

State Management

Zustand-based stores for managing editor state, UI state, and template data with TypeScript support.

State Architecture

The state management system uses Zustand to provide predictable, type-safe state management across the entire vibecode application. It's organized into specialized stores that handle different aspects of the application state.

Design Philosophy: Separate concerns into focused stores, maintain immutability, provide TypeScript safety, and enable time-travel debugging.

Store Architecture

Editor Store
Manages blocks, prompt data, and core editing operations

State

  • blocks: Block[]
  • history: Block[][]
  • currentHistoryIndex: number
  • isDirty: boolean

Actions

  • addBlock()
  • updateBlock()
  • deleteBlock()
  • reorderBlocks()
  • undo() / redo()
Editor UI Store
Handles UI state, focus management, and interface interactions

State

  • focusedBlockId: string | null
  • selectedBlockIds: string[]
  • draggedBlockId: string | null
  • showSlashMenu: boolean

Actions

  • setFocusedBlock()
  • setSelectedBlocks()
  • clearSelection()
  • toggleSlashMenu()
Template Store
Manages prompt templates and saved recipes

State

  • templates: Template[]
  • currentTemplate: Template | null
  • isLoading: boolean
  • searchQuery: string

Actions

  • loadTemplate()
  • saveTemplate()
  • deleteTemplate()
  • searchTemplates()

Store Implementation

Editor Store Implementation
Core state management for blocks and editing operations
store/editor-store.ts
import { create } from 'zustand';
import { devtools, subscribeWithSelector } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import type { Block, BlockType } from '@/types/prompt-blocks';
import { nanoid } from 'nanoid';

interface EditorState {
  blocks: Block[];
  history: Block[][];
  currentHistoryIndex: number;
  isDirty: boolean;
  lastSaved: Date | null;
}

interface EditorActions {
  // Block operations
  addBlock: (block: Block, afterBlockId?: string) => void;
  updateBlock: (blockId: string, updates: Partial<Block>) => void;
  deleteBlock: (blockId: string) => void;
  moveBlock: (blockId: string, direction: 'up' | 'down') => void;
  reorderBlocks: (draggedId: string, targetId: string, position: 'before' | 'after') => void;
  
  // Batch operations
  loadBlocks: (blocks: Block[]) => void;
  clearBlocks: () => void;
  duplicateBlock: (blockId: string) => void;
  
  // History operations
  undo: () => void;
  redo: () => void;
  pushToHistory: () => void;
  
  // State management
  markClean: () => void;
  markDirty: () => void;
}

export const useEditorStore = create<EditorState & EditorActions>()(
  devtools(
    subscribeWithSelector(
      immer((set, get) => ({
        // Initial state
        blocks: [],
        history: [[]],
        currentHistoryIndex: 0,
        isDirty: false,
        lastSaved: null,

        // Block operations
        addBlock: (block, afterBlockId) => {
          set((state) => {
            const insertIndex = afterBlockId 
              ? state.blocks.findIndex(b => b.id === afterBlockId) + 1
              : state.blocks.length;
            
            state.blocks.splice(insertIndex, 0, block);
            state.isDirty = true;
          });
          get().pushToHistory();
        },

        updateBlock: (blockId, updates) => {
          set((state) => {
            const block = state.blocks.find(b => b.id === blockId);
            if (block) {
              Object.assign(block, updates);
              state.isDirty = true;
            }
          });
          get().pushToHistory();
        },

        deleteBlock: (blockId) => {
          set((state) => {
            const index = state.blocks.findIndex(b => b.id === blockId);
            if (index !== -1) {
              state.blocks.splice(index, 1);
              state.isDirty = true;
            }
          });
          get().pushToHistory();
        },

        // ... rest of implementation
      }))
    ),
    { name: 'editor-store' }
  )
);

Store Integration

Cross-Store Communication
How different stores interact and share data
hooks/use-editor-integration.ts
import { useEffect } from 'react';
import { useEditorStore } from '@/store/editor-store';
import { useEditorUIStore } from '@/store/editor-ui-store';
import { useTemplateStore } from '@/store/template-store';

export function useEditorIntegration() {
  const editorStore = useEditorStore();
  const uiStore = useEditorUIStore();
  const templateStore = useTemplateStore();

  // Auto-save functionality
  useEffect(() => {
    const unsubscribe = useEditorStore.subscribe(
      (state) => state.blocks,
      (blocks) => {
        if (editorStore.isDirty) {
          localStorage.setItem('vibecode-auto-save', JSON.stringify({
            blocks,
            timestamp: Date.now()
          }));
        }
      },
      { fireImmediately: false }
    );

    return unsubscribe;
  }, []);

  // Template loading integration
  const loadTemplateIntoEditor = async (templateId: string) => {
    await templateStore.loadTemplate(templateId);
    if (templateStore.currentTemplate) {
      editorStore.loadBlocks(templateStore.currentTemplate.blocks);
      uiStore.clearSelection();
      uiStore.clearFocus();
    }
  };

  return {
    loadTemplateIntoEditor,
    ...editorStore,
    ...uiStore,
    templates: templateStore.templates
  };
}

Best Practices

Do
  • Use Immer middleware for safe immutable updates
  • Separate concerns into focused stores
  • Use subscribeWithSelector for targeted subscriptions
  • Enable devtools for debugging
Don't
  • Mutate state directly - always use set() function
  • Create circular dependencies between stores
  • Put everything in one massive store
  • Forget to unsubscribe from store subscriptions