Added autotagger, allowing to generate tags for images. With the ability to use autotagger in cycles and conditions. Added new convenient nodes. Fixed and optimized some previous nodes. Added more description and sources.
142 lines
3.8 KiB
JavaScript
142 lines
3.8 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|LevelPixel") {
|
|
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;
|
|
};
|
|
}
|
|
},
|
|
});
|