From d53f65d1f3a10f4e94d692d7b2edaa5116a34400 Mon Sep 17 00:00:00 2001 From: Sammy Franklin Date: Mon, 23 Oct 2023 21:19:54 -0700 Subject: [PATCH] fixing file subflows ui, allowing export of widgets --- web/extensions/core/subflow.js | 119 ++++++++++++++++++++++----------- web/lib/litegraph.core.js | 71 ++++++++++++++------ web/scripts/widgets.js | 1 - 3 files changed, 133 insertions(+), 58 deletions(-) diff --git a/web/extensions/core/subflow.js b/web/extensions/core/subflow.js index d93fa8058..66a3aca8d 100644 --- a/web/extensions/core/subflow.js +++ b/web/extensions/core/subflow.js @@ -1,8 +1,4 @@ import { app } from "../../scripts/app.js"; -import { api } from "../../scripts/api.js"; -import { ComfyWidgets } from "/scripts/widgets.js"; -const CONFIG = Symbol(); -// const GET_CONFIG = Symbol(); import { GET_CONFIG } from "./widgetInputs.js"; function getConfig(widgetName) { @@ -116,23 +112,6 @@ function getConfig(widgetName) { app.registerExtension({ name: "Comfy.Subflow", beforeRegisterNodeDef(nodeType, nodeData, app) { - - // console.log("in init") - // LiteGraph.registerNodeType( - // "InMemorySubflow", - // Object.assign(InMemorySubflow, { - // title: "InMemorySubflow", - // }) - // ); - - // LiteGraph.registerNodeType( - // "FileSubflow", - // Object.assign(FileSubflow, { - // title: "FileSubflow", - // }) - // ); - - // FileSubflow.category = "utils"; const refreshPins = (node, subflow) => { if(!subflow) return; @@ -157,28 +136,37 @@ app.registerExtension({ const exports = subflowNode.properties?.exports; if (exports) { let pinNum = 0; - for (const inputRef of exports.inputs) { - console.log(subflowNode.inputs); - const input = subflowNode.inputs.find(q => q.name === inputRef); + for (const exportedInput of exports.inputs) { + const input = subflowNode.inputs.find(q => q.name === exportedInput.name); if (!input) continue; const { name, type, link, slot_index, ...extras } = input; - console.log("Input"); - console.log(input); - console.log(extras); - if (extras.widget) { - const w = extras.widget; - const config = getConfig.call(this, input.name) ?? [input.type, w.options || {}]; - extras.widget[GET_CONFIG] = () => config; - console.log(extras); - console.log(input.type); - } + // console.log("Input"); + // console.log(input); + // console.log(extras); + // if (extras.widget) { + // // const w = extras.widget; + // // const config = getConfig.call(this, input.name) ?? [input.type, w.options || {}]; + // // console.log(config); + // // extras.widget[GET_CONFIG] = () => config; + // // console.log(extras); + // // console.log(input.type); + // // console.log(subflowNode); + // // const convertedWidget = subflowNode.widgets.find((w) => w.name == inputRef); + // // node.widgets.push(convertedWidget); + // // const widgetIndex = subflowNode.inputs.findIndex(q => q.name === inputRef); + // // const widget = node.addWidget("number", inputRef, 1, ()=>{}, {min: 0, max:1, step:.1, round:.01, precision:2}); + // // node.widgets.push({type: "number", name: inputRef, value: 1, callback: ()=>{}, options: {min: 0, max:1, step:.1, round:.01, precision:2}}); + // console.log("adding widget", exportedInput.name); + // // convertToInput(node, widget, config); + // } + node.addInput(input.name, input.type, extras ); inputSlots.push([subflowNode.id, pinNum]); pinNum++; } pinNum = 0; - for (const outputRef of exports.outputs) { - const output = subflowNode.outputs.find(q => q.name === outputRef); + for (const exportedOutput of exports.outputs) { + const output = subflowNode.outputs.find(q => q.name === exportedOutput.name); if (!output) continue; node.addOutput(output.name, output.type); outputSlots.push([subflowNode.id, pinNum]); @@ -190,11 +178,65 @@ app.registerExtension({ node.size[0] = 180; }; - const refreshNode = async (node, subflow) => { + const refreshWidgets = (node, subflow, recoverValues) => { + if (!subflow) + return; + + if (node.widgets) { + // Allow widgets to cleanup + let subflowWidget; + for (const w of node.widgets) { + if (w.type == "button") { + subflowWidget = w; + } else if (w.onRemove) { + w.onRemove(); + } + } + node.widgets = [subflowWidget]; + } + + const subflowNodes = subflow.nodes; + let widgetIndex = 1; + for (const subflowNode of subflowNodes) { + const exports = subflowNode.properties?.exports; + if (exports) { + + for (const exportedWidget of exports.widgets) { + let type = exportedWidget.config[0]; + let options = type; + if (type instanceof Array) { + options = { values: type }; + type = "combo"; + } else { + options = exportedWidget.config[1]; + } + if (type === "INT" || type === "FLOAT") { + type = "number"; + } + const getWidgetCallback = (widgetIndex) => { + return (v) => { + console.log(node); + node.widgets_values[widgetIndex] = v; + } + }; + let value = exportedWidget.value; + if (recoverValues) { + value = node.widgets_values[widgetIndex] ?? value; + } + node.addWidget(type, exportedWidget.name, value, getWidgetCallback(widgetIndex), options); + widgetIndex++; + } + + } + } + }; + + const refreshNode = (node, subflow) => { if (!subflow) return; node.subflow = subflow; refreshPins(node, subflow); + refreshWidgets(node, subflow, false); node.size[0] = Math.max(100, LiteGraph.NODE_TEXT_SIZE * node.title.length * 0.45 + 160); }; @@ -204,7 +246,8 @@ app.registerExtension({ // } if (nodeData.name == "FileSubflow") { - nodeType.prototype.refreshNode = function(subflow) { refreshNode(this, subflow); }; + nodeType.prototype.onConfigure = function() { refreshWidgets(this, this.subflow, true); }; + nodeType.prototype.refreshNode = function(subflow) { refreshNode(this, subflow); }; nodeType.prototype.getExportedOutput = function(slot) { return this.subflow.extras.outputSlots[slot]; } nodeType.prototype.getExportedInput = function(slot) { return this.subflow.extras.inputSlots[slot]; } diff --git a/web/lib/litegraph.core.js b/web/lib/litegraph.core.js index 0cf4881d5..959a26c1f 100644 --- a/web/lib/litegraph.core.js +++ b/web/lib/litegraph.core.js @@ -12953,31 +12953,45 @@ LGraphNode.prototype.executeAction = function(action) }; LGraphCanvas.onMenuNodeExport = function(value, options, e, menu, node) { + console.log(node); if (!node) { throw "no node passed"; } - const getOption = ({ name, type, isExported }, pinType) => { - const color = LGraphCanvas.link_type_colors[type]; - const innerHtml = `${name}${isExported ? " (exported)" : ""} `; + function getConfig(widgetName) { + const { nodeData } = this.constructor; + return nodeData?.input?.required[widgetName] ?? nodeData?.input?.optional?.[widgetName]; + } + + const getOption = (opt, pinType) => { + const color = LGraphCanvas.link_type_colors[opt.type]; + const innerHtml = `${opt.name}${opt.isExported ? " (exported)" : ""} `; return { - name, - type: pinType, - content: innerHtml + pinType, + content: innerHtml, + ...opt }; }; - let inputs = node.inputs ?? []; + let inputs = (node.inputs ?? []).filter((input) => !input.widget); + let widgets = (node.widgets ?? []).filter(({type}) => type != "button"); let outputs = node.outputs ?? []; + const isExported = (exports, v) => { + return exports.find(exp => exp.name == v.name) !== undefined; + }; if (node.properties?.exports?.inputs) { - inputs = inputs.map( input => ( {...input, isExported: node.properties.exports.inputs.includes(input.name) }) ); + inputs = inputs.map( input => ( {...input, isExported: isExported(node.properties.exports.inputs, input) }) ); + } + if (node.properties?.exports?.widgets) { + widgets = widgets.map( widget => ( {...widget, isExported: isExported(node.properties.exports.widgets, widget) }) ); } if (node.properties?.exports?.outputs) { - outputs = outputs.map( output => ( {...output, isExported: node.properties.exports.outputs.includes(output.name) }) ); + outputs = outputs.map( output => ( {...output, isExported: isExported(node.properties.exports.outputs, output) }) ); } const exportableVars = [ ...inputs.map((input) => getOption(input, "input")), + ...widgets.map((widget) => getOption(widget, "widget")), null, ...outputs.map((output) => getOption(output, "output")), ]; @@ -12995,23 +13009,42 @@ LGraphNode.prototype.executeAction = function(action) node.graph.beforeChange(/*?*/); //node const exportVar = () => { - if (!node.properties.exports) { - node.properties.exports = { inputs: [], outputs: [] }; + if(!node.properties.exports) { + node.properties.exports = {}; } - + if (!node.properties.exports?.inputs) { + node.properties.exports.inputs = []; + } + if (!node.properties.exports?.widgets) { + node.properties.exports.widgets = []; + } + if (!node.properties.exports?.outputs) { + node.properties.exports.outputs = []; + } + const toggle = (arr, val) => { - if (arr.includes(val)) { - const i = arr.indexOf(val); + const i = arr.findIndex(ele => ele.name === val.name); + if ( i >= 0 ) { arr.splice(i, 1); } else { - arr.push(val); + const extras = {}; + // copy widget params + console.log(val); + if (val.options) { + extras.config = getConfig.call(node, val.name) ?? [val.type, val.options || {}]; + extras.value = val.value; + console.log(extras.config); + } + arr.push({ name: val.name, ...extras }); } }; - if (v.type == "input") { - toggle(node.properties.exports.inputs, v.name); - } else if (v.type == "output") { - toggle(node.properties.exports.outputs, v.name); + if (v.pinType == "input") { + toggle(node.properties.exports.inputs, v); + } else if(v.pinType == "widget") { + toggle(node.properties.exports.widgets, v); + } else if (v.pinType == "output") { + toggle(node.properties.exports.outputs, v); } }; exportVar(); diff --git a/web/scripts/widgets.js b/web/scripts/widgets.js index 4ef96516b..65cc1c6aa 100644 --- a/web/scripts/widgets.js +++ b/web/scripts/widgets.js @@ -570,7 +570,6 @@ export const ComfyWidgets = { document.body.append(fileInput); // Create the button widget for selecting the files - console.log("adding widge") uploadWidget = node.addWidget("button", "choose file with subflow", "subflow", () => { fileInput.click(); });