Documentation
v1.0.0GitHub
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