diff --git a/js/comfyui-gui-builder.js b/js/comfyui-gui-builder.js new file mode 100644 index 00000000..e6de81c6 --- /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: "frame-title-container", + }, + [ + $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: "frame-title-container", + }, + 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 e65a2c65..60e38740 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(); @@ -44,12 +45,16 @@ docStyle.innerHTML = ` #cm-manager-dialog { width: 1000px; - height: 455px; + 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; @@ -80,6 +85,7 @@ docStyle.innerHTML = ` } .cm-menu-container { + padding : calc(var(--spacing)*2); column-gap: 20px; display: flex; flex-wrap: wrap; @@ -140,8 +146,8 @@ docStyle.innerHTML = ` } .cm-notice-board { - width: 290px; - height: 230px; + width: auto; + height: 280px; overflow: auto; color: var(--input-text); border: 1px solid var(--descrip-text); @@ -238,68 +244,50 @@ var is_updating = false; // copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts const style = ` #workflowgallery-button { - width: 310px; - height: 27px; + height: 50px; padding: 0px !important; - position: relative; - overflow: hidden; - font-size: 17px !important; } #cm-nodeinfo-button { - width: 310px; - height: 27px; - padding: 0px !important; - position: relative; - overflow: hidden; - font-size: 17px !important; + } #cm-manual-button { - width: 310px; - height: 27px; - position: relative; - overflow: hidden; + } .cm-button { - width: 310px; - height: 30px; + width: auto; position: relative; overflow: hidden; - font-size: 17px !important; + background-color: var(--comfy-menu-secondary-bg); + border-color: var(--border-color); + color: color: var(--input-text); +} + +.cm-button:hover { + filter: brightness(125%); } .cm-button-red { - width: 310px; - height: 30px; - position: relative; - overflow: hidden; - font-size: 17px !important; background-color: #500000 !important; + border-color: #88181b !important; color: white !important; } +.cm-button-red:hover { + background-color: #88181b !important; +} .cm-button-orange { - width: 310px; - height: 30px; - position: relative; - overflow: hidden; - font-size: 17px !important; font-weight: bold; background-color: orange !important; color: black !important; } .cm-experimental-button { - width: 290px; - height: 30px; - position: relative; - overflow: hidden; - font-size: 17px !important; + width: 100%; } .cm-experimental { - width: 310px; border: 1px solid #555; border-radius: 5px; padding: 10px; @@ -326,8 +314,14 @@ const style = ` .cm-menu-combo { cursor: pointer; - width: 310px; - box-sizing: border-box; + 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 { @@ -831,7 +825,7 @@ class ManagerMenuDialog extends ComfyDialog { const isElectron = 'electronAPI' in window; update_comfyui_button = - $el("button.cm-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Update ComfyUI", style: { @@ -842,7 +836,7 @@ class ManagerMenuDialog extends ComfyDialog { }); switch_comfyui_button = - $el("button.cm-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Switch ComfyUI", style: { @@ -853,7 +847,7 @@ class ManagerMenuDialog extends ComfyDialog { }); restart_stop_button = - $el("button.cm-button-red", { + $el("button.p-button.p-component.cm-button-red", { type: "button", textContent: "Restart", onclick: () => restartOrStop() @@ -861,7 +855,7 @@ class ManagerMenuDialog extends ComfyDialog { if(isElectron) { update_all_button = - $el("button.cm-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Update All Custom Nodes", onclick: @@ -870,7 +864,7 @@ class ManagerMenuDialog extends ComfyDialog { } else { update_all_button = - $el("button.cm-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Update All", onclick: @@ -880,7 +874,7 @@ class ManagerMenuDialog extends ComfyDialog { const res = [ - $el("button.cm-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Custom Nodes Manager", onclick: @@ -892,7 +886,7 @@ class ManagerMenuDialog extends ComfyDialog { } }), - $el("button.cm-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Install Missing Custom Nodes", onclick: @@ -904,7 +898,7 @@ class ManagerMenuDialog extends ComfyDialog { } }), - $el("button.cm-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Custom Nodes In Workflow", onclick: @@ -916,8 +910,8 @@ class ManagerMenuDialog extends ComfyDialog { } }), - $el("br", {}, []), - $el("button.cm-button", { + $el("div", {}, []), + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Model Manager", onclick: @@ -929,7 +923,7 @@ class ManagerMenuDialog extends ComfyDialog { } }), - $el("button.cm-button", { + $el("button.p-button.p-component.cm-button", { type: "button", textContent: "Install via Git URL", onclick: async () => { @@ -941,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, ]; @@ -960,12 +954,13 @@ class ManagerMenuDialog extends ComfyDialog { let self = this; // db mode + 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"; - this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'DB: Channel (1day cache)' }, [])); - this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, [])); - this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, [])); + 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()) @@ -975,14 +970,16 @@ class ManagerMenuDialog extends ComfyDialog { api.fetchApi(`/manager/db_mode?value=${event.target.value}`); }); + const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo); + // preview method 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"; - preview_combo.appendChild($el('option', { value: 'auto', text: 'Preview method: Auto' }, [])); - preview_combo.appendChild($el('option', { value: 'taesd', text: 'Preview method: TAESD (slow)' }, [])); - preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Preview method: Latent2RGB (fast)' }, [])); - preview_combo.appendChild($el('option', { value: 'none', text: 'Preview method: None (very fast)' }, [])); + preview_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled"; + 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)' }, [])); api.fetchApi('/manager/preview_method') .then(response => response.text()) @@ -992,10 +989,12 @@ class ManagerMenuDialog extends ComfyDialog { api.fetchApi(`/manager/preview_method?value=${event.target.value}`); }); + const previewSetttingItem = createSettingsCombo("Preview method", preview_combo); + // channel 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"; + 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 => { @@ -1004,7 +1003,7 @@ class ManagerMenuDialog extends ComfyDialog { for (let i in urls) { if (urls[i] != '') { let name_url = urls[i].split('::'); - channel_combo.appendChild($el('option', { value: name_url[0], text: `Channel: ${name_url[0]}` }, [])); + channel_combo.appendChild($el('option', { value: name_url[0], text: `${name_url[0]}` }, [])); } } @@ -1019,11 +1018,13 @@ class ManagerMenuDialog extends ComfyDialog { } }); + const channelSetttingItem = createSettingsCombo("Channel", channel_combo); + // share 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"; + share_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled"; const share_options = [ ['none', 'None'], ['openart', 'OpenArt AI'], @@ -1034,7 +1035,7 @@ class ManagerMenuDialog extends ComfyDialog { ['all', 'All'], ]; for (const option of share_options) { - share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, [])); + share_combo.appendChild($el('option', { value: option[0], text: `${option[1]}` }, [])); } api.fetchApi('/manager/share_option') @@ -1056,12 +1057,14 @@ class ManagerMenuDialog extends ComfyDialog { } }); + 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"; - component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Component: Use workflow version' }, [])); - component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, [])); - component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, [])); + 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 => { @@ -1074,15 +1077,14 @@ class ManagerMenuDialog extends ComfyDialog { set_component_policy(event.target.value); }); - update_policy_combo = document.createElement("select"); + const componentSetttingItem = createSettingsCombo("Component", component_policy_combo); - if(isElectron) - update_policy_combo.style.display = 'none'; + 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"; - update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'Update: ComfyUI Stable Version' }, [])); - update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'Update: ComfyUI Nightly Version' }, [])); + 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 => { @@ -1093,20 +1095,22 @@ class ManagerMenuDialog extends ComfyDialog { api.fetchApi(`/manager/policy/update?value=${event.target.value}`); }); - return [ - $el("br", {}, []), - this.datasrc_combo, - channel_combo, - preview_combo, - share_combo, - component_policy_combo, - update_policy_combo, - $el("br", {}, []), + const updateSetttingItem = createSettingsCombo("Update", update_policy_combo); + + if(isElectron) + updateSetttingItem.style.display = 'none'; - $el("br", {}, []), - $el("filedset.cm-experimental", {}, [ + return [ + dbRetrievalSetttingItem, + channelSetttingItem, + previewSetttingItem, + shareSetttingItem, + componentSetttingItem, + updateSetttingItem, + //[TODO] replace mt-2 with wrapper div with flex column gap + $el("filedset.cm-experimental.mt-auto", {}, [ $el("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]), - $el("button.cm-experimental-button", { + $el("button.p-button.p-component.cm-button.cm-experimental-button", { type: "button", textContent: "Snapshot Manager", onclick: @@ -1116,7 +1120,7 @@ class ManagerMenuDialog extends ComfyDialog { SnapshotManager.instance.show(); } }), - $el("button.cm-experimental-button", { + $el("button.p-button.p-component.cm-button.cm-experimental-button.mt-2", { type: "button", textContent: "Install PIP packages", onclick: @@ -1134,7 +1138,7 @@ class ManagerMenuDialog extends ComfyDialog { createControlsRight() { const elts = [ - $el("button.cm-button", { + $el("button.p-button.p-component.cm-button", { id: 'cm-manual-button', type: "button", textContent: "Community Manual", @@ -1185,11 +1189,11 @@ class ManagerMenuDialog extends ComfyDialog { }) ]), - $el("button", { + $el("button.p-button.p-component.cm-button", { id: 'workflowgallery-button', type: "button", style: { - ...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {}) + // ...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {}) }, onclick: (e) => { const last_visited_site = localStorage.getItem("wg_last_visited") @@ -1212,7 +1216,7 @@ class ManagerMenuDialog extends ComfyDialog { }, [ $el("p", { id: 'workflowgallery-button-last-visited-label', - textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : ''})`, + textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : 'none selected'})`, style: { 'text-align': 'center', 'color': 'var(--input-text)', @@ -1228,13 +1232,12 @@ class ManagerMenuDialog extends ComfyDialog { }) ]), - $el("button.cm-button", { + $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"); } }), - $el("br", {}, []), ]; var textarea = document.createElement("div"); @@ -1249,31 +1252,23 @@ class ManagerMenuDialog extends ComfyDialog { constructor() { super(); - const close_button = $el("button", { id: "cm-close-button", type: "button", textContent: "Close", onclick: () => this.close() }); + 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 content = - $el("div.comfy-modal-content", - [ - $el("tr.cm-title", {}, [ - $el("font", {size:6, color:"white"}, [`ComfyUI Manager ${manager_version}`])] - ), - $el("br", {}, []), - $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 - $el("br", {}, []), - close_button, - ] - ); - - content.style.width = '100%'; - content.style.height = '100%'; - - this.element = $el("div.comfy-modal", { id:'cm-manager-dialog', parent: document.body }, [ content ]); + this.element = frame; } get isVisible() { @@ -1281,7 +1276,7 @@ class ManagerMenuDialog extends ComfyDialog { } show() { - this.element.style.display = "block"; + this.element.style.display = "flex"; } toggleVisibility() { diff --git a/js/custom-nodes-manager.css b/js/custom-nodes-manager.css index 1a918e5b..4ea82580 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,21 @@ } .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 +46,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 +90,6 @@ flex-wrap: wrap; gap: 5px; align-items: center; - padding: 0 5px; } .cn-manager-header label { @@ -91,16 +101,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 { @@ -588,6 +614,10 @@ height: 100%; } +.cn-install-buttons button { + padding: 4px 8px; +} + .cn-selected-buttons { display: flex; gap: 5px; 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 = ` -
- - -
-
-
-
-
-
-
-