import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; import { ComfyWidgets } from "../../scripts/widgets.js"; const tokenPreviewWidgetName = "__tokens"; class TokenProgressHandler { constructor() { this.nodeOutputs = {}; this.initEventListeners(); } initEventListeners() { api.addEventListener("executing", ({ detail }) => { if (!detail) { return; } const nodeId = detail; if (!this.nodeOutputs[nodeId]) { this.nodeOutputs[nodeId] = {}; } this.nodeOutputs[nodeId].tokens = null; }); api.addEventListener("progress", ({ detail }) => { const nodeId = detail.node; if (!this.nodeOutputs[nodeId]) { this.nodeOutputs[nodeId] = {}; } if (detail.output && detail.output.next_token) { if (!this.nodeOutputs[nodeId].tokens) { this.nodeOutputs[nodeId].tokens = ""; } this.nodeOutputs[nodeId].tokens += detail.output.next_token; this.updateTokenWidget(nodeId, this.nodeOutputs[nodeId].tokens); app.graph.setDirtyCanvas(true, false); } }); } updateTokenWidget(nodeId, tokens) { const node = app.graph.getNodeById(nodeId); if (node && node.widgets) { let widget = node.widgets.find((w) => w.name === tokenPreviewWidgetName); if (!widget) { widget = ComfyWidgets.STRING(node, tokenPreviewWidgetName, ["STRING", { multiline: true }], app).widget; widget.inputEl.readOnly = true; widget.inputEl.style.opacity = 0.7; } widget.serializeValue = async () => {}; widget.value = tokens; } } } app.registerExtension({ name: "Comfy.TokenProgress", setup() { this.tokenProgressHandler = new TokenProgressHandler(); }, });