643 lines
25 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
import os
from urllib.request import urlopen
import folder_paths
from .. import easyCache
from ..config import FOOOCUS_STYLES_DIR, MAX_SEED_NUM, PROMPT_TEMPLATE, RESOURCES_DIR
from ..libs.log import log_node_info
from ..libs.utils import AlwaysEqualProxy
from ..libs.wildcards import WildcardProcessor, get_wildcard_list, process
# 正面提示词
class positivePrompt:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {"required": {
"positive": ("STRING", {"default": "", "multiline": True, "placeholder": "Positive"}),}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("positive",)
FUNCTION = "main"
CATEGORY = "EasyUse/Prompt"
@staticmethod
def main(positive):
return positive,
# 通配符提示词
class wildcardsPrompt:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
wildcard_list = get_wildcard_list()
return {"required": {
"text": ("STRING", {"default": "", "multiline": True, "dynamicPrompts": False, "placeholder": "(Support wildcard)"}),
"Select to add LoRA": (["Select the LoRA to add to the text"] + folder_paths.get_filename_list("loras"),),
"Select to add Wildcard": (["Select the Wildcard to add to the text"] + wildcard_list,),
"seed": ("INT", {"default": 0, "min": 0, "max": MAX_SEED_NUM}),
"multiline_mode": ("BOOLEAN", {"default": False}),
},
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},
}
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("text", "populated_text")
OUTPUT_IS_LIST = (True, True)
FUNCTION = "main"
CATEGORY = "EasyUse/Prompt"
def main(self, *args, **kwargs):
prompt = kwargs["prompt"] if "prompt" in kwargs else None
seed = kwargs["seed"]
# Clean loaded_objects
if prompt:
easyCache.update_loaded_objects(prompt)
text = kwargs['text']
if "multiline_mode" in kwargs and kwargs["multiline_mode"]:
populated_text = []
_text = []
text = text.split("\n")
for t in text:
_text.append(t)
populated_text.append(process(t, seed))
text = _text
else:
populated_text = [process(text, seed)]
text = [text]
return {"ui": {"value": [seed]}, "result": (text, populated_text)}
# 通配符提示词矩阵,会按顺序返回包含通配符的提示词所生成的所有可能
class wildcardsPromptMatrix:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
wildcard_list = get_wildcard_list()
return {"required": {
"text": ("STRING", {"default": "", "multiline": True, "dynamicPrompts": False, "placeholder": "(Support Lora Block Weight and wildcard)"}),
"Select to add LoRA": (["Select the LoRA to add to the text"] + folder_paths.get_filename_list("loras"),),
"Select to add Wildcard": (["Select the Wildcard to add to the text"] + wildcard_list,),
"offset": ("INT", {"default": 0, "min": 0, "step": 1, "control_after_generate": True}),
},
"optional":{
"output_limit": ("INT", {"default": 1, "min": -1, "step": 1, "tooltip": "Output All Probilities"})
},
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},
}
RETURN_TYPES = ("STRING", "INT", "INT")
RETURN_NAMES = ("populated_text", "total", "factors")
OUTPUT_IS_LIST = (True, False, True)
FUNCTION = "main"
CATEGORY = "EasyUse/Prompt"
def main(self, *args, **kwargs):
prompt = kwargs["prompt"] if "prompt" in kwargs else None
offset = kwargs["offset"]
output_limit = kwargs.get("output_limit", 1)
# Clean loaded_objects
if prompt:
easyCache.update_loaded_objects(prompt)
text = kwargs['text']
p = WildcardProcessor(text)
total = p.total()
limit = total if output_limit > total or output_limit == -1 else output_limit
offset = 0 if output_limit == -1 else offset
populated_text = p.getmany(limit, offset) if output_limit != 1 else [p.getn(offset)]
return {"ui": {"value": [offset]}, "result": (populated_text, p.total(), list(p.placeholder_choices.values()))}
# 负面提示词
class negativePrompt:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {"required": {
"negative": ("STRING", {"default": "", "multiline": True, "placeholder": "Negative"}),}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("negative",)
FUNCTION = "main"
CATEGORY = "EasyUse/Prompt"
@staticmethod
def main(negative):
return negative,
# 风格提示词选择器
class stylesPromptSelector:
@classmethod
def INPUT_TYPES(s):
styles = ["fooocus_styles"]
styles_dir = FOOOCUS_STYLES_DIR
for file_name in os.listdir(styles_dir):
file = os.path.join(styles_dir, file_name)
if os.path.isfile(file) and file_name.endswith(".json"):
if file_name != "fooocus_styles.json":
styles.append(file_name.split(".")[0])
return {
"required": {
"styles": (styles, {"default": "fooocus_styles"}),
},
"optional": {
"positive": ("STRING", {"forceInput": True}),
"negative": ("STRING", {"forceInput": True}),
"select_styles": ("EASY_PROMPT_STYLES", {}),
},
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},
}
RETURN_TYPES = ("STRING", "STRING",)
RETURN_NAMES = ("positive", "negative",)
CATEGORY = 'EasyUse/Prompt'
FUNCTION = 'run'
def run(self, styles, positive='', negative='', select_styles=None, prompt=None, extra_pnginfo=None, my_unique_id=None):
values = []
all_styles = {}
positive_prompt, negative_prompt = '', negative
fooocus_custom_dir = os.path.join(FOOOCUS_STYLES_DIR, 'fooocus_styles.json')
if styles == "fooocus_styles" and not os.path.exists(fooocus_custom_dir):
file = os.path.join(RESOURCES_DIR, styles + '.json')
else:
file = os.path.join(FOOOCUS_STYLES_DIR, styles + '.json')
f = open(file, 'r', encoding='utf-8')
data = json.load(f)
f.close()
for d in data:
all_styles[d['name']] = d
# if my_unique_id in prompt:
# if prompt[my_unique_id]["inputs"]['select_styles']:
# values = prompt[my_unique_id]["inputs"]['select_styles'].split(',')
if isinstance(select_styles, str):
values = select_styles.split(',')
else:
values = select_styles if select_styles else []
has_prompt = False
if len(values) == 0:
return (positive, negative)
for index, val in enumerate(values):
if 'prompt' in all_styles[val]:
if "{prompt}" in all_styles[val]['prompt'] and has_prompt == False:
positive_prompt = all_styles[val]['prompt'].replace('{prompt}', positive)
has_prompt = True
elif "{prompt}" in all_styles[val]['prompt']:
positive_prompt += ', ' + all_styles[val]['prompt'].replace(', {prompt}', '').replace('{prompt}', '')
else:
positive_prompt = all_styles[val]['prompt'] if positive_prompt == '' else positive_prompt + ', ' + all_styles[val]['prompt']
if 'negative_prompt' in all_styles[val]:
negative_prompt += ', ' + all_styles[val]['negative_prompt'] if negative_prompt else all_styles[val]['negative_prompt']
if has_prompt == False and positive:
positive_prompt = positive + positive_prompt + ', '
return (positive_prompt, negative_prompt)
#prompt
class prompt:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"text": ("STRING", {"default": "", "multiline": True, "placeholder": "Prompt"}),
"prefix": (["Select the prefix add to the text"] + PROMPT_TEMPLATE["prefix"], {"default": "Select the prefix add to the text"}),
"subject": (["👤Select the subject add to the text"] + PROMPT_TEMPLATE["subject"], {"default": "👤Select the subject add to the text"}),
"action": (["🎬Select the action add to the text"] + PROMPT_TEMPLATE["action"], {"default": "🎬Select the action add to the text"}),
"clothes": (["👚Select the clothes add to the text"] + PROMPT_TEMPLATE["clothes"], {"default": "👚Select the clothes add to the text"}),
"environment": (["Select the illumination environment add to the text"] + PROMPT_TEMPLATE["environment"], {"default": "Select the illumination environment add to the text"}),
"background": (["🎞Select the background add to the text"] + PROMPT_TEMPLATE["background"], {"default": "🎞Select the background add to the text"}),
"nsfw": (["🔞Select the nsfw add to the text"] + PROMPT_TEMPLATE["nsfw"], {"default": "🔞Select the nsfw add to the text"}),
},"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("prompt",)
FUNCTION = "doit"
CATEGORY = "EasyUse/Prompt"
def doit(self, *args, **kwargs):
text = kwargs['text']
return (text,)
#promptList
class promptList:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"prompt_1": ("STRING", {"multiline": True, "default": ""}),
"prompt_2": ("STRING", {"multiline": True, "default": ""}),
"prompt_3": ("STRING", {"multiline": True, "default": ""}),
"prompt_4": ("STRING", {"multiline": True, "default": ""}),
"prompt_5": ("STRING", {"multiline": True, "default": ""}),
},
"optional": {
"optional_prompt_list": ("LIST",)
}
}
RETURN_TYPES = ("LIST", "STRING")
RETURN_NAMES = ("prompt_list", "prompt_strings")
OUTPUT_IS_LIST = (False, True)
FUNCTION = "run"
CATEGORY = "EasyUse/Prompt"
def run(self, **kwargs):
prompts = []
if "optional_prompt_list" in kwargs:
for l in kwargs["optional_prompt_list"]:
prompts.append(l)
# Iterate over the received inputs in sorted order.
for k in sorted(kwargs.keys()):
v = kwargs[k]
# Only process string input ports.
if isinstance(v, str) and v != '':
prompts.append(v)
return (prompts, prompts)
#promptLine
class promptLine:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"prompt": ("STRING", {"multiline": True, "default": "text"}),
"start_index": ("INT", {"default": 0, "min": 0, "max": 9999}),
"max_rows": ("INT", {"default": 1000, "min": 1, "max": 9999}),
},
"hidden":{
"workflow_prompt": "PROMPT", "my_unique_id": "UNIQUE_ID"
}
}
RETURN_TYPES = ("STRING", AlwaysEqualProxy('*'))
RETURN_NAMES = ("STRING", "COMBO")
OUTPUT_IS_LIST = (True, True)
FUNCTION = "generate_strings"
CATEGORY = "EasyUse/Prompt"
def generate_strings(self, prompt, start_index, max_rows, workflow_prompt=None, my_unique_id=None):
lines = prompt.split('\n')
# lines = [zh_to_en([v])[0] if has_chinese(v) else v for v in lines if v]
start_index = max(0, min(start_index, len(lines) - 1))
end_index = min(start_index + max_rows, len(lines))
rows = lines[start_index:end_index]
return (rows, rows)
import comfy.utils
from server import PromptServer
from ..libs.messages import MessageCancelled, Message
any_type = AlwaysEqualProxy("*")
class promptAwait:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"now": (any_type,),
"prompt": ("STRING", {"multiline": True, "default": "", "placeholder":"Enter a prompt or use voice to enter to text"}),
"toolbar":("EASY_PROMPT_AWAIT_BAR",),
},
"optional":{
"prev": (any_type,),
},
"hidden": {"workflow_prompt": "PROMPT", "my_unique_id": "UNIQUE_ID", "extra_pnginfo": "EXTRA_PNGINFO"},
}
RETURN_TYPES = (any_type, "STRING", "BOOLEAN", "INT")
RETURN_NAMES = ("output", "prompt", "continue", "seed")
FUNCTION = "await_select"
CATEGORY = "EasyUse/Prompt"
def await_select(self, now, prompt, toolbar, prev=None, workflow_prompt=None, my_unique_id=None, extra_pnginfo=None, **kwargs):
id = my_unique_id
id = id.split('.')[len(id.split('.')) - 1] if "." in id else id
if ":" in id:
id = id.split(":")[0]
pbar = comfy.utils.ProgressBar(100)
pbar.update_absolute(30)
PromptServer.instance.send_sync('easyuse_prompt_await', {"id": id})
try:
res = Message.waitForMessage(id, asList=False)
if res is None or res == "-1":
result = (now, prompt, False, 0)
else:
input = now if res['select'] == 'now' or prev is None else prev
result = (input, res['prompt'], False if res['result'] == -1 else True, res['seed'] if res['unlock'] else res['last_seed'])
pbar.update_absolute(100)
return result
except MessageCancelled:
pbar.update_absolute(100)
raise comfy.model_management.InterruptProcessingException()
class promptConcat:
@classmethod
def INPUT_TYPES(cls):
return {"required": {},
"optional": {
"prompt1": ("STRING", {"multiline": False, "default": "", "forceInput": True}),
"prompt2": ("STRING", {"multiline": False, "default": "", "forceInput": True}),
"separator": ("STRING", {"multiline": False, "default": ""}),
},
}
RETURN_TYPES = ("STRING", )
RETURN_NAMES = ("prompt", )
FUNCTION = "concat_text"
CATEGORY = "EasyUse/Prompt"
def concat_text(self, prompt1="", prompt2="", separator=""):
return (prompt1 + separator + prompt2,)
class promptReplace:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt": ("STRING", {"multiline": True, "default": "", "forceInput": True}),
},
"optional": {
"find1": ("STRING", {"multiline": False, "default": ""}),
"replace1": ("STRING", {"multiline": False, "default": ""}),
"find2": ("STRING", {"multiline": False, "default": ""}),
"replace2": ("STRING", {"multiline": False, "default": ""}),
"find3": ("STRING", {"multiline": False, "default": ""}),
"replace3": ("STRING", {"multiline": False, "default": ""}),
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("prompt",)
FUNCTION = "replace_text"
CATEGORY = "EasyUse/Prompt"
def replace_text(self, prompt, find1="", replace1="", find2="", replace2="", find3="", replace3=""):
prompt = prompt.replace(find1, replace1)
prompt = prompt.replace(find2, replace2)
prompt = prompt.replace(find3, replace3)
return (prompt,)
# 肖像大师
# Created by AI Wiz Art (Stefano Flore)
# Version: 2.2
# https://stefanoflore.it
# https://ai-wiz.art
class portraitMaster:
@classmethod
def INPUT_TYPES(s):
max_float_value = 1.95
prompt_path = os.path.join(RESOURCES_DIR, 'portrait_prompt.json')
if not os.path.exists(prompt_path):
response = urlopen('https://raw.githubusercontent.com/yolain/ComfyUI-Easy-Use/main/resources/portrait_prompt.json')
temp_prompt = json.loads(response.read())
prompt_serialized = json.dumps(temp_prompt, indent=4)
with open(prompt_path, "w") as f:
f.write(prompt_serialized)
del response, temp_prompt
# Load local
with open(prompt_path, 'r') as f:
list = json.load(f)
keys = [
['shot', 'COMBO', {"key": "shot_list"}], ['shot_weight', 'FLOAT'],
['gender', 'COMBO', {"default": "Woman", "key": "gender_list"}], ['age', 'INT', {"default": 30, "min": 18, "max": 90, "step": 1, "display": "slider"}],
['nationality_1', 'COMBO', {"default": "Chinese", "key": "nationality_list"}], ['nationality_2', 'COMBO', {"key": "nationality_list"}], ['nationality_mix', 'FLOAT'],
['body_type', 'COMBO', {"key": "body_type_list"}], ['body_type_weight', 'FLOAT'], ['model_pose', 'COMBO', {"key": "model_pose_list"}], ['eyes_color', 'COMBO', {"key": "eyes_color_list"}],
['facial_expression', 'COMBO', {"key": "face_expression_list"}], ['facial_expression_weight', 'FLOAT'], ['face_shape', 'COMBO', {"key": "face_shape_list"}], ['face_shape_weight', 'FLOAT'], ['facial_asymmetry', 'FLOAT'],
['hair_style', 'COMBO', {"key": "hair_style_list"}], ['hair_color', 'COMBO', {"key": "hair_color_list"}], ['disheveled', 'FLOAT'], ['beard', 'COMBO', {"key": "beard_list"}],
['skin_details', 'FLOAT'], ['skin_pores', 'FLOAT'], ['dimples', 'FLOAT'], ['freckles', 'FLOAT'],
['moles', 'FLOAT'], ['skin_imperfections', 'FLOAT'], ['skin_acne', 'FLOAT'], ['tanned_skin', 'FLOAT'],
['eyes_details', 'FLOAT'], ['iris_details', 'FLOAT'], ['circular_iris', 'FLOAT'], ['circular_pupil', 'FLOAT'],
['light_type', 'COMBO', {"key": "light_type_list"}], ['light_direction', 'COMBO', {"key": "light_direction_list"}], ['light_weight', 'FLOAT']
]
widgets = {}
for i, obj in enumerate(keys):
if obj[1] == 'COMBO':
key = obj[2]['key'] if obj[2] and 'key' in obj[2] else obj[0]
_list = list[key].copy()
_list.insert(0, '-')
widgets[obj[0]] = (_list, {**obj[2]})
elif obj[1] == 'FLOAT':
widgets[obj[0]] = ("FLOAT", {"default": 0, "step": 0.05, "min": 0, "max": max_float_value, "display": "slider",})
elif obj[1] == 'INT':
widgets[obj[0]] = (obj[1], obj[2])
del list
return {
"required": {
**widgets,
"photorealism_improvement": (["enable", "disable"],),
"prompt_start": ("STRING", {"multiline": True, "default": "raw photo, (realistic:1.5)"}),
"prompt_additional": ("STRING", {"multiline": True, "default": ""}),
"prompt_end": ("STRING", {"multiline": True, "default": ""}),
"negative_prompt": ("STRING", {"multiline": True, "default": ""}),
}
}
RETURN_TYPES = ("STRING", "STRING",)
RETURN_NAMES = ("positive", "negative",)
FUNCTION = "pm"
CATEGORY = "EasyUse/Prompt"
def pm(self, shot="-", shot_weight=1, gender="-", body_type="-", body_type_weight=0, eyes_color="-",
facial_expression="-", facial_expression_weight=0, face_shape="-", face_shape_weight=0,
nationality_1="-", nationality_2="-", nationality_mix=0.5, age=30, hair_style="-", hair_color="-",
disheveled=0, dimples=0, freckles=0, skin_pores=0, skin_details=0, moles=0, skin_imperfections=0,
wrinkles=0, tanned_skin=0, eyes_details=1, iris_details=1, circular_iris=1, circular_pupil=1,
facial_asymmetry=0, prompt_additional="", prompt_start="", prompt_end="", light_type="-",
light_direction="-", light_weight=0, negative_prompt="", photorealism_improvement="disable", beard="-",
model_pose="-", skin_acne=0):
prompt = []
if gender == "-":
gender = ""
else:
if age <= 25 and gender == 'Woman':
gender = 'girl'
if age <= 25 and gender == 'Man':
gender = 'boy'
gender = " " + gender + " "
if nationality_1 != '-' and nationality_2 != '-':
nationality = f"[{nationality_1}:{nationality_2}:{round(nationality_mix, 2)}]"
elif nationality_1 != '-':
nationality = nationality_1 + " "
elif nationality_2 != '-':
nationality = nationality_2 + " "
else:
nationality = ""
if prompt_start != "":
prompt.append(f"{prompt_start}")
if shot != "-" and shot_weight > 0:
prompt.append(f"({shot}:{round(shot_weight, 2)})")
prompt.append(f"({nationality}{gender}{round(age)}-years-old:1.5)")
if body_type != "-" and body_type_weight > 0:
prompt.append(f"({body_type}, {body_type} body:{round(body_type_weight, 2)})")
if model_pose != "-":
prompt.append(f"({model_pose}:1.5)")
if eyes_color != "-":
prompt.append(f"({eyes_color} eyes:1.25)")
if facial_expression != "-" and facial_expression_weight > 0:
prompt.append(
f"({facial_expression}, {facial_expression} expression:{round(facial_expression_weight, 2)})")
if face_shape != "-" and face_shape_weight > 0:
prompt.append(f"({face_shape} shape face:{round(face_shape_weight, 2)})")
if hair_style != "-":
prompt.append(f"({hair_style} hairstyle:1.25)")
if hair_color != "-":
prompt.append(f"({hair_color} hair:1.25)")
if beard != "-":
prompt.append(f"({beard}:1.15)")
if disheveled != "-" and disheveled > 0:
prompt.append(f"(disheveled:{round(disheveled, 2)})")
if prompt_additional != "":
prompt.append(f"{prompt_additional}")
if skin_details > 0:
prompt.append(f"(skin details, skin texture:{round(skin_details, 2)})")
if skin_pores > 0:
prompt.append(f"(skin pores:{round(skin_pores, 2)})")
if skin_imperfections > 0:
prompt.append(f"(skin imperfections:{round(skin_imperfections, 2)})")
if skin_acne > 0:
prompt.append(f"(acne, skin with acne:{round(skin_acne, 2)})")
if wrinkles > 0:
prompt.append(f"(skin imperfections:{round(wrinkles, 2)})")
if tanned_skin > 0:
prompt.append(f"(tanned skin:{round(tanned_skin, 2)})")
if dimples > 0:
prompt.append(f"(dimples:{round(dimples, 2)})")
if freckles > 0:
prompt.append(f"(freckles:{round(freckles, 2)})")
if moles > 0:
prompt.append(f"(skin pores:{round(moles, 2)})")
if eyes_details > 0:
prompt.append(f"(eyes details:{round(eyes_details, 2)})")
if iris_details > 0:
prompt.append(f"(iris details:{round(iris_details, 2)})")
if circular_iris > 0:
prompt.append(f"(circular iris:{round(circular_iris, 2)})")
if circular_pupil > 0:
prompt.append(f"(circular pupil:{round(circular_pupil, 2)})")
if facial_asymmetry > 0:
prompt.append(f"(facial asymmetry, face asymmetry:{round(facial_asymmetry, 2)})")
if light_type != '-' and light_weight > 0:
if light_direction != '-':
prompt.append(f"({light_type} {light_direction}:{round(light_weight, 2)})")
else:
prompt.append(f"({light_type}:{round(light_weight, 2)})")
if prompt_end != "":
prompt.append(f"{prompt_end}")
prompt = ", ".join(prompt)
prompt = prompt.lower()
if photorealism_improvement == "enable":
prompt = prompt + ", (professional photo, balanced photo, balanced exposure:1.2), (film grain:1.15)"
if photorealism_improvement == "enable":
negative_prompt = negative_prompt + ", (shinny skin, reflections on the skin, skin reflections:1.25)"
log_node_info("Portrait Master as generate the prompt:", prompt)
return (prompt, negative_prompt,)
NODE_CLASS_MAPPINGS = {
"easy positive": positivePrompt,
"easy negative": negativePrompt,
"easy wildcards": wildcardsPrompt,
"easy wildcardsMatrix": wildcardsPromptMatrix,
"easy prompt": prompt,
"easy promptList": promptList,
"easy promptLine": promptLine,
"easy promptAwait": promptAwait,
"easy promptConcat": promptConcat,
"easy promptReplace": promptReplace,
"easy stylesSelector": stylesPromptSelector,
"easy portraitMaster": portraitMaster,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"easy positive": "Positive",
"easy negative": "Negative",
"easy wildcards": "Wildcards",
"easy wildcardsMatrix": "Wildcards Matrix",
"easy prompt": "Prompt",
"easy promptList": "PromptList",
"easy promptLine": "PromptLine",
"easy promptAwait": "PromptAwait",
"easy promptConcat": "PromptConcat",
"easy promptReplace": "PromptReplace",
"easy stylesSelector": "Styles Selector",
"easy portraitMaster": "Portrait Master",
}