diff --git a/js/comfyui-gui-builder.js b/js/comfyui-gui-builder.js new file mode 100644 index 00000000..73357edd --- /dev/null +++ b/js/comfyui-gui-builder.js @@ -0,0 +1,227 @@ +import { $el } from "../../scripts/ui.js"; + +function normalizeContent(content) { + const tmp = document.createElement('div'); + if (typeof content === 'string') { + tmp.innerHTML = content; + return Array.from(tmp.childNodes); + } + if (content instanceof Node) { + return content; + } + return content; +} + +export function createSettingsCombo(label, content) { + const settingItem = $el("div.setting-item", {}, [ + $el("div.flex.flex-row.items-center.gap-2",[ + $el("div.form-label.flex.grow.items-center", [ + $el("span.text-muted", { textContent: label },) + ]), + $el("div.form-input.flex.justify-end", + [content] + ) + ] + ) + ]); + return settingItem; +} + +export function buildGuiFrame(dialogId, title, iconClass, content, owner) { + const dialog_mask = $el("div.p-dialog-mask.p-overlay-mask.p-overlay-mask-enter", { + parent: document.body, + style: { + position: "fixed", + height: "100%", + width: "100%", + left: "0px", + top: "0px", + display: "flex", + justifyContent: "center", + alignItems: "center", + pointerEvents: "auto", + zIndex: "1000" + }, + onclick: (e) => { + if (e.target === dialog_mask) { + owner.close(); + } + } + // data-pc-section="mask" + }); + + const header_actions = $el("div.p-dialog-header-actions", { + // [TODO] + // data-pc-section="headeractions" + } + ); + + const close_button = $el("button.p-button.p-component.p-button-icon-only.p-button-secondary.p-button-rounded.p-button-text.p-dialog-close-button", { + parent: header_actions, + type: "button", + ariaLabel: "Close", + onclick: () => owner.close(), + // "data-pc-name": "pcclosebutton", + // "data-p-disabled": "false", + // "data-p-severity": "secondary", + // "data-pc-group-section": "headericon", + // "data-pc-extend": "button", + // "data-pc-section": "root", + // [FIXME] Not sure how to do most of the SVG using $el + innerHTML: ' ' + } + ); + + const dialog_header = $el("div.p-dialog-header", + [ + $el("div", [ + $el("div", + { + id: "cm-manager", + }, + [ + $el("h2.px-4", [ + $el(iconClass, { + style: { + "font-size": "1.25rem", + "margin-right": ".5rem" + } + }), + $el("span", { textContent: title }) + ]) + ] + ) + ]), + header_actions + ] + ); + + const contentFrame = $el("div.p-dialog-content", {}, normalizeContent(content)); + const manager_dialog = $el("div.p-dialog.p-component.global-dialog", { + id: dialogId, + parent: dialog_mask, + style: { + 'display': 'flex', + 'flex-direction': 'column', + 'pointer-events': 'auto', + 'margin': '0px', + }, + role: 'dialog', + ariaModal: 'true', + // [TODO] + // ariaLabbelledby: 'cm-title', + // maximized: 'false', + // data-pc-name: 'dialog', + // data-pc-section: 'root', + // data-pd-focustrap: 'true' + }, + [ dialog_header, contentFrame ] + ); + + const hidden_accessible = $el("span.p-hidden-accessible.p-hidden-focusable", { + parent: manager_dialog, + tabindex: "0", + role: "presentation", + ariaHidden: "true", + "data-p-hidden-accessible": "true", + "data-p-hidden-focusable": "true", + "data-pc-section": "firstfocusableelement" + }); + + return dialog_mask; +} + +export function buildGuiFrameCustomHeader(dialogId, customHeader, content, owner) { + const dialog_mask = $el("div.p-dialog-mask.p-overlay-mask.p-overlay-mask-enter", { + parent: document.body, + style: { + position: "fixed", + height: "100%", + width: "100%", + left: "0px", + top: "0px", + display: "flex", + justifyContent: "center", + alignItems: "center", + pointerEvents: "auto", + zIndex: "1000" + }, + onclick: (e) => { + if (e.target === dialog_mask) { + owner.close(); + } + } + // data-pc-section="mask" + }); + + const header_actions = $el("div.p-dialog-header-actions", { + // [TODO] + // data-pc-section="headeractions" + } + ); + + const close_button = $el("button.p-button.p-component.p-button-icon-only.p-button-secondary.p-button-rounded.p-button-text.p-dialog-close-button", { + parent: header_actions, + type: "button", + ariaLabel: "Close", + onclick: () => owner.close(), + // "data-pc-name": "pcclosebutton", + // "data-p-disabled": "false", + // "data-p-severity": "secondary", + // "data-pc-group-section": "headericon", + // "data-pc-extend": "button", + // "data-pc-section": "root", + // [FIXME] Not sure how to do most of the SVG using $el + innerHTML: ' ' + } + ); + + const _customHeader = normalizeContent(customHeader); + const dialog_header = $el("div.p-dialog-header", + [ + $el("div", [ + $el("div", + { + id: "cm-manager", + }, + Array.isArray(_customHeader) ? _customHeader : [_customHeader] + ) + ]), + header_actions + ] + ); + + const contentFrame = $el("div.p-dialog-content", {}, normalizeContent(content)); + const manager_dialog = $el("div.p-dialog.p-component.global-dialog", { + id: dialogId, + parent: dialog_mask, + style: { + 'display': 'flex', + 'flex-direction': 'column', + 'pointer-events': 'auto', + 'margin': '0px', + }, + role: 'dialog', + ariaModal: 'true', + // [TODO] + // ariaLabbelledby: 'cm-title', + // maximized: 'false', + // data-pc-name: 'dialog', + // data-pc-section: 'root', + // data-pd-focustrap: 'true' + }, + [ dialog_header, contentFrame ] + ); + + const hidden_accessible = $el("span.p-hidden-accessible.p-hidden-focusable", { + parent: manager_dialog, + tabindex: "0", + role: "presentation", + ariaHidden: "true", + "data-p-hidden-accessible": "true", + "data-p-hidden-focusable": "true", + "data-pc-section": "firstfocusableelement" + }); + + return dialog_mask; +} \ No newline at end of file diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js index 7f83b6ad..e94c515f 100644 --- a/js/comfyui-manager.js +++ b/js/comfyui-manager.js @@ -20,6 +20,7 @@ import { ComponentBuilderDialog, getPureName, load_components, set_component_pol 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(); @@ -253,21 +254,17 @@ const style = ` } -.cm-column-button { - margin-bottom: calc(var(--spacing)*2); -} - .cm-button { width: auto; position: relative; overflow: hidden; - background-color: #2b2a33; - border-color: #444454; - color: #ddd; + background-color: var(--comfy-menu-secondary-bg); + border-color: var(--border-color); + color: color: var(--input-text); } .cm-button:hover { - background-color: #52525e; + filter: brightness(125%); } .cm-button-red { @@ -318,9 +315,13 @@ const style = ` .cm-menu-combo { cursor: pointer; padding: 0.5em 0.5em; - border: 1px solid #52525b; + border: 1px solid var(--border-color); border-radius: 6px; - background: #09090b; + background: var(--comfy-menu-secondary-bg); +} + +.cm-menu-combo:hover { + filter: brightness(125%); } .cm-small-button { @@ -824,7 +825,7 @@ class ManagerMenuDialog extends ComfyDialog { const isElectron = 'electronAPI' in window; update_comfyui_button = - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Update ComfyUI", style: { @@ -835,7 +836,7 @@ class ManagerMenuDialog extends ComfyDialog { }); switch_comfyui_button = - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Switch ComfyUI", style: { @@ -846,7 +847,7 @@ class ManagerMenuDialog extends ComfyDialog { }); restart_stop_button = - $el("button.p-button.p-component.cm-column-button.cm-button-red", { + $el("button.p-button.p-component.cm-button-red", { type: "button", textContent: "Restart", onclick: () => restartOrStop() @@ -854,7 +855,7 @@ class ManagerMenuDialog extends ComfyDialog { if(isElectron) { update_all_button = - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Update All Custom Nodes", onclick: @@ -863,7 +864,7 @@ class ManagerMenuDialog extends ComfyDialog { } else { update_all_button = - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Update All", onclick: @@ -873,7 +874,7 @@ class ManagerMenuDialog extends ComfyDialog { const res = [ - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Custom Nodes Manager", onclick: @@ -885,7 +886,7 @@ class ManagerMenuDialog extends ComfyDialog { } }), - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Install Missing Custom Nodes", onclick: @@ -897,7 +898,7 @@ class ManagerMenuDialog extends ComfyDialog { } }), - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Custom Nodes In Workflow", onclick: @@ -909,8 +910,8 @@ class ManagerMenuDialog extends ComfyDialog { } }), - $el("br", {}, []), - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("div", {}, []), + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Model Manager", onclick: @@ -922,7 +923,7 @@ class ManagerMenuDialog extends ComfyDialog { } }), - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Install via Git URL", onclick: async () => { @@ -934,13 +935,13 @@ class ManagerMenuDialog extends ComfyDialog { } }), - $el("br", {}, []), + $el("div", {}, []), update_all_button, update_comfyui_button, switch_comfyui_button, // fetch_updates_button, - $el("br", {}, []), + $el("div", {}, []), restart_stop_button, ]; @@ -969,7 +970,7 @@ class ManagerMenuDialog extends ComfyDialog { api.fetchApi(`/manager/db_mode?value=${event.target.value}`); }); - const dbRetrievalSetttingItem = this.createSettingsCombo("DB", this.datasrc_combo); + const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo); // preview method let preview_combo = document.createElement("select"); @@ -988,7 +989,7 @@ class ManagerMenuDialog extends ComfyDialog { api.fetchApi(`/manager/preview_method?value=${event.target.value}`); }); - const previewSetttingItem = this.createSettingsCombo("Preview method", preview_combo); + const previewSetttingItem = createSettingsCombo("Preview method", preview_combo); // channel let channel_combo = document.createElement("select"); @@ -1017,7 +1018,7 @@ class ManagerMenuDialog extends ComfyDialog { } }); - const channelSetttingItem = this.createSettingsCombo("Channel", channel_combo); + const channelSetttingItem = createSettingsCombo("Channel", channel_combo); // share @@ -1056,7 +1057,7 @@ class ManagerMenuDialog extends ComfyDialog { } }); - const shareSetttingItem = this.createSettingsCombo("Share", share_combo); + 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."); @@ -1076,7 +1077,7 @@ class ManagerMenuDialog extends ComfyDialog { set_component_policy(event.target.value); }); - const componentSetttingItem = this.createSettingsCombo("Component", component_policy_combo); + const componentSetttingItem = createSettingsCombo("Component", component_policy_combo); update_policy_combo = document.createElement("select"); @@ -1094,25 +1095,22 @@ class ManagerMenuDialog extends ComfyDialog { api.fetchApi(`/manager/policy/update?value=${event.target.value}`); }); - const updateSetttingItem = this.createSettingsCombo("Update", update_policy_combo); + const updateSetttingItem = createSettingsCombo("Update", update_policy_combo); if(isElectron) updateSetttingItem.style.display = 'none'; return [ - // $el("br", {}, []), dbRetrievalSetttingItem, channelSetttingItem, previewSetttingItem, shareSetttingItem, componentSetttingItem, updateSetttingItem, - // $el("br", {}, []), - - // $el("br", {}, []), + $el("filedset.cm-experimental.mt-auto", {}, [ $el("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]), - $el("button.p-button.p-component.cm-button.cm-experimental-button.cm-column-button", { + $el("button.p-button.p-component.cm-button.cm-experimental-button", { type: "button", textContent: "Snapshot Manager", onclick: @@ -1140,7 +1138,7 @@ class ManagerMenuDialog extends ComfyDialog { createControlsRight() { const elts = [ - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { id: 'cm-manual-button', type: "button", textContent: "Community Manual", @@ -1191,7 +1189,7 @@ class ManagerMenuDialog extends ComfyDialog { }) ]), - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { id: 'workflowgallery-button', type: "button", style: { @@ -1234,7 +1232,7 @@ class ManagerMenuDialog extends ComfyDialog { }) ]), - $el("button.p-button.p-component.cm-button.cm-column-button", { + $el("button.p-button.p-component.cm-button", { id: 'cm-nodeinfo-button', type: "button", textContent: "Nodes Info", @@ -1251,154 +1249,26 @@ class ManagerMenuDialog extends ComfyDialog { return elts; } - createSettingsCombo(label, content) { - const settingItem = $el("div.setting-item.mb-4", {}, [ - $el("div.flex.flex-row.items-center.gap-2",[ - $el("div.form-label.flex.grow.items-center", [ - $el("span.text-muted", { textContent: label },) - ]), - $el("div.form-input.flex.justify-end", - [content] - ) - ] - ) - ]); - return settingItem; - } - - createSettingsButton(label, content) { - const settingItem = $el("div.setting-item.mb-4", {}, [ - $el("div.flex.flex-row.items-center.gap-2",[ - $el("div.form-label.flex.grow.items-center", [ - $el("span.text-muted", { textContent: label },) - ]), - $el("div.form-input.flex.justify-end", - [content] - ) - ] - ) - ]); - return settingItem; - } - constructor() { super(); - const dialog_mask = $el("div.p-dialog-mask.p-overlay-mask.p-overlay-mask-enter", { - parent: document.body, - style: { - position: "fixed", - height: "100%", - width: "100%", - left: "0px", - top: "0px", - display: "flex", - justifyContent: "center", - alignItems: "center", - pointerEvents: "auto", - zIndex: "1000" - }, - onclick: (e) => { - if (e.target === dialog_mask) { - this.close(); - } - } - // data-pc-section="mask" - }); - - const header_actions = $el("div.p-dialog-header-actions", { - // [FIXME] - // data-pc-section="headeractions" - } - ); - - const close_button = $el("button.p-button.p-component.p-button-icon-only.p-button-secondary.p-button-rounded.p-button-text.p-dialog-close-button", { - parent: header_actions, - type: "button", - ariaLabel: "Close", - onclick: () => this.close(), - // "data-pc-name": "pcclosebutton", - // "data-p-disabled": "false", - // "data-p-severity": "secondary", - // "data-pc-group-section": "headericon", - // "data-pc-extend": "button", - // "data-pc-section": "root", - // [FIXME] Not sure how to do most of the SVG using $el - innerHTML: ' ' - } - ); - - const dialog_header = $el("div.p-dialog-header", + const content = $el("div.cm-menu-container", [ - $el("div", [ - $el("div", - { - id: "cm-manager", - }, - [ - $el("h2.px-4", [ - // [TODO] Find better icon - $el("i.mdi.mdi-puzzle", { - style: { - "font-size": "1.25rem", - "margin-right": ".5rem" - } - }), - $el("span", { textContent: `ComfyUI Manager ${manager_version}` }) - ]) - ] - ) - ]), - header_actions + $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 content = $el("div.p-dialog-content", - [ - $el("div.cm-menu-container", - [ - $el("div.cm-menu-column", [...this.createControlsLeft()]), - $el("div.cm-menu-column", [...this.createControlsMid()]), - $el("div.cm-menu-column", [...this.createControlsRight()]) - ]), - ] - ); + const frame = buildGuiFrame( + 'cm-manager-dialog', // dialog id + `ComfyUI Manager ${manager_version}`, // dialog title + "i.mdi.mdi-puzzle", // dialog icon class to show before title + content, // dialog content element + this + ); // send this so we can attach close functions - content.style.width = '100%'; - content.style.height = '100%'; - - const manager_dialog = $el("div.p-dialog.p-component.global-dialog", { - id:'cm-manager-dialog', - parent: dialog_mask, - style: { - 'display': 'flex', - 'flex-direction': 'column', - 'pointer-events': 'auto', - 'margin': '0px', - }, - role: 'dialog', - ariaModal: 'true', - // [FIXME] - // ariaLabbelledby: 'cm-title', - // maximized: 'false', - // data-pc-name: 'dialog', - // data-pc-section: 'root', - // data-pd-focustrap: 'true' - }, - [ dialog_header, content ] - ); - - const hidden_accessible = $el("span.p-hidden-accessible.p-hidden-focusable", { - parent: manager_dialog, - tabindex: "0", - role: "presentation", - ariaHidden: "true", - "data-p-hidden-accessible": "true", - "data-p-hidden-focusable": "true", - "data-pc-section": "firstfocusableelement" - }); - - this.element = dialog_mask; + this.element = frame; } get isVisible() { diff --git a/js/custom-nodes-manager.css b/js/custom-nodes-manager.css index 1a918e5b..4e0dd4d5 100644 --- a/js/custom-nodes-manager.css +++ b/js/custom-nodes-manager.css @@ -1,8 +1,9 @@ .cn-manager { --grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; z-index: 1099; - width: 80%; - height: 80%; + width: 80vw; + height: 75vh; + min-height: 30em; display: flex; flex-direction: column; gap: 10px; @@ -10,6 +11,7 @@ font-family: arial, sans-serif; text-underline-offset: 3px; outline: none; + margin: calc(var(--spacing)*2); } .cn-manager .cn-flex-auto { @@ -17,17 +19,22 @@ } .cn-manager button { + width: auto; + position: relative; + overflow: hidden; font-size: 16px; color: var(--input-text); background-color: var(--comfy-input-bg); - border-radius: 8px; border-color: var(--border-color); - border-style: solid; margin: 0; padding: 4px 8px; min-width: 100px; } +.cn-manager button:hover { + filter: brightness(125%); +} + .cn-manager button:disabled, .cn-manager input:disabled, .cn-manager select:disabled { @@ -40,8 +47,13 @@ .cn-manager .cn-manager-restart { display: none; - background-color: #500000; - color: white; + background-color: #500000 !important; + border-color: #88181b !important; + color: white !important; +} + +.cn-manager .cn-manager-restart:hover { + background-color: #88181b !important; } .cn-manager .cn-manager-stop { @@ -79,7 +91,6 @@ flex-wrap: wrap; gap: 5px; align-items: center; - padding: 0 5px; } .cn-manager-header label { @@ -91,16 +102,32 @@ .cn-manager-filter { height: 28px; line-height: 28px; + + cursor: pointer; + padding: 0.5em 0.5em; + border: 1px solid var(--border-color); + border-radius: 6px; + background: var(--comfy-input-bg); +} + +.cn-manager-filter:hover { + filter: brightness(125%); } .cn-manager-keywords { height: 28px; line-height: 28px; padding: 0 5px 0 26px; + background: var(--comfy-input-bg); background-size: 16px; background-position: 5px center; background-repeat: no-repeat; background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E"); + + border: 1px solid var(--border-color); + border-radius: 6px; + + outline-color: transparent; } .cn-manager-status { diff --git a/js/custom-nodes-manager.js b/js/custom-nodes-manager.js index a4ca3653..b290df61 100644 --- a/js/custom-nodes-manager.js +++ b/js/custom-nodes-manager.js @@ -1,6 +1,7 @@ import { app } from "../../scripts/app.js"; import { ComfyDialog, $el } from "../../scripts/ui.js"; import { api } from "../../scripts/api.js"; +import { buildGuiFrameCustomHeader, createSettingsCombo } from "./comfyui-gui-builder.js"; import { manager_instance, rebootAPI, install_via_git_url, @@ -18,32 +19,19 @@ loadCss("./custom-nodes-manager.css"); const gridId = "node"; const pageHtml = ` -
- - -
-
-
-
-
-
-
-