diff --git a/src_web/comfyui/base_node.ts b/src_web/comfyui/base_node.ts index 3a68eac..3ce74ab 100644 --- a/src_web/comfyui/base_node.ts +++ b/src_web/comfyui/base_node.ts @@ -21,23 +21,11 @@ import { importIndividualNodesInnerOnDragDrop, importIndividualNodesInnerOnDragOver, } from "./feature_import_individual_nodes.js"; -import {defineProperty, moveArrayItem} from "rgthree/common/shared_utils.js"; - -/** - * Finds the `mode` accessor on the prototype chain. Newer ComfyUI frontends track `mode` as shell - * state through an accessor on LGraphNode (backed by `_state.mode`), so our instance-level override - * must delegate to it. Otherwise `_state.mode` (used when serializing) never sees the change. - * Returns null on legacy frontends, where `mode` is a plain data property. - */ -function findPrototypeModeDescriptor(node: object): PropertyDescriptor | null { - let proto = Object.getPrototypeOf(node); - while (proto) { - const descriptor = Object.getOwnPropertyDescriptor(proto, "mode"); - if (descriptor && (descriptor.get || descriptor.set)) return descriptor; - proto = Object.getPrototypeOf(proto); - } - return null; -} +import { + defineProperty, + findPrototypePropertyDescriptor, + moveArrayItem, +} from "rgthree/common/shared_utils.js"; /** * A base node with standard methods, directly extending the LGraphNode. @@ -109,7 +97,10 @@ export abstract class RgthreeBaseNode extends LGraphNode { this.checkAndRunOnConstructed(); }); - const protoMode = findPrototypeModeDescriptor(this); + // Newer ComfyUI frontends track `mode` through an accessor on LGraphNode (backed by + // `_state.mode`, which is what gets serialized), so our override must delegate to it. + // On legacy frontends `mode` is a plain data property and this is null. + const protoMode = findPrototypePropertyDescriptor(this, "mode"); const readMode = (): LGraphEventMode | undefined => protoMode?.get ? protoMode.get.call(this) : this.rgthree_mode; diff --git a/src_web/common/shared_utils.ts b/src_web/common/shared_utils.ts index 4adea7e..bc08d9c 100644 --- a/src_web/common/shared_utils.ts +++ b/src_web/common/shared_utils.ts @@ -235,6 +235,25 @@ export function defineProperty(instance: any, property: string, desc: PropertyDe return Object.defineProperty(instance, property, desc); } +/** + * Finds the accessor (getter and/or setter) for `property` on the prototype chain of `instance`, + * skipping `instance`'s own properties. Useful when an instance-level property override needs to + * delegate to an accessor defined by a parent class. Returns null if no accessor is found, e.g. + * when the property is a plain data property. + */ +export function findPrototypePropertyDescriptor( + instance: any, + property: string, +): PropertyDescriptor | null { + let proto = Object.getPrototypeOf(instance); + while (proto) { + const descriptor = Object.getOwnPropertyDescriptor(proto, property); + if (descriptor && (descriptor.get || descriptor.set)) return descriptor; + proto = Object.getPrototypeOf(proto); + } + return null; +} + /** * Determines if two DataViews are equal. */ diff --git a/web/comfyui/base_node.js b/web/comfyui/base_node.js index 487c744..11fbe52 100644 --- a/web/comfyui/base_node.js +++ b/web/comfyui/base_node.js @@ -5,23 +5,7 @@ import { LogLevel, rgthree } from "./rgthree.js"; import { addHelpMenuItem } from "./utils.js"; import { RgthreeHelpDialog } from "../../rgthree/common/dialog.js"; import { importIndividualNodesInnerOnDragDrop, importIndividualNodesInnerOnDragOver, } from "./feature_import_individual_nodes.js"; -import { defineProperty, moveArrayItem } from "../../rgthree/common/shared_utils.js"; -/** - * Finds the `mode` accessor on the prototype chain. Newer ComfyUI frontends track `mode` as shell - * state through an accessor on LGraphNode (backed by `_state.mode`), so our instance-level override - * must delegate to it. Otherwise `_state.mode` (used when serializing) never sees the change. - * Returns null on legacy frontends, where `mode` is a plain data property. - */ -function findPrototypeModeDescriptor(node) { - let proto = Object.getPrototypeOf(node); - while (proto) { - const descriptor = Object.getOwnPropertyDescriptor(proto, "mode"); - if (descriptor && (descriptor.get || descriptor.set)) - return descriptor; - proto = Object.getPrototypeOf(proto); - } - return null; -} +import { defineProperty, findPrototypePropertyDescriptor, moveArrayItem, } from "../../rgthree/common/shared_utils.js"; export class RgthreeBaseNode extends LGraphNode { constructor(title = RgthreeBaseNode.title, skipOnConstructedCall = true) { super(title); @@ -48,18 +32,16 @@ export class RgthreeBaseNode extends LGraphNode { } this.checkAndRunOnConstructed(); }); - const protoMode = findPrototypeModeDescriptor(this); + const protoMode = findPrototypePropertyDescriptor(this, "mode"); const readMode = () => (protoMode === null || protoMode === void 0 ? void 0 : protoMode.get) ? protoMode.get.call(this) : this.rgthree_mode; defineProperty(this, "mode", { get: () => readMode(), set: (mode) => { const oldMode = readMode(); if (protoMode === null || protoMode === void 0 ? void 0 : protoMode.set) { - // Newer frontends: write through so `_state.mode` (used by serialization) stays in sync. protoMode.set.call(this, mode); } else { - // Legacy frontends: keep the original behavior. this.rgthree_mode = mode; } if (oldMode != mode) { diff --git a/web/common/shared_utils.js b/web/common/shared_utils.js index 61ba549..e858a97 100644 --- a/web/common/shared_utils.js +++ b/web/common/shared_utils.js @@ -156,6 +156,16 @@ export function defineProperty(instance, property, desc) { } return Object.defineProperty(instance, property, desc); } +export function findPrototypePropertyDescriptor(instance, property) { + let proto = Object.getPrototypeOf(instance); + while (proto) { + const descriptor = Object.getOwnPropertyDescriptor(proto, property); + if (descriptor && (descriptor.get || descriptor.set)) + return descriptor; + proto = Object.getPrototypeOf(proto); + } + return null; +} export function areDataViewsEqual(a, b) { if (a.byteLength !== b.byteLength) { return false;