760 lines
30 KiB
JavaScript
760 lines
30 KiB
JavaScript
import { app } from "../../../scripts/app.js";
|
|
import { api } from "../../../scripts/api.js";
|
|
import { addPreviewMenuOptions, applyPreviewHiddenState } from "./core/preview_menu.js";
|
|
import {
|
|
addCopyMediaFrameMenuOption,
|
|
addSaveMediaMenuOption,
|
|
collectDropPayload,
|
|
installImagePreviewLayout,
|
|
} from "./core/media_utils.js";
|
|
import { saveMaskFromClipspaceToSidecar } from "./core/image_mask_sidecar.js";
|
|
|
|
app.registerExtension({
|
|
name: "ComfyUI-1hewNodes.load_image",
|
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
if (nodeData.name !== "1hew_LoadImage") {
|
|
return;
|
|
}
|
|
|
|
const applyPreviewStyle = (node) => {
|
|
applyPreviewHiddenState(node);
|
|
};
|
|
|
|
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
|
nodeType.prototype.getExtraMenuOptions = function (_, options) {
|
|
const r = getExtraMenuOptions ? getExtraMenuOptions.apply(this, arguments) : undefined;
|
|
|
|
// Remove "Save Mask" option if present (robust check)
|
|
if (Array.isArray(options)) {
|
|
for (let i = options.length - 1; i >= 0; i--) {
|
|
const opt = options[i];
|
|
if (opt && typeof opt.content === "string" && opt.content.trim() === "Save Mask") {
|
|
options.splice(i, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add standard preview options (Hide/Show, etc.)
|
|
addPreviewMenuOptions(options, { app, currentNode: this });
|
|
|
|
let imgEl = null;
|
|
if (this.imageWidget && this.imageWidget.element) {
|
|
imgEl = this.imageWidget.element.querySelector("img");
|
|
}
|
|
|
|
const getImgElFromNode = (node) =>
|
|
node?.imageWidget?.element?.querySelector("img");
|
|
|
|
addSaveMediaMenuOption(options, {
|
|
app,
|
|
currentNode: this,
|
|
content: "Save Image",
|
|
getMediaElFromNode: getImgElFromNode,
|
|
filenamePrefix: "image",
|
|
filenameExt: "png",
|
|
});
|
|
|
|
if (imgEl && imgEl.src) {
|
|
addCopyMediaFrameMenuOption(options, {
|
|
content: "Copy Image",
|
|
getWidth: () => imgEl.naturalWidth,
|
|
getHeight: () => imgEl.naturalHeight,
|
|
drawToCanvas: (ctx) => ctx.drawImage(imgEl, 0, 0),
|
|
copyErrorMessage: "Failed to copy image to clipboard:",
|
|
prepareErrorMessage: "Error preparing image copy:",
|
|
});
|
|
}
|
|
|
|
return r;
|
|
};
|
|
|
|
const onConfigure = nodeType.prototype.onConfigure;
|
|
nodeType.prototype.onConfigure = function () {
|
|
const r = onConfigure ? onConfigure.apply(this, arguments) : undefined;
|
|
if (this.widgets) {
|
|
const fileWidget = this.widgets.find((w) => w.name === "file");
|
|
if (fileWidget && fileWidget.value) {
|
|
this._comfy1hewLoadImagePendingPreview = true;
|
|
setTimeout(() => {
|
|
const update = this.updatePreview;
|
|
if (typeof update === "function") {
|
|
this._comfy1hewLoadImagePendingPreview = false;
|
|
update.call(this);
|
|
}
|
|
}, 0);
|
|
}
|
|
}
|
|
setTimeout(() => applyPreviewStyle(this), 0);
|
|
const ensurePreviewLayout = () => {
|
|
if (this.imageWidget.aspectRatio) {
|
|
requestAutoSize();
|
|
updateLayout();
|
|
}
|
|
};
|
|
|
|
// 初始多次尝试触发布局更新,解决首次加载出画框问题
|
|
setTimeout(ensurePreviewLayout, 100);
|
|
setTimeout(ensurePreviewLayout, 500);
|
|
|
|
return r;
|
|
};
|
|
|
|
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
|
nodeType.prototype.onNodeCreated = function () {
|
|
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
|
|
|
|
const fileWidget = this.widgets.find((w) => w.name === "file");
|
|
const indexWidget = this.widgets.find((w) => w.name === "index");
|
|
const includeSubdirWidget = this.widgets.find(
|
|
(w) => w.name === "include_subdir"
|
|
);
|
|
const allWidget = this.widgets.find((w) => w.name === "all");
|
|
|
|
try {
|
|
const baseW =
|
|
Array.isArray(this.size) &&
|
|
typeof this.size[0] === "number" &&
|
|
this.size[0] > 0
|
|
? this.size[0]
|
|
: 200;
|
|
const desiredSize = this.computeSize();
|
|
const baseH =
|
|
Array.isArray(desiredSize) && typeof desiredSize[1] === "number"
|
|
? desiredSize[1]
|
|
: 0;
|
|
this._comfy1hewLoadImageBaseSize = [baseW, baseH];
|
|
} catch {}
|
|
this._comfy1hewLoadImageWasEmptyPath = true;
|
|
this._comfy1hewLoadImageRedrawQueued = false;
|
|
this._comfy1hewLoadImageHadPreview = false;
|
|
this._comfy1hewLoadImageLastImgSrc = undefined;
|
|
this._comfy1hewImagePreviewUserResized = false;
|
|
|
|
const isValidImageFile = (file) => {
|
|
if (!file) return false;
|
|
if (file.type && file.type.startsWith("image/")) return true;
|
|
const name = (file.name || "").toLowerCase();
|
|
return (
|
|
name.endsWith(".png") ||
|
|
name.endsWith(".jpg") ||
|
|
name.endsWith(".jpeg") ||
|
|
name.endsWith(".webp") ||
|
|
name.endsWith(".bmp") ||
|
|
name.endsWith(".tiff") ||
|
|
name.endsWith(".gif")
|
|
);
|
|
};
|
|
|
|
const uploadFilesAsFolder = async (pairs, hasDirectory) => {
|
|
const files = (pairs || []).filter((p) => isValidImageFile(p?.file));
|
|
if (files.length === 0) return;
|
|
|
|
const form = new FormData();
|
|
for (const p of files) {
|
|
const name = p.relativePath || p.file.name;
|
|
form.append("files", p.file, name);
|
|
}
|
|
|
|
const res = await api.fetchApi("/1hew/upload_images", {
|
|
method: "POST",
|
|
body: form,
|
|
});
|
|
if (res.status !== 200) return;
|
|
|
|
const data = await res.json();
|
|
const uploadedFolder = data?.folder;
|
|
const uploadedFiles = data?.files;
|
|
|
|
if (!uploadedFolder && (!uploadedFiles || uploadedFiles.length === 0)) return;
|
|
|
|
let finalPath = uploadedFolder;
|
|
// If backend returns specific file paths and we uploaded files, try to use the direct file path
|
|
if (Array.isArray(uploadedFiles) && uploadedFiles.length > 0) {
|
|
// If we only have one file, use its full path
|
|
if (uploadedFiles.length === 1) {
|
|
finalPath = uploadedFiles[0];
|
|
}
|
|
}
|
|
|
|
if (fileWidget) {
|
|
fileWidget.value = finalPath;
|
|
if (typeof fileWidget.callback === "function") {
|
|
fileWidget.callback();
|
|
}
|
|
}
|
|
|
|
if (indexWidget) {
|
|
indexWidget.value = 0;
|
|
if (typeof indexWidget.callback === "function") {
|
|
indexWidget.callback();
|
|
}
|
|
}
|
|
|
|
if (includeSubdirWidget && hasDirectory) {
|
|
includeSubdirWidget.value = true;
|
|
if (typeof includeSubdirWidget.callback === "function") {
|
|
includeSubdirWidget.callback();
|
|
}
|
|
}
|
|
|
|
if (this.updatePreview) {
|
|
await this.updatePreview();
|
|
}
|
|
|
|
app.graph.setDirtyCanvas(true, true);
|
|
};
|
|
|
|
const imageEl = document.createElement("img");
|
|
Object.assign(imageEl.style, {
|
|
width: "100%",
|
|
maxWidth: "100%",
|
|
height: "auto",
|
|
maxHeight: "calc(100% - 20px)",
|
|
display: "block",
|
|
flex: "0 0 auto",
|
|
objectFit: "contain",
|
|
minHeight: "0",
|
|
});
|
|
|
|
const infoEl = document.createElement("div");
|
|
Object.assign(infoEl.style, {
|
|
width: "100%",
|
|
height: "20px",
|
|
lineHeight: "20px",
|
|
textAlign: "center",
|
|
fontSize: "12px",
|
|
color: "#aaa",
|
|
fontFamily: "sans-serif",
|
|
flex: "0 0 20px",
|
|
background: "transparent",
|
|
whiteSpace: "nowrap",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
});
|
|
infoEl.innerText = "";
|
|
|
|
const container = document.createElement("div");
|
|
Object.assign(container.style, {
|
|
display: "none",
|
|
flexDirection: "column",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
width: "100%",
|
|
height: "100%",
|
|
minHeight: "0",
|
|
overflow: "hidden",
|
|
boxSizing: "border-box",
|
|
});
|
|
|
|
container.appendChild(imageEl);
|
|
container.appendChild(infoEl);
|
|
|
|
let domDragDepth = 0;
|
|
const setDragPassthrough = (active) => {
|
|
imageEl.style.pointerEvents = active ? "none" : "";
|
|
infoEl.style.pointerEvents = active ? "none" : "";
|
|
};
|
|
|
|
const fileInputEl = document.createElement("input");
|
|
fileInputEl.type = "file";
|
|
fileInputEl.accept = "image/*";
|
|
fileInputEl.style.display = "none";
|
|
container.appendChild(fileInputEl);
|
|
|
|
const uploadWidget = this.addWidget(
|
|
"button",
|
|
"choose file to upload",
|
|
"image",
|
|
() => {
|
|
app.canvas.node_widget = null;
|
|
try {
|
|
fileInputEl.value = "";
|
|
} catch {}
|
|
fileInputEl.click();
|
|
}
|
|
);
|
|
uploadWidget.serialize = false;
|
|
|
|
fileInputEl.addEventListener("change", async () => {
|
|
const file = fileInputEl.files && fileInputEl.files[0];
|
|
if (!file) return;
|
|
try {
|
|
await uploadFilesAsFolder(
|
|
[{ file: file, relativePath: file?.name }],
|
|
false
|
|
);
|
|
} catch {}
|
|
});
|
|
|
|
imageEl.addEventListener("contextmenu", (e) => {
|
|
e.preventDefault();
|
|
const node = this;
|
|
if (app.canvas) {
|
|
app.canvas.processContextMenu(node, e);
|
|
}
|
|
});
|
|
|
|
this.imageWidget = this.addDOMWidget(
|
|
"image_preview",
|
|
"div",
|
|
container,
|
|
{
|
|
serialize: false,
|
|
hideOnZoom: false,
|
|
}
|
|
);
|
|
|
|
this.imageWidget.computeSize = function (width) {
|
|
if (this.aspectRatio) {
|
|
const maxAspectRatio =
|
|
typeof this._comfy1hew_maxAspectRatio === "number"
|
|
? this._comfy1hew_maxAspectRatio
|
|
: null;
|
|
const aspectRatio =
|
|
maxAspectRatio && isFinite(maxAspectRatio)
|
|
? Math.min(this.aspectRatio, maxAspectRatio)
|
|
: this.aspectRatio;
|
|
let height = width * aspectRatio + 20;
|
|
const maxPreviewHeight =
|
|
typeof this._comfy1hew_maxPreviewHeight === "number"
|
|
? this._comfy1hew_maxPreviewHeight
|
|
: null;
|
|
if (maxPreviewHeight && isFinite(maxPreviewHeight)) {
|
|
height = Math.min(height, maxPreviewHeight);
|
|
}
|
|
return [width, height];
|
|
}
|
|
return [width, 0];
|
|
};
|
|
|
|
this._comfy1hewImageAutoSizeKey = "";
|
|
const { requestAutoSize, updateLayout } = installImagePreviewLayout({
|
|
app,
|
|
node: this,
|
|
imageWidget: this.imageWidget,
|
|
container,
|
|
imageEl,
|
|
allWidget,
|
|
});
|
|
|
|
const resetNodeHeightToBase = () => {
|
|
try {
|
|
const baseH =
|
|
Array.isArray(this._comfy1hewLoadImageBaseSize)
|
|
&& Number.isFinite(this._comfy1hewLoadImageBaseSize[1])
|
|
? this._comfy1hewLoadImageBaseSize[1]
|
|
: null;
|
|
if (typeof baseH === "number" && baseH > 0) {
|
|
this.setSize([this.size[0], baseH]);
|
|
return;
|
|
}
|
|
} catch {}
|
|
// Fallback: avoid collapsing to zero-height which can make the DOM preview overflow the node frame
|
|
try {
|
|
this.setSize([this.size[0], 130]);
|
|
} catch {}
|
|
};
|
|
|
|
const handleDomDragEnter = (e) => {
|
|
if (!e) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
domDragDepth += 1;
|
|
setDragPassthrough(true);
|
|
if (e.dataTransfer) {
|
|
e.dataTransfer.dropEffect = "copy";
|
|
}
|
|
};
|
|
|
|
const handleDomDragOver = (e) => {
|
|
if (!e) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
setDragPassthrough(true);
|
|
if (e.dataTransfer) {
|
|
e.dataTransfer.dropEffect = "copy";
|
|
}
|
|
};
|
|
|
|
const handleDomDragLeave = (e) => {
|
|
if (!e) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
domDragDepth = Math.max(0, domDragDepth - 1);
|
|
if (domDragDepth === 0) {
|
|
setDragPassthrough(false);
|
|
}
|
|
};
|
|
|
|
const handleDomDrop = (e) => {
|
|
if (!e) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
domDragDepth = 0;
|
|
setDragPassthrough(false);
|
|
(async () => {
|
|
try {
|
|
const payload = await collectDropPayload(e);
|
|
const pairs = (payload?.pairs || []).filter((p) => p?.file);
|
|
const hasDirectory = Boolean(payload?.hasDirectory);
|
|
await uploadFilesAsFolder(pairs, hasDirectory);
|
|
} catch {}
|
|
})();
|
|
};
|
|
|
|
const dragTargets = [container, imageEl, infoEl];
|
|
for (const target of dragTargets) {
|
|
target.addEventListener("dragenter", handleDomDragEnter);
|
|
target.addEventListener("dragover", handleDomDragOver);
|
|
target.addEventListener("dragleave", handleDomDragLeave);
|
|
target.addEventListener("drop", handleDomDrop);
|
|
}
|
|
|
|
this.updatePreview = async () => {
|
|
this._comfy1hewLoadImageReqId = (this._comfy1hewLoadImageReqId || 0) + 1;
|
|
const reqId = this._comfy1hewLoadImageReqId;
|
|
|
|
const file = fileWidget.value;
|
|
const index = indexWidget.value;
|
|
const includeSubdir = includeSubdirWidget
|
|
? includeSubdirWidget.value
|
|
: true;
|
|
const all = allWidget ? allWidget.value : false;
|
|
if (this.imageWidget) {
|
|
this.imageWidget._comfy1hew_maxAspectRatio = all ? 1.25 : null;
|
|
// Match native Load Image more closely: keep a compact default
|
|
// preview, but allow the user to enlarge it by resizing the node.
|
|
this.imageWidget._comfy1hew_maxPreviewHeight = all ? 260 : 220;
|
|
}
|
|
const trimmedFile = String(file || "").trim();
|
|
if (trimmedFile === "") {
|
|
this._comfy1hewLoadImageWasEmptyPath = true;
|
|
const lastImgSrc = this._comfy1hewLoadImageLastImgSrc;
|
|
|
|
try {
|
|
imageEl.onload = null;
|
|
imageEl.onerror = null;
|
|
imageEl.removeAttribute("src");
|
|
} catch {}
|
|
this._comfy1hewLoadImageHadPreview = false;
|
|
this._comfy1hewImageAutoSizeKey = "";
|
|
|
|
this.imageWidget.aspectRatio = undefined;
|
|
infoEl.innerText = "";
|
|
updateLayout();
|
|
resetNodeHeightToBase();
|
|
|
|
try {
|
|
const clipspace = window?.ComfyApp?.clipspace;
|
|
const clipspaceNode = window?.ComfyApp?.clipspace_return_node;
|
|
const clipspaceImgs = clipspace?.imgs;
|
|
|
|
const normalizeSrc = (src) => {
|
|
try {
|
|
const u = new URL(src);
|
|
u.searchParams.delete("t");
|
|
u.searchParams.delete("preview");
|
|
return u.toString();
|
|
} catch {
|
|
return src;
|
|
}
|
|
};
|
|
const lastSrcNorm =
|
|
typeof lastImgSrc === "string"
|
|
? normalizeSrc(lastImgSrc)
|
|
: undefined;
|
|
|
|
const shouldClearClipspace =
|
|
clipspaceNode === this ||
|
|
(typeof lastSrcNorm === "string" &&
|
|
Array.isArray(clipspaceImgs) &&
|
|
clipspaceImgs.some((i) => {
|
|
if (!i?.src) return false;
|
|
return normalizeSrc(i.src) === lastSrcNorm;
|
|
}));
|
|
|
|
if (shouldClearClipspace) {
|
|
window.ComfyApp.clipspace = null;
|
|
window.ComfyApp.clipspace_return_node = null;
|
|
|
|
const dialog =
|
|
window?.comfyAPI?.clipspace?.ClipspaceDialog;
|
|
if (dialog?.instance?.close) {
|
|
dialog.instance.close();
|
|
}
|
|
|
|
const maskEditor =
|
|
window?.comfyAPI?.maskEditorOld
|
|
?.MaskEditorDialogOld;
|
|
if (maskEditor?.instance?.close) {
|
|
maskEditor.instance.close();
|
|
} else if (maskEditor?.getInstance) {
|
|
const inst = maskEditor.getInstance();
|
|
if (inst?.close) {
|
|
inst.close();
|
|
}
|
|
}
|
|
}
|
|
} catch {}
|
|
this._comfy1hewLoadImageLastImgSrc = undefined;
|
|
|
|
if (app?.graph?.setDirtyCanvas) {
|
|
app.graph.setDirtyCanvas(true, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
this._comfy1hewLoadImageWasEmptyPath = false;
|
|
|
|
const params = new URLSearchParams({
|
|
file: file,
|
|
include_subdir: includeSubdir,
|
|
t: Date.now(),
|
|
});
|
|
if (!all) {
|
|
params.set("index", index);
|
|
}
|
|
params.set("all", all ? "true" : "false");
|
|
|
|
const url = `/1hew/view_image_from_folder?${params.toString()}`;
|
|
const normalizeUrl = (src) => {
|
|
if (!src) return "";
|
|
try {
|
|
const u = new URL(src, window.location.href);
|
|
u.searchParams.delete("t");
|
|
u.searchParams.delete("preview");
|
|
return u.toString();
|
|
} catch {
|
|
return String(src);
|
|
}
|
|
};
|
|
const desiredNorm = normalizeUrl(url);
|
|
const currentNorm = normalizeUrl(imageEl.src);
|
|
|
|
if (currentNorm !== desiredNorm) {
|
|
this._comfy1hewImageAutoSizeKey = "";
|
|
container.style.display = "flex";
|
|
|
|
imageEl.onload = () => {
|
|
if (String(imageEl.dataset.comfy1hewReqId || "") !== String(reqId)) {
|
|
return;
|
|
}
|
|
if (imageEl.naturalWidth && imageEl.naturalHeight) {
|
|
this.imageWidget.aspectRatio =
|
|
imageEl.naturalHeight / imageEl.naturalWidth;
|
|
infoEl.innerText = `${imageEl.naturalWidth} x ${imageEl.naturalHeight}`;
|
|
this._comfy1hewLoadImageHadPreview = true;
|
|
try {
|
|
const u = new URL(imageEl.src);
|
|
u.searchParams.delete("t");
|
|
u.searchParams.delete("preview");
|
|
this._comfy1hewLoadImageLastImgSrc = u.toString();
|
|
} catch {
|
|
this._comfy1hewLoadImageLastImgSrc = imageEl.src;
|
|
}
|
|
requestAutoSize();
|
|
updateLayout();
|
|
}
|
|
};
|
|
|
|
imageEl.onerror = () => {
|
|
if (String(imageEl.dataset.comfy1hewReqId || "") !== String(reqId)) {
|
|
return;
|
|
}
|
|
this.imageWidget.aspectRatio = undefined;
|
|
infoEl.innerText = "";
|
|
this._comfy1hewLoadImageHadPreview = false;
|
|
updateLayout();
|
|
resetNodeHeightToBase();
|
|
};
|
|
|
|
imageEl.dataset.comfy1hewReqId = String(reqId);
|
|
imageEl.src = url;
|
|
updateLayout();
|
|
}
|
|
};
|
|
|
|
this.onDropFile = function (file) {
|
|
(async () => {
|
|
try {
|
|
await uploadFilesAsFolder([{ file, relativePath: file?.name }], false);
|
|
} catch {}
|
|
})();
|
|
return true;
|
|
};
|
|
|
|
this.onDragDrop = function (e, graphCanvas) {
|
|
(async () => {
|
|
try {
|
|
const payload = await collectDropPayload(e);
|
|
const pairs = (payload?.pairs || []).filter((p) => p?.file);
|
|
const hasDirectory = Boolean(payload?.hasDirectory);
|
|
await uploadFilesAsFolder(pairs, hasDirectory);
|
|
} catch {}
|
|
})();
|
|
return true;
|
|
};
|
|
|
|
this.onDragOver = function (e) {
|
|
if (e.dataTransfer) {
|
|
e.dataTransfer.dropEffect = "copy";
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// 监听 widget 变化
|
|
if (fileWidget) fileWidget.callback = this.updatePreview;
|
|
if (indexWidget) indexWidget.callback = this.updatePreview;
|
|
if (includeSubdirWidget) includeSubdirWidget.callback = this.updatePreview;
|
|
if (allWidget) allWidget.callback = this.updatePreview;
|
|
|
|
const computeStateKey = () => {
|
|
const p = String(fileWidget ? fileWidget.value : "");
|
|
const i = String(indexWidget ? indexWidget.value : 0);
|
|
const s = String(includeSubdirWidget ? includeSubdirWidget.value : true);
|
|
const a = String(allWidget ? allWidget.value : false);
|
|
return `${p}||${i}||${s}||${a}`;
|
|
};
|
|
|
|
this._comfy1hewLoadImageStateKey = computeStateKey();
|
|
if (!this._comfy1hewLoadImagePoller) {
|
|
this._comfy1hewLoadImagePoller = setInterval(() => {
|
|
try {
|
|
const nextKey = computeStateKey();
|
|
if (nextKey === this._comfy1hewLoadImageStateKey) {
|
|
return;
|
|
}
|
|
this._comfy1hewLoadImageStateKey = nextKey;
|
|
if (typeof this.updatePreview === "function") {
|
|
this.updatePreview();
|
|
}
|
|
} catch {}
|
|
}, 200);
|
|
}
|
|
|
|
const onPaste = (e) => {
|
|
if (!e.clipboardData) return;
|
|
|
|
// Check if this node is selected
|
|
if (!app.canvas.selected_nodes || !app.canvas.selected_nodes[this.id]) {
|
|
return;
|
|
}
|
|
|
|
const items = e.clipboardData.items;
|
|
if (!items) return;
|
|
|
|
const files = [];
|
|
for (let i = 0; i < items.length; i++) {
|
|
if (items[i].kind === 'file' && items[i].type.startsWith('image/')) {
|
|
const file = items[i].getAsFile();
|
|
if (file) {
|
|
const name = file.name || "pasted_image.png";
|
|
files.push({ file: file, relativePath: name });
|
|
}
|
|
}
|
|
}
|
|
|
|
if (files.length > 0) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
e.stopImmediatePropagation();
|
|
uploadFilesAsFolder(files, false);
|
|
}
|
|
};
|
|
document.addEventListener("paste", onPaste, { capture: true });
|
|
|
|
const originalOnRemoved = this.onRemoved;
|
|
this.onRemoved = function () {
|
|
try {
|
|
document.removeEventListener("paste", onPaste, { capture: true });
|
|
} catch {}
|
|
try {
|
|
setDragPassthrough(false);
|
|
for (const target of dragTargets) {
|
|
target.removeEventListener("dragenter", handleDomDragEnter);
|
|
target.removeEventListener("dragover", handleDomDragOver);
|
|
target.removeEventListener("dragleave", handleDomDragLeave);
|
|
target.removeEventListener("drop", handleDomDrop);
|
|
}
|
|
} catch {}
|
|
try {
|
|
if (this._comfy1hewLoadImagePoller) {
|
|
clearInterval(this._comfy1hewLoadImagePoller);
|
|
this._comfy1hewLoadImagePoller = null;
|
|
}
|
|
} catch {}
|
|
if (originalOnRemoved) {
|
|
return originalOnRemoved.apply(this, arguments);
|
|
}
|
|
};
|
|
|
|
const ensurePreviewLayout = () => {
|
|
if (this.imageWidget.aspectRatio) {
|
|
requestAutoSize();
|
|
updateLayout();
|
|
}
|
|
};
|
|
|
|
// 初始多次尝试触发布局更新,解决首次加载出画框问题
|
|
setTimeout(ensurePreviewLayout, 100);
|
|
setTimeout(ensurePreviewLayout, 500);
|
|
|
|
// 初始加载
|
|
if (fileWidget && fileWidget.value) {
|
|
this.updatePreview();
|
|
}
|
|
if (this._comfy1hewLoadImagePendingPreview) {
|
|
this._comfy1hewLoadImagePendingPreview = false;
|
|
setTimeout(() => {
|
|
if (this.updatePreview) {
|
|
this.updatePreview();
|
|
}
|
|
}, 0);
|
|
}
|
|
setTimeout(() => applyPreviewStyle(this), 0);
|
|
setTimeout(() => applyPreviewStyle(this), 120);
|
|
setTimeout(() => applyPreviewStyle(this), 600);
|
|
|
|
return r;
|
|
};
|
|
|
|
|
|
|
|
if (!window.__comfy1hewLoadImageClipspacePatched) {
|
|
window.__comfy1hewLoadImageClipspacePatched = true;
|
|
const install = () => {
|
|
const comfyApp = window?.ComfyApp;
|
|
if (!comfyApp) {
|
|
return;
|
|
}
|
|
const originalPaste = comfyApp.pasteFromClipspace;
|
|
if (typeof originalPaste !== "function") {
|
|
return;
|
|
}
|
|
if (comfyApp.__comfy1hewPasteFromClipspaceWrapped) {
|
|
return;
|
|
}
|
|
comfyApp.__comfy1hewPasteFromClipspaceWrapped = true;
|
|
comfyApp.pasteFromClipspace = function () {
|
|
const r2 = originalPaste.apply(this, arguments);
|
|
setTimeout(() => {
|
|
const node = window?.ComfyApp?.clipspace_return_node;
|
|
if (node?.type !== "load_image") {
|
|
return;
|
|
}
|
|
saveMaskFromClipspaceToSidecar({ node, api, app });
|
|
}, 0);
|
|
return r2;
|
|
};
|
|
};
|
|
setTimeout(install, 0);
|
|
}
|
|
|
|
},
|
|
});
|