Files
HowToSD-ComfyUI-Pt-Wrapper/web/js/ShowTextGeneric.js
T

131 lines
4.8 KiB
JavaScript

/*
This code is based on
https://github.com/pythongosssss/ComfyUI-Custom-Scripts/blob/main/web/js/showText.js
See credit/credit.md for the full license.
*/
import { app } from "../../../scripts/app.js";
import { ComfyWidgets } from "../../../scripts/widgets.js";
app.registerExtension({
name: "HowToSD.PtWrapperShowTextGeneric",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
// Each node needs to match NODE_CLASS_MAPPINGS in Python
const targetNodes = ["PtShowText", "PtShowSize"]
const noConvertedFieldNodes = ["PtShowText", "PtShowSize"]
if (targetNodes.includes(nodeData.name)) {
function populate(text) {
// 'this' is not bound automatically — it's explicitly set via .call() in onExecuted.
if (this.widgets) {
// On older frontend versions there is a hidden converted-widget
// Legacy version: const isConvertedWidget = +!!this.inputs?.[0].widget;
const hasInputWidget = !!(this.inputs?.[0]?.widget); // Coerce to boolean: true if widget exists
let isConvertedWidget = hasInputWidget ? 1 : 0; // Convert boolean to integer (1 or 0)
if (noConvertedFieldNodes.includes(nodeData.name)){ // Remove all widgets
isConvertedWidget = 0;
}
// Remove all dynamically added widgets, preserving the auto-converted input widget if present
for (let i = isConvertedWidget; i < this.widgets.length; i++) {
this.widgets[i].onRemove?.();
}
this.widgets.length = isConvertedWidget;
}
const v = [...text]; // Shallow copy of text array into v
// If the first element is empty or undefined, remove it from the array
if (!v[0]) {
v.shift();
}
for (let row of v) { // Iterate over v. v can be 1d or 2d.
if (!(row instanceof Array)) row = [row];
for (const elem of row) {
// Create a new STRING widget
// Use field name like "text_0", "text_1", accounting for existing widgets
const fieldName = "text_" + (this.widgets?.length ?? 0);
const w = ComfyWidgets["STRING"](
this,
fieldName,
["STRING", { multiline: true }],
app).widget;
w.inputEl.readOnly = true;
w.inputEl.style.opacity = 0.6;
w.value = elem;
}
}
/*
Defer until the next animation frame so all widgets are rendered and layout is stable.
Then compute the node size needed to fit new content.
If computeSize underestimates it, use the current node size as the floor to preserve layout.
*/
requestAnimationFrame(() => {
const sz = this.computeSize(); // Compute size based on current widgets
// Adjust to ensure the node fits its current visual width/height
if (sz[0] < this.size[0]) sz[0] = this.size[0]; // Fit existing node width
if (sz[1] < this.size[1]) sz[1] = this.size[1]; // Fit existing node height
this.onResize?.(sz); // Apply the size adjustment
app.graph.setDirtyCanvas(true, false); // Trigger a redraw of the canvas
});
}
// **onExecute** setup
// When the node is executed we will be sent the input text, display this in the widget
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function (message) {
// Call the original onExecuted handler (if one exists), forwarding all arguments
onExecuted?.apply(this, arguments);
// Call our populate function, binding 'this' to the current node
populate.call(this, message.text);
};
// **configure** setup
const VALUES = Symbol(); // Unique internal key to store widget values
const configure = nodeType.prototype.configure;
nodeType.prototype.configure = function () {
// Store unmodified widget values as they get removed on configure by new frontend
// This value is applied in onConfigure
this[VALUES] = arguments[0]?.widgets_values;
// Call the original configure handler, if it exists
return configure?.apply(this, arguments);
};
// **onConfigure** setup
// Populate node's widgets using the saved values in Configure
const onConfigure = nodeType.prototype.onConfigure;
nodeType.prototype.onConfigure = function () {
onConfigure?.apply(this, arguments);
// Read saved widget values
const widgets_values = this[VALUES];
if (widgets_values?.length) {
// In newer frontend there seems to be a delay in creating the initial widget
requestAnimationFrame(() => {
// Legacy code
// populate.call(this,
// widgets_values.slice(+(widgets_values.length > 1 && this.inputs?.[0].widget)));
const hasAutoWidget = widgets_values.length > 1 && this.inputs?.[0]?.widget;
let startIndex = hasAutoWidget ? 1 : 0;
if (noConvertedFieldNodes.includes(nodeData.name)){ // Remove all widgets
startIndex = 0;
}
populate.call(this, widgets_values.slice(startIndex));
});
}
};
}
},
});