0.31
50
README.md
@@ -1,4 +1,4 @@
|
||||
# 🔗 Comfyui : Bjornulf_custom_nodes v0.29 🔗
|
||||
# 🔗 Comfyui : Bjornulf_custom_nodes v0.31 🔗
|
||||
|
||||
# ❤️ 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.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.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
|
||||
|
||||
## 1/2/3 - 👁 + ✒ Show/Write Text
|
||||

|
||||

|
||||

|
||||
## 1 - 👁 Show (Text, Int, Float)
|
||||
|
||||

|
||||
|
||||
**Description:**
|
||||
Three simple nodes, two to write and one to show text.
|
||||
Write nodes is a textarea where you can write your text.
|
||||
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.
|
||||
The show node will only display text, or a list of several texts. (read only node)
|
||||
3 types are managed : Green is for STRING type, Orange is for FLOAT type and blue is for INT type.
|
||||
|
||||
## 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
|
||||

|
||||
@@ -102,13 +122,19 @@ Combine multiple text inputs into a single output. (can have separation with : c
|
||||
|
||||
**Description:**
|
||||
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
|
||||

|
||||
|
||||
**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
|
||||

|
||||
@@ -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.
|
||||
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 .write_text import WriteText
|
||||
from .write_text_console import WriteTextInConsole
|
||||
from .write_image_environment import WriteImageEnvironment
|
||||
from .write_image_characters import WriteImageCharacters
|
||||
from .write_image_character import WriteImageCharacter
|
||||
@@ -17,8 +16,8 @@ from .loop_samplers import LoopSamplers
|
||||
from .loop_schedulers import LoopSchedulers
|
||||
from .ollama import ollamaLoader
|
||||
from .show_text import ShowText
|
||||
from .show_int import ShowInt
|
||||
from .show_float import ShowFloat
|
||||
# from .show_int import ShowInt
|
||||
# from .show_float import ShowFloat
|
||||
from .save_text import SaveText
|
||||
from .save_tmp_image import SaveTmpImage
|
||||
from .save_image_path import SaveImagePath
|
||||
@@ -47,6 +46,8 @@ from .random_image import RandomImage
|
||||
from .loop_write_text import LoopWriteText
|
||||
# from .random_checkpoint import RandomCheckpoint
|
||||
from .loop_model_clip_vae import LoopModelClipVae
|
||||
from .write_text_advanced import WriteTextAdvanced
|
||||
# from .show import ShowWhatever
|
||||
|
||||
# from .pass_preview_image import PassPreviewImage
|
||||
# from .check_black_image import CheckBlackImage
|
||||
@@ -57,6 +58,8 @@ from .loop_model_clip_vae import LoopModelClipVae
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
# "Bjornulf_CustomStringType": CustomStringType,
|
||||
"Bjornulf_ollamaLoader": ollamaLoader,
|
||||
"Bjornulf_WriteText": WriteText,
|
||||
# "Bjornulf_ShowWhatever": ShowWhatever,
|
||||
"Bjornulf_LoopModelClipVae": LoopModelClipVae,
|
||||
# "Bjoenulf_RandomCheckpoint": RandomCheckpoint,
|
||||
"Bjornulf_LoopWriteText": LoopWriteText,
|
||||
@@ -78,8 +81,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
# "Bjornulf_CheckBlackImage": CheckBlackImage,
|
||||
# "Bjornulf_ClearVRAM": ClearVRAM,
|
||||
"Bjornulf_SaveBjornulfLobeChat": SaveBjornulfLobeChat,
|
||||
"Bjornulf_WriteText": WriteText,
|
||||
"Bjornulf_WriteTextInConsole": WriteTextInConsole,
|
||||
"Bjornulf_WriteTextAdvanced": WriteTextAdvanced,
|
||||
"Bjornulf_RemoveTransparency": RemoveTransparency,
|
||||
"Bjornulf_GrayscaleTransform": GrayscaleTransform,
|
||||
"Bjornulf_CombineBackgroundOverlay": CombineBackgroundOverlay,
|
||||
@@ -88,8 +90,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
# "Bjornulf_WriteImageCharacter": WriteImageCharacter,
|
||||
# "Bjornulf_WriteImageAllInOne": WriteImageAllInOne,
|
||||
"Bjornulf_ShowText": ShowText,
|
||||
"Bjornulf_ShowInt": ShowInt,
|
||||
"Bjornulf_ShowFloat": ShowFloat,
|
||||
# "Bjornulf_ShowInt": ShowInt,
|
||||
# "Bjornulf_ShowFloat": ShowFloat,
|
||||
"Bjornulf_SaveText": SaveText,
|
||||
"Bjornulf_ResizeImage": ResizeImage,
|
||||
"Bjornulf_SaveImageToFolder": SaveImageToFolder,
|
||||
@@ -111,10 +113,9 @@ NODE_CLASS_MAPPINGS = {
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
|
||||
"Bjornulf_WriteText": "✒ Write Text",
|
||||
"Bjornulf_WriteTextInConsole": "✒🗔 Write Text (Console too) ",
|
||||
"Bjornulf_LoopWriteText": "♻ Loop (✒ Write Text)",
|
||||
"Bjornulf_WriteTextAdvanced": "✒🗔 Advanced Write Text",
|
||||
"Bjornulf_LoopWriteTextAdvanced": "♻ Loop (✒🗔 Advanced Write Text)",
|
||||
"Bjornulf_LoopModelClipVae": "♻ Loop (Model+Clip+Vae)",
|
||||
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
|
||||
"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_TextToStringAndSeed": "🔢 Text with random Seed",
|
||||
# "Bjornulf_ClearVRAM": "🧹 Clear VRAM",
|
||||
"Bjornulf_ShowText": "👁 Show (Text)",
|
||||
"Bjornulf_ShowInt": "👁 Show (Int)",
|
||||
"Bjornulf_ShowFloat": "👁 Show (Float)",
|
||||
# "Bjornulf_ShowWhatever": "👁 Show Anything",
|
||||
"Bjornulf_ShowText": "👁 Show (Text, Int, Float)",
|
||||
# "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)",
|
||||
|
||||
@@ -1,21 +1,39 @@
|
||||
class LoopBasicBatch:
|
||||
class Everything(str):
|
||||
def __ne__(self, __value: object) -> bool:
|
||||
return False
|
||||
|
||||
class LoopBasicBatch:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"nb_loops": ("INT", {"default": 1, "min": 0, "max": 1000, "step": 1}),
|
||||
"default_text": ("STRING", {"default": "Default input"})
|
||||
},
|
||||
"optional": {
|
||||
"input": (Everything("*"),)
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
OUTPUT_IS_LIST = (True, False)
|
||||
RETURN_TYPES = (Everything("*"),)
|
||||
RETURN_NAMES = ("output",)
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
FUNCTION = "create_loop_basic_batch"
|
||||
CATEGORY = "Bjornulf"
|
||||
|
||||
def create_loop_basic_batch(self, nb_loops):
|
||||
range_values = list()
|
||||
while nb_loops > 0:
|
||||
range_values.append(1)
|
||||
nb_loops -= 1
|
||||
return (range_values,)
|
||||
def create_loop_basic_batch(self, nb_loops, default_text, input=None):
|
||||
if input is not None:
|
||||
return ([input] * nb_loops,)
|
||||
|
||||
# Determine the type of the default_text
|
||||
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]
|
||||
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.29"
|
||||
version = "0.31"
|
||||
license = {file = "LICENSE"}
|
||||
|
||||
[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:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"text_value": ("STRING", {"forceInput": True}),
|
||||
"text_int_float": (Everything("*"), {"forceInput": True}),
|
||||
},
|
||||
}
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "show_text"
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = (True,)
|
||||
CATEGORY = "Bjornulf"
|
||||
|
||||
def show_text(self, text_value):
|
||||
return {"ui": {"text": text_value}}
|
||||
def detect_type(self, 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
|
||||
const seedWidget = node.widgets.find(w => w.name === "seed");
|
||||
if (seedWidget) {
|
||||
seedWidget.type = "HIDDEN";
|
||||
}
|
||||
// const seedWidget = node.widgets.find(w => w.name === "seed");
|
||||
// if (seedWidget) {
|
||||
// seedWidget.type = "HIDDEN";
|
||||
// }
|
||||
|
||||
// Move number_of_inputs to the top initially
|
||||
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 = {
|
||||
readOnly: true,
|
||||
opacity: 1,
|
||||
padding: '10px',
|
||||
padding: '4px',
|
||||
paddingLeft: '7px',
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '5px',
|
||||
backgroundColor: '#222',
|
||||
@@ -13,7 +14,7 @@ const textAreaStyles = {
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.4',
|
||||
resize: 'vertical',
|
||||
resize: 'none',
|
||||
overflowY: 'auto',
|
||||
};
|
||||
|
||||
@@ -22,30 +23,34 @@ app.registerExtension({
|
||||
name: "Bjornulf.ShowText",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
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) {
|
||||
if (this.widgets) {
|
||||
for (let i = 1; i < this.widgets.length; i++) {
|
||||
const pos = this.widgets.findIndex((w) => w.name === "text");
|
||||
if (pos !== -1) {
|
||||
for (let i = pos; i < this.widgets.length; i++) {
|
||||
this.widgets[i].onRemove?.();
|
||||
}
|
||||
this.widgets.length = 1;
|
||||
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(() => {
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import re
|
||||
import random
|
||||
import time
|
||||
|
||||
class WriteText:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"text": ("STRING", {"multiline": True}),
|
||||
},
|
||||
"hidden": {
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"control_after_update": ("INT", {"default": 0})
|
||||
"text": ("STRING", {"multiline": True, "lines": 10}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -21,24 +13,5 @@ class WriteText:
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "Bjornulf"
|
||||
|
||||
def write_text(self, text, seed=None, control_after_update=None):
|
||||
# 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
|
||||
def write_text(self, text):
|
||||
return (text,)
|
||||
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
|
||||