diff --git a/README.md b/README.md index 5a650e2..7f425d0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ๐Ÿ”— Comfyui : Bjornulf_custom_nodes v0.26 ๐Ÿ”— +# ๐Ÿ”— Comfyui : Bjornulf_custom_nodes v0.28 ๐Ÿ”— # โค๏ธ Coffee : โ˜•โ˜•โ˜•โ˜•โ˜• 5/5 @@ -74,10 +74,12 @@ wget --content-disposition -P /workspace/ComfyUI/models/checkpoints "https://civ - **v0.24**: Add a new node: Pause, select input, pick one. - **v0.25**: Two new nodes: Loop Images and Random image. - **v0.26**: New node : Loop write Text. Also increase nb of inputs allowed for most nodes. (+ update some breaking changes) +- **v0.27**: Two new nodes : Loop (Model+Clip+Vae) and Random (Model+Clip+Vae) - aka Checkpoint / Model +- **v0.28**: Fix random texts and add a lot of screenshots examples for several nodes. # ๐Ÿ“ Nodes descriptions -## 1/2 - ๐Ÿ‘ + โœ’ Show/Write Text +## 1/1b/2 - ๐Ÿ‘ + โœ’ Show/Write Text ![Show Text](screenshots/write+show_text.png) ![Show Text](screenshots/write_in_console.png) ![Show Text](screenshots/write_text_select.png) @@ -98,7 +100,8 @@ Combine multiple text inputs into a single output. (can have separation with : c ![Random Text](screenshots/random_text.png) **Description:** -Generate and display random text from a predefined list. Great for creating random prompts. +Generate and display random text from a predefined list. Great for creating random prompts. +You can change fixed/randomize for `control_after_generate` to have a different text each time you run the workflow. (or not) ## 5 - โ™ป Loop ![Loop](screenshots/loop.png) @@ -112,6 +115,8 @@ General-purpose loop node. **Description:** Cycle through a list of text inputs. +Here is an example of usage with combine texts and flux : +![Loop Texts example](screenshots/loop_text_example.png) ## 7 - โ™ป Loop Integer ![Loop Integer](screenshots/loop_integer.png) @@ -120,31 +125,36 @@ Cycle through a list of text inputs. **Description:** Iterate through a range of integer values, good for `steps` in ksampler, etc... -โ— Don't forget that you can convert ksampler widgets to input by right-clicking the ksampler node : +โ— Don't forget that you can convert ksampler widgets to input by right-clicking the ksampler node : ![Widget to Input](screenshots/widget-to-input.png) -Here is an example of usage with ksampler (Notice that it isn't optimized, but good enough for testing) : +Here is an example of usage with ksampler (Notice that with "steps" this node isn't optimized, but good enough for quick testing.) : ![Widget to Input](screenshots/example_loop_integer.png) - ## 8 - โ™ป Loop Float ![Loop Float](screenshots/loop_float.png) ![Loop Float + Show Text](screenshots/loop_float+show_text.png) **Description:** -Loop through a range of floating-point numbers, good for `cfg`, `denoise`, etc... +Loop through a range of floating-point numbers, good for `cfg`, `denoise`, etc... + +Here is an example with controlnet, trying to make a red cat based on a blue rabbit : +![Loop All Samplers](screenshots/loop_float_example.png) ## 10 - โ™ป Loop All Samplers ![Loop All Samplers](screenshots/loop_all_samplers.png) **Description:** -Iterate over all available samplers to apply them sequentially. Ideal for testing. +Iterate over all available samplers to apply them sequentially. Ideal for testing. + +Here is an example of looping over all the samplers with the normal scheduler : +![Loop All Samplers](screenshots/example_loop_all_samplers.png) ## 11 - โ™ป Loop All Schedulers ![Loop All Schedulers](screenshots/loop_all_schedulers.png) **Description:** -Iterate over all available schedulers to apply them sequentially. Ideal for testing. +Iterate over all available schedulers to apply them sequentially. Ideal for testing. (same idea as sampler above, but for schedulers) ## 12 - โ™ป Loop Combos ![Loop Combos](screenshots/loop_combos.png) @@ -153,6 +163,9 @@ Iterate over all available schedulers to apply them sequentially. Ideal for test Generate a loop from a list of my own custom combinations (scheduler+sampler), or select one combo manually. Good for testing. +Example of usage to see the differences between different combinations : +![example combos](screenshots/example_combos.png) + ## 13/14 - ๐Ÿ“ + ๐Ÿ–ผ Resize and Save Exact name โš ๏ธ๐Ÿ’ฃ ![Resize and Save Exact](screenshots/resize_save_exact.png) @@ -247,7 +260,8 @@ Need clean greenscreen ofc. (Can adjust threshold but very basic node.) ![Random line from input](screenshots/random_line_from_input.png) **Description:** -Take a random line from an input text. (When using multiple "Write Text" nodes is annoying for example, you can use that and just copy/paste a list from outside.) +Take a random line from an input text. (When using multiple "Write Text" nodes is annoying for example, you can use that and just copy/paste a list from outside.) +You can change fixed/randomize for `control_after_generate` to have a different text each time you run the workflow. (or not) ## 27 - โ™ป Loop (All Lines from input) ![Loop input](screenshots/loop_all_lines.png) @@ -363,14 +377,14 @@ You can connect this node to anything you want, above is an example with IMAGE. ### 37 - ๐ŸŽฒ๐Ÿ–ผ Random Image -![pick input](screenshots/random_image.png) +![random image](screenshots/random_image.png) **Description:** Just take a random image from a list of images. ### 38 - โ™ป๐Ÿ–ผ Loop (Images) -![pick input](screenshots/loop_images.png) +![loop images](screenshots/loop_images.png) **Description:** Loop over a list of images. @@ -379,8 +393,22 @@ Above is an example of the loop images node sending them to an Ipadapter style t ### 39 - โ™ป Loop (โœ’ Write Text) -![pick input](screenshots/loop_write_text.png) +![loop write text](screenshots/loop_write_text.png) **Description:** If you need a quick loop but you don't want something too complex with a loop node, you can use this combined write text + loop. -It will take the same special syntax as the write text node `{blue|red}`, but it will loop over ALL the possibilities instead of taking one at random. \ No newline at end of file +It will take the same special syntax as the write text node `{blue|red}`, but it will loop over ALL the possibilities instead of taking one at random. + +### 40 - ๐ŸŽฒ Random (Model+Clip+Vae) - aka Checkpoint / Model + +![pick input](screenshots/random_checkpoint.png) + +**Description:** +Just take a trio at random from a load checkpoint node. + +### 41 - โ™ป Loop (Model+Clip+Vae) - aka Checkpoint / Model + +![pick input](screenshots/loop_checkpoint.png) + +**Description:** +Loop over all the trios from several checkpoint node. \ No newline at end of file diff --git a/__init__.py b/__init__.py index 5143d9c..cf502d3 100644 --- a/__init__.py +++ b/__init__.py @@ -46,6 +46,7 @@ from .loop_images import LoopImages from .random_image import RandomImage from .loop_write_text import LoopWriteText # from .random_checkpoint import RandomCheckpoint +from .loop_model_clip_vae import LoopModelClipVae # from .pass_preview_image import PassPreviewImage # from .check_black_image import CheckBlackImage @@ -56,6 +57,7 @@ from .loop_write_text import LoopWriteText NODE_CLASS_MAPPINGS = { # "Bjornulf_CustomStringType": CustomStringType, "Bjornulf_ollamaLoader": ollamaLoader, + "Bjornulf_LoopModelClipVae": LoopModelClipVae, # "Bjoenulf_RandomCheckpoint": RandomCheckpoint, "Bjornulf_LoopWriteText": LoopWriteText, "Bjornulf_LoopImages": LoopImages, @@ -110,52 +112,13 @@ NODE_CLASS_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = { # "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!", - "Bjornulf_ollamaLoader": "๐Ÿฆ™ Ollama (Description)", - # "Bjoenulf_RandomCheckpoint": "๐ŸŽฒ Random Checkpoint", - "Bjornulf_LoopWriteText": "โ™ป Loop (โœ’ Write Text)", - "Bjornulf_LoopImages": "โ™ป๐Ÿ–ผ Loop (Images)", - "Bjornulf_RandomImage": "๐ŸŽฒ๐Ÿ–ผ Random Image", - # "Bjornulf_PassPreviewImage": "๐Ÿ–ผโฎ• Pass Preview Image", - "Bjornulf_PickInput": "โธ๏ธ๐Ÿ” Paused. Select input, Pick one", - "Bjornulf_PauseResume": "โธ๏ธ Paused. Resume or Stop ?", - "Bjornulf_FreeVRAM": "๐Ÿงน Free VRAM hack", - "Bjornulf_CombineTextsByLines": "โ™ป Loop (All Lines from input ๐Ÿ”— combine by lines)", - "Bjornulf_TextToSpeech": "๐Ÿ”Š TTS - Text to Speech", - "Bjornulf_CharacterDescriptionGenerator": "๐Ÿง‘๐Ÿ“ Character Description Generator", - "Bjornulf_ImageMaskCutter": "๐Ÿ–ผโœ‚ Cut Image with Mask", - "Bjornulf_LoadImageWithTransparency": "๐Ÿ–ผ Load Image with Transparency โ–ข", - "Bjornulf_GreenScreenToTransparency": "๐ŸŸฉโžœโ–ข Green Screen to Transparency", - # "Bjornulf_CheckBlackImage": "๐Ÿ”ฒ Check Black Image (Empty mask)", - "Bjornulf_SaveBjornulfLobeChat": "๐Ÿ–ผ๐Ÿ’ฌ Save image for Bjornulf LobeChat", - "Bjornulf_TextToStringAndSeed": "๐Ÿ”ข Text with random Seed", - # "Bjornulf_ClearVRAM": "๐Ÿงน Clear VRAM", - "Bjornulf_RandomLineFromInput": "๐ŸŽฒ Random line from input", - "Bjornulf_ShowText": "๐Ÿ‘ Show (Text)", - "Bjornulf_ShowInt": "๐Ÿ‘ Show (Int)", - "Bjornulf_ShowFloat": "๐Ÿ‘ Show (Float)", - "Bjornulf_CombineBackgroundOverlay": "๐Ÿ–ผ+๐Ÿ–ผ Combine images (Background+Overlay alpha)", - "Bjornulf_GrayscaleTransform": "๐Ÿ–ผโžœ๐Ÿ”ฒ Image to grayscale (black & white)", - "Bjornulf_RemoveTransparency": "โ–ขโžœโฌ› Remove image Transparency (alpha)", - # "๐Ÿ”ฒโžœโฌ› Transparency to color", - "Bjornulf_ResizeImage": "๐Ÿ“ Resize Image", - "Bjornulf_SaveImagePath": "๐Ÿ–ผ Save Image (exact path, exact name) โš ๏ธ๐Ÿ’ฃ", - "Bjornulf_SaveImageToFolder": "๐Ÿ–ผ๐Ÿ“ Save Image(s) to a folder", - "Bjornulf_SaveTmpImage": "๐Ÿ–ผ Save Image (tmp_api.png) โš ๏ธ๐Ÿ’ฃ", - # "Bjornulf_SaveApiImage": "๐Ÿ–ผ Save Image (./output/api_00001.png...)", - "Bjornulf_SaveText": "๐Ÿ’พ Save Text", #Make SaveCharacter, SaveLocation, SaveCamera, SaveAction, SaveClothes, SaveEmotion... - "Bjornulf_LoadText": "๐Ÿ“ฅ Load Text", #Make LoadCharacter, LoadLocation, LoadCamera, LoadAction, LoadClothes, LoadEmotion... "Bjornulf_WriteText": "โœ’ Write Text", "Bjornulf_WriteTextInConsole": "โœ’๐Ÿ—” Write Text (Console too) ", - # "Bjornulf_WriteImageEnvironment": "โœ’ Write Image Environment", - # "Bjornulf_WriteImageCharacters": "โœ’ Write Image Characters", - # "Bjornulf_WriteImageCharacter": "โœ’ Write Image Character", - # "Bjornulf_WriteImageAllInOne": "โœ’ Write Image All-in-one", - "Bjornulf_CombineTexts": "๐Ÿ”— Combine (Texts)", + "Bjornulf_LoopWriteText": "โ™ป Loop (โœ’ Write Text)", + "Bjornulf_LoopModelClipVae": "โ™ป Loop (Model+Clip+Vae)", + "Bjornulf_LoopImages": "โ™ป๐Ÿ–ผ Loop (Images)", + "Bjornulf_CombineTextsByLines": "โ™ป Loop (All Lines from input ๐Ÿ”— combine by lines)", "Bjornulf_LoopTexts": "โ™ป Loop (Texts)", - "Bjornulf_RandomTexts": "๐ŸŽฒ Random (Texts)", - "Bjornulf_RandomModelClipVae": "๐ŸŽฒ Random (Model+Clip+Vae)", - "Bjornulf_imagesToVideo": "๐Ÿ“น images to video (FFmpeg)", - "Bjornulf_VideoPingPong": "๐Ÿ“น video PingPong", "Bjornulf_LoopFloat": "โ™ป Loop (Float)", "Bjornulf_LoopInteger": "โ™ป Loop (Integer)", "Bjornulf_LoopBasicBatch": "โ™ป Loop", @@ -163,6 +126,40 @@ NODE_DISPLAY_NAME_MAPPINGS = { "Bjornulf_LoopSamplers": "โ™ป Loop (All Samplers)", "Bjornulf_LoopSchedulers": "โ™ป Loop (All Schedulers)", "Bjornulf_LoopCombosSamplersSchedulers": "โ™ป Loop (My combos Samplerโš”Scheduler)", + "Bjornulf_RandomImage": "๐ŸŽฒ๐Ÿ–ผ Random Image", + "Bjornulf_RandomLineFromInput": "๐ŸŽฒ Random line from input", + "Bjornulf_RandomTexts": "๐ŸŽฒ Random (Texts)", + "Bjornulf_RandomModelClipVae": "๐ŸŽฒ Random (Model+Clip+Vae)", + # "Bjornulf_PassPreviewImage": "๐Ÿ–ผโฎ• Pass Preview Image", + "Bjornulf_CharacterDescriptionGenerator": "๐Ÿง‘๐Ÿ“ Character Description Generator", + "Bjornulf_GreenScreenToTransparency": "๐ŸŸฉโžœโ–ข Green Screen to Transparency", + # "Bjornulf_CheckBlackImage": "๐Ÿ”ฒ Check Black Image (Empty mask)", + "Bjornulf_SaveBjornulfLobeChat": "๐Ÿ–ผ๐Ÿ’ฌ Save image for Bjornulf LobeChat", + "Bjornulf_TextToStringAndSeed": "๐Ÿ”ข Text with random Seed", + # "Bjornulf_ClearVRAM": "๐Ÿงน Clear VRAM", + "Bjornulf_ShowText": "๐Ÿ‘ Show (Text)", + "Bjornulf_ShowInt": "๐Ÿ‘ Show (Int)", + "Bjornulf_ShowFloat": "๐Ÿ‘ Show (Float)", + "Bjornulf_ImageMaskCutter": "๐Ÿ–ผโœ‚ Cut Image with Mask", + "Bjornulf_LoadImageWithTransparency": "๐Ÿ–ผ Load Image with Transparency โ–ข", + "Bjornulf_CombineBackgroundOverlay": "๐Ÿ–ผ+๐Ÿ–ผ Combine images (Background+Overlay alpha)", + "Bjornulf_GrayscaleTransform": "๐Ÿ–ผโžœ๐Ÿ”ฒ Image to grayscale (black & white)", + "Bjornulf_RemoveTransparency": "โ–ขโžœโฌ› Remove image Transparency (alpha)", + "Bjornulf_ResizeImage": "๐Ÿ“ Resize Image", + "Bjornulf_SaveImagePath": "๐Ÿ–ผ Save Image (exact path, exact name) โš ๏ธ๐Ÿ’ฃ", + "Bjornulf_SaveImageToFolder": "๐Ÿ–ผ๐Ÿ“ Save Image(s) to a folder", + "Bjornulf_SaveTmpImage": "๐Ÿ–ผ Save Image (tmp_api.png) โš ๏ธ๐Ÿ’ฃ", + # "Bjornulf_SaveApiImage": "๐Ÿ–ผ Save Image (./output/api_00001.png...)", + "Bjornulf_SaveText": "๐Ÿ’พ Save Text", + # "Bjornulf_LoadText": "๐Ÿ“ฅ Load Text", + "Bjornulf_CombineTexts": "๐Ÿ”— Combine (Texts)", + "Bjornulf_imagesToVideo": "๐Ÿ“น images to video (FFmpeg)", + "Bjornulf_VideoPingPong": "๐Ÿ“น video PingPong", + "Bjornulf_ollamaLoader": "๐Ÿฆ™ Ollama (Description)", + "Bjornulf_FreeVRAM": "๐Ÿงน Free VRAM hack", + "Bjornulf_TextToSpeech": "๐Ÿ”Š TTS - Text to Speech", + "Bjornulf_PickInput": "โธ๏ธ๐Ÿ” Paused. Select input, Pick one", + "Bjornulf_PauseResume": "โธ๏ธ Paused. Resume or Stop ?", } WEB_DIRECTORY = "./web" diff --git a/loop_combine_texts_by_lines.py b/loop_combine_texts_by_lines.py index 7f9b13d..2b2086a 100644 --- a/loop_combine_texts_by_lines.py +++ b/loop_combine_texts_by_lines.py @@ -12,7 +12,7 @@ class CombineTextsByLines: RETURN_NAMES = tuple([f"line_{i+1}" for i in range(50)]) FUNCTION = "extract_lines" OUTPUT_NODE = True - CATEGORY = "text" + CATEGORY = "Bjornulf" OUTPUT_IS_LIST = tuple([True] * 50) # Indicate that all outputs are lists def extract_lines(self, number_of_inputs, number_of_lines, **kwargs): diff --git a/loop_model_clip_vae.py b/loop_model_clip_vae.py new file mode 100644 index 0000000..be0e5ed --- /dev/null +++ b/loop_model_clip_vae.py @@ -0,0 +1,31 @@ +class LoopModelClipVae: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 10, "step": 1}), + "model_1": ("MODEL", {"forceInput": True}), + "clip_1": ("CLIP", {"forceInput": True}), + "vae_1": ("VAE", {"forceInput": True}), + "model_2": ("MODEL", {"forceInput": True}), + "clip_2": ("CLIP", {"forceInput": True}), + "vae_2": ("VAE", {"forceInput": True}), + }, + "hidden": { + **{f"model_{i}": ("MODEL", {"forceInput": True}) for i in range(3, 11)}, + **{f"clip_{i}": ("CLIP", {"forceInput": True}) for i in range(3, 11)}, + **{f"vae_{i}": ("VAE", {"forceInput": True}) for i in range(3, 11)} + } + } + + RETURN_TYPES = ("MODEL", "CLIP", "VAE") + FUNCTION = "return_all" + OUTPUT_IS_LIST = (True, True, True) + CATEGORY = "Bjornulf" + + def return_all(self, number_of_inputs, **kwargs): + models = [kwargs[f"model_{i}"] for i in range(1, number_of_inputs + 1) if f"model_{i}" in kwargs] + clips = [kwargs[f"clip_{i}"] for i in range(1, number_of_inputs + 1) if f"clip_{i}" in kwargs] + vaes = [kwargs[f"vae_{i}"] for i in range(1, number_of_inputs + 1) if f"vae_{i}" in kwargs] + + return (models, clips, vaes) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 0802e72..6eb0208 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "bjornulf_custom_nodes" description = "Nodes: Ollama, Text to Speech, Combine Texts, Random Texts, Save image for Bjornulf LobeChat, Text with random Seed, Random line from input, Combine images, Image to grayscale (black & white), Remove image Transparency (alpha), Resize Image, ..." -version = "0.26" +version = "0.28" license = {file = "LICENSE"} [project.urls] diff --git a/random_texts.py b/random_texts.py index 21e72b6..85ed801 100644 --- a/random_texts.py +++ b/random_texts.py @@ -7,21 +7,20 @@ class RandomTexts: "required": { "number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 30, "step": 1}), "number_of_random": ("INT", {"default": 1, "min": 1, "max": 30, "step": 1}), - # "text_1": ("STRING", {"forceInput": "True"}), - # "text_2": ("STRING", {"forceInput": "True"}), - "seed": ("INT", {"default": "1"}), #Used with control_after_generate, + "seed": ("INT", {"default": 0}), }, "hidden": { - **{f"text_{i}": ("STRING", {"forceInput": "True"}) for i in range(1, 31)} + **{f"text_{i}": ("STRING", {"forceInput": True}) for i in range(1, 31)} } } RETURN_TYPES = ("STRING",) FUNCTION = "random_texts" - OUTPUT_IS_LIST = (False,) + OUTPUT_IS_LIST = (True,) CATEGORY = "Bjornulf" - def random_texts(self, number_of_inputs, number_of_random, **kwargs): - texts = [kwargs[f"text_{i}"] for i in range(1, number_of_inputs + 1) if f"text_{i}" in kwargs] + def random_texts(self, number_of_inputs, number_of_random, seed, **kwargs): + random.seed(seed) + texts = [kwargs[f"text_{i}"] for i in range(1, number_of_inputs + 1) if f"text_{i}" in kwargs and kwargs[f"text_{i}"]] random_texts = random.sample(texts, min(number_of_random, len(texts))) return (random_texts,) \ No newline at end of file diff --git a/screenshots/example_combos.png b/screenshots/example_combos.png new file mode 100644 index 0000000..1ea33f1 Binary files /dev/null and b/screenshots/example_combos.png differ diff --git a/screenshots/example_loop_all_samplers.png b/screenshots/example_loop_all_samplers.png new file mode 100644 index 0000000..466b48e Binary files /dev/null and b/screenshots/example_loop_all_samplers.png differ diff --git a/screenshots/loop_checkpoint.png b/screenshots/loop_checkpoint.png new file mode 100644 index 0000000..f5a942a Binary files /dev/null and b/screenshots/loop_checkpoint.png differ diff --git a/screenshots/loop_float_example.png b/screenshots/loop_float_example.png new file mode 100644 index 0000000..339de21 Binary files /dev/null and b/screenshots/loop_float_example.png differ diff --git a/screenshots/loop_text_example.png b/screenshots/loop_text_example.png new file mode 100644 index 0000000..c0ef7ea Binary files /dev/null and b/screenshots/loop_text_example.png differ diff --git a/screenshots/random_checkpoint.png b/screenshots/random_checkpoint.png new file mode 100644 index 0000000..47f5205 Binary files /dev/null and b/screenshots/random_checkpoint.png differ diff --git a/web/js/loop_model_clip_vae.js b/web/js/loop_model_clip_vae.js new file mode 100644 index 0000000..56235a3 --- /dev/null +++ b/web/js/loop_model_clip_vae.js @@ -0,0 +1,65 @@ +import { app } from "../../../scripts/app.js"; + +app.registerExtension({ + name: "Bjornulf.LoopModelClipVae", + async nodeCreated(node) { + if (node.comfyClass === "Bjornulf_LoopModelClipVae") { + const updateInputs = () => { + const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs"); + if (!numInputsWidget) return; + + const numInputs = numInputsWidget.value; + + // Initialize node.inputs if it doesn't exist + if (!node.inputs) { + node.inputs = []; + } + + // Filter existing model, clip, and vae inputs + const existingModelInputs = node.inputs.filter(input => input.name.startsWith('model_')); + const existingClipInputs = node.inputs.filter(input => input.name.startsWith('clip_')); + const existingVaeInputs = node.inputs.filter(input => input.name.startsWith('vae_')); + + // Determine if we need to add or remove inputs + if (existingModelInputs.length < numInputs || existingClipInputs.length < numInputs || existingVaeInputs.length < numInputs) { + // Add new model, clip, and vae inputs if not enough existing + for (let i = Math.max(existingModelInputs.length, existingClipInputs.length, existingVaeInputs.length) + 1; i <= numInputs; i++) { + const modelInputName = `model_${i}`; + const clipInputName = `clip_${i}`; + const vaeInputName = `vae_${i}`; + if (!node.inputs.find(input => input.name === modelInputName)) { + node.addInput(modelInputName, "MODEL"); + } + if (!node.inputs.find(input => input.name === clipInputName)) { + node.addInput(clipInputName, "CLIP"); + } + if (!node.inputs.find(input => input.name === vaeInputName)) { + node.addInput(vaeInputName, "VAE"); + } + } + } else { + // Remove excess model, clip, and vae inputs if too many + node.inputs = node.inputs.filter(input => + (!input.name.startsWith('model_') && !input.name.startsWith('clip_') && !input.name.startsWith('vae_')) || + (parseInt(input.name.split('_')[1]) <= numInputs) + ); + } + + node.setSize(node.computeSize()); + }; + + // Move number_of_inputs to the top initially + const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs"); + if (numInputsWidget) { + node.widgets = [numInputsWidget, ...node.widgets.filter(w => w !== numInputsWidget)]; + numInputsWidget.callback = () => { + updateInputs(); + app.graph.setDirtyCanvas(true); + }; + } + + // Delay the initial update to ensure node is fully initialized + setTimeout(updateInputs, 0); + } + } +}); \ No newline at end of file