diff --git a/README.md b/README.md index 9144686..6fb5d27 100644 --- a/README.md +++ b/README.md @@ -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
-Changes since 7.0 +Older changes since 7.0 ## 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).
diff --git a/__init__.py b/__init__.py index c338ad8..00d3dcd 100644 --- a/__init__.py +++ b/__init__.py @@ -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"] diff --git a/docs/restrictions.png b/docs/restrictions.png index ad8e957..29c50da 100644 Binary files a/docs/restrictions.png and b/docs/restrictions.png differ diff --git a/js/shared.js b/js/shared.js index 5c93ef6..bcdccd3 100644 --- a/js/shared.js +++ b/js/shared.js @@ -1,4 +1,4 @@ -export const VERSION = "7.6.2" +export const VERSION = "7.7" class Shared { constructor() { diff --git a/js/ue.css b/js/ue.css index 47f7697..574e16d 100644 --- a/js/ue.css +++ b/js/ue.css @@ -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; diff --git a/js/ue_properties.js b/js/ue_properties.js index da13aff..8d00f6b 100644 --- a/js/ue_properties.js +++ b/js/ue_properties.js @@ -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 } diff --git a/js/ue_properties_editor.js b/js/ue_properties_editor.js index 68a0dcb..9f5b3da 100644 --- a/js/ue_properties_editor.js +++ b/js/ue_properties_editor.js @@ -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 } diff --git a/js/use_everywhere_apply.js b/js/use_everywhere_apply.js index 9695ffb..947b9d1 100644 --- a/js/use_everywhere_apply.js +++ b/js/use_everywhere_apply.js @@ -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) }) }; diff --git a/js/use_everywhere_classes.js b/js/use_everywhere_classes.js index 4bea47c..1df2bff 100644 --- a/js/use_everywhere_classes.js +++ b/js/use_everywhere_classes.js @@ -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 diff --git a/js/use_everywhere_subgraph_utils.js b/js/use_everywhere_subgraph_utils.js index 3a8d593..3a7db4c 100644 --- a/js/use_everywhere_subgraph_utils.js +++ b/js/use_everywhere_subgraph_utils.js @@ -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; diff --git a/js/use_everywhere_ui.js b/js/use_everywhere_ui.js index c336733..0613d9c 100644 --- a/js/use_everywhere_ui.js +++ b/js/use_everywhere_ui.js @@ -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; diff --git a/js/use_everywhere_utilities.js b/js/use_everywhere_utilities.js index 6def3c4..bc16378 100644 --- a/js/use_everywhere_utilities.js +++ b/js/use_everywhere_utilities.js @@ -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 { diff --git a/pyproject.toml b/pyproject.toml index 8084cbd..08cc45a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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"