import { app } from "/scripts/app.js"; // Simple debounce function function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(this, args); }, delay); }; } // Use the class name registered in NODE_CLASS_MAPPINGS const nodeClassName = "MySwitchIndex"; app.registerExtension({ name: "MultiModel.MySwitchIndex", async beforeRegisterNodeDef(nodeType, nodeData, app) { // Compare with the class name if (nodeData.name === nodeClassName) { function handleModeSwitch(modeWidget, indexWidget, app, value) { const updateVisibility = (modeValue) => { if (indexWidget) { const shouldBeHidden = (modeValue !== "index"); indexWidget.hidden = shouldBeHidden; if (app.graph) app.graph.setDirtyCanvas(true, false); } }; updateVisibility(value ?? modeWidget.value); // Перевизначаємо callback const originalModeCallback = modeWidget.callback; modeWidget.callback = (val) => { originalModeCallback?.apply(modeWidget, [val]); updateVisibility(val); }; } function updateDynamicInputIndexes(node) { const prefix = node._input_prefix || "input_"; let dynamicInputs = node.inputs ? node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index") : []; for (let i = 0; i < dynamicInputs.length; i++) { const expectedName = `${prefix}${i + 1}`; if (dynamicInputs[i].name !== expectedName) { dynamicInputs[i].name = expectedName; } } let maxIdx = dynamicInputs.length > 0 ? dynamicInputs.length : 1; node.last_connected = -1; for (let i = 0; i < dynamicInputs.length; i++) { const inp = dynamicInputs[i]; if (inp.link != null) node.last_connected = node.inputs.indexOf(inp); } node._last_input_index = maxIdx; // Останній порожній динамічний вхід let lastEmptyIdx = -1; for (let i = node.inputs.length - 1; i >= 0; i--) { if (node.inputs[i].name !== "mode" && node.inputs[i].name !== "index" && node.inputs[i].name.startsWith(prefix) && node.inputs[i].link == null) { lastEmptyIdx = i; break; } } node.last_empty = lastEmptyIdx; } function updateDynamicInputsOnConnect(node, type, index, connected) { if (node._updatingInputs) return; node._updatingInputs = true; const changedInput = node.inputs[index]; const inputName = changedInput?.name; const isDynamic = inputName && inputName !== "mode" && inputName !== "index"; if (!isDynamic) { node._updatingInputs = false; return; } if (connected) { if (index === node.last_empty) { const prefix = node._input_prefix || "input_"; let dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); node.addInput(`${prefix}${dynamicInputs.length + 1}`, "*"); updateDynamicInputIndexes(node); } } else { let dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); let emptyInputs = dynamicInputs.filter(inp => inp.link == null); if (index === node.last_connected && emptyInputs.length > 1) { node.removeInput(index); updateDynamicInputIndexes(node); } } node._updatingInputs = false; } function handleNodeCreation(node, app) { // --- Віджети --- const modeWidget = node.widgets.find(w => w.name === "mode"); const indexWidget = node.widgets.find(w => w.name === "index"); if (!modeWidget || !indexWidget) { return; } handleModeSwitch(modeWidget, indexWidget, app); // --- Динамічні входи (last_empty, last_connected) --- node._input_prefix = "input_"; const prefix = node._input_prefix; let dynamicInputs = node.inputs ? node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index") : []; if (dynamicInputs.length === 0) { node.addInput(`${prefix}1`, "*"); dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); } let connectedInputs = dynamicInputs.filter(inp => inp.link != null); let emptyInputs = dynamicInputs.filter(inp => inp.link == null); if (emptyInputs.length === 0) { node.addInput(`${prefix}${connectedInputs.length + 1}`, "*"); dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); } else if (emptyInputs.length > 1) { for (let i = node.inputs.length - 1; i >= 0; i--) { if (node.inputs[i].name !== "mode" && node.inputs[i].name !== "index" && node.inputs[i].link == null && emptyInputs.length > 1) { node.removeInput(i); emptyInputs.pop(); } } dynamicInputs = node.inputs.filter(inp => inp.name !== "mode" && inp.name !== "index"); } updateDynamicInputIndexes(node); // --- Зберігаємо попередній стан links --- node._prevLinks = []; for (let i = 0; i < node.inputs.length; i++) { node._prevLinks[i] = node.inputs[i].link; } if (app.graph) app.graph.setDirtyCanvas(true, true); } function handleConnectionsChange(node, type, index, connected, link_info, slot, app) { updateDynamicInputsOnConnect(node, type, index, connected); node._prevLinks = []; for (let i = 0; i < node.inputs.length; i++) { node._prevLinks[i] = node.inputs[i].link; } } // --- Патчимо onNodeCreated --- const onNodeCreated = nodeType.prototype.onNodeCreated; nodeType.prototype.onNodeCreated = function() { onNodeCreated?.apply(this, arguments); setTimeout(() => { handleNodeCreation(this, app); }, 500); }; // --- Патчимо onConnectionsChange --- const onConnectionsChange = nodeType.prototype.onConnectionsChange; nodeType.prototype.onConnectionsChange = function(type, index, connected, link_info, slot) { onConnectionsChange?.apply(this, arguments); handleConnectionsChange(this, type, index, connected, link_info, slot, app); }; } }, });