import { app } from "../../../scripts/app.js"; import { addMenuHandler } from "./common/utils.js"; import { addNode } from "./common/utils.js"; function createKSamplerEntry(node, samplerType, subNodeType = null, isSDXL = false) { const samplerLabelMap = { "Eff": "KSampler (Efficient)", "Adv": "KSampler Adv. (Efficient)", "SDXL": "KSampler SDXL (Eff.)" }; const subNodeLabelMap = { "XYPlot": "XY Plot", "NoiseControl": "Noise Control Script", "HiResFix": "HighRes-Fix Script", "TiledUpscale": "Tiled Upscaler Script", "AnimateDiff": "AnimateDiff Script" }; const nicknameMap = { "KSampler (Efficient)": "KSampler", "KSampler Adv. (Efficient)": "KSampler(Adv)", "KSampler SDXL (Eff.)": "KSampler", "XY Plot": "XY Plot", "Noise Control Script": "NoiseControl", "HighRes-Fix Script": "HiResFix", "Tiled Upscaler Script": "TiledUpscale", "AnimateDiff Script": "AnimateDiff" }; const kSamplerLabel = samplerLabelMap[samplerType]; const subNodeLabel = subNodeLabelMap[subNodeType]; const kSamplerNickname = nicknameMap[kSamplerLabel]; const subNodeNickname = nicknameMap[subNodeLabel]; const contentLabel = subNodeNickname ? `${kSamplerNickname} + ${subNodeNickname}` : kSamplerNickname; return { content: contentLabel, callback: function() { const kSamplerNode = addNode(kSamplerLabel, node, { shiftX: node.size[0] + 50 }); // Standard connections for all samplers node.connect(0, kSamplerNode, 0); // MODEL node.connect(1, kSamplerNode, 1); // CONDITIONING+ node.connect(2, kSamplerNode, 2); // CONDITIONING- // Additional connections for non-SDXL if (!isSDXL) { node.connect(3, kSamplerNode, 3); // LATENT node.connect(4, kSamplerNode, 4); // VAE } if (subNodeLabel) { const subNode = addNode(subNodeLabel, node, { shiftX: 50, shiftY: node.size[1] + 50 }); const dependencyIndex = isSDXL ? 3 : 5; node.connect(dependencyIndex, subNode, 0); subNode.connect(0, kSamplerNode, dependencyIndex); } }, }; } function createStackerNode(node, type) { const stackerLabelMap = { "LoRA": "LoRA Stacker", "ControlNet": "Control Net Stacker" }; const contentLabel = stackerLabelMap[type]; return { content: contentLabel, callback: function() { const stackerNode = addNode(contentLabel, node); // Calculate the left shift based on the width of the new node const shiftX = -(stackerNode.size[0] + 25); stackerNode.pos[0] += shiftX; // Adjust the x position of the new node // Introduce a Y offset of 200 for ControlNet Stacker node if (type === "ControlNet") { stackerNode.pos[1] += 300; } // Connect outputs to the Efficient Loader based on type if (type === "LoRA") { stackerNode.connect(0, node, 0); } else if (type === "ControlNet") { stackerNode.connect(0, node, 1); } }, }; } function createXYPlotNode(node, type) { const contentLabel = "XY Plot"; return { content: contentLabel, callback: function() { const xyPlotNode = addNode(contentLabel, node); // Center the X coordinate of the XY Plot node const centerXShift = (node.size[0] - xyPlotNode.size[0]) / 2; xyPlotNode.pos[0] += centerXShift; // Adjust the Y position to place it below the loader node xyPlotNode.pos[1] += node.size[1] + 60; // Depending on the node type, connect the appropriate output to the XY Plot node if (type === "Efficient") { node.connect(6, xyPlotNode, 0); } else if (type === "SDXL") { node.connect(3, xyPlotNode, 0); } }, }; } function getMenuValues(type, node) { const subNodeTypes = [null, "XYPlot", "NoiseControl", "HiResFix", "TiledUpscale", "AnimateDiff"]; const excludedSubNodeTypes = ["NoiseControl", "HiResFix", "TiledUpscale", "AnimateDiff"]; // Nodes to exclude from the menu const menuValues = []; // Add the new node types to the menu first for the correct order menuValues.push(createStackerNode(node, "LoRA")); menuValues.push(createStackerNode(node, "ControlNet")); for (const subNodeType of subNodeTypes) { // Skip adding submenu items that are in the excludedSubNodeTypes array if (!excludedSubNodeTypes.includes(subNodeType)) { const menuEntry = createKSamplerEntry(node, type === "Efficient" ? "Eff" : "SDXL", subNodeType, type === "SDXL"); menuValues.push(menuEntry); } } // Insert the standalone XY Plot option after the KSampler without any subNodeTypes and before any other KSamplers with subNodeTypes menuValues.splice(3, 0, createXYPlotNode(node, type)); return menuValues; } function showAddLinkMenuCommon(value, options, e, menu, node, type) { const values = getMenuValues(type, node); new LiteGraph.ContextMenu(values, { event: e, callback: null, parentMenu: menu, node: node }); return false; } // Extension Definition app.registerExtension({ name: "efficiency.addLinks", async beforeRegisterNodeDef(nodeType, nodeData, app) { const linkTypes = { "Efficient Loader": "Efficient", "Eff. Loader SDXL": "SDXL" }; const linkType = linkTypes[nodeData.name]; if (linkType) { addMenuHandler(nodeType, function(insertOption) { insertOption({ content: "⛓ Add link...", has_submenu: true, callback: (value, options, e, menu, node) => showAddLinkMenuCommon(value, options, e, menu, node, linkType) }); }); } }, });