diff --git a/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts b/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts index b3ec4f0614..0d358f56e4 100644 --- a/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts @@ -142,13 +142,29 @@ export const workflowSlice = createSlice({ builder.addCase(nodeEditorReset, () => deepClone(initialWorkflowState)); builder.addCase(nodesChanged, (state, action) => { - // Not all changes to nodes should result in the workflow being marked touched - action.payload.forEach((change) => { - if (change.type === 'remove') { - state.exposedFields = state.exposedFields.filter((f) => f.nodeId !== change.id); + // If a node was removed, we should remove any exposed fields that were associated with it. However, node changes + // may remove and then add the same node back. For example, when updating a workflow, we replace old nodes with + // updated nodes. In this case, we should not remove the exposed fields. To handle this, we find the last remove + // and add changes for each exposed field. If the remove change comes after the add change, we remove the exposed + // field. + const exposedFieldsToRemove: FieldIdentifier[] = []; + state.exposedFields.forEach((field) => { + const removeIndex = action.payload.findLastIndex( + (change) => change.type === 'remove' && change.id === field.nodeId + ); + const addIndex = action.payload.findLastIndex( + (change) => change.type === 'add' && change.item.id === field.nodeId + ); + if (removeIndex > addIndex) { + exposedFieldsToRemove.push({ nodeId: field.nodeId, fieldName: field.fieldName }); } }); + state.exposedFields = state.exposedFields.filter( + (field) => !exposedFieldsToRemove.some((f) => isEqual(f, field)) + ); + + // Not all changes to nodes should result in the workflow being marked touched const filteredChanges = action.payload.filter((change) => { // We always want to mark the workflow as touched if a node is added, removed, or reset if (['add', 'remove', 'reset'].includes(change.type)) { @@ -165,7 +181,7 @@ export const workflowSlice = createSlice({ return false; }); - if (filteredChanges.length > 0) { + if (filteredChanges.length > 0 || exposedFieldsToRemove.length > 0) { state.isTouched = true; } });