Skip to content

Commit b59060d

Browse files
feat(ui): improved undo/redo history grouping for selections and postiino changes
1 parent 0d64d53 commit b59060d

File tree

1 file changed

+28
-9
lines changed

1 file changed

+28
-9
lines changed

invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts

+28-9
Original file line numberDiff line numberDiff line change
@@ -775,16 +775,31 @@ const isEdgeSelectionAction = (action: UnknownAction): action is EdgeSelectionAc
775775
return false;
776776
};
777777

778-
type NodePositionOrDimensionAction = {
778+
type NodeDimensionChangeAction = {
779+
type: ReturnType<typeof nodesChanged>['type'];
780+
payload: NodeDimensionChange[];
781+
};
782+
783+
const isDimensionsChangeAction = (action: UnknownAction): action is NodeDimensionChangeAction => {
784+
if (!nodesChanged.match(action)) {
785+
return false;
786+
}
787+
if (action.payload.every((change) => change.type === 'dimensions')) {
788+
return true;
789+
}
790+
return false;
791+
};
792+
793+
type NodePositionChangeAction = {
779794
type: ReturnType<typeof nodesChanged>['type'];
780795
payload: (NodeDimensionChange | NodePositionChange)[];
781796
};
782797

783-
const isDimensionsOrPositionAction = (action: UnknownAction): action is NodePositionOrDimensionAction => {
798+
const isPositionChangeAction = (action: UnknownAction): action is NodePositionChangeAction => {
784799
if (!nodesChanged.match(action)) {
785800
return false;
786801
}
787-
if (action.payload.every((change) => change.type === 'dimensions' || change.type === 'position')) {
802+
if (action.payload.every((change) => change.type === 'position')) {
788803
return true;
789804
}
790805
return false;
@@ -837,18 +852,14 @@ export const nodesUndoableConfig: UndoableOptions<NodesState, UnknownAction> = {
837852
limit: 64,
838853
undoType: nodesSlice.actions.undo.type,
839854
redoType: nodesSlice.actions.redo.type,
840-
groupBy: (action, state, history) => {
841-
if (isNodeSelectionAction(action) || isEdgeSelectionAction(action)) {
842-
// Changes to selection should never be recorded on their own
843-
return history.group;
844-
}
855+
groupBy: (action, _state, _history) => {
845856
if (isHighFrequencyFieldChangeAction(action)) {
846857
// Group by type, node id and field name
847858
const { type, payload } = action;
848859
const { nodeId, fieldName } = payload;
849860
return `${type}-${nodeId}-${fieldName}`;
850861
}
851-
if (isDimensionsOrPositionAction(action)) {
862+
if (isPositionChangeAction(action)) {
852863
const ids = action.payload.map((change) => change.id).join(',');
853864
// Group by type and node ids
854865
return `dimensions-or-position-${ids}`;
@@ -875,6 +886,14 @@ export const nodesUndoableConfig: UndoableOptions<NodesState, UnknownAction> = {
875886
if (!action.type.startsWith(nodesSlice.name)) {
876887
return false;
877888
}
889+
// Ignore actions that only select or deselect nodes and edges
890+
if (isNodeSelectionAction(action) || isEdgeSelectionAction(action)) {
891+
return false;
892+
}
893+
if (isDimensionsChangeAction(action)) {
894+
// Ignore actions that only change the dimensions of nodes - these are internal to reactflow
895+
return false;
896+
}
878897
return true;
879898
},
880899
};

0 commit comments

Comments
 (0)