Merge pull request #445 from chrisgoringe/7.7

7.7
This commit is contained in:
chrisgoringe
2026-02-23 10:16:52 +11:00
committed by GitHub
13 changed files with 80 additions and 60 deletions
+15 -5
View File
@@ -24,6 +24,12 @@ Yes. See Combo Clone node [here](https://github.com/chrisgoringe/cg-use-everywhe
# Recent Changes
## 7.7
- Added "Apply to unrepeated output" options
- Cleaned up restrictions dialog
- Internal refactoring to conform to ComfyUI changes
## 7.6
- Added visual indicators to subgraph nodes
@@ -31,7 +37,7 @@ Yes. See Combo Clone node [here](https://github.com/chrisgoringe/cg-use-everywhe
- Added "Send to Any" option in restrictions
<details>
<summary>Changes since 7.0</summary>
<summary>Older changes since 7.0</summary>
## 7.5
@@ -140,10 +146,6 @@ The node will only send data to another node if the regex matches the receiving
The Group and Colour restrictions will constrain the node to only send to nodes in (or not in) the same group, and of the same (or different) colour.
If you select multiple restrictions, all must be satisfied for the node to send.
If any restrictions are applied, the `Anything Everywhere` node gets a green circle in the top left hand corner, and a tooltip if you hover the mouse over it (as long as Show Links is not set to None).
`Repeated Types` determines behaviour when more than one input of the same type is connected to an `Anything Everywhere` node.
In this case an additional constraint is used to disambiguate which inputs match
by comparing the name of the input slot on the `Anything Everywhere` node with
@@ -151,8 +153,16 @@ either the name of the input slot on the target node, or the name of the target
The `match start` and `match end` options require that the input names match from the start (or end) for the full length of the shorter:
so you can match `seed` to `seed` or `noise_seed` by naming the `Anything Everywhere` input `seed` and selecting `Match end of input names`.
If `Apply to Unrepeated` is set to `Yes`, the `Repeated Types` rule will be applied to all outputs, whether or not they are repeated.
`Send To Any`(default 'no') can be used to allow this node to send data to inputs with the `Any` type.
`Priority` is used to resolve conflicts between multiple sending nodes. It has a default value (which generally follows the rule that the more restrictive the rules, the higher the priority), but you can override that value.
If you select multiple restrictions, all must be satisfied for the node to send.
If any restrictions are applied, the `Anything Everywhere` node gets a green circle in the top left hand corner, and a tooltip if you hover the mouse over it (as long as Show Links is not set to None).
You can rename input slots by right-clicking on the input dot - but you can't rename widget inputs - this is a limitation imposed by ComfyUI ([discussion](https://github.com/Comfy-Org/ComfyUI_frontend/issues/3654)). The work-around is to rename the target node (or use multiple `Anything Everywhere` nodes with other constraints, especially color matching).
</details>
+1 -1
View File
@@ -1,7 +1,7 @@
from comfy_api.latest import ComfyExtension, io
from .use_everywhere import SeedEverywhere, AnythingEverywhere, AnythingSomewhere, AnythingEverywhereTriplet, SimpleString, ComboClone, AnythingEverywherePrompts
UE_VERSION = "7.6.2"
UE_VERSION = "7.7"
WEB_DIRECTORY = "./js"
__all__ = [ "WEB_DIRECTORY"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 85 KiB

+1 -1
View File
@@ -1,4 +1,4 @@
export const VERSION = "7.6.2"
export const VERSION = "7.7"
class Shared {
constructor() {
+33 -16
View File
@@ -32,32 +32,49 @@
background-color: rgb(45 24 24 / 80%);
}
.ue_properties_table {
display: flex;
flex-direction: column;
}
.ue_editor_header {
width: 100%;
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;
width: 100%;
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;
border-bottom: thin dashed #ddd;
padding-bottom: 2px;
margin-bottom: 2px;
}
.ue_editor_body {
padding: 0px 4px;
.ue_properties_row {
display: flex;
flex-direction: row;
}
.ue_editor_body th {
.break_below {
border-bottom: thin dashed #ddd;
padding-bottom: 2px;
}
span.ue_properties_title {
text-align: right;
flex-grow: 1;
padding-right: 4px;
}
.ue_editor_body th::after {
.ue_properties_title::after {
content: ':';
}
.ue_editor_body td {
select {
width: 100%;
}
.ue_properties_cell {
flex-grow: 0;
width: 250px
}
.ue_editor_footer {
margin: 8px;
border: thin solid white;
@@ -67,7 +84,7 @@
.regex_input_container {
display: flex;
width: 250px;
width: 100%;
justify-content: space-between;
input {
flex-grow: 1;
+1
View File
@@ -71,6 +71,7 @@ const DEFAULT_PROPERTIES = {
group_regex_invert : false,
priority : undefined,
repeated_type_rule : 0,
apply_to_unrepeated : 0,
string_to_combo : 0,
send_to_any : 0
}
+19 -4
View File
@@ -23,16 +23,19 @@ export function edit_restrictions(a,b,c,d, node) { // a,b,c,d parameters are ign
}
function add_row(table, header) {
const row = document.createElement('tr')
const row = document.createElement('div')
row.className = 'ue_properties_row'
table.appendChild(row)
const header_elem = document.createElement('th')
const header_elem = document.createElement('span')
header_elem.className = 'ue_properties_title'
header_elem.innerText = header
row.appendChild(header_elem)
return row
}
function add_cell(row, cell) {
const td = document.createElement('td')
const td = document.createElement('span')
td.className = 'ue_properties_cell'
row.appendChild(td)
td.appendChild(cell)
}
@@ -52,7 +55,8 @@ function changed(node, property, value) {
}
function create_editor_html(node) {
const table = document.createElement('table')
const table = document.createElement('div')
table.className = 'ue_properties_table'
for (var i=0; i<=2; i++) {
const name = REGEXES[i]
@@ -74,6 +78,8 @@ function create_editor_html(node) {
create_element('input', contents, {type:'text', value:node.properties.ue_properties[`${name}_regex`] || ''}).
addEventListener('input', (e)=>{ changed(node, `${name}_regex`, e.target.value)})
if (i==2) { row.classList.add('break_below') }
add_cell(row,contents)
}
@@ -86,24 +92,32 @@ function create_editor_html(node) {
const col_select = document.createElement('select')
add_cell(col_row,col_select)
add_select_options(node, col_select, COLOR_RESTRICTION_OPTIONS, `color_restricted` )
col_row.classList.add('break_below')
const repeated_type_row = add_row(table, i18n("Repeated Types"))
const repeated_type_select = document.createElement('select')
add_cell(repeated_type_row,repeated_type_select)
add_select_options(node, repeated_type_select, REPEATED_TYPE_OPTIONS, `repeated_type_rule`)
const apply_to_unrepeated_row = add_row(table, i18n("Apply to Unrepeated"))
const apply_to_unrepeated_select = document.createElement('select')
add_cell(apply_to_unrepeated_row, apply_to_unrepeated_select)
add_select_options(node, apply_to_unrepeated_select, ["no", "yes"], `apply_to_unrepeated`)
apply_to_unrepeated_row.classList.add('break_below')
if (node.inputs.find((i)=>(i.type=="STRING"))) {
const send_to_combos_row = add_row(table, i18n("String to Combos"))
const send_to_combos_select = document.createElement('select')
add_cell(send_to_combos_row, send_to_combos_select)
add_select_options(node, send_to_combos_select, ["no", "yes"], `string_to_combo`)
send_to_combos_row.classList.add('break_below')
}
const send_to_any_row = add_row(table, i18n("Send to Any"))
const send_to_any_select = document.createElement('select')
add_cell(send_to_any_row, send_to_any_select)
add_select_options(node, send_to_any_select, ["no", "yes"], `send_to_any`)
send_to_any_row.classList.add('break_below')
const priority_row = add_row(table, i18n("Priority"))
const priority_edit = document.createElement("input")
@@ -116,6 +130,7 @@ function create_editor_html(node) {
priority_edit.id = 'priority_value'
if (!node.properties.ue_properties.priority) priority_edit.style.opacity = 0.5
add_cell(priority_row,priority_edit)
priority_row.classList.add('break_below')
return table
}
+3 -3
View File
@@ -66,7 +66,7 @@ function _convert_graph_to_links(graph, ues, control_node_id) {
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_node = graph.getNodeById(graph.last_node_id);
const new_subgraph = new_subgraph_node?.subgraph;
added_links.forEach(added_link => {
@@ -96,7 +96,7 @@ function _convert_graph_to_links(graph, ues, control_node_id) {
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]
const control_node_in_graph = graph.getNodeById(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)=>{
@@ -121,7 +121,7 @@ function _convert_graph_to_links(graph, ues, control_node_id) {
}
removed_links.forEach(llink => {
graph._nodes_by_id[llink.origin_id].connect(llink.origin_slot, graph._nodes_by_id[llink.target_id], llink.target_slot)
graph.getNodeById(llink.origin_id).connect(llink.origin_slot, graph.getNodeById(llink.target_id), llink.target_slot)
})
};
+1 -1
View File
@@ -334,7 +334,7 @@ export class UseEverywhereList {
if (connection.link && check_if_able_to_broadcast(node,i)) {
const input_regex = undefined
var additional_requirement = null
if (duplicated_broadcasted_types.has(connection.type)) {
if (duplicated_broadcasted_types.has(connection.type) || node.properties.ue_properties.apply_to_unrepeated) {
const input_name = possible.label || possible.name
const rule = node.properties.ue_properties?.repeated_type_rule || 0
if (rule == 0) { // 0 is exact match of input name
+1 -24
View File
@@ -40,7 +40,7 @@ export function fix_new_subgraph_node(node) {
subgraph.inputNode.slots.forEach((slot, i)=>{
slot.linkIds.forEach((lid) => {
const link = subgraph.links[lid]
const target = subgraph._nodes_by_id[link.target_id]
const target = subgraph.getNodeById(link.target_id)
const slot_name = target.inputs[link.target_slot].name
const ue_connectable = target.properties.ue_properties.widget_ue_connectable[slot_name]
if (ue_connectable) {
@@ -58,29 +58,6 @@ export function fix_new_subgraph_node(node) {
}
}
function fix_subgraph_widgets(node) {
return
// some code for Issue 390 - fixing widgets on subgraph nodes - which doesn't work yet
const graph = node.subgraph
const widgets = node.widgets
if (!graph) return
shared.graphAnalyser.analyse_graph(graph, true)
node.inputs.forEach((input,slot)=>{
if (!input.widget) {
const connected_widgets = graph.extra['ue_links'].
filter((uel)=>(uel.upstream=="-10" && uel.upstream_slot==slot)).
map((uel)=>graph._nodes_by_id[uel.downstream]?.inputs[uel.downstream_slot]?.widget).
filter((w)=>(w))
if (connected_widgets.length>0) {
const new_widget = node.addWidget()
input._widget = new_widget
input.widget = {name:new_widget.name}
}
}
})
}
ue_callbacks.register_allnode_callback('afterConfigureGraph', fix_subgraph_widgets, true )
class WrappedIONode {
constructor(io_node) {
this.io_node = io_node;
+3 -3
View File
@@ -169,7 +169,7 @@ export class LinkRenderController extends Pausable {
disable_all_connected_widgets( ) {
this.widgets_disabled = []
app.canvas.graph.extra['ue_links']?.forEach((uel) => {
const node = app.canvas.graph._nodes_by_id[uel.downstream]
const node = app.canvas.graph.getNodeById(uel.downstream)
if (node) {
const name = node.inputs[uel.downstream_slot]?.name;
if (name) {
@@ -337,7 +337,7 @@ export class LinkRenderController extends Pausable {
graph : m.node.graph,
sending_to : m.node,
input_index : m.node_index,
control_node : m.node.graph._nodes_by_id[m.id],
control_node : m.node.graph.getNodeById(m.id),
control_node_input_index : m.index,
type : m.type,
}, ctx, 0, "#F00")
@@ -388,7 +388,7 @@ export class LinkRenderController extends Pausable {
/* this is the end node; get the position of the input */
var pos2 = node.getConnectionPos(true, ue_connection.input_index, this.slot_pos1);
const control_node = graph._nodes_by_id[ue_connection.control_node.id]
const control_node = graph.getNodeById(ue_connection.control_node.id)
if (!control_node) {
Logger.problem(`Couldn't find position for UE link ${ue_connection}.`,null,true)
return;
+1 -1
View File
@@ -48,7 +48,7 @@ export function get_real_node(node_id, graph) {
const nid = node_id.toString();
if (nid==-10) return wrap_input(graph.inputNode); // special case for subgraph input
if (nid==-20) return wrap_input(graph.outputNode); // special case for subgraph input
return graph._nodes_by_id[nid];
return graph.getNodeById(nid);
}
export class Logger {
+1 -1
View File
@@ -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 = "7.6.2"
version = "7.7"
license = { file = "LICENSE" }
dependencies = [
"comfyui-frontend-package>=1.25.0"