From 32fe4dc4113fe66dd3a1184d4aa9c7529b3d4da8 Mon Sep 17 00:00:00 2001
From: space-nuko <24979496+space-nuko@users.noreply.github.com>
Date: Tue, 6 Jun 2023 12:38:48 -0500
Subject: [PATCH] Update litegraph
---
web/lib/litegraph.core.js | 694 +++++++++++++++++++++++++++++---------
web/lib/litegraph.css | 11 +-
web/scripts/app.js | 21 ++
web/scripts/widgets.js | 9 +
4 files changed, 574 insertions(+), 161 deletions(-)
diff --git a/web/lib/litegraph.core.js b/web/lib/litegraph.core.js
index 5eb97eea5..10da9d5aa 100644
--- a/web/lib/litegraph.core.js
+++ b/web/lib/litegraph.core.js
@@ -91,6 +91,8 @@
AUTOHIDE_TITLE: 3,
VERTICAL_LAYOUT: "vertical", // arrange nodes vertically
+ BASE_SLOT_TYPES: ["*", "array", "object", "number", "string", "enum", "boolean", "table"],
+
proxy: null, //used to redirect calls
node_images_path: "",
@@ -148,6 +150,9 @@
// use this if you must have node IDs that are unique across all graphs and subgraphs.
use_uuids: false,
+ // use a combo widget for selecting graph input/output types instead of a text box
+ graph_inputs_outputs_use_combo_widget: false,
+
/**
* Register a node class so it can be listed when the user wants to create a new one
* @method registerNodeType
@@ -614,6 +619,40 @@
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,a=>(a^Math.random()*16>>a/4).toString(16));
},
+ getSlotTypeName: function(type) {
+ if (type === LiteGraph.EVENT || type === LiteGraph.ACTION) {
+ return "event"
+ }
+ else if (type === LiteGraph.DEFAULT) {
+ return "default"
+ }
+ return type;
+ },
+
+ getSlotTypesIn: function() {
+ let result = []
+ result = result.concat(LiteGraph.BASE_SLOT_TYPES)
+ result = result.concat([LiteGraph.EVENT])
+ result = result.concat(LiteGraph.slot_types_in)
+ return result
+ },
+
+ getSlotTypesInFormatted: function() {
+ return LiteGraph.getSlotTypesIn().map(LiteGraph.getSlotTypeName);
+ },
+
+ getSlotTypesOut: function() {
+ let result = []
+ result = result.concat(LiteGraph.BASE_SLOT_TYPES)
+ result = result.concat([LiteGraph.EVENT])
+ result = result.concat(LiteGraph.slot_types_out)
+ return result
+ },
+
+ getSlotTypesOutFormatted: function() {
+ return LiteGraph.getSlotTypesOut().map(LiteGraph.getSlotTypeName);
+ },
+
/**
* Returns if the types of two slots are compatible (taking into account wildcards, etc)
* @method isValidConnection
@@ -886,6 +925,10 @@
this.list_of_graphcanvas = [];
}
this.list_of_graphcanvas.push(graphcanvas);
+
+ if (graphcanvas.onGraphAttached) {
+ graphcanvas.onGraphAttached(this);
+ }
};
/**
@@ -904,6 +947,10 @@
}
graphcanvas.graph = null;
this.list_of_graphcanvas.splice(pos, 1);
+
+ if (graphcanvas.onGraphDetached) {
+ graphcanvas.onGraphDetached(this);
+ }
};
/**
@@ -5470,10 +5517,32 @@ LGraphNode.prototype.executeAction = function(action)
if (!this._graph_stack) {
this._graph_stack = [];
}
- this._graph_stack.push(this.graph);
+ const offset = [this.ds.offset[0], this.ds.offset[1]]
+ this._graph_stack.push({ graph: this.graph, offset, scale: this.ds.scale });
}
graph.attachCanvas(this);
+
+ const offset = [0, 0]
+
+ if (graph._nodes.length > 0) {
+ let min_x = Number.MAX_SAFE_INTEGER;
+ let max_x = 0;
+ let min_y = Number.MAX_SAFE_INTEGER;
+ let max_y = 0;
+ for (const node of graph._nodes) {
+ min_x = Math.min(node.pos[0], min_x);
+ max_x = Math.max(node.pos[0] + node.size[0], max_x);
+ min_y = Math.min(node.pos[1], min_y);
+ max_y = Math.max(node.pos[1] + node.size[1], max_y);
+ }
+ offset[0] = -(min_x + (max_x - min_x) / 2) + this.canvas.width / 2;
+ offset[1] = -(min_y + (max_y - min_y) / 2) + this.canvas.height / 2;
+ }
+
+ this.ds.offset = offset;
+ this.ds.scale = 1
+
this.checkPanels();
this.setDirty(true, true);
};
@@ -5488,8 +5557,8 @@ LGraphNode.prototype.executeAction = function(action)
if (!this._graph_stack || this._graph_stack.length == 0) {
return;
}
- var subgraph_node = this.graph._subgraph_node;
- var graph = this._graph_stack.pop();
+ const subgraph_node = this.graph._subgraph_node;
+ const { graph, offset, scale } = this._graph_stack.pop();
this.selected_nodes = {};
this.highlighted_links = {};
graph.attachCanvas(this);
@@ -5498,9 +5567,14 @@ LGraphNode.prototype.executeAction = function(action)
this.centerOnNode(subgraph_node);
this.selectNodes([subgraph_node]);
}
- // when close sub graph back to offset [0, 0] scale 1
- this.ds.offset = [0, 0]
- this.ds.scale = 1
+ this.ds.offset = offset
+ this.ds.scale = scale
+ };
+
+ LGraphCanvas.prototype.closeAllSubgraphs = function() {
+ while (this._graph_stack && this._graph_stack.length > 0) {
+ this.closeSubgraph();
+ }
};
/**
@@ -7366,6 +7440,10 @@ LGraphNode.prototype.executeAction = function(action)
if (this.onShowNodePanel) {
this.onShowNodePanel(n);
}
+ else
+ {
+ this.showShowNodePanel(n);
+ }
if (this.onNodeDblClicked) {
this.onNodeDblClicked(n);
@@ -8053,27 +8131,6 @@ LGraphNode.prototype.executeAction = function(action)
if (input.not_subgraph_input)
continue;
- //input button clicked
- if (this.drawButton(20, y + 2, w - 20, h - 2)) {
- var type = subnode.constructor.input_node_type || "graph/input";
- this.graph.beforeChange();
- var newnode = LiteGraph.createNode(type);
- if (newnode) {
- subgraph.add(newnode);
- this.block_click = false;
- this.last_click_position = null;
- this.selectNodes([newnode]);
- this.node_dragged = newnode;
- this.dragging_canvas = false;
- newnode.setProperty("name", input.name);
- newnode.setProperty("type", input.type);
- this.node_dragged.pos[0] = this.graph_mouse[0] - 5;
- this.node_dragged.pos[1] = this.graph_mouse[1] - 5;
- this.graph.afterChange();
- }
- else
- console.error("graph input node not found:", type);
- }
ctx.fillStyle = "#9C9";
ctx.beginPath();
ctx.arc(w - 16, y + h * 0.5, 5, 0, 2 * Math.PI);
@@ -8123,27 +8180,6 @@ LGraphNode.prototype.executeAction = function(action)
if (output.not_subgraph_input)
continue;
- //output button clicked
- if (this.drawButton(canvas_w - w, y + 2, w - 20, h - 2)) {
- var type = subnode.constructor.output_node_type || "graph/output";
- this.graph.beforeChange();
- var newnode = LiteGraph.createNode(type);
- if (newnode) {
- subgraph.add(newnode);
- this.block_click = false;
- this.last_click_position = null;
- this.selectNodes([newnode]);
- this.node_dragged = newnode;
- this.dragging_canvas = false;
- newnode.setProperty("name", output.name);
- newnode.setProperty("type", output.type);
- this.node_dragged.pos[0] = this.graph_mouse[0] - 5;
- this.node_dragged.pos[1] = this.graph_mouse[1] - 5;
- this.graph.afterChange();
- }
- else
- console.error("graph input node not found:", type);
- }
ctx.fillStyle = "#9C9";
ctx.beginPath();
ctx.arc(canvas_w - w + 16, y + h * 0.5, 5, 0, 2 * Math.PI);
@@ -8272,8 +8308,9 @@ LGraphNode.prototype.executeAction = function(action)
//show subgraph stack header
if (this._graph_stack && this._graph_stack.length) {
ctx.save();
- var parent_graph = this._graph_stack[this._graph_stack.length - 1];
- var subgraph_node = this.graph._subgraph_node;
+ const top_entry = this._graph_stack[this._graph_stack.length - 1];
+ const parent_graph = top_entry.graph;
+ const subgraph_node = this.graph._subgraph_node;
ctx.strokeStyle = subgraph_node.bgcolor;
ctx.lineWidth = 10;
ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2);
@@ -12694,31 +12731,74 @@ LGraphNode.prototype.executeAction = function(action)
elem.querySelector(".name").innerText = input.name;
elem.querySelector(".type").innerText = input.type;
elem.querySelector("button").addEventListener("click",function(e){
- node.removeInput( Number( this.parentNode.dataset["slot"] ) );
+ const inputName = this.parentNode.dataset["name"]
+ node.removeGraphInput(inputName);
inner_refresh();
});
}
+ node.graph.setDirtyCanvas(true, false);
}
- //add extra
- var html = " + NameType";
- var elem = panel.addHTML(html,"subgraph_property extra", true);
- elem.querySelector("button").addEventListener("click", function(e){
- var elem = this.parentNode;
- var name = elem.querySelector(".name").value;
- var type = elem.querySelector(".type").value;
- if(!name || node.findInputSlot(name) != -1)
- return;
- node.addInput(name,type);
- elem.querySelector(".name").value = "";
- elem.querySelector(".type").value = "";
- inner_refresh();
- });
+ //add extra
+ var html = `
++
+Name
+
+Type
+
+`;
+ var elem = panel.addHTML(html, "subgraph_property extra", true);
+ const nameInput = elem.querySelector(".name");
+ const typeInput = elem.querySelector(".type");
+ const addButton = elem.querySelector("button");
- inner_refresh();
- this.canvas.parentNode.appendChild(panel);
- return panel;
+ for (const inType of LiteGraph.getSlotTypesIn()) {
+ var opt = document.createElement('option');
+ opt.value = inType
+ opt.innerHTML = LiteGraph.getSlotTypeName(inType)
+ typeInput.appendChild(opt);
+ if (inType === "*") {
+ opt.selected = true;
+ }
+ }
+
+ const addInput = () => {
+ const name = nameInput.value;
+ let type = typeInput.value;
+ if (type === "-1")
+ type = BuiltInSlotType.EVENT;
+
+ if (!name || node.findInputSlot(name) != -1)
+ return;
+
+ this.addGraphInputNode(node, name, type)
+ nameInput.value = "";
+ typeInput.value = "";
+ inner_refresh();
+ nameInput.focus();
+ }
+
+ const checkSubmit = (e) => {
+ if (e.keyCode == 13) {
+ addInput()
+ e.preventDefault();
+ }
+ else if (e.keyCode == 27) {
+ panel.close();
+ e.preventDefault();
+ }
+ }
+
+ addButton.addEventListener("click", addInput);
+ nameInput.addEventListener("keydown", checkSubmit);
+ typeInput.addEventListener("keydown", checkSubmit);
+
+ inner_refresh();
+ this.canvas.parentNode.appendChild(panel);
+ nameInput.focus();
+ return panel;
}
+
LGraphCanvas.prototype.showSubgraphPropertiesDialogRight = function (node) {
// console.log("showing subgraph properties dialog");
@@ -12747,52 +12827,158 @@ LGraphNode.prototype.executeAction = function(action)
elem.querySelector(".name").innerText = input.name;
elem.querySelector(".type").innerText = input.type;
elem.querySelector("button").addEventListener("click", function (e) {
- node.removeOutput(Number(this.parentNode.dataset["slot"]));
+ const outputName = this.parentNode.dataset["name"]
+ node.removeGraphOutput(outputName);
inner_refresh();
});
}
+ node.graph.setDirtyCanvas(true, false);
}
//add extra
- var html = " + NameType";
+ var html = `
++
+Name
+
+Type
+
+`;
var elem = panel.addHTML(html, "subgraph_property extra", true);
- elem.querySelector(".name").addEventListener("keydown", function (e) {
- if (e.keyCode == 13) {
- addOutput.apply(this)
+ const nameInput = elem.querySelector(".name");
+ const typeInput = elem.querySelector(".type");
+ const addButton = elem.querySelector("button");
+
+ for (const outType of LiteGraph.getSlotTypesOut()) {
+ var opt = document.createElement('option');
+ opt.value = outType
+ opt.innerHTML = LiteGraph.getSlotTypeName(outType)
+ typeInput.appendChild(opt);
+ if (outType === "*") {
+ opt.selected = true;
}
- })
- elem.querySelector("button").addEventListener("click", function (e) {
- addOutput.apply(this)
- });
- function addOutput() {
- var elem = this.parentNode;
- var name = elem.querySelector(".name").value;
- var type = elem.querySelector(".type").value;
+ }
+
+ const addOutput = () => {
+ const name = nameInput.value;
+ let type = typeInput.value;
+ if (type === "-1")
+ type = BuiltInSlotType.EVENT;
+
if (!name || node.findOutputSlot(name) != -1)
return;
- node.addOutput(name, type);
- elem.querySelector(".name").value = "";
- elem.querySelector(".type").value = "";
+
+ this.addGraphOutputNode(node, name, type)
+ nameInput.value = "";
+ typeInput.value = "";
inner_refresh();
+ nameInput.focus();
}
+ const checkSubmit = (e) => {
+ if (e.keyCode == 13) {
+ addOutput()
+ e.preventDefault();
+ }
+ else if (e.keyCode == 27) {
+ panel.close();
+ e.preventDefault();
+ }
+ }
+
+ addButton.addEventListener("click", addOutput);
+ nameInput.addEventListener("keydown", checkSubmit);
+ typeInput.addEventListener("keydown", checkSubmit);
+
inner_refresh();
this.canvas.parentNode.appendChild(panel);
+ nameInput.focus();
return panel;
}
+
+ LGraphCanvas.prototype.addGraphInputNode = function(subgraphNode, name, type) {
+ // Check if there's already an input
+ const existing = this.graph.findNodesByType("graph/input")
+ .find(node => node.properties.name === name)
+
+ if (existing) {
+ this.selectNodes([existing])
+ return;
+ }
+
+ // graph input node must have a non-empty type
+ if (!type || type === "")
+ type = "*"
+
+ const pos = [
+ (this.canvas.width * 0.25) / this.ds.scale - this.ds.offset[0],
+ (this.canvas.height * 0.5) / this.ds.scale - this.ds.offset[1]
+ ]
+
+ this.graph.beforeChange();
+ const pair = subgraphNode.addGraphInput(name, type, pos);
+ if (pair) {
+ const newnode = pair.innerNode;
+ this.selectNodes([newnode]);
+ this.graph.afterChange();
+ }
+ else {
+ console.error("graph input node not found:", type);
+ }
+ }
+
+ LGraphCanvas.prototype.addGraphOutputNode = function(subgraphNode, name, type) {
+ // Check if there's already an output
+ const existing = this.graph.findNodesByType("graph/output")
+ .find(node => node.properties.name === name)
+
+ if (existing) {
+ this.selectNodes([existing])
+ return;
+ }
+
+ // graph output node must have a non-empty type
+ if (!type || type === "")
+ type = "*"
+
+ const pos = [
+ (this.canvas.width * 0.75) / this.ds.scale - this.ds.offset[0],
+ (this.canvas.height * 0.5) / this.ds.scale - this.ds.offset[1]
+ ]
+
+ this.graph.beforeChange();
+ const pair = subgraphNode.addGraphOutput(name, type, pos);
+ if (pair) {
+ const newnode = pair.innerNode;
+ this.selectNodes([newnode]);
+ this.graph.afterChange();
+ }
+ else {
+ console.error("graph input node not found:", type);
+ }
+ }
+
LGraphCanvas.prototype.checkPanels = function()
{
- if(!this.canvas)
- return;
- var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog");
- for(var i = 0; i < panels.length; ++i)
- {
- var panel = panels[i];
- if( !panel.node )
- continue;
- if( !panel.node.graph || panel.graph != this.graph )
- panel.close();
- }
+ if (!this.canvas)
+ return;
+
+ var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog");
+ for (var i = 0; i < panels.length; ++i) {
+ var panel = panels[i]
+ if (!panel.node)
+ continue;
+ if (!panel.node.graph)
+ panel.close();
+
+ if (panel.node.graph != this.graph) {
+ if (panel.node.type === "graph/subgraph" && this.graph._is_subgraph && this.graph === panel.node.subgraph) {
+ continue
+ }
+ else {
+ panel.close();
+ }
+ }
+ }
}
LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) {
@@ -13132,10 +13318,6 @@ LGraphNode.prototype.executeAction = function(action)
has_submenu: true,
callback: LGraphCanvas.onShowMenuNodeProperties
},
- {
- content: "Properties Panel",
- callback: function(item, options, e, menu, node) { LGraphCanvas.active_canvas.showShowNodePanel(node) }
- },
null,
{
content: "Title",
@@ -14774,6 +14956,158 @@ if (typeof exports != "undefined") {
return node;
};
+ Subgraph.prototype.getValidGraphInputName = function(baseName)
+ {
+ baseName = baseName ||"newInput"
+ let name = baseName
+ let existing = this.getInnerGraphInput(name)
+ let i = 1;
+ while (existing != null) {
+ name = `${baseName}_${i++}`
+ existing = this.getInnerGraphInput(name)
+ }
+ return name;
+ }
+
+ Subgraph.prototype.getValidGraphOutputName = function(baseName)
+ {
+ baseName = baseName || "newOutput"
+ let name = baseName
+ let existing = this.getInnerGraphOutput(name)
+ let i = 1;
+ while (existing != null) {
+ name = `${baseName}_${i++}`
+ existing = this.getInnerGraphOutput(name)
+ }
+ return name;
+ }
+
+ Subgraph.prototype.getInnerGraphInput = function(outerInputName) {
+ const graphInput = this.subgraph._nodes.find(n => {
+ return n.type === "graph/input"
+ && n.properties.name === outerInputName
+ })
+
+ return graphInput || null;
+ }
+
+ Subgraph.prototype.getInnerGraphOutput = function(outerOutputName) {
+ const graphOutput = this.subgraph._nodes.find(n => {
+ return n.type === "graph/output"
+ && n.properties.name === outerOutputName
+ })
+
+ return graphOutput || null;
+ }
+
+ Subgraph.prototype.addGraphInput = function(name, type, pos) {
+ name = this.getValidGraphInputName(name)
+
+ const innerNode = LiteGraph.createNode("graph/input");
+ if (innerNode == null)
+ return null;
+
+ let outerType = type;
+
+ // console.debug("[Subgraph] addGraphInput", name, type, outerType, pos)
+
+ // These will run onPropertyChanged.
+ innerNode.setProperty("name", name)
+ innerNode.setProperty("type", type)
+
+ this.subgraph.add(innerNode);
+ const nodeSize = innerNode.computeSize();
+ if (pos)
+ innerNode.pos = [pos[0] - nodeSize[0] * 0.5, pos[1] - nodeSize[1] * 0.5];
+
+ // The following call will add an input slot to this node automatically from onSubgraphNewInput.
+ this.subgraph.addInput(name, outerType, null);
+
+ const outerInputIndex = this.inputs.length - 1;
+ const outerInput = this.inputs[outerInputIndex]
+
+ return { innerNode, outerInput, outerInputIndex }
+ }
+
+ Subgraph.prototype.addGraphOutput = function(name, type, pos) {
+ name = this.getValidGraphOutputName(name)
+
+ const innerNode = LiteGraph.createNode("graph/output");
+ if (innerNode == null)
+ return null;
+
+ let outerType = type;
+
+ // console.debug("[Subgraph] addGraphOutput", name, type, outerType, pos)
+
+ // These will run onPropertyChanged.
+ innerNode.setProperty("name", name)
+ innerNode.setProperty("type", type)
+
+ this.subgraph.add(innerNode);
+ const nodeSize = innerNode.computeSize();
+ if (pos)
+ innerNode.pos = [pos[0], pos[1] - nodeSize[1] * 0.5];
+
+ // The following call will add an output slot to this node automatically from onSubgraphNewOutput.
+ this.subgraph.addOutput(name, outerType, null);
+
+ const outerOutputIndex = this.outputs.length - 1;
+ const outerOutput = this.outputs[outerOutputIndex]
+
+ return { innerNode, outerOutput, outerOutputIndex }
+ }
+
+ Subgraph.prototype.removeGraphInput = function(inputName) {
+ const inputSlot = this.findInputSlot(inputName);
+ if (inputSlot == null) {
+ console.error("[Subgraph] No input in slot!", inputName)
+ return;
+ }
+
+ const innerNodes = this.subgraph.findNodesByType("graph/input").filter(n => n.properties.name === inputName);
+
+ if (innerNodes.length > 0) {
+ // Removing the nodes will also trigger removeInput from subgraph hooks
+ for (const node of innerNodes) {
+ this.subgraph.remove(node);
+ }
+ }
+ else {
+ console.warn("[Subgraph] No GraphInputs found on input removal", inputName)
+
+ // remove the input ourselves since no subgraph hook was triggered
+ const index = this.findInputSlot(inputName)
+ if (index !== -1)
+ this.removeInput(index);
+ }
+ }
+
+ Subgraph.prototype.removeGraphOutput = function(outputName) {
+ const outputSlot = this.findOutputSlot(outputName);
+ if (outputSlot == null) {
+ console.error("[Subgraph] No output in slot!", outputName)
+ return;
+ }
+
+ const innerNodes = this.subgraph.findNodesByType("graph/output").filter(n => n.properties.name === outputName);
+
+ if (innerNodes.length > 0) {
+ // Removing the nodes will also trigger removeOutput from subgraph hooks
+ for (const node of innerNodes) {
+ this.subgraph.remove(node);
+ }
+ }
+ else {
+ console.warn("[Subgraph] No GraphOutputs found on output removal", outputName)
+
+ // remove the output ourselves since no subgraph hook was triggered
+ const index = this.findOutputSlot(outputName)
+ if (index !== -1)
+ this.removeOutput(index);
+ }
+ }
+
Subgraph.prototype.buildFromNodes = function(nodes)
{
//clear all?
@@ -14869,6 +15203,7 @@ if (typeof exports != "undefined") {
//Input for a subgraph
function GraphInput() {
this.addOutput("", "number");
+ this.clonable = false;
this.name_in_graph = "";
this.properties = {
@@ -14883,28 +15218,33 @@ if (typeof exports != "undefined") {
"text",
"Name",
this.properties.name,
- function(v) {
- if (!v) {
- return;
- }
- that.setProperty("name",v);
- }
- );
- this.type_widget = this.addWidget(
- "text",
- "Type",
- this.properties.type,
- function(v) {
- that.setProperty("type",v);
- }
+ this.setName.bind(this),
);
+ if (LiteGraph.graph_inputs_outputs_use_combo_widget) {
+ this.type_widget = this.addWidget(
+ "combo",
+ "Type",
+ LiteGraph.getSlotTypeName(this.properties.type),
+ this.setType.bind(this),
+ { values: LiteGraph.getSlotTypesInFormatted }
+ );
+ }
+ else {
+ this.type_widget = this.addWidget(
+ "text",
+ "Type",
+ LiteGraph.getSlotTypeName(this.properties.type),
+ this.setType.bind(this),
+ );
+ }
+
this.value_widget = this.addWidget(
"number",
"Value",
this.properties.value,
function(v) {
- that.setProperty("value",v);
+ that.setProperty("value", v);
}
);
@@ -14916,15 +15256,45 @@ if (typeof exports != "undefined") {
GraphInput.desc = "Input of the graph";
GraphInput.prototype.onConfigure = function()
+
{
this.updateType();
}
+ GraphInput.prototype.setName = function(v)
+
+ {
+ if (v == null || v === this.properties.name) {
+ return
+ }
+ const subgraph = this.graph._subgraph_node
+ if (!subgraph)
+ return;
+ v = subgraph.getValidGraphInputName(v);
+ this.setProperty("name", v);
+ }
+
+ GraphInput.prototype.setType = function(v)
+
+ {
+ if (!v) {
+ v = "*"
+ }
+
+ let type = v;
+ if (v === "-1" || v === "event")
+ type = LiteGraph.EVENT
+ else if (v === "0")
+ type = "*"
+
+ this.setProperty("type", type);
+ }
+
//ensures the type in the node output and the type in the associated graph input are the same
GraphInput.prototype.updateType = function()
{
var type = this.properties.type;
- this.type_widget.value = type;
+ this.type_widget.value = LiteGraph.getSlotTypeName(type);
//update output
if(this.outputs[0].type != type)
@@ -15029,56 +15399,37 @@ if (typeof exports != "undefined") {
//Output for a subgraph
function GraphOutput() {
this.addInput("", "");
+ this.clonable = false;
this.name_in_graph = "";
this.properties = { name: "", type: "" };
var that = this;
- // Object.defineProperty(this.properties, "name", {
- // get: function() {
- // return that.name_in_graph;
- // },
- // set: function(v) {
- // if (v == "" || v == that.name_in_graph) {
- // return;
- // }
- // if (that.name_in_graph) {
- // //already added
- // that.graph.renameOutput(that.name_in_graph, v);
- // } else {
- // that.graph.addOutput(v, that.properties.type);
- // }
- // that.name_widget.value = v;
- // that.name_in_graph = v;
- // },
- // enumerable: true
- // });
+ this.name_widget = this.addWidget(
+ "text",
+ "Name",
+ this.properties.name,
+ this.setName.bind(this)
+ );
- // Object.defineProperty(this.properties, "type", {
- // get: function() {
- // return that.inputs[0].type;
- // },
- // set: function(v) {
- // if (v == "action" || v == "event") {
- // v = LiteGraph.ACTION;
- // }
- // if (!LiteGraph.isValidConnection(that.inputs[0].type,v))
- // that.disconnectInput(0);
- // that.inputs[0].type = v;
- // if (that.name_in_graph) {
- // //already added
- // that.graph.changeOutputType(
- // that.name_in_graph,
- // that.inputs[0].type
- // );
- // }
- // that.type_widget.value = v || "";
- // },
- // enumerable: true
- // });
+ if (LiteGraph.graph_inputs_outputs_use_combo_widget) {
+ this.type_widget = this.addWidget(
+ "combo",
+ "Type",
+ LiteGraph.getSlotTypeName(this.properties.type),
+ this.setType.bind(this),
+ { values: LiteGraph.getSlotTypesOutFormatted }
+ );
+ }
+ else {
+ this.type_widget = this.addWidget(
+ "text",
+ "Type",
+ LiteGraph.getSlotTypeName(this.properties.type),
+ this.setType.bind(this),
+ );
+ }
- this.name_widget = this.addWidget("text","Name",this.properties.name,"name");
- this.type_widget = this.addWidget("text","Type",this.properties.type,"type");
this.widgets_up = true;
this.size = [180, 60];
}
@@ -15086,6 +15437,31 @@ if (typeof exports != "undefined") {
GraphOutput.title = "Output";
GraphOutput.desc = "Output of the graph";
+ GraphOutput.prototype.setName = function (v) {
+ if (v == null || v === this.properties.name) {
+ return
+ }
+ const subgraph = this.graph._subgraph_node
+ if (!subgraph)
+ return;
+ v = subgraph.getValidGraphOutputName(v);
+ this.setProperty("name", v);
+ }
+
+ GraphOutput.prototype.setType = function (v) {
+ if (!v) {
+ v = "*"
+ }
+
+ let type = v;
+ if (v === "-1" || v === "event")
+ type = LiteGraph.EVENT
+ else if (v === "0")
+ type = "*"
+
+ this.setProperty("type", type);
+ }
+
GraphOutput.prototype.onPropertyChanged = function (name, v) {
if (name == "name") {
if (v == "" || v == this.name_in_graph || v == "enabled") {
@@ -15112,7 +15488,7 @@ if (typeof exports != "undefined") {
GraphOutput.prototype.updateType = function () {
var type = this.properties.type;
if (this.type_widget)
- this.type_widget.value = type;
+ this.type_widget.value = LiteGraph.getSlotTypeName(type);
//update output
if (this.inputs[0].type != type) {
diff --git a/web/lib/litegraph.css b/web/lib/litegraph.css
index 918858f41..b77ab7761 100644
--- a/web/lib/litegraph.css
+++ b/web/lib/litegraph.css
@@ -438,13 +438,20 @@
padding-left: 4px;
}
+.litegraph .subgraph_property > select.type {
+ margin-right: 20px;
+ padding-left: 4px;
+ background-color: black;
+ border: 0;
+}
+
.litegraph .subgraph_property span.label {
display: inline-block;
width: 60px;
padding: 0px 10px;
}
-.litegraph .subgraph_property input {
+.litegraph .subgraph_property input, .litegraph .subgraph_property select {
width: 140px;
color: #999;
background-color: #1A1A1A;
@@ -468,7 +475,7 @@
color: #ccc;
}
-.litegraph .subgraph_property.extra input {
+.litegraph .subgraph_property.extra input, .litegraph .subgraph_property.extra select {
background-color: #111;
}
diff --git a/web/scripts/app.js b/web/scripts/app.js
index e01a3898a..a2c5658e1 100644
--- a/web/scripts/app.js
+++ b/web/scripts/app.js
@@ -51,6 +51,7 @@ export class ComfyApp {
*/
this.nodePreviewImages = {};
+ /**
* Stores `true` for nodes that are executing (the node or its parent subgraphs)
* @type {Set}
*/
@@ -828,6 +829,25 @@ export class ComfyApp {
}
}
+ /**
+ * Notifies widgets when graph is changed
+ */
+ #addCanvasAttachDetachHandlers() {
+ const self = this;
+
+ LGraphCanvas.prototype.onGraphAttached = function(graph) {
+ if (node.onGraphAttached)
+ node.onGraphAttached()
+ }
+
+ LGraphCanvas.prototype.onGraphDetached = function(graph) {
+ for (const node of graph._nodes) {
+ if (node.onGraphDetached)
+ node.onGraphDetached()
+ }
+ }
+ }
+
/**
* Draws node highlights (executing, drag drop) and progress bar
*/
@@ -1034,6 +1054,7 @@ export class ComfyApp {
*/
async setup() {
LiteGraph.use_uuids = true;
+ LiteGraph.graph_inputs_outputs_use_combo_widget = true;
LiteGraph.registered_node_types["graph/input"].skip_list = true;
LiteGraph.registered_node_types["graph/output"].skip_list = true;
diff --git a/web/scripts/widgets.js b/web/scripts/widgets.js
index dfa26aef4..066ace9e9 100644
--- a/web/scripts/widgets.js
+++ b/web/scripts/widgets.js
@@ -179,6 +179,7 @@ function addMultilineWidget(node, name, opts, app) {
};
widget.inputEl = document.createElement("textarea");
widget.inputEl.className = "comfy-multiline-input";
+ widget.inputEl.style.display = "none";
widget.inputEl.value = opts.defaultVal;
widget.inputEl.placeholder = opts.placeholder || "";
document.addEventListener("mousedown", function (event) {
@@ -216,6 +217,14 @@ function addMultilineWidget(node, name, opts, app) {
}
};
+ node.onGraphAttached = function() {
+ widget.inputEl.style.display = "block";
+ }
+
+ node.onGraphDetached = function() {
+ widget.inputEl.style.display = "none";
+ }
+
widget.onRemove = () => {
widget.inputEl?.remove();