Files
arcum42-ComfyUI_SageUtils/nodes/text.py
T

492 lines
31 KiB
Python

# Text nodes.
# This contains any nodes that are dealing with text, including setting text, joining text, cleaning text, and viewing text.
from __future__ import annotations
from comfy.comfy_types.node_typing import ComfyNodeABC, InputTypeDict, IO
import random
# Import specific utilities instead of wildcard import
from ..utils import (
clean_text, get_save_file_path,
sage_wildcard_path,
path_manager
)
from dynamicprompts.generators import RandomPromptGenerator
from dynamicprompts.wildcards.wildcard_manager import WildcardManager
class SageSetWildcardText(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"optional": {
"prefix": (IO.STRING, {"defaultInput": True, "multiline": True}),
"suffix": (IO.STRING, {"defaultInput": True, "multiline": True})
},
"required": {
"str": (IO.STRING, {"forceInput": False, "dynamicPrompts": True, "multiline": True}),
"seed": (IO.INT, {"defaultInput": False, "default": 0, "min": 0, "max": 2**32-1, "step": 1}),
"clean": (IO.BOOLEAN, {"defaultInput": False, "default": False})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("str",)
FUNCTION = "set_wildcard_text"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Loads user defined wildcard from the wildcards directory, and applies them to any wildcards in the text."
def set_wildcard_text(self, str, prefix=None, suffix=None, seed=0, clean = False) -> tuple[str]:
"""
Sets the text with wildcards replaced by their values.
"""
str = f"{prefix or ''}{str}{suffix or ''}"
# Initialize the wildcard manager
wildcard_manager = WildcardManager(sage_wildcard_path)
# Generate a random prompt using the wildcard manager
generator = RandomPromptGenerator(wildcard_manager, seed=seed)
# Replace wildcards in the string
str = generator.generate(str)[0]
# Clean the string if requested
if clean:
str = clean_text(str)
return (str,)
class Sage_SetText(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"optional": {
"prefix": (IO.STRING, {"defaultInput": True, "multiline": True}),
"suffix": (IO.STRING, {"defaultInput": True, "multiline": True})
},
"required": {
"str": (IO.STRING, {"forceInput": False, "dynamicPrompts": True, "multiline": True})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("str",)
FUNCTION = "pass_str"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Sets some text."
def pass_str(self, str, prefix=None, suffix=None) -> tuple[str]:
return (f"{prefix or ''}{str}{suffix or ''}",)
class Sage_TextSwitch(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"str": (IO.STRING, {"forceInput": True, "multiline": True}),
"active": (IO.BOOLEAN, {"defaultInput": False, "default": True})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("str",)
FUNCTION = "text_switch"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Passes the text if active is true, otherwise passes an empty string."
def text_switch(self, str: str, active: bool) -> tuple[str]:
return (str if active else "",)
class Sage_SaveText(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"filename_prefix": (IO.STRING, {"default": "ComfyUI_Text", "tooltip": "The prefix for the file to save. This may include formatting information such as %date:yyyy-MM-dd% to include values from nodes."}),
"file_extension": (IO.STRING, {"defaultInput": False, "default": "txt", "tooltip": "The file extension to use for the saved file."}),
"text": (IO.STRING, {"forceInput": True, "multiline": True})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("filepath",)
FUNCTION = "save_text"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Saves the text to a file."
def save_text(self, filename_prefix: str, file_extension: str, text: str) -> tuple[str]:
if file_extension.startswith('.'):
file_extension = file_extension[1:]
full_path = get_save_file_path(filename_prefix, file_extension)
if not full_path:
raise ValueError("Invalid file path.")
with open(full_path, 'w', encoding='utf-8') as file:
file.write(text)
print(f"Text saved to {full_path}")
return (full_path,)
class Sage_JoinText(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"separator": (IO.STRING, {"defaultInput": False, "default": ', '}),
"add_separator_to_end": (IO.BOOLEAN, {"defaultInput": False, "default": False, "tooltip": "Add separator to the end of the joined string."}),
"str1": (IO.STRING, {"defaultInput": True, "multiline": True}),
"str2": (IO.STRING, {"defaultInput": True, "multiline": True}),
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("str",)
FUNCTION = "join_str"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Joins two strings with a separator."
def join_str(self, separator, add_separator_to_end , str1, str2) -> tuple[str]:
if add_separator_to_end:
# Add separator to the end of the joined string
return (separator.join([str1, str2]) + separator,)
else:
"""
Normal join without adding separator to the end.
"""
# Join the strings with the separator
return (separator.join([str1, str2]),)
class Sage_TripleJoinText(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"separator": (IO.STRING, {"defaultInput": False, "default": ', '}),
"add_separator_to_end": (IO.BOOLEAN, {"defaultInput": False, "default": False, "tooltip": "Add separator to the end of the joined string."}),
"str1": (IO.STRING, {"defaultInput": True, "multiline": True}),
"str2": (IO.STRING, {"defaultInput": True, "multiline": True}),
"str3": (IO.STRING, {"defaultInput": True, "multiline": True})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("str",)
FUNCTION = "join_str"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Joins three strings with a separator."
def join_str(self, separator, add_separator_to_end, str1, str2, str3) -> tuple[str]:
if (add_separator_to_end):
return (separator.join([str1, str2, str3]) + separator,)
else:
return (separator.join([str1, str2, str3]),)
class Sage_CleanText(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"str": (IO.STRING, {"defaultInput": True, "multiline": True}),
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("cleaned_string",)
FUNCTION = "clean_str"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Cleans up the string given."
def clean_str(self, str) -> tuple[str]:
return (clean_text(str),)
class Sage_ViewAnything(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"any": (IO.ANY, {"forceInput": True, "multiline": True})
}
}
RETURN_TYPES = (IO.STRING,)
FUNCTION = "show_text"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Shows some text."
OUTPUT_NODE = True
INPUT_IS_LIST = True
def show_text(self, any) -> dict:
#print(f"Text is '{any}'")
str = ""
if isinstance(any, list):
for t in any:
str += f"{t}\n"
#print(f"String is '{t}'")
else:
str = any
#print(f"String is '{str}'")
return { "ui": {"text": str}, "result" : (str,) }
class Sage_TextRandomLine(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"text": (IO.STRING, {"defaultInput": True, "multiline": True}),
"seed": (IO.INT, {"defaultInput": False, "default": 0, "min": 0, "max": 2**32-1, "step": 1})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("random_line",)
FUNCTION = "get_random_line"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Returns a random line from the given text."
def get_random_line(self, text: str, seed: int) -> tuple[str]:
lines = text.splitlines()
if not lines:
return ("",)
# Set the random seed for reproducibility
random.seed(seed)
random_line = lines[random.randint(0, len(lines) - 1)].strip() # Use seed to select a line
return (random_line,)
class Sage_TextSelectLine(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"text": (IO.STRING, {"defaultInput": True, "multiline": True}),
"line_number": (IO.INT, {"defaultInput": False, "default": 0, "min": 0, "max": 10000, "step": 1})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("selected_line",)
FUNCTION = "select_line"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Selects a specific line from the given text based on the line number. Line numbers start from 0. If the line number is out of range, it will select the first or last line as appropriate."
def select_line(self, text: str, line_number: int) -> tuple[str]:
lines = text.splitlines()
if line_number < 0:
line_number = 0
elif line_number >= len(lines):
line_number = len(lines) - 1
return (lines[line_number].strip(),)
# This node takes a text box (with a prefix and suffix) and three optional strings, and substitutes the strings for the
# placeholders in the text box.
class Sage_TextSubstitution(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"text": (IO.STRING, {"defaultInput": True, "multiline": True})
},
"optional": {
"prefix": (IO.STRING, {"defaultInput": True, "multiline": True}),
"suffix": (IO.STRING, {"defaultInput": True, "multiline": True}),
"str1": (IO.STRING, {"defaultInput": True, "multiline": True}),
"str2": (IO.STRING, {"defaultInput": True, "multiline": True}),
"str3": (IO.STRING, {"defaultInput": True, "multiline": True}),
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("result",)
FUNCTION = "substitute_text"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Substitutes the placeholders in the text with the provided strings. The placeholders are {str1}, {str2}, and {str3}. The prefix and suffix are added to the final result."
def substitute_text(self, text: str, prefix: str = "", suffix: str = "", str1: str = "", str2: str = "", str3: str = "") -> tuple[str]:
"""
Substitutes the placeholders in the text with the provided strings.
The placeholders are {str1}, {str2}, and {str3}.
The prefix and suffix are added to the final result.
"""
# Replace placeholders with actual strings
result = text.format(str1=str1, str2=str2, str3=str3)
# Add prefix and suffix
result = f"{prefix}{result}{suffix}"
return (result,)
class Sage_PonyPrefix(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"add_score": (IO.BOOLEAN, {"defaultInput": False}),
"rating": (["none", "safe", "questionable", "explicit"], {"defaultInput": False}),
"source": (["none", "pony", "furry", "anime", "cartoon", "3d", "western", "comic", "monster"], {"defaultInput": False}),
}
}
RETURN_TYPES = (IO.STRING,)
FUNCTION = "create_prefix"
CATEGORY = "Sage Utils/text"
def create_prefix(self, add_score, rating, source, prompt=None) -> tuple[str]:
prefix = "score_9, score_8_up, score_7_up, score_6_up, score_5_up, score_4_up, " if add_score else ""
prefix += f"source_{source}, " if source != "none" else ""
prefix += f"rating_{rating}, " if rating != "none" else ""
return (prefix,)
class Sage_TextWeight(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"text": (IO.STRING, {"defaultInput": True, "multiline": True}),
"weight": (IO.FLOAT, {"defaultInput": False, "default": 1.0, "min": -10.0, "max": 10.0, "step": 0.05}),
"separator": (IO.STRING, {"defaultInput": False, "default": ', '})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("weighted_text",)
FUNCTION = "weight_text"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Applies a weight to a text string."
def weight_text(self, text: str, weight: float, separator: str) -> tuple[str]:
return (f"({text}:{weight:.2f}){separator}",)
class Sage_HiDreamE1_Instruction(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"instruction": (IO.STRING, {"defaultInput": True, "multiline": True}),
"description": (IO.STRING, {"defaultInput": True, "multiline": True})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("prompt",)
FUNCTION = "generate_prompt"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Generates a prompt for HiDream E1 based on the given instruction and description."
def generate_prompt(self, instruction: str, description: str) -> tuple[str]:
# Clean the instruction and description
cleaned_instruction = clean_text(instruction)
cleaned_description = clean_text(description)
if not cleaned_instruction:
raise ValueError("Instruction cannot be empty.")
elif not cleaned_description:
raise ValueError("Description cannot be empty.")
elif cleaned_instruction[-1] != ".":
print(f"Last character of instruction is '{cleaned_instruction[-1]}'")
cleaned_instruction += "."
# Generate the prompt
prompt = f"Instruction: {cleaned_instruction}\nDescription: {cleaned_description}"
return (prompt,)
pony_strings = ["aav", "aax", "aba", "aca", "acb", "acl", "acm", "acs", "aee", "aef", "aek", "aer", "aet", "aeu", "aew", "aex", "aey", "aff", "aga", "agi", "ago", "ahk", "ahl", "ahz", "aij", "ain", "aiu", "ajm", "aju", "ajy", "akd", "ake", "aki", "akk", "akm", "akr", "aku", "ali", "alp", "amu", "ana", "ani", "anu", "aoa", "aob", "aoj", "aov", "aox", "aoy", "api", "apm", "apo", "aqe", "aqg", "aqu", "aqx", "arb", "aro", "asa", "asm", "asn", "aso", "aua", "aur", "auv", "awd", "awf", "awm", "awv", "axp", "ayb", "ayl", "ayp", "ayq", "ayv", "ayw", "ayy", "aze", "azv", "baf", "bbq", "bcg", "bdc", "bdr", "bem", "bfb", "bfg", "bfq", "bfu", "bfv", "bgf", "bgk", "bgn", "bgv", "bha", "bhb", "bhl", "bhr", "bhz", "bif", "bih", "bim", "bip", "biy", "bjp", "bke", "bkm", "bks", "bku", "bkx", "bna", "bnp", "bnv", "bol", "bom", "bor", "bou", "bpb", "bpc", "bpw", "bpx", "brd", "brk", "brl", "brn", "brp", "brr", "brs", "brw", "brx", "bry", "brz", "bse", "bsl", "bsv", "bub", "bur", "bvk", "bvm", "bvq", "bwf", "bwl", "bwt", "bwu", "bwy", "bxh", "bys", "bzi", "bzl", "bzm", "cad", "cak", "cbr", "cbu", "cch", "cdr", "cds", "cgv", "chl", "ciu", "cle", "cln", "cly", "coh", "coi", "coy", "coz", "crb", "crr", "csb", "csf", "csz", "cte", "cwn", "cxd", "cxg", "cxh", "cxl", "cxw", "cxz", "cyq", "cyu", "czi", "dap", "dbg", "dbj", "dbu", "dbw", "dcd", "dce", "dch", "dck", "ddb", "ddk", "ddp", "deh", "dfd", "dfe", "dfk", "dfm", "dfo", "dhg", "dhl", "dih", "dit", "dja", "djv", "dkd", "dkg", "dki", "dko", "dkr", "dks", "dkt", "dku", "dkv", "dkw", "dky", "dlv", "dmb", "dmf", "dmg", "dmj", "dmk", "dmp", "dnw", "dpa", "dpb", "dpc", "dpf", "dph", "dpj", "dpk", "dpn", "dpo", "dpz", "dsh", "dsk", "dso", "dtb", "dtc", "dtd", "dth", "dtt", "dtu", "dtv", "dty", "dtz", "dvs", "dwc", "dwn", "dww", "dwx", "dwy", "dxo", "dxs", "dxv", "dyu", "dyv", "dza", "dze", "dze", "ebo", "ebp", "ebu", "efk", "egb", "egb", "egk", "egv", "egx", "ehb", "ehf", "ehh", "ehr", "ehx", "ehz", "eim", "ejt", "eka", "eke", "eki", "eky", "ela", "ema", "emc", "eoa", "eob", "eod", "eou", "eov", "eoy", "eqb", "eqc", "eqg", "eqr", "eqt", "eti", "euk", "eum", "evg", "ewi", "ewo", "ewu", "ewy", "exl", "eza", "ezo", "ezy", "fai", "fay", "fbg", "fbu", "fbv", "fbw", "fdv", "fdw", "fdz", "fei", "fem", "fey", "ffs", "fgd", "fgk", "fgq", "fgv", "fgz", "fhb", "fhl", "fhy", "fii", "fjt", "fju", "fjv", "fjx", "fke", "fkm", "fku", "fkw", "fla", "fln", "fpb", "fpw", "fpx", "fpz", "fqx", "fru", "frv", "frw", "fsb", "fsd", "fsf", "fso", "fsp", "fsv", "fvb", "fvd", "fvm", "fvn", "fvs", "fvv", "fvx", "fwh", "fwt", "fwx", "fwy", "fxc", "fxd", "fxv", "fyu", "fyx", "fyy", "fzj", "fzl", "fzm", "fzv", "fzw", "gad", "gaf", "gar", "gax", "gbu", "gcd", "gcg", "gch", "gcx", "gdr", "gea", "ght", "gjt", "gjv", "gjw", "gkb", "gkr", "gmq", "gmz", "goj", "gom", "gor", "gou", "gpc", "gpj", "gpn", "gpo", "gpw", "gpx", "grb", "grp", "grt", "gsb", "gsf", "gsh", "gsu", "gtv", "gtz", "gvb", "gvt", "gwg", "gwh", "gwl", "gwm", "gwt", "gwv", "gwy", "gwz", "gxh", "gxm", "gyy", "gzl", "gzm", "gzr", "gzw", "hag", "hai", "haj", "haz", "hbz", "hcd", "hch", "hda", "hdr", "hep", "hga", "hgt", "hgv", "hij", "hik", "hiq", "hiu", "hjt", "hka", "hke", "hki", "hku", "hlg", "hlk", "hll", "hlt", "hlu", "hmj", "hmp", "hna", "hnj", "hns", "hnu", "hpb", "hpw", "hpx", "hqr", "hsk", "hsn", "htm", "htv", "hua", "hui", "hvi", "hvy", "hwa", "hwd", "hwh", "hwj", "hwl", "hwu", "hwv", "hwz", "hxh", "hya", "hzj", "hzl", "hzm", "hzt", "iao", "iaw", "ibw", "idz", "ieb", "iee", "iel", "iew", "ifl", "iga", "igh", "igo", "igu", "iha", "ihb", "ihc", "ihh", "ihl", "iho", "ihp", "ihr", "ihv", "ihw", "ihz", "iia", "iim", "iin", "iio", "iiy", "ijb", "ijd", "ije", "ijg", "ijh", "iji", "ijk", "ijl", "ijm", "ijp", "ijq", "ijs", "ijv", "ijw", "ijx", "ijy", "ijz", "ikf", "ikm", "ikp", "iku", "iky", "ilb", "ilg", "ilp", "ilr", "ima", "imf", "imo", "inc", "ior", "ipi", "iqt", "iri", "iro", "iru", "iry", "ito", "iuc", "iud", "iue", "iui", "iuk", "iun", "ivh", "ivm", "iwg", "iwj", "iwl", "iwo", "iwp", "iwt", "iwu", "iwv", "iww", "iwy", "ixb", "ixe", "ixz", "iyb", "iyi", "iyo", "iyu", "jaf", "jah", "jaj", "jap", "jbc", "jbg", "jbj", "jbm", "jcd", "jch", "jcp", "jcy", "jdd", "jdg", "jds", "jel", "jfa", "jfb", "jfe", "jfm", "jfn", "jgd", "jgk", "jgm", "jhp", "jhy", "jio", "jju", "jjv", "jjz", "jke", "jkg", "jki", "jkv", "jkw", "jlk", "jln", "jlv", "jme", "jmf", "jmj", "jms", "jmv", "jnj", "jnl", "jpo", "jpw", "jpx", "jqr", "jrm", "jrn", "jrq", "jru", "jsf", "jsm", "jso", "jst", "jsv", "jtj", "jtm", "jtv", "juh", "jui", "jun", "jvb", "jvi", "jvj", "jvm", "jvn", "jvs", "jwh", "jwl", "jwt", "jwv", "jww", "jxd", "jxh", "jxm", "jyk", "jza", "jzd", "jze", "jzg", "jzj", "jzl", "jzm", "jzp", "kab", "kcd", "kch", "kdg", "kdk", "kdr", "kds", "kga", "kgd", "kgq", "kgv", "kgw", "khq", "kib", "kig", "kih", "kjt", "kjw", "klg", "kll", "klm", "kln", "klo", "kmj", "kmp", "kmq", "kmu", "kmw", "kmz", "kna", "koi", "koo", "kou", "kpb", "kpl", "kpm", "kpw", "kqr", "kqx", "ksb", "ksd", "ksf", "ksg", "ksh", "kuh", "kuu", "kvk", "kvl", "kvm", "kvx", "kwl", "kws", "kwv", "kwy", "kxf", "kxg", "kxl", "kxm", "kyg", "kyy", "kzf", "kzg", "kzl", "kzm", "kzr", "kzs", "kzt", "kzw", "lap", "lbi", "lbj", "lbk", "lbo", "lbp", "lbq", "lbu", "lbv", "lbw", "lcf", "lcm", "lcn", "lcp", "lcv", "ldu", "ldv", "lek", "lfh", "lgu", "lgv", "lhb", "lhc", "lhh", "lhy", "lia", "ljw", "lkb", "lkf", "lkg", "lkr", "llq", "lmb", "lml", "lmx", "lmy", "lmz", "lnf", "lnh", "lnp", "lnq", "lnv", "lnw", "loi", "lox", "lpb", "lpc", "lpm", "lpn", "lpt", "lpw", "lpx", "lqf", "lql", "lqx", "lrl", "lru", "lsc", "lsf", "lte", "ltr", "ltv", "lus", "lux", "luz", "lvm", "lvu", "lwb", "lwh", "lwl", "lwn", "lwq", "lwu", "lwy", "lwz", "lxb", "lxh", "lym", "lyn", "lyr", "lzg", "lzj", "lzl", "lzt", "lzy", "lzz", "mbb", "mbg", "mbo", "mdf", "mdg", "mdh", "mdl", "mdo", "mdr", "mdv", "mdw", "met", "mey", "mha", "mhb", "mhf", "mhg", "mhj", "mhk", "mhp", "mhv", "mhx", "mhy", "mii", "mio", "mjb", "mjm", "mjy", "mkb", "mkg", "mkl", "mkx", "mlx", "mmo", "mmr", "moc", "mpa", "mpf", "mph", "mpj", "mpk", "mpl", "mpn", "mpq", "mpr", "mpt", "mpu", "mpv", "mpw", "mpz", "mru", "msh", "msy", "mtd", "muh", "mui", "mul", "mup", "mur", "muu", "muy", "mwb", "mwf", "mwi", "mwn", "mwq", "mwt", "mwz", "mxj", "mxu", "myr", "myu", "mzg", "nan", "nar", "nax", "nbg", "nbi", "ncb", "ncc", "ncd", "nch", "ncl", "ncp", "ncv", "ncx", "nda", "ndr", "ndx", "nev", "nfd", "ngv", "nhd", "nhk", "nhp", "nhu", "nhv", "nhz", "nia", "nie", "nii", "nin", "nir", "nis", "niu", "nke", "nkf", "nki", "nkk", "nko", "nku", "nkv", "nkw", "nlo", "nlv", "nmb", "nmp", "nmu", "nmz", "nna", "nox", "npb", "npn", "npw", "npx", "nqr", "nqx", "nrf", "nrg", "nrh", "nsb", "nsc", "ntd", "nto", "nts", "ntu", "ntv", "ntz", "nvg", "nvi", "nvj", "nvk", "nvl", "nvo", "nvu", "nvv", "nwm", "nwn", "nwy", "nyi", "nyj", "nyk", "nyp", "nyr", "nyy", "nzb", "nzo", "oaa", "oat", "oav", "oax", "obo", "obu", "oca", "ode", "odh", "odk", "odl", "odp", "odr", "oee", "oel", "oey", "ofa", "ofp", "oge", "ogf", "ogk", "ogl", "ogr", "ogv", "oha", "ohg", "ohv", "ohw", "oia", "oib", "oih", "oii", "oim", "oip", "oir", "oix", "ojb", "ojn", "ojt", "ojv", "ojw", "oka", "okf", "olu", "ome", "omi", "omo", "omu", "omv", "onz", "ooh", "oou", "oov", "opb", "opg", "opk", "opl", "opq", "opv", "opw", "orh", "ori", "ose", "ota", "ott", "otv", "oue", "owb", "owf", "owg", "owh", "owi", "owz", "oxz", "oya", "oyj", "oym", "oyq", "oyu", "oyv", "oyy", "oyz", "oza", "ozo", "paf", "pag", "par", "pbc", "pbi", "pbv", "pbw", "pcd", "pdg", "pdk", "pdl", "pdn", "pdo", "pgm", "pgw", "pha", "phy", "pjy", "pkm", "pku", "pln", "pme", "pmj", "pmk", "pml", "pmp", "pnf", "pon", "poo", "ppp", "pri", "psf", "psm", "psp", "ptj", "pvo", "pvs", "pwh", "pwl", "pwn", "pwt", "pwy", "pxg", "pxh", "pxo", "pyb", "pyh", "pyq", "pyy", "pyz", "pzl", "pzm", "pzp", "pzw", "qag", "qak", "qar", "qaw", "qaz", "qbg", "qbu", "qbv", "qbw", "qbx", "qcd", "qch", "qci", "qcq", "qcy", "qcz", "qdc", "qdg", "qdk", "qdl", "qdr", "qgk", "qgm", "qgq", "qgs", "qgv", "qgy", "qgz", "qha", "qhb", "qhh", "qhp", "qhr", "qhy", "qhz", "qia", "qji", "qjl", "qjt", "qju", "qjv", "qjw", "qjx", "qjy", "qkp", "qkr", "qlh", "qlj", "qlt", "qmj", "qml", "qmp", "qmu", "qnj", "qob", "qoc", "qoe", "qoy", "qoz", "qpb", "qpp", "qpw", "qpx", "qqf", "qqr", "qqt", "qqv", "qqx", "qri", "qrj", "qrk", "qrp", "qru", "qsf", "qsv", "qtj", "qvj", "qvn", "qwg", "qwh", "qwl", "qwn", "qwt", "qwu", "qwv", "qwy", "qxh", "qxm", "qxq", "qxs", "qym", "qyp", "qyt", "qzl", "qzm", "qzo", "qzt", "rak", "rbh", "rbi", "rbj", "rbm", "rbq", "rbv", "rbw", "rbx", "rbz", "rcd", "rcf", "rch", "rea", "rek", "rga", "rgx", "rha", "rhc", "rhh", "rhn", "rhv", "riu", "rjg", "rjt", "rjy", "rjz", "rkf", "rkg", "rkq", "rkx", "rmv", "roc", "rou", "rov", "rpw", "rpy", "rpz", "rra", "rrg", "rsd", "rsl", "rsn", "rss", "rui", "rup", "rwy", "rxb", "rxg", "rxh", "rxj", "rxk", "rxw", "rxz", "ryb", "ryn", "rys", "rza", "rzj", "rzl", "rzm", "sae", "saz", "sbk", "sbl", "sdr", "seb", "seu", "sfv", "sfw", "sfy", "sgh", "sgy", "sha", "shq", "sht", "shu", "sid", "sij", "siu", "sjb", "sjc", "sjd", "sje", "sjf", "sjg", "sjh", "sji", "sjj", "sjk", "sjl", "sjm", "sjp", "sjq", "sjs", "sjt", "sju", "sjv", "sjw", "sjx", "sjy", "sjz", "skd", "sko", "sku", "slu", "sme", "smf", "smg", "smh", "smj", "smk", "smk", "sml", "smn", "smp", "smr", "smv", "smz", "sog", "soh", "soi", "soj", "sot", "sou", "spe", "sph", "srf", "srg", "srn", "srr", "srs", "sru", "srv", "srx", "ssp", "stj", "stk", "sud", "swf", "swg", "swl", "sww", "syn", "syu", "szo", "szw", "taj", "tal", "tat", "tcg", "tcj", "tcl", "tcv", "tdc", "tdj", "tdr", "tds", "tdz", "tet", "tfv", "tgt", "thn", "tir", "tiv", "tjt", "tju", "tke", "tkw", "tle", "tlv", "tmu", "tnb", "tnj", "tnl", "tnn", "tnp", "tnr", "tnu", "tnv", "tnw", "tpa", "tpb", "tpc", "tpn", "tpw", "tpx", "tqx", "tsu", "ttp", "tvg", "tvu", "tvx", "twb", "twi", "twu", "tww", "tyb", "tyr", "tyv", "uaa", "uab", "uag", "uai", "uan", "uao", "uap", "uar", "uaw", "uaz", "ube", "ubg", "ubj", "ubk", "ubv", "ubw", "uca", "uch", "uco", "ucs", "udr", "uds", "uea", "uee", "uef", "ufa", "ufb", "ufd", "ufg", "ufo", "ufs", "ufv", "ufw", "ufy", "uga", "ugu", "ugy", "uha", "uhf", "uhi", "uhl", "uhp", "uhr", "uhy", "uie", "uim", "uio", "uip", "uiw", "uix", "ujf", "ujg", "uji", "ujj", "ujn", "ujs", "ujt", "uju", "ujw", "ujx", "ujy", "uks", "ula", "ulb", "ulc", "ulg", "ulh", "ulj", "ulk", "ulm", "uln", "ulp", "ulq", "ulr", "uls", "ulv", "ulw", "ulx", "ulz", "umb", "ume", "umf", "umh", "umj", "umk", "uml", "umn", "umo", "ump", "umr", "ums", "umv", "umx", "umy", "uno", "uob", "uoe", "uog", "uop", "uou", "uov", "uoy", "uoz", "upl", "uqa", "uqb", "uqc", "uqi", "uqt", "uqx", "ura", "urd", "uru", "usu", "utu", "uua", "uub", "uuc", "uud", "uue", "uuf", "uuh", "uui", "uuk", "uum", "uun", "uuq", "uva", "uvb", "uvd", "uvi", "uvm", "uvo", "uvs", "uvt", "uvy", "uwe", "uwh", "uwl", "uwo", "uwp", "uws", "uws", "uwt", "uwy", "uxd", "uxi", "uyd", "uye", "uyf", "uym", "uyz", "uzo", "uzu", "uzv", "uzw", "vag", "var", "vbb", "vbg", "vbi", "vbm", "vbu", "vcd", "vch", "vcv", "vdc", "vdl", "vdq", "vdr", "ven", "vew", "vex", "vey", "vfc", "vfe", "vgf", "vgo", "vgv", "vgx", "vgy", "vhb", "vhr", "vhv", "vhy", "vim", "viv", "vix", "vjb", "vjt", "vke", "vlh", "vlj", "vln", "vlv", "vmj", "vml", "vmz", "vna", "voc", "vpb", "vph", "vpw", "vrj", "vrn", "vrv", "vsh", "vso", "vtd", "vtv", "vud", "vui", "vuj", "vuk", "vum", "vun", "vvi", "vwh", "vwl", "vxh", "vxi", "vxv", "vyv", "vzl", "vzm", "vzo", "vzp", "wau", "wav", "wba", "wbi", "wbs", "wbu", "wcd", "wcy", "wda", "wdr", "wew", "wfa", "wfg", "wfj", "wfk", "wfm", "wfw", "wfy", "wgf", "wgg", "wgi", "wgm", "wgs", "wgv", "wha", "wiz", "wjd", "wjt", "wju", "wjv", "wke", "wko", "wkx", "wli", "wlk", "wlt", "wlv", "wlz", "wma", "wmb", "wmf", "wmg", "wmj", "wmk", "wmp", "wms", "wmv", "wmw", "wnp", "wnv", "wnw", "woi", "woj", "wou", "wov", "woy", "wpa", "wpb", "wpc", "wpf", "wpl", "wpo", "wpp", "wps", "wpt", "wpw", "wpx", "wqb", "wqr", "wrl", "wry", "wsb", "wsf", "wsn", "wsp", "wsv", "wtd", "wti", "wtr", "wtv", "wtw", "wuk", "wun", "wva", "wvb", "wve", "wvi", "wvy", "wwd", "wwn", "wwv", "wwy", "wxg", "wxh", "wxi", "wxj", "wxr", "wxu", "wxw", "wxz", "wyy", "wzg", "wzi", "wzm", "wzp", "wzp", "wzq", "wzu", "wzw", "wzx", "xag", "xar", "xaz", "xbi", "xbm", "xbu", "xbw", "xcd", "xch", "xcq", "xdr", "xds", "xfy", "xgm", "xgq", "xhb", "xhh", "xie", "xih", "xii", "xij", "xik", "xio", "xiq", "xiu", "xiv", "xjw", "xkg", "xkk", "xkl", "xkq", "xku", "xlh", "xlv", "xlw", "xlx", "xmj", "xob", "xoi", "xoy", "xpb", "xph", "xpk", "xpn", "xpw", "xqx", "xrj", "xrl", "xru", "xrw", "xsb", "xsd", "xsh", "xsl", "xtd", "xtj", "xuc", "xui", "xuo", "xvj", "xwg", "xwj", "xwp", "xwt", "xwu", "xwv", "xwy", "xxb", "xxi", "xyu", "xyy", "xzb", "xzf", "xzi", "xzj", "xzl", "xzo", "xzp", "xzv", "yaa", "yag", "yai", "yam", "ych", "ydc", "yeb", "yej", "yeq", "yga", "ygn", "ygq", "ygr", "ygv", "ygz", "yha", "yhb", "yhy", "yia", "yib", "yik", "yiu", "yiy", "yjt", "yjw", "yjy", "yku", "yle", "ylv", "ymp", "yne", "ynn", "ynr", "yoa", "yob", "yoh", "yok", "ypn", "ypw", "ypx", "ypy", "yqx", "yrl", "yrm", "yru", "ysu", "yte", "ytj", "ytm", "ytq", "ytr", "ytv", "yuh", "yui", "yuj", "yvj", "yvm", "yvn", "ywh", "ywt", "yxh", "yyd", "yyg", "yyi", "yyp", "yyr", "yyu", "yyz", "yza", "yzy", "zab", "zac", "zay", "zbg", "zbi", "zbj", "zbw", "zcd", "zcx", "zdg", "zdm", "zds", "zeb", "zei", "zeu", "zfz", "zgd", "zgg", "zgm", "zgq", "zgv", "zhp", "zhr", "zhy", "zib", "zix", "ziy", "ziz", "zjt", "zju", "zjw", "zke", "zkf", "zky", "zlv", "zmb", "zmj", "zmt", "zmv", "zna", "znw", "znz", "zou", "zpa", "zpx", "zqx", "zri", "zrj", "zro", "zrp", "zru", "zrw", "zsb", "zsh", "ztv", "zue", "zun", "zvj", "zvm", "zvn", "zvu", "zwt", "zwv", "zxv", "zzg", "zzj", "zzk", "zzp", "zzr"]
class Sage_PonyStyle(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls) -> InputTypeDict:
return {
"required": {
"style": (pony_strings, {"defaultInput": False, "multi_select": True, "chip": True, "placeholder": "Pony Style"})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("text",)
FUNCTION = "return_style"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Adds the chosen three letter artist styles from Pony v6."
def return_style(self, style) -> tuple[str]:
return (", ".join(style),)
class Sage_ViewNotes(ComfyNodeABC):
@classmethod
def INPUT_TYPES(cls):
# Get list of files in notes directory
notes_files = []
try:
notes_path = path_manager.notes_path
if notes_path.exists():
notes_files = [f.name for f in notes_path.iterdir() if f.is_file()]
notes_files.sort() # Sort alphabetically
except Exception as e:
print(f"Error reading notes directory: {e}")
notes_files = ["No files found"]
if not notes_files:
notes_files = ["No files found"]
# type: ignore
return {
"required": {
"filename": (IO.COMBO, {"options": notes_files})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("content",)
FUNCTION = "view_notes"
CATEGORY = "Sage Utils/text"
DESCRIPTION = "Views the contents of a selected file from the notes directory."
OUTPUT_NODE = True
def view_notes(self, filename) -> dict:
"""
Reads and returns the content of the selected notes file.
"""
try:
if filename == "No files found":
content = "No notes files found in the notes directory."
else:
notes_file_path = path_manager.notes_path / filename
if notes_file_path.exists() and notes_file_path.is_file():
with open(notes_file_path, 'r', encoding='utf-8') as file:
content = file.read()
else:
content = f"File '{filename}' not found in notes directory."
except Exception as e:
content = f"Error reading file '{filename}': {str(e)}"
return {"ui": {"text": content}, "result": (content,) }