From 90bdb4d6571ea730a3bbdcbb0bfc64cd7bfd0fe1 Mon Sep 17 00:00:00 2001 From: Enrico Date: Tue, 15 Oct 2024 17:55:58 +0200 Subject: [PATCH] wip working preferences, wip passing image on hidden widget with serialize --- Compositor4.py | 13 +- web/compositor4.js | 38 +++++ web/compositor4_node.js | 82 ++++++---- web/compositor4preferences.js | 285 ++++++++++++++++++++++++++++++++++ web/index.html | 1 + web/init.js | 39 +---- web/toolbar4.js | 16 +- 7 files changed, 397 insertions(+), 77 deletions(-) create mode 100644 web/compositor4preferences.js diff --git a/Compositor4.py b/Compositor4.py index 0f7f09e..3e56163 100644 --- a/Compositor4.py +++ b/Compositor4.py @@ -43,6 +43,7 @@ class Compositor4: "hidden": { "extra_pnginfo": "EXTRA_PNGINFO", "node_id": "UNIQUE_ID", + "test":"STRING" }, } @@ -56,7 +57,8 @@ class Compositor4: node_id = kwargs.pop('node_id', None) - imageName = kwargs.get('imageName', "new.png") + imageName = kwargs.get('test', "new.png") + #compositionName = kwargs.get('test', "new.png") config = kwargs.get('config', "default") padding = config["padding"] @@ -92,15 +94,16 @@ class Compositor4: } # break and send a message to the gui as if it was "executed" below - detail = {"output": ui, "node": node_id} - PromptServer.instance.send_sync("compositor_init", detail) + # detail = {"output": ui, "node": node_id} + # PromptServer.instance.send_sync("compositor_init", detail) imageExists = folder_paths.exists_annotated_filepath(imageName) # block when config changed - if imageName == "new.png" or not imageExists or configChanged: + #if imageName == "new.png" or not imageExists or configChanged: + if not imageExists or configChanged: return { "ui": ui, - "result": (ExecutionBlocker(None), ExecutionBlocker(None)) + "result": (ExecutionBlocker(None), ExecutionBlocker(None)) } image_path = folder_paths.get_annotated_filepath(imageName) diff --git a/web/compositor4.js b/web/compositor4.js index f5d90f6..b8cbc0a 100644 --- a/web/compositor4.js +++ b/web/compositor4.js @@ -1,3 +1,4 @@ +import { api } from "../../scripts/api.js"; import { Toolbar, ICON_URLS, BUTTON_INDICES, fetchSVGIcon } from './toolbar4.js'; class Compositor4 { @@ -292,6 +293,7 @@ class Compositor4 { this.fabricCanvas.renderAll(); } + /** downloads the iamge */ downloadFile(content, filename) { let link = document.createElement("a"); @@ -317,6 +319,7 @@ class Compositor4 { } } + /** saves the image as base64*/ exportAsBase64() { return this.fabricCanvas.toDataURL({ format: "png", @@ -371,6 +374,41 @@ class Compositor4 { // Redraw the new images on the canvas this.setupImages(); } + + dataURLToBlob(dataURL){ + const parts = dataURL.split(','); + const mime = parts[0].match(/:(.*?);/)[1]; + const binary = atob(parts[1]); + const array = []; + for (let i = 0; i < binary.length; i++) { + array.push(binary.charCodeAt(i)); + } + return new Blob([new Uint8Array(array)], {type: mime}); +} + + // take #compositor3.js as example and make a version of uploadImage that takes the ouptupt of exportAsBase64and uploads it to the /upload/image endpoint, make the method async, keep the naming convention and the logic to hanle the subfolder + async uploadImage(dataURL){ + const b64 = this.dataURLToBlob(dataURL); + const UPLOAD_ENDPOINT = "/upload/image"; + + const name = `${+new Date()}.png`; + const file = new File([b64], name); + const body = new FormData(); + + body.append("image", file); + body.append("subfolder", "compositor"); + body.append("type", "temp"); + + const result = await api.fetchApi(UPLOAD_ENDPOINT, { + method: "POST", + body, + }) + + + return await result.json() + + } + } export default Compositor4; \ No newline at end of file diff --git a/web/compositor4_node.js b/web/compositor4_node.js index 196a756..9b6d6d8 100644 --- a/web/compositor4_node.js +++ b/web/compositor4_node.js @@ -1,6 +1,8 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; - +import Compositor4 from "./compositor4.js"; +import { fabric } from "./fabric.js"; +import {PREFERENCES, addCompositorPreferences, getPreferences} from "./compositor4preferences.js"; import Init from "./init.js"; const COMPOSITOR = Symbol(); @@ -21,23 +23,26 @@ function getCompositorWidget(node, widgetName) { app.registerExtension({ name: "Comfy.Compositor4", -// async getCustomWidgets(app) { -// return { -// async COMPOSITOR(node, inputName, inputData, app) { - -// const container = document.createElement("div"); -// await this.initEditor(container); + // async getCustomWidgets(app) { + // return { + // async COMPOSITOR(node, inputName, inputData, app) { -// /** -// * NOTE: hideOnZoom:false FIXES not being able to take screenshot and disappearing on zoom out -// * but creates some inconsistencies as lines get too small to be rendered properly -// */ -// return {widget: node.addDOMWidget(inputName, "COMPOSITOR4", container, {hideOnZoom: false})}; -// }, -// }; -// }, + // const container = document.createElement("div"); + // await this.initEditor(container); + + // /** + // * NOTE: hideOnZoom:false FIXES not being able to take screenshot and disappearing on zoom out + // * but creates some inconsistencies as lines get too small to be rendered properly + // */ + // return {widget: node.addDOMWidget(inputName, "COMPOSITOR4", container, {hideOnZoom: false})}; + // }, + // }; + // }, async setup(app) { + + addCompositorPreferences(PREFERENCES); + function executingMessageHandler(event) {} function executedMessageHandler(event, a, b) {} @@ -82,31 +87,40 @@ app.registerExtension({ el.style.backgroundColor = "transparent"; el.width = 800; el.height = 600; - el.id="compositor4container"; - - - - + el.id = "compositor4container"; + el.value = "foobar.jpg"; + + const imageNameWidget = getCompositorWidget(node, "imageName"); + node.editorWidget = node.addDOMWidget("test", "test", el, { - serialize: false, + serialize: true, hideOnZoom: false, + resizable: false, + + getValue() { + console.log("getValue", el.value); + return el.value; + }, + setValue(v2) { + // imageNameWidget.value = v2; + console.log("setValue", v2); + el.value = v2; + }, }); - + + // const originalSerializeValue = node.editorWidget.serializeValue; + // node.editorWidget.serializeValue = (e) => { + // console.log("serializeValue",e); + // //return el.value; + // }; const init = new Init(); - const compositor = await init.initEditor(el); - console.log("compositor", compositor); - node.setSize(compositor.getWidgetDimensions()); + const preferences = getPreferences() + const editor = await init.initEditor(el, preferences); + console.log("editor", editor); + node.setSize(editor.getWidgetDimensions()); node.setDirtyCanvas(true, true); - } - - - - + }, }); - - - - diff --git a/web/compositor4preferences.js b/web/compositor4preferences.js new file mode 100644 index 0000000..96e370d --- /dev/null +++ b/web/compositor4preferences.js @@ -0,0 +1,285 @@ +import { app } from "../../scripts/app.js"; + +const PREFERENCES = { + composition: { + fill: "#444444", // Dark gray color for the rectangle + stroke: "green", // Green border + strokeWidth: 1, + }, + toolbar: { + fill: "#666666", + height: 40, + position: { left: 0, top: 0 }, + buttonSpacing: 4, + iconSize: 40, // Set the icon size to 30 + }, + fabricCanvas: { + backgroundColor: "#555555", // Darker gray color for the canvas background + }, + equalizeHeight: { + enabled: true, + }, + snapToGrid: { + gridSize: 20, + enabled: false, + isGridVisible: false, + }, + ignoreTransparentPixels: { + enabled: false, + }, + erosColor: "magenta", // Add eros color preference + activeBorderColor: "magenta", // Border color when button is active + inactiveBorderColor: "black", // Border color when button is inactive + toggledBorderColor: "orange", // Border color when button is toggled + standalone: false, // whether we are testing this in index.html or in the comfy editor +}; + +function addCompositorPreferences(preferences) { + app.ui.settings.addSetting({ + id: "Compositor4.Composition.FILL", + name: "Composition Fill", + tooltip: "Fill color for the composition rectangle", + type: "text", + defaultValue: preferences.composition.fill, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.Composition.STROKE", + name: "Composition Stroke", + tooltip: "Stroke color for the composition rectangle", + type: "text", + defaultValue: preferences.composition.stroke, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.Composition.STROKE_WIDTH", + name: "Composition Stroke Width", + tooltip: "Stroke width for the composition rectangle", + type: "number", + defaultValue: preferences.composition.strokeWidth, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.Toolbar.FILL", + name: "Toolbar Fill", + tooltip: "Fill color for the toolbar", + type: "text", + defaultValue: preferences.toolbar.fill, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.Toolbar.HEIGHT", + name: "Toolbar Height", + tooltip: "Height of the toolbar", + type: "number", + defaultValue: preferences.toolbar.height, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.Toolbar.POSITION", + name: "Toolbar Position", + tooltip: "Position of the toolbar", + type: "object", + defaultValue: preferences.toolbar.position, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.Toolbar.BUTTON_SPACING", + name: "Toolbar Button Spacing", + tooltip: "Spacing between toolbar buttons", + type: "number", + defaultValue: preferences.toolbar.buttonSpacing, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.Toolbar.ICON_SIZE", + name: "Toolbar Icon Size", + tooltip: "Size of the toolbar icons", + type: "number", + defaultValue: preferences.toolbar.iconSize, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.FabricCanvas.BACKGROUND_COLOR", + name: "Fabric Canvas Background Color", + tooltip: "Background color for the fabric canvas", + type: "text", + defaultValue: preferences.fabricCanvas.backgroundColor, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.EqualizeHeight.ENABLED", + name: "Equalize Height Enabled", + tooltip: "Enable or disable equalize height", + type: "boolean", + defaultValue: preferences.equalizeHeight.enabled, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.SnapToGrid.GRID_SIZE", + name: "Snap to Grid Size", + tooltip: "Grid size for snap to grid", + type: "number", + defaultValue: preferences.snapToGrid.gridSize, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.SnapToGrid.ENABLED", + name: "Snap to Grid Enabled", + tooltip: "Enable or disable snap to grid", + type: "boolean", + defaultValue: preferences.snapToGrid.enabled, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.SnapToGrid.IS_GRID_VISIBLE", + name: "Snap to Grid Visibility", + tooltip: "Visibility of the snap to grid", + type: "boolean", + defaultValue: preferences.snapToGrid.isGridVisible, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.IgnoreTransparentPixels.ENABLED", + name: "Ignore Transparent Pixels Enabled", + tooltip: "Enable or disable ignoring transparent pixels", + type: "boolean", + defaultValue: preferences.ignoreTransparentPixels.enabled, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.ErosColor", + name: "Eros Color", + tooltip: "Color for eros", + type: "text", + defaultValue: preferences.erosColor, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.ActiveBorderColor", + name: "Active Border Color", + tooltip: "Border color when button is active", + type: "text", + defaultValue: preferences.activeBorderColor, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.InactiveBorderColor", + name: "Inactive Border Color", + tooltip: "Border color when button is inactive", + type: "text", + defaultValue: preferences.inactiveBorderColor, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.ToggledBorderColor", + name: "Toggled Border Color", + tooltip: "Border color when button is toggled", + type: "text", + defaultValue: preferences.toggledBorderColor, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); + + app.ui.settings.addSetting({ + id: "Compositor4.Standalone", + name: "Standalone Mode", + tooltip: "Whether we are testing this in index.html or in the comfy editor", + type: "boolean", + defaultValue: preferences.standalone, + onChange: (newVal, oldVal) => { + // Handle change + }, + }); +} + +function getPreferences() { + return { + composition: { + fill: app.ui.settings.getSettingValue("Compositor4.Composition.FILL", "#444444"), + stroke: app.ui.settings.getSettingValue("Compositor4.Composition.STROKE", "green"), + strokeWidth: app.ui.settings.getSettingValue("Compositor4.Composition.STROKE_WIDTH", 1), + }, + toolbar: { + fill: app.ui.settings.getSettingValue("Compositor4.Toolbar.FILL", "#666666"), + height: app.ui.settings.getSettingValue("Compositor4.Toolbar.HEIGHT", 40), + position: app.ui.settings.getSettingValue("Compositor4.Toolbar.POSITION", { left: 0, top: 0 }), + buttonSpacing: app.ui.settings.getSettingValue("Compositor4.Toolbar.BUTTON_SPACING", 4), + iconSize: app.ui.settings.getSettingValue("Compositor4.Toolbar.ICON_SIZE", 40), + }, + fabricCanvas: { + backgroundColor: app.ui.settings.getSettingValue("Compositor4.FabricCanvas.BACKGROUND_COLOR", "#555555"), + }, + equalizeHeight: { + enabled: app.ui.settings.getSettingValue("Compositor4.EqualizeHeight.ENABLED", true), + }, + snapToGrid: { + gridSize: app.ui.settings.getSettingValue("Compositor4.SnapToGrid.GRID_SIZE", 20), + enabled: app.ui.settings.getSettingValue("Compositor4.SnapToGrid.ENABLED", false), + isGridVisible: app.ui.settings.getSettingValue("Compositor4.SnapToGrid.IS_GRID_VISIBLE", false), + }, + ignoreTransparentPixels: { + enabled: app.ui.settings.getSettingValue("Compositor4.IgnoreTransparentPixels.ENABLED", false), + }, + erosColor: app.ui.settings.getSettingValue("Compositor4.ErosColor", "magenta"), + activeBorderColor: app.ui.settings.getSettingValue("Compositor4.ActiveBorderColor", "magenta"), + inactiveBorderColor: app.ui.settings.getSettingValue("Compositor4.InactiveBorderColor", "black"), + toggledBorderColor: app.ui.settings.getSettingValue("Compositor4.ToggledBorderColor", "orange"), + standalone: app.ui.settings.getSettingValue("Compositor4.Standalone", false), + }; +} + +// Call the function to add settings +export { addCompositorPreferences, PREFERENCES, getPreferences}; diff --git a/web/index.html b/web/index.html index e839bae..18c315d 100644 --- a/web/index.html +++ b/web/index.html @@ -45,6 +45,7 @@ activeBorderColor: "magenta", // Border color when button is active inactiveBorderColor: "black", // Border color when button is inactive toggledBorderColor: "orange", // Border color when button is toggled + standalone:true, }; const config = JSON.stringify({ diff --git a/web/init.js b/web/init.js index 0ea7f04..0e73e0c 100644 --- a/web/init.js +++ b/web/init.js @@ -1,45 +1,12 @@ import Compositor4 from "./compositor4.js"; -import { fabric } from "./fabric.js"; + class Init { - async initEditor(container) { + async initEditor(container, preferences) { // const query = 'cats'; // Change this to your desired search query const query = "matisse"; // Change this to your desired search query try { const images = await this.fetchArtInstituteImages(query, 6); - const PREFERENCES = { - composition: { - fill: "#444444", // Dark gray color for the rectangle - stroke: "green", // Green border - strokeWidth: 1, - }, - toolbar: { - fill: "#666666", - height: 40, - position: { left: 0, top: 0 }, - buttonSpacing: 4, - iconSize: 40, // Set the icon size to 30 - }, - fabricCanvas: { - backgroundColor: "#555555", // Darker gray color for the canvas background - }, - equalizeHeight: { - enabled: true, - }, - snapToGrid: { - gridSize: 20, - enabled: false, - isGridVisible: false, - }, - ignoreTransparentPixels: { - enabled: false, - }, - erosColor: "magenta", // Add eros color preference - activeBorderColor: "magenta", // Border color when button is active - inactiveBorderColor: "black", // Border color when button is inactive - toggledBorderColor: "orange", // Border color when button is toggled - }; - const config = JSON.stringify({ width: 800, height: 600, @@ -52,7 +19,7 @@ class Init { }); // const container = document.createElement("div"); // container.id = "compositor4"; - const compositor = new Compositor4(container, config, PREFERENCES); + const compositor = new Compositor4(container, config, preferences); // trigger the executed event const event = new CustomEvent("executed", { detail: config }); document.dispatchEvent(event); diff --git a/web/toolbar4.js b/web/toolbar4.js index f95935f..14e1e2b 100644 --- a/web/toolbar4.js +++ b/web/toolbar4.js @@ -1,3 +1,5 @@ + + export const ICON_URLS = { loadPreset: "https://icons.getbootstrap.com/assets/icons/file-earmark-arrow-up.svg", savePreset: "https://icons.getbootstrap.com/assets/icons/file-earmark-arrow-down.svg", @@ -203,10 +205,20 @@ export class Toolbar { } async addExportButton() { - const onClick = () => { + const onClick = async() => { console.log("Exporting image..."); const base64Image = this.compositor.exportAsBase64(); - this.compositor.downloadFile(base64Image, `${this.compositor.seed}.png`); + // + if(this.preferences.standalone){ + this.compositor.downloadFile(base64Image, `${this.compositor.seed}.png`); + + }else{ + const result = await this.compositor.uploadImage(base64Image); + // + this.compositor.containerEl.value = `compositor/${result.name} [temp]`; + console.log(result); + } + }; await this.addToolbarButton(ICON_URLS.export, onClick, BUTTON_INDICES.indexOf("export"));