| const { app } = window.comfyAPI.app; |
|
|
| function addNode(name, nextTo, options) { |
| options = { side: "left", select: true, shiftY: 0, shiftX: 0, ...(options || {}) }; |
| const node = LiteGraph.createNode(name); |
| const graph = app.canvas?.graph || app.graph; |
| graph.add(node); |
|
|
| node.pos = [ |
| options.side === "left" ? nextTo.pos[0] - (node.size[0] + options.offset): nextTo.pos[0] + nextTo.size[0] + options.offset, |
| nextTo.pos[1] + options.shiftY, |
| ]; |
|
|
| |
| if (options.side === "left") { |
| |
| if (node.outputs && node.outputs.length > 0 && nextTo.inputs && nextTo.inputs.length > 0) { |
| for (let i = 0; i < nextTo.inputs.length; i++) { |
| if (!nextTo.inputs[i].link) { |
| node.pos[1] += i * (node.size[1] + 32); |
| node.connect(0, nextTo, i); |
| break; |
| } |
| } |
| } |
| } else { |
| |
| if (nextTo.outputs && nextTo.outputs.length > 0 && node.inputs && node.inputs.length > 0) { |
| for (let o = 0; o < nextTo.outputs.length; o++) { |
| if (!nextTo.outputs[o].links || nextTo.outputs[o].links.length === 0) { |
| |
| node.pos[1] += o * (node.size[1] + 32); |
| for (let i = 0; i < node.inputs.length; i++) { |
| if (!node.inputs[i].link) { |
| nextTo.connect(o, node, i); |
| break; |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| if (options.select) { |
| app.canvas.selectNode(node, false); |
| } |
| return node; |
| } |
|
|
| |
| window.kjNodes = window.kjNodes || {}; |
| window.kjNodes.addNode = addNode; |
|
|
| app.registerExtension({ |
| name: "KJNodes.Contextmenu", |
| settings: [ |
| { |
| id: "KJNodes.helpPopup", |
| name: "Help popups", |
| category: ["KJNodes", "General", "Help popups"], |
| tooltip: "Show help popups when hovering over KJNodes", |
| defaultValue: true, |
| type: "boolean", |
| }, |
| { |
| id: "KJNodes.showSetGetInConnectionMenu", |
| name: "Show Set/Get in connection menu", |
| category: ["KJNodes", "Set & Get", "Show Set/Get in connection menu"], |
| tooltip: "Add Set/Get entries to the slot connection menu (next to Add Reroute)", |
| type: "boolean", |
| defaultValue: true, |
| }, |
| ], |
| getNodeMenuItems(node) { |
| try { |
| const items = []; |
|
|
| if (node.inputs && node.inputs.length > 0) { |
| const selectedForConvert = window.kjNodes.snapshotSelectedNodes(node); |
| items.push( |
| { |
| content: "Add GetNode", |
| callback: () => { addNode("GetNode", node, { side: "left", offset: 30 }); } |
| }, |
| { |
| content: "Add SetNode", |
| callback: () => { addNode("SetNode", node, { side: "right", offset: 30 }); } |
| }, |
| { |
| content: "Add PreviewAsTextNode", |
| callback: () => { addNode("PreviewAny", node, { side: "right", offset: 30 }); } |
| }, |
| { |
| content: "Convert all outputs to Set/Get", |
| callback: () => { |
| for (const n of selectedForConvert) window.kjNodes.convertOutputsToSetGet(node.graph, n); |
| } |
| }, |
| ); |
| } |
|
|
| const cls = node.constructor?.comfyClass || node.comfyClass; |
| if (cls && window.kjNodes?.recreateNode) { |
| items.push({ |
| content: "Recreate node", |
| has_submenu: true, |
| submenu: { |
| options: [ |
| { content: "Keep widget values", callback: () => window.kjNodes.recreateNode(node, cls, { resetValues: false }) }, |
| { content: "Reset widget values", callback: () => window.kjNodes.recreateNode(node, cls, { resetValues: true }) }, |
| ], |
| }, |
| }); |
| } |
|
|
| return items; |
| } catch (err) { |
| console.error("[KJNodes.Contextmenu] getNodeMenuItems failed:", err); |
| return []; |
| } |
| }, |
| async setup(app) { |
| const origShowConnectionMenu = app.canvas.showConnectionMenu.bind(app.canvas); |
|
|
| |
| app.canvas.showConnectionMenu = function(optPass) { |
| const showSetGet = app.ui.settings.getSettingValue("KJNodes.showSetGetInConnectionMenu") ?? true; |
| if (!showSetGet) return origShowConnectionMenu(optPass); |
|
|
| const isFrom = optPass.nodeFrom && optPass.slotFrom != null; |
| const nodeType = isFrom ? "SetNode" : "GetNode"; |
| const label = isFrom ? "Add SetNode" : "Add GetNode"; |
|
|
| const OrigCM = LiteGraph.ContextMenu; |
| let interceptActive = true; |
| LiteGraph.ContextMenu = function(options, menuOpts) { |
| LiteGraph.ContextMenu = OrigCM; |
| if (!interceptActive) return new OrigCM(options, menuOpts); |
| interceptActive = false; |
|
|
| const idx = options.indexOf("Add Reroute"); |
| if (idx !== -1) options.splice(idx + 1, 0, label); |
|
|
| const origCb = menuOpts.callback; |
| menuOpts.callback = function(v, cbOpts, e) { |
| if (v === label) { |
| const node = LiteGraph.createNode(nodeType); |
| if (node) { |
| const graph = app.canvas?.graph || app.graph; |
| node.pos = [optPass.e?.canvasX ?? 0, optPass.e?.canvasY ?? 0]; |
| graph.add(node); |
| if (isFrom && optPass.nodeFrom) { |
| const slotIdx = typeof optPass.slotFrom === 'number' |
| ? optPass.slotFrom |
| : optPass.nodeFrom.findOutputSlot(optPass.slotFrom.name); |
| optPass.nodeFrom.connect(slotIdx, node, 0); |
| } else if (optPass.nodeTo) { |
| const slotIdx = typeof optPass.slotTo === 'number' |
| ? optPass.slotTo |
| : optPass.nodeTo.findInputSlot(optPass.slotTo.name); |
| node.connect(0, optPass.nodeTo, slotIdx); |
| } |
| graph.change(); |
| app.canvas.setDirty(true, true); |
| } |
| return; |
| } |
| return origCb?.call(this, v, cbOpts, e); |
| }; |
|
|
| return new OrigCM(options, menuOpts); |
| }; |
| LiteGraph.ContextMenu.prototype = OrigCM.prototype; |
|
|
| const result = origShowConnectionMenu(optPass); |
| LiteGraph.ContextMenu = OrigCM; |
| interceptActive = false; |
| return result; |
| }; |
| } |
| }); |
|
|