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
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>
417 lines
14 KiB
JavaScript
417 lines
14 KiB
JavaScript
export function getResolver(timeout = 5000) {
|
|
const resolver = {};
|
|
resolver.id = generateId(8);
|
|
resolver.completed = false;
|
|
resolver.resolved = false;
|
|
resolver.rejected = false;
|
|
resolver.promise = new Promise((resolve, reject) => {
|
|
resolver.reject = (e) => {
|
|
resolver.completed = true;
|
|
resolver.rejected = true;
|
|
reject(e);
|
|
};
|
|
resolver.resolve = (data) => {
|
|
resolver.completed = true;
|
|
resolver.resolved = true;
|
|
resolve(data);
|
|
};
|
|
});
|
|
resolver.timeout = setTimeout(() => {
|
|
if (!resolver.completed) {
|
|
resolver.reject();
|
|
}
|
|
}, timeout);
|
|
return resolver;
|
|
}
|
|
const DEBOUNCE_FN_TO_PROMISE = new WeakMap();
|
|
export function debounce(fn, ms = 64) {
|
|
if (!DEBOUNCE_FN_TO_PROMISE.get(fn)) {
|
|
DEBOUNCE_FN_TO_PROMISE.set(fn, wait(ms).then(() => {
|
|
DEBOUNCE_FN_TO_PROMISE.delete(fn);
|
|
fn();
|
|
}));
|
|
}
|
|
return DEBOUNCE_FN_TO_PROMISE.get(fn);
|
|
}
|
|
export function check(value, msg = "", ...args) {
|
|
if (!value) {
|
|
console.error(msg, ...(args || []));
|
|
throw new Error(msg || "Error");
|
|
}
|
|
}
|
|
export function wait(ms = 16) {
|
|
if (ms === 16) {
|
|
return new Promise((resolve) => {
|
|
requestAnimationFrame(() => {
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
resolve();
|
|
}, ms);
|
|
});
|
|
}
|
|
export function deepFreeze(obj) {
|
|
const propNames = Reflect.ownKeys(obj);
|
|
for (const name of propNames) {
|
|
const value = obj[name];
|
|
if ((value && typeof value === "object") || typeof value === "function") {
|
|
deepFreeze(value);
|
|
}
|
|
}
|
|
return Object.freeze(obj);
|
|
}
|
|
function dec2hex(dec) {
|
|
return dec.toString(16).padStart(2, "0");
|
|
}
|
|
export function generateId(length) {
|
|
const arr = new Uint8Array(length / 2);
|
|
crypto.getRandomValues(arr);
|
|
return Array.from(arr, dec2hex).join("");
|
|
}
|
|
export function getObjectValue(obj, objKey, def) {
|
|
if (!obj || !objKey)
|
|
return def;
|
|
const keys = objKey.split(".");
|
|
const key = keys.shift();
|
|
const found = obj[key];
|
|
if (keys.length) {
|
|
return getObjectValue(found, keys.join("."), def);
|
|
}
|
|
return found;
|
|
}
|
|
export function setObjectValue(obj, objKey, value, createMissingObjects = true) {
|
|
if (!obj || !objKey)
|
|
return obj;
|
|
const keys = objKey.split(".");
|
|
const key = keys.shift();
|
|
if (obj[key] === undefined) {
|
|
if (!createMissingObjects) {
|
|
return;
|
|
}
|
|
obj[key] = {};
|
|
}
|
|
if (!keys.length) {
|
|
obj[key] = value;
|
|
}
|
|
else {
|
|
if (typeof obj[key] != "object") {
|
|
obj[key] = {};
|
|
}
|
|
setObjectValue(obj[key], keys.join("."), value, createMissingObjects);
|
|
}
|
|
return obj;
|
|
}
|
|
export function moveArrayItem(arr, itemOrFrom, to) {
|
|
const from = typeof itemOrFrom === "number" ? itemOrFrom : arr.indexOf(itemOrFrom);
|
|
arr.splice(to, 0, arr.splice(from, 1)[0]);
|
|
}
|
|
export function removeArrayItem(arr, itemOrIndex) {
|
|
const index = typeof itemOrIndex === "number" ? itemOrIndex : arr.indexOf(itemOrIndex);
|
|
arr.splice(index, 1);
|
|
}
|
|
export function injectCss(href) {
|
|
if (document.querySelector(`link[href^="${href}"]`)) {
|
|
return Promise.resolve();
|
|
}
|
|
return new Promise((resolve) => {
|
|
const link = document.createElement("link");
|
|
link.setAttribute("rel", "stylesheet");
|
|
link.setAttribute("type", "text/css");
|
|
const timeout = setTimeout(resolve, 1000);
|
|
link.addEventListener("load", (e) => {
|
|
clearInterval(timeout);
|
|
resolve();
|
|
});
|
|
link.href = href;
|
|
document.head.appendChild(link);
|
|
});
|
|
}
|
|
export function defineProperty(instance, property, desc) {
|
|
var _a, _b, _c, _d, _e, _f;
|
|
const existingDesc = Object.getOwnPropertyDescriptor(instance, property);
|
|
if ((existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.configurable) === false) {
|
|
throw new Error(`Error: rgthree-comfy cannot define un-configurable property "${property}"`);
|
|
}
|
|
if ((existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.get) && desc.get) {
|
|
const descGet = desc.get;
|
|
desc.get = () => {
|
|
existingDesc.get.apply(instance, []);
|
|
return descGet.apply(instance, []);
|
|
};
|
|
}
|
|
if ((existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.set) && desc.set) {
|
|
const descSet = desc.set;
|
|
desc.set = (v) => {
|
|
existingDesc.set.apply(instance, [v]);
|
|
return descSet.apply(instance, [v]);
|
|
};
|
|
}
|
|
desc.enumerable = (_b = (_a = desc.enumerable) !== null && _a !== void 0 ? _a : existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.enumerable) !== null && _b !== void 0 ? _b : true;
|
|
desc.configurable = (_d = (_c = desc.configurable) !== null && _c !== void 0 ? _c : existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.configurable) !== null && _d !== void 0 ? _d : true;
|
|
if (!desc.get && !desc.set) {
|
|
desc.writable = (_f = (_e = desc.writable) !== null && _e !== void 0 ? _e : existingDesc === null || existingDesc === void 0 ? void 0 : existingDesc.writable) !== null && _f !== void 0 ? _f : true;
|
|
}
|
|
return Object.defineProperty(instance, property, desc);
|
|
}
|
|
export function areDataViewsEqual(a, b) {
|
|
if (a.byteLength !== b.byteLength) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < a.byteLength; i++) {
|
|
if (a.getUint8(i) !== b.getUint8(i)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
function looksLikeBase64(source) {
|
|
return source.length > 500 || source.startsWith("data:") || source.includes(";base64,");
|
|
}
|
|
export function areArrayBuffersEqual(a, b) {
|
|
if (a == b || !a || !b) {
|
|
return a == b;
|
|
}
|
|
return areDataViewsEqual(new DataView(a), new DataView(b));
|
|
}
|
|
export function newCanvas(widthOrPtOrImage, height) {
|
|
let width;
|
|
if (typeof widthOrPtOrImage !== "number") {
|
|
width = widthOrPtOrImage.width;
|
|
height = widthOrPtOrImage.height;
|
|
}
|
|
else {
|
|
width = widthOrPtOrImage;
|
|
height = height;
|
|
}
|
|
if (height == null) {
|
|
throw new Error("Invalid height supplied when creating new canvas object.");
|
|
}
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
if (widthOrPtOrImage instanceof HTMLImageElement) {
|
|
const ctx = canvas.getContext("2d");
|
|
ctx.drawImage(widthOrPtOrImage, 0, 0, width, height);
|
|
}
|
|
return canvas;
|
|
}
|
|
export function getCanvasImageData(image) {
|
|
const canvas = newCanvas(image);
|
|
const ctx = canvas.getContext("2d");
|
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
return [canvas, ctx, imageData];
|
|
}
|
|
export async function convertToBase64(source) {
|
|
if (source instanceof Promise) {
|
|
source = await source;
|
|
}
|
|
if (typeof source === "string" && looksLikeBase64(source)) {
|
|
return source;
|
|
}
|
|
if (typeof source === "string" || source instanceof Blob || source instanceof ArrayBuffer) {
|
|
return convertToBase64(await loadImage(source));
|
|
}
|
|
if (source instanceof HTMLImageElement) {
|
|
if (looksLikeBase64(source.src)) {
|
|
return source.src;
|
|
}
|
|
const [canvas, ctx, imageData] = getCanvasImageData(source);
|
|
return convertToBase64(canvas);
|
|
}
|
|
if (source instanceof HTMLCanvasElement) {
|
|
return source.toDataURL("image/png");
|
|
}
|
|
throw Error("Unknown source to convert to base64.");
|
|
}
|
|
export async function convertToArrayBuffer(source) {
|
|
if (source instanceof Promise) {
|
|
source = await source;
|
|
}
|
|
if (source instanceof ArrayBuffer) {
|
|
return source;
|
|
}
|
|
if (typeof source === "string") {
|
|
if (looksLikeBase64(source)) {
|
|
var binaryString = atob(source.replace(/^.*?;base64,/, ""));
|
|
var bytes = new Uint8Array(binaryString.length);
|
|
for (var i = 0; i < binaryString.length; i++) {
|
|
bytes[i] = binaryString.charCodeAt(i);
|
|
}
|
|
return bytes.buffer;
|
|
}
|
|
return convertToArrayBuffer(await loadImage(source));
|
|
}
|
|
if (source instanceof HTMLImageElement) {
|
|
const [canvas, ctx, imageData] = getCanvasImageData(source);
|
|
return convertToArrayBuffer(canvas);
|
|
}
|
|
if (source instanceof HTMLCanvasElement) {
|
|
return convertToArrayBuffer(source.toDataURL());
|
|
}
|
|
if (source instanceof Blob) {
|
|
return source.arrayBuffer();
|
|
}
|
|
throw Error("Unknown source to convert to arraybuffer.");
|
|
}
|
|
export async function loadImage(source) {
|
|
if (source instanceof Promise) {
|
|
source = await source;
|
|
}
|
|
if (source instanceof HTMLImageElement) {
|
|
return loadImage(source.src);
|
|
}
|
|
if (source instanceof Blob) {
|
|
return loadImage(source.arrayBuffer());
|
|
}
|
|
if (source instanceof HTMLCanvasElement) {
|
|
return loadImage(source.toDataURL());
|
|
}
|
|
if (source instanceof ArrayBuffer) {
|
|
var binary = "";
|
|
var bytes = new Uint8Array(source);
|
|
var len = bytes.byteLength;
|
|
for (var i = 0; i < len; i++) {
|
|
binary += String.fromCharCode(bytes[i]);
|
|
}
|
|
return loadImage(`data:${getMimeTypeFromArrayBuffer(bytes)};base64,${btoa(binary)}`);
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
const img = new Image();
|
|
img.addEventListener("load", () => {
|
|
resolve(img);
|
|
});
|
|
img.addEventListener("error", () => {
|
|
reject(img);
|
|
});
|
|
img.src = source;
|
|
});
|
|
}
|
|
function getMimeTypeFromArrayBuffer(buffer) {
|
|
const len = 4;
|
|
if (buffer.length >= len) {
|
|
let signatureArr = new Array(len);
|
|
for (let i = 0; i < len; i++)
|
|
signatureArr[i] = buffer[i].toString(16);
|
|
const signature = signatureArr.join("").toUpperCase();
|
|
switch (signature) {
|
|
case "89504E47":
|
|
return "image/png";
|
|
case "47494638":
|
|
return "image/gif";
|
|
case "25504446":
|
|
return "application/pdf";
|
|
case "FFD8FFDB":
|
|
case "FFD8FFE0":
|
|
return "image/jpeg";
|
|
case "504B0304":
|
|
return "application/zip";
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
export class Broadcaster extends EventTarget {
|
|
constructor(channelName) {
|
|
super();
|
|
this.queue = {};
|
|
this.queue = {};
|
|
this.channel = new BroadcastChannel(channelName);
|
|
this.channel.addEventListener("message", (e) => {
|
|
this.onMessage(e);
|
|
});
|
|
}
|
|
getId() {
|
|
let id;
|
|
do {
|
|
id = generateId(6);
|
|
} while (this.queue[id]);
|
|
return id;
|
|
}
|
|
async broadcastAndWait(action, payload, options) {
|
|
const id = this.getId();
|
|
this.queue[id] = getResolver(options === null || options === void 0 ? void 0 : options.timeout);
|
|
this.channel.postMessage({
|
|
id,
|
|
action,
|
|
payload,
|
|
});
|
|
let response;
|
|
try {
|
|
response = await this.queue[id].promise;
|
|
}
|
|
catch (e) {
|
|
console.log("CAUGHT", e);
|
|
response = [];
|
|
}
|
|
return response;
|
|
}
|
|
broadcast(action, payload) {
|
|
this.channel.postMessage({
|
|
id: this.getId(),
|
|
action,
|
|
payload,
|
|
});
|
|
}
|
|
reply(replyId, action, payload) {
|
|
this.channel.postMessage({
|
|
id: this.getId(),
|
|
replyId,
|
|
action,
|
|
payload,
|
|
});
|
|
}
|
|
openWindowAndWaitForMessage(rgthreePath, windowName) {
|
|
const id = this.getId();
|
|
this.queue[id] = getResolver();
|
|
const win = window.open(`/rgthree/${rgthreePath}#broadcastLoadMsgId=${id}`, windowName);
|
|
return { window: win, promise: this.queue[id].promise };
|
|
}
|
|
onMessage(e) {
|
|
var _a, _b;
|
|
const msgId = ((_a = e.data) === null || _a === void 0 ? void 0 : _a.replyId) || "";
|
|
const queueItem = this.queue[msgId];
|
|
if (queueItem) {
|
|
if (queueItem.completed) {
|
|
console.error(`${msgId} already completed..`);
|
|
}
|
|
queueItem.deferment = queueItem.deferment || { data: [] };
|
|
queueItem.deferment.data.push(e.data.payload);
|
|
queueItem.deferment.timeout && clearTimeout(queueItem.deferment.timeout);
|
|
queueItem.deferment.timeout = setTimeout(() => {
|
|
queueItem.resolve(queueItem.deferment.data);
|
|
}, 250);
|
|
}
|
|
else {
|
|
this.dispatchEvent(new CustomEvent("rgthree-broadcast-message", {
|
|
detail: Object.assign({ replyTo: (_b = e.data) === null || _b === void 0 ? void 0 : _b.id }, e.data),
|
|
}));
|
|
}
|
|
}
|
|
addMessageListener(callback, options) {
|
|
return super.addEventListener("rgthree-broadcast-message", callback, options);
|
|
}
|
|
}
|
|
const broadcastChannelMap = new Map();
|
|
export function broadcastOnChannel(channel, action, payload) {
|
|
let queue = broadcastChannelMap.get(channel);
|
|
if (!queue) {
|
|
broadcastChannelMap.set(channel, {});
|
|
queue = broadcastChannelMap.get(channel);
|
|
}
|
|
let id;
|
|
do {
|
|
id = generateId(6);
|
|
} while (queue[id]);
|
|
queue[id] = getResolver();
|
|
channel.postMessage({
|
|
id,
|
|
action,
|
|
payload,
|
|
});
|
|
return queue[id].promise;
|
|
}
|