0.31
50
README.md
@@ -1,4 +1,4 @@
|
|||||||
# 🔗 Comfyui : Bjornulf_custom_nodes v0.29 🔗
|
# 🔗 Comfyui : Bjornulf_custom_nodes v0.31 🔗
|
||||||
|
|
||||||
# ❤️ Coffee : ☕☕☕☕☕ 5/5
|
# ❤️ Coffee : ☕☕☕☕☕ 5/5
|
||||||
|
|
||||||
@@ -77,19 +77,39 @@ wget --content-disposition -P /workspace/ComfyUI/models/checkpoints "https://civ
|
|||||||
- **v0.27**: Two new nodes : Loop (Model+Clip+Vae) and Random (Model+Clip+Vae) - aka Checkpoint / Model
|
- **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.
|
- **v0.28**: Fix random texts and add a lot of screenshots examples for several nodes.
|
||||||
- **v0.29**: Fix floating points issues with loop float node.
|
- **v0.29**: Fix floating points issues with loop float node.
|
||||||
|
- **v0.30**: Update the basic Loop node with optional input.
|
||||||
|
- **v0.31**: ❗Sorry, Breaking changes for Write/Show text nodes, cleaner system : 1 simple write text and the other is 1 advanced with console and special syntax. Also Show can now manage INT, FLOAT, TEXT.
|
||||||
|
|
||||||
# 📝 Nodes descriptions
|
# 📝 Nodes descriptions
|
||||||
|
|
||||||
## 1/2/3 - 👁 + ✒ Show/Write Text
|
## 1 - 👁 Show (Text, Int, Float)
|
||||||

|
|
||||||

|

|
||||||

