Initial Version
This commit is contained in:
Igal Ogonov
2024-12-28 00:59:00 +02:00
parent c47f4e4303
commit b2ce7583b7
12 changed files with 650 additions and 1 deletions
+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+2
View File
@@ -169,3 +169,5 @@ cython_debug/
# PyPI configuration file
.pypirc
examples/desktop.ini
examples/1.mkv
+69 -1
View File
@@ -1 +1,69 @@
# ComfyUI_Simple_String_Repository
# ComfyUI_Simple_String_Repository
A small utility node (in multiple variations) which assists prompt building by allowing you to use "quick and dirty" collections of strings, separated via new-line and indexed inside the string itself, directly in ComfyUI - as opposed to loading from massive collections of external files.
The nodes also support basic "randomness" syntax, which is explained in the [sd dynamic prompts repo](https://github.com/adieyal/sd-dynamic-prompts).
However, unlike the dynamic prompts in the above repo, this node aims at helping you build prompts faster using many small collections of strings already relevant to your specific project / workflow, as opposed to exploring different combinations of random elements from massive lists.
This is better illustrated below.
Install
-----------------------------------------------------------------------------
**Option 1**
Install via [ComfyUI-Manager](https://github.com/ltdrdata/ComfyUI-Manager).
**Option 2**
Clone the repository into your ComfyUI custom_nodes directory.
```text
git clone https://github.com/IgalOgonov/ComfyUI_Simple_String_Repository
```
The Node(s)
-----------------------------------------------------------------------------
The nodes are all called "Simple String Repository".
The there are three **variations** based on the number of potentially selected strings (`Small` for 3, no suffix for 5, and `Large` for 10), and each node has a "compact" version which is worse for automated workflow but more comfortable if you intend to set all selection parameters (1 required, 2 optional) manually.
![basic usage](examples/1.gif)
Each node has the `string` input, which corresponds to the collection of strings you wish to use.
By design, each collection should represent alternative tags belonging to the same "group", e.g. art styles, type of atmosphere, color scheme, artist, etc.
Below is the `delimiter`, used to separate multiple outputs in case you select more than one strings from the collection.
Afterwards is where the `Compact` and `Regular` nodes differ.
In regular node, each `target` (the index of the sub-string you wish to select) has three relevant widgets:
* The index itself (`target`)
* Its strength (`strength`)
* In case of a collection of randomly interchangeable strings, as explained in the [sd dynamic prompts repo](https://github.com/adieyal/sd-dynamic-prompts), you may explicitly select one of the options via `random` (index starts at 0), or leave it at `-1` for a random output.
The `Compact` nodes simply shove all three parameters into a single `,`-separated string. All optional parameters can be omitted.
The **indexes** are highly **recommended**, but **optional**.
Any string that does not have an index will get assigned its line number as an index during runtime.
When duplicate indexes exist (typically, this would only happen because of a mess caused by mixing indexed and unindexed strings), only the first one can be selected.
The **front-end functionality** helps avoid that:
1. `Add Missing Indexes` - This button will add unique numerical indexes to any strings missing them. Will also fix duplicates, but replacing any duplicate identifier with a new one.
The new indexes are always the lowest available unique index.
Good for when you already have specific automated workflows reliant on specific numbers, but overall, this is considered a quick and dirty fix.
2. `Regenerate All Indexes` - Strips all strings from their existing indexes, then assigns new ones, based on the line number.
3. `Remove All Indexes` - Strips all strings from their indexes.
4. `Reset Target Parameters` Resets the selection parameters, making the output blank.
Intended Usage
-----------------------------------------------------------------------------
![alt text](2.png)
Those nodes don't really have much functionality by themselves - they are meant to supplement existing prompt builder nodes, such as [String Function Tree](https://github.com/wolfden/ComfyUi_String_Function_Tree), that are meant for prompt building (as opposed to prompt generation / random exploration).
The collections themselves can be created manually (e.g. explore articles about art styles relevant to your project, and copy-paste relevant styles into an "Art Styles" node), or you can take subsets from existing collections [like the ones over here](https://github.com/adieyal/sd-dynamic-prompts/tree/main/collections).
As you may have noticed from the earlier preview, the node does support the basic randomness syntax (e.g. `{something|like|this}`), although it's not optimized for it.
![alt text](3-1.png)
Note that you don't have to use short strings - you may also use large collections as a single string, as long as you're fine with giving up precision for such strings.
+24
View File
@@ -0,0 +1,24 @@
from .py.simple_string_repository import SimpleStringRepositorySmall,SimpleStringRepositorySmallCompact, SimpleStringRepository, SimpleStringRepositoryCompact, SimpleStringRepositoryLarge, SimpleStringRepositoryLargeCompact
NODE_CLASS_MAPPINGS = {
"SimpleStringRepositorySmall": SimpleStringRepositorySmall,
"SimpleStringRepositorySmallCompact": SimpleStringRepositorySmallCompact,
"SimpleStringRepository": SimpleStringRepository,
"SimpleStringRepositoryCompact": SimpleStringRepositoryCompact,
"SimpleStringRepositoryLarge": SimpleStringRepositoryLarge,
"SimpleStringRepositoryLargeCompact": SimpleStringRepositoryLargeCompact,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"SimpleStringRepositorySmall": "Simple String Repository Small (IO)",
"SimpleStringRepositorySmallCompact": "Simple String Repository Small Compact (IO)",
"SimpleStringRepository": "Simple String Repository (IO)",
"SimpleStringRepositoryCompact": "Simple String Repository Compact (IO)",
"SimpleStringRepositoryLarge": "Simple String Repository Large (IO)",
"SimpleStringRepositoryLargeCompact": "Simple String Repository Large Compact (IO)",
}
WEB_DIRECTORY = "./web"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

+296
View File
@@ -0,0 +1,296 @@
from .util import Util
class SimpleStringRepositorySmall:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"string": ("STRING", {"multiline": True}),
"delimiter": ([",", " "], {}),
},
"optional": {
"target1": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength1": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random1": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target2": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength2": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random2": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target3": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength3": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random3": ("INT", {"default":-1,"min": -1,"max": 1000})
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("parsed_string",)
CATEGORY = "utils"
FUNCTION = "exec"
OUTPUT_NODE = True
def IS_CHANGED(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
target3=None, strength3=None, random3=None):
parsedInputs = [
{"t":target1,"s":strength1,"r":random1},
{"t":target2,"s":strength2,"r":random2},
{"t":target3,"s":strength3,"r":random3}
]
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(parsedInputs)]
#Main Function
def exec(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
target3=None, strength3=None, random3=None):
#Parse inputs for clarity
parsedInputs = [
{"t":target1,"s":strength1,"r":random1},
{"t":target2,"s":strength2,"r":random2},
{"t":target3,"s":strength3,"r":random3}
]
return Util.common_string_repo_function(string,delimiter,parsedInputs)
class SimpleStringRepositorySmallCompact:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"string": ("STRING", {"multiline": True}),
"delimiter": ([",", " "], {}),
},
"optional": {
"target1": ("STRING", {"default":'0,1,-1'}),
"target2": ("STRING", {"default":'0,1,-1'}),
"target3": ("STRING", {"default":'0,1,-1'})
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("parsed_string",)
CATEGORY = "utils"
FUNCTION = "exec"
OUTPUT_NODE = True
def IS_CHANGED(self, string, delimiter, target1=None, target2=None, target3=None):
targets = [target1,target2,target3]
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(Util.parseCompactInputs(targets))]
#Main Function
def exec(self, string, delimiter, target1=None, target2=None, target3=None):
targets = [target1,target2,target3]
return Util.common_string_repo_function(string,delimiter,Util.parseCompactInputs(targets))
class SimpleStringRepository:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"string": ("STRING", {"multiline": True}),
"delimiter": ([",", " "], {}),
},
"optional": {
"target1": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength1": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random1": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target2": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength2": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random2": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target3": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength3": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random3": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target4": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength4": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random4": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target5": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength5": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random5": ("INT", {"default":-1,"min": -1,"max": 1000}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("parsed_string",)
CATEGORY = "utils"
FUNCTION = "exec"
OUTPUT_NODE = True
def IS_CHANGED(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
target3=None, strength3=None, random3=None, target4=None, strength4=None, random4=None, target5=None, strength5=None, random5=None):
parsedInputs = [
{"t":target1,"s":strength1,"r":random1},
{"t":target2,"s":strength2,"r":random2},
{"t":target3,"s":strength3,"r":random3},
{"t":target4,"s":strength4,"r":random4},
{"t":target5,"s":strength5,"r":random5}
]
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(parsedInputs)]
#Main Function
def exec(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
target3=None, strength3=None, random3=None, target4=None, strength4=None, random4=None, target5=None, strength5=None, random5=None):
#Parse inputs for clarity
parsedInputs = [
{"t":target1,"s":strength1,"r":random1},
{"t":target2,"s":strength2,"r":random2},
{"t":target3,"s":strength3,"r":random3},
{"t":target4,"s":strength4,"r":random4},
{"t":target5,"s":strength5,"r":random5}
]
return Util.common_string_repo_function(string,delimiter,parsedInputs)
class SimpleStringRepositoryCompact:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"string": ("STRING", {"multiline": True}),
"delimiter": ([",", " "], {}),
},
"optional": {
"target1": ("STRING", {"default":'0,1,-1'}),
"target2": ("STRING", {"default":'0,1,-1'}),
"target3": ("STRING", {"default":'0,1,-1'}),
"target4": ("STRING", {"default":'0,1,-1'}),
"target5": ("STRING", {"default":'0,1,-1'})
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("parsed_string",)
CATEGORY = "utils"
FUNCTION = "exec"
OUTPUT_NODE = True
def IS_CHANGED(self, string, delimiter, target1=None, target2=None, target3=None, target4=None, target5=None):
targets = [target1,target2,target3,target4,target5]
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(Util.parseCompactInputs(targets))]
#Main Function
def exec(self, string, delimiter, target1=None, target2=None, target3=None, target4=None, target5=None):
targets = [target1,target2,target3,target4,target5]
return Util.common_string_repo_function(string,delimiter,Util.parseCompactInputs(targets))
class SimpleStringRepositoryLarge:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"string": ("STRING", {"multiline": True}),
"delimiter": ([",", " "], {}),
},
"optional": {
"target1": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength1": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random1": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target2": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength2": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random2": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target3": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength3": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random3": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target4": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength4": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random4": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target5": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength5": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random5": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target6": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength6": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random6": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target7": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength7": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random7": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target8": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength8": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random8": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target9": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength9": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random9": ("INT", {"default":-1,"min": -1,"max": 1000}),
"target10": ("INT", {"default":None,"min": 0,"max": 100000000}),
"strength10": ("FLOAT", {"default":1.0,"min": 0,"max": 10}),
"random10": ("INT", {"default":-1,"min": -1,"max": 1000}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("parsed_string",)
CATEGORY = "utils"
FUNCTION = "exec"
OUTPUT_NODE = True
def IS_CHANGED(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
target3=None, strength3=None, random3=None, target4=None, strength4=None, random4=None, target5=None, strength5=None, random5=None,
target6=None, strength6=None, random6=None, target7=None, strength7=None, random7=None,
target8=None, strength8=None, random8=None, target9=None, strength9=None, random9=None, target10=None, strength10=None, random10=None):
parsedInputs = [
{"t":target1,"s":strength1,"r":random1},
{"t":target2,"s":strength2,"r":random2},
{"t":target3,"s":strength3,"r":random3},
{"t":target4,"s":strength4,"r":random4},
{"t":target5,"s":strength5,"r":random5},
{"t":target6,"s":strength6,"r":random6},
{"t":target7,"s":strength7,"r":random7},
{"t":target8,"s":strength8,"r":random8},
{"t":target9,"s":strength9,"r":random9},
{"t":target10,"s":strength10,"r":random10}
]
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(parsedInputs)]
#Main Function
def exec(self, string, delimiter, target1=None, strength1=None, random1=None, target2=None, strength2=None, random2=None,
target3=None, strength3=None, random3=None, target4=None, strength4=None, random4=None, target5=None, strength5=None, random5=None,
target6=None, strength6=None, random6=None, target7=None, strength7=None, random7=None,
target8=None, strength8=None, random8=None, target9=None, strength9=None, random9=None, target10=None, strength10=None, random10=None):
#Parse inputs for clarity
parsedInputs = [
{"t":target1,"s":strength1,"r":random1},
{"t":target2,"s":strength2,"r":random2},
{"t":target3,"s":strength3,"r":random3},
{"t":target4,"s":strength4,"r":random4},
{"t":target5,"s":strength5,"r":random5},
{"t":target6,"s":strength6,"r":random6},
{"t":target7,"s":strength7,"r":random7},
{"t":target8,"s":strength8,"r":random8},
{"t":target9,"s":strength9,"r":random9},
{"t":target10,"s":strength10,"r":random10}
]
return Util.common_string_repo_function(string,delimiter,parsedInputs)
class SimpleStringRepositoryLargeCompact:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"string": ("STRING", {"multiline": True}),
"delimiter": ([",", " "], {}),
},
"optional": {
"target1": ("STRING", {"default":'0,1,-1'}),
"target2": ("STRING", {"default":'0,1,-1'}),
"target3": ("STRING", {"default":'0,1,-1'}),
"target4": ("STRING", {"default":'0,1,-1'}),
"target5": ("STRING", {"default":'0,1,-1'}),
"target6": ("STRING", {"default":'0,1,-1'}),
"target7": ("STRING", {"default":'0,1,-1'}),
"target8": ("STRING", {"default":'0,1,-1'}),
"target9": ("STRING", {"default":'0,1,-1'}),
"target10": ("STRING", {"default":'0,1,-1'})
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("parsed_string",)
CATEGORY = "utils"
FUNCTION = "exec"
OUTPUT_NODE = True
def IS_CHANGED(self, string, delimiter, target1=None, target2=None, target3=None, target4=None, target5=None,
target6=None, target7=None, target8=None, target9=None, target10=None):
targets = [target1,target2,target3,target4,target5,target6,target7,target8,target9,target10]
return (True, float("NaN"))[Util.areThereTargetsWithRandomness(Util.parseCompactInputs(targets))]
#Main Function
def exec(self, string, delimiter, target1=None, target2=None, target3=None, target4=None, target5=None,
target6=None, target7=None, target8=None, target9=None, target10=None):
targets = [target1,target2,target3,target4,target5,target6,target7,target8,target9,target10]
return Util.common_string_repo_function(string,delimiter,Util.parseCompactInputs(targets))
+149
View File
@@ -0,0 +1,149 @@
import re
import random
class Util:
@staticmethod
def var_dump(var, prefix=''):
"""
You know you're a php developer when the first thing you ask for
when learning a new language is 'Where's var_dump?????'
"""
my_type = '[' + var.__class__.__name__ + '(' + str(len(var)) + ')]:'
print(prefix, my_type, sep='')
prefix += ' '
for i in var:
if type(i) in (list, tuple, dict, set):
Util.var_dump(i, prefix)
else:
if isinstance(var, dict):
print(prefix, i, ': (', var[i].__class__.__name__, ') ', var[i], sep='')
else:
print(prefix, '(', i.__class__.__name__, ') ', i, sep='')
@staticmethod
def removing_leading_whitespaces(text):
"""
Remove leading whitespaces
"""
return re.sub(r"^\s+","",text)
@staticmethod
def areThereTargetsWithRandomness(inputs):
for tuple in inputs:
if(tuple["r"] == -1 and tuple["t"] != None):
return True
return False
@staticmethod
def parseCompactInputs(inputs):
newInputs = []
for string in inputs:
parsedString = string.split(',')
newTuple = {
"t":int(parsedString[0])
}
if(len(parsedString) >= 2):
newTuple["s"] = float(parsedString[1])
else:
newTuple["s"] = 1.0
if(len(parsedString) >= 3):
newTuple["r"] = int(parsedString[2])
else:
newTuple["r"] = -1
newInputs.append(newTuple)
return newInputs
@staticmethod
def common_string_repo_function(string, delimiter, inputs):
"""
Common function which is actually the string repo main function.
The classes are just wrappers around it with a different number of similar inputs.
"""
orphanStrings = []
newDict = {}
parsingIndex = 0
parsedString = string.splitlines()
# Parse all strings, separated by newline.
# A properly formatted string should either have ALL strings with unique numeric identifiers, or none at all.
# Strings without a leading number will be assigned one based on their line number.
# In case of a mix of both, unexpected behavior may occur - strings without an identifier will be assigned one, and subsequent duplicate numeric identifiers are ignored.
for line in parsedString:
existingId = None
currentString = None
parsingIndex += 1
parsedLine = line.split(".")
if(len(parsedLine) != 1):
existingId = parsedLine[0]
currentString = parsedLine[1]
else:
currentString = parsedLine[0]
Util.removing_leading_whitespaces(currentString)
if(not existingId):
existingId = parsingIndex
existingId = int(existingId)
if existingId in newDict:
orphanStrings.extend((currentString,))
else:
newDict[existingId] = currentString
# Debug:
# print("Dict:")
# for k,v in newDict.items():
# print(k, "corresponds to", v)
# print("Orphans:")
# for v in orphanStrings:
# print(v)
#Parse all inputs
finalString = ''
for tuple in inputs:
#Target does not exist!
if(tuple["t"] not in newDict):
# Debug:
#print("target", tuple["t"], "does not exist!")
continue
#Target exists!
baseStr = newDict[tuple["t"]]
#Handle randomness, if applicable
if(re.fullmatch("^{.+}$",baseStr)):
baseStr = baseStr[1:][:-1]
baseStr = baseStr.split("|")
if(tuple["r"] == -1):
tuple["r"] = random.randint(0, len(baseStr)-1)
if(tuple["r"] >= len(baseStr)):
# Debug:
#print("Random string ", tuple["r"], "in target",tuple["t"],baseStr, "does not exist!")
continue
baseStr = baseStr[tuple["r"]]
#Handle Strength
if(not tuple["s"]):
# Debug:
#print("Target",tuple["t"], "Has strength 0")
continue
elif(tuple["s"] != 1):
tuple["s"] = round(tuple["s"],2)
baseStr = f'({baseStr}:{tuple["s"]})'
finalString += baseStr + delimiter
finalString = finalString[:-1]
# Debug:
#print(finalString)
return {"ui": {"text": (finalString,)}, "result": (finalString,)}
+14
View File
@@ -0,0 +1,14 @@
[project]
name = "comfyui_simple_string_repository"
description = "Custom node that allows storing and accessing strings, meant to be parts of a prompt, in a simplified manner. Partially supports dynamic prompt syntax."
version = "1.0.0"
license = "LICENSE"
[project.urls]
Repository = "https://github.com/IgalOgonov/ComfyUI_Simple_String_Repository"
# Used by Comfy Registry https://comfyregistry.org
[tool.comfy]
PublisherId = "io"
DisplayName = "Simple String Repository"
Icon = ""
+94
View File
@@ -0,0 +1,94 @@
import { app } from "../../../scripts/app.js";
import { ComfyWidgets } from "../../../scripts/widgets.js";
// Displays input text on a node
app.registerExtension({
name: "io.SimpleStringRepository",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if(nodeType?.comfyClass.startsWith('SimpleStringRepository')){
//TODO Perhaps add stuff
}
},
async nodeCreated(node, app) {
let nodeClass= node?.comfyClass;
if(nodeClass.startsWith('SimpleStringRepository')){
let isCompact = node?.comfyClass.endsWith('Compact');
let extractParsedStrings = ()=>{
let str_widget = node.widgets[0];
let str = str_widget.value;
return str.split('\n');
}
//Adds numerical indexes to strings, does not destroy or change existing ones.
node.addWidget("button", "✍ Add Missing Indexes", null, () => {
let currentStrings = extractParsedStrings();
let map = {}
let orphans = {};
for(let i in currentStrings){
let index = null;
let str = currentStrings[i];
if(str.includes('.')){
index = str.split('.')[0];
str = str.split('.')[1];
}
if(!index || map[index])
orphans[i] = str;
else
map[index] = str;
}
let newIndex = 1;
for(let i in orphans){
while(map[newIndex] !== undefined)
newIndex++;
currentStrings[i] = newIndex + '.' + orphans[i];
map[newIndex] = orphans[i];
}
node.widgets[0].value = currentStrings.join('\n');
}, { serialize: false });
//Regenerates all string indexes, potentially changing existing ones.
node.addWidget("button", "⚠ Regenerate All Indexes", null, () => {
let currentStrings = extractParsedStrings();
for(let i in currentStrings){
if(currentStrings[i].includes('.'))
currentStrings[i] = currentStrings[i].split('.')[1];
currentStrings[i] = (i-0+1) +'.'+ currentStrings[i];
}
node.widgets[0].value = currentStrings.join('\n');
}, { serialize: false });
//Strips all strings of their indexes
node.addWidget("button", "❌ Remove All Indexes", null, () => {
let currentStrings = extractParsedStrings();
for(let i in currentStrings){
if(currentStrings[i].includes('.'))
currentStrings[i] = currentStrings[i].split('.')[1];
}
node.widgets[0].value = currentStrings.join('\n');
}, { serialize: false });
//Resets Parameters (Target/Strength/Randomness)
node.addWidget("button", "↺ Reset Target Parameters", null, () => {
for(let i in node.widgets){
let widget = node.widgets[i];
switch(isCompact){
case true:
if(widget.name.match(/^target\d+$/))
widget.value = '0,1,-1';
break;
case false:
if(widget.name.match(/^target\d+$/))
widget.value = 0;
if(widget.name.match(/^strength\d+$/))
widget.value = 1;
if(widget.name.match(/^random\d+$/))
widget.value = -1;
break;
}
}
}, { serialize: false });
}
},
});