Portal Community

Redo Implementation

redo: () => {
    const { undoStack, redoStack, nodes, edges } = get();

    if (redoStack.length === 0) return;  // Nothing to redo

    // Pop the top snapshot from redo stack
    const [next, ...remainingRedo] = redoStack;

    // Push current state to undo stack
    const currentSnapshot: HistorySnapshot = {
        nodes: JSON.parse(JSON.stringify(nodes)),
        edges: JSON.parse(JSON.stringify(edges))
    };

    // Restore the graph to the redo snapshot
    set({
        nodes    : next.nodes,
        edges    : next.edges,
        undoStack: [currentSnapshot, ...undoStack],
        redoStack: remainingRedo
    });
}

Keyboard Binding

// useKeyboardShortcuts.ts
if (isMod && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) {
    e.preventDefault();
    useWorkflowStore.getState().redo();
}

Redo Stack Clearing

// In historyAction — any new action clears the redo stack:
historyAction: (fn) => {
    // ...
    set({ undoStack: newUndoStack, redoStack: [] });  // ← redo stack cleared
    fn();
}

// This is the standard behavior:
// Undo twice → can redo twice
// Undo twice → make a new edit → redo stack is gone — cannot redo

Stack State Transitions

User ActionUndo StackRedo Stack
Add node A[∅][]
Add node B[∅, A][]
Ctrl+Z (undo)[∅][A+B]
Ctrl+Y (redo)[∅, A][]
Ctrl+Z (undo)[∅][A+B]
Add node C (new action)[∅, A][] ← cleared