|
|
||||||
|
|
||||||
**Description:**
|
**Description:**
|
||||||
Three simple nodes, two to write and one to show text.
|
The show node will only display text, or a list of several texts. (read only node)
|
||||||
Write nodes is a textarea where you can write your text.
|
3 types are managed : Green is for STRING type, Orange is for FLOAT type and blue is for INT type.
|
||||||
The show text node will only display the text. (That's why I made it a different color : green, uneditable, display only.)
|
|
||||||
Write text nodes now allow for special syntax to accept random variants, like `{hood|helmet}` will randomly choose between hood or helmet.
|
## 2 - ✒ Write Text
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
Simple node to write text.
|
||||||
|
|
||||||
|
## 3 - ✒🗔 Advanced Write Text
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
Advanced Write Text node allows for special syntax to accept random variants, like `{hood|helmet}` will randomly choose between hood or helmet.
|
||||||
|
You also have `seed` and `control_after_generate` to manage the randomness.
|
||||||
|
It is also displaying the text in the comfyui console. (Useful for debugging)
|
||||||
|
Example of console logs :
|
||||||
|
```
|
||||||
|
Raw text: photo of a {green|blue|red|orange|yellow} {cat|rat|house}
|
||||||
|
Picked text: photo of a green house
|
||||||
|
```
|
||||||
|
|
||||||
## 4 - 🔗 Combine Texts
|
## 4 - 🔗 Combine Texts
|
||||||

|

|
||||||
@@ -102,13 +122,19 @@ Combine multiple text inputs into a single output. (can have separation with : c
|
|||||||
|
|
||||||
**Description:**
|
**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)
|
You also have `control_after_generate` to manage the randomness.
|
||||||
|
|
||||||
## 6 - ♻ Loop
|
## 6 - ♻ Loop
|
||||||

|

|
||||||
|
|
||||||
**Description:**
|
**Description:**
|
||||||
General-purpose loop node.
|
General-purpose loop node, you can connect that in between anything.
|
||||||
|
It has an optional input, if no input is given, it will loop over the value of the STRING "if_no_input" (take you can edit).
|
||||||
|
❗ Careful this node accept everything as input and output, so you can use it with texts, integers, images, mask, segs etc... but be consistent with your inputs/outputs.
|
||||||
|
Do not use this Loop if you can do otherwise.
|
||||||
|
|
||||||
|
This is an example together with my node 28, to force a different seed for each iteration :
|
||||||
|

|
||||||
|
|
||||||
## 7 - ♻ Loop Texts
|
## 7 - ♻ Loop Texts
|
||||||

|

|
||||||
@@ -392,7 +418,7 @@ Loop over a list of images.
|
|||||||
Usage example : You have a list of images, and you want to apply the same process to all of them.
|
Usage example : You have a list of images, and you want to apply the same process to all of them.
|
||||||
Above is an example of the loop images node sending them to an Ipadapter style transfer workflow. (Same seed of course.)
|
Above is an example of the loop images node sending them to an Ipadapter style transfer workflow. (Same seed of course.)
|
||||||
|
|
||||||
### 39 - ♻ Loop (✒ Write Text)
|
### 39 - ♻ Loop (✒🗔 Advanced Write Text)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
28
__init__.py
@@ -1,6 +1,5 @@
|
|||||||
from .images_to_video import imagesToVideo
|
from .images_to_video import imagesToVideo
|
||||||
from .write_text import WriteText
|
from .write_text import WriteText
|
||||||
from .write_text_console import WriteTextInConsole
|
|
||||||
from .write_image_environment import WriteImageEnvironment
|
from .write_image_environment import WriteImageEnvironment
|
||||||
from .write_image_characters import WriteImageCharacters
|
from .write_image_characters import WriteImageCharacters
|
||||||
from .write_image_character import WriteImageCharacter
|
from .write_image_character import WriteImageCharacter
|
||||||
@@ -17,8 +16,8 @@ from .loop_samplers import LoopSamplers
|
|||||||
from .loop_schedulers import LoopSchedulers
|
from .loop_schedulers import LoopSchedulers
|
||||||
from .ollama import ollamaLoader
|
from .ollama import ollamaLoader
|
||||||
from .show_text import ShowText
|
from .show_text import ShowText
|
||||||
from .show_int import ShowInt
|
# from .show_int import ShowInt
|
||||||
from .show_float import ShowFloat
|
# from .show_float import ShowFloat
|
||||||
from .save_text import SaveText
|
from .save_text import SaveText
|
||||||
from .save_tmp_image import SaveTmpImage
|
from .save_tmp_image import SaveTmpImage
|
||||||
from .save_image_path import SaveImagePath
|
from .save_image_path import SaveImagePath
|
||||||
@@ -47,6 +46,8 @@ from .random_image import RandomImage
|
|||||||
from .loop_write_text import LoopWriteText
|
from .loop_write_text import LoopWriteText
|
||||||
# from .random_checkpoint import RandomCheckpoint
|
# from .random_checkpoint import RandomCheckpoint
|
||||||
from .loop_model_clip_vae import LoopModelClipVae
|
from .loop_model_clip_vae import LoopModelClipVae
|
||||||
|
from .write_text_advanced import WriteTextAdvanced
|
||||||
|
# from .show import ShowWhatever
|
||||||
|
|
||||||
# from .pass_preview_image import PassPreviewImage
|
# from .pass_preview_image import PassPreviewImage
|
||||||
# from .check_black_image import CheckBlackImage
|
# from .check_black_image import CheckBlackImage
|
||||||
@@ -57,6 +58,8 @@ from .loop_model_clip_vae import LoopModelClipVae
|
|||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
# "Bjornulf_CustomStringType": CustomStringType,
|
# "Bjornulf_CustomStringType": CustomStringType,
|
||||||
"Bjornulf_ollamaLoader": ollamaLoader,
|
"Bjornulf_ollamaLoader": ollamaLoader,
|
||||||
|
"Bjornulf_WriteText": WriteText,
|
||||||
|
# "Bjornulf_ShowWhatever": ShowWhatever,
|
||||||
"Bjornulf_LoopModelClipVae": LoopModelClipVae,
|
"Bjornulf_LoopModelClipVae": LoopModelClipVae,
|
||||||
# "Bjoenulf_RandomCheckpoint": RandomCheckpoint,
|
# "Bjoenulf_RandomCheckpoint": RandomCheckpoint,
|
||||||
"Bjornulf_LoopWriteText": LoopWriteText,
|
"Bjornulf_LoopWriteText": LoopWriteText,
|
||||||
@@ -78,8 +81,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
# "Bjornulf_CheckBlackImage": CheckBlackImage,
|
# "Bjornulf_CheckBlackImage": CheckBlackImage,
|
||||||
# "Bjornulf_ClearVRAM": ClearVRAM,
|
# "Bjornulf_ClearVRAM": ClearVRAM,
|
||||||
"Bjornulf_SaveBjornulfLobeChat": SaveBjornulfLobeChat,
|
"Bjornulf_SaveBjornulfLobeChat": SaveBjornulfLobeChat,
|
||||||
"Bjornulf_WriteText": WriteText,
|
"Bjornulf_WriteTextAdvanced": WriteTextAdvanced,
|
||||||
"Bjornulf_WriteTextInConsole": WriteTextInConsole,
|
|
||||||
"Bjornulf_RemoveTransparency": RemoveTransparency,
|
"Bjornulf_RemoveTransparency": RemoveTransparency,
|
||||||
"Bjornulf_GrayscaleTransform": GrayscaleTransform,
|
"Bjornulf_GrayscaleTransform": GrayscaleTransform,
|
||||||
"Bjornulf_CombineBackgroundOverlay": CombineBackgroundOverlay,
|
"Bjornulf_CombineBackgroundOverlay": CombineBackgroundOverlay,
|
||||||
@@ -88,8 +90,8 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
# "Bjornulf_WriteImageCharacter": WriteImageCharacter,
|
# "Bjornulf_WriteImageCharacter": WriteImageCharacter,
|
||||||
# "Bjornulf_WriteImageAllInOne": WriteImageAllInOne,
|
# "Bjornulf_WriteImageAllInOne": WriteImageAllInOne,
|
||||||
"Bjornulf_ShowText": ShowText,
|
"Bjornulf_ShowText": ShowText,
|
||||||
"Bjornulf_ShowInt": ShowInt,
|
# "Bjornulf_ShowInt": ShowInt,
|
||||||
"Bjornulf_ShowFloat": ShowFloat,
|
# "Bjornulf_ShowFloat": ShowFloat,
|
||||||
"Bjornulf_SaveText": SaveText,
|
"Bjornulf_SaveText": SaveText,
|
||||||
"Bjornulf_ResizeImage": ResizeImage,
|
"Bjornulf_ResizeImage": ResizeImage,
|
||||||
"Bjornulf_SaveImageToFolder": SaveImageToFolder,
|
"Bjornulf_SaveImageToFolder": SaveImageToFolder,
|
||||||
@@ -111,10 +113,9 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
|
|
||||||
"Bjornulf_WriteText": "✒ Write Text",
|
"Bjornulf_WriteText": "✒ Write Text",
|
||||||
"Bjornulf_WriteTextInConsole": "✒🗔 Write Text (Console too) ",
|
"Bjornulf_WriteTextAdvanced": "✒🗔 Advanced Write Text",
|
||||||
"Bjornulf_LoopWriteText": "♻ Loop (✒ Write Text)",
|
"Bjornulf_LoopWriteTextAdvanced": "♻ Loop (✒🗔 Advanced Write Text)",
|
||||||
"Bjornulf_LoopModelClipVae": "♻ Loop (Model+Clip+Vae)",
|
"Bjornulf_LoopModelClipVae": "♻ Loop (Model+Clip+Vae)",
|
||||||
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
|
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
|
||||||
"Bjornulf_CombineTextsByLines": "♻ Loop (All Lines from input 🔗 combine by lines)",
|
"Bjornulf_CombineTextsByLines": "♻ Loop (All Lines from input 🔗 combine by lines)",
|
||||||
@@ -137,9 +138,10 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"Bjornulf_SaveBjornulfLobeChat": "🖼💬 Save image for Bjornulf LobeChat",
|
"Bjornulf_SaveBjornulfLobeChat": "🖼💬 Save image for Bjornulf LobeChat",
|
||||||
"Bjornulf_TextToStringAndSeed": "🔢 Text with random Seed",
|
"Bjornulf_TextToStringAndSeed": "🔢 Text with random Seed",
|
||||||
# "Bjornulf_ClearVRAM": "🧹 Clear VRAM",
|
# "Bjornulf_ClearVRAM": "🧹 Clear VRAM",
|
||||||
"Bjornulf_ShowText": "👁 Show (Text)",
|
# "Bjornulf_ShowWhatever": "👁 Show Anything",
|
||||||
"Bjornulf_ShowInt": "👁 Show (Int)",
|
"Bjornulf_ShowText": "👁 Show (Text, Int, Float)",
|
||||||
"Bjornulf_ShowFloat": "👁 Show (Float)",
|
# "Bjornulf_ShowInt": "👁 Show (Int)",
|
||||||
|
# "Bjornulf_ShowFloat": "👁 Show (Float)",
|
||||||
"Bjornulf_ImageMaskCutter": "🖼✂ Cut Image with Mask",
|
"Bjornulf_ImageMaskCutter": "🖼✂ Cut Image with Mask",
|
||||||
"Bjornulf_LoadImageWithTransparency": "🖼 Load Image with Transparency ▢",
|
"Bjornulf_LoadImageWithTransparency": "🖼 Load Image with Transparency ▢",
|
||||||
"Bjornulf_CombineBackgroundOverlay": "🖼+🖼 Combine images (Background+Overlay alpha)",
|
"Bjornulf_CombineBackgroundOverlay": "🖼+🖼 Combine images (Background+Overlay alpha)",
|
||||||
|
|||||||
@@ -1,21 +1,39 @@
|
|||||||
class LoopBasicBatch:
|
class Everything(str):
|
||||||
|
def __ne__(self, __value: object) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
class LoopBasicBatch:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"nb_loops": ("INT", {"default": 1, "min": 0, "max": 1000, "step": 1}),
|
"nb_loops": ("INT", {"default": 1, "min": 0, "max": 1000, "step": 1}),
|
||||||
|
"default_text": ("STRING", {"default": "Default input"})
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"input": (Everything("*"),)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = ("INT",)
|
RETURN_TYPES = (Everything("*"),)
|
||||||
OUTPUT_IS_LIST = (True, False)
|
RETURN_NAMES = ("output",)
|
||||||
|
OUTPUT_IS_LIST = (True,)
|
||||||
FUNCTION = "create_loop_basic_batch"
|
FUNCTION = "create_loop_basic_batch"
|
||||||
CATEGORY = "Bjornulf"
|
CATEGORY = "Bjornulf"
|
||||||
|
|
||||||
def create_loop_basic_batch(self, nb_loops):
|
def create_loop_basic_batch(self, nb_loops, default_text, input=None):
|
||||||
range_values = list()
|
if input is not None:
|
||||||
while nb_loops > 0:
|
return ([input] * nb_loops,)
|
||||||
range_values.append(1)
|
|
||||||
nb_loops -= 1
|
# Determine the type of the default_text
|
||||||
return (range_values,)
|
if default_text.isdigit():
|
||||||
|
self.RETURN_TYPES = ("INT",)
|
||||||
|
output = int(default_text)
|
||||||
|
elif default_text.replace('.', '', 1).isdigit() and default_text.count('.') == 1:
|
||||||
|
self.RETURN_TYPES = ("FLOAT",)
|
||||||
|
output = float(default_text)
|
||||||
|
else:
|
||||||
|
self.RETURN_TYPES = ("STRING",)
|
||||||
|
output = default_text
|
||||||
|
|
||||||
|
return ([output] * nb_loops,)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "bjornulf_custom_nodes"
|
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, ..."
|
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.29"
|
version = "0.31"
|
||||||
license = {file = "LICENSE"}
|
license = {file = "LICENSE"}
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 73 KiB |
BIN
screenshots/loop3.png
Normal file
|
After Width: | Height: | Size: 390 KiB |
BIN
screenshots/loop4.png
Normal file
|
After Width: | Height: | Size: 674 KiB |
BIN
screenshots/show.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 46 KiB |
BIN
screenshots/write.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
screenshots/write_advanced.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 39 KiB |
@@ -1,18 +0,0 @@
|
|||||||
class ShowFloat:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"float_value": ("FLOAT", {"forceInput": True}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
INPUT_IS_LIST = True
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
FUNCTION = "show_float"
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
INPUT_IS_LIST = (True,)
|
|
||||||
CATEGORY = "Bjornulf"
|
|
||||||
|
|
||||||
def show_float(self, float_value):
|
|
||||||
return {"ui": {"text": float_value}}
|
|
||||||
18
show_int.py
@@ -1,18 +0,0 @@
|
|||||||
class ShowInt:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(cls):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"int_value": ("INT", {"forceInput": True}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
INPUT_IS_LIST = True
|
|
||||||
RETURN_TYPES = ()
|
|
||||||
FUNCTION = "show_int"
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
INPUT_IS_LIST = (True,)
|
|
||||||
CATEGORY = "Bjornulf"
|
|
||||||
|
|
||||||
def show_int(self, int_value):
|
|
||||||
return {"ui": {"text": int_value}}
|
|
||||||
33
show_text.py
@@ -1,18 +1,43 @@
|
|||||||
|
class Everything(str):
|
||||||
|
def __ne__(self, _):
|
||||||
|
return False
|
||||||
|
|
||||||
class ShowText:
|
class ShowText:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"text_value": ("STRING", {"forceInput": True}),
|
"text_int_float": (Everything("*"), {"forceInput": True}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
INPUT_IS_LIST = True
|
|
||||||
RETURN_TYPES = ()
|
RETURN_TYPES = ()
|
||||||
FUNCTION = "show_text"
|
FUNCTION = "show_text"
|
||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
INPUT_IS_LIST = (True,)
|
INPUT_IS_LIST = (True,)
|
||||||
CATEGORY = "Bjornulf"
|
CATEGORY = "Bjornulf"
|
||||||
|
|
||||||
def show_text(self, text_value):
|
def detect_type(self, value):
|
||||||
return {"ui": {"text": text_value}}
|
if isinstance(value, int):
|
||||||
|
return 'integer'
|
||||||
|
elif isinstance(value, float):
|
||||||
|
# Check if it has a decimal part
|
||||||
|
if value % 1 == 0:
|
||||||
|
return 'float' if str(value).endswith('.0') else 'integer'
|
||||||
|
return 'float'
|
||||||
|
elif isinstance(value, str):
|
||||||
|
try:
|
||||||
|
float_val = float(value)
|
||||||
|
if '.' in value:
|
||||||
|
return 'float string'
|
||||||
|
if float_val.is_integer():
|
||||||
|
return 'integer string'
|
||||||
|
return 'float string'
|
||||||
|
except ValueError:
|
||||||
|
return 'normal string'
|
||||||
|
else:
|
||||||
|
return 'other type'
|
||||||
|
|
||||||
|
def show_text(self, text_int_float):
|
||||||
|
type_info = [f"{value}" for value in text_int_float]
|
||||||
|
return {"ui": {"text": type_info}}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { app } from "../../../scripts/app.js";
|
|
||||||
|
|
||||||
app.registerExtension({
|
|
||||||
name: "Bjornulf.RandomLineFromInput",
|
|
||||||
async nodeCreated(node) {
|
|
||||||
if (node.comfyClass === "Bjornulf_RandomLineFromInput") {
|
|
||||||
// Set seed widget to hidden input
|
|
||||||
const seedWidget = node.widgets.find(w => w.name === "seed");
|
|
||||||
if (seedWidget) {
|
|
||||||
seedWidget.type = "HIDDEN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { app } from "../../../scripts/app.js";
|
|
||||||
|
|
||||||
app.registerExtension({
|
|
||||||
name: "Bjornulf.TextToStringAndSeed",
|
|
||||||
async nodeCreated(node) {
|
|
||||||
if (node.comfyClass === "Bjornulf_TextToStringAndSeed") {
|
|
||||||
// Set seed widget to hidden input
|
|
||||||
const seedWidget = node.widgets.find(w => w.name === "seed");
|
|
||||||
if (seedWidget) {
|
|
||||||
seedWidget.type = "HIDDEN";
|
|
||||||
}
|
|
||||||
// Set seed widget to hidden input
|
|
||||||
const controlWidget = node.widgets.find(w => w.name === "control_after_generate");
|
|
||||||
if (controlWidget) {
|
|
||||||
controlWidget.type = "HIDDEN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -36,10 +36,10 @@ app.registerExtension({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Set seed widget to hidden input
|
// Set seed widget to hidden input
|
||||||
const seedWidget = node.widgets.find(w => w.name === "seed");
|
// const seedWidget = node.widgets.find(w => w.name === "seed");
|
||||||
if (seedWidget) {
|
// if (seedWidget) {
|
||||||
seedWidget.type = "HIDDEN";
|
// seedWidget.type = "HIDDEN";
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Move number_of_inputs to the top initially
|
// Move number_of_inputs to the top initially
|
||||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs");
|
const numInputsWidget = node.widgets.find(w => w.name === "number_of_inputs");
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import { app } from "../../../scripts/app.js";
|
|
||||||
import { ComfyWidgets } from "../../../scripts/widgets.js";
|
|
||||||
|
|
||||||
// Styles for the text area
|
|
||||||
const textAreaStyles = {
|
|
||||||
readOnly: true,
|
|
||||||
opacity: 1,
|
|
||||||
padding: '10px',
|
|
||||||
border: '1px solid #ccc',
|
|
||||||
borderRadius: '5px',
|
|
||||||
backgroundColor: '#222',
|
|
||||||
color: 'Lime',
|
|
||||||
fontFamily: 'Arial, sans-serif',
|
|
||||||
fontSize: '14px',
|
|
||||||
lineHeight: '1.4',
|
|
||||||
resize: 'vertical',
|
|
||||||
overflowY: 'auto',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Displays input text on a node
|
|
||||||
app.registerExtension({
|
|
||||||
name: "Bjornulf.ShowFloat",
|
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
||||||
if (nodeData.name === "Bjornulf_ShowFloat") {
|
|
||||||
function createStyledTextArea(text) {
|
|
||||||
const widget = ComfyWidgets["STRING"](this, "text", ["STRING", { multiline: true }], app).widget;
|
|
||||||
widget.inputEl.readOnly = true;
|
|
||||||
const textArea = widget.inputEl;
|
|
||||||
|
|
||||||
Object.assign(textArea.style, textAreaStyles);
|
|
||||||
textArea.classList.add('bjornulf-show-text');
|
|
||||||
widget.value = text;
|
|
||||||
return widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
function populate(text) {
|
|
||||||
if (this.widgets) {
|
|
||||||
for (let i = 1; i < this.widgets.length; i++) {
|
|
||||||
this.widgets[i].onRemove?.();
|
|
||||||
}
|
|
||||||
this.widgets.length = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const v = Array.isArray(text) ? text : [text];
|
|
||||||
for (const list of v) {
|
|
||||||
if (list) {
|
|
||||||
createStyledTextArea.call(this, list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
const sz = this.computeSize();
|
|
||||||
if (sz[0] < this.size[0]) sz[0] = this.size[0];
|
|
||||||
if (sz[1] < this.size[1]) sz[1] = this.size[1];
|
|
||||||
this.onResize?.(sz);
|
|
||||||
app.graph.setDirtyCanvas(true, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
onExecuted?.apply(this, arguments);
|
|
||||||
populate.call(this, message.text);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onConfigure = nodeType.prototype.onConfigure;
|
|
||||||
nodeType.prototype.onConfigure = function () {
|
|
||||||
onConfigure?.apply(this, arguments);
|
|
||||||
if (this.widgets_values?.length) {
|
|
||||||
populate.call(this, this.widgets_values);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import { app } from "../../../scripts/app.js";
|
|
||||||
import { ComfyWidgets } from "../../../scripts/widgets.js";
|
|
||||||
|
|
||||||
// Styles for the text area
|
|
||||||
const textAreaStyles = {
|
|
||||||
readOnly: true,
|
|
||||||
opacity: 1,
|
|
||||||
padding: '10px',
|
|
||||||
border: '1px solid #ccc',
|
|
||||||
borderRadius: '5px',
|
|
||||||
backgroundColor: '#222',
|
|
||||||
color: 'Lime',
|
|
||||||
fontFamily: 'Arial, sans-serif',
|
|
||||||
fontSize: '14px',
|
|
||||||
lineHeight: '1.4',
|
|
||||||
resize: 'vertical',
|
|
||||||
overflowY: 'auto',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Displays input text on a node
|
|
||||||
app.registerExtension({
|
|
||||||
name: "Bjornulf.ShowInt",
|
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
||||||
if (nodeData.name === "Bjornulf_ShowInt") {
|
|
||||||
function createStyledTextArea(text) {
|
|
||||||
const widget = ComfyWidgets["STRING"](this, "text", ["STRING", { multiline: true }], app).widget;
|
|
||||||
widget.inputEl.readOnly = true;
|
|
||||||
const textArea = widget.inputEl;
|
|
||||||
|
|
||||||
Object.assign(textArea.style, textAreaStyles);
|
|
||||||
textArea.classList.add('bjornulf-show-text');
|
|
||||||
widget.value = text;
|
|
||||||
return widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
function populate(text) {
|
|
||||||
if (this.widgets) {
|
|
||||||
for (let i = 1; i < this.widgets.length; i++) {
|
|
||||||
this.widgets[i].onRemove?.();
|
|
||||||
}
|
|
||||||
this.widgets.length = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const v = Array.isArray(text) ? text : [text];
|
|
||||||
for (const list of v) {
|
|
||||||
if (list) {
|
|
||||||
createStyledTextArea.call(this, list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
const sz = this.computeSize();
|
|
||||||
if (sz[0] < this.size[0]) sz[0] = this.size[0];
|
|
||||||
if (sz[1] < this.size[1]) sz[1] = this.size[1];
|
|
||||||
this.onResize?.(sz);
|
|
||||||
app.graph.setDirtyCanvas(true, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
onExecuted?.apply(this, arguments);
|
|
||||||
populate.call(this, message.text);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onConfigure = nodeType.prototype.onConfigure;
|
|
||||||
nodeType.prototype.onConfigure = function () {
|
|
||||||
onConfigure?.apply(this, arguments);
|
|
||||||
if (this.widgets_values?.length) {
|
|
||||||
populate.call(this, this.widgets_values);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -5,7 +5,8 @@ import { ComfyWidgets } from "../../../scripts/widgets.js";
|
|||||||
const textAreaStyles = {
|
const textAreaStyles = {
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
padding: '10px',
|
padding: '4px',
|
||||||
|
paddingLeft: '7px',
|
||||||
border: '1px solid #ccc',
|
border: '1px solid #ccc',
|
||||||
borderRadius: '5px',
|
borderRadius: '5px',
|
||||||
backgroundColor: '#222',
|
backgroundColor: '#222',
|
||||||
@@ -13,7 +14,7 @@ const textAreaStyles = {
|
|||||||
fontFamily: 'Arial, sans-serif',
|
fontFamily: 'Arial, sans-serif',
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
lineHeight: '1.4',
|
lineHeight: '1.4',
|
||||||
resize: 'vertical',
|
resize: 'none',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,32 +23,36 @@ app.registerExtension({
|
|||||||
name: "Bjornulf.ShowText",
|
name: "Bjornulf.ShowText",
|
||||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||||
if (nodeData.name === "Bjornulf_ShowText") {
|
if (nodeData.name === "Bjornulf_ShowText") {
|
||||||
function createStyledTextArea(text) {
|
|
||||||
const widget = ComfyWidgets["STRING"](this, "text", ["STRING", { multiline: true }], app).widget;
|
|
||||||
widget.inputEl.readOnly = true;
|
|
||||||
const textArea = widget.inputEl;
|
|
||||||
|
|
||||||
Object.assign(textArea.style, textAreaStyles);
|
|
||||||
textArea.classList.add('bjornulf-show-text');
|
|
||||||
widget.value = text;
|
|
||||||
return widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
function populate(text) {
|
function populate(text) {
|
||||||
if (this.widgets) {
|
if (this.widgets) {
|
||||||
for (let i = 1; i < this.widgets.length; i++) {
|
const pos = this.widgets.findIndex((w) => w.name === "text");
|
||||||
this.widgets[i].onRemove?.();
|
if (pos !== -1) {
|
||||||
}
|
for (let i = pos; i < this.widgets.length; i++) {
|
||||||
this.widgets.length = 1;
|
this.widgets[i].onRemove?.();
|
||||||
}
|
}
|
||||||
|
this.widgets.length = pos;
|
||||||
const v = Array.isArray(text) ? text : [text];
|
|
||||||
for (const list of v) {
|
|
||||||
if (list) {
|
|
||||||
createStyledTextArea.call(this, list);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const list of text) {
|
||||||
|
const w = ComfyWidgets["STRING"](this, "text", ["STRING", { multiline: true }], app).widget;
|
||||||
|
w.inputEl.readOnly = true;
|
||||||
|
Object.assign(w.inputEl.style, textAreaStyles);
|
||||||
|
|
||||||
|
// Improved type detection
|
||||||
|
let color = 'Lime'; // Default color for strings
|
||||||
|
const value = list.toString().trim();
|
||||||
|
|
||||||
|
if (/^-?\d+$/.test(value)) {
|
||||||
|
color = '#0096FF'; // Integer
|
||||||
|
} else if (/^-?\d*\.?\d+$/.test(value)) {
|
||||||
|
color = 'orange'; // Float
|
||||||
|
}
|
||||||
|
|
||||||
|
w.inputEl.style.color = color;
|
||||||
|
w.value = list;
|
||||||
|
}
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const sz = this.computeSize();
|
const sz = this.computeSize();
|
||||||
if (sz[0] < this.size[0]) sz[0] = this.size[0];
|
if (sz[0] < this.size[0]) sz[0] = this.size[0];
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
import re
|
|
||||||
import random
|
|
||||||
import time
|
|
||||||
|
|
||||||
class WriteText:
|
class WriteText:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"text": ("STRING", {"multiline": True}),
|
"text": ("STRING", {"multiline": True, "lines": 10}),
|
||||||
},
|
|
||||||
"hidden": {
|
|
||||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
|
||||||
"control_after_update": ("INT", {"default": 0})
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,24 +13,5 @@ class WriteText:
|
|||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
CATEGORY = "Bjornulf"
|
CATEGORY = "Bjornulf"
|
||||||
|
|
||||||
def write_text(self, text, seed=None, control_after_update=None):
|
def write_text(self, text):
|
||||||
# If seed is not provided, generate a new one
|
return (text,)
|
||||||
if seed is None:
|
|
||||||
seed = int(time.time() * 1000)
|
|
||||||
|
|
||||||
# Use the seed to initialize the random number generator
|
|
||||||
random.seed(seed)
|
|
||||||
|
|
||||||
def replace_random(match):
|
|
||||||
options = match.group(1).split('|')
|
|
||||||
return random.choice(options)
|
|
||||||
|
|
||||||
pattern = r'\{([^}]+)\}'
|
|
||||||
result = re.sub(pattern, replace_random, text)
|
|
||||||
|
|
||||||
return (result,)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def IS_CHANGED(s, text, seed=None, control_after_update=None):
|
|
||||||
# This method is called to determine if the node needs to be re-executed
|
|
||||||
return float("nan") # Always re-execute to ensure consistency
|
|
||||||
48
write_text_advanced.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import re
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class WriteTextAdvanced:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"text": ("STRING", {"multiline": True, "lines": 10}),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
RETURN_NAMES = ("text",)
|
||||||
|
FUNCTION = "write_text_special"
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
CATEGORY = "Bjornulf"
|
||||||
|
|
||||||
|
def write_text_special(self, text, seed=None):
|
||||||
|
logging.info(f"Raw text: {text}")
|
||||||
|
# If seed is not provided, generate a new one
|
||||||
|
if len(text) > 10000:
|
||||||
|
return ("Text too large to process at once. Please split into smaller parts.",)
|
||||||
|
|
||||||
|
if seed is None or seed == 0:
|
||||||
|
seed = int(time.time() * 1000)
|
||||||
|
|
||||||
|
random.seed(seed)
|
||||||
|
|
||||||
|
pattern = r'\{([^}]+)\}'
|
||||||
|
|
||||||
|
def replace_random(match):
|
||||||
|
return random.choice(match.group(1).split('|'))
|
||||||
|
|
||||||
|
result = re.sub(pattern, replace_random, text)
|
||||||
|
logging.info(f"Picked text: {result}")
|
||||||
|
|
||||||
|
return (result,)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def IS_CHANGED(s, text, seed=None):
|
||||||
|
return (text, seed)
|
||||||
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import re
|
|
||||||
import random
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
class WriteTextInConsole:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(s):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"text": ("STRING", {"multiline": True}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# INPUT_IS_LIST = True
|
|
||||||
RETURN_TYPES = ("STRING",)
|
|
||||||
RETURN_NAMES = ("text",)
|
|
||||||
FUNCTION = "write_text_in_console"
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
OUTPUT_IS_LIST = (False,)
|
|
||||||
CATEGORY = "Bjornulf"
|
|
||||||
|
|
||||||
def write_text_in_console(self, text, seed=None, control_after_update=None):
|
|
||||||
logging.info(f"Text: {text}")
|
|
||||||
# If seed is not provided, generate a new one
|
|
||||||
if seed is None:
|
|
||||||
seed = int(time.time() * 1000)
|
|
||||||
|
|
||||||
# Use the seed to initialize the random number generator
|
|
||||||
random.seed(seed)
|
|
||||||
|
|
||||||
def replace_random(match):
|
|
||||||
options = match.group(1).split('|')
|
|
||||||
return random.choice(options)
|
|
||||||
|
|
||||||
pattern = r'\{([^}]+)\}'
|
|
||||||
result = re.sub(pattern, replace_random, text)
|
|
||||||
|
|
||||||
return (result,)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def IS_CHANGED(s, text, seed=None, control_after_update=None):
|
|
||||||
# This method is called to determine if the node needs to be re-executed
|
|
||||||
return float("nan") # Always re-execute to ensure consistency
|
|
||||||