Files

145 lines
4.5 KiB
JavaScript

import { api } from "../../scripts/api.js";
import { app } from "../../scripts/app.js";
export class Util {
// Server
static AddMessageListener(messagePath, handlerFunc) {
api.addEventListener(messagePath, handlerFunc);
}
// Widget
static SetTextAreaContent(widget, text) {
widget.element.textContent = text
}
static SetTextAreaScrollPos(widget, pos01) {
widget.element.scroll(0, widget.element.scrollHeight * pos01)
}
static AddReadOnlyTextArea(node, name, text, placeholder = "") {
const inputEl = document.createElement("textarea");
inputEl.className = "comfy-multiline-input";
inputEl.placeholder = placeholder
inputEl.spellcheck = false
inputEl.readOnly = true
inputEl.textContent = text
return node.addDOMWidget(name, "", inputEl, {
serialize: false,
});
}
static AddButtonWidget(node, label, callback, value = null) {
return node.addWidget("button", label, value, callback);
}
}
// 通用终端管理器
export class TerminalManager {
constructor(messagePath, nodeType) {
this.messagePath = messagePath;
this.nodeType = nodeType;
this.textVersion = 0;
this.lines = new Array();
// 监听消息
Util.AddMessageListener(messagePath, (event) => {
this.textVersion++;
if (event.detail.clear) {
this.lines.length = 0;
}
let totalText = String(event.detail.text || "");
this.lines.push(...(totalText.split("\n")));
if (this.lines.length > 1024) {
this.lines = this.lines.slice(-1024);
}
// 刷新所有相关节点
for (let i = 0; i < app.graph._nodes.length; i++) {
var node = app.graph._nodes[i];
if (node.type == nodeType && node.setDirtyCanvas) {
node.setDirtyCanvas(true);
}
}
});
}
// 清空终端
clearTerminal() {
this.lines.length = 0;
this.textVersion++;
}
// 获取终端内容
getContent() {
return this.lines.join("\n");
}
// 创建节点时的设置
async setupNode(node) {
var textArea = Util.AddReadOnlyTextArea(node, "terminal", "");
setTimeout(() => {
if (textArea.element) {
textArea.element.style.backgroundColor = "#000000ff";
textArea.element.style.color = "#ffffffff";
textArea.element.style.fontFamily = "monospace";
}
}, 0);
// 动态导入避免重复声明问题
try {
const { MultiButtonWidget } = await import("./multi_button_widget.js");
const { queueSelectedOutputNodes } = await import("./queue_shortcut.js");
// 添加多按钮组件:清理日志 + 执行
const buttons = [
{
text: "清理日志",
callback: () => {
this.clearTerminal();
}
},
{
text: "执行",
callback: () => {
queueSelectedOutputNodes();
}
}
];
const multiButtonWidget = MultiButtonWidget(app, "", {
labelWidth: 0,
buttonSpacing: 4
}, buttons);
node.addCustomWidget(multiButtonWidget);
} catch (error) {
console.error("Failed to load button components:", error);
// 如果动态导入失败,回退到原来的单按钮
let clearBtn = Util.AddButtonWidget(node, "清空日志", () => {
this.clearTerminal();
});
clearBtn.width = 128;
}
node.terminalVersion = -1;
return node;
}
// 绘制时的更新
updateNode(node, onDrawForeground, ctx, graphcanvas) {
if (node.terminalVersion != this.textVersion) {
node.terminalVersion = this.textVersion;
for (var i = 0; i < node.widgets.length; i++) {
var wid = node.widgets[i];
if (wid.name == "terminal") {
Util.SetTextAreaContent(wid, this.getContent());
Util.SetTextAreaScrollPos(wid, 1.0);
break;
}
}
}
return onDrawForeground?.apply(node, [ctx, graphcanvas]);
}
}