From b5cde5936fef7329c09e5b4efc1527b21f51d05a Mon Sep 17 00:00:00 2001 From: "Dr.Lt.Data" Date: Sun, 28 Jan 2024 13:47:39 +0900 Subject: [PATCH] feat: double-click options --- __init__.py | 24 +++++++-- js/comfyui-manager.js | 26 ++++++++- js/node_fixer.js | 119 +++++++++++++++++++++++++++++++++++------- 3 files changed, 144 insertions(+), 25 deletions(-) diff --git a/__init__.py b/__init__.py index a788e447..f02dcee0 100644 --- a/__init__.py +++ b/__init__.py @@ -29,7 +29,7 @@ except: print(f"[WARN] ComfyUI-Manager: Your ComfyUI version is outdated. Please update to the latest version.") -version = [2, 4, 2] +version = [2, 5] version_str = f"V{version[0]}.{version[1]}" + (f'.{version[2]}' if len(version) > 2 else '') print(f"### Loading: ComfyUI-Manager ({version_str})") @@ -175,7 +175,8 @@ def write_config(): 'bypass_ssl': get_config()['bypass_ssl'], 'default_ui': get_config()['default_ui'], 'component_policy': get_config()['component_policy'], - "windows_selector_event_loop_policy": get_config()['windows_selector_event_loop_policy'], + 'double_click_policy': get_config()['double_click_policy'], + 'windows_selector_event_loop_policy': get_config()['windows_selector_event_loop_policy'], } with open(config_path, 'w') as configfile: config.write(configfile) @@ -196,6 +197,7 @@ def read_config(): 'bypass_ssl': default_conf['bypass_ssl'] if 'bypass_ssl' in default_conf else False, 'default_ui': default_conf['default_ui'] if 'default_ui' in default_conf else 'none', 'component_policy': default_conf['component_policy'] if 'component_policy' in default_conf else 'workflow', + 'double_click_policy': default_conf['double_click_policy'] if 'double_click_policy' in default_conf else 'copy-all', "windows_selector_event_loop_policy": default_conf['windows_selector_event_loop_policy'] if 'windows_selector_event_loop_policy' in default_conf else False, } @@ -209,7 +211,8 @@ def read_config(): 'bypass_ssl': False, 'default_ui': 'none', 'component_policy': 'workflow', - "windows_selector_event_loop_policy": False + 'double_click_policy': 'copy-all', + 'windows_selector_event_loop_policy': False } @@ -261,6 +264,10 @@ def set_component_policy(mode): get_config()['component_policy'] = mode +def set_double_click_policy(mode): + get_config()['double_click_policy'] = mode + + def try_install_script(url, repo_path, install_cmd): if platform.system() == "Windows" and comfy_ui_commit_datetime.date() >= comfy_ui_required_commit_datetime.date(): if not os.path.exists(startup_script_path): @@ -1786,6 +1793,17 @@ async def component_policy(request): return web.Response(status=200) +@server.PromptServer.instance.routes.get("/manager/dbl_click/policy") +async def dbl_click_policy(request): + if "value" in request.rel_url.query: + set_double_click_policy(request.rel_url.query['value']) + write_config() + else: + return web.Response(text=get_config()['double_click_policy'], status=200) + + return web.Response(status=200) + + @server.PromptServer.instance.routes.get("/manager/channel_url_list") async def channel_url_list(request): channels = get_channel_dict() diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js index 341f2a09..66f93750 100644 --- a/js/comfyui-manager.js +++ b/js/comfyui-manager.js @@ -17,12 +17,13 @@ import { SnapshotManager } from "./snapshot.js"; import { ModelInstaller } from "./model-downloader.js"; import { manager_instance, setManagerInstance, install_via_git_url, install_pip, rebootAPI, free_models } from "./common.js"; import { ComponentBuilderDialog, load_components, set_component_policy, getPureName } from "./components-manager.js"; +import { set_double_click_policy } from "./node_fixer.js"; var docStyle = document.createElement('style'); docStyle.innerHTML = ` #cm-manager-dialog { width: 1000px; - height: 495px; + height: 520px; box-sizing: content-box; z-index: 10000; } @@ -136,7 +137,7 @@ docStyle.innerHTML = ` .cm-notice-board { width: 290px; - height: 230px; + height: 270px; overflow: auto; color: var(--input-text); border: 1px solid var(--descrip-text); @@ -906,6 +907,26 @@ class ManagerMenuDialog extends ComfyDialog { set_component_policy(event.target.value); }); + let dbl_click_policy_combo = document.createElement("select"); + dbl_click_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use."); + dbl_click_policy_combo.className = "cm-menu-combo"; + dbl_click_policy_combo.appendChild($el('option', { value: 'none', text: 'Double-Click: None' }, [])); + dbl_click_policy_combo.appendChild($el('option', { value: 'copy-all', text: 'Double-Click: Copy All Connections' }, [])); + dbl_click_policy_combo.appendChild($el('option', { value: 'copy-input', text: 'Double-Click: Copy Input Connections' }, [])); + dbl_click_policy_combo.appendChild($el('option', { value: 'possible-input', text: 'Double-Click: Possible Input Connections' }, [])); + + api.fetchApi('/manager/dbl_click/policy') + .then(response => response.text()) + .then(data => { + dbl_click_policy_combo.value = data; + set_double_click_policy(data); + }); + + dbl_click_policy_combo.addEventListener('change', function (event) { + api.fetchApi(`/manager/dbl_click/policy?value=${event.target.value}`); + set_double_click_policy(event.target.value); + }); + api.fetchApi('/manager/share_option') .then(response => response.text()) .then(data => { @@ -935,6 +956,7 @@ class ManagerMenuDialog extends ComfyDialog { default_ui_combo, share_combo, component_policy_combo, + dbl_click_policy_combo, $el("br", {}, []), $el("br", {}, []), diff --git a/js/node_fixer.js b/js/node_fixer.js index 01d5b958..aae62aca 100644 --- a/js/node_fixer.js +++ b/js/node_fixer.js @@ -1,6 +1,16 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; +let double_click_policy = "copy-all"; + +api.fetchApi('/manager/dbl_click/policy') + .then(response => response.text()) + .then(data => set_double_click_policy(data)); + +export function set_double_click_policy(mode) { + double_click_policy = mode; +} + function addMenuHandler(nodeType, cb) { const getOpts = nodeType.prototype.getExtraMenuOptions; nodeType.prototype.getExtraMenuOptions = function () { @@ -36,7 +46,59 @@ function lookup_nearest_nodes(node) { return nearest_node; } -function node_info_copy(src, dest) { +function lookup_nearest_inputs(node) { + let input_map = {}; + + for(let i in node.inputs) { + let input = node.inputs[i]; + + if(input_map[input.type]) + continue; + + input_map[input.type] = {distance: Infinity, input_name: input.name, node: null, slot: null}; + } + + let x = node.pos[0] + node.size[0]/2; + let y = node.pos[1] + node.size[1]/2; + + for(let other of app.graph._nodes) { + if(other === node || !other.outputs) + continue; + + let dist = distance(node, other); + + for(let input_type in input_map) { + for(let j in other.outputs) { + let output = other.outputs[j]; + if(output.type == input_type) { + if(input_map[input_type].distance > dist) { + input_map[input_type].distance = dist; + input_map[input_type].node = other; + input_map[input_type].slot = parseInt(j); + } + } + } + } + } + + let res = {}; + for (let i in input_map) { + if (input_map[i].node) { + res[i] = input_map[i]; + } + } + + return res; +} + +function connect_inputs(nearest_inputs, node) { + for(let i in nearest_inputs) { + let info = nearest_inputs[i]; + info.node.connect(info.slot, node.id, info.input_name); + } +} + +function node_info_copy(src, dest, connect_both) { // copy input connections for(let i in src.inputs) { let input = src.inputs[i]; @@ -48,25 +110,27 @@ function node_info_copy(src, dest) { } // copy output connections - let output_links = {}; - for(let i in src.outputs) { - let output = src.outputs[i]; - if(output.links) { - let links = []; - for(let j in output.links) { - links.push(app.graph.links[output.links[j]]); + if(connect_both) { + let output_links = {}; + for(let i in src.outputs) { + let output = src.outputs[i]; + if(output.links) { + let links = []; + for(let j in output.links) { + links.push(app.graph.links[output.links[j]]); + } + output_links[output.name] = links; } - output_links[output.name] = links; } - } - for(let i in dest.outputs) { - let links = output_links[dest.outputs[i].name]; - if(links) { - for(let j in links) { - let link = links[j]; - let target_node = app.graph.getNodeById(link.target_id); - dest.connect(parseInt(i), target_node, link.target_slot); + for(let i in dest.outputs) { + let links = output_links[dest.outputs[i].name]; + if(links) { + for(let j in links) { + let link = links[j]; + let target_node = app.graph.getNodeById(link.target_id); + dest.connect(parseInt(i), target_node, link.target_slot); + } } } } @@ -81,15 +145,30 @@ app.registerExtension({ let orig_dblClick = node.onDblClick; node.onDblClick = () => { orig_dblClick?.apply?.(this, arguments); + if(node.inputs?.some(x => x.link != null) || node.outputs?.some(x => x.links != null && x.links.length > 0) ) return; if(!node.inputs && !node.outputs) return; - let src_node = lookup_nearest_nodes(node); - if(src_node) - node_info_copy(src_node, node); + switch(double_click_policy) { + case "copy-all": + case "copy-input": + { + let src_node = lookup_nearest_nodes(node); + if(src_node) + node_info_copy(src_node, node, double_click_policy == "copy-all"); + } + break; + case "possible-input": + { + let nearest_inputs = lookup_nearest_inputs(node); + if(nearest_inputs) + connect_inputs(nearest_inputs, node); + } + break; + } } },