import { app } from "/scripts/app.js"; import { ComfyWidgets } from "/scripts/widgets.js"; /* ------------------------------------------------------------------ Extension: Comfy.GenerateTextNode Adds a read‑only multiline STRING widget to the "Generate Text (LLMToolkit)" node that displays the LLM response directly on the node. The node still outputs the normal "context" so it can be connected to an external Display Text node as before. -------------------------------------------------------------------*/ app.registerExtension({ name: "Comfy.GenerateTextNode", async beforeRegisterNodeDef(nodeType, nodeData, app) { // Only act on our LLMToolkitTextGenerator node if (nodeData.name !== "LLMToolkitTextGenerator") return; /* ----------------------------- 1. Inject widget on creation ----------------------------- */ const origOnNodeCreated = nodeType.prototype.onNodeCreated; nodeType.prototype.onNodeCreated = function () { // Invoke original handler first const ret = origOnNodeCreated ? origOnNodeCreated.apply(this, arguments) : undefined; // Build a unique widget name so each node instance has its own key const existingNodes = app.graph._nodes.filter(n => n.type === nodeData.name); const widgetName = `${nodeData.name}_response_${existingNodes.length}`; // Create read‑only STRING widget for displaying response const widgetInfo = ComfyWidgets.STRING( this, widgetName, ["STRING", { default: "", placeholder: "LLM response will appear here …", multiline: true, }], app ); // Make read‑only widgetInfo.widget.inputEl.readOnly = true; // Store reference for quick updates this.__llmResponseWidget = widgetInfo.widget || widgetInfo; // Ensure node size grows to fit another widget this.setSize(this.computeSize()); return ret; }; /* ----------------------------- 2. Helper to update widget ----------------------------- */ function updateResponseWidget(textContainer) { if (!this.__llmResponseWidget) return; // Extract final text string (textContainer may be array/string) let valueToSet = ""; if (Array.isArray(textContainer)) { valueToSet = textContainer.join("\n"); } else if (typeof textContainer === "string") { valueToSet = textContainer; } // Trim and write valueToSet = valueToSet.trim(); this.__llmResponseWidget.value = valueToSet; if (this.__llmResponseWidget.inputEl) this.__llmResponseWidget.inputEl.value = valueToSet; app.graph.setDirtyCanvas(true); } /* ----------------------------- 3. Hook into onExecuted to receive message ----------------------------- */ const origOnExecuted = nodeType.prototype.onExecuted; nodeType.prototype.onExecuted = function (message) { if (origOnExecuted) origOnExecuted.apply(this, arguments); // message is typically { ui: { string: [...] }, result: (…) } let textData = undefined; if (message?.ui?.string) { textData = message.ui.string; } else if (message?.string) { textData = message.string; } else if (Array.isArray(message)) { textData = message; } updateResponseWidget.call(this, textData); }; /* ----------------------------- 4. Restore saved value on load ----------------------------- */ const origOnConfigure = nodeType.prototype.onConfigure; nodeType.prototype.onConfigure = function (config) { if (origOnConfigure) origOnConfigure.apply(this, arguments); if (!this.__llmResponseWidget || !config?.widgets_values?.length) return; const idx = this.widgets.findIndex(w => w === this.__llmResponseWidget); if (idx !== -1 && config.widgets_values.length > idx) { const savedVal = config.widgets_values[idx] || ""; this.__llmResponseWidget.value = savedVal; if (this.__llmResponseWidget.inputEl) this.__llmResponseWidget.inputEl.value = savedVal; } }; }, });