diff --git a/server.py b/server.py index 1267938b9..f3caa2a9f 100644 --- a/server.py +++ b/server.py @@ -8,6 +8,7 @@ import uuid import json import glob from PIL import Image +import numpy as np try: import aiohttp @@ -112,19 +113,24 @@ class PromptServer(): files = glob.glob(os.path.join(self.web_root, 'extensions/**/*.js'), recursive=True) return web.json_response(list(map(lambda f: "/" + os.path.relpath(f, self.web_root).replace("\\", "/"), files))) + def get_dir_by_type(dir_type): + if dir_type is None: + type_dir = folder_paths.get_input_directory() + elif dir_type == "input": + type_dir = folder_paths.get_input_directory() + elif dir_type == "temp": + type_dir = folder_paths.get_temp_directory() + elif dir_type == "output": + type_dir = folder_paths.get_output_directory() + + return type_dir + @routes.post("/upload/image") async def upload_image(request): post = await request.post() image = post.get("image") - if post.get("type") is None: - upload_dir = folder_paths.get_input_directory() - elif post.get("type") == "input": - upload_dir = folder_paths.get_input_directory() - elif post.get("type") == "temp": - upload_dir = folder_paths.get_temp_directory() - elif post.get("type") == "output": - upload_dir = folder_paths.get_output_directory() + upload_dir = get_dir_by_type(post.get("type")) if not os.path.exists(upload_dir): os.makedirs(upload_dir) @@ -155,14 +161,7 @@ class PromptServer(): image = post.get("image") original_image = post.get("original_image") - if post.get("type") is None: - upload_dir = folder_paths.get_input_directory() - elif post.get("type") == "input": - upload_dir = folder_paths.get_input_directory() - elif post.get("type") == "temp": - upload_dir = folder_paths.get_temp_directory() - elif post.get("type") == "output": - upload_dir = folder_paths.get_output_directory() + upload_dir = get_dir_by_type(post.get("type")) if not os.path.exists(upload_dir): os.makedirs(upload_dir) @@ -180,10 +179,13 @@ class PromptServer(): filepath = os.path.join(upload_dir, filename) - original_pil = Image.open(original_image) - mask_pil = Image.open(image.file) - alpha_channel = mask_pil.getchannel('A') - original_pil.putalpha(alpha_channel, (0, 0)) + original_pil = Image.open(original_image.file).convert('RGBA') + mask_pil = Image.open(image.file).convert('RGBA') + + # alpha copy + new_alpha = mask_pil.getchannel('A') + original_pil.putalpha(new_alpha) + original_pil.save(filepath) return web.json_response({"name": filename}) diff --git a/web/extensions/core/clipspace.js b/web/extensions/core/clipspace.js index 330d43bde..4ac6ca54b 100644 --- a/web/extensions/core/clipspace.js +++ b/web/extensions/core/clipspace.js @@ -2,268 +2,54 @@ import { app } from "/scripts/app.js"; import { ComfyDialog, $el } from "/scripts/ui.js"; import { ComfyApp } from "/scripts/app.js"; -// Helper function to convert a data URL to a Blob object -function dataURLToBlob(dataURL) { - const parts = dataURL.split(';base64,'); - const contentType = parts[0].split(':')[1]; - const byteString = atob(parts[1]); - const arrayBuffer = new ArrayBuffer(byteString.length); - const uint8Array = new Uint8Array(arrayBuffer); - for (let i = 0; i < byteString.length; i++) { - uint8Array[i] = byteString.charCodeAt(i); +export class ClipspaceDialog extends ComfyDialog { + static items = []; + static is_opened = false; // prevent redundant popup + + static registerButton(name, callback) { + const item = + $el("button", { + type: "button", + textContent: name, + onclick: callback + }) + + ClipspaceDialog.items.push(item); } - return new Blob([arrayBuffer], { type: contentType }); -} -async function invalidateImage(filepath, formData) { - await fetch('/upload/image', { - method: 'POST', - body: formData - }).then(response => {}).catch(error => { - console.error('Error:', error); - }); - - ComfyApp.clipspace.imgs[0] = new Image(); - ComfyApp.clipspace.imgs[0].src = `view?filename=${filepath.filename}&type=${filepath.type}`; -} - -class ClipspaceDialog extends ComfyDialog { constructor() { super(); - this.element = $el("div.comfy-modal", { parent: document.body }, - [ - $el("div.comfy-modal-content", - [ - ...this.createButtons()]), - ]); + this.element = + $el("div.comfy-modal", { parent: document.body }, + [$el("div.comfy-modal-content",[...this.createButtons()]),] + ); } createButtons() { - return [ + const buttons = []; + + for(let idx in ClipspaceDialog.items) { + const item = ClipspaceDialog.items[idx]; + buttons.push(ClipspaceDialog.items[idx]); + } + + buttons.push( $el("button", { type: "button", - textContent: "Save", + textContent: "Close", onclick: () => { - const backupCtx = this.backupCanvas.getContext('2d', {transparent: true}); - backupCtx.clearRect(0,0,this.backupCanvas.width,this.backupCanvas.height); - backupCtx.drawImage(this.maskCanvas, - 0, 0, this.maskCanvas.width, this.maskCanvas.height, - 0, 0, this.backupCanvas.width, this.backupCanvas.height); - - // paste mask data into alpha channel - const backupData = backupCtx.getImageData(0, 0, this.backupCanvas.width, this.backupCanvas.height); - - for (let i = 0; i < backupData.data.length; i += 4) { - if(backupData.data[i+3] == 255) - backupData.data[i+3] = 0; - else - backupData.data[i+3] = 255; - - backupData.data[i] = 0; - backupData.data[i+1] = 0; - backupData.data[i+2] = 0; - } - - backupCtx.globalCompositeOperation = 'source-over'; - backupCtx.putImageData(backupData, 0, 0); - - const dataURL = this.backupCanvas.toDataURL(); - const blob = dataURLToBlob(dataURL); - - /* - // copy image data - backupCtx.globalCompositeOperation = 'copy'; - backupCtx.globalAlpha = 1.0; - backupCtx.drawImage(this.image, 0, 0); - backupCtx.globalCompositeOperation = 'source-over'; - - const backupData2 = backupCtx.getImageData(0, 0, this.backupCanvas.width, this.backupCanvas.height); - - // restore alpha channel - var cnt_r = 0; - for (let i = 0; i < backupData2.data.length; i += 4) { - if(backupData2.data[i] == 0) { - cnt_r++; - } - - backupData2.data[i + 3] = backupData.data[i + 3]; - } - - // I don't know why RGB channel is effected by this code.... - backupCtx.putImageData(backupData2, 0, 0); - - const dataURL2 = this.backupCanvas.toDataURL(); - const blob2 = dataURLToBlob(dataURL2); - */ - - const formData = new FormData(); - const filename = "clipspace-mask-" + performance.now() + ".png"; - - const item = - { - "filename": filename, - "subfolder": "", - "type": "temp", - }; - - console.log(ComfyApp.clipspace); - if(ComfyApp.clipspace.images) - ComfyApp.clipspace.images[0] = item; - - if(ComfyApp.clipspace.widgets) { - const index = ComfyApp.clipspace.widgets.findIndex(obj => obj.name === 'image'); - console.log(index); - ComfyApp.clipspace.widgets[index].value = item; - } - - formData.append('image', blob, filename); - formData.append('original-imagepath', ComfyApp.clipspace.); - formData.append('type', "temp"); - invalidateImage(item, formData); + ClipspaceDialog.is_opened = false; this.close(); } - }), - $el("button", { - type: "button", - textContent: "Cancel", - onclick: () => this.close(), - }), - $el("button", { - type: "button", - textContent: "Clear", - onclick: () => { - this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height); - }, - }), - ]; + }) + ); + + return buttons; } show() { - const imgCanvas = document.createElement('canvas'); - const maskCanvas = document.createElement('canvas'); - const backupCanvas = document.createElement('canvas'); - - imgCanvas.id = "imageCanvas"; - maskCanvas.id = "maskCanvas"; - backupCanvas.id = "backupCanvas"; - - this.element.appendChild(imgCanvas); - this.element.appendChild(maskCanvas); - + ClipspaceDialog.is_opened = true; this.element.style.display = "block"; - imgCanvas.style.position = "relative"; - imgCanvas.style.top = "200"; - imgCanvas.style.left = "0"; - - maskCanvas.style.position = "absolute"; - - const imgCtx = imgCanvas.getContext('2d'); - const maskCtx = maskCanvas.getContext('2d'); - const backupCtx = backupCanvas.getContext('2d'); - - this.maskCanvas = maskCanvas; - this.maskCtx = maskCtx; - this.backupCanvas = backupCanvas; - - window.addEventListener("resize", () => { - // repositioning - imgCanvas.width = window.innerWidth - 250; - imgCanvas.height = window.innerHeight - 300; - - // redraw image - let drawWidth = image.width; - let drawHeight = image.height; - if (image.width > imgCanvas.width) { - drawWidth = imgCanvas.width; - drawHeight = (drawWidth / image.width) * image.height; - } - - if (drawHeight > imgCanvas.height) { - drawHeight = imgCanvas.height; - drawWidth = (drawHeight / image.height) * image.width; - } - - imgCtx.drawImage(image, 0, 0, drawWidth, drawHeight); - - // update mask - backupCtx.drawImage(maskCanvas, 0, 0, maskCanvas.width, maskCanvas.height, 0, 0, backupCanvas.width, backupCanvas.height); - maskCanvas.width = drawWidth; - maskCanvas.height = drawHeight; - maskCanvas.style.top = imgCanvas.offsetTop + "px"; - maskCanvas.style.left = imgCanvas.offsetLeft + "px"; - maskCtx.drawImage(backupCanvas, 0, 0, backupCanvas.width, backupCanvas.height, 0, 0, maskCanvas.width, maskCanvas.height); - }); - - // image load - const image = new Image(); - image.onload = function() { - backupCanvas.width = image.width; - backupCanvas.height = image.height; - window.dispatchEvent(new Event('resize')); - }; - - const filepath = ComfyApp.clipspace.images; - console.log(ComfyApp.clipspace); - console.log(ComfyApp.clipspace.imgs[0]); - image.src = ComfyApp.clipspace.imgs[0].src; - this.image = image; - - // event handler for user drawing ------ - let brush_size = 10; - - function draw_point(event) { - const maskRect = maskCanvas.getBoundingClientRect(); - const x = event.offsetX || event.targetTouches[0].clientX - maskRect.left; - const y = event.offsetY || event.targetTouches[0].clientY - maskRect.top; - - maskCtx.beginPath(); - maskCtx.fillStyle = "rgb(0,0,0)"; - maskCtx.globalCompositeOperation = "source-over"; - maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false); - maskCtx.fill(); - } - - function draw_move(event) { - if (event.buttons === 1) { - event.preventDefault(); - const maskRect = maskCanvas.getBoundingClientRect(); - const x = event.offsetX || event.targetTouches[0].clientX - maskRect.left; - const y = event.offsetY || event.targetTouches[0].clientY - maskRect.top; - - maskCtx.beginPath(); - maskCtx.fillStyle = "rgb(0,0,0)"; - maskCtx.globalCompositeOperation = "source-over"; - maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false); - maskCtx.fill(); - } - else if(event.buttons === 2) { - event.preventDefault(); - const maskRect = maskCanvas.getBoundingClientRect(); - const x = event.offsetX || event.targetTouches[0].clientX - maskRect.left; - const y = event.offsetY || event.targetTouches[0].clientY - maskRect.top; - - maskCtx.beginPath(); - maskCtx.globalCompositeOperation = "destination-out"; - maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false); - maskCtx.fill(); - } - } - - function handleWheelEvent(event) { - if(event.deltaY < 0) - brush_size = Math.min(brush_size+2, 100); - else - brush_size = Math.max(brush_size-2, 1); - } - - maskCanvas.addEventListener("contextmenu", (event) => { - event.preventDefault(); - }); - - maskCanvas.addEventListener('wheel', handleWheelEvent); - maskCanvas.addEventListener('mousedown', draw_point); - maskCanvas.addEventListener('mousemove', draw_move); - maskCanvas.addEventListener('touchmove', draw_move); } } @@ -272,11 +58,13 @@ app.registerExtension({ init(app) { app.openClipspace = function () { - let dlg = new ClipspaceDialog(app); - if(ComfyApp.clipspace) - dlg.show(); - else - app.ui.dialog.show("Clipspace is Empty!"); + if(!ClipspaceDialog.is_opened) { + let dlg = new ClipspaceDialog(app); + if(ComfyApp.clipspace) + dlg.show(); + else + app.ui.dialog.show("Clipspace is Empty!"); + } }; } }); \ No newline at end of file diff --git a/web/extensions/core/maskeditor.js b/web/extensions/core/maskeditor.js new file mode 100644 index 000000000..722a0d50c --- /dev/null +++ b/web/extensions/core/maskeditor.js @@ -0,0 +1,376 @@ +import { app } from "/scripts/app.js"; +import { ComfyDialog, $el } from "/scripts/ui.js"; +import { ComfyApp } from "/scripts/app.js"; +import { ClipspaceDialog } from "/extensions/core/clipspace.js"; + +// Helper function to convert a data URL to a Blob object +function dataURLToBlob(dataURL) { + const parts = dataURL.split(';base64,'); + const contentType = parts[0].split(':')[1]; + const byteString = atob(parts[1]); + const arrayBuffer = new ArrayBuffer(byteString.length); + const uint8Array = new Uint8Array(arrayBuffer); + for (let i = 0; i < byteString.length; i++) { + uint8Array[i] = byteString.charCodeAt(i); + } + return new Blob([arrayBuffer], { type: contentType }); +} + +function loadedImageToBlob(image) { + const canvas = document.createElement('canvas'); + + canvas.width = image.width; + canvas.height = image.height; + + const ctx = canvas.getContext('2d'); + + ctx.drawImage(image, 0, 0); + + const dataURL = canvas.toDataURL('image/png', 1); + const blob = dataURLToBlob(dataURL); + + return blob; +} + +async function uploadMask(filepath, formData) { + await fetch('/upload/mask', { + method: 'POST', + body: formData + }).then(response => {}).catch(error => { + console.error('Error:', error); + }); + + ComfyApp.clipspace.imgs[0] = new Image(); + ComfyApp.clipspace.imgs[0].src = `view?filename=${filepath.filename}&type=${filepath.type}`; + ComfyApp.clipspace.images = [filepath]; +} + +function removeRGB(image, backupCanvas, backupCtx, maskCtx) { + // paste mask data into alpha channel + backupCtx.drawImage(image, 0, 0, backupCanvas.width, backupCanvas.height); + const backupData = backupCtx.getImageData(0, 0, backupCanvas.width, backupCanvas.height); + + // refine mask image + for (let i = 0; i < backupData.data.length; i += 4) { + if(backupData.data[i+3] == 255) + backupData.data[i+3] = 0; + else + backupData.data[i+3] = 255; + + backupData.data[i] = 0; + backupData.data[i+1] = 0; + backupData.data[i+2] = 0; + } + + backupCtx.globalCompositeOperation = 'source-over'; + backupCtx.putImageData(backupData, 0, 0); +} + +class MaskEditorDialog extends ComfyDialog { + constructor() { + super(); + this.element = $el("div.comfy-modal", { parent: document.body }, + [ + $el("div.comfy-modal-content", + [ + ...this.createButtons()]), + ]); + } + + createButtons() { + return []; +// $el("button", { +// type: "button", +// textContent: "Save", +// onclick: () => this.save(), +// }), +// $el("button", { +// type: "button", +// textContent: "Cancel", +// onclick: () => this.close(), +// }), +// $el("button", { +// type: "button", +// textContent: "Clear", +// onclick: () => { +// this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height); +// this.backupCtx.clearRect(0, 0, this.backupCanvas.width, this.backupCanvas.height); +// }, +// }), +// ]; + } + + clearMask(self) { + } + + setlayout(imgCanvas, maskCanvas) { + const self = this; + var bottom_panel = document.createElement("div"); + bottom_panel.style.position = "fixed"; + bottom_panel.style.bottom = "0"; + bottom_panel.style.left = "0"; + bottom_panel.style.right = "0"; + bottom_panel.style.height = "50px"; + + var clearButton = document.createElement("button"); + clearButton.innerText = "Clear"; + clearButton.style.position = "absolute"; + clearButton.style.left = "20px"; + clearButton.addEventListener("click", function() { + self.maskCtx.clearRect(0, 0, self.maskCanvas.width, self.maskCanvas.height); + self.backupCtx.clearRect(0, 0, self.backupCanvas.width, self.backupCanvas.height); + }); + + var saveButton = document.createElement("button"); + saveButton.innerText = "Save"; + saveButton.style.position = "absolute"; + saveButton.style.right = "110px"; + saveButton.addEventListener("click", function() { self.save(); }); + + var cancelButton = document.createElement("button"); + cancelButton.innerText = "Cancel"; + cancelButton.style.position = "absolute"; + cancelButton.style.right = "20px"; + cancelButton.addEventListener("click", function() { self.close(); }); + + this.element.appendChild(imgCanvas); + this.element.appendChild(maskCanvas); + this.element.appendChild(bottom_panel); + + bottom_panel.appendChild(clearButton); + bottom_panel.appendChild(saveButton); + bottom_panel.appendChild(cancelButton); + + this.element.style.display = "block"; + imgCanvas.style.position = "relative"; + imgCanvas.style.top = "200"; + imgCanvas.style.left = "0"; + + maskCanvas.style.position = "absolute"; + } + + show() { + // layout + const imgCanvas = document.createElement('canvas'); + const maskCanvas = document.createElement('canvas'); + const backupCanvas = document.createElement('canvas'); + + imgCanvas.id = "imageCanvas"; + maskCanvas.id = "maskCanvas"; + backupCanvas.id = "backupCanvas"; + + this.setlayout(imgCanvas, maskCanvas); + + // prepare content + + this.maskCanvas = maskCanvas; + this.backupCanvas = backupCanvas; + this.maskCtx = maskCanvas.getContext('2d'); + this.backupCtx = backupCanvas.getContext('2d'); + + // separate original_imgs and imgs + if(ComfyApp.clipspace.imgs[0] === ComfyApp.clipspace.original_imgs[0]) { + console.log(ComfyApp.clipspace.imgs[0]); + var copiedImage = new Image(); + copiedImage.src = ComfyApp.clipspace.original_imgs[0].src; + ComfyApp.clipspace.imgs = [copiedImage]; + } + + this.setImages(imgCanvas, backupCanvas); + this.setEventHandler(maskCanvas); + } + + setImages(imgCanvas, backupCanvas) { + const imgCtx = imgCanvas.getContext('2d'); + const backupCtx = backupCanvas.getContext('2d'); + const maskCtx = this.maskCtx; + const maskCanvas = this.maskCanvas; + + // image load + const orig_image = new Image(); + window.addEventListener("resize", () => { + // repositioning + imgCanvas.width = window.innerWidth - 250; + imgCanvas.height = window.innerHeight - 300; + + // redraw image + let drawWidth = orig_image.width; + let drawHeight = orig_image.height; + if (orig_image.width > imgCanvas.width) { + drawWidth = imgCanvas.width; + drawHeight = (drawWidth / orig_image.width) * orig_image.height; + } + + if (drawHeight > imgCanvas.height) { + drawHeight = imgCanvas.height; + drawWidth = (drawHeight / orig_image.height) * orig_image.width; + } + + imgCtx.drawImage(orig_image, 0, 0, drawWidth, drawHeight); + + // update mask + backupCtx.drawImage(maskCanvas, 0, 0, maskCanvas.width, maskCanvas.height, 0, 0, backupCanvas.width, backupCanvas.height); + maskCanvas.width = drawWidth; + maskCanvas.height = drawHeight; + maskCanvas.style.top = imgCanvas.offsetTop + "px"; + maskCanvas.style.left = imgCanvas.offsetLeft + "px"; + maskCtx.drawImage(backupCanvas, 0, 0, backupCanvas.width, backupCanvas.height, 0, 0, maskCanvas.width, maskCanvas.height); + }); + + const filepath = ComfyApp.clipspace.images; + + const touched_image = new Image(); + + touched_image.onload = function() { + backupCanvas.width = touched_image.width; + backupCanvas.height = touched_image.height; + + removeRGB(touched_image, backupCanvas, backupCtx, maskCtx); + }; + + touched_image.src = ComfyApp.clipspace.imgs[0].src; + + // original image load + orig_image.onload = function() { + window.dispatchEvent(new Event('resize')); + }; + + orig_image.src = ComfyApp.clipspace.original_imgs[0].src; + this.image = orig_image; + } + + setEventHandler(maskCanvas) { + let brush_size = 10; + const maskCtx = maskCanvas.getContext('2d'); + + function draw_point(event) { + console.log(event.button); + if (event.button == 0) { + const maskRect = maskCanvas.getBoundingClientRect(); + const x = event.offsetX || event.targetTouches[0].clientX - maskRect.left; + const y = event.offsetY || event.targetTouches[0].clientY - maskRect.top; + + maskCtx.beginPath(); + maskCtx.fillStyle = "rgb(0,0,0)"; + maskCtx.globalCompositeOperation = "source-over"; + maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false); + maskCtx.fill(); + } + } + + function draw_move(event) { + if (event.buttons === 1) { + event.preventDefault(); + const maskRect = maskCanvas.getBoundingClientRect(); + const x = event.offsetX || event.targetTouches[0].clientX - maskRect.left; + const y = event.offsetY || event.targetTouches[0].clientY - maskRect.top; + + maskCtx.beginPath(); + maskCtx.fillStyle = "rgb(0,0,0)"; + maskCtx.globalCompositeOperation = "source-over"; + maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false); + maskCtx.fill(); + } + else if(event.buttons === 2) { + event.preventDefault(); + const maskRect = maskCanvas.getBoundingClientRect(); + const x = event.offsetX || event.targetTouches[0].clientX - maskRect.left; + const y = event.offsetY || event.targetTouches[0].clientY - maskRect.top; + + maskCtx.beginPath(); + maskCtx.globalCompositeOperation = "destination-out"; + maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false); + maskCtx.fill(); + } + } + + function handleWheelEvent(event) { + if(event.deltaY < 0) + brush_size = Math.min(brush_size+2, 100); + else + brush_size = Math.max(brush_size-2, 1); + } + + maskCanvas.addEventListener("contextmenu", (event) => { + event.preventDefault(); + }); + + maskCanvas.addEventListener('wheel', handleWheelEvent); + maskCanvas.addEventListener('mousedown', draw_point); + maskCanvas.addEventListener('mousemove', draw_move); + maskCanvas.addEventListener('touchmove', draw_move); + + } + + save() { + const backupCtx = this.backupCanvas.getContext('2d', {willReadFrequently:true}); + + backupCtx.clearRect(0,0,this.backupCanvas.width,this.backupCanvas.height); + backupCtx.drawImage(this.maskCanvas, + 0, 0, this.maskCanvas.width, this.maskCanvas.height, + 0, 0, this.backupCanvas.width, this.backupCanvas.height); + + // paste mask data into alpha channel + const backupData = backupCtx.getImageData(0, 0, this.backupCanvas.width, this.backupCanvas.height); + + // refine mask image + for (let i = 0; i < backupData.data.length; i += 4) { + if(backupData.data[i+3] == 255) + backupData.data[i+3] = 0; + else + backupData.data[i+3] = 255; + + backupData.data[i] = 0; + backupData.data[i+1] = 0; + backupData.data[i+2] = 0; + } + + backupCtx.globalCompositeOperation = 'source-over'; + backupCtx.putImageData(backupData, 0, 0); + + const formData = new FormData(); + const filename = "clipspace-mask-" + performance.now() + ".png"; + + const item = + { + "filename": filename, + "subfolder": "", + "type": "temp", + }; + + if(ComfyApp.clipspace.images) + ComfyApp.clipspace.images[0] = item; + + if(ComfyApp.clipspace.widgets) { + const index = ComfyApp.clipspace.widgets.findIndex(obj => obj.name === 'image'); + + if(index >= 0) + ComfyApp.clipspace.widgets[index].value = item; + } + + const dataURL = this.backupCanvas.toDataURL(); + const blob = dataURLToBlob(dataURL); + + const original_blob = loadedImageToBlob(ComfyApp.clipspace.original_imgs[0]); + + formData.append('image', blob, filename); + formData.append('original_image', original_blob); + formData.append('type', "temp"); + + uploadMask(item, formData); + this.close(); + } +} + +app.registerExtension({ + name: "Comfy.MaskEditor", + init(app) { + const callback = + function () { + let dlg = new MaskEditorDialog(app); + dlg.show(); + }; + + ClipspaceDialog.registerButton("MaskEditor", callback); + } +}); \ No newline at end of file