Changed name of mirror node to try discourage manual adding.
Fixed callback functions and restoring callbacks when the last mirror is deleted. If the original is deleted then the mirror tells the user that it is broken. Disabled creating a mirror of something with no widgets.
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UINode": "UINode [GroxicTinch]",
|
||||
"UINode": "Mirror node [Do not add manually!]",
|
||||
}
|
||||
|
||||
WEB_DIRECTORY = "./web/comfyui"
|
||||
|
||||
+109
-50
@@ -1,9 +1,7 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
// [FIXME] Delete the UINode if the original node is deleted
|
||||
// [FIXME] Order of widgets doesnt persist properly when refreshing page
|
||||
// [FIXME] elements arent working (preview image, customtext)
|
||||
// [FIXME] adding multiple mirrors will cause callback restoring to break
|
||||
// [TODO] Add a way to get taken to the original node from the UINode
|
||||
// [TODO] Combine many nodes into one UINode
|
||||
|
||||
@@ -27,6 +25,9 @@ function createMirror(node) {
|
||||
uiNode.inputs = [];
|
||||
uiNode.outputs = [];
|
||||
|
||||
node.properties._easyui_mirrorNodeIds = node.properties._easyui_mirrorNodeIds || [];
|
||||
node.properties._easyui_mirrorNodeIds.push(uiNode.id);
|
||||
|
||||
app.canvas.setDirty(true);
|
||||
app.canvas.draw(true);
|
||||
|
||||
@@ -38,7 +39,7 @@ app.registerExtension({
|
||||
|
||||
beforeRegisterNodeDef(nodeTypeInfo) {
|
||||
const proto = nodeTypeInfo.prototype;
|
||||
const original = proto.getExtraMenuOptions;
|
||||
const original = proto.getExtraMenuOptions;
|
||||
|
||||
// hide the UINode from the node list
|
||||
if (nodeTypeInfo.comfyClass === 'UINode') {
|
||||
@@ -50,14 +51,16 @@ app.registerExtension({
|
||||
if (original) {
|
||||
original.apply(this, arguments);
|
||||
}
|
||||
|
||||
|
||||
options.push(null); // inserts a divider
|
||||
// 2) if it's a UINode, Don't add “Create Mirror Copy”
|
||||
if (this.type !== 'UINode') {
|
||||
options.push({
|
||||
content: '🖼️ EasyUI – Create Mirror Copy',
|
||||
callback: () => createMirror(this)
|
||||
});
|
||||
if(this.widgets.length > 0) {
|
||||
options.push({
|
||||
content: '🖼️ EasyUI – Create Mirror Copy',
|
||||
callback: () => createMirror(this)
|
||||
});
|
||||
}
|
||||
options.push({
|
||||
content: '🖼️ EasyUI [DEBUG]',
|
||||
callback: () => {
|
||||
@@ -82,15 +85,15 @@ app.registerExtension({
|
||||
// Add configure override for UINode to restore widgets on load
|
||||
if (nodeTypeInfo.comfyClass === 'UINode') {
|
||||
const originalOnAdded = proto.onAdded;
|
||||
const originalOnRemoved = proto.onRemoved;
|
||||
const originalConfigure = proto.configure;
|
||||
const originalOnRemoved = proto.onRemoved;
|
||||
const originalDrawTitleBox = proto.drawTitleBox;
|
||||
|
||||
proto.onAdded = function() {
|
||||
// call any existing onAdded first
|
||||
if (originalOnAdded) originalOnAdded.apply(this, arguments);
|
||||
|
||||
// if this UINode was NOT created via createMirror (no original_node_id),
|
||||
// add a label or change its title
|
||||
|
||||
// if this UINode was NOT created via createMirror (no original_node_id), change title
|
||||
setTimeout(() => {
|
||||
// Need timeout or else this.properties.original_node_id isnt set in time for the mirrored uinodes
|
||||
if (!this.properties?.original_node_id) {
|
||||
@@ -100,8 +103,8 @@ app.registerExtension({
|
||||
element.innerText = 'This UINode is not linked. It should not be created manually. Please right click on a node and select "🖼️ EasyUI – Create Mirror Copy"';
|
||||
this.addDOMWidget('Notice', 'customtext', element);
|
||||
|
||||
this.width = 355;
|
||||
this.height = 122;
|
||||
this.size[0] = 355;
|
||||
this.size[1] = 122;
|
||||
|
||||
// force a redraw so the new widget/title shows up immediately
|
||||
app.canvas.setDirty(true);
|
||||
@@ -129,23 +132,49 @@ app.registerExtension({
|
||||
};
|
||||
|
||||
proto.onRemoved = function() {
|
||||
// 1) restore any original callbacks
|
||||
const originalNodeId = this.properties.original_node_id;
|
||||
const originalNode = app.canvas.graph.getNodeById(originalNodeId);
|
||||
if (originalNode && originalNode.widgets) {
|
||||
originalNode.widgets.forEach(w => {
|
||||
if (w._easyui_originalCallback) {
|
||||
w.callback = w._easyui_originalCallback;
|
||||
delete w._easyui_originalCallback;
|
||||
if (w._easyui_mirrorCallbacks && w._easyui_mirrorCallbacks.has(this.id)) {
|
||||
w._easyui_mirrorCallbacks.delete(this.id);
|
||||
if (w._easyui_mirrorCallbacks.size === 0) {
|
||||
if (w._easyui_originalCallback) {
|
||||
w.callback = w._easyui_originalCallback;
|
||||
delete w._easyui_originalCallback;
|
||||
}
|
||||
delete w._easyui_mirrorCallbacks;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 2) call the original onRemoved if there was one
|
||||
|
||||
// Call the original onRemoved if there was one
|
||||
if (originalOnRemoved) {
|
||||
originalOnRemoved.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
proto.drawTitleBox = function(ctx) {
|
||||
const originalNodeId = this.properties.original_node_id;
|
||||
if (originalNodeId) {
|
||||
const originalNode = app.canvas.graph.getNodeById(originalNodeId);
|
||||
|
||||
if (!originalNode) {
|
||||
console.warn(`Original node with ID ${originalNodeId} not found.`);
|
||||
this.title = '❌ Orphaned UINode';
|
||||
this.properties.original_node_id = null;
|
||||
this.widgets = [];
|
||||
const element = document.createElement('span');
|
||||
element.innerText = 'This UINode is not linked. The original Node was deleted';
|
||||
this.addDOMWidget('Notice', 'customtext', element);
|
||||
}
|
||||
}
|
||||
|
||||
if (originalDrawTitleBox) {
|
||||
originalDrawTitleBox.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -316,7 +345,7 @@ function cloneWidgets(originalNode, uiNode) {
|
||||
uiNode.properties.hiddenWidgets = uiNode.properties.hiddenWidgets || [];
|
||||
uiNode.properties.widgetOrder = uiNode.properties.widgetOrder || [];
|
||||
|
||||
if(originalNode && originalNode.widgets) {
|
||||
if (originalNode && originalNode.widgets) {
|
||||
// Create a map of original widgets for easier lookup
|
||||
const widgetMap = new Map(originalNode.widgets.map(widget => [widget.name, widget]));
|
||||
|
||||
@@ -324,12 +353,12 @@ function cloneWidgets(originalNode, uiNode) {
|
||||
const orderedWidgets = uiNode.properties.widgetOrder
|
||||
.map(name => widgetMap.get(name))
|
||||
.filter(widget => widget !== undefined);
|
||||
|
||||
|
||||
// Add any remaining widgets that weren't in the saved order
|
||||
const remainingWidgets = originalNode.widgets.filter(
|
||||
widget => !uiNode.properties.widgetOrder.includes(widget.name)
|
||||
);
|
||||
|
||||
|
||||
// Combine ordered and remaining widgets
|
||||
const allWidgets = [...orderedWidgets, ...remainingWidgets];
|
||||
|
||||
@@ -337,7 +366,7 @@ function cloneWidgets(originalNode, uiNode) {
|
||||
const type = origW.type || 'string';
|
||||
const options = origW.options || {};
|
||||
try {
|
||||
// Example: Mark some widgets to be hidden initially
|
||||
// Do not show nodes that are currently inputs
|
||||
const isHidden = uiNode.properties.hiddenWidgets.includes(origW.name);
|
||||
if (origW.computedDisabled || isHidden || ignoredWidgets.includes(type)) {
|
||||
if (!isHidden) {
|
||||
@@ -345,11 +374,15 @@ function cloneWidgets(originalNode, uiNode) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const oldCallback = origW.callback;
|
||||
origW._easyui_originalCallback = oldCallback;
|
||||
|
||||
// Store original callback and initialize mirror callbacks
|
||||
if (!origW._easyui_originalCallback) {
|
||||
origW._easyui_originalCallback = origW.callback;
|
||||
origW._easyui_mirrorCallbacks = new Map();
|
||||
}
|
||||
|
||||
var newW;
|
||||
|
||||
|
||||
switch (type) {
|
||||
case 'custom':
|
||||
// [TODO]
|
||||
@@ -358,29 +391,36 @@ function cloneWidgets(originalNode, uiNode) {
|
||||
const element = origW.element.cloneNode(true);
|
||||
newW = uiNode.addDOMWidget(origW.name, type, element);
|
||||
newW.inputEl = element;
|
||||
|
||||
// initialize value
|
||||
newW.isSyncing = false;
|
||||
|
||||
// Initialize value
|
||||
newW.value = origW.value;
|
||||
|
||||
// 2) Listen on the cloned textarea → push back to original
|
||||
// Mirror → Original
|
||||
element.addEventListener('input', e => {
|
||||
if (newW.isSyncing) return;
|
||||
newW.isSyncing = true;
|
||||
origW.element.value = e.target.value;
|
||||
origW.value = e.target.value;
|
||||
if (typeof origW.callback === 'function') {
|
||||
origW.callback.call(origW, e.target.value);
|
||||
}
|
||||
app.canvas.setDirty(true);
|
||||
newW.isSyncing = false;
|
||||
});
|
||||
|
||||
// 3) Listen on the original textarea → push forward to clone
|
||||
origW.element.addEventListener('input', e => {
|
||||
if (newW.isSyncing) return;
|
||||
newW.isSyncing = true;
|
||||
element.value = e.target.value;
|
||||
newW.value = e.target.value;
|
||||
app.canvas.setDirty(true);
|
||||
newW.isSyncing = false;
|
||||
});
|
||||
// Original → Mirror
|
||||
if (!origW._easyui_mirrorCallbacks.has(uiNode.id)) {
|
||||
origW._easyui_mirrorCallbacks.set(uiNode.id, function(v) {
|
||||
if (!newW.isSyncing) {
|
||||
newW.isSyncing = true;
|
||||
element.value = v;
|
||||
newW.value = v;
|
||||
newW.isSyncing = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
// Note: Original event listener should be set once elsewhere
|
||||
break;
|
||||
default:
|
||||
newW = uiNode.addWidget(
|
||||
@@ -388,24 +428,43 @@ function cloneWidgets(originalNode, uiNode) {
|
||||
origW.name,
|
||||
origW.value,
|
||||
function(...args) {
|
||||
if (newW.isSyncing) return;
|
||||
newW.isSyncing = true;
|
||||
const v = args[0];
|
||||
origW.value = v;
|
||||
if (typeof oldCallback === 'function') {
|
||||
oldCallback.call(origW, ...args);
|
||||
if (typeof origW.callback === 'function') {
|
||||
origW.callback.call(origW, v);
|
||||
}
|
||||
app.canvas.setDirty(true);
|
||||
newW.isSyncing = false;
|
||||
},
|
||||
options
|
||||
);
|
||||
newW.isSyncing = false;
|
||||
|
||||
origW.callback = function(...args) {
|
||||
const v = args[0];
|
||||
if (typeof oldCallback === 'function') {
|
||||
oldCallback.call(this, ...args);
|
||||
}
|
||||
newW.value = v;
|
||||
app.canvas.setDirty(true);
|
||||
};
|
||||
if (origW._easyui_mirrorCallbacks.size === 0) {
|
||||
origW.callback = function(...args) {
|
||||
for (const [key, value] of origW._easyui_mirrorCallbacks) {
|
||||
if (typeof value === 'function') {
|
||||
value.call(this, args[0]);
|
||||
}
|
||||
}
|
||||
if (typeof origW._easyui_originalCallback === 'function') {
|
||||
origW._easyui_originalCallback.call(this, ...args);
|
||||
}
|
||||
app.canvas.setDirty(true);
|
||||
};
|
||||
}
|
||||
|
||||
if (!origW._easyui_mirrorCallbacks.has(uiNode.id)) {
|
||||
origW._easyui_mirrorCallbacks.set(uiNode.id, function(v) {
|
||||
if (!newW.isSyncing) {
|
||||
newW.isSyncing = true;
|
||||
newW.value = v;
|
||||
newW.isSyncing = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user