This commit is contained in:
David 2025-12-15 00:36:49 +09:00 committed by GitHub
commit b1a4165faf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 574 additions and 225 deletions

227
js/comfyui-gui-builder.js Normal file
View File

@ -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: '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" class="p-icon p-button-icon" aria-hidden="true"><path d="M8.01186 7.00933L12.27 2.75116C12.341 2.68501 12.398 2.60524 12.4375 2.51661C12.4769 2.42798 12.4982 2.3323 12.4999 2.23529C12.5016 2.13827 12.4838 2.0419 12.4474 1.95194C12.4111 1.86197 12.357 1.78024 12.2884 1.71163C12.2198 1.64302 12.138 1.58893 12.0481 1.55259C11.9581 1.51625 11.8617 1.4984 11.7647 1.50011C11.6677 1.50182 11.572 1.52306 11.4834 1.56255C11.3948 1.60204 11.315 1.65898 11.2488 1.72997L6.99067 5.98814L2.7325 1.72997C2.59553 1.60234 2.41437 1.53286 2.22718 1.53616C2.03999 1.53946 1.8614 1.61529 1.72901 1.74767C1.59663 1.88006 1.5208 2.05865 1.5175 2.24584C1.5142 2.43303 1.58368 2.61419 1.71131 2.75116L5.96948 7.00933L1.71131 11.2675C1.576 11.403 1.5 11.5866 1.5 11.7781C1.5 11.9696 1.576 12.1532 1.71131 12.2887C1.84679 12.424 2.03043 12.5 2.2219 12.5C2.41338 12.5 2.59702 12.424 2.7325 12.2887L6.99067 8.03052L11.2488 12.2887C11.3843 12.424 11.568 12.5 11.7594 12.5C11.9509 12.5 12.1346 12.424 12.27 12.2887C12.4053 12.1532 12.4813 11.9696 12.4813 11.7781C12.4813 11.5866 12.4053 11.403 12.27 11.2675L8.01186 7.00933Z" fill="currentColor"></path></svg><span class="p-button-label" data-pc-section="label">&nbsp;</span><!---->'
}
);
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: '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" class="p-icon p-button-icon" aria-hidden="true"><path d="M8.01186 7.00933L12.27 2.75116C12.341 2.68501 12.398 2.60524 12.4375 2.51661C12.4769 2.42798 12.4982 2.3323 12.4999 2.23529C12.5016 2.13827 12.4838 2.0419 12.4474 1.95194C12.4111 1.86197 12.357 1.78024 12.2884 1.71163C12.2198 1.64302 12.138 1.58893 12.0481 1.55259C11.9581 1.51625 11.8617 1.4984 11.7647 1.50011C11.6677 1.50182 11.572 1.52306 11.4834 1.56255C11.3948 1.60204 11.315 1.65898 11.2488 1.72997L6.99067 5.98814L2.7325 1.72997C2.59553 1.60234 2.41437 1.53286 2.22718 1.53616C2.03999 1.53946 1.8614 1.61529 1.72901 1.74767C1.59663 1.88006 1.5208 2.05865 1.5175 2.24584C1.5142 2.43303 1.58368 2.61419 1.71131 2.75116L5.96948 7.00933L1.71131 11.2675C1.576 11.403 1.5 11.5866 1.5 11.7781C1.5 11.9696 1.576 12.1532 1.71131 12.2887C1.84679 12.424 2.03043 12.5 2.2219 12.5C2.41338 12.5 2.59702 12.424 2.7325 12.2887L6.99067 8.03052L11.2488 12.2887C11.3843 12.424 11.568 12.5 11.7594 12.5C11.9509 12.5 12.1346 12.424 12.27 12.2887C12.4053 12.1532 12.4813 11.9696 12.4813 11.7781C12.4813 11.5866 12.4053 11.403 12.27 11.2675L8.01186 7.00933Z" fill="currentColor"></path></svg><span class="p-button-label" data-pc-section="label">&nbsp;</span><!---->'
}
);
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;
}

View File

@ -20,6 +20,7 @@ import { ComponentBuilderDialog, getPureName, load_components, set_component_pol
import { CustomNodesManager } from "./custom-nodes-manager.js"; import { CustomNodesManager } from "./custom-nodes-manager.js";
import { ModelManager } from "./model-manager.js"; import { ModelManager } from "./model-manager.js";
import { SnapshotManager } from "./snapshot.js"; import { SnapshotManager } from "./snapshot.js";
import { buildGuiFrame, createSettingsCombo } from "./comfyui-gui-builder.js";
let manager_version = await getVersion(); let manager_version = await getVersion();
@ -44,12 +45,16 @@ docStyle.innerHTML = `
#cm-manager-dialog { #cm-manager-dialog {
width: 1000px; width: 1000px;
height: 455px; height: auto;
box-sizing: content-box; box-sizing: content-box;
z-index: 1000; z-index: 1000;
overflow-y: auto; overflow-y: auto;
} }
#cm-manager-dialog br {
margin-bottom: 1em;
}
.cb-widget { .cb-widget {
width: 400px; width: 400px;
height: 25px; height: 25px;
@ -80,6 +85,7 @@ docStyle.innerHTML = `
} }
.cm-menu-container { .cm-menu-container {
padding : calc(var(--spacing)*2);
column-gap: 20px; column-gap: 20px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@ -140,8 +146,8 @@ docStyle.innerHTML = `
} }
.cm-notice-board { .cm-notice-board {
width: 290px; width: auto;
height: 230px; height: 280px;
overflow: auto; overflow: auto;
color: var(--input-text); color: var(--input-text);
border: 1px solid var(--descrip-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 // copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
const style = ` const style = `
#workflowgallery-button { #workflowgallery-button {
width: 310px; height: 50px;
height: 27px;
padding: 0px !important; padding: 0px !important;
position: relative;
overflow: hidden;
font-size: 17px !important;
} }
#cm-nodeinfo-button { #cm-nodeinfo-button {
width: 310px;
height: 27px;
padding: 0px !important;
position: relative;
overflow: hidden;
font-size: 17px !important;
} }
#cm-manual-button { #cm-manual-button {
width: 310px;
height: 27px;
position: relative;
overflow: hidden;
} }
.cm-button { .cm-button {
width: 310px; width: auto;
height: 30px;
position: relative; position: relative;
overflow: hidden; 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 { .cm-button-red {
width: 310px;
height: 30px;
position: relative;
overflow: hidden;
font-size: 17px !important;
background-color: #500000 !important; background-color: #500000 !important;
border-color: #88181b !important;
color: white !important; color: white !important;
} }
.cm-button-red:hover {
background-color: #88181b !important;
}
.cm-button-orange { .cm-button-orange {
width: 310px;
height: 30px;
position: relative;
overflow: hidden;
font-size: 17px !important;
font-weight: bold; font-weight: bold;
background-color: orange !important; background-color: orange !important;
color: black !important; color: black !important;
} }
.cm-experimental-button { .cm-experimental-button {
width: 290px; width: 100%;
height: 30px;
position: relative;
overflow: hidden;
font-size: 17px !important;
} }
.cm-experimental { .cm-experimental {
width: 310px;
border: 1px solid #555; border: 1px solid #555;
border-radius: 5px; border-radius: 5px;
padding: 10px; padding: 10px;
@ -326,8 +314,14 @@ const style = `
.cm-menu-combo { .cm-menu-combo {
cursor: pointer; cursor: pointer;
width: 310px; padding: 0.5em 0.5em;
box-sizing: border-box; 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 { .cm-small-button {
@ -831,7 +825,7 @@ class ManagerMenuDialog extends ComfyDialog {
const isElectron = 'electronAPI' in window; const isElectron = 'electronAPI' in window;
update_comfyui_button = update_comfyui_button =
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Update ComfyUI", textContent: "Update ComfyUI",
style: { style: {
@ -842,7 +836,7 @@ class ManagerMenuDialog extends ComfyDialog {
}); });
switch_comfyui_button = switch_comfyui_button =
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Switch ComfyUI", textContent: "Switch ComfyUI",
style: { style: {
@ -853,7 +847,7 @@ class ManagerMenuDialog extends ComfyDialog {
}); });
restart_stop_button = restart_stop_button =
$el("button.cm-button-red", { $el("button.p-button.p-component.cm-button-red", {
type: "button", type: "button",
textContent: "Restart", textContent: "Restart",
onclick: () => restartOrStop() onclick: () => restartOrStop()
@ -861,7 +855,7 @@ class ManagerMenuDialog extends ComfyDialog {
if(isElectron) { if(isElectron) {
update_all_button = update_all_button =
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Update All Custom Nodes", textContent: "Update All Custom Nodes",
onclick: onclick:
@ -870,7 +864,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
else { else {
update_all_button = update_all_button =
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Update All", textContent: "Update All",
onclick: onclick:
@ -880,7 +874,7 @@ class ManagerMenuDialog extends ComfyDialog {
const res = const res =
[ [
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Custom Nodes Manager", textContent: "Custom Nodes Manager",
onclick: onclick:
@ -892,7 +886,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Install Missing Custom Nodes", textContent: "Install Missing Custom Nodes",
onclick: onclick:
@ -904,7 +898,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Custom Nodes In Workflow", textContent: "Custom Nodes In Workflow",
onclick: onclick:
@ -916,8 +910,8 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("br", {}, []), $el("div", {}, []),
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Model Manager", textContent: "Model Manager",
onclick: onclick:
@ -929,7 +923,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
type: "button", type: "button",
textContent: "Install via Git URL", textContent: "Install via Git URL",
onclick: async () => { onclick: async () => {
@ -941,13 +935,13 @@ class ManagerMenuDialog extends ComfyDialog {
} }
}), }),
$el("br", {}, []), $el("div", {}, []),
update_all_button, update_all_button,
update_comfyui_button, update_comfyui_button,
switch_comfyui_button, switch_comfyui_button,
// fetch_updates_button, // fetch_updates_button,
$el("br", {}, []), $el("div", {}, []),
restart_stop_button, restart_stop_button,
]; ];
@ -960,12 +954,13 @@ class ManagerMenuDialog extends ComfyDialog {
let self = this; let self = this;
// db mode // db mode
this.datasrc_combo = document.createElement("select"); 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.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.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled ";
this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'DB: Channel (1day cache)' }, [])); this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'Channel (1day cache)' }, []));
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, [])); this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'Local' }, []));
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, [])); this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'Channel (remote)' }, []));
api.fetchApi('/manager/db_mode') api.fetchApi('/manager/db_mode')
.then(response => response.text()) .then(response => response.text())
@ -975,14 +970,16 @@ class ManagerMenuDialog extends ComfyDialog {
api.fetchApi(`/manager/db_mode?value=${event.target.value}`); api.fetchApi(`/manager/db_mode?value=${event.target.value}`);
}); });
const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo);
// preview method // preview method
let preview_combo = document.createElement("select"); 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.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process.");
preview_combo.className = "cm-menu-combo"; preview_combo.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
preview_combo.appendChild($el('option', { value: 'auto', text: 'Preview method: Auto' }, [])); preview_combo.appendChild($el('option', { value: 'auto', text: 'Auto' }, []));
preview_combo.appendChild($el('option', { value: 'taesd', text: 'Preview method: TAESD (slow)' }, [])); preview_combo.appendChild($el('option', { value: 'taesd', text: 'TAESD (slow)' }, []));
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Preview method: Latent2RGB (fast)' }, [])); preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Latent2RGB (fast)' }, []));
preview_combo.appendChild($el('option', { value: 'none', text: 'Preview method: None (very fast)' }, [])); preview_combo.appendChild($el('option', { value: 'none', text: 'None (very fast)' }, []));
api.fetchApi('/manager/preview_method') api.fetchApi('/manager/preview_method')
.then(response => response.text()) .then(response => response.text())
@ -992,10 +989,12 @@ class ManagerMenuDialog extends ComfyDialog {
api.fetchApi(`/manager/preview_method?value=${event.target.value}`); api.fetchApi(`/manager/preview_method?value=${event.target.value}`);
}); });
const previewSetttingItem = createSettingsCombo("Preview method", preview_combo);
// channel // channel
let channel_combo = document.createElement("select"); 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.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') api.fetchApi('/manager/channel_url_list')
.then(response => response.json()) .then(response => response.json())
.then(async data => { .then(async data => {
@ -1004,7 +1003,7 @@ class ManagerMenuDialog extends ComfyDialog {
for (let i in urls) { for (let i in urls) {
if (urls[i] != '') { if (urls[i] != '') {
let name_url = urls[i].split('::'); 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 // share
let share_combo = document.createElement("select"); 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.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 = [ const share_options = [
['none', 'None'], ['none', 'None'],
['openart', 'OpenArt AI'], ['openart', 'OpenArt AI'],
@ -1034,7 +1035,7 @@ class ManagerMenuDialog extends ComfyDialog {
['all', 'All'], ['all', 'All'],
]; ];
for (const option of share_options) { 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') 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"); 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.setAttribute("title", "When loading the workflow, configure which version of the component to use.");
component_policy_combo.className = "cm-menu-combo"; 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: 'Component: Use workflow version' }, [])); component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Use workflow version' }, []));
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, [])); component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Use higher version' }, []));
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, [])); component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Use my version' }, []));
api.fetchApi('/manager/policy/component') api.fetchApi('/manager/policy/component')
.then(response => response.text()) .then(response => response.text())
.then(data => { .then(data => {
@ -1074,15 +1077,14 @@ class ManagerMenuDialog extends ComfyDialog {
set_component_policy(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 = document.createElement("select");
if(isElectron)
update_policy_combo.style.display = 'none';
update_policy_combo.setAttribute("title", "Sets the policy to be applied when performing an update."); 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.className = "cm-menu-combo p-select p-component p-inputwrapper p-inputwrapper-filled";
update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'Update: ComfyUI Stable Version' }, [])); update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'ComfyUI Stable Version' }, []));
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'Update: ComfyUI Nightly Version' }, [])); update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'ComfyUI Nightly Version' }, []));
api.fetchApi('/manager/policy/update') api.fetchApi('/manager/policy/update')
.then(response => response.text()) .then(response => response.text())
.then(data => { .then(data => {
@ -1093,20 +1095,22 @@ class ManagerMenuDialog extends ComfyDialog {
api.fetchApi(`/manager/policy/update?value=${event.target.value}`); api.fetchApi(`/manager/policy/update?value=${event.target.value}`);
}); });
return [ const updateSetttingItem = createSettingsCombo("Update", update_policy_combo);
$el("br", {}, []),
this.datasrc_combo,
channel_combo,
preview_combo,
share_combo,
component_policy_combo,
update_policy_combo,
$el("br", {}, []),
$el("br", {}, []), if(isElectron)
$el("filedset.cm-experimental", {}, [ updateSetttingItem.style.display = 'none';
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("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]),
$el("button.cm-experimental-button", { $el("button.p-button.p-component.cm-button.cm-experimental-button", {
type: "button", type: "button",
textContent: "Snapshot Manager", textContent: "Snapshot Manager",
onclick: onclick:
@ -1116,7 +1120,7 @@ class ManagerMenuDialog extends ComfyDialog {
SnapshotManager.instance.show(); SnapshotManager.instance.show();
} }
}), }),
$el("button.cm-experimental-button", { $el("button.p-button.p-component.cm-button.cm-experimental-button.mt-2", {
type: "button", type: "button",
textContent: "Install PIP packages", textContent: "Install PIP packages",
onclick: onclick:
@ -1134,7 +1138,7 @@ class ManagerMenuDialog extends ComfyDialog {
createControlsRight() { createControlsRight() {
const elts = [ const elts = [
$el("button.cm-button", { $el("button.p-button.p-component.cm-button", {
id: 'cm-manual-button', id: 'cm-manual-button',
type: "button", type: "button",
textContent: "Community Manual", textContent: "Community Manual",
@ -1185,11 +1189,11 @@ class ManagerMenuDialog extends ComfyDialog {
}) })
]), ]),
$el("button", { $el("button.p-button.p-component.cm-button", {
id: 'workflowgallery-button', id: 'workflowgallery-button',
type: "button", type: "button",
style: { style: {
...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {}) // ...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {})
}, },
onclick: (e) => { onclick: (e) => {
const last_visited_site = localStorage.getItem("wg_last_visited") const last_visited_site = localStorage.getItem("wg_last_visited")
@ -1212,7 +1216,7 @@ class ManagerMenuDialog extends ComfyDialog {
}, [ }, [
$el("p", { $el("p", {
id: 'workflowgallery-button-last-visited-label', 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: { style: {
'text-align': 'center', 'text-align': 'center',
'color': 'var(--input-text)', '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', id: 'cm-nodeinfo-button',
type: "button", type: "button",
textContent: "Nodes Info", textContent: "Nodes Info",
onclick: () => { window.open("https://ltdrdata.github.io/", "comfyui-node-info"); } onclick: () => { window.open("https://ltdrdata.github.io/", "comfyui-node-info"); }
}), }),
$el("br", {}, []),
]; ];
var textarea = document.createElement("div"); var textarea = document.createElement("div");
@ -1249,31 +1252,23 @@ class ManagerMenuDialog extends ComfyDialog {
constructor() { constructor() {
super(); 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 = const frame = buildGuiFrame(
$el("div.comfy-modal-content", 'cm-manager-dialog', // dialog id
[ `ComfyUI Manager ${manager_version}`, // dialog title
$el("tr.cm-title", {}, [ "i.mdi.mdi-puzzle", // dialog icon class to show before title
$el("font", {size:6, color:"white"}, [`ComfyUI Manager ${manager_version}`])] content, // dialog content element
), this
$el("br", {}, []), ); // send this so we can attach close functions
$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()])
]),
$el("br", {}, []), this.element = frame;
close_button,
]
);
content.style.width = '100%';
content.style.height = '100%';
this.element = $el("div.comfy-modal", { id:'cm-manager-dialog', parent: document.body }, [ content ]);
} }
get isVisible() { get isVisible() {
@ -1281,7 +1276,7 @@ class ManagerMenuDialog extends ComfyDialog {
} }
show() { show() {
this.element.style.display = "block"; this.element.style.display = "flex";
} }
toggleVisibility() { toggleVisibility() {

View File

@ -1,8 +1,9 @@
.cn-manager { .cn-manager {
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; --grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
z-index: 1099; z-index: 1099;
width: 80%; width: 80vw;
height: 80%; height: 75vh;
min-height: 30em;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
@ -10,6 +11,7 @@
font-family: arial, sans-serif; font-family: arial, sans-serif;
text-underline-offset: 3px; text-underline-offset: 3px;
outline: none; outline: none;
margin: calc(var(--spacing)*2);
} }
.cn-manager .cn-flex-auto { .cn-manager .cn-flex-auto {
@ -17,17 +19,21 @@
} }
.cn-manager button { .cn-manager button {
width: auto;
position: relative;
overflow: hidden;
font-size: 16px; font-size: 16px;
color: var(--input-text); color: var(--input-text);
background-color: var(--comfy-input-bg); background-color: var(--comfy-input-bg);
border-radius: 8px;
border-color: var(--border-color); border-color: var(--border-color);
border-style: solid;
margin: 0; margin: 0;
padding: 4px 8px;
min-width: 100px; min-width: 100px;
} }
.cn-manager button:hover {
filter: brightness(125%);
}
.cn-manager button:disabled, .cn-manager button:disabled,
.cn-manager input:disabled, .cn-manager input:disabled,
.cn-manager select:disabled { .cn-manager select:disabled {
@ -40,8 +46,13 @@
.cn-manager .cn-manager-restart { .cn-manager .cn-manager-restart {
display: none; display: none;
background-color: #500000; background-color: #500000 !important;
color: white; border-color: #88181b !important;
color: white !important;
}
.cn-manager .cn-manager-restart:hover {
background-color: #88181b !important;
} }
.cn-manager .cn-manager-stop { .cn-manager .cn-manager-stop {
@ -79,7 +90,6 @@
flex-wrap: wrap; flex-wrap: wrap;
gap: 5px; gap: 5px;
align-items: center; align-items: center;
padding: 0 5px;
} }
.cn-manager-header label { .cn-manager-header label {
@ -91,16 +101,32 @@
.cn-manager-filter { .cn-manager-filter {
height: 28px; height: 28px;
line-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 { .cn-manager-keywords {
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
padding: 0 5px 0 26px; padding: 0 5px 0 26px;
background: var(--comfy-input-bg);
background-size: 16px; background-size: 16px;
background-position: 5px center; background-position: 5px center;
background-repeat: no-repeat; 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"); 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 { .cn-manager-status {
@ -588,6 +614,10 @@
height: 100%; height: 100%;
} }
.cn-install-buttons button {
padding: 4px 8px;
}
.cn-selected-buttons { .cn-selected-buttons {
display: flex; display: flex;
gap: 5px; gap: 5px;

View File

@ -1,6 +1,7 @@
import { app } from "../../scripts/app.js"; import { app } from "../../scripts/app.js";
import { ComfyDialog, $el } from "../../scripts/ui.js"; import { ComfyDialog, $el } from "../../scripts/ui.js";
import { api } from "../../scripts/api.js"; import { api } from "../../scripts/api.js";
import { buildGuiFrameCustomHeader, createSettingsCombo } from "./comfyui-gui-builder.js";
import { import {
manager_instance, rebootAPI, install_via_git_url, manager_instance, rebootAPI, install_via_git_url,
@ -18,32 +19,19 @@ loadCss("./custom-nodes-manager.css");
const gridId = "node"; const gridId = "node";
const pageHtml = ` const pageHtml = `
<div class="cn-manager-header"> <div class="cn-manager cn-manager-dark">
<label>Filter <div class="cn-manager-grid"></div>
<select class="cn-manager-filter"></select> <div class="cn-manager-selection"></div>
</label> <div class="cn-manager-message"></div>
<input class="cn-manager-keywords" type="search" placeholder="Search" /> <div class="cn-manager-footer">
<div class="cn-manager-status"></div> <button class="cn-manager-restart p-button p-component">Restart</button>
<div class="cn-flex-auto"></div> <button class="cn-manager-stop p-button p-component">Stop</button>
<div class="cn-manager-channel"></div> <div class="cn-flex-auto"></div>
</div> <button class="cn-manager-used-in-workflow p-button p-component">Used In Workflow</button>
<div class="cn-manager-grid"></div> <button class="cn-manager-check-update p-button p-component">Check Update</button>
<div class="cn-manager-selection"></div> <button class="cn-manager-check-missing p-button p-component">Check Missing</button>
<div class="cn-manager-message"></div> <button class="cn-manager-install-url p-button p-component">Install via Git URL</button>
<div class="cn-manager-footer"> </div>
<button class="cn-manager-back">
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 8H18M2 8L8 2M2 8L8 14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Back
</button>
<button class="cn-manager-restart">Restart</button>
<button class="cn-manager-stop">Stop</button>
<div class="cn-flex-auto"></div>
<button class="cn-manager-used-in-workflow">Used In Workflow</button>
<button class="cn-manager-check-update">Check Update</button>
<button class="cn-manager-check-missing">Check Missing</button>
<button class="cn-manager-install-url">Install via Git URL</button>
</div> </div>
`; `;
@ -89,11 +77,26 @@ export class CustomNodesManager {
} }
init() { init() {
this.element = $el("div", { const header = $el("div.cn-manager-header.px-2", {}, [
parent: document.body, // $el("label", {}, [
className: "comfy-modal cn-manager" // $el("span", { textContent: "Filter" }),
}); // $el("select.cn-manager-filter")
this.element.innerHTML = pageHtml; // ]),
createSettingsCombo("Filter", $el("select.cn-manager-filter")),
$el("input.cn-manager-keywords.p-inputtext.p-component", { type: "search", placeholder: "Search" }),
$el("div.cn-manager-status"),
$el("div.cn-flex-auto"),
$el("div.cn-manager-channel")
]);
const frame = buildGuiFrameCustomHeader(
'cn-manager-dialog', // dialog id
header, // custom header element
pageHtml, // dialog content element
this
); // send this so we can attach close functions
this.element = frame;
this.element.setAttribute("tabindex", 0); this.element.setAttribute("tabindex", 0);
this.element.focus(); this.element.focus();
@ -372,7 +375,7 @@ export class CustomNodesManager {
return list.map(id => { return list.map(id => {
const bt = buttons[id]; const bt = buttons[id];
return `<button class="cn-btn-${id}" group="${action}" mode="${bt.mode}">${bt.label}</button>`; return `<button class="cn-btn-${id} p-button p-component" group="${action}" mode="${bt.mode}">${bt.label}</button>`;
}).join(""); }).join("");
} }
@ -655,7 +658,6 @@ export class CustomNodesManager {
} }
renderGrid() { renderGrid() {
// update theme // update theme
const globalStyle = window.getComputedStyle(document.body); const globalStyle = window.getComputedStyle(document.body);
this.colorVars = { this.colorVars = {

View File

@ -1,13 +1,15 @@
.cmm-manager { .cmm-manager {
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; --grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
z-index: 1099; z-index: 1099;
width: 80%; width: 80vw;
height: 80%; height: 75vh;
min-height: 30em;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
color: var(--fg-color); color: var(--fg-color);
font-family: arial, sans-serif; font-family: arial, sans-serif;
margin: calc(var(--spacing)*2);
} }
.cmm-manager .cmm-flex-auto { .cmm-manager .cmm-flex-auto {
@ -18,14 +20,15 @@
font-size: 16px; font-size: 16px;
color: var(--input-text); color: var(--input-text);
background-color: var(--comfy-input-bg); background-color: var(--comfy-input-bg);
border-radius: 8px;
border-color: var(--border-color); border-color: var(--border-color);
border-style: solid;
margin: 0; margin: 0;
padding: 4px 8px;
min-width: 100px; min-width: 100px;
} }
.cmm-manager button:hover {
filter: brightness(125%);
}
.cmm-manager button:disabled, .cmm-manager button:disabled,
.cmm-manager input:disabled, .cmm-manager input:disabled,
.cmm-manager select:disabled { .cmm-manager select:disabled {
@ -38,7 +41,7 @@
.cmm-manager .cmm-manager-refresh { .cmm-manager .cmm-manager-refresh {
display: none; display: none;
background-color: #000080; background-color: #000080 !important;
color: white; color: white;
} }
@ -53,7 +56,6 @@
flex-wrap: wrap; flex-wrap: wrap;
gap: 5px; gap: 5px;
align-items: center; align-items: center;
padding: 0 5px;
} }
.cmm-manager-header label { .cmm-manager-header label {
@ -67,16 +69,34 @@
.cmm-manager-filter { .cmm-manager-filter {
height: 28px; height: 28px;
line-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);
}
.cmm-manager-type:hover,
.cmm-manager-base:hover,
.cmm-manager-filter:hover {
filter: brightness(125%);
} }
.cmm-manager-keywords { .cmm-manager-keywords {
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
padding: 0 5px 0 26px; padding: 0 5px 0 26px;
background: var(--comfy-input-bg);
background-size: 16px; background-size: 16px;
background-position: 5px center; background-position: 5px center;
background-repeat: no-repeat; 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"); 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;
} }
.cmm-manager-status { .cmm-manager-status {
@ -148,6 +168,10 @@
color: white; color: white;
} }
.cmm-btn-install {
padding: 4px 8px;
}
.cmm-btn-download { .cmm-btn-download {
width: 18px; width: 18px;
height: 18px; height: 18px;

View File

@ -9,39 +9,22 @@ import { api } from "../../scripts/api.js";
// https://cenfun.github.io/turbogrid/api.html // https://cenfun.github.io/turbogrid/api.html
import TG from "./turbogrid.esm.js"; import TG from "./turbogrid.esm.js";
import { buildGuiFrameCustomHeader, createSettingsCombo } from "./comfyui-gui-builder.js";
loadCss("./model-manager.css"); loadCss("./model-manager.css");
const gridId = "model"; const gridId = "model";
const pageHtml = ` const pageHtml = `
<div class="cmm-manager-header"> <div class="cmm-manager cmm-manager-dark">
<label>Filter <div class="cmm-manager-grid"></div>
<select class="cmm-manager-filter"></select> <div class="cmm-manager-selection"></div>
</label> <div class="cmm-manager-message"></div>
<label>Type <div class="cmm-manager-footer">
<select class="cmm-manager-type"></select> <button class="cmm-manager-refresh p-button p-component">Refresh</button>
</label> <button class="cmm-manager-stop p-button p-component">Stop</button>
<label>Base <div class="cmm-flex-auto"></div>
<select class="cmm-manager-base"></select> </div>
</label>
<input class="cmm-manager-keywords" type="search" placeholder="Search" />
<div class="cmm-manager-status"></div>
<div class="cmm-flex-auto"></div>
</div>
<div class="cmm-manager-grid"></div>
<div class="cmm-manager-selection"></div>
<div class="cmm-manager-message"></div>
<div class="cmm-manager-footer">
<button class="cmm-manager-back">
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 8H18M2 8L8 2M2 8L8 14" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Back
</button>
<button class="cmm-manager-refresh">Refresh</button>
<button class="cmm-manager-stop">Stop</button>
<div class="cmm-flex-auto"></div>
</div> </div>
`; `;
@ -64,11 +47,23 @@ export class ModelManager {
} }
init() { init() {
this.element = $el("div", { const header = $el("div.cmm-manager-header", {}, [
parent: document.body, createSettingsCombo("Filter", $el("select.cmm-manager-filter")),
className: "comfy-modal cmm-manager" createSettingsCombo("Type", $el("select.cmm-manager-type")),
}); createSettingsCombo("Base", $el("select.cmm-manager-base")),
this.element.innerHTML = pageHtml; $el("input.cmm-manager-keywords.p-inputtext.p-component", { type: "search", placeholder: "Search" }),
$el("div.cmm-manager-status"),
$el("div.cmm-flex-auto")
]);
const frame = buildGuiFrameCustomHeader(
'cmm-manager-dialog', // dialog id
header, // custom header element
pageHtml, // dialog content element
this
); // send this so we can attach close functions
this.element = frame;
this.initFilter(); this.initFilter();
this.bindEvents(); this.bindEvents();
this.initGrid(); this.initGrid();
@ -347,7 +342,7 @@ export class ModelManager {
if (installed === "True") { if (installed === "True") {
return `<div class="cmm-icon-passed">${icons.passed}</div>`; return `<div class="cmm-icon-passed">${icons.passed}</div>`;
} }
return `<button class="cmm-btn-install" mode="install">Install</button>`; return `<button class="cmm-btn-install p-button p-component" mode="install">Install</button>`;
} }
}, { }, {
id: 'url', id: 'url',
@ -420,7 +415,7 @@ export class ModelManager {
} }
this.selectedModels = selectedList; this.selectedModels = selectedList;
this.showSelection(`<span>Selected <b>${selectedList.length}</b> models <button class="cmm-btn-install" mode="install">Install</button>`); this.showSelection(`<span>Selected <b>${selectedList.length}</b> models <button class="cmm-btn-install p-button p-component" mode="install">Install</button>`);
} }
focusInstall(item) { focusInstall(item) {

65
js/snapshot.css Normal file
View File

@ -0,0 +1,65 @@
.snapshot-manager {
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
z-index: 1099;
width: 80vw;
height: 75vh;
min-height: 30em;
display: flex;
flex-direction: column;
gap: 10px;
color: var(--fg-color);
font-family: arial, sans-serif;
text-underline-offset: 3px;
outline: none;
margin: calc(var(--spacing)*2);
}
.snapshot-manager button {
width: auto;
position: relative;
overflow: hidden;
font-size: 16px;
color: var(--input-text);
background-color: var(--comfy-input-bg);
border-color: var(--border-color);
margin: 0;
min-width: 100px;
padding: 4px 8px;
}
.snapshot-manager .snapshot-restore-btn {
background-color: #00158f !important;
border-color: #2025b9 !important;
color: white !important;
}
.snapshot-manager .snapshot-remove-btn {
background-color: #970000 !important;
border-color: #be2127 !important;
color: white !important;
}
.snapshot-manager button:hover {
filter: brightness(125%);
}
.snapshot-manager .data-btns {
display: flex;
flex-direction: column;
gap: calc(var(--spacing)*2);
padding: calc(var(--spacing)*2);
align-items: center;
justify-content: center;
height: 100%;
}
.snapshot-footer {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
}
.snapshot-manager .cn-flex-auto {
flex: auto;
}

View File

@ -1,8 +1,10 @@
import { app } from "../../scripts/app.js"; import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js" import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js"; import { ComfyDialog, $el } from "../../scripts/ui.js";
import { manager_instance, rebootAPI, show_message, handle403Response } from "./common.js"; import { manager_instance, rebootAPI, show_message, handle403Response, loadCss } from "./common.js";
import { buildGuiFrame } from "./comfyui-gui-builder.js";
loadCss("./snapshot.css");
async function restore_snapshot(target) { async function restore_snapshot(target) {
if(SnapshotManager.instance) { if(SnapshotManager.instance) {
@ -27,7 +29,7 @@ async function restore_snapshot(target) {
} }
finally { finally {
await SnapshotManager.instance.invalidateControl(); await SnapshotManager.instance.invalidateControl();
SnapshotManager.instance.updateMessage("<BR>To apply the snapshot, please <button id='cm-reboot-button2' class='cm-small-button'>RESTART</button> ComfyUI. And refresh browser.", 'cm-reboot-button2'); SnapshotManager.instance.updateMessage("<BR>To apply the snapshot, please <button id='cm-reboot-button2' class='p-button p-component'>RESTART</button> ComfyUI. And refresh browser.", 'cm-reboot-button2');
} }
} }
} }
@ -88,6 +90,8 @@ export class SnapshotManager extends ComfyDialog {
message_box = null; message_box = null;
data = null; data = null;
content = $el("div.snapshot-manager");
clear() { clear() {
this.restore_buttons = []; this.restore_buttons = [];
this.message_box = null; this.message_box = null;
@ -96,9 +100,18 @@ export class SnapshotManager extends ComfyDialog {
constructor(app, manager_dialog) { constructor(app, manager_dialog) {
super(); super();
this.manager_dialog = manager_dialog; // this.manager_dialog = manager_dialog;
this.search_keyword = ''; this.search_keyword = '';
this.element = $el("div.comfy-modal", { parent: document.body }, []);
const frame = buildGuiFrame(
'snapshot-manager-dialog', // dialog id
'Snapshot Manager', // title
'i.mdi.mdi-puzzle', // icon class
this.content, // dialog content element
this
); // send this so we can attach close functions
this.element = frame;
} }
async remove_item() { async remove_item() {
@ -109,7 +122,7 @@ export class SnapshotManager extends ComfyDialog {
createControls() { createControls() {
return [ return [
$el("button.cm-small-button", { $el("button.p-button.p-component", {
type: "button", type: "button",
textContent: "Close", textContent: "Close",
onclick: () => { this.close(); } onclick: () => { this.close(); }
@ -132,8 +145,8 @@ export class SnapshotManager extends ComfyDialog {
this.clear(); this.clear();
this.data = (await getSnapshotList()).items; this.data = (await getSnapshotList()).items;
while (this.element.children.length) { while (this.content.children.length) {
this.element.removeChild(this.element.children[0]); this.content.removeChild(this.content.children[0]);
} }
await this.createGrid(); await this.createGrid();
@ -204,20 +217,21 @@ export class SnapshotManager extends ComfyDialog {
data2.innerHTML = `&nbsp;${data}`; data2.innerHTML = `&nbsp;${data}`;
var data_button = document.createElement('td'); var data_button = document.createElement('td');
data_button.style.textAlign = "center"; data_button.style.textAlign = "center";
data_button.className = "data-btns";
var restoreBtn = document.createElement('button'); var restoreBtn = document.createElement('button');
restoreBtn.className = "snapshot-restore-btn p-button p-component";
restoreBtn.innerHTML = 'Restore'; restoreBtn.innerHTML = 'Restore';
restoreBtn.style.width = "100px"; restoreBtn.style.width = "100px";
restoreBtn.style.backgroundColor = 'blue';
restoreBtn.addEventListener('click', function() { restoreBtn.addEventListener('click', function() {
restore_snapshot(data); restore_snapshot(data);
}); });
var removeBtn = document.createElement('button'); var removeBtn = document.createElement('button');
removeBtn.className = "snapshot-remove-btn p-button p-component";
removeBtn.innerHTML = 'Remove'; removeBtn.innerHTML = 'Remove';
removeBtn.style.width = "100px"; removeBtn.style.width = "100px";
removeBtn.style.backgroundColor = 'red';
removeBtn.addEventListener('click', function() { removeBtn.addEventListener('click', function() {
remove_snapshot(data); remove_snapshot(data);
@ -241,13 +255,14 @@ export class SnapshotManager extends ComfyDialog {
let self = this; let self = this;
const panel = document.createElement('div'); const panel = document.createElement('div');
panel.style.width = "100%"; panel.style.width = "100%";
panel.style.height = "100%";
panel.appendChild(grid); panel.appendChild(grid);
function handleResize() { function handleResize() {
const parentHeight = self.element.clientHeight; const parentHeight = self.element.clientHeight;
const gridHeight = parentHeight - 200; const gridHeight = parentHeight - 200;
grid.style.height = gridHeight + "px"; // grid.style.height = gridHeight + "px";
} }
window.addEventListener("resize", handleResize); window.addEventListener("resize", handleResize);
@ -256,25 +271,17 @@ export class SnapshotManager extends ComfyDialog {
grid.style.width = "100%"; grid.style.width = "100%";
grid.style.height = "100%"; grid.style.height = "100%";
grid.style.overflowY = "scroll"; grid.style.overflowY = "scroll";
this.element.style.height = "85%";
this.element.style.width = "80%"; this.content.appendChild(panel);
this.element.appendChild(panel);
handleResize(); handleResize();
} }
async createBottomControls() { async createBottomControls() {
var close_button = document.createElement("button");
close_button.className = "cm-small-button";
close_button.innerHTML = "Close";
close_button.onclick = () => { this.close(); }
close_button.style.display = "inline-block";
var save_button = document.createElement("button"); var save_button = document.createElement("button");
save_button.className = "cm-small-button"; save_button.className = "p-button p-component";
save_button.innerHTML = "Save snapshot"; save_button.innerHTML = "Save snapshot";
save_button.onclick = () => { save_current_snapshot(); } save_button.onclick = () => { save_current_snapshot(); }
save_button.style.display = "inline-block";
save_button.style.horizontalAlign = "right"; save_button.style.horizontalAlign = "right";
save_button.style.width = "170px"; save_button.style.width = "170px";
@ -282,15 +289,19 @@ export class SnapshotManager extends ComfyDialog {
this.message_box.style.height = '60px'; this.message_box.style.height = '60px';
this.message_box.style.verticalAlign = 'middle'; this.message_box.style.verticalAlign = 'middle';
this.element.appendChild(this.message_box); const footer = $el("div.snapshot-footer");
this.element.appendChild(close_button); const spacer = $el("div.cn-flex-auto");
this.element.appendChild(save_button); footer.appendChild(spacer);
footer.appendChild(save_button);
this.content.appendChild(this.message_box);
this.content.appendChild(footer);
} }
async show() { async show() {
try { try {
this.invalidateControl(); this.invalidateControl();
this.element.style.display = "block"; this.element.style.display = "flex";
this.element.style.zIndex = 1099; this.element.style.zIndex = 1099;
} }
catch(exception) { catch(exception) {