diff --git a/README.md b/README.md
index bbd186f..9f17c0a 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,7 @@ _(In aqua blue above)_ Chooses the first non-null context.
_(In purple above)_ Add a collection of all connected nodes allowing a single-spot as a "dashboard" to quickly enable and disable nodes. Two distinct nodes; one for "Muting" connected nodes, and one for "Bypassing" connected nodes.
-Also, you can use the **Node Combiner** as UI-only virtual node that allows you to connect any number of nodes as an input, into a single output. As of right now, this is only useful for cleaning up noodles to the Muter node and **any other use will likely not work at all.**
+Also, you can use the **Node Collector** as UI-only virtual node that allows you to connect any number of nodes as an input, into a single output. As of right now, this is only useful for cleaning up noodles to the Muter node and **any other use will likely not work at all.**
A powerful combination: Using Context, Context Switch, & Fast Muter
@@ -79,6 +79,6 @@ Also, you can use the **Node Combiner** as UI-only virtual node that allows you
- The workflow seen here would be a common one where we can generate a handful of base previews cheaply with a random seed, and then choose one to upscale and save to disk.
-4. Lastly, and optionally, you can see the `Node Combiner`. Use it to clean up noodles if you want and connect it to the muter. You can connect anything to it, but doing so may break your workflow's execution.
+4. Lastly, and optionally, you can see the `Node Collector`. Use it to clean up noodles if you want and connect it to the muter. You can connect anything to it, but doing so may break your workflow's execution.
diff --git a/__dev__.py b/__dev__.py
index 9dd1116..ee45c00 100644
--- a/__dev__.py
+++ b/__dev__.py
@@ -2,11 +2,14 @@ import subprocess
import os
import shutil
-subprocess.run(["tsc"])
-
THIS_DIR=os.path.dirname(os.path.abspath(__file__))
DIR_DEV_JS=os.path.abspath(f'{THIS_DIR}/js')
DIR_WEB_JS=os.path.abspath(f'{THIS_DIR}/../../web/extensions/rgthree')
+shutil.rmtree(DIR_DEV_JS)
+
+subprocess.run(["tsc"])
+
+
shutil.rmtree(DIR_WEB_JS)
shutil.copytree(DIR_DEV_JS, DIR_WEB_JS, dirs_exist_ok=True)
\ No newline at end of file
diff --git a/js/base_node_dispatcher.js b/js/base_node_dispatcher.js
index 71bb71d..44cf992 100644
--- a/js/base_node_dispatcher.js
+++ b/js/base_node_dispatcher.js
@@ -13,11 +13,14 @@ export class BaseNodeDispatcher extends LGraphNode {
this.connections = [];
this.addInput("", "*");
}
+ isPassThroughType(type) {
+ return (type === null || type === void 0 ? void 0 : type.includes('Reroute')) || (type === null || type === void 0 ? void 0 : type.includes('Node Combiner')) || (type === null || type === void 0 ? void 0 : type.includes('Node Collector'));
+ }
doChainLookup(startNode = this) {
let rootNodes = [];
const slotsToRemove = [];
const type = startNode.constructor.type;
- if (startNode === this || (type === null || type === void 0 ? void 0 : type.includes('Reroute')) || (type === null || type === void 0 ? void 0 : type.includes('Combiner'))) {
+ if (startNode === this || this.isPassThroughType(type)) {
const removeDups = startNode === this;
for (const input of startNode.inputs) {
const linkId = input.link;
@@ -27,7 +30,7 @@ export class BaseNodeDispatcher extends LGraphNode {
const link = app.graph.links[linkId];
const originNode = app.graph.getNodeById(link.origin_id);
const originNodeType = originNode.constructor.type;
- if ((originNodeType === null || originNodeType === void 0 ? void 0 : originNodeType.includes('Reroute')) || (originNodeType === null || originNodeType === void 0 ? void 0 : originNodeType.includes('Combiner'))) {
+ if (this.isPassThroughType(originNodeType)) {
for (const foundNode of this.doChainLookup(originNode)) {
if (!rootNodes.includes(foundNode)) {
rootNodes.push(foundNode);
diff --git a/js/node_collector.js b/js/node_collector.js
new file mode 100644
index 0000000..4d83c7e
--- /dev/null
+++ b/js/node_collector.js
@@ -0,0 +1,95 @@
+import { app } from "../../scripts/app.js";
+import { addConnectionLayoutSupport } from "./utils.js";
+class CollectorNode extends LGraphNode {
+ constructor(title = CollectorNode.title) {
+ super(title);
+ this.isVirtualNode = true;
+ this.properties = this.properties || {};
+ this.connections = [];
+ this.addInput("", "*");
+ this.addOutput("Output", "*");
+ }
+ clone() {
+ const cloned = super.clone();
+ return cloned;
+ }
+ updateOutputLinks(startNode = this) {
+ const type = startNode.constructor.type;
+ if (startNode.onConnectionsChainChange) {
+ startNode.onConnectionsChainChange();
+ }
+ if (startNode === this || (type === null || type === void 0 ? void 0 : type.includes('Reroute')) || (type === null || type === void 0 ? void 0 : type.includes('Combiner'))) {
+ for (const output of startNode.outputs) {
+ if (!output.links || !output.links.length)
+ continue;
+ for (const linkId of output.links) {
+ const link = app.graph.links[linkId];
+ const targetNode = app.graph.getNodeById(link.target_id);
+ targetNode && this.updateOutputLinks(targetNode);
+ }
+ }
+ }
+ }
+ onConnectionsChange(_type, _slotIndex, _isConnected, link_info, _ioSlot) {
+ if (!link_info)
+ return;
+ this.stabilizeInputsOutputs();
+ this.updateOutputLinks();
+ }
+ stabilizeInputsOutputs() {
+ var _a, _b;
+ for (let index = this.inputs.length - 1; index >= 0; index--) {
+ const input = this.inputs[index];
+ if (!input.link) {
+ this.removeInput(index);
+ }
+ }
+ this.addInput('', '*');
+ const outputLength = ((_b = (_a = this.outputs[0]) === null || _a === void 0 ? void 0 : _a.links) === null || _b === void 0 ? void 0 : _b.length) || 0;
+ if (outputLength > 1) {
+ this.outputs[0].links.length = 1;
+ }
+ }
+}
+CollectorNode.legacyType = "Node Combiner (rgthree)";
+CollectorNode.title = "Node Collector (rgthree)";
+CollectorNode.category = 'rgthree';
+CollectorNode._category = 'rgthree';
+app.registerExtension({
+ name: "rgthree.NodeCollector",
+ registerCustomNodes() {
+ console.log('registerCustomNodes');
+ addConnectionLayoutSupport(CollectorNode, app, [['Left', 'Right'], ['Right', 'Left']]);
+ LiteGraph.registerNodeType(CollectorNode.title, CollectorNode);
+ CollectorNode.category = CollectorNode._category;
+ },
+ async loadedGraphNode(node) {
+ if (node.type === CollectorNode.legacyType) {
+ const newNode = new CollectorNode();
+ newNode.pos = [...node.pos];
+ newNode.size = [...node.size];
+ newNode.properties = Object.assign({}, node.properties);
+ const links = [];
+ for (const [index, output] of node.outputs.entries()) {
+ for (const linkId of (output.links || [])) {
+ const link = app.graph.links[linkId];
+ const targetNode = app.graph.getNodeById(link.target_id);
+ links.push({ node: newNode, slot: index, targetNode, targetSlot: link.target_slot });
+ }
+ }
+ for (const [index, input] of node.inputs.entries()) {
+ const linkId = input.link;
+ if (linkId) {
+ const link = app.graph.links[linkId];
+ const originNode = app.graph.getNodeById(link.origin_id);
+ links.push({ node: originNode, slot: link.origin_slot, targetNode: newNode, targetSlot: index });
+ }
+ }
+ app.graph.add(newNode);
+ app.graph.remove(node);
+ for (const link of links) {
+ link.node.connect(link.slot, link.targetNode, link.targetSlot);
+ }
+ }
+ },
+});
diff --git a/js/node_combiner.js b/js/node_combiner.js
deleted file mode 100644
index 2e5ef4f..0000000
--- a/js/node_combiner.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import { app } from "../../scripts/app.js";
-import { addConnectionLayoutSupport } from "./utils.js";
-app.registerExtension({
- name: "rgthree.NodeCombiner",
- registerCustomNodes() {
- class CombinerNode extends LGraphNode {
- constructor(title = CombinerNode.title) {
- super(title);
- this.isVirtualNode = true;
- this.properties = this.properties || {};
- this.connections = [];
- this.addInput("", "*");
- this.addOutput("Output", "*");
- }
- clone() {
- const cloned = super.clone();
- return cloned;
- }
- updateOutputLinks(startNode = this) {
- const type = startNode.constructor.type;
- if (startNode.onConnectionsChainChange) {
- startNode.onConnectionsChainChange();
- }
- if (startNode === this || (type === null || type === void 0 ? void 0 : type.includes('Reroute')) || (type === null || type === void 0 ? void 0 : type.includes('Combiner'))) {
- for (const output of startNode.outputs) {
- if (!output.links || !output.links.length)
- continue;
- for (const linkId of output.links) {
- const link = app.graph.links[linkId];
- const targetNode = app.graph.getNodeById(link.target_id);
- targetNode && this.updateOutputLinks(targetNode);
- }
- }
- }
- }
- onConnectionsChange(_type, _slotIndex, _isConnected, link_info, _ioSlot) {
- if (!link_info)
- return;
- this.stabilizeInputsOutputs();
- this.updateOutputLinks();
- }
- stabilizeInputsOutputs() {
- var _a, _b;
- for (let index = this.inputs.length - 1; index >= 0; index--) {
- const input = this.inputs[index];
- if (!input.link) {
- this.removeInput(index);
- }
- }
- this.addInput('', '*');
- const outputLength = ((_b = (_a = this.outputs[0]) === null || _a === void 0 ? void 0 : _a.links) === null || _b === void 0 ? void 0 : _b.length) || 0;
- if (outputLength > 1) {
- this.outputs[0].links.length = 1;
- }
- }
- }
- CombinerNode.title = "Node Combiner (rgthree)";
- CombinerNode.category = 'rgthree';
- CombinerNode._category = 'rgthree';
- addConnectionLayoutSupport(CombinerNode, app, [['Left', 'Right'], ['Right', 'Left']]);
- LiteGraph.registerNodeType(CombinerNode.title, CombinerNode);
- CombinerNode.category = CombinerNode._category;
- },
-});
diff --git a/js/reroute.js b/js/reroute.js
index c8412d3..36cc8b4 100644
--- a/js/reroute.js
+++ b/js/reroute.js
@@ -136,7 +136,7 @@ app.registerExtension({
RerouteNode._category = 'rgthree';
RerouteNode.title_mode = LiteGraph.NO_TITLE;
RerouteNode.collapsable = false;
- RerouteNode.layout_slot_offset = 0;
+ RerouteNode.layout_slot_offset = 5;
RerouteNode.size = [40, 30];
addConnectionLayoutSupport(RerouteNode, app, [
["Left", "Right"],
diff --git a/js/smartswitch.js b/js/smartswitch.js
deleted file mode 100644
index 3918c74..0000000
--- a/js/smartswitch.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";
diff --git a/py/context_switch.py b/py/context_switch.py
index 882006f..ebb4f54 100644
--- a/py/context_switch.py
+++ b/py/context_switch.py
@@ -20,18 +20,21 @@ class RgthreeContextSwitch:
},
}
- RETURN_TYPES = ("RGTHREE_CONTEXT",)
- RETURN_NAMES = ("CONTEXT",)
+ RETURN_TYPES = ("RGTHREE_CONTEXT", "MODEL", "CLIP", "VAE", "CONDITIONING", "CONDITIONING", "LATENT", "IMAGE", "INT",)
+ RETURN_NAMES = ("CONTEXT", "MODEL", "CLIP", "VAE", "POSITIVE", "NEGATIVE", "LATENT", "IMAGE", "SEED",)
FUNCTION = "switch"
def switch(self, ctx_01=None, ctx_02=None, ctx_03=None, ctx_04=None, prompt=None):
+ ctx=None
if ctx_01 != None:
- return (ctx_01,)
- if ctx_02 != None:
- return (ctx_02,)
- if ctx_03 != None:
- return (ctx_03,)
- if ctx_04 != None:
- return (ctx_04,)
- return (None,)
+ ctx = ctx_01
+ elif ctx_02 != None:
+ ctx = ctx_02
+ elif ctx_03 != None:
+ ctx = ctx_03
+ elif ctx_04 != None:
+ ctx = ctx_04
+ if ctx != None:
+ return (ctx, ctx['model'], ctx['clip'], ctx['vae'], ctx['positive'], ctx['negative'], ctx['latent'], ctx['images'], ctx['seed'],)
+ return (None,None,None,None,None,None,None,None,None,)
diff --git a/ts/base_node_dispatcher.ts b/ts/base_node_dispatcher.ts
index e086c6d..f255d7f 100644
--- a/ts/base_node_dispatcher.ts
+++ b/ts/base_node_dispatcher.ts
@@ -29,11 +29,15 @@ export class BaseNodeDispatcher extends LGraphNode {
this.addInput("", "*");
}
+ private isPassThroughType(type: string|null) {
+ return type?.includes('Reroute') || type?.includes('Node Combiner') || type?.includes('Node Collector');
+ }
+
private doChainLookup(startNode: TLGraphNode = this) {
let rootNodes: TLGraphNode[] = [];
const slotsToRemove = [];
const type = (startNode.constructor as typeof TLGraphNode).type;
- if (startNode === this || type?.includes('Reroute') || type?.includes('Combiner')) {
+ if (startNode === this || this.isPassThroughType(type)) {
const removeDups = startNode === this;
for (const input of startNode.inputs) {
const linkId: number | null = input!.link;
@@ -43,7 +47,7 @@ export class BaseNodeDispatcher extends LGraphNode {
const link: LLink = (app.graph as LGraph).links[linkId]!;
const originNode: TLGraphNode = (app.graph as LGraph).getNodeById(link.origin_id)!;
const originNodeType = (originNode.constructor as typeof TLGraphNode).type;
- if (originNodeType?.includes('Reroute') || originNodeType?.includes('Combiner')) {
+ if (this.isPassThroughType(originNodeType)) {
for (const foundNode of this.doChainLookup(originNode)) {
if (!rootNodes.includes(foundNode)) {
rootNodes.push(foundNode);
diff --git a/ts/node_collector.ts b/ts/node_collector.ts
new file mode 100644
index 0000000..c8df183
--- /dev/null
+++ b/ts/node_collector.ts
@@ -0,0 +1,130 @@
+// /
+// @ts-ignore
+import { app } from "../../scripts/app.js";
+import type {LLink, LGraph, INodeInputSlot, INodeOutputSlot, LGraphNode as TLGraphNode, LiteGraph as TLiteGraph} from './typings/litegraph.js';
+import { addConnectionLayoutSupport } from "./utils.js";
+
+declare const LiteGraph: typeof TLiteGraph;
+declare const LGraphNode: typeof TLGraphNode;
+
+class CollectorNode extends LGraphNode {
+
+ static legacyType = "Node Combiner (rgthree)";
+ static override title = "Node Collector (rgthree)";
+ // `category` seems to get reset at register, so we'll
+ // re-reset it after the register call. ¯\_(ツ)_/¯
+ static category = 'rgthree';
+ static _category = 'rgthree';
+
+ isVirtualNode = true;
+
+ constructor(title = CollectorNode.title) {
+ super(title);
+ this.properties = this.properties || {};
+ this.connections = [];
+ this.addInput("", "*");
+ this.addOutput("Output", "*");
+ }
+
+ override clone() {
+ const cloned = super.clone();
+ return cloned;
+ }
+
+ private updateOutputLinks(startNode: TLGraphNode = this) {
+ const type = (startNode.constructor as typeof TLGraphNode).type;
+ // @ts-ignore
+ if (startNode.onConnectionsChainChange) {
+ // @ts-ignore
+ startNode.onConnectionsChainChange();
+ }
+ if (startNode === this || type?.includes('Reroute') || type?.includes('Combiner')) {
+ for (const output of startNode.outputs) {
+ if (!output.links || !output.links.length) continue;
+ for (const linkId of output.links) {
+ const link: LLink = (app.graph as LGraph).links[linkId]!;
+ const targetNode: TLGraphNode = (app.graph as LGraph).getNodeById(link.target_id)!;
+ targetNode && this.updateOutputLinks(targetNode)
+ }
+ }
+ }
+ }
+
+ override onConnectionsChange(_type: number, _slotIndex: number, _isConnected: boolean, link_info: LLink, _ioSlot: (INodeOutputSlot | INodeInputSlot)) {
+ if (!link_info) return;
+ this.stabilizeInputsOutputs();
+ // Follow outputs to see if we need to trigger an onConnectionChange.
+ this.updateOutputLinks();
+ }
+
+ private stabilizeInputsOutputs() {
+ for (let index = this.inputs.length - 1; index >= 0; index--) {
+ const input = this.inputs[index]!;
+ if (!input.link) {
+ this.removeInput(index);
+ }
+ }
+ this.addInput('', '*');
+
+ const outputLength = this.outputs[0]?.links?.length || 0;
+ if (outputLength > 1) {
+ this.outputs[0]!.links!.length = 1;
+ }
+ }
+}
+
+app.registerExtension({
+ name: "rgthree.NodeCollector",
+ registerCustomNodes() {
+ console.log('registerCustomNodes')
+
+ // @ts-ignore: Fix incorrect litegraph typings.
+ addConnectionLayoutSupport(CollectorNode, app, [['Left','Right'],['Right','Left']]);
+
+ LiteGraph.registerNodeType(CollectorNode.title, CollectorNode);
+ CollectorNode.category = CollectorNode._category;
+ },
+
+ /**
+ * In case anyone has the old Node Combiner (which was a bad name since it doesn't really
+ * combine, but rather collects), let's port it over to the new Collector. There will be an
+ * error that shows for these users, since ComfyUI doesn't know we're porting it, but when they
+ * look for the redbox, they won't see it and a refresh will fix it (since it will then be
+ * ported).
+ */
+ async loadedGraphNode(node: TLGraphNode) {
+ if (node.type === CollectorNode.legacyType) {
+ // Create a new CollectorNode.
+ const newNode = new CollectorNode();
+ // Port the position, size, and properties from the old node.
+ newNode.pos = [...node.pos];
+ newNode.size = [...node.size];
+ newNode.properties = {...node.properties};
+ // We now collect the links data, inputs and outputs, of the old node since these will be
+ // lost when we remove it.
+ const links: any[] = [];
+ for (const [index, output] of node.outputs.entries()) {
+ for (const linkId of (output.links || [])) {
+ const link: LLink = (app.graph as LGraph).links[linkId]!;
+ const targetNode = app.graph.getNodeById(link.target_id);
+ links.push({node: newNode, slot: index, targetNode, targetSlot: link.target_slot});
+ }
+ }
+ for (const [index, input] of node.inputs.entries()) {
+ const linkId = input.link;
+ if (linkId) {
+ const link: LLink = (app.graph as LGraph).links[linkId]!;
+ const originNode = app.graph.getNodeById(link.origin_id);
+ links.push({node: originNode, slot: link.origin_slot, targetNode: newNode, targetSlot: index});
+ }
+ }
+ // Add the new node, remove the old node.
+ app.graph.add(newNode);
+ app.graph.remove(node);
+ // Now go through and connect the other nodes up as they were.
+ for (const link of links) {
+ link.node.connect(link.slot, link.targetNode, link.targetSlot);
+ }
+ }
+ },
+});
\ No newline at end of file
diff --git a/ts/node_combiner.ts b/ts/node_combiner.ts
deleted file mode 100644
index e3bd6b3..0000000
--- a/ts/node_combiner.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-// /
-// @ts-ignore
-import { app } from "../../scripts/app.js";
-import type {LLink, LGraph, INodeInputSlot, INodeOutputSlot, LGraphNode as TLGraphNode, LiteGraph as TLiteGraph} from './typings/litegraph.js';
-import { addConnectionLayoutSupport } from "./utils.js";
-
-declare const LiteGraph: typeof TLiteGraph;
-declare const LGraphNode: typeof TLGraphNode;
-
-app.registerExtension({
- name: "rgthree.NodeCombiner",
- registerCustomNodes() {
- class CombinerNode extends LGraphNode {
-
- static override title = "Node Combiner (rgthree)";
- // `category` seems to get reset at register, so we'll
- // re-reset it after the register call. ¯\_(ツ)_/¯
- static category = 'rgthree';
- static _category = 'rgthree';
-
- isVirtualNode = true;
-
- constructor(title = CombinerNode.title) {
- super(title);
- this.properties = this.properties || {};
- this.connections = [];
- this.addInput("", "*");
- this.addOutput("Output", "*");
- }
-
- override clone() {
- const cloned = super.clone();
- return cloned;
- }
-
- private updateOutputLinks(startNode: TLGraphNode = this) {
- const type = (startNode.constructor as typeof TLGraphNode).type;
- // @ts-ignore
- if (startNode.onConnectionsChainChange) {
- // @ts-ignore
- startNode.onConnectionsChainChange();
- }
- if (startNode === this || type?.includes('Reroute') || type?.includes('Combiner')) {
- for (const output of startNode.outputs) {
- if (!output.links || !output.links.length) continue;
- for (const linkId of output.links) {
- const link: LLink = (app.graph as LGraph).links[linkId]!;
- const targetNode: TLGraphNode = (app.graph as LGraph).getNodeById(link.target_id)!;
- targetNode && this.updateOutputLinks(targetNode)
- }
- }
- }
- }
-
- override onConnectionsChange(_type: number, _slotIndex: number, _isConnected: boolean, link_info: LLink, _ioSlot: (INodeOutputSlot | INodeInputSlot)) {
- if (!link_info) return;
- this.stabilizeInputsOutputs();
- // Follow outputs to see if we need to trigger an onConnectionChange.
- this.updateOutputLinks();
- }
-
- private stabilizeInputsOutputs() {
- for (let index = this.inputs.length - 1; index >= 0; index--) {
- const input = this.inputs[index]!;
- if (!input.link) {
- this.removeInput(index);
- }
- }
- this.addInput('', '*');
-
- const outputLength = this.outputs[0]?.links?.length || 0;
- if (outputLength > 1) {
- this.outputs[0]!.links!.length = 1;
- }
- }
- }
-
- // @ts-ignore: Fix incorrect litegraph typings.
- addConnectionLayoutSupport(CombinerNode, app, [['Left','Right'],['Right','Left']]);
-
- LiteGraph.registerNodeType(CombinerNode.title, CombinerNode);
- CombinerNode.category = CombinerNode._category;
- },
-});
-
diff --git a/ts/reroute.ts b/ts/reroute.ts
index 8a578bc..6be2c62 100644
--- a/ts/reroute.ts
+++ b/ts/reroute.ts
@@ -21,7 +21,7 @@ app.registerExtension({
static _category = 'rgthree';
static readonly title_mode = LiteGraph.NO_TITLE;
static collapsable = false;
- static layout_slot_offset = 0;
+ static layout_slot_offset = 5;
static size: Vector2 = [40, 30]; // Starting size, read from within litegraph.core
readonly isVirtualNode?: boolean;
diff --git a/ts/utils.ts b/ts/utils.ts
index a0969b8..4f361dc 100644
--- a/ts/utils.ts
+++ b/ts/utils.ts
@@ -177,7 +177,7 @@ export function getConnectionPosForLayout(node: TLGraphNode, isInput: boolean, s
}, 0));
// Set the direction first. This is how the connection line will be drawn.
cxn.dir = data[0];
- // If we are only 10px wide or tall, then put it one the end.
+ // If we are only 10px wide or tall, then put it one the end
if (node.size[0] == 10 && ['Left', 'Right'].includes(side) && ['Top', 'Bottom'].includes(otherSide)) {
side = otherSide === 'Top' ? 'Bottom' : 'Top';
} else if (node.size[1] == 10 && ['Top', 'Bottom'].includes(side) && ['Left', 'Right'].includes(otherSide)) {