Files
ComfyUI-Ethanfel-Prompt-Bui…/style_config.py

149 lines
6.2 KiB
Python

from __future__ import annotations
import json
from typing import Any
STYLE_CONFIG_SCHEMA = "sxcp_style_config_v1"
STYLE_COMBINE_MODES = ("replace", "add", "disabled")
STYLE_PRESETS = {
"category_default": {
"style": "",
"positive_suffix": "",
"summary": "category default style",
},
"realistic_photo": {
"style": "realistic adult photographic scene, natural camera capture",
"positive_suffix": "Use realistic skin texture, natural light response, coherent anatomy, readable contact points, and believable spatial depth.",
"summary": "realistic photographic style",
},
"creator_phone_photo": {
"style": "realistic creator-shot phone photo, natural adult social-media image",
"positive_suffix": "Use handheld camera realism, natural skin texture, readable body positioning, and believable room depth.",
"summary": "creator phone photo style",
},
"documentary_flash": {
"style": "realistic direct-flash documentary photo, raw adult snapshot",
"positive_suffix": "Use direct flash, natural skin texture, sharp foreground detail, visible contact points, and unpolished camera realism.",
"summary": "direct flash documentary style",
},
"cinematic_realism": {
"style": "cinematic realistic adult scene, natural lens perspective",
"positive_suffix": "Use realistic anatomy, readable blocking, natural depth, motivated lighting, and coherent camera perspective.",
"summary": "cinematic realism style",
},
"comic_pinup_colored_pencil": {
"style": "adult erotic coloured-pencil comic pin-up style",
"positive_suffix": "Use crisp comic linework, detailed hatching, warm erotic lighting, soft skin shading, and tactile textured paper.",
"summary": "colored-pencil comic pin-up style",
},
"flat_vector_comic": {
"style": "flat vector adult comic illustration",
"positive_suffix": "Use flat color, clean graphic shapes, crisp outlines, simplified shadows, and readable adult body positioning.",
"summary": "flat vector comic style",
},
}
def style_pool_preset_choices() -> list[str]:
return list(STYLE_PRESETS)
def style_combine_mode_choices() -> list[str]:
return list(STYLE_COMBINE_MODES)
def _clean_text(value: Any) -> str:
return str(value or "").strip()
def _join_text(*values: Any) -> str:
parts: list[str] = []
for value in values:
text = _clean_text(value)
if text and text not in parts:
parts.append(text.rstrip("."))
return ". ".join(parts)
def parse_style_config(style_config: str | dict[str, Any] | None) -> dict[str, Any]:
if not style_config:
return {"enabled": False, "combine_mode": "disabled", "style": "", "positive_suffix": "", "negative_prompt": ""}
if isinstance(style_config, dict):
raw = dict(style_config)
else:
try:
raw = json.loads(str(style_config))
except json.JSONDecodeError:
return {"enabled": False, "combine_mode": "disabled", "style": "", "positive_suffix": "", "negative_prompt": ""}
if raw.get("schema") != STYLE_CONFIG_SCHEMA:
return {"enabled": False, "combine_mode": "disabled", "style": "", "positive_suffix": "", "negative_prompt": ""}
combine_mode = _clean_text(raw.get("combine_mode")) or "replace"
if combine_mode not in STYLE_COMBINE_MODES:
combine_mode = "replace"
return {
"schema": STYLE_CONFIG_SCHEMA,
"version": 1,
"enabled": bool(raw.get("enabled", True)) and combine_mode != "disabled",
"combine_mode": combine_mode,
"preset": _clean_text(raw.get("preset")) or "category_default",
"style": _clean_text(raw.get("style")),
"positive_suffix": _clean_text(raw.get("positive_suffix")),
"negative_prompt": _clean_text(raw.get("negative_prompt")),
"summary": _clean_text(raw.get("summary")) or "style config",
}
def build_style_config_json(
*,
enabled: bool = True,
combine_mode: str = "replace",
preset: str = "category_default",
custom_style: str = "",
custom_positive_suffix: str = "",
custom_negative: str = "",
style_config: str | dict[str, Any] | None = "",
) -> str:
if combine_mode not in STYLE_COMBINE_MODES:
combine_mode = "replace"
base = parse_style_config(style_config)
preset_entry = STYLE_PRESETS.get(preset, STYLE_PRESETS["category_default"])
style = _clean_text(custom_style) or preset_entry["style"]
positive_suffix = _clean_text(custom_positive_suffix) or preset_entry["positive_suffix"]
negative_prompt = _clean_text(custom_negative)
if combine_mode == "add" and base.get("enabled"):
style = _join_text(base.get("style"), style)
positive_suffix = _join_text(base.get("positive_suffix"), positive_suffix)
negative_prompt = _join_text(base.get("negative_prompt"), negative_prompt)
payload = {
"schema": STYLE_CONFIG_SCHEMA,
"version": 1,
"enabled": bool(enabled) and combine_mode != "disabled",
"combine_mode": combine_mode,
"preset": preset,
"style": style,
"positive_suffix": positive_suffix,
"negative_prompt": negative_prompt,
"summary": "style disabled" if not enabled or combine_mode == "disabled" else preset_entry["summary"],
}
return json.dumps(payload, ensure_ascii=True, sort_keys=True)
def resolve_style_fields(base_style: str, base_positive_suffix: str, style_config: str | dict[str, Any] | None) -> tuple[str, str]:
config = parse_style_config(style_config)
if not config.get("enabled"):
return base_style, base_positive_suffix
if config["combine_mode"] == "add":
return (
_join_text(base_style, config.get("style")),
_join_text(base_positive_suffix, config.get("positive_suffix")),
)
return config.get("style", "") or base_style, config.get("positive_suffix", "") or base_positive_suffix
def merge_negative_prompt(base_negative: str, style_config: str | dict[str, Any] | None) -> str:
config = parse_style_config(style_config)
if not config.get("enabled"):
return base_negative
return _join_text(base_negative, config.get("negative_prompt"))