Added handling of sockets

Started rework of UI elements
Added pnginfo handling
This commit is contained in:
pythongosssss
2023-03-02 21:34:29 +00:00
committed by GitHub
parent 2eaa664089
commit 7e436ba9cc
6 changed files with 522 additions and 520 deletions

View File

@@ -1,74 +1,8 @@
import { ComfyWidgets } from "./widgets.js";
import { ComfyUI } from "./ui.js";
import { api } from "./api.js";
import { defaultGraph } from "./defaultGraph.js";
class ComfyDialog {
constructor() {
this.element = document.createElement("div");
this.element.classList.add("comfy-modal");
const content = document.createElement("div");
content.classList.add("comfy-modal-content");
this.textElement = document.createElement("p");
content.append(this.textElement);
const closeBtn = document.createElement("button");
closeBtn.type = "button";
closeBtn.textContent = "CLOSE";
content.append(closeBtn);
closeBtn.onclick = () => this.close();
this.element.append(content);
document.body.append(this.element);
}
close() {
this.element.style.display = "none";
}
show(html) {
this.textElement.innerHTML = html;
this.element.style.display = "flex";
}
}
class ComfyQueue {
constructor() {
this.element = document.createElement("div");
}
async update() {
if (this.element.style.display !== "none") {
await this.load();
}
}
async show() {
this.element.style.display = "block";
await this.load();
}
async load() {
const queue = await api.getQueue();
}
hide() {
this.element.style.display = "none";
}
}
class ComfyUI {
constructor(app) {
this.app = app;
this.menuContainer = document.createElement("div");
this.menuContainer.classList.add("comfy-menu");
document.body.append(this.menuContainer);
this.dialog = new ComfyDialog();
this.queue = new ComfyQueue();
}
}
import { getPngMetadata } from "./pnginfo.js";
class ComfyApp {
constructor() {
@@ -360,6 +294,103 @@ class ComfyApp {
};
}
#addDropHandler() {
// Get prompt from dropped PNG or json
document.addEventListener("drop", async (event) => {
event.preventDefault();
event.stopPropagation();
const file = event.dataTransfer.files[0];
if (file.type === "image/png") {
const pngInfo = await getPngMetadata(file);
if (pngInfo && pngInfo.workflow) {
this.loadGraphData(JSON.parse(pngInfo.workflow));
}
} else if (file.type === "application/json" || file.name.endsWith(".json")) {
const reader = new FileReader();
reader.onload = () => {
this.loadGraphData(JSON.parse(reader.result));
};
reader.readAsText(file);
}
prompt_file_load(file);
});
}
#addDrawNodeProgressHandler() {
const orig = LGraphCanvas.prototype.drawNodeShape;
const self = this;
LGraphCanvas.prototype.drawNodeShape = function (node, ctx, size, fgcolor, bgcolor, selected, mouse_over) {
const res = orig.apply(this, arguments);
if (node.id + "" === self.runningNodeId) {
const shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE;
ctx.lineWidth = 1;
ctx.globalAlpha = 0.8;
ctx.beginPath();
if (shape == LiteGraph.BOX_SHAPE)
ctx.rect(-6, -6 + LiteGraph.NODE_TITLE_HEIGHT, 12 + size[0] + 1, 12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT);
else if (shape == LiteGraph.ROUND_SHAPE || (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed))
ctx.roundRect(
-6,
-6 - LiteGraph.NODE_TITLE_HEIGHT,
12 + size[0] + 1,
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
this.round_radius * 2
);
else if (shape == LiteGraph.CARD_SHAPE)
ctx.roundRect(
-6,
-6 + LiteGraph.NODE_TITLE_HEIGHT,
12 + size[0] + 1,
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
this.round_radius * 2,
2
);
else if (shape == LiteGraph.CIRCLE_SHAPE)
ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5 + 6, 0, Math.PI * 2);
ctx.strokeStyle = "#0f0";
ctx.stroke();
ctx.strokeStyle = fgcolor;
ctx.globalAlpha = 1;
if (self.progress) {
ctx.fillStyle = "green";
ctx.fillRect(0, 0, size[0] * (self.progress.value / self.progress.max), 6);
ctx.fillStyle = bgcolor;
}
}
return res;
};
}
#addApiUpdateHandlers() {
api.addEventListener("status", (status) => {
console.log(status);
});
api.addEventListener("reconnecting", () => {});
api.addEventListener("reconnected", () => {});
api.addEventListener("progress", ({ detail }) => {
this.progress = detail;
this.graph.setDirtyCanvas(true, false);
});
api.addEventListener("executing", ({ detail }) => {
this.progress = null;
this.runningNodeId = detail;
this.graph.setDirtyCanvas(true, false);
});
api.addEventListener("executed", (e) => {});
api.init();
}
/**
* Set up the app on the page
*/
@@ -406,6 +437,9 @@ class ComfyApp {
// Save current workflow automatically
setInterval(() => localStorage.setItem("workflow", JSON.stringify(this.graph.serialize())), 1000);
this.#addDrawNodeProgressHandler();
this.#addApiUpdateHandlers();
this.#addDropHandler();
await this.#invokeExtensionsAsync("setup");
}
@@ -561,6 +595,8 @@ class ComfyApp {
}
}
// TODO: check dynamic prompts here
this.canvas.draw(true, true);
await this.ui.queue.update();
}

45
web/scripts/pnginfo.js Normal file
View File

@@ -0,0 +1,45 @@
export function getPngMetadata(file) {
return new Promise((r) => {
const reader = new FileReader();
reader.onload = (event) => {
// Get the PNG data as a Uint8Array
const pngData = new Uint8Array(event.target.result);
const dataView = new DataView(pngData.buffer);
// Check that the PNG signature is present
if (dataView.getUint32(0) !== 0x89504e47) {
console.error("Not a valid PNG file");
r();
return;
}
// Start searching for chunks after the PNG signature
let offset = 8;
let txt_chunks = {};
// Loop through the chunks in the PNG file
while (offset < pngData.length) {
// Get the length of the chunk
const length = dataView.getUint32(offset);
// Get the chunk type
const type = String.fromCharCode(...pngData.slice(offset + 4, offset + 8));
if (type === "tEXt") {
// Get the keyword
let keyword_end = offset + 8;
while (pngData[keyword_end] !== 0) {
keyword_end++;
}
const keyword = String.fromCharCode(...pngData.slice(offset + 8, keyword_end));
// Get the text
const text = String.fromCharCode(...pngData.slice(keyword_end + 1, offset + 8 + length));
txt_chunks[keyword] = text;
}
offset += 12 + length;
}
r(txt_chunks);
};
reader.readAsArrayBuffer(file);
});
}

152
web/scripts/ui.js Normal file
View File

@@ -0,0 +1,152 @@
import { api } from "./api.js";
class ComfyDialog {
constructor() {
this.element = document.createElement("div");
this.element.classList.add("comfy-modal");
const content = document.createElement("div");
content.classList.add("comfy-modal-content");
this.textElement = document.createElement("p");
content.append(this.textElement);
const closeBtn = document.createElement("button");
closeBtn.type = "button";
closeBtn.textContent = "CLOSE";
content.append(closeBtn);
closeBtn.onclick = () => this.close();
this.element.append(content);
document.body.append(this.element);
}
close() {
this.element.style.display = "none";
}
show(html) {
this.textElement.innerHTML = html;
this.element.style.display = "flex";
}
}
class ComfyList {
constructor() {
this.element = document.createElement("div");
this.element.style.display = "none";
this.element.textContent = "hello";
}
get visible() {
return this.element.style.display !== "none";
}
async load() {
// const queue = await api.getQueue();
}
async update() {
if (this.visible) {
await this.load();
}
}
async show() {
this.element.style.display = "block";
await this.load();
}
hide() {
this.element.style.display = "none";
}
toggle() {
if (this.visible) {
this.hide();
return false;
} else {
this.show();
return true;
}
}
}
export class ComfyUI {
constructor(app) {
this.app = app;
this.dialog = new ComfyDialog();
this.queue = new ComfyList();
this.history = new ComfyList();
this.menuContainer = document.createElement("div");
this.menuContainer.classList.add("comfy-menu");
this.queueSize = document.createElement("span");
this.menuContainer.append(this.queueSize);
this.addAction("Queue Prompt", () => {
app.queuePrompt(0);
}, "queue");
this.btnContainer = document.createElement("div");
this.btnContainer.classList.add("comfy-menu-btns");
this.menuContainer.append(this.btnContainer);
this.addAction(
"Queue Front",
() => {
app.queuePrompt(-1);
},
"sm"
);
this.addAction(
"See Queue",
(btn) => {
btn.textContent = this.queue.toggle() ? "Close" : "See Queue";
},
"sm"
);
this.addAction(
"See History",
(btn) => {
btn.textContent = this.history.toggle() ? "Close" : "See History";
},
"sm"
);
this.menuContainer.append(this.queue.element);
this.menuContainer.append(this.history.element);
this.addAction("Save", () => {
app.queuePrompt(-1);
});
this.addAction("Load", () => {
app.queuePrompt(-1);
});
this.addAction("Clear", () => {
app.queuePrompt(-1);
});
this.addAction("Load Default", () => {
app.queuePrompt(-1);
});
document.body.append(this.menuContainer);
this.setStatus({ exec_info: { queue_remaining: "X" } });
}
addAction(text, cb, cls) {
const btn = document.createElement("button");
btn.classList.add("comfy-menu-btn-" + (cls || "lg"));
btn.textContent = text;
btn.onclick = () => {
cb(btn);
};
(cls === "sm" ? this.btnContainer : this.menuContainer).append(btn);
}
setStatus(status) {
this.queueSize.textContent = "Queue size: " + (status ? status.exec_info.queue_remaining : "ERR");
}
}

View File

@@ -44,7 +44,6 @@ function addMultilineWidget(node, name, defaultVal, dynamicPrompt, app) {
const visible = app.canvas.ds.scale > 0.5;
const t = ctx.getTransform();
const margin = 10;
console.log("back you go")
Object.assign(this.inputEl.style, {
left: `${t.a * margin + t.e}px`,
top: `${t.d * (y + widgetHeight - margin) + t.f}px`,