145 lines
4.5 KiB
JavaScript
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]);
|
|
}
|
|
}
|