Files
MajoorWaldi-ComfyUI-Majoor-…/ui/features/dnd/canvasLoaderNode.ts
T

188 lines
6.4 KiB
TypeScript

import { AUDIO_EXTS, IMAGE_EXTS, MODEL3D_EXTS, VIDEO_EXTS } from "./utils/constants.js";
import { pickBestMediaPathWidget } from "./targets/node.js";
import { ensureComboHasValue } from "./targets/node.js";
import { markCanvasDirty } from "./targets/canvas.js";
import { createHostNode } from "../../app/hostAdapter.js";
const _getPayloadExt = (payload: any) => {
const filename = String(payload?.filename || "");
const dot = filename.lastIndexOf(".");
return dot >= 0 ? filename.slice(dot).toLowerCase() : "";
};
const _getLoaderNodeCandidates = (payload: any) => {
const kind = String(payload?.kind || "").toLowerCase();
const ext = _getPayloadExt(payload);
if (kind === "image" || IMAGE_EXTS.has(ext)) return ["LoadImage"];
if (kind === "video" || VIDEO_EXTS.has(ext)) {
return ["LoadVideo", "VHS_LoadVideo", "VideoLoader"];
}
if (kind === "audio" || AUDIO_EXTS.has(ext)) {
return ["LoadAudio", "VHS_LoadAudioUpload", "VHS_LoadAudio", "AudioLoader"];
}
if (kind === "model3d" || MODEL3D_EXTS.has(ext)) {
return ["Load3D", "LoadModel", "LoadMesh", "LoadGLB", "LoadOBJ", "LoadSTL", "LoadPLY"];
}
return [];
};
const _setWidgetValue = (widget: any, value: any) => {
if (!widget) return;
if (widget.type === "combo") ensureComboHasValue(widget, value);
widget.value = value;
try {
widget.callback?.(widget.value);
} catch (e: any) {
console.debug?.(e);
}
try {
if (typeof widget.onchange === "function") {
widget.onchange(value);
}
} catch (e: any) {
console.debug?.(e);
}
};
const _getCanvasCenterPos = (app: any) => {
const canvasEl = app?.canvas?.canvas || document.querySelector("canvas");
const rect = canvasEl?.getBoundingClientRect?.();
const rectWidth = rect ? Number(rect.width || rect.right - rect.left) : 0;
const rectHeight = rect ? Number(rect.height || rect.bottom - rect.top) : 0;
const width = Number(rectWidth || canvasEl?.width || 800);
const height = Number(rectHeight || canvasEl?.height || 600);
if (!rect) return [Math.max(0, width / 2), Math.max(0, height / 2)];
const left = rect ? Number(rect.left) || 0 : 0;
const top = rect ? Number(rect.top) || 0 : 0;
return _getCanvasPosFromClient(app, left + Math.max(0, width / 2), top + Math.max(0, height / 2));
};
const _getCanvasPosFromClient = (app: any, clientX: any, clientY: any) => {
const canvasEl = app?.canvas?.canvas || document.querySelector("canvas");
const rect = canvasEl?.getBoundingClientRect?.();
const ds = app?.canvas?.ds || {};
const scale = Number(ds.scale) || 1;
const off = ds.offset || [0, 0];
const offX = Array.isArray(off) ? Number(off[0]) || 0 : Number(off?.x) || 0;
const offY = Array.isArray(off) ? Number(off[1]) || 0 : Number(off?.y) || 0;
if (!rect || !Number.isFinite(Number(clientX)) || !Number.isFinite(Number(clientY))) {
return _getCanvasCenterPos(app);
}
return [
(Number(clientX) - rect.left) / scale - offX,
(Number(clientY) - rect.top) / scale - offY,
];
};
export const getDropCanvasPos = (app: any, event: any = null) => {
if (Number.isFinite(Number(event?.clientX)) && Number.isFinite(Number(event?.clientY))) {
return _getCanvasPosFromClient(app, event.clientX, event.clientY);
}
try {
const pos = app?.canvas?.convertEventToCanvasOffset?.(event);
if (Array.isArray(pos)) return [Number(pos[0]) || 0, Number(pos[1]) || 0];
} catch (e: any) {
console.debug?.(e);
}
return _getCanvasPosFromClient(app, event?.clientX, event?.clientY);
};
export const createCanvasLoaderNode = ({
app,
payload,
relativePath,
event = null,
droppedExt = "",
position = null,
}: any) => {
const graph = app?.graph ?? app?.canvas?.graph ?? null;
if (!graph || typeof graph.add !== "function") return false;
let created: any = null;
for (const nodeType of _getLoaderNodeCandidates(payload)) {
try {
created = createHostNode(nodeType, app);
if (created) break;
} catch (e: any) {
console.debug?.(e);
}
}
if (!created) return false;
created.pos = Array.isArray(position)
? [Number(position[0]) || 0, Number(position[1]) || 0]
: getDropCanvasPos(app, event);
graph.add(created);
const widget = pickBestMediaPathWidget(created, payload, droppedExt);
if (widget) _setWidgetValue(widget, relativePath);
markCanvasDirty(app);
return true;
};
export const createCanvasLoaderNodes = ({ app, items = [], event = null, gap = 90 }: any) => {
const list = Array.isArray(items) ? items.filter(Boolean) : [];
if (!list.length) return 0;
const origin = getDropCanvasPos(app, event);
let count = 0;
for (const item of list) {
const ok = createCanvasLoaderNode({
app,
payload: item.payload,
relativePath: item.relativePath,
event,
droppedExt: item.droppedExt,
position: [origin[0] + count * Number(gap || 90), origin[1]],
});
if (ok) count += 1;
}
return count;
};
export const writeMediaPathWidgetValue = _setWidgetValue;
export const createLoaderAndConnect = ({
app,
payload,
relativePath,
targetNode,
inputSlotIndex,
event = null,
}: any) => {
const graph = app?.graph ?? app?.canvas?.graph ?? null;
if (!graph || typeof graph.add !== "function" || !targetNode) return false;
let loaderNode: any = null;
for (const nodeType of _getLoaderNodeCandidates(payload)) {
try {
loaderNode = createHostNode(nodeType, app);
if (loaderNode) break;
} catch (e: any) {
console.debug?.(e);
}
}
if (!loaderNode) return false;
const targetX = Number(targetNode.pos[0]) || 0;
const targetY = Number(targetNode.pos[1]) || 0;
const sizeW = Array.isArray(loaderNode.size) ? Number(loaderNode.size[0]) || 220 : 220;
loaderNode.pos = [targetX - sizeW - 60, targetY + Number(inputSlotIndex) * 20];
graph.add(loaderNode);
const droppedExt = _getPayloadExt(payload);
const widget = pickBestMediaPathWidget(loaderNode, payload, droppedExt);
if (widget) {
_setWidgetValue(widget, relativePath);
}
try {
loaderNode.connect(0, targetNode, inputSlotIndex);
} catch (e: any) {
console.debug?.(e);
}
markCanvasDirty(app);
return true;
};