Skip to content

Commit 5e67cd7

Browse files
authored
Update for 1.18
Frontend1.16
2 parents 69e3c8b + 9d078a3 commit 5e67cd7

18 files changed

+673
-545
lines changed

README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,38 @@ Problems? Jump down to [logging and debugging](https://github.com/chrisgoringe/c
88

99
Ideas for how to improve the nodes (or bug reports) - [raise an issue](https://github.com/chrisgoringe/cg-use-everywhere/issues)
1010

11-
Shameless plug for my other nodes -> Check out [Image Picker](https://github.com/chrisgoringe/cg-image-picker) for another way to make some workflows smoother. And leave a star if you like something!
11+
Shameless plug for my other nodes -> Check out [Image Picker](https://github.com/chrisgoringe/cg-image-filter) for another way to make some workflows smoother. And leave a star if you like something!
12+
13+
---
14+
15+
## Update for ComfyUI front end 1.16
16+
17+
ComfyUI front end 1.16 made a major change to the way that inputs and widgets work, which had a significant impact on the UE nodes.
18+
19+
A widget is no longer converted to an input in order to connect to it; instead, you just connect an output to it, and it converts.
20+
21+
This is quite cool - except that it meant that there was no way for UE to tell if a widget was supposed to be an empty input for UE to feed.
22+
23+
So there is a new mechanism for this. Here's a picture of a node that I have right-clicked:
24+
25+
![116](docs/116.png)
26+
27+
Things to notice:
28+
29+
- There is a new menu `UE Connectable Widgets` which lists all the widgets. Those marked as connectable have a green bar to the left of the name.
30+
- Select a widget to toggle its value
31+
- Next to the widget, where an input would be, the connectable widgets have a graphical indication of their state.
32+
- The smaller dark circle (next to steps) indicate a connectable node which is not connected (widget value will be used)
33+
- The light circle (next to cfg) and greying out the widget indicates that a UE node will provide the value
34+
- No icon (all the other widgets) indicates that they are not UE connectable
35+
- Other inputs still have the indications of normal connection or UE connection (here model is a UE connection)
36+
37+
You'll probably find that turning the `showlinks` option on helps, and the `highlight` option is required for those widget indicators which help a lot.
38+
39+
![options116](docs/options116.png)
40+
41+
Hopefully old workflows will be automatically converted when you load them. Hopefully.
42+
1243

1344
---
1445

@@ -36,6 +67,9 @@ UE nodes mostly work with group nodes. But there are a couple of important thing
3667

3768
## Latest updates
3869

70+
6.0 (April 2025)
71+
- Very major rebuild to deal with latest version of Comfy
72+
3973
5.0 (6th August 2024)
4074
- Significant change to core logic. Should greatly reduce incompatibility issues, but may have unexpoected consequences!
4175
- Added to right-click menu on nodes the option to make a node reject all UE connections.

__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .use_everywhere import SeedEverywhere, AnythingEverywherePrompts
22

3-
UE_VERSION = "5.0.10"
3+
UE_VERSION = "6.0.0"
44

55
NODE_CLASS_MAPPINGS = { "Seed Everywhere": SeedEverywhere }
66

docs/116.png

51.5 KB
Loading

docs/options116.png

34 KB
Loading

docs/showlinks.png

21.6 KB
Loading

js/use_everywhere.js

Lines changed: 107 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { app } from "../../scripts/app.js";
22
import { api } from "../../scripts/api.js";
33

4-
import { is_UEnode, is_helper, inject, Logger, get_real_node, defineProperty } from "./use_everywhere_utilities.js";
5-
import { displayMessage, update_input_label, indicate_restriction, UpdateBlocker } from "./use_everywhere_ui.js";
4+
import { is_UEnode, is_helper, inject, Logger, get_real_node, defineProperty, graphConverter } from "./use_everywhere_utilities.js";
5+
import { displayMessage, update_input_label, indicate_restriction } from "./use_everywhere_ui.js";
66
import { LinkRenderController } from "./use_everywhere_ui.js";
7-
import { autoCreateMenu } from "./use_everywhere_autocreate.js";
8-
import { add_autoprompts } from "./use_everywhere_autoprompt.js";
97
import { GraphAnalyser } from "./use_everywhere_graph_analysis.js";
10-
import { main_menu_settings, node_menu_settings, canvas_menu_settings, non_ue_menu_settings } from "./use_everywhere_settings.js";
8+
import { node_menu_settings, canvas_menu_settings, non_ue_menu_settings, SETTINGS } from "./use_everywhere_settings.js";
119
import { add_debug } from "./ue_debug.js";
10+
import { settingsCache } from "./use_everywhere_cache.js";
1211

1312
/*
1413
The ui component that looks after the link rendering
@@ -33,6 +32,7 @@ function inject_outdating_into_object_method(object, methodname, tracetext) {
3332

3433
app.registerExtension({
3534
name: "cg.customnodes.use_everywhere",
35+
settings: SETTINGS,
3636

3737
async beforeRegisterNodeDef(nodeType, nodeData, app) {
3838
/*
@@ -72,6 +72,8 @@ app.registerExtension({
7272
inject_outdating_into_objects(options,'callback',`menu option on ${this.id}`);
7373
}
7474

75+
76+
7577
/*
7678
When a UE node is created, we set the group and color restriction properties.
7779
We also create pseudo-widgets for all the inputs so that they can be searched
@@ -113,12 +115,13 @@ app.registerExtension({
113115
const acm = node.afterChangeMade
114116
node.afterChangeMade = (p, v) => {
115117
acm?.(p,v)
116-
if (p==='bgcolor') {
117-
if (node.mode!=4) linkRenderController.mark_link_list_outdated();
118-
}
118+
//if (p==='bgcolor') {
119+
// if (node._being_drawn) return; // the bgcolor gets set by the built in node draw...
120+
// linkRenderController.mark_link_list_outdated();
121+
//}
119122
if (p==='mode') {
120123
linkRenderController.mark_link_list_outdated();
121-
node.widgets?.forEach((widget) => {widget.onModeChange?.(v)});
124+
node.widgets?.forEach((widget) => {widget.onModeChange?.(v)}); // no idea why I have this?
122125
}
123126
}
124127

@@ -138,6 +141,7 @@ app.registerExtension({
138141
}
139142
}
140143

144+
141145
if (is_helper(node)) { // editing a helper node makes the list dirty
142146
inject_outdating_into_objects(node.widgets,'callback',`widget callback on ${this.id}`);
143147
}
@@ -149,21 +153,12 @@ app.registerExtension({
149153
setTimeout( ()=>{linkRenderController.mark_link_list_outdated()}, 100 );
150154
},
151155

152-
// When a graph node is loaded collapsed the UI need to know
153-
// probably not needed now autocomplete is gone?
154-
loadedGraphNode(node) { if (node.flags.collapsed && node.loaded_when_collapsed) node.loaded_when_collapsed(); },
156+
// When a graph node is loaded convert it if needed
157+
loadedGraphNode(node) {
158+
if (graphConverter.running_116_plus()) { graphConverter.convert_if_pre_116(node); }
159+
},
155160

156161
async setup() {
157-
/*
158-
Add css for the autocomplete. Probably not needed now
159-
*/
160-
const head = document.getElementsByTagName('HEAD')[0];
161-
const link = document.createElement('link');
162-
link.rel = 'stylesheet';
163-
link.type = 'text/css';
164-
link.href = 'extensions/cg-use-everywhere/ue.css';
165-
head.appendChild(link);
166-
167162
/*
168163
Listen for message-handler event from python code
169164
*/
@@ -180,34 +175,56 @@ app.registerExtension({
180175
if (linkRenderController) linkRenderController.note_queue_size(detail ? detail.exec_info.queue_remaining : 0)
181176
});
182177

183-
/*
184-
Don't modify the graph when saving the workflow or api
185-
*/
186-
const _original_save_onclick = document.getElementById('comfy-save-button').onclick;
187-
document.getElementById('comfy-save-button').onclick = function() {
188-
graphAnalyser.pause();
189-
_original_save_onclick();
190-
graphAnalyser.unpause()
191-
}
192-
const _original_save_api_onclick = document.getElementById('comfy-dev-save-api-button').onclick;
193-
document.getElementById('comfy-dev-save-api-button').onclick = function() {
194-
graphAnalyser.pause();
195-
// should check for UE links here and give a warning: #217
196-
_original_save_api_onclick();
197-
graphAnalyser.unpause();
178+
/* if we are on version 1.16 or later, stash input data to convert nodes when they are loaded */
179+
if (graphConverter.running_116_plus()) {
180+
const original_loadGraphData = app.loadGraphData;
181+
app.loadGraphData = function (data) {
182+
try {
183+
graphConverter.store_node_input_map(data);
184+
} catch (e) { Logger.log_error(Logger.ERROR, `in loadGraphData ${e}`); }
185+
const cvw_was = settingsCache.getSettingValue("Comfy.Validation.Workflows")
186+
if (settingsCache.getSettingValue("AE.block_graph_validation")) {
187+
app.ui.settings.setSettingValue("Comfy.Validation.Workflows", false);
188+
}
189+
original_loadGraphData.apply(this, arguments);
190+
app.ui.settings.setSettingValue("Comfy.Validation.Workflows", cvw_was);
191+
}
198192
}
199193

200194
/*
201195
When we draw a node, render the virtual connection points
202196
*/
203197
const original_drawNode = LGraphCanvas.prototype.drawNode;
204198
LGraphCanvas.prototype.drawNode = function(node, ctx) {
205-
UpdateBlocker.push()
199+
206200
try {
201+
linkRenderController.pause('drawNode')
207202
const v = original_drawNode.apply(this, arguments);
208203
linkRenderController.highlight_ue_connections(node, ctx);
204+
if (node._last_seen_bg !== node.bgcolor) linkRenderController.mark_link_list_outdated();
205+
node._last_seen_bg = node.bgcolor
209206
return v
210-
} finally { UpdateBlocker.pop() }
207+
} catch (e) {
208+
Logger.log_error(Logger.ERROR, e)
209+
} finally {
210+
linkRenderController.unpause()
211+
}
212+
}
213+
214+
const original_drawFrontCanvas = LGraphCanvas.prototype.drawFrontCanvas
215+
LGraphCanvas.prototype.drawFrontCanvas = function() {
216+
try {
217+
linkRenderController.disable_all_connected_widgets(true)
218+
return original_drawFrontCanvas.apply(this, arguments);
219+
} catch (e) {
220+
Logger.log_error(Logger.ERROR, e)
221+
} finally {
222+
try {
223+
linkRenderController.disable_all_connected_widgets(false)
224+
} catch (e) {
225+
Logger.log_error(Logger.ERROR, e)
226+
}
227+
}
211228
}
212229

213230
/*
@@ -216,13 +233,18 @@ app.registerExtension({
216233
const drawConnections = LGraphCanvas.prototype.drawConnections;
217234
LGraphCanvas.prototype.drawConnections = function(ctx) {
218235
drawConnections?.apply(this, arguments);
219-
linkRenderController.render_all_ue_links(ctx);
236+
try {
237+
linkRenderController.render_all_ue_links(ctx);
238+
} catch (e) {
239+
Logger.log_error(Logger.ERROR, e)
240+
}
220241
}
221242

222243
/*
223-
Add to the main settings
244+
Add to the main settings
245+
now specified as a parameter of register
224246
*/
225-
main_menu_settings();
247+
//main_menu_settings();
226248

227249
/*
228250
Canvas menu is the right click on backdrop.
@@ -240,57 +262,65 @@ app.registerExtension({
240262
return options;
241263
}
242264

243-
/*
244-
When you drag from a node, showConnectionMenu is called. If shift key is pressed call ours
245-
Broken #219
265+
/*
266+
Finding a widget by it's name is something done a lot of times in rendering,
267+
so add a method that caches the names that can be used deep in the rendering code.
268+
269+
TODO: Ought to delete this._widgetNameMap when widgets are added or removed.
246270
*/
247-
const showSearchBox = LGraphCanvas.prototype.showSearchBox;
248-
LGraphCanvas.prototype.showSearchBox = function (optPass) {
249-
if (optPass.shiftKey) {
250-
autoCreateMenu.apply(this, arguments);
251-
} else {
252-
this.use_original_menu = true;
253-
showSearchBox.apply(this, arguments);
254-
this.use_original_menu = false;
271+
LGraphNode.prototype._getWidgetByName = function(nm) {
272+
if (this._widgetNameMap === undefined) {
273+
this._widgetNameMap = {}
274+
this.widgets?.forEach((w)=>{this._widgetNameMap[w.name] = w})
255275
}
276+
return this._widgetNameMap[nm]
256277
}
257278

258-
/*
259-
To allow us to use the shift drag above, we need to intercept 'allow_searchbox' sometimes
260-
(because searchbox is the default behaviour when shift dragging)
261-
Broken #219
262-
*/
263-
var original_allow_searchbox = app.canvas.allow_searchbox;
264-
defineProperty(app.canvas, 'allow_searchbox', {
265-
get : function() {
266-
if (this.use_original_menu) { return original_allow_searchbox; }
267-
if(app.ui.settings.getSettingValue('AE.replacesearch') && this.connecting_output) {
268-
return false;
269-
} else { return original_allow_searchbox; }
270-
},
271-
set : function(v) { original_allow_searchbox = v; }
272-
});
273-
274279

275280
},
276281

277282
init() {
278283
graphAnalyser = GraphAnalyser.instance();
284+
linkRenderController = LinkRenderController.instance(graphAnalyser);
285+
286+
var prompt_being_queued = false;
287+
288+
const original_graphToPrompt = app.graphToPrompt;
279289
app.graphToPrompt = async function () {
280-
return graphAnalyser.analyse_graph(true, true, false);
290+
if (prompt_being_queued) {
291+
return await graphAnalyser.graph_to_prompt( graphAnalyser.analyse_graph(true, true) );
292+
} else {
293+
return await original_graphToPrompt.apply(app, arguments);
294+
}
281295
}
282-
283-
linkRenderController = LinkRenderController.instance(graphAnalyser);
284296

285-
add_autoprompts();
297+
const original_queuePrompt = app.queuePrompt;
298+
app.queuePrompt = async function () {
299+
prompt_being_queued = true;
300+
try {
301+
return await original_queuePrompt.apply(app, arguments);
302+
} finally {
303+
prompt_being_queued = false;
304+
}
305+
}
306+
307+
app.canvas.__node_over = app.canvas.node_over;
308+
defineProperty(app.canvas, 'node_over', {
309+
get: ( )=>{return app.canvas.__node_over },
310+
set: (v)=>{app.canvas.__node_over = v; linkRenderController.node_over_changed(v)}
311+
} )
286312

287313
if (false) add_debug();
288314

289-
const version = __COMFYUI_FRONTEND_VERSION__.split('.')
290-
if (parseInt(version[0])>=1 && (parseInt(version[0])>1 || parseInt(version[1])>=16)) {
291-
alert("Use Everywhere nodes may not work correctly with this version of ComfyUI. See https://github.com/chrisgoringe/cg-use-everywhere/discussions/287.")
292-
}
315+
},
316+
317+
beforeConfigureGraph() {
318+
linkRenderController.pause("before configure", 1000)
319+
graphAnalyser.pause("before configure", 1000)
320+
},
293321

322+
afterConfigureGraph() {
323+
graphConverter.remove_saved_ue_links()
294324
}
295325

296326
});

js/use_everywhere_apply.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
import { app } from "../../scripts/app.js";
2-
import { is_UEnode, get_real_node } from "./use_everywhere_utilities.js";
2+
import { display_name } from "./use_everywhere_classes.js";
3+
import { is_UEnode, get_real_node, Logger } from "./use_everywhere_utilities.js";
34

45

5-
function _convert_to_links(ue) {
6+
function _convert_to_links(ue, added_links) {
67
const output_node_id = ue.output[0];
78
const output_index = ue.output[1];
89
const output_node = get_real_node(output_node_id);
10+
Logger.log(Logger.INFORMATION, "Adding links for " + ue.description);
911
ue.sending_to.forEach((st) => {
1012
const input_node_id = st.node.id;
1113
const input_node = get_real_node(input_node_id);
1214
const input_index = st.input_index;
13-
output_node.connect(output_index, input_node, input_index);
15+
const new_link = output_node.connect(output_index, input_node, input_index);
16+
if (!new_link)
17+
console.error("Failed to connect nodes: " +
18+
`${output_node_id}[${output_index}] -> ` +
19+
`${input_node_id}[${input_index}].`);
20+
else { // Memorize the links we are adding to remove them later
21+
if (added_links)
22+
added_links.push(new_link.id);
23+
Logger.log(Logger.INFORMATION, ` -> ${display_name(st.node)}, ${st.input.name} ` +
24+
`(${st.node.id}.${st.input_index}) (ID: ${new_link.id})`);
25+
}
1426
});
1527
}
1628

17-
function convert_to_links(ues, control_node_id) {
29+
function convert_to_links(ues, control_node_id, added_links=undefined) {
1830
ues.ues.forEach((ue)=> {
19-
if (control_node_id==-1 || ue.controller.id == control_node_id) _convert_to_links(ue);
31+
if (control_node_id==-1 || ue.controller.id == control_node_id) _convert_to_links(ue, added_links);
2032
});
33+
return added_links;
2134
}
2235

2336
function remove_all_ues() {

0 commit comments

Comments
 (0)