Files
ComfyUI/custom_nodes/rgthree-comfy/src_web/comfyui/utils_deprecated_comfyui.ts
jaidaken f09734b0ee
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Add custom nodes, Civitai loras (LFS), and vast.ai setup script
Includes 30 custom nodes committed directly, 7 Civitai-exclusive
loras stored via Git LFS, and a setup script that installs all
dependencies and downloads HuggingFace-hosted models on vast.ai.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 00:56:42 +00:00

295 lines
10 KiB
TypeScript

/**
* [🤮] ComfyUI started deprecating the use of their legacy JavaScript files. These are ports/shims
* since we relied on them at one point.
*
* TODO: Should probably remove these all together at some point.
*/
import {app} from "scripts/app.js";
import type {INodeInputSlot, INodeOutputSlot, InputSpec, LGraphNode} from "@comfyorg/frontend";
/** Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/extensions/core/widgetInputs.ts#L462 */
interface PrimitiveNode extends LGraphNode {
recreateWidget(): void;
onLastDisconnect(): void;
}
/** Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/renderer/utils/nodeTypeGuards.ts */
function isPrimitiveNode(node: LGraphNode): node is PrimitiveNode {
return node.type === "PrimitiveNode";
}
/**
* CONFIG and GET_CONFIG in https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/services/litegraphService.ts
* are not accessible publicly, so we need to look at a slot.widget's symbols and check to see if it
* matches rather than access it directly. Cool...
*/
function getWidgetGetConfigSymbols(slot: INodeOutputSlot | INodeInputSlot): {
CONFIG?: symbol;
GET_CONFIG?: symbol;
} {
const widget = slot?.widget;
if (!widget) return {};
const syms = Object.getOwnPropertySymbols(widget || {});
for (const sym of syms) {
const symVal = widget![sym];
const isGetConfig = typeof symVal === "function";
let maybeCfg = isGetConfig ? symVal() : symVal;
if (
Array.isArray(maybeCfg) &&
maybeCfg.length >= 2 &&
typeof maybeCfg[0] === "string" &&
(maybeCfg[0] === "*" || typeof maybeCfg[1]?.type === "string")
) {
return isGetConfig ? {GET_CONFIG: sym} : {CONFIG: sym};
}
}
return {};
}
/**
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/extensions/core/widgetInputs.ts
*/
export function getWidgetConfig(slot: INodeOutputSlot | INodeInputSlot): InputSpec {
const configSyms = getWidgetGetConfigSymbols(slot);
const widget = slot.widget || ({} as any);
return (
(configSyms.CONFIG && widget[configSyms.CONFIG]) ??
(configSyms.GET_CONFIG && widget[configSyms.GET_CONFIG]?.()) ?? ["*", {}]
);
}
/**
* This is lossy, since we don't have access to GET_CONFIG Symbol, we cannot accurately set it. As a
* best-chance we can look for a function that seems to return a
*
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/extensions/core/widgetInputs.ts
*/
export function setWidgetConfig(
slot: INodeOutputSlot | INodeInputSlot | undefined,
config: InputSpec,
) {
if (!slot?.widget) return;
if (config) {
const configSyms = getWidgetGetConfigSymbols(slot);
const widget = slot.widget || ({} as any);
if (configSyms.GET_CONFIG) {
widget[configSyms.GET_CONFIG] = () => config;
} else if (configSyms.CONFIG) {
widget[configSyms.CONFIG] = config;
} else {
console.error(
"Cannot set widget Config. This is due to ComfyUI removing the ability to call legacy " +
"JavaScript APIs that are now deprecated without new, supported APIs. It's possible " +
"some things in rgthree-comfy do not work correctly. If you see this, please file a bug.",
);
}
} else {
delete slot.widget;
}
if ("link" in slot) {
const link = app.graph.links[(slot as INodeInputSlot)?.link ?? -1];
if (link) {
const originNode = app.graph.getNodeById(link.origin_id);
if (originNode && isPrimitiveNode(originNode)) {
if (config) {
originNode.recreateWidget();
} else if (!app.configuringGraph) {
originNode.disconnectOutput(0);
originNode.onLastDisconnect();
}
}
}
}
}
/**
* A slimmed-down version of `mergeIfValid` for only what was needed in rgthree-comfy.
*
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/extensions/core/widgetInputs.ts
*/
export function mergeIfValid(
output: INodeOutputSlot | INodeInputSlot,
config2: InputSpec,
): InputSpec[1] | null {
const config1 = getWidgetConfig(output);
const customSpec = mergeInputSpec(config1, config2);
if (customSpec) {
setWidgetConfig(output, customSpec);
}
return customSpec?.[1] ?? null;
}
/**
* Merges two input specs.
*
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/utils/nodeDefUtil.ts
*/
const mergeInputSpec = (spec1: InputSpec, spec2: InputSpec): InputSpec | null => {
const type1 = getInputSpecType(spec1);
const type2 = getInputSpecType(spec2);
if (type1 !== type2) {
return null;
}
if (isIntInputSpec(spec1) || isFloatInputSpec(spec1)) {
return mergeNumericInputSpec(spec1, spec2 as typeof spec1);
}
if (isComboInputSpec(spec1)) {
return mergeComboInputSpec(spec1, spec2 as typeof spec1);
}
return mergeCommonInputSpec(spec1, spec2);
};
/**
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/schemas/nodeDefSchema.ts
*/
function getInputSpecType(inputSpec: InputSpec): string {
return isComboInputSpec(inputSpec) ? "COMBO" : inputSpec[0];
}
/**
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/schemas/nodeDefSchema.ts
*/
function isComboInputSpecV1(inputSpec: InputSpec) {
return Array.isArray(inputSpec[0]);
}
/**
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/schemas/nodeDefSchema.ts
*/
function isIntInputSpec(inputSpec: InputSpec) {
return inputSpec[0] === "INT";
}
/**
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/schemas/nodeDefSchema.ts
*/
function isFloatInputSpec(inputSpec: InputSpec) {
return inputSpec[0] === "FLOAT";
}
/**
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/schemas/nodeDefSchema.ts
*/
function isComboInputSpecV2(inputSpec: InputSpec) {
return inputSpec[0] === "COMBO";
}
/**
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/schemas/nodeDefSchema.ts
*/
function isComboInputSpec(inputSpec: InputSpec) {
return isComboInputSpecV1(inputSpec) || isComboInputSpecV2(inputSpec);
}
/** Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/utils/nodeDefUtil.ts */
const getRange = (options: any) => {
const min = options.min ?? -Infinity;
const max = options.max ?? Infinity;
return {min, max};
};
/** Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/utils/nodeDefUtil.ts */
const mergeNumericInputSpec = <T extends any>(spec1: any, spec2: any): T | null => {
const type = spec1[0];
const options1 = spec1[1] ?? {};
const options2 = spec2[1] ?? {};
const range1 = getRange(options1);
const range2 = getRange(options2);
// If the ranges do not overlap, return null
if (range1.min > range2.max || range1.max < range2.min) {
return null;
}
const step1 = options1.step ?? 1;
const step2 = options2.step ?? 1;
const mergedOptions = {
// Take intersection of ranges
min: Math.max(range1.min, range2.min),
max: Math.min(range1.max, range2.max),
step: lcm(step1, step2),
};
return mergeCommonInputSpec(
[type, {...options1, ...mergedOptions}] as T,
[type, {...options2, ...mergedOptions}] as T,
);
};
/** Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/utils/nodeDefUtil.ts */
const mergeComboInputSpec = <T extends any>(spec1: any, spec2: any): T | null => {
const options1 = spec1[1] ?? {};
const options2 = spec2[1] ?? {};
const comboOptions1 = getComboSpecComboOptions(spec1);
const comboOptions2 = getComboSpecComboOptions(spec2);
const intersection = comboOptions1.filter((value) => comboOptions2.includes(value));
// If the intersection is empty, return null
if (intersection.length === 0) {
return null;
}
return mergeCommonInputSpec(
["COMBO", {...options1, options: intersection}] as T,
["COMBO", {...options2, options: intersection}] as T,
);
};
/** Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/utils/nodeDefUtil.ts */
const mergeCommonInputSpec = <T extends InputSpec>(spec1: T, spec2: T): T | null => {
const type = getInputSpecType(spec1);
const options1 = spec1[1] ?? {};
const options2 = spec2[1] ?? {};
const compareKeys = [...new Set([...Object.keys(options1), ...Object.keys(options2)])].filter(
(key: string) => !IGNORE_KEYS.has(key),
);
const mergeIsValid = compareKeys.every((key: string) => {
const value1 = options1[key];
const value2 = options2[key];
return value1 === value2 || (value1 == null && value2 == null);
});
return mergeIsValid ? ([type, {...options1, ...options2}] as T) : null;
};
/** Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/utils/nodeDefUtil.ts */
const IGNORE_KEYS = new Set<string>([
"default",
"forceInput",
"defaultInput",
"control_after_generate",
"multiline",
"tooltip",
"dynamicPrompts",
]);
/**
* Derived from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/schemas/nodeDefSchema.ts
*/
function getComboSpecComboOptions(inputSpec: any): (number | string)[] {
return (isComboInputSpecV2(inputSpec) ? inputSpec[1]?.options : inputSpec[0]) ?? [];
}
/** Taken from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/utils/mathUtil.ts */
const lcm = (a: number, b: number): number => {
return Math.abs(a * b) / gcd(a, b);
};
/** Taken from https://github.com/Comfy-Org/ComfyUI_frontend/blob/1f3fb90b1b79c4190b3faa7928b05a8ba3671307/src/utils/mathUtil.ts */
const gcd = (a: number, b: number): number => {
return b === 0 ? a : gcd(b, a % b);
};