update install

This commit is contained in:
cenfun 2024-06-21 11:40:37 +08:00
parent da5f2010fd
commit ce69e124d5
2 changed files with 87 additions and 338 deletions

View File

@ -207,5 +207,6 @@ export async function fetchData(route, options) {
export const icons = { export const icons = {
search: '<svg viewBox="0 0 24 24" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21 21-4.486-4.494M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0"/></svg>', search: '<svg viewBox="0 0 24 24" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21 21-4.486-4.494M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0"/></svg>',
extensions: '<svg viewBox="64 64 896 896" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M843.5 737.4c-12.4-75.2-79.2-129.1-155.3-125.4S550.9 676 546 752c-153.5-4.8-208-40.7-199.1-113.7 3.3-27.3 19.8-41.9 50.1-49 18.4-4.3 38.8-4.9 57.3-3.2 1.7.2 3.5.3 5.2.5 11.3 2.7 22.8 5 34.3 6.8 34.1 5.6 68.8 8.4 101.8 6.6 92.8-5 156-45.9 159.2-132.7 3.1-84.1-54.7-143.7-147.9-183.6-29.9-12.8-61.6-22.7-93.3-30.2-14.3-3.4-26.3-5.7-35.2-7.2-7.9-75.9-71.5-133.8-147.8-134.4S189.7 168 180.5 243.8s40 146.3 114.2 163.9 149.9-23.3 175.7-95.1c9.4 1.7 18.7 3.6 28 5.8 28.2 6.6 56.4 15.4 82.4 26.6 70.7 30.2 109.3 70.1 107.5 119.9-1.6 44.6-33.6 65.2-96.2 68.6-27.5 1.5-57.6-.9-87.3-5.8-8.3-1.4-15.9-2.8-22.6-4.3-3.9-.8-6.6-1.5-7.8-1.8l-3.1-.6c-2.2-.3-5.9-.8-10.7-1.3-25-2.3-52.1-1.5-78.5 4.6-55.2 12.9-93.9 47.2-101.1 105.8-15.7 126.2 78.6 184.7 276 188.9 29.1 70.4 106.4 107.9 179.6 87 73.3-20.9 119.3-93.4 106.9-168.6M329.1 345.2a83.3 83.3 0 1 1 .01-166.61 83.3 83.3 0 0 1-.01 166.61M695.6 845a83.3 83.3 0 1 1 .01-166.61A83.3 83.3 0 0 1 695.6 845"/></svg>', extensions: '<svg viewBox="64 64 896 896" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M843.5 737.4c-12.4-75.2-79.2-129.1-155.3-125.4S550.9 676 546 752c-153.5-4.8-208-40.7-199.1-113.7 3.3-27.3 19.8-41.9 50.1-49 18.4-4.3 38.8-4.9 57.3-3.2 1.7.2 3.5.3 5.2.5 11.3 2.7 22.8 5 34.3 6.8 34.1 5.6 68.8 8.4 101.8 6.6 92.8-5 156-45.9 159.2-132.7 3.1-84.1-54.7-143.7-147.9-183.6-29.9-12.8-61.6-22.7-93.3-30.2-14.3-3.4-26.3-5.7-35.2-7.2-7.9-75.9-71.5-133.8-147.8-134.4S189.7 168 180.5 243.8s40 146.3 114.2 163.9 149.9-23.3 175.7-95.1c9.4 1.7 18.7 3.6 28 5.8 28.2 6.6 56.4 15.4 82.4 26.6 70.7 30.2 109.3 70.1 107.5 119.9-1.6 44.6-33.6 65.2-96.2 68.6-27.5 1.5-57.6-.9-87.3-5.8-8.3-1.4-15.9-2.8-22.6-4.3-3.9-.8-6.6-1.5-7.8-1.8l-3.1-.6c-2.2-.3-5.9-.8-10.7-1.3-25-2.3-52.1-1.5-78.5 4.6-55.2 12.9-93.9 47.2-101.1 105.8-15.7 126.2 78.6 184.7 276 188.9 29.1 70.4 106.4 107.9 179.6 87 73.3-20.9 119.3-93.4 106.9-168.6M329.1 345.2a83.3 83.3 0 1 1 .01-166.61 83.3 83.3 0 0 1-.01 166.61M695.6 845a83.3 83.3 0 1 1 .01-166.61A83.3 83.3 0 0 1 695.6 845"/></svg>',
conflicts: '<svg viewBox="0 0 400 400" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="m397.2 350.4.2-.2-180-320-.2.2C213.8 24.2 207.4 20 200 20s-13.8 4.2-17.2 10.4l-.2-.2-180 320 .2.2c-1.6 2.8-2.8 6-2.8 9.6 0 11 9 20 20 20h360c11 0 20-9 20-20 0-3.6-1.2-6.8-2.8-9.6M220 340h-40v-40h40zm0-60h-40V120h40z"/></svg>' conflicts: '<svg viewBox="0 0 400 400" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="m397.2 350.4.2-.2-180-320-.2.2C213.8 24.2 207.4 20 200 20s-13.8 4.2-17.2 10.4l-.2-.2-180 320 .2.2c-1.6 2.8-2.8 6-2.8 9.6 0 11 9 20 20 20h360c11 0 20-9 20-20 0-3.6-1.2-6.8-2.8-9.6M220 340h-40v-40h40zm0-60h-40V120h40z"/></svg>',
passed: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 426.667 426.667"><path fill="#6AC259" d="M213.333,0C95.518,0,0,95.514,0,213.333s95.518,213.333,213.333,213.333c117.828,0,213.333-95.514,213.333-213.333S331.157,0,213.333,0z M174.199,322.918l-93.935-93.931l31.309-31.309l62.626,62.622l140.894-140.898l31.309,31.309L174.199,322.918z"/></svg>'
} }

View File

@ -20,7 +20,7 @@ const pageCss = `
font-family: arial, sans-serif; font-family: arial, sans-serif;
} }
.cmm-manager .cn-flex-auto { .cmm-manager .cmm-flex-auto {
flex: auto; flex: auto;
} }
@ -46,12 +46,6 @@ const pageCss = `
background-color: var(--comfy-input-bg); background-color: var(--comfy-input-bg);
} }
.cmm-manager .cmm-manager-restart {
display: none;
background-color: #500000;
color: white;
}
.cmm-manager-header { .cmm-manager-header {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@ -66,6 +60,8 @@ const pageCss = `
align-items: center; align-items: center;
} }
.cmm-manager-type,
.cmm-manager-base,
.cmm-manager-filter { .cmm-manager-filter {
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
@ -108,13 +104,13 @@ const pageCss = `
background: var(--bg-color); background: var(--bg-color);
} }
.cmm-manager-grid .cn-node-name a { .cmm-manager-grid .cmm-node-name a {
color: skyblue; color: skyblue;
text-decoration: none; text-decoration: none;
word-break: break-word; word-break: break-word;
} }
.cmm-manager-grid .cn-node-desc a { .cmm-manager-grid .cmm-node-desc a {
color: #5555FF; color: #5555FF;
font-weight: bold; font-weight: bold;
text-decoration: none; text-decoration: none;
@ -124,158 +120,31 @@ const pageCss = `
text-decoration: underline; text-decoration: underline;
} }
.cmm-manager-grid .cn-extensions-button, .cmm-icon-passed {
.cmm-manager-grid .cn-conflicts-button {
display: inline-block;
width: 20px; width: 20px;
height: 20px; height: 20px;
color: green; position: absolute;
border: none; left: calc(50% - 10px);
padding: 0; top: calc(50% - 10px);
margin: 0;
background: none;
min-width: 20px;
} }
.cmm-manager-grid .cn-conflicts-button { .cmm-manager .cmm-btn-enable {
color: orange;
}
.cmm-manager-grid .cn-extensions-list,
.cmm-manager-grid .cn-conflicts-list {
line-height: normal;
text-align: left;
max-height: 80%;
min-height: 200px;
min-width: 300px;
overflow-y: auto;
font-size: 12px;
border-radius: 5px;
padding: 10px;
filter: drop-shadow(2px 5px 5px rgb(0 0 0 / 30%));
}
.cmm-manager-grid .cn-extensions-list {
border-color: var(--bg-color);
}
.cmm-manager-grid .cn-conflicts-list {
background-color: #CCCC55;
color: #AA3333;
}
.cmm-manager-grid .cn-extensions-list h3,
.cmm-manager-grid .cn-conflicts-list h3 {
margin: 0;
padding: 5px 0;
color: #000;
}
.cn-tag-list {
display: flex;
flex-wrap: wrap;
gap: 5px;
align-items: center;
margin-bottom: 5px;
}
.cn-tag-list > div {
background-color: var(--border-color);
border-radius: 5px;
padding: 0 5px;
}
.cn-install-buttons {
display: flex;
flex-direction: column;
gap: 3px;
padding: 3px;
align-items: center;
justify-content: center;
height: 100%;
}
.cn-selected-buttons {
display: flex;
gap: 5px;
align-items: center;
padding-right: 20px;
}
.cmm-manager .cn-btn-enable {
background-color: blue; background-color: blue;
color: white; color: white;
} }
.cmm-manager .cn-btn-disable { .cmm-manager .cmm-btn-disable {
background-color: MediumSlateBlue; background-color: MediumSlateBlue;
color: white; color: white;
} }
.cmm-manager .cn-btn-update { .cmm-manager .cmm-btn-install {
background-color: blue;
color: white;
}
.cmm-manager .cn-btn-try-update {
background-color: Gray;
color: white;
}
.cmm-manager .cn-btn-try-fix {
background-color: #6495ED;
color: white;
}
.cmm-manager .cn-btn-install {
background-color: black; background-color: black;
color: white; color: white;
} }
.cmm-manager .cn-btn-try-install { .cmm-manager-light .cmm-node-name a {
background-color: Gray;
color: white;
}
.cmm-manager .cn-btn-uninstall {
background-color: red;
color: white;
}
@keyframes cn-btn-loading-bg {
0% {
left: 0;
}
100% {
left: -100px;
}
}
.cmm-manager button.cn-btn-loading {
position: relative;
overflow: hidden;
border-color: rgb(0 119 207 / 80%);
background-color: var(--comfy-input-bg);
}
.cmm-manager button.cn-btn-loading::after {
position: absolute;
top: 0;
left: 0;
content: "";
width: 500px;
height: 100%;
background-image: repeating-linear-gradient(
-45deg,
rgb(0 119 207 / 30%),
rgb(0 119 207 / 30%) 10px,
transparent 10px,
transparent 15px
);
animation: cn-btn-loading-bg 3s linear infinite;
}
.cmm-manager-light .cn-node-name a {
color: blue; color: blue;
} }
@ -283,7 +152,7 @@ const pageCss = `
background-color: #ccc !important; background-color: #ccc !important;
} }
.cmm-manager-light .cn-btn-install { .cmm-manager-light .cmm-btn-install {
background-color: #333; background-color: #333;
} }
@ -302,14 +171,13 @@ const pageHtml = `
</label> </label>
<input class="cmm-manager-keywords" type="search" placeholder="Search" /> <input class="cmm-manager-keywords" type="search" placeholder="Search" />
<div class="cmm-manager-status"></div> <div class="cmm-manager-status"></div>
<div class="cn-flex-auto"></div> <div class="cmm-flex-auto"></div>
</div> </div>
<div class="cmm-manager-grid"></div> <div class="cmm-manager-grid"></div>
<div class="cmm-manager-message"></div> <div class="cmm-manager-message"></div>
<div class="cmm-manager-footer"> <div class="cmm-manager-footer">
<button class="cmm-manager-close">Close</button> <button class="cmm-manager-close">Close</button>
<button class="cmm-manager-restart">Restart</button> <div class="cmm-flex-auto"></div>
<div class="cn-flex-auto"></div>
</div> </div>
`; `;
@ -325,7 +193,6 @@ export class ModelManager {
this.type = ''; this.type = '';
this.base = ''; this.base = '';
this.keywords = ''; this.keywords = '';
this.restartMap = {};
this.init(); this.init();
} }
@ -360,9 +227,6 @@ export class ModelManager {
}, { }, {
label: "Not Installed", label: "Not Installed",
value: "False" value: "False"
}, {
label: "Unknown",
value: "None"
}]; }];
this.typeList = [{ this.typeList = [{
@ -397,73 +261,23 @@ export class ModelManager {
} }
getFilterItem(filter) {
return this.filterList.find(it => it.value === filter)
}
getInstallButtons(installed, title) {
const buttons = {
"install": {
label: "Install",
mode: "install"
},
"try-install": {
label: "Try install",
mode: "install"
}
}
const installGroups = {
"False": ["install"],
'None': ["try-install"]
}
const list = installGroups[installed];
if (!list) {
return "";
}
return list.map(id => {
const bt = buttons[id];
return `<button class="cn-btn-${id}" group="${installed}" mode="${bt.mode}">${bt.label}</button>`;
}).join("");
}
getButton(target) {
if(!target) {
return;
}
const mode = target.getAttribute("mode");
if (!mode) {
return;
}
const group = target.getAttribute("group");
if (!group) {
return;
}
return {
group,
mode,
target,
label: target.innerText
}
}
bindEvents() { bindEvents() {
const eventsMap = { const eventsMap = {
".cmm-manager-filter": { ".cmm-manager-filter": {
change: (e) => { change: (e) => {
this.filter = e.target.value;
this.updateGrid(); this.updateGrid();
} }
}, },
".cmm-manager-type": { ".cmm-manager-type": {
change: (e) => { change: (e) => {
this.type = e.target.value;
this.updateGrid(); this.updateGrid();
} }
}, },
".cmm-manager-base": { ".cmm-manager-base": {
change: (e) => { change: (e) => {
this.base = e.target.value;
this.updateGrid(); this.updateGrid();
} }
}, },
@ -483,14 +297,6 @@ export class ModelManager {
click: (e) => this.close() click: (e) => this.close()
}, },
".cmm-manager-restart": {
click: () => {
if(rebootAPI()) {
this.close();
this.manager_dialog.close();
}
}
}
}; };
Object.keys(eventsMap).forEach(selector => { Object.keys(eventsMap).forEach(selector => {
const target = this.element.querySelector(selector); const target = this.element.querySelector(selector);
@ -524,10 +330,16 @@ export class ModelManager {
}); });
grid.bind('onClick', (e, d) => { grid.bind('onClick', (e, d) => {
const btn = this.getButton(d.e.target); const { rowItem } = d;
if (btn) { const target = d.e.target;
this.installNodes([d.rowItem.hash], btn, d.rowItem.title); const mode = target.getAttribute("mode");
if (mode === "install") {
this.installModel(rowItem);
} }
this.grid.selectAll(false);
this.grid.setRowSelected(rowItem);
}); });
grid.setOption({ grid.setOption({
@ -544,7 +356,7 @@ export class ModelManager {
bindContainerResize: true, bindContainerResize: true,
cellResizeObserver: (rowItem, columnItem) => { cellResizeObserver: (rowItem, columnItem) => {
const autoHeightColumns = ['name', 'installed', 'description']; const autoHeightColumns = ['name', 'description'];
return autoHeightColumns.includes(columnItem.id) return autoHeightColumns.includes(columnItem.id)
}, },
@ -556,9 +368,18 @@ export class ModelManager {
let shouldShown = grid.highlightKeywordsFilter(rowItem, searchableColumns, this.keywords); let shouldShown = grid.highlightKeywordsFilter(rowItem, searchableColumns, this.keywords);
if (shouldShown) { if (shouldShown) {
if(this.filter && rowItem.filterTypes) { if(this.filter && rowItem.installed !== this.filter) {
shouldShown = rowItem.filterTypes.includes(this.filter); return false;
} }
if(this.type && rowItem.type !== this.type) {
return false;
}
if(this.base && rowItem.base !== this.base) {
return false;
}
} }
return shouldShown; return shouldShown;
@ -595,24 +416,26 @@ export class ModelManager {
width: 200, width: 200,
minWidth: 100, minWidth: 100,
maxWidth: 500, maxWidth: 500,
classMap: 'cn-node-name', classMap: 'cmm-node-name',
formatter: function(name, rowItem, columnItem, cellNode) { formatter: function(name, rowItem, columnItem, cellNode) {
return `<a href=${rowItem.reference} target="_blank"><b>${name}</b></a>`; return `<a href=${rowItem.reference} target="_blank"><b>${name}</b></a>`;
} }
}, { }, {
id: 'installed', id: 'installed',
name: 'Install', name: 'Download',
width: 130, width: 130,
minWidth: 110, minWidth: 110,
maxWidth: 200, maxWidth: 200,
sortable: false, sortable: false,
align: 'center', align: 'center',
formatter: (installed, rowItem, columnItem) => { formatter: (installed, rowItem, columnItem) => {
if (rowItem.restart) { if (rowItem.refresh) {
return `<font color="red">Restart Required</span>`; return `<font color="red">Refresh Required</span>`;
} }
const buttons = this.getInstallButtons(installed, rowItem.title); if (installed === "True") {
return `<div class="cn-install-buttons">${buttons}</div>`; return `<div class="cmm-icon-passed">${icons.passed}</div>`;
}
return `<button class="cmm-btn-install" mode="install">Install</button>`;
} }
}, { }, {
id: 'type', id: 'type',
@ -626,14 +449,15 @@ export class ModelManager {
name: 'Description', name: 'Description',
width: 400, width: 400,
maxWidth: 5000, maxWidth: 5000,
classMap: 'cn-node-desc' classMap: 'cmm-node-desc'
}, { }, {
id: 'filename', id: 'filename',
name: 'Filename', name: 'Filename',
width: 150 width: 200
}, { }, {
id: "save_path", id: "save_path",
name: 'Save Path' name: 'Save Path',
width: 200
}]; }];
this.grid.setData({ this.grid.setData({
@ -654,97 +478,38 @@ export class ModelManager {
// =========================================================================================== // ===========================================================================================
focusInstall(item, mode) { async installModel(item) {
const cellNode = this.grid.getCellNode(item, "installed");
if (cellNode) {
const cellBtn = cellNode.querySelector(`button[mode="${mode}"]`);
if (cellBtn) {
cellBtn.classList.add("cn-btn-loading");
return true
}
}
}
async installNodes(list, btn, title) {
const { target, label, mode} = btn;
if(mode === "uninstall") {
title = title || `${list.length} custom nodes`;
if (!confirm(`Are you sure uninstall ${title}?`)) {
return;
}
}
target.classList.add("cn-btn-loading");
this.showLoading(); this.showLoading();
this.showError(""); this.showError("");
let needRestart = false; this.showStatus(`Install ${item.name} ...`);
let errorMsg = "";
for (const hash of list) {
const item = this.grid.getRowItemBy("hash", hash); this.grid.selectAll(false);
if (!item) { this.grid.setRowSelected(item);
errorMsg = `Not found custom node: ${hash}`;
break;
}
this.grid.scrollRowIntoView(item);
if (!this.focusInstall(item, mode)) {
this.grid.onNextUpdated(() => {
this.focusInstall(item, mode);
});
}
this.showStatus(`${label} ${item.title} ...`);
const data = item.originalData; const data = item.originalData;
const res = await fetchData(`/customnode/${mode}`, { const res = await fetchData('/model/install', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data) body: JSON.stringify(data)
}); });
if (res.error) { if (res.error) {
const errorMsg = `Install failed: ${item.name} ${res.error.message}`;
errorMsg = `${item.title} ${mode} failed: `; this.showError(errorMsg);
if(res.status == 403) { this.hideLoading();
errorMsg += `This action is not allowed with this security level configuration.`; return;
} else if(res.status == 404) {
errorMsg += `With the current security level configuration, only custom nodes from the <B>"default channel"</B> can be installed.`;
} else {
errorMsg += res.error.message;
} }
break; item.refresh = true;
}
needRestart = true;
this.grid.setRowSelected(item, false);
item.restart = true;
this.restartMap[item.hash] = true;
this.grid.updateCell(item, "installed"); this.grid.updateCell(item, "installed");
//console.log(res.data);
}
this.hideLoading(); this.hideLoading();
target.classList.remove("cn-btn-loading");
if (errorMsg) { this.showStatus(`Install ${item.name} successfully`);
this.showError(errorMsg); this.showMessage(`To apply the installed model, please click the 'Refresh' button on the main menu.`, "red")
} else {
this.showStatus(`${label} ${list.length} custom node(s) successfully`);
}
if (needRestart) {
this.showRestart();
this.showMessage(`To apply the installed/updated/disabled/enabled custom node, please restart ComfyUI. And refresh browser.`, "red")
}
} }
@ -753,31 +518,32 @@ export class ModelManager {
const typeMap = new Map(); const typeMap = new Map();
const baseMap = new Map(); const baseMap = new Map();
models.forEach((item, i) => { models.forEach((item, i) => {
const { type, base, name, reference } = item; const { type, base, name, reference } = item;
item.originalData = JSON.parse(JSON.stringify(item));
item.hash = md5(name + reference); item.hash = md5(name + reference);
item.id = i + 1; item.id = i + 1;
baseMap.set(type, type); typeMap.set(type, type);
typeMap.set(base, base); baseMap.set(base, base);
}); });
this.typeList = [{ this.typeList = [{
label: "All", label: "All",
value: "" value: ""
}]; }];
this.baseList = [{
label: "All",
value: ""
}];
typeMap.forEach(type => { typeMap.forEach(type => {
this.typeList.push({ this.typeList.push({
label: type, label: type,
value: type value: type
}); });
}); });
this.baseList = [{
label: "All",
value: ""
}];
baseMap.forEach(base => { baseMap.forEach(base => {
this.baseList.push({ this.baseList.push({
label: base, label: base,
@ -794,7 +560,7 @@ export class ModelManager {
this.showLoading(); this.showLoading();
this.showStatus(`Loading data ...`); this.showStatus(`Loading external model list ...`);
const mode = manager_instance.datasrc_combo.value; const mode = manager_instance.datasrc_combo.value;
@ -808,6 +574,7 @@ export class ModelManager {
const { models } = res.data; const { models } = res.data;
this.modelList = this.getModelList(models); this.modelList = this.getModelList(models);
// console.log("models", this.modelList);
this.updateFilter(); this.updateFilter();
@ -858,7 +625,6 @@ export class ModelManager {
setDisabled(disabled) { setDisabled(disabled) {
const $close = this.element.querySelector(".cmm-manager-close"); const $close = this.element.querySelector(".cmm-manager-close");
const $restart = this.element.querySelector(".cmm-manager-restart");
const list = [ const list = [
".cmm-manager-header input", ".cmm-manager-header input",
@ -869,7 +635,7 @@ export class ModelManager {
}) })
.flat() .flat()
.filter(it => { .filter(it => {
return it !== $close && it !== $restart; return it !== $close;
}); });
list.forEach($elem => { list.forEach($elem => {
@ -880,24 +646,6 @@ export class ModelManager {
} }
}); });
Array.from(this.element.querySelectorAll(".cn-btn-loading")).forEach($elem => {
$elem.classList.remove("cn-btn-loading");
});
}
showRestart() {
this.element.querySelector(".cmm-manager-restart").style.display = "block";
}
setFilter(filterValue) {
let filter = "";
const filterItem = this.getFilterItem(filterValue);
if(filterItem) {
filter = filterItem.value;
}
this.filter = filter;
this.element.querySelector(".cmm-manager-filter").value = filter;
} }
setKeywords(keywords = "") { setKeywords(keywords = "") {