| import { api } from "../../scripts/api.js"; |
| import { app } from "../../scripts/app.js"; |
| import { $el, ComfyDialog } from "../../scripts/ui.js"; |
| import { |
| SUPPORTED_OUTPUT_NODE_TYPES, |
| ShareDialog, |
| ShareDialogChooser, |
| getPotentialOutputsAndOutputNodes, |
| showOpenArtShareDialog, |
| showShareDialog, |
| showYouMLShareDialog |
| } from "./comfyui-share-common.js"; |
| import { OpenArtShareDialog } from "./comfyui-share-openart.js"; |
| import { |
| free_models, install_pip, install_via_git_url, manager_instance, |
| rebootAPI, setManagerInstance, show_message, customAlert, customPrompt, |
| infoToast, showTerminal, setNeedRestart, handle403Response |
| } from "./common.js"; |
| import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js"; |
| import { CustomNodesManager } from "./custom-nodes-manager.js"; |
| import { ModelManager } from "./model-manager.js"; |
| import { SnapshotManager } from "./snapshot.js"; |
| import { buildGuiFrame, createSettingsCombo } from "./comfyui-gui-builder.js"; |
|
|
| let manager_version = await getVersion(); |
|
|
| var docStyle = document.createElement('style'); |
| docStyle.innerHTML = ` |
| .comfy-toast { |
| position: fixed; |
| bottom: 20px; |
| left: 50%; |
| transform: translateX(-50%); |
| background-color: rgba(0, 0, 0, 0.7); |
| color: white; |
| padding: 10px 20px; |
| border-radius: 5px; |
| z-index: 1000; |
| transition: opacity 0.5s; |
| } |
| |
| .comfy-toast-fadeout { |
| opacity: 0; |
| } |
| |
| #cm-manager-dialog { |
| width: 1000px; |
| height: auto; |
| box-sizing: content-box; |
| z-index: 1000; |
| overflow-y: auto; |
| } |
| |
| #cm-manager-dialog br { |
| margin-bottom: 1em; |
| } |
| |
| .cb-widget { |
| width: 400px; |
| height: 25px; |
| box-sizing: border-box; |
| z-index: 1000; |
| margin-top: 10px; |
| margin-bottom: 5px; |
| } |
| |
| .cb-widget-input { |
| width: 305px; |
| height: 25px; |
| box-sizing: border-box; |
| } |
| .cb-widget-input:disabled { |
| background-color: #444444; |
| color: white; |
| } |
| |
| .cb-widget-input-label { |
| width: 90px; |
| height: 25px; |
| box-sizing: border-box; |
| color: white; |
| text-align: right; |
| display: inline-block; |
| margin-right: 5px; |
| } |
| |
| .cm-menu-container { |
| padding : calc(var(--spacing)*2); |
| column-gap: 20px; |
| display: flex; |
| flex-wrap: wrap; |
| justify-content: center; |
| box-sizing: content-box; |
| } |
| |
| .cm-menu-column { |
| display: flex; |
| flex-direction: column; |
| flex: 1 1 auto; |
| width: 300px; |
| box-sizing: content-box; |
| } |
| |
| .cm-title { |
| background-color: black; |
| text-align: center; |
| height: 40px; |
| width: calc(100% - 10px); |
| font-weight: bold; |
| justify-content: center; |
| align-content: center; |
| vertical-align: middle; |
| } |
| |
| #custom-nodes-grid a { |
| color: #5555FF; |
| font-weight: bold; |
| text-decoration: none; |
| } |
| |
| #custom-nodes-grid a:hover { |
| color: #7777FF; |
| text-decoration: underline; |
| } |
| |
| #external-models-grid a { |
| color: #5555FF; |
| font-weight: bold; |
| text-decoration: none; |
| } |
| |
| #external-models-grid a:hover { |
| color: #7777FF; |
| text-decoration: underline; |
| } |
| |
| #alternatives-grid a { |
| color: #5555FF; |
| font-weight: bold; |
| text-decoration: none; |
| } |
| |
| #alternatives-grid a:hover { |
| color: #7777FF; |
| text-decoration: underline; |
| } |
| |
| .cm-notice-board { |
| width: auto; |
| height: 280px; |
| overflow: auto; |
| color: var(--input-text); |
| border: 1px solid var(--descrip-text); |
| padding: 5px 10px; |
| overflow-x: hidden; |
| box-sizing: content-box; |
| } |
| |
| .cm-notice-board > ul { |
| display: block; |
| list-style-type: disc; |
| margin-block-start: 1em; |
| margin-block-end: 1em; |
| margin-inline-start: 0px; |
| margin-inline-end: 0px; |
| padding-inline-start: 40px; |
| } |
| |
| .cm-conflicted-nodes-text { |
| background-color: #CCCC55 !important; |
| color: #AA3333 !important; |
| font-size: 10px; |
| border-radius: 5px; |
| padding: 10px; |
| } |
| |
| .cm-warn-note { |
| background-color: #101010 !important; |
| color: #FF3800 !important; |
| font-size: 13px; |
| border-radius: 5px; |
| padding: 10px; |
| overflow-x: hidden; |
| overflow: auto; |
| } |
| |
| .cm-info-note { |
| background-color: #101010 !important; |
| color: #FF3800 !important; |
| font-size: 13px; |
| border-radius: 5px; |
| padding: 10px; |
| overflow-x: hidden; |
| overflow: auto; |
| } |
| `; |
|
|
| function is_legacy_front() { |
| let compareVersion = '1.2.49'; |
| try { |
| const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__']; |
| if (typeof frontendVersion !== 'string') { |
| return false; |
| } |
|
|
| function parseVersion(versionString) { |
| const parts = versionString.split('.').map(Number); |
| return parts.length === 3 && parts.every(part => !isNaN(part)) ? parts : null; |
| } |
|
|
| const currentVersion = parseVersion(frontendVersion); |
| const comparisonVersion = parseVersion(compareVersion); |
|
|
| if (!currentVersion || !comparisonVersion) { |
| return false; |
| } |
|
|
| for (let i = 0; i < 3; i++) { |
| if (currentVersion[i] > comparisonVersion[i]) { |
| return false; |
| } else if (currentVersion[i] < comparisonVersion[i]) { |
| return true; |
| } |
| } |
|
|
| return false; |
| } catch { |
| return true; |
| } |
| } |
|
|
| document.head.appendChild(docStyle); |
|
|
| var update_comfyui_button = null; |
| var switch_comfyui_button = null; |
| var update_all_button = null; |
| var restart_stop_button = null; |
| var update_policy_combo = null; |
|
|
| let share_option = 'all'; |
| var is_updating = false; |
|
|
|
|
| |
| const style = ` |
| #workflowgallery-button { |
| height: 50px; |
| padding: 0px !important; |
| } |
| #cm-nodeinfo-button { |
| |
| } |
| #cm-manual-button { |
| |
| } |
| |
| .cm-button { |
| width: auto; |
| position: relative; |
| overflow: hidden; |
| background-color: var(--comfy-menu-secondary-bg); |
| border-color: var(--border-color); |
| color: var(--input-text); |
| } |
| |
| .cm-button:hover { |
| filter: brightness(125%); |
| } |
| |
| .cm-button-red { |
| background-color: #500000 !important; |
| border-color: #88181b !important; |
| color: white !important; |
| } |
| |
| .cm-button-red:hover { |
| background-color: #88181b !important; |
| } |
| |
| .cm-button-orange { |
| font-weight: bold; |
| background-color: orange !important; |
| color: black !important; |
| } |
| |
| .cm-experimental-button { |
| width: 100%; |
| } |
| |
| .cm-experimental { |
| border: 1px solid #555; |
| border-radius: 5px; |
| padding: 10px; |
| align-items: center; |
| text-align: center; |
| justify-content: center; |
| box-sizing: border-box; |
| } |
| |
| .cm-experimental-legend { |
| margin-top: -20px; |
| margin-left: 50%; |
| width:auto; |
| height:20px; |
| font-size: 13px; |
| font-weight: bold; |
| background-color: #990000; |
| color: #CCFFFF; |
| border-radius: 5px; |
| text-align: center; |
| transform: translateX(-50%); |
| display: block; |
| } |
| |
| .cm-menu-combo { |
| cursor: pointer; |
| padding: 0.5em 0.5em; |
| border: 1px solid var(--border-color); |
| border-radius: 6px; |
| background: var(--comfy-menu-secondary-bg); |
| } |
| |
| .cm-menu-combo:hover { |
| filter: brightness(125%); |
| } |
| |
| .cm-small-button { |
| width: 120px; |
| height: 30px; |
| position: relative; |
| overflow: hidden; |
| box-sizing: border-box; |
| font-size: 17px !important; |
| } |
| |
| #cm-install-customnodes-button { |
| width: 200px; |
| height: 30px; |
| position: relative; |
| overflow: hidden; |
| box-sizing: border-box; |
| font-size: 17px !important; |
| } |
| |
| .cm-search-filter { |
| width: 200px; |
| height: 30px !important; |
| position: relative; |
| overflow: hidden; |
| box-sizing: border-box; |
| } |
| |
| .cb-node-label { |
| width: 400px; |
| height:28px; |
| color: black; |
| background-color: #777777; |
| font-size: 18px; |
| text-align: center; |
| font-weight: bold; |
| } |
| |
| #cm-close-button { |
| width: calc(100% - 65px); |
| bottom: 10px; |
| position: absolute; |
| overflow: hidden; |
| } |
| |
| #cm-save-button { |
| width: calc(100% - 65px); |
| bottom:40px; |
| position: absolute; |
| overflow: hidden; |
| } |
| #cm-save-button:disabled { |
| background-color: #444444; |
| } |
| |
| .pysssss-workflow-arrow-2 { |
| position: absolute; |
| top: 0; |
| bottom: 0; |
| right: 0; |
| font-size: 12px; |
| display: flex; |
| align-items: center; |
| width: 24px; |
| justify-content: center; |
| background: rgba(255,255,255,0.1); |
| content: "▼"; |
| } |
| .pysssss-workflow-arrow-2:after { |
| content: "▼"; |
| } |
| .pysssss-workflow-arrow-2:hover { |
| filter: brightness(1.6); |
| background-color: var(--comfy-menu-bg); |
| } |
| .pysssss-workflow-popup-2 ~ .litecontextmenu { |
| transform: scale(1.3); |
| } |
| #workflowgallery-button-menu { |
| z-index: 10000000000 !important; |
| } |
| #cm-manual-button-menu { |
| z-index: 10000000000 !important; |
| } |
| `; |
|
|
| async function init_share_option() { |
| api.fetchApi('/manager/share_option') |
| .then(response => response.text()) |
| .then(data => { |
| share_option = data || 'all'; |
| }); |
| } |
|
|
| async function init_notice(notice) { |
| api.fetchApi('/manager/notice') |
| .then(response => response.text()) |
| .then(data => { |
| notice.innerHTML = data; |
| }) |
| } |
|
|
| await init_share_option(); |
|
|
|
|
| async function set_inprogress_mode() { |
| update_comfyui_button.disabled = true; |
| update_comfyui_button.style.backgroundColor = "gray"; |
|
|
| update_all_button.disabled = true; |
| update_all_button.style.backgroundColor = "gray"; |
|
|
| switch_comfyui_button.disabled = true; |
| switch_comfyui_button.style.backgroundColor = "gray"; |
|
|
| restart_stop_button.innerText = 'Stop'; |
| } |
|
|
|
|
| async function reset_action_buttons() { |
| const isElectron = 'electronAPI' in window; |
|
|
| if(isElectron) { |
| update_all_button.innerText = "Update All Custom Nodes"; |
| } |
| else { |
| update_all_button.innerText = "Update All"; |
| } |
|
|
| update_comfyui_button.innerText = "Update ComfyUI"; |
| switch_comfyui_button.innerText = "Switch ComfyUI"; |
| restart_stop_button.innerText = 'Restart'; |
|
|
| update_comfyui_button.disabled = false; |
| update_all_button.disabled = false; |
| switch_comfyui_button.disabled = false; |
|
|
| update_comfyui_button.style.backgroundColor = ""; |
| update_all_button.style.backgroundColor = ""; |
| switch_comfyui_button.style.backgroundColor = ""; |
| } |
|
|
| async function updateComfyUI() { |
| let prev_text = update_comfyui_button.innerText; |
| update_comfyui_button.innerText = "Updating ComfyUI..."; |
|
|
| set_inprogress_mode(); |
|
|
| const response = await api.fetchApi('/manager/queue/update_comfyui'); |
|
|
| showTerminal(); |
|
|
| is_updating = true; |
| await api.fetchApi('/manager/queue/start'); |
| } |
|
|
| function showVersionSelectorDialog(versions, current, onSelect) { |
| const dialog = new ComfyDialog(); |
| dialog.element.style.zIndex = 1100; |
| dialog.element.style.width = "300px"; |
| dialog.element.style.padding = "0"; |
| dialog.element.style.backgroundColor = "#2a2a2a"; |
| dialog.element.style.border = "1px solid #3a3a3a"; |
| dialog.element.style.borderRadius = "8px"; |
| dialog.element.style.boxSizing = "border-box"; |
| dialog.element.style.overflow = "hidden"; |
|
|
| const contentStyle = { |
| width: "300px", |
| display: "flex", |
| flexDirection: "column", |
| alignItems: "center", |
| padding: "20px", |
| boxSizing: "border-box", |
| gap: "15px" |
| }; |
|
|
| let selectedVersion = versions[0]; |
|
|
| const versionList = $el("select", { |
| multiple: true, |
| size: Math.min(10, versions.length), |
| style: { |
| width: "260px", |
| height: "auto", |
| backgroundColor: "#383838", |
| color: "#ffffff", |
| border: "1px solid #4a4a4a", |
| borderRadius: "4px", |
| padding: "5px", |
| boxSizing: "border-box" |
| } |
| }, |
| versions.map((v, index) => $el("option", { |
| value: v, |
| textContent: v, |
| selected: v === current |
| })) |
| ); |
|
|
| versionList.addEventListener('change', (e) => { |
| selectedVersion = e.target.value; |
| Array.from(e.target.options).forEach(opt => { |
| opt.selected = opt.value === selectedVersion; |
| }); |
| }); |
|
|
| const content = $el("div", { |
| style: contentStyle |
| }, [ |
| $el("h3", { |
| textContent: "Select Version", |
| style: { |
| color: "#ffffff", |
| backgroundColor: "#1a1a1a", |
| padding: "10px 15px", |
| margin: "0 0 10px 0", |
| width: "260px", |
| textAlign: "center", |
| borderRadius: "4px", |
| boxSizing: "border-box", |
| whiteSpace: "nowrap", |
| overflow: "hidden", |
| textOverflow: "ellipsis" |
| } |
| }), |
| versionList, |
| $el("div", { |
| style: { |
| display: "flex", |
| justifyContent: "space-between", |
| width: "260px", |
| gap: "10px" |
| } |
| }, [ |
| $el("button", { |
| textContent: "Cancel", |
| onclick: () => dialog.close(), |
| style: { |
| flex: "1", |
| padding: "8px", |
| backgroundColor: "#4a4a4a", |
| color: "#ffffff", |
| border: "none", |
| borderRadius: "4px", |
| cursor: "pointer", |
| whiteSpace: "nowrap", |
| overflow: "hidden", |
| textOverflow: "ellipsis" |
| } |
| }), |
| $el("button", { |
| textContent: "Select", |
| onclick: () => { |
| if (selectedVersion) { |
| onSelect(selectedVersion); |
| dialog.close(); |
| } else { |
| customAlert("Please select a version."); |
| } |
| }, |
| style: { |
| flex: "1", |
| padding: "8px", |
| backgroundColor: "#4CAF50", |
| color: "#ffffff", |
| border: "none", |
| borderRadius: "4px", |
| cursor: "pointer", |
| whiteSpace: "nowrap", |
| overflow: "hidden", |
| textOverflow: "ellipsis" |
| } |
| }), |
| ]) |
| ]); |
|
|
| dialog.show(content); |
| } |
|
|
| async function switchComfyUI() { |
| switch_comfyui_button.disabled = true; |
| switch_comfyui_button.style.backgroundColor = "gray"; |
| |
| let res = await api.fetchApi(`/comfyui_manager/comfyui_versions`, { cache: "no-store" }); |
|
|
| switch_comfyui_button.disabled = false; |
| switch_comfyui_button.style.backgroundColor = ""; |
|
|
| if(res.status == 200) { |
| let obj = await res.json(); |
|
|
| let versions = []; |
| let default_version; |
|
|
| for(let v of obj.versions) { |
| default_version = v; |
| versions.push(v); |
| } |
|
|
| showVersionSelectorDialog(versions, obj.current, async (selected_version) => { |
| if(selected_version == 'nightly') { |
| update_policy_combo.value = 'nightly-comfyui'; |
| api.fetchApi('/manager/policy/update?value=nightly-comfyui'); |
| } |
| else { |
| update_policy_combo.value = 'stable-comfyui'; |
| api.fetchApi('/manager/policy/update?value=stable-comfyui'); |
| } |
|
|
| let response = await api.fetchApi(`/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" }); |
| if (response.status == 200) { |
| infoToast(`ComfyUI version is switched to ${selected_version}`); |
| } |
| else { |
| customAlert('Failed to switch ComfyUI version.'); |
| } |
| }); |
| } |
| else { |
| customAlert('Failed to fetch ComfyUI versions.'); |
| } |
| } |
|
|
| async function onQueueStatus(event) { |
| const isElectron = 'electronAPI' in window; |
|
|
| if(event.detail.status == 'in_progress') { |
| set_inprogress_mode(); |
| update_all_button.innerText = `in progress.. (${event.detail.done_count}/${event.detail.total_count})`; |
| } |
| else if(event.detail.status == 'done') { |
| reset_action_buttons(); |
|
|
| if(!is_updating) { |
| return; |
| } |
|
|
| is_updating = false; |
|
|
| let success_list = []; |
| let failed_list = []; |
| let comfyui_state = null; |
|
|
| for(let k in event.detail.nodepack_result){ |
| let v = event.detail.nodepack_result[k]; |
|
|
| if(k == 'comfyui') { |
| comfyui_state = v; |
| continue; |
| } |
|
|
| if(v.msg == 'success') { |
| success_list.push(k); |
| } |
| else if(v.msg != 'skip') |
| failed_list.push(k); |
| } |
|
|
| let msg = ""; |
| |
| if(success_list.length == 0 && comfyui_state.startsWith('skip')) { |
| if(failed_list.length == 0) { |
| msg += "You are already up to date."; |
| } |
| } |
| else { |
| msg = "To apply the updates, you need to <button class='cm-small-button' id='cm-reboot-button5'>RESTART</button> ComfyUI.<hr>"; |
|
|
| if(comfyui_state == 'success-nightly') { |
| msg += "ComfyUI has been updated to latest nightly version.<BR><BR>"; |
| infoToast("ComfyUI has been updated to the latest nightly version."); |
| } |
| else if(comfyui_state.startsWith('success-stable')) { |
| const ver = comfyui_state.split("-").pop(); |
| msg += `ComfyUI has been updated to ${ver}.<BR><BR>`; |
| infoToast(`ComfyUI has been updated to ${ver}`); |
| } |
| else if(comfyui_state == 'skip') { |
| msg += "ComfyUI is already up to date.<BR><BR>" |
| } |
| else if(comfyui_state != null) { |
| msg += "Failed to update ComfyUI.<BR><BR>" |
| } |
|
|
| if(success_list.length > 0) { |
| msg += "The following custom nodes have been updated:<ul>"; |
| for(let x in success_list) { |
| let k = success_list[x]; |
| let url = event.detail.nodepack_result[k].url; |
| let title = event.detail.nodepack_result[k].title; |
| if(url) { |
| msg += `<li><a href='${url}' target='_blank'>${title}</a></li>`; |
| } |
| else { |
| msg += `<li>${k}</li>`; |
| } |
| } |
| msg += "</ul>"; |
| } |
|
|
| setNeedRestart(true); |
| } |
| |
| if(failed_list.length > 0) { |
| msg += '<br>The update for the following custom nodes has failed:<ul>'; |
| for(let x in failed_list) { |
| let k = failed_list[x]; |
| let url = event.detail.nodepack_result[k].url; |
| let title = event.detail.nodepack_result[k].title; |
| if(url) { |
| msg += `<li><a href='${url}' target='_blank'>${title}</a></li>`; |
| } |
| else { |
| msg += `<li>${k}</li>`; |
| } |
| } |
|
|
| msg += '</ul>' |
| } |
|
|
| show_message(msg); |
|
|
| const rebootButton = document.getElementById('cm-reboot-button5'); |
| rebootButton?.addEventListener("click", |
| async function() { |
| if(await rebootAPI()) { |
| manager_instance.close(); |
| } |
| }); |
| } |
| } |
|
|
| api.addEventListener("cm-queue-status", onQueueStatus); |
|
|
|
|
| async function updateAll(update_comfyui) { |
| update_all_button.innerText = "Updating..."; |
|
|
| set_inprogress_mode(); |
|
|
| var mode = manager_instance.datasrc_combo.value; |
|
|
| showTerminal(); |
|
|
| if(update_comfyui) { |
| update_all_button.innerText = "Updating ComfyUI..."; |
| await api.fetchApi('/manager/queue/update_comfyui'); |
| } |
|
|
| const response = await api.fetchApi(`/manager/queue/update_all?mode=${mode}`); |
|
|
| if (response.status == 403) { |
| await handle403Response(response); |
| reset_action_buttons(); |
| } |
| else if (response.status == 401) { |
| customAlert('Another task is already in progress. Please stop the ongoing task first.'); |
| reset_action_buttons(); |
| } |
| else if(response.status == 200) { |
| is_updating = true; |
| await api.fetchApi('/manager/queue/start'); |
| } |
| } |
|
|
| function newDOMTokenList(initialTokens) { |
| const tmp = document.createElement(`div`); |
|
|
| const classList = tmp.classList; |
| if (initialTokens) { |
| initialTokens.forEach(token => { |
| classList.add(token); |
| }); |
| } |
|
|
| return classList; |
| } |
|
|
| |
| |
| |
| const isOutputNode = (node) => { |
| return SUPPORTED_OUTPUT_NODE_TYPES.includes(node.type); |
| } |
|
|
| function restartOrStop() { |
| if(restart_stop_button.innerText == 'Restart'){ |
| rebootAPI(); |
| } |
| else { |
| api.fetchApi('/manager/queue/reset'); |
| infoToast('Cancel', 'Remaining tasks will stop after completing the current task.'); |
| } |
| } |
|
|
| |
| class ManagerMenuDialog extends ComfyDialog { |
| createControlsMid() { |
| let self = this; |
| const isElectron = 'electronAPI' in window; |
| |
| update_comfyui_button = |
| $el("button.p-button.p-component.cm-button", { |
| type: "button", |
| textContent: "Update ComfyUI", |
| style: { |
| display: isElectron ? 'none' : 'block' |
| }, |
| onclick: |
| () => updateComfyUI() |
| }); |
|
|
| switch_comfyui_button = |
| $el("button.p-button.p-component.cm-button", { |
| type: "button", |
| textContent: "Switch ComfyUI", |
| style: { |
| display: isElectron ? 'none' : 'block' |
| }, |
| onclick: |
| () => switchComfyUI() |
| }); |
|
|
| restart_stop_button = |
| $el("button.p-button.p-component.cm-button-red", { |
| type: "button", |
| textContent: "Restart", |
| onclick: () => restartOrStop() |
| }); |
|
|
| if(isElectron) { |
| update_all_button = |
| $el("button.p-button.p-component.cm-button", { |
| type: "button", |
| textContent: "Update All Custom Nodes", |
| onclick: |
| () => updateAll(false) |
| }); |
| } |
| else { |
| update_all_button = |
| $el("button.p-button.p-component.cm-button", { |
| type: "button", |
| textContent: "Update All", |
| onclick: |
| () => updateAll(true) |
| }); |
| } |
|
|
| const res = |
| [ |
| $el("button.p-button.p-component.cm-button", { |
| type: "button", |
| textContent: "Custom Nodes Manager", |
| onclick: |
| () => { |
| if(!CustomNodesManager.instance) { |
| CustomNodesManager.instance = new CustomNodesManager(app, self); |
| } |
| CustomNodesManager.instance.show(CustomNodesManager.ShowMode.NORMAL); |
| } |
| }), |
|
|
| $el("button.p-button.p-component.cm-button", { |
| type: "button", |
| textContent: "Install Missing Custom Nodes", |
| onclick: |
| () => { |
| if(!CustomNodesManager.instance) { |
| CustomNodesManager.instance = new CustomNodesManager(app, self); |
| } |
| CustomNodesManager.instance.show(CustomNodesManager.ShowMode.MISSING); |
| } |
| }), |
|
|
| $el("button.p-button.p-component.cm-button", { |
| type: "button", |
| textContent: "Custom Nodes In Workflow", |
| onclick: |
| () => { |
| if(!CustomNodesManager.instance) { |
| CustomNodesManager.instance = new CustomNodesManager(app, self); |
| } |
| CustomNodesManager.instance.show(CustomNodesManager.ShowMode.IN_WORKFLOW); |
| } |
| }), |
| |
| $el("div", {}, []), |
| $el("button.p-button.p-component.cm-button", { |
| type: "button", |
| textContent: "Model Manager", |
| onclick: |
| () => { |
| if(!ModelManager.instance) { |
| ModelManager.instance = new ModelManager(app, self); |
| } |
| ModelManager.instance.show(); |
| } |
| }), |
|
|
| $el("button.p-button.p-component.cm-button", { |
| type: "button", |
| textContent: "Install via Git URL", |
| onclick: async () => { |
| var url = await customPrompt("Please enter the URL of the Git repository to install", ""); |
|
|
| if (url !== null) { |
| install_via_git_url(url, self); |
| } |
| } |
| }), |
|
|
| $el("div", {}, []), |
| update_all_button, |
| update_comfyui_button, |
| switch_comfyui_button, |
| |
|
|
| $el("div", {}, []), |
| restart_stop_button, |
| ]; |
|
|
| return res; |
| } |
|
|
| createControlsLeft() { |
| const isElectron = 'electronAPI' in window; |
|
|
| let self = this; |
|
|
| |
|
|
| this.datasrc_combo = document.createElement("select"); |
| this.datasrc_combo.setAttribute("title", "Configure where to retrieve node/model information. If set to 'local,' the channel is ignored, and if set to 'channel (remote),' it fetches the latest information each time the list is opened."); |
| this.datasrc_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled "; |
| this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'Channel (1day cache)' }, [])); |
| this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'Local' }, [])); |
| this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'Channel (remote)' }, [])); |
|
|
| api.fetchApi('/manager/db_mode') |
| .then(response => response.text()) |
| .then(data => { this.datasrc_combo.value = data; }); |
|
|
| this.datasrc_combo.addEventListener('change', function (event) { |
| api.fetchApi(`/manager/db_mode?value=${event.target.value}`); |
| }); |
|
|
| const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo); |
|
|
| |
| let preview_combo = document.createElement("select"); |
| preview_combo.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process."); |
| preview_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled"; |
|
|
| |
| preview_combo.appendChild($el('option', { value: '', text: 'Loading...', disabled: true }, [])); |
| preview_combo.appendChild($el('option', { value: 'auto', text: 'Auto' }, [])); |
| preview_combo.appendChild($el('option', { value: 'taesd', text: 'TAESD (slow)' }, [])); |
| preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Latent2RGB (fast)' }, [])); |
| preview_combo.appendChild($el('option', { value: 'none', text: 'None (very fast)' }, [])); |
|
|
| |
| preview_combo.disabled = true; |
| preview_combo.value = ''; |
|
|
| |
| api.fetchApi('/manager/preview_method') |
| .then(response => response.text()) |
| .then(data => { |
| |
| preview_combo.querySelector('option[value=""]')?.remove(); |
|
|
| if (data === "DISABLED") { |
| |
| preview_combo.disabled = true; |
| preview_combo.value = 'auto'; |
|
|
| |
| preview_combo.setAttribute("aria-disabled", "true"); |
| preview_combo.setAttribute("aria-label", |
| "Preview method setting (disabled - managed by ComfyUI). " + |
| "Use Settings > Execution > Live preview method instead." |
| ); |
|
|
| |
| preview_combo.setAttribute("title", |
| "This feature is now provided natively by ComfyUI. " + |
| "Please use 'Settings > Execution > Live preview method' instead." |
| ); |
|
|
| |
| preview_combo.style.opacity = '0.6'; |
| preview_combo.style.cursor = 'not-allowed'; |
| } else { |
| |
| preview_combo.disabled = false; |
| preview_combo.value = data; |
|
|
| |
| preview_combo.setAttribute("aria-label", |
| "Preview method setting. Select how latent variables are decoded during preview." |
| ); |
| } |
| }) |
| .catch(error => { |
| console.error('[ComfyUI-Manager] Failed to fetch preview method status:', error); |
| |
| preview_combo.querySelector('option[value=""]')?.remove(); |
| preview_combo.disabled = false; |
| preview_combo.value = 'auto'; |
| }); |
|
|
| preview_combo.addEventListener('change', function (event) { |
| |
| if (preview_combo.disabled) { |
| event.preventDefault(); |
| return; |
| } |
|
|
| |
| api.fetchApi(`/manager/preview_method?value=${event.target.value}`) |
| .then(response => { |
| if (response.status === 403) { |
| |
| alert( |
| 'This feature is now provided natively by ComfyUI.\n' + |
| 'Please use \'Settings > Execution > Live preview method\' instead.' |
| ); |
| preview_combo.disabled = true; |
| preview_combo.style.opacity = '0.6'; |
| preview_combo.style.cursor = 'not-allowed'; |
|
|
| |
| preview_combo.setAttribute("aria-disabled", "true"); |
| preview_combo.setAttribute("aria-label", |
| "Preview method setting (disabled - managed by ComfyUI). " + |
| "Use Settings > Execution > Live preview method instead." |
| ); |
| } |
| }) |
| .catch(error => { |
| console.error('[ComfyUI-Manager] Preview method update failed:', error); |
| }); |
| }); |
|
|
| const previewSetttingItem = createSettingsCombo("Preview method", preview_combo); |
|
|
| |
| let channel_combo = document.createElement("select"); |
| channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list."); |
| channel_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled"; |
| api.fetchApi('/manager/channel_url_list') |
| .then(response => response.json()) |
| .then(async data => { |
| try { |
| let urls = data.list; |
| for (let i in urls) { |
| if (urls[i] != '') { |
| let name_url = urls[i].split('::'); |
| channel_combo.appendChild($el('option', { value: name_url[0], text: `${name_url[0]}` }, [])); |
| } |
| } |
|
|
| channel_combo.addEventListener('change', function (event) { |
| api.fetchApi(`/manager/channel_url_list?value=${event.target.value}`); |
| }); |
|
|
| channel_combo.value = data.selected; |
| } |
| catch (exception) { |
|
|
| } |
| }); |
|
|
| const channelSetttingItem = createSettingsCombo("Channel", channel_combo); |
|
|
|
|
| |
| let share_combo = document.createElement("select"); |
| share_combo.setAttribute("title", "Hide the share button in the main menu or set the default action upon clicking it. Additionally, configure the default share site when sharing via the context menu's share button."); |
| share_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled"; |
| const share_options = [ |
| ['none', 'None'], |
| ['openart', 'OpenArt AI'], |
| ['youml', 'YouML'], |
| ['matrix', 'Matrix Server'], |
| ['comfyworkflows', 'ComfyWorkflows'], |
| ['copus', 'Copus'], |
| ['all', 'All'], |
| ]; |
| for (const option of share_options) { |
| share_combo.appendChild($el('option', { value: option[0], text: `${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; |
| share_option = value; |
| api.fetchApi(`/manager/share_option?value=${value}`); |
| const shareButton = document.getElementById("shareButton"); |
| if (value === 'none') { |
| shareButton.style.display = "none"; |
| } else { |
| shareButton.style.display = "inline-block"; |
| } |
| }); |
|
|
| const shareSetttingItem = createSettingsCombo("Share", share_combo); |
|
|
| let component_policy_combo = document.createElement("select"); |
| component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use."); |
| component_policy_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled"; |
| component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Use workflow version' }, [])); |
| component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Use higher version' }, [])); |
| component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Use my version' }, [])); |
| api.fetchApi('/manager/policy/component') |
| .then(response => response.text()) |
| .then(data => { |
| component_policy_combo.value = data; |
| set_component_policy(data); |
| }); |
|
|
| component_policy_combo.addEventListener('change', function (event) { |
| api.fetchApi(`/manager/policy/component?value=${event.target.value}`); |
| set_component_policy(event.target.value); |
| }); |
|
|
| const componentSetttingItem = createSettingsCombo("Component", component_policy_combo); |
|
|
| update_policy_combo = document.createElement("select"); |
| |
| update_policy_combo.setAttribute("title", "Sets the policy to be applied when performing an update."); |
| update_policy_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled"; |
| update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'ComfyUI Stable Version' }, [])); |
| update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'ComfyUI Nightly Version' }, [])); |
| api.fetchApi('/manager/policy/update') |
| .then(response => response.text()) |
| .then(data => { |
| update_policy_combo.value = data; |
| }); |
|
|
| update_policy_combo.addEventListener('change', function (event) { |
| api.fetchApi(`/manager/policy/update?value=${event.target.value}`); |
| }); |
|
|
| const updateSetttingItem = createSettingsCombo("Update", update_policy_combo); |
| |
| if(isElectron) |
| updateSetttingItem.style.display = 'none'; |
|
|
| return [ |
| dbRetrievalSetttingItem, |
| channelSetttingItem, |
| previewSetttingItem, |
| shareSetttingItem, |
| componentSetttingItem, |
| updateSetttingItem, |
| |
| $el("filedset.cm-experimental.mt-auto", {}, [ |
| $el("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]), |
| $el("button.p-button.p-component.cm-button.cm-experimental-button", { |
| type: "button", |
| textContent: "Snapshot Manager", |
| onclick: |
| () => { |
| if(!SnapshotManager.instance) |
| SnapshotManager.instance = new SnapshotManager(app, self); |
| SnapshotManager.instance.show(); |
| } |
| }), |
| $el("button.p-button.p-component.cm-button.cm-experimental-button.mt-2", { |
| type: "button", |
| textContent: "Install PIP packages", |
| onclick: |
| async () => { |
| var url = await customPrompt("Please enumerate the pip packages to be installed.\n\nExample: insightface opencv-python-headless>=4.1.1\n", ""); |
|
|
| if (url !== null) { |
| install_pip(url, self); |
| } |
| } |
| }) |
| ]), |
| ]; |
| } |
|
|
| createControlsRight() { |
| const elts = [ |
| $el("button.p-button.p-component.cm-button", { |
| id: 'cm-manual-button', |
| type: "button", |
| textContent: "Community Manual", |
| onclick: () => { window.open("https://blenderneko.github.io/ComfyUI-docs/", "comfyui-community-manual"); } |
| }, [ |
| $el("div.pysssss-workflow-arrow-2", { |
| id: `cm-manual-button-arrow`, |
| onclick: (e) => { |
| e.preventDefault(); |
| e.stopPropagation(); |
|
|
| LiteGraph.closeAllContextMenus(); |
| const menu = new LiteGraph.ContextMenu( |
| [ |
| { |
| title: "ComfyUI Docs", |
| callback: () => { window.open("https://docs.comfy.org/", "comfyui-official-manual"); }, |
| }, |
| { |
| title: "Comfy Custom Node How To", |
| callback: () => { window.open("https://github.com/chrisgoringe/Comfy-Custom-Node-How-To/wiki/aaa_index", "comfyui-community-manual1"); }, |
| }, |
| { |
| title: "ComfyUI Guide To Making Custom Nodes", |
| callback: () => { window.open("https://github.com/Suzie1/ComfyUI_Guide_To_Making_Custom_Nodes/wiki", "comfyui-community-manual2"); }, |
| }, |
| { |
| title: "ComfyUI Examples", |
| callback: () => { window.open("https://comfyanonymous.github.io/ComfyUI_examples", "comfyui-community-manual3"); }, |
| }, |
| { |
| title: "Close", |
| callback: () => { |
| LiteGraph.closeAllContextMenus(); |
| }, |
| } |
| ], |
| { |
| event: e, |
| scale: 1.3, |
| }, |
| window |
| ); |
| |
| menu.root.id = "cm-manual-button-menu"; |
| menu.root.classList.add("pysssss-workflow-popup-2"); |
| }, |
| }) |
| ]), |
|
|
| $el("button.p-button.p-component.cm-button", { |
| id: 'workflowgallery-button', |
| type: "button", |
| style: { |
| |
| }, |
| onclick: (e) => { |
| const last_visited_site = localStorage.getItem("wg_last_visited") |
| if (!!last_visited_site) { |
| window.open(last_visited_site, last_visited_site); |
| } else { |
| this.handleWorkflowGalleryButtonClick(e) |
| } |
| }, |
| }, [ |
| $el("p", { |
| textContent: 'Workflow Gallery', |
| style: { |
| 'text-align': 'center', |
| 'color': 'var(--input-text)', |
| 'font-size': '18px', |
| 'margin': 0, |
| 'padding': 0, |
| } |
| }, [ |
| $el("p", { |
| id: 'workflowgallery-button-last-visited-label', |
| textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : 'none selected'})`, |
| style: { |
| 'text-align': 'center', |
| 'color': 'var(--input-text)', |
| 'font-size': '12px', |
| 'margin': 0, |
| 'padding': 0, |
| } |
| }) |
| ]), |
| $el("div.pysssss-workflow-arrow-2", { |
| id: `comfyworkflows-button-arrow`, |
| onclick: this.handleWorkflowGalleryButtonClick |
| }) |
| ]), |
|
|
| $el("button.p-button.p-component.cm-button", { |
| id: 'cm-nodeinfo-button', |
| type: "button", |
| textContent: "Nodes Info", |
| onclick: () => { window.open("https://ltdrdata.github.io/", "comfyui-node-info"); } |
| }), |
| ]; |
|
|
| var textarea = document.createElement("div"); |
| textarea.className = "cm-notice-board"; |
| elts.push(textarea); |
|
|
| init_notice(textarea); |
|
|
| return elts; |
| } |
|
|
| constructor() { |
| super(); |
|
|
| const content = $el("div.cm-menu-container", |
| [ |
| $el("div.cm-menu-column.gap-2", [...this.createControlsLeft()]), |
| $el("div.cm-menu-column.gap-2", [...this.createControlsMid()]), |
| $el("div.cm-menu-column.gap-2", [...this.createControlsRight()]) |
| ] |
| ); |
|
|
| const frame = buildGuiFrame( |
| 'cm-manager-dialog', |
| `ComfyUI Manager ${manager_version}`, |
| "i.mdi.mdi-puzzle", |
| content, |
| this |
| ); |
|
|
| this.element = frame; |
| } |
|
|
| get isVisible() { |
| return this.element?.style?.display !== "none"; |
| } |
|
|
| show() { |
| this.element.style.display = "flex"; |
| } |
|
|
| toggleVisibility() { |
| if (this.isVisible) { |
| this.close(); |
| } else { |
| this.show(); |
| } |
| } |
|
|
| handleWorkflowGalleryButtonClick(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| LiteGraph.closeAllContextMenus(); |
|
|
| |
| |
| const modifyButtonStyle = (url) => { |
| const workflowGalleryButton = document.getElementById('workflowgallery-button'); |
| workflowGalleryButton.style.height = '50px'; |
| const lastVisitedLabel = document.getElementById('workflowgallery-button-last-visited-label'); |
| lastVisitedLabel.textContent = `(${url.split('/')[2]})`; |
| } |
|
|
| const menu = new LiteGraph.ContextMenu( |
| [ |
| { |
| title: "Share your art", |
| callback: () => { |
| if (share_option === 'openart') { |
| showOpenArtShareDialog(); |
| return; |
| } else if (share_option === 'matrix' || share_option === 'comfyworkflows') { |
| showShareDialog(share_option); |
| return; |
| } else if (share_option === 'youml') { |
| showYouMLShareDialog(); |
| return; |
| } |
|
|
| if (!ShareDialogChooser.instance) { |
| ShareDialogChooser.instance = new ShareDialogChooser(); |
| } |
| ShareDialogChooser.instance.show(); |
| }, |
| }, |
| { |
| title: "Open 'openart.ai'", |
| callback: () => { |
| const url = "https://openart.ai/workflows/dev"; |
| localStorage.setItem("wg_last_visited", url); |
| window.open(url, url); |
| modifyButtonStyle(url); |
| }, |
| }, |
| { |
| title: "Open 'youml.com'", |
| callback: () => { |
| const url = "https://youml.com/?from=comfyui-share"; |
| localStorage.setItem("wg_last_visited", url); |
| window.open(url, url); |
| modifyButtonStyle(url); |
| }, |
| }, |
| { |
| title: "Open 'comfyworkflows.com'", |
| callback: () => { |
| const url = "https://comfyworkflows.com/"; |
| localStorage.setItem("wg_last_visited", url); |
| window.open(url, url); |
| modifyButtonStyle(url); |
| }, |
| }, |
| { |
| title: "Open 'esheep'", |
| callback: () => { |
| const url = "https://www.esheep.com"; |
| localStorage.setItem("wg_last_visited", url); |
| window.open(url, url); |
| modifyButtonStyle(url); |
| }, |
| }, |
| { |
| title: "Open 'Copus.io'", |
| callback: () => { |
| const url = "https://www.copus.io"; |
| localStorage.setItem("wg_last_visited", url); |
| window.open(url, url); |
| modifyButtonStyle(url); |
| }, |
| }, |
| { |
| title: "Close", |
| callback: () => { |
| LiteGraph.closeAllContextMenus(); |
| }, |
| } |
| ], |
| { |
| event: e, |
| scale: 1.3, |
| }, |
| window |
| ); |
| |
| menu.root.id = "workflowgallery-button-menu"; |
| menu.root.classList.add("pysssss-workflow-popup-2"); |
| } |
| } |
|
|
| async function getVersion() { |
| let version = await api.fetchApi(`/manager/version`); |
| return await version.text(); |
| } |
|
|
| app.registerExtension({ |
| name: "Comfy.ManagerMenu", |
|
|
| aboutPageBadges: [ |
| { |
| label: `ComfyUI-Manager ${manager_version}`, |
| url: 'https://github.com/ltdrdata/ComfyUI-Manager', |
| icon: 'pi pi-th-large' |
| } |
| ], |
|
|
| commands: [ |
| { |
| id: "Comfy.Manager.Menu.ToggleVisibility", |
| label: "Toggle Manager Menu Visibility", |
| icon: "mdi mdi-puzzle", |
| function: () => { |
| if (!manager_instance) { |
| setManagerInstance(new ManagerMenuDialog()); |
| manager_instance.show(); |
| } else { |
| manager_instance.toggleVisibility(); |
| } |
| }, |
| }, |
| { |
| id: "Comfy.Manager.CustomNodesManager.ToggleVisibility", |
| label: "Toggle Custom Nodes Manager Visibility", |
| icon: "pi pi-server", |
| function: () => { |
| if (CustomNodesManager.instance?.isVisible) { |
| CustomNodesManager.instance.close(); |
| return; |
| } |
|
|
| if (!manager_instance) { |
| setManagerInstance(new ManagerMenuDialog()); |
| } |
| if (!CustomNodesManager.instance) { |
| CustomNodesManager.instance = new CustomNodesManager(app, self); |
| } |
| CustomNodesManager.instance.show(CustomNodesManager.ShowMode.NORMAL); |
| }, |
| } |
| ], |
|
|
| init() { |
| $el("style", { |
| textContent: style, |
| parent: document.head, |
| }); |
| }, |
| async setup() { |
| let orig_clear = app.graph.clear; |
| app.graph.clear = function () { |
| orig_clear.call(app.graph); |
| load_components(); |
| }; |
|
|
| load_components(); |
|
|
| |
| |
| const showStartupAlerts = async () => { |
| let toastWaitCount = 0; |
| const waitForToast = () => { |
| if (window['app']?.extensionManager?.toast) { |
| fetch('/manager/startup_alerts') |
| .then(response => response.ok ? response.json() : []) |
| .then(alerts => { |
| for (const alert of alerts) { |
| customAlert(alert.message); |
| } |
| }) |
| .catch(e => console.warn('[ComfyUI-Manager] Failed to fetch startup alerts:', e)); |
| } else if (toastWaitCount < 300) { |
| toastWaitCount++; |
| setTimeout(waitForToast, 100); |
| } else { |
| console.warn('[ComfyUI-Manager] Timeout waiting for toast. Startup alerts skipped.'); |
| } |
| }; |
| waitForToast(); |
| }; |
| showStartupAlerts(); |
|
|
| const menu = document.querySelector(".comfy-menu"); |
| const separator = document.createElement("hr"); |
|
|
| separator.style.margin = "20px 0"; |
| separator.style.width = "100%"; |
| menu.append(separator); |
|
|
| try { |
| |
| |
| let cmGroup = new (await import("../../scripts/ui/components/buttonGroup.js")).ComfyButtonGroup( |
| new(await import("../../scripts/ui/components/button.js")).ComfyButton({ |
| icon: "puzzle", |
| action: () => { |
| if(!manager_instance) |
| setManagerInstance(new ManagerMenuDialog()); |
| manager_instance.show(); |
| }, |
| tooltip: "ComfyUI Manager", |
| content: "Manager", |
| classList: "comfyui-button comfyui-menu-mobile-collapse primary" |
| }).element, |
| new(await import("../../scripts/ui/components/button.js")).ComfyButton({ |
| icon: "star", |
| action: () => { |
| if(!manager_instance) |
| setManagerInstance(new ManagerMenuDialog()); |
|
|
| if(!CustomNodesManager.instance) { |
| CustomNodesManager.instance = new CustomNodesManager(app, self); |
| } |
| CustomNodesManager.instance.show(CustomNodesManager.ShowMode.FAVORITES); |
| }, |
| tooltip: "Show favorite custom node list" |
| }).element, |
| new(await import("../../scripts/ui/components/button.js")).ComfyButton({ |
| icon: "vacuum-outline", |
| action: () => { |
| free_models(); |
| }, |
| tooltip: "Unload Models" |
| }).element, |
| new(await import("../../scripts/ui/components/button.js")).ComfyButton({ |
| icon: "vacuum", |
| action: () => { |
| free_models(true); |
| }, |
| tooltip: "Free model and node cache" |
| }).element, |
| new(await import("../../scripts/ui/components/button.js")).ComfyButton({ |
| icon: "share", |
| action: () => { |
| if (share_option === 'openart') { |
| showOpenArtShareDialog(); |
| return; |
| } else if (share_option === 'matrix' || share_option === 'comfyworkflows') { |
| showShareDialog(share_option); |
| return; |
| } else if (share_option === 'youml') { |
| showYouMLShareDialog(); |
| return; |
| } |
|
|
| if(!ShareDialogChooser.instance) { |
| ShareDialogChooser.instance = new ShareDialogChooser(); |
| } |
| ShareDialogChooser.instance.show(); |
| }, |
| tooltip: "Share" |
| }).element |
| ); |
|
|
| app.menu?.settingsGroup.element.before(cmGroup.element); |
| } |
| catch(exception) { |
| console.log('ComfyUI is outdated. New style menu based features are disabled.'); |
| } |
|
|
| |
| const managerButton = document.createElement("button"); |
| managerButton.textContent = "Manager"; |
| managerButton.onclick = () => { |
| if(!manager_instance) |
| setManagerInstance(new ManagerMenuDialog()); |
| manager_instance.show(); |
| } |
| menu.append(managerButton); |
|
|
| const shareButton = document.createElement("button"); |
| shareButton.id = "shareButton"; |
| shareButton.textContent = "Share"; |
| shareButton.onclick = () => { |
| if (share_option === 'openart') { |
| showOpenArtShareDialog(); |
| return; |
| } else if (share_option === 'matrix' || share_option === 'comfyworkflows') { |
| showShareDialog(share_option); |
| return; |
| } else if (share_option === 'youml') { |
| showYouMLShareDialog(); |
| return; |
| } |
|
|
| if(!ShareDialogChooser.instance) { |
| ShareDialogChooser.instance = new ShareDialogChooser(); |
| } |
| ShareDialogChooser.instance.show(); |
| } |
| |
| shareButton.style.background = "linear-gradient(90deg, #00C9FF 0%, #92FE9D 100%)"; |
| shareButton.style.color = "black"; |
|
|
| |
| |
| const shouldShowShareButton = share_option !== 'none'; |
| shareButton.style.display = shouldShowShareButton ? "inline-block" : "none"; |
|
|
| menu.append(shareButton); |
| }, |
|
|
| async beforeRegisterNodeDef(nodeType, nodeData, app) { |
| this._addExtraNodeContextMenu(nodeType, app); |
| }, |
|
|
| _addExtraNodeContextMenu(node, app) { |
| const origGetExtraMenuOptions = node.prototype.getExtraMenuOptions; |
| node.prototype.cm_menu_added = true; |
| node.prototype.getExtraMenuOptions = function (_, options) { |
| origGetExtraMenuOptions?.apply?.(this, arguments); |
|
|
| if (node.category.startsWith('group nodes>')) { |
| options.push({ |
| content: "Save As Component", |
| callback: (obj) => { |
| if (!ComponentBuilderDialog.instance) { |
| ComponentBuilderDialog.instance = new ComponentBuilderDialog(); |
| } |
| ComponentBuilderDialog.instance.target_node = node; |
| ComponentBuilderDialog.instance.show(); |
| } |
| }, null); |
| } |
|
|
| if (isOutputNode(node)) { |
| const { potential_outputs } = getPotentialOutputsAndOutputNodes([this]); |
| const hasOutput = potential_outputs.length > 0; |
|
|
| |
| |
| if (options[options.length - 1] !== null) { |
| options.push(null); |
| } |
|
|
| 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); |
| } |
| } |
| }, |
| }); |
|
|