| 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); |
| app.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.nodeAutoColor", |
| name: "Auto-color nodes", |
| category: ["KJNodes", "Set & Get", "Auto-color nodes"], |
| tooltip: "Automatically color Set/Get nodes based on their connection type", |
| type: "boolean", |
| defaultValue: true, |
| }, |
| { |
| id: "KJNodes.disablePrefix", |
| name: "Disable Set_/Get_ prefix", |
| category: ["KJNodes", "Set & Get", "Disable Set_/Get_ prefix"], |
| tooltip: "Prevents automatically adding Set_ and Get_ prefixes to node titles", |
| defaultValue: false, |
| type: "boolean", |
| }, |
| { |
| 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.shiftMiddleClickSetGet", |
| name: "Shift+middle-click creates Set/Get", |
| category: ["KJNodes", "Set & Get", "Shift+middle-click creates Set/Get"], |
| tooltip: "Shift+middle-click on a slot creates a connected SetNode (output) or GetNode (input) instead of Reroute", |
| type: "boolean", |
| defaultValue: true, |
| }, |
| { |
| id: "KJNodes.middleClickSetGet", |
| name: "Middle-click creates Set/Get", |
| category: ["KJNodes", "Set & Get", "Middle-click creates Set/Get"], |
| tooltip: "Middle-click on a slot creates Set/Get instead of Reroute (overrides core behavior)", |
| type: "boolean", |
| defaultValue: false, |
| }, |
| { |
| 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) { |
| if (!node.inputs || node.inputs.length === 0) return []; |
| const selectedForConvert = window.kjNodes.snapshotSelectedNodes(node); |
| return [ |
| { |
| 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); |
| } |
| }, |
| ]; |
| }, |
| async setup(app) { |
| |
| const origShowConnectionMenu = app.canvas.showConnectionMenu.bind(app.canvas); |
| app.canvas.showConnectionMenu = function(optPass) { |
| if (!(app.ui.settings.getSettingValue("KJNodes.showSetGetInConnectionMenu") ?? true)) return origShowConnectionMenu(optPass); |
|
|
| const isFrom = optPass.nodeFrom && optPass.slotFrom; |
| const nodeType = isFrom ? "SetNode" : "GetNode"; |
| const label = isFrom ? "Add SetNode" : "Add GetNode"; |
|
|
| |
| const origEvent = optPass.e; |
|
|
| 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 canvas = app.canvas; |
| const graph = canvas.graph || app.graph; |
| const node = LiteGraph.createNode(nodeType); |
| if (!node || !graph) return; |
|
|
| |
| node.pos = [origEvent?.canvasX ?? 0, origEvent?.canvasY ?? 0]; |
| graph.add(node); |
|
|
| if (isFrom && optPass.nodeFrom) { |
| const slotObj = optPass.slotFrom; |
| const slotIdx = typeof slotObj === 'number' |
| ? slotObj |
| : optPass.nodeFrom.findOutputSlot(slotObj.name); |
| optPass.nodeFrom.connect(slotIdx, node, 0); |
| } else if (optPass.nodeTo) { |
| const slotObj = optPass.slotTo; |
| const slotIdx = typeof slotObj === 'number' |
| ? slotObj |
| : optPass.nodeTo.findInputSlot(slotObj.name); |
| node.connect(0, optPass.nodeTo, slotIdx); |
| } |
| graph.change(); |
| 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; |
| }; |
|
|
| |
| |
| let _fromMiddleClick = false; |
| let _lastMiddleClickShift = false; |
| const origProcessMiddle = LGraphCanvas.prototype._processMiddleButton; |
| LGraphCanvas.prototype._processMiddleButton = function(e, node) { |
| _fromMiddleClick = true; |
| _lastMiddleClickShift = !!e.shiftKey; |
| const wantSetGet = _lastMiddleClickShift |
| ? app.ui.settings.getSettingValue("KJNodes.shiftMiddleClickSetGet") ?? true |
| : app.ui.settings.getSettingValue("KJNodes.middleClickSetGet") ?? false; |
| const saved = LiteGraph.middle_click_slot_add_default_node; |
| if (wantSetGet) { |
| LiteGraph.middle_click_slot_add_default_node = true; |
| } else { |
| |
| const coreSetting = app.ui.settings.getSettingValue("Comfy.Node.MiddleClickRerouteNode"); |
| if (coreSetting === false) LiteGraph.middle_click_slot_add_default_node = false; |
| } |
| const result = origProcessMiddle.call(this, e, node); |
| LiteGraph.middle_click_slot_add_default_node = saved; |
| return result; |
| }; |
| const origCreateDefault = LGraphCanvas.prototype.createDefaultNodeForSlot; |
| LGraphCanvas.prototype.createDefaultNodeForSlot = function(optPass) { |
| if (!_fromMiddleClick) return origCreateDefault.call(this, optPass); |
| _fromMiddleClick = false; |
| const wantSetGet = _lastMiddleClickShift |
| ? app.ui.settings.getSettingValue("KJNodes.shiftMiddleClickSetGet") ?? true |
| : app.ui.settings.getSettingValue("KJNodes.middleClickSetGet") ?? false; |
| if (!wantSetGet) return origCreateDefault.call(this, optPass); |
|
|
| const isFrom = optPass.nodeFrom && optPass.slotFrom !== null; |
| const sourceNode = isFrom ? optPass.nodeFrom : optPass.nodeTo; |
| const slotIndex = isFrom ? optPass.slotFrom : optPass.slotTo; |
| if (!sourceNode || slotIndex == null) return origCreateDefault.call(this, optPass); |
| const graph = this.graph || app.graph; |
| if (!graph) return origCreateDefault.call(this, optPass); |
|
|
| const newNode = LiteGraph.createNode(isFrom ? "SetNode" : "GetNode"); |
| if (!newNode) return false; |
| const bounding = sourceNode.getBounding(); |
| newNode.pos = [ |
| isFrom ? bounding[0] + bounding[2] + 30 : bounding[0] - 200, |
| optPass.position?.[1] ?? bounding[1] |
| ]; |
| graph.add(newNode); |
| if (isFrom) sourceNode.connect(slotIndex, newNode, 0); |
| else newNode.connect(0, sourceNode, slotIndex); |
| graph.change(); |
| this.setDirty(true, true); |
| return true; |
| }; |
| } |
| }); |
|
|