From 76d32c98229677b357795248f7337bef2d62851c Mon Sep 17 00:00:00 2001 From: kijai Date: Fri, 29 Sep 2023 18:00:42 +0300 Subject: [PATCH] First commit Most stuff probably still buggy --- .gitignore | 6 + README.md | 7 + __init__.py | 4 + favicon-active.ico | Bin 0 -> 1025 bytes favicon.ico | Bin 0 -> 1006 bytes nodes.py | 110 +++++++++++ web/green.png | Bin 0 -> 2340 bytes web/js/appearance.js | 22 +++ web/js/browserstatus.js | 41 +++++ web/js/jsnodes.js | 30 +++ web/js/setgetnodes.js | 397 ++++++++++++++++++++++++++++++++++++++++ web/red.png | Bin 0 -> 2334 bytes 12 files changed, 617 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 __init__.py create mode 100644 favicon-active.ico create mode 100644 favicon.ico create mode 100644 nodes.py create mode 100644 web/green.png create mode 100644 web/js/appearance.js create mode 100644 web/js/browserstatus.js create mode 100644 web/js/jsnodes.js create mode 100644 web/js/setgetnodes.js create mode 100644 web/red.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a11d94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +__pycache__ +/venv +.vscode +*.ckpt +*.safetensors +*.pth \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f44d9ab --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# KJNodes for ComfyUI + +Various quality of life -nodes for ComfyUI, mostly just visual stuff to improve usability. + +# Installation + +1. Clone this repo into `custom_nodes` folder. \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..5109219 --- /dev/null +++ b/__init__.py @@ -0,0 +1,4 @@ +from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS + +WEB_DIRECTORY = "./web" +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] \ No newline at end of file diff --git a/favicon-active.ico b/favicon-active.ico new file mode 100644 index 0000000000000000000000000000000000000000..64045ab56e87879adb039b1fdb0bbbe0462143e7 GIT binary patch literal 1025 zcmV+c1pfPpP)2_cLzYkg${)#NUCQNmW45}rfL(sFrRhy>W9+WGH&(jQv0q23F*C5&_xuw7OW zFG>)pVJZ0(o;`}g#%Y)hXW-^-7Ed_>qBK$5*n&+C+hp~P+jDOxKMGD>_}y;jVPu;4 z>%R;nEsJh7iMc6)!&KFCKgyh6Ty4I?8hTBi@NKL(e}s$zFSy@*V3?;cA%itH<-Pi zXF~f>7P;&P^WjU&clGh+5f!U732Cv4o0lsf>x&N-&wCv+S~!B-ZUT31S78$^B$gZq zhE-&W6fI)FY&jqaEu5Jw;@U(D>eCKrj~Cp;)V0P-XHI=+v_WNAMr6v3#AX@&{x)1r zz%mFtZUgC>fw$gImX7BvFvcT(wy_z$=@iQB^VwcJXb<7*OPv$x40}w( zuTN?i_U)k5F;Z^6NNfc;U*{8MHjkI> z2AuglSXvWa#!wdA&;)0@s8}5v9HFwn5!vaw&Y3oUx6_4s?gf7MEd__7z^XK{yqQ6K zbrGs6!LWc^kA3K~5&P={c92eg8VWkCi&xIh*}V;Vc_fKjv1uT77dwePWOw#axc?Kj v$S4fnj`;C0`b-&0%Xnon-~gi>{uf{XkwLT^zYmgi00000NkvXXu0mjf;d$w6 literal 0 HcmV?d00001 diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..08df2481551bb6903735fa69d658b5abfb0a5ae1 GIT binary patch literal 1006 zcmVY8+a0h?mq>Mi zx$acL0<|S&Avt#Ttuw`&p~ZnaS~#8_2uC&2$YwUfos&5-eI;1q5h(cw#NttJ^mk6Z z(2?iZ2fQTI4DHg}Q=`^`f3hWVoQO`I;%I*WtaRMzUW%#k1qNTd0N_g@rqhEDkI&pMfMxk#8pd&uG*B0MBou41SMJP`_p9iO%1H2aK&Vj%7S4XU%8hrx1V~4RZz|W$LkD!j`*&yPx#bKzw*^j(1d5?=b^zOgpa6=~333aL z>%rxDuN?|4a}%*RHa2rDhwA->N&7=R|2MIZdmvsHG?2bu0MSiwAqKtf!7>LyT=VC> zw)=Iq)_ux04+YE$1x%;nIk(=h?>?xC;n*nNzcCb`;889qFq~6N1(3aN6NP_Q8{L7} zH$)lCWw-KRr`iNAOeKXgpaG zsh3N^sf!17Fy`A(Bibkx$+cFcLEal3z|w34YAOzx2JT!}@aepW1bYF)(ve83vxXzi z|MFM&vDp_vlU9LjuW$FMYxGRZ$4R>ZRR-E?J1|OzcvW+eXy(z=n+QckXps;sKJ3*E zAAQ0R8sa$;3TrnNnI(tPaU53)DDCFJbxrtf9hLF{GAmC&gLTmF2QY06%S5>{rhri9 z)6sO;zxK||Z7aBgZ*`%@Bs{wR5WD&RP~IzG<%fG1ITwMXL{ZtUVKC_hP~D4{Q!0eu cm;V=F0ROq3b07*qoM6N<$g8zZc;s5{u literal 0 HcmV?d00001 diff --git a/nodes.py b/nodes.py new file mode 100644 index 0000000..37059dd --- /dev/null +++ b/nodes.py @@ -0,0 +1,110 @@ +import nodes + +class INTConstant: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "value": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + }, + } + + RETURN_TYPES = ("INT",) + RETURN_NAMES = ("value",) + FUNCTION = "get_value" + + CATEGORY = "KJNodes" + + def get_value(self, value): + return (value,) + +class ConditioningMultiCombine: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "conditioning_1": ("CONDITIONING", ), + "conditioning_2": ("CONDITIONING", ), + }} + + RETURN_TYPES = ("CONDITIONING",) + FUNCTION = "combine" + CATEGORY = "KJNodes" + + def combine(self, combine, **kwargs): + cond_combine_node = nodes.ConditioningCombine() + cond = kwargs["c1"] + for c in range(1, combine): + new_cond = kwargs[f"c{c + 1}"] + cond = cond_combine_node.combine(new_cond, cond)[0] + return (cond,) + +class ConditioningSetMaskAndCombine: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "positive_1": ("CONDITIONING", ), + "negative_1": ("CONDITIONING", ), + "positive_2": ("CONDITIONING", ), + "negative_2": ("CONDITIONING", ), + "mask_1": ("MASK", ), + "mask_2": ("MASK", ), + "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "set_cond_area": (["default", "mask bounds"],), + } + } + + RETURN_TYPES = ("CONDITIONING","CONDITIONING",) + RETURN_NAMES = ("combined_positive", "combined_negative",) + FUNCTION = "append" + CATEGORY = "KJNodes" + + def append(self, positive_1, negative_1, positive_2, negative_2, mask_1, mask_2, set_cond_area, strength): + c = [] + c2 = [] + set_area_to_bounds = False + if set_cond_area != "default": + set_area_to_bounds = True + if len(mask_1.shape) < 3: + mask_1 = mask_1.unsqueeze(0) + if len(mask_2.shape) < 3: + mask_2 = mask_2.unsqueeze(0) + for t in positive_1: + n = [t[0], t[1].copy()] + _, h, w = mask_1.shape + n[1]['mask'] = mask_1 + n[1]['set_area_to_bounds'] = set_area_to_bounds + n[1]['mask_strength'] = strength + c.append(n) + for t in positive_2: + n = [t[0], t[1].copy()] + _, h, w = mask_2.shape + n[1]['mask'] = mask_2 + n[1]['set_area_to_bounds'] = set_area_to_bounds + n[1]['mask_strength'] = strength + c.append(n) + for t in negative_1: + n = [t[0], t[1].copy()] + _, h, w = mask_1.shape + n[1]['mask'] = mask_1 + n[1]['set_area_to_bounds'] = set_area_to_bounds + n[1]['mask_strength'] = strength + c2.append(n) + for t in negative_2: + n = [t[0], t[1].copy()] + _, h, w = mask_2.shape + n[1]['mask'] = mask_2 + n[1]['set_area_to_bounds'] = set_area_to_bounds + n[1]['mask_strength'] = strength + c2.append(n) + return (c, c2) + +NODE_CLASS_MAPPINGS = { + "INTConstant": INTConstant, + "ConditioningMultiCombine": ConditioningMultiCombine, + "ConditioningSetMaskAndCombine": ConditioningSetMaskAndCombine, +} +NODE_DISPLAY_NAME_MAPPINGS = { + "INTConstant": "INT Constant", + "ConditioningMultiCombine": "Conditioning Multi Combine", + "ConditioningSetMaskAndCombine": "ConditioningSetMaskAndCombine", +} \ No newline at end of file diff --git a/web/green.png b/web/green.png new file mode 100644 index 0000000000000000000000000000000000000000..900964e4b3907145fe1e75a5b58473567450e16d GIT binary patch literal 2340 zcmbtW`#%$GAKsW8vWHqiVq|h^IhC0+IUk24a%inh6JicIA5xx>Q-^t(!>AZd4O4n+ zA}t|CX_;sqhvbk$$P)HW@AG-yKj6JT_w~KL_jP^m`+NU(-&rU(d$7a-2><{9c66}u z6tMCR#C8c{R6@e4fDEyx9dWL%T7ncHCxC+hz<*c>cmN>sw;~2q_$QYZXn;_J+Rird zOLlfqJL}(LSnF}0AQ6vn@D+%=_wE4rZZu_6AWB8so{qM|hhhPyG2zkp=uqr=^E*u+ zi2_S*$Ff03V*&cQ`UX0>NF9A6WxeCZ$BrB8BI>-rX9P9UJ2fZqVOW5!uJPYfZ@%O3 z%bk9|k0zjhn|kdYGKT~rct<=T&q zpT`pZ*XF3+u~w@G@&JHnildFy$MO{suqX5{DGKPCwOWA#z7kdE>7Ya=Tzy~A?Kpz_ z#_L>2xcnz;{q5Ukh%q}3*H;?VI-NA#Sq4@C<>>iouIJ~NK5?ekX%D_1JlcHX8~QTX zf?xH*DWJx0IVLrct}d`@FQHAU)(A!gr`WOj5Km7Jid3pEw^Qet z0?8(ZB!I!^9K+`rHvckh)Mp~WU+Tqc)(WrXP{q3Fm|O|(`a9d7bFup7iK{)2lUAE> zUMo27`ZfwgU-VdBS2^HFHF-viIj%80CfrokGguTVW0!J+&7!~+gnZhkthzX?0dud4 zTnp2m-)M20RNtt5ik5cIa2>st#C`@dPKuT+V(pE3>7xr8K*v`X(UM_DBoX7xuFMa- zWQDQH2q%U*;4HniynOpg^7`a0b{W%(BBnrQkoprz+W~EvX&S&f1Iv%T5yeCOHs`3m zX^g3U#gwzthY?|tOV9v@F4=)F5T4SA47Tvwtc!UD#ef(YCe-QN3mD+7+PNOsN6G0E z!Is;l*XHk3BPCRw4Uf0jNj*CN&ARaRQfuCOcIt6J*j`Q zZq5fU{O{WH-wU=hR>r*yYN7Q=JN{MIH7?EI&Gsu#p2wP+rEdP%42Db3>GYvW#L(LR z99+8qX&X(wCLGe`UvI(+Yxi-M3^VO2(R1b$OBo_@IUU;7uG{)ix18JnK{KVzkKQI{ zHjRQladEO-p`@?eTS{1`P&*2PuXTwW)rMI_K^_V`>=> z#|4p)&@VOa4u!oxRDw$sD-M38h&#|9ctC@z(oPYY9Yu@1VDaULu4jExCw;lfDcp@N zd{trHqx^b**4M0Jg9gayGm6byuJ^J7r)ci!@saH3MX(t$CD1FQ4C4FBG;CnZl^A#J zmN=m3ir4K2&b*W8D1d{NZ zFozHaeD?_FBcM`cA=&Y4{PPo}CLM(yyhd7UFwJQW7)zM7a9FsH{iI4|C1lyMpRqzF zo_%P1vzhZ0vJiqd)i{#vM2!7qzfeJ{?HqvDKxA+3{5YwA$OHKo6hWYK2dz4-7N| zf=?neB(efF2{xUKe)~9Y(o*+!%qJziK{FJ~^1i~oDWmH_3kz^@FaDjdckb~xv+859 zM1SN=ZuG)B!-KpDm?w*Ha41*(hws*+v&Hf+^a_HiVo%s!N&y_9DnyO zI#_6oqIcUQV~7H86lXmtOO4<#WCPn5=Nh%$8~0?)nVPo_mNp>GEKQPBV|w$~BZM%A zeA~qR^B~uQvt@xZiXM2z+S$nB4ZK^{e8BG5BKwf%7%rN}H3Y=Gncr%`qf>j1x>fv9 zrWvTKI9dSSGv`sYD4ZZ+Y1!3~R?raY_!?Z(AgXFMnDn-17Dww1^1bvt!~(D=QK zryh-P-t65YU85FFR*JxAYNh4gtqyWHlR8n(zhCe68O$2z$K(7WY{!6AG#~5-xbC|Nl=5z|q#t=Arf3EB^rinH$Xj literal 0 HcmV?d00001 diff --git a/web/js/appearance.js b/web/js/appearance.js new file mode 100644 index 0000000..305c58a --- /dev/null +++ b/web/js/appearance.js @@ -0,0 +1,22 @@ +import { app } from "../../../scripts/app.js"; + + +app.registerExtension({ + name: "KJNodes.appearance", + nodeCreated(node) { + const title = node.getTitle(); + switch (title) { + case "INT Constant": + node.setSize([200, 58]); + node.color = LGraphCanvas.node_colors.green.color; + node.bgcolor = LGraphCanvas.node_colors.green.bgcolor; + break; + case "ConditioningMultiCombine": + + node.color = LGraphCanvas.node_colors.brown.color; + node.bgcolor = LGraphCanvas.node_colors.brown.bgcolor; + break; + + } + } +}); diff --git a/web/js/browserstatus.js b/web/js/browserstatus.js new file mode 100644 index 0000000..c796b6f --- /dev/null +++ b/web/js/browserstatus.js @@ -0,0 +1,41 @@ +import { api } from "../../../scripts/api.js"; +import { app } from "../../../scripts/app.js"; + +app.registerExtension({ + name: "KJNodes.browserstatus", + setup() { + api.addEventListener("status", ({ detail }) => { + let title = "ComfyUI"; + let favicon = "green"; + let queueRemaining = detail && detail.exec_info.queue_remaining; + + if (queueRemaining) { + favicon = "red"; + + title = `00% - ${queueRemaining} | ${title}`; + } + let link = document.querySelector("link[rel~='icon']"); + if (!link) { + link = document.createElement("link"); + link.rel = "icon"; + document.head.appendChild(link); + } + link.href = new URL(`../${favicon}.png`, import.meta.url); + document.title = title; + + }); +//add progress to the title + api.addEventListener("progress", ({ detail }) => { + const { value, max } = detail; + const progress = Math.floor((value / max) * 100); + let title = document.title; + + if (!isNaN(progress) && progress >= 0 && progress <= 100) { + const paddedProgress = String(progress).padStart(2, '0'); + title = `${paddedProgress}% ${title.replace(/^\d+%\s/, '')}`; + } + + document.title = title; + }); + }, +}); \ No newline at end of file diff --git a/web/js/jsnodes.js b/web/js/jsnodes.js new file mode 100644 index 0000000..bd8998b --- /dev/null +++ b/web/js/jsnodes.js @@ -0,0 +1,30 @@ +import { app } from "../../../scripts/app.js"; + +app.registerExtension({ + name: "KJNodes.ConditioningMultiCombine", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + switch (nodeData.name) { + case "ConditioningMultiCombine": + nodeType.prototype.onNodeCreated = function () { + this.inputs_offset = nodeData.name.includes("selective")?1:0 + this.cond_type = "CONDITIONING" + this.addWidget("button", "Add", null, () => { + if (!this.inputs) { + this.inputs = []; + } + if (this.inputs.length < 20) { + const newInputName = `conditioning_${this.inputs.length + 1}`; + this.addInput(newInputName, this.cond_type); + } + }); + this.addWidget("button", "Remove", null, () => { + if (this.inputs.length > 2) { + const lastInputIndex = this.inputs.length - 1; + this.removeInput(lastInputIndex); + } + }); + } + break; + } + }, +}); \ No newline at end of file diff --git a/web/js/setgetnodes.js b/web/js/setgetnodes.js new file mode 100644 index 0000000..e8165f9 --- /dev/null +++ b/web/js/setgetnodes.js @@ -0,0 +1,397 @@ +import { app } from "../../../scripts/app.js"; +import { ComfyWidgets } from '../../../scripts/widgets.js'; +//based on diffus3's SetGet: https://github.com/diffus3/ComfyUI-extensions + +// Nodes that allow you to tunnel connections for cleaner graphs +app.registerExtension({ + name: "SetNode", + registerCustomNodes() { + class SetNode { + defaultVisibility = true; + serialize_widgets = true; + constructor() { + if (!this.properties) { + this.properties = { + "previousName": "" + }; + } + this.properties.showOutputText = SetNode.defaultVisibility; + + const node = this; + + + this.addWidget( + "text", + "Constant", + '', + (s, t, u, v, x) => { + node.validateName(node.graph); + this.update(); + this.properties.previousName = this.widgets[0].value; + }, + {} + ) + + this.addInput("*", "*"); + this.addOutput("*", '*'); + + + this.onConnectionsChange = function( + slotType, //1 = input, 2 = output + slot, + isChangeConnect, + link_info, + output + ) { + //On Disconnect + if (slotType == 1 && !isChangeConnect) { + this.inputs[slot].type = '*'; + this.inputs[slot].name = '*'; + this.title = "Set" + } + if (slotType == 2 && !isChangeConnect) { + this.outputs[slot].type = '*'; + this.outputs[slot].name = '*'; + } + //On Connect + if (link_info && node.graph && slotType == 1 && isChangeConnect) { + + const fromNode = node.graph._nodes.find((otherNode) => otherNode.id == link_info.origin_id); + const type = fromNode.outputs[link_info.origin_slot].type; + this.title = "Set_" + type; + if (this.widgets[0].value == ''){ + this.widgets[0].value = type + } + + this.validateName(node.graph); + this.inputs[0].type = type; + this.inputs[0].name = type; + + switch (type) { + case "MODEL": + this.color = LGraphCanvas.node_colors.blue.color; + this.bgcolor = LGraphCanvas.node_colors.blue.bgcolor; + break; + case "LATENT": + this.color = LGraphCanvas.node_colors.purple.color; + this.bgcolor = LGraphCanvas.node_colors.purple.bgcolor; + break; + case "VAE": + this.color = LGraphCanvas.node_colors.red.color; + this.bgcolor = LGraphCanvas.node_colors.red.bgcolor; + break; + case "CONDITIONING": + this.color = LGraphCanvas.node_colors.brown.color; + this.bgcolor = LGraphCanvas.node_colors.brown.bgcolor; + break; + case "IMAGE": + this.color = LGraphCanvas.node_colors.pale_blue.color; + this.bgcolor = LGraphCanvas.node_colors.pale_blue.bgcolor; + break; + case "CLIP": + this.color = LGraphCanvas.node_colors.yellow.color; + this.bgcolor = LGraphCanvas.node_colors.yellow.bgcolor; + break; + case "INT": + this.color = LGraphCanvas.node_colors.green.color; + this.bgcolor = LGraphCanvas.node_colors.green.bgcolor; + break; + } + } + if (link_info && node.graph && slotType == 2 && isChangeConnect) { + const fromNode = node.graph._nodes.find((otherNode) => otherNode.id == link_info.origin_id); + const type = fromNode.inputs[link_info.origin_slot].type; + + this.outputs[0].type = type; + this.outputs[0].name = type; + } + + + //Update either way + this.update(); + } + + this.validateName = function(graph) { + let widgetValue = node.widgets[0].value; + + if (widgetValue != '') { + let tries = 0; + let collisions = []; + + do { + collisions = graph._nodes.filter((otherNode) => { + if (otherNode == this) { + return false; + } + if (otherNode.type == 'SetNode' && otherNode.widgets[0].value === widgetValue) { + return true; + } + return false; + }) + if (collisions.length > 0) { + widgetValue = node.widgets[0].value + "_" + tries; + } + tries++; + } while (collisions.length > 0) + node.widgets[0].value = widgetValue; + this.update(); + } + } + + this.clone = function () { + console.log("CLONE"); + const cloned = SetNode.prototype.clone.apply(this); + cloned.inputs[0].name = '*'; + cloned.inputs[0].type = '*'; + cloned.value = ''; + cloned.properties.previousName = ''; + cloned.size = cloned.computeSize(); + return cloned; + }; + + this.onAdded = function(graph) { + this.validateName(graph); + } + + + this.update = function() { + if (node.graph) { + this.findGetters(node.graph).forEach((getter) => { + getter.setType(this.inputs[0].type); + }); + if (this.widgets[0].value) { + this.findGetters(node.graph, true).forEach((getter) => { + getter.setName(this.widgets[0].value) + }); + } + + const allGetters = node.graph._nodes.filter((otherNode) => otherNode.type == "GetNode"); + allGetters.forEach((otherNode) => { + if (otherNode.setComboValues) { + otherNode.setComboValues(); + } + }) + } + } + + + this.findGetters = function(graph, checkForPreviousName) { + const name = checkForPreviousName ? this.properties.previousName : this.widgets[0].value; + return graph._nodes.filter((otherNode) => { + if (otherNode.type == 'GetNode' && otherNode.widgets[0].value === name && name != '') { + return true; + } + return false; + }) + } + + + // This node is purely frontend and does not impact the resulting prompt so should not be serialized + this.isVirtualNode = true; + } + + onRemoved() { + console.log("onRemove"); + console.log(this); + console.log(this.flags); + const allGetters = this.graph._nodes.filter((otherNode) => otherNode.type == "GetNode"); + allGetters.forEach((otherNode) => { + if (otherNode.setComboValues) { + otherNode.setComboValues([this]); + } + }) + } + } + + + LiteGraph.registerNodeType( + "SetNode", + Object.assign(SetNode, { + title: "Set", + }) + ); + + SetNode.category = "KJNodes"; + }, +}); + + +app.registerExtension({ + name: "GetNode", + registerCustomNodes() { + class GetNode { + + defaultVisibility = true; + serialize_widgets = true; + + constructor() { + if (!this.properties) { + this.properties = {}; + } + this.properties.showOutputText = GetNode.defaultVisibility; + + const node = this; + this.addWidget( + "combo", + "Constant", + "", + (e) => { + this.onRename(); + }, + { + values: () => { + const setterNodes = graph._nodes.filter((otherNode) => otherNode.type == 'SetNode'); + return setterNodes.map((otherNode) => otherNode.widgets[0].value).sort(); + } + } + ) + + + this.addOutput("*", '*'); + + + this.onConnectionsChange = function( + slotType, //0 = output, 1 = input + slot, //self-explanatory + isChangeConnect, + link_info, + output + ) { + this.validateLinks(); + } + + + this.setName = function(name) { + console.log("renaming getter: "); + console.log(node.widgets[0].value + " -> " + name); + node.widgets[0].value = name; + node.onRename(); + node.serialize(); + + } + + + this.onRename = function() { + console.log("onRename"); + + const setter = this.findSetter(node.graph); + if (setter) { + let linkType = (setter.inputs[0].type); + + this.setType(linkType); + this.title = "Get_" + setter.inputs[0].type; + + switch (linkType) { + case "MODEL": + this.color = LGraphCanvas.node_colors.blue.color; + this.bgcolor = LGraphCanvas.node_colors.blue.bgcolor; + break; + case "LATENT": + this.color = LGraphCanvas.node_colors.purple.color; + this.bgcolor = LGraphCanvas.node_colors.purple.bgcolor; + break; + case "VAE": + this.color = LGraphCanvas.node_colors.red.color; + this.bgcolor = LGraphCanvas.node_colors.red.bgcolor; + break; + case "CONDITIONING": + this.color = LGraphCanvas.node_colors.brown.color; + this.bgcolor = LGraphCanvas.node_colors.brown.bgcolor; + break; + case "IMAGE": + this.color = LGraphCanvas.node_colors.pale_blue.color; + this.bgcolor = LGraphCanvas.node_colors.pale_blue.bgcolor; + break; + case "CLIP": + this.color = LGraphCanvas.node_colors.yellow.color; + this.bgcolor = LGraphCanvas.node_colors.yellow.bgcolor; + break; + case "INT": + this.color = LGraphCanvas.node_colors.green.color; + this.bgcolor = LGraphCanvas.node_colors.green.bgcolor; + break; + } + + } else { + this.setType('*'); + } + } + + this.clone = function () { + const cloned = GetNode.prototype.clone.apply(this); + cloned.size = cloned.computeSize(); + return cloned; + }; + + this.validateLinks = function() { + console.log("validating links"); + if (this.outputs[0].type != '*' && this.outputs[0].links) { + console.log("in"); + this.outputs[0].links.forEach((linkId) => { + const link = node.graph.links[linkId]; + if (link && link.type != this.outputs[0].type && link.type != '*') { + console.log("removing link"); + node.graph.removeLink(linkId) + } + }) + } + } + + this.setType = function(type) { + this.outputs[0].name = type; + this.outputs[0].type = type; + this.validateLinks(); + } + + this.findSetter = function(graph) { + const name = this.widgets[0].value; + return graph._nodes.find((otherNode) => { + if (otherNode.type == 'SetNode' && otherNode.widgets[0].value === name && name != '') { + return true; + } + return false; + }) + } + + // This node is purely frontend and does not impact the resulting prompt so should not be serialized + this.isVirtualNode = true; + } + + + getInputLink(slot) { + const setter = this.findSetter(this.graph); + + if (setter) { + const slot_info = setter.inputs[slot]; + console.log("slot info"); + console.log(slot_info); + console.log(this.graph.links); + const link = this.graph.links[ slot_info.link ]; + console.log("link:"); + console.log(link); + return link; + } else { + console.log(this.widgets[0]); + console.log(this.widgets[0].value); + throw new Error("No SetNode found for " + this.widgets[0].value + "(" + this.type + ")"); + } + + } + onAdded(graph) { + //this.setComboValues(); + //this.validateName(graph); + } + + } + + + LiteGraph.registerNodeType( + "GetNode", + Object.assign(GetNode, { + title: "Get", + }) + ); + + GetNode.category = "KJNodes"; + }, +}); diff --git a/web/red.png b/web/red.png new file mode 100644 index 0000000000000000000000000000000000000000..4352c118b2c5fa6f33edc4d99a5e4d22649ff827 GIT binary patch literal 2334 zcmbtW`9IWa8~)B%vQxIg5kr*7kj9!p_84Rg!dS=BV6sJwgifJ_Y#rnn*=j;%-=!=u zmd27}!WlAX>`Np2J9^LOynn#^eD3SM?&rGhXZh`U60H#?yj)^j008isnHpYWveXec z*qJ#bGIEniFpPtlx22^5vjk2uVaf#PUj~E30JeV=4v_f2wE$BC{MFAKE#t%)8;2at ze+`O4O+T~XylLvl6uD0v0W>&qXOAiJhZ;GA8vFTU0G+VFP`^-rjF)aki7&-R=*gODx3J4*kjZXB0&plHGNZKp>2vK2UFy3iLPzHe+pBy`ixw| z8MS>Kv619JsF>%tK7(VivdRg!UUc%TBw*??TQ;6gKBaf!Wi zT0p(ibF0Ib+RwMUkUnL(w|DCF9Bl&uh+Gl;`-)O}UJI#SG|zuP@v_cDP{`wA5>Fww zKBvPVGB%e@mZP6_mJ%`F&%3icL#Q<{LL2YdTaq6MIj4`|_GRu_KR5_~khYmTu#)Sl zp;OtlB#os{MPjA`MLa7c>YY~iD?&YcTqExiq)P_Vo@METKfHSfN}5ye{3a-#aV%z? zQ%j~JaR~Q>rO(Nz%?>tly-TpNJrd1|ogRZnndSbUM<3{YEB}GL$*@YIx*?1yZxCa`H5@Y0CTlEBJ+F?}w!lV|){ow^r1_j2^4` zr=;GxL`}rPZFfr0LTTK?+mlV;gx49ZNy%2d{N@VWbXC(Q<9Wp~+qA_NHcUBHydd96 zej#7A6U&pWpuvI-tC}~-GD5S@O6EFd2DzrH!B6t?s~k#3-a-a&3iL`xOo6>&#;)J`iGDC+8fI_VK3Sv&z9%#s__}A*Z*!{c{5D4R9*bLCgM@a__&({twVPE zTuW$eoshBk%3N15C1h93_Ezy5TZSa|g1l-?0lRH{SPhvcg7r99oMY1n{JMDPT?kUJ zRvFju&31_me_6gcy#{#E#a~USHy@YT%`tLsDJUZdhpV}*#<;%nY{L6k(c zU@CmDl9>gKkD5wtgZqvx8UtCv0cqQx#)lpMsA9L-po251Y99p7c3*_AO8J$gTso1s&QoT#PV%6)m5(c)8z$+;dG?KDog+vToXqjJfYTT2-dPkU{LsrB zQ#X#H{@DnmRBlwGWR}CSx~ODSGn<}#O2DMGm~GO!Rs?I&ssLYImoz(&anIw{3wLmx*J5j+K@zLom!KMuco&3+V&&LxW^o!Q zoTVF>0J_RsX9;p>ERUMJkkoGZPE^rpY>^T83L?arp(%QjcfmZt#4RIMG|}GUn#Cu? zVbv(?Jlt(34mP!YwWCg(I;7Cy7ie(tl1t;{;xVdfteuPP2vmF$aSoi+!UzWsFqH$ zyMLbee#h7@cRPtc8Lcto*k5_RHe9a^H{qqZD&BThW-V>lVu+Qs%BqYFcG=AO%LqQ! zA*VlAKiVZqZ=i6rpHbVWzRf;AR^8gxn5E75=D%9_r4KMh60Lj>1}aD4$wRc|NbBEo*`Ky}Q~ODg1q1di15fyGUh)2}tVq zZN@fXuBCj>Z}{&F7ksKP&xJD40(m~#Z4>^_na%hggGIh)DcywH7#^hr@cRv8zRP#x zod;C9^)oW^D_DzQ@Ylxv4RiqwZxOwQZLE9`_TUC)v zZK7odY7H#<-e?)_Y5m=B z)x%_Eblc(j%TdkH6;6EF3-bL zb}CM>%m+M6#W>@}6y_5iy;;xPD{WmmAb+JqV0Ekw z(Z!(70To>aN6hmBC~hW%$R$-Jzv*7zY1e`hnZPy8;Xcq^=UeX|G^%k+eEa0{2-9@O z&_