import { app } from "../../scripts/app.js"; const customPipeLineLink = "#7737AA" const customPipeLineSDXLLink = "#0DC52B" const customIntLink = "#29699C" const customXYPlotLink = "#74DA5D" const customLoraStackLink = "#87C7B7" const customStringLink = "#7CBB1A" var customLinkColors = JSON.parse(localStorage.getItem('Comfy.Settings.ttN.customLinkColors')) || {}; if (!customLinkColors["PIPE_LINE"] || !LGraphCanvas.link_type_colors["PIPE_LINE"]) {customLinkColors["PIPE_LINE"] = customPipeLineLink;} if (!customLinkColors["PIPE_LINE_SDXL"] || !LGraphCanvas.link_type_colors["PIPE_LINE_SDXL"]) {customLinkColors["PIPE_LINE_SDXL"] = customPipeLineSDXLLink;} if (!customLinkColors["INT"] || !LGraphCanvas.link_type_colors["INT"]) {customLinkColors["INT"] = customIntLink;} if (!customLinkColors["XYPLOT"] || !LGraphCanvas.link_type_colors["XYPLOT"]) {customLinkColors["XYPLOT"] = customXYPlotLink;} if (!customLinkColors["ADV_XYPLOT"] || !LGraphCanvas.link_type_colors["ADV_XYPLOT"]) {customLinkColors["ADV_XYPLOT"] = customXYPlotLink;} if (!customLinkColors["LORA_STACK"] || !LGraphCanvas.link_type_colors["LORA_STACK"]) {customLinkColors["LORA_STACK"] = customLoraStackLink;} if (!customLinkColors["CONTROL_NET_STACK"] || !LGraphCanvas.link_type_colors["CONTROL_NET_STACK"]) {customLinkColors["CONTROL_NET_STACK"] = customLoraStackLink;} if (!customLinkColors["STRING"] || !LGraphCanvas.link_type_colors["STRING"]) {customLinkColors["STRING"] = customStringLink;} localStorage.setItem('Comfy.Settings.ttN.customLinkColors', JSON.stringify(customLinkColors)); app.registerExtension({ name: "comfy.ttN.interface", init() { function adjustToGrid(val, gridSize) { return Math.round(val / gridSize) * gridSize; } function moveNodeBasedOnKey(e, node, gridSize, shiftMult) { switch (e.code) { case 'ArrowUp': node.pos[1] -= gridSize * shiftMult; break; case 'ArrowDown': node.pos[1] += gridSize * shiftMult; break; case 'ArrowLeft': node.pos[0] -= gridSize * shiftMult; break; case 'ArrowRight': node.pos[0] += gridSize * shiftMult; break; } node.setDirtyCanvas(true, true); } function keyMoveNode(e, node) { let gridSize = JSON.parse(localStorage.getItem('Comfy.Settings.Comfy.SnapToGrid.GridSize')); gridSize = gridSize ? parseInt(gridSize) : 1; let shiftMult = e.shiftKey ? 10 : 1; node.pos[0] = adjustToGrid(node.pos[0], gridSize); node.pos[1] = adjustToGrid(node.pos[1], gridSize); moveNodeBasedOnKey(e, node, gridSize, shiftMult); } function getSelectedNodes(e) { const inputField = e.composedPath()[0]; if (inputField.tagName === "TEXTAREA") return; if (e.ctrlKey && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.code)) { let graphcanvas = LGraphCanvas.active_canvas; for (let node in graphcanvas.selected_nodes) { keyMoveNode(e, graphcanvas.selected_nodes[node]); } } } window.addEventListener("keydown", getSelectedNodes, true); LGraphCanvas.prototype.ttNcreateDialog = function (htmlContent, onOK, onCancel) { var dialog = document.createElement("div"); dialog.is_modified = false; dialog.className = "ttN-dialog"; dialog.innerHTML = htmlContent + ""; dialog.close = function() { if (dialog.parentNode) { dialog.parentNode.removeChild(dialog); } }; var inputs = Array.from(dialog.querySelectorAll("input, select")); inputs.forEach(input => { input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { // ESC onCancel && onCancel(); dialog.close(); } else if (e.keyCode == 13) { // Enter onOK && onOK(dialog, inputs.map(input => input.value)); dialog.close(); } else if (e.keyCode != 13 && e.target.localName != "textarea") { return; } e.preventDefault(); e.stopPropagation(); }); }); var graphcanvas = LGraphCanvas.active_canvas; var canvas = graphcanvas.canvas; var rect = canvas.getBoundingClientRect(); var offsetx = -20; var offsety = -20; if (rect) { offsetx -= rect.left; offsety -= rect.top; } if (event) { dialog.style.left = event.clientX + offsetx + "px"; dialog.style.top = event.clientY + offsety + "px"; } else { dialog.style.left = canvas.width * 0.5 + offsetx + "px"; dialog.style.top = canvas.height * 0.5 + offsety + "px"; } var button = dialog.querySelector("#ok"); button.addEventListener("click", function() { onOK && onOK(dialog, inputs.map(input => input.value)); dialog.close(); }); canvas.parentNode.appendChild(dialog); if(inputs) inputs[0].focus(); var dialogCloseTimer = null; dialog.addEventListener("mouseleave", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); //dialog.close(); }); dialog.addEventListener("mouseenter", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if(dialogCloseTimer) clearTimeout(dialogCloseTimer); }); return dialog; }; LGraphCanvas.prototype.ttNsetNodeDimension = function (node) { const nodeWidth = node.size[0]; const nodeHeight = node.size[1]; let input_html = ""; input_html += ""; LGraphCanvas.prototype.ttNcreateDialog("Width/Height" + input_html, function(dialog, values) { var widthValue = Number(values[0]) ? values[0] : nodeWidth; var heightValue = Number(values[1]) ? values[1] : nodeHeight; let sz = node.computeSize(); node.setSize([Math.max(sz[0], widthValue), Math.max(sz[1], heightValue)]); if (dialog.parentNode) { dialog.parentNode.removeChild(dialog); } node.setDirtyCanvas(true, true); }, null ); }; LGraphCanvas.prototype.ttNsetSlotTypeColor = function(slot){ var slotColor = LGraphCanvas.link_type_colors[slot.output.type].toUpperCase(); var slotType = slot.output.type; // Check if the color is in the correct format if (!/^#([0-9A-F]{3}){1,2}$/i.test(slotColor)) { slotColor = "#FFFFFF"; } // Check if browser supports color input type var inputType = "color"; var inputID = " id='colorPicker'"; var inputElem = document.createElement("input"); inputElem.setAttribute("type", inputType); if (inputElem.type !== "color") { // If it doesn't, fall back to text input inputType = "text"; inputID = " "; } let input_html = ""; input_html += ""; // Add a default button input_html += ""; // Add a reset button var dialog = LGraphCanvas.prototype.ttNcreateDialog("" + slotType + "" + input_html, function(dialog, values){ var hexColor = values[0].toUpperCase(); if (!/^#([0-9A-F]{3}){1,2}$/i.test(hexColor)) { return } if (hexColor === slotColor) { return } var customLinkColors = JSON.parse(localStorage.getItem('Comfy.Settings.ttN.customLinkColors')) || {}; if (!customLinkColors[slotType + "_ORIG"]) {customLinkColors[slotType + "_ORIG"] = slotColor}; customLinkColors[slotType] = hexColor; localStorage.setItem('Comfy.Settings.ttN.customLinkColors', JSON.stringify(customLinkColors)); app.canvas.default_connection_color_byType[slotType] = hexColor; LGraphCanvas.link_type_colors[slotType] = hexColor; } ); var resetButton = dialog.querySelector("#reset"); resetButton.addEventListener("click", function() { var colorInput = dialog.querySelector("input[type='" + inputType + "']"); colorInput.value = slotColor; }); var defaultButton = dialog.querySelector("#Default"); defaultButton.addEventListener("click", function() { var customLinkColors = JSON.parse(localStorage.getItem('Comfy.Settings.ttN.customLinkColors')) || {}; if (customLinkColors[slotType+"_ORIG"]) { app.canvas.default_connection_color_byType[slotType] = customLinkColors[slotType+"_ORIG"]; LGraphCanvas.link_type_colors[slotType] = customLinkColors[slotType+"_ORIG"]; delete customLinkColors[slotType+"_ORIG"]; delete customLinkColors[slotType]; } localStorage.setItem('Comfy.Settings.ttN.customLinkColors', JSON.stringify(customLinkColors)); dialog.close() }) var colorPicker = dialog.querySelector("input[type='" + inputType + "']"); colorPicker.addEventListener("focusout", function(e) { this.focus(); }); }; LGraphCanvas.prototype.ttNdefaultBGcolor = function(node, defaultBGColor){ setTimeout(() => { if (defaultBGColor !== 'default' && !node.color) { node.addProperty('ttNbgOverride', defaultBGColor); node.color=defaultBGColor.color; node.bgcolor=defaultBGColor.bgcolor; } if (node.color && node.properties.ttNbgOverride) { if (node.properties.ttNbgOverride !== defaultBGColor && node.color === node.properties.ttNbgOverride.color) { if (defaultBGColor === 'default') { delete node.properties.ttNbgOverride delete node.color delete node.bgcolor } else { node.properties.ttNbgOverride = defaultBGColor node.color=defaultBGColor.color; node.bgcolor=defaultBGColor.bgcolor; } } if (node.properties.ttNbgOverride !== defaultBGColor && node.color !== node.properties.ttNbgOverride?.color) { delete node.properties.ttNbgOverride } } }, 0); }; LGraphCanvas.prototype.ttNfixNodeSize = function(node){ setTimeout(() => { node.onResize(node.size); }, 0); }; LGraphCanvas.ttNonShowLinkStyles = function(value, options, e, menu, node) { new LiteGraph.ContextMenu( LiteGraph.LINK_RENDER_MODES, { event: e, callback: inner_clicked, parentMenu: menu, node: node } ); function inner_clicked(v) { if (!node) { return; } var kV = Object.values(LiteGraph.LINK_RENDER_MODES).indexOf(v); localStorage.setItem('Comfy.Settings.Comfy.LinkRenderMode', JSON.stringify(String(kV))); app.canvas.links_render_mode = kV; app.graph.setDirtyCanvas(true); } return false; }; LGraphCanvas.ttNlinkStyleBorder = function(value, options, e, menu, node) { new LiteGraph.ContextMenu( [false, true], { event: e, callback: inner_clicked, parentMenu: menu, node: node } ); function inner_clicked(v) { if (!node) { return; } localStorage.setItem('Comfy.Settings.ttN.links_render_border', JSON.stringify(v)); app.canvas.render_connections_border = v; } return false; }; LGraphCanvas.ttNlinkStyleShadow = function(value, options, e, menu, node) { new LiteGraph.ContextMenu( [false, true], { event: e, callback: inner_clicked, parentMenu: menu, node: node } ); function inner_clicked(v) { if (!node) { return; } localStorage.setItem('Comfy.Settings.ttN.links_render_shadow', JSON.stringify(v)); app.canvas.render_connections_shadows = v; } return false; }; LGraphCanvas.ttNsetDefaultBGColor = function(value, options, e, menu, node) { if (!node) { throw "no node for color"; } var values = []; values.push({ value: null, content: "No Color" }); for (var i in LGraphCanvas.node_colors) { var color = LGraphCanvas.node_colors[i]; var value = { value: i, content: "" + i + "" }; values.push(value); } new LiteGraph.ContextMenu(values, { event: e, callback: inner_clicked, parentMenu: menu, node: node }); function inner_clicked(v) { if (!node) { return; } var defaultBGColor = v.value ? LGraphCanvas.node_colors[v.value] : 'default'; localStorage.setItem('Comfy.Settings.ttN.defaultBGColor', JSON.stringify(defaultBGColor)); for (var i in app.graph._nodes) { LGraphCanvas.prototype.ttNdefaultBGcolor(app.graph._nodes[i], defaultBGColor); } node.setDirtyCanvas(true, true); } return false; }; LGraphCanvas.prototype.ttNupdateRenderSettings = function (app) { let showLinkBorder = Number(localStorage.getItem('Comfy.Settings.ttN.links_render_border')); if (showLinkBorder !== undefined) {app.canvas.render_connections_border = showLinkBorder} let showLinkShadow = Number(localStorage.getItem('Comfy.Settings.ttN.links_render_shadow')); if (showLinkShadow !== undefined) {app.canvas.render_connections_shadows = showLinkShadow} let showExecOrder = localStorage.getItem('Comfy.Settings.ttN.showExecutionOrder'); if (showExecOrder === 'true') {app.canvas.render_execution_order = true} else {app.canvas.render_execution_order = false} var customLinkColors = JSON.parse(localStorage.getItem('Comfy.Settings.ttN.customLinkColors')) || {}; Object.assign(app.canvas.default_connection_color_byType, customLinkColors); Object.assign(LGraphCanvas.link_type_colors, customLinkColors); } }, beforeRegisterNodeDef(nodeType, nodeData, app) { nodeType.prototype.getSlotMenuOptions = (slot) => { let menu_info = []; if ( slot && slot.output && slot.output.links && slot.output.links.length ) { menu_info.push({ content: "Disconnect Links", slot: slot }); } var _slot = slot.input || slot.output; if (_slot.removable){ menu_info.push( _slot.locked ? "Cannot remove" : { content: "Remove Slot", slot: slot } ); } if (!_slot.nameLocked){ menu_info.push({ content: "Rename Slot", slot: slot }); } menu_info.push({ content: "🌏 Slot Type Color", slot: slot, callback: () => { LGraphCanvas.prototype.ttNsetSlotTypeColor(slot) } }); menu_info.push({ content: "🌏 Show Link Border", has_submenu: true, slot: slot, callback: LGraphCanvas.ttNlinkStyleBorder }); menu_info.push({ content: "🌏 Show Link Shadow", has_submenu: true, slot: slot, callback: LGraphCanvas.ttNlinkStyleShadow }); menu_info.push({ content: "🌏 Link Style", has_submenu: true, slot: slot, callback: LGraphCanvas.ttNonShowLinkStyles }); return menu_info; } }, setup() { LGraphCanvas.prototype.ttNupdateRenderSettings(app); }, nodeCreated(node) { LGraphCanvas.prototype.ttNfixNodeSize(node); let defaultBGColor = JSON.parse(localStorage.getItem('Comfy.Settings.ttN.defaultBGColor')); if (defaultBGColor) {LGraphCanvas.prototype.ttNdefaultBGcolor(node, defaultBGColor)}; }, loadedGraphNode(node, app) { LGraphCanvas.prototype.ttNupdateRenderSettings(app); let defaultBGColor = JSON.parse(localStorage.getItem('Comfy.Settings.ttN.defaultBGColor')); if (defaultBGColor) {LGraphCanvas.prototype.ttNdefaultBGcolor(node, defaultBGColor)}; }, }); var styleElement = document.createElement("style"); const cssCode = ` .ttN-dialog { top: 10px; left: 10px; min-height: 1em; background-color: var(--comfy-menu-bg); font-size: 1.2em; box-shadow: 0 0 7px black !important; z-index: 10; display: grid; border-radius: 7px; padding: 7px 7px; position: fixed; } .ttN-dialog .name { display: inline-block; min-height: 1.5em; font-size: 14px; font-family: sans-serif; color: var(--descrip-text); padding: 0; vertical-align: middle; justify-self: center; } .ttN-dialog input, .ttN-dialog textarea, .ttN-dialog select { margin: 3px; min-width: 60px; min-height: 1.5em; background-color: var(--comfy-input-bg); border: 2px solid; border-color: var(--border-color); color: var(--input-text); border-radius: 14px; padding-left: 10px; outline: none; } .ttN-dialog #colorPicker { margin: 0px; min-width: 100%; min-height: 2.5em; border-radius: 0px; padding: 0px 2px 0px 2px; border: unset; } .ttN-dialog textarea { min-height: 150px; } .ttN-dialog button { margin-top: 3px; vertical-align: top; background-color: #999; border: 0; padding: 4px 18px; border-radius: 20px; cursor: pointer; } .ttN-dialog button.rounded, .ttN-dialog input.rounded { border-radius: 0 12px 12px 0; } .ttN-dialog .helper { overflow: auto; max-height: 200px; } .ttN-dialog .help-item { padding-left: 10px; } .ttN-dialog .help-item:hover, .ttN-dialog .help-item.selected { cursor: pointer; background-color: white; color: black; } ` styleElement.innerHTML = cssCode document.head.appendChild(styleElement);