From 286b731de3313a3b80362a692e60d7c7e229fe1b Mon Sep 17 00:00:00 2001 From: "Dr.Lt.Data" Date: Sat, 29 Apr 2023 14:39:03 +0900 Subject: [PATCH] copy/paste bug fixes for batch images enhance selector preview on clipspace menu add img_paste_mode option into clipspace menu --- web/extensions/core/clipspace.js | 120 +++++++++++++++++++++--------- web/extensions/core/maskeditor.js | 5 +- web/scripts/app.js | 53 ++++++++++--- 3 files changed, 130 insertions(+), 48 deletions(-) diff --git a/web/extensions/core/clipspace.js b/web/extensions/core/clipspace.js index 3ede7e848..0b4802d63 100644 --- a/web/extensions/core/clipspace.js +++ b/web/extensions/core/clipspace.js @@ -4,13 +4,14 @@ import { ComfyApp } from "/scripts/app.js"; export class ClipspaceDialog extends ComfyDialog { static items = []; - static is_opened = false; // prevent redundant popup + static instance = null; - static registerButton(name, callback) { + static registerButton(name, contextPredicate, callback) { const item = $el("button", { type: "button", textContent: name, + contextPredicate: contextPredicate, onclick: callback }) @@ -18,59 +19,109 @@ export class ClipspaceDialog extends ComfyDialog { } static invalidatePreview() { - const img_preview = document.getElementById("clipspace_preview"); - img_preview.src = ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src; - img_preview.style.height = "100px"; + if(ComfyApp.clipspace && ComfyApp.clipspace.imgs && ComfyApp.clipspace.imgs.length > 0) { + const img_preview = document.getElementById("clipspace_preview"); + if(img_preview) { + img_preview.src = ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src; + img_preview.style.maxHeight = "100%"; + img_preview.style.maxWidth = "100%"; + } + } } + static invalidate() { + if(ClipspaceDialog.instance) { + const self = ClipspaceDialog.instance; + // allow reconstruct controls when copying from non-image to image content. + const children = $el("div.comfy-modal-content",[ + self.createImgSettings(), +// self.createImgPreview(), + ...self.createButtons() + ]); + + if(self.element) { + // update + self.element.removeChild(self.element.firstChild); + self.element.appendChild(children); + } + else { + // new + self.element = $el("div.comfy-modal", { parent: document.body }, [children,]); + } + + ClipspaceDialog.invalidatePreview(); + } + } + constructor() { super(); - this.element = - $el("div.comfy-modal", { parent: document.body }, - [$el("div.comfy-modal-content",[ - this.createImgSelector(), - this.createImgPreview(), - ...this.createButtons()]),] - ); } - createButtons() { + createButtons(self) { const buttons = []; for(let idx in ClipspaceDialog.items) { const item = ClipspaceDialog.items[idx]; - buttons.push(ClipspaceDialog.items[idx]); + if(!item.contextPredicate || item.contextPredicate()) + buttons.push(ClipspaceDialog.items[idx]); } buttons.push( $el("button", { type: "button", textContent: "Close", - onclick: () => { - ClipspaceDialog.is_opened = false; - this.close(); - } + onclick: () => { this.close(); } }) ); return buttons; } - createImgSelector() { - if(ComfyApp.clipspace.imgs != undefined) { - const combo_items = []; + createImgSettings() { + if(ComfyApp.clipspace.imgs) { + const combo_items = []; const imgs = ComfyApp.clipspace.imgs; for(let i=0; i < imgs.length; i++) { combo_items.push($el("option", {value:i}, [`${i}`])); } - const combo = $el("select", + const combo1 = $el("select", {id:"clipspace_img_selector", onchange:(event) => { ComfyApp.clipspace['selectedIndex'] = event.target.selectedIndex; ClipspaceDialog.invalidatePreview(); } }, combo_items); - return combo; + + const row1 = + $el("tr", {},[ + $el("td", {}, [$el("font", {color:"white"}, ["Select Image"])]), + $el("td", {}, [combo1]) + ]); + + + const combo2 = $el("select", + {id:"clipspace_img_paste_mode", onchange:(event) => { + ComfyApp.clipspace['img_paste_mode'] = event.target.value; + } }, + [ + $el("option", {value:'all'}, 'all'), + $el("option", {value:'selected'}, 'selected') + ]); + + const row2 = + $el("tr", {},[ + $el("td", {}, [$el("font", {color:"white"}, ["Paste Mode"])]), + $el("td", {}, [combo2]) + ]); + + const td = $el("td", {align:'center', width:'100px', height:'100px', colSpan:'2'}, + [ $el("img",{id:"clipspace_preview"},[]) ] + ); + + const row3 = + $el("tr", {}, [td]); + + return $el("table", {}, [row1, row2, row3]); } else { return []; @@ -78,7 +129,7 @@ export class ClipspaceDialog extends ComfyDialog { } createImgPreview() { - if(ComfyApp.clipspace.imgs != undefined) { + if(ComfyApp.clipspace.imgs) { return $el("img",{id:"clipspace_preview"}); } else @@ -86,10 +137,8 @@ export class ClipspaceDialog extends ComfyDialog { } show() { - ClipspaceDialog.is_opened = true; const img_preview = document.getElementById("clipspace_preview"); - img_preview.src = ComfyApp.clipspace.imgs[0].src; - img_preview.style.height = "100px"; + ClipspaceDialog.invalidate(); this.element.style.display = "block"; } @@ -100,13 +149,16 @@ app.registerExtension({ init(app) { app.openClipspace = function () { - if(!ClipspaceDialog.is_opened) { - let dlg = new ClipspaceDialog(app); - if(ComfyApp.clipspace) - dlg.show(); - else - app.ui.dialog.show("Clipspace is Empty!"); - } + if(!ClipspaceDialog.instance) { + ClipspaceDialog.instance = new ClipspaceDialog(app); + ComfyApp.clipspace_invalidate_handler = ClipspaceDialog.invalidate; + } + + if(ComfyApp.clipspace) { + ClipspaceDialog.instance.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 index 2896ce9b3..b3e170dde 100644 --- a/web/extensions/core/maskeditor.js +++ b/web/extensions/core/maskeditor.js @@ -42,7 +42,7 @@ async function uploadMask(filepath, formData) { ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']] = new Image(); ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src = `view?filename=${filepath.filename}&type=${filepath.type}`; - ComfyApp.clipspace.images = [filepath]; + ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']] = filepath; ClipspaceDialog.invalidatePreview(); } @@ -562,6 +562,7 @@ app.registerExtension({ dlg.show(); }; - ClipspaceDialog.registerButton("MaskEditor", callback); + const context_predicate = () => ComfyApp.clipspace && ComfyApp.clipspace.imgs && ComfyApp.clipspace.imgs.length > 0 + ClipspaceDialog.registerButton("MaskEditor", context_predicate, callback); } }); \ No newline at end of file diff --git a/web/scripts/app.js b/web/scripts/app.js index bf5768bd5..18d101e8e 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -25,6 +25,7 @@ export class ComfyApp { * @type {serialized node object} */ static clipspace = null; + static clipspace_invalidate_handler = null; constructor() { this.ui = new ComfyUI(this); @@ -164,8 +165,13 @@ export class ComfyApp { 'imgs': imgs, 'original_imgs': orig_imgs, 'images': this.images, - 'selectedIndex': 0 + 'selectedIndex': 0, + 'img_paste_mode': 'all' }; + + if(ComfyApp.clipspace_invalidate_handler) { + ComfyApp.clipspace_invalidate_handler(); + } } }); @@ -174,31 +180,52 @@ export class ComfyApp { { content: "Paste (Clipspace)", callback: () => { - if(ComfyApp.clipspace != null) { + if(ComfyApp.clipspace) { // image paste - if(ComfyApp.clipspace.imgs != undefined && this.imgs != undefined && this.widgets != null) { + if(ComfyApp.clipspace.imgs && this.imgs) { var filename = ""; if(this.images && ComfyApp.clipspace.images) { - this.images = ComfyApp.clipspace.images; + if(ComfyApp.clipspace['img_paste_mode'] == 'selected') { + app.nodeOutputs[this.id + ""].images = this.images = [ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']]]; + + } + else + app.nodeOutputs[this.id + ""].images = this.images = ComfyApp.clipspace.images; } - if(ComfyApp.clipspace.images != undefined) { - const clip_image = ComfyApp.clipspace.images[0]; + if(ComfyApp.clipspace.imgs) { + // deep-copy to cut link with clipspace + if(ComfyApp.clipspace['img_paste_mode'] == 'selected') { + const img = new Image(); + img.src = ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src; + this.imgs = [img]; + } + else { + const imgs = []; + for(let i=0; i obj.name === 'image'); if(index_in_clip >= 0) { filename = `${ComfyApp.clipspace.widgets[index_in_clip].value}`; } } - const index = this.widgets.findIndex(obj => obj.name === 'image'); - if(index >= 0 && filename != "" && ComfyApp.clipspace.imgs != undefined) { - this.imgs = ComfyApp.clipspace.imgs; - + // for Load Image node. + const index = this.widgets.findIndex(obj => obj.name === 'image'); + if(index >= 0 && filename != "") { this.widgets[index].value = filename; if(this.widgets_values != undefined) { this.widgets_values[index] = filename; @@ -207,7 +234,7 @@ export class ComfyApp { } // ensure render after update widget_value - if(ComfyApp.clipspace.widgets != null && this.widgets != null) { + if(ComfyApp.clipspace.widgets && this.widgets) { ComfyApp.clipspace.widgets.forEach(({ type, name, value }) => { const prop = Object.values(this.widgets).find(obj => obj.type === type && obj.name === name); if (prop && prop.type != 'button') { @@ -216,6 +243,8 @@ export class ComfyApp { }); } } + + app.graph.setDirtyCanvas(true); } } );