mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-02-13 06:52:42 +08:00
add via node menu + canvas
refactor
This commit is contained in:
parent
3cd503f243
commit
3c20922b33
@ -29,11 +29,6 @@ function getLinks(config) {
|
|||||||
return { linksTo, linksFrom };
|
return { linksTo, linksFrom };
|
||||||
}
|
}
|
||||||
|
|
||||||
// function getInnerLinkType(config, link) {
|
|
||||||
// const [outputNodeId, outputNodeSlot] = link;
|
|
||||||
// return config.nodes[outputNodeId].outputs[outputNodeSlot].type;
|
|
||||||
// }
|
|
||||||
|
|
||||||
function buildNodeDef(config, nodeName, defs, workflow) {
|
function buildNodeDef(config, nodeName, defs, workflow) {
|
||||||
const slots = {
|
const slots = {
|
||||||
inputs: {},
|
inputs: {},
|
||||||
@ -168,6 +163,127 @@ function buildNodeDef(config, nodeName, defs, workflow) {
|
|||||||
return newDef;
|
return newDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ConvertToGroupAction {
|
||||||
|
getName() {
|
||||||
|
const name = prompt("Enter group name");
|
||||||
|
if (!name) return;
|
||||||
|
|
||||||
|
const nodeId = "workflow/" + name;
|
||||||
|
|
||||||
|
if (app.graph.extra?.groupNodes?.[name]) {
|
||||||
|
if (app.graph._nodes.find((n) => n.type === nodeId)) {
|
||||||
|
alert(
|
||||||
|
"An in use group node with this name already exists embedded in this workflow, please remove any instances or use a new name."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} else if (
|
||||||
|
!confirm(
|
||||||
|
"An group node with this name already exists embedded in this workflow, are you sure you want to overwrite it?"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
async register(name) {
|
||||||
|
// Use the built in copyToClipboard function to generate the node data we need
|
||||||
|
const backup = localStorage.getItem("litegrapheditor_clipboard");
|
||||||
|
app.canvas.copyToClipboard();
|
||||||
|
const config = JSON.parse(localStorage.getItem("litegrapheditor_clipboard"));
|
||||||
|
localStorage.setItem("litegrapheditor_clipboard", backup);
|
||||||
|
const def = buildNodeDef(config, name, globalDefs, true);
|
||||||
|
await app.registerNodeDef("workflow/" + name, def);
|
||||||
|
return { config, def };
|
||||||
|
}
|
||||||
|
|
||||||
|
findOutput(slots, link, index) {
|
||||||
|
const outputMap = slots.outputs;
|
||||||
|
for (const k in outputMap) {
|
||||||
|
const o = outputMap[k];
|
||||||
|
if (o.node === index && o.slot === link.origin_slot) {
|
||||||
|
return +k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linkOutputs(newNode, node, slots, index) {
|
||||||
|
if (node.outputs) {
|
||||||
|
for (const output of node.outputs) {
|
||||||
|
if (!output.links) continue;
|
||||||
|
for (const l of output.links) {
|
||||||
|
const link = app.graph.links[l];
|
||||||
|
|
||||||
|
const targetNode = app.graph.getNodeById(link.target_id);
|
||||||
|
const slot = this.findOutput(slots, link, index);
|
||||||
|
if (slot != null) {
|
||||||
|
newNode.connect(slot, targetNode, link.target_slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linkInputs(newNode, config, slots) {
|
||||||
|
for (const link of config.links ?? []) {
|
||||||
|
const [, originSlot, targetId, targetSlot, actualOriginId] = link;
|
||||||
|
const originNode = app.graph.getNodeById(actualOriginId);
|
||||||
|
if (!originNode) continue; // this node is in the group
|
||||||
|
originNode.connect(originSlot, newNode.id, slots.inputs[targetId][targetSlot]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
convert(name, config, def) {
|
||||||
|
const newNode = LiteGraph.createNode("workflow/" + name);
|
||||||
|
app.graph.add(newNode);
|
||||||
|
|
||||||
|
let top;
|
||||||
|
let left;
|
||||||
|
let index = 0;
|
||||||
|
const slots = def[GROUP_SLOTS];
|
||||||
|
for (const id in app.canvas.selected_nodes) {
|
||||||
|
const node = app.graph.getNodeById(id);
|
||||||
|
if (left == null || node.pos[0] < left) {
|
||||||
|
left = node.pos[0];
|
||||||
|
}
|
||||||
|
if (top == null || node.pos[1] < top) {
|
||||||
|
top = node.pos[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.linkOutputs(newNode, node, slots, index++);
|
||||||
|
|
||||||
|
app.graph.remove(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.linkInputs(newNode, config, slots);
|
||||||
|
|
||||||
|
newNode.pos = [left, top];
|
||||||
|
}
|
||||||
|
|
||||||
|
addOption(options, index) {
|
||||||
|
options.splice(index + 1, null, {
|
||||||
|
content: `Convert to Group Node`,
|
||||||
|
disabled:
|
||||||
|
Object.keys(app.canvas.selected_nodes || {}).length < 2 ||
|
||||||
|
Object.values(app.canvas.selected_nodes).find((n) => n.constructor.nodeData?.[IS_GROUP_NODE]),
|
||||||
|
callback: async () => {
|
||||||
|
const name = this.getName();
|
||||||
|
let extra = app.graph.extra;
|
||||||
|
if (!extra) app.graph.extra = extra = {};
|
||||||
|
let groupNodes = extra.groupNodes;
|
||||||
|
if (!groupNodes) extra.groupNodes = groupNodes = {};
|
||||||
|
|
||||||
|
const { config, def } = await this.register(name);
|
||||||
|
groupNodes[name] = config;
|
||||||
|
|
||||||
|
this.convert(name, config, def);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const id = "Comfy.GroupNode";
|
const id = "Comfy.GroupNode";
|
||||||
let globalDefs;
|
let globalDefs;
|
||||||
const ext = {
|
const ext = {
|
||||||
@ -176,99 +292,7 @@ const ext = {
|
|||||||
const orig = LGraphCanvas.prototype.getCanvasMenuOptions;
|
const orig = LGraphCanvas.prototype.getCanvasMenuOptions;
|
||||||
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
|
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
|
||||||
const options = orig.apply(this, arguments);
|
const options = orig.apply(this, arguments);
|
||||||
|
new ConvertToGroupAction().addOption(options, options.length);
|
||||||
options.push(null);
|
|
||||||
options.push({
|
|
||||||
content: `Convert to Group Node`,
|
|
||||||
disabled: !Object.keys(app.canvas.selected_nodes || {}).length,
|
|
||||||
callback: async () => {
|
|
||||||
const name = prompt("Enter group name");
|
|
||||||
if (!name) return;
|
|
||||||
|
|
||||||
const nodeId = "workflow/" + name;
|
|
||||||
|
|
||||||
let extra = app.graph.extra;
|
|
||||||
if (!extra) app.graph.extra = extra = {};
|
|
||||||
let groupNodes = extra.groupNodes;
|
|
||||||
if (!groupNodes) extra.groupNodes = groupNodes = {};
|
|
||||||
|
|
||||||
if (name in groupNodes) {
|
|
||||||
if (app.graph._nodes.find((n) => n.type === nodeId)) {
|
|
||||||
alert(
|
|
||||||
"An in use group node with this name already exists embedded in this workflow, please remove any instances or use a new name."
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
} else if (
|
|
||||||
!confirm(
|
|
||||||
"An group node with this name already exists embedded in this workflow, are you sure you want to overwrite it?"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the built in copyToClipboard function to generate the node data we need
|
|
||||||
const backup = localStorage.getItem("litegrapheditor_clipboard");
|
|
||||||
app.canvas.copyToClipboard();
|
|
||||||
const config = JSON.parse(localStorage.getItem("litegrapheditor_clipboard"));
|
|
||||||
localStorage.setItem("litegrapheditor_clipboard", backup);
|
|
||||||
const def = buildNodeDef(config, name, globalDefs, true);
|
|
||||||
await app.registerNodeDef("workflow/" + name, def);
|
|
||||||
|
|
||||||
groupNodes[name] = config;
|
|
||||||
|
|
||||||
const newNode = LiteGraph.createNode("workflow/" + name);
|
|
||||||
app.graph.add(newNode);
|
|
||||||
|
|
||||||
let top;
|
|
||||||
let left;
|
|
||||||
let index = 0;
|
|
||||||
const slots = def[GROUP_SLOTS];
|
|
||||||
for (const id in app.canvas.selected_nodes) {
|
|
||||||
const node = app.graph.getNodeById(id);
|
|
||||||
if (left == null || node.pos[0] < left) {
|
|
||||||
left = node.pos[0];
|
|
||||||
}
|
|
||||||
if (top == null || node.pos[1] < top) {
|
|
||||||
top = node.pos[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relink outputs to the group
|
|
||||||
if (node.outputs) {
|
|
||||||
for (const output of node.outputs) {
|
|
||||||
if (!output.links) continue;
|
|
||||||
for (const l of output.links) {
|
|
||||||
const link = app.graph.links[l];
|
|
||||||
|
|
||||||
const targetNode = app.graph.getNodeById(link.target_id);
|
|
||||||
const outputMap = slots.outputs;
|
|
||||||
for (const k in outputMap) {
|
|
||||||
const o = outputMap[k];
|
|
||||||
if (o.node === index && o.slot === link.origin_slot) {
|
|
||||||
newNode.connect(+k, targetNode, link.target_slot);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
app.graph.remove(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Relink inputs to the group
|
|
||||||
for (const link of config.links ?? []) {
|
|
||||||
const [_, originSlot, targetId, targetSlot, actualOriginId] = link;
|
|
||||||
const originNode = app.graph.getNodeById(actualOriginId);
|
|
||||||
if (!originNode) continue; // this node is in the group
|
|
||||||
originNode.connect(originSlot, newNode.id, slots.inputs[targetId][targetSlot]);
|
|
||||||
}
|
|
||||||
|
|
||||||
newNode.pos = [left, top];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -341,8 +365,7 @@ const ext = {
|
|||||||
let top;
|
let top;
|
||||||
let left;
|
let left;
|
||||||
const selectedIds = Object.keys(app.canvas.selected_nodes);
|
const selectedIds = Object.keys(app.canvas.selected_nodes);
|
||||||
for (let nodeIndex = 0; nodeIndex < selectedIds.length; nodeIndex++) {
|
for (const id of selectedIds) {
|
||||||
const id = selectedIds[nodeIndex];
|
|
||||||
const newNode = app.graph.getNodeById(id);
|
const newNode = app.graph.getNodeById(id);
|
||||||
if (left == null || newNode.pos[0] < left) {
|
if (left == null || newNode.pos[0] < left) {
|
||||||
left = newNode.pos[0];
|
left = newNode.pos[0];
|
||||||
@ -488,6 +511,17 @@ const ext = {
|
|||||||
|
|
||||||
return innerNodes;
|
return innerNodes;
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
const getExtraMenuOptions = node.getExtraMenuOptions ?? node.prototype.getExtraMenuOptions;
|
||||||
|
node.getExtraMenuOptions = function (_, options) {
|
||||||
|
let i = options.findIndex((o) => o.content === "Outputs");
|
||||||
|
if (i === -1) i = options.length;
|
||||||
|
else i++;
|
||||||
|
|
||||||
|
new ConvertToGroupAction().addOption(options, i);
|
||||||
|
|
||||||
|
return getExtraMenuOptions.apply(this, arguments);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user