Added support for converting widgets to inputs (and back)

This commit is contained in:
pythongosssss
2023-03-23 21:37:19 +00:00
parent dd095efc2c
commit 7a7e3288ee
4 changed files with 385 additions and 20 deletions

View File

@@ -494,7 +494,7 @@ class ComfyApp {
// Create and mount the LiteGraph in the DOM
const canvasEl = (this.canvasEl = Object.assign(document.createElement("canvas"), { id: "graph-canvas" }));
canvasEl.tabIndex = "1"
canvasEl.tabIndex = "1";
document.body.prepend(canvasEl);
this.graph = new LGraph();
@@ -525,7 +525,10 @@ class ComfyApp {
this.loadGraphData(workflow);
restored = true;
}
} catch (err) {}
} catch (err) {
console.error("Error loading previous workflow", err);
debugger;
}
// We failed to restore a workflow so load the default
if (!restored) {
@@ -572,12 +575,8 @@ class ComfyApp {
const type = inputData[0];
if (Array.isArray(type)) {
// Enums e.g. latent rotation
let defaultValue = type[0];
if (inputData[1] && inputData[1].default) {
defaultValue = inputData[1].default;
}
this.addWidget("combo", inputName, defaultValue, () => {}, { values: type });
// Enums
Object.assign(config, widgets.COMBO(this, inputName, inputData, app) || {});
} else if (`${type}:${inputName}` in widgets) {
// Support custom widgets by Type:Name
Object.assign(config, widgets[`${type}:${inputName}`](this, inputName, inputData, app) || {});
@@ -667,11 +666,15 @@ class ComfyApp {
async graphToPrompt() {
const workflow = this.graph.serialize();
const output = {};
for (const n of workflow.nodes) {
const node = this.graph.getNodeById(n.id);
// Process nodes in order of execution
for (const node of this.graph.computeExecutionOrder(false)) {
const n = workflow.nodes.find((n) => n.id === node.id);
if (node.isVirtualNode) {
// Don't serialize frontend only nodes
// Don't serialize frontend only nodes but let them make changes
if (node.applyToGraph) {
node.applyToGraph(workflow);
}
continue;
}
@@ -695,7 +698,11 @@ class ComfyApp {
let link = node.getInputLink(i);
while (parent && parent.isVirtualNode) {
link = parent.getInputLink(link.origin_slot);
parent = parent.getInputNode(link.origin_slot);
if (link) {
parent = parent.getInputNode(link.origin_slot);
} else {
parent = null;
}
}
if (link) {

View File

@@ -10,9 +10,8 @@ function getNumberDefaults(inputData, defaultStep) {
return { val: defaultVal, config: { min, max, step: 10.0 * step } };
}
function seedWidget(node, inputName, inputData) {
const seed = ComfyWidgets.INT(node, inputName, inputData);
const randomize = node.addWidget("toggle", "Random seed after every gen", true, function (v) {}, {
export function addRandomizeWidget(node, targetWidget, name, defaultValue = false) {
const randomize = node.addWidget("toggle", name, defaultValue, function (v) {}, {
on: "enabled",
off: "disabled",
serialize: false, // Don't include this in prompt.
@@ -20,14 +19,28 @@ function seedWidget(node, inputName, inputData) {
randomize.afterQueued = () => {
if (randomize.value) {
seed.widget.value = Math.floor(Math.random() * 1125899906842624);
const min = targetWidget.options?.min;
const max = targetWidget.options?.max;
if (min != null || max != null) {
targetWidget.value = Math.floor(Math.random() * ((max ?? 9999999999) - (min ?? 0) + 1) + (min ?? 0));
} else {
targetWidget.value = Math.floor(Math.random() * 1125899906842624);
}
}
};
return randomize;
}
function seedWidget(node, inputName, inputData) {
const seed = ComfyWidgets.INT(node, inputName, inputData);
const randomize = addRandomizeWidget(node, seed.widget, "Random seed after every gen", true);
seed.widget.linkedWidgets = [randomize];
return { widget: seed, randomize };
}
const MultilineSymbol = Symbol();
const MultilineResizeSymbol = Symbol();
function addMultilineWidget(node, name, opts, app) {
const MIN_SIZE = 50;
@@ -95,7 +108,7 @@ function addMultilineWidget(node, name, opts, app) {
// Calculate it here instead
computeSize(node.size);
}
const visible = app.canvas.ds.scale > 0.5;
const visible = app.canvas.ds.scale > 0.5 && this.type === "customtext";
const t = ctx.getTransform();
const margin = 10;
Object.assign(this.inputEl.style, {
@@ -149,9 +162,22 @@ function addMultilineWidget(node, name, opts, app) {
}
};
if (!(MultilineSymbol in node)) {
node[MultilineSymbol] = true;
const onResize = node.onResize;
widget.onRemove = () => {
widget.inputEl?.remove();
// Restore original size handler if we are the last
if (!--node[MultilineSymbol]) {
node.onResize = node[MultilineResizeSymbol];
delete node[MultilineSymbol];
delete node[MultilineResizeSymbol];
}
};
if (node[MultilineSymbol]) {
node[MultilineSymbol]++;
} else {
node[MultilineSymbol] = 1;
const onResize = (node[MultilineResizeSymbol] = node.onResize);
node.onResize = function (size) {
computeSize(size);
@@ -199,6 +225,14 @@ export const ComfyWidgets = {
return { widget: node.addWidget("text", inputName, defaultVal, () => {}, {}) };
}
},
COMBO(node, inputName, inputData) {
const type = inputData[0];
let defaultValue = type[0];
if (inputData[1] && inputData[1].default) {
defaultValue = inputData[1].default;
}
return { widget: node.addWidget("combo", inputName, defaultValue, () => {}, { values: type }) };
},
IMAGEUPLOAD(node, inputName, inputData, app) {
const imageWidget = node.widgets.find((w) => w.name === "image");
let uploadWidget;