mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-26 05:24:09 -03:00
fix: keep bypass/mute state on frontend 1.53+ node shell state (#1123)
ComfyUI frontend 1.53 turned LGraphNode.mode into a prototype accessor backed by node._state, and serialize() now reads that state directly. Redefining mode on the instance shadowed the setter, so bypass/mute never reached the serialized workflow and silently reverted to Always on save/reload or workflow tab switch. Add interceptModeChange() in web/comfyui/utils.js: it delegates to the prototype accessor when one exists (observing changes only), and falls back to the legacy closure accessor on older frontends. Use it in lora_loader.js and in the Vue widgets' setupModeChangeHandler, which covers the LoRA provider/aggregator nodes with the same latent bug.
This commit is contained in:
@@ -188,6 +188,74 @@ export function chainCallback(object, property, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a `mode` accessor (getter/setter) on the node's prototype chain.
|
||||
* Since ComfyUI frontend 1.53, `LGraphNode.mode` is a prototype accessor
|
||||
* backed by the node's shell state (`node._state.mode`); on legacy frontends
|
||||
* `mode` is a plain data property on the instance and no accessor exists.
|
||||
*/
|
||||
function findModeAccessor(node) {
|
||||
let proto = Object.getPrototypeOf(node);
|
||||
while (proto && proto !== Object.prototype) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(proto, "mode");
|
||||
if (descriptor && (descriptor.get || descriptor.set)) {
|
||||
return descriptor;
|
||||
}
|
||||
proto = Object.getPrototypeOf(proto);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe node mode changes (Always/Never/On Trigger/Bypass) without
|
||||
* breaking the frontend's own `mode` property.
|
||||
*
|
||||
* Since ComfyUI frontend 1.53, `mode` is a prototype accessor backed by
|
||||
* shell state and serialization reads that state directly. Redefining `mode`
|
||||
* on the instance would shadow the prototype setter, so bypass/mute never
|
||||
* reaches the serialized workflow and silently reverts to Always on reload.
|
||||
* When a prototype accessor exists we delegate to it and only observe the
|
||||
* change; on legacy frontends we keep the value in a closure as before.
|
||||
*
|
||||
* @param {Object} node - The litegraph node instance
|
||||
* @param {(newMode: number, oldMode: number) => void} onModeChange - Called when the mode actually changes
|
||||
*/
|
||||
export function interceptModeChange(node, onModeChange) {
|
||||
const delegate = findModeAccessor(node);
|
||||
|
||||
if (delegate && typeof delegate.get === "function" && typeof delegate.set === "function") {
|
||||
Object.defineProperty(node, "mode", {
|
||||
configurable: true,
|
||||
get() {
|
||||
return delegate.get.call(this);
|
||||
},
|
||||
set(value) {
|
||||
const oldValue = delegate.get.call(this);
|
||||
delegate.set.call(this, value);
|
||||
if (oldValue !== value) {
|
||||
onModeChange(value, oldValue);
|
||||
}
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let currentMode = node.mode;
|
||||
Object.defineProperty(node, "mode", {
|
||||
configurable: true,
|
||||
get() {
|
||||
return currentMode;
|
||||
},
|
||||
set(value) {
|
||||
const oldValue = currentMode;
|
||||
currentMode = value;
|
||||
if (oldValue !== value) {
|
||||
onModeChange(value, oldValue);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a toast notification
|
||||
* @param {Object|string} options - Toast options object or message string for backward compatibility
|
||||
|
||||
Reference in New Issue
Block a user