Comfy-Org / Comfy-Org/ComfyUI

Loading Workflow Data, forceInput and Multiline Text

Open
#9,017 1 comment 0 reactions 0 assignees View on GitHub
bug-cop:non-blocking Potential Bug
Dominant language
Python
Stars
133k
Forks
15.7k
Avg merge
1d 6h
Merged PRs (30d)
155

Description

### Custom Node Testing

- [x] I have tried disabling custom nodes and the issue persists (see [how to disable custom nodes](https://docs.comfy.org/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled) if you need help)

### Expected Behavior

**Descriptions:**
The problem is that when writing a custom extension that adds another widget to the node (for example, my **_Speech And Recognition extension_**: https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/blob/master/ExtrasNode/js/extras_speech_and_recognition.js), which has a multi-text field and forceInput, it incorrectly loads data from the workflow.

Maybe I somehow incorrectly add the widget to the node, but with two multi-text fields, everything works fine 😄 .

**I made a more simplified version of the verification extension, which uses one checkbox and stores its value.**

https://github.com/user-attachments/assets/66cf7800-ff29-40e4-93d3-236dc9f8c8b4

**_Example python code nodes._**
```py
class TestTextNode:

@ classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {"default": "", "forceInput":True}),
"words": ("STRING", {"multiline": True, "default": ""}),
},
}

RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("STRING",)
CATEGORY = "alekpet-test"
DESCRIPTION = "This node outputs text."
FUNCTION = "ret_text"

def ret_text(self, text="", words=""):
result_text = text * 5
return (result_text,)

class TestTextTwoNode:

@ classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {"default": "", "forceInput":True}),
"words": ("STRING", {"multiline": True, "default": ""}),
"words2": ("STRING", {"multiline": True, "default": ""}),
},
}

RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("STRING",)
CATEGORY = "alekpet-test"
DESCRIPTION = "This node outputs text."
FUNCTION = "ret_text_two"

def ret_text_two(self, text="", words="", words2=""):
result_text = text * 5
return (result_text,)
```

**_Example js code extension._**
```js
import { app } from "../../scripts/app.js";
import { $el } from "../../scripts/ui.js";

const idExt = "alekpet.hello_world";

// -- Extension: Speak text & Recognition speech --
app.registerExtension({
name: idExt,

async beforeRegisterNodeDef(nodeType, nodeData, app) {
// Node Created
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = async function () {
const ret = onNodeCreated
? onNodeCreated.apply(this, arguments)
: undefined;

let nodeIsMultiString = false;

if (nodeData?.input && nodeData?.input?.required) {
for (const inp of Object.keys(nodeData.input.required)) {
if (
nodeData.input.required[inp][1]?.multiline &&
!nodeData.input.required[inp][1]?.forceInput
) {
const type = nodeData.input.required[inp][0];

if (["STRING"].includes(type)) {
nodeIsMultiString = true;
break;
}
}
}
}

if (nodeData?.output) {
for (const out of nodeData.output) {
const isElementTextArea = this?.widgets?.some(
(w) =>
w?.element?.tagName === "TEXTAREA" ||
w?.inputEl?.tagName === "TEXTAREA"
);

if (isElementTextArea && ["STRING"].includes(out)) {
nodeIsMultiString = true;
break;
}
}
}

if (!nodeIsMultiString) return ret;

// Find all widget type customtext
const widgetsTextMulti = this?.widgets?.filter((w) =>
["customtext", "converted-widget"].includes(w.type)
);

const isIncludesSpeech = this?.widgets?.some(
(w) => w.type === "hello_world_type"
);

if (!isIncludesSpeech && widgetsTextMulti?.length) {
widgetsTextMulti.forEach(async (w) => {
const el = $el("input.widget_hello_world_el", { type: "checkbox" });
this.addDOMWidget("widget_hello_world", "hello_world_type", el, {
setValue(v) {
el.checked = v;
},
getValue() {
return !!el.checked;
},
});
});
}

return ret;
};
},
});

```

**Video console.log:**
https://github.com/user-attachments/assets/9866e660-82cb-4a87-9f45-4cf1d23f247c

The entire extension and test users can be found here: [alek_test.zip](https://github.com/user-attachments/files/21387317/alek_test.zip)

And the workflow for testing: [load_data_workflow_forcedinput_one_mtext.json](https://github.com/user-attachments/files/21387238/load_data_workflow_forcedinput_one_mtext.json)

### Actual Behavior

It should load and save normally as with one multi-text field. As with input data that does not have a property in the forceInput field.

### Steps to Reproduce

1. Enable or install an extension that adds a new widget to a multi-text field
2. Add a widget with forceInput and one multi-text field to the workflow
3. Change the values in the fields
4. Switch to another workflow or refresh the page.
5. See the result of loading values.****

### Debug Logs

```powershell
In the onSerialize method if you track all the values are saved correctly.

this.onSerialize = (o)=>{
console.log(o.type, o.widgets_values)
}

Video console.log below in the Expected Behavior
```

### Other

Perhaps it is necessary to somehow process the data when loading, but why does everything work with two or more multi-text fields, but with one the loading of values breaks, reducing the data in the widgets_values array.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.