ImageGen / ui /shared /ui_components.py
RioShiina's picture
feat: add Ming-Image architecture & Prompt Enhancer and update upstream deps
fd0b72b verified
Raw
History Blame Contribute Delete
53.5 kB
import gradio as gr
from comfy_integration.nodes import SAMPLER_CHOICES, SCHEDULER_CHOICES
from core.settings import (
MAX_LORAS, LORA_SOURCE_CHOICES, MAX_EMBEDDINGS, MAX_CONDITIONINGS,
MAX_CONTROLNETS, MAX_IPADAPTERS, RESOLUTION_MAP, ARCHITECTURES_CONFIG,
MODEL_MAP_CHECKPOINT, MODEL_TYPE_MAP, FEATURES_CONFIG, ARCH_CATEGORIES_MAP,
VAE_DIR, MODEL_DEFAULTS_CONFIG
)
import yaml
import os
from functools import lru_cache
from utils.app_utils import save_uploaded_file
default_model_name = list(MODEL_MAP_CHECKPOINT.keys())[0] if MODEL_MAP_CHECKPOINT else None
default_m_type = MODEL_TYPE_MAP.get(default_model_name, "SDXL") if default_model_name else "SDXL"
default_architectures_dict = ARCHITECTURES_CONFIG.get('architectures', {})
default_arch_model_type = default_architectures_dict.get(default_m_type, {}).get("model_type", default_m_type.lower().replace(" ", "").replace(".", ""))
default_arch_features = FEATURES_CONFIG.get(default_arch_model_type, {})
default_enabled_chains = default_arch_features.get('enabled_chains', [])
default_vals = MODEL_DEFAULTS_CONFIG.get('Default', {})
DEFAULT_STEPS = default_vals.get('steps', 20)
DEFAULT_CFG = default_vals.get('cfg', 5.0)
DEFAULT_SAMPLER = default_vals.get('sampler_name', 'euler')
DEFAULT_SCHEDULER = default_vals.get('scheduler', 'simple')
DEFAULT_POS_PROMPT = default_vals.get('positive_prompt', '')
DEFAULT_NEG_PROMPT = default_vals.get('negative_prompt', '')
@lru_cache(maxsize=1)
def get_ipadapter_config_from_yaml():
try:
_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
_IPADAPTER_LIST_PATH = os.path.join(_PROJECT_ROOT, 'yaml', 'ipadapter.yaml')
with open(_IPADAPTER_LIST_PATH, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
return config
except Exception as e:
print(f"Warning: Could not load ipadapter.yaml for UI components: {e}")
return {}
def get_ipadapter_presets(arch="SDXL"):
config = get_ipadapter_config_from_yaml()
presets = []
if config:
std_presets = config.get("IPAdapter_presets", {}).get(arch, [])
face_presets = config.get("IPAdapter_FaceID_presets", {}).get(arch, [])
if std_presets:
presets.extend(std_presets)
if face_presets:
presets.extend(face_presets)
return presets if presets else ["STANDARD (medium strength)"]
def create_model_architecture_filter_ui(prefix):
components = {}
ordered_architectures = ARCHITECTURES_CONFIG.get("architecture_order", [])
choices = ["ALL"] + ordered_architectures
components[f'model_arch_{prefix}'] = gr.Radio(
label="Model Architecture",
choices=choices,
value="ALL",
interactive=True,
visible=True
)
return components
def create_category_filter_ui(prefix):
valid_cats = list(set(cat for cats in ARCH_CATEGORIES_MAP.values() for cat in cats))
cat_choices = ["ALL"] + sorted(valid_cats)
components = {}
components[f'model_cat_{prefix}'] = gr.Dropdown(
label="Filter Models",
choices=cat_choices,
value="ALL",
interactive=True,
scale=1,
allow_custom_value=True
)
return components
def create_base_parameter_ui(prefix, defaults=None):
if defaults is None:
defaults = {}
components = {}
with gr.Row():
components[f'aspect_ratio_{prefix}'] = gr.Dropdown(
label="Aspect Ratio",
choices=list(RESOLUTION_MAP.get('sdxl', {}).keys()),
value="1:1 (Square)",
interactive=True,
allow_custom_value=True
)
with gr.Row():
components[f'width_{prefix}'] = gr.Number(label="Width", value=defaults.get('w', 1024), interactive=True)
components[f'height_{prefix}'] = gr.Number(label="Height", value=defaults.get('h', 1024), interactive=True)
with gr.Row():
components[f'sampler_{prefix}'] = gr.Dropdown(
label="Sampler",
choices=SAMPLER_CHOICES,
value=DEFAULT_SAMPLER if DEFAULT_SAMPLER in SAMPLER_CHOICES else (SAMPLER_CHOICES[0] if SAMPLER_CHOICES else 'euler')
)
components[f'scheduler_{prefix}'] = gr.Dropdown(
label="Scheduler",
choices=SCHEDULER_CHOICES,
value=DEFAULT_SCHEDULER if DEFAULT_SCHEDULER in SCHEDULER_CHOICES else (SCHEDULER_CHOICES[0] if SCHEDULER_CHOICES else 'simple')
)
with gr.Row():
components[f'steps_{prefix}'] = gr.Slider(label="Steps", minimum=1, maximum=100, step=1, value=DEFAULT_STEPS)
components[f'cfg_{prefix}'] = gr.Slider(label="CFG Scale", minimum=1.0, maximum=20.0, step=0.1, value=DEFAULT_CFG)
with gr.Row():
components[f'seed_{prefix}'] = gr.Number(label="Seed (-1 for random)", value=-1, precision=0)
components[f'batch_size_{prefix}'] = gr.Slider(label="Batch Size", minimum=1, maximum=16, step=1, value=1)
with gr.Row():
components[f'zero_gpu_{prefix}'] = gr.Number(label="ZeroGPU Duration (s)", value=60, placeholder="Default: 60s", info="Optional: Set how long to reserve the GPU.")
return components
def create_lora_settings_ui(prefix: str):
components = {}
lora_rows, lora_sources, lora_ids, lora_scales, lora_uploads = [], [], [], [], []
with gr.Accordion("LoRA Settings", open=False, visible=('lora' in default_enabled_chains)) as lora_accordion:
components[f'lora_accordion_{prefix}'] = lora_accordion
gr.Markdown("💡 **Tip:** When downloading from Civitai, please use the **Version ID**, not the Model ID. You can find the Version ID in the URL (e.g., `civitai.com/models/123?modelVersionId=456`) or under the model's download button. When downloading from Hugging Face, please use the format: `repo_id/filename.extension` or `repo_id/folder_path/filename.extension` (e.g., `lightx2v/Qwen-Image-Lightning/Qwen-Image-Lightning-4steps-V2.0-bf16.safetensors`).")
components[f'lora_count_state_{prefix}'] = gr.State(1)
for i in range(MAX_LORAS):
with gr.Row(visible=i==0) as row:
source = gr.Dropdown(label=f"LoRA Source {i+1}", choices=LORA_SOURCE_CHOICES, value=LORA_SOURCE_CHOICES[0], scale=1)
lora_id = gr.Textbox(label="Civitai Version ID / HF file / Upload File", scale=2, type="text")
scale = gr.Slider(label=f"Scale", minimum=0.0, maximum=2.0, step=0.05, value=1.0, scale=1)
upload = gr.UploadButton(label="Upload", file_types=[".safetensors"], scale=1)
lora_rows.append(row)
lora_sources.append(source)
lora_ids.append(lora_id)
lora_scales.append(scale)
lora_uploads.append(upload)
with gr.Row():
components[f'add_lora_button_{prefix}'] = gr.Button("Add LoRA", variant="secondary")
components[f'delete_lora_button_{prefix}'] = gr.Button("Remove LoRA", variant="secondary", visible=False)
components[f'lora_rows_{prefix}'] = lora_rows
components[f'lora_sources_{prefix}'] = lora_sources
components[f'lora_ids_{prefix}'] = lora_ids
components[f'lora_scales_{prefix}'] = lora_scales
components[f'lora_uploads_{prefix}'] = lora_uploads
all_lora_components_flat = []
for i in range(MAX_LORAS):
all_lora_components_flat.extend([lora_sources[i], lora_ids[i], lora_scales[i], lora_uploads[i]])
components[f'all_lora_components_flat_{prefix}'] = all_lora_components_flat
return components
def create_controlnet_ui(prefix: str, max_units=MAX_CONTROLNETS):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("ControlNet Settings", open=False, visible=('controlnet' in default_enabled_chains)) as accordion:
components[key('controlnet_accordion')] = accordion
cn_rows, images, series, types, strengths, filepaths = [], [], [], [], [], []
components.update({
key('controlnet_rows'): cn_rows,
key('controlnet_images'): images,
key('controlnet_series'): series,
key('controlnet_types'): types,
key('controlnet_strengths'): strengths,
key('controlnet_filepaths'): filepaths
})
for i in range(max_units):
with gr.Row(visible=(i < 1)) as row:
with gr.Column(scale=1):
images.append(gr.Image(label=f"Control Image {i+1}", type="pil", sources=["upload"], height=256))
with gr.Column(scale=2):
types.append(gr.Dropdown(label="Type", choices=[], interactive=True, allow_custom_value=True))
series.append(gr.Dropdown(label="Series", choices=[], interactive=True, allow_custom_value=True))
strengths.append(gr.Slider(label="Strength", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
filepaths.append(gr.State(None))
cn_rows.append(row)
with gr.Row():
components[key('add_controlnet_button')] = gr.Button("✚ Add ControlNet")
components[key('delete_controlnet_button')] = gr.Button("âž– Delete ControlNet", visible=False)
components[key('controlnet_count_state')] = gr.State(1)
all_cn_components_flat = []
for i in range(max_units):
all_cn_components_flat.extend([
images[i], types[i], series[i], strengths[i], filepaths[i]
])
components[key('all_controlnet_components_flat')] = all_cn_components_flat
return components
def create_krea2_controlnet_ui(prefix: str, max_units=MAX_CONTROLNETS):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Krea2 ControlNet Settings", open=False, visible=('krea2_controlnet' in default_enabled_chains)) as accordion:
components[key('krea2_controlnet_accordion')] = accordion
gr.Markdown("💡 **Tip:** Processed using the [facok/comfyui-krea2-controlnet](https://github.com/facok/comfyui-krea2-controlnet) node.")
cn_rows, images, series, types, strengths, filepaths = [], [], [], [], [], []
components.update({
key('krea2_controlnet_rows'): cn_rows,
key('krea2_controlnet_images'): images,
key('krea2_controlnet_series'): series,
key('krea2_controlnet_types'): types,
key('krea2_controlnet_strengths'): strengths,
key('krea2_controlnet_filepaths'): filepaths
})
for i in range(max_units):
with gr.Row(visible=(i < 1)) as row:
with gr.Column(scale=1):
images.append(gr.Image(label=f"Control Image {i+1}", type="pil", sources=["upload"], height=256))
with gr.Column(scale=2):
types.append(gr.Dropdown(label="Type", choices=[], interactive=True, allow_custom_value=True))
series.append(gr.Dropdown(label="Series", choices=[], interactive=True, allow_custom_value=True))
strengths.append(gr.Slider(label="Strength", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
filepaths.append(gr.State(None))
cn_rows.append(row)
with gr.Row():
components[key('add_krea2_controlnet_button')] = gr.Button("✚ Add Krea2 ControlNet")
components[key('delete_krea2_controlnet_button')] = gr.Button("âž– Delete Krea2 ControlNet", visible=False)
components[key('krea2_controlnet_count_state')] = gr.State(1)
all_cn_components_flat = []
for i in range(max_units):
all_cn_components_flat.extend([
images[i], types[i], series[i], strengths[i], filepaths[i]
])
components[key('all_krea2_controlnet_components_flat')] = all_cn_components_flat
return components
def create_anima_controlnet_lllite_ui(prefix: str, max_units=MAX_CONTROLNETS):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Anima ControlNet LLLite Settings", open=False, visible=('anima_controlnet_lllite' in default_enabled_chains)) as accordion:
components[key('anima_controlnet_lllite_accordion')] = accordion
cn_rows, images, series, types, strengths, filepaths, start_percents, end_percents = [], [], [], [], [], [], [], []
components.update({
key('anima_controlnet_lllite_rows'): cn_rows,
key('anima_controlnet_lllite_images'): images,
key('anima_controlnet_lllite_series'): series,
key('anima_controlnet_lllite_types'): types,
key('anima_controlnet_lllite_strengths'): strengths,
key('anima_controlnet_lllite_filepaths'): filepaths,
key('anima_controlnet_lllite_start_percents'): start_percents,
key('anima_controlnet_lllite_end_percents'): end_percents
})
for i in range(max_units):
with gr.Row(visible=(i < 1)) as row:
with gr.Column(scale=1):
images.append(gr.Image(label=f"Control Image {i+1}", type="pil", sources=["upload"], height=256))
with gr.Column(scale=2):
types.append(gr.Dropdown(label="Type", choices=[], interactive=True, allow_custom_value=True))
series.append(gr.Dropdown(label="Series", choices=[], interactive=True, allow_custom_value=True))
strengths.append(gr.Slider(label="Strength", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
with gr.Row(visible=False):
start_percents.append(gr.State(0.0))
end_percents.append(gr.State(1.0))
filepaths.append(gr.State(None))
cn_rows.append(row)
with gr.Row():
components[key('add_anima_controlnet_lllite_button')] = gr.Button("✚ Add Lllite")
components[key('delete_anima_controlnet_lllite_button')] = gr.Button("âž– Delete Lllite", visible=False)
components[key('anima_controlnet_lllite_count_state')] = gr.State(1)
all_cn_components_flat = []
for i in range(max_units):
all_cn_components_flat.extend([
images[i], types[i], series[i], strengths[i], filepaths[i], start_percents[i], end_percents[i]
])
components[key('all_anima_controlnet_lllite_components_flat')] = all_cn_components_flat
return components
def create_diffsynth_controlnet_ui(prefix: str, max_units=MAX_CONTROLNETS):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("DiffSynth ControlNet Settings", open=False, visible=('diffsynth_controlnet' in default_enabled_chains)) as accordion:
components[key('diffsynth_controlnet_accordion')] = accordion
cn_rows, images, series, types, strengths, filepaths = [], [], [], [], [], []
components.update({
key('diffsynth_controlnet_rows'): cn_rows,
key('diffsynth_controlnet_images'): images,
key('diffsynth_controlnet_series'): series,
key('diffsynth_controlnet_types'): types,
key('diffsynth_controlnet_strengths'): strengths,
key('diffsynth_controlnet_filepaths'): filepaths
})
for i in range(max_units):
with gr.Row(visible=(i < 1)) as row:
with gr.Column(scale=1):
images.append(gr.Image(label=f"Control Image {i+1}", type="pil", sources=["upload"], height=256))
with gr.Column(scale=2):
types.append(gr.Dropdown(label="Type", choices=[], interactive=True, allow_custom_value=True))
series.append(gr.Dropdown(label="Series", choices=[], interactive=True, allow_custom_value=True))
strengths.append(gr.Slider(label="Strength", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
filepaths.append(gr.State(None))
cn_rows.append(row)
with gr.Row():
components[key('add_diffsynth_controlnet_button')] = gr.Button("✚ Add DiffSynth ControlNet")
components[key('delete_diffsynth_controlnet_button')] = gr.Button("âž– Delete DiffSynth ControlNet", visible=False)
components[key('diffsynth_controlnet_count_state')] = gr.State(1)
all_cn_components_flat = []
for i in range(max_units):
all_cn_components_flat.extend([
images[i], types[i], series[i], strengths[i], filepaths[i]
])
components[key('all_diffsynth_controlnet_components_flat')] = all_cn_components_flat
return components
def create_ipadapter_ui(prefix: str, max_units=MAX_IPADAPTERS):
components = {}
key = lambda name: f"{name}_{prefix}"
sdxl_presets = get_ipadapter_presets("SDXL")
default_preset = sdxl_presets[0] if sdxl_presets else None
with gr.Accordion("IPAdapter Settings", open=False, visible=('ipadapter' in default_enabled_chains)) as accordion:
components[key('ipadapter_accordion')] = accordion
gr.Markdown("💡 **Tip:** Processed using the [cubiq/ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) node.")
with gr.Row():
components[key('ipadapter_final_preset')] = gr.Dropdown(
label="Preset (for all images)",
choices=sdxl_presets,
value=default_preset,
interactive=True,
allow_custom_value=True
)
components[key('ipadapter_embeds_scaling')] = gr.Dropdown(
label="Embeds Scaling",
choices=['V only', 'K+V', 'K+V w/ C penalty', 'K+mean(V) w/ C penalty'],
value='V only',
interactive=True
)
with gr.Row():
components[key('ipadapter_combine_method')] = gr.Dropdown(
label="Combine Method",
choices=["concat", "add", "subtract", "average", "norm average", "max", "min"],
value="concat",
interactive=True
)
components[key('ipadapter_final_weight')] = gr.Slider(label="Final Weight", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True)
components[key('ipadapter_final_lora_strength')] = gr.Slider(label="Final LoRA Strength", minimum=0.0, maximum=2.0, step=0.05, value=0.6, interactive=True, visible=False)
gr.Markdown("---")
ipa_rows, images, weights, lora_strengths = [], [], [], []
components.update({
key('ipadapter_rows'): ipa_rows,
key('ipadapter_images'): images,
key('ipadapter_weights'): weights,
key('ipadapter_lora_strengths'): lora_strengths
})
for i in range(max_units):
with gr.Row(visible=(i < 1)) as row:
with gr.Column(scale=1):
images.append(gr.Image(label=f"IPAdapter Image {i+1}", type="pil", sources=["upload"], height=256))
with gr.Column(scale=2):
weights.append(gr.Slider(label="Weight", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
lora_strengths.append(gr.Slider(label="LoRA Strength", minimum=0.0, maximum=2.0, step=0.05, value=0.6, interactive=True, visible=False))
ipa_rows.append(row)
with gr.Row():
components[key('add_ipadapter_button')] = gr.Button("✚ Add IPAdapter")
components[key('delete_ipadapter_button')] = gr.Button("âž– Delete IPAdapter", visible=False)
components[key('ipadapter_count_state')] = gr.State(1)
all_ipa_components_flat = images + weights + lora_strengths
all_ipa_components_flat += [
components[key('ipadapter_final_preset')],
components[key('ipadapter_final_weight')],
components[key('ipadapter_final_lora_strength')],
components[key('ipadapter_embeds_scaling')],
components[key('ipadapter_combine_method')],
]
components[key('all_ipadapter_components_flat')] = all_ipa_components_flat
return components
def create_flux1_ipadapter_ui(prefix: str, max_units=MAX_IPADAPTERS):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("IPAdapter Settings (FLUX.1)", open=False, visible=('flux1_ipadapter' in default_enabled_chains)) as accordion:
components[key('flux1_ipadapter_accordion')] = accordion
gr.Markdown("💡 **Tip:** Processed using the [Shakker-Labs/ComfyUI-IPAdapter-Flux](https://github.com/Shakker-Labs/ComfyUI-IPAdapter-Flux) node.")
ipa_rows, images, weights, start_percents, end_percents = [], [], [], [], []
components.update({
key('flux1_ipadapter_rows'): ipa_rows,
key('flux1_ipadapter_images'): images,
key('flux1_ipadapter_weights'): weights,
key('flux1_ipadapter_start_percents'): start_percents,
key('flux1_ipadapter_end_percents'): end_percents,
})
for i in range(max_units):
with gr.Row(visible=(i < 1)) as row:
with gr.Column(scale=1):
images.append(gr.Image(label=f"IPAdapter Image {i+1}", type="pil", sources=["upload"], height=256))
with gr.Column(scale=2):
weights.append(gr.Slider(label="Weight", minimum=0.0, maximum=2.0, step=0.05, value=0.6, interactive=True))
with gr.Row():
start_percents.append(gr.Slider(label="Start At", minimum=0.0, maximum=1.0, step=0.01, value=0.0, interactive=True))
end_percents.append(gr.Slider(label="End At", minimum=0.0, maximum=1.0, step=0.01, value=0.6, interactive=True))
ipa_rows.append(row)
with gr.Row():
components[key('add_flux1_ipadapter_button')] = gr.Button("✚ Add IPAdapter (FLUX)")
components[key('delete_flux1_ipadapter_button')] = gr.Button("âž– Delete IPAdapter (FLUX)", visible=False)
components[key('flux1_ipadapter_count_state')] = gr.State(1)
all_flux1_ipa_components_flat = images + weights + start_percents + end_percents
components[key('all_flux1_ipadapter_components_flat')] = all_flux1_ipa_components_flat
return components
def create_sd3_ipadapter_ui(prefix: str, max_units=MAX_IPADAPTERS):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("IPAdapter Settings (SD3)", open=False, visible=('sd3_ipadapter' in default_enabled_chains)) as accordion:
components[key('sd3_ipadapter_accordion')] = accordion
gr.Markdown("💡 **Tip:** Processed using the [Slickytail/ComfyUI-InstantX-IPAdapter-SD3](https://github.com/Slickytail/ComfyUI-InstantX-IPAdapter-SD3) node.")
ipa_rows, images, weights, start_percents, end_percents = [], [], [], [], []
components.update({
key('sd3_ipadapter_rows'): ipa_rows,
key('sd3_ipadapter_images'): images,
key('sd3_ipadapter_weights'): weights,
key('sd3_ipadapter_start_percents'): start_percents,
key('sd3_ipadapter_end_percents'): end_percents,
})
for i in range(max_units):
with gr.Row(visible=(i < 1)) as row:
with gr.Column(scale=1):
images.append(gr.Image(label=f"IPAdapter Image {i+1}", type="pil", sources=["upload"], height=256))
with gr.Column(scale=2):
weights.append(gr.Slider(label="Weight", minimum=0.0, maximum=2.0, step=0.05, value=0.5, interactive=True))
with gr.Row():
start_percents.append(gr.Slider(label="Start At", minimum=0.0, maximum=1.0, step=0.01, value=0.0, interactive=True))
end_percents.append(gr.Slider(label="End At", minimum=0.0, maximum=1.0, step=0.01, value=1.0, interactive=True))
ipa_rows.append(row)
with gr.Row():
components[key('add_sd3_ipadapter_button')] = gr.Button("✚ Add IPAdapter (SD3)")
components[key('delete_sd3_ipadapter_button')] = gr.Button("âž– Delete IPAdapter (SD3)", visible=False)
components[key('sd3_ipadapter_count_state')] = gr.State(1)
all_sd3_ipa_components_flat = images + weights + start_percents + end_percents
components[key('all_sd3_ipadapter_components_flat')] = all_sd3_ipa_components_flat
return components
def create_style_ui(prefix: str):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Style Settings (FLUX.1)", open=False, visible=('style' in default_enabled_chains)) as accordion:
components[key('style_accordion')] = accordion
style_rows, images, strengths = [], [], []
components.update({
key('style_rows'): style_rows,
key('style_images'): images,
key('style_strengths'): strengths
})
for i in range(5):
with gr.Row(visible=(i < 1)) as row:
with gr.Column(scale=1):
images.append(gr.Image(label=f"Style Image {i+1}", type="pil", sources=["upload"], height=256))
with gr.Column(scale=2):
strengths.append(gr.Slider(label="Strength", minimum=0.0, maximum=2.0, step=0.05, value=1.0, interactive=True))
style_rows.append(row)
with gr.Row():
components[key('add_style_button')] = gr.Button("✚ Add Style (FLUX)")
components[key('delete_style_button')] = gr.Button("âž– Delete Style (FLUX)", visible=False)
components[key('style_count_state')] = gr.State(1)
all_style_components_flat = images + strengths
components[key('all_style_components_flat')] = all_style_components_flat
return components
def create_embedding_ui(prefix: str):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Embedding Settings", open=False, visible=('embedding' in default_enabled_chains)) as accordion:
components[key('embedding_accordion')] = accordion
gr.Markdown("💡 **Tip:** When downloading from Civitai, please use the **Version ID**, not the Model ID. You can find the Version ID in the URL (e.g., `civitai.com/models/123?modelVersionId=456`) or under the model's download button. For example, entering the Version ID 456 will automatically save the file as \"civitai/456.safetensors\", and you will need to manually enter `embedding:civitai/456` in either your prompt or negative prompt to activate it. When downloading from Hugging Face, please use the format: repo_id/filename.extension or repo_id/folder_path/filename.extension (e.g., ilikebigturtles/lazypos/lazypos.safetensors or ilikebigturtles/lazyneg/lazyneg.safetensors). For Hugging Face files, you will need to enter embedding:huggingface/filename (e.g., entering embedding:huggingface/lazypos in your positive prompt, or embedding:huggingface/lazyneg in your negative prompt) to activate it. For uploaded files, enter `embedding:upload_file/filename` in either your positive prompt or negative prompt to activate it.")
embedding_rows, sources, ids, files, upload_buttons = [], [], [], [], []
components.update({
key('embedding_rows'): embedding_rows,
key('embeddings_sources'): sources,
key('embeddings_ids'): ids,
key('embeddings_files'): files,
key('embeddings_uploads'): upload_buttons
})
for i in range(MAX_EMBEDDINGS):
with gr.Row(visible=(i < 1)) as row:
sources.append(gr.Dropdown(label=f"Embedding Source {i+1}", choices=LORA_SOURCE_CHOICES, value="Civitai", scale=1, interactive=True))
ids.append(gr.Textbox(label="Civitai Version ID / HF file / Upload File", scale=3, interactive=True, type="text"))
upload_btn = gr.UploadButton("Upload", file_types=[".safetensors"], scale=1)
files.append(gr.State(None))
upload_buttons.append(upload_btn)
embedding_rows.append(row)
with gr.Row():
components[key('add_embedding_button')] = gr.Button("✚ Add Embedding")
components[key('delete_embedding_button')] = gr.Button("âž– Delete Embedding", visible=False)
components[key('embedding_count_state')] = gr.State(1)
all_embedding_components_flat = []
for i in range(MAX_EMBEDDINGS):
all_embedding_components_flat.extend([sources[i], ids[i], files[i]])
components[key('all_embedding_components_flat')] = all_embedding_components_flat
return components
def create_conditioning_ui(prefix: str):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Conditioning Settings", open=False, visible=('conditioning' in default_enabled_chains)) as accordion:
components[key('conditioning_accordion')] = accordion
gr.Markdown("💡 **Tip:** Define rectangular areas and assign specific prompts to them. Coordinates (X, Y) start from the top-left corner.")
cond_rows, prompts, widths, heights, xs, ys, strengths = [], [], [], [], [], [], []
components.update({
key('conditioning_rows'): cond_rows,
key('conditioning_prompts'): prompts,
key('conditioning_widths'): widths,
key('conditioning_heights'): heights,
key('conditioning_xs'): xs,
key('conditioning_ys'): ys,
key('conditioning_strengths'): strengths
})
for i in range(MAX_CONDITIONINGS):
with gr.Column(visible=(i < 1)) as row_wrapper:
prompts.append(gr.Textbox(label=f"Area Prompt {i+1}", lines=2, interactive=True))
with gr.Row():
xs.append(gr.Number(label="X", value=0, interactive=True, step=8, scale=1))
ys.append(gr.Number(label="Y", value=0, interactive=True, step=8, scale=1))
widths.append(gr.Number(label="Width", value=512, interactive=True, step=8, scale=1))
heights.append(gr.Number(label="Height", value=512, interactive=True, step=8, scale=1))
strengths.append(gr.Slider(label="Strength", minimum=0.1, maximum=2.0, step=0.05, value=1.0, interactive=True, scale=2))
cond_rows.append(row_wrapper)
with gr.Row():
components[key('add_conditioning_button')] = gr.Button("✚ Add Area")
components[key('delete_conditioning_button')] = gr.Button("âž– Delete Area", visible=False)
components[key('conditioning_count_state')] = gr.State(1)
all_cond_components_flat = prompts + widths + heights + xs + ys + strengths
components[key('all_conditioning_components_flat')] = all_cond_components_flat
return components
def on_vae_upload(file_obj):
if not file_obj:
return gr.update(), gr.update(), None
uploaded_filename = save_uploaded_file(file_obj, VAE_DIR)
return uploaded_filename, "Upload File", file_obj
def create_vae_override_ui(prefix: str):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("VAE Settings (Override)", open=False, visible=('vae' in default_enabled_chains)) as vae_accordion:
components[key('vae_accordion')] = vae_accordion
gr.Markdown("💡 **Tip:** When downloading from Civitai, please use the **Version ID**, not the Model ID. You can find the Version ID in the URL (e.g., `civitai.com/models/123?modelVersionId=456`) or under the model's download button. When downloading from Hugging Face, please use the format: `repo_id/filename.extension` or `repo_id/folder_path/filename.extension` (e.g., `madebyollin/sdxl-vae-fp16-fix/sdxl_vae.safetensors`).")
with gr.Row():
components[key('vae_source')] = gr.Dropdown(
label="VAE Source",
choices=LORA_SOURCE_CHOICES,
value="Civitai",
scale=1,
interactive=True
)
components[key('vae_id')] = gr.Textbox(
label="Civitai Version ID / HF file / Upload File",
scale=3,
interactive=True,
type="text"
)
upload_btn = gr.UploadButton(
"Upload",
file_types=[".safetensors"],
scale=1
)
components[key('vae_upload_button')] = upload_btn
components[key('vae_file')] = gr.State(None)
upload_btn.upload(
fn=on_vae_upload,
inputs=[upload_btn],
outputs=[components[key('vae_id')], components[key('vae_source')], components[key('vae_file')]]
)
return components
def create_reference_latent_ui(prefix: str, max_units=10):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Reference Edit Settings", open=False, visible=('reference_latent' in default_enabled_chains)) as ref_accordion:
components[key('reference_latent_accordion')] = ref_accordion
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
ref_image_groups = []
ref_image_inputs = []
with gr.Row():
for i in range(max_units):
with gr.Column(visible=(i < 1), min_width=160) as img_col:
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
ref_image_groups.append(img_col)
ref_image_inputs.append(img_comp)
components[key('reference_latent_rows')] = ref_image_groups
components[key('reference_latent_images')] = ref_image_inputs
with gr.Row():
components[key('add_reference_latent_button')] = gr.Button("✚ Add Reference Image")
components[key('delete_reference_latent_button')] = gr.Button("âž– Delete Reference Image", visible=False)
components[key('reference_latent_count_state')] = gr.State(1)
components[key('all_reference_latent_components_flat')] = ref_image_inputs
return components
def create_hidream_o1_reference_ui(prefix: str, max_units=10):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("HiDream-O1 Reference Edit Settings", open=False, visible=('hidream_o1_reference' in default_enabled_chains)) as ref_accordion:
components[key('hidream_o1_reference_accordion')] = ref_accordion
gr.Markdown("💡 **Tip:** Please use **HiDream-O1-Image-Dev** (HiDream-O1-Image will time out), and set the resolution to **4.0MP** (e.g., 2048x2048). In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
ref_image_groups = []
ref_image_inputs = []
with gr.Row():
for i in range(max_units):
with gr.Column(visible=(i < 1), min_width=160) as img_col:
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
ref_image_groups.append(img_col)
ref_image_inputs.append(img_comp)
components[key('hidream_o1_reference_rows')] = ref_image_groups
components[key('hidream_o1_reference_images')] = ref_image_inputs
with gr.Row():
components[key('add_hidream_o1_reference_button')] = gr.Button("✚ Add Reference Image")
components[key('delete_hidream_o1_reference_button')] = gr.Button("âž– Delete Reference Image", visible=False)
components[key('hidream_o1_reference_count_state')] = gr.State(1)
components[key('all_hidream_o1_reference_components_flat')] = ref_image_inputs
return components
def create_sensenova_reference_ui(prefix: str, max_units=10):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("SenseNova Reference Edit Settings", open=False, visible=('sensenova_reference' in default_enabled_chains)) as ref_accordion:
components[key('sensenova_reference_accordion')] = ref_accordion
gr.Markdown("💡 **Tip:** For SenseNova models, this feature enables reference image editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
ref_image_groups = []
ref_image_inputs = []
with gr.Row():
for i in range(max_units):
with gr.Column(visible=(i < 1), min_width=160) as img_col:
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
ref_image_groups.append(img_col)
ref_image_inputs.append(img_comp)
components[key('sensenova_reference_rows')] = ref_image_groups
components[key('sensenova_reference_images')] = ref_image_inputs
with gr.Row():
components[key('add_sensenova_reference_button')] = gr.Button("✚ Add Reference Image")
components[key('delete_sensenova_reference_button')] = gr.Button("âž– Delete Reference Image", visible=False)
components[key('sensenova_reference_count_state')] = gr.State(1)
components[key('all_sensenova_reference_components_flat')] = ref_image_inputs
return components
def create_joyai_reference_ui(prefix: str, max_units=2):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("JoyAI Reference Edit Settings", open=False, visible=('joyai_image' in default_enabled_chains)) as ref_accordion:
components[key('joyai_reference_accordion')] = ref_accordion
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit** (JoyAI-Image-Edit recommended), while adding multiple images performs an **Image Combine** (JoyAI-Image-Edit-Plus recommended with ZeroGPU Duration (s) set to 120).")
ref_image_groups = []
ref_image_inputs = []
with gr.Row():
for i in range(max_units):
with gr.Column(visible=(i < 1), min_width=160) as img_col:
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
ref_image_groups.append(img_col)
ref_image_inputs.append(img_comp)
components[key('joyai_reference_rows')] = ref_image_groups
components[key('joyai_reference_images')] = ref_image_inputs
with gr.Row():
components[key('add_joyai_reference_button')] = gr.Button("✚ Add Reference Image")
components[key('delete_joyai_reference_button')] = gr.Button("âž– Delete Reference Image", visible=False)
components[key('joyai_reference_count_state')] = gr.State(1)
components[key('all_joyai_reference_components_flat')] = ref_image_inputs
return components
def create_reference_image_ui(prefix: str, max_units=10):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Reference Image Edit Settings", open=False, visible=('reference_image' in default_enabled_chains)) as ref_accordion:
components[key('reference_image_accordion')] = ref_accordion
gr.Markdown("💡 **Tip:** For multimodal models, this feature enables powerful editing and combining capabilities. In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
ref_image_groups = []
ref_image_inputs = []
with gr.Row():
for i in range(max_units):
with gr.Column(visible=(i < 1), min_width=160) as img_col:
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
ref_image_groups.append(img_col)
ref_image_inputs.append(img_comp)
components[key('reference_image_rows')] = ref_image_groups
components[key('reference_image_images')] = ref_image_inputs
with gr.Row():
components[key('add_reference_image_button')] = gr.Button("✚ Add Reference Image")
components[key('delete_reference_image_button')] = gr.Button("âž– Delete Reference Image", visible=False)
components[key('reference_image_count_state')] = gr.State(1)
components[key('all_reference_image_components_flat')] = ref_image_inputs
return components
def create_pid_ui(prefix: str):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("PiD Settings", open=False, visible=('pid' in default_enabled_chains)) as pid_accordion:
components[key('pid_accordion')] = pid_accordion
gr.Markdown("💡 **Tip:** Use PiD (Pixel Diffusion Decoder) instead of the VAE Decoder for 4x decoding.")
with gr.Row():
components[key('pid_settings')] = gr.Dropdown(
label="PiD Mode",
choices=["OFF", "ON"],
value="OFF",
interactive=True
)
return components
def create_krea2_identity_edit_ui(prefix: str, max_units=2):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Krea2 Identity Edit Settings", open=False, visible=('krea2_identity_edit' in default_enabled_chains)) as ref_accordion:
components[key('krea2_identity_edit_accordion')] = ref_accordion
gr.Markdown("💡 **Tip:** Processed using the [lbouaraba/comfyui-krea2edit](https://github.com/lbouaraba/comfyui-krea2edit) node. (Krea-2-Turbo recommended, Krea-2-Raw need set ZeroGPU Duration (s) to 120 ) In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
ref_image_groups = []
ref_image_inputs = []
with gr.Row():
for i in range(max_units):
with gr.Column(visible=(i < 1), min_width=160) as img_col:
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
ref_image_groups.append(img_col)
ref_image_inputs.append(img_comp)
components[key('krea2_identity_edit_rows')] = ref_image_groups
components[key('krea2_identity_edit_images')] = ref_image_inputs
with gr.Row():
components[key('add_krea2_identity_edit_button')] = gr.Button("✚ Add Reference Image")
components[key('delete_krea2_identity_edit_button')] = gr.Button("âž– Delete Reference Image", visible=False)
components[key('krea2_identity_edit_count_state')] = gr.State(1)
components[key('all_krea2_identity_edit_components_flat')] = ref_image_inputs
return components
def create_qwen_image_edit_ui(prefix: str, max_units=3):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Qwen-Image Edit Settings", open=False, visible=('qwen_image_edit' in default_enabled_chains)) as ref_accordion:
components[key('qwen_image_edit_accordion')] = ref_accordion
gr.Markdown("💡 **Tip:** (lightx2v/Qwen-Image-Edit-2511-Lightning recommended) In txt2img mode, adding a single reference image performs an **Image Edit**, while adding multiple images performs an **Image Combine**.")
ref_image_groups = []
ref_image_inputs = []
with gr.Row():
for i in range(max_units):
with gr.Column(visible=(i < 1), min_width=160) as img_col:
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
ref_image_groups.append(img_col)
ref_image_inputs.append(img_comp)
components[key('qwen_image_edit_rows')] = ref_image_groups
components[key('qwen_image_edit_images')] = ref_image_inputs
with gr.Row():
components[key('add_qwen_image_edit_button')] = gr.Button("✚ Add Reference Image")
components[key('delete_qwen_image_edit_button')] = gr.Button("âž– Delete Reference Image", visible=False)
components[key('qwen_image_edit_count_state')] = gr.State(1)
components[key('all_qwen_image_edit_components_flat')] = ref_image_inputs
return components
def create_krea2_reference_edit_ui(prefix: str, max_units=3):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Krea2 Style Reference Edit Settings", open=False, visible=('krea2_style_reference' in default_enabled_chains)) as ref_accordion:
components[key('krea2_reference_edit_accordion')] = ref_accordion
gr.Markdown("💡 **Tip:** (Krea-2-Turbo recommended) Add style reference images to perform style reference editing.")
ref_image_groups = []
ref_image_inputs = []
with gr.Row():
for i in range(max_units):
with gr.Column(visible=(i < 1), min_width=160) as img_col:
img_comp = gr.Image(type="pil", label=f"Ref. {i+1}", sources=["upload"], height=150)
ref_image_groups.append(img_col)
ref_image_inputs.append(img_comp)
components[key('krea2_reference_edit_rows')] = ref_image_groups
components[key('krea2_reference_edit_images')] = ref_image_inputs
with gr.Row():
components[key('add_krea2_reference_edit_button')] = gr.Button("✚ Add Reference Image")
components[key('delete_krea2_reference_edit_button')] = gr.Button("âž– Delete Reference Image", visible=False)
components[key('krea2_reference_edit_count_state')] = gr.State(1)
components[key('all_krea2_reference_edit_components_flat')] = ref_image_inputs
return components
def create_qwen_image_2_1_prompt_enhancer_ui(prefix: str):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Qwen-Image-2.1 Prompt Enhancer Settings", open=False, visible=('qwen_image_2_1_prompt_enhancer' in default_enabled_chains)) as enhancer_accordion:
components[key('qwen_image_2_1_prompt_enhancer_accordion')] = enhancer_accordion
gr.Markdown("💡 **Tip:** Automatically selects between [Qwen-Image-2.1-PE-T2I](https://huggingface.co/Qwen/Qwen-Image-2.1-PE-T2I) and [Qwen-Image-2.1-PE-I2I](https://huggingface.co/Qwen/Qwen-Image-2.1-PE-I2I) for prompt enhancement based on input context.")
with gr.Row():
components[key('qwen_image_2_1_prompt_enhancer_enable')] = gr.Checkbox(
label="Enable Qwen-Image-2.1 Prompt Enhancer",
value=False,
interactive=True,
scale=2
)
components[key('qwen_image_2_1_prompt_enhancer_thinking')] = gr.Checkbox(
label="Enable Thinking (Requires longer ZeroGPU Duration)",
value=False,
interactive=True,
scale=2
)
components[key('qwen_image_2_1_prompt_enhancer_max_length')] = gr.Number(
label="Max Length",
value=4096,
precision=0,
minimum=16,
step=16,
interactive=True,
scale=1
)
return components
DEFAULT_MING_IMAGE_SYSTEM_PROMPT = (
"You are a senior visual designer and image-prompt engineer. Expand the user's request into one precise, high-resolution Figma-style caption. Return only one JSON object. "
"Use exactly two top-level keys. `canvas_settings` contains exactly `aspect_ratio`, `ambient_lighting`, and `image_style`. `layers` lists visible groups from background to topmost overlay. Every layer contains exactly `description`, `coordinates`, `hierarchy_and_relation`, and `color_specs`; `color_specs` is an array of hex colors. "
'`coordinates` MUST be one string, never an object or array, in exactly this form: `"cx: 0.500, cy: 0.500, w: 1.000, h: 1.000"`. Values are normalized; each bbox encloses its complete owned object and stays inside the canvas. '
"A layer is one selectable visible semantic group: background, full person, coherent object, panel, card, row, or text block. Prefer the fewest groups that preserve the layout. Keep people and objects intact. Never create invisible parents, guides, placeholders, empty layers, duplicate summaries, or multiple owners for one element. "
"Preserve every user-supplied rendered string character-for-character and as one contiguous string. Unless multiple visible copies are requested, it must occur exactly once across all `description` fields and zero times in `hierarchy_and_relation`. Quote it only where describing its visible rendering; refer to the related subject elsewhere with unquoted semantic wording. Enumerate intended copy, invent extra copy sparingly, and never hide content behind \"other text\", \"remaining labels\", or \"etc.\" "
"Describe concrete composition, typography, materials, texture, lighting, pose, and camera treatment without literary filler. Use `hierarchy_and_relation` only for ownership, alignment, containment, stacking, and occlusion. "
"Infer structured layouts first. Use one complete layer per card and state its row and column. A compact secondary table may be one layer only if every header and cell is listed; otherwise use a visible shared frame when present, one complete header, and one complete layer per body row, binding values to columns and stating blanks. Enumerate sequences, schedules, spans, gaps, and vacant tracks in visual order. Do not mistake ordinary alignment for a table. "
"Silently verify schema, string coordinates, Z-order, exact-text counts, geometry, bbox validity, and completeness."
)
def create_ming_image_prompt_enhancer_ui(prefix: str):
components = {}
key = lambda name: f"{name}_{prefix}"
with gr.Accordion("Ming-Image Prompt Enhancer Settings", open=False, visible=('ming_image_prompt_enhancer' in default_enabled_chains)) as enhancer_accordion:
components[key('ming_image_prompt_enhancer_accordion')] = enhancer_accordion
gr.Markdown("💡 **Tip:** Leverages [Qwen3.8-27B](https://huggingface.co/Qwen/Qwen3.8-27B) guided by the official [System Prompt](https://github.com/inclusionAI/Ming-Image#text-to-image-prompt-rewriting) to automatically enhance prompts into structured layouts based on input context.")
with gr.Row():
components[key('ming_image_prompt_enhancer_enable')] = gr.Checkbox(
label="Enable Ming-Image Prompt Enhancer",
value=False,
interactive=True,
scale=2
)
components[key('ming_image_prompt_enhancer_thinking')] = gr.Checkbox(
label="Enable Thinking (Requires longer ZeroGPU Duration)",
value=False,
interactive=True,
scale=2
)
components[key('ming_image_prompt_enhancer_max_length')] = gr.Number(
label="Max Length",
value=4096,
precision=0,
minimum=16,
step=16,
interactive=True,
scale=1
)
components[key('ming_image_prompt_enhancer_system_prompt')] = gr.Textbox(
label="System Prompt",
value=DEFAULT_MING_IMAGE_SYSTEM_PROMPT,
lines=4,
max_lines=12,
interactive=True
)
return components