If this is what you want: Demo gif
Then here is what you need:
my_custom_nodes/
├── js/
│ └── TextOrFile.js
├── TextOrFile.py
└── __init__.py
# __init__.py
from .TextOrFile import TextOrFile
NODE_CLASS_MAPPINGS = { "TextOrFile": TextOrFile }
NODE_DISPLAY_NAME_MAPPINGS = { "TextOrFile": "Text or File" }
WEB_DIRECTORY = "./js"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
# TextOrFile.py
class TextOrFile:
CATEGORY = "Custom"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"mode": (["text", "file"],),
"text_content": ("STRING", {"multiline": False, "default": "Text", "forceInput": False}),
"file_path": ("STRING", {"multiline": False, "default": "/", "forceInput": False}),
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "run"
def run(self, mode, text_content, file_path):
if mode == "text":
return (text_content,)
return (file_path,)
// js/TextOrFile.js
import { app } from "../../scripts/app.js";
app.registerExtension({
name: "text_or_file_ui",
async nodeCreated(node) {
if (node.comfyClass !== "TextOrFile") return;
// Locate the primary dropdown widget
const modeWidget = node.widgets.find(w => w.name === "mode");
const origCallback = modeWidget.callback;
// Override its callback to add/remove inputs on change
modeWidget.callback = (value) => {
origCallback?.call(node, value);
// Get widget indices
const textWidgetIndex = node.widgets.findIndex(w => w.name === "text_content");
const fileWidgetIndex = node.widgets.findIndex(w => w.name === "file_path");
if (value === "text") {
// Add text content widget
if (textWidgetIndex === -1) node.addWidget("STRING", "text_content", "default-value-here");
// Remove file path widget
if (fileWidgetIndex !== -1) node.widgets.splice(fileWidgetIndex, 1);
} else if (value === "file") {
// Add file path widget
if (fileWidgetIndex === -1) node.addWidget("STRING", "file_path", "default-file-path-here");
// Remove text content widget
if (textWidgetIndex !== -1) node.widgets.splice(textWidgetIndex, 1);
}
// Force a redraw so the UI updates immediately
node.setDirtyCanvas(true, true);
};
// Initialize once on node creation
modeWidget.callback(modeWidget.value);
}
});
Sources: