From d23db705dd6429b566e2bbc20a631eec23de2f2e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:47:25 +1000 Subject: [PATCH] feat(ui): improved undo/redo history grouping --- .../src/features/nodes/store/nodesSlice.ts | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts index e392092c29..ee1320cc6a 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts @@ -816,6 +816,23 @@ const isHighFrequencyFormChangeAction = isAnyOf( formElementContainerDataChanged ); +// Match workflow changes that are high frequency and should be grouped together - for example, when a user is +// updating the workflow description, we don't want to create a new undo group for every keystroke. +const isHighFrequencyWorkflowDetailsAction = isAnyOf( + workflowNameChanged, + workflowDescriptionChanged, + workflowTagsChanged, + workflowAuthorChanged, + workflowNotesChanged, + workflowVersionChanged, + workflowContactChanged +); + +// Match node-scoped actions that are high frequency and should be grouped together - for example, when a user is +// updating the node label, we don't want to create a new undo group for every keystroke. Or when a user is writing +// a note in a notes node, we don't want to create a new undo group for every keystroke. +const isHighFrequencyNodeScopedAction = isAnyOf(nodeLabelChanged, nodeNotesChanged, notesNodeValueChanged); + export const nodesUndoableConfig: UndoableOptions = { limit: 64, undoType: nodesSlice.actions.undo.type, @@ -826,21 +843,31 @@ export const nodesUndoableConfig: UndoableOptions = { return history.group; } if (isHighFrequencyFieldChangeAction(action)) { - // Group high frequency field changes together when they are of the same type and for the same field + // Group by type, node id and field name const { type, payload } = action; const { nodeId, fieldName } = payload; return `${type}-${nodeId}-${fieldName}`; } if (isDimensionsOrPositionAction(action)) { const ids = action.payload.map((change) => change.id).join(','); + // Group by type and node ids return `dimensions-or-position-${ids}`; } if (isHighFrequencyFormChangeAction(action)) { - // Group high frequency form changes together when they are of the same type and for the same element + // Group by type and form element id const { type, payload } = action; const { id } = payload; return `${type}-${id}`; } + if (isHighFrequencyWorkflowDetailsAction(action)) { + return 'workflow-details'; + } + if (isHighFrequencyNodeScopedAction(action)) { + const { type, payload } = action; + const { nodeId } = payload; + // Group by type and node id + return `${type}-${nodeId}`; + } return null; }, filter: (action, _state, _history) => {