mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-01-21 20:00:17 +08:00
Refactor ui.js
This commit is contained in:
parent
aa2ddfabb9
commit
e5b4352878
@ -4,49 +4,49 @@ function $el(tag, propsOrChildren, children) {
|
|||||||
const split = tag.split(".");
|
const split = tag.split(".");
|
||||||
const element = document.createElement(split.shift());
|
const element = document.createElement(split.shift());
|
||||||
element.classList.add(...split);
|
element.classList.add(...split);
|
||||||
if (propsOrChildren) {
|
if (!propsOrChildren) return element;
|
||||||
if (Array.isArray(propsOrChildren)) {
|
if (Array.isArray(propsOrChildren)) {
|
||||||
element.append(...propsOrChildren);
|
element.append(...propsOrChildren);
|
||||||
} else {
|
return element;
|
||||||
const parent = propsOrChildren.parent;
|
|
||||||
delete propsOrChildren.parent;
|
|
||||||
const cb = propsOrChildren.$;
|
|
||||||
delete propsOrChildren.$;
|
|
||||||
|
|
||||||
if (propsOrChildren.style) {
|
|
||||||
Object.assign(element.style, propsOrChildren.style);
|
|
||||||
delete propsOrChildren.style;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(element, propsOrChildren);
|
|
||||||
if (children) {
|
|
||||||
element.append(...children);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent) {
|
|
||||||
parent.append(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cb) {
|
|
||||||
cb(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { parent, style } = propsOrChildren;
|
||||||
|
const cb = propsOrChildren.$;
|
||||||
|
delete propsOrChildren.parent;
|
||||||
|
delete propsOrChildren.$;
|
||||||
|
|
||||||
|
if (style) {
|
||||||
|
Object.assign(element.style, style);
|
||||||
|
delete propsOrChildren.style;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(element, propsOrChildren);
|
||||||
|
if (children) {
|
||||||
|
element.append(...children);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent?.append(element);
|
||||||
|
cb?.(element);
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComfyDialog {
|
class ComfyDialog {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.element = $el("div.comfy-modal", { parent: document.body }, [
|
const p = $el("p", {
|
||||||
$el("div.comfy-modal-content", [
|
$: (p) => (this.textElement = p)
|
||||||
$el("p", { $: (p) => (this.textElement = p) }),
|
});
|
||||||
$el("button", {
|
|
||||||
type: "button",
|
const button = $el("button", {
|
||||||
textContent: "CLOSE",
|
type: "button",
|
||||||
onclick: () => this.close(),
|
textContent: "CLOSE",
|
||||||
}),
|
onclick: this.close.bind(this)
|
||||||
]),
|
});
|
||||||
]);
|
|
||||||
|
const modalContent = $el("div.comfy-modal-content", [p, button]);
|
||||||
|
this.element = $el("div.comfy-modal",
|
||||||
|
{ parent: document.body }, [modalContent]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
@ -76,55 +76,52 @@ class ComfySettingsDialog extends ComfyDialog {
|
|||||||
|
|
||||||
const settingId = "Comfy.Settings." + id;
|
const settingId = "Comfy.Settings." + id;
|
||||||
const v = localStorage[settingId];
|
const v = localStorage[settingId];
|
||||||
let value = v == null ? defaultValue : JSON.parse(v);
|
|
||||||
|
// JSON.parse(null) -> null
|
||||||
|
// If v can be undefined -> JSON.parse(v ?? null)
|
||||||
|
let value = JSON.parse(v) ?? defaultValue;
|
||||||
|
|
||||||
// Trigger initial setting of value
|
// Trigger initial setting of value
|
||||||
if (onChange) {
|
onChange?.(value, undefined);
|
||||||
onChange(value, undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.settings.push({
|
const setter = (v) => {
|
||||||
render: () => {
|
onChange?.(v, value);
|
||||||
const setter = (v) => {
|
localStorage[settingId] = JSON.stringify(v);
|
||||||
if (onChange) {
|
value = v;
|
||||||
onChange(v, value);
|
};
|
||||||
}
|
|
||||||
localStorage[settingId] = JSON.stringify(v);
|
|
||||||
value = v;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof type === "function") {
|
const createLable = (input) => (
|
||||||
return type(name, setter);
|
$el("div", [
|
||||||
}
|
$el("label", { textContent: name || id }, [input])
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
switch (type) {
|
const render = () => {
|
||||||
case "boolean":
|
if (typeof type === "function") {
|
||||||
return $el("div", [
|
return type(name, setter);
|
||||||
$el("label", { textContent: name || id }, [
|
}
|
||||||
$el("input", {
|
|
||||||
type: "checkbox",
|
if (type == 'boolean') {
|
||||||
checked: !!value,
|
return createLable(
|
||||||
oninput: (e) => {
|
$el("input", {
|
||||||
setter(e.target.checked);
|
type: "checkbox",
|
||||||
},
|
checked: !!value,
|
||||||
}),
|
oninput: (e) => setter(e.target.checked),
|
||||||
]),
|
})
|
||||||
]);
|
);
|
||||||
default:
|
}
|
||||||
console.warn("Unsupported setting type, defaulting to text");
|
|
||||||
return $el("div", [
|
console.warn("Unsupported setting type, defaulting to text");
|
||||||
$el("label", { textContent: name || id }, [
|
return createLable(
|
||||||
$el("input", {
|
$el("input", {
|
||||||
value,
|
value,
|
||||||
oninput: (e) => {
|
oninput: (e) => setter(e.target.value),
|
||||||
setter(e.target.value);
|
})
|
||||||
},
|
);
|
||||||
}),
|
|
||||||
]),
|
};
|
||||||
]);
|
|
||||||
}
|
this.settings.push({ render });
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
@ -136,6 +133,7 @@ class ComfySettingsDialog extends ComfyDialog {
|
|||||||
class ComfyList {
|
class ComfyList {
|
||||||
#type;
|
#type;
|
||||||
#text;
|
#text;
|
||||||
|
element;
|
||||||
|
|
||||||
constructor(text, type) {
|
constructor(text, type) {
|
||||||
this.#text = text;
|
this.#text = text;
|
||||||
@ -150,39 +148,40 @@ class ComfyList {
|
|||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const items = await api.getItems(this.#type);
|
const items = await api.getItems(this.#type);
|
||||||
|
|
||||||
|
const processItem = (item) => {
|
||||||
|
// Allow items to specify a custom remove action (e.g. for interrupt current prompt)
|
||||||
|
const removeAction = item.remove || {
|
||||||
|
name: "Delete",
|
||||||
|
cb: () => api.deleteItem(this.#type, item.prompt[1]),
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadButton = $el("button", {
|
||||||
|
textContent: "Load",
|
||||||
|
onclick: () => {
|
||||||
|
if (item.outputs) {
|
||||||
|
this.app.nodeOutputs = item.outputs;
|
||||||
|
}
|
||||||
|
this.app.loadGraphData(item.prompt[3].extra_pnginfo.workflow);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const removeButton = $el("button", {
|
||||||
|
textContent: removeAction.name,
|
||||||
|
onclick: async () => {
|
||||||
|
await removeAction.cb();
|
||||||
|
await this.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $el("div", { textContent: item.prompt[0] + ": " }, [loadButton, removeButton]);
|
||||||
|
};
|
||||||
|
const processSection = (section) => [
|
||||||
|
$el("h4", { textContent: section }),
|
||||||
|
$el("div.comfy-list-items", [...items[section].map(processItem)]),
|
||||||
|
];
|
||||||
|
|
||||||
this.element.replaceChildren(
|
this.element.replaceChildren(
|
||||||
...Object.keys(items).flatMap((section) => [
|
...Object.keys(items).flatMap(processSection),
|
||||||
$el("h4", {
|
|
||||||
textContent: section,
|
|
||||||
}),
|
|
||||||
$el("div.comfy-list-items", [
|
|
||||||
...items[section].map((item) => {
|
|
||||||
// Allow items to specify a custom remove action (e.g. for interrupt current prompt)
|
|
||||||
const removeAction = item.remove || {
|
|
||||||
name: "Delete",
|
|
||||||
cb: () => api.deleteItem(this.#type, item.prompt[1]),
|
|
||||||
};
|
|
||||||
return $el("div", { textContent: item.prompt[0] + ": " }, [
|
|
||||||
$el("button", {
|
|
||||||
textContent: "Load",
|
|
||||||
onclick: () => {
|
|
||||||
if (item.outputs) {
|
|
||||||
app.nodeOutputs = item.outputs;
|
|
||||||
}
|
|
||||||
app.loadGraphData(item.prompt[3].extra_pnginfo.workflow);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
$el("button", {
|
|
||||||
textContent: removeAction.name,
|
|
||||||
onclick: async () => {
|
|
||||||
await removeAction.cb();
|
|
||||||
await this.update();
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
$el("div.comfy-list-actions", [
|
$el("div.comfy-list-actions", [
|
||||||
$el("button", {
|
$el("button", {
|
||||||
textContent: "Clear " + this.#text,
|
textContent: "Clear " + this.#text,
|
||||||
@ -191,15 +190,14 @@ class ComfyList {
|
|||||||
await this.load();
|
await this.load();
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
$el("button", { textContent: "Refresh", onclick: () => this.load() }),
|
$el("button", { textContent: "Refresh", onclick: this.load.bind(this) }),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update() {
|
async update() {
|
||||||
if (this.visible) {
|
if (!this.visible) return;
|
||||||
await this.load();
|
await this.load();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async show() {
|
async show() {
|
||||||
@ -215,13 +213,8 @@ class ComfyList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
if (this.visible) {
|
(this.visible ? this.hide : this.show).call(this);
|
||||||
this.hide();
|
return !this.visible;
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
this.show();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,26 +234,30 @@ export class ComfyUI {
|
|||||||
this.history.update();
|
this.history.update();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.createMenuContainer();
|
||||||
|
this.setStatus({ exec_info: { queue_remaining: "X" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
createMenuContainer() {
|
||||||
const fileInput = $el("input", {
|
const fileInput = $el("input", {
|
||||||
type: "file",
|
type: "file",
|
||||||
accept: ".json,image/png",
|
accept: ".json,image/png",
|
||||||
style: { display: "none" },
|
style: { display: "none" },
|
||||||
parent: document.body,
|
parent: document.body,
|
||||||
onchange: () => {
|
onchange: () => this.app.handleFile(fileInput.files[0]),
|
||||||
app.handleFile(fileInput.files[0]);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.menuContainer = $el("div.comfy-menu", { parent: document.body }, [
|
this.menuContainer = $el("div.comfy-menu", { parent: document.body }, [
|
||||||
$el("div", { style: { overflow: "hidden", position: "relative", width: "100%" } }, [
|
$el("div", { style: { overflow: "hidden", position: "relative", width: "100%" } }, [
|
||||||
$el("span", { $: (q) => (this.queueSize = q) }),
|
$el("span", { $: (q) => (this.queueSize = q) }),
|
||||||
$el("button.comfy-settings-btn", { textContent: "⚙️", onclick: () => this.settings.show() }),
|
$el("button.comfy-settings-btn", { textContent: "⚙️", onclick: this.settings.show.bind(this.settings) }),
|
||||||
]),
|
]),
|
||||||
$el("button.comfy-queue-btn", { textContent: "Queue Prompt", onclick: () => app.queuePrompt(0, this.batchCount) }),
|
$el("button.comfy-queue-btn", { textContent: "Queue Prompt", onclick: () => this.app.queuePrompt(0, this.batchCount) }),
|
||||||
$el("div", {}, [
|
$el("div", {}, [
|
||||||
$el("label", { innerHTML: "Extra options"}, [
|
$el("label", { innerHTML: "Extra options" }, [
|
||||||
$el("input", { type: "checkbox",
|
$el("input", {
|
||||||
onchange: (i) => {
|
type: "checkbox",
|
||||||
|
onchange: (i) => {
|
||||||
document.getElementById('extraOptions').style.display = i.srcElement.checked ? "block" : "none";
|
document.getElementById('extraOptions').style.display = i.srcElement.checked ? "block" : "none";
|
||||||
this.batchCount = i.srcElement.checked ? document.getElementById('batchCountInputRange').value : 1;
|
this.batchCount = i.srcElement.checked ? document.getElementById('batchCountInputRange').value : 1;
|
||||||
document.getElementById('autoQueueCheckbox').checked = false;
|
document.getElementById('autoQueueCheckbox').checked = false;
|
||||||
@ -268,26 +265,29 @@ export class ComfyUI {
|
|||||||
})
|
})
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
$el("div", { id: "extraOptions", style: { width: "100%", display: "none" }}, [
|
$el("div", { id: "extraOptions", style: { width: "100%", display: "none" } }, [
|
||||||
$el("label", { innerHTML: "Batch count" }, [
|
$el("label", { innerHTML: "Batch count" }, [
|
||||||
$el("input", { id: "batchCountInputNumber", type: "number", value: this.batchCount, min: "1", style: { width: "35%", "margin-left": "0.4em" },
|
$el("input", {
|
||||||
oninput: (i) => {
|
id: "batchCountInputNumber", type: "number", value: this.batchCount, min: "1", style: { width: "35%", "margin-left": "0.4em" },
|
||||||
|
oninput: (i) => {
|
||||||
this.batchCount = i.target.value;
|
this.batchCount = i.target.value;
|
||||||
document.getElementById('batchCountInputRange').value = this.batchCount;
|
document.getElementById('batchCountInputRange').value = this.batchCount;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$el("input", { id: "batchCountInputRange", type: "range", min: "1", max: "100", value: this.batchCount,
|
$el("input", {
|
||||||
|
id: "batchCountInputRange", type: "range", min: "1", max: "100", value: this.batchCount,
|
||||||
oninput: (i) => {
|
oninput: (i) => {
|
||||||
this.batchCount = i.srcElement.value;
|
this.batchCount = i.srcElement.value;
|
||||||
document.getElementById('batchCountInputNumber').value = i.srcElement.value;
|
document.getElementById('batchCountInputNumber').value = i.srcElement.value;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$el("input", { id: "autoQueueCheckbox", type: "checkbox", checked: false, title: "automatically queue prompt when the queue size hits 0",
|
$el("input", {
|
||||||
|
id: "autoQueueCheckbox", type: "checkbox", checked: false, title: "automatically queue prompt when the queue size hits 0",
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
$el("div.comfy-menu-btns", [
|
$el("div.comfy-menu-btns", [
|
||||||
$el("button", { textContent: "Queue Front", onclick: () => app.queuePrompt(-1, this.batchCount) }),
|
$el("button", { textContent: "Queue Front", onclick: () => this.app.queuePrompt(-1, this.batchCount) }),
|
||||||
$el("button", {
|
$el("button", {
|
||||||
$: (b) => (this.queue.button = b),
|
$: (b) => (this.queue.button = b),
|
||||||
textContent: "View Queue",
|
textContent: "View Queue",
|
||||||
@ -310,7 +310,7 @@ export class ComfyUI {
|
|||||||
$el("button", {
|
$el("button", {
|
||||||
textContent: "Save",
|
textContent: "Save",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
const json = JSON.stringify(app.graph.serialize(), null, 2); // convert the data to a JSON string
|
const json = JSON.stringify(this.app.graph.serialize(), null, 2); // convert the data to a JSON string
|
||||||
const blob = new Blob([json], { type: "application/json" });
|
const blob = new Blob([json], { type: "application/json" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = $el("a", {
|
const a = $el("a", {
|
||||||
@ -326,21 +326,18 @@ export class ComfyUI {
|
|||||||
}, 0);
|
}, 0);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
$el("button", { textContent: "Load", onclick: () => fileInput.click() }),
|
$el("button", { textContent: "Load", onclick: fileInput.click.bind(fileInput) }),
|
||||||
$el("button", { textContent: "Clear", onclick: () => app.graph.clear() }),
|
$el("button", { textContent: "Clear", onclick: () => this.app.graph.clear() }),
|
||||||
$el("button", { textContent: "Load Default", onclick: () => app.loadGraphData() }),
|
$el("button", { textContent: "Load Default", onclick: () => this.app.loadGraphData() })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.setStatus({ exec_info: { queue_remaining: "X" } });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(status) {
|
setStatus(status) {
|
||||||
this.queueSize.textContent = "Queue size: " + (status ? status.exec_info.queue_remaining : "ERR");
|
this.queueSize.textContent = "Queue size: " + status?.exec_info?.queue_remaining ?? "ERR";
|
||||||
if (status) {
|
if (!status) return;
|
||||||
if (this.lastQueueSize != 0 && status.exec_info.queue_remaining == 0 && document.getElementById('autoQueueCheckbox').checked) {
|
if (this.lastQueueSize != 0 && status.exec_info.queue_remaining == 0 && document.getElementById('autoQueueCheckbox').checked) {
|
||||||
app.queuePrompt(0, this.batchCount);
|
this.app.queuePrompt(0, this.batchCount);
|
||||||
}
|
|
||||||
this.lastQueueSize = status.exec_info.queue_remaining
|
|
||||||
}
|
}
|
||||||
|
this.lastQueueSize = status.exec_info.queue_remaining
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user