208 lines
5.6 KiB
JavaScript
208 lines
5.6 KiB
JavaScript
import { app } from "../../../scripts/app.js";
|
|
import { ComfyWidgets } from "../../../scripts/widgets.js";
|
|
import { api } from "../../../scripts/api.js";
|
|
|
|
class LevelPixel {
|
|
constructor() {
|
|
if (!window.__levelpixel__) {
|
|
window.__levelpixel__ = Symbol("__levelpixel__");
|
|
}
|
|
this.symbol = window.__levelpixel__;
|
|
}
|
|
|
|
getState(node) {
|
|
return node[this.symbol] || {};
|
|
}
|
|
|
|
setState(node, state) {
|
|
node[this.symbol] = state;
|
|
app.canvas.setDirty(true);
|
|
}
|
|
|
|
addStatusTagHandler(nodeType) {
|
|
if (nodeType[this.symbol]?.statusTagHandler) {
|
|
return;
|
|
}
|
|
if (!nodeType[this.symbol]) {
|
|
nodeType[this.symbol] = {};
|
|
}
|
|
nodeType[this.symbol] = {
|
|
statusTagHandler: true,
|
|
};
|
|
|
|
api.addEventListener("levelpixel/update_status", ({ detail }) => {
|
|
let { node, progress, text } = detail;
|
|
const n = app.graph.getNodeById(+(node || app.runningNodeId));
|
|
if (!n) return;
|
|
const state = this.getState(n);
|
|
state.status = Object.assign(state.status || {}, { progress: text ? progress : null, text: text || null });
|
|
this.setState(n, state);
|
|
});
|
|
|
|
const self = this;
|
|
const onDrawForeground = nodeType.prototype.onDrawForeground;
|
|
nodeType.prototype.onDrawForeground = function (ctx) {
|
|
const r = onDrawForeground?.apply?.(this, arguments);
|
|
const state = self.getState(this);
|
|
if (!state?.status?.text) {
|
|
return r;
|
|
}
|
|
|
|
const { fgColor, bgColor, text, progress, progressColor } = { ...state.status };
|
|
|
|
ctx.save();
|
|
ctx.font = "12px sans-serif";
|
|
const sz = ctx.measureText(text);
|
|
ctx.fillStyle = bgColor || "dodgerblue";
|
|
ctx.beginPath();
|
|
ctx.roundRect(0, -LiteGraph.NODE_TITLE_HEIGHT - 20, sz.width + 12, 20, 5);
|
|
ctx.fill();
|
|
|
|
if (progress) {
|
|
ctx.fillStyle = progressColor || "green";
|
|
ctx.beginPath();
|
|
ctx.roundRect(0, -LiteGraph.NODE_TITLE_HEIGHT - 20, (sz.width + 12) * progress, 20, 5);
|
|
ctx.fill();
|
|
}
|
|
|
|
ctx.fillStyle = fgColor || "#fff";
|
|
ctx.fillText(text, 6, -LiteGraph.NODE_TITLE_HEIGHT - 6);
|
|
ctx.restore();
|
|
return r;
|
|
};
|
|
}
|
|
}
|
|
|
|
const levelpixel = new LevelPixel();
|
|
|
|
app.registerExtension({
|
|
name: "levelpixel.Autotagger",
|
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
levelpixel.addStatusTagHandler(nodeType);
|
|
|
|
if (nodeData.name === "Autotagger|LP") {
|
|
const onExecuted = nodeType.prototype.onExecuted;
|
|
nodeType.prototype.onExecuted = function (message) {
|
|
const r = onExecuted?.apply?.(this, arguments);
|
|
|
|
const pos = this.widgets.findIndex((w) => w.name === "tags");
|
|
if (pos !== -1) {
|
|
for (let i = pos; i < this.widgets.length; i++) {
|
|
this.widgets[i].onRemove?.();
|
|
}
|
|
this.widgets.length = pos;
|
|
}
|
|
|
|
for (const list of message.tags) {
|
|
const w = ComfyWidgets["STRING"](this, "tags", ["STRING", { multiline: true }], app).widget;
|
|
w.inputEl.readOnly = true;
|
|
w.inputEl.style.opacity = 0.6;
|
|
w.value = list;
|
|
}
|
|
|
|
this.onResize?.(this.size);
|
|
|
|
return r;
|
|
};
|
|
} else {
|
|
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
|
nodeType.prototype.getExtraMenuOptions = function (_, options) {
|
|
const r = getExtraMenuOptions?.apply?.(this, arguments);
|
|
let img;
|
|
if (this.imageIndex != null) {
|
|
// An image is selected so select that
|
|
img = this.imgs[this.imageIndex];
|
|
} else if (this.overIndex != null) {
|
|
// No image is selected but one is hovered
|
|
img = this.imgs[this.overIndex];
|
|
}
|
|
if (img) {
|
|
let pos = options.findIndex((o) => o.content === "Save Image");
|
|
if (pos === -1) {
|
|
pos = 0;
|
|
} else {
|
|
pos++;
|
|
}
|
|
options.splice(pos, 0, {
|
|
content: "Autotagger",
|
|
callback: async () => {
|
|
let src = img.src;
|
|
src = src.replace("/view?", `/levelpixel/autotagger/tag?node=${this.id}&clientId=${api.clientId}&`);
|
|
const res = await (await fetch(src)).json();
|
|
alert(res);
|
|
},
|
|
});
|
|
}
|
|
|
|
return r;
|
|
};
|
|
}
|
|
},
|
|
});
|
|
|
|
|
|
app.registerExtension({
|
|
name: "levelpixel.LoraTagLoader",
|
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
levelpixel.addStatusTagHandler(nodeType);
|
|
|
|
if (nodeData.name === "LoraTagLoader|LP") {
|
|
const onExecuted = nodeType.prototype.onExecuted;
|
|
nodeType.prototype.onExecuted = function (message) {
|
|
const r = onExecuted?.apply?.(this, arguments);
|
|
|
|
const pos = this.widgets.findIndex((w) => w.name === "log");
|
|
if (pos !== -1) {
|
|
for (let i = pos; i < this.widgets.length; i++) {
|
|
this.widgets[i].onRemove?.();
|
|
}
|
|
this.widgets.length = pos;
|
|
}
|
|
|
|
for (const list of message.log) {
|
|
const w = ComfyWidgets["STRING"](this, "log", ["STRING", { multiline: true }], app).widget;
|
|
w.inputEl.readOnly = true;
|
|
w.inputEl.style.opacity = 0.6;
|
|
w.value = list;
|
|
}
|
|
|
|
this.onResize?.(this.size);
|
|
|
|
return r;
|
|
};
|
|
} else {
|
|
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
|
nodeType.prototype.getExtraMenuOptions = function (_, options) {
|
|
const r = getExtraMenuOptions?.apply?.(this, arguments);
|
|
let img;
|
|
if (this.imageIndex != null) {
|
|
// An image is selected so select that
|
|
img = this.imgs[this.imageIndex];
|
|
} else if (this.overIndex != null) {
|
|
// No image is selected but one is hovered
|
|
img = this.imgs[this.overIndex];
|
|
}
|
|
if (img) {
|
|
let pos = options.findIndex((o) => o.content === "Save Image");
|
|
if (pos === -1) {
|
|
pos = 0;
|
|
} else {
|
|
pos++;
|
|
}
|
|
options.splice(pos, 0, {
|
|
content: "LoraTagLoader",
|
|
callback: async () => {
|
|
let src = img.src;
|
|
src = src.replace("/view?", `/levelpixel/loratagloader/tag?node=${this.id}&clientId=${api.clientId}&`);
|
|
const res = await (await fetch(src)).json();
|
|
alert(res);
|
|
},
|
|
});
|
|
}
|
|
|
|
return r;
|
|
};
|
|
}
|
|
},
|
|
});
|