@@ -12,42 +12,54 @@ Shameless plug for my other nodes -> Check out [Image Picker](https://github.com
|
||||
|
||||
---
|
||||
|
||||
# Update for ComfyUI front end 1.16 and above
|
||||
# 7.0 start of the new UE
|
||||
|
||||
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.
|
||||
Over the next few releases I am aiming to reduce the UE zoo down to a single node that can do everything, but is as easy (or easier) to use than the current nodes.
|
||||
|
||||
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.
|
||||
The first step in this process is merging the `AnythingEverywhere` and `AnythingEverywhere?` nodes by moving the regex restrictions into a separate dialog that can be used by either. In future iterations the group and color restrictions will also move to this dialog (so people find them!)
|
||||
|
||||
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.
|
||||
---
|
||||
|
||||
So there is a new mechanism for this. Here's a picture of a node that I have right-clicked:
|
||||
# 6.3 Update for ComfyUI front end (version TBC) - subgraphs
|
||||
|
||||

|
||||
With the addition of subgraphs, group nodes are no longer supported in UE from 6.3 onwards.
|
||||
|
||||
Things to notice:
|
||||
Instead, UE 6.3 has the following support for subgraphs:
|
||||
|
||||
- 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.
|
||||
- Select a widget to toggle its value
|
||||
- Next to the widget, where an input would be, the connectable widgets have a graphical indication of their state.
|
||||
- The smaller dark circle (next to steps) indicate a connectable node which is not connected (widget value will be used)
|
||||
- The light circle (next to cfg) and greying out the widget indicates that a UE node will provide the value
|
||||
- No icon (all the other widgets) indicates that they are not UE connectable
|
||||
- Other inputs still have the indications of normal connection or UE connection (here model is a UE connection)
|
||||
- UE nodes can be used in the main graph and in subgraphs.
|
||||
- UE nodes only broadcast within the graph or subgraph they are in.
|
||||
- Subgraph nodes are just nodes. You can connect UE nodes to their outputs, and UE links will connect to their inputs.
|
||||
- Within a subgraph you can connect from the input panel to a UE node.
|
||||
- UE nodes within the subgraph will *not* broadcast to the output panel.
|
||||
- When you convert a set of nodes to a subgraph, UE links will work as long as:
|
||||
- the UE node (Control) and the node feeding it (Source) are both in the subgraph, or
|
||||
- neither the Source nor Control are in the subgraph, or
|
||||
- the Source is in the subgraph, and the Control is not, and no Target nodes are in the subgraph
|
||||
|
||||
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.
|
||||
|
||||

|
||||
## Subgraph creation
|
||||
|
||||
Hopefully old workflows will be automatically converted when you load them. Hopefully.
|
||||
There are three nodes involved in every UE link:
|
||||
- Source (the link sending the data),
|
||||
- Control (the UE node connected to the source),
|
||||
- Target (the node that is receiving the data as an input)
|
||||
|
||||
## Naming widgets
|
||||
This is how those cases are treated:
|
||||
|
||||
The `Prompts Everywhere`, `Seed Everywhere`, and `Anything Everywhere?` all make use of the name of an input. On some occasions this might mean you want to rename an input.
|
||||
|Support|Source|Control|Target||
|
||||
|-|-|-|-|-|
|
||||
|Yes|Graph|Graph|Graph|Nothing changes|
|
||||
|Yes|Graph|Graph|Subgraph|The subgraph will have inputs for the data; in the subgraph the input panel is connected to the Target with a real link|
|
||||
|No|Graph|Subgraph|Graph|Not supported|
|
||||
|No*|Graph|Subgraph|Subgraph|Not supported|
|
||||
|Yes|Subgraph|Graph|Graph|The subgraph will be connected to the Control|
|
||||
|No|Subgraph|Graph|Subgraph|Not supported|
|
||||
|Yes|Subgraph|Subgraph|Graph|The Source will be connected to the Control *and* the output panel in the subgraph, the output will be connected to the Target with a real link|
|
||||
|Yes|Subgraph|Subgraph|Subgraph|All nodes will be connected in the subgraph as they were in the graph|
|
||||
|
||||
For an input that can't be a widget, that's trivial - right click on the input dot and select `Rename Slot`. The name will be updated, and the new name displayed next to the dot.
|
||||
|
||||
If an input *can* be a widget, you can rename it in the same way, but the new name *does not get displayed*. The name has changed, and the UE nodes will use the new name, but the old name is still shown. I consider this to be a bug in the Comfy front-end, and have raised an issue [here](https://github.com/Comfy-Org/ComfyUI_frontend/issues/3654). Unfortunately it appears that Comfy UI is going to remove the ability to rename a widget entirely, so best to avoid using widget input names if at all possible.
|
||||
No* indicates a case that does not work, but might get implemented.
|
||||
|
||||
No indicates a case I'm unlikely ever to support
|
||||
|
||||
---
|
||||
|
||||
@@ -75,9 +87,8 @@ UE nodes mostly work with group nodes. But there are a couple of important thing
|
||||
|
||||
## Latest updates
|
||||
|
||||
6.2 (June 2025)
|
||||
- Mostly internal issues
|
||||
- Third party integration (in 6.2.2, July 2025)
|
||||
6.3 June/July 2025
|
||||
- support for ComfyUI subgraphs
|
||||
|
||||
6.1 (May 2025)
|
||||
- Mostly some bug-fixes (#216, #217, #300)
|
||||
@@ -276,3 +287,43 @@ Bypassing and disabling nodes works, but with one catch. If you have a UE nodes
|
||||
|||
|
||||
|
||||
This is unlikely to be fixed, but should be fairly easy to avoid!
|
||||
|
||||
|
||||
---
|
||||
# Some older information
|
||||
|
||||
# Update for ComfyUI front end 1.16 and above
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
So there is a new mechanism for this. Here's a picture of a node that I have right-clicked:
|
||||
|
||||

|
||||
|
||||
Things to notice:
|
||||
|
||||
- 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.
|
||||
- Select a widget to toggle its value
|
||||
- Next to the widget, where an input would be, the connectable widgets have a graphical indication of their state.
|
||||
- The smaller dark circle (next to steps) indicate a connectable node which is not connected (widget value will be used)
|
||||
- The light circle (next to cfg) and greying out the widget indicates that a UE node will provide the value
|
||||
- No icon (all the other widgets) indicates that they are not UE connectable
|
||||
- Other inputs still have the indications of normal connection or UE connection (here model is a UE connection)
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
Hopefully old workflows will be automatically converted when you load them. Hopefully.
|
||||
|
||||
## Naming widgets
|
||||
|
||||
The `Prompts Everywhere`, `Seed Everywhere`, and `Anything Everywhere?` all make use of the name of an input. On some occasions this might mean you want to rename an input.
|
||||
|
||||
For an input that can't be a widget, that's trivial - right click on the input dot and select `Rename Slot`. The name will be updated, and the new name displayed next to the dot.
|
||||
|
||||
If an input *can* be a widget, you can rename it in the same way, but the new name *does not get displayed*. The name has changed, and the UE nodes will use the new name, but the old name is still shown. I consider this to be a bug in the Comfy front-end, and have raised an issue [here](https://github.com/Comfy-Org/ComfyUI_frontend/issues/3654). Unfortunately it appears that Comfy UI is going to remove the ability to rename a widget entirely, so best to avoid using widget input names if at all possible.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
from .use_everywhere import SeedEverywhere, AnythingEverywherePrompts
|
||||
|
||||
UE_VERSION = "6.2.2"
|
||||
UE_VERSION = "7.0"
|
||||
|
||||
NODE_CLASS_MAPPINGS = { "Seed Everywhere": SeedEverywhere }
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { create } from "./use_everywhere_utilities.js"
|
||||
import { settingsCache } from "./use_everywhere_cache.js"
|
||||
|
||||
|
||||
export class FloatingWindow extends HTMLDivElement {
|
||||
constructor() {
|
||||
super()
|
||||
this.classList.add('ue_editor')
|
||||
this.header = create('div', 'ue_editor_header', this)
|
||||
this.body = create('div', 'ue_editor_body', this)
|
||||
this.footer = create('div', 'ue_editor_footer', this, {innerText:"Close"})
|
||||
|
||||
this.header.addEventListener('mousedown',this.start_dragging.bind(this))
|
||||
this.addEventListener('mousemove', this.mousemovedover.bind(this))
|
||||
document.addEventListener('mousemove', this.mousemoved.bind(this))
|
||||
document.addEventListener('mouseup',this.stop_dragging.bind(this))
|
||||
document.addEventListener('mouseleave',this.stop_dragging.bind(this))
|
||||
this.footer.addEventListener('click', this.hide.bind(this))
|
||||
|
||||
this.dragging = false
|
||||
this.hide()
|
||||
document.body.append(this)
|
||||
}
|
||||
|
||||
show() {
|
||||
this.style.display = 'flex';
|
||||
const tt = document.getElementById('ue_tooltip')
|
||||
if (tt) tt.style.display = 'none'
|
||||
this.showing = true
|
||||
}
|
||||
hide() {
|
||||
this.style.display = 'none'
|
||||
this.showing = false
|
||||
}
|
||||
set_title(title) {
|
||||
this.header.innerText = title
|
||||
}
|
||||
set_body(element) {
|
||||
this.body.innerHTML = ""
|
||||
this.body.appendChild(element)
|
||||
}
|
||||
|
||||
maybe_move_to(x,y) {
|
||||
if (this.position && settingsCache.getSettingValue("Use Everywhere.Graphics.preserve edit window position")) return
|
||||
this.move_to(x,y)
|
||||
}
|
||||
|
||||
move_to(x,y) {
|
||||
this.position = {x:x,y:y}
|
||||
this.style.left = `${this.position.x}px`
|
||||
this.style.top = `${this.position.y}px`
|
||||
}
|
||||
|
||||
swallow(e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
start_dragging(e) {
|
||||
this.dragging = true
|
||||
this.swallow(e)
|
||||
}
|
||||
|
||||
stop_dragging(e) {
|
||||
this.dragging = false
|
||||
}
|
||||
|
||||
mousemovedover(e) {
|
||||
this.mousemoved(e)
|
||||
this.swallow(e)
|
||||
}
|
||||
|
||||
mousemoved(e) {
|
||||
if (this.dragging) this.move_to( this.position.x + e.movementX , this.position.y + e.movementY )
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
customElements.define('ue-floating', FloatingWindow, {extends: 'div'})
|
||||
export const edit_window = new FloatingWindow()
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
|
||||
const FUNCTIONAL = {
|
||||
seed_input_regex : "seed|随机种",
|
||||
prompt_regex : "(_|\\b)pos(itive|_|\\b)|^prompt|正面",
|
||||
negative_regex : "(_|\\b)neg(ative|_|\\b)|负面",
|
||||
}
|
||||
|
||||
const DISPLAY = {
|
||||
title : "title",
|
||||
input : "input",
|
||||
group : "group",
|
||||
Group : "Group",
|
||||
Color : "Colour",
|
||||
Priority : "Priority",
|
||||
prompt_regexes_text : "Prompt Everywhere",
|
||||
"No restrictions" : "No restrictions",
|
||||
"Send only within group" : "Send only within group",
|
||||
"Send only not within group" : "Send only not within group",
|
||||
"Send only to same color" : "Send only to same color",
|
||||
"Send only to different color" : "Send only to different color",
|
||||
}
|
||||
|
||||
export const GROUP_RESTRICTION_OPTIONS = [i18n("No restrictions"), i18n("Send only within group"), i18n("Send only not within group")]
|
||||
export const COLOR_RESTRICTION_OPTIONS = [i18n("No restrictions"), i18n("Send only to same color"), i18n("Send only to different color")]
|
||||
|
||||
const toTitleCase = (phrase) => {
|
||||
return phrase
|
||||
.toLowerCase()
|
||||
.split(' ')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
export function i18n(v, extras) {
|
||||
var r = DISPLAY[v] || v
|
||||
if (extras?.titlecase) r = toTitleCase(r)
|
||||
return r
|
||||
}
|
||||
|
||||
export function default_regex(v) {
|
||||
return FUNCTIONAL[v]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
import { any_restrictions, describe_restrictions } from "./ue_properties.js";
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { create } from "./use_everywhere_utilities.js";
|
||||
import { edit_window } from "./floating_window.js";
|
||||
import { settingsCache } from "./use_everywhere_cache.js";
|
||||
|
||||
const HOVERTIME = 500
|
||||
var hover_node_id = null
|
||||
var mouse_pos = [0,0]
|
||||
|
||||
const ue_tooltip_element = create('span', 'ue_tooltip', document.body, {id:'ue_tooltip'})
|
||||
|
||||
function show_tooltip(node) {
|
||||
if (!node) return
|
||||
ue_tooltip_element.style.display = "block"
|
||||
ue_tooltip_element.style.left = `${mouse_pos.x+10}px`
|
||||
ue_tooltip_element.style.top = `${mouse_pos.y+5}px`
|
||||
ue_tooltip_element.innerHTML = ""
|
||||
ue_tooltip_element.appendChild(describe_restrictions(node))
|
||||
ue_tooltip_element.showing = node.id
|
||||
}
|
||||
|
||||
function show_on_hover() {
|
||||
if (mouse_pos.x==app.canvas.mouse[0] && mouse_pos.y==app.canvas.mouse[1]) {
|
||||
show_tooltip(app.canvas.node_over)
|
||||
} else if (app.canvas.node_over?.id == hover_node_id) {
|
||||
mouse_pos = { x:app.canvas.mouse[0], y:app.canvas.mouse[1] }
|
||||
setTimeout(show_on_hover, HOVERTIME)
|
||||
}
|
||||
}
|
||||
|
||||
function hide_tooltip() {
|
||||
var ue_tooltip_element = document.getElementById('ue_tooltip')
|
||||
if (ue_tooltip_element) {
|
||||
ue_tooltip_element.style.display = "none"
|
||||
ue_tooltip_element.showing = null
|
||||
}
|
||||
}
|
||||
|
||||
export function maybe_show_tooltip() {
|
||||
if (!settingsCache.getSettingValue('Use Everywhere.Graphics.tooltips')) return
|
||||
|
||||
const node = app.canvas?.node_over
|
||||
hover_node_id = node?.id
|
||||
|
||||
if (!node) return hide_tooltip()
|
||||
if (edit_window.showing) return hide_tooltip()
|
||||
if (!(node.IS_UE && any_restrictions(node))) return hide_tooltip()
|
||||
|
||||
if (ue_tooltip_element.showing) return
|
||||
|
||||
mouse_pos = { x:app.canvas.mouse[0], y:app.canvas.mouse[1] }
|
||||
setTimeout(show_on_hover, HOVERTIME)
|
||||
}
|
||||
@@ -1,43 +1,61 @@
|
||||
@layer ue {
|
||||
#ue_tooltip {
|
||||
z-index : 9000;
|
||||
border: thin solid gray;
|
||||
color:black;
|
||||
position: absolute;
|
||||
padding: 4px;
|
||||
}
|
||||
#ue_tooltip table {
|
||||
border-collapse: collapse;
|
||||
|
||||
.litegraph label.ueprompt {
|
||||
padding:0px;
|
||||
font-size: 12px;
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
box-shadow: none !important;
|
||||
margin:0px;
|
||||
height:30px;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
}
|
||||
#ue_tooltip tr:nth-child(odd) {
|
||||
background-color: rgba(234, 255, 234, 0.67);
|
||||
}
|
||||
#ue_tooltip tr:nth-child(even) {
|
||||
background-color: rgba(255, 234, 234, 0.67);
|
||||
}
|
||||
#ue_tooltip th {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.litegraph span.ueprompttext {
|
||||
margin: 0px;
|
||||
min-width: 30px;
|
||||
font-family: Arial, sans-serif;
|
||||
color: var(--descrip-text);
|
||||
text-align: right;
|
||||
padding: 2px 2px 4px 0px;
|
||||
background-color: inherit;
|
||||
}
|
||||
.ue_editor {
|
||||
z-index : 9000;
|
||||
border: thin solid gray;
|
||||
color:white;
|
||||
position: absolute;
|
||||
padding: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: rgb(45 24 24 / 80%);
|
||||
}
|
||||
|
||||
.litegraph span.uepromptspan {
|
||||
margin: 0px;
|
||||
.ue_editor_header {
|
||||
width: 100%;
|
||||
padding-left:12px;
|
||||
background-color: inherit;
|
||||
}
|
||||
text-align: center;
|
||||
background-color: rgb(45 24 24 / 80%);
|
||||
background-size: 100% 14px;
|
||||
background-image: repeating-linear-gradient(0deg, #636363, black 1px, rgb(45 24 24 / 80%) 1px, rgb(45 24 24 / 80%) 2px);
|
||||
padding-top: 4px;
|
||||
|
||||
.litegraph input.uepromptinput {
|
||||
padding: 0 0 0 6px;
|
||||
font-size: 12px;
|
||||
border-radius: 12px;
|
||||
border: 2px solid var(--border-color);
|
||||
color: var(--input-text);
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
ue_hide {
|
||||
display: none;
|
||||
}
|
||||
.ue_editor_body {
|
||||
padding: 0px 4px;
|
||||
}
|
||||
.ue_editor_body th {
|
||||
text-align: right;
|
||||
}
|
||||
.ue_editor_body th::after {
|
||||
content: ':';
|
||||
}
|
||||
|
||||
.ue_editor_footer {
|
||||
margin: 8px;
|
||||
border: thin solid white;
|
||||
padding: 2px 30px;
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import { VERSION, version_at_least, is_UEnode, graphConverter, fix_inputs, create } from "./use_everywhere_utilities.js"
|
||||
import { i18n, default_regex, GROUP_RESTRICTION_OPTIONS, COLOR_RESTRICTION_OPTIONS } from "./i18n.js";
|
||||
|
||||
const ALL_REGEXES = ['title', 'input', 'prompt', 'negative', 'group']
|
||||
|
||||
function any_regex_restrictions(node) {
|
||||
if (!node.properties.ue_properties) return false
|
||||
var restricted = false
|
||||
ALL_REGEXES.forEach((r)=>{
|
||||
const reg = node.properties.ue_properties[`${r}_regex`]
|
||||
restricted = restricted || (reg && reg.length>0)
|
||||
})
|
||||
return restricted
|
||||
}
|
||||
|
||||
export function any_restrictions(node) {
|
||||
return (
|
||||
node.properties.ue_properties.group_restricted ||
|
||||
node.properties.ue_properties.color_restricted ||
|
||||
node.properties.ue_properties.priority ||
|
||||
any_regex_restrictions(node)
|
||||
)
|
||||
}
|
||||
|
||||
export function describe_restrictions(node) {
|
||||
const statements = []
|
||||
if (node.properties.ue_properties) {
|
||||
ALL_REGEXES.forEach((r)=>{
|
||||
const reg = node.properties.ue_properties[`${r}_regex`]
|
||||
if (reg && reg.length>0) statements.push([`${i18n(r)} regex`, reg])
|
||||
})
|
||||
if (node.properties.ue_properties.group_restricted) statements.push(['group',i18n(GROUP_RESTRICTION_OPTIONS[node.properties.ue_properties.group_restricted])])
|
||||
if (node.properties.ue_properties.color_restricted) statements.push(['color',i18n(COLOR_RESTRICTION_OPTIONS[node.properties.ue_properties.color_restricted])])
|
||||
}
|
||||
const table = create('table')
|
||||
statements.forEach((s)=>{
|
||||
const row = create('tr', null, table)
|
||||
create('th', null, row, {innerText:`${i18n(s[0], {titlecase:true})}:`})
|
||||
create('td', null, row, {innerText:s[1]})
|
||||
})
|
||||
return table
|
||||
}
|
||||
|
||||
export function default_priority(node) {
|
||||
var p = 10
|
||||
if (node.type === "Seed Everywhere" || node.type === "Prompts Everywhere") p += 10
|
||||
if (any_regex_restrictions(node)) p += 20
|
||||
if (node.properties.ue_properties.group_restricted > 0) p += 3
|
||||
if (node.properties.ue_properties.color_restricted > 0) p += 6
|
||||
return p
|
||||
}
|
||||
|
||||
const DEFAULT_PROPERTIES = {
|
||||
version : VERSION,
|
||||
group_restricted : 0,
|
||||
color_restricted : 0,
|
||||
widget_ue_connectable : {},
|
||||
title_regex : null,
|
||||
input_regex : null,
|
||||
group_regex : null,
|
||||
priority : undefined,
|
||||
}
|
||||
|
||||
/*
|
||||
Called whenever any node is created.
|
||||
|
||||
If the graph is still being configured, then that means the node is being created as part of a load.
|
||||
In that case the properties need to be set later, in setup_ue_properties_onload
|
||||
*/
|
||||
export function setup_ue_properties_oncreate(node) {
|
||||
node.IS_UE = is_UEnode(node)
|
||||
if (graphConverter.graph_being_configured) return
|
||||
if (node.IS_UE) {
|
||||
if (!node.properties) node.properties = {}
|
||||
node.properties.ue_properties = {...DEFAULT_PROPERTIES}
|
||||
convert_node_types(node)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Convert node properties when loaded.
|
||||
*/
|
||||
export function setup_ue_properties_onload(node) {
|
||||
if (node.IS_UE) {
|
||||
if ( !version_at_least(node.properties?.ue_properties?.version, "7.0") ) {
|
||||
// convert a pre 7.0 UE node
|
||||
node.properties.ue_properties = {
|
||||
version : VERSION,
|
||||
group_restricted : node.properties.group_restricted,
|
||||
color_restricted : node.properties.color_restricted,
|
||||
widget_ue_connectable : node.properties.widget_ue_connectable,
|
||||
title_regex : node.widgets_values?.[0],
|
||||
input_regex : node.widgets_values?.[1],
|
||||
group_regex : node.widgets_values?.[2],
|
||||
priority : undefined,
|
||||
}
|
||||
delete node.properties.group_restricted
|
||||
delete node.properties.color_restricted
|
||||
delete node.properties.widget_ue_connectable
|
||||
}
|
||||
convert_node_types(node)
|
||||
}
|
||||
}
|
||||
|
||||
function convert_node_types(node) {
|
||||
if (!node.IS_UE) return
|
||||
|
||||
if (node.type=="Anything Everywhere?") {
|
||||
node.widgets.forEach((w)=>{w.hidden=true})
|
||||
if (node.title=="Anything Everywhere?") node.title = "Anything Everywhere"
|
||||
node.type = "Anything Everywhere"
|
||||
} else if (node.type=="Anything Everywhere3") {
|
||||
if (node.title=="Anything Everywhere3") node.title = "Anything Everywhere"
|
||||
node.type = "Anything Everywhere"
|
||||
} else if (node.type=="Seed Everywhere") {
|
||||
node.properties.ue_properties.fixed_inputs = true
|
||||
node.properties.ue_properties.seed_inputs = true
|
||||
node.properties.ue_properties.input_regex = node.properties.ue_properties.input_regex || default_regex('seed_input_regex')
|
||||
} else if (node.type=="Prompts Everywhere") {
|
||||
node.properties.ue_properties.fixed_inputs = true
|
||||
node.properties.ue_properties.prompt_regexes = true
|
||||
}
|
||||
|
||||
ALL_REGEXES.forEach((r)=>{
|
||||
const rname = `${r}_regex`
|
||||
if (node.properties.ue_properties[rname]==".*") node.properties.ue_properties[rname] = undefined
|
||||
})
|
||||
|
||||
fix_inputs(node)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { LinkRenderController } from "./use_everywhere_ui.js";
|
||||
import { i18n, default_regex } from "./i18n.js";
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { default_priority } from "./ue_properties.js";
|
||||
import { GROUP_RESTRICTION_OPTIONS, COLOR_RESTRICTION_OPTIONS } from "./i18n.js";
|
||||
import { edit_window } from "./floating_window.js";
|
||||
|
||||
const REGEXES = ['title', 'input', 'group']
|
||||
const P_REGEXES = ['prompt', 'negative']
|
||||
|
||||
export function edit_restrictions(a,b,c,d, node) {
|
||||
edit_window.set_body(create_editor_html(node))
|
||||
edit_window.set_title(`Restrictions for node #${node.id}`)
|
||||
edit_window.maybe_move_to(app.canvas.mouse[0]+10, app.canvas.mouse[1]+10)
|
||||
edit_window.show()
|
||||
}
|
||||
|
||||
function add_row(table, header) {
|
||||
const row = document.createElement('tr')
|
||||
table.appendChild(row)
|
||||
const header_elem = document.createElement('th')
|
||||
header_elem.innerText = header
|
||||
row.appendChild(header_elem)
|
||||
return row
|
||||
}
|
||||
|
||||
function add_cell(row, cell) {
|
||||
const td = document.createElement('td')
|
||||
row.appendChild(td)
|
||||
td.appendChild(cell)
|
||||
}
|
||||
|
||||
function changed(node, property, value) {
|
||||
node.properties.ue_properties[property] = value
|
||||
|
||||
if (!node.properties.ue_properties.priority) {
|
||||
document.getElementById('priority_value').value = `${default_priority(node)}`
|
||||
}
|
||||
|
||||
if (node.properties.ue_properties.prompt_regexes) {
|
||||
for (var i=0; i<2; i++) {
|
||||
if (!node.properties.ue_properties[`${P_REGEXES[i]}_regex`]) {
|
||||
document.getElementById(`${P_REGEXES[i]}_regex_value`).value = default_regex(`${P_REGEXES[i]}_regex`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const elem = document.getElementById(`${property}_value`)
|
||||
if (elem) elem.style.opacity = (value) ? "1" : "0.5"
|
||||
|
||||
LinkRenderController.instance().mark_link_list_outdated()
|
||||
app.canvas.setDirty(true,true)
|
||||
}
|
||||
|
||||
function create_editor_html(node) {
|
||||
const table = document.createElement('table')
|
||||
|
||||
for (var i=0; i<=2; i++) {
|
||||
|
||||
if (i==1 && node.properties.ue_properties.prompt_regexes) {
|
||||
for (var j=0; j<2; j++) {
|
||||
const name = P_REGEXES[j]
|
||||
const row = add_row(table, `${i18n(name)} regex`)
|
||||
const input = document.createElement('input')
|
||||
input.value = node.properties.ue_properties[`${name}_regex`] || default_regex(`${name}_regex`)
|
||||
if (!node.properties.ue_properties[`${name}_regex`]) input.style.opacity = 0.5
|
||||
input.id = `${name}_regex_value`
|
||||
input.style.width = '250px'
|
||||
input.addEventListener('input', ()=>{ changed(node, `${name}_regex`, input.value)})
|
||||
add_cell(row,input)
|
||||
}
|
||||
} else {
|
||||
const name = REGEXES[i]
|
||||
const row = add_row(table, `${i18n(name)} regex`)
|
||||
const input = document.createElement('input')
|
||||
input.value = node.properties.ue_properties[`${name}_regex`] || ''
|
||||
input.addEventListener('input', ()=>{ changed(node, `${name}_regex`, input.value)})
|
||||
add_cell(row,input)
|
||||
}
|
||||
}
|
||||
|
||||
const gr_row = add_row(table, i18n("Group"))
|
||||
const gr_select = document.createElement('select')
|
||||
add_cell(gr_row,gr_select)
|
||||
GROUP_RESTRICTION_OPTIONS.forEach((gro, i)=>{
|
||||
const gr_option = document.createElement('option')
|
||||
gr_option.value = `${i}`
|
||||
gr_option.innerText = gro
|
||||
gr_select.appendChild(gr_option)
|
||||
})
|
||||
gr_select.value = `${node.properties.ue_properties.group_restricted || 0}`
|
||||
gr_select.addEventListener('input', ()=>{ changed(node, `group_restricted`, parseInt(gr_select.value))})
|
||||
|
||||
const col_row = add_row(table, i18n("Color"))
|
||||
const col_select = document.createElement('select')
|
||||
add_cell(col_row,col_select)
|
||||
COLOR_RESTRICTION_OPTIONS.forEach((cro, i)=>{
|
||||
const col_option = document.createElement('option')
|
||||
col_option.value = `${i}`
|
||||
col_option.innerText = cro
|
||||
col_select.appendChild(col_option)
|
||||
})
|
||||
col_select.value = `${node.properties.ue_properties.color_restricted || 0}`
|
||||
col_select.addEventListener('input', ()=>{ changed(node, `color_restricted`, parseInt(col_select.value))})
|
||||
|
||||
const priority_row = add_row(table, i18n("Priority"))
|
||||
const priority_edit = document.createElement("input")
|
||||
priority_edit.value = `${node.properties.ue_properties.priority || default_priority(node)}`
|
||||
priority_edit.addEventListener('input', ()=>{
|
||||
const p = parseInt(priority_edit.value)
|
||||
if (p) changed(node, `priority`, p)
|
||||
if (priority_edit.value=='') changed(node, `priority`, undefined)
|
||||
})
|
||||
priority_edit.id = 'priority_value'
|
||||
if (!node.properties.ue_properties.priority) priority_edit.style.opacity = 0.5
|
||||
add_cell(priority_row,priority_edit)
|
||||
return table
|
||||
}
|
||||
+118
-68
@@ -1,14 +1,17 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
import { is_UEnode, is_helper, inject, Logger, get_real_node, defineProperty, graphConverter } from "./use_everywhere_utilities.js";
|
||||
import { is_UEnode, is_helper, inject, Logger, get_real_node, defineProperty, graphConverter, fix_inputs, create } from "./use_everywhere_utilities.js";
|
||||
import { update_input_label, indicate_restriction } from "./use_everywhere_ui.js";
|
||||
import { LinkRenderController } from "./use_everywhere_ui.js";
|
||||
import { GraphAnalyser } from "./use_everywhere_graph_analysis.js";
|
||||
import { node_menu_settings, canvas_menu_settings, non_ue_menu_settings, SETTINGS } from "./use_everywhere_settings.js";
|
||||
import { canvas_menu_settings, SETTINGS, add_extra_menu_items } from "./use_everywhere_settings.js";
|
||||
import { add_debug } from "./ue_debug.js";
|
||||
import { settingsCache } from "./use_everywhere_cache.js";
|
||||
import { convert_to_links } from "./use_everywhere_apply.js";
|
||||
import { get_subgraph_input_type, link_is_from_subgraph_input, node_graph, visible_graph } from "./use_everywhere_subgraph_utils.js";
|
||||
import { any_restrictions, setup_ue_properties_oncreate, setup_ue_properties_onload } from "./ue_properties.js";
|
||||
import { edit_restrictions } from "./ue_properties_editor.js";
|
||||
|
||||
/*
|
||||
The ui component that looks after the link rendering
|
||||
@@ -30,6 +33,20 @@ function inject_outdating_into_object_method(object, methodname, tracetext) {
|
||||
if (object) inject(object, methodname, tracetext, linkRenderController.mark_link_list_outdated, linkRenderController);
|
||||
}
|
||||
|
||||
class Deferred {
|
||||
constructor() { this.deferred_actions = [] }
|
||||
push(x) { this.deferred_actions.push(x) } // add action of the form: { fn:function, args:array }
|
||||
execute() {
|
||||
while (this.deferred_actions.length>0) {
|
||||
const action = this.deferred_actions.pop()
|
||||
try { action?.fn(...action?.args) }
|
||||
catch (e) { Logger.log_error(e) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deferred_actions = new Deferred()
|
||||
|
||||
app.registerExtension({
|
||||
name: "cg.customnodes.use_everywhere",
|
||||
settings: SETTINGS,
|
||||
@@ -42,60 +59,78 @@ app.registerExtension({
|
||||
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
||||
nodeType.prototype.onConnectionsChange = function (side,slot,connect,link_info,output) {
|
||||
if (this.IS_UE && side==1) { // side 1 is input
|
||||
if (this.type=="Anything Everywhere?" && slot!=0) {
|
||||
// don't do anything for the regexs
|
||||
} else {
|
||||
const type = (connect && link_info) ? get_real_node(link_info?.origin_id)?.outputs[link_info?.origin_slot]?.type : undefined;
|
||||
this.input_type[slot] = type;
|
||||
if (link_info) link_info.type = type ? type : "*";
|
||||
update_input_label(this, slot, app);
|
||||
var type = '*'
|
||||
if (connect && link_info) {
|
||||
var connected_type
|
||||
if (link_is_from_subgraph_input(link_info)) { // input slot of subgraph
|
||||
connected_type = get_subgraph_input_type(node_graph(this), link_info.origin_slot)
|
||||
} else {
|
||||
connected_type = get_real_node(link_info.origin_id, node_graph(this))?.outputs[link_info.origin_slot]?.type
|
||||
}
|
||||
if (connected_type) type = connected_type;
|
||||
};
|
||||
this.inputs[slot].type = type;
|
||||
if (link_info) link_info.type = type
|
||||
update_input_label(this, slot, app);
|
||||
if (!graphConverter.graph_being_configured) {
|
||||
// do the fix at the end of graph change
|
||||
deferred_actions.push( { fn:fix_inputs, args:[this,]} )
|
||||
// disconnecting doesn't trigger graphChange call?
|
||||
setTimeout(deferred_actions.execute.bind(deferred_actions), 100)
|
||||
}
|
||||
}
|
||||
linkRenderController.mark_link_list_outdated();
|
||||
onConnectionsChange?.apply(this, arguments);
|
||||
};
|
||||
|
||||
/*
|
||||
Reject duplicated inputs for now
|
||||
*/
|
||||
if (is_UEnode(nodeType)) {
|
||||
const onConnectInput = nodeType.prototype.onConnectInput
|
||||
nodeType.prototype.onConnectInput = function (index, type) {
|
||||
if (!this.properties.ue_properties.fixed_inputs) {
|
||||
if (this.inputs.find((i, j)=>(i.type==type && j!=index))) return false
|
||||
}
|
||||
return onConnectInput?.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Extra menu options are the node right click menu.
|
||||
We add to this list, and also insert a link list outdate to everything.
|
||||
*/
|
||||
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
||||
nodeType.prototype.getExtraMenuOptions = function(_, options) {
|
||||
getExtraMenuOptions?.apply(this, arguments);
|
||||
if (is_UEnode(this)) {
|
||||
node_menu_settings(options, this);
|
||||
} else {
|
||||
non_ue_menu_settings(options, this);
|
||||
add_extra_menu_items(nodeType.prototype, inject_outdating_into_object_method)
|
||||
|
||||
if (is_UEnode(nodeType)) {
|
||||
const original_onDrawTitleBar = nodeType.prototype.onDrawTitleBar;
|
||||
nodeType.prototype.onDrawTitleBar = function(ctx, title_height) {
|
||||
original_onDrawTitleBar?.apply(this, arguments);
|
||||
if (any_restrictions(this)) indicate_restriction(ctx, title_height);
|
||||
}
|
||||
inject_outdating_into_objects(options,'callback',`menu option on ${this.id}`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
When a UE node is created, we set the group and color restriction properties.
|
||||
We also create pseudo-widgets for all the inputs so that they can be searched
|
||||
and to avoid other code throwing errors.
|
||||
*/
|
||||
if (is_UEnode(nodeType)) {
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
|
||||
if (!this.properties) this.properties = {}
|
||||
this.properties.group_restricted = 0;
|
||||
this.properties.color_restricted = 0;
|
||||
/*if (!this.properties) this.properties = {}
|
||||
if (this.inputs) {
|
||||
if (!this.widgets) this.widgets = [];
|
||||
for (const input of this.inputs) {
|
||||
if (input.widget && !this.widgets.find((w) => w.name === input.widget.name)) this.widgets.push(input.widget)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
Logger.log_detail(`Node ${this.id} created`)
|
||||
setup_ue_properties_oncreate(this)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async nodeCreated(node) {
|
||||
// TODO see if we still need this
|
||||
if (!node.__mode) {
|
||||
node.__mode = node.mode
|
||||
defineProperty(node, "mode", {
|
||||
@@ -103,42 +138,16 @@ app.registerExtension({
|
||||
set: (v)=>{node.__mode = v; node.afterChangeMade?.('mode', v);}
|
||||
})
|
||||
}
|
||||
if (!node.__bgcolor) {
|
||||
node.__bgcolor = node.bgcolor
|
||||
defineProperty(node,"bgcolor", {
|
||||
get: ( )=>{return node.__bgcolor},
|
||||
set: (v)=>{node.__bgcolor = v; node.afterChangeMade?.('bgcolor', v);}
|
||||
})
|
||||
}
|
||||
const acm = node.afterChangeMade
|
||||
|
||||
const original_afterChangeMade = node.afterChangeMade
|
||||
node.afterChangeMade = (p, v) => {
|
||||
acm?.(p,v)
|
||||
//if (p==='bgcolor') {
|
||||
// if (node._being_drawn) return; // the bgcolor gets set by the built in node draw...
|
||||
// linkRenderController.mark_link_list_outdated();
|
||||
//}
|
||||
original_afterChangeMade?.(p,v)
|
||||
if (p==='mode') {
|
||||
linkRenderController.mark_link_list_outdated();
|
||||
node.widgets?.forEach((widget) => {widget.onModeChange?.(v)}); // no idea why I have this?
|
||||
}
|
||||
}
|
||||
|
||||
node.IS_UE = is_UEnode(node);
|
||||
if (node.IS_UE) {
|
||||
node.input_type = [undefined, undefined, undefined]; // for dynamic input types
|
||||
|
||||
// If a widget on a UE node is edited, link list is dirty
|
||||
inject_outdating_into_objects(node.widgets,'callback',`widget callback on ${node.id}`);
|
||||
|
||||
// draw the indication of group restrictions
|
||||
const original_onDrawTitleBar = node.onDrawTitleBar;
|
||||
node.onDrawTitleBar = function(ctx, title_height) {
|
||||
original_onDrawTitleBar?.apply(this, arguments);
|
||||
if (node.properties.group_restricted || node.properties.color_restricted) indicate_restriction(ctx, title_height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (is_helper(node)) { // editing a helper node makes the list dirty
|
||||
inject_outdating_into_objects(node.widgets,'callback',`widget callback on ${this.id}`);
|
||||
}
|
||||
@@ -146,7 +155,8 @@ app.registerExtension({
|
||||
// removing a node makes the list dirty
|
||||
inject_outdating_into_object_method(node, 'onRemoved', `node ${node.id} removed`)
|
||||
|
||||
graphConverter.on_node_created(node)
|
||||
// check if the extra menu_items have been added (catch subgraph niode creation)
|
||||
add_extra_menu_items(node, inject_outdating_into_object_method)
|
||||
|
||||
// creating a node makes the link list dirty - but give the system a moment to finish
|
||||
setTimeout( ()=>{linkRenderController.mark_link_list_outdated()}, 100 );
|
||||
@@ -154,11 +164,22 @@ app.registerExtension({
|
||||
|
||||
// When a graph node is loaded convert it if needed
|
||||
loadedGraphNode(node) {
|
||||
if (graphConverter.running_116_plus()) { graphConverter.convert_if_pre_116(node); }
|
||||
if (graphConverter.running_116_plus()) {
|
||||
graphConverter.convert_if_pre_116(node);
|
||||
if (node.isSubgraphNode?.()) {
|
||||
node.subgraph.nodes.forEach((n) => {
|
||||
graphConverter.convert_if_pre_116(n);
|
||||
})
|
||||
}
|
||||
}
|
||||
setup_ue_properties_onload(node)
|
||||
},
|
||||
|
||||
async setup() {
|
||||
|
||||
create('link', null, document.getElementsByTagName('HEAD')[0],
|
||||
{'rel':'stylesheet', 'type':'text/css', 'href': new URL("./ue.css", import.meta.url).href } )
|
||||
|
||||
api.addEventListener("status", ({detail}) => {
|
||||
if (linkRenderController) linkRenderController.note_queue_size(detail ? detail.exec_info.queue_remaining : 0)
|
||||
});
|
||||
@@ -184,7 +205,6 @@ app.registerExtension({
|
||||
*/
|
||||
const original_drawNode = LGraphCanvas.prototype.drawNode;
|
||||
LGraphCanvas.prototype.drawNode = function(node, ctx) {
|
||||
|
||||
try {
|
||||
linkRenderController.pause('drawNode')
|
||||
const v = original_drawNode.apply(this, arguments);
|
||||
@@ -268,19 +288,26 @@ app.registerExtension({
|
||||
graphAnalyser = GraphAnalyser.instance();
|
||||
linkRenderController = LinkRenderController.instance(graphAnalyser);
|
||||
|
||||
const original_afterChange = app.graph.afterChange
|
||||
app.graph.afterChange = function () {
|
||||
deferred_actions.execute()
|
||||
original_afterChange?.apply(this, arguments)
|
||||
}
|
||||
|
||||
|
||||
var prompt_being_queued = false;
|
||||
|
||||
const original_graphToPrompt = app.graphToPrompt;
|
||||
app.graphToPrompt = async function () {
|
||||
if (prompt_being_queued) {
|
||||
return await graphAnalyser.graph_to_prompt( graphAnalyser.analyse_graph(true, true) );
|
||||
return await graphAnalyser.graph_to_prompt( );
|
||||
} else {
|
||||
return await original_graphToPrompt.apply(app, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
app.ue_modified_prompt = async function () {
|
||||
return await graphAnalyser.graph_to_prompt( graphAnalyser.analyse_graph(true, true) );
|
||||
return await graphAnalyser.graph_to_prompt();
|
||||
}
|
||||
|
||||
const original_queuePrompt = app.queuePrompt;
|
||||
@@ -299,6 +326,21 @@ app.registerExtension({
|
||||
set: (v)=>{app.canvas.__node_over = v; linkRenderController.node_over_changed(v)}
|
||||
} )
|
||||
|
||||
app.canvas.canvas.addEventListener('litegraph:set-graph', ()=>{
|
||||
linkRenderController.mark_link_list_outdated()
|
||||
setTimeout(()=>{app.canvas.setDirty(true,true)},200)
|
||||
})
|
||||
|
||||
app.canvas.canvas.addEventListener('litegraph:canvas', (e)=>{
|
||||
if (e?.detail?.subType=='node-double-click') {
|
||||
const node = e.detail.node
|
||||
if (node.IS_UE) {
|
||||
if (app.ui.settings.getSettingValue('Comfy.Node.DoubleClickTitleToEdit') && e.detail.originalEvent.canvasY<node.pos[1]) return
|
||||
edit_restrictions(null, null, null, null, node)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (false) add_debug();
|
||||
|
||||
const export_api_label = Array.from(document.getElementsByClassName('p-menubar-item-label')).find((e)=>e.innerText=='Export (API)')
|
||||
@@ -320,11 +362,18 @@ app.registerExtension({
|
||||
|
||||
const original_subgraph = app.graph.convertToSubgraph
|
||||
app.graph.convertToSubgraph = function () {
|
||||
const cur_list = graphAnalyser.analyse_graph(true, true)
|
||||
const mods = convert_to_links(cur_list, -1);
|
||||
const r = original_subgraph.apply(this, arguments);
|
||||
mods.restorer()
|
||||
return r
|
||||
const ctb_was = graphAnalyser.connect_to_bypassed
|
||||
graphAnalyser.connect_to_bypassed = true
|
||||
try {
|
||||
|
||||
const cur_list = graphAnalyser.wait_to_analyse_visible_graph()
|
||||
const mods = convert_to_links(cur_list, null, visible_graph());
|
||||
const r = original_subgraph.apply(this, arguments);
|
||||
mods.restorer()
|
||||
return r
|
||||
} finally {
|
||||
graphAnalyser.connect_to_bypassed = ctb_was
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -335,7 +384,8 @@ app.registerExtension({
|
||||
},
|
||||
|
||||
afterConfigureGraph() {
|
||||
graphConverter.remove_saved_ue_links()
|
||||
graphConverter.remove_saved_ue_links_recursively(app.graph)
|
||||
//convert_old_nodes(app.graph)
|
||||
graphConverter.graph_being_configured = false
|
||||
}
|
||||
|
||||
|
||||
+73
-19
@@ -1,19 +1,20 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { display_name } from "./use_everywhere_classes.js";
|
||||
import { master_graph, node_graph } from "./use_everywhere_subgraph_utils.js";
|
||||
import { is_UEnode, get_real_node, Logger } from "./use_everywhere_utilities.js";
|
||||
|
||||
|
||||
function _convert_to_links(ue, added_links, removed_links) {
|
||||
const output_node_id = ue.output[0];
|
||||
const output_index = ue.output[1];
|
||||
const output_node = get_real_node(output_node_id);
|
||||
const output_node = get_real_node(output_node_id, ue.graph);
|
||||
Logger.log_info("Adding links for " + ue.description);
|
||||
ue.sending_to.forEach((st) => {
|
||||
const input_node_id = st.node.id;
|
||||
const input_node = get_real_node(input_node_id);
|
||||
const input_node = get_real_node(input_node_id, ue.graph);
|
||||
const input_index = st.input_index;
|
||||
if (input_node.inputs[input_index].link) {
|
||||
const llink = app.graph.links[input_node.inputs[input_index].link]
|
||||
if (input_node.inputs[input_index].link) { // why would this be happening?
|
||||
const llink = ue.graph.links[input_node.inputs[input_index].link]
|
||||
removed_links.push( {...llink} )
|
||||
}
|
||||
const new_link = output_node.connect(output_index, input_node, input_index);
|
||||
@@ -22,44 +23,97 @@ function _convert_to_links(ue, added_links, removed_links) {
|
||||
`${output_node_id}[${output_index}] -> ` +
|
||||
`${input_node_id}[${input_index}].`);
|
||||
else { // Memorize the links we are adding to remove them later
|
||||
if (added_links) added_links.push(new_link.id);
|
||||
if (added_links) {
|
||||
new_link.control_node = ue.controller;
|
||||
added_links.push(new_link);
|
||||
}
|
||||
Logger.log_info(` -> ${display_name(st.node)}, ${st.input.name} ` +
|
||||
`(${st.node.id}.${st.input_index}) (ID: ${new_link.id})`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function find_link_to_subgraph_node(id) {
|
||||
const last_node = app.graph._nodes_by_id[app.graph.last_node_id];
|
||||
const the_input = last_node.inputs.find(input => input.linkIds.includes(id));
|
||||
if (!the_input) throw new Error(`Link ID ${id} not found in inputs of most recently added node`);
|
||||
return the_input.link
|
||||
function convert_to_links(ues, control_node, graph) {
|
||||
if (control_node) {
|
||||
if (!graph) graph = node_graph(control_node)
|
||||
return _convert_graph_to_links(graph, ues, control_node.id );
|
||||
} else {
|
||||
if (!graph) graph = master_graph();
|
||||
return _convert_graph_to_links(graph, ues, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
function convert_to_links(ues, control_node_id) {
|
||||
function _convert_graph_to_links(graph, ues, control_node_id) {
|
||||
if (!ues?.ues) return {restorer:function(){}, added_links:[]}
|
||||
const added_links = []
|
||||
const removed_links = []
|
||||
ues.ues.forEach((ue)=> {
|
||||
if (control_node_id==-1 || ue.controller.id == control_node_id) _convert_to_links(ue, added_links, removed_links);
|
||||
if (control_node_id==undefined || ue.controller.id == control_node_id) _convert_to_links(ue, added_links, removed_links);
|
||||
});
|
||||
|
||||
const restorer = function() {
|
||||
added_links.forEach(id => {
|
||||
const links_within_graph_ids = new Set()
|
||||
const links_within_subgraph_ids = new Set()
|
||||
const links_into_subgraph_ids = new Set()
|
||||
const links_out_of_subgraph = new Set()
|
||||
const problem_links = new Set()
|
||||
|
||||
const new_subgraph_node = graph._nodes_by_id[graph.last_node_id];
|
||||
const new_subgraph = new_subgraph_node?.subgraph;
|
||||
|
||||
added_links.forEach(added_link => {
|
||||
var id = added_link.id
|
||||
try {
|
||||
if (app.graph.links[id]) {
|
||||
app.graph.removeLink(id); // the link still exists, so remove it
|
||||
} else {
|
||||
id = find_link_to_subgraph_node(id)
|
||||
app.graph.removeLink(id)
|
||||
if (graph.links[id]) {
|
||||
links_within_graph_ids.add(id);
|
||||
} else {
|
||||
const link_to_subgraph_node = new_subgraph_node?.inputs.find(input => input.linkIds.includes(id))?.link
|
||||
if (link_to_subgraph_node) {
|
||||
links_into_subgraph_ids.add(link_to_subgraph_node)
|
||||
} else {
|
||||
if (new_subgraph.links[id]) {
|
||||
if (new_subgraph.links[id].target_id==-20) {
|
||||
links_out_of_subgraph.add(added_link)
|
||||
} else {
|
||||
links_within_subgraph_ids.add(id)
|
||||
}
|
||||
} else problem_links.add(added_link)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
problem_links.add(added_link)
|
||||
Logger.log_error(e);
|
||||
}
|
||||
});
|
||||
|
||||
links_out_of_subgraph.forEach((llink)=>{
|
||||
const control_node_id = llink.control_node?.id
|
||||
const control_node_in_graph = graph._nodes_by_id[control_node_id]
|
||||
if (control_node_in_graph) {
|
||||
// the control node is outside, so we should disconnect anything else that is connected to the same output of the subgraph node
|
||||
new_subgraph_node.outputs[new_subgraph.links[llink.id].target_slot].links.forEach((link_id)=>{
|
||||
if (graph.links[link_id].target_id==control_node_id) {
|
||||
// leave the link to the UE node
|
||||
} else {
|
||||
// remove others
|
||||
links_within_graph_ids.add(link_id)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// control node is inside, leave the link, since we can't UE connect to the output panel
|
||||
}
|
||||
})
|
||||
|
||||
links_within_graph_ids.forEach((lid)=>{graph.removeLink(lid)})
|
||||
links_into_subgraph_ids.forEach((lid)=>{graph.removeLink(lid)})
|
||||
links_within_subgraph_ids.forEach((lid)=>{new_subgraph.removeLink(lid)})
|
||||
|
||||
if (problem_links.size>0) {
|
||||
Logger.log_problem("Failed to work out how to remove some temporary links:", problem_links)
|
||||
}
|
||||
|
||||
removed_links.forEach(llink => {
|
||||
app.graph._nodes_by_id[llink.origin_id].connect(llink.origin_slot, app.graph._nodes_by_id[llink.target_id], llink.target_slot)
|
||||
graph._nodes_by_id[llink.origin_id].connect(llink.origin_slot, graph._nodes_by_id[llink.target_id], llink.target_slot)
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
import { default_regex } from "./i18n.js";
|
||||
import { default_priority } from "./ue_properties.js";
|
||||
import { node_graph, visible_graph } from "./use_everywhere_subgraph_utils.js";
|
||||
import { nodes_in_my_group, nodes_not_in_my_group, nodes_my_color, nodes_not_my_color, nodes_in_groups_matching } from "./use_everywhere_ui.js";
|
||||
import { Logger, node_is_live, get_real_node } from "./use_everywhere_utilities.js";
|
||||
import { Logger, node_is_live, get_real_node, get_connection } from "./use_everywhere_utilities.js";
|
||||
|
||||
function display_name(node) {
|
||||
|
||||
export function display_name(node) {
|
||||
if (node?.title) return node.title;
|
||||
if (node?.type) return node.type;
|
||||
if (node?.properties['Node name for S&R']) return node.properties['Node name for S&R'];
|
||||
return "un-nameable node";
|
||||
}
|
||||
|
||||
function regex_for(node, k) {
|
||||
try {
|
||||
const w0 = node.properties.ue_properties[`${k}_regex`]
|
||||
return (w0 && w0!='.*') ? new RegExp(w0) : null;
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The UseEverywhere object represents a single 'broadcast'. It generally contains
|
||||
controller - the UE node that controls the broadcase
|
||||
@@ -16,16 +29,24 @@ The UseEverywhere object represents a single 'broadcast'. It generally contains
|
||||
output - the output that is being rebroadcast as a list (node_id, output_index)
|
||||
title_regex, input_regex - the UE? matching rules
|
||||
priority - priorty :)
|
||||
graph - the graph or subgraph
|
||||
*/
|
||||
class UseEverywhere {
|
||||
constructor() {
|
||||
this.sending_to = [];
|
||||
Object.assign(this, arguments[0]);
|
||||
if (this.priority === undefined) this.priority = 0;
|
||||
const from_node = get_real_node(this?.output[0]);
|
||||
const to_node = get_real_node(this?.controller.id);
|
||||
if (this.graph === undefined) this.graph = visible_graph();
|
||||
|
||||
const from_node = get_real_node(this?.output[0], this.graph);
|
||||
const to_node = get_real_node(this?.controller.id, this.graph)
|
||||
try {
|
||||
if (this.control_node_input_index>=0) {
|
||||
if (this.output[0]==-10) {
|
||||
this.description = `source subgraph input slot ${this?.output[1]} ` +
|
||||
`-> control "${display_name(to_node)}", ${to_node.inputs[this?.control_node_input_index].name} (${this?.controller.id}.${this?.control_node_input_index}) ` +
|
||||
`"${this.type}" <- (priority ${this.priority})`;
|
||||
}
|
||||
else if (this.control_node_input_index>=0) {
|
||||
this.description = `source "${display_name(from_node)}", ${from_node.outputs[this?.output[1]].name} (${this?.output[0]}.${this?.output[1]}) ` +
|
||||
`-> control "${display_name(to_node)}", ${to_node.inputs[this?.control_node_input_index].name} (${this?.controller.id}.${this?.control_node_input_index}) ` +
|
||||
`"${this.type}" <- (priority ${this.priority})`;
|
||||
@@ -37,7 +58,6 @@ class UseEverywhere {
|
||||
// for breakpointing
|
||||
throw e;
|
||||
}
|
||||
// this.description = `source ${this?.output[0]}.${this?.output[1]} -> control ${this?.controller.id}.${this?.control_node_input_index} "${this.type}" <- (priority ${this.priority})`;
|
||||
if (this.title_regex) this.description += ` - node title regex '${this.title_regex.source}'`;
|
||||
if (this.input_regex) this.description += ` - input name regex '${this.input_regex.source}'`;
|
||||
}
|
||||
@@ -102,12 +122,12 @@ class UseEverywhere {
|
||||
}
|
||||
|
||||
function validity_errors(params) {
|
||||
if (!node_is_live(params.controller)) return `UE node ${params.output[0]} is not alive`;
|
||||
if (!node_is_live(get_real_node(params.output[0]))) return `upstream node ${params.output[0]} is not alive`;
|
||||
if (!node_is_live(params.controller)) return `UE node ${params.controller.id} is not alive`;
|
||||
if (params.output[0]!=-10 && !node_is_live(get_real_node(params.output[0], params.graph))) return `upstream node ${params.output[0]} is not alive`;
|
||||
return "";
|
||||
}
|
||||
|
||||
class UseEverywhereList {
|
||||
export class UseEverywhereList {
|
||||
constructor() { this.ues = []; this.unmatched_inputs = []; }
|
||||
|
||||
differs_from(another_uel) {
|
||||
@@ -119,42 +139,26 @@ class UseEverywhereList {
|
||||
return false;
|
||||
}
|
||||
|
||||
add_ue(node, control_node_input_index, type, output, title_regex, input_regex, group_regex, priority) {
|
||||
add_ue(node, control_node_input_index, type, output, input_regex_override) {
|
||||
const params = {
|
||||
controller: node,
|
||||
control_node_input_index: control_node_input_index,
|
||||
type: type,
|
||||
output: output,
|
||||
title_regex: title_regex,
|
||||
input_regex: input_regex,
|
||||
group_regex: group_regex,
|
||||
priority: priority
|
||||
title_regex: regex_for(node, 'title'),
|
||||
input_regex: input_regex_override || regex_for(node, 'input'),
|
||||
group_regex: regex_for(node, 'group'),
|
||||
priority: node.properties.ue_properties.priority || default_priority(node),
|
||||
graph: node_graph(node),
|
||||
};
|
||||
const real_node = get_real_node(node.id);
|
||||
if (!real_node) {
|
||||
Logger.log_problem(`Node ${node.id} not found`, params);
|
||||
return;
|
||||
}
|
||||
if (real_node.properties.group_restricted == 1) {
|
||||
params.restrict_to = nodes_in_my_group(node.id);
|
||||
params.priority += 0.1;
|
||||
}
|
||||
if (real_node.properties.group_restricted == 2) {
|
||||
params.restrict_to = nodes_not_in_my_group(node.id);
|
||||
params.priority += 0.1;
|
||||
}
|
||||
if (real_node.properties.color_restricted == 1) {
|
||||
params.restrict_to = nodes_my_color(node.id, params.restrict_to);
|
||||
params.priority += 0.3;
|
||||
}
|
||||
if (real_node.properties.color_restricted == 2) {
|
||||
params.restrict_to = nodes_not_my_color(node.id, params.restrict_to);
|
||||
params.priority += 0.3;
|
||||
}
|
||||
if (group_regex) {
|
||||
params.restrict_to = nodes_in_groups_matching(group_regex, params.restrict_to);
|
||||
}
|
||||
if (real_node.properties["priority_boost"]) params.priority += real_node.properties["priority_boost"];
|
||||
|
||||
if (node.properties.ue_properties.group_restricted == 1) params.restrict_to = nodes_in_my_group(node);
|
||||
if (node.properties.ue_properties.group_restricted == 2) params.restrict_to = nodes_not_in_my_group(node);
|
||||
|
||||
if (node.properties.ue_properties.color_restricted == 1) params.restrict_to = nodes_my_color(node, params.restrict_to);
|
||||
if (node.properties.ue_properties.color_restricted == 2) params.restrict_to = nodes_not_my_color(node, params.restrict_to);
|
||||
|
||||
if (params.group_regex) params.restrict_to = nodes_in_groups_matching(params.group_regex, params.restrict_to, graph);
|
||||
|
||||
var error = ""
|
||||
var ue = null;
|
||||
@@ -168,7 +172,7 @@ class UseEverywhereList {
|
||||
this.ues.push(ue);
|
||||
Logger.log_detail(`Added ${ue.description}`)
|
||||
} else {
|
||||
Logger.log_problem(`Rejected ${ue?.description} because ${error}`, params);
|
||||
Logger.log_problem(`Rejected ${ue?.description} because ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,38 +204,6 @@ class UseEverywhereList {
|
||||
return matches[0];
|
||||
}
|
||||
|
||||
print_all() {
|
||||
this.ues.forEach((ue) => { console.log(ue.describe()); });
|
||||
}
|
||||
|
||||
all_unmatched_inputs(type) {
|
||||
return this.unmatched_inputs.filter((ui)=>ui.input.type==type);
|
||||
}
|
||||
|
||||
all_nodes_with_unmatched_input(type) {
|
||||
const result = new Set();
|
||||
this.all_unmatched_inputs(type).forEach((ui) => {
|
||||
result.add(display_name(ui.node));
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
all_unmatched_input_names(type) {
|
||||
const result = new Set();
|
||||
this.all_unmatched_inputs(type).forEach((ui) => {
|
||||
result.add(ui.input.label ? ui.input.label : ui.input.name);
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
all_group_names() {
|
||||
const result = new Set();
|
||||
app.graph._groups.forEach((group) => {
|
||||
result.add(group.title);
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
all_connected_inputs(for_node) {
|
||||
const ue_connections = [];
|
||||
this.ues.forEach((ue) => {
|
||||
@@ -240,7 +212,7 @@ class UseEverywhereList {
|
||||
ue_connections.push({
|
||||
type : ue.type,
|
||||
input_index : st.input_index,
|
||||
control_node : get_real_node(ue.controller.id),
|
||||
control_node : get_real_node(ue.controller.id, ue.graph),
|
||||
control_node_input_index : ue.control_node_input_index,
|
||||
sending_to : st.node,
|
||||
});
|
||||
@@ -257,32 +229,37 @@ class UseEverywhereList {
|
||||
ue_connections.push({
|
||||
type : ue.type,
|
||||
input_index : st.input_index,
|
||||
control_node : get_real_node(ue.controller.id),
|
||||
control_node : get_real_node(ue.controller.id, ue.graph),
|
||||
control_node_input_index : ue.control_node_input_index,
|
||||
sending_to : st.node,
|
||||
sending_to : st.node,
|
||||
graph: ue.graph,
|
||||
});
|
||||
});
|
||||
});
|
||||
return ue_connections;
|
||||
}
|
||||
|
||||
all_ue_connections_for(node_id) {
|
||||
const ue_connections = [];
|
||||
this.ues.forEach((ue) => {
|
||||
ue.sending_to.forEach((st) => {
|
||||
if (get_real_node(st.node.id).id==node_id || get_real_node(ue.controller.id).id==node_id) {
|
||||
ue_connections.push({
|
||||
type : ue.type,
|
||||
input_index : st.input_index,
|
||||
control_node : get_real_node(ue.controller.id),
|
||||
control_node_input_index : ue.control_node_input_index,
|
||||
sending_to : st.node,
|
||||
});
|
||||
add_ue_from_node(node) {
|
||||
if (node.properties.ue_properties.seed_inputs) {
|
||||
this.add_ue(node, -1, "INT", [node.id.toString(),0], regex_for(node, 'input'));
|
||||
} else {
|
||||
for (var i=0; i<node.inputs.length; i++) {
|
||||
const connection = get_connection(node, i);
|
||||
if (connection.link) {
|
||||
const input_regex = (node.properties.ue_properties.prompt_regexes) ? prompt_regex(node,i) : undefined
|
||||
this.add_ue(node, i, connection.type, [connection.link.origin_id.toString(), connection.link.origin_slot], input_regex);
|
||||
}
|
||||
});
|
||||
});
|
||||
return ue_connections;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {UseEverywhereList, display_name}
|
||||
const P_REGEXES = ['prompt', 'negative']
|
||||
const PROMPT_REGEXES = [new RegExp(default_regex('prompt_regex')), new RegExp(default_regex('negative_regex'))]
|
||||
|
||||
function prompt_regex(node, i) {
|
||||
const reg = node.properties.ue_properties[`${P_REGEXES[i]}_regex`]
|
||||
if (reg) return new RegExp(reg)
|
||||
else return PROMPT_REGEXES[i]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { GroupNodeHandler } from "../core/groupNode.js";
|
||||
import { UseEverywhereList } from "./use_everywhere_classes.js";
|
||||
import { add_ue_from_node, add_ue_from_node_in_group } from "./use_everywhere_nodes.js";
|
||||
import { node_in_loop, node_is_live, is_connected, is_UEnode, Logger, get_real_node, Pausable } from "./use_everywhere_utilities.js";
|
||||
import { convert_to_links } from "./use_everywhere_apply.js";
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { settingsCache } from "./use_everywhere_cache.js";
|
||||
import { master_graph, node_graph, visible_graph } from "./use_everywhere_subgraph_utils.js";
|
||||
|
||||
class GraphAnalyser extends Pausable {
|
||||
static _instance;
|
||||
@@ -17,20 +16,29 @@ class GraphAnalyser extends Pausable {
|
||||
super('GraphAnalyser')
|
||||
this.original_graphToPrompt = app.graphToPrompt;
|
||||
this.ambiguity_messages = [];
|
||||
this.latest_ues = null
|
||||
}
|
||||
|
||||
async graph_to_prompt(cur_list) {
|
||||
modify_graphs_recursively(graph, mods) {
|
||||
const modifications = convert_to_links( this.analyse_graph(graph), null, graph )
|
||||
mods.push( modifications );
|
||||
if (!graph.extra) graph.extra = {}
|
||||
graph.extra['links_added_by_ue'] = modifications.added_links.map(x=>x.id)
|
||||
graph.nodes.filter((node)=>(node.subgraph)).forEach((node) => {this.modify_graphs_recursively(node.subgraph, mods);});
|
||||
}
|
||||
|
||||
async graph_to_prompt() {
|
||||
var p;
|
||||
// Convert the virtual links into real connections
|
||||
this.pause('graph_to_prompt')
|
||||
try { // For each UseEverywhere object add its connections
|
||||
const mods = convert_to_links(cur_list, -1);
|
||||
try {
|
||||
const mods = []
|
||||
this.modify_graphs_recursively(master_graph(), mods);
|
||||
|
||||
// Now create the prompt using the ComfyUI original functionality and the patched graph
|
||||
app.graph.extra['links_added_by_ue'] = mods.added_links;
|
||||
p = await this.original_graphToPrompt.apply(app);
|
||||
// Remove the added virtual links
|
||||
mods.restorer()
|
||||
//addedLinks.forEach(id => { app.graph.removeLink(id); });
|
||||
mods.forEach((mod)=>{mod.restorer()})
|
||||
|
||||
} catch (e) {
|
||||
Logger.log_error(e)
|
||||
} finally {
|
||||
@@ -45,92 +53,23 @@ class GraphAnalyser extends Pausable {
|
||||
return p;
|
||||
}
|
||||
|
||||
analyse_graph(wait_if_blocked, about_to_submit) {
|
||||
analyse_visible_graph() { return this.analyse_graph(visible_graph()); }
|
||||
|
||||
analyse_master_graph() { return this.analyse_graph(master_graph()); }
|
||||
|
||||
wait_to_analyse_visible_graph() { return this.wait_to_analyse_graph(visible_graph()); }
|
||||
|
||||
wait_to_analyse_master_graph() { return this.wait_to_analyse_graph(master_graph()); }
|
||||
|
||||
wait_to_analyse_graph(graph) {
|
||||
if (this.paused()) {
|
||||
if (wait_if_blocked) {
|
||||
Logger.log_problem("Don't know how to wait")
|
||||
}
|
||||
return null
|
||||
Logger.log_problem("Don't know how to wait", null, true);
|
||||
}
|
||||
this.ambiguity_messages = [];
|
||||
var p = { workflow:app.graph.serialize() };
|
||||
const treat_bypassed_as_live = settingsCache.getSettingValue("Use Everywhere.Options.connect_to_bypassed")
|
||||
const live_nodes = p.workflow.nodes.filter((node) => node_is_live(node, treat_bypassed_as_live))
|
||||
|
||||
// Create a UseEverywhereList and populate it from all live (not bypassed) UE nodes
|
||||
const ues = new UseEverywhereList();
|
||||
live_nodes.filter((node) => is_UEnode(node)).forEach(node => { add_ue_from_node(ues, node); })
|
||||
// and nodes in group nodes
|
||||
live_nodes.filter((node) => (get_real_node(node.id) && GroupNodeHandler.isGroupNode(get_real_node(node.id)))).forEach( groupNode => {
|
||||
const group_data = GroupNodeHandler.getGroupData(get_real_node(groupNode.id));
|
||||
group_data.nodeData.nodes.filter((node) => is_UEnode(node)).forEach(node => {
|
||||
add_ue_from_node_in_group(ues, node, groupNode.id, group_data);
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// List all unconnected inputs on non-UE nodes which are connectable
|
||||
const connectable = []
|
||||
live_nodes.filter((node) => !is_UEnode(node)).forEach(node => {
|
||||
const real_node = get_real_node(node.id);
|
||||
return this.analyse_graph(graph);
|
||||
}
|
||||
|
||||
if (real_node && !real_node.properties.rejects_ue_links) {
|
||||
var gpData = GroupNodeHandler.getGroupData(real_node);
|
||||
const isGrp = !!gpData;
|
||||
const o2n = isGrp ? Object.entries(gpData.oldToNewInputMap) : null;
|
||||
//if (!real_node._widget_name_map) real_node._widget_name_map = real_node.widgets?.map(w => w.name) || [];
|
||||
real_node.inputs?.forEach((input,index) => {
|
||||
if (is_connected(input, treat_bypassed_as_live)) return;
|
||||
if (real_node.reject_ue_connection && real_node.reject_ue_connection(input)) return;
|
||||
if (real_node._getWidgetByName(input.name) && !(real_node.properties['widget_ue_connectable'] && real_node.properties['widget_ue_connectable'][input.name])) return;
|
||||
connectable.push({node, input, index, isGrp, real_node, o2n});
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// see if we can connect them
|
||||
const links_added = new Set();
|
||||
connectable.forEach(({node, input, index, isGrp, real_node, o2n}) => {
|
||||
/*
|
||||
if isGrp, then
|
||||
node refers to the outer node (group node), real_node to the inner node.
|
||||
input and index are on real_node
|
||||
if not isGRp, node==real_node
|
||||
*/
|
||||
|
||||
var ue = ues.find_best_match(real_node, input, this.ambiguity_messages);
|
||||
if (ue) {
|
||||
const upstream_node_id = ue.output[0]
|
||||
const real_upstream_node = get_real_node(upstream_node_id);
|
||||
var effective_output = [ue.output[0], ue.output[1]]; // [node_id, slot]
|
||||
|
||||
|
||||
if (upstream_node_id!=real_upstream_node.id) { // the upstream node is a group node, so get the inner node and slot
|
||||
const upGpData = GroupNodeHandler.getGroupData(real_upstream_node);
|
||||
const up_inner_node = upGpData.newToOldOutputMap[ue.output[1]].node;
|
||||
const up_inner_node_index = up_inner_node.index;
|
||||
const up_inner_node_id = real_upstream_node.getInnerNodes()[up_inner_node_index].id;
|
||||
const up_inner_node_slot = upGpData.newToOldOutputMap[ue.output[1]].slot;
|
||||
effective_output = [`${up_inner_node_id}`, up_inner_node_slot];
|
||||
}
|
||||
|
||||
|
||||
links_added.add({
|
||||
//"downstream":real_target_node.id, "downstream_slot":real_target_node_slot,
|
||||
"downstream":real_node.id, "downstream_slot":index,
|
||||
"upstream":effective_output[0], "upstream_slot":effective_output[1],
|
||||
"controller":ue.controller.id,
|
||||
"type":ue.type
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.graph.extra['ue_links'] = Array.from(links_added)
|
||||
|
||||
if (this.ambiguity_messages.length) Logger.log_problem("Ambiguous connections", this.ambiguity_messages, true);
|
||||
|
||||
// if there are loops report them and raise an exception
|
||||
if (about_to_submit && settingsCache.getSettingValue('Use Everywhere.Options.checkloops')) {
|
||||
maybe_check_for_loops() {
|
||||
if (settingsCache.getSettingValue('Use Everywhere.Options.checkloops')) {
|
||||
try {
|
||||
node_in_loop(live_nodes, links_added);
|
||||
} catch (e) {
|
||||
@@ -143,8 +82,51 @@ class GraphAnalyser extends Pausable {
|
||||
throw new Error(`Loop Detected ${e.stack}, ${e.ues}`, {"cause":e});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ues;
|
||||
analyse_graph(graph) {
|
||||
this.ambiguity_messages = [];
|
||||
const treat_bypassed_as_live = settingsCache.getSettingValue("Use Everywhere.Options.connect_to_bypassed") || this.connect_to_bypassed
|
||||
const live_nodes = graph.nodes.filter((node) => node_is_live(node, treat_bypassed_as_live))
|
||||
|
||||
// Create a UseEverywhereList and populate it from all live (not bypassed) UE nodes
|
||||
const ues = new UseEverywhereList();
|
||||
live_nodes.filter((node) => is_UEnode(node)).filter((node)=>node_is_live(node,false)).forEach(node => { ues.add_ue_from_node(node); })
|
||||
|
||||
// List all unconnected inputs on non-UE nodes which are connectable
|
||||
const connectable = []
|
||||
live_nodes.filter((node) => !is_UEnode(node)).forEach(node => {
|
||||
if (node && !node.properties.rejects_ue_links) {
|
||||
//if (!real_node._widget_name_map) real_node._widget_name_map = real_node.widgets?.map(w => w.name) || [];
|
||||
node.inputs?.forEach((input,index) => {
|
||||
if (is_connected(input, treat_bypassed_as_live, node_graph(node))) return;
|
||||
if (node.reject_ue_connection && node.reject_ue_connection(input)) return;
|
||||
if (node._getWidgetByName(input.name) && !(node.properties?.ue_properties?.widget_ue_connectable && node.properties.ue_properties.widget_ue_connectable[input.name])) return;
|
||||
connectable.push({node, input, index});
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// see if we can connect them
|
||||
const links_added = new Set();
|
||||
connectable.forEach(({node, input, index}) => {
|
||||
var ue = ues.find_best_match(node, input, this.ambiguity_messages);
|
||||
if (ue) {
|
||||
links_added.add({
|
||||
"downstream":node.id, "downstream_slot":index,
|
||||
"upstream":ue.output[0], "upstream_slot":ue.output[1],
|
||||
"controller":ue.controller.id,
|
||||
"type":ue.type
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
graph.extra['ue_links'] = Array.from(links_added)
|
||||
|
||||
if (this.ambiguity_messages.length) Logger.log_problem("Ambiguous connections", this.ambiguity_messages, true);
|
||||
|
||||
this.latest_ues = ues;
|
||||
return this.latest_ues;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
import { handle_bypass, get_real_node, get_group_node } from "./use_everywhere_utilities.js";
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
const CONVERTED_TYPE = "converted-widget";
|
||||
// import {CONVERTED_TYPE} from "../../extensions/core/widgetInputs.js"
|
||||
|
||||
/*
|
||||
If a widget hasn't been converted, just get it's value
|
||||
If it has, *try* to go upstream
|
||||
*/
|
||||
function get_widget_or_input_values(node_obj, widget_id) {
|
||||
if (node_obj.widgets[widget_id]?.type.startsWith(CONVERTED_TYPE)) {
|
||||
try {
|
||||
const name = node_obj.widgets[widget_id].name;
|
||||
const input_id = node_obj.inputs.findIndex((input) => input.name==name);
|
||||
const connection = get_connection(node_obj, input_id, "STRING");
|
||||
const upstream_node_obj = get_real_node(connection.link.origin_id.toString());
|
||||
const widget = upstream_node_obj.widgets.find((w) => w.name.toLowerCase() == upstream_node_obj.outputs[connection.link.origin_slot].name.toLowerCase());
|
||||
return widget.value;
|
||||
} catch (error) {
|
||||
return "NOT CONNECTED DONT MATCH";
|
||||
}
|
||||
}
|
||||
return node_obj.widgets[widget_id].value;
|
||||
}
|
||||
|
||||
function add_ue_from_node_in_group(ues, node, group_node_id, group_data) {
|
||||
const group_node = get_real_node(group_node_id);
|
||||
const ue_node = group_node.getInnerNodes()[node.index];
|
||||
ue_node.in_group_with_data = group_data;
|
||||
ue_node.getInnerNodesOfGroup = group_node.getInnerNodes;
|
||||
add_ue_from_node(ues, ue_node)
|
||||
}
|
||||
|
||||
function get_available_input_name(inputs, the_input, type) {
|
||||
const used_names = [];
|
||||
inputs.forEach((input) => { if (input!=the_input) used_names.push(input.name); });
|
||||
const base = `UE ${type.toLowerCase()}`;
|
||||
if (!used_names.includes(base)) return base;
|
||||
for (var i=2; ;i++) {
|
||||
if (!used_names.includes(`${base}${i}`)) return `${base}${i}`;
|
||||
}
|
||||
}
|
||||
|
||||
function get_connection(node, i, override_type) {
|
||||
const in_link = node?.inputs[i]?.link;
|
||||
var type = override_type;
|
||||
var link = undefined;
|
||||
if (in_link) {
|
||||
if (!override_type) type = get_real_node(node.id.toString())?.input_type[i];
|
||||
link = handle_bypass(app.graph.links[in_link],type);
|
||||
} else if (node.in_group_with_data) {
|
||||
if (node.in_group_with_data.linksTo[node.index] && node.in_group_with_data.linksTo[node.index][i]) {
|
||||
const group_style_link = node.in_group_with_data.linksTo[node.index][i];
|
||||
link = { "origin_id":node.getInnerNodesOfGroup()[group_style_link[0]].id, "origin_slot" : group_style_link[1] };
|
||||
if (!override_type) type = group_style_link[5];
|
||||
} else { // group external input
|
||||
const group_node = get_group_node(node.id);
|
||||
const group_node_input = group_node.inputs[node.in_group_with_data.oldToNewInputMap[node.index][i]];
|
||||
const link_n = group_node_input.link;
|
||||
if (link_n) {
|
||||
link = app.graph.links[link_n];
|
||||
if (!override_type) type = app.graph._nodes_by_id[link.origin_id].outputs[link.origin_slot].type;
|
||||
// update the group input node... and the link type
|
||||
group_node_input.type = type;
|
||||
group_node_input.name = get_available_input_name(group_node.inputs, group_node_input, type);
|
||||
link.type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { link:link, type:type }
|
||||
}
|
||||
|
||||
/*
|
||||
Add UseEverywhere broadcasts from this node to the list
|
||||
*/
|
||||
function add_ue_from_node(ues, node) {
|
||||
if (node.type === "Seed Everywhere") ues.add_ue(node, -1, "INT", [node.id.toString(),0],
|
||||
undefined, new RegExp("seed|随机种"), undefined, 5);
|
||||
|
||||
if (node.type === "Anything Everywhere?") {
|
||||
const connection = get_connection(node, 0);
|
||||
if (connection.link) {
|
||||
const node_obj = get_real_node(node.id.toString());
|
||||
const w0 = get_widget_or_input_values(node_obj,0);
|
||||
const r0 = new RegExp(w0);
|
||||
const w1 = get_widget_or_input_values(node_obj,1);
|
||||
const r1 = (w1.startsWith('+')) ? w1 : new RegExp(w1);
|
||||
const w2 = get_widget_or_input_values(node_obj,2);
|
||||
const r2 = (w2 && w2!=".*") ? new RegExp(w2) : null;
|
||||
ues.add_ue(node, 0, connection.type, [connection.link.origin_id.toString(), connection.link.origin_slot], r0, r1, r2, 10);
|
||||
}
|
||||
}
|
||||
if (node.type === "Prompts Everywhere") {
|
||||
for (var i=0; i<2; i++) {
|
||||
const connection = get_connection(node, i);
|
||||
if (connection.link) ues.add_ue(node, i, connection.type, [connection.link.origin_id.toString(), connection.link.origin_slot],
|
||||
undefined, new RegExp(["(_|\\b)pos(itive|_|\\b)|^prompt|正面","(_|\\b)neg(ative|_|\\b)|负面"][i]), undefined, 5);
|
||||
}
|
||||
}
|
||||
if (node.type === "Anything Everywhere") {
|
||||
const connection = get_connection(node, 0);
|
||||
if (connection.link) ues.add_ue(node, 0, connection.type, [connection.link.origin_id.toString(),connection. link.origin_slot], undefined, undefined, undefined, 2);
|
||||
}
|
||||
if (node.type === "Anything Everywhere3") {
|
||||
for (var i=0; i<3; i++) {
|
||||
const connection = get_connection(node, i);
|
||||
if (connection.link) ues.add_ue(node, i, connection.type, [connection.link.origin_id.toString(), connection.link.origin_slot]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {add_ue_from_node, add_ue_from_node_in_group}
|
||||
@@ -2,13 +2,16 @@ import { app } from "../../scripts/app.js";
|
||||
import { GraphAnalyser } from "./use_everywhere_graph_analysis.js";
|
||||
import { LinkRenderController } from "./use_everywhere_ui.js";
|
||||
import { convert_to_links, remove_all_ues } from "./use_everywhere_apply.js";
|
||||
import { has_priority_boost } from "./use_everywhere_utilities.js";
|
||||
import { VERSION } from "./use_everywhere_utilities.js";
|
||||
import { settingsCache } from "./use_everywhere_cache.js";
|
||||
import { visible_graph } from "./use_everywhere_subgraph_utils.js";
|
||||
import { edit_restrictions } from "./ue_properties_editor.js";
|
||||
import { is_UEnode } from "./use_everywhere_utilities.js";
|
||||
|
||||
export const SETTINGS = [
|
||||
{
|
||||
id: "Use Everywhere.About",
|
||||
name: "Version 6.2.2",
|
||||
name: `Version ${VERSION}`,
|
||||
type: () => {return document.createElement('span')},
|
||||
},
|
||||
{
|
||||
@@ -50,6 +53,21 @@ export const SETTINGS = [
|
||||
defaultValue: true,
|
||||
onChange: settingsCache.onSettingChange,
|
||||
},
|
||||
{
|
||||
id: "Use Everywhere.Graphics.preserve edit window position",
|
||||
name: "Save restrictions edit window position",
|
||||
type: "boolean",
|
||||
defaultValue: false,
|
||||
onChange: settingsCache.onSettingChange,
|
||||
tooltip: "If off, the edit window appears where the mouse is"
|
||||
},
|
||||
{
|
||||
id: "Use Everywhere.Graphics.tooltips",
|
||||
name: "Show restrictions as tooltip",
|
||||
type: "boolean",
|
||||
defaultValue: true,
|
||||
onChange: settingsCache.onSettingChange,
|
||||
},
|
||||
{
|
||||
id: "Use Everywhere.Options.connect_to_bypassed",
|
||||
name: "Connect to bypassed nodes",
|
||||
@@ -112,35 +130,11 @@ function submenu(properties, property, options, e, menu, node) {
|
||||
}
|
||||
}
|
||||
|
||||
const GROUP_RESTRICTION_OPTIONS = ["No restrictions", "Send only within group", "Send only not within group"]
|
||||
function group_restriction_submenu(value, options, e, menu, node) {
|
||||
submenu(node.properties, "group_restricted", GROUP_RESTRICTION_OPTIONS, e, menu, node);
|
||||
}
|
||||
|
||||
const COLOR_RESTRICTION_OPTIONS = ["No restrictions", "Send only to same color", "Send only to different color"]
|
||||
function color_restriction_submenu(value, options, e, menu, node) {
|
||||
submenu(node.properties, "color_restricted", COLOR_RESTRICTION_OPTIONS, e, menu, node);
|
||||
}
|
||||
|
||||
function priority_boost_submenu(value, options, e, menu, node) {
|
||||
const current = (node.properties["priority_boost"] ? node.properties["priority_boost"] : 0) + 1;
|
||||
const submenu = new LiteGraph.ContextMenu(
|
||||
[0,1,2,3,4,5,6,7,8,9],
|
||||
{ event: e, callback: function (v) {
|
||||
node.properties["priority_boost"] = parseInt(v);
|
||||
LinkRenderController.instance().mark_link_list_outdated();
|
||||
},
|
||||
parentMenu: menu, node:node}
|
||||
)
|
||||
const current_element = submenu.root.querySelector(`:nth-child(${current})`);
|
||||
if (current_element) current_element.style.borderLeft = "2px solid #484";
|
||||
}
|
||||
|
||||
function highlight_selected(submenu_root, node, names) {
|
||||
names.forEach((name, i) => {
|
||||
const current_element = submenu_root?.querySelector(`:nth-child(${i+1})`);
|
||||
if (current_element) {
|
||||
if (node.properties['widget_ue_connectable'][name]) {
|
||||
if (node.properties.ue_properties['widget_ue_connectable'][name]) {
|
||||
current_element.style.borderLeft = "2px solid #484";
|
||||
} else {
|
||||
current_element.style.borderLeft = "";
|
||||
@@ -152,7 +146,8 @@ function highlight_selected(submenu_root, node, names) {
|
||||
}
|
||||
|
||||
function widget_ue_submenu(value, options, e, menu, node) {
|
||||
if (!(node.properties['widget_ue_connectable'])) node.properties['widget_ue_connectable'] = {};
|
||||
if (!(node.properties.ue_properties)) node.properties.ue_properties = {}
|
||||
if (!(node.properties.ue_properties.widget_ue_connectable)) node.properties.ue_properties.widget_ue_connectable = {};
|
||||
|
||||
const linkedWidgets = new Set()
|
||||
node.widgets
|
||||
@@ -169,7 +164,7 @@ function widget_ue_submenu(value, options, e, menu, node) {
|
||||
const submenu = new LiteGraph.ContextMenu(
|
||||
names,
|
||||
{ event: e, callback: function (v) {
|
||||
node.properties['widget_ue_connectable'][v] = !!!node.properties['widget_ue_connectable'][v];
|
||||
node.properties.ue_properties.widget_ue_connectable[v] = !!!node.properties.ue_properties.widget_ue_connectable[v];
|
||||
LinkRenderController.instance().mark_link_list_outdated();
|
||||
highlight_selected(this.parentElement, node, names)
|
||||
return true; // keep open
|
||||
@@ -179,6 +174,21 @@ function widget_ue_submenu(value, options, e, menu, node) {
|
||||
highlight_selected(submenu.root, node, names)
|
||||
}
|
||||
|
||||
export function add_extra_menu_items(node_or_node_type, ioio) {
|
||||
if (node_or_node_type.ue_extra_menu_items_added) return
|
||||
const getExtraMenuOptions = node_or_node_type.getExtraMenuOptions;
|
||||
node_or_node_type.getExtraMenuOptions = function(_, options) {
|
||||
getExtraMenuOptions?.apply(this, arguments);
|
||||
if (is_UEnode(this)) {
|
||||
node_menu_settings(options, this);
|
||||
} else {
|
||||
non_ue_menu_settings(options, this);
|
||||
}
|
||||
ioio(options,'callback',`menu option on ${this.id}`);
|
||||
}
|
||||
node_or_node_type.ue_extra_menu_items_added = true
|
||||
}
|
||||
|
||||
export function non_ue_menu_settings(options, node) {
|
||||
options.push(null);
|
||||
options.push(
|
||||
@@ -202,30 +212,19 @@ export function non_ue_menu_settings(options, node) {
|
||||
|
||||
export function node_menu_settings(options, node) {
|
||||
options.push(null);
|
||||
if (has_priority_boost(node)) options.push(
|
||||
options.push(
|
||||
{
|
||||
content: "Priority Boost",
|
||||
has_submenu: true,
|
||||
callback: priority_boost_submenu,
|
||||
}
|
||||
content: "Edit restrictions",
|
||||
callback: edit_restrictions,
|
||||
}
|
||||
)
|
||||
options.push(
|
||||
{
|
||||
content: "Group Restrictions",
|
||||
has_submenu: true,
|
||||
callback: group_restriction_submenu,
|
||||
},
|
||||
{
|
||||
content: "Color Restrictions",
|
||||
has_submenu: true,
|
||||
callback: color_restriction_submenu,
|
||||
},
|
||||
{
|
||||
content: "Convert to real links",
|
||||
callback: async () => {
|
||||
const ues = GraphAnalyser.instance().analyse_graph(true);
|
||||
convert_to_links(ues, node.id);
|
||||
app.graph.remove(node);
|
||||
const ues = GraphAnalyser.instance().wait_to_analyse_visible_graph();
|
||||
convert_to_links(ues, node);
|
||||
visible_graph().remove(node);
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -246,10 +245,10 @@ export function canvas_menu_settings(options) {
|
||||
content: "Convert all UEs to real links",
|
||||
callback: async () => {
|
||||
if (window.confirm("This will convert all links created by Use Everywhere to real links, and delete all the Use Everywhere nodes. Is that what you want?")) {
|
||||
const ues = GraphAnalyser.instance().analyse_graph(true);
|
||||
const ues = GraphAnalyser.instance().wait_to_analyse_visible_graph();
|
||||
LinkRenderController.instance().pause("convert");
|
||||
try {
|
||||
convert_to_links(ues, -1);
|
||||
convert_to_links(ues, visible_graph());
|
||||
remove_all_ues(true);
|
||||
} finally {
|
||||
app.graph.change();
|
||||
|
||||
@@ -1,6 +1,39 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
export function visible_graph() { return app.canvas.graph }
|
||||
export function visible_graph_id() { return app.canvas.graph.id }
|
||||
export function node_graph_id(node) { return node.graph.id }
|
||||
export function in_visible_graph(node) { return node_graph_id(node) == visible_graph_id() }
|
||||
export function master_graph() { return app.graph }
|
||||
export function master_graph_id() { return master_graph().id }
|
||||
|
||||
export function visible_graph() { return app.canvas.graph }
|
||||
export function visible_graph_id() { return visible_graph().id }
|
||||
|
||||
export function node_graph(node) { return node.graph }
|
||||
export function node_graph_id(node) { return node_graph(node).id }
|
||||
|
||||
export function in_visible_graph(node) {
|
||||
try {
|
||||
return node_graph_id(node) == visible_graph_id()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function get_subgraph_input_type(graph, slot) { return graph.inputNode.slots[slot].type }
|
||||
export function link_is_from_subgraph_input(link) { return link.origin_id==-10 }
|
||||
|
||||
class WrappedInputNode {
|
||||
constructor(subgraph_input_node) {
|
||||
this.subgraph_input_node = subgraph_input_node;
|
||||
this.graph = subgraph_input_node.subgraph;
|
||||
}
|
||||
|
||||
connect(output_index, input_node, input_index) {
|
||||
this.graph.last_link_id += 1
|
||||
this.graph.links[this.graph.last_link_id] = new LLink(this.graph.last_link_id, this.subgraph_input_node.slots[output_index].type, -10, output_index, input_node.id, input_index)
|
||||
input_node.inputs[input_index].link = this.graph.last_link_id;
|
||||
return this.graph.links[this.graph.last_link_id]
|
||||
}
|
||||
|
||||
}
|
||||
export function wrap_input(subgraph_input_node) {
|
||||
return new WrappedInputNode(subgraph_input_node);
|
||||
}
|
||||
+38
-32
@@ -1,25 +1,25 @@
|
||||
import { Logger, get_real_node, get_group_node, Pausable } from "./use_everywhere_utilities.js";
|
||||
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||
import { Logger, get_real_node, Pausable } from "./use_everywhere_utilities.js";
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { settingsCache } from "./use_everywhere_cache.js";
|
||||
import { in_visible_graph } from "./use_everywhere_subgraph_utils.js";
|
||||
import { in_visible_graph, node_graph } from "./use_everywhere_subgraph_utils.js";
|
||||
import { maybe_show_tooltip } from "./tooltip_window.js";
|
||||
|
||||
function nodes_in_my_group(node_id) {
|
||||
function nodes_in_my_group(node) {
|
||||
const nodes_in = new Set();
|
||||
app.graph._groups.forEach((group) => {
|
||||
node_graph(node)._groups.forEach((group) => {
|
||||
if (!app.canvas.selected_group_moving) group.recomputeInsideNodes();
|
||||
if (group._nodes?.find((node) => { return (node.id===node_id) } )) {
|
||||
group._nodes.forEach((node) => { nodes_in.add(node.id) } )
|
||||
if (group._nodes?.find((nd) => { return (nd.id===node.id) } )) {
|
||||
group._nodes.forEach((nd) => { nodes_in.add(nd.id) } )
|
||||
}
|
||||
});
|
||||
return [...nodes_in];
|
||||
}
|
||||
|
||||
function nodes_not_in_my_group(node_id) {
|
||||
const nid = nodes_in_my_group(node_id);
|
||||
function nodes_not_in_my_group(node) {
|
||||
const nid = nodes_in_my_group(node);
|
||||
const nodes_not_in = [];
|
||||
app.graph._nodes.forEach((node) => {
|
||||
if (!nid.includes(node.id)) nodes_not_in.push(node.id);
|
||||
node_graph(node)._nodes.forEach((nd) => {
|
||||
if (!nid.includes(nd.id)) nodes_not_in.push(nd.id);
|
||||
});
|
||||
return nodes_not_in;
|
||||
}
|
||||
@@ -44,31 +44,31 @@ function nodes_in_groups_matching(regex, already_limited_to) {
|
||||
}
|
||||
|
||||
|
||||
function nodes_my_color(node_id, already_limited_to) {
|
||||
function nodes_my_color(node, already_limited_to) {
|
||||
const nodes_in = new Set();
|
||||
const color = get_real_node(node_id).color;
|
||||
const color = node.color;
|
||||
if (already_limited_to) {
|
||||
already_limited_to.forEach((nid) => {
|
||||
if (get_real_node(nid).color==color) nodes_in.add(nid)
|
||||
if (get_real_node(nid, node_graph(node)).color==color) nodes_in.add(nid)
|
||||
})
|
||||
} else {
|
||||
app.graph._nodes.forEach((node) => {
|
||||
if (node.color==color) nodes_in.add(node.id)
|
||||
node_graph(node)._nodes.forEach((nd) => {
|
||||
if (nd.color==color) nodes_in.add(nd.id)
|
||||
})
|
||||
}
|
||||
return [...nodes_in];
|
||||
}
|
||||
|
||||
function nodes_not_my_color(node_id, already_limited_to) {
|
||||
function nodes_not_my_color(node, already_limited_to) {
|
||||
const nodes_in = new Set();
|
||||
const color = get_real_node(node_id).color;
|
||||
const color = get_real_node(node.id).color;
|
||||
if (already_limited_to) {
|
||||
already_limited_to.forEach((nid) => {
|
||||
if (get_real_node(nid).color!=color) nodes_in.add(nid)
|
||||
if (get_real_node(nid, node_graph(node)).color!=color) nodes_in.add(nid)
|
||||
})
|
||||
} else {
|
||||
app.graph._nodes.forEach((node) => {
|
||||
if (node.color!=color) nodes_in.add(node.id)
|
||||
node_graph(node)._nodes.forEach((nd) => {
|
||||
if (nd.color!=color) nodes_in.add(nd.id)
|
||||
})
|
||||
}
|
||||
return [...nodes_in];
|
||||
@@ -85,9 +85,10 @@ function indicate_restriction(ctx, title_height) {
|
||||
}
|
||||
|
||||
function update_input_label(node, slot, app) {
|
||||
if (node.input_type[slot]) {
|
||||
node.inputs[slot].label = node.input_type[slot];
|
||||
node.inputs[slot].color_on = app.canvas.default_connection_color_byType[node.input_type[slot]];
|
||||
const type = node.inputs[slot].type
|
||||
if (type) {
|
||||
node.inputs[slot].label = type;
|
||||
node.inputs[slot].color_on = app.canvas.default_connection_color_byType[type];
|
||||
} else {
|
||||
node.inputs[slot].label = "anything";
|
||||
node.inputs[slot].color_on = undefined;
|
||||
@@ -152,7 +153,7 @@ class LinkRenderController extends Pausable {
|
||||
|
||||
_request_link_list_update() {
|
||||
try {
|
||||
const ues = this.the_graph_analyser.analyse_graph(false)
|
||||
const ues = this.the_graph_analyser.analyse_visible_graph()
|
||||
if (ues==null) return false // graph analyser was paused
|
||||
this.ue_list = ues;
|
||||
if (this.ue_list.differs_from(this.last_used_ue_list)) app.graph.change();
|
||||
@@ -194,7 +195,7 @@ class LinkRenderController extends Pausable {
|
||||
try {
|
||||
this.pause('highlight_ue_connections')
|
||||
if (!this._list_ready()) return;
|
||||
const unconnected_connectables = node.properties?.widget_ue_connectable ? new Set(Object.keys(node.properties.widget_ue_connectable).filter((name) => (node.properties.widget_ue_connectable[name]))) : new Set()
|
||||
const unconnected_connectables = node.properties?.ue_properties?.widget_ue_connectable ? new Set(Object.keys(node.properties.ue_properties.widget_ue_connectable).filter((name) => (node.properties.ue_properties.widget_ue_connectable[name]))) : new Set()
|
||||
node.inputs.filter((input)=>(input.link)).forEach((input) => { unconnected_connectables.delete(input.name) });
|
||||
|
||||
if (this.ue_list.all_connected_inputs) {
|
||||
@@ -268,8 +269,8 @@ class LinkRenderController extends Pausable {
|
||||
}
|
||||
|
||||
node_in_ueconnection(ue_connection, id) {
|
||||
if (ue_connection.control_node && get_group_node(ue_connection.control_node.id)?.id == id) return true
|
||||
if (ue_connection.sending_to && get_group_node(ue_connection.sending_to.id)?.id == id) return true
|
||||
if (ue_connection.control_node?.id == id) return true
|
||||
if (ue_connection.sending_to?.id == id) return true
|
||||
}
|
||||
|
||||
any_node_in_ueconnection(ue_connection, list_of_nodes) {
|
||||
@@ -277,7 +278,7 @@ class LinkRenderController extends Pausable {
|
||||
}
|
||||
|
||||
render_all_ue_links(ctx) {
|
||||
|
||||
maybe_show_tooltip()
|
||||
if (this.paused()) return;
|
||||
try {
|
||||
this.pause('render_all_ue_links')
|
||||
@@ -340,8 +341,8 @@ class LinkRenderController extends Pausable {
|
||||
|
||||
_render_ue_link(ue_connection, ctx, animate) {
|
||||
try {
|
||||
|
||||
const node = get_real_node(ue_connection.sending_to.id);
|
||||
const graph = ue_connection.graph
|
||||
const node = get_real_node(ue_connection.sending_to.id, graph);
|
||||
|
||||
/* this is the end node; get the position of the input */
|
||||
var pos2 = node.getConnectionPos(true, ue_connection.input_index, this.slot_pos1);
|
||||
@@ -350,7 +351,12 @@ class LinkRenderController extends Pausable {
|
||||
which is displayed with an output: the class records control_node_input_index as -ve (-1 => 0, -2 => 1...) */
|
||||
const input_source = (ue_connection.control_node_input_index >= 0);
|
||||
const source_index = input_source ? ue_connection.control_node_input_index : -1-ue_connection.control_node_input_index;
|
||||
const pos1 = get_group_node(ue_connection.control_node.id).getConnectionPos(input_source, source_index, this.slot_pos2);
|
||||
const pos1 = graph._nodes_by_id[ue_connection.control_node.id]?.getConnectionPos(input_source, source_index, this.slot_pos2);
|
||||
|
||||
if (!pos1) {
|
||||
Logger.problem(`Couldn't find position for UE link ${ue_connection}.`,null,true)
|
||||
return;
|
||||
}
|
||||
|
||||
/* get the direction that we start and end */
|
||||
const delta_x = pos2[0] - pos1[0];
|
||||
|
||||
+147
-140
@@ -1,6 +1,53 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { GroupNodeHandler } from "../core/groupNode.js";
|
||||
import { settingsCache } from "./use_everywhere_cache.js";
|
||||
import { link_is_from_subgraph_input, node_graph, visible_graph, wrap_input } from "./use_everywhere_subgraph_utils.js";
|
||||
|
||||
export const VERSION = "7.0"
|
||||
|
||||
export function create( tag, clss, parent, properties ) {
|
||||
const nd = document.createElement(tag);
|
||||
if (clss) clss.split(" ").forEach((s) => nd.classList.add(s))
|
||||
if (parent) parent.appendChild(nd);
|
||||
if (properties) Object.assign(nd, properties);
|
||||
return nd;
|
||||
}
|
||||
|
||||
/*
|
||||
Return 1 if x is a later version than y (or y is not defined)
|
||||
Return -1 if x is an earlier version than y (or x is not defined)
|
||||
Return 0 if they are the same version
|
||||
*/
|
||||
function version_compare(x,y) {
|
||||
if (x==y) return 0
|
||||
if (!y) return 1
|
||||
if (!x) return -1
|
||||
const xbits = x.split('.')
|
||||
const ybits = y.split('.')
|
||||
var result = 0
|
||||
for (var i=0; result!=0 && i<Math.min(xbits.length, ybits.length); i++) {
|
||||
if (parseInt(xbits[i]) < parseInt(ybits[i])) result = -1
|
||||
if (parseInt(xbits[i]) > parseInt(ybits[i])) result = 1
|
||||
}
|
||||
if (result==0) {
|
||||
if (xbits.length < ybits.length) result = -1
|
||||
if (xbits.length > ybits.length) result = 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function version_at_least(x,y) {
|
||||
return (version_compare(x,y) >= 0)
|
||||
}
|
||||
|
||||
/*
|
||||
Return the node object for this node_id.
|
||||
*/
|
||||
function get_real_node(node_id, graph) {
|
||||
if (!graph) graph = visible_graph()
|
||||
const nid = node_id.toString();
|
||||
if (nid==-10) return wrap_input(graph.inputNode); // special case for subgraph input
|
||||
return graph._nodes_by_id[nid];
|
||||
}
|
||||
|
||||
class Logger {
|
||||
static LIMITED_LOG_BLOCKED = false;
|
||||
@@ -9,10 +56,14 @@ class Logger {
|
||||
|
||||
static log_error(message) { console.error(message) }
|
||||
|
||||
static log(message, array, limited) {
|
||||
static log(message, foreachable, limited) {
|
||||
if (limited && Logger.check_limited()) return
|
||||
console.log(message);
|
||||
if (array) for (var i=0; i<array.length; i++) { console.log(array[i]) }
|
||||
try {
|
||||
foreachable?.forEach((x)=>{console.log(x)})
|
||||
} catch {
|
||||
let a;
|
||||
}
|
||||
}
|
||||
|
||||
static check_limited() {
|
||||
@@ -64,32 +115,8 @@ class GraphConverter {
|
||||
Logger.log_detail("stored node_input_map", this.node_input_map);
|
||||
}
|
||||
|
||||
on_node_created(node) {
|
||||
if (this.graph_being_configured) {
|
||||
/*
|
||||
If the graph is being configured, we are still loading old nodes.
|
||||
These might need to be converted, but we can't do that yet
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(node.properties)) node.properties = {};
|
||||
if (node.properties.widget_ue_connectable) {
|
||||
console.log(`already has widget_ue_connectable`)
|
||||
return
|
||||
}
|
||||
node.properties['widget_ue_connectable'] = {}
|
||||
console.log(`added widget_ue_connectable`)
|
||||
}
|
||||
|
||||
clean_ue_node(node) {
|
||||
|
||||
const gpData = GroupNodeHandler.getGroupData(node)
|
||||
const isGrp = !!gpData;
|
||||
if (isGrp) {
|
||||
let a;
|
||||
}
|
||||
|
||||
var expected_inputs = 1
|
||||
if (node.type == "Seed Everywhere") expected_inputs = 0
|
||||
if (node.type == "Prompts Everywhere") expected_inputs = 2
|
||||
@@ -97,8 +124,8 @@ class GraphConverter {
|
||||
if (node.type == "Anything Everywhere?") expected_inputs = 4
|
||||
|
||||
// remove all the 'anything' inputs (because they may be duplicated)
|
||||
const removed = node.inputs.filter(i=>i.label=='anything')
|
||||
node.inputs = node.inputs.filter(i=>i.label!='anything')
|
||||
const removed = node.inputs.filter(i=>(i.label=='anything' || i.label=='*'))
|
||||
node.inputs = node.inputs.filter(i=>(i.label!='anything' && i.label!='*'))
|
||||
// add them back as required
|
||||
while (node.inputs.length < expected_inputs) { node.inputs.push(removed.pop()) }
|
||||
// the input comes before the regex widgets in UE?
|
||||
@@ -109,59 +136,65 @@ class GraphConverter {
|
||||
}
|
||||
// fix the localized names
|
||||
node.inputs = node.inputs.map((input) => {
|
||||
if (input.localized_name=='anything') input.localized_name = input.name
|
||||
if (!input.localized_name || input.localized_name.startsWith('anything')) input.localized_name = input.name
|
||||
return input;
|
||||
})
|
||||
|
||||
// set types to match
|
||||
node.input_type = node.inputs.map((i)=>{
|
||||
var type = i.type;
|
||||
if (type=='*') {
|
||||
if (i.link) type = app.graph.links[i.link].type;
|
||||
else type = (i.label && i.label!='anything') ? i.label : i.name;
|
||||
node.inputs.forEach((input) => {
|
||||
if (input.type=='*') {
|
||||
const graph = node_graph(node);
|
||||
if (input.link) {
|
||||
const llink = graph.links[input.link];
|
||||
if (link_is_from_subgraph_input(llink)) {
|
||||
input.type = get_subgraph_input_type(graph, llink.origin_slot);
|
||||
} else {
|
||||
input.type = llink.type;
|
||||
}
|
||||
} else {
|
||||
input.type = (input.label && input.label!='anything') ? input.label : input.name
|
||||
}
|
||||
}
|
||||
return type
|
||||
})
|
||||
});
|
||||
|
||||
Logger.log_detail(`clean_ue_node ${node.id} (${node.type})`, node.inputs, node.input_type);
|
||||
Logger.log_detail(`clean_ue_node ${node.id} (${node.type})`, node.inputs);
|
||||
}
|
||||
|
||||
convert_if_pre_116(node) {
|
||||
if (!node) return;
|
||||
if (!(node.properties)) node.properties = {};
|
||||
|
||||
if (node.IS_UE) this.clean_ue_node(node)
|
||||
|
||||
if (node.properties.widget_ue_connectable) return
|
||||
if (node.properties?.ue_properties?.widget_ue_connectable) return
|
||||
if (node.properties?.widget_ue_connectable) return // pre 7.0 node which will be converted
|
||||
|
||||
if (!this.given_message) {
|
||||
Logger.log_info(`Graph was saved with a version of ComfyUI before 1.16, so Anything Everywhere will try to work out which widgets are connectable`);
|
||||
this.given_message = true;
|
||||
}
|
||||
|
||||
node.properties['widget_ue_connectable'] = {}
|
||||
if (!node.properties.ue_properties) node.properties.ue_properties = {}
|
||||
node.properties.ue_properties['widget_ue_connectable'] = {}
|
||||
const widget_names = node.widgets?.map(w => w.name) || [];
|
||||
|
||||
if (!(this.node_input_map[node.id])) {
|
||||
Logger.log_detail(`node ${node.id} (${node.type} has no node_input_map`);
|
||||
} else {
|
||||
this.node_input_map[node.id].filter((input_name)=>widget_names.includes(input_name)).forEach((input_name) => {
|
||||
node.properties['widget_ue_connectable'][input_name] = true;
|
||||
node.properties.ue_properties['widget_ue_connectable'][input_name] = true;
|
||||
this.did_conversion = true;
|
||||
Logger.log_info(`node ${node.id} widget ${input_name} marked as accepting UE because it was an input when saved`);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//node.properties.ue116converted = true;
|
||||
}
|
||||
|
||||
remove_saved_ue_links() {
|
||||
if (app.graph.extra?.links_added_by_ue) {
|
||||
app.graph.extra.links_added_by_ue.forEach((link) => { app.graph.links.delete(link); })
|
||||
remove_saved_ue_links_recursively(graph) {
|
||||
if (graph.extra?.links_added_by_ue) {
|
||||
graph.extra.links_added_by_ue.forEach((link_id) => { app.graph.links.delete(link_id); })
|
||||
}
|
||||
graph.nodes.filter((node)=>(node.subgraph)).forEach((node) => {this.remove_saved_ue_links_recursively(node.subgraph);});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const graphConverter = GraphConverter.instance();
|
||||
@@ -175,9 +208,8 @@ class LoopError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
function find_all_upstream(node_id, links_added) {
|
||||
function find_all_upstream(node, links_added) {
|
||||
const all_upstream = [];
|
||||
const node = get_real_node(node_id);
|
||||
node?.inputs?.forEach((input) => { // normal links
|
||||
const link_id = input.link;
|
||||
if (link_id) {
|
||||
@@ -186,48 +218,23 @@ function find_all_upstream(node_id, links_added) {
|
||||
}
|
||||
});
|
||||
links_added.forEach((la)=>{ // UE links
|
||||
if (get_real_node(la.downstream).id==node.id) {
|
||||
if (la.downstream==node.id) {
|
||||
all_upstream.push({id:la.upstream, slot:la.upstream_slot, ue:la.controller.toString()})
|
||||
}
|
||||
});
|
||||
if (node.id != get_group_node(node.id).id) { // node is in group
|
||||
const grp_nd = get_group_node(node.id).id;
|
||||
const group_data = GroupNodeHandler.getGroupData(get_group_node(node.id));
|
||||
const indx = group_data.nodeData.nodes.findIndex((n)=>n.pos[0]==node.pos[0] && n.pos[1]==node.pos[1]);
|
||||
if (indx>=0) {
|
||||
if (GroupNodeHandler.getGroupData(app.graph._nodes_by_id[grp_nd])?.linksTo?.[indx] ) { // links within group
|
||||
Object.values(GroupNodeHandler.getGroupData(app.graph._nodes_by_id[grp_nd]).linksTo[indx]).forEach((internal_link) => {
|
||||
all_upstream.push({id:`${grp_nd}:${internal_link[0]}`, slot:internal_link[1]});
|
||||
});
|
||||
}
|
||||
if (GroupNodeHandler.getGroupData(app.graph._nodes_by_id[grp_nd]).oldToNewInputMap?.[indx]) { // links out of group
|
||||
Object.values(GroupNodeHandler.getGroupData(app.graph._nodes_by_id[grp_nd]).oldToNewInputMap?.[indx]).forEach((groupInput) => {
|
||||
const link_id = get_group_node(node.id).inputs?.[groupInput]?.link;
|
||||
if (link_id) {
|
||||
const link = app.graph.links[link_id];
|
||||
if (link) all_upstream.push({id:link.origin_id, slot:link.origin_slot});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return all_upstream;
|
||||
}
|
||||
|
||||
function recursive_follow(node_id, start_node_id, links_added, stack, nodes_cleared, ues, count, slot) {
|
||||
const node = get_real_node(node_id);
|
||||
if (slot>=0 && GroupNodeHandler.isGroupNode(node)) { // link into group
|
||||
const mapped = GroupNodeHandler.getGroupData(node).newToOldOutputMap[slot];
|
||||
return recursive_follow(`${node.id}:${mapped.node.index}`, start_node_id, links_added, stack, nodes_cleared, ues, count, mapped.slot);
|
||||
}
|
||||
function recursive_follow(node, links_added, stack, nodes_cleared, ues, count, slot) {
|
||||
count += 1;
|
||||
if (stack.includes(node.id.toString())) throw new LoopError(node.id, new Set(stack), new Set(ues));
|
||||
if (nodes_cleared.has(node.id.toString())) return;
|
||||
stack.push(node.id.toString());
|
||||
|
||||
find_all_upstream(node.id, links_added).forEach((upstream) => {
|
||||
find_all_upstream(node, links_added).forEach((upstream) => {
|
||||
if (upstream.ue) ues.push(upstream.ue);
|
||||
count = recursive_follow(upstream.id, start_node_id, links_added, stack, nodes_cleared, ues, count, upstream.slot);
|
||||
count = recursive_follow(upstream, links_added, stack, nodes_cleared, ues, count, upstream.slot);
|
||||
if (upstream.ue) ues.pop();
|
||||
})
|
||||
|
||||
@@ -244,11 +251,11 @@ links_added is a list of the UE virtuals links
|
||||
function node_in_loop(live_nodes, links_added) {
|
||||
var nodes_to_check = [];
|
||||
const nodes_cleared = new Set();
|
||||
live_nodes.forEach((n)=>nodes_to_check.push(get_real_node(n.id).id));
|
||||
live_nodes.forEach((n)=>nodes_to_check.push(n));
|
||||
var count = 0;
|
||||
while (nodes_to_check.length>0) {
|
||||
const node_id = nodes_to_check.pop();
|
||||
count += recursive_follow(node_id, node_id, links_added, [], nodes_cleared, [], 0, -1);
|
||||
const node = nodes_to_check.pop();
|
||||
count += recursive_follow(node, links_added, [], nodes_cleared, [], 0, -1);
|
||||
nodes_to_check = nodes_to_check.filter((nid)=>!nodes_cleared.has(nid.toString()));
|
||||
}
|
||||
console.log(`node_in_loop made ${count} checks`)
|
||||
@@ -274,10 +281,11 @@ Given a link object, and the type of the link,
|
||||
go upstream, following links with the same type, until you find a parent node which isn't bypassed.
|
||||
If either type or original link is null, or if the upstream thread ends, return null
|
||||
*/
|
||||
function handle_bypass(original_link, type) {
|
||||
function handle_bypass(original_link, type, graph) {
|
||||
if (!type || !original_link) return null;
|
||||
var link = original_link;
|
||||
var parent = get_real_node(link.origin_id);
|
||||
if (link_is_from_subgraph_input(link)) return link
|
||||
var parent = get_real_node(link.origin_id, graph);
|
||||
if (!parent) return null;
|
||||
while (node_is_bypassed(parent)) {
|
||||
if (!parent.inputs) return null;
|
||||
@@ -285,69 +293,25 @@ function handle_bypass(original_link, type) {
|
||||
if (parent?.inputs[link.origin_slot]?.type == type) link_id = parent.inputs[link.origin_slot].link; // try matching number first
|
||||
else link_id = parent.inputs.find((input)=>input.type==type)?.link;
|
||||
if (!link_id) { return null; }
|
||||
link = app.graph.links[link_id];
|
||||
parent = get_real_node(link.origin_id);
|
||||
link = graph.links[link_id];
|
||||
parent = get_real_node(link.origin_id, graph);
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
function all_group_nodes() {
|
||||
return app.graph._nodes.filter((node) => GroupNodeHandler.isGroupNode(node));
|
||||
}
|
||||
|
||||
function is_in_group(node_id, group_node) {
|
||||
return group_node.getInnerNodes().find((inner_node) => (inner_node.id==node_id));
|
||||
}
|
||||
|
||||
/*
|
||||
Return the group node if this node_id is part of a group, else return the node itself.
|
||||
Returns a full node object
|
||||
*/
|
||||
function get_group_node(node_id) {
|
||||
const nid = node_id.toString();
|
||||
var gn = app.graph._nodes_by_id[nid];
|
||||
if (!gn && nid.includes(':')) gn = app.graph._nodes_by_id[nid.split(':')[0]];
|
||||
if (!gn) gn = all_group_nodes().find((group_node) => is_in_group(nid, group_node));
|
||||
if (!gn) Logger.log_error(`get_group node couldn't find ${nid}`)
|
||||
return gn;
|
||||
}
|
||||
|
||||
/*
|
||||
Return the node object for this node_id.
|
||||
- if it's in _nodes_by_id return it
|
||||
- if it is of the form x:y find it in group node x
|
||||
- if it is the real node number of something in a group, get it from the group
|
||||
*/
|
||||
function get_real_node(node_id) {
|
||||
const nid = node_id.toString();
|
||||
var rn = app.graph._nodes_by_id[nid];
|
||||
if (!rn && nid.includes(':')) rn = app.graph._nodes_by_id[nid.split(':')[0]]?.getInnerNodes()[nid.split(':')[1]]
|
||||
if (!rn) {
|
||||
all_group_nodes().forEach((node) => {
|
||||
if (!rn) rn = node.getInnerNodes().find((inner_node) => (inner_node.id==nid));
|
||||
})
|
||||
}
|
||||
if (!rn) Logger.log_info(`get_real_node couldn't find ${node_id} - ok during loading, shortly after node deletion etc.`)
|
||||
return rn;
|
||||
}
|
||||
|
||||
function get_all_nodes_within(node_id) {
|
||||
const node = get_group_node(node_id);
|
||||
if (GroupNodeHandler.isGroupNode(node)) return node.getInnerNodes();
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Does this input connect upstream to a live node?
|
||||
*/
|
||||
function is_connected(input, treat_bypassed_as_live) {
|
||||
function is_connected(input, treat_bypassed_as_live, graph) {
|
||||
const link_id = input.link;
|
||||
if (link_id === null) return false; // no connection
|
||||
var the_link = app.graph.links[link_id];
|
||||
var the_link = graph.links[link_id];
|
||||
if (!the_link) return false;
|
||||
if (treat_bypassed_as_live) return true;
|
||||
the_link = handle_bypass(the_link, the_link.type); // find the link upstream of bypasses
|
||||
the_link = handle_bypass(the_link, the_link.type, graph); // find the link upstream of bypasses
|
||||
if (!the_link) return false; // no source for data.
|
||||
return true;
|
||||
}
|
||||
@@ -359,14 +323,11 @@ function is_UEnode(node_or_nodeType) {
|
||||
const title = node_or_nodeType.type || node_or_nodeType.comfyClass;
|
||||
return ((title) && (title.startsWith("Anything Everywhere") || title==="Seed Everywhere" || title==="Prompts Everywhere"))
|
||||
}
|
||||
|
||||
function is_helper(node_or_nodeType) {
|
||||
const title = node_or_nodeType.type || node_or_nodeType.comfyClass;
|
||||
return ((title) && (title.startsWith("Simple String")))
|
||||
}
|
||||
function has_priority_boost(node_or_nodeType) {
|
||||
const title = node_or_nodeType.type || node_or_nodeType.comfyClass;
|
||||
return ((title) && (title == "Anything Everywhere?"))
|
||||
}
|
||||
|
||||
/*
|
||||
Inject a call into a method on object with name methodname.
|
||||
@@ -382,7 +343,7 @@ function inject(object, methodname, tracetext, injection, injectionthis, injecti
|
||||
}
|
||||
|
||||
|
||||
export { node_in_loop, handle_bypass, node_is_live, is_connected, is_UEnode, is_helper, inject, Logger, get_real_node, get_group_node, get_all_nodes_within, has_priority_boost}
|
||||
export { node_in_loop, handle_bypass, node_is_live, is_connected, is_UEnode, is_helper, inject, Logger, get_real_node }
|
||||
|
||||
export function defineProperty(instance, property, desc) {
|
||||
const existingDesc = Object.getOwnPropertyDescriptor(instance, property);
|
||||
@@ -437,4 +398,50 @@ export class Pausable {
|
||||
return (this.pause_depth>0)
|
||||
}
|
||||
on_unpause(){}
|
||||
}
|
||||
|
||||
export function get_connection(node, i, override_type) {
|
||||
const graph = node_graph(node)
|
||||
const in_link = node?.inputs[i]?.link;
|
||||
var type = override_type;
|
||||
var link = undefined;
|
||||
if (in_link) {
|
||||
if (!override_type) type = node.inputs[i].type;
|
||||
link = handle_bypass(graph.links[in_link], type, graph);
|
||||
}
|
||||
return { link:link, type:type }
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This is called in various places (node load, creation, link change) to ensure there is exactly one empty input
|
||||
*/
|
||||
export function fix_inputs(node) {
|
||||
if (!node.graph) return // node has been deleted prior to the fix
|
||||
if (node.properties.ue_properties.fixed_inputs) return
|
||||
|
||||
const empty_inputs = node.inputs.filter((inputslot)=>(inputslot.type=='*'))
|
||||
var excess_inputs = empty_inputs.length - 1
|
||||
|
||||
if (excess_inputs<0) {
|
||||
try {
|
||||
node.properties.ue_properties.next_input_index = (node.properties.ue_properties.next_input_index || 10) + 1
|
||||
node.addInput(`anything${node.properties.ue_properties.next_input_index}`, "*", {label:"anything"})
|
||||
fix_inputs(node)
|
||||
} catch (e) {
|
||||
Logger.log_error(e)
|
||||
}
|
||||
} else if (excess_inputs>0) {
|
||||
const idx = node.inputs.findIndex((inputslot)=>(inputslot.type=='*'))
|
||||
if (idx>=0) {
|
||||
try {
|
||||
node.removeInput(idx)
|
||||
fix_inputs(node)
|
||||
} catch (e) {
|
||||
Logger.log_error(e)
|
||||
}
|
||||
} else {
|
||||
Logger.log_problem(`Something very odd happened in fix_inputs for ${node.id}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "cg-use-everywhere"
|
||||
description = "A set of nodes that allow data to be 'broadcast' to some or all unconnected inputs. Greatly reduces link spaghetti."
|
||||
version = "6.2.2"
|
||||
version = "7.0"
|
||||
license = { file = "LICENSE" }
|
||||
|
||||
[project.urls]
|
||||
|
||||
@@ -46,6 +46,7 @@ class AnythingEverywherePrompts(Base):
|
||||
return ()
|
||||
|
||||
class AnythingEverywhereTriplet(Base):
|
||||
CATEGORY = "everywhere/deprecated"
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required":{},
|
||||
@@ -55,6 +56,7 @@ class AnythingEverywhereTriplet(Base):
|
||||
return ()
|
||||
|
||||
class AnythingSomewhere(Base):
|
||||
CATEGORY = "everywhere/deprecated"
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required":{},
|
||||
|
||||
Reference in New Issue
Block a user