From c8d369f52cc715f0379f850a7034e4e26c3a3510 Mon Sep 17 00:00:00 2001 From: Ricky Sekulich Date: Sun, 2 Nov 2025 15:33:26 -0600 Subject: [PATCH] Changed node behavior to support text file uploads. --- __init__.py | 9 ++- __pycache__/__init__.cpython-313.pyc | Bin 0 -> 187 bytes __pycache__/nodes.cpython-313.pyc | Bin 0 -> 2278 bytes js/placeholder.js | 0 js/textUploadWidget.js | 101 +++++++++++++++++++++++++++ nodes.py | 41 +++++------ 6 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 __pycache__/__init__.cpython-313.pyc create mode 100644 __pycache__/nodes.cpython-313.pyc delete mode 100644 js/placeholder.js create mode 100644 js/textUploadWidget.js diff --git a/__init__.py b/__init__.py index 80bf072..1249293 100644 --- a/__init__.py +++ b/__init__.py @@ -1 +1,8 @@ -from .nodes import * \ No newline at end of file +# Import our node classes +from .nodes import * + +# This tells ComfyUI to look for a 'js' folder in our directory +WEB_DIRECTORY = "js" + +# Tell ComfyUI about all the parts of our node +__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY'] \ No newline at end of file diff --git a/__pycache__/__init__.cpython-313.pyc b/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1277ddcc82a88d0e895e6006c5d55602fe8c6f5 GIT binary patch literal 187 zcmey&%ge<81P_j~XNmyn#~=<2FhLog`GAb648aV+jNT09ObQI?Oq$Fu8G(YDjJFuI z{D35DUVchy@k)lzAT77-ovmU5it@8klS_(Yobz+jDnmVEl1qzA@^jn=7DS~1~EP`Gcq#XWe_Q119AY& CZZFUP literal 0 HcmV?d00001 diff --git a/__pycache__/nodes.cpython-313.pyc b/__pycache__/nodes.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6b85e06380ad8119c9eeb44566e7eda70d1fd09 GIT binary patch literal 2278 zcmZ`4OKcNYaQ5e4*5u>F3E`6siPRcV=TjgM0hI|MiRzfh8<$3LyE67V*~r-qZ#N|P zfXt;-y_6hKs9LE=t*Z1=iBsDfLFzqxDx#$aq;lcrG^$X`rSsO_*izfK+IjQanR)YO zW_N?Z0N{G?i&y*E3-CK7+~uz#yGaJ%4#*%g?*pmq3}Mntl6O^W`w=)%B?y;>q^~A% zu_KV?PM}4)Lhu2YY#+WM7@q;q0l#q~s>ofK47ylt{~yi9nud#|xfGmWE0pW`?W!<` z{K@wKQjlzR^=rG#{-^$Bsz1d}&H^-mgwg9j{2g!vWUdor-t|QSvXBn+Go(QlWDoA* z9AmN-{X9gxuc)I$k&jxdR<`C8pc*UeoN?K!R+K86Dh_tsd33ZGcuf>BE|yxG}F~k z(%EIxG8Pqj0yCY=So7MlsW`%f!*T@CrR#RXL}Ds6txV5MjmmaMeIBB9k~Z{Y9zsxp zvIm0gFPaW~wQ%?2vcT=%SLTGKKp6yoV3(c+L-WYf$%(`mVmuAm_S5t!6fJiAJC?qb zr4`Jqd!y8b2VW;sVLaj;sAVZxuSGR+C56gf4`-uXAql*!1YwrQ}Q{;cbdyS{X~rDjCZn+LdKXH5G;Qph2>a0_Se%KZ{U6 z2cdKLPhf{-1s~?n)4aoagupI`Seatw7HI7%#kWHTOK}_xO$SQjbURua|C4VN1}kQ% zF}tAvaC$TrlxRI$#$p1yf7h8#bYxH z&EuXd!jLZf*crF|PKs>bnaUNOBSR>`)(7(&1Z9q4nCIYs4k64{aQ`#$*kke76ERxy zmjlq$@_F&I;@!y8#@=0y+sE3Cy`Oe(d;PaoZm!(Aar4IQE9-M>bL)k*!oAZQnJ3}? zXWju!wBXr3IrQVg{e=hNXD7$8z6H;t#@-!jp%i#6zQqjg0D|&|3*Vr&Te4< mB9dDN2arC@K7!7_{#a!26O(q{59iMJ@|*pw2p{@92>$`;GZYK} literal 0 HcmV?d00001 diff --git a/js/placeholder.js b/js/placeholder.js deleted file mode 100644 index e69de29..0000000 diff --git a/js/textUploadWidget.js b/js/textUploadWidget.js new file mode 100644 index 0000000..1d8caaf --- /dev/null +++ b/js/textUploadWidget.js @@ -0,0 +1,101 @@ +import { app } from "../../../scripts/app.js"; + +// This file just needs to register the new widget type +function textUploadWidget(node, inputName, inputData, app) { + const widget = { + type: inputData[0], // "TEXTUPLOAD" + name: inputName, + size: [128, 88], // Widget size + draw(ctx, node, width, y) {}, // No need to draw anything + computeSize(...args) { + return [128, 88]; // Match the size + }, + async serializeValue(nodeId, widgetIndex) { + return node.widgets[widgetIndex].value; + }, + }; + + // Create the "Choose File" button + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.accept = "text/plain,.txt"; // <--- This is the important change + fileInput.style.display = "none"; + + // Handle file selection + fileInput.addEventListener("change", (event) => { + if (fileInput.files.length) { + uploadFile(fileInput.files[0]); + } + }); + + // Create the button that opens the file picker + const uploadButton = document.createElement("button"); + uploadButton.type = "button"; + uploadButton.innerText = "Choose .txt File to Upload"; + uploadButton.className = "comfy-btn"; + uploadButton.style.width = "100%"; // Make button fill widget + uploadButton.style.marginTop = "10px"; + uploadButton.addEventListener("click", () => { + fileInput.click(); + }); + + // Function to handle the upload + async function uploadFile(file) { + const body = new FormData(); + // We use "image" to match the server's /upload/image endpoint + // The server doesn't care about the file type, just saves it. + body.append("image", file); + body.append("overwrite", "true"); + widget.value = file.name; // Store the filename for the backend + + uploadButton.innerText = "Uploading..."; + + try { + const response = await fetch("/upload/image", { + method: "POST", + body: body, + }); + + if (response.status === 200) { + uploadButton.innerText = file.name; // Show the new filename + node.setValue(file.name); // Set the node's internal value + } else { + alert(response.status + " - " + response.statusText); + uploadButton.innerText = "Choose .txt File to Upload"; + } + } catch (error) { + alert(error); + uploadButton.innerText = "Choose .txt File to Upload"; + } + } + + // Add the button to the node's widget area + node.addDOMWidget(inputName, "button", uploadButton, widget); + document.body.appendChild(fileInput); // Add the hidden file input + + // Handle drag and drop + node.onDragOver = function (e) { + if (e.dataTransfer.items.length > 0) { + const item = e.dataTransfer.items[0]; + // Allow .txt files (which often have no MIME type) or text/plain + return item.kind === 'file' && (item.type === "text/plain" || item.type === ""); + } + return false; + }; + + node.onDragDrop = function (e) { + let handled = false; + for (const file of e.dataTransfer.files) { + if (file.type === "text/plain" || file.name.endsWith(".txt")) { + uploadFile(file); + handled = true; + } + } + return handled; + }; + + return widget; +} + +// Register our new "TEXTUPLOAD" widget with ComfyUI +app.registerWidget("TEXTUPLOAD", textUploadWidget); \ No newline at end of file diff --git a/nodes.py b/nodes.py index a71f3e1..fd4d0db 100644 --- a/nodes.py +++ b/nodes.py @@ -1,66 +1,61 @@ import os import folder_paths -# --- Get Text Files --- -# 1. Get the ComfyUI 'input' directory -input_dir = folder_paths.get_input_directory() - -# 2. Find all .txt files in that directory -# We os.path.isfile to make sure we don't grab directories -text_files = [ - f for f in os.listdir(input_dir) - if os.path.isfile(os.path.join(input_dir, f)) and f.endswith(".txt") -] - # --- The Node Class --- class LoadTextFile: """ - A node that loads text from a file in the ComfyUI/input/ directory. + A node that loads text from a file. + It uses a custom widget to upload a .txt file. """ @classmethod def INPUT_TYPES(s): return { "required": { - # This creates the dropdown widget with our file list - "file_name": (text_files, ) + # This string "image" MUST match the name in the + # FormData.append("image", file) in the JS. + # It tells ComfyUI to use our new "TEXTUPLOAD" widget. + "image": ("TEXTUPLOAD", ) } } # What the node will output RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("text",) # Optional, but good practice + RETURN_NAMES = ("text",) # The function that runs when the node is executed FUNCTION = "load_text" # The category in the right-click menu CATEGORY = "Rathius_ComfyNodes/Utils" + + # We don't want this node to run on file load, only when queued + def IS_CHANGED(self, **kwargs): + return float("inf") - def load_text(self, file_name): - # Get the full path to the file - file_path = os.path.join(input_dir, file_name) + def load_text(self, image): + # 'image' is the filename (e.g., "my_prompt.txt") + # The widget has already uploaded it to the 'input' folder. + file_path = os.path.join(folder_paths.get_input_directory(), image) try: # Open and read the file with open(file_path, 'r', encoding='utf-8') as f: text_content = f.read() - # Nodes must return their outputs as a tuple + # Return the text content as a tuple return (text_content, ) except Exception as e: print(f"[LoadTextFile] Error: {e}") - # Return the error as a string so you can see it in ComfyUI return (f"Error loading file: {e}", ) # --- Node Registration --- -# This tells ComfyUI about our new node NODE_CLASS_MAPPINGS = { "LoadTextFile_Rathius": LoadTextFile } -# This sets the name that appears in the Add Node menu +# I updated the display name to reflect its new function NODE_DISPLAY_NAME_MAPPINGS = { - "LoadTextFile_Rathius": "Load Text from File" + "LoadTextFile_Rathius": "Load Text from File (Upload)" } \ No newline at end of file