Refactor ui.js

This commit is contained in:
ikiovi 2023-03-21 16:00:23 +03:00 committed by GitHub
parent aa2ddfabb9
commit e5b4352878
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -4,49 +4,49 @@ function $el(tag, propsOrChildren, children) {
const split = tag.split(".");
const element = document.createElement(split.shift());
element.classList.add(...split);
if (propsOrChildren) {
if (Array.isArray(propsOrChildren)) {
element.append(...propsOrChildren);
} else {
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);
}
}
if (!propsOrChildren) return element;
if (Array.isArray(propsOrChildren)) {
element.append(...propsOrChildren);
return 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;
}
class ComfyDialog {
constructor() {
this.element = $el("div.comfy-modal", { parent: document.body }, [
$el("div.comfy-modal-content", [
$el("p", { $: (p) => (this.textElement = p) }),
$el("button", {
type: "button",
textContent: "CLOSE",
onclick: () => this.close(),
}),
]),
]);
const p = $el("p", {
$: (p) => (this.textElement = p)
});
const button = $el("button", {
type: "button",
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() {
@ -76,55 +76,52 @@ class ComfySettingsDialog extends ComfyDialog {
const settingId = "Comfy.Settings." + id;
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
if (onChange) {
onChange(value, undefined);
}
onChange?.(value, undefined);
this.settings.push({
render: () => {
const setter = (v) => {
if (onChange) {
onChange(v, value);
}
localStorage[settingId] = JSON.stringify(v);
value = v;
};
const setter = (v) => {
onChange?.(v, value);
localStorage[settingId] = JSON.stringify(v);
value = v;
};
if (typeof type === "function") {
return type(name, setter);
}
const createLable = (input) => (
$el("div", [
$el("label", { textContent: name || id }, [input])
])
);
switch (type) {
case "boolean":
return $el("div", [
$el("label", { textContent: name || id }, [
$el("input", {
type: "checkbox",
checked: !!value,
oninput: (e) => {
setter(e.target.checked);
},
}),
]),
]);
default:
console.warn("Unsupported setting type, defaulting to text");
return $el("div", [
$el("label", { textContent: name || id }, [
$el("input", {
value,
oninput: (e) => {
setter(e.target.value);
},
}),
]),
]);
}
},
});
const render = () => {
if (typeof type === "function") {
return type(name, setter);
}
if (type == 'boolean') {
return createLable(
$el("input", {
type: "checkbox",
checked: !!value,
oninput: (e) => setter(e.target.checked),
})
);
}
console.warn("Unsupported setting type, defaulting to text");
return createLable(
$el("input", {
value,
oninput: (e) => setter(e.target.value),
})
);
};
this.settings.push({ render });
}
show() {
@ -136,6 +133,7 @@ class ComfySettingsDialog extends ComfyDialog {
class ComfyList {
#type;
#text;
element;
constructor(text, type) {
this.#text = text;
@ -150,39 +148,40 @@ class ComfyList {
async load() {
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(
...Object.keys(items).flatMap((section) => [
$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();
},
}),
]);
}),
]),
]),
...Object.keys(items).flatMap(processSection),
$el("div.comfy-list-actions", [
$el("button", {
textContent: "Clear " + this.#text,
@ -191,15 +190,14 @@ class ComfyList {
await this.load();
},
}),
$el("button", { textContent: "Refresh", onclick: () => this.load() }),
$el("button", { textContent: "Refresh", onclick: this.load.bind(this) }),
])
);
}
async update() {
if (this.visible) {
await this.load();
}
if (!this.visible) return;
await this.load();
}
async show() {
@ -215,13 +213,8 @@ class ComfyList {
}
toggle() {
if (this.visible) {
this.hide();
return false;
} else {
this.show();
return true;
}
(this.visible ? this.hide : this.show).call(this);
return !this.visible;
}
}
@ -241,26 +234,30 @@ export class ComfyUI {
this.history.update();
});
this.createMenuContainer();
this.setStatus({ exec_info: { queue_remaining: "X" } });
}
createMenuContainer() {
const fileInput = $el("input", {
type: "file",
accept: ".json,image/png",
style: { display: "none" },
parent: document.body,
onchange: () => {
app.handleFile(fileInput.files[0]);
},
onchange: () => this.app.handleFile(fileInput.files[0]),
});
this.menuContainer = $el("div.comfy-menu", { parent: document.body }, [
$el("div", { style: { overflow: "hidden", position: "relative", width: "100%" } }, [
$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("label", { innerHTML: "Extra options"}, [
$el("input", { type: "checkbox",
onchange: (i) => {
$el("label", { innerHTML: "Extra options" }, [
$el("input", {
type: "checkbox",
onchange: (i) => {
document.getElementById('extraOptions').style.display = i.srcElement.checked ? "block" : "none";
this.batchCount = i.srcElement.checked ? document.getElementById('batchCountInputRange').value : 1;
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("input", { id: "batchCountInputNumber", type: "number", value: this.batchCount, min: "1", style: { width: "35%", "margin-left": "0.4em" },
oninput: (i) => {
$el("input", {
id: "batchCountInputNumber", type: "number", value: this.batchCount, min: "1", style: { width: "35%", "margin-left": "0.4em" },
oninput: (i) => {
this.batchCount = i.target.value;
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) => {
this.batchCount = 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("button", { textContent: "Queue Front", onclick: () => app.queuePrompt(-1, this.batchCount) }),
$el("button", { textContent: "Queue Front", onclick: () => this.app.queuePrompt(-1, this.batchCount) }),
$el("button", {
$: (b) => (this.queue.button = b),
textContent: "View Queue",
@ -310,7 +310,7 @@ export class ComfyUI {
$el("button", {
textContent: "Save",
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 url = URL.createObjectURL(blob);
const a = $el("a", {
@ -326,21 +326,18 @@ export class ComfyUI {
}, 0);
},
}),
$el("button", { textContent: "Load", onclick: () => fileInput.click() }),
$el("button", { textContent: "Clear", onclick: () => app.graph.clear() }),
$el("button", { textContent: "Load Default", onclick: () => app.loadGraphData() }),
$el("button", { textContent: "Load", onclick: fileInput.click.bind(fileInput) }),
$el("button", { textContent: "Clear", onclick: () => this.app.graph.clear() }),
$el("button", { textContent: "Load Default", onclick: () => this.app.loadGraphData() })
]);
this.setStatus({ exec_info: { queue_remaining: "X" } });
}
setStatus(status) {
this.queueSize.textContent = "Queue size: " + (status ? status.exec_info.queue_remaining : "ERR");
if (status) {
if (this.lastQueueSize != 0 && status.exec_info.queue_remaining == 0 && document.getElementById('autoQueueCheckbox').checked) {
app.queuePrompt(0, this.batchCount);
}
this.lastQueueSize = status.exec_info.queue_remaining
this.queueSize.textContent = "Queue size: " + status?.exec_info?.queue_remaining ?? "ERR";
if (!status) return;
if (this.lastQueueSize != 0 && status.exec_info.queue_remaining == 0 && document.getElementById('autoQueueCheckbox').checked) {
this.app.queuePrompt(0, this.batchCount);
}
this.lastQueueSize = status.exec_info.queue_remaining
}
}