Browse Source

feat(33-01): add AI workflow snapshot infrastructure to store

Add snapshot state and actions to capture, revert, and clear workflow
state before AI-initiated changes. Includes manual change tracking
that auto-clears snapshot after 3 user edits.

- Add previousWorkflowSnapshot state (nodes, edges, groups, edgeStyle)
- Add manualChangeCount state
- Implement captureSnapshot() to save current workflow state
- Implement revertToSnapshot() to restore snapshot
- Implement clearSnapshot() to remove snapshot
- Implement incrementManualChangeCount() with auto-clear at 3 changes
- Wire manual change tracking into addNode, removeNode, onConnect,
  removeEdge, onNodesChange (remove only), onEdgesChange (remove only)
- Call clearSnapshot() in clearWorkflow and loadWorkflow

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
18a21398cd
  1. 95
      src/store/workflowStore.ts

95
src/store/workflowStore.ts

@ -205,6 +205,21 @@ interface WorkflowStore {
setNavigationTarget: (nodeId: string | null) => void;
setFocusedCommentNodeId: (nodeId: string | null) => void;
resetViewedComments: () => void;
// AI change snapshot state
previousWorkflowSnapshot: {
nodes: WorkflowNode[];
edges: WorkflowEdge[];
groups: Record<string, NodeGroup>;
edgeStyle: EdgeStyle;
} | null;
manualChangeCount: number;
// AI change snapshot actions
captureSnapshot: () => void;
revertToSnapshot: () => void;
clearSnapshot: () => void;
incrementManualChangeCount: () => void;
}
let nodeIdCounter = 0;
@ -343,6 +358,10 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
navigationTarget: null,
focusedCommentNodeId: null,
// AI change snapshot initial state
previousWorkflowSnapshot: null,
manualChangeCount: 0,
setEdgeStyle: (style: EdgeStyle) => {
set({ edgeStyle: style });
},
@ -389,6 +408,8 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
hasUnsavedChanges: true,
}));
get().incrementManualChangeCount();
return id;
},
@ -411,6 +432,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
),
hasUnsavedChanges: true,
}));
get().incrementManualChangeCount();
},
onNodesChange: (changes: NodeChange<WorkflowNode>[]) => {
@ -418,19 +440,33 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
const hasMeaningfulChange = changes.some(
(c) => c.type !== "select" && c.type !== "dimensions"
);
// Track manual changes only for remove operations (not position/selection/dimensions)
const hasRemoveChange = changes.some((c) => c.type === "remove");
set((state) => ({
nodes: applyNodeChanges(changes, state.nodes),
...(hasMeaningfulChange ? { hasUnsavedChanges: true } : {}),
}));
if (hasRemoveChange) {
get().incrementManualChangeCount();
}
},
onEdgesChange: (changes: EdgeChange<WorkflowEdge>[]) => {
// Only mark as unsaved for meaningful changes (not selection changes)
const hasMeaningfulChange = changes.some((c) => c.type !== "select");
// Track manual changes only for remove operations (not selection)
const hasRemoveChange = changes.some((c) => c.type === "remove");
set((state) => ({
edges: applyEdgeChanges(changes, state.edges),
...(hasMeaningfulChange ? { hasUnsavedChanges: true } : {}),
}));
if (hasRemoveChange) {
get().incrementManualChangeCount();
}
},
onConnect: (connection: Connection) => {
@ -444,6 +480,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
),
hasUnsavedChanges: true,
}));
get().incrementManualChangeCount();
},
addEdgeWithType: (connection: Connection, edgeType: string) => {
@ -465,6 +502,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
edges: state.edges.filter((edge) => edge.id !== edgeId),
hasUnsavedChanges: true,
}));
get().incrementManualChangeCount();
},
toggleEdgePause: (edgeId: string) => {
@ -2489,6 +2527,9 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
// Reset viewed comments when loading new workflow
viewedCommentNodeIds: new Set<string>(),
});
// Clear any snapshot when user manually loads a workflow
get().clearSnapshot();
},
clearWorkflow: () => {
@ -2512,6 +2553,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
// Reset viewed comments when clearing workflow
viewedCommentNodeIds: new Set<string>(),
});
get().clearSnapshot();
},
addToGlobalHistory: (item: Omit<ImageHistoryItem, "id">) => {
@ -2904,4 +2946,57 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
resetViewedComments: () => {
set({ viewedCommentNodeIds: new Set<string>() });
},
// AI change snapshot actions
captureSnapshot: () => {
const state = get();
// Deep copy the current workflow state to avoid reference sharing
const snapshot = {
nodes: JSON.parse(JSON.stringify(state.nodes)),
edges: JSON.parse(JSON.stringify(state.edges)),
groups: JSON.parse(JSON.stringify(state.groups)),
edgeStyle: state.edgeStyle,
};
set({
previousWorkflowSnapshot: snapshot,
manualChangeCount: 0,
});
},
revertToSnapshot: () => {
const state = get();
if (state.previousWorkflowSnapshot) {
set({
nodes: state.previousWorkflowSnapshot.nodes,
edges: state.previousWorkflowSnapshot.edges,
groups: state.previousWorkflowSnapshot.groups,
edgeStyle: state.previousWorkflowSnapshot.edgeStyle,
previousWorkflowSnapshot: null,
manualChangeCount: 0,
hasUnsavedChanges: true,
});
}
},
clearSnapshot: () => {
set({
previousWorkflowSnapshot: null,
manualChangeCount: 0,
});
},
incrementManualChangeCount: () => {
const state = get();
const newCount = state.manualChangeCount + 1;
// Automatically clear snapshot after 3 manual changes
if (newCount >= 3) {
set({
previousWorkflowSnapshot: null,
manualChangeCount: 0,
});
} else {
set({ manualChangeCount: newCount });
}
},
}));

Loading…
Cancel
Save