rerouteNode.js 5.72 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { app } from "../../scripts/app.js";

// Node that allows you to redirect connections for cleaner graphs

app.registerExtension({
	name: "Comfy.RerouteNode",
	registerCustomNodes() {
		class RerouteNode {
			constructor() {
				if (!this.properties) {
					this.properties = {};
				}
				this.properties.showOutputText = RerouteNode.defaultVisibility;

				this.addInput("", "*");
				this.addOutput(this.properties.showOutputText ? "*" : "", "*");
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

				this.onConnectionsChange = function (type, index, connected, link_info) {
					// Prevent multiple connections to different types when we have no input
					if (connected && type === LiteGraph.OUTPUT) {
						// Ignore wildcard nodes as these will be updated to real types
						const types = new Set(this.outputs[0].links.map((l) => app.graph.links[l].type).filter((t) => t !== "*"));
						if (types.size > 1) {
							for (let i = 0; i < this.outputs[0].links.length - 1; i++) {
								const linkId = this.outputs[0].links[i];
								const link = app.graph.links[linkId];
								const node = app.graph.getNodeById(link.target_id);
								node.disconnectInput(link.target_slot);
							}
						}
					}

					// Find root input
					let currentNode = this;
					let updateNodes = [];
					let inputType = null;
pythongosssss's avatar
pythongosssss committed
37
					let inputNode = null;
38
39
40
41
42
43
44
45
46
47
48
49
					while (currentNode) {
						updateNodes.unshift(currentNode);
						const linkId = currentNode.inputs[0].link;
						if (linkId !== null) {
							const link = app.graph.links[linkId];
							const node = app.graph.getNodeById(link.origin_id);
							const type = node.constructor.type;
							if (type === "Reroute") {
								// Move the previous node
								currentNode = node;
							} else {
								// We've found the end
pythongosssss's avatar
pythongosssss committed
50
								inputNode = currentNode;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
								inputType = node.outputs[link.origin_slot].type;
								break;
							}
						} else {
							// This path has no input node
							currentNode = null;
							break;
						}
					}

					// Find all outputs
					const nodes = [this];
					let outputType = null;
					while (nodes.length) {
						currentNode = nodes.pop();
						const outputs = (currentNode.outputs ? currentNode.outputs[0].links : []) || [];
						if (outputs.length) {
							for (const linkId of outputs) {
								const link = app.graph.links[linkId];
pythongosssss's avatar
pythongosssss committed
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
								// When disconnecting sometimes the link is still registered
								if (!link) continue;

								const node = app.graph.getNodeById(link.target_id);
								const type = node.constructor.type;

								if (type === "Reroute") {
									// Follow reroute nodes
									nodes.push(node);
									updateNodes.push(node);
								} else {
									// We've found an output
									const nodeOutType = node.inputs[link.target_slot].type;
									if (inputType && nodeOutType !== inputType) {
										// The output doesnt match our input so disconnect it
										node.disconnectInput(link.target_slot);
									} else {
										outputType = nodeOutType;
									}
								}
							}
						} else {
							// No more outputs for this path
						}
					}

pythongosssss's avatar
pythongosssss committed
97
					const displayType = inputType || outputType || "*";
pythongosssss's avatar
pythongosssss committed
98
					const color = LGraphCanvas.link_type_colors[displayType];
pythongosssss's avatar
pythongosssss committed
99

100
101
102
103
104
					// Update the types of each node
					for (const node of updateNodes) {
						// If we dont have an input type we are always wildcard but we'll show the output type
						// This lets you change the output link to a different type and all nodes will update
						node.outputs[0].type = inputType || "*";
pythongosssss's avatar
pythongosssss committed
105
106
						node.__outputType = displayType;
						node.outputs[0].name = node.properties.showOutputText ? displayType : "";
107
						node.size = node.computeSize();
pythongosssss's avatar
pythongosssss committed
108
109
110
111

						for (const l of node.outputs[0].links || []) {
							app.graph.links[l].color = color;
						}
112
					}
pythongosssss's avatar
pythongosssss committed
113
114
115
116

					if (inputNode) {
						app.graph.links[inputNode.inputs[0].link].color = color;
					}
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
				};

				this.clone = function () {
					const cloned = RerouteNode.prototype.clone.apply(this);
					cloned.removeOutput(0);
					cloned.addOutput(this.properties.showOutputText ? "*" : "", "*");
					cloned.size = cloned.computeSize();
					return cloned;
				};

				// This node is purely frontend and does not impact the resulting prompt so should not be serialized
				this.isVirtualNode = true;
			}

			getExtraMenuOptions(_, options) {
				options.unshift(
					{
						content: (this.properties.showOutputText ? "Hide" : "Show") + " Type",
						callback: () => {
							this.properties.showOutputText = !this.properties.showOutputText;
							if (this.properties.showOutputText) {
138
								this.outputs[0].name = this.__outputType || this.outputs[0].type;
139
140
141
142
							} else {
								this.outputs[0].name = "";
							}
							this.size = this.computeSize();
143
							app.graph.setDirtyCanvas(true, true);
144
145
146
147
148
149
150
151
152
153
154
155
156
157
						},
					},
					{
						content: (RerouteNode.defaultVisibility ? "Hide" : "Show") + " Type By Default",
						callback: () => {
							RerouteNode.setDefaultTextVisibility(!RerouteNode.defaultVisibility);
						},
					}
				);
			}

			computeSize() {
				return [
					this.properties.showOutputText && this.outputs && this.outputs.length
pythongosssss's avatar
pythongosssss committed
158
159
						? Math.max(75, LiteGraph.NODE_TEXT_SIZE * this.outputs[0].name.length * 0.6 + 40)
						: 75,
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
					26,
				];
			}

			static setDefaultTextVisibility(visible) {
				RerouteNode.defaultVisibility = visible;
				if (visible) {
					localStorage["Comfy.RerouteNode.DefaultVisibility"] = "true";
				} else {
					delete localStorage["Comfy.RerouteNode.DefaultVisibility"];
				}
			}
		}

		// Load default visibility
		RerouteNode.setDefaultTextVisibility(!!localStorage["Comfy.RerouteNode.DefaultVisibility"]);

		LiteGraph.registerNodeType(
			"Reroute",
			Object.assign(RerouteNode, {
				title_mode: LiteGraph.NO_TITLE,
				title: "Reroute",
pythongosssss's avatar
pythongosssss committed
182
				collapsable: false,
183
184
185
186
187
188
			})
		);

		RerouteNode.category = "utils";
	},
});