Spaces:
Running
Running
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 () { | |
const r = getOpts.apply(this, arguments); | |
cb.apply(this, arguments); | |
return r; | |
}; | |
} | |
function distance(node1, node2) { | |
let dx = (node1.pos[0] + node1.size[0]/2) - (node2.pos[0] + node2.size[0]/2); | |
let dy = (node1.pos[1] + node1.size[1]/2) - (node2.pos[1] + node2.size[1]/2); | |
return Math.sqrt(dx * dx + dy * dy); | |
} | |
function lookup_nearest_nodes(node) { | |
let nearest_distance = Infinity; | |
let nearest_node = null; | |
for(let other of app.graph._nodes) { | |
if(other === node) | |
continue; | |
let dist = distance(node, other); | |
if (dist < nearest_distance && dist < 1000) { | |
nearest_distance = dist; | |
nearest_node = other; | |
} | |
} | |
return nearest_node; | |
} | |
function lookup_nearest_inputs(node) { | |
let input_map = {}; | |
for(let i in node.inputs) { | |
let input = node.inputs[i]; | |
if(input.link || input_map[input.type]) | |
continue; | |
input_map[input.type] = {distance: Infinity, input_name: input.name, node: null, slot: null}; | |
} | |
let x = node.pos[0]; | |
let y = node.pos[1] + node.size[1]/2; | |
for(let other of app.graph._nodes) { | |
if(other === node || !other.outputs) | |
continue; | |
let dx = x - (other.pos[0] + other.size[0]); | |
let dy = y - (other.pos[1] + other.size[1]/2); | |
if(dx < 0) | |
continue; | |
let dist = Math.sqrt(dx * dx + dy * dy); | |
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_shape) { | |
// copy input connections | |
for(let i in src.inputs) { | |
let input = src.inputs[i]; | |
if (input.widget !== undefined) { | |
const destWidget = dest.widgets.find(x => x.name === input.widget.name); | |
dest.convertWidgetToInput(destWidget); | |
} | |
if(input.link) { | |
let link = app.graph.links[input.link]; | |
let src_node = app.graph.getNodeById(link.origin_id); | |
src_node.connect(link.origin_slot, dest.id, input.name); | |
} | |
} | |
// copy output connections | |
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; | |
} | |
} | |
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); | |
} | |
} | |
} | |
} | |
if(copy_shape) { | |
dest.color = src.color; | |
dest.bgcolor = src.bgcolor; | |
dest.size = max(src.size, dest.size); | |
} | |
app.graph.afterChange(); | |
} | |
app.registerExtension({ | |
name: "Comfy.Manager.NodeFixer", | |
async nodeCreated(node, app) { | |
let orig_dblClick = node.onDblClick; | |
node.onDblClick = function (e, pos, self) { | |
orig_dblClick?.apply?.(this, arguments); | |
if((!node.inputs && !node.outputs) || pos[1] > 0) | |
return; | |
switch(double_click_policy) { | |
case "copy-all": | |
case "copy-full": | |
case "copy-input": | |
{ | |
if(node.inputs?.some(x => x.link != null) || node.outputs?.some(x => x.links != null && x.links.length > 0) ) | |
return; | |
let src_node = lookup_nearest_nodes(node); | |
if(src_node) | |
{ | |
let both_connection = double_click_policy != "copy-input"; | |
let copy_shape = double_click_policy == "copy-full"; | |
node_info_copy(src_node, node, both_connection, copy_shape); | |
} | |
} | |
break; | |
case "possible-input": | |
{ | |
let nearest_inputs = lookup_nearest_inputs(node); | |
if(nearest_inputs) | |
connect_inputs(nearest_inputs, node); | |
} | |
break; | |
case "dual": | |
{ | |
if(pos[0] < node.size[0]/2) { | |
// left: possible-input | |
let nearest_inputs = lookup_nearest_inputs(node); | |
if(nearest_inputs) | |
connect_inputs(nearest_inputs, node); | |
} | |
else { | |
// right: copy-all | |
if(node.inputs?.some(x => x.link != null) || node.outputs?.some(x => x.links != null && x.links.length > 0) ) | |
return; | |
let src_node = lookup_nearest_nodes(node); | |
if(src_node) | |
node_info_copy(src_node, node, true); | |
} | |
} | |
break; | |
} | |
} | |
}, | |
beforeRegisterNodeDef(nodeType, nodeData, app) { | |
addMenuHandler(nodeType, function (_, options) { | |
options.push({ | |
content: "Fix node (recreate)", | |
callback: () => { | |
let new_node = LiteGraph.createNode(nodeType.comfyClass); | |
new_node.pos = [this.pos[0], this.pos[1]]; | |
app.canvas.graph.add(new_node, false); | |
node_info_copy(this, new_node, true); | |
app.canvas.graph.remove(this); | |
}, | |
}); | |
}); | |
} | |
}); | |