| import { app } from "../../scripts/app.js"; | |
| import { IoDirection, addConnectionLayoutSupport, addMenuItem, matchLocalSlotsToServer, replaceNode, } from "./utils.js"; | |
| import { RgthreeBaseServerNode } from "./base_node.js"; | |
| import { SERVICE as KEY_EVENT_SERVICE } from "./services/key_events_services.js"; | |
| import { debounce, wait } from "../../rgthree/common/shared_utils.js"; | |
| import { removeUnusedInputsFromEnd } from "./utils_inputs_outputs.js"; | |
| import { NodeTypesString } from "./constants.js"; | |
| function findMatchingIndexByTypeOrName(otherNode, otherSlot, ctxSlots) { | |
| const otherNodeType = (otherNode.type || "").toUpperCase(); | |
| const otherNodeName = (otherNode.title || "").toUpperCase(); | |
| let otherSlotType = otherSlot.type; | |
| if (Array.isArray(otherSlotType) || otherSlotType.includes(",")) { | |
| otherSlotType = "COMBO"; | |
| } | |
| const otherSlotName = otherSlot.name.toUpperCase().replace("OPT_", "").replace("_NAME", ""); | |
| let ctxSlotIndex = -1; | |
| if (["CONDITIONING", "INT", "STRING", "FLOAT", "COMBO"].includes(otherSlotType)) { | |
| ctxSlotIndex = ctxSlots.findIndex((ctxSlot) => { | |
| const ctxSlotName = ctxSlot.name.toUpperCase().replace("OPT_", "").replace("_NAME", ""); | |
| let ctxSlotType = ctxSlot.type; | |
| if (Array.isArray(ctxSlotType) || ctxSlotType.includes(",")) { | |
| ctxSlotType = "COMBO"; | |
| } | |
| if (ctxSlotType !== otherSlotType) { | |
| return false; | |
| } | |
| if (ctxSlotName === otherSlotName || | |
| (ctxSlotName === "SEED" && otherSlotName.includes("SEED")) || | |
| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("AT_STEP")) || | |
| (ctxSlotName === "STEP_REFINER" && otherSlotName.includes("REFINER_STEP"))) { | |
| return true; | |
| } | |
| if ((otherNodeType.includes("POSITIVE") || otherNodeName.includes("POSITIVE")) && | |
| ((ctxSlotName === "POSITIVE" && otherSlotType === "CONDITIONING") || | |
| (ctxSlotName === "TEXT_POS_G" && otherSlotName.includes("TEXT_G")) || | |
| (ctxSlotName === "TEXT_POS_L" && otherSlotName.includes("TEXT_L")))) { | |
| return true; | |
| } | |
| if ((otherNodeType.includes("NEGATIVE") || otherNodeName.includes("NEGATIVE")) && | |
| ((ctxSlotName === "NEGATIVE" && otherSlotType === "CONDITIONING") || | |
| (ctxSlotName === "TEXT_NEG_G" && otherSlotName.includes("TEXT_G")) || | |
| (ctxSlotName === "TEXT_NEG_L" && otherSlotName.includes("TEXT_L")))) { | |
| return true; | |
| } | |
| return false; | |
| }); | |
| } | |
| else { | |
| ctxSlotIndex = ctxSlots.map((s) => s.type).indexOf(otherSlotType); | |
| } | |
| return ctxSlotIndex; | |
| } | |
| export class BaseContextNode extends RgthreeBaseServerNode { | |
| constructor(title) { | |
| super(title); | |
| this.___collapsed_width = 0; | |
| } | |
| get _collapsed_width() { | |
| return this.___collapsed_width; | |
| } | |
| set _collapsed_width(width) { | |
| const canvas = app.canvas; | |
| const ctx = canvas.canvas.getContext("2d"); | |
| const oldFont = ctx.font; | |
| ctx.font = canvas.title_text_font; | |
| let title = this.title.trim(); | |
| this.___collapsed_width = 30 + (title ? 10 + ctx.measureText(title).width : 0); | |
| ctx.font = oldFont; | |
| } | |
| connectByType(slot, sourceNode, sourceSlotType, optsIn) { | |
| let canConnect = super.connectByType && | |
| super.connectByType.call(this, slot, sourceNode, sourceSlotType, optsIn); | |
| if (!super.connectByType) { | |
| canConnect = LGraphNode.prototype.connectByType.call(this, slot, sourceNode, sourceSlotType, optsIn); | |
| } | |
| if (!canConnect && slot === 0) { | |
| const ctrlKey = KEY_EVENT_SERVICE.ctrlKey; | |
| for (const [index, input] of (sourceNode.inputs || []).entries()) { | |
| if (input.link && !ctrlKey) { | |
| continue; | |
| } | |
| const thisOutputSlot = findMatchingIndexByTypeOrName(sourceNode, input, this.outputs); | |
| if (thisOutputSlot > -1) { | |
| this.connect(thisOutputSlot, sourceNode, index); | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| connectByTypeOutput(slot, sourceNode, sourceSlotType, optsIn) { | |
| var _a; | |
| let canConnect = super.connectByTypeOutput && | |
| super.connectByTypeOutput.call(this, slot, sourceNode, sourceSlotType, optsIn); | |
| if (!super.connectByType) { | |
| canConnect = LGraphNode.prototype.connectByTypeOutput.call(this, slot, sourceNode, sourceSlotType, optsIn); | |
| } | |
| if (!canConnect && slot === 0) { | |
| const ctrlKey = KEY_EVENT_SERVICE.ctrlKey; | |
| for (const [index, output] of (sourceNode.outputs || []).entries()) { | |
| if (((_a = output.links) === null || _a === void 0 ? void 0 : _a.length) && !ctrlKey) { | |
| continue; | |
| } | |
| const thisInputSlot = findMatchingIndexByTypeOrName(sourceNode, output, this.inputs); | |
| if (thisInputSlot > -1) { | |
| sourceNode.connect(index, this, thisInputSlot); | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| static setUp(comfyClass, nodeData, ctxClass) { | |
| RgthreeBaseServerNode.registerForOverride(comfyClass, nodeData, ctxClass); | |
| wait(500).then(() => { | |
| LiteGraph.slot_types_default_out["RGTHREE_CONTEXT"] = | |
| LiteGraph.slot_types_default_out["RGTHREE_CONTEXT"] || []; | |
| LiteGraph.slot_types_default_out["RGTHREE_CONTEXT"].push(comfyClass.comfyClass); | |
| }); | |
| } | |
| static onRegisteredForOverride(comfyClass, ctxClass) { | |
| addConnectionLayoutSupport(ctxClass, app, [ | |
| ["Left", "Right"], | |
| ["Right", "Left"], | |
| ]); | |
| setTimeout(() => { | |
| ctxClass.category = comfyClass.category; | |
| }); | |
| } | |
| } | |
| class ContextNode extends BaseContextNode { | |
| constructor(title = ContextNode.title) { | |
| super(title); | |
| } | |
| static setUp(comfyClass, nodeData) { | |
| BaseContextNode.setUp(comfyClass, nodeData, ContextNode); | |
| } | |
| static onRegisteredForOverride(comfyClass, ctxClass) { | |
| BaseContextNode.onRegisteredForOverride(comfyClass, ctxClass); | |
| addMenuItem(ContextNode, app, { | |
| name: "Convert To Context Big", | |
| callback: (node) => { | |
| replaceNode(node, ContextBigNode.type); | |
| }, | |
| }); | |
| } | |
| } | |
| ContextNode.title = NodeTypesString.CONTEXT; | |
| ContextNode.type = NodeTypesString.CONTEXT; | |
| ContextNode.comfyClass = NodeTypesString.CONTEXT; | |
| class ContextBigNode extends BaseContextNode { | |
| constructor(title = ContextBigNode.title) { | |
| super(title); | |
| } | |
| static setUp(comfyClass, nodeData) { | |
| BaseContextNode.setUp(comfyClass, nodeData, ContextBigNode); | |
| } | |
| static onRegisteredForOverride(comfyClass, ctxClass) { | |
| BaseContextNode.onRegisteredForOverride(comfyClass, ctxClass); | |
| addMenuItem(ContextBigNode, app, { | |
| name: "Convert To Context (Original)", | |
| callback: (node) => { | |
| replaceNode(node, ContextNode.type); | |
| }, | |
| }); | |
| } | |
| } | |
| ContextBigNode.title = NodeTypesString.CONTEXT_BIG; | |
| ContextBigNode.type = NodeTypesString.CONTEXT_BIG; | |
| ContextBigNode.comfyClass = NodeTypesString.CONTEXT_BIG; | |
| class BaseContextMultiCtxInputNode extends BaseContextNode { | |
| constructor(title) { | |
| super(title); | |
| this.stabilizeBound = this.stabilize.bind(this); | |
| this.addContextInput(5); | |
| } | |
| addContextInput(num = 1) { | |
| for (let i = 0; i < num; i++) { | |
| this.addInput(`ctx_${String(this.inputs.length + 1).padStart(2, "0")}`, "RGTHREE_CONTEXT"); | |
| } | |
| } | |
| onConnectionsChange(type, slotIndex, isConnected, link, ioSlot) { | |
| var _a; | |
| (_a = super.onConnectionsChange) === null || _a === void 0 ? void 0 : _a.apply(this, [...arguments]); | |
| if (type === LiteGraph.INPUT) { | |
| this.scheduleStabilize(); | |
| } | |
| } | |
| scheduleStabilize(ms = 64) { | |
| return debounce(this.stabilizeBound, 64); | |
| } | |
| stabilize() { | |
| removeUnusedInputsFromEnd(this, 4); | |
| this.addContextInput(); | |
| } | |
| } | |
| class ContextSwitchNode extends BaseContextMultiCtxInputNode { | |
| constructor(title = ContextSwitchNode.title) { | |
| super(title); | |
| } | |
| static setUp(comfyClass, nodeData) { | |
| BaseContextNode.setUp(comfyClass, nodeData, ContextSwitchNode); | |
| } | |
| static onRegisteredForOverride(comfyClass, ctxClass) { | |
| BaseContextNode.onRegisteredForOverride(comfyClass, ctxClass); | |
| addMenuItem(ContextSwitchNode, app, { | |
| name: "Convert To Context Switch Big", | |
| callback: (node) => { | |
| replaceNode(node, ContextSwitchBigNode.type); | |
| }, | |
| }); | |
| } | |
| } | |
| ContextSwitchNode.title = NodeTypesString.CONTEXT_SWITCH; | |
| ContextSwitchNode.type = NodeTypesString.CONTEXT_SWITCH; | |
| ContextSwitchNode.comfyClass = NodeTypesString.CONTEXT_SWITCH; | |
| class ContextSwitchBigNode extends BaseContextMultiCtxInputNode { | |
| constructor(title = ContextSwitchBigNode.title) { | |
| super(title); | |
| } | |
| static setUp(comfyClass, nodeData) { | |
| BaseContextNode.setUp(comfyClass, nodeData, ContextSwitchBigNode); | |
| } | |
| static onRegisteredForOverride(comfyClass, ctxClass) { | |
| BaseContextNode.onRegisteredForOverride(comfyClass, ctxClass); | |
| addMenuItem(ContextSwitchBigNode, app, { | |
| name: "Convert To Context Switch", | |
| callback: (node) => { | |
| replaceNode(node, ContextSwitchNode.type); | |
| }, | |
| }); | |
| } | |
| } | |
| ContextSwitchBigNode.title = NodeTypesString.CONTEXT_SWITCH_BIG; | |
| ContextSwitchBigNode.type = NodeTypesString.CONTEXT_SWITCH_BIG; | |
| ContextSwitchBigNode.comfyClass = NodeTypesString.CONTEXT_SWITCH_BIG; | |
| class ContextMergeNode extends BaseContextMultiCtxInputNode { | |
| constructor(title = ContextMergeNode.title) { | |
| super(title); | |
| } | |
| static setUp(comfyClass, nodeData) { | |
| BaseContextNode.setUp(comfyClass, nodeData, ContextMergeNode); | |
| } | |
| static onRegisteredForOverride(comfyClass, ctxClass) { | |
| BaseContextNode.onRegisteredForOverride(comfyClass, ctxClass); | |
| addMenuItem(ContextMergeNode, app, { | |
| name: "Convert To Context Merge Big", | |
| callback: (node) => { | |
| replaceNode(node, ContextMergeBigNode.type); | |
| }, | |
| }); | |
| } | |
| } | |
| ContextMergeNode.title = NodeTypesString.CONTEXT_MERGE; | |
| ContextMergeNode.type = NodeTypesString.CONTEXT_MERGE; | |
| ContextMergeNode.comfyClass = NodeTypesString.CONTEXT_MERGE; | |
| class ContextMergeBigNode extends BaseContextMultiCtxInputNode { | |
| constructor(title = ContextMergeBigNode.title) { | |
| super(title); | |
| } | |
| static setUp(comfyClass, nodeData) { | |
| BaseContextNode.setUp(comfyClass, nodeData, ContextMergeBigNode); | |
| } | |
| static onRegisteredForOverride(comfyClass, ctxClass) { | |
| BaseContextNode.onRegisteredForOverride(comfyClass, ctxClass); | |
| addMenuItem(ContextMergeBigNode, app, { | |
| name: "Convert To Context Switch", | |
| callback: (node) => { | |
| replaceNode(node, ContextMergeNode.type); | |
| }, | |
| }); | |
| } | |
| } | |
| ContextMergeBigNode.title = NodeTypesString.CONTEXT_MERGE_BIG; | |
| ContextMergeBigNode.type = NodeTypesString.CONTEXT_MERGE_BIG; | |
| ContextMergeBigNode.comfyClass = NodeTypesString.CONTEXT_MERGE_BIG; | |
| const contextNodes = [ | |
| ContextNode, | |
| ContextBigNode, | |
| ContextSwitchNode, | |
| ContextSwitchBigNode, | |
| ContextMergeNode, | |
| ContextMergeBigNode, | |
| ]; | |
| const contextTypeToServerDef = {}; | |
| function fixBadConfigs(node) { | |
| const wrongName = node.outputs.find((o, i) => o.name === "CLIP_HEIGTH"); | |
| if (wrongName) { | |
| wrongName.name = "CLIP_HEIGHT"; | |
| } | |
| } | |
| app.registerExtension({ | |
| name: "rgthree.Context", | |
| async beforeRegisterNodeDef(nodeType, nodeData) { | |
| for (const ctxClass of contextNodes) { | |
| if (nodeData.name === ctxClass.type) { | |
| contextTypeToServerDef[ctxClass.type] = nodeData; | |
| ctxClass.setUp(nodeType, nodeData); | |
| break; | |
| } | |
| } | |
| }, | |
| async nodeCreated(node) { | |
| const type = node.type || node.constructor.type; | |
| const serverDef = type && contextTypeToServerDef[type]; | |
| if (serverDef) { | |
| fixBadConfigs(node); | |
| matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef); | |
| if (!type.includes("Switch") && !type.includes("Merge")) { | |
| matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef); | |
| } | |
| } | |
| }, | |
| async loadedGraphNode(node) { | |
| const type = node.type || node.constructor.type; | |
| const serverDef = type && contextTypeToServerDef[type]; | |
| if (serverDef) { | |
| fixBadConfigs(node); | |
| matchLocalSlotsToServer(node, IoDirection.OUTPUT, serverDef); | |
| if (!type.includes("Switch") && !type.includes("Merge")) { | |
| matchLocalSlotsToServer(node, IoDirection.INPUT, serverDef); | |
| } | |
| } | |
| }, | |
| }); | |