Move prototype accessor lookup into shared_utils as a generic helper

This commit is contained in:
unclehooru
2026-10-05 17:05:16 +09:00
parent 10f3769d37
commit 8863b9e658
4 changed files with 40 additions and 38 deletions
+9 -18
View File
@@ -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;
+19
View File
@@ -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.
*/
+2 -20
View File
@@ -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) {
+10
View File
@@ -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;