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