From defe95c0e80307349d6032fd523e481b7ce1b954 Mon Sep 17 00:00:00 2001
From: pingren <5123601+Pingren@users.noreply.github.com>
Date: Thu, 16 Nov 2023 15:40:33 +0800
Subject: [PATCH 01/21] refactor: rename comfyui-share
---
js/{comfyui-share.js => comfyui-share-common.js} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename js/{comfyui-share.js => comfyui-share-common.js} (100%)
diff --git a/js/comfyui-share.js b/js/comfyui-share-common.js
similarity index 100%
rename from js/comfyui-share.js
rename to js/comfyui-share-common.js
From f07fddcdad06306593ff908fff1402dbddb3b69e Mon Sep 17 00:00:00 2001
From: pingren <5123601+Pingren@users.noreply.github.com>
Date: Thu, 16 Nov 2023 16:14:10 +0800
Subject: [PATCH 02/21] feat: add ShareDialogChooser and OpenArtShareDialog
basic UI
---
js/comfyui-manager.js | 12 ++-
js/comfyui-share-common.js | 192 ++++++++++++++++++++++++++++++++++++
js/comfyui-share-openart.js | 124 +++++++++++++++++++++++
3 files changed, 324 insertions(+), 4 deletions(-)
create mode 100644 js/comfyui-share-openart.js
diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js
index 914dec96..492fd403 100644
--- a/js/comfyui-manager.js
+++ b/js/comfyui-manager.js
@@ -1,7 +1,7 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js";
-import { ShareDialog, SUPPORTED_OUTPUT_NODE_TYPES, getPotentialOutputsAndOutputNodes } from "./comfyui-share.js";
+import { ShareDialog, SUPPORTED_OUTPUT_NODE_TYPES, getPotentialOutputsAndOutputNodes, ShareDialogChooser } from "./comfyui-share-common.js";
import { CustomNodesInstaller } from "./custom-nodes-downloader.js";
import { AlternativesInstaller } from "./a1111-alter-downloader.js";
import { SnapshotManager } from "./snapshot.js";
@@ -586,9 +586,13 @@ app.registerExtension({
const shareButton = document.createElement("button");
shareButton.textContent = "Share";
shareButton.onclick = () => {
- if (!ShareDialog.instance) {
- ShareDialog.instance = new ShareDialog();
+
+ if(!ShareDialogChooser.instance) {
+ ShareDialogChooser.instance = new ShareDialogChooser();
}
+ // TODO: DEV ONLY, remove this line
+ ShareDialogChooser.instance.show({ potential_outputs : [], potential_output_nodes: [] });
+ return
app.graphToPrompt().then(prompt => {
// console.log({ prompt })
@@ -608,7 +612,7 @@ app.registerExtension({
return;
}
- ShareDialog.instance.show({ potential_outputs, potential_output_nodes });
+ ShareDialogChooser.instance.show({ potential_outputs, potential_output_nodes });
});
}
// make the background color a gradient of blue to green
diff --git a/js/comfyui-share-common.js b/js/comfyui-share-common.js
index 9ea8aef0..48bf794a 100644
--- a/js/comfyui-share-common.js
+++ b/js/comfyui-share-common.js
@@ -1,6 +1,7 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js";
+import { OpenArtShareDialog } from "./comfyui-share-openart.js";
export const SUPPORTED_OUTPUT_NODE_TYPES = [
"PreviewImage",
@@ -149,7 +150,198 @@ export function parseURLPath(urlPath) {
// Return the object with the parsed parameters
return parsedParams;
}
+export class ShareDialogChooser extends ComfyDialog {
+ static instance = null;
+ constructor() {
+ super();
+ this.element = $el("div.comfy-modal", {
+ parent: document.body, style: {
+ 'overflow-y': "auto",
+ }
+ },
+ [$el("div.comfy-modal-content",
+ {},
+ [...this.createButtons()]),
+ ]);
+
+ }
+ createButtons() {
+ const handleShowOpenArtShareDialog = () => {
+ if (!OpenArtShareDialog.instance) {
+ OpenArtShareDialog.instance = new OpenArtShareDialog();
+ }
+ OpenArtShareDialog.instance.show({ potential_outputs: this.potential_output_nodes, potential_output_nodes: this.potential_output_nodes })
+ this.close();
+ }
+
+ const handleShowShareDialog = () => {
+ if (!ShareDialog.instance) {
+ ShareDialog.instance = new ShareDialog();
+ }
+ ShareDialog.instance.show({ potential_outputs: this.potential_output_nodes, potential_output_nodes: this.potential_output_nodes })
+ this.close();
+ }
+ const buttons = [
+ {
+ key: "openart",
+ textContent: "OpenArt AI",
+ website: "https://openart.ai/workflows/",
+ description: "Best place to share your workflow and art.",
+ onclick: handleShowOpenArtShareDialog
+ },
+ {
+ key: "matrix",
+ textContent: "Matrix Server",
+ website: "https://app.element.io/#/room/%23comfyui_space%3Amatrix.org",
+ description: "Share your art on the official ComfyUI matrix server.",
+ onclick: handleShowShareDialog
+ },
+ {
+ key: "comfyworkflows",
+ textContent: "ComfyWorkflows",
+ website: "https://comfyworkflows.com",
+ description: "Share ComfyUI art: Download & drop any image into ComfyUI to load its workflow.",
+ onclick: handleShowShareDialog
+ },
+ ];
+
+ function createShareButtonsWithDescriptions() {
+ // Responsive container
+ const container = $el("div", {
+ style: {
+ display: "flex",
+ 'flex-wrap': 'wrap',
+ 'justify-content': 'space-around',
+ 'padding': '20px',
+ }
+ });
+
+ buttons.forEach(b => {
+ const button = $el("button", {
+ type: "button",
+ textContent: b.textContent,
+ onclick: b.onclick,
+ style: {
+ 'width': '25%',
+ 'minWidth': '200px',
+ 'background-color': b.backgroundColor || '',
+ 'border-radius': '5px',
+ 'cursor': 'pointer',
+ 'padding': '5px 5px',
+ 'margin-bottom': '5px',
+ 'transition': 'background-color 0.3s',
+ }
+ });
+ button.addEventListener('mouseover', () => {
+ button.style.backgroundColor = '#007BFF'; // Change color on hover
+ });
+ button.addEventListener('mouseout', () => {
+ button.style.backgroundColor = b.backgroundColor || '';
+ });
+
+ const description = $el("p", {
+ textContent: b.description,
+ style: {
+ 'text-align': 'center',
+ color: 'white',
+ 'font-style': 'italic',
+ 'font-size': '14px',
+ 'margin-bottom': '10px',
+ },
+ });
+
+ const websiteLink = $el("a", {
+ textContent: "π Website",
+ href: b.website,
+ target: "_blank",
+ style: {
+ color: 'white',
+ 'margin-left': '10px',
+ 'font-size': '12px',
+ 'text-decoration': 'none',
+ 'align-self': 'center',
+ },
+ });
+
+ // Add highlight to the website link
+ websiteLink.addEventListener('mouseover', () => {
+ websiteLink.style.opacity = '0.7';
+ });
+
+ websiteLink.addEventListener('mouseout', () => {
+ websiteLink.style.opacity = '1';
+ });
+
+ const buttonLinkContainer = $el("div", {
+ style: {
+ display: 'flex',
+ 'align-items': 'center',
+ 'margin-bottom': '10px',
+ }
+ }, [button, websiteLink]);
+
+ const column = $el("div", {
+ style: {
+ 'flex-basis': '100%',
+ 'margin': '10px',
+ 'padding': '20px',
+ 'border': '1px solid #ddd',
+ 'border-radius': '5px',
+ 'box-shadow': '0 2px 4px rgba(0, 0, 0, 0.1)',
+ }
+ }, [buttonLinkContainer, description]);
+
+ container.appendChild(column);
+ });
+
+ return container;
+ }
+
+
+ return [
+ $el("tr.td", { width: "100%" }, [
+ $el("font", { size: 6, color: "white" }, [`Where would you like to share your workflow?`]),
+ ]),
+ $el("br", {}, []),
+
+ $el("div.cm-menu-container", {
+ id: "comfyui-share-container"
+ }, [
+ $el("div.cm-menu-column", [
+ $el("p", {
+ size: 3, color: "white", style: {
+ color: 'white'
+ }
+ }),
+ createShareButtonsWithDescriptions(),
+ $el("br", {}, []),
+ ]),
+ ]),
+ $el("div.cm-menu-container", {
+ id: "comfyui-share-container"
+ }, [
+ $el("button", {
+ type: "button",
+ style: {
+ margin: "0 25px",
+ width: "100%",
+ },
+ textContent: "Close",
+ onclick: () => {
+ this.close()
+ }
+ }),
+ $el("br", {}, []),
+ ]),
+ ];
+ }
+ show({ potential_outputs, potential_output_nodes }) {
+ this.element.style.display = "block";
+ this.potential_outputs = potential_outputs;
+ this.potential_output_nodes = potential_output_nodes;
+ }
+}
export class ShareDialog extends ComfyDialog {
static instance = null;
static matrix_auth = { homeserver: "matrix.org", username: "", password: "" };
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
new file mode 100644
index 00000000..93e3130e
--- /dev/null
+++ b/js/comfyui-share-openart.js
@@ -0,0 +1,124 @@
+import { app } from "../../scripts/app.js";
+import { api } from "../../scripts/api.js"
+import { ComfyDialog, $el } from "../../scripts/ui.js";
+
+const LOCAL_STORAGE_KEY = "openart_comfy_workflow_key";
+
+export class OpenArtShareDialog extends ComfyDialog {
+ static instance = null;
+
+ constructor() {
+ super();
+ this.element = $el("div.comfy-modal", {
+ parent: document.body, style: {
+ 'overflow-y': "auto",
+ }
+ },
+ [$el("div.comfy-modal-content",
+ {},
+ [...this.createButtons()]),
+ ]);
+ this.selectedOutputIndex = 0;
+ }
+ readKeyFromLocalStorage() {
+ return localStorage.getItem(LOCAL_STORAGE_KEY) || '';
+ }
+ saveKeyToLocalStorage(value) {
+ localStorage.setItem(LOCAL_STORAGE_KEY, value);
+ }
+ createButtons() {
+ const sectionStyle = {
+ marginBottom: '20px',
+ padding: '15px',
+ borderRadius: '8px',
+ boxShadow: '0 2px 4px rgba(0, 0, 0, 0.05)'
+ };
+
+ const inputStyle = {
+ display: 'block',
+ minWidth: '500px',
+ width: '100%',
+ padding: '10px',
+ margin: '10px 0',
+ borderRadius: '4px',
+ border: '1px solid #ddd',
+ boxSizing: 'border-box'
+ };
+
+ const headerLabelStyle = {
+ color: "#f8f8f8",
+ display: 'block',
+ marginBottom: '15px',
+ fontWeight: 'bold',
+ textDecoration: 'none',
+ fontSize: '20px',
+ };
+
+ const labelStyle = {
+ color: "#f8f8f8",
+ display: 'block',
+ marginBottom: '5px',
+ fontWeight: 'bold',
+ textDecoration: 'none',
+ };
+
+ const buttonStyle = {
+ padding: '10px 80px',
+ margin: '10px 5px',
+ borderRadius: '4px',
+ border: 'none',
+ cursor: 'pointer',
+ color: '#fff',
+ backgroundColor: '#007bff'
+ };
+
+ this.keyInput = $el("input", { type: 'text', placeholder: "Share Your key", style: inputStyle })
+ // Account Section
+ const AccountSection = $el("div", { style: sectionStyle }, [
+ $el("a", { style: headerLabelStyle, href: "https://openart.ai/workflows" }, ["Check out 1000+ workflows others have uploaded."]),
+ $el("a", { style: headerLabelStyle, href: "https://openart.ai/workflows" }, ["You can get OpenArt key at https://openart.ai/"]),
+ $el("label", { style: labelStyle }, ["OpenArt API Key"]),
+ this.keyInput,
+ ]);
+
+ // Additional Inputs Section
+ const additionalInputsSection = $el("div", { style: sectionStyle }, [
+ $el("label", { style: labelStyle }, ["Details"]),
+ $el("input", { type: "text", placeholder: "Title (required)", style: inputStyle }),
+ $el("textarea", { placeholder: "Description (optional)", style: {
+ ...inputStyle,
+ minHeight: '100px',
+ } }),
+ ]);
+
+ // Share and Close Buttons
+ const buttonsSection = $el("div", { style: { textAlign: 'right', marginTop: '20px', display: 'flex', justifyContent: 'space-between' } }, [
+ $el("button", { type: "button", textContent: "Close", style: {
+ ...buttonStyle,
+ backgroundColor: undefined
+ }, onclick: () => { this.close(); } }),
+ $el("button", { type: "submit", textContent: "Share", style: buttonStyle, onclick: () => { this.share(); } }),
+ ]);
+
+ // Final Message Section
+ const finalMessage = $el("div", { style: { color: "white", textAlign: "center", padding: "10px" } }, []);
+ // Composing the full layout
+ const layout = [
+ AccountSection,
+ additionalInputsSection,
+ buttonsSection,
+ finalMessage,
+ ];
+
+ return layout;
+ }
+ async share() {
+ this.saveKeyToLocalStorage(this.keyInput.value);
+ }
+ show({ potential_outputs, potential_output_nodes }) {
+ this.element.style.display = "block";
+ // read key from local storage and set it to the input
+ const key = this.readKeyFromLocalStorage();
+ this.keyInput.value = key;
+ }
+}
\ No newline at end of file
From 06307b75b319132d276b14224c4e4513ffd691a7 Mon Sep 17 00:00:00 2001
From: pingren <5123601+Pingren@users.noreply.github.com>
Date: Thu, 16 Nov 2023 17:09:01 +0800
Subject: [PATCH 03/21] feat: add upload Image section
---
js/comfyui-share-openart.js | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index 93e3130e..fdb0d536 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -28,7 +28,7 @@ export class OpenArtShareDialog extends ComfyDialog {
}
createButtons() {
const sectionStyle = {
- marginBottom: '20px',
+ marginBottom: '10px',
padding: '15px',
borderRadius: '8px',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.05)'
@@ -57,7 +57,7 @@ export class OpenArtShareDialog extends ComfyDialog {
const labelStyle = {
color: "#f8f8f8",
display: 'block',
- marginBottom: '5px',
+ margin: '10px 0',
fontWeight: 'bold',
textDecoration: 'none',
};
@@ -71,19 +71,41 @@ export class OpenArtShareDialog extends ComfyDialog {
color: '#fff',
backgroundColor: '#007bff'
};
+
+ // upload images input
+ this.uploadImagesInput = $el("input", { type: 'file', multiple: false, style: inputStyle })
- this.keyInput = $el("input", { type: 'text', placeholder: "Share Your key", style: inputStyle })
+ this.uploadImagesInput.addEventListener('change', async (e) => {
+ const file = e.target.files[0];
+ if (!file) {
+ return;
+ }
+ const reader = new FileReader();
+ reader.onload = async (e) => {
+ const imgData = e.target.result;
+ this.previewImage.src = imgData;
+ };
+ reader.readAsDataURL(file);
+ });
+
+ // preview image
+ this.previewImage = $el("img", { src: "", style: { maxWidth: '100%', maxHeight: '100px' } });
+
+ this.keyInput = $el("input", { type: 'text', placeholder: "Copy & paste your API key", style: inputStyle })
// Account Section
const AccountSection = $el("div", { style: sectionStyle }, [
$el("a", { style: headerLabelStyle, href: "https://openart.ai/workflows" }, ["Check out 1000+ workflows others have uploaded."]),
- $el("a", { style: headerLabelStyle, href: "https://openart.ai/workflows" }, ["You can get OpenArt key at https://openart.ai/"]),
+ $el("a", { style: headerLabelStyle, href: "https://openart.ai/workflows" }, ["You can get API key at https://openart.ai/"]),
$el("label", { style: labelStyle }, ["OpenArt API Key"]),
this.keyInput,
]);
// Additional Inputs Section
const additionalInputsSection = $el("div", { style: sectionStyle }, [
- $el("label", { style: labelStyle }, ["Details"]),
+ $el("label", { style: labelStyle }, ["Image/Thumbnail (Required)"]),
+ this.uploadImagesInput,
+ this.previewImage,
+ $el("label", { style: labelStyle }, ["Workflow Information"]),
$el("input", { type: "text", placeholder: "Title (required)", style: inputStyle }),
$el("textarea", { placeholder: "Description (optional)", style: {
...inputStyle,
From bc6083647aa634b36d76569ce2043885853d6abe Mon Sep 17 00:00:00 2001
From: pingren <5123601+Pingren@users.noreply.github.com>
Date: Fri, 17 Nov 2023 15:32:30 +0800
Subject: [PATCH 04/21] feat: updated share UI
---
js/comfyui-share-openart.js | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index fdb0d536..68401ad5 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -3,13 +3,27 @@ import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js";
const LOCAL_STORAGE_KEY = "openart_comfy_workflow_key";
+const DEFAULT_HOMEPAGE_URL = "https://openart.ai/workflows/dev?developer=true";
+
+const style = `
+.openart-share-dialog a {
+ color: #f8f8f8;
+}
+.openart-share-dialog a:hover {
+ color: #007bff;
+}
+`;
export class OpenArtShareDialog extends ComfyDialog {
static instance = null;
constructor() {
super();
- this.element = $el("div.comfy-modal", {
+ $el("style", {
+ textContent: style,
+ parent: document.head,
+ });
+ this.element = $el("div.comfy-modal.openart-share-dialog", {
parent: document.body, style: {
'overflow-y': "auto",
}
@@ -45,12 +59,10 @@ export class OpenArtShareDialog extends ComfyDialog {
boxSizing: 'border-box'
};
- const headerLabelStyle = {
- color: "#f8f8f8",
+ const hyperLinkStyle = {
display: 'block',
marginBottom: '15px',
fontWeight: 'bold',
- textDecoration: 'none',
fontSize: '20px',
};
@@ -94,8 +106,8 @@ export class OpenArtShareDialog extends ComfyDialog {
this.keyInput = $el("input", { type: 'text', placeholder: "Copy & paste your API key", style: inputStyle })
// Account Section
const AccountSection = $el("div", { style: sectionStyle }, [
- $el("a", { style: headerLabelStyle, href: "https://openart.ai/workflows" }, ["Check out 1000+ workflows others have uploaded."]),
- $el("a", { style: headerLabelStyle, href: "https://openart.ai/workflows" }, ["You can get API key at https://openart.ai/"]),
+ $el("a", { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL }, ["Check out 1000+ workflows others have uploaded."]),
+ $el("a", { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL }, ["You can get API key at here."]),
$el("label", { style: labelStyle }, ["OpenArt API Key"]),
this.keyInput,
]);
@@ -106,7 +118,7 @@ export class OpenArtShareDialog extends ComfyDialog {
this.uploadImagesInput,
this.previewImage,
$el("label", { style: labelStyle }, ["Workflow Information"]),
- $el("input", { type: "text", placeholder: "Title (required)", style: inputStyle }),
+ $el("input", { type: "text", placeholder: "Name (required)", style: inputStyle }),
$el("textarea", { placeholder: "Description (optional)", style: {
...inputStyle,
minHeight: '100px',
From d13a6e4b2a308a6999a2106a75d044478ef242d9 Mon Sep 17 00:00:00 2001
From: pingren <5123601+Pingren@users.noreply.github.com>
Date: Fri, 17 Nov 2023 17:56:57 +0800
Subject: [PATCH 05/21] feat(WIP): share workflow
---
js/comfyui-share-openart.js | 440 +++++++++++++++++++++++++-----------
1 file changed, 311 insertions(+), 129 deletions(-)
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index 68401ad5..0aac2000 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -1,10 +1,13 @@
import { app } from "../../scripts/app.js";
-import { api } from "../../scripts/api.js"
+import { api } from "../../scripts/api.js";
import { ComfyDialog, $el } from "../../scripts/ui.js";
const LOCAL_STORAGE_KEY = "openart_comfy_workflow_key";
const DEFAULT_HOMEPAGE_URL = "https://openart.ai/workflows/dev?developer=true";
+// const API_ENDPOINT = "https://openart.ai/api";
+const API_ENDPOINT = "http://localhost:8080/api";
+
const style = `
.openart-share-dialog a {
color: #f8f8f8;
@@ -15,144 +18,323 @@ const style = `
`;
export class OpenArtShareDialog extends ComfyDialog {
- static instance = null;
+ static instance = null;
- constructor() {
- super();
- $el("style", {
- textContent: style,
- parent: document.head,
- });
- this.element = $el("div.comfy-modal.openart-share-dialog", {
- parent: document.body, style: {
- 'overflow-y': "auto",
- }
- },
- [$el("div.comfy-modal-content",
- {},
- [...this.createButtons()]),
- ]);
- this.selectedOutputIndex = 0;
- }
- readKeyFromLocalStorage() {
- return localStorage.getItem(LOCAL_STORAGE_KEY) || '';
- }
- saveKeyToLocalStorage(value) {
- localStorage.setItem(LOCAL_STORAGE_KEY, value);
- }
- createButtons() {
- const sectionStyle = {
- marginBottom: '10px',
- padding: '15px',
- borderRadius: '8px',
- boxShadow: '0 2px 4px rgba(0, 0, 0, 0.05)'
- };
+ constructor() {
+ super();
+ $el("style", {
+ textContent: style,
+ parent: document.head,
+ });
+ this.element = $el(
+ "div.comfy-modal.openart-share-dialog",
+ {
+ parent: document.body,
+ style: {
+ "overflow-y": "auto",
+ },
+ },
+ [$el("div.comfy-modal-content", {}, [...this.createButtons()])]
+ );
+ this.selectedOutputIndex = 0;
+ this.uploadedImages = [];
+ }
+ readKeyFromLocalStorage() {
+ return localStorage.getItem(LOCAL_STORAGE_KEY) || "";
+ }
+ saveKeyToLocalStorage(value) {
+ localStorage.setItem(LOCAL_STORAGE_KEY, value);
+ }
+ createButtons() {
+ const sectionStyle = {
+ marginBottom: "10px",
+ padding: "15px",
+ borderRadius: "8px",
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)",
+ };
- const inputStyle = {
- display: 'block',
- minWidth: '500px',
- width: '100%',
- padding: '10px',
- margin: '10px 0',
- borderRadius: '4px',
- border: '1px solid #ddd',
- boxSizing: 'border-box'
- };
+ const inputStyle = {
+ display: "block",
+ minWidth: "500px",
+ width: "100%",
+ padding: "10px",
+ margin: "10px 0",
+ borderRadius: "4px",
+ border: "1px solid #ddd",
+ boxSizing: "border-box",
+ };
- const hyperLinkStyle = {
- display: 'block',
- marginBottom: '15px',
- fontWeight: 'bold',
- fontSize: '20px',
- };
+ const hyperLinkStyle = {
+ display: "block",
+ marginBottom: "15px",
+ fontWeight: "bold",
+ fontSize: "20px",
+ };
- const labelStyle = {
- color: "#f8f8f8",
- display: 'block',
- margin: '10px 0',
- fontWeight: 'bold',
- textDecoration: 'none',
- };
+ const labelStyle = {
+ color: "#f8f8f8",
+ display: "block",
+ margin: "10px 0",
+ fontWeight: "bold",
+ textDecoration: "none",
+ };
- const buttonStyle = {
- padding: '10px 80px',
- margin: '10px 5px',
- borderRadius: '4px',
- border: 'none',
- cursor: 'pointer',
- color: '#fff',
- backgroundColor: '#007bff'
- };
+ const buttonStyle = {
+ padding: "10px 80px",
+ margin: "10px 5px",
+ borderRadius: "4px",
+ border: "none",
+ cursor: "pointer",
+ color: "#fff",
+ backgroundColor: "#007bff",
+ };
- // upload images input
- this.uploadImagesInput = $el("input", { type: 'file', multiple: false, style: inputStyle })
-
- this.uploadImagesInput.addEventListener('change', async (e) => {
- const file = e.target.files[0];
- if (!file) {
- return;
- }
- const reader = new FileReader();
- reader.onload = async (e) => {
- const imgData = e.target.result;
- this.previewImage.src = imgData;
- };
- reader.readAsDataURL(file);
- });
+ // upload images input
+ this.uploadImagesInput = $el("input", {
+ type: "file",
+ multiple: false,
+ style: inputStyle,
+ });
- // preview image
- this.previewImage = $el("img", { src: "", style: { maxWidth: '100%', maxHeight: '100px' } });
+ this.uploadImagesInput.addEventListener("change", async (e) => {
+ const file = e.target.files[0];
+ if (!file) {
+ return;
+ }
+ const reader = new FileReader();
+ reader.onload = async (e) => {
+ const imgData = e.target.result;
+ this.previewImage.src = imgData;
+ };
+ reader.readAsDataURL(file);
+ });
- this.keyInput = $el("input", { type: 'text', placeholder: "Copy & paste your API key", style: inputStyle })
- // Account Section
- const AccountSection = $el("div", { style: sectionStyle }, [
- $el("a", { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL }, ["Check out 1000+ workflows others have uploaded."]),
- $el("a", { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL }, ["You can get API key at here."]),
- $el("label", { style: labelStyle }, ["OpenArt API Key"]),
- this.keyInput,
- ]);
+ // preview image
+ this.previewImage = $el("img", {
+ src: "",
+ style: { maxWidth: "100%", maxHeight: "100px" },
+ });
- // Additional Inputs Section
- const additionalInputsSection = $el("div", { style: sectionStyle }, [
- $el("label", { style: labelStyle }, ["Image/Thumbnail (Required)"]),
- this.uploadImagesInput,
- this.previewImage,
- $el("label", { style: labelStyle }, ["Workflow Information"]),
- $el("input", { type: "text", placeholder: "Name (required)", style: inputStyle }),
- $el("textarea", { placeholder: "Description (optional)", style: {
- ...inputStyle,
- minHeight: '100px',
- } }),
- ]);
+ this.keyInput = $el("input", {
+ type: "text",
+ placeholder: "Copy & paste your API key",
+ style: inputStyle,
+ });
+ this.NameInput = $el("input", {
+ type: "text",
+ placeholder: "Name (required)",
+ style: inputStyle,
+ });
+ this.descriptionInput = $el("textarea", {
+ placeholder: "Description (optional)",
+ style: {
+ ...inputStyle,
+ minHeight: "100px",
+ },
+ });
- // Share and Close Buttons
- const buttonsSection = $el("div", { style: { textAlign: 'right', marginTop: '20px', display: 'flex', justifyContent: 'space-between' } }, [
- $el("button", { type: "button", textContent: "Close", style: {
- ...buttonStyle,
- backgroundColor: undefined
- }, onclick: () => { this.close(); } }),
- $el("button", { type: "submit", textContent: "Share", style: buttonStyle, onclick: () => { this.share(); } }),
- ]);
+ // Account Section
+ const AccountSection = $el("div", { style: sectionStyle }, [
+ $el("a", { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL }, [
+ "Check out 1000+ workflows others have uploaded.",
+ ]),
+ $el("a", { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL }, [
+ "You can get API key at here.",
+ ]),
+ $el("label", { style: labelStyle }, ["OpenArt API Key"]),
+ this.keyInput,
+ ]);
- // Final Message Section
- const finalMessage = $el("div", { style: { color: "white", textAlign: "center", padding: "10px" } }, []);
- // Composing the full layout
- const layout = [
- AccountSection,
- additionalInputsSection,
- buttonsSection,
- finalMessage,
- ];
+ // Additional Inputs Section
+ const additionalInputsSection = $el("div", { style: sectionStyle }, [
+ $el("label", { style: labelStyle }, ["Image/Thumbnail (Required)"]),
+ this.uploadImagesInput,
+ this.previewImage,
+ $el("label", { style: labelStyle }, ["Workflow Information"]),
+ this.NameInput,
+ this.descriptionInput,
+ ]);
- return layout;
+ this.shareButton = $el("button", {
+ type: "submit",
+ textContent: "Share",
+ style: buttonStyle,
+ onclick: () => {
+ this.handleShareButtonClick();
+ },
+ });
+
+ // Share and Close Buttons
+ const buttonsSection = $el(
+ "div",
+ {
+ style: {
+ textAlign: "right",
+ marginTop: "20px",
+ display: "flex",
+ justifyContent: "space-between",
+ },
+ },
+ [
+ $el("button", {
+ type: "button",
+ textContent: "Close",
+ style: {
+ ...buttonStyle,
+ backgroundColor: undefined,
+ },
+ onclick: () => {
+ this.close();
+ },
+ }),
+ this.shareButton,
+ ]
+ );
+
+ // Message Section
+ this.message = $el(
+ "div",
+ {
+ style: {
+ color: "#ff3d00",
+ textAlign: "center",
+ padding: "10px",
+ fontSize: "20px",
+ },
+ },
+ []
+ );
+ // Composing the full layout
+ const layout = [
+ AccountSection,
+ additionalInputsSection,
+ this.message,
+ buttonsSection,
+ ];
+
+ return layout;
+ }
+ async fetchApi(path, options, statusText) {
+ if (statusText) {
+ this.message.textContent = statusText;
}
- async share() {
- this.saveKeyToLocalStorage(this.keyInput.value);
- }
- show({ potential_outputs, potential_output_nodes }) {
- this.element.style.display = "block";
- // read key from local storage and set it to the input
- const key = this.readKeyFromLocalStorage();
- this.keyInput.value = key;
- }
-}
\ No newline at end of file
+ const addSearchParams = (url, params = {}) =>
+ new URL(
+ `${url.origin}${url.pathname}?${new URLSearchParams([
+ ...Array.from(url.searchParams.entries()),
+ ...Object.entries(params),
+ ])}`
+ );
+
+ const fullPath = addSearchParams(new URL(API_ENDPOINT + path), {
+ workflow_api_key: this.keyInput.value,
+ });
+
+ const response = await fetch(fullPath, options);
+
+ if (statusText) {
+ this.message.textContent = "";
+ }
+
+ return await response.json();
+ }
+ async uploadThumbnail(uploadFile) {
+ const form = new FormData();
+ form.append("file", uploadFile);
+ try {
+ const res = await this.fetchApi(
+ `/workflows/upload_thumbnail`,
+ {
+ method: "POST",
+ body: form,
+ },
+ "Uploading thumbnail..."
+ );
+ if (res.status === 200 && res.data) {
+ const { image_url, width, height } = res.data;
+ this.uploadedImages.push({
+ url: image_url,
+ width,
+ height,
+ });
+ }
+ } catch (e) {
+ if (e?.response?.status === 413) {
+ throw new Error("File size is too large (max 20MB)");
+ } else {
+ throw new Error("Error uploading thumbnail: " + e.message);
+ }
+ }
+ }
+ async handleShareButtonClick() {
+ try {
+ this.shareButton.disabled = true;
+ this.shareButton.textContent = "Sharing...";
+ await this.share();
+ } catch (e) {
+ alert(e.message);
+ }
+ this.shareButton.disabled = false;
+ this.shareButton.textContent = "Share";
+ this.message.textContent = "";
+ this.uploadedImages = [];
+ }
+ async share() {
+ this.uploadedImages = [];
+ const prompt = await app.graphToPrompt();
+ const workflowJSON = prompt["workflow"];
+ const form_values = {
+ name: this.NameInput.value,
+ description: this.descriptionInput.value,
+ };
+
+ if (!this.keyInput.value) {
+ throw new Error("API key is required");
+ }
+
+ if (!this.uploadImagesInput.files[0]) {
+ throw new Error("Thumbnail is required");
+ }
+
+ if (!form_values.name) {
+ throw new Error("Name is required");
+ }
+
+ for (const file of this.uploadImagesInput.files) {
+ await this.uploadThumbnail(file);
+ }
+
+ if (this.uploadImagesInput.files.length === 0) {
+ throw new Error("No thumbnail uploaded");
+ }
+
+ try {
+ const response = await this.fetchApi(
+ "/workflows/publish",
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ workflow_json: workflowJSON,
+ upload_images: this.uploadedImages,
+ form_values,
+ }),
+ },
+ "Uploading workflow..."
+ );
+
+ if (response.status === 200) {
+ console.log(response.data);
+ this.saveKeyToLocalStorage(this.keyInput.value);
+ }
+ } catch (e) {
+ throw new Error("Error sharing workflow: " + e.message);
+ }
+ }
+ show({ potential_outputs, potential_output_nodes }) {
+ this.element.style.display = "block";
+ // read key from local storage and set it to the input
+ const key = this.readKeyFromLocalStorage();
+ this.keyInput.value = key;
+ }
+}
From e2e7b748e39615edb6493065e8a897b8a93d9aeb Mon Sep 17 00:00:00 2001
From: Ping <5123601+pingren@users.noreply.github.com>
Date: Fri, 17 Nov 2023 20:46:57 +0800
Subject: [PATCH 06/21] feat: share workflow
---
js/comfyui-share-openart.js | 83 +++++++++++++++++++++++++------------
1 file changed, 56 insertions(+), 27 deletions(-)
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index 0aac2000..4cadd449 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -94,11 +94,13 @@ export class OpenArtShareDialog extends ComfyDialog {
type: "file",
multiple: false,
style: inputStyle,
+ accept: "image/*",
});
this.uploadImagesInput.addEventListener("change", async (e) => {
const file = e.target.files[0];
if (!file) {
+ this.previewImage.src = "";
return;
}
const reader = new FileReader();
@@ -135,12 +137,16 @@ export class OpenArtShareDialog extends ComfyDialog {
// Account Section
const AccountSection = $el("div", { style: sectionStyle }, [
- $el("a", { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL }, [
- "Check out 1000+ workflows others have uploaded.",
- ]),
- $el("a", { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL }, [
- "You can get API key at here.",
- ]),
+ $el(
+ "a",
+ { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL, target: "_blank" },
+ ["Check out 1000+ workflows others have uploaded."]
+ ),
+ $el(
+ "a",
+ { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL, target: "_blank" },
+ ["You can get API key at here."]
+ ),
$el("label", { style: labelStyle }, ["OpenArt API Key"]),
this.keyInput,
]);
@@ -225,18 +231,27 @@ export class OpenArtShareDialog extends ComfyDialog {
...Object.entries(params),
])}`
);
-
- const fullPath = addSearchParams(new URL(API_ENDPOINT + path), {
- workflow_api_key: this.keyInput.value,
- });
+
+ const fullPath = addSearchParams(new URL(API_ENDPOINT + path), {
+ workflow_api_key: this.keyInput.value,
+ });
const response = await fetch(fullPath, options);
+ if (!response.ok) {
+ throw new Error(response.statusText);
+ }
+
if (statusText) {
this.message.textContent = "";
}
-
- return await response.json();
+ const data = await response.json();
+ return {
+ ok: response.ok,
+ statusText: response.statusText,
+ status: response.status,
+ data,
+ };
}
async uploadThumbnail(uploadFile) {
const form = new FormData();
@@ -250,7 +265,8 @@ export class OpenArtShareDialog extends ComfyDialog {
},
"Uploading thumbnail..."
);
- if (res.status === 200 && res.data) {
+
+ if (res.ok && res.data) {
const { image_url, width, height } = res.data;
this.uploadedImages.push({
url: image_url,
@@ -267,6 +283,8 @@ export class OpenArtShareDialog extends ComfyDialog {
}
}
async handleShareButtonClick() {
+ this.message.textContent = "";
+ this.saveKeyToLocalStorage(this.keyInput.value);
try {
this.shareButton.disabled = true;
this.shareButton.textContent = "Sharing...";
@@ -274,13 +292,10 @@ export class OpenArtShareDialog extends ComfyDialog {
} catch (e) {
alert(e.message);
}
- this.shareButton.disabled = false;
- this.shareButton.textContent = "Share";
- this.message.textContent = "";
- this.uploadedImages = [];
+ this.shareButton.disabled = false;
+ this.shareButton.textContent = "Share";
}
async share() {
- this.uploadedImages = [];
const prompt = await app.graphToPrompt();
const workflowJSON = prompt["workflow"];
const form_values = {
@@ -300,12 +315,23 @@ export class OpenArtShareDialog extends ComfyDialog {
throw new Error("Name is required");
}
- for (const file of this.uploadImagesInput.files) {
- await this.uploadThumbnail(file);
- }
+ if (!this.uploadedImages.length) {
+ for (const file of this.uploadImagesInput.files) {
+ try {
+ await this.uploadThumbnail(file);
+ } catch (e) {
+ this.uploadedImages = [];
+ throw new Error(e.message);
+ }
+ }
- if (this.uploadImagesInput.files.length === 0) {
- throw new Error("No thumbnail uploaded");
+ if (this.uploadImagesInput.files.length === 0) {
+ throw new Error("No thumbnail uploaded");
+ }
+
+ if (this.uploadImagesInput.files.length === 0) {
+ throw new Error("No thumbnail uploaded");
+ }
}
try {
@@ -323,15 +349,18 @@ export class OpenArtShareDialog extends ComfyDialog {
"Uploading workflow..."
);
- if (response.status === 200) {
- console.log(response.data);
- this.saveKeyToLocalStorage(this.keyInput.value);
+ if (response.ok) {
+ const { workflow_id } = response.data;
+ if (workflow_id) {
+ const url = `https://openart.ai/workflows/-/-/${workflow_id}`;
+ this.message.innerHTML = `Workflow has been shared successfully. Click here to view it.`;
+ }
}
} catch (e) {
throw new Error("Error sharing workflow: " + e.message);
}
}
- show({ potential_outputs, potential_output_nodes }) {
+ show({ potential_outputs, potential_output_nodes } = {}) {
this.element.style.display = "block";
// read key from local storage and set it to the input
const key = this.readKeyFromLocalStorage();
From 407f9b899e3ddf0c00da7053b852a09e473f8f29 Mon Sep 17 00:00:00 2001
From: Ping <5123601+pingren@users.noreply.github.com>
Date: Fri, 17 Nov 2023 20:47:46 +0800
Subject: [PATCH 07/21] feat: delay share condition check
---
js/comfyui-manager.js | 25 ++-----------------------
js/comfyui-share-common.js | 29 +++++++++++++++++++++++------
2 files changed, 25 insertions(+), 29 deletions(-)
diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js
index 492fd403..aae8b853 100644
--- a/js/comfyui-manager.js
+++ b/js/comfyui-manager.js
@@ -590,30 +590,9 @@ app.registerExtension({
if(!ShareDialogChooser.instance) {
ShareDialogChooser.instance = new ShareDialogChooser();
}
- // TODO: DEV ONLY, remove this line
- ShareDialogChooser.instance.show({ potential_outputs : [], potential_output_nodes: [] });
+
+ ShareDialogChooser.instance.show();
return
-
- app.graphToPrompt().then(prompt => {
- // console.log({ prompt })
- return app.graph._nodes;
- }).then(nodes => {
- // console.log({ nodes });
- const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
-
- if (potential_outputs.length === 0) {
- if (potential_output_nodes.length === 0) {
- // todo: add support for other output node types (animatediff combine, etc.)
- const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", ");
- alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`);
- } else {
- alert("To share this, first run a prompt. Once it's done, click 'Share'.\n\nNOTE: Images of the Share target can only be selected in the PreviewImage, SaveImage, and VHS_VideoCombine nodes. In the case of VHS_VideoCombine, only the image/gif and image/webp formats are supported.");
- }
- return;
- }
-
- ShareDialogChooser.instance.show({ potential_outputs, potential_output_nodes });
- });
}
// make the background color a gradient of blue to green
shareButton.style.background = "linear-gradient(90deg, #00C9FF 0%, #92FE9D 100%)";
diff --git a/js/comfyui-share-common.js b/js/comfyui-share-common.js
index 48bf794a..6f99d74d 100644
--- a/js/comfyui-share-common.js
+++ b/js/comfyui-share-common.js
@@ -170,7 +170,7 @@ export class ShareDialogChooser extends ComfyDialog {
if (!OpenArtShareDialog.instance) {
OpenArtShareDialog.instance = new OpenArtShareDialog();
}
- OpenArtShareDialog.instance.show({ potential_outputs: this.potential_output_nodes, potential_output_nodes: this.potential_output_nodes })
+ OpenArtShareDialog.instance.show()
this.close();
}
@@ -178,8 +178,27 @@ export class ShareDialogChooser extends ComfyDialog {
if (!ShareDialog.instance) {
ShareDialog.instance = new ShareDialog();
}
- ShareDialog.instance.show({ potential_outputs: this.potential_output_nodes, potential_output_nodes: this.potential_output_nodes })
- this.close();
+ app.graphToPrompt().then(prompt => {
+ // console.log({ prompt })
+ return app.graph._nodes;
+ }).then(nodes => {
+ // console.log({ nodes });
+ const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
+
+ if (potential_outputs.length === 0) {
+ if (potential_output_nodes.length === 0) {
+ // todo: add support for other output node types (animatediff combine, etc.)
+ const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", ");
+ alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`);
+ } else {
+ alert("To share this, first run a prompt. Once it's done, click 'Share'.\n\nNOTE: Images of the Share target can only be selected in the PreviewImage, SaveImage, and VHS_VideoCombine nodes. In the case of VHS_VideoCombine, only the image/gif and image/webp formats are supported.");
+ }
+ return;
+ }
+
+ ShareDialogChooser.instance.show({ potential_outputs, potential_output_nodes });
+ this.close();
+ });
}
const buttons = [
@@ -336,10 +355,8 @@ export class ShareDialogChooser extends ComfyDialog {
]),
];
}
- show({ potential_outputs, potential_output_nodes }) {
+ show() {
this.element.style.display = "block";
- this.potential_outputs = potential_outputs;
- this.potential_output_nodes = potential_output_nodes;
}
}
export class ShareDialog extends ComfyDialog {
From ddf64ba12beac6c9ab5269e3ee45331c901e6984 Mon Sep 17 00:00:00 2001
From: Ping <5123601+pingren@users.noreply.github.com>
Date: Sat, 18 Nov 2023 09:03:33 +0800
Subject: [PATCH 08/21] fix: typo in handleShowShareDialog
---
js/comfyui-share-common.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/comfyui-share-common.js b/js/comfyui-share-common.js
index 6f99d74d..42b514e6 100644
--- a/js/comfyui-share-common.js
+++ b/js/comfyui-share-common.js
@@ -196,7 +196,7 @@ export class ShareDialogChooser extends ComfyDialog {
return;
}
- ShareDialogChooser.instance.show({ potential_outputs, potential_output_nodes });
+ ShareDialog.instance.show({ potential_outputs, potential_output_nodes });
this.close();
});
}
From 1a5b2dc36ac89784e4c6229119a25c548b6141c0 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Fri, 17 Nov 2023 17:58:56 -0700
Subject: [PATCH 09/21] Minor polish
---
js/comfyui-share-common.js | 33 +++++++++---------
js/comfyui-share-openart.js | 69 ++++++++++++++++++++++++-------------
2 files changed, 61 insertions(+), 41 deletions(-)
diff --git a/js/comfyui-share-common.js b/js/comfyui-share-common.js
index 42b514e6..d213c2f6 100644
--- a/js/comfyui-share-common.js
+++ b/js/comfyui-share-common.js
@@ -163,7 +163,7 @@ export class ShareDialogChooser extends ComfyDialog {
{},
[...this.createButtons()]),
]);
-
+
}
createButtons() {
const handleShowOpenArtShareDialog = () => {
@@ -173,7 +173,7 @@ export class ShareDialogChooser extends ComfyDialog {
OpenArtShareDialog.instance.show()
this.close();
}
-
+
const handleShowShareDialog = () => {
if (!ShareDialog.instance) {
ShareDialog.instance = new ShareDialog();
@@ -206,21 +206,21 @@ export class ShareDialogChooser extends ComfyDialog {
key: "openart",
textContent: "OpenArt AI",
website: "https://openart.ai/workflows/",
- description: "Best place to share your workflow and art.",
+ description: "Share ComfyUI workflows and art on OpenArt.ai",
onclick: handleShowOpenArtShareDialog
},
{
key: "matrix",
textContent: "Matrix Server",
website: "https://app.element.io/#/room/%23comfyui_space%3Amatrix.org",
- description: "Share your art on the official ComfyUI matrix server.",
+ description: "Share your art on the official ComfyUI matrix server",
onclick: handleShowShareDialog
},
{
key: "comfyworkflows",
textContent: "ComfyWorkflows",
website: "https://comfyworkflows.com",
- description: "Share ComfyUI art: Download & drop any image into ComfyUI to load its workflow.",
+ description: "Share ComfyUI art on comfyworkflows.com",
onclick: handleShowShareDialog
},
];
@@ -235,7 +235,7 @@ export class ShareDialogChooser extends ComfyDialog {
'padding': '20px',
}
});
-
+
buttons.forEach(b => {
const button = $el("button", {
type: "button",
@@ -258,18 +258,17 @@ export class ShareDialogChooser extends ComfyDialog {
button.addEventListener('mouseout', () => {
button.style.backgroundColor = b.backgroundColor || '';
});
-
+
const description = $el("p", {
textContent: b.description,
style: {
- 'text-align': 'center',
+ 'text-align': 'left',
color: 'white',
- 'font-style': 'italic',
'font-size': '14px',
'margin-bottom': '10px',
},
});
-
+
const websiteLink = $el("a", {
textContent: "π Website",
href: b.website,
@@ -287,11 +286,11 @@ export class ShareDialogChooser extends ComfyDialog {
websiteLink.addEventListener('mouseover', () => {
websiteLink.style.opacity = '0.7';
});
-
+
websiteLink.addEventListener('mouseout', () => {
websiteLink.style.opacity = '1';
});
-
+
const buttonLinkContainer = $el("div", {
style: {
display: 'flex',
@@ -299,7 +298,7 @@ export class ShareDialogChooser extends ComfyDialog {
'margin-bottom': '10px',
}
}, [button, websiteLink]);
-
+
const column = $el("div", {
style: {
'flex-basis': '100%',
@@ -310,13 +309,13 @@ export class ShareDialogChooser extends ComfyDialog {
'box-shadow': '0 2px 4px rgba(0, 0, 0, 0.1)',
}
}, [buttonLinkContainer, description]);
-
+
container.appendChild(column);
});
-
+
return container;
}
-
+
return [
$el("tr.td", { width: "100%" }, [
@@ -895,4 +894,4 @@ export class ShareDialog extends ComfyDialog {
this.radio_buttons.appendChild(new_radio_buttons);
this.element.style.display = "block";
}
-}
\ No newline at end of file
+}
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index 4cadd449..f8faac0c 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -68,7 +68,7 @@ export class OpenArtShareDialog extends ComfyDialog {
display: "block",
marginBottom: "15px",
fontWeight: "bold",
- fontSize: "20px",
+ fontSize: "14px",
};
const labelStyle = {
@@ -135,18 +135,37 @@ export class OpenArtShareDialog extends ComfyDialog {
},
});
+ // LinkSection
+ this.communityLink = $el("a", {
+ style: hyperLinkStyle,
+ href: DEFAULT_HOMEPAGE_URL,
+ target: "_blank"
+ }, ["π Check out thousands of workflows shared from the community"])
+ this.getAPIKeyLink = $el("a", {
+ style: {
+ ...hyperLinkStyle,
+ color: "#59E8C6"
+ },
+ href: DEFAULT_HOMEPAGE_URL,
+ target: "_blank"
+ }, ["π Get your API key here"])
+ const LinkSection = $el(
+ "div",
+ {
+ style: {
+ marginTop: "10px",
+ display: "flex",
+ flexDirection: "column",
+ },
+ },
+ [
+ this.communityLink,
+ this.getAPIKeyLink,
+ ]
+ );
+
// Account Section
const AccountSection = $el("div", { style: sectionStyle }, [
- $el(
- "a",
- { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL, target: "_blank" },
- ["Check out 1000+ workflows others have uploaded."]
- ),
- $el(
- "a",
- { style: hyperLinkStyle, href: DEFAULT_HOMEPAGE_URL, target: "_blank" },
- ["You can get API key at here."]
- ),
$el("label", { style: labelStyle }, ["OpenArt API Key"]),
this.keyInput,
]);
@@ -161,6 +180,20 @@ export class OpenArtShareDialog extends ComfyDialog {
this.descriptionInput,
]);
+ // Message Section
+ this.message = $el(
+ "div",
+ {
+ style: {
+ color: "#ff3d00",
+ textAlign: "center",
+ padding: "10px",
+ fontSize: "20px",
+ },
+ },
+ []
+ );
+
this.shareButton = $el("button", {
type: "submit",
textContent: "Share",
@@ -197,21 +230,9 @@ export class OpenArtShareDialog extends ComfyDialog {
]
);
- // Message Section
- this.message = $el(
- "div",
- {
- style: {
- color: "#ff3d00",
- textAlign: "center",
- padding: "10px",
- fontSize: "20px",
- },
- },
- []
- );
// Composing the full layout
const layout = [
+ LinkSection,
AccountSection,
additionalInputsSection,
this.message,
From 676455b0eb52571e9201609a5d27d84367a787dd Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Sat, 18 Nov 2023 01:00:39 -0700
Subject: [PATCH 10/21] Move share setting to ComfyUI Manager dialog and
provide share options.
---
js/comfyui-manager.js | 74 ++++++++++++++---------
js/comfyui-share-common.js | 115 +++++++++++++++++++-----------------
js/comfyui-share-openart.js | 5 +-
3 files changed, 111 insertions(+), 83 deletions(-)
diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js
index aae8b853..5dd9e18e 100644
--- a/js/comfyui-manager.js
+++ b/js/comfyui-manager.js
@@ -1,7 +1,7 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js";
-import { ShareDialog, SUPPORTED_OUTPUT_NODE_TYPES, getPotentialOutputsAndOutputNodes, ShareDialogChooser } from "./comfyui-share-common.js";
+import { ShareDialog, SUPPORTED_OUTPUT_NODE_TYPES, getPotentialOutputsAndOutputNodes, ShareDialogChooser, showOpenArtShareDialog, showShareDialog } from "./comfyui-share-common.js";
import { CustomNodesInstaller } from "./custom-nodes-downloader.js";
import { AlternativesInstaller } from "./a1111-alter-downloader.js";
import { SnapshotManager } from "./snapshot.js";
@@ -42,7 +42,7 @@ const style = `
#comfyworkflows-button {
position: relative;
overflow: hidden;
- }
+ }
.pysssss-workflow-arrow-2 {
position: absolute;
top: 0;
@@ -235,16 +235,16 @@ async function updateAll(update_check_checkbox) {
function newDOMTokenList(initialTokens) {
const tmp = document.createElement(`div`);
-
+
const classList = tmp.classList;
if (initialTokens) {
initialTokens.forEach(token => {
classList.add(token);
});
}
-
+
return classList;
- }
+ }
// -----------
@@ -401,12 +401,37 @@ class ManagerMenuDialog extends ComfyDialog {
}
});
+ // share
+ let share_combo = document.createElement("select");
+ const share_options = [
+ ['none', 'None'],
+ ['openart', 'OpenArt AI'],
+ ['matrix', 'Matrix Server'],
+ ['comfyworkflows', 'ComfyWorkflows'],
+ ['all', 'All'],
+ ];
+ for (const option of share_options) {
+ share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
+ }
+ share_combo.addEventListener('change', function (event) {
+ const value = event.target.value;
+ localStorage.setItem("share_option", value);
+ const shareButton = document.getElementById("shareButton");
+ if (value === 'none') {
+ shareButton.style.display = "none";
+ } else {
+ shareButton.style.display = "inline-block";
+ }
+ });
+ share_combo.value = localStorage.getItem("share_option") || 'all';
+
return [
$el("div", {}, [this.local_mode_checkbox, checkbox_text, this.update_check_checkbox, uc_checkbox_text]),
$el("br", {}, []),
preview_combo,
badge_combo,
channel_combo,
+ share_combo,
$el("hr", {}, []),
$el("center", {}, ["!! EXPERIMENTAL !!"]),
@@ -466,14 +491,14 @@ class ManagerMenuDialog extends ComfyDialog {
if (!ShareDialog.instance) {
ShareDialog.instance = new ShareDialog();
}
-
+
app.graphToPrompt().then(prompt => {
// console.log({ prompt })
return app.graph._nodes;
}).then(nodes => {
// console.log({ nodes });
const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
-
+
if (potential_outputs.length === 0) {
if (potential_output_nodes.length === 0) {
// todo: add support for other output node types (animatediff combine, etc.)
@@ -484,7 +509,7 @@ class ManagerMenuDialog extends ComfyDialog {
}
return;
}
-
+
ShareDialog.instance.show({ potential_outputs, potential_output_nodes });
});
},
@@ -582,37 +607,32 @@ app.registerExtension({
}
menu.append(managerButton);
-
const shareButton = document.createElement("button");
+ shareButton.id = "shareButton";
shareButton.textContent = "Share";
shareButton.onclick = () => {
+ const shareOption = localStorage.getItem("share_option") || 'all';
+ if (shareOption === 'openart') {
+ showOpenArtShareDialog();
+ return;
+ } else if (shareOption === 'matrix' || shareOption === 'comfyworkflows') {
+ showShareDialog();
+ return;
+ }
if(!ShareDialogChooser.instance) {
ShareDialogChooser.instance = new ShareDialogChooser();
}
-
ShareDialogChooser.instance.show();
- return
}
// make the background color a gradient of blue to green
shareButton.style.background = "linear-gradient(90deg, #00C9FF 0%, #92FE9D 100%)";
shareButton.style.color = "black";
- app.ui.settings.addSetting({
- id: "ComfyUIManager.ShowShareButtonInMainMenu",
- name: "Show 'Share' button in the main menu",
- type: "boolean",
- defaultValue: true,
- onChange: (value) => {
- if (value) {
- // show the button
- shareButton.style.display = "inline-block";
- } else {
- // hide the button
- shareButton.style.display = "none";
- }
- }
- });
+ // Load share option from local storage to determine whether to show
+ // the share button.
+ const shouldShowShareButton = localStorage.getItem("share_option") !== 'none';
+ shareButton.style.display = shouldShowShareButton ? "inline-block" : "none";
menu.append(shareButton);
},
@@ -712,4 +732,4 @@ app.registerExtension({
};
}
}
-});
\ No newline at end of file
+});
diff --git a/js/comfyui-share-common.js b/js/comfyui-share-common.js
index d213c2f6..d871c83a 100644
--- a/js/comfyui-share-common.js
+++ b/js/comfyui-share-common.js
@@ -150,6 +150,39 @@ export function parseURLPath(urlPath) {
// Return the object with the parsed parameters
return parsedParams;
}
+
+
+export const showOpenArtShareDialog = () => {
+ if (!OpenArtShareDialog.instance) {
+ OpenArtShareDialog.instance = new OpenArtShareDialog();
+ }
+ OpenArtShareDialog.instance.show();
+}
+
+export const showShareDialog = () => {
+ if (!ShareDialog.instance) {
+ ShareDialog.instance = new ShareDialog();
+ }
+ app.graphToPrompt().then(prompt => {
+ // console.log({ prompt })
+ return app.graph._nodes;
+ }).then(nodes => {
+ // console.log({ nodes });
+ const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
+ if (potential_outputs.length === 0) {
+ if (potential_output_nodes.length === 0) {
+ // todo: add support for other output node types (animatediff combine, etc.)
+ const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", ");
+ alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`);
+ } else {
+ alert("To share this, first run a prompt. Once it's done, click 'Share'.\n\nNOTE: Images of the Share target can only be selected in the PreviewImage, SaveImage, and VHS_VideoCombine nodes. In the case of VHS_VideoCombine, only the image/gif and image/webp formats are supported.");
+ }
+ return;
+ }
+ ShareDialog.instance.show({ potential_outputs, potential_output_nodes });
+ });
+}
+
export class ShareDialogChooser extends ComfyDialog {
static instance = null;
constructor() {
@@ -166,62 +199,36 @@ export class ShareDialogChooser extends ComfyDialog {
}
createButtons() {
- const handleShowOpenArtShareDialog = () => {
- if (!OpenArtShareDialog.instance) {
- OpenArtShareDialog.instance = new OpenArtShareDialog();
- }
- OpenArtShareDialog.instance.show()
- this.close();
- }
-
- const handleShowShareDialog = () => {
- if (!ShareDialog.instance) {
- ShareDialog.instance = new ShareDialog();
- }
- app.graphToPrompt().then(prompt => {
- // console.log({ prompt })
- return app.graph._nodes;
- }).then(nodes => {
- // console.log({ nodes });
- const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
-
- if (potential_outputs.length === 0) {
- if (potential_output_nodes.length === 0) {
- // todo: add support for other output node types (animatediff combine, etc.)
- const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", ");
- alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`);
- } else {
- alert("To share this, first run a prompt. Once it's done, click 'Share'.\n\nNOTE: Images of the Share target can only be selected in the PreviewImage, SaveImage, and VHS_VideoCombine nodes. In the case of VHS_VideoCombine, only the image/gif and image/webp formats are supported.");
- }
- return;
- }
-
- ShareDialog.instance.show({ potential_outputs, potential_output_nodes });
- this.close();
- });
- }
-
const buttons = [
{
key: "openart",
textContent: "OpenArt AI",
website: "https://openart.ai/workflows/",
description: "Share ComfyUI workflows and art on OpenArt.ai",
- onclick: handleShowOpenArtShareDialog
+ onclick: () => {
+ showOpenArtShareDialog();
+ this.close();
+ }
},
{
key: "matrix",
textContent: "Matrix Server",
website: "https://app.element.io/#/room/%23comfyui_space%3Amatrix.org",
description: "Share your art on the official ComfyUI matrix server",
- onclick: handleShowShareDialog
+ onclick: () => {
+ showShareDialog();
+ this.close();
+ }
},
{
key: "comfyworkflows",
textContent: "ComfyWorkflows",
website: "https://comfyworkflows.com",
description: "Share ComfyUI art on comfyworkflows.com",
- onclick: handleShowShareDialog
+ onclick: () => {
+ showShareDialog();
+ this.close();
+ }
},
];
@@ -316,22 +323,22 @@ export class ShareDialogChooser extends ComfyDialog {
return container;
}
-
return [
- $el("tr.td", { width: "100%" }, [
- $el("font", { size: 6, color: "white" }, [`Where would you like to share your workflow?`]),
- ]),
- $el("br", {}, []),
+ $el("p", {
+ textContent: 'Choose a platform to share your workflow',
+ style: {
+ 'text-align': 'center',
+ 'color': 'white',
+ 'font-size': '18px',
+ 'margin-bottom': '10px',
+ },
+ }
+ ),
$el("div.cm-menu-container", {
id: "comfyui-share-container"
}, [
$el("div.cm-menu-column", [
- $el("p", {
- size: 3, color: "white", style: {
- color: 'white'
- }
- }),
createShareButtonsWithDescriptions(),
$el("br", {}, []),
]),
@@ -391,12 +398,12 @@ export class ShareDialog extends ComfyDialog {
this.matrix_destination_checkbox = $el("input", { type: 'checkbox', id: "matrix_destination" }, [])
const matrix_destination_checkbox_text = $el("label", {}, [" ComfyUI Matrix server"])
this.matrix_destination_checkbox.style.color = "var(--fg-color)";
- this.matrix_destination_checkbox.checked = false; //true;
+ this.matrix_destination_checkbox.checked = localStorage.getItem("share_option") === 'matrix'; //true;
this.comfyworkflows_destination_checkbox = $el("input", { type: 'checkbox', id: "comfyworkflows_destination" }, [])
const comfyworkflows_destination_checkbox_text = $el("label", {}, [" ComfyWorkflows.com"])
this.comfyworkflows_destination_checkbox.style.color = "var(--fg-color)";
- this.comfyworkflows_destination_checkbox.checked = true;
+ this.comfyworkflows_destination_checkbox.checked = localStorage.getItem("share_option") !== 'matrix';
this.matrix_homeserver_input = $el("input", { type: 'text', id: "matrix_homeserver", placeholder: "matrix.org", value: ShareDialog.matrix_auth.homeserver || 'matrix.org' }, []);
this.matrix_username_input = $el("input", { type: 'text', placeholder: "Username", value: ShareDialog.matrix_auth.username || '' }, []);
@@ -453,8 +460,8 @@ export class ShareDialog extends ComfyDialog {
textContent: "Close",
onclick: () => {
// Reset state
- this.matrix_destination_checkbox.checked = false;
- this.comfyworkflows_destination_checkbox.checked = true;
+ this.matrix_destination_checkbox.checked = localStorage.getItem("share_option") === 'matrix';
+ this.comfyworkflows_destination_checkbox.checked = localStorage.getItem("share_option") !== 'matrix';
this.share_button.textContent = "Share";
this.share_button.style.display = "inline-block";
this.final_message.innerHTML = "";
@@ -625,8 +632,8 @@ export class ShareDialog extends ComfyDialog {
textContent: "Close",
onclick: () => {
// Reset state
- this.matrix_destination_checkbox.checked = false;
- this.comfyworkflows_destination_checkbox.checked = true;
+ this.matrix_destination_checkbox.checked = localStorage.getItem("share_option") === 'matrix';
+ this.comfyworkflows_destination_checkbox.checked = localStorage.getItem("share_option") !== 'matrix';
this.share_button.textContent = "Share";
this.share_button.style.display = "inline-block";
this.final_message.innerHTML = "";
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index f8faac0c..a52c7678 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -4,9 +4,10 @@ import { ComfyDialog, $el } from "../../scripts/ui.js";
const LOCAL_STORAGE_KEY = "openart_comfy_workflow_key";
const DEFAULT_HOMEPAGE_URL = "https://openart.ai/workflows/dev?developer=true";
+//const DEFAULT_HOMEPAGE_URL = "http://localhost:8080/workflows/dev?developer=true";
-// const API_ENDPOINT = "https://openart.ai/api";
-const API_ENDPOINT = "http://localhost:8080/api";
+ const API_ENDPOINT = "https://openart.ai/api";
+//const API_ENDPOINT = "http://localhost:8080/api";
const style = `
.openart-share-dialog a {
From 442942aae4c9ce70b3a5b11c77380ba08d46bae0 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Sun, 19 Nov 2023 01:26:38 -0700
Subject: [PATCH 11/21] Store share setting and key in backend config file
---
__init__.py | 62 +++++++++++++++++++++++-----
js/comfyui-manager.js | 31 ++++++++++----
js/comfyui-share-common.js | 81 ++++++++++++++++++++++---------------
js/comfyui-share-openart.js | 43 ++++++++++++++++----
4 files changed, 159 insertions(+), 58 deletions(-)
diff --git a/__init__.py b/__init__.py
index c18b0ab2..7b00495d 100644
--- a/__init__.py
+++ b/__init__.py
@@ -115,6 +115,7 @@ def write_config():
'git_exe': get_config()['git_exe'],
'channel_url': get_config()['channel_url'],
'channel_url_list': get_config()['channel_url_list'],
+ 'share_option': get_config()['share_option'],
'bypass_ssl': get_config()['bypass_ssl']
}
with open(config_path, 'w') as configfile:
@@ -146,6 +147,7 @@ def read_config():
'git_exe': default_conf['git_exe'] if 'git_exe' in default_conf else '',
'channel_url': default_conf['channel_url'] if 'channel_url' in default_conf else 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main',
'channel_url_list': ch_url_list,
+ 'share_option': default_conf['share_option'] if 'share_option' in default_conf else 'all',
'bypass_ssl': default_conf['bypass_ssl'] if 'bypass_ssl' in default_conf else False,
}
@@ -156,6 +158,7 @@ def read_config():
'git_exe': '',
'channel_url': 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main',
'channel_url_list': '',
+ 'share_option': 'all',
'bypass_ssl': False
}
@@ -401,7 +404,7 @@ def git_pull(path):
origin = repo.remote(name='origin')
origin.pull()
repo.git.submodule('update', '--init', '--recursive')
-
+
repo.close()
return True
@@ -1344,7 +1347,7 @@ async def install_model(request):
if json_data['url'].startswith('https://github.com') or json_data['url'].startswith('https://huggingface.co'):
model_dir = get_model_dir(json_data)
download_url(json_data['url'], model_dir)
-
+
return web.json_response({}, content_type='application/json')
else:
res = download_url_with_agent(json_data['url'], model_path)
@@ -1408,6 +1411,29 @@ async def channel_url_list(request):
return web.Response(status=200)
+
+@server.PromptServer.instance.routes.get("/manager/share_option")
+async def channel_url_list(request):
+ if "value" in request.rel_url.query:
+ get_config()['share_option'] = request.rel_url.query['value']
+ write_config()
+ else:
+ return web.Response(text=get_config()['share_option'], status=200)
+
+ return web.Response(status=200)
+
+
+def get_openart_auth():
+ if not os.path.exists(os.path.join(folder_paths.base_path, ".openart_key")):
+ return None
+ try:
+ with open(os.path.join(folder_paths.base_path, ".openart_key"), "r") as f:
+ openart_key = f.read().strip()
+ return openart_key if openart_key else None
+ except:
+ return None
+
+
def get_matrix_auth():
if not os.path.exists(os.path.join(folder_paths.base_path, "matrix_auth")):
return None
@@ -1437,6 +1463,22 @@ def get_comfyworkflows_auth():
except:
return None
+@server.PromptServer.instance.routes.get("/manager/get_openart_auth")
+async def api_get_openart_auth(request):
+ # print("Getting stored Matrix credentials...")
+ openart_key = get_openart_auth()
+ if not openart_key:
+ return web.Response(status=404)
+ return web.json_response({"openart_key": openart_key})
+
+@server.PromptServer.instance.routes.post("/manager/set_openart_auth")
+async def api_set_openart_auth(request):
+ json_data = await request.json()
+ openart_key = json_data['openart_key']
+ with open(os.path.join(folder_paths.base_path, ".openart_key"), "w") as f:
+ f.write(openart_key)
+ return web.Response(status=200)
+
@server.PromptServer.instance.routes.get("/manager/get_matrix_auth")
async def api_get_matrix_auth(request):
# print("Getting stored Matrix credentials...")
@@ -1491,13 +1533,13 @@ async def share_art(request):
prompt = json_data['prompt']
potential_outputs = json_data['potential_outputs']
selected_output_index = json_data['selected_output_index']
-
+
try:
output_to_share = potential_outputs[int(selected_output_index)]
except:
# for now, pick the first output
output_to_share = potential_outputs[0]
-
+
assert output_to_share['type'] in ('image', 'output')
output_dir = folder_paths.get_output_directory()
@@ -1522,7 +1564,7 @@ async def share_art(request):
if "comfyworkflows" in share_destinations:
share_website_host = "https://comfyworkflows.com"
share_endpoint = f"{share_website_host}/api"
-
+
# get presigned urls
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
async with session.post(
@@ -1530,7 +1572,7 @@ async def share_art(request):
json={
"assetFileName": asset_filename,
"assetFileType": assetFileType,
- "workflowJsonFileName" : 'workflow.json',
+ "workflowJsonFileName" : 'workflow.json',
"workflowJsonFileType" : 'application/json',
},
@@ -1541,7 +1583,7 @@ async def share_art(request):
assetFileKey = presigned_urls_json["assetFileKey"]
workflowJsonFilePresignedUrl = presigned_urls_json["workflowJsonFilePresignedUrl"]
workflowJsonFileKey = presigned_urls_json["workflowJsonFileKey"]
-
+
# upload asset
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
async with session.put(assetFilePresignedUrl, data=open(asset_filepath, "rb")) as resp:
@@ -1592,7 +1634,7 @@ async def share_art(request):
homeserver = homeserver.replace("http://", "https://")
if not homeserver.startswith("https://"):
homeserver = "https://" + homeserver
-
+
client = MatrixClient(homeserver)
try:
token = client.login(username=matrix_auth['username'], password=matrix_auth['password'])
@@ -1604,7 +1646,7 @@ async def share_art(request):
matrix = MatrixHttpApi(homeserver, token=token)
with open(asset_filepath, 'rb') as f:
mxc_url = matrix.media_upload(f.read(), content_type, filename=filename)['content_uri']
-
+
workflow_json_mxc_url = matrix.media_upload(prompt['workflow'], 'application/json', filename='workflow.json')['content_uri']
text_content = ""
@@ -1621,7 +1663,7 @@ async def share_art(request):
import traceback
traceback.print_exc()
return web.json_response({"error" : "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500)
-
+
return web.json_response({
"comfyworkflows" : {
"url" : None if "comfyworkflows" not in share_destinations else f"{share_website_host}/workflows/{workflowId}",
diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js
index 5dd9e18e..d6e9c8e9 100644
--- a/js/comfyui-manager.js
+++ b/js/comfyui-manager.js
@@ -36,6 +36,7 @@ var update_comfyui_button = null;
var fetch_updates_button = null;
var update_all_button = null;
var badge_mode = "none";
+let share_option = 'all';
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
const style = `
@@ -79,7 +80,16 @@ async function init_badge_mode() {
.then(data => { badge_mode = data; })
}
+async function init_share_option() {
+ api.fetchApi('/manager/share_option')
+ .then(response => response.text())
+ .then(data => {
+ share_option = data || 'all';
+ });
+}
+
await init_badge_mode();
+await init_share_option();
async function fetchNicknames() {
@@ -413,9 +423,18 @@ class ManagerMenuDialog extends ComfyDialog {
for (const option of share_options) {
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
}
+
+ api.fetchApi('/manager/share_option')
+ .then(response => response.text())
+ .then(data => {
+ share_combo.value = data || 'all';
+ share_option = data || 'all';
+ });
+
share_combo.addEventListener('change', function (event) {
const value = event.target.value;
- localStorage.setItem("share_option", value);
+ share_option = value;
+ api.fetchApi(`/manager/share_option?value=${value}`);
const shareButton = document.getElementById("shareButton");
if (value === 'none') {
shareButton.style.display = "none";
@@ -423,7 +442,6 @@ class ManagerMenuDialog extends ComfyDialog {
shareButton.style.display = "inline-block";
}
});
- share_combo.value = localStorage.getItem("share_option") || 'all';
return [
$el("div", {}, [this.local_mode_checkbox, checkbox_text, this.update_check_checkbox, uc_checkbox_text]),
@@ -611,12 +629,11 @@ app.registerExtension({
shareButton.id = "shareButton";
shareButton.textContent = "Share";
shareButton.onclick = () => {
- const shareOption = localStorage.getItem("share_option") || 'all';
- if (shareOption === 'openart') {
+ if (share_option === 'openart') {
showOpenArtShareDialog();
return;
- } else if (shareOption === 'matrix' || shareOption === 'comfyworkflows') {
- showShareDialog();
+ } else if (share_option === 'matrix' || share_option === 'comfyworkflows') {
+ showShareDialog(share_option);
return;
}
@@ -631,7 +648,7 @@ app.registerExtension({
// Load share option from local storage to determine whether to show
// the share button.
- const shouldShowShareButton = localStorage.getItem("share_option") !== 'none';
+ const shouldShowShareButton = share_option !== 'none';
shareButton.style.display = shouldShowShareButton ? "inline-block" : "none";
menu.append(shareButton);
diff --git a/js/comfyui-share-common.js b/js/comfyui-share-common.js
index d871c83a..9f100ab1 100644
--- a/js/comfyui-share-common.js
+++ b/js/comfyui-share-common.js
@@ -159,28 +159,31 @@ export const showOpenArtShareDialog = () => {
OpenArtShareDialog.instance.show();
}
-export const showShareDialog = () => {
+export const showShareDialog = async (share_option) => {
if (!ShareDialog.instance) {
- ShareDialog.instance = new ShareDialog();
+ ShareDialog.instance = new ShareDialog(share_option);
}
- app.graphToPrompt().then(prompt => {
- // console.log({ prompt })
- return app.graph._nodes;
- }).then(nodes => {
- // console.log({ nodes });
- const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
- if (potential_outputs.length === 0) {
- if (potential_output_nodes.length === 0) {
- // todo: add support for other output node types (animatediff combine, etc.)
- const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", ");
- alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`);
- } else {
- alert("To share this, first run a prompt. Once it's done, click 'Share'.\n\nNOTE: Images of the Share target can only be selected in the PreviewImage, SaveImage, and VHS_VideoCombine nodes. In the case of VHS_VideoCombine, only the image/gif and image/webp formats are supported.");
- }
- return;
- }
- ShareDialog.instance.show({ potential_outputs, potential_output_nodes });
- });
+ return app.graphToPrompt()
+ .then(prompt => {
+ // console.log({ prompt })
+ return app.graph._nodes;
+ })
+ .then(nodes => {
+ // console.log({ nodes });
+ const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
+ if (potential_outputs.length === 0) {
+ if (potential_output_nodes.length === 0) {
+ // todo: add support for other output node types (animatediff combine, etc.)
+ const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", ");
+ alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`);
+ } else {
+ alert("To share this, first run a prompt. Once it's done, click 'Share'.\n\nNOTE: Images of the Share target can only be selected in the PreviewImage, SaveImage, and VHS_VideoCombine nodes. In the case of VHS_VideoCombine, only the image/gif and image/webp formats are supported.");
+ }
+ return false;
+ }
+ ShareDialog.instance.show({ potential_outputs, potential_output_nodes, share_option });
+ return true;
+ });
}
export class ShareDialogChooser extends ComfyDialog {
@@ -215,9 +218,10 @@ export class ShareDialogChooser extends ComfyDialog {
textContent: "Matrix Server",
website: "https://app.element.io/#/room/%23comfyui_space%3Amatrix.org",
description: "Share your art on the official ComfyUI matrix server",
- onclick: () => {
- showShareDialog();
- this.close();
+ onclick: async () => {
+ showShareDialog('matrix').then((suc) => {
+ suc && this.close();
+ })
}
},
{
@@ -226,8 +230,9 @@ export class ShareDialogChooser extends ComfyDialog {
website: "https://comfyworkflows.com",
description: "Share ComfyUI art on comfyworkflows.com",
onclick: () => {
- showShareDialog();
- this.close();
+ showShareDialog('comfyworkflows').then((suc) => {
+ suc && this.close();
+ })
}
},
];
@@ -370,8 +375,9 @@ export class ShareDialog extends ComfyDialog {
static matrix_auth = { homeserver: "matrix.org", username: "", password: "" };
static cw_sharekey = "";
- constructor() {
+ constructor(share_option) {
super();
+ this.share_option = share_option;
this.element = $el("div.comfy-modal", {
parent: document.body, style: {
'overflow-y': "auto",
@@ -398,12 +404,12 @@ export class ShareDialog extends ComfyDialog {
this.matrix_destination_checkbox = $el("input", { type: 'checkbox', id: "matrix_destination" }, [])
const matrix_destination_checkbox_text = $el("label", {}, [" ComfyUI Matrix server"])
this.matrix_destination_checkbox.style.color = "var(--fg-color)";
- this.matrix_destination_checkbox.checked = localStorage.getItem("share_option") === 'matrix'; //true;
+ this.matrix_destination_checkbox.checked = this.share_option === 'matrix'; //true;
this.comfyworkflows_destination_checkbox = $el("input", { type: 'checkbox', id: "comfyworkflows_destination" }, [])
const comfyworkflows_destination_checkbox_text = $el("label", {}, [" ComfyWorkflows.com"])
this.comfyworkflows_destination_checkbox.style.color = "var(--fg-color)";
- this.comfyworkflows_destination_checkbox.checked = localStorage.getItem("share_option") !== 'matrix';
+ this.comfyworkflows_destination_checkbox.checked = this.share_option !== 'matrix';
this.matrix_homeserver_input = $el("input", { type: 'text', id: "matrix_homeserver", placeholder: "matrix.org", value: ShareDialog.matrix_auth.homeserver || 'matrix.org' }, []);
this.matrix_username_input = $el("input", { type: 'text', placeholder: "Username", value: ShareDialog.matrix_auth.username || '' }, []);
@@ -460,8 +466,8 @@ export class ShareDialog extends ComfyDialog {
textContent: "Close",
onclick: () => {
// Reset state
- this.matrix_destination_checkbox.checked = localStorage.getItem("share_option") === 'matrix';
- this.comfyworkflows_destination_checkbox.checked = localStorage.getItem("share_option") !== 'matrix';
+ this.matrix_destination_checkbox.checked = this.share_option === 'matrix';
+ this.comfyworkflows_destination_checkbox.checked = this.share_option !== 'matrix';
this.share_button.textContent = "Share";
this.share_button.style.display = "inline-block";
this.final_message.innerHTML = "";
@@ -632,8 +638,8 @@ export class ShareDialog extends ComfyDialog {
textContent: "Close",
onclick: () => {
// Reset state
- this.matrix_destination_checkbox.checked = localStorage.getItem("share_option") === 'matrix';
- this.comfyworkflows_destination_checkbox.checked = localStorage.getItem("share_option") !== 'matrix';
+ this.matrix_destination_checkbox.checked = this.share_option === 'matrix';
+ this.comfyworkflows_destination_checkbox.checked = this.share_option !== 'matrix';
this.share_button.textContent = "Share";
this.share_button.style.display = "inline-block";
this.final_message.innerHTML = "";
@@ -825,7 +831,7 @@ export class ShareDialog extends ComfyDialog {
return res;
}
- show({ potential_outputs, potential_output_nodes }) {
+ show({ potential_outputs, potential_output_nodes, share_option }) {
// console.log({ potential_outputs, potential_output_nodes })
this.radio_buttons.innerHTML = ""; // clear the radio buttons
const new_radio_buttons = $el("div", {
@@ -900,5 +906,14 @@ export class ShareDialog extends ComfyDialog {
// this.radio_buttons.appendChild(subheader);
this.radio_buttons.appendChild(new_radio_buttons);
this.element.style.display = "block";
+
+ share_option = share_option || this.share_option;
+ if (share_option === 'comfyworkflows') {
+ this.matrix_destination_checkbox.checked = false;
+ this.comfyworkflows_destination_checkbox.checked = true;
+ } else {
+ this.matrix_destination_checkbox.checked = true;
+ this.comfyworkflows_destination_checkbox.checked = false;
+ }
}
}
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index a52c7678..d0b7337e 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -40,12 +40,35 @@ export class OpenArtShareDialog extends ComfyDialog {
this.selectedOutputIndex = 0;
this.uploadedImages = [];
}
- readKeyFromLocalStorage() {
- return localStorage.getItem(LOCAL_STORAGE_KEY) || "";
+
+ async readKey() {
+ let key = ""
+ try {
+ // console.log("Fetching openart key")
+ key = await api.fetchApi(`/manager/get_openart_auth`)
+ .then(response => response.json())
+ .then(data => {
+ return data.openart_key;
+ })
+ .catch(error => {
+ // console.log(error);
+ });
+ } catch (error) {
+ // console.log(error);
+ }
+ return key || "";
}
- saveKeyToLocalStorage(value) {
- localStorage.setItem(LOCAL_STORAGE_KEY, value);
+
+ async saveKey(value) {
+ await api.fetchApi(`/manager/set_openart_auth`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ openart_key: value
+ })
+ });
}
+
createButtons() {
const sectionStyle = {
marginBottom: "10px",
@@ -242,6 +265,7 @@ export class OpenArtShareDialog extends ComfyDialog {
return layout;
}
+
async fetchApi(path, options, statusText) {
if (statusText) {
this.message.textContent = statusText;
@@ -275,6 +299,7 @@ export class OpenArtShareDialog extends ComfyDialog {
data,
};
}
+
async uploadThumbnail(uploadFile) {
const form = new FormData();
form.append("file", uploadFile);
@@ -304,9 +329,10 @@ export class OpenArtShareDialog extends ComfyDialog {
}
}
}
+
async handleShareButtonClick() {
this.message.textContent = "";
- this.saveKeyToLocalStorage(this.keyInput.value);
+ await this.saveKey(this.keyInput.value);
try {
this.shareButton.disabled = true;
this.shareButton.textContent = "Sharing...";
@@ -317,6 +343,7 @@ export class OpenArtShareDialog extends ComfyDialog {
this.shareButton.disabled = false;
this.shareButton.textContent = "Share";
}
+
async share() {
const prompt = await app.graphToPrompt();
const workflowJSON = prompt["workflow"];
@@ -382,10 +409,10 @@ export class OpenArtShareDialog extends ComfyDialog {
throw new Error("Error sharing workflow: " + e.message);
}
}
- show({ potential_outputs, potential_output_nodes } = {}) {
+
+ async show({ potential_outputs, potential_output_nodes } = {}) {
this.element.style.display = "block";
- // read key from local storage and set it to the input
- const key = this.readKeyFromLocalStorage();
+ const key = await this.readKey();
this.keyInput.value = key;
}
}
From 07b85b66ae860cb65050e00edc3e996c1cd4ae8f Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Sun, 19 Nov 2023 01:41:45 -0700
Subject: [PATCH 12/21] Fix typo
---
__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/__init__.py b/__init__.py
index 7b00495d..ef37beab 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1413,7 +1413,7 @@ async def channel_url_list(request):
@server.PromptServer.instance.routes.get("/manager/share_option")
-async def channel_url_list(request):
+async def share_option(request):
if "value" in request.rel_url.query:
get_config()['share_option'] = request.rel_url.query['value']
write_config()
From 20bffcaa405fd858d4fca68d6cf658090bdd9415 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Fri, 24 Nov 2023 16:37:19 -0700
Subject: [PATCH 13/21] Right click to share; Support SaveAnimatedWEBP as the
output node
---
__init__.py | 20 ++-
js/comfyui-manager.js | 50 +++++-
js/comfyui-share-common.js | 58 ++++++-
js/comfyui-share-openart.js | 325 +++++++++++++++++++++++++++++++-----
4 files changed, 404 insertions(+), 49 deletions(-)
diff --git a/__init__.py b/__init__.py
index 3f2118c1..53a4533c 100644
--- a/__init__.py
+++ b/__init__.py
@@ -826,14 +826,22 @@ def get_current_snapshot():
def save_snapshot_with_postfix(postfix):
- now = datetime.datetime.now()
+ now = datetime.datetime.now()
- date_time_format = now.strftime("%Y-%m-%d_%H-%M-%S")
- file_name = f"{date_time_format}_{postfix}"
+ date_time_format = now.strftime("%Y-%m-%d_%H-%M-%S")
+ file_name = f"{date_time_format}_{postfix}"
- path = os.path.join(os.path.dirname(__file__), 'snapshots', f"{file_name}.json")
- with open(path, "w") as json_file:
- json.dump(get_current_snapshot(), json_file, indent=4)
+ path = os.path.join(os.path.dirname(__file__), 'snapshots', f"{file_name}.json")
+ with open(path, "w") as json_file:
+ json.dump(get_current_snapshot(), json_file, indent=4)
+
+
+@server.PromptServer.instance.routes.get("/snapshot/get_current")
+async def get_current_snapshot_api(request):
+ try:
+ return web.json_response(get_current_snapshot(), content_type='application/json')
+ except:
+ return web.Response(status=400)
@server.PromptServer.instance.routes.get("/snapshot/save")
diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js
index d6e9c8e9..d2e64916 100644
--- a/js/comfyui-manager.js
+++ b/js/comfyui-manager.js
@@ -2,6 +2,7 @@ import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js";
import { ShareDialog, SUPPORTED_OUTPUT_NODE_TYPES, getPotentialOutputsAndOutputNodes, ShareDialogChooser, showOpenArtShareDialog, showShareDialog } from "./comfyui-share-common.js";
+import { OpenArtShareDialog } from "./comfyui-share-openart.js";
import { CustomNodesInstaller } from "./custom-nodes-downloader.js";
import { AlternativesInstaller } from "./a1111-alter-downloader.js";
import { SnapshotManager } from "./snapshot.js";
@@ -256,6 +257,18 @@ function newDOMTokenList(initialTokens) {
return classList;
}
+/**
+ * Check whether the node is a potential output node (img, gif or video output)
+ */
+const isOutputNode = (node) => {
+ return [
+ "VHS_VideoCombine",
+ "PreviewImage",
+ "SaveImage",
+ "ADE_AnimateDiffCombine",
+ "SaveAnimatedWEBP",
+ ].includes(node.type);
+}
// -----------
class ManagerMenuDialog extends ComfyDialog {
@@ -695,10 +708,12 @@ app.registerExtension({
}
return r;
};
+
+ this._addExtraNodeContextMenu(nodeType, app);
},
async loadedGraphNode(node, app) {
- if (node.has_errors) {
+ if (node.has_errors) {
const onDrawForeground = node.onDrawForeground;
node.onDrawForeground = function (ctx) {
const r = onDrawForeground?.apply?.(this, arguments);
@@ -748,5 +763,36 @@ app.registerExtension({
return r;
};
}
- }
+ },
+
+ _addExtraNodeContextMenu(node, app) {
+ node.prototype.getExtraMenuOptions = function (_, options) {
+ if (isOutputNode(node)) {
+ const { potential_outputs } = getPotentialOutputsAndOutputNodes([this]);
+ const hasOutput = potential_outputs.length > 0;
+ options.push({
+ content: "ποΈ Share Output",
+ disabled: !hasOutput,
+ callback: (obj) => {
+ if (!ShareDialog.instance) {
+ ShareDialog.instance = new ShareDialog();
+ }
+ const shareButton = document.getElementById("shareButton");
+ if (shareButton) {
+ const currentNode = this;
+ if (!OpenArtShareDialog.instance) {
+ OpenArtShareDialog.instance = new OpenArtShareDialog();
+ }
+ OpenArtShareDialog.instance.selectedNodeId = currentNode.id;
+ if (!ShareDialog.instance) {
+ ShareDialog.instance = new ShareDialog(share_option);
+ }
+ ShareDialog.instance.selectedNodeId = currentNode.id;
+ shareButton.click();
+ }
+ }
+ }, null);
+ }
+ }
+ },
});
diff --git a/js/comfyui-share-common.js b/js/comfyui-share-common.js
index 9f100ab1..91082f41 100644
--- a/js/comfyui-share-common.js
+++ b/js/comfyui-share-common.js
@@ -8,6 +8,7 @@ export const SUPPORTED_OUTPUT_NODE_TYPES = [
"SaveImage",
"VHS_VideoCombine",
"ADE_AnimateDiffCombine",
+ "SaveAnimatedWEBP",
]
var docStyle = document.createElement('style');
@@ -127,6 +128,17 @@ export function getPotentialOutputsAndOutputNodes(nodes) {
}
}
}
+ else if (node.type === "SaveAnimatedWEBP") {
+ potential_output_nodes.push(node);
+
+ // check if node has an 'images' array property
+ if (node.hasOwnProperty("images") && Array.isArray(node.images)) {
+ // iterate over the images array and add each image to the potential_outputs array
+ for (let j = 0; j < node.images.length; j++) {
+ potential_outputs.push({ "type": "image", "image": node.images[j], "title": node.title });
+ }
+ }
+ }
}
return { potential_outputs, potential_output_nodes };
}
@@ -156,7 +168,16 @@ export const showOpenArtShareDialog = () => {
if (!OpenArtShareDialog.instance) {
OpenArtShareDialog.instance = new OpenArtShareDialog();
}
- OpenArtShareDialog.instance.show();
+
+ return app.graphToPrompt()
+ .then(prompt => {
+ // console.log({ prompt })
+ return app.graph._nodes;
+ })
+ .then(nodes => {
+ const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
+ OpenArtShareDialog.instance.show({ potential_outputs, potential_output_nodes});
+ })
}
export const showShareDialog = async (share_option) => {
@@ -199,7 +220,7 @@ export class ShareDialogChooser extends ComfyDialog {
{},
[...this.createButtons()]),
]);
-
+ this.selectedNodeId = null;
}
createButtons() {
const buttons = [
@@ -832,6 +853,26 @@ export class ShareDialog extends ComfyDialog {
}
show({ potential_outputs, potential_output_nodes, share_option }) {
+ // Sort `potential_output_nodes` by node ID to make the order always
+ // consistent, but we should also keep `potential_outputs` in the same
+ // order as `potential_output_nodes`.
+ const potential_output_to_order = {};
+ potential_output_nodes.forEach((node, index) => {
+ potential_output_to_order[node.id] =[node, potential_outputs[index]];
+ })
+ // Sort the object `potential_output_to_order` by key (node ID)
+ const sorted_potential_output_to_order = Object.fromEntries(
+ Object.entries(potential_output_to_order).sort((a, b) => a[0].id - b[0].id)
+ );
+ const sorted_potential_outputs = []
+ const sorted_potential_output_nodes = []
+ for (const [key, value] of Object.entries(sorted_potential_output_to_order)) {
+ sorted_potential_output_nodes.push(value[0]);
+ sorted_potential_outputs.push(value[1]);
+ }
+ potential_output_nodes = sorted_potential_output_nodes;
+ potential_outputs = sorted_potential_outputs;
+
// console.log({ potential_outputs, potential_output_nodes })
this.radio_buttons.innerHTML = ""; // clear the radio buttons
const new_radio_buttons = $el("div", {
@@ -841,6 +882,7 @@ export class ShareDialog extends ComfyDialog {
'max-height': '400px',
}
}, potential_outputs.map((output, index) => {
+ const potential_output_node = potential_output_nodes[index];
const radio_button = $el("input", { type: 'radio', name: "selectOutputImages", value: index, required: index === 0 }, [])
let radio_button_img;
if (output.type === "image" || output.type === "temp") {
@@ -859,7 +901,17 @@ export class ShareDialog extends ComfyDialog {
// }
}, [output.title])
radio_button.style.color = "var(--fg-color)";
- radio_button.checked = index === 0;
+
+ // Make the radio button checked if it's the selected node,
+ // otherwise make the first radio button checked.
+ if (this.selectedNodeId) {
+ if (this.selectedNodeId === potential_output_node.id) {
+ radio_button.checked = true;
+ }
+ } else {
+ radio_button.checked = index === 0;
+ }
+
if (radio_button.checked) {
this.selectedOutputIndex = index;
}
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index d0b7337e..f2fe1e2f 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -6,18 +6,35 @@ const LOCAL_STORAGE_KEY = "openart_comfy_workflow_key";
const DEFAULT_HOMEPAGE_URL = "https://openart.ai/workflows/dev?developer=true";
//const DEFAULT_HOMEPAGE_URL = "http://localhost:8080/workflows/dev?developer=true";
- const API_ENDPOINT = "https://openart.ai/api";
+const API_ENDPOINT = "https://openart.ai/api";
//const API_ENDPOINT = "http://localhost:8080/api";
const style = `
-.openart-share-dialog a {
- color: #f8f8f8;
-}
-.openart-share-dialog a:hover {
- color: #007bff;
-}
+ .openart-share-dialog a {
+ color: #f8f8f8;
+ }
+ .openart-share-dialog a:hover {
+ color: #007bff;
+ }
+ .output_label {
+ border: 5px solid transparent;
+ }
+ .output_label:hover {
+ border: 5px solid #59E8C6;
+ }
+ .output_label.checked {
+ border: 5px solid #59E8C6;
+ }
`;
+// Shared component styles
+const sectionStyle = {
+ marginBottom: 0,
+ padding: 0,
+ borderRadius: "8px",
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)",
+};
+
export class OpenArtShareDialog extends ComfyDialog {
static instance = null;
@@ -38,13 +55,14 @@ export class OpenArtShareDialog extends ComfyDialog {
[$el("div.comfy-modal-content", {}, [...this.createButtons()])]
);
this.selectedOutputIndex = 0;
+ this.selectedNodeId = null;
this.uploadedImages = [];
+ this.selectedFile = null;
}
async readKey() {
let key = ""
try {
- // console.log("Fetching openart key")
key = await api.fetchApi(`/manager/get_openart_auth`)
.then(response => response.json())
.then(data => {
@@ -70,13 +88,6 @@ export class OpenArtShareDialog extends ComfyDialog {
}
createButtons() {
- const sectionStyle = {
- marginBottom: "10px",
- padding: "15px",
- borderRadius: "8px",
- boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)",
- };
-
const inputStyle = {
display: "block",
minWidth: "500px",
@@ -125,12 +136,20 @@ export class OpenArtShareDialog extends ComfyDialog {
const file = e.target.files[0];
if (!file) {
this.previewImage.src = "";
+ this.previewImage.style.display = "none";
return;
}
const reader = new FileReader();
reader.onload = async (e) => {
const imgData = e.target.result;
this.previewImage.src = imgData;
+ this.previewImage.style.display = "block";
+ this.selectedFile = null
+ // Once user uploads an image, we uncheck all radio buttons
+ this.radioButtons.forEach((ele) => {
+ ele.checked = false;
+ ele.parentElement.classList.remove("checked");
+ });
};
reader.readAsDataURL(file);
});
@@ -138,7 +157,7 @@ export class OpenArtShareDialog extends ComfyDialog {
// preview image
this.previewImage = $el("img", {
src: "",
- style: { maxWidth: "100%", maxHeight: "100px" },
+ style: { maxWidth: "100%", maxHeight: "100px", display: "none" },
});
this.keyInput = $el("input", {
@@ -148,7 +167,7 @@ export class OpenArtShareDialog extends ComfyDialog {
});
this.NameInput = $el("input", {
type: "text",
- placeholder: "Name (required)",
+ placeholder: "Title (required)",
style: inputStyle,
});
this.descriptionInput = $el("textarea", {
@@ -159,6 +178,19 @@ export class OpenArtShareDialog extends ComfyDialog {
},
});
+ // Header Section
+ const headerSection = $el("h3", {
+ textContent: "Share your workflow to OpenArt",
+ size: 3,
+ color: "white",
+ style: {
+ 'text-align': 'center',
+ color: 'white',
+ padding: '10px',
+ 'margin-bottom': '10px',
+ }
+ });
+
// LinkSection
this.communityLink = $el("a", {
style: hyperLinkStyle,
@@ -173,7 +205,7 @@ export class OpenArtShareDialog extends ComfyDialog {
href: DEFAULT_HOMEPAGE_URL,
target: "_blank"
}, ["π Get your API key here"])
- const LinkSection = $el(
+ const linkSection = $el(
"div",
{
style: {
@@ -189,16 +221,25 @@ export class OpenArtShareDialog extends ComfyDialog {
);
// Account Section
- const AccountSection = $el("div", { style: sectionStyle }, [
+ const accountSection = $el("div", { style: sectionStyle }, [
$el("label", { style: labelStyle }, ["OpenArt API Key"]),
this.keyInput,
]);
- // Additional Inputs Section
- const additionalInputsSection = $el("div", { style: sectionStyle }, [
- $el("label", { style: labelStyle }, ["Image/Thumbnail (Required)"]),
+ // Output Upload Section
+ const outputUploadSection = $el("div", { style: sectionStyle }, [
+ $el("label", { style: labelStyle }, ["Upload Image/Thumbnail (Required)"]),
this.uploadImagesInput,
this.previewImage,
+ ]);
+
+ // Outputs Section
+ this.outputsSection = $el("div", {
+ id: "selectOutputs",
+ }, []);
+
+ // Additional Inputs Section
+ const additionalInputsSection = $el("div", { style: sectionStyle }, [
$el("label", { style: labelStyle }, ["Workflow Information"]),
this.NameInput,
this.descriptionInput,
@@ -256,8 +297,11 @@ export class OpenArtShareDialog extends ComfyDialog {
// Composing the full layout
const layout = [
- LinkSection,
- AccountSection,
+ headerSection,
+ linkSection,
+ accountSection,
+ outputUploadSection,
+ this.outputsSection,
additionalInputsSection,
this.message,
buttonsSection,
@@ -347,6 +391,7 @@ export class OpenArtShareDialog extends ComfyDialog {
async share() {
const prompt = await app.graphToPrompt();
const workflowJSON = prompt["workflow"];
+ const workflowAPIJSON = prompt["output"];
const form_values = {
name: this.NameInput.value,
description: this.descriptionInput.value,
@@ -356,30 +401,41 @@ export class OpenArtShareDialog extends ComfyDialog {
throw new Error("API key is required");
}
- if (!this.uploadImagesInput.files[0]) {
+ if (!this.uploadImagesInput.files[0] && !this.selectedFile) {
throw new Error("Thumbnail is required");
}
if (!form_values.name) {
- throw new Error("Name is required");
+ throw new Error("Title is required");
}
+ const current_snapshot = await api.fetchApi(`/snapshot/get_current`)
+ .then(response => response.json())
+ .catch(error => {
+ // console.log(error);
+ });
+
+
if (!this.uploadedImages.length) {
- for (const file of this.uploadImagesInput.files) {
- try {
- await this.uploadThumbnail(file);
- } catch (e) {
- this.uploadedImages = [];
- throw new Error(e.message);
+ if (this.selectedFile) {
+ await this.uploadThumbnail(this.selectedFile);
+ } else {
+ for (const file of this.uploadImagesInput.files) {
+ try {
+ await this.uploadThumbnail(file);
+ } catch (e) {
+ this.uploadedImages = [];
+ throw new Error(e.message);
+ }
}
- }
- if (this.uploadImagesInput.files.length === 0) {
- throw new Error("No thumbnail uploaded");
- }
+ if (this.uploadImagesInput.files.length === 0) {
+ throw new Error("No thumbnail uploaded");
+ }
- if (this.uploadImagesInput.files.length === 0) {
- throw new Error("No thumbnail uploaded");
+ if (this.uploadImagesInput.files.length === 0) {
+ throw new Error("No thumbnail uploaded");
+ }
}
}
@@ -393,6 +449,10 @@ export class OpenArtShareDialog extends ComfyDialog {
workflow_json: workflowJSON,
upload_images: this.uploadedImages,
form_values,
+ advanced_config: {
+ workflow_api_json: workflowAPIJSON,
+ snapshot: current_snapshot,
+ }
}),
},
"Uploading workflow..."
@@ -403,6 +463,18 @@ export class OpenArtShareDialog extends ComfyDialog {
if (workflow_id) {
const url = `https://openart.ai/workflows/-/-/${workflow_id}`;
this.message.innerHTML = `Workflow has been shared successfully. Click here to view it.`;
+ this.previewImage.src = "";
+ this.previewImage.style.display = "none";
+ this.uploadedImages = [];
+ this.NameInput.value = "";
+ this.descriptionInput.value = "";
+ this.radioButtons.forEach((ele) => {
+ ele.checked = false;
+ ele.parentElement.classList.remove("checked");
+ });
+ this.selectedOutputIndex = 0;
+ this.selectedNodeId = null;
+ this.selectedFile = null;
}
}
} catch (e) {
@@ -410,9 +482,186 @@ export class OpenArtShareDialog extends ComfyDialog {
}
}
+ async fetchImageBlob(url) {
+ const response = await fetch(url);
+ const blob = await response.blob();
+ return blob;
+ }
+
async show({ potential_outputs, potential_output_nodes } = {}) {
+ // Sort `potential_output_nodes` by node ID to make the order always
+ // consistent, but we should also keep `potential_outputs` in the same
+ // order as `potential_output_nodes`.
+ const potential_output_to_order = {};
+ potential_output_nodes.forEach((node, index) => {
+ potential_output_to_order[node.id] =[node, potential_outputs[index]];
+ })
+ // Sort the object `potential_output_to_order` by key (node ID)
+ const sorted_potential_output_to_order = Object.fromEntries(
+ Object.entries(potential_output_to_order).sort((a, b) => a[0].id - b[0].id)
+ );
+ const sorted_potential_outputs = []
+ const sorted_potential_output_nodes = []
+ for (const [key, value] of Object.entries(sorted_potential_output_to_order)) {
+ sorted_potential_output_nodes.push(value[0]);
+ sorted_potential_outputs.push(value[1]);
+ }
+ potential_output_nodes = sorted_potential_output_nodes;
+ potential_outputs = sorted_potential_outputs;
+
+ this.message.innerHTML = "";
+ this.message.textContent = "";
this.element.style.display = "block";
+ this.previewImage.src = "";
+ this.previewImage.style.display = "none";
const key = await this.readKey();
this.keyInput.value = key;
+
+ // If `selectedNodeId` is provided, we will select the corresponding radio
+ // button for the node. In addition, we move the selected radio button to
+ // the top of the list.
+ if (this.selectedNodeId) {
+ const index = potential_output_nodes.findIndex(node => node.id === this.selectedNodeId);
+ if (index >= 0) {
+ this.selectedOutputIndex = index;
+ }
+ }
+
+ this.radioButtons = [];
+ const new_radio_buttons = $el("div",
+ {
+ id: "selectOutput-Options",
+ style: {
+ 'overflow-y': 'scroll',
+ 'max-height': '250px',
+
+ 'display': 'grid',
+ 'grid-template-columns': 'repeat(auto-fit, minmax(100px, 1fr))',
+ 'grid-template-rows': 'auto',
+ 'grid-column-gap': '10px',
+ 'grid-row-gap': '10px',
+ 'margin-bottom': '10px',
+ 'padding': '10px',
+ 'border-radius': '8px',
+ 'box-shadow': '0 2px 4px rgba(0, 0, 0, 0.05)',
+ 'background-color': 'var(--bg-color)',
+ }
+ },
+ potential_outputs.map((output, index) => {
+ const radio_button = $el("input", { type: 'radio', name: "selectOutputImages", value: index, required: index === 0 }, [])
+ let radio_button_img;
+ if (output.type === "image" || output.type === "temp") {
+ radio_button_img = $el("img", { src: `/view?filename=${output.image.filename}&subfolder=${output.image.subfolder}&type=${output.image.type}`, style: { width: "100px", height: "100px", objectFit: "cover", borderRadius: "5px" } }, []);
+ } else if (output.type === "output") {
+ radio_button_img = $el("img", { src: output.output.value, style: { width: "auto", height: "100px", objectFit: "cover", borderRadius: "5px" } }, []);
+ } else {
+ // unsupported output type
+ // this should never happen
+ // TODO
+ radio_button_img = $el("img", { src: "", style: { width: "auto", height: "100px" } }, []);
+ }
+ const radio_button_text = $el("span", {
+ style: {
+ color: 'gray',
+ display: 'block',
+ fontSize: '12px',
+ overflowX: 'hidden',
+ textOverflow: 'ellipsis',
+ textWrap: 'nowrap',
+ maxWidth: '100px',
+ }
+ }, [output.title])
+ const node_id_chip = $el("span", {
+ style: {
+ color: '#FBFBFD',
+ display: 'block',
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ fontSize: '12px',
+ overflowX: 'hidden',
+ padding: '2px 3px',
+ textOverflow: 'ellipsis',
+ textWrap: 'nowrap',
+ maxWidth: '100px',
+ position: 'absolute',
+ top: '3px',
+ left: '3px',
+ borderRadius: '3px',
+ }
+ }, [`Node: ${potential_output_nodes[index].id}`])
+ radio_button.style.color = "var(--fg-color)";
+ radio_button.checked = this.selectedOutputIndex === index;
+
+ radio_button.onchange = async () => {
+ this.selectedOutputIndex = parseInt(radio_button.value);
+
+ // Remove the "checked" class from all radio buttons
+ this.radioButtons.forEach((ele) => {
+ ele.parentElement.classList.remove("checked");
+ });
+ radio_button.parentElement.classList.add("checked");
+
+ this.fetchImageBlob(radio_button_img.src).then((blob) => {
+ const file = new File([blob], output.image.filename, {
+ type: blob.type,
+ });
+ this.previewImage.src = radio_button_img.src;
+ this.previewImage.style.display = "block";
+ this.selectedFile = file;
+ })
+ };
+
+ if (radio_button.checked) {
+ this.fetchImageBlob(radio_button_img.src).then((blob) => {
+ const file = new File([blob], output.image.filename, {
+ type: blob.type,
+ });
+ this.previewImage.src = radio_button_img.src;
+ this.previewImage.style.display = "block";
+ this.selectedFile = file;
+ })
+ }
+
+ this.radioButtons.push(radio_button);
+
+ return $el(`label.output_label${radio_button.checked ? '.checked' : ''}`, {
+ style: {
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "center",
+ justifyContent: "center",
+ marginBottom: "10px",
+ cursor: "pointer",
+ position: 'relative',
+ }
+ }, [radio_button_img, radio_button_text, radio_button, node_id_chip]);
+ })
+ );
+
+ const header = $el("div",
+ {
+ textContent: "Or choose an output below",
+ fontSize: '15px',
+ color: "white",
+ style: {
+ ...sectionStyle,
+ color: 'white',
+ },
+ },
+ [
+ $el("p", {
+ textContent: "Scroll to see all",
+ size: 2,
+ color: "white",
+ style: {
+ color: 'white',
+ margin: '5px 0',
+ fontSize: '12px',
+ },
+ }, [])
+ ]
+ );
+ this.outputsSection.innerHTML = "";
+ this.outputsSection.appendChild(header);
+ this.outputsSection.appendChild(new_radio_buttons);
}
}
From 12e7f3648e2c386d7be91d3b4663197664c99ad9 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Fri, 24 Nov 2023 17:15:21 -0700
Subject: [PATCH 14/21] Add checkbox for participating the contest
---
js/comfyui-share-openart.js | 33 +++++++++++++++++++++++++++++++--
1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index f2fe1e2f..9fcc5d37 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -245,6 +245,31 @@ export class OpenArtShareDialog extends ComfyDialog {
this.descriptionInput,
]);
+ // OpenArt Contest Section
+ this.joinContestCheckbox = $el("input",{type:'checkbox', id:"join_contest"},[])
+ this.joinContestDescription = $el("a", {
+ style: {
+ ...hyperLinkStyle,
+ display: 'inline-block',
+ color: "#59E8C6",
+ fontSize: '12px',
+ marginLeft: '10px',
+ marginBottom: 0,
+ },
+ href: "https://contest.openart.ai/",
+ target: "_blank"
+ }, ["π I'm participating in the OpenArt workflow contest"])
+ this.joinContestLabel = $el("label", {
+ style: {
+ display: 'flex',
+ alignItems: 'center',
+ cursor: 'pointer',
+ }
+ },[this.joinContestCheckbox, this.joinContestDescription])
+ const contestSection = $el("div", { style: sectionStyle }, [
+ this.joinContestLabel,
+ ]);
+
// Message Section
this.message = $el(
"div",
@@ -303,6 +328,7 @@ export class OpenArtShareDialog extends ComfyDialog {
outputUploadSection,
this.outputsSection,
additionalInputsSection,
+ contestSection,
this.message,
buttonsSection,
];
@@ -439,6 +465,8 @@ export class OpenArtShareDialog extends ComfyDialog {
}
}
+ const join_contest = this.joinContestCheckbox.checked;
+
try {
const response = await this.fetchApi(
"/workflows/publish",
@@ -452,7 +480,8 @@ export class OpenArtShareDialog extends ComfyDialog {
advanced_config: {
workflow_api_json: workflowAPIJSON,
snapshot: current_snapshot,
- }
+ },
+ join_contest,
}),
},
"Uploading workflow..."
@@ -507,7 +536,7 @@ export class OpenArtShareDialog extends ComfyDialog {
sorted_potential_outputs.push(value[1]);
}
potential_output_nodes = sorted_potential_output_nodes;
- potential_outputs = sorted_potential_outputs;
+ potential_outputs = sorted_potential_outputs.filter((output) => !!output);
this.message.innerHTML = "";
this.message.textContent = "";
From 355991ecc95d377381a1b4b475fb47f814a21755 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Fri, 24 Nov 2023 17:28:16 -0700
Subject: [PATCH 15/21] Add contest notice
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 9df5886a..fe07c816 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,8 @@

## NOTICE
-* If you wish to hide the 'Share' button, please refer to the settings menu.
+* π John us for the [ComfyUI Workflow Contest](https://contest.openart.ai/), hosted by OpenArt AI (11.27.2023 - 12.15.2023). Our esteemed judge panel includes Scott E. Detweiler, Olivio Sarikas, MERJICιΊ¦ζ©, among others. We're also thrilled to have the authors of ComfyUI Manager and AnimateDiff as our special guests!
+* If you wish to hide the "Share" button, click "Manager" and choose "Share: None" option.
* You can see whole nodes info on [ComfyUI Nodes Info](https://ltdrdata.github.io/) page.
* Versions prior to V0.22.2 will no longer detect missing nodes unless using a local database. Please update ComfyUI-Manager to the latest version.
From e32072ab3465827c4d0d3124e6f5809ea0ca9c3c Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Fri, 24 Nov 2023 19:06:40 -0700
Subject: [PATCH 16/21] Fix the original missing menu options
---
js/comfyui-manager.js | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js
index d2e64916..091185c5 100644
--- a/js/comfyui-manager.js
+++ b/js/comfyui-manager.js
@@ -766,10 +766,19 @@ app.registerExtension({
},
_addExtraNodeContextMenu(node, app) {
- node.prototype.getExtraMenuOptions = function (_, options) {
- if (isOutputNode(node)) {
+ const origGetExtraMenuOptions = node.prototype.getExtraMenuOptions;
+ node.prototype.getExtraMenuOptions = function (_, options) {
+ origGetExtraMenuOptions?.apply?.(this, arguments);
+ if (isOutputNode(node)) {
const { potential_outputs } = getPotentialOutputsAndOutputNodes([this]);
const hasOutput = potential_outputs.length > 0;
+
+ // Check if the previous menu option is `null`. If it's not,
+ // then we need to add a `null` as a separator.
+ if (options[options.length - 1] !== null) {
+ options.push(null);
+ }
+
options.push({
content: "ποΈ Share Output",
disabled: !hasOutput,
@@ -793,6 +802,6 @@ app.registerExtension({
}
}, null);
}
- }
+ }
},
});
From bb2a2ece881e72a8e8846b1084dcbdca5f03dc98 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Fri, 24 Nov 2023 19:44:58 -0700
Subject: [PATCH 17/21] Better layout for uploading thumbnails
---
js/comfyui-share-openart.js | 39 ++++++++++++++-----------------------
1 file changed, 15 insertions(+), 24 deletions(-)
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index 9fcc5d37..c7b3cc29 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -33,6 +33,9 @@ const sectionStyle = {
padding: 0,
borderRadius: "8px",
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)",
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "center",
};
export class OpenArtShareDialog extends ComfyDialog {
@@ -109,7 +112,7 @@ export class OpenArtShareDialog extends ComfyDialog {
const labelStyle = {
color: "#f8f8f8",
display: "block",
- margin: "10px 0",
+ margin: "10px 0 0 0",
fontWeight: "bold",
textDecoration: "none",
};
@@ -157,7 +160,7 @@ export class OpenArtShareDialog extends ComfyDialog {
// preview image
this.previewImage = $el("img", {
src: "",
- style: { maxWidth: "100%", maxHeight: "100px", display: "none" },
+ style: { width: "100%", maxHeight: "100px", objectFit: "contain", display: "none" },
});
this.keyInput = $el("input", {
@@ -186,8 +189,7 @@ export class OpenArtShareDialog extends ComfyDialog {
style: {
'text-align': 'center',
color: 'white',
- padding: '10px',
- 'margin-bottom': '10px',
+ margin: '0 0 10px 0',
}
});
@@ -222,15 +224,16 @@ export class OpenArtShareDialog extends ComfyDialog {
// Account Section
const accountSection = $el("div", { style: sectionStyle }, [
- $el("label", { style: labelStyle }, ["OpenArt API Key"]),
+ $el("label", { style: labelStyle }, ["1οΈβ£ OpenArt API Key"]),
this.keyInput,
]);
// Output Upload Section
const outputUploadSection = $el("div", { style: sectionStyle }, [
- $el("label", { style: labelStyle }, ["Upload Image/Thumbnail (Required)"]),
- this.uploadImagesInput,
+ $el("label", { style: {...labelStyle, margin: "10px 0 0 0"} }, ["2οΈβ£ Image/Thumbnail (Required)"]),
+ $el("div", { style: { ...labelStyle, margin: "5px 0 10px 0", fontSize: '12px', fontWeight: "normal"} }, ["Upload or choose one from the outputs"]),
this.previewImage,
+ this.uploadImagesInput,
]);
// Outputs Section
@@ -240,7 +243,7 @@ export class OpenArtShareDialog extends ComfyDialog {
// Additional Inputs Section
const additionalInputsSection = $el("div", { style: sectionStyle }, [
- $el("label", { style: labelStyle }, ["Workflow Information"]),
+ $el("label", { style: labelStyle }, ["3οΈβ£ Workflow Information"]),
this.NameInput,
this.descriptionInput,
]);
@@ -562,7 +565,7 @@ export class OpenArtShareDialog extends ComfyDialog {
id: "selectOutput-Options",
style: {
'overflow-y': 'scroll',
- 'max-height': '250px',
+ 'max-height': '200px',
'display': 'grid',
'grid-template-columns': 'repeat(auto-fit, minmax(100px, 1fr))',
@@ -666,29 +669,17 @@ export class OpenArtShareDialog extends ComfyDialog {
})
);
- const header = $el("div",
- {
- textContent: "Or choose an output below",
- fontSize: '15px',
- color: "white",
- style: {
- ...sectionStyle,
- color: 'white',
- },
- },
- [
+ const header =
$el("p", {
- textContent: "Scroll to see all",
+ textContent: this.radioButtons.length === 0 ? "Queue Prompt to see the outputs" : "Outputs (scroll to see all)",
size: 2,
color: "white",
style: {
color: 'white',
- margin: '5px 0',
+ margin: '0 0 5px 0',
fontSize: '12px',
},
}, [])
- ]
- );
this.outputsSection.innerHTML = "";
this.outputsSection.appendChild(header);
this.outputsSection.appendChild(new_radio_buttons);
From ab257ed7507b8f533a686751f6772173608ce919 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Fri, 24 Nov 2023 21:56:14 -0700
Subject: [PATCH 18/21] Clear `this.uploadedImages` when opening the dialog
---
js/comfyui-share-openart.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index c7b3cc29..39c398fe 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -548,6 +548,7 @@ export class OpenArtShareDialog extends ComfyDialog {
this.previewImage.style.display = "none";
const key = await this.readKey();
this.keyInput.value = key;
+ this.uploadedImages = [];
// If `selectedNodeId` is provided, we will select the corresponding radio
// button for the node. In addition, we move the selected radio button to
From 3dc35c049895186a86d0e9d0945a3a97e1b5dbb4 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Sat, 25 Nov 2023 00:54:20 -0700
Subject: [PATCH 19/21] Make sure arrays `potential_output_nodes` and
`potential_outputs` have the same length to avoid `undefined` bug when
sharing
---
js/comfyui-share-common.js | 20 +++++++++-----------
js/comfyui-share-openart.js | 4 ++--
2 files changed, 11 insertions(+), 13 deletions(-)
diff --git a/js/comfyui-share-common.js b/js/comfyui-share-common.js
index 91082f41..e821f867 100644
--- a/js/comfyui-share-common.js
+++ b/js/comfyui-share-common.js
@@ -47,30 +47,26 @@ export function getPotentialOutputsAndOutputNodes(nodes) {
}
if (node.type === "SaveImage") {
- potential_output_nodes.push(node);
-
// check if node has an 'images' array property
if (node.hasOwnProperty("images") && Array.isArray(node.images)) {
// iterate over the images array and add each image to the potential_outputs array
for (let j = 0; j < node.images.length; j++) {
+ potential_output_nodes.push(node);
potential_outputs.push({ "type": "image", "image": node.images[j], "title": node.title });
}
}
}
else if (node.type === "PreviewImage") {
- potential_output_nodes.push(node);
-
// check if node has an 'images' array property
if (node.hasOwnProperty("images") && Array.isArray(node.images)) {
// iterate over the images array and add each image to the potential_outputs array
for (let j = 0; j < node.images.length; j++) {
+ potential_output_nodes.push(node);
potential_outputs.push({ "type": "image", "image": node.images[j], "title": node.title });
}
}
}
else if (node.type === "VHS_VideoCombine") {
- potential_output_nodes.push(node);
-
// check if node has a 'widgets' array property, with type 'image'
if (node.hasOwnProperty("widgets") && Array.isArray(node.widgets)) {
// iterate over the widgets array and add each image to the potential_outputs array
@@ -84,6 +80,7 @@ export function getPotentialOutputsAndOutputNodes(nodes) {
if (parsedURLVals.type !== "output") {
// TODO
}
+ potential_output_nodes.push(node);
potential_outputs.push({ "type": "output", 'title': node.title, "output": { "filename": parsedURLVals.filename, "subfolder": parsedURLVals.subfolder, "value": widgetValue, "format": parsedURLVals.format } });
}
} else if (node.widgets[j].type === "preview") {
@@ -100,6 +97,7 @@ export function getPotentialOutputsAndOutputNodes(nodes) {
if (parsedURLVals.type !== "output") {
// TODO
}
+ potential_output_nodes.push(node);
potential_outputs.push({ "type": "output", 'title': node.title, "output": { "filename": parsedURLVals.filename, "subfolder": parsedURLVals.subfolder, "value": `/view?filename=${parsedURLVals.filename}&subfolder=${parsedURLVals.subfolder}&type=${parsedURLVals.type}&format=${parsedURLVals.format}`, "format": parsedURLVals.format } });
}
}
@@ -107,8 +105,6 @@ export function getPotentialOutputsAndOutputNodes(nodes) {
}
}
else if (node.type === "ADE_AnimateDiffCombine") {
- potential_output_nodes.push(node);
-
// check if node has a 'widgets' array property, with type 'image'
if (node.hasOwnProperty("widgets") && Array.isArray(node.widgets)) {
// iterate over the widgets array and add each image to the potential_outputs array
@@ -122,6 +118,7 @@ export function getPotentialOutputsAndOutputNodes(nodes) {
// TODO
continue;
}
+ potential_output_nodes.push(node);
potential_outputs.push({ "type": "output", 'title': node.title, "output": { "filename": parsedURLVals.filename, "subfolder": parsedURLVals.subfolder, "type": parsedURLVals.type, "value": widgetValue, "format": parsedURLVals.format } });
}
}
@@ -129,17 +126,18 @@ export function getPotentialOutputsAndOutputNodes(nodes) {
}
}
else if (node.type === "SaveAnimatedWEBP") {
- potential_output_nodes.push(node);
-
// check if node has an 'images' array property
if (node.hasOwnProperty("images") && Array.isArray(node.images)) {
// iterate over the images array and add each image to the potential_outputs array
for (let j = 0; j < node.images.length; j++) {
+ potential_output_nodes.push(node);
potential_outputs.push({ "type": "image", "image": node.images[j], "title": node.title });
}
}
}
}
+
+ // Note: make sure that two arrays are the same length
return { potential_outputs, potential_output_nodes };
}
@@ -858,7 +856,7 @@ export class ShareDialog extends ComfyDialog {
// order as `potential_output_nodes`.
const potential_output_to_order = {};
potential_output_nodes.forEach((node, index) => {
- potential_output_to_order[node.id] =[node, potential_outputs[index]];
+ potential_output_to_order[node.id] = [node, potential_outputs[index]];
})
// Sort the object `potential_output_to_order` by key (node ID)
const sorted_potential_output_to_order = Object.fromEntries(
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index 39c398fe..11e65d92 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -526,7 +526,7 @@ export class OpenArtShareDialog extends ComfyDialog {
// order as `potential_output_nodes`.
const potential_output_to_order = {};
potential_output_nodes.forEach((node, index) => {
- potential_output_to_order[node.id] =[node, potential_outputs[index]];
+ potential_output_to_order[node.id] = [node, potential_outputs[index]];
})
// Sort the object `potential_output_to_order` by key (node ID)
const sorted_potential_output_to_order = Object.fromEntries(
@@ -539,7 +539,7 @@ export class OpenArtShareDialog extends ComfyDialog {
sorted_potential_outputs.push(value[1]);
}
potential_output_nodes = sorted_potential_output_nodes;
- potential_outputs = sorted_potential_outputs.filter((output) => !!output);
+ potential_outputs = sorted_potential_outputs;
this.message.innerHTML = "";
this.message.textContent = "";
From e6f90270150794863d7ac24ec3dc29f8cd97f597 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Sat, 25 Nov 2023 01:47:22 -0700
Subject: [PATCH 20/21] Fix filename issue
---
js/comfyui-share-openart.js | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index 11e65d92..6bcbcaeb 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -583,10 +583,13 @@ export class OpenArtShareDialog extends ComfyDialog {
potential_outputs.map((output, index) => {
const radio_button = $el("input", { type: 'radio', name: "selectOutputImages", value: index, required: index === 0 }, [])
let radio_button_img;
+ let filename;
if (output.type === "image" || output.type === "temp") {
radio_button_img = $el("img", { src: `/view?filename=${output.image.filename}&subfolder=${output.image.subfolder}&type=${output.image.type}`, style: { width: "100px", height: "100px", objectFit: "cover", borderRadius: "5px" } }, []);
+ filename = output.image.filename
} else if (output.type === "output") {
radio_button_img = $el("img", { src: output.output.value, style: { width: "auto", height: "100px", objectFit: "cover", borderRadius: "5px" } }, []);
+ filename = output.filename
} else {
// unsupported output type
// this should never happen
@@ -634,7 +637,7 @@ export class OpenArtShareDialog extends ComfyDialog {
radio_button.parentElement.classList.add("checked");
this.fetchImageBlob(radio_button_img.src).then((blob) => {
- const file = new File([blob], output.image.filename, {
+ const file = new File([blob], filename, {
type: blob.type,
});
this.previewImage.src = radio_button_img.src;
@@ -645,7 +648,7 @@ export class OpenArtShareDialog extends ComfyDialog {
if (radio_button.checked) {
this.fetchImageBlob(radio_button_img.src).then((blob) => {
- const file = new File([blob], output.image.filename, {
+ const file = new File([blob], filename, {
type: blob.type,
});
this.previewImage.src = radio_button_img.src;
From 33293bd0a619b5f305b065bd6863ddc87a9a4171 Mon Sep 17 00:00:00 2001
From: johnqiao
Date: Sat, 25 Nov 2023 02:05:56 -0700
Subject: [PATCH 21/21] Update the style when switching between upload
thumbnail and select output
---
js/comfyui-share-openart.js | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js
index 6bcbcaeb..77af0c51 100644
--- a/js/comfyui-share-openart.js
+++ b/js/comfyui-share-openart.js
@@ -153,6 +153,11 @@ export class OpenArtShareDialog extends ComfyDialog {
ele.checked = false;
ele.parentElement.classList.remove("checked");
});
+
+ // Add the opacity style toggle here to indicate that they only need
+ // to upload one image or choose one from the outputs.
+ this.outputsSection.style.opacity = 0.35;
+ this.uploadImagesInput.style.opacity = 1;
};
reader.readAsDataURL(file);
});
@@ -160,7 +165,13 @@ export class OpenArtShareDialog extends ComfyDialog {
// preview image
this.previewImage = $el("img", {
src: "",
- style: { width: "100%", maxHeight: "100px", objectFit: "contain", display: "none" },
+ style: {
+ width: "100%",
+ maxHeight: "100px",
+ objectFit: "contain",
+ display: "none",
+ marginTop: '10px',
+ },
});
this.keyInput = $el("input", {
@@ -231,7 +242,6 @@ export class OpenArtShareDialog extends ComfyDialog {
// Output Upload Section
const outputUploadSection = $el("div", { style: sectionStyle }, [
$el("label", { style: {...labelStyle, margin: "10px 0 0 0"} }, ["2οΈβ£ Image/Thumbnail (Required)"]),
- $el("div", { style: { ...labelStyle, margin: "5px 0 10px 0", fontSize: '12px', fontWeight: "normal"} }, ["Upload or choose one from the outputs"]),
this.previewImage,
this.uploadImagesInput,
]);
@@ -644,6 +654,11 @@ export class OpenArtShareDialog extends ComfyDialog {
this.previewImage.style.display = "block";
this.selectedFile = file;
})
+
+ // Add the opacity style toggle here to indicate that they only need
+ // to upload one image or choose one from the outputs.
+ this.outputsSection.style.opacity = 1;
+ this.uploadImagesInput.style.opacity = 0.35;
};
if (radio_button.checked) {
@@ -655,6 +670,10 @@ export class OpenArtShareDialog extends ComfyDialog {
this.previewImage.style.display = "block";
this.selectedFile = file;
})
+ // Add the opacity style toggle here to indicate that they only need
+ // to upload one image or choose one from the outputs.
+ this.outputsSection.style.opacity = 1;
+ this.uploadImagesInput.style.opacity = 0.35;
}
this.radioButtons.push(radio_button);
@@ -675,7 +694,7 @@ export class OpenArtShareDialog extends ComfyDialog {
const header =
$el("p", {
- textContent: this.radioButtons.length === 0 ? "Queue Prompt to see the outputs" : "Outputs (scroll to see all)",
+ textContent: this.radioButtons.length === 0 ? "Queue Prompt to see the outputs" : "Or choose one from the outputs (scroll to see all)",
size: 2,
color: "white",
style: {