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 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
} }
} }