Restore Pixaroma-style save image UI
This commit is contained in:
@@ -0,0 +1,461 @@
|
||||
import { app } from "/scripts/app.js";
|
||||
|
||||
const NODE_NAME = "DumasSaveImage";
|
||||
const MIN_W = 474;
|
||||
const MIN_H = 620;
|
||||
const DEFAULT_PATTERN = "image_%date:yyyy-MM-dd%_%counter%";
|
||||
const ACCENT = "#e66a2c";
|
||||
const CHIPS = [
|
||||
{ label: "+ Input name", token: "%input%" },
|
||||
{ label: "+ Date", token: "%date:yyyy-MM-dd%" },
|
||||
{ label: "+ Time", token: "%date:hh-mm-ss%" },
|
||||
{ label: "+ Counter", token: "%counter%" },
|
||||
{ label: "+ Width", token: "%width%" },
|
||||
{ label: "+ Height", token: "%height%" },
|
||||
{ label: "+ Batch #", token: "%batch_num%" },
|
||||
];
|
||||
|
||||
let cssDone = false;
|
||||
|
||||
function injectCSS() {
|
||||
if (cssDone || document.getElementById("dumas-save-image-css")) {
|
||||
cssDone = true;
|
||||
return;
|
||||
}
|
||||
cssDone = true;
|
||||
const style = document.createElement("style");
|
||||
style.id = "dumas-save-image-css";
|
||||
style.textContent = [
|
||||
".dsi-root{position:relative;width:100%;height:100%;box-sizing:border-box;}",
|
||||
".dsi-inner{position:absolute;inset:0;display:flex;flex-direction:column;gap:10px;padding:8px 10px 6px;box-sizing:border-box;overflow:hidden;font-family:'Segoe UI',system-ui,sans-serif;}",
|
||||
".dsi-lab{display:block;font-size:10px;font-weight:600;letter-spacing:.07em;text-transform:uppercase;color:var(--dsi-acc,#e66a2c);margin-bottom:4px;}",
|
||||
".dsi-row{display:flex;gap:6px;align-items:center;}",
|
||||
".dsi-field{flex:1;min-width:0;background:#1d1d1d;border:1px solid #444;border-radius:4px;color:#e0e0e0;padding:5px 8px;font-size:12px;outline:none;box-sizing:border-box;font-family:inherit;width:100%;}",
|
||||
".dsi-field:focus{border-color:var(--dsi-acc,#e66a2c);}",
|
||||
".dsi-field.mono{font-family:Consolas,ui-monospace,monospace;}",
|
||||
".dsi-btn{background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.16);color:#ccc;border-radius:4px;padding:5px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:5px;white-space:nowrap;flex:0 0 auto;font-family:inherit;user-select:none;}",
|
||||
".dsi-btn:hover{background:var(--dsi-acc,#e66a2c);border-color:var(--dsi-acc,#e66a2c);color:#fff;}",
|
||||
".dsi-btn:disabled{opacity:.45;cursor:default;}",
|
||||
".dsi-btn:disabled:hover{background:rgba(255,255,255,.05);border-color:rgba(255,255,255,.16);color:#ccc;}",
|
||||
".dsi-primary{background:var(--dsi-acc,#e66a2c);border-color:var(--dsi-acc,#e66a2c);color:#fff;}",
|
||||
".dsi-primary:hover{background:#ff7d58;border-color:#ff7d58;color:#fff;}",
|
||||
".dsi-grow{flex:1 1 0;min-width:0;}",
|
||||
".dsi-hint{font-size:10px;color:#8f8f8f;line-height:1.45;}",
|
||||
".dsi-chips{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;}",
|
||||
".dsi-chip{background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.14);color:#ccc;border-radius:4px;padding:2px 7px;font-size:11px;cursor:pointer;font-family:inherit;user-select:none;}",
|
||||
".dsi-chip:hover{border-color:var(--dsi-acc,#e66a2c);color:#eee;}",
|
||||
".dsi-prev{background:#1d1d1d;border:1px solid #444;border-radius:4px;padding:6px 8px;margin-top:8px;}",
|
||||
".dsi-prevlab{font-size:10px;color:#8f8f8f;margin-bottom:2px;}",
|
||||
".dsi-prevpath{font-family:Consolas,ui-monospace,monospace;font-size:11px;color:#ffb59e;word-break:break-all;line-height:1.45;}",
|
||||
".dsi-seg{display:inline-flex;border:1px solid #444;border-radius:999px;overflow:hidden;flex:0 0 auto;}",
|
||||
".dsi-seg button{background:#1d1d1d;color:#aaa;border:none;padding:4px 11px;font-size:12px;cursor:pointer;font-family:inherit;}",
|
||||
".dsi-seg button.on{background:var(--dsi-acc,#e66a2c);color:#fff;}",
|
||||
".dsi-btnrow{display:flex;flex-wrap:wrap;gap:6px;align-items:stretch;}",
|
||||
".dsi-viewsec{flex:1 1 0;min-height:0;display:flex;flex-direction:column;gap:4px;}",
|
||||
".dsi-view{position:relative;flex:1 1 0;min-height:180px;background:#151515;border:1px solid #3c3c3c;border-radius:4px;overflow:hidden;}",
|
||||
".dsi-view.has{background:transparent;border-color:transparent;}",
|
||||
".dsi-img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;display:none;}",
|
||||
".dsi-ph{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#777;font-size:11px;padding:8px;text-align:center;}",
|
||||
".dsi-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,.5);border:1px solid rgba(255,255,255,.18);color:#ddd;border-radius:4px;padding:4px 7px;font-size:11px;cursor:pointer;display:none;}",
|
||||
".dsi-view.has:hover .dsi-nav.show{display:block;}",
|
||||
".dsi-nav.prev{left:6px;}",
|
||||
".dsi-nav.next{right:6px;}",
|
||||
".dsi-nav:hover{background:var(--dsi-acc,#e66a2c);border-color:var(--dsi-acc,#e66a2c);color:#fff;}",
|
||||
".dsi-count{position:absolute;right:6px;bottom:6px;background:rgba(0,0,0,.55);color:#ddd;font-size:11px;padding:1px 7px;border-radius:3px;display:none;}",
|
||||
".dsi-info{flex:0 0 auto;text-align:center;font-size:11px;color:#8f8f8f;min-height:14px;line-height:1.25;word-break:break-word;}",
|
||||
].join("\n");
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
function el(tag, className, text) {
|
||||
const node = document.createElement(tag);
|
||||
if (className) node.className = className;
|
||||
if (text != null) node.textContent = text;
|
||||
return node;
|
||||
}
|
||||
|
||||
function hideWidget(widget) {
|
||||
if (!widget || widget.__dsiHidden) return;
|
||||
widget.__dsiHidden = true;
|
||||
widget.computeSize = () => [0, -4];
|
||||
widget.type = "hidden";
|
||||
widget.hidden = true;
|
||||
}
|
||||
|
||||
function getWidget(node, name) {
|
||||
return node.widgets?.find((widget) => widget?.name === name) || null;
|
||||
}
|
||||
|
||||
function setWidgetValue(node, name, value) {
|
||||
const widget = getWidget(node, name);
|
||||
if (!widget) return;
|
||||
widget.value = value;
|
||||
widget.callback?.(value, app.canvas, node, app);
|
||||
node.setDirtyCanvas?.(true, true);
|
||||
}
|
||||
|
||||
function pad(value, width) {
|
||||
return String(value).padStart(width, "0");
|
||||
}
|
||||
|
||||
function resolveDateTokens(value) {
|
||||
if (!value.includes("%date:")) return value;
|
||||
const now = new Date();
|
||||
return value.replace(/%date:([^%]+)%/g, (_match, format) => format.replace(
|
||||
/yyyy|yy|MM|dd|hh|mm|ss/g,
|
||||
(token) => {
|
||||
if (token === "yyyy") return pad(now.getFullYear(), 4);
|
||||
if (token === "yy") return String(now.getFullYear()).slice(-2);
|
||||
if (token === "MM") return pad(now.getMonth() + 1, 2);
|
||||
if (token === "dd") return pad(now.getDate(), 2);
|
||||
if (token === "hh") return pad(now.getHours(), 2);
|
||||
if (token === "mm") return pad(now.getMinutes(), 2);
|
||||
if (token === "ss") return pad(now.getSeconds(), 2);
|
||||
return token;
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
function buildPreviewPath(node, ui) {
|
||||
const folder = (ui.folderInput.value || "").trim() || "ComfyUI output";
|
||||
const format = ui.format === "jpg" ? ".jpg" : ".png";
|
||||
let pattern = ui.patternInput.value || DEFAULT_PATTERN;
|
||||
pattern = resolveDateTokens(pattern);
|
||||
pattern = pattern
|
||||
.replaceAll("%input%", "input")
|
||||
.replaceAll("%input2%", "input2")
|
||||
.replaceAll("%width%", String(node._dsiImageWidth || 0))
|
||||
.replaceAll("%height%", String(node._dsiImageHeight || 0))
|
||||
.replaceAll("%batch_num%", "0")
|
||||
.replaceAll("%counter%", "001");
|
||||
return `${folder}/${pattern}${format}`.replaceAll("\\", "/");
|
||||
}
|
||||
|
||||
function imageUrl(meta) {
|
||||
return `/view?filename=${encodeURIComponent(meta.filename)}&type=${encodeURIComponent(meta.type)}&subfolder=${encodeURIComponent(meta.subfolder || "")}&t=${Date.now()}`;
|
||||
}
|
||||
|
||||
function updateActionState(node, ui) {
|
||||
const hasImage = !!(node._dsiImages && node._dsiImages.length);
|
||||
ui.openButton.disabled = !hasImage;
|
||||
ui.copyButton.disabled = !hasImage;
|
||||
ui.prevButton.classList.toggle("show", !!(hasImage && node._dsiImages.length > 1));
|
||||
ui.nextButton.classList.toggle("show", !!(hasImage && node._dsiImages.length > 1));
|
||||
ui.count.style.display = hasImage && node._dsiImages.length > 1 ? "block" : "none";
|
||||
}
|
||||
|
||||
async function copyCurrentImage(node) {
|
||||
const entry = node._dsiImages?.[node._dsiImageIndex || 0];
|
||||
if (!entry || !navigator.clipboard?.write || typeof ClipboardItem === "undefined") return false;
|
||||
const response = await fetch(imageUrl(entry));
|
||||
if (!response.ok) return false;
|
||||
const blob = await response.blob();
|
||||
await navigator.clipboard.write([new ClipboardItem({ [blob.type || "image/png"]: blob })]);
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderPreview(node, ui) {
|
||||
const images = node._dsiImages || [];
|
||||
const currentIndex = Math.max(0, Math.min(node._dsiImageIndex || 0, images.length - 1));
|
||||
node._dsiImageIndex = currentIndex;
|
||||
ui.prevPath.textContent = buildPreviewPath(node, ui);
|
||||
ui.folderButton.title = (ui.folderInput.value || "").trim()
|
||||
? `Copy folder path: ${(ui.folderInput.value || "").trim()}`
|
||||
: "Using ComfyUI output folder";
|
||||
|
||||
if (!images.length) {
|
||||
ui.view.classList.remove("has");
|
||||
ui.img.style.display = "none";
|
||||
ui.placeholder.style.display = "flex";
|
||||
ui.count.style.display = "none";
|
||||
ui.info.textContent = ui.saveOnRun
|
||||
? "Run the workflow to save and preview the result here"
|
||||
: "Preview mode is on. Run the workflow to show the latest image here";
|
||||
updateActionState(node, ui);
|
||||
return;
|
||||
}
|
||||
|
||||
const current = images[currentIndex];
|
||||
ui.view.classList.add("has");
|
||||
ui.placeholder.style.display = "none";
|
||||
ui.img.style.display = "block";
|
||||
ui.img.src = imageUrl(current);
|
||||
ui.count.textContent = `${currentIndex + 1} / ${images.length}`;
|
||||
ui.info.textContent = images.length > 1
|
||||
? `Showing saved image ${currentIndex + 1} of ${images.length}`
|
||||
: "Showing latest saved image";
|
||||
updateActionState(node, ui);
|
||||
}
|
||||
|
||||
function syncFromWidgets(node, ui) {
|
||||
ui.folderInput.value = String(getWidget(node, "folder")?.value || "");
|
||||
ui.patternInput.value = String(getWidget(node, "pattern")?.value || DEFAULT_PATTERN);
|
||||
ui.format = String(getWidget(node, "format")?.value || "png").toLowerCase() === "jpg" ? "jpg" : "png";
|
||||
ui.saveOnRun = !!getWidget(node, "save_on_run")?.value;
|
||||
ui.embedWorkflow = !!getWidget(node, "embed_workflow")?.value;
|
||||
ui.fmtPng.classList.toggle("on", ui.format === "png");
|
||||
ui.fmtJpg.classList.toggle("on", ui.format === "jpg");
|
||||
ui.modeSave.classList.toggle("on", ui.saveOnRun);
|
||||
ui.modePreview.classList.toggle("on", !ui.saveOnRun);
|
||||
renderPreview(node, ui);
|
||||
}
|
||||
|
||||
function appendToken(input, token) {
|
||||
const start = input.selectionStart ?? input.value.length;
|
||||
const end = input.selectionEnd ?? input.value.length;
|
||||
input.value = `${input.value.slice(0, start)}${token}${input.value.slice(end)}`;
|
||||
input.focus();
|
||||
input.setSelectionRange(start + token.length, start + token.length);
|
||||
}
|
||||
|
||||
function createRoot(node) {
|
||||
injectCSS();
|
||||
const root = el("div", "dsi-root");
|
||||
root.style.setProperty("--dsi-acc", ACCENT);
|
||||
const inner = el("div", "dsi-inner");
|
||||
root.appendChild(inner);
|
||||
|
||||
const folderSection = el("div");
|
||||
const folderRow = el("div", "dsi-row");
|
||||
const folderInput = el("input", "dsi-field");
|
||||
folderInput.type = "text";
|
||||
folderInput.spellcheck = false;
|
||||
folderInput.placeholder = "ComfyUI output folder";
|
||||
const browseButton = el("button", "dsi-btn", "Browse");
|
||||
browseButton.type = "button";
|
||||
folderRow.append(folderInput, browseButton);
|
||||
folderSection.append(folderRow, el("div", "dsi-hint", "Leave empty for the ComfyUI output folder. For your own folder, click Browse once."));
|
||||
inner.appendChild(folderSection);
|
||||
|
||||
const nameSection = el("div");
|
||||
nameSection.appendChild(el("span", "dsi-lab", "Filename"));
|
||||
const patternInput = el("input", "dsi-field mono");
|
||||
patternInput.type = "text";
|
||||
patternInput.spellcheck = false;
|
||||
patternInput.placeholder = DEFAULT_PATTERN;
|
||||
nameSection.appendChild(patternInput);
|
||||
const chips = el("div", "dsi-chips");
|
||||
for (const chip of CHIPS) {
|
||||
const chipButton = el("button", "dsi-chip", chip.label);
|
||||
chipButton.type = "button";
|
||||
chipButton.addEventListener("click", () => {
|
||||
appendToken(patternInput, chip.token);
|
||||
setWidgetValue(node, "pattern", patternInput.value);
|
||||
renderPreview(node, ui);
|
||||
});
|
||||
chips.appendChild(chipButton);
|
||||
}
|
||||
const previewBox = el("div", "dsi-prev");
|
||||
previewBox.appendChild(el("div", "dsi-prevlab", "Will save as"));
|
||||
const previewPath = el("div", "dsi-prevpath", "");
|
||||
previewBox.appendChild(previewPath);
|
||||
nameSection.append(chips, previewBox, el("div", "dsi-hint", "Type / in the name to create subfolders."));
|
||||
inner.appendChild(nameSection);
|
||||
|
||||
const buttonRowSection = el("div");
|
||||
const buttonRow = el("div", "dsi-btnrow");
|
||||
const openButton = el("button", "dsi-btn dsi-primary dsi-grow", "Open");
|
||||
openButton.type = "button";
|
||||
const copyButton = el("button", "dsi-btn dsi-primary dsi-grow", "Copy");
|
||||
copyButton.type = "button";
|
||||
const folderButton = el("button", "dsi-btn dsi-primary dsi-grow", "Folder");
|
||||
folderButton.type = "button";
|
||||
const formatSeg = el("div", "dsi-seg");
|
||||
const fmtPng = el("button", null, "PNG");
|
||||
fmtPng.type = "button";
|
||||
const fmtJpg = el("button", null, "JPG");
|
||||
fmtJpg.type = "button";
|
||||
formatSeg.append(fmtPng, fmtJpg);
|
||||
const modeSeg = el("div", "dsi-seg");
|
||||
const modeSave = el("button", null, "Save");
|
||||
modeSave.type = "button";
|
||||
const modePreview = el("button", null, "Preview");
|
||||
modePreview.type = "button";
|
||||
modeSeg.append(modeSave, modePreview);
|
||||
buttonRow.append(openButton, copyButton, folderButton, formatSeg, modeSeg);
|
||||
buttonRowSection.appendChild(buttonRow);
|
||||
inner.appendChild(buttonRowSection);
|
||||
|
||||
const viewSection = el("div", "dsi-viewsec");
|
||||
const view = el("div", "dsi-view");
|
||||
const img = el("img", "dsi-img");
|
||||
const placeholder = el("div", "dsi-ph", "Run the workflow to save and preview the result here");
|
||||
const prevButton = el("button", "dsi-nav prev", "◀");
|
||||
prevButton.type = "button";
|
||||
const nextButton = el("button", "dsi-nav next", "▶");
|
||||
nextButton.type = "button";
|
||||
const count = el("div", "dsi-count", "");
|
||||
view.append(img, placeholder, prevButton, nextButton, count);
|
||||
const info = el("div", "dsi-info", "");
|
||||
viewSection.append(view, info);
|
||||
inner.appendChild(viewSection);
|
||||
|
||||
const ui = {
|
||||
root,
|
||||
folderInput,
|
||||
browseButton,
|
||||
patternInput,
|
||||
prevPath: previewPath,
|
||||
fmtPng,
|
||||
fmtJpg,
|
||||
modeSave,
|
||||
modePreview,
|
||||
openButton,
|
||||
copyButton,
|
||||
folderButton,
|
||||
view,
|
||||
img,
|
||||
placeholder,
|
||||
prevButton,
|
||||
nextButton,
|
||||
count,
|
||||
info,
|
||||
format: "png",
|
||||
saveOnRun: true,
|
||||
embedWorkflow: true,
|
||||
};
|
||||
|
||||
folderInput.addEventListener("input", () => {
|
||||
setWidgetValue(node, "folder", folderInput.value);
|
||||
renderPreview(node, ui);
|
||||
});
|
||||
patternInput.addEventListener("input", () => {
|
||||
setWidgetValue(node, "pattern", patternInput.value);
|
||||
renderPreview(node, ui);
|
||||
});
|
||||
browseButton.addEventListener("click", async () => {
|
||||
if (typeof window.showDirectoryPicker !== "function") return;
|
||||
try {
|
||||
const handle = await window.showDirectoryPicker();
|
||||
if (!handle) return;
|
||||
folderInput.value = handle.name || folderInput.value;
|
||||
setWidgetValue(node, "folder", folderInput.value);
|
||||
renderPreview(node, ui);
|
||||
} catch (_error) {
|
||||
// Ignore picker cancel/failure.
|
||||
}
|
||||
});
|
||||
fmtPng.addEventListener("click", () => {
|
||||
ui.format = "png";
|
||||
setWidgetValue(node, "format", "png");
|
||||
syncFromWidgets(node, ui);
|
||||
});
|
||||
fmtJpg.addEventListener("click", () => {
|
||||
ui.format = "jpg";
|
||||
setWidgetValue(node, "format", "jpg");
|
||||
syncFromWidgets(node, ui);
|
||||
});
|
||||
modeSave.addEventListener("click", () => {
|
||||
ui.saveOnRun = true;
|
||||
setWidgetValue(node, "save_on_run", true);
|
||||
syncFromWidgets(node, ui);
|
||||
});
|
||||
modePreview.addEventListener("click", () => {
|
||||
ui.saveOnRun = false;
|
||||
setWidgetValue(node, "save_on_run", false);
|
||||
syncFromWidgets(node, ui);
|
||||
});
|
||||
openButton.addEventListener("click", () => {
|
||||
const entry = node._dsiImages?.[node._dsiImageIndex || 0];
|
||||
if (!entry) return;
|
||||
window.open(imageUrl(entry), "_blank", "noopener,noreferrer");
|
||||
});
|
||||
copyButton.addEventListener("click", async () => {
|
||||
try {
|
||||
await copyCurrentImage(node);
|
||||
ui.info.textContent = "Copied current image to clipboard";
|
||||
} catch (_error) {
|
||||
ui.info.textContent = "Could not copy image to clipboard";
|
||||
}
|
||||
});
|
||||
folderButton.addEventListener("click", async () => {
|
||||
const text = (folderInput.value || "").trim() || "ComfyUI output folder";
|
||||
try {
|
||||
if (navigator.clipboard?.writeText) {
|
||||
await navigator.clipboard.writeText(text);
|
||||
ui.info.textContent = `Copied folder path: ${text}`;
|
||||
}
|
||||
} catch (_error) {
|
||||
ui.info.textContent = text;
|
||||
}
|
||||
});
|
||||
prevButton.addEventListener("click", () => {
|
||||
if (!node._dsiImages?.length) return;
|
||||
node._dsiImageIndex = (node._dsiImageIndex - 1 + node._dsiImages.length) % node._dsiImages.length;
|
||||
renderPreview(node, ui);
|
||||
});
|
||||
nextButton.addEventListener("click", () => {
|
||||
if (!node._dsiImages?.length) return;
|
||||
node._dsiImageIndex = (node._dsiImageIndex + 1) % node._dsiImages.length;
|
||||
renderPreview(node, ui);
|
||||
});
|
||||
|
||||
node._dsiUI = ui;
|
||||
syncFromWidgets(node, ui);
|
||||
return root;
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "Dumas.SaveImage",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||
if (nodeData.name !== NODE_NAME) return;
|
||||
|
||||
const originalOnNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
originalOnNodeCreated?.apply(this, arguments);
|
||||
this.hideOutputImages = true;
|
||||
this._dsiImages = [];
|
||||
this._dsiImageIndex = 0;
|
||||
this._dsiImageWidth = 0;
|
||||
this._dsiImageHeight = 0;
|
||||
|
||||
hideWidget(getWidget(this, "folder"));
|
||||
hideWidget(getWidget(this, "pattern"));
|
||||
hideWidget(getWidget(this, "format"));
|
||||
hideWidget(getWidget(this, "quality"));
|
||||
hideWidget(getWidget(this, "embed_workflow"));
|
||||
hideWidget(getWidget(this, "save_on_run"));
|
||||
|
||||
const root = createRoot(this);
|
||||
const widget = this.addDOMWidget("dumas_save_image", "dumas_save_image", root, {
|
||||
serialize: false,
|
||||
hideOnZoom: false,
|
||||
});
|
||||
widget.computeLayoutSize = () => ({ minHeight: MIN_H, minWidth: MIN_W });
|
||||
|
||||
this.size[0] = Math.max(this.size[0] || 0, MIN_W);
|
||||
this.size[1] = Math.max(this.size[1] || 0, MIN_H);
|
||||
};
|
||||
|
||||
const originalOnExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (output) {
|
||||
originalOnExecuted?.apply(this, arguments);
|
||||
const ui = this._dsiUI;
|
||||
this._dsiImages = Array.isArray(output?.images) ? output.images : [];
|
||||
this._dsiImageIndex = Math.max(0, this._dsiImages.length - 1);
|
||||
if (this._dsiImages.length) {
|
||||
const first = this._dsiImages[0];
|
||||
const image = new Image();
|
||||
image.onload = () => {
|
||||
this._dsiImageWidth = image.naturalWidth || 0;
|
||||
this._dsiImageHeight = image.naturalHeight || 0;
|
||||
ui && renderPreview(this, ui);
|
||||
};
|
||||
image.src = imageUrl(first);
|
||||
}
|
||||
ui && renderPreview(this, ui);
|
||||
};
|
||||
|
||||
const originalOnConfigure = nodeType.prototype.onConfigure;
|
||||
nodeType.prototype.onConfigure = function () {
|
||||
originalOnConfigure?.apply(this, arguments);
|
||||
this._dsiUI && syncFromWidgets(this, this._dsiUI);
|
||||
};
|
||||
|
||||
const originalOnRemoved = nodeType.prototype.onRemoved;
|
||||
nodeType.prototype.onRemoved = function () {
|
||||
this._dsiUI = null;
|
||||
return originalOnRemoved?.apply(this, arguments);
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user