Files
FaberVS-MultiModel/js/active_model.js
T
2025-04-29 15:48:55 +03:00

738 lines
38 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
console.log("%c[ActiveModel] JS файл завантажився! (v2-dynamic-inputs)", "color: white; background: #007acc; font-weight: bold; padding: 2px 8px; border-radius: 3px;");
// --- Loading js/active_model.js --- v2-dynamic-inputs
import { app } from "/scripts/app.js"; // Виправлено шлях на абсолютний
// Цільовий тип вузла, який ми шукаємо
const TARGET_NODE_TYPE = "ModelParamsPipe";
// Режими роботи вузлів ComfyUI/LiteGraph
const MODE_ALWAYS = 0; // Завжди активний
const MODE_BYPASS = 4; // Режим обходу (Bypass)
const NODE_TITLE = "Active Model"; // Змінено заголовок
const NODE_TYPE = "ActiveModel"; // І тип
class ActiveModel {
// Налаштування для LiteGraph
constructor() {
// Цей вузол є переважно контейнером для дій
this.properties = {};
console.log(`[${ActiveModel.title}] CONSTRUCTOR CALLED.`);
// Спробуємо знайти та вивести вузли одразу при створенні
this.logTargetNodeNames();
}
// Статичні властивості для визначення типу вузла
static title = "Active Model";
static type = "ActiveModel"; // Унікальний тип для цього вузла
// Функція для пошуку та логування імен цільових вузлів
logTargetNodeNames() {
console.log(`[${ActiveModel.title}] Searching for nodes of type '${TARGET_NODE_TYPE}'...`);
const nodes = app.graph._nodes.filter(node => node.type === TARGET_NODE_TYPE);
console.log(`[${ActiveModel.title}] Found ${nodes.length} nodes:`);
if (nodes.length > 0) {
nodes.forEach(n => console.log(` - Node ID: ${n.id}, Title: ${n.title}, Current mode: ${n.mode}`));
} else {
console.log(` (No nodes found)`);
}
return nodes; // Повертаємо для можливого використання в onAction
}
// Метод для встановлення режиму для всіх знайдених цільових вузлів
setHubsMode(mode) {
const nodes = this.logTargetNodeNames();
if (nodes.length === 0) {
console.log(`[${ActiveModel.title}] No target nodes found to set mode.`);
return; // Виходимо, якщо немає цільових вузлів
}
const modeStr = mode === MODE_ALWAYS ? "ALWAYS" : (mode === MODE_BYPASS ? "BYPASS" : mode);
console.log(`[${ActiveModel.title}] Attempting to set mode to ${modeStr} for ${nodes.length} nodes.`);
let changed = false;
for (const node of nodes) {
console.log(` - Processing Node ID: ${node.id}, Title: ${node.title}, Current mode: ${node.mode}`);
if (node.mode !== mode) {
node.mode = mode;
console.log(` - Mode set to: ${node.mode}`);
changed = true;
} else {
console.log(` - Mode already set to ${node.mode}. Skipping.`);
}
}
if (changed) {
console.log(`[${ActiveModel.title}] Triggering canvas redraw.`);
app.graph.setDirtyCanvas(true, true);
} else {
console.log(`[${ActiveModel.title}] No modes were changed. Skipping redraw.`);
}
}
// Спрощена обробка дій для тестування
onAction(action) {
console.log(`[${ActiveModel.title}] === onAction ENTERED === Action: ${action}`);
// Просто логуємо знайдені вузли ще раз при виклику дії
this.logTargetNodeNames();
// Логіку зміни режимів поки що прибрали
// if (action === "Bypass All Hubs") { ... }
}
// Цей вузол не виконує обчислень, тому onExecute може бути порожнім
onExecute() {
// console.log(`[${ActiveModel.title}] Executed (likely no-op).`);
}
// Статичний метод для налаштування та реєстрації вузла
static setUp(clazz) {
// Явно встановлюємо статичні властивості, які потрібні LiteGraph
clazz.title = ActiveModel.title;
clazz.type = ActiveModel.type;
clazz.title_mode = LiteGraph.NORMAL_TITLE;
clazz.collapsable = true;
clazz.size = [240, 50];
clazz.category = "MultiModel"; // Змінюємо категорію на основну
console.log(`[${ActiveModel.title}] Attempting to register node type via setUp: ${clazz.type}`);
try {
// Реєструємо сам клас
LiteGraph.registerNodeType(clazz.type, clazz);
console.log(`[${ActiveModel.title}] Node type registration via setUp SUCCESSFUL.`);
} catch (e) {
console.error(`[${ActiveModel.title}] Node type registration via setUp FAILED:`, e);
}
}
}
// Визначаємо дії, доступні в контекстному меню
ActiveModel.exposedActions = ["Log Hub Names", "Enable All Hubs", "Bypass All Hubs", "Toggle All Hubs"];
// Визначаємо статичні властивості title та type тут, щоб вони були доступні для setUp
ActiveModel.title = "Active Model";
ActiveModel.type = "ActiveModel";
// --- Допоміжні функції --- //
// Функція пошуку цільових вузлів (тепер отримує їх з сервісу)
function findTargetNodes() {
return HubNodesService.getHubNodes();
}
// Функція зміни режиму ОДНОГО вузла
function setHubMode(targetNode, mode) {
if (!targetNode) return;
const currentMode = targetNode.mode;
const modeStr = mode === MODE_ALWAYS ? "ON" : "OFF";
console.log(`[${NODE_TITLE}] Setting Node ID ${targetNode.id} ('${targetNode.title}') to mode ${modeStr}`);
// Якщо ми вмикаємо вузол (MODE_ALWAYS), і при цьому поточний режим НЕ MODE_ALWAYS
// (тобто вузол був вимкнений, а тепер ми його вмикаємо)
if (mode === MODE_ALWAYS && currentMode !== MODE_ALWAYS) {
// Потрібно перевірити, чи є інші активні вузли, і вимкнути їх
const allHubNodes = HubNodesService.getHubNodes();
const otherActiveNodes = allHubNodes.filter(n => n.id !== targetNode.id && n.mode === MODE_ALWAYS);
if (otherActiveNodes.length > 0) {
console.log(`[${NODE_TITLE}] Found ${otherActiveNodes.length} other active nodes, disabling them...`);
otherActiveNodes.forEach(node => {
node.mode = MODE_BYPASS;
console.log(`[${NODE_TITLE}] Disabled node ID ${node.id} ('${node.title}')`);
// Оновлюємо віджети всіх байпасерів для цього вузла
updateAllBypasserWidgets(node);
});
}
}
// Змінюємо режим цільового вузла, незалежно від того, чи є інші активні вузли
if (currentMode !== mode) {
targetNode.mode = mode;
app.graph.setDirtyCanvas(true, true);
// Також оновлюємо віджети для цього вузла
updateAllBypasserWidgets(targetNode);
}
}
// Нова функція для оновлення віджетів у всіх байпасерах для конкретного вузла
function updateAllBypasserWidgets(targetNode) {
if (!targetNode) return;
console.log(`[${NODE_TITLE}] Updating widgets for node ID: ${targetNode.id} in all bypassers`);
const bypassers = HubNodesService.bypasserNodes;
bypassers.forEach(bypasser => {
if (!bypasser || !bypasser.widgets) return;
const widgetName = `hub_${targetNode.id}`;
const widget = bypasser.widgets.find(w => w.name === widgetName);
if (widget) {
// Оновлюємо значення віджета
const isBypassed = targetNode.mode === MODE_BYPASS;
const newValue = !isBypassed;
// Оновлюємо також заголовок, щоб він відповідав новому формату
const newLabel = `${targetNode.title || '(Untitled)'}`;
let changed = false;
if (widget.value !== newValue) {
console.log(`[${NODE_TITLE}] Updating widget value in bypasser ID: ${bypasser.id} for node ID: ${targetNode.id} to: ${newValue}`);
widget.value = newValue;
changed = true;
}
if (widget.label !== newLabel) {
console.log(`[${NODE_TITLE}] Updating widget label in bypasser ID: ${bypasser.id} for node ID: ${targetNode.id} to: "${newLabel}"`);
widget.label = newLabel;
changed = true;
}
if (changed) {
bypasser.graph.setDirtyCanvas(true, false);
}
}
});
}
// --- Сервіс для керування вузлами --- //
const HubNodesService = {
hubNodes: [],
bypasserNodes: [],
updateTimeout: null,
updateDelay: 100,
app: null, // Додано для доступу до app
registerBypasserNode(node) {
if (!this.bypasserNodes.find(n => n.id === node.id)) {
console.log(`[HubNodesService] Registering bypasser node ID: ${node.id}`);
this.bypasserNodes.push(node);
this.scheduleUpdate(`registerBypasser_${node.id}`);
}
},
unregisterBypasserNode(node) {
const index = this.bypasserNodes.findIndex(n => n.id === node.id);
if (index !== -1) {
console.log(`[HubNodesService] Unregistering bypasser node ID: ${node.id}`);
this.bypasserNodes.splice(index, 1);
// Оновлення не потрібне при видаленні самого байпасера
} else {
console.log(`[HubNodesService] Node ID ${node.id} not found for unregistration.`);
}
},
getHubNodes() {
return [...this.hubNodes];
},
// --- Нові методи для сповіщень від Hub --- //
notifyHubAdded(node) {
if (!node) return;
console.log(`[HubNodesService] Hub node added notification received: ID ${node.id}`);
if (!this.hubNodes.find(n => n.id === node.id)) {
this.hubNodes.push(node);
this.hubNodes.sort((a, b) => a.id - b.id); // Підтримуємо сортування
this.scheduleUpdate(`notifyHubAdded_${node.id}`);
} else {
console.log(`[HubNodesService] Hub node ID ${node.id} already exists.`);
}
},
notifyHubRemoved(node) {
if (!node) return;
console.log(`[HubNodesService] Hub node removed notification received: ID ${node.id}`);
const index = this.hubNodes.findIndex(n => n.id === node.id);
if (index !== -1) {
this.hubNodes.splice(index, 1);
this.scheduleUpdate(`notifyHubRemoved_${node.id}`);
} else {
console.log(`[HubNodesService] Hub node ID ${node.id} not found for removal notification.`);
}
},
notifyHubRenamed(node) {
if (!node) return;
console.log(`[HubNodesService] Hub node rename notification received: ID ${node.id}`);
// Сам список hubNodes містить посилання на об'єкти вузлів,
// тому оновлювати список не потрібно, зміна title вже відбулася.
// Просто плануємо оновлення віджетів, щоб відобразити нову назву.
this.scheduleUpdate(`notifyHubRenamed_${node.id}`);
},
// --- Кінець нових методів --- //
scheduleUpdate(reason = "unknown") {
console.log(`[HubNodesService] Scheduling update. Reason: ${reason}`);
if (this.updateTimeout) {
clearTimeout(this.updateTimeout);
}
this.updateTimeout = setTimeout(() => {
console.log("[HubNodesService] Timeout reached. Running update...");
this.updateTimeout = null;
// Оновлення списку hubNodes більше не потрібне тут
// this.refreshHubNodes();
this.bypasserNodes.forEach(node => {
if (node.refreshWidgets) {
console.log(`[HubNodesService] Refreshing widgets for bypasser node ID: ${node.id}`);
node.refreshWidgets();
} else {
console.warn(`[HubNodesService] Bypasser node ID: ${node.id} missing refreshWidgets method.`);
}
});
console.log("[HubNodesService] Update finished.");
}, this.updateDelay);
}
};
app.registerExtension({
name: "MultiModel.ActiveModel.ServiceDriven", // Оновлено назву розширення
setup(appInstance) {
console.log(`[${NODE_TITLE}] === Extension setup CALLED ===`);
// Реєструємо сервіс, щоб він був доступний іншим розширенням
appInstance.HubNodesService = HubNodesService;
HubNodesService.app = appInstance;
// Додаємо перехоплення onNodeAdded для відстеження додавання нашої ноди
if (appInstance.graph && !appInstance.graph._activemodel_onNodeAdded_attached) {
const origNodeAdded = appInstance.graph.onNodeAdded;
appInstance.graph.onNodeAdded = function(node) {
// Спочатку викликаємо оригінальний обробник
if (origNodeAdded) {
origNodeAdded.call(this, node);
}
// Перевіряємо, чи це наша нода ActiveModel
if (node.type === NODE_TYPE) {
console.log(`[${NODE_TITLE}] Our node type was added to graph: ID ${node.id}`);
// Викликаємо обробку ноди для додавання віджетів
const extension = appInstance.extensions.find(ext => ext.name === "MultiModel.ActiveModel.ServiceDriven");
if (extension) {
// Конфігуруємо ноду
extension.loadedGraphNode(node, appInstance);
// Оновлюємо віджети з невеликою затримкою
setTimeout(() => {
if (node.refreshWidgets) {
console.log(`[${NODE_TITLE}] Running refreshWidgets for newly added node ID: ${node.id}`);
node.refreshWidgets();
}
}, 100);
}
}
};
appInstance.graph._activemodel_onNodeAdded_attached = true;
console.log(`[${NODE_TITLE}] Attached our onNodeAdded handler to graph`);
}
// Початкове сканування потрібне, щоб знайти вузли, які вже існують при завантаженні
setTimeout(() => {
console.log("[HubNodesService] Initial node scan after setup.");
// Знаходимо існуючі Hub та Bypasser ноди і реєструємо їх
let initialHubsFound = 0;
let initialBypassersFound = 0;
appInstance.graph._nodes.forEach(node => {
if (node.type === TARGET_NODE_TYPE) {
HubNodesService.notifyHubAdded(node);
initialHubsFound++;
} else if (node.type === NODE_TYPE) {
// Реєстрація bypasser'а відбувається також у loadedGraphNode,
// але продублюємо тут на випадок проблем з порядком
HubNodesService.registerBypasserNode(node);
initialBypassersFound++;
// Додаємо додаткову ініціалізацію для вже існуючих Bypasser нод
this.loadedGraphNode(node, appInstance);
}
});
console.log(`[HubNodesService] Initial scan found ${initialHubsFound} hubs and ${initialBypassersFound} bypassers.`);
// Додаємо окремий виклик для оновлення віджетів для всіх bypassers з більшою затримкою
if (initialBypassersFound > 0) {
setTimeout(() => {
console.log(`[${NODE_TITLE}] Refreshing widgets for ${initialBypassersFound} bypassers after initial scan`);
HubNodesService.bypasserNodes.forEach(node => {
if (node && node.refreshWidgets) {
node.refreshWidgets();
}
});
}, 500);
}
// Оновлення віджетів запланується всередині notifyHubAdded/registerBypasserNode
}, 500);
},
// loadedGraphNode тепер відповідає ТІЛЬКИ за конфігурацію САМОГО ActiveModel
loadedGraphNode(node, appInstance) {
if (node.type === NODE_TYPE) {
console.log(`[${NODE_TITLE}] Configuring node instance ID: ${node.id}`);
node.app = appInstance;
node.IS_BYPASSER_NODE = true;
delete node.onAction; // Видаляємо, бо додамо getActions
// Визначаємо refreshWidgets
node.refreshWidgets = function() {
console.log(`[${NODE_TITLE} ID: ${this.id}] Running refreshWidgets...`);
const targetNodes = findTargetNodes(); // Отримуємо з сервісу
console.log(`[${NODE_TITLE} ID: ${this.id}] Got ${targetNodes.length} target nodes from service.`);
// Додаємо детальну діагностику
if (targetNodes.length === 0) {
console.log(`[${NODE_TITLE} ID: ${this.id}] No target nodes found. HubNodesService state:`,
{hubNodesCount: HubNodesService.hubNodes.length,
bypassersCount: HubNodesService.bypasserNodes.length});
} else {
console.log(`[${NODE_TITLE} ID: ${this.id}] Target nodes details:`,
targetNodes.map(n => ({id: n.id, title: n.title, mode: n.mode})));
}
// --- Подальша логіка залишається ТАКОЮ Ж САМОЮ, як і раніше ---
// (Порівняння віджетів, додавання, видалення, оновлення)
const currentWidgets = this.widgets ? [...this.widgets] : [];
let widgetsChanged = false;
const activeWidgetNames = new Set();
// Add/Update widgets
targetNodes.forEach(targetNode => {
if (!targetNode) {
console.warn(`[${NODE_TITLE} ID: ${this.id}] Found a null/undefined targetNode in the list from service. Skipping.`);
return; // Пропускаємо невалідні ноди
}
const widgetName = `hub_${targetNode.id}`;
// Прибираємо ID ноди з заголовка
const widgetLabel = `${targetNode.title || '(Untitled)'}`;
const isBypassed = targetNode.mode === MODE_BYPASS;
const widgetValue = !isBypassed;
activeWidgetNames.add(widgetName);
let existingWidget = this.widgets?.find(w => w.name === widgetName);
if (existingWidget) {
let labelChanged = false;
if (existingWidget.label !== widgetLabel) {
existingWidget.label = widgetLabel;
labelChanged = true;
}
if (existingWidget.value !== widgetValue) {
existingWidget.value = widgetValue;
widgetsChanged = true;
}
if (labelChanged) widgetsChanged = true;
if (!existingWidget.callback) {
existingWidget.callback = (value) => {
// Нова логіка: якщо перемикач вмикається, вимикаємо всі інші
// якщо вимикається - просто вимикаємо
const nodeToChange = app.graph.getNodeById(targetNode.id);
if (nodeToChange) setHubMode(nodeToChange, value ? MODE_ALWAYS : MODE_BYPASS);
};
widgetsChanged = true;
}
} else {
this.addWidget(
"toggle", widgetName, widgetValue,
(value) => {
// Нова логіка: дозволяємо будь-які переключення
const nodeToChange = app.graph.getNodeById(targetNode.id);
if (nodeToChange) setHubMode(nodeToChange, value ? MODE_ALWAYS : MODE_BYPASS);
else this.refreshWidgets();
},
{ on: "ON", off: "OFF" }
);
this.widgets[this.widgets.length - 1].label = widgetLabel;
widgetsChanged = true;
}
});
// Remove outdated widgets
let removedWidgets = false;
if (this.widgets) {
for (let i = this.widgets.length - 1; i >= 0; i--) {
const widget = this.widgets[i];
if (widget.name?.startsWith("hub_") && !activeWidgetNames.has(widget.name)) {
this.widgets.splice(i, 1);
widgetsChanged = true;
removedWidgets = true;
}
}
}
// Resize node only if widgets were added/removed
if (widgetsChanged) {
// ... (логіка зміни розміру залишається такою ж) ...
const baseHeight = LiteGraph.NODE_TITLE_HEIGHT + 5;
const widgetHeight = LiteGraph.NODE_WIDGET_HEIGHT;
const spacing = 4;
const hubWidgets = this.widgets?.filter(w => w.name?.startsWith("hub_")) || [];
const numWidgets = hubWidgets.length;
const requiredHeight = baseHeight + numWidgets * (widgetHeight + spacing) + (numWidgets > 0 ? spacing : 0);
const computedSize = this.computeSize();
const newHeight = Math.max(computedSize[1], requiredHeight);
if (Math.abs(this.size[1] - newHeight) > 2 || removedWidgets) {
this.size[1] = newHeight;
this.graph.setDirtyCanvas(true, true);
} else if (widgetsChanged) {
this.graph.setDirtyCanvas(true, false);
}
}
// Перевірка на кількість активних вузлів після оновлення віджетів
setTimeout(() => {
const activeNodes = targetNodes.filter(node => node.mode === MODE_ALWAYS);
console.log(`[${NODE_TITLE} ID: ${this.id}] After refresh: ${activeNodes.length} active nodes`);
// Якщо більше одного активного вузла, залишаємо тільки перший
// Але не активуємо автоматично вузли, якщо всі вимкнені
if (activeNodes.length > 1) {
console.log(`[${NODE_TITLE} ID: ${this.id}] Multiple active nodes (${activeNodes.length}), keeping only the first one...`);
// Залишаємо перший вузол активним, інші вимикаємо
for (let i = 1; i < activeNodes.length; i++) {
setHubMode(activeNodes[i], MODE_BYPASS);
// Оновлюємо відповідний віджет
const widgetName = `hub_${activeNodes[i].id}`;
const widget = this.widgets?.find(w => w.name === widgetName);
if (widget) {
widget.value = false;
}
}
this.graph.setDirtyCanvas(true, false);
}
}, 100);
console.log(`[${NODE_TITLE} ID: ${this.id}] refreshWidgets finished.`);
};
// Визначаємо дії для контекстного меню
node.getActions = function() {
const actions = [];
if (this.widgets?.some(w => w.name.startsWith("hub_"))) {
actions.push({ name: "Activate First Hub", callback: () => this.setAllHubsMode(MODE_ALWAYS) });
actions.push({ name: "Switch To Next Hub", callback: () => this.toggleAllHubsMode() });
}
actions.push({ name: "Log Hub Names (Console)", callback: () => this.logTargetNodeNames() });
return actions;
};
node.logTargetNodeNames = function() { // Додаємо метод до екземпляра
console.log(`[${NODE_TITLE} ID: ${this.id}] Logging target node names...`);
const nodes = HubNodesService.getHubNodes();
console.log(` Found ${nodes.length} nodes via service:`);
if (nodes.length > 0) {
nodes.forEach(n => console.log(` - Node ID: ${n.id}, Title: ${n.title}, Current mode: ${n.mode}`));
} else {
console.log(` (No nodes found)`);
}
};
// Оновлений метод для встановлення режиму всіх хабів
node.setAllHubsMode = function(mode) {
console.log(`[${NODE_TITLE} ID: ${this.id}] setAllHubsMode called with mode: ${mode}`);
const hubNodes = HubNodesService.getHubNodes();
if (hubNodes.length === 0) {
console.log(`[${NODE_TITLE} ID: ${this.id}] No hub nodes found to set mode.`);
return;
}
if (mode === MODE_ALWAYS) {
// Активуємо тільки перший вузол, інші вимикаємо
console.log(`[${NODE_TITLE} ID: ${this.id}] Enabling only the first hub node, disabling others.`);
hubNodes.forEach((node, index) => {
const newMode = index === 0 ? MODE_ALWAYS : MODE_BYPASS;
if (node.mode !== newMode) {
node.mode = newMode;
updateAllBypasserWidgets(node);
}
});
app.graph.setDirtyCanvas(true, true);
} else if (mode === MODE_BYPASS) {
// Якщо режим BYPASS, вимикаємо всі вузли
console.log(`[${NODE_TITLE} ID: ${this.id}] Disabling all hub nodes.`);
let changed = false;
hubNodes.forEach(node => {
if (node.mode !== MODE_BYPASS) {
node.mode = MODE_BYPASS;
updateAllBypasserWidgets(node);
changed = true;
}
});
if (changed) {
app.graph.setDirtyCanvas(true, true);
}
}
};
// Оновлений метод для перемикання вузлів
node.toggleAllHubsMode = function() {
console.log(`[${NODE_TITLE} ID: ${this.id}] toggleAllHubsMode called`);
const hubNodes = HubNodesService.getHubNodes();
if (hubNodes.length === 0) {
console.log(`[${NODE_TITLE} ID: ${this.id}] No hub nodes found to toggle.`);
return;
}
// Перевіряємо, чи є хоча б один активний вузол
const activeNodes = hubNodes.filter(n => n.mode === MODE_ALWAYS);
if (activeNodes.length === 0) {
// Якщо немає активних вузлів, активуємо перший
console.log(`[${NODE_TITLE} ID: ${this.id}] No active nodes, activating the first one.`);
if (hubNodes.length > 0) {
const node = hubNodes[0];
node.mode = MODE_ALWAYS;
updateAllBypasserWidgets(node);
app.graph.setDirtyCanvas(true, true);
}
} else {
// Якщо є активний вузол, знаходимо його індекс і активуємо наступний
const activeNode = activeNodes[0];
const currentIndex = hubNodes.findIndex(n => n.id === activeNode.id);
// Вимикаємо поточний активний вузол
activeNode.mode = MODE_BYPASS;
updateAllBypasserWidgets(activeNode);
// Знаходимо наступний вузол (або переходимо до першого, якщо це останній)
const nextIndex = (currentIndex + 1) % hubNodes.length;
const nextNode = hubNodes[nextIndex];
console.log(`[${NODE_TITLE} ID: ${this.id}] Toggling from node ID ${activeNode.id} to node ID ${nextNode.id}`);
// Активуємо наступний вузол
nextNode.mode = MODE_ALWAYS;
updateAllBypasserWidgets(nextNode);
app.graph.setDirtyCanvas(true, true);
}
};
// Реєстрація/видалення самого bypasser'а в сервісі
node.onAdded = function() {
console.log(`[${NODE_TITLE} ID: ${this.id}] Bypasser node added to graph. Registering with service.`);
if (appInstance.HubNodesService) { // Перевіряємо наявність сервісу
appInstance.HubNodesService.registerBypasserNode(this);
} else {
console.error(`[${NODE_TITLE} ID: ${this.id}] Cannot register bypasser, HubNodesService not found!`);
}
};
node.onRemoved = function() {
console.log(`[${NODE_TITLE} ID: ${this.id}] Bypasser node removed from graph. Unregistering from service.`);
if (appInstance.HubNodesService) {
appInstance.HubNodesService.unregisterBypasserNode(this);
} else {
console.error(`[${NODE_TITLE} ID: ${this.id}] Cannot unregister bypasser, HubNodesService not found!`);
}
};
// Початкова реєстрація, якщо вузол вже існує при завантаженні
if (appInstance.HubNodesService) {
appInstance.HubNodesService.registerBypasserNode(node);
} else {
// Якщо сервіс ще не створено (малоймовірно, але можливо),
// реєстрація відбудеться пізніше через onAdded
console.warn(`[${NODE_TITLE} ID: ${node.id}] Initial registration deferred, HubNodesService not yet available.`);
}
// Початкове оновлення віджетів для цього конкретного вузла
setTimeout(() => { if (node.refreshWidgets) node.refreshWidgets(); }, 50);
}
},
getExtraMenuOptions(_, options) {
options.push({
content: "Add Active Model",
callback: () => {
const node = LiteGraph.createNode(NODE_TYPE);
// Записуємо текст для коментаря, щоб показати функціональність
node.properties.desc = "Controls the flow of Model Parameters Pipe Hubs";
app.graph.add(node);
// Розміщення вузла в центрі видимої області
const canvas = app.canvas;
const visible_rect = canvas.visible_area;
node.pos[0] = visible_rect[0] + visible_rect[2] * 0.5 - node.size[0] * 0.5;
node.pos[1] = visible_rect[1] + visible_rect[3] * 0.5 - node.size[1] * 0.5;
console.log(`[${NODE_TITLE}] Node created from menu with ID: ${node.id}`);
// Явно налаштуємо ноду після створення з меню
if (app.HubNodesService) {
// Отримуємо розширення
const extension = app.extensions.find(ext => ext.name === "MultiModel.ActiveModel.ServiceDriven");
if (extension) {
// Конфігуруємо ноду
console.log(`[${NODE_TITLE}] Manually configuring node created from menu`);
extension.loadedGraphNode(node, app);
// Оновлюємо віджети з затримкою
setTimeout(() => {
if (node.refreshWidgets) {
console.log(`[${NODE_TITLE}] Refreshing widgets for node created from menu`);
node.refreshWidgets();
}
}, 100);
}
}
}
});
},
nodeRegistered(type, clazz) {
console.log(`[${NODE_TITLE}] nodeRegistered called for type: ${type}`);
if (type === NODE_TYPE) {
try {
console.log(`[${NODE_TITLE}] Running additional setup for our node type`);
ActiveModel.setUp(clazz);
} catch (e) {
console.error(`[${NODE_TITLE}] Error in additional node setup:`, e);
}
}
},
// Додаємо обробники створення ноди для максимального покриття
nodeCreated(node) {
console.log(`[${NODE_TITLE}] nodeCreated called for node type: ${node?.type || 'undefined'}, ID: ${node?.id || 'undefined'}`);
if (node && node.type === NODE_TYPE) {
console.log(`[${NODE_TITLE}] Our node created, configuring it...`);
this.loadedGraphNode(node, app);
// Запускаємо оновлення віджетів з затримкою
setTimeout(() => {
if (node.refreshWidgets) {
console.log(`[${NODE_TITLE}] Refreshing widgets after node creation`);
node.refreshWidgets();
}
}, 100);
}
},
onNodeAdded(node) {
console.log(`[${NODE_TITLE}] onNodeAdded callback called for node type: ${node?.type || 'undefined'}, ID: ${node?.id || 'undefined'}`);
if (node && node.type === NODE_TYPE) {
console.log(`[${NODE_TITLE}] Our node added via onNodeAdded, configuring it...`);
this.loadedGraphNode(node, app);
// Запускаємо оновлення віджетів з затримкою
setTimeout(() => {
if (node.refreshWidgets) {
console.log(`[${NODE_TITLE}] Refreshing widgets after node was added`);
node.refreshWidgets();
}
}, 100);
}
}
});
console.log(`[${NODE_TITLE}] JS file loaded`);