mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-08-02 15:04:50 +08:00
Group node fixes (#2259)
* Prevent cleaning graph state on undo/redo * Remove pause rendering due to LG bug * Fix crash on disconnected internal reroutes * Fix widget inputs being incorrect order and value * Fix initial primitive values on connect * basic support for basic rerouted converted inputs * Populate primitive to reroute input * dont crash on bad primitive links * Fix convert to group changing control value * reduce restrictions * fix random crash in tests
This commit is contained in:
@@ -174,6 +174,11 @@ export class GroupNodeConfig {
|
||||
node.index = i;
|
||||
this.processNode(node, seenInputs, seenOutputs);
|
||||
}
|
||||
|
||||
for (const p of this.#convertedToProcess) {
|
||||
p();
|
||||
}
|
||||
this.#convertedToProcess = null;
|
||||
await app.registerNodeDef("workflow/" + this.name, this.nodeDef);
|
||||
}
|
||||
|
||||
@@ -192,7 +197,10 @@ export class GroupNodeConfig {
|
||||
if (!this.linksFrom[sourceNodeId]) {
|
||||
this.linksFrom[sourceNodeId] = {};
|
||||
}
|
||||
this.linksFrom[sourceNodeId][sourceNodeSlot] = l;
|
||||
if (!this.linksFrom[sourceNodeId][sourceNodeSlot]) {
|
||||
this.linksFrom[sourceNodeId][sourceNodeSlot] = [];
|
||||
}
|
||||
this.linksFrom[sourceNodeId][sourceNodeSlot].push(l);
|
||||
|
||||
if (!this.linksTo[targetNodeId]) {
|
||||
this.linksTo[targetNodeId] = {};
|
||||
@@ -230,11 +238,11 @@ export class GroupNodeConfig {
|
||||
// Skip as its not linked
|
||||
if (!linksFrom) return;
|
||||
|
||||
let type = linksFrom["0"][5];
|
||||
let type = linksFrom["0"][0][5];
|
||||
if (type === "COMBO") {
|
||||
// Use the array items
|
||||
const source = node.outputs[0].widget.name;
|
||||
const fromTypeName = this.nodeData.nodes[linksFrom["0"][2]].type;
|
||||
const fromTypeName = this.nodeData.nodes[linksFrom["0"][0][2]].type;
|
||||
const fromType = globalDefs[fromTypeName];
|
||||
const input = fromType.input.required[source] ?? fromType.input.optional[source];
|
||||
type = input[0];
|
||||
@@ -258,10 +266,33 @@ export class GroupNodeConfig {
|
||||
return null;
|
||||
}
|
||||
|
||||
let config = {};
|
||||
let rerouteType = "*";
|
||||
if (linksFrom) {
|
||||
const [, , id, slot] = linksFrom["0"];
|
||||
rerouteType = this.nodeData.nodes[id].inputs[slot].type;
|
||||
for (const [, , id, slot] of linksFrom["0"]) {
|
||||
const node = this.nodeData.nodes[id];
|
||||
const input = node.inputs[slot];
|
||||
if (rerouteType === "*") {
|
||||
rerouteType = input.type;
|
||||
}
|
||||
if (input.widget) {
|
||||
const targetDef = globalDefs[node.type];
|
||||
const targetWidget =
|
||||
targetDef.input.required[input.widget.name] ?? targetDef.input.optional[input.widget.name];
|
||||
|
||||
const widget = [targetWidget[0], config];
|
||||
const res = mergeIfValid(
|
||||
{
|
||||
widget,
|
||||
},
|
||||
targetWidget,
|
||||
false,
|
||||
null,
|
||||
widget
|
||||
);
|
||||
config = res?.customConfig ?? config;
|
||||
}
|
||||
}
|
||||
} else if (linksTo) {
|
||||
const [id, slot] = linksTo["0"];
|
||||
rerouteType = this.nodeData.nodes[id].outputs[slot].type;
|
||||
@@ -282,10 +313,11 @@ export class GroupNodeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
config.forceInput = true;
|
||||
return {
|
||||
input: {
|
||||
required: {
|
||||
[rerouteType]: [rerouteType, {}],
|
||||
[rerouteType]: [rerouteType, config],
|
||||
},
|
||||
},
|
||||
output: [rerouteType],
|
||||
@@ -420,10 +452,18 @@ export class GroupNodeConfig {
|
||||
defaultInput: true,
|
||||
});
|
||||
this.nodeDef.input.required[name] = config;
|
||||
this.newToOldWidgetMap[name] = { node, inputName };
|
||||
|
||||
if (!this.oldToNewWidgetMap[node.index]) {
|
||||
this.oldToNewWidgetMap[node.index] = {};
|
||||
}
|
||||
this.oldToNewWidgetMap[node.index][inputName] = name;
|
||||
|
||||
inputMap[slots.length + i] = this.inputCount++;
|
||||
}
|
||||
}
|
||||
|
||||
#convertedToProcess = [];
|
||||
processNodeInputs(node, seenInputs, inputs) {
|
||||
const inputMapping = [];
|
||||
|
||||
@@ -434,7 +474,11 @@ export class GroupNodeConfig {
|
||||
const linksTo = this.linksTo[node.index] ?? {};
|
||||
const inputMap = (this.oldToNewInputMap[node.index] = {});
|
||||
this.processInputSlots(inputs, node, slots, linksTo, inputMap, seenInputs);
|
||||
this.processConvertedWidgets(inputs, node, slots, converted, linksTo, inputMap, seenInputs);
|
||||
|
||||
// Converted inputs have to be processed after all other nodes as they'll be at the end of the list
|
||||
this.#convertedToProcess.push(() =>
|
||||
this.processConvertedWidgets(inputs, node, slots, converted, linksTo, inputMap, seenInputs)
|
||||
);
|
||||
|
||||
return inputMapping;
|
||||
}
|
||||
@@ -597,11 +641,15 @@ export class GroupNodeHandler {
|
||||
const output = this.groupData.newToOldOutputMap[link.origin_slot];
|
||||
let innerNode = this.innerNodes[output.node.index];
|
||||
let l;
|
||||
while (innerNode.type === "Reroute") {
|
||||
while (innerNode?.type === "Reroute") {
|
||||
l = innerNode.getInputLink(0);
|
||||
innerNode = innerNode.getInputNode(0);
|
||||
}
|
||||
|
||||
if (!innerNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (l && GroupNodeHandler.isGroupNode(innerNode)) {
|
||||
return innerNode.updateLink(l);
|
||||
}
|
||||
@@ -669,6 +717,8 @@ export class GroupNodeHandler {
|
||||
top = newNode.pos[1];
|
||||
}
|
||||
|
||||
if (!newNode.widgets) continue;
|
||||
|
||||
const map = this.groupData.oldToNewWidgetMap[innerNode.index];
|
||||
if (map) {
|
||||
const widgets = Object.keys(map);
|
||||
@@ -725,7 +775,7 @@ export class GroupNodeHandler {
|
||||
}
|
||||
};
|
||||
|
||||
const reconnectOutputs = () => {
|
||||
const reconnectOutputs = (selectedIds) => {
|
||||
for (let groupOutputId = 0; groupOutputId < node.outputs?.length; groupOutputId++) {
|
||||
const output = node.outputs[groupOutputId];
|
||||
if (!output.links) continue;
|
||||
@@ -865,7 +915,7 @@ export class GroupNodeHandler {
|
||||
if (innerNode.type === "PrimitiveNode") {
|
||||
innerNode.primitiveValue = newValue;
|
||||
const primitiveLinked = this.groupData.primitiveToWidget[old.node.index];
|
||||
for (const linked of primitiveLinked) {
|
||||
for (const linked of primitiveLinked ?? []) {
|
||||
const node = this.innerNodes[linked.nodeId];
|
||||
const widget = node.widgets.find((w) => w.name === linked.inputName);
|
||||
|
||||
@@ -874,6 +924,18 @@ export class GroupNodeHandler {
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else if (innerNode.type === "Reroute") {
|
||||
const rerouteLinks = this.groupData.linksFrom[old.node.index];
|
||||
for (const [_, , targetNodeId, targetSlot] of rerouteLinks["0"]) {
|
||||
const node = this.innerNodes[targetNodeId];
|
||||
const input = node.inputs[targetSlot];
|
||||
if (input.widget) {
|
||||
const widget = node.widgets?.find((w) => w.name === input.widget.name);
|
||||
if (widget) {
|
||||
widget.value = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const widget = innerNode.widgets?.find((w) => w.name === old.inputName);
|
||||
@@ -901,33 +963,58 @@ export class GroupNodeHandler {
|
||||
this.node.widgets[targetWidgetIndex + i].value = primitiveNode.widgets[i].value;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
populateReroute(node, nodeId, map) {
|
||||
if (node.type !== "Reroute") return;
|
||||
|
||||
const link = this.groupData.linksFrom[nodeId]?.[0]?.[0];
|
||||
if (!link) return;
|
||||
const [, , targetNodeId, targetNodeSlot] = link;
|
||||
const targetNode = this.groupData.nodeData.nodes[targetNodeId];
|
||||
const inputs = targetNode.inputs;
|
||||
const targetWidget = inputs?.[targetNodeSlot].widget;
|
||||
if (!targetWidget) return;
|
||||
|
||||
const offset = inputs.length - (targetNode.widgets_values?.length ?? 0);
|
||||
const v = targetNode.widgets_values?.[targetNodeSlot - offset];
|
||||
if (v == null) return;
|
||||
|
||||
const widgetName = Object.values(map)[0];
|
||||
const widget = this.node.widgets.find(w => w.name === widgetName);
|
||||
if(widget) {
|
||||
widget.value = v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
populateWidgets() {
|
||||
if (!this.node.widgets) return;
|
||||
|
||||
for (let nodeId = 0; nodeId < this.groupData.nodeData.nodes.length; nodeId++) {
|
||||
const node = this.groupData.nodeData.nodes[nodeId];
|
||||
|
||||
if (!node.widgets_values?.length) continue;
|
||||
|
||||
const map = this.groupData.oldToNewWidgetMap[nodeId];
|
||||
const map = this.groupData.oldToNewWidgetMap[nodeId] ?? {};
|
||||
const widgets = Object.keys(map);
|
||||
|
||||
if (!node.widgets_values?.length) {
|
||||
// special handling for populating values into reroutes
|
||||
// this allows primitives connect to them to pick up the correct value
|
||||
this.populateReroute(node, nodeId, map);
|
||||
continue;
|
||||
}
|
||||
|
||||
let linkedShift = 0;
|
||||
for (let i = 0; i < widgets.length; i++) {
|
||||
const oldName = widgets[i];
|
||||
const newName = map[oldName];
|
||||
const widgetIndex = this.node.widgets.findIndex((w) => w.name === newName);
|
||||
const mainWidget = this.node.widgets[widgetIndex];
|
||||
if (!newName) {
|
||||
// New name will be null if its a converted widget
|
||||
this.populatePrimitive(node, nodeId, oldName, i, linkedShift);
|
||||
|
||||
if (this.populatePrimitive(node, nodeId, oldName, i, linkedShift)) {
|
||||
// Find the inner widget and shift by the number of linked widgets as they will have been removed too
|
||||
const innerWidget = this.innerNodes[nodeId].widgets?.find((w) => w.name === oldName);
|
||||
linkedShift += innerWidget.linkedWidgets?.length ?? 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (widgetIndex === -1) {
|
||||
continue;
|
||||
}
|
||||
|
Reference in New Issue
Block a user