Flow Studio
History Middleware
historyMiddleware is a Zustand middleware that wraps workflowStore. It intercepts calls to tracked actions, takes a snapshot before the mutation, and manages two stacks (undo and redo) alongside the main store state.
Middleware Structure
// historyMiddleware.ts
import { StateCreator, StoreMutatorIdentifier } from 'zustand';
type HistorySnapshot = {
nodes : WorkflowNode[];
edges : WorkflowEdge[];
};
// The middleware adds three fields to the store:
type HistoryExtension = {
undoStack : HistorySnapshot[];
redoStack : HistorySnapshot[];
historyAction: (fn: () => void) => void;
};
export const historyMiddleware = <T extends WorkflowState>(
config: StateCreator<T & HistoryExtension>
): StateCreator<T & HistoryExtension> =>
(set, get, api) => ({
...config(set, get, api),
undoStack : [],
redoStack : [],
historyAction: (fn: () => void) => {
const { nodes, edges, undoStack } = get();
// Take snapshot of graph before mutation
const snapshot: HistorySnapshot = {
nodes: JSON.parse(JSON.stringify(nodes)),
edges: JSON.parse(JSON.stringify(edges))
};
// Push to undo stack (cap at HISTORY_STACK_SIZE)
const newUndoStack = [snapshot, ...undoStack].slice(0, HISTORY_STACK_SIZE);
// Apply mutation and clear redo stack
set({ undoStack: newUndoStack, redoStack: [] } as any);
fn();
}
});
How workflowStore Uses It
// workflowStore.ts — tracked actions call historyAction
export const useWorkflowStore = create<WorkflowState & HistoryExtension>()(
historyMiddleware(
(set, get) => ({
nodes: [], edges: [],
addNode: (node) => {
get().historyAction(() => {
set(state => ({ nodes: [...state.nodes, node] }));
});
},
removeNode: (id) => {
get().historyAction(() => {
set(state => ({
nodes: state.nodes.filter(n => n.id !== id),
edges: state.edges.filter(e => e.source !== id && e.target !== id)
}));
});
},
// ...
})
)
);
Middleware vs. Separate Store
History is a middleware (not a separate store) because it needs to intercept mutations at the moment they happen — before the state changes. A separate store would receive notifications after the fact and would miss the pre-mutation snapshot.