From 2924fcd9e239bf9689bb3bda72474fd58d40d851 Mon Sep 17 00:00:00 2001 From: chrmc40 Date: Sat, 27 Jun 2026 15:31:10 -0700 Subject: [PATCH] Krea 2 Realism recipe: t2i/i2i templates + inpaint/i2i/upscale editor-modal toggles (abliterated encoder, realism/bypass LoRAs, ClownsharKSampler in i2i; seam-safe qwen VAE in masked/tiled modes). Plus full-frame i2i editor button, Krea 2 Turbo style-LoRA picker, and viewer/model-settings updates. --- .gitignore | 1 + __init__.py | 1 + core/lora_catalog.py | 109 + data/loras/krea2-styles.json | 95 + data/models/krea2_raw.json | 13 + data/templates/krea2-raw-t2i.json | 16 +- .../templates/krea2-turbo-i2i-highdetail.json | 604 ++-- data/templates/krea2-turbo-i2i.json | 506 ++-- data/templates/krea2-turbo-realism-i2i.json | 140 + data/templates/krea2-turbo-realism-t2i.json | 129 + .../templates/krea2-turbo-t2i-highdetail.json | 518 ++-- data/templates/krea2-turbo-t2i.json | 454 +-- js/lib/editor-context-menu.js | 2 +- js/lib/fullscreen-bridge.js | 2 +- js/lib/i2i-background.js | 557 ++++ js/lib/inpaint-background.js | 27 +- js/lib/model-indicator-bridge.js | 2 +- js/lib/model-settings-bridge.js | 131 + js/lib/svelte/assets/promptchain-svelte.css | 116 +- js/lib/svelte/promptchain-model-settings.js | 417 ++- js/lib/svelte/promptchain-viewer.js | 2646 ++++++++++++----- js/lib/upscale-background.js | 60 +- js/lib/upscale-from-image.js | 34 +- js/lib/viewer-bridge.js | 9 + js/main.js | 2 +- pyproject.toml | 2 +- svelte-src/components/EditModal.svelte | 73 +- svelte-src/components/I2IModal.svelte | 648 ++++ svelte-src/components/ImageViewer.svelte | 67 +- svelte-src/components/InpaintModal.svelte | 11 + .../components/UpscaleOptionsModal.svelte | 11 + .../model/ModelSettingsModal.svelte | 5 + .../components/model/SettingsTab.svelte | 136 + 33 files changed, 5649 insertions(+), 1895 deletions(-) create mode 100644 core/lora_catalog.py create mode 100644 data/loras/krea2-styles.json create mode 100644 data/templates/krea2-turbo-realism-i2i.json create mode 100644 data/templates/krea2-turbo-realism-t2i.json create mode 100644 js/lib/i2i-background.js create mode 100644 svelte-src/components/I2IModal.svelte diff --git a/.gitignore b/.gitignore index 0baa87f..54ae32f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ data/* !data/prompts/ !data/models/ !data/templates/ +!data/loras/ !data/tag-builder/ data/models/_hash_index.json cache/ diff --git a/__init__.py b/__init__.py index 975eccf..cba710b 100644 --- a/__init__.py +++ b/__init__.py @@ -49,6 +49,7 @@ from .core import tag_api # noqa: F401 from .core import wildcard_api # noqa: F401 from .core import history_api # noqa: F401 from .core import model_api # noqa: F401 +from .core import lora_catalog # noqa: F401 from .core import civitai_api # noqa: F401 from .core import system_api # noqa: F401 from .core import node_install_api # noqa: F401 diff --git a/core/lora_catalog.py b/core/lora_catalog.py new file mode 100644 index 0000000..3313821 --- /dev/null +++ b/core/lora_catalog.py @@ -0,0 +1,109 @@ +# LoRA catalog — curated, scope-filtered LoRA packs the model panel offers as +# swappable "styles". Data lives in data/loras/*.json (shipped) and +# user/PromptChain/loras/*.json (user-added, shadows by id). Each file: +# { "scope": {type, architecture, family}, "loras": [ {id,label,filename, +# folder,trigger,strength_default,size_bytes,source}, ... ] } +# Served scope-filtered to the active model so e.g. Krea 2 Turbo styles only +# surface on Krea 2 Turbo. + +import json +from pathlib import Path +from typing import Optional + +from aiohttp import web +import server +import folder_paths + +routes = server.PromptServer.instance.routes + +_cache: Optional[list] = None +_cache_signature: Optional[tuple] = None + + +def _system_dir() -> Path: + return Path(__file__).parent.parent / "data" / "loras" + + +def _user_dir() -> Path: + return Path(folder_paths.get_user_directory()) / "PromptChain" / "loras" + + +def _dir_signature() -> tuple: + # Mirror model_settings' mtime watch: a dir's mtime bumps when files are + # added/removed/atomically replaced (a /deploy pull), so shipped catalogs + # refresh without a restart. In-place edits to an existing file don't bump + # the dir mtime — those need a restart. + sig = [] + for directory in (_system_dir(), _user_dir()): + try: + sig.append(directory.stat().st_mtime_ns) + except OSError: + sig.append(0) + return tuple(sig) + + +def _read_json(path: Path) -> Optional[dict]: + try: + with open(path, encoding="utf-8") as fh: + data = json.load(fh) + return data if isinstance(data, dict) else None + except Exception: + return None + + +def _ingest() -> list: + """All catalog files as (scope, loras) records. User dir is read last so a + user catalog with the same lora id shadows the shipped one.""" + by_id: dict[str, dict] = {} + order: list[str] = [] + for directory in (_system_dir(), _user_dir()): + if not directory.is_dir(): + continue + for path in sorted(directory.glob("*.json")): + data = _read_json(path) + if not data: + continue + scope = data.get("scope", {}) + for lora in data.get("loras", []): + lid = lora.get("id") or lora.get("filename") + if not lid: + continue + if lid not in by_id: + order.append(lid) + by_id[lid] = {**lora, "scope": scope} + return [by_id[lid] for lid in order] + + +def _get_all() -> list: + global _cache, _cache_signature + sig = _dir_signature() + if _cache is not None and sig == _cache_signature: + return _cache + _cache = _ingest() + _cache_signature = sig + return _cache + + +def _matches_scope(scope: dict, architecture: str, family: Optional[str]) -> bool: + scope_type = scope.get("type", "architecture") + if scope_type == "family": + return scope.get("architecture") == architecture and scope.get("family") == family + return scope.get("architecture") == architecture + + +def list_loras(architecture: str, family: Optional[str] = None) -> list: + """Scope-filtered LoRAs for the active model, scope stripped from each.""" + out = [] + for lora in _get_all(): + if _matches_scope(lora.get("scope", {}), architecture, family): + out.append({k: v for k, v in lora.items() if k != "scope"}) + return out + + +@routes.get("/promptchain/loras/catalog") +async def _api_lora_catalog(request): + architecture = request.query.get("architecture", "").strip() + family = request.query.get("family", "").strip() or None + if not architecture: + return web.json_response({"loras": []}) + return web.json_response({"loras": list_loras(architecture, family)}) diff --git a/data/loras/krea2-styles.json b/data/loras/krea2-styles.json new file mode 100644 index 0000000..02334ff --- /dev/null +++ b/data/loras/krea2-styles.json @@ -0,0 +1,95 @@ +{ + "scope": { "type": "family", "architecture": "krea2", "family": "krea2_turbo" }, + "loras": [ + { + "id": "krea2-retroanime", + "label": "Retro Anime", + "filename": "krea2_retroanime.safetensors", + "folder": "loras", + "trigger": "Purple retro anime style", + "strength_default": 1.0, + "size_bytes": 469291992, + "source": { "provider": "huggingface", "repo": "Comfy-Org/Krea-2", "path": "loras/krea2_retroanime.safetensors" } + }, + { + "id": "krea2-darkbrush", + "label": "Dark Brush", + "filename": "krea2_darkbrush.safetensors", + "folder": "loras", + "trigger": "monochrome ink wash style", + "strength_default": 1.0, + "size_bytes": 469291992, + "source": { "provider": "huggingface", "repo": "Comfy-Org/Krea-2", "path": "loras/krea2_darkbrush.safetensors" } + }, + { + "id": "krea2-sunsetblur", + "label": "Sunset Blur", + "filename": "krea2_sunsetblur.safetensors", + "folder": "loras", + "trigger": "ethereal motion blur style", + "strength_default": 1.0, + "size_bytes": 469291992, + "source": { "provider": "huggingface", "repo": "Comfy-Org/Krea-2", "path": "loras/krea2_sunsetblur.safetensors" } + }, + { + "id": "krea2-neondrip", + "label": "Neon Drip", + "filename": "krea2_neondrip.safetensors", + "folder": "loras", + "trigger": "Textured abstract style", + "strength_default": 1.0, + "size_bytes": 469291992, + "source": { "provider": "huggingface", "repo": "Comfy-Org/Krea-2", "path": "loras/krea2_neondrip.safetensors" } + }, + { + "id": "krea2-vintagetarot", + "label": "Vintage Tarot", + "filename": "krea2_vintagetarot.safetensors", + "folder": "loras", + "trigger": "vintage tarot style", + "strength_default": 1.0, + "size_bytes": 469291992, + "source": { "provider": "huggingface", "repo": "Comfy-Org/Krea-2", "path": "loras/krea2_vintagetarot.safetensors" } + }, + { + "id": "krea2-dotmatrix", + "label": "Dot Matrix", + "filename": "krea2_dotmatrix.safetensors", + "folder": "loras", + "trigger": "Monochrome stippling style", + "strength_default": 1.0, + "size_bytes": 469291992, + "source": { "provider": "huggingface", "repo": "Comfy-Org/Krea-2", "path": "loras/krea2_dotmatrix.safetensors" } + }, + { + "id": "krea2-softwatercolor", + "label": "Soft Watercolor", + "filename": "krea2_softwatercolor.safetensors", + "folder": "loras", + "trigger": "Art Deco watercolor style", + "strength_default": 1.0, + "size_bytes": 469291992, + "source": { "provider": "huggingface", "repo": "Comfy-Org/Krea-2", "path": "loras/krea2_softwatercolor.safetensors" } + }, + { + "id": "krea2-kidsdrawing", + "label": "Kids Drawing", + "filename": "krea2_kidsdrawing.safetensors", + "folder": "loras", + "trigger": "naive expressive sketch style", + "strength_default": 1.0, + "size_bytes": 469291992, + "source": { "provider": "huggingface", "repo": "Comfy-Org/Krea-2", "path": "loras/krea2_kidsdrawing.safetensors" } + }, + { + "id": "krea2-rainywindow", + "label": "Rainy Window", + "filename": "krea2_rainywindow.safetensors", + "folder": "loras", + "trigger": "rainy window style", + "strength_default": 1.0, + "size_bytes": 469291992, + "source": { "provider": "huggingface", "repo": "Comfy-Org/Krea-2", "path": "loras/krea2_rainywindow.safetensors" } + } + ] +} diff --git a/data/models/krea2_raw.json b/data/models/krea2_raw.json index 5315abf..91d896f 100644 --- a/data/models/krea2_raw.json +++ b/data/models/krea2_raw.json @@ -96,6 +96,19 @@ "path": "vae/qwen_image_vae.safetensors" }, "sha256": null + }, + { + "label": "Filter-Bypass Projector-Scale LoRA", + "folder": "loras", + "filename": "krea2_filter_bypass_projector_scale.safetensors", + "size_bytes": 268, + "optional": true, + "source": { + "provider": "huggingface", + "repo": "Beinsezii/Krea-2-Raw-Projector-Scale-LoRA-Diffusers", + "path": "pytorch_lora_weights.safetensors" + }, + "sha256": null } ], "nodes": { diff --git a/data/templates/krea2-raw-t2i.json b/data/templates/krea2-raw-t2i.json index 7774add..8e575e7 100644 --- a/data/templates/krea2-raw-t2i.json +++ b/data/templates/krea2-raw-t2i.json @@ -38,7 +38,7 @@ 116 ], "widgets_values": [ - "" + "blurry, low-resolution, grainy, pixelated, distorted, compression artifacts, watermark" ], "properties": { "Node name for S&R": "CLIPTextEncode" @@ -57,10 +57,10 @@ "widgets_values": [ 826502184750291, "randomize", - 30, + 25, 3.5, - "er_sde", - "beta", + "euler", + "simple", 1 ], "properties": { @@ -145,8 +145,8 @@ 144 ], "widgets_values": [ - 1024, - 1024, + 1408, + 1408, 1 ], "properties": { @@ -183,8 +183,8 @@ "widgets_values": [ 0.90625, 0.5, - 1024, - 1024 + 1408, + 1408 ], "properties": { "Node name for S&R": "ModelSamplingFlux" diff --git a/data/templates/krea2-turbo-i2i-highdetail.json b/data/templates/krea2-turbo-i2i-highdetail.json index e0c0bb9..f04cbe9 100644 --- a/data/templates/krea2-turbo-i2i-highdetail.json +++ b/data/templates/krea2-turbo-i2i-highdetail.json @@ -1,289 +1,317 @@ -{ - "name": "Image-to-Image (High Detail)", - "category": "Generation", - "order": 4, - "scope": { - "type": "family", - "architecture": "krea2", - "family": "krea2_turbo" - }, - "created_at": 1782364000, - "id": "b7c3e8a1-4f2d-4a6b-9c0e-2a1f6d3b9f0a", - "nodes": [ - { - "type": "CLIPTextEncode", - "offset": [ - 820.0, - 20.0 - ], - "size": [ - 225, - 116 - ], - "widgets_values": [ - "" - ], - "properties": { - "Node name for S&R": "CLIPTextEncode" - } - }, - { - "type": "KSampler", - "offset": [ - 1440.0, - 120.0 - ], - "size": [ - 330, - 474 - ], - "widgets_values": [ - 826502184750291, - "randomize", - 8, - 1, - "euler", - "simple", - 0.6 - ], - "properties": { - "Node name for S&R": "KSampler" - } - }, - { - "type": "VAEUtils_VAEDecodeTiled", - "offset": [ - 1800.0, - 20.0 - ], - "size": [ - 300, - 220 - ], - "widgets_values": [ - -1, - false, - 512, - 64, - 4096, - 64 - ], - "properties": { - "Node name for S&R": "VAEUtils_VAEDecodeTiled" - } - }, - { - "type": "SaveImage", - "offset": [ - 2140.0, - -10.0 - ], - "size": [ - 276.984375, - 322.984375 - ], - "widgets_values": [ - "Krea2/img2img-highdetail/%date:yyyy-MM-dd_hhmmss%" - ], - "properties": {} - }, - { - "type": "CLIPLoader", - "offset": [ - 820.0, - 240.0 - ], - "size": [ - 346.78125, - 117.328125 - ], - "widgets_values": [ - "qwen3vl_4b_fp8_scaled.safetensors", - "krea2", - "default" - ], - "properties": { - "Node name for S&R": "CLIPLoader" - } - }, - { - "type": "UNETLoader", - "offset": [ - 1100.0, - 280.0 - ], - "size": [ - 371.65625, - 82.65625 - ], - "widgets_values": [ - "krea2_turbo_bf16.safetensors", - "default" - ], - "properties": { - "Node name for S&R": "UNETLoader" - } - }, - { - "type": "LoadImage", - "offset": [ - 780.0, - 420.0 - ], - "size": [ - 270, - 340 - ], - "widgets_values": [ - "", - "image" - ], - "properties": { - "Node name for S&R": "LoadImage" - } - }, - { - "type": "VAEUtils_CustomVAELoader", - "offset": [ - 1440.0, - 620.0 - ], - "size": [ - 366.40625, - 82.65625 - ], - "widgets_values": [ - "Wan2.1_VAE_upscale2x_imageonly_real_v1.safetensors", - true - ], - "properties": { - "Node name for S&R": "VAEUtils_CustomVAELoader" - } - }, - { - "type": "ConditioningKrea2Rebalance", - "offset": [ - 1100.0, - 20.0 - ], - "size": [ - 300, - 106 - ], - "widgets_values": [ - 2.0, - "1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.6,2.5,1.05,2.0,1.0" - ], - "properties": { - "Node name for S&R": "ConditioningKrea2Rebalance" - } - }, - { - "type": "VAEEncode", - "offset": [ - 1130.0, - 500.0 - ], - "size": [ - 225, - 72 - ], - "widgets_values": [], - "properties": { - "Node name for S&R": "VAEEncode" - } - }, - { - "type": "VAELoader", - "offset": [ - 780.0, - 790.0 - ], - "size": [ - 366.40625, - 82.65625 - ], - "widgets_values": [ - "qwen_image_vae.safetensors" - ], - "properties": { - "Node name for S&R": "VAELoader" - } - } - ], - "connections": [ - { - "from_node_idx": 0, - "from_slot": 0, - "to_node_idx": 8, - "to_slot": 0 - }, - { - "from_node_idx": 8, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 1 - }, - { - "from_node_idx": 0, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 2 - }, - { - "from_node_idx": 1, - "from_slot": 0, - "to_node_idx": 2, - "to_slot": 0 - }, - { - "from_node_idx": 7, - "from_slot": 0, - "to_node_idx": 2, - "to_slot": 1 - }, - { - "from_node_idx": 2, - "from_slot": 0, - "to_node_idx": 3, - "to_slot": 0 - }, - { - "from_node_idx": 4, - "from_slot": 0, - "to_node_idx": 0, - "to_slot": 0 - }, - { - "from_node_idx": 5, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 0 - }, - { - "from_node_idx": 9, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 3 - }, - { - "from_node_idx": 6, - "from_slot": 0, - "to_node_idx": 9, - "to_slot": 0 - }, - { - "from_node_idx": 10, - "from_slot": 0, - "to_node_idx": 9, - "to_slot": 1 - } - ], - "anchorConnections": [ - { - "from_slot": 1, - "to_node_idx": 0, - "to_slot": 1 - } - ] +{ + "name": "Image-to-Image (High Detail)", + "category": "Generation", + "order": 4, + "scope": { + "type": "family", + "architecture": "krea2", + "family": "krea2_turbo" + }, + "created_at": 1782364000, + "id": "b7c3e8a1-4f2d-4a6b-9c0e-2a1f6d3b9f0a", + "nodes": [ + { + "type": "CLIPTextEncode", + "offset": [ + 820.0, + 20.0 + ], + "size": [ + 225, + 116 + ], + "widgets_values": [ + "" + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + }, + { + "type": "KSampler", + "offset": [ + 1440.0, + 120.0 + ], + "size": [ + 330, + 474 + ], + "widgets_values": [ + 826502184750291, + "randomize", + 8, + 1, + "euler", + "simple", + 0.6 + ], + "properties": { + "Node name for S&R": "KSampler" + } + }, + { + "type": "VAEUtils_VAEDecodeTiled", + "offset": [ + 1800.0, + 20.0 + ], + "size": [ + 300, + 220 + ], + "widgets_values": [ + -1, + false, + 512, + 64, + 4096, + 64 + ], + "properties": { + "Node name for S&R": "VAEUtils_VAEDecodeTiled" + } + }, + { + "type": "SaveImage", + "offset": [ + 2140.0, + -10.0 + ], + "size": [ + 276.984375, + 322.984375 + ], + "widgets_values": [ + "Krea2/img2img-highdetail/%date:yyyy-MM-dd_hhmmss%" + ], + "properties": {} + }, + { + "type": "CLIPLoader", + "offset": [ + 820.0, + 240.0 + ], + "size": [ + 346.78125, + 117.328125 + ], + "widgets_values": [ + "qwen3vl_4b_fp8_scaled.safetensors", + "krea2", + "default" + ], + "properties": { + "Node name for S&R": "CLIPLoader" + } + }, + { + "type": "UNETLoader", + "offset": [ + 1100.0, + 280.0 + ], + "size": [ + 371.65625, + 82.65625 + ], + "widgets_values": [ + "krea2_turbo_bf16.safetensors", + "default" + ], + "properties": { + "Node name for S&R": "UNETLoader" + } + }, + { + "type": "LoadImage", + "offset": [ + 780.0, + 420.0 + ], + "size": [ + 270, + 340 + ], + "widgets_values": [ + "", + "image" + ], + "properties": { + "Node name for S&R": "LoadImage" + } + }, + { + "type": "VAEUtils_CustomVAELoader", + "offset": [ + 1440.0, + 620.0 + ], + "size": [ + 366.40625, + 82.65625 + ], + "widgets_values": [ + "Wan2.1_VAE_upscale2x_imageonly_real_v1.safetensors", + true + ], + "properties": { + "Node name for S&R": "VAEUtils_CustomVAELoader" + } + }, + { + "type": "ConditioningKrea2Rebalance", + "offset": [ + 1100.0, + 20.0 + ], + "size": [ + 300, + 106 + ], + "widgets_values": [ + 2.0, + "1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.6,2.5,1.05,2.0,1.0" + ], + "properties": { + "Node name for S&R": "ConditioningKrea2Rebalance" + } + }, + { + "type": "VAEEncode", + "offset": [ + 1130.0, + 500.0 + ], + "size": [ + 225, + 72 + ], + "widgets_values": [], + "properties": { + "Node name for S&R": "VAEEncode" + } + }, + { + "type": "VAELoader", + "offset": [ + 780.0, + 790.0 + ], + "size": [ + 366.40625, + 82.65625 + ], + "widgets_values": [ + "qwen_image_vae.safetensors" + ], + "properties": { + "Node name for S&R": "VAELoader" + } + }, + { + "type": "CLIPTextEncode", + "offset": [ + 820.0, + 220.0 + ], + "size": [ + 225, + 116 + ], + "widgets_values": [ + "" + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + } + ], + "connections": [ + { + "from_node_idx": 0, + "from_slot": 0, + "to_node_idx": 8, + "to_slot": 0 + }, + { + "from_node_idx": 8, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 1 + }, + { + "from_node_idx": 11, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 2 + }, + { + "from_node_idx": 1, + "from_slot": 0, + "to_node_idx": 2, + "to_slot": 0 + }, + { + "from_node_idx": 7, + "from_slot": 0, + "to_node_idx": 2, + "to_slot": 1 + }, + { + "from_node_idx": 2, + "from_slot": 0, + "to_node_idx": 3, + "to_slot": 0 + }, + { + "from_node_idx": 4, + "from_slot": 0, + "to_node_idx": 0, + "to_slot": 0 + }, + { + "from_node_idx": 5, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 0 + }, + { + "from_node_idx": 9, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 3 + }, + { + "from_node_idx": 6, + "from_slot": 0, + "to_node_idx": 9, + "to_slot": 0 + }, + { + "from_node_idx": 10, + "from_slot": 0, + "to_node_idx": 9, + "to_slot": 1 + }, + { + "from_node_idx": 4, + "from_slot": 0, + "to_node_idx": 11, + "to_slot": 0 + } + ], + "anchorConnections": [ + { + "from_slot": 1, + "to_node_idx": 0, + "to_slot": 1 + }, + { + "from_slot": 2, + "to_node_idx": 11, + "to_slot": 1 + } + ] } \ No newline at end of file diff --git a/data/templates/krea2-turbo-i2i.json b/data/templates/krea2-turbo-i2i.json index 61bb882..12aa742 100644 --- a/data/templates/krea2-turbo-i2i.json +++ b/data/templates/krea2-turbo-i2i.json @@ -1,240 +1,268 @@ -{ - "name": "Image-to-Image", - "category": "Generation", - "order": 1, - "scope": { - "type": "family", - "architecture": "krea2", - "family": "krea2_turbo" - }, - "created_at": 1782327800, - "id": "krea2-turbo-i2i", - "nodes": [ - { - "type": "CLIPTextEncode", - "offset": [ - 899.9999977111815, - 0 - ], - "size": [ - 225, - 116 - ], - "widgets_values": [ - "" - ], - "properties": { - "Node name for S&R": "CLIPTextEncode" - } - }, - { - "type": "KSampler", - "offset": [ - 1699.9999685974124, - 160.0 - ], - "size": [ - 330, - 474 - ], - "widgets_values": [ - 826502184750291, - "randomize", - 8, - 1, - "euler", - "simple", - 0.6 - ], - "properties": { - "Node name for S&R": "KSampler" - } - }, - { - "type": "VAEDecode", - "offset": [ - 1750.0000429382326, - 20.0 - ], - "size": [ - 225, - 72 - ], - "widgets_values": [], - "properties": { - "Node name for S&R": "VAEDecode" - } - }, - { - "type": "SaveImage", - "offset": [ - 2090.0000285339356, - -10.0 - ], - "size": [ - 276.984375, - 322.984375 - ], - "widgets_values": [ - "Krea2/img2img/%date:yyyy-MM-dd_hhmmss%" - ], - "properties": {} - }, - { - "type": "CLIPLoader", - "offset": [ - 860.0000519714356, - 200.0 - ], - "size": [ - 346.78125, - 117.328125 - ], - "widgets_values": [ - "qwen3vl_4b_fp8_scaled.safetensors", - "krea2", - "default" - ], - "properties": { - "Node name for S&R": "CLIPLoader" - } - }, - { - "type": "UNETLoader", - "offset": [ - 1239.9999069433584, - 0 - ], - "size": [ - 371.65625, - 82.65625 - ], - "widgets_values": [ - "krea2_turbo_fp8_scaled.safetensors", - "default" - ], - "properties": { - "Node name for S&R": "UNETLoader" - } - }, - { - "type": "LoadImage", - "offset": [ - 1240.0, - 210.0 - ], - "size": [ - 270, - 340 - ], - "widgets_values": [ - "", - "image" - ], - "properties": { - "Node name for S&R": "LoadImage" - } - }, - { - "type": "VAELoader", - "offset": [ - 1239.993419293212, - 390.0 - ], - "size": [ - 366.40625, - 82.65625 - ], - "widgets_values": [ - "qwen_image_vae.safetensors" - ], - "properties": { - "Node name for S&R": "VAELoader" - } - }, - { - "type": "VAEEncode", - "offset": [ - 1610.0, - 210.0 - ], - "size": [ - 225, - 72 - ], - "widgets_values": [], - "properties": { - "Node name for S&R": "VAEEncode" - } - } - ], - "connections": [ - { - "from_node_idx": 0, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 1 - }, - { - "from_node_idx": 0, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 2 - }, - { - "from_node_idx": 1, - "from_slot": 0, - "to_node_idx": 2, - "to_slot": 0 - }, - { - "from_node_idx": 2, - "from_slot": 0, - "to_node_idx": 3, - "to_slot": 0 - }, - { - "from_node_idx": 4, - "from_slot": 0, - "to_node_idx": 0, - "to_slot": 0 - }, - { - "from_node_idx": 5, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 0 - }, - { - "from_node_idx": 7, - "from_slot": 0, - "to_node_idx": 2, - "to_slot": 1 - }, - { - "from_node_idx": 8, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 3 - }, - { - "from_node_idx": 6, - "from_slot": 0, - "to_node_idx": 8, - "to_slot": 0 - }, - { - "from_node_idx": 7, - "from_slot": 0, - "to_node_idx": 8, - "to_slot": 1 - } - ], - "anchorConnections": [ - { - "from_slot": 1, - "to_node_idx": 0, - "to_slot": 1 - } - ] +{ + "name": "Image-to-Image", + "category": "Generation", + "order": 1, + "scope": { + "type": "family", + "architecture": "krea2", + "family": "krea2_turbo" + }, + "created_at": 1782327800, + "id": "krea2-turbo-i2i", + "nodes": [ + { + "type": "CLIPTextEncode", + "offset": [ + 899.9999977111815, + 0 + ], + "size": [ + 225, + 116 + ], + "widgets_values": [ + "" + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + }, + { + "type": "KSampler", + "offset": [ + 1699.9999685974124, + 160.0 + ], + "size": [ + 330, + 474 + ], + "widgets_values": [ + 826502184750291, + "randomize", + 8, + 1, + "euler", + "simple", + 0.6 + ], + "properties": { + "Node name for S&R": "KSampler" + } + }, + { + "type": "VAEDecode", + "offset": [ + 1750.0000429382326, + 20.0 + ], + "size": [ + 225, + 72 + ], + "widgets_values": [], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "type": "SaveImage", + "offset": [ + 2090.0000285339356, + -10.0 + ], + "size": [ + 276.984375, + 322.984375 + ], + "widgets_values": [ + "Krea2/img2img/%date:yyyy-MM-dd_hhmmss%" + ], + "properties": {} + }, + { + "type": "CLIPLoader", + "offset": [ + 860.0000519714356, + 200.0 + ], + "size": [ + 346.78125, + 117.328125 + ], + "widgets_values": [ + "qwen3vl_4b_fp8_scaled.safetensors", + "krea2", + "default" + ], + "properties": { + "Node name for S&R": "CLIPLoader" + } + }, + { + "type": "UNETLoader", + "offset": [ + 1239.9999069433584, + 0 + ], + "size": [ + 371.65625, + 82.65625 + ], + "widgets_values": [ + "krea2_turbo_fp8_scaled.safetensors", + "default" + ], + "properties": { + "Node name for S&R": "UNETLoader" + } + }, + { + "type": "LoadImage", + "offset": [ + 1240.0, + 210.0 + ], + "size": [ + 270, + 340 + ], + "widgets_values": [ + "", + "image" + ], + "properties": { + "Node name for S&R": "LoadImage" + } + }, + { + "type": "VAELoader", + "offset": [ + 1239.993419293212, + 390.0 + ], + "size": [ + 366.40625, + 82.65625 + ], + "widgets_values": [ + "qwen_image_vae.safetensors" + ], + "properties": { + "Node name for S&R": "VAELoader" + } + }, + { + "type": "VAEEncode", + "offset": [ + 1610.0, + 210.0 + ], + "size": [ + 225, + 72 + ], + "widgets_values": [], + "properties": { + "Node name for S&R": "VAEEncode" + } + }, + { + "type": "CLIPTextEncode", + "offset": [ + 899.9999977111815, + 200.0 + ], + "size": [ + 225, + 116 + ], + "widgets_values": [ + "" + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + } + ], + "connections": [ + { + "from_node_idx": 0, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 1 + }, + { + "from_node_idx": 9, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 2 + }, + { + "from_node_idx": 1, + "from_slot": 0, + "to_node_idx": 2, + "to_slot": 0 + }, + { + "from_node_idx": 2, + "from_slot": 0, + "to_node_idx": 3, + "to_slot": 0 + }, + { + "from_node_idx": 4, + "from_slot": 0, + "to_node_idx": 0, + "to_slot": 0 + }, + { + "from_node_idx": 5, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 0 + }, + { + "from_node_idx": 7, + "from_slot": 0, + "to_node_idx": 2, + "to_slot": 1 + }, + { + "from_node_idx": 8, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 3 + }, + { + "from_node_idx": 6, + "from_slot": 0, + "to_node_idx": 8, + "to_slot": 0 + }, + { + "from_node_idx": 7, + "from_slot": 0, + "to_node_idx": 8, + "to_slot": 1 + }, + { + "from_node_idx": 4, + "from_slot": 0, + "to_node_idx": 9, + "to_slot": 0 + } + ], + "anchorConnections": [ + { + "from_slot": 1, + "to_node_idx": 0, + "to_slot": 1 + }, + { + "from_slot": 2, + "to_node_idx": 9, + "to_slot": 1 + } + ] } \ No newline at end of file diff --git a/data/templates/krea2-turbo-realism-i2i.json b/data/templates/krea2-turbo-realism-i2i.json new file mode 100644 index 0000000..b8b7357 --- /dev/null +++ b/data/templates/krea2-turbo-realism-i2i.json @@ -0,0 +1,140 @@ +{ + "name": "Image-to-Image (Realism)", + "category": "Generation", + "order": 3, + "scope": { + "type": "family", + "architecture": "krea2", + "family": "krea2_turbo" + }, + "created_at": 1782328900, + "id": "krea2-turbo-realism-i2i", + "nodes": [ + { + "type": "CLIPTextEncode", + "offset": [900, 0], + "size": [225, 116], + "widgets_values": [""], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + }, + { + "type": "CLIPTextEncode", + "offset": [900, 180], + "size": [225, 116], + "widgets_values": [""], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + }, + { + "type": "ClownsharKSampler_Beta", + "offset": [1700, 160], + "size": [373.90625, 756], + "widgets_values": [0.5, "exponential/ddim", "beta57", 8, -1, 0.6, 1, 1033759726676101, "randomize", "standard", false], + "properties": { + "Node name for S&R": "ClownsharKSampler_Beta", + "aux_id": "tashiscool/RES4LYF" + } + }, + { + "type": "VAEDecode", + "offset": [2120, 20], + "size": [225, 72], + "widgets_values": [], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "type": "SaveImage", + "offset": [2120, 140], + "size": [276.984375, 322.984375], + "widgets_values": ["Krea2-Realism/img2img/%date:yyyy-MM-dd_hhmmss%"], + "properties": {} + }, + { + "type": "CLIPLoader", + "offset": [540, 340], + "size": [346.78125, 117.328125], + "widgets_values": ["Huihui-Qwen3-VL-4B-Instruct-abliterated-fp8_scaled.safetensors", "krea2", "default"], + "properties": { + "Node name for S&R": "CLIPLoader" + } + }, + { + "type": "UNETLoader", + "offset": [1240, -40], + "size": [371.65625, 82.65625], + "widgets_values": ["krea2_turbo_bf16.safetensors", "default"], + "properties": { + "Node name for S&R": "UNETLoader" + } + }, + { + "type": "LoadImage", + "offset": [1240, 420], + "size": [270, 340], + "widgets_values": ["", "image"], + "properties": { + "Node name for S&R": "LoadImage" + } + }, + { + "type": "VAELoader", + "offset": [820, 600], + "size": [366.40625, 82.65625], + "widgets_values": ["wan_2.1_vae.safetensors"], + "properties": { + "Node name for S&R": "VAELoader" + } + }, + { + "type": "VAEEncode", + "offset": [1560, 300], + "size": [225, 72], + "widgets_values": [], + "properties": { + "Node name for S&R": "VAEEncode" + } + }, + { + "type": "LoraLoaderModelOnly", + "offset": [1240, 90], + "size": [371.65625, 82], + "widgets_values": ["krea2_turbo_projector_scale.safetensors", 0.5], + "properties": { + "Node name for S&R": "LoraLoaderModelOnly" + } + }, + { + "type": "LoraLoaderModelOnly", + "offset": [1240, 240], + "size": [371.65625, 82], + "widgets_values": ["Krea2-realism-V1.safetensors", 0.6], + "properties": { + "Node name for S&R": "LoraLoaderModelOnly" + } + } + ], + "connections": [ + {"from_node_idx": 0, "from_slot": 0, "to_node_idx": 2, "to_slot": 1}, + {"from_node_idx": 1, "from_slot": 0, "to_node_idx": 2, "to_slot": 2}, + {"from_node_idx": 2, "from_slot": 0, "to_node_idx": 3, "to_slot": 0}, + {"from_node_idx": 3, "from_slot": 0, "to_node_idx": 4, "to_slot": 0}, + {"from_node_idx": 5, "from_slot": 0, "to_node_idx": 0, "to_slot": 0}, + {"from_node_idx": 5, "from_slot": 0, "to_node_idx": 1, "to_slot": 0}, + {"from_node_idx": 6, "from_slot": 0, "to_node_idx": 10, "to_slot": 0}, + {"from_node_idx": 10, "from_slot": 0, "to_node_idx": 11, "to_slot": 0}, + {"from_node_idx": 11, "from_slot": 0, "to_node_idx": 2, "to_slot": 0}, + {"from_node_idx": 9, "from_slot": 0, "to_node_idx": 2, "to_slot": 3}, + {"from_node_idx": 7, "from_slot": 0, "to_node_idx": 9, "to_slot": 0}, + {"from_node_idx": 8, "from_slot": 0, "to_node_idx": 9, "to_slot": 1}, + {"from_node_idx": 8, "from_slot": 0, "to_node_idx": 3, "to_slot": 1} + ], + "anchorConnections": [ + {"from_slot": 1, "to_node_idx": 0, "to_slot": 1}, + {"from_slot": 2, "to_node_idx": 1, "to_slot": 1} + ] +} diff --git a/data/templates/krea2-turbo-realism-t2i.json b/data/templates/krea2-turbo-realism-t2i.json new file mode 100644 index 0000000..d4b1d8a --- /dev/null +++ b/data/templates/krea2-turbo-realism-t2i.json @@ -0,0 +1,129 @@ +{ + "name": "Text-to-Image (Realism)", + "category": "Generation", + "order": 2, + "scope": { + "type": "family", + "architecture": "krea2", + "family": "krea2_turbo" + }, + "created_at": 1782328900, + "id": "d8f3a1c2-6e4b-4f9a-b1c7-3a8e5d2f9b14", + "nodes": [ + { + "type": "CLIPTextEncode", + "offset": [900, 0], + "size": [225, 116], + "widgets_values": [""], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + }, + { + "type": "CLIPTextEncode", + "offset": [900, 180], + "size": [225, 116], + "widgets_values": [""], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + }, + { + "type": "ClownsharKSampler_Beta", + "offset": [1700, 160], + "size": [373.90625, 756], + "widgets_values": [0.5, "exponential/ddim", "beta57", 8, -1, 1, 1, 1033759726676101, "randomize", "standard", false], + "properties": { + "Node name for S&R": "ClownsharKSampler_Beta", + "aux_id": "tashiscool/RES4LYF" + } + }, + { + "type": "VAEDecode", + "offset": [2120, 20], + "size": [225, 72], + "widgets_values": [], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "type": "SaveImage", + "offset": [2120, 140], + "size": [276.984375, 322.984375], + "widgets_values": ["Krea2-Realism/%date:yyyy-MM-dd_hhmmss%"], + "properties": {} + }, + { + "type": "CLIPLoader", + "offset": [540, 340], + "size": [346.78125, 117.328125], + "widgets_values": ["Huihui-Qwen3-VL-4B-Instruct-abliterated-fp8_scaled.safetensors", "krea2", "default"], + "properties": { + "Node name for S&R": "CLIPLoader" + } + }, + { + "type": "UNETLoader", + "offset": [1240, -40], + "size": [371.65625, 82.65625], + "widgets_values": ["krea2_turbo_bf16.safetensors", "default"], + "properties": { + "Node name for S&R": "UNETLoader" + } + }, + { + "type": "EmptySD3LatentImage", + "offset": [1240, 420], + "size": [324, 144], + "widgets_values": [1152, 2048, 1], + "properties": { + "Node name for S&R": "EmptySD3LatentImage" + } + }, + { + "type": "VAELoader", + "offset": [1240, 600], + "size": [366.40625, 82.65625], + "widgets_values": ["wan_2.1_vae.safetensors"], + "properties": { + "Node name for S&R": "VAELoader" + } + }, + { + "type": "LoraLoaderModelOnly", + "offset": [1240, 90], + "size": [371.65625, 82], + "widgets_values": ["krea2_turbo_projector_scale.safetensors", 0.5], + "properties": { + "Node name for S&R": "LoraLoaderModelOnly" + } + }, + { + "type": "LoraLoaderModelOnly", + "offset": [1240, 240], + "size": [371.65625, 82], + "widgets_values": ["Krea2-realism-V1.safetensors", 0.6], + "properties": { + "Node name for S&R": "LoraLoaderModelOnly" + } + } + ], + "connections": [ + {"from_node_idx": 0, "from_slot": 0, "to_node_idx": 2, "to_slot": 1}, + {"from_node_idx": 1, "from_slot": 0, "to_node_idx": 2, "to_slot": 2}, + {"from_node_idx": 2, "from_slot": 0, "to_node_idx": 3, "to_slot": 0}, + {"from_node_idx": 3, "from_slot": 0, "to_node_idx": 4, "to_slot": 0}, + {"from_node_idx": 5, "from_slot": 0, "to_node_idx": 0, "to_slot": 0}, + {"from_node_idx": 5, "from_slot": 0, "to_node_idx": 1, "to_slot": 0}, + {"from_node_idx": 6, "from_slot": 0, "to_node_idx": 9, "to_slot": 0}, + {"from_node_idx": 9, "from_slot": 0, "to_node_idx": 10, "to_slot": 0}, + {"from_node_idx": 10, "from_slot": 0, "to_node_idx": 2, "to_slot": 0}, + {"from_node_idx": 7, "from_slot": 0, "to_node_idx": 2, "to_slot": 3}, + {"from_node_idx": 8, "from_slot": 0, "to_node_idx": 3, "to_slot": 1} + ], + "anchorConnections": [ + {"from_slot": 1, "to_node_idx": 0, "to_slot": 1}, + {"from_slot": 2, "to_node_idx": 1, "to_slot": 1} + ] +} diff --git a/data/templates/krea2-turbo-t2i-highdetail.json b/data/templates/krea2-turbo-t2i-highdetail.json index d59c50f..247ce80 100644 --- a/data/templates/krea2-turbo-t2i-highdetail.json +++ b/data/templates/krea2-turbo-t2i-highdetail.json @@ -1,246 +1,274 @@ -{ - "name": "Text-to-Image (High Detail)", - "category": "Generation", - "order": 3, - "scope": { - "type": "family", - "architecture": "krea2", - "family": "krea2_turbo" - }, - "created_at": 1782362000, - "id": "b7c3e8a1-4f2d-4a6b-9c0e-2a1f6d3b9f09", - "nodes": [ - { - "type": "CLIPTextEncode", - "offset": [ - 820.0, - 20.0 - ], - "size": [ - 225, - 116 - ], - "widgets_values": [ - "" - ], - "properties": { - "Node name for S&R": "CLIPTextEncode" - } - }, - { - "type": "KSampler", - "offset": [ - 1440.0, - 120.0 - ], - "size": [ - 330, - 474 - ], - "widgets_values": [ - 826502184750291, - "randomize", - 8, - 1, - "euler", - "simple", - 1 - ], - "properties": { - "Node name for S&R": "KSampler" - } - }, - { - "type": "VAEUtils_VAEDecodeTiled", - "offset": [ - 1800.0, - 20.0 - ], - "size": [ - 300, - 220 - ], - "widgets_values": [ - -1, - false, - 512, - 64, - 4096, - 64 - ], - "properties": { - "Node name for S&R": "VAEUtils_VAEDecodeTiled" - } - }, - { - "type": "SaveImage", - "offset": [ - 2140.0, - -10.0 - ], - "size": [ - 276.984375, - 322.984375 - ], - "widgets_values": [ - "Krea2/highdetail/%date:yyyy-MM-dd_hhmmss%" - ], - "properties": {} - }, - { - "type": "CLIPLoader", - "offset": [ - 820.0, - 240.0 - ], - "size": [ - 346.78125, - 117.328125 - ], - "widgets_values": [ - "qwen3vl_4b_fp8_scaled.safetensors", - "krea2", - "default" - ], - "properties": { - "Node name for S&R": "CLIPLoader" - } - }, - { - "type": "UNETLoader", - "offset": [ - 1100.0, - 280.0 - ], - "size": [ - 371.65625, - 82.65625 - ], - "widgets_values": [ - "krea2_turbo_bf16.safetensors", - "default" - ], - "properties": { - "Node name for S&R": "UNETLoader" - } - }, - { - "type": "EmptySD3LatentImage", - "offset": [ - 1100.0, - 420.0 - ], - "size": [ - 324, - 144 - ], - "widgets_values": [ - 1024, - 1536, - 1 - ], - "properties": { - "Node name for S&R": "EmptySD3LatentImage" - } - }, - { - "type": "VAEUtils_CustomVAELoader", - "offset": [ - 1440.0, - 620.0 - ], - "size": [ - 366.40625, - 82.65625 - ], - "widgets_values": [ - "Wan2.1_VAE_upscale2x_imageonly_real_v1.safetensors", - true - ], - "properties": { - "Node name for S&R": "VAEUtils_CustomVAELoader" - } - }, - { - "type": "ConditioningKrea2Rebalance", - "offset": [ - 1100.0, - 20.0 - ], - "size": [ - 300, - 106 - ], - "widgets_values": [ - 2.0, - "1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.6,2.5,1.05,2.0,1.0" - ], - "properties": { - "Node name for S&R": "ConditioningKrea2Rebalance" - } - } - ], - "connections": [ - { - "from_node_idx": 0, - "from_slot": 0, - "to_node_idx": 8, - "to_slot": 0 - }, - { - "from_node_idx": 8, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 1 - }, - { - "from_node_idx": 0, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 2 - }, - { - "from_node_idx": 1, - "from_slot": 0, - "to_node_idx": 2, - "to_slot": 0 - }, - { - "from_node_idx": 7, - "from_slot": 0, - "to_node_idx": 2, - "to_slot": 1 - }, - { - "from_node_idx": 2, - "from_slot": 0, - "to_node_idx": 3, - "to_slot": 0 - }, - { - "from_node_idx": 4, - "from_slot": 0, - "to_node_idx": 0, - "to_slot": 0 - }, - { - "from_node_idx": 5, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 0 - }, - { - "from_node_idx": 6, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 3 - } - ], - "anchorConnections": [ - { - "from_slot": 1, - "to_node_idx": 0, - "to_slot": 1 - } - ] +{ + "name": "Text-to-Image (High Detail)", + "category": "Generation", + "order": 3, + "scope": { + "type": "family", + "architecture": "krea2", + "family": "krea2_turbo" + }, + "created_at": 1782362000, + "id": "b7c3e8a1-4f2d-4a6b-9c0e-2a1f6d3b9f09", + "nodes": [ + { + "type": "CLIPTextEncode", + "offset": [ + 820.0, + 20.0 + ], + "size": [ + 225, + 116 + ], + "widgets_values": [ + "" + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + }, + { + "type": "KSampler", + "offset": [ + 1440.0, + 120.0 + ], + "size": [ + 330, + 474 + ], + "widgets_values": [ + 826502184750291, + "randomize", + 8, + 1, + "euler", + "simple", + 1 + ], + "properties": { + "Node name for S&R": "KSampler" + } + }, + { + "type": "VAEUtils_VAEDecodeTiled", + "offset": [ + 1800.0, + 20.0 + ], + "size": [ + 300, + 220 + ], + "widgets_values": [ + -1, + false, + 512, + 64, + 4096, + 64 + ], + "properties": { + "Node name for S&R": "VAEUtils_VAEDecodeTiled" + } + }, + { + "type": "SaveImage", + "offset": [ + 2140.0, + -10.0 + ], + "size": [ + 276.984375, + 322.984375 + ], + "widgets_values": [ + "Krea2/highdetail/%date:yyyy-MM-dd_hhmmss%" + ], + "properties": {} + }, + { + "type": "CLIPLoader", + "offset": [ + 820.0, + 240.0 + ], + "size": [ + 346.78125, + 117.328125 + ], + "widgets_values": [ + "qwen3vl_4b_fp8_scaled.safetensors", + "krea2", + "default" + ], + "properties": { + "Node name for S&R": "CLIPLoader" + } + }, + { + "type": "UNETLoader", + "offset": [ + 1100.0, + 280.0 + ], + "size": [ + 371.65625, + 82.65625 + ], + "widgets_values": [ + "krea2_turbo_bf16.safetensors", + "default" + ], + "properties": { + "Node name for S&R": "UNETLoader" + } + }, + { + "type": "EmptySD3LatentImage", + "offset": [ + 1100.0, + 420.0 + ], + "size": [ + 324, + 144 + ], + "widgets_values": [ + 1024, + 1536, + 1 + ], + "properties": { + "Node name for S&R": "EmptySD3LatentImage" + } + }, + { + "type": "VAEUtils_CustomVAELoader", + "offset": [ + 1440.0, + 620.0 + ], + "size": [ + 366.40625, + 82.65625 + ], + "widgets_values": [ + "Wan2.1_VAE_upscale2x_imageonly_real_v1.safetensors", + true + ], + "properties": { + "Node name for S&R": "VAEUtils_CustomVAELoader" + } + }, + { + "type": "ConditioningKrea2Rebalance", + "offset": [ + 1100.0, + 20.0 + ], + "size": [ + 300, + 106 + ], + "widgets_values": [ + 2.0, + "1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.6,2.5,1.05,2.0,1.0" + ], + "properties": { + "Node name for S&R": "ConditioningKrea2Rebalance" + } + }, + { + "type": "CLIPTextEncode", + "offset": [ + 820.0, + 220.0 + ], + "size": [ + 225, + 116 + ], + "widgets_values": [ + "" + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + } + ], + "connections": [ + { + "from_node_idx": 0, + "from_slot": 0, + "to_node_idx": 8, + "to_slot": 0 + }, + { + "from_node_idx": 8, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 1 + }, + { + "from_node_idx": 9, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 2 + }, + { + "from_node_idx": 1, + "from_slot": 0, + "to_node_idx": 2, + "to_slot": 0 + }, + { + "from_node_idx": 7, + "from_slot": 0, + "to_node_idx": 2, + "to_slot": 1 + }, + { + "from_node_idx": 2, + "from_slot": 0, + "to_node_idx": 3, + "to_slot": 0 + }, + { + "from_node_idx": 4, + "from_slot": 0, + "to_node_idx": 0, + "to_slot": 0 + }, + { + "from_node_idx": 5, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 0 + }, + { + "from_node_idx": 6, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 3 + }, + { + "from_node_idx": 4, + "from_slot": 0, + "to_node_idx": 9, + "to_slot": 0 + } + ], + "anchorConnections": [ + { + "from_slot": 1, + "to_node_idx": 0, + "to_slot": 1 + }, + { + "from_slot": 2, + "to_node_idx": 9, + "to_slot": 1 + } + ] } \ No newline at end of file diff --git a/data/templates/krea2-turbo-t2i.json b/data/templates/krea2-turbo-t2i.json index 8d7258f..8cf20b6 100644 --- a/data/templates/krea2-turbo-t2i.json +++ b/data/templates/krea2-turbo-t2i.json @@ -1,214 +1,242 @@ -{ - "name": "Text-to-Image", - "category": "Generation", - "order": 0, - "scope": { - "type": "family", - "architecture": "krea2", - "family": "krea2_turbo" - }, - "created_at": 1782327800, - "id": "b7c3e8a1-4f2d-4a6b-9c0e-2a1f6d3b9e07", - "nodes": [ - { - "type": "CLIPTextEncode", - "offset": [ - 899.9999977111815, - 0 - ], - "size": [ - 225, - 116 - ], - "widgets_values": [ - "" - ], - "properties": { - "Node name for S&R": "CLIPTextEncode" - } - }, - { - "type": "KSampler", - "offset": [ - 1699.9999685974124, - 160.0 - ], - "size": [ - 330, - 474 - ], - "widgets_values": [ - 826502184750291, - "randomize", - 8, - 1, - "euler", - "simple", - 1 - ], - "properties": { - "Node name for S&R": "KSampler" - } - }, - { - "type": "VAEDecode", - "offset": [ - 1750.0000429382326, - 20.0 - ], - "size": [ - 225, - 72 - ], - "widgets_values": [], - "properties": { - "Node name for S&R": "VAEDecode" - } - }, - { - "type": "SaveImage", - "offset": [ - 2090.0000285339356, - -10.0 - ], - "size": [ - 276.984375, - 322.984375 - ], - "widgets_values": [ - "Krea2/%date:yyyy-MM-dd_hhmmss%" - ], - "properties": {} - }, - { - "type": "CLIPLoader", - "offset": [ - 860.0000519714356, - 200.0 - ], - "size": [ - 346.78125, - 117.328125 - ], - "widgets_values": [ - "qwen3vl_4b_fp8_scaled.safetensors", - "krea2", - "default" - ], - "properties": { - "Node name for S&R": "CLIPLoader" - } - }, - { - "type": "UNETLoader", - "offset": [ - 1239.9999069433584, - 0 - ], - "size": [ - 371.65625, - 82.65625 - ], - "widgets_values": [ - "krea2_turbo_fp8_scaled.safetensors", - "default" - ], - "properties": { - "Node name for S&R": "UNETLoader" - } - }, - { - "type": "EmptySD3LatentImage", - "offset": [ - 1249.9999315600576, - 200.0 - ], - "size": [ - 324, - 144 - ], - "widgets_values": [ - 1024, - 1536, - 1 - ], - "properties": { - "Node name for S&R": "EmptySD3LatentImage" - } - }, - { - "type": "VAELoader", - "offset": [ - 1239.993419293212, - 390.0 - ], - "size": [ - 366.40625, - 82.65625 - ], - "widgets_values": [ - "qwen_image_vae.safetensors" - ], - "properties": { - "Node name for S&R": "VAELoader" - } - } - ], - "connections": [ - { - "from_node_idx": 0, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 1 - }, - { - "from_node_idx": 0, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 2 - }, - { - "from_node_idx": 1, - "from_slot": 0, - "to_node_idx": 2, - "to_slot": 0 - }, - { - "from_node_idx": 2, - "from_slot": 0, - "to_node_idx": 3, - "to_slot": 0 - }, - { - "from_node_idx": 4, - "from_slot": 0, - "to_node_idx": 0, - "to_slot": 0 - }, - { - "from_node_idx": 5, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 0 - }, - { - "from_node_idx": 6, - "from_slot": 0, - "to_node_idx": 1, - "to_slot": 3 - }, - { - "from_node_idx": 7, - "from_slot": 0, - "to_node_idx": 2, - "to_slot": 1 - } - ], - "anchorConnections": [ - { - "from_slot": 1, - "to_node_idx": 0, - "to_slot": 1 - } - ] +{ + "name": "Text-to-Image", + "category": "Generation", + "order": 0, + "scope": { + "type": "family", + "architecture": "krea2", + "family": "krea2_turbo" + }, + "created_at": 1782327800, + "id": "b7c3e8a1-4f2d-4a6b-9c0e-2a1f6d3b9e07", + "nodes": [ + { + "type": "CLIPTextEncode", + "offset": [ + 899.9999977111815, + 0 + ], + "size": [ + 225, + 116 + ], + "widgets_values": [ + "" + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + }, + { + "type": "KSampler", + "offset": [ + 1699.9999685974124, + 160.0 + ], + "size": [ + 330, + 474 + ], + "widgets_values": [ + 826502184750291, + "randomize", + 8, + 1, + "euler", + "simple", + 1 + ], + "properties": { + "Node name for S&R": "KSampler" + } + }, + { + "type": "VAEDecode", + "offset": [ + 1750.0000429382326, + 20.0 + ], + "size": [ + 225, + 72 + ], + "widgets_values": [], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "type": "SaveImage", + "offset": [ + 2090.0000285339356, + -10.0 + ], + "size": [ + 276.984375, + 322.984375 + ], + "widgets_values": [ + "Krea2/%date:yyyy-MM-dd_hhmmss%" + ], + "properties": {} + }, + { + "type": "CLIPLoader", + "offset": [ + 860.0000519714356, + 200.0 + ], + "size": [ + 346.78125, + 117.328125 + ], + "widgets_values": [ + "qwen3vl_4b_fp8_scaled.safetensors", + "krea2", + "default" + ], + "properties": { + "Node name for S&R": "CLIPLoader" + } + }, + { + "type": "UNETLoader", + "offset": [ + 1239.9999069433584, + 0 + ], + "size": [ + 371.65625, + 82.65625 + ], + "widgets_values": [ + "krea2_turbo_fp8_scaled.safetensors", + "default" + ], + "properties": { + "Node name for S&R": "UNETLoader" + } + }, + { + "type": "EmptySD3LatentImage", + "offset": [ + 1249.9999315600576, + 200.0 + ], + "size": [ + 324, + 144 + ], + "widgets_values": [ + 1024, + 1536, + 1 + ], + "properties": { + "Node name for S&R": "EmptySD3LatentImage" + } + }, + { + "type": "VAELoader", + "offset": [ + 1239.993419293212, + 390.0 + ], + "size": [ + 366.40625, + 82.65625 + ], + "widgets_values": [ + "qwen_image_vae.safetensors" + ], + "properties": { + "Node name for S&R": "VAELoader" + } + }, + { + "type": "CLIPTextEncode", + "offset": [ + 899.9999977111815, + 200.0 + ], + "size": [ + 225, + 116 + ], + "widgets_values": [ + "" + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + } + } + ], + "connections": [ + { + "from_node_idx": 0, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 1 + }, + { + "from_node_idx": 8, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 2 + }, + { + "from_node_idx": 1, + "from_slot": 0, + "to_node_idx": 2, + "to_slot": 0 + }, + { + "from_node_idx": 2, + "from_slot": 0, + "to_node_idx": 3, + "to_slot": 0 + }, + { + "from_node_idx": 4, + "from_slot": 0, + "to_node_idx": 0, + "to_slot": 0 + }, + { + "from_node_idx": 5, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 0 + }, + { + "from_node_idx": 6, + "from_slot": 0, + "to_node_idx": 1, + "to_slot": 3 + }, + { + "from_node_idx": 7, + "from_slot": 0, + "to_node_idx": 2, + "to_slot": 1 + }, + { + "from_node_idx": 4, + "from_slot": 0, + "to_node_idx": 8, + "to_slot": 0 + } + ], + "anchorConnections": [ + { + "from_slot": 1, + "to_node_idx": 0, + "to_slot": 1 + }, + { + "from_slot": 2, + "to_node_idx": 8, + "to_slot": 1 + } + ] } \ No newline at end of file diff --git a/js/lib/editor-context-menu.js b/js/lib/editor-context-menu.js index 17e582d..8473061 100644 --- a/js/lib/editor-context-menu.js +++ b/js/lib/editor-context-menu.js @@ -37,7 +37,7 @@ function closeMenu() { // ── graph traversal ────────────────────────────────────────────── -const KSAMPLER_TYPES = new Set(["KSampler", "KSamplerAdvanced", "PromptChain_RegionalDetailer", "SamplerCustomAdvanced", "UltimateSDUpscale", "UltimateSDUpscaleNoUpscale", "PromptChain_IdeogramSampler"]); +const KSAMPLER_TYPES = new Set(["KSampler", "KSamplerAdvanced", "PromptChain_RegionalDetailer", "SamplerCustomAdvanced", "UltimateSDUpscale", "UltimateSDUpscaleNoUpscale", "PromptChain_IdeogramSampler", "ClownsharKSampler_Beta"]); const LOAD_IMAGE_TYPES = new Set(["LoadImage", "LoadImageOutput"]); function _findDownstream(startNode, targetTypes, graph) { const visited = new Set(); diff --git a/js/lib/fullscreen-bridge.js b/js/lib/fullscreen-bridge.js index c822b39..a85825f 100644 --- a/js/lib/fullscreen-bridge.js +++ b/js/lib/fullscreen-bridge.js @@ -55,7 +55,7 @@ let focusedGroup = null; // PromptChain feeding them. Mirrored from main.js — the canonical set. const KSAMPLER_TYPES = new Set([ "KSampler", "KSamplerAdvanced", "PromptChain_RegionalDetailer", "SamplerCustomAdvanced", "UltimateSDUpscale", "UltimateSDUpscaleNoUpscale", - "PromptChain_IdeogramSampler", + "PromptChain_IdeogramSampler", "ClownsharKSampler_Beta", ]); // Workflow-level fullscreen state stash. The preview panels are diff --git a/js/lib/i2i-background.js b/js/lib/i2i-background.js new file mode 100644 index 0000000..fefc864 --- /dev/null +++ b/js/lib/i2i-background.js @@ -0,0 +1,557 @@ +// Editor "i2i": full-frame image-to-image. The Edit modal hands its flattened +// composite (already snapped to a /16 canvas by openI2iHandoff, so the VAE's +// own center-crop is a no-op and the result composites back aligned) to a clean +// LoadImage → VAEEncode → KSampler(denoise) → VAEDecode → PreviewImage +// graph — no mask, no tiling, no crop. The render runs off-screen (PreviewImage +// → temp dir, the user's tabs untouched) and returns to Edit as a new layer. +// +// This is the no-mask sibling of the inpaint subsystem: it reuses prepareInpaint +// (wrapped) for caps + the engine list, the same content-addressed upload, the +// same loader clusters per engine, and the same background-tracker lifecycle. +// The one net-new piece is the clean graph TAIL — the inpaint builders all end +// in PromptChain_MaskedDetail (crop-and-detail), which would downscale a full +// frame to its max_size; i2i samples the whole frame instead. + +import { app } from "../../../scripts/app.js"; +import { api } from "../../../scripts/api.js"; +import { externallyTrackedPrompts, armExternalQueue, disarmExternalQueue } from "./history.js"; +import { prepareInpaint, sourcePromptText, createTracker, freshWorkflowId } from "./inpaint-background.js"; +import { offscreenIdBase } from "./offscreen-graph.js"; + +function toast(severity, detail) { + app.extensionManager?.toast?.add({ severity, summary: "i2i", detail, life: 7000 }); +} + +const numOr = (v, fallback) => (typeof v === "number" && Number.isFinite(v) ? v : fallback); + +// Per-engine i2i recipe (denoise/steps/cfg/sampler/scheduler), read off the live +// data/templates/*-i2i.json this session. The modal seeds its dials from this; +// the builders only fall back to it when an option is missing. Keyed by an +// architecture family; krea2/zimage split raw-vs-turbo by filename. +export const I2I_RECIPE_DEFAULTS = { + sdxl: { denoise: 0.55, steps: 35, cfg: 4.0, sampler: "dpmpp_2m_sde", scheduler: "karras" }, + flux1: { denoise: 0.55, steps: 25, cfg: 1.0, sampler: "euler", scheduler: "simple", guidance: 3.5 }, + flux2: { denoise: 0.55, steps: 25, cfg: 1.0, sampler: "euler", scheduler: "simple", guidance: 3.5 }, + krea2_raw: { denoise: 0.35, steps: 40, cfg: 3.5, sampler: "er_sde", scheduler: "beta" }, + krea2_turbo: { denoise: 0.35, steps: 8, cfg: 1.0, sampler: "euler", scheduler: "simple" }, + zimage_base: { denoise: 0.55, steps: 50, cfg: 4.0, sampler: "dpmpp_sde", scheduler: "beta" }, + zimage_turbo: { denoise: 0.55, steps: 9, cfg: 1.3, sampler: "res_multistep", scheduler: "beta" }, + generic: { denoise: 0.50, steps: 30, cfg: 4.0, sampler: "euler", scheduler: "normal" }, +}; + +// The single source of truth for i2i defaults: resolve the recipe for an +// architecture/filename, layering the model's own saved gen recipe where it's +// meaningful. Returns a FRESH object (never a shared I2I_RECIPE_DEFAULTS ref). +// `gen` = the model config's t2i KSampler settings (from fetchEngineModels). +// SDXL honors its stored steps/cfg/sampler/scheduler; distilled DiTs +// (flux/krea2/zimage) have arch-fixed recipes a stored t2i gen shouldn't +// override, except flux's step count. The modal reads this via caps — it has no +// table of its own (no drift). +export function i2iRecipe(architecture, filename = "", gen = null) { + const a = String(architecture || "").toLowerCase(); + const turbo = /turbo/i.test(filename); + const raw = /raw/i.test(filename) && !turbo; + let base; + if (a === "krea2") base = raw ? I2I_RECIPE_DEFAULTS.krea2_raw : I2I_RECIPE_DEFAULTS.krea2_turbo; + else if (a === "sdxl") base = I2I_RECIPE_DEFAULTS.sdxl; + else if (a === "flux" || a === "flux1") base = I2I_RECIPE_DEFAULTS.flux1; + else if (a === "flux2") base = I2I_RECIPE_DEFAULTS.flux2; + else if (a === "zimage" || a === "z-image") base = turbo ? I2I_RECIPE_DEFAULTS.zimage_turbo : I2I_RECIPE_DEFAULTS.zimage_base; + else base = I2I_RECIPE_DEFAULTS.generic; + const r = { ...base }; + if (gen) { + if (a === "sdxl") { + if (Number.isFinite(gen.steps)) r.steps = gen.steps; + if (Number.isFinite(gen.cfg)) r.cfg = gen.cfg; + if (gen.sampler) r.sampler = gen.sampler; + if (gen.scheduler) r.scheduler = gen.scheduler; + } else if ((a === "flux" || a === "flux1") && Number.isFinite(gen.steps)) { + r.steps = gen.steps; + } + } + return r; +} + +// Caps for the i2i modal: the inpaint caps minus everything mask-spatial. Full +// frame has no region to resolve, so regional/depth modes and the conditions +// panel (which patch the UNET around a mask) don't apply. +export async function prepareI2I(entry) { + const prepared = await prepareInpaint(entry); + if (!prepared) return null; + prepared.caps.regionalAvailable = false; + prepared.caps.depthAvailable = false; + // P0 engine roster: Source + SDXL / FLUX.1 / Krea 2 (true denoise i2i). Qwen + // Edit is an instruction edit at denoise 1.0 — a different feature, deferred. + // Stamp each engine with its resolved i2i recipe (the modal reads .i2i — no + // recipe table lives in the component). + prepared.caps.engineModels = (prepared.caps.engineModels || []) + .filter((m) => m.architecture !== "qwen_edit") + .map((m) => ({ ...m, i2i: i2iRecipe(m.architecture, m.filename, m.gen) })); + // The Source engine's recipe (only its denoise is used — steps/cfg/sampler are + // inherited from the image's own embedded render). Drives the default denoise, + // so a krea2 source opens at krea2's i2i denoise. + prepared.caps.sourceI2iRecipe = i2iRecipe(prepared.caps.sourceModelInfo?.architecture || "", "", null); + prepared.caps.defaultDenoise = prepared.caps.sourceI2iRecipe.denoise ?? 0.55; + return prepared; +} + +// ── graph builders ──────────────────────────────────────────────────────── + +function probeCombo(type, widget) { + try { + const node = window.LiteGraph?.createNode?.(type); + return node?.widgets?.find((w) => w.name === widget)?.options?.values || []; + } catch { return []; } +} + +const PC_NODE = () => ({ + prompt: "", mode: "combine", switch_index: 1, + locked: false, disabled: false, cached_output: "", cached_neg_output: "", + iterate_index: 0, iterate_cycle: 1, collapsed: false, + wildcard_modes: "", cached_regions: "", +}); + +function makeAdder(graph) { + // Render-node ids start far above every live-canvas id so ComfyUI's id-keyed + // output painter never lands this background render on a visible node (see + // offscreen-graph.js). Mirrors the inpaint builders. + let nextId = offscreenIdBase(); + return (class_type, inputs) => { + const id = String(nextId++); + graph[id] = { class_type, inputs }; + return id; + }; +} + +// Shared clean tail: encode the loaded image, partially denoise it, decode, +// preview. `modelRef`/`clipRef`/`vaeRef` are [id, slot] links into `graph`. +function attachI2ITail(graph, add, { inputRef, modelRef, clipRef, vaeRef, options, guidance, steps, cfg, sampler, scheduler, clown }) { + const loadId = add("LoadImage", { image: inputRef }); + const encId = add("VAEEncode", { pixels: [loadId, 0], vae: vaeRef }); + const pc = PC_NODE(); pc.prompt = options.prompt || ""; + const pcId = add("PromptChain_PromptChain", pc); + let posRef = [add("CLIPTextEncode", { text: [pcId, 1], clip: clipRef }), 0]; + const negRef = [add("CLIPTextEncode", { text: [pcId, 2], clip: clipRef }), 0]; + if (typeof guidance === "number") { + posRef = [add("FluxGuidance", { conditioning: posRef, guidance }), 0]; + } + const denoise = typeof options.denoise === "number" ? options.denoise : 0.55; + // Realism full-frame i2i: the same RES4LYF ClownsharKSampler_Beta the realism + // t2i template uses. Full-frame output (not a composited patch), so a sampler + // NODE is allowed here. cfg pinned 1 like Turbo; denoise from the dial. + const ksId = clown + ? add("ClownsharKSampler_Beta", { + model: modelRef, positive: posRef, negative: negRef, latent_image: [encId, 0], + eta: 0.5, sampler_name: "exponential/ddim", scheduler: "beta57", + steps: steps ?? 8, steps_to_run: -1, denoise, cfg: cfg ?? 1.0, + seed: Math.floor(Math.random() * 0xffffffffff), sampler_mode: "standard", bongmath: false, + }) + : add("KSampler", { + model: modelRef, positive: posRef, negative: negRef, latent_image: [encId, 0], + seed: Math.floor(Math.random() * 0xffffffffff), + steps, cfg, sampler_name: sampler, scheduler, denoise, + }); + const decId = add("VAEDecode", { samples: [ksId, 0], vae: vaeRef }); + const previewId = add("PreviewImage", { images: [decId, 0] }); + return { ksId, previewId }; +} + +// SDXL picker engine: a fresh checkpoint i2i — no embedded render data needed, +// so plain photos / imported images work too. Mirrors buildInpaintEnginePrompt +// upstream (checkpoint + two encodes), clean tail. +export function buildI2IEngineSdxl(data, options) { + const filename = options.engineModel?.filename; + if (!filename) throw new Error("no engine model picked"); + const graph = {}; + const add = makeAdder(graph); + const ckptId = add("CheckpointLoaderSimple", { ckpt_name: filename }); + const gen = options.engineGen || {}; + const { ksId } = attachI2ITail(graph, add, { + inputRef: data.input_ref, modelRef: [ckptId, 0], clipRef: [ckptId, 1], vaeRef: [ckptId, 2], options, + steps: numOr(options.steps, numOr(gen.steps, 35)), + cfg: numOr(options.cfg, numOr(gen.cfg, 4)), + sampler: options.sampler || gen.sampler || "dpmpp_2m_sde", + scheduler: options.scheduler || gen.scheduler || "karras", + }); + return { graph, detailId: ksId }; +} + +// FLUX.1 picker engine: guidance rides FluxGuidance on the positive, KSampler +// cfg pinned 1.0 (a real cfg would double sampling). Mirrors buildFlux1InpaintPrompt. +export function buildI2IEngineFlux1(data, options) { + const filename = options.engineModel?.filename; + if (!filename) throw new Error("no engine model picked"); + const clipOpts = probeCombo("DualCLIPLoader", "clip_name1"); + const clipL = clipOpts.find((o) => /(^|[\\/])clip_l\./i.test(o)) || clipOpts.find((o) => /clip_l/i.test(o)); + const t5 = clipOpts.find((o) => /t5xxl.*fp16/i.test(o)) || clipOpts.find((o) => /t5xxl/i.test(o)); + const vaeFile = probeCombo("VAELoader", "vae_name").find((o) => /(^|[\\/])ae\.(safetensors|sft)$/i.test(o)); + if (!clipL || !t5 || !vaeFile) throw new Error("the FLUX text encoders / VAE aren't installed"); + const graph = {}; + const add = makeAdder(graph); + const unetId = add("UNETLoader", { unet_name: filename, weight_dtype: "default" }); + const clipId = add("DualCLIPLoader", { clip_name1: clipL, clip_name2: t5, type: "flux", device: "default" }); + const vaeId = add("VAELoader", { vae_name: vaeFile }); + const gen = options.engineGen || {}; + const { ksId } = attachI2ITail(graph, add, { + inputRef: data.input_ref, modelRef: [unetId, 0], clipRef: [clipId, 0], vaeRef: [vaeId, 0], options, + guidance: I2I_RECIPE_DEFAULTS.flux1.guidance, + steps: numOr(options.steps, numOr(gen.steps, 25)), + cfg: 1.0, + sampler: options.sampler || gen.sampler || "euler", + scheduler: options.scheduler || gen.scheduler || "simple", + }); + return { graph, detailId: ksId }; +} + +// Krea 2 picker engine: a DiT that wires like FLUX.1 minus the guidance regime +// (cfg-native, no FluxGuidance). RAW needs the resolution shift + full steps + +// real cfg; Turbo is the pinned 8-step/cfg-1 recipe. Mirrors buildKrea2InpaintPrompt. +export function buildI2IEngineKrea2(data, options) { + const filename = options.engineModel?.filename; + if (!filename) throw new Error("no engine model picked"); + // Realism mode (krea2_turbo): abliterated Qwen3-VL encoder + wan_2.1 VAE + + // projector/realism LoRAs + the ClownsharKSampler tail. i2i is full-frame (a + // whole new layer, not a composited patch), so wan VAE is safe and the Clown + // sampler node can run — matching the realism t2i template exactly. + const realism = !!options.realism; + const clipOpts = probeCombo("CLIPLoader", "clip_name"); + const loraOpts = probeCombo("LoraLoaderModelOnly", "lora_name"); + const clipFile = realism + ? clipOpts.find((o) => /huihui.*qwen3.?vl.*4b.*abliterated|qwen3.?vl.*4b.*abliterated/i.test(o)) + : (clipOpts.find((o) => /qwen3vl.*4b/i.test(o)) || clipOpts.find((o) => /qwen3.?vl.*4b/i.test(o))); + const vaeFile = realism + ? probeCombo("VAELoader", "vae_name").find((o) => /wan.*2[._]?1.*vae/i.test(o) && !/upscale|2x/i.test(o)) + : probeCombo("VAELoader", "vae_name").find((o) => /qwen.*image.*vae/i.test(o)); + const projLora = realism && loraOpts.find((o) => /krea2_turbo_projector_scale/i.test(o)); + const realLora = realism && loraOpts.find((o) => /krea2-realism/i.test(o)); + if (!clipFile || !vaeFile) throw new Error(realism + ? "Realism mode needs the abliterated Qwen3-VL encoder + wan_2.1 VAE installed" + : "the Krea 2 text encoder / VAE aren't installed"); + if (realism && (!projLora || !realLora)) throw new Error("Realism mode needs the Krea2-realism-V1 + krea2_turbo_projector_scale LoRAs installed"); + const graph = {}; + const add = makeAdder(graph); + const unetId = add("UNETLoader", { unet_name: filename, weight_dtype: "default" }); + const clipId = add("CLIPLoader", { clip_name: clipFile, type: "krea2", device: "default" }); + const vaeId = add("VAELoader", { vae_name: vaeFile }); + const raw = /raw/i.test(filename) && !/turbo/i.test(filename); + let modelRef = [unetId, 0]; + if (raw) { + // RAW over-shifts on the baked 1.15 mu — replace it with a resolution-aware + // shift (the same node the raw t2i/i2i templates insert). i2i has no + // MaskedDetail crop, so the sampled latent IS the input size — feed those + // real dims, not a stale 1024², or the mu schedule is wrong for non-square / + // non-1024 inputs. + const msfId = add("ModelSamplingFlux", { + model: [unetId, 0], max_shift: 0.90625, base_shift: 0.5, + width: data.width || 1024, height: data.height || 1024, + }); + modelRef = [msfId, 0]; + } + if (realism) { + const proj = add("LoraLoaderModelOnly", { model: modelRef, lora_name: projLora, strength_model: 0.5 }); + const real = add("LoraLoaderModelOnly", { model: [proj, 0], lora_name: realLora, strength_model: 0.6 }); + modelRef = [real, 0]; + } + const { ksId } = attachI2ITail(graph, add, { + inputRef: data.input_ref, modelRef, clipRef: [clipId, 0], vaeRef: [vaeId, 0], options, + clown: realism, + steps: numOr(options.steps, raw ? 40 : 8), + cfg: numOr(options.cfg, raw ? 3.5 : 1.0), + sampler: options.sampler || (raw ? "er_sde" : "euler"), + scheduler: options.scheduler || (raw ? "beta" : "simple"), + }); + return { graph, detailId: ksId }; +} + +// Architecture of the image's own render graph — picks the conditioning regime +// for the source graft (which model stream / guidance the fresh encodes feed). +// Mirrors inpaint-background's inpaintArch. +function sourceArch(entries) { + const has = (...cls) => entries.some((n) => cls.includes(n?.class_type)); + if (has("CheckpointLoaderSimple", "CheckpointLoader")) return "sdxl"; + const clipType = String(entries.find((n) => n?.class_type === "CLIPLoader")?.inputs?.type || "").toLowerCase(); + if (clipType === "flux2") return "flux2"; + if (clipType === "krea2") return "krea2"; + if (clipType === "lumina2") return "zimage"; + if (has("DualCLIPLoader") || clipType === "flux" || has("UNETLoader", "UnetLoaderGGUF")) return "flux1"; + return "other"; +} + +// Source graft: re-diffuse the image with ITS OWN model. Traces the model / clip +// / vae out of the embedded API prompt (the truest "this image, more diffused") +// and attaches the clean i2i tail, applying the arch's conditioning regime. +// Mirrors buildInpaintPrompt's tracing; swaps the MaskedDetail tail for the +// plain encode/sample/decode. Throws (engine picker is the fallback) if the +// graph can't be traced. +export function buildI2ISource(apiPrompt, data, options) { + const graph = {}; + for (const [id, entry] of Object.entries(apiPrompt || {})) { + if (!entry || typeof entry.class_type !== "string") continue; + graph[id] = { class_type: entry.class_type, inputs: JSON.parse(JSON.stringify(entry.inputs || {})) }; + } + const idByClass = (...cls) => Object.keys(graph).find((id) => cls.includes(graph[id].class_type)); + const isLink = (v) => Array.isArray(v) && v.length === 2 && !!graph[String(v[0])]; + + const SAMPLER_CLASSES = ["KSampler", "KSamplerAdvanced", "PromptChain_MaskedDetail", "UltimateSDUpscale"]; + const anchorId = Object.keys(graph).find((id) => SAMPLER_CLASSES.includes(graph[id].class_type)); + if (!anchorId) throw new Error("no sampler in this image's render data"); + const src = graph[anchorId]; + + // Trace to the unpatched model (keep LoRA loaders / ModelSampling shims; drop + // only the AttentionCouple, which bakes the source gen's regional prompts). + let modelRef = src.inputs.model; + while (isLink(modelRef) && graph[String(modelRef[0])].class_type === "PromptChain_AttentionCouple") { + modelRef = graph[String(modelRef[0])].inputs.model; + } + if (!isLink(modelRef)) throw new Error("the sampler has no model wired"); + + const ckptId = idByClass("CheckpointLoaderSimple", "CheckpointLoader"); + const encNodeId = idByClass("CLIPTextEncode"); + const regionalId = idByClass("PromptChain_AttentionCouple", "PromptChain_RegionalConditioning"); + const clipLoaderId = idByClass("CLIPLoader"); + const clipRef = (encNodeId && isLink(graph[encNodeId].inputs.clip) && graph[encNodeId].inputs.clip) + || (regionalId && isLink(graph[regionalId].inputs.clip) && graph[regionalId].inputs.clip) + || (clipLoaderId && [clipLoaderId, 0]) + || (ckptId && [ckptId, 1]); + const decId = idByClass("VAEDecode", "VAEUtils_VAEDecodeTiled", "VAEDecodeTiled"); + let vaeRef = (decId && isLink(graph[decId].inputs.vae) && graph[decId].inputs.vae) + || (isLink(src.inputs.vae) && src.inputs.vae) + || (ckptId && [ckptId, 2]); + if (!clipRef) throw new Error("couldn't trace the clip/vae lines"); + + const add = makeAdder(graph); + + // A decode-only / 2x custom VAE loader can't ENCODE — i2i needs both. Graft a + // stock VAELoader on the shared latent if the trace failed or hit that loader. + const tracedVae = isLink(vaeRef) ? graph[String(vaeRef[0])] : null; + if (!vaeRef || (tracedVae && tracedVae.class_type === "VAEUtils_CustomVAELoader")) { + const vaeName = probeCombo("VAELoader", "vae_name").find((o) => /qwen.*image.*vae/i.test(o)) + || probeCombo("VAELoader", "vae_name").find((o) => /wan.*2[._]?1.*vae/i.test(o) && !/upscale|2x/i.test(o)); + if (!vaeName) throw new Error("couldn't trace the clip/vae lines"); + vaeRef = [add("VAELoader", { vae_name: vaeName }), 0]; + } + + const arch = sourceArch(Object.values(graph)); + const fluxLike = arch === "flux1" || arch === "flux2"; + const { ksId, previewId } = attachI2ITail(graph, add, { + inputRef: data.input_ref, modelRef, clipRef, vaeRef, options, + guidance: fluxLike ? I2I_RECIPE_DEFAULTS.flux1.guidance : undefined, + steps: numOr(options.steps, numOr(src.inputs.steps, 30)), + // flux pins cfg 1.0 (guidance carries it); other arches inherit the dial. + cfg: fluxLike ? 1.0 : numOr(options.cfg, numOr(src.inputs.cfg, 5)), + sampler: options.sampler || (typeof src.inputs.sampler_name === "string" ? src.inputs.sampler_name : "euler"), + scheduler: options.scheduler || (typeof src.inputs.scheduler === "string" ? src.inputs.scheduler : "normal"), + }); + + // Prune to the new PreviewImage's upstream closure — the source gen (its + // sampler, pose, couple, old encodes, saves, and ITS own PreviewImage/saves) + // all falls away. Walk from the terminal preview so VAEDecode/KSampler/loaders + // are all retained. + const keep = new Set(); + const stack = [previewId]; + while (stack.length) { + const id = stack.pop(); + if (!id || keep.has(id)) continue; + keep.add(id); + for (const v of Object.values(graph[id].inputs || {})) { + if (isLink(v)) stack.push(String(v[0])); + } + } + for (const id of Object.keys(graph)) if (!keep.has(id)) delete graph[id]; + + return { graph, detailId: ksId }; +} + +function buildI2IGraph(data, options) { + if (options.engine === "sdxl-ckpt" && options.engineModel) return buildI2IEngineSdxl(data, options); + if (options.engine === "flux1-unet" && options.engineModel) return buildI2IEngineFlux1(data, options); + if (options.engine === "krea2-unet" && options.engineModel) return buildI2IEngineKrea2(data, options); + return buildI2ISource(data.prompt, data, options); +} + +function collectI2IMeta(graph, detailId, data, options) { + const byClass = (...cls) => Object.values(graph).find((n) => cls.includes(n.class_type)); + const loader = byClass("CheckpointLoaderSimple", "CheckpointLoader") || byClass("UNETLoader"); + const lit = (v) => (Array.isArray(v) || v === undefined ? null : v); + const ks = graph[detailId].inputs; + return { + parent_filename: data.input_ref, + model: lit(loader?.inputs?.ckpt_name) || lit(loader?.inputs?.unet_name) || null, + seed: lit(ks.seed), steps: lit(ks.steps), cfg: lit(ks.cfg), + sampler: lit(ks.sampler_name), scheduler: lit(ks.scheduler), + denoise: options.denoise ?? null, prompt: options.prompt || "", negative: "", + }; +} + +// Background i2i render. Mirrors runInpaint's lifecycle, with two differences: +// no mask upload, and listeners are registered BEFORE the queue with a pending +// buffer that replays once prompt_id is known — a full-frame i2i can be served +// from cache (~0s), and an execution_success arriving before queuePrompt's +// response sets prompt_id would otherwise be dropped and the promise would hang +// (the bug just fixed in enlargeImageInBackground). runInpaint registers after +// the queue and lacks this guard; i2i must not inherit that race. +export function runI2IInBackground(prepared, options = {}) { + const tracker = createTracker(); + const run = { cancelled: false, promptId: null, executionStarted: false, cleanup: null }; + tracker.cancel = async () => { + if (run.cancelled) return; + run.cancelled = true; + tracker.emit({ phase: "cancelled" }); + try { + if (run.promptId) { + if (run.executionStarted) await api.interrupt(); + else { + await api.fetchApi("/queue", { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ delete: [run.promptId] }), + }); + run.cleanup?.(); + } + } + } catch (e) { + console.warn("[PromptChain] i2i cancel failed", e); + } + }; + + (async () => { + const { data } = prepared; + tracker.emit({ phase: "building" }); + let built; + try { + built = buildI2IGraph(data, options); + } catch (e) { + console.error("[PromptChain][i2i] build failed:", e); + tracker.emit({ phase: "error", message: `build failed: ${e.message}` }); + toast("error", `i2i build failed: ${e.message}`); + return; + } + if (run.cancelled) return; + + tracker.emit({ phase: "queueing" }); + const workflowId = freshWorkflowId(); + const workflowExtra = data.workflow ? { ...data.workflow, id: workflowId } : undefined; + + let promptId = null; + let idKnown = false; + let meta = null; + let lastTemp = null; + let previewUrl = null; + const pending = []; + const setPreview = (blob) => { + if (previewUrl) URL.revokeObjectURL(previewUrl); + previewUrl = blob ? URL.createObjectURL(blob) : null; + return previewUrl; + }; + const cleanup = () => { + api.removeEventListener("execution_start", onStart); + api.removeEventListener("progress", onProgress); + api.removeEventListener("b_preview", onPreview); + api.removeEventListener("executed", onExecuted); + api.removeEventListener("execution_success", onSuccess); + api.removeEventListener("execution_error", onError); + api.removeEventListener("execution_interrupted", onError); + if (promptId) externallyTrackedPrompts.delete(promptId); + setPreview(null); + }; + run.cleanup = cleanup; + + const takeStart = (d) => { if (d?.prompt_id === promptId) run.executionStarted = true; }; + const takeProgress = (d) => { + if (d?.prompt_id !== promptId || run.cancelled) return; + run.executionStarted = true; + tracker.emit({ phase: "running", value: d.value, max: d.max, previewUrl }); + }; + const takeExecuted = (d) => { + if (d?.prompt_id !== promptId || !d?.output?.images?.length) return; + lastTemp = d.output.images[d.output.images.length - 1]; + }; + const takeSuccess = (d) => { + if (d?.prompt_id !== promptId) return; + cleanup(); + if (run.cancelled) return; + if (!lastTemp) { tracker.emit({ phase: "error", message: "run produced no image" }); return; } + tracker.emit({ phase: "done", temp: lastTemp, meta, workflowId }); + }; + const takeError = (d) => { + if (d?.prompt_id !== promptId) return; + cleanup(); + if (run.cancelled) return; + const message = d?.exception_message || "execution failed"; + tracker.emit({ phase: "error", message }); + toast("error", `i2i failed: ${message}`); + }; + + // b_preview carries no prompt_id and self-gates on run.executionStarted, so + // it's safe to handle live. The prompt_id-matching events buffer until the id + // is known, then replay. + const onStart = ({ detail }) => idKnown ? takeStart(detail) : pending.push(["start", detail]); + const onProgress = ({ detail }) => idKnown ? takeProgress(detail) : pending.push(["progress", detail]); + const onExecuted = ({ detail }) => idKnown ? takeExecuted(detail) : pending.push(["executed", detail]); + const onSuccess = ({ detail }) => idKnown ? takeSuccess(detail) : pending.push(["success", detail]); + const onError = ({ detail }) => idKnown ? takeError(detail) : pending.push(["error", detail]); + const onPreview = ({ detail }) => { + if (run.cancelled || !run.executionStarted || !(detail instanceof Blob)) return; + const url = setPreview(detail); + const s = tracker.state; + if (s.phase === "running") tracker.emit({ ...s, previewUrl: url }); + }; + api.addEventListener("execution_start", onStart); + api.addEventListener("progress", onProgress); + api.addEventListener("b_preview", onPreview); + api.addEventListener("executed", onExecuted); + api.addEventListener("execution_success", onSuccess); + api.addEventListener("execution_error", onError); + api.addEventListener("execution_interrupted", onError); + + armExternalQueue(); + try { + const previewMethod = app.extensionManager?.setting?.get?.("Comfy.Execution.PreviewMethod") + ?? app.ui?.settings?.getSettingValue?.("Comfy.Execution.PreviewMethod") + ?? "default"; + const res = await api.queuePrompt(0, { output: built.graph, workflow: workflowExtra }, { previewMethod }); + promptId = res?.prompt_id || null; + } catch (e) { + disarmExternalQueue(); + cleanup(); + const message = e?.response?.error?.message || e?.message || "queue rejected the prompt"; + tracker.emit({ phase: "error", message }); + toast("error", `i2i failed to queue: ${message}`); + return; + } + if (!promptId) { + disarmExternalQueue(); + cleanup(); + tracker.emit({ phase: "error", message: "queue returned no prompt id" }); + return; + } + externallyTrackedPrompts.add(promptId); + disarmExternalQueue(); + run.promptId = promptId; + meta = collectI2IMeta(built.graph, built.detailId, data, options); + if (run.cancelled) { + try { + await api.fetchApi("/queue", { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ delete: [promptId] }), + }); + } catch {} + cleanup(); + return; + } + tracker.emit({ phase: "running" }); + + // The id is known — drain anything that arrived first (a cached run can have + // already fired success). executed before success so lastTemp is set. + idKnown = true; + for (const [k, d] of pending) if (k === "start") takeStart(d); + for (const [k, d] of pending) if (k === "progress") takeProgress(d); + for (const [k, d] of pending) if (k === "executed") takeExecuted(d); + for (const [k, d] of pending) { if (k === "success") takeSuccess(d); else if (k === "error") takeError(d); } + pending.length = 0; + })().catch((e) => { + console.error("[PromptChain] i2i run failed", e); + tracker.emit({ phase: "error", message: e?.message || "unexpected failure" }); + toast("error", `i2i failed: ${e?.message || e}`); + }); + return tracker; +} diff --git a/js/lib/inpaint-background.js b/js/lib/inpaint-background.js index 38881b1..6d587c8 100644 --- a/js/lib/inpaint-background.js +++ b/js/lib/inpaint-background.js @@ -24,12 +24,12 @@ function toast(severity, detail) { app.extensionManager?.toast?.add({ severity, summary: "Inpaint", detail, life: 7000 }); } -function freshWorkflowId() { +export function freshWorkflowId() { if (globalThis.crypto?.randomUUID) return crypto.randomUUID(); return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2, 10)}`; } -function createTracker() { +export function createTracker() { const listeners = new Set(); const tracker = { state: { phase: "building" }, @@ -770,10 +770,24 @@ export function buildKrea2InpaintPrompt(data, options, maskName) { return node?.widgets?.find((w) => w.name === widget)?.options?.values || []; } catch { return []; } }; + // Realism mode (krea2_turbo): abliterated Qwen3-VL encoder + projector/realism + // LoRAs. VAE stays qwen_image_vae on purpose — MaskedDetail composites the + // re-detailed patch back into the qwen-decoded frame, so decoding it through + // wan_2.1 would shift colour/texture and leave a seam (same reason the krea2 + // tile upscale keeps qwen_image_vae). + const realism = !!options.realism; const clipOpts = probe("CLIPLoader", "clip_name"); - const clipFile = clipOpts.find((o) => /qwen3vl.*4b/i.test(o)) || clipOpts.find((o) => /qwen3.?vl.*4b/i.test(o)); + const loraOpts = probe("LoraLoaderModelOnly", "lora_name"); + const clipFile = realism + ? clipOpts.find((o) => /huihui.*qwen3.?vl.*4b.*abliterated|qwen3.?vl.*4b.*abliterated/i.test(o)) + : (clipOpts.find((o) => /qwen3vl.*4b/i.test(o)) || clipOpts.find((o) => /qwen3.?vl.*4b/i.test(o))); const vaeFile = probe("VAELoader", "vae_name").find((o) => /qwen.*image.*vae/i.test(o)); - if (!clipFile || !vaeFile) throw new Error("the Krea 2 text encoder / VAE aren't installed"); + const projLora = realism && loraOpts.find((o) => /krea2_turbo_projector_scale/i.test(o)); + const realLora = realism && loraOpts.find((o) => /krea2-realism/i.test(o)); + if (!clipFile || !vaeFile) throw new Error(realism + ? "Realism mode needs the abliterated Qwen3-VL encoder installed (Huihui-Qwen3-VL-4B-Instruct-abliterated)" + : "the Krea 2 text encoder / VAE aren't installed"); + if (realism && (!projLora || !realLora)) throw new Error("Realism mode needs the Krea2-realism-V1 + krea2_turbo_projector_scale LoRAs installed"); const graph = {}; let nextId = offscreenIdBase(); // render-node ids must stay off the live canvas — else execution images paint onto a live node sharing the id (see offscreen-graph.js) @@ -808,6 +822,11 @@ export function buildKrea2InpaintPrompt(data, options, maskName) { const msfId = add("ModelSamplingFlux", { model: [unetId, 0], max_shift: 0.90625, base_shift: 0.5, width: 1024, height: 1024 }); modelRef = [msfId, 0]; } + if (realism) { + const proj = add("LoraLoaderModelOnly", { model: modelRef, lora_name: projLora, strength_model: 0.5 }); + const real = add("LoraLoaderModelOnly", { model: [proj, 0], lora_name: realLora, strength_model: 0.6 }); + modelRef = [real, 0]; + } const detailId = add("PromptChain_MaskedDetail", { model: modelRef, vae: [vaeId, 0], image: [loadId, 0], mask: [maskId, 0], diff --git a/js/lib/model-indicator-bridge.js b/js/lib/model-indicator-bridge.js index c02f77b..706611c 100644 --- a/js/lib/model-indicator-bridge.js +++ b/js/lib/model-indicator-bridge.js @@ -39,7 +39,7 @@ api.addEventListener("promptchain_recognition_done", () => { const KSAMPLER_TYPES = new Set([ "KSampler", "KSamplerAdvanced", "PromptChain_RegionalDetailer", "SamplerCustomAdvanced", "UltimateSDUpscale", "UltimateSDUpscaleNoUpscale", - "PromptChain_IdeogramSampler", + "PromptChain_IdeogramSampler", "ClownsharKSampler_Beta", ]); const MODEL_WIDGET_NAMES = ["ckpt_name", "unet_name", "model_name"]; diff --git a/js/lib/model-settings-bridge.js b/js/lib/model-settings-bridge.js index 67478f9..c3af9f3 100644 --- a/js/lib/model-settings-bridge.js +++ b/js/lib/model-settings-bridge.js @@ -943,6 +943,136 @@ function injectPuLID(pcNode) { return true; } +// ── Krea 2 style LoRA (always-present "Style" panel section) ──────── +// One swappable model-only LoRA on the sampler's model line, paired with a +// trigger word in the prompt. Selecting a style swaps both the LoRA file and +// the tracked trigger fragment; null removes the node and the trigger. The +// panel downloads the file first, so it's assumed on disk here (the combo-option +// push covers a lora list ComfyUI hasn't rescanned yet). +function escapeRegExp(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } + +function setStyleWidget(node, name, value, isCombo) { + const w = node.widgets?.find(x => x.name === name); + if (!w) return; + if (isCombo && w.options?.values && !w.options.values.includes(value)) w.options.values.push(value); + w.value = value; + w.callback?.(value); +} + +function spliceStyleLora(graph, loraNode, ksNode) { + const modelIdx = ksNode.inputs?.findIndex(i => i.name === "model"); + if (modelIdx == null || modelIdx < 0 || ksNode.inputs[modelIdx].link == null) return; + const link = getLink(graph, ksNode.inputs[modelIdx].link); + if (!link) return; + const source = graph.getNodeById(link.origin_id); + if (!source) return; + const sourceSlot = link.origin_slot ?? (source.outputs?.findIndex(o => o.type === "MODEL") ?? 0); + const loraIn = loraNode.inputs?.findIndex(i => i.name === "model") ?? 0; + ksNode.disconnectInput(modelIdx); + source.connect(sourceSlot, loraNode, loraIn); + loraNode.connect(0, ksNode, modelIdx); +} + +function removeStyleLora(graph, loraNode, ksNode) { + // Reconnect the LoRA's upstream model source straight to the sampler so the + // model line survives the node's removal. + const loraIn = loraNode.inputs?.findIndex(i => i.name === "model") ?? 0; + const inLink = loraNode.inputs?.[loraIn]?.link; + const modelIdx = ksNode.inputs?.findIndex(i => i.name === "model"); + if (inLink != null && modelIdx != null && modelIdx >= 0) { + const l = getLink(graph, inLink); + const source = l && graph.getNodeById(l.origin_id); + if (source) { + ksNode.disconnectInput(modelIdx); + source.connect(l.origin_slot ?? 0, ksNode, modelIdx); + } + } + graph.remove(loraNode); +} + +// Keep a single tracked trigger (pcNode.properties.pcrStyleTrigger) in the +// positive half of the prompt. Swapping replaces the old fragment with the new; +// only the tracked substring is touched, never other user text. +function swapStyleTrigger(pcNode, newTrigger) { + const prev = pcNode.properties?.pcrStyleTrigger || ""; + if (prev === (newTrigger || "")) return; + const view = pcNode._pcrEditor; + const promptWidget = pcNode.widgets?.find(w => w.name === "prompt"); + const doc = view ? view.state.doc.toString() : String(promptWidget?.value || ""); + + // Never insert past the "Negative Prompt:" marker — triggers are positive. + const NEG_RE = /(^|\n)[ \t]*negative\s+prompt\s*:/i; + const m = doc.match(NEG_RE); + const splitAt = m ? m.index : doc.length; + let pos = doc.slice(0, splitAt); + const tail = doc.slice(splitAt); + + const strip = (text, trig) => trig + ? text.replace(new RegExp("(,\\s*)?" + escapeRegExp(trig) + "(\\s*,)?", "i"), + (_, a, b) => (a && b) ? ", " : "") + : text; + pos = strip(pos, prev); + if (newTrigger) pos = strip(pos, newTrigger); // guard against a pre-existing copy + pos = pos.replace(/,\s*,/g, ",").replace(/\s+$/, ""); + if (newTrigger) pos = pos ? pos + ", " + newTrigger : newTrigger; + + const next = tail ? (pos ? pos + tail : tail.replace(/^\n/, "")) : pos; + + if (view) { + view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: next } }); + } else if (promptWidget) { + promptWidget.value = next; + promptWidget.callback?.(next); + } + pcNode.properties.pcrStyleTrigger = newTrigger || ""; +} + +// Apply / swap / clear the Krea 2 style LoRA. `lora` is a catalog entry +// {id, filename, trigger, ...} or null to remove. Returns true on success. +export function applyStyleLora(pcNode, lora, strength) { + const graph = pcNode.graph || app.graph; + if (!graph) return false; + const LG = window.LiteGraph || graph.constructor?.LiteGraph; + if (!LG) return false; + + const ksNode = findDownstreamNodes(pcNode).find( + n => n.comfyClass === "KSampler" || n.comfyClass === "KSamplerAdvanced" + ); + if (!ksNode) return false; + + pcNode.properties = pcNode.properties || {}; + const existing = (graph._nodes || []).find( + n => n.comfyClass === "LoraLoaderModelOnly" && n.properties?.pcrStyleLora + ); + + if (!lora) { + if (existing) removeStyleLora(graph, existing, ksNode); + swapStyleTrigger(pcNode, null); + pcNode.properties.pcrStyleLora = ""; + graph.setDirtyCanvas(true, true); + return true; + } + + let loraNode = existing; + if (!loraNode) { + loraNode = LG.createNode("LoraLoaderModelOnly"); + if (!loraNode) return false; + loraNode.properties = loraNode.properties || {}; + loraNode.properties.pcrStyleLora = true; + loraNode.title = "Style LoRA"; + loraNode.pos = [pcNode.pos[0] + 420, pcNode.pos[1] + 520]; + graph.add(loraNode); + spliceStyleLora(graph, loraNode, ksNode); + } + setStyleWidget(loraNode, "lora_name", lora.filename, true); + setStyleWidget(loraNode, "strength_model", strength, false); + + swapStyleTrigger(pcNode, lora.trigger || null); + pcNode.properties.pcrStyleLora = lora.id || lora.filename; + graph.setDirtyCanvas(true, true); + return true; +} + // IPAdapter style transfer ("Style Reference", cubiq's ComfyUI_IPAdapter_plus). // The UnifiedLoader bundles the ipadapter weights + CLIP-Vision encoder into // one IPADAPTER pipe; IPAdapterAdvanced with weight_type "style transfer" @@ -2703,6 +2833,7 @@ export async function showModelModal(pcNode, modelInfo, anchorEl, { tab = null } closeModelModal(); window.dispatchEvent(new CustomEvent("pcr-open-model-picker", { detail: { nodeId: pcNode.id } })); }, + onApplyStyleLora: (lora, strength) => applyStyleLora(pcNode, lora, strength), onInjectNode: async (type) => { // ControlNet items arrive as "ControlNet:" (depth/canny/…); // the base type drives requirements + the install modal. diff --git a/js/lib/svelte/assets/promptchain-svelte.css b/js/lib/svelte/assets/promptchain-svelte.css index 57d556c..85ff5b5 100644 --- a/js/lib/svelte/assets/promptchain-svelte.css +++ b/js/lib/svelte/assets/promptchain-svelte.css @@ -667,6 +667,116 @@ border-radius: 5px; word-break: break-word; } + .pcr-ip-modal.svelte-uprazu { + width: 96vw; height: 94vh; + min-width: 900px; max-width: 96vw; + display: flex; flex-direction: column; + } + .pcr-ip-body.svelte-uprazu { display: flex; gap: 16px; flex: 1; min-height: 0; } + .pcr-ip-left.svelte-uprazu { flex: 1; min-width: 0; display: flex; flex-direction: column; } + .pcr-ip-right.svelte-uprazu { flex: 0 0 320px; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; padding-right: 4px; } + .pcr-ip-tabs.svelte-uprazu { display: flex; align-items: center; margin-bottom: 8px; } + .pcr-ip-seg.svelte-uprazu { display: flex; border: 1px solid #3a3a3a; border-radius: 5px; overflow: hidden; } + .pcr-ip-tab.svelte-uprazu { padding: 4px 18px; font-size: 12px; color: #aaa; cursor: pointer; background: transparent; } + .pcr-ip-tab.svelte-uprazu + .pcr-ip-tab:where(.svelte-uprazu) { border-left: 1px solid #3a3a3a; } + .pcr-ip-tab.svelte-uprazu:hover:not(.active) { color: #ddd; } + .pcr-ip-tab.active.svelte-uprazu { background: #c85909; color: #fff; } + .pcr-ip-tools.svelte-uprazu { display: flex; align-items: center; gap: 8px; margin-left: auto; align-self: center; } + .pcr-ip-tool-btn.svelte-uprazu { + padding: 3px 10px; font-size: 11.5px; color: #aaa; + background: #2a2a2a; border: 1px solid #3a3a3a; border-radius: 4px; cursor: pointer; + } + .pcr-ip-tool-btn.svelte-uprazu:hover { color: #fff; border-color: #555; } + .pcr-ip-tool-btn.on.svelte-uprazu { color: #fff; border-color: #c85909; background: rgba(200, 89, 9, 0.2); } + .pcr-ip-tool-btn.svelte-uprazu:disabled { opacity: 0.4; cursor: default; } + .pcr-ip-viewport.svelte-uprazu { + position: relative; overflow: hidden; + flex: 1; min-height: 0; background: #141414; + border: 1px solid #3a3a3a; border-radius: 6px; + touch-action: none; cursor: grab; + } + .pcr-ip-viewport.svelte-uprazu:active { cursor: grabbing; } + .pcr-ip-stage.svelte-uprazu { position: absolute; transform-origin: 0 0; } + .pcr-ip-stage.svelte-uprazu img:where(.svelte-uprazu) { display: block; user-select: none; pointer-events: none; } + .pcr-ip-split-before.svelte-uprazu { position: absolute; inset: 0; z-index: 4; pointer-events: none; } + .pcr-ip-split-divider.svelte-uprazu { + position: absolute; top: 0; bottom: 0; + width: 18px; margin-left: -9px; + z-index: 7; cursor: ew-resize; touch-action: none; + } + .pcr-ip-split-divider.svelte-uprazu::before { + content: ""; position: absolute; top: 0; bottom: 0; left: 50%; + width: 2px; margin-left: -1px; + background: rgba(255, 255, 255, 0.9); box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5); + } + .pcr-ip-split-knob.svelte-uprazu { + position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); + width: 30px; height: 30px; border-radius: 50%; + background: rgba(20, 20, 20, 0.85); border: 2px solid rgba(255, 255, 255, 0.9); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5); + display: flex; align-items: center; justify-content: center; color: #fff; + } + .pcr-ip-split-knob.svelte-uprazu svg:where(.svelte-uprazu) { width: 18px; height: 18px; } + .pcr-ip-split-label.svelte-uprazu { + position: absolute; top: 8px; z-index: 6; + padding: 2px 7px; font-size: 11px; font-weight: 600; letter-spacing: 0.3px; + color: #fff; background: rgba(20, 20, 20, 0.7); border-radius: 4px; + pointer-events: none; + } + .pcr-ip-split-label.before.svelte-uprazu { left: 8px; } + .pcr-ip-split-label.after.svelte-uprazu { right: 8px; } + .pcr-ip-center.svelte-uprazu { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; } + .pcr-ip-live-preview.svelte-uprazu { max-width: 100%; max-height: 100%; object-fit: contain; } + .pcr-ip-waiting.svelte-uprazu { font-size: 12.5px; color: #777; } + .pcr-ip-combos.svelte-uprazu { display: flex; gap: 10px; } + .pcr-ip-combo.svelte-uprazu { flex: 1; display: flex; flex-direction: column; gap: 4px; min-width: 0; } + .pcr-ip-select.svelte-uprazu { + width: 100%; box-sizing: border-box; + padding: 5px 8px; font-size: 12px; color: #999; + background: #1c1c1c; border: 1px solid #3a3a3a; border-radius: 5px; + outline: none; cursor: pointer; + } + .pcr-ip-select.at-rec.svelte-uprazu { color: #5ed357; border-color: rgba(94, 211, 87, 0.45); } + .pcr-ip-progress.svelte-uprazu { position: absolute; left: 16px; right: 16px; bottom: 12px; display: flex; align-items: center; gap: 10px; } + .pcr-ip-bar.svelte-uprazu { flex: 1; height: 6px; border-radius: 3px; background: rgba(0, 0, 0, 0.55); border: 1px solid #3a3a3a; overflow: hidden; } + .pcr-ip-bar-fill.svelte-uprazu { height: 100%; background: #c85909; transition: width 0.2s ease; } + .pcr-ip-bar-fill.indeterminate.svelte-uprazu { width: 35%; animation: svelte-uprazu-pcr-i2i-slide 1.2s ease-in-out infinite alternate; } + @keyframes svelte-uprazu-pcr-i2i-slide { from { margin-left: 0; } to { margin-left: 65%; } } + .pcr-ip-bar-label.svelte-uprazu { font-size: 10.5px; color: #bbb; } + .pcr-ip-label.svelte-uprazu { font-size: 11px; color: #888; margin-top: 6px; } + .pcr-ip-adv-head.svelte-uprazu { margin-top: 8px; font-size: 11px; color: #888; cursor: pointer; user-select: none; } + .pcr-ip-adv-head.svelte-uprazu:hover { color: #ccc; } + .pcr-ip-hint.svelte-uprazu { font-size: 11px; color: #777; } + .pcr-ip-prompt.svelte-uprazu { + min-height: 200px; resize: vertical; + padding: 8px 10px; font-size: 12.5px; line-height: 1.45; color: #ddd; + background: #1c1c1c; border: 1px solid #3a3a3a; border-radius: 5px; + outline: none; font-family: inherit; + } + .pcr-ip-prompt.svelte-uprazu:focus { border-color: #c85909; } + .pcr-ip-prompt-editor.svelte-uprazu { + height: 220px; padding: 0; resize: none; overflow: hidden; + display: flex; flex-direction: column; + } + .pcr-ip-prompt-editor.svelte-uprazu:focus-within { border-color: #c85909; } + .pcr-ip-prompt-editor.svelte-uprazu .cm-editor { flex: 1; min-height: 0; height: 100%; } + .pcr-ip-prompt-editor.svelte-uprazu .cm-scroller { overflow: auto; } + .pcr-ip-refprompt.svelte-uprazu { + height: 110px; resize: vertical; + padding: 7px 10px; font-size: 11.5px; line-height: 1.4; color: #9a9a9a; + background: #161616; border: 1px solid #313131; border-radius: 5px; + outline: none; font-family: inherit; cursor: text; user-select: text; + } + .pcr-ip-denoise.svelte-uprazu { display: flex; align-items: center; gap: 10px; } + .pcr-ip-denoise.svelte-uprazu .pcr-ip-label:where(.svelte-uprazu) { flex: none; width: 52px; margin-top: 0; } + .pcr-modal-btn.svelte-uprazu:disabled { opacity: 0.45; cursor: default; } + .pcr-ip-error.svelte-uprazu { + margin: 0 2px 2px; padding: 7px 12px; + font-size: 12px; color: #ff8a80; line-height: 1.4; + background: rgba(229, 57, 53, 0.12); border: 1px solid rgba(229, 57, 53, 0.4); + border-radius: 5px; word-break: break-word; + } + .pcr-adj-backdrop.svelte-k7hhb3 { z-index: 10010; } .pcr-adj-modal.svelte-k7hhb3 { width: 97vw; height: 95vh; @@ -980,8 +1090,9 @@ }*/ /* (unused) .pcr-ed-ip-prompt:focus { border-color: #c85909; }*/ /* (unused) .pcr-ed-ip-row { display: flex; align-items: center; gap: 10px; margin: 4px 0; }*/ - .pcr-ed-render-row.svelte-1ozzqkm { display: flex; gap: 8px; } - .pcr-ed-render-row.svelte-1ozzqkm .pcr-ed-ip-render:where(.svelte-1ozzqkm) { flex: 1; margin-top: 0; width: auto; white-space: nowrap; } + .pcr-ed-render-row.svelte-1ozzqkm { display: flex; flex-wrap: wrap; gap: 8px; } + /* ~50% basis → two buttons per row (a lone trailing button grows to full). */ + .pcr-ed-render-row.svelte-1ozzqkm .pcr-ed-ip-render:where(.svelte-1ozzqkm) { flex: 1 1 calc(50% - 4px); margin-top: 0; width: auto; white-space: nowrap; } .pcr-ed-ip-render.svelte-1ozzqkm { width: 100%; margin-top: 6px; padding: 7px 10px; font-size: 12.5px; color: #fff; cursor: pointer; background: #c85909; border: none; border-radius: 5px; @@ -6706,6 +6817,7 @@ letter-spacing: 0; } .pcr-section-expand-toggle.svelte-82atyh:hover { color: #999; } + .pcr-style-status.svelte-82atyh { font-size: 11px; color: #5dcaff; } /* widget rows */ .pcr-model-panel-row.svelte-82atyh { diff --git a/js/lib/svelte/promptchain-model-settings.js b/js/lib/svelte/promptchain-model-settings.js index 1ac1bd8..af80e05 100644 --- a/js/lib/svelte/promptchain-model-settings.js +++ b/js/lib/svelte/promptchain-model-settings.js @@ -1,6 +1,7 @@ -import { d as delegate, p as push, a as prop, s as state, c as proxy, u as user_effect, e as set, f as sibling, o as bind_this, t as template_effect, g as get, x as set_attribute, q as set_value, j as delegated, k as append, l as pop, m as user_derived, n as child, A as from_html, w as each, z as index, i as if_block, G as set_checked, y as set_text, h as set_class, v as first_child, D as comment, I as to_array, a1 as set_selected, R as text, E as untrack, M as unmount, L as mount } from "./disclose-version-uq4tn5Y6.js"; +import { d as delegate, p as push, a as prop, s as state, c as proxy, u as user_effect, e as set, f as sibling, o as bind_this, t as template_effect, g as get, x as set_attribute, q as set_value, j as delegated, k as append, l as pop, m as user_derived, n as child, A as from_html, w as each, z as index, i as if_block, G as set_checked, y as set_text, h as set_class, v as first_child, D as comment, a1 as set_selected, I as to_array, R as text, E as untrack, M as unmount, L as mount } from "./disclose-version-uq4tn5Y6.js"; import { s as set_style } from "./style-Boi27oOu.js"; import { F as FAMILIES, A as ARCHITECTURES } from "./model-constants-cVxhUf51.js"; +import { api } from "/scripts/api.js"; import { S as SettingsSlider } from "./SettingsSlider-CKF_XmgB.js"; import { b as bind_value } from "./input-DFQhebEz.js"; import { b as bind_select_value } from "./select-Dp4ExMMc.js"; @@ -197,24 +198,29 @@ var root_28 = from_html(`/
`, 1); var root_18$1 = from_html(`
`); var root_2$5 = from_html(`
`); -var root_30 = from_html(`
`); -var root_31 = from_html(` `, 1); -var root_36 = from_html(``); -var root_35 = from_html(` `, 1); -var root_39 = from_html(``); +var root_31 = from_html(``); +var root_32 = from_html(`
strength
`); +var root_33 = from_html(`
`); +var root_30 = from_html(`
Style
LoRA
`); +var root_34 = from_html(`
`); +var root_35 = from_html(` `, 1); var root_40 = from_html(``); -var root_41 = from_html(``); -var root_34 = from_html(``); -var root_33 = from_html(`
`); -var root_43 = from_html(``); -var root_44 = from_html(``); -var root_42 = from_html(``); -var root_32 = from_html(`
`, 1); -var root$5 = from_html(`
`, 1); +var root_39 = from_html(` `, 1); +var root_43 = from_html(``); +var root_44 = from_html(``); +var root_45 = from_html(``); +var root_38 = from_html(``); +var root_37 = from_html(`
`); +var root_47 = from_html(``); +var root_48 = from_html(``); +var root_46 = from_html(``); +var root_36 = from_html(`
`, 1); +var root$5 = from_html(`
`, 1); function SettingsTab($$anchor, $$props) { - var _a, _b, _c; + var _a, _b, _c, _d, _e; push($$props, true); - let detected = prop($$props, "detected", 19, () => []), savedConfig = prop($$props, "savedConfig", 3, null), rangeMode = prop($$props, "rangeMode", 3, false), expandedSections = prop($$props, "expandedSections", 23, () => ({})); + let detected = prop($$props, "detected", 19, () => []), savedConfig = prop($$props, "savedConfig", 3, null), rangeMode = prop($$props, "rangeMode", 3, false), expandedSections = prop($$props, "expandedSections", 23, () => ({})), pcNode = prop($$props, "pcNode", 3, null), architecture = prop($$props, "architecture", 3, ""), family = prop($$props, "family", 3, ""), onApplyStyleLora = prop($$props, "onApplyStyleLora", 3, () => { + }); const DISPLAY_NAMES = { sampler_name: "Sampler", stop_at_clip_layer: "Last Layer", @@ -474,6 +480,128 @@ function SettingsTab($$anchor, $$props) { null // the group (e.g. ControlNet) drilled into ); + const showStyleSection = user_derived(() => architecture() === "krea2" && family() === "krea2_turbo"); + let styleCatalog = state(proxy([])); + let styleInstalled = state(proxy( + {} + // filename → exists + )); + let styleSelected = state(proxy(((_e = (_d = pcNode()) == null ? void 0 : _d.properties) == null ? void 0 : _e.pcrStyleLora) || "")); + let styleStrength = state(1); + let styleBusy = state(false); + let styleStatus = state(""); + user_effect(() => { + if (!get(showStyleSection)) return; + let cancelled = false; + (async () => { + try { + const res = await fetch(`/promptchain/loras/catalog?architecture=${encodeURIComponent(architecture())}&family=${encodeURIComponent(family())}`); + const data = await res.json(); + if (cancelled) return; + set(styleCatalog, data.loras || [], true); + const cur = get(styleCatalog).find((l) => l.id === get(styleSelected)); + if (cur) set(styleStrength, cur.strength_default ?? 1, true); + await refreshStyleInstalled(); + } catch (e) { + console.warn("[PromptChain] style catalog load failed:", e); + } + })(); + return () => { + cancelled = true; + }; + }); + async function refreshStyleInstalled() { + if (!get(styleCatalog).length) return; + try { + const res = await fetch("/promptchain/models/check-files", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + files: get(styleCatalog).map((l) => ({ + filename: l.filename, + folder: l.folder || "loras", + size_bytes: l.size_bytes || 0 + })) + }) + }); + const data = await res.json(); + const map = {}; + for (const r of data.results || []) map[r.filename] = r.exists; + set(styleInstalled, map, true); + } catch (e) { + console.warn("[PromptChain] style install check failed:", e); + } + } + function downloadStyle(lora) { + return new Promise((resolve) => { + const s = lora.source || {}; + const url = s.provider === "huggingface" ? `https://huggingface.co/${s.repo}/resolve/main/${s.path}` : s.download_url || s.url || ""; + if (!url) { + resolve(false); + return; + } + const cleanup = () => { + api.removeEventListener("promptchain_download_progress", onProgress); + api.removeEventListener("promptchain_download_done", onDone); + }; + function onProgress({ detail }) { + if (detail.filename !== lora.filename) return; + set(styleStatus, `Downloading ${lora.label}: ${detail.progress}%`); + } + function onDone({ detail }) { + if (detail.filename !== lora.filename) return; + cleanup(); + resolve(detail.status === "completed"); + } + api.addEventListener("promptchain_download_progress", onProgress); + api.addEventListener("promptchain_download_done", onDone); + fetch("/promptchain/civitai/download", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ url, filename: lora.filename, folder: lora.folder || "loras" }) + }).then((r) => r.json()).then((d) => { + if (d.error) { + cleanup(); + set(styleStatus, d.error, true); + resolve(false); + } + }).catch((err) => { + cleanup(); + set(styleStatus, err.message, true); + resolve(false); + }); + }); + } + async function onStyleChange(id) { + var _a2, _b2; + set(styleSelected, id, true); + if (!id) { + onApplyStyleLora()(null, get(styleStrength)); + set(styleStatus, ""); + return; + } + const lora = get(styleCatalog).find((l) => l.id === id); + if (!lora) return; + set(styleStrength, lora.strength_default ?? get(styleStrength) ?? 1, true); + if (!get(styleInstalled)[lora.filename]) { + set(styleBusy, true); + const ok = await downloadStyle(lora); + set(styleBusy, false); + if (!ok) { + set(styleStatus, get(styleStatus) || "Download failed", true); + set(styleSelected, ((_b2 = (_a2 = pcNode()) == null ? void 0 : _a2.properties) == null ? void 0 : _b2.pcrStyleLora) || "", true); + return; + } + set(styleInstalled, { ...get(styleInstalled), [lora.filename]: true }, true); + set(styleStatus, ""); + } + onApplyStyleLora()(lora, get(styleStrength)); + } + function onStyleStrength(v) { + set(styleStrength, v, true); + const lora = get(styleCatalog).find((l) => l.id === get(styleSelected)); + if (lora && get(styleInstalled)[lora.filename]) onApplyStyleLora()(lora, v); + } let editMenuOpen = state(false); let injectable = user_derived(() => $$props.injectableNodes()); var fragment = root$5(); @@ -1064,23 +1192,90 @@ function SettingsTab($$anchor, $$props) { template_effect(() => set_text(text2, `${(config().label || type()) ?? ""} `)); append($$anchor2, div_2); }); - var node_24 = sibling(div, 2); + var node_24 = sibling(node_2, 2); { - var consequent_20 = ($$anchor2) => { + var consequent_22 = ($$anchor2) => { var div_16 = root_30(); - var text_11 = child(div_16); - template_effect(() => set_text(text_11, `Save failed: ${get(saveError) ?? ""}`)); + var div_17 = sibling(child(div_16), 2); + var div_18 = sibling(child(div_17), 2); + var select_3 = child(div_18); + var option_3 = child(select_3); + option_3.value = option_3.__value = ""; + var node_25 = sibling(option_3); + each(node_25, 17, () => get(styleCatalog), index, ($$anchor3, l) => { + var option_4 = root_31(); + var text_11 = child(option_4); + var option_4_value = {}; + template_effect(() => { + set_selected(option_4, get(l).id === get(styleSelected)); + set_text(text_11, `${get(l).label ?? ""}${get(styleInstalled)[get(l).filename] ? "" : " ⬇"}`); + if (option_4_value !== (option_4_value = get(l).id)) { + option_4.value = (option_4.__value = get(l).id) ?? ""; + } + }); + append($$anchor3, option_4); + }); + var node_26 = sibling(div_17, 2); + { + var consequent_20 = ($$anchor3) => { + var div_19 = root_32(); + var node_27 = sibling(child(div_19), 2); + SettingsSlider(node_27, { + min: 0, + max: 1.5, + step: 0.05, + get value() { + return get(styleStrength); + }, + onChange: (v) => onStyleStrength(v) + }); + append($$anchor3, div_19); + }; + if_block(node_26, ($$render) => { + if (get(styleSelected)) $$render(consequent_20); + }); + } + var node_28 = sibling(node_26, 2); + { + var consequent_21 = ($$anchor3) => { + var div_20 = root_33(); + var span_5 = child(div_20); + var text_12 = child(span_5); + template_effect(() => set_text(text_12, get(styleStatus))); + append($$anchor3, div_20); + }; + if_block(node_28, ($$render) => { + if (get(styleStatus)) $$render(consequent_21); + }); + } + template_effect(() => { + select_3.disabled = get(styleBusy); + set_selected(option_3, !get(styleSelected)); + }); + delegated("change", select_3, (e) => onStyleChange(e.target.value)); append($$anchor2, div_16); }; if_block(node_24, ($$render) => { - if (get(saveError)) $$render(consequent_20); + if (get(showStyleSection)) $$render(consequent_22); }); } - var div_17 = sibling(node_24, 2); - var node_25 = child(div_17); + var node_29 = sibling(div, 2); { - var consequent_21 = ($$anchor2) => { - var fragment_17 = root_31(); + var consequent_23 = ($$anchor2) => { + var div_21 = root_34(); + var text_13 = child(div_21); + template_effect(() => set_text(text_13, `Save failed: ${get(saveError) ?? ""}`)); + append($$anchor2, div_21); + }; + if_block(node_29, ($$render) => { + if (get(saveError)) $$render(consequent_23); + }); + } + var div_22 = sibling(node_29, 2); + var node_30 = child(div_22); + { + var consequent_24 = ($$anchor2) => { + var fragment_17 = root_35(); var button_2 = first_child(fragment_17); var button_3 = sibling(button_2, 2); delegated("click", button_2, (e) => { @@ -1091,37 +1286,37 @@ function SettingsTab($$anchor, $$props) { append($$anchor2, fragment_17); }; var alternate_4 = ($$anchor2) => { - var fragment_18 = root_32(); - var node_26 = first_child(fragment_18); + var fragment_18 = root_36(); + var node_31 = first_child(fragment_18); { - var consequent_26 = ($$anchor3) => { - var div_18 = root_33(); - var button_4 = child(div_18); - var node_27 = sibling(button_4, 2); + var consequent_29 = ($$anchor3) => { + var div_23 = root_37(); + var button_4 = child(div_23); + var node_32 = sibling(button_4, 2); { - var consequent_25 = ($$anchor4) => { - var div_19 = root_34(); - var node_28 = child(div_19); + var consequent_28 = ($$anchor4) => { + var div_24 = root_38(); + var node_33 = child(div_24); { - var consequent_22 = ($$anchor5) => { - var fragment_19 = root_35(); - var div_20 = first_child(fragment_19); - var text_12 = child(div_20); - var node_29 = sibling(div_20, 2); - each(node_29, 17, () => get(addSubmenu).children, index, ($$anchor6, child$1) => { - var div_21 = root_36(); - var text_13 = child(div_21); - template_effect(() => set_text(text_13, get(child$1).label)); - delegated("click", div_21, (e) => { + var consequent_25 = ($$anchor5) => { + var fragment_19 = root_39(); + var div_25 = first_child(fragment_19); + var text_14 = child(div_25); + var node_34 = sibling(div_25, 2); + each(node_34, 17, () => get(addSubmenu).children, index, ($$anchor6, child$1) => { + var div_26 = root_40(); + var text_15 = child(div_26); + template_effect(() => set_text(text_15, get(child$1).label)); + delegated("click", div_26, (e) => { e.stopPropagation(); set(addMenuOpen, false); set(addSubmenu, null); $$props.onInjectNode(get(child$1).value); }); - append($$anchor6, div_21); + append($$anchor6, div_26); }); - template_effect(() => set_text(text_12, `‹ ${get(addSubmenu).label ?? ""}`)); - delegated("click", div_20, (e) => { + template_effect(() => set_text(text_14, `‹ ${get(addSubmenu).label ?? ""}`)); + delegated("click", div_25, (e) => { e.stopPropagation(); set(addSubmenu, null); }); @@ -1129,46 +1324,46 @@ function SettingsTab($$anchor, $$props) { }; var alternate_3 = ($$anchor5) => { var fragment_20 = comment(); - var node_30 = first_child(fragment_20); - each(node_30, 17, () => get(injectable), index, ($$anchor6, item) => { + var node_35 = first_child(fragment_20); + each(node_35, 17, () => get(injectable), index, ($$anchor6, item) => { var fragment_21 = comment(); - var node_31 = first_child(fragment_21); + var node_36 = first_child(fragment_21); { - var consequent_23 = ($$anchor7) => { - var div_22 = root_39(); - var text_14 = child(div_22); - template_effect(() => set_text(text_14, get(item))); - delegated("click", div_22, (e) => { + var consequent_26 = ($$anchor7) => { + var div_27 = root_43(); + var text_16 = child(div_27); + template_effect(() => set_text(text_16, get(item))); + delegated("click", div_27, (e) => { e.stopPropagation(); set(addMenuOpen, false); $$props.onInjectNode(get(item)); }); - append($$anchor7, div_22); + append($$anchor7, div_27); }; - var consequent_24 = ($$anchor7) => { - var div_23 = root_40(); - var text_15 = child(div_23); - template_effect(() => set_text(text_15, `${get(item).label ?? ""} ▸`)); - delegated("click", div_23, (e) => { + var consequent_27 = ($$anchor7) => { + var div_28 = root_44(); + var text_17 = child(div_28); + template_effect(() => set_text(text_17, `${get(item).label ?? ""} ▸`)); + delegated("click", div_28, (e) => { e.stopPropagation(); set(addSubmenu, get(item), true); }); - append($$anchor7, div_23); + append($$anchor7, div_28); }; var alternate_2 = ($$anchor7) => { - var div_24 = root_41(); - var text_16 = child(div_24); - template_effect(() => set_text(text_16, get(item).label)); - delegated("click", div_24, (e) => { + var div_29 = root_45(); + var text_18 = child(div_29); + template_effect(() => set_text(text_18, get(item).label)); + delegated("click", div_29, (e) => { e.stopPropagation(); set(addMenuOpen, false); $$props.onInjectNode(get(item).value); }); - append($$anchor7, div_24); + append($$anchor7, div_29); }; - if_block(node_31, ($$render) => { - if (typeof get(item) === "string") $$render(consequent_23); - else if (get(item).children) $$render(consequent_24, 1); + if_block(node_36, ($$render) => { + if (typeof get(item) === "string") $$render(consequent_26); + else if (get(item).children) $$render(consequent_27, 1); else $$render(alternate_2, -1); }); } @@ -1176,15 +1371,15 @@ function SettingsTab($$anchor, $$props) { }); append($$anchor5, fragment_20); }; - if_block(node_28, ($$render) => { - if (get(addSubmenu)) $$render(consequent_22); + if_block(node_33, ($$render) => { + if (get(addSubmenu)) $$render(consequent_25); else $$render(alternate_3, -1); }); } - append($$anchor4, div_19); + append($$anchor4, div_24); }; - if_block(node_27, ($$render) => { - if (get(addMenuOpen)) $$render(consequent_25); + if_block(node_32, ($$render) => { + if (get(addMenuOpen)) $$render(consequent_28); }); } delegated("click", button_4, (e) => { @@ -1193,63 +1388,63 @@ function SettingsTab($$anchor, $$props) { set(addSubmenu, null); set(editMenuOpen, false); }); - append($$anchor3, div_18); + append($$anchor3, div_23); }; - if_block(node_26, ($$render) => { - if (get(injectable).length) $$render(consequent_26); + if_block(node_31, ($$render) => { + if (get(injectable).length) $$render(consequent_29); }); } - var div_25 = sibling(node_26, 2); - var button_5 = child(div_25); - var node_32 = sibling(button_5, 2); + var div_30 = sibling(node_31, 2); + var button_5 = child(div_30); + var node_37 = sibling(button_5, 2); { - var consequent_29 = ($$anchor3) => { - var div_26 = root_42(); - var div_27 = child(div_26); - var node_33 = sibling(div_27, 2); + var consequent_32 = ($$anchor3) => { + var div_31 = root_46(); + var div_32 = child(div_31); + var node_38 = sibling(div_32, 2); { - var consequent_27 = ($$anchor4) => { - var div_28 = root_43(); - delegated("click", div_28, (e) => { + var consequent_30 = ($$anchor4) => { + var div_33 = root_47(); + delegated("click", div_33, (e) => { e.stopPropagation(); set(editMenuOpen, false); handleApply(); }); - append($$anchor4, div_28); + append($$anchor4, div_33); }; - if_block(node_33, ($$render) => { - if (savedConfig()) $$render(consequent_27); + if_block(node_38, ($$render) => { + if (savedConfig()) $$render(consequent_30); }); } - var node_34 = sibling(node_33, 2); + var node_39 = sibling(node_38, 2); { - var consequent_28 = ($$anchor4) => { - var div_29 = root_44(); - delegated("click", div_29, async (e) => { + var consequent_31 = ($$anchor4) => { + var div_34 = root_48(); + delegated("click", div_34, async (e) => { var _a2; e.stopPropagation(); set(editMenuOpen, false); await fetch(`/promptchain/models/settings/${((_a2 = savedConfig()) == null ? void 0 : _a2._hash) || ""}`, { method: "DELETE" }); $$props.onReopen({}); }); - append($$anchor4, div_29); + append($$anchor4, div_34); }; - if_block(node_34, ($$render) => { - if (userSaved && hasSystem) $$render(consequent_28); + if_block(node_39, ($$render) => { + if (userSaved && hasSystem) $$render(consequent_31); }); } - delegated("click", div_27, (e) => { + delegated("click", div_32, (e) => { e.stopPropagation(); set(editMenuOpen, false); $$props.onReopen({ rangeMode: true }); }); - append($$anchor3, div_26); + append($$anchor3, div_31); }; - if_block(node_32, ($$render) => { - if (get(editMenuOpen)) $$render(consequent_29); + if_block(node_37, ($$render) => { + if (get(editMenuOpen)) $$render(consequent_32); }); } - var button_6 = sibling(div_25, 2); + var button_6 = sibling(div_30, 2); template_effect(() => button_6.disabled = get(saveDisabled)); delegated("click", button_5, (e) => { e.stopPropagation(); @@ -1259,8 +1454,8 @@ function SettingsTab($$anchor, $$props) { delegated("click", button_6, handleSave); append($$anchor2, fragment_18); }; - if_block(node_25, ($$render) => { - if (rangeMode()) $$render(consequent_21); + if_block(node_30, ($$render) => { + if (rangeMode()) $$render(consequent_24); else $$render(alternate_4, -1); }); } @@ -2996,6 +3191,18 @@ function ModelSettingsModal($$anchor, $$props) { get onInjectNode() { return $$props.onInjectNode; }, + get pcNode() { + return $$props.pcNode; + }, + get architecture() { + return get(detectedArch); + }, + get family() { + return get(currentFamily); + }, + get onApplyStyleLora() { + return $$props.onApplyStyleLora; + }, get onClose() { return $$props.onClose; } diff --git a/js/lib/svelte/promptchain-viewer.js b/js/lib/svelte/promptchain-viewer.js index fbf0562..95f58cf 100644 --- a/js/lib/svelte/promptchain-viewer.js +++ b/js/lib/svelte/promptchain-viewer.js @@ -75,11 +75,11 @@ async function saveModalSetup(fetchApi, hash, kind, data, dims = null, planes = return false; } } -var root_1$9 = from_html(``); -var root_3$a = from_html(` /`, 1); -var root_2$a = from_html(` `, 1); -var root_5$9 = from_html(`
`); -var root_4$9 = from_html(`
`); +var root_1$a = from_html(``); +var root_3$b = from_html(` /`, 1); +var root_2$b = from_html(` `, 1); +var root_5$a = from_html(`
`); +var root_4$a = from_html(`
`); var root$2 = from_html(`
`); function SavePathInput($$anchor, $$props) { push($$props, true); @@ -237,7 +237,7 @@ function SavePathInput($$anchor, $$props) { var node = child(div_1); { var consequent = ($$anchor2) => { - var input = root_1$9(); + var input = root_1$a(); bind_this(input, ($$value) => editEl = $$value, () => editEl); template_effect(() => set_value(input, value())); delegated("input", input, (e) => { @@ -280,10 +280,10 @@ function SavePathInput($$anchor, $$props) { append($$anchor2, input); }; var alternate = ($$anchor2) => { - var fragment = root_2$a(); + var fragment = root_2$b(); var node_1 = first_child(fragment); each(node_1, 17, () => get(chips), index, ($$anchor3, chip, i) => { - var fragment_1 = root_3$a(); + var fragment_1 = root_3$b(); var button = first_child(fragment_1); let classes_1; var text_1 = child(button); @@ -329,9 +329,9 @@ function SavePathInput($$anchor, $$props) { var node_2 = sibling(div_1, 2); { var consequent_1 = ($$anchor2) => { - var div_2 = root_4$9(); + var div_2 = root_4$a(); each(div_2, 21, () => get(matches), index, ($$anchor3, m, i) => { - var div_3 = root_5$9(); + var div_3 = root_5$a(); let classes_2; var text_2 = sibling(child(div_3)); template_effect(() => { @@ -358,12 +358,12 @@ function SavePathInput($$anchor, $$props) { pop(); } delegate(["click", "input", "keydown", "mousedown"]); -var root_3$9 = from_html(`
No matching models
`); -var root_6$8 = from_html(`
`); -var root_8$8 = from_html(``); -var root_7$8 = from_html(`
`); -var root_5$8 = from_html(` `, 1); -var root_2$9 = from_html(`
`, 1); +var root_3$a = from_html(`
No matching models
`); +var root_6$9 = from_html(`
`); +var root_8$9 = from_html(``); +var root_7$9 = from_html(`
`); +var root_5$9 = from_html(` `, 1); +var root_2$a = from_html(`
`, 1); var root$1 = from_html(` `, 1); function SearchableSelect($$anchor, $$props) { push($$props, true); @@ -478,7 +478,7 @@ function SearchableSelect($$anchor, $$props) { set(open, false); }, children: ($$anchor3, $$slotProps) => { - var fragment_2 = root_2$9(); + var fragment_2 = root_2$a(); var div = first_child(fragment_2); var input = child(div); bind_this(input, ($$value) => set(searchEl, $$value), () => get(searchEl)); @@ -486,18 +486,18 @@ function SearchableSelect($$anchor, $$props) { var node_1 = child(div_1); { var consequent = ($$anchor4) => { - var div_2 = root_3$9(); + var div_2 = root_3$a(); append($$anchor4, div_2); }; var alternate = ($$anchor4) => { var fragment_3 = comment(); var node_2 = first_child(fragment_3); each(node_2, 17, () => get(filteredGroups), index, ($$anchor5, g) => { - var fragment_4 = root_5$8(); + var fragment_4 = root_5$9(); var node_3 = first_child(fragment_4); { var consequent_1 = ($$anchor6) => { - var div_3 = root_6$8(); + var div_3 = root_6$9(); var text_2 = child(div_3); template_effect(() => set_text(text_2, get(g).label)); append($$anchor6, div_3); @@ -509,14 +509,14 @@ function SearchableSelect($$anchor, $$props) { var node_4 = sibling(node_3, 2); each(node_4, 17, () => get(g).options, index, ($$anchor6, opt) => { const i = user_derived(() => get(flat).indexOf(get(opt))); - var div_4 = root_7$8(); + var div_4 = root_7$9(); let classes; var span_2 = child(div_4); html(span_2, () => highlightText(get(opt).label), true); var node_5 = sibling(span_2, 2); { var consequent_2 = ($$anchor7) => { - var span_3 = root_8$8(); + var span_3 = root_8$9(); span_3.textContent = "✓"; append($$anchor7, span_3); }; @@ -568,59 +568,60 @@ function SearchableSelect($$anchor, $$props) { pop(); } delegate(["click", "keydown"]); -var root_2$8 = from_html(``); -var root_3$8 = from_html(``); -var root_5$7 = from_html(`
After
`, 1); -var root_7$7 = from_html(``); -var root_6$7 = from_html(`
`); -var root_4$8 = from_html(`
`, 1); -var root_9$6 = from_html(`
`); -var root_10$6 = from_html(`
`); -var root_12$6 = from_html(`
`); -var root_11$5 = from_html(`
`, 1); -var root_8$7 = from_html(`
`); -var root_13$6 = from_html(``); -var root_14$6 = from_html(`

No re-detail engine is available for this image — a plain model upscale (ESRGAN) will be used.

`); -var root_16$5 = from_html(` `); -var root_17$4 = from_html(`re-renders the whole frame at ~2MP from your instruction — composition can shift slightly; the climb model below pushes to the target size`); +var root_2$9 = from_html(``); +var root_3$9 = from_html(``); +var root_5$8 = from_html(`
After
`, 1); +var root_7$8 = from_html(``); +var root_6$8 = from_html(`
`); +var root_4$9 = from_html(`
`, 1); +var root_9$7 = from_html(`
`); +var root_10$7 = from_html(`
`); +var root_12$7 = from_html(`
`); +var root_11$6 = from_html(`
`, 1); +var root_8$8 = from_html(`
`); +var root_13$7 = from_html(``); +var root_14$7 = from_html(`

No re-detail engine is available for this image — a plain model upscale (ESRGAN) will be used.

`); +var root_16$6 = from_html(` `); +var root_17$5 = from_html(`re-renders the whole frame at ~2MP from your instruction — composition can shift slightly; the climb model below pushes to the target size`); var root_18$4 = from_html(`deterministic enlargement with the climb model below — add a Restore step for degraded sources (webcam, jpeg, blur)`); -var root_15$5 = from_html(`
Engine
`); -var root_20$3 = from_html(``); -var root_21$3 = from_html(`
Depth from
3D pose
This image
`); -var root_19$4 = from_html(`
Mode
`); -var root_23$3 = from_html(``); -var root_24$2 = from_html(``); -var root_22$2 = from_html(`
Character lock
keeps the character from drifting while tiles re-detail — the image itself is the reference
`); -var root_27$3 = from_html(`
`); -var root_28$2 = from_html(``); -var root_29$3 = from_html(`Workflow prompt (reference) `, 1); -var root_25$3 = from_html(`
`); -var root_31$3 = from_html(`
This quality is bigger than the region's footprint — how should it land?
The whole document scales up and the region lands pixel-for-pixel — full quality kept. The UltraSharp-enlarged image becomes the new Background, so erasing the upscale layer reveals it.
The render is rescaled back into the region — re-detailed, but softer than 1:1.
`); -var root_32$3 = from_html(``); -var root_33$2 = from_html(`
Denoise
`); -var root_30$3 = from_html(`
Scale
`); -var root_34$3 = from_html(`
`); -var root_36$3 = from_html(`
`); -var root_37$3 = from_html(``); -var root_35$3 = from_html(`
`); -var root_39$3 = from_html(``); +var root_19$5 = from_html(``); +var root_15$6 = from_html(`
Engine
`); +var root_21$4 = from_html(``); +var root_22$3 = from_html(`
Depth from
3D pose
This image
`); +var root_20$4 = from_html(`
Mode
`); +var root_24$4 = from_html(``); +var root_25$3 = from_html(``); +var root_23$4 = from_html(`
Character lock
keeps the character from drifting while tiles re-detail — the image itself is the reference
`); +var root_28$3 = from_html(`
`); +var root_29$4 = from_html(``); +var root_30$3 = from_html(`Workflow prompt (reference) `, 1); +var root_26$4 = from_html(`
`); +var root_32$3 = from_html(`
This quality is bigger than the region's footprint — how should it land?
The whole document scales up and the region lands pixel-for-pixel — full quality kept. The UltraSharp-enlarged image becomes the new Background, so erasing the upscale layer reveals it.
The render is rescaled back into the region — re-detailed, but softer than 1:1.
`); +var root_33$3 = from_html(``); +var root_34$2 = from_html(`
Denoise
`); +var root_31$3 = from_html(`
Scale
`); +var root_35$3 = from_html(`
`); +var root_37$3 = from_html(`
`); +var root_38$3 = from_html(``); +var root_36$3 = from_html(`
`); var root_40$3 = from_html(``); -var root_38$3 = from_html(`
`); -var root_42$3 = from_html(` `); -var root_44$3 = from_html(``); -var root_47$3 = from_html(``); -var root_46$3 = from_html(`
`); -var root_48$2 = from_html(`
`); -var root_43$3 = from_html(`
`); -var root_41$3 = from_html(`
▶ Advanced
`, 1); -var root_49$2 = from_html(`
Save to
`); -var root_51$2 = from_html(``); -var root_53$2 = from_html(``); -var root_54$2 = from_html(``); -var root_52$2 = from_html(` `, 1); -var root_56$2 = from_html(``); -var root_55$2 = from_html(` `, 1); -var root_1$8 = from_html(`
`); +var root_41$3 = from_html(``); +var root_39$3 = from_html(`
`); +var root_43$3 = from_html(` `); +var root_45$3 = from_html(``); +var root_48$3 = from_html(``); +var root_47$3 = from_html(`
`); +var root_49$2 = from_html(`
`); +var root_44$3 = from_html(`
`); +var root_42$3 = from_html(`
▶ Advanced
`, 1); +var root_50$2 = from_html(`
Save to
`); +var root_52$2 = from_html(``); +var root_54$2 = from_html(``); +var root_55$2 = from_html(``); +var root_53$2 = from_html(` `, 1); +var root_57$2 = from_html(``); +var root_56$2 = from_html(` `, 1); +var root_1$9 = from_html(`
`); function UpscaleOptionsModal($$anchor, $$props) { push($$props, true); let caps = prop($$props, "caps", 3, null), fetchApi = prop($$props, "fetchApi", 3, null), filename = prop($$props, "filename", 3, ""), width = prop($$props, "width", 3, 0), height = prop($$props, "height", 3, 0), previewUrl = prop($$props, "previewUrl", 3, ""), progress = prop($$props, "progress", 3, null), onCancelRun = prop($$props, "onCancelRun", 3, null), onViewResult = prop($$props, "onViewResult", 3, null), onUseInEdit = prop( @@ -721,6 +722,11 @@ function UpscaleOptionsModal($$anchor, $$props) { return ((_b = (_a = caps()) == null ? void 0 : _a.engineModels) == null ? void 0 : _b.find((m) => m.hash === get(engineSel))) || null; }); let engineKind = user_derived(() => get(engineEntry) ? get(engineEntry).architecture === "qwen_edit" ? "qwen" : get(engineEntry).architecture === "zimage" ? "zimage" : get(engineEntry).architecture === "krea2" ? "krea2" : get(engineEntry).architecture === "flux" ? "flux1" : "sdxl" : get(engineSel) === "source" ? "source" : "plain"); + let realism = state(false); + let realismAvailable = user_derived(() => { + var _a; + return get(engineKind) === "krea2" && /turbo/i.test(((_a = get(engineEntry)) == null ? void 0 : _a.filename) || ""); + }); let sourceGraftable = user_derived(() => { var _a; return get(engineKind) === "source" && !!((_a = caps()) == null ? void 0 : _a.graftable); @@ -1163,6 +1169,7 @@ function UpscaleOptionsModal($$anchor, $$props) { if (get(tileEngine) && get(climbStage) !== "ultrasharp") base.climbStage = get(climbStage); if (get(engineKind) !== "source" && get(climbModel)) base.climbModel = get(climbModel); if (get(engineKind) === "source" && ((_c = caps()) == null ? void 0 : _c.architecture) === "flux2") base.preserveDefocus = get(preserveDefocus); + if (get(realismAvailable)) base.realism = get(realism); if (get(tileEngine) || get(engineKind) === "qwen") { base.prompt = get(prompt); } else if (get(prompt).trim() && get(prompt).trim() !== basePrefillFor(get(engineKind)).trim()) { @@ -1394,8 +1401,8 @@ function UpscaleOptionsModal($$anchor, $$props) { var fragment = comment(); var node_1 = first_child(fragment); { - var consequent_43 = ($$anchor2) => { - var div = root_1$8(); + var consequent_44 = ($$anchor2) => { + var div = root_1$9(); let styles; var div_1 = child(div); var div_2 = child(div_1); @@ -1404,7 +1411,7 @@ function UpscaleOptionsModal($$anchor, $$props) { var node_2 = sibling(span, 2); { var consequent = ($$anchor3) => { - var button = root_2$8(); + var button = root_2$9(); delegated("click", button, function(...$$args) { var _a; (_a = $$props.onCancel) == null ? void 0 : _a.apply(this, $$args); @@ -1422,18 +1429,18 @@ function UpscaleOptionsModal($$anchor, $$props) { var node_3 = child(div_5); { var consequent_1 = ($$anchor3) => { - var img = root_3$8(); + var img = root_3$9(); template_effect(() => set_attribute(img, "src", get(liveTile))); append($$anchor3, img); }; var consequent_5 = ($$anchor3) => { - var fragment_1 = root_4$8(); + var fragment_1 = root_4$9(); var div_6 = first_child(fragment_1); var img_1 = child(div_6); var node_4 = sibling(div_6, 2); { var consequent_2 = ($$anchor4) => { - var fragment_2 = root_5$7(); + var fragment_2 = root_5$8(); var div_7 = first_child(fragment_2); var div_8 = child(div_7); var img_2 = child(div_8); @@ -1463,13 +1470,13 @@ function UpscaleOptionsModal($$anchor, $$props) { var node_5 = sibling(node_4, 2); { var consequent_4 = ($$anchor4) => { - var div_11 = root_6$7(); + var div_11 = root_6$8(); var span_1 = child(div_11); var text_3 = child(span_1); var node_6 = sibling(span_1, 2); { var consequent_3 = ($$anchor5) => { - var button_1 = root_7$7(); + var button_1 = root_7$8(); let classes_1; template_effect(() => classes_1 = set_class(button_1, 1, "pcr-up-fit-btn svelte-xw7bpl", null, classes_1, { on: get(compareSplit) })); delegated("click", button_1, toggleCompare); @@ -1504,23 +1511,23 @@ function UpscaleOptionsModal($$anchor, $$props) { var node_7 = sibling(div_5, 2); { var consequent_9 = ($$anchor3) => { - var div_12 = root_8$7(); + var div_12 = root_8$8(); var node_8 = child(div_12); { var consequent_6 = ($$anchor4) => { - var div_13 = root_9$6(); + var div_13 = root_9$7(); var text_4 = child(div_13); template_effect(() => set_text(text_4, `Done${progress().filename ? ` — ${progress().filename}` : ""}`)); append($$anchor4, div_13); }; var consequent_7 = ($$anchor4) => { - var div_14 = root_10$6(); + var div_14 = root_10$7(); var text_5 = child(div_14); template_effect(() => set_text(text_5, progress().message || "Failed")); append($$anchor4, div_14); }; var alternate = ($$anchor4) => { - var fragment_3 = root_11$5(); + var fragment_3 = root_11$6(); var div_15 = first_child(fragment_3); var text_6 = child(div_15); var div_16 = sibling(div_15, 2); @@ -1529,7 +1536,7 @@ function UpscaleOptionsModal($$anchor, $$props) { var node_9 = sibling(div_16, 2); { var consequent_8 = ($$anchor5) => { - var div_18 = root_12$6(); + var div_18 = root_12$7(); var text_7 = child(div_18); template_effect(() => set_text(text_7, get(progressLabel))); append($$anchor5, div_18); @@ -1562,7 +1569,7 @@ function UpscaleOptionsModal($$anchor, $$props) { var node_10 = child(div_19); { var consequent_10 = ($$anchor3) => { - var button_3 = root_13$6(); + var button_3 = root_13$7(); delegated("click", button_3, applySavedSetup); append($$anchor3, button_3); }; @@ -1573,7 +1580,7 @@ function UpscaleOptionsModal($$anchor, $$props) { var node_11 = sibling(node_10, 2); { var consequent_11 = ($$anchor3) => { - var p = root_14$6(); + var p = root_14$7(); append($$anchor3, p); }; if_block(node_11, ($$render) => { @@ -1583,8 +1590,8 @@ function UpscaleOptionsModal($$anchor, $$props) { } var node_12 = sibling(node_11, 2); { - var consequent_15 = ($$anchor3) => { - var div_20 = root_15$5(); + var consequent_16 = ($$anchor3) => { + var div_20 = root_15$6(); var div_21 = sibling(child(div_20), 2); var node_13 = child(div_21); SearchableSelect(node_13, { @@ -1604,13 +1611,13 @@ function UpscaleOptionsModal($$anchor, $$props) { var node_14 = sibling(node_13, 2); { var consequent_12 = ($$anchor4) => { - var span_2 = root_16$5(); + var span_2 = root_16$6(); var text_8 = child(span_2); template_effect(() => set_text(text_8, get(engineKind) === "flux1" ? "the climb model below pushes to the target, then this FLUX.1 model re-details every tile with the prompt below — low denoise holds structure without a ControlNet" : get(engineKind) === "zimage" ? "the climb model below pushes to the target, then this Z-Image model re-details every tile with the prompt below — Base holds structure at low denoise; Turbo (distilled) needs ~0.7 or it blotches" : get(engineKind) === "krea2" ? "the climb model below pushes to the target, then this Krea 2 model re-details every tile with the prompt below — distilled turbo re-detail at moderate denoise, no ControlNet" : "the climb model below pushes to the target, then this checkpoint re-details every tile (structure locked by a tile ControlNet) with the prompt below")); append($$anchor4, span_2); }; var consequent_13 = ($$anchor4) => { - var span_3 = root_17$4(); + var span_3 = root_17$5(); append($$anchor4, span_3); }; var consequent_14 = ($$anchor4) => { @@ -1623,19 +1630,31 @@ function UpscaleOptionsModal($$anchor, $$props) { else if (get(engineKind) === "plain") $$render(consequent_14, 2); }); } + var node_15 = sibling(node_14, 2); + { + var consequent_15 = ($$anchor4) => { + var label = root_19$5(); + var input = child(label); + bind_checked(input, () => get(realism), ($$value) => set(realism, $$value)); + append($$anchor4, label); + }; + if_block(node_15, ($$render) => { + if (get(realismAvailable)) $$render(consequent_15); + }); + } append($$anchor3, div_20); }; if_block(node_12, ($$render) => { - if (get(engineChoices) > 1) $$render(consequent_15); + if (get(engineChoices) > 1) $$render(consequent_16); }); } - var node_15 = sibling(node_12, 2); + var node_16 = sibling(node_12, 2); { - var consequent_17 = ($$anchor3) => { - var div_22 = root_19$4(); + var consequent_18 = ($$anchor3) => { + var div_22 = root_20$4(); var select = sibling(child(div_22), 2); each(select, 21, () => MODE_ROWS, index, ($$anchor4, row) => { - var option = root_20$3(); + var option = root_21$4(); var text_9 = child(option); var option_value = {}; template_effect(() => { @@ -1653,10 +1672,10 @@ function UpscaleOptionsModal($$anchor, $$props) { init_select(select); var div_23 = sibling(select, 2); var text_10 = child(div_23); - var node_16 = sibling(div_23, 2); + var node_17 = sibling(div_23, 2); { - var consequent_16 = ($$anchor4) => { - var div_24 = root_21$3(); + var consequent_17 = ($$anchor4) => { + var div_24 = root_22$3(); var div_25 = sibling(child(div_24), 2); var div_26 = child(div_25); let classes_4; @@ -1681,8 +1700,8 @@ function UpscaleOptionsModal($$anchor, $$props) { }); append($$anchor4, div_24); }; - if_block(node_16, ($$render) => { - if (get(mode) === "depth") $$render(consequent_16); + if_block(node_17, ($$render) => { + if (get(mode) === "depth") $$render(consequent_17); }); } template_effect(() => { @@ -1695,23 +1714,23 @@ function UpscaleOptionsModal($$anchor, $$props) { delegated("change", select, (e) => pickModeKey(e.currentTarget.value)); append($$anchor3, div_22); }; - if_block(node_15, ($$render) => { + if_block(node_16, ($$render) => { var _a; - if (((_a = caps()) == null ? void 0 : _a.graftable) && get(engineKind) === "source") $$render(consequent_17); + if (((_a = caps()) == null ? void 0 : _a.graftable) && get(engineKind) === "source") $$render(consequent_18); }); } - var node_17 = sibling(node_15, 2); + var node_18 = sibling(node_16, 2); { - var consequent_20 = ($$anchor3) => { - var div_28 = root_22$2(); + var consequent_21 = ($$anchor3) => { + var div_28 = root_23$4(); var div_29 = sibling(child(div_28), 2); var select_1 = child(div_29); var option_1 = child(select_1); option_1.value = option_1.__value = "none"; - var node_18 = sibling(option_1); + var node_19 = sibling(option_1); { - var consequent_18 = ($$anchor4) => { - var option_2 = root_23$3(); + var consequent_19 = ($$anchor4) => { + var option_2 = root_24$4(); var text_11 = child(option_2); option_2.value = option_2.__value = "ipadapter"; template_effect(() => { @@ -1720,15 +1739,15 @@ function UpscaleOptionsModal($$anchor, $$props) { }); append($$anchor4, option_2); }; - if_block(node_18, ($$render) => { + if_block(node_19, ($$render) => { var _a, _b; - if (((_a = caps().conditions.ipadapter) == null ? void 0 : _a.ok) || ((_b = caps().conditions.ipadapter) == null ? void 0 : _b.installable)) $$render(consequent_18); + if (((_a = caps().conditions.ipadapter) == null ? void 0 : _a.ok) || ((_b = caps().conditions.ipadapter) == null ? void 0 : _b.installable)) $$render(consequent_19); }); } - var node_19 = sibling(node_18); + var node_20 = sibling(node_19); { - var consequent_19 = ($$anchor4) => { - var option_3 = root_24$2(); + var consequent_20 = ($$anchor4) => { + var option_3 = root_25$3(); var text_12 = child(option_3); option_3.value = option_3.__value = "pulid"; template_effect(() => { @@ -1737,61 +1756,61 @@ function UpscaleOptionsModal($$anchor, $$props) { }); append($$anchor4, option_3); }; - if_block(node_19, ($$render) => { + if_block(node_20, ($$render) => { var _a, _b; - if (((_a = caps().conditions.pulid) == null ? void 0 : _a.ok) || ((_b = caps().conditions.pulid) == null ? void 0 : _b.installable)) $$render(consequent_19); + if (((_a = caps().conditions.pulid) == null ? void 0 : _a.ok) || ((_b = caps().conditions.pulid) == null ? void 0 : _b.installable)) $$render(consequent_20); }); } bind_select_value(select_1, () => get(condition), ($$value) => set(condition, $$value)); append($$anchor3, div_28); }; - if_block(node_17, ($$render) => { + if_block(node_18, ($$render) => { var _a, _b, _c, _d, _e, _f, _g, _h; - if (get(sourceGraftable) && (((_b = (_a = caps().conditions) == null ? void 0 : _a.ipadapter) == null ? void 0 : _b.ok) || ((_d = (_c = caps().conditions) == null ? void 0 : _c.ipadapter) == null ? void 0 : _d.installable) || ((_f = (_e = caps().conditions) == null ? void 0 : _e.pulid) == null ? void 0 : _f.ok) || ((_h = (_g = caps().conditions) == null ? void 0 : _g.pulid) == null ? void 0 : _h.installable))) $$render(consequent_20); + if (get(sourceGraftable) && (((_b = (_a = caps().conditions) == null ? void 0 : _a.ipadapter) == null ? void 0 : _b.ok) || ((_d = (_c = caps().conditions) == null ? void 0 : _c.ipadapter) == null ? void 0 : _d.installable) || ((_f = (_e = caps().conditions) == null ? void 0 : _e.pulid) == null ? void 0 : _f.ok) || ((_h = (_g = caps().conditions) == null ? void 0 : _g.pulid) == null ? void 0 : _h.installable))) $$render(consequent_21); }); } - var node_20 = sibling(node_17, 2); + var node_21 = sibling(node_18, 2); { - var consequent_23 = ($$anchor3) => { - var div_30 = root_25$3(); + var consequent_24 = ($$anchor3) => { + var div_30 = root_26$4(); var div_31 = child(div_30); var text_13 = child(div_31); var div_32 = sibling(div_31, 2); var span_5 = child(div_32); var text_14 = child(span_5); - var node_21 = sibling(span_5, 2); - key(node_21, () => get(promptSeedKey) + ":" + get(promptRestoreNonce), ($$anchor4) => { + var node_22 = sibling(span_5, 2); + key(node_22, () => get(promptSeedKey) + ":" + get(promptRestoreNonce), ($$anchor4) => { var fragment_4 = comment(); - var node_22 = first_child(fragment_4); + var node_23 = first_child(fragment_4); { - var consequent_21 = ($$anchor5) => { - var div_33 = root_27$3(); + var consequent_22 = ($$anchor5) => { + var div_33 = root_28$3(); action(div_33, ($$node) => promptEditor == null ? void 0 : promptEditor($$node)); append($$anchor5, div_33); }; var alternate_1 = ($$anchor5) => { - var textarea = root_28$2(); + var textarea = root_29$4(); bind_value(textarea, () => get(prompt), ($$value) => set(prompt, $$value)); append($$anchor5, textarea); }; - if_block(node_22, ($$render) => { - if (mountPromptEditor()) $$render(consequent_21); + if_block(node_23, ($$render) => { + if (mountPromptEditor()) $$render(consequent_22); else $$render(alternate_1, -1); }); } append($$anchor4, fragment_4); }); - var node_23 = sibling(node_21, 2); + var node_24 = sibling(node_22, 2); { - var consequent_22 = ($$anchor4) => { - var fragment_5 = root_29$3(); + var consequent_23 = ($$anchor4) => { + var fragment_5 = root_30$3(); var textarea_1 = sibling(first_child(fragment_5), 2); bind_this(textarea_1, ($$value) => refEl = $$value, () => refEl); template_effect(() => set_value(textarea_1, caps().referencePrompt)); append($$anchor4, fragment_5); }; - if_block(node_23, ($$render) => { - if (caps().referencePrompt && get(engineKind) !== "qwen") $$render(consequent_22); + if_block(node_24, ($$render) => { + if (caps().referencePrompt && get(engineKind) !== "qwen") $$render(consequent_23); }); } template_effect(() => { @@ -1800,24 +1819,24 @@ function UpscaleOptionsModal($$anchor, $$props) { }); append($$anchor3, div_30); }; - if_block(node_20, ($$render) => { - if (get(sourceGraftable) && caps().prefillPrompt != null || get(tileEngine) || get(engineKind) === "qwen") $$render(consequent_23); + if_block(node_21, ($$render) => { + if (get(sourceGraftable) && caps().prefillPrompt != null || get(tileEngine) || get(engineKind) === "qwen") $$render(consequent_24); }); } - var div_34 = sibling(node_20, 2); - var node_24 = sibling(child(div_34), 2); + var div_34 = sibling(node_21, 2); + var node_25 = sibling(child(div_34), 2); { - var consequent_27 = ($$anchor3) => { - var div_35 = root_30$3(); + var consequent_28 = ($$anchor3) => { + var div_35 = root_31$3(); var div_36 = child(div_35); let classes_6; - var node_25 = sibling(child(div_36), 2); + var node_26 = sibling(child(div_36), 2); { let $0 = user_derived(() => { var _a; return (_a = caps()) == null ? void 0 : _a.defaultUpscaleBy; }); - SettingsSlider(node_25, { + SettingsSlider(node_26, { get min() { return get(scaleSlider).min; }, @@ -1841,10 +1860,10 @@ function UpscaleOptionsModal($$anchor, $$props) { } var div_37 = sibling(div_36, 2); var text_15 = child(div_37); - var node_26 = sibling(div_37, 2); + var node_27 = sibling(div_37, 2); { - var consequent_24 = ($$anchor4) => { - var div_38 = root_31$3(); + var consequent_25 = ($$anchor4) => { + var div_38 = root_32$3(); var div_39 = sibling(child(div_38), 2); let classes_7; var div_40 = sibling(child(div_39), 2); @@ -1869,29 +1888,29 @@ function UpscaleOptionsModal($$anchor, $$props) { }); append($$anchor4, div_38); }; - if_block(node_26, ($$render) => { - if (get(needsGrow)) $$render(consequent_24); - }); - } - var node_27 = sibling(node_26, 2); - { - var consequent_25 = ($$anchor4) => { - var label = root_32$3(); - var input = child(label); - template_effect(() => set_checked(input, get(ultrasharpUnder))); - delegated("change", input, (e) => setUltrasharpUnder(e.currentTarget.checked)); - append($$anchor4, label); - }; if_block(node_27, ($$render) => { - if (get(landsInFootprint)) $$render(consequent_25); + if (get(needsGrow)) $$render(consequent_25); }); } var node_28 = sibling(node_27, 2); { var consequent_26 = ($$anchor4) => { - var div_45 = root_33$2(); - var node_29 = sibling(child(div_45), 2); - SettingsSlider(node_29, { + var label_1 = root_33$3(); + var input_1 = child(label_1); + template_effect(() => set_checked(input_1, get(ultrasharpUnder))); + delegated("change", input_1, (e) => setUltrasharpUnder(e.currentTarget.checked)); + append($$anchor4, label_1); + }; + if_block(node_28, ($$render) => { + if (get(landsInFootprint)) $$render(consequent_26); + }); + } + var node_29 = sibling(node_28, 2); + { + var consequent_27 = ($$anchor4) => { + var div_45 = root_34$2(); + var node_30 = sibling(child(div_45), 2); + SettingsSlider(node_30, { min: 0.05, get max() { return get(denoiseMax); @@ -1909,8 +1928,8 @@ function UpscaleOptionsModal($$anchor, $$props) { }); append($$anchor4, div_45); }; - if_block(node_28, ($$render) => { - if (get(sourceGraftable) || get(tileEngine)) $$render(consequent_26); + if_block(node_29, ($$render) => { + if (get(sourceGraftable) || get(tileEngine)) $$render(consequent_27); }); } template_effect(() => { @@ -1919,14 +1938,14 @@ function UpscaleOptionsModal($$anchor, $$props) { }); append($$anchor3, div_35); }; - if_block(node_24, ($$render) => { - if (get(sourceGraftable) || get(tileEngine) || get(engineKind) === "qwen" || get(engineKind) === "plain") $$render(consequent_27); + if_block(node_25, ($$render) => { + if (get(sourceGraftable) || get(tileEngine) || get(engineKind) === "qwen" || get(engineKind) === "plain") $$render(consequent_28); }); } - var node_30 = sibling(node_24, 2); + var node_31 = sibling(node_25, 2); { - var consequent_28 = ($$anchor3) => { - var div_46 = root_34$3(); + var consequent_29 = ($$anchor3) => { + var div_46 = root_35$3(); var div_47 = child(div_46); var select_2 = sibling(child(div_47), 2); let classes_9; @@ -1955,18 +1974,18 @@ function UpscaleOptionsModal($$anchor, $$props) { bind_select_value(select_2, () => get(plainEngine), ($$value) => set(plainEngine, $$value)); append($$anchor3, div_46); }; - if_block(node_30, ($$render) => { - if (onUseInEdit() && get(engineKind) !== "plain") $$render(consequent_28); + if_block(node_31, ($$render) => { + if (onUseInEdit() && get(engineKind) !== "plain") $$render(consequent_29); }); } - var node_31 = sibling(node_30, 2); + var node_32 = sibling(node_31, 2); { - var consequent_30 = ($$anchor3) => { - var div_48 = root_35$3(); - var node_32 = child(div_48); + var consequent_31 = ($$anchor3) => { + var div_48 = root_36$3(); + var node_33 = child(div_48); { - var consequent_29 = ($$anchor4) => { - var div_49 = root_36$3(); + var consequent_30 = ($$anchor4) => { + var div_49 = root_37$3(); var select_3 = sibling(child(div_49), 2); let classes_10; var option_6 = child(select_3); @@ -1994,15 +2013,15 @@ function UpscaleOptionsModal($$anchor, $$props) { bind_select_value(select_3, () => get(climbStage), ($$value) => set(climbStage, $$value)); append($$anchor4, div_49); }; - if_block(node_32, ($$render) => { - if (get(engineKind) !== "qwen") $$render(consequent_29); + if_block(node_33, ($$render) => { + if (get(engineKind) !== "qwen") $$render(consequent_30); }); } - var div_50 = sibling(node_32, 2); + var div_50 = sibling(node_33, 2); var select_4 = sibling(child(div_50), 2); let classes_11; each(select_4, 21, () => caps().upscaleModelOptions, index, ($$anchor4, opt) => { - var option_8 = root_37$3(); + var option_8 = root_38$3(); let styles_5; var text_22 = child(option_8); var option_8_value = {}; @@ -2023,20 +2042,20 @@ function UpscaleOptionsModal($$anchor, $$props) { bind_select_value(select_4, () => get(climbModel), ($$value) => set(climbModel, $$value)); append($$anchor3, div_48); }; - if_block(node_31, ($$render) => { + if_block(node_32, ($$render) => { var _a, _b; - if (get(engineKind) !== "source" && ((_b = (_a = caps()) == null ? void 0 : _a.upscaleModelOptions) == null ? void 0 : _b.length)) $$render(consequent_30); + if (get(engineKind) !== "source" && ((_b = (_a = caps()) == null ? void 0 : _a.upscaleModelOptions) == null ? void 0 : _b.length)) $$render(consequent_31); }); } - var node_33 = sibling(node_31, 2); + var node_34 = sibling(node_32, 2); { - var consequent_31 = ($$anchor3) => { - var div_51 = root_38$3(); + var consequent_32 = ($$anchor3) => { + var div_51 = root_39$3(); var div_52 = child(div_51); var select_5 = sibling(child(div_52), 2); let classes_12; each(select_5, 21, () => caps().samplerOptions, index, ($$anchor4, opt) => { - var option_9 = root_39$3(); + var option_9 = root_40$3(); let styles_6; var text_23 = child(option_9); var option_9_value = {}; @@ -2063,7 +2082,7 @@ function UpscaleOptionsModal($$anchor, $$props) { var select_6 = sibling(child(div_53), 2); let classes_13; each(select_6, 21, () => caps().schedulerOptions, index, ($$anchor4, opt) => { - var option_10 = root_40$3(); + var option_10 = root_41$3(); let styles_7; var text_24 = child(option_10); var option_10_value = {}; @@ -2094,60 +2113,60 @@ function UpscaleOptionsModal($$anchor, $$props) { bind_select_value(select_6, () => get(scheduler), ($$value) => set(scheduler, $$value)); append($$anchor3, div_51); }; - if_block(node_33, ($$render) => { + if_block(node_34, ($$render) => { var _a; - if ((get(sourceGraftable) || get(tileEngine) || get(engineKind) === "qwen") && ((_a = caps().samplerOptions) == null ? void 0 : _a.length)) $$render(consequent_31); + if ((get(sourceGraftable) || get(tileEngine) || get(engineKind) === "qwen") && ((_a = caps().samplerOptions) == null ? void 0 : _a.length)) $$render(consequent_32); }); } - var node_34 = sibling(node_33, 2); + var node_35 = sibling(node_34, 2); { - var consequent_36 = ($$anchor3) => { - var fragment_6 = root_41$3(); + var consequent_37 = ($$anchor3) => { + var fragment_6 = root_42$3(); var div_54 = first_child(fragment_6); var span_6 = child(div_54); let classes_14; - var node_35 = sibling(span_6, 2); + var node_36 = sibling(span_6, 2); { - var consequent_32 = ($$anchor4) => { - var span_7 = root_42$3(); + var consequent_33 = ($$anchor4) => { + var span_7 = root_43$3(); var text_25 = child(span_7); template_effect(($0) => set_text(text_25, `${$0 ?? ""} changed`), [() => Object.keys(get(advanced)).length]); append($$anchor4, span_7); }; var d_1 = user_derived(() => Object.keys(get(advanced)).length); - if_block(node_35, ($$render) => { - if (get(d_1)) $$render(consequent_32); + if_block(node_36, ($$render) => { + if (get(d_1)) $$render(consequent_33); }); } - var node_36 = sibling(div_54, 2); + var node_37 = sibling(div_54, 2); { - var consequent_35 = ($$anchor4) => { - var div_55 = root_43$3(); - var node_37 = child(div_55); + var consequent_36 = ($$anchor4) => { + var div_55 = root_44$3(); + var node_38 = child(div_55); { - var consequent_33 = ($$anchor5) => { - var label_1 = root_44$3(); - var input_1 = child(label_1); - bind_checked(input_1, () => get(preserveDefocus), ($$value) => set(preserveDefocus, $$value)); - append($$anchor5, label_1); + var consequent_34 = ($$anchor5) => { + var label_2 = root_45$3(); + var input_2 = child(label_2); + bind_checked(input_2, () => get(preserveDefocus), ($$value) => set(preserveDefocus, $$value)); + append($$anchor5, label_2); }; - if_block(node_37, ($$render) => { + if_block(node_38, ($$render) => { var _a; - if (get(engineKind) === "source" && ((_a = caps()) == null ? void 0 : _a.architecture) === "flux2") $$render(consequent_33); + if (get(engineKind) === "source" && ((_a = caps()) == null ? void 0 : _a.architecture) === "flux2") $$render(consequent_34); }); } - var node_38 = sibling(node_37, 2); - each(node_38, 17, () => get(engineKind) === "qwen" ? [] : ADV_COMBOS, index, ($$anchor5, combo) => { + var node_39 = sibling(node_38, 2); + each(node_39, 17, () => get(engineKind) === "qwen" ? [] : ADV_COMBOS, index, ($$anchor5, combo) => { var fragment_7 = comment(); - var node_39 = first_child(fragment_7); + var node_40 = first_child(fragment_7); { - var consequent_34 = ($$anchor6) => { - var div_56 = root_46$3(); + var consequent_35 = ($$anchor6) => { + var div_56 = root_47$3(); var span_8 = child(div_56); var text_26 = child(span_8); var select_7 = sibling(span_8, 2); each(select_7, 21, () => caps()[get(combo).optionsKey], index, ($$anchor7, opt) => { - var option_11 = root_47$3(); + var option_11 = root_48$3(); let styles_8; var text_27 = child(option_11); var option_11_value = {}; @@ -2176,27 +2195,27 @@ function UpscaleOptionsModal($$anchor, $$props) { delegated("change", select_7, (e) => setAdv(get(combo).name, e.target.value)); append($$anchor6, div_56); }; - if_block(node_39, ($$render) => { + if_block(node_40, ($$render) => { var _a; - if ((_a = caps()[get(combo).optionsKey]) == null ? void 0 : _a.length) $$render(consequent_34); + if ((_a = caps()[get(combo).optionsKey]) == null ? void 0 : _a.length) $$render(consequent_35); }); } append($$anchor5, fragment_7); }); - var node_40 = sibling(node_38, 2); + var node_41 = sibling(node_39, 2); each( - node_40, + node_41, 17, () => get(engineKind) === "qwen" ? ADV_SLIDERS.filter((r) => r.name === "steps" || r.name === "cfg") : ADV_SLIDERS, index, ($$anchor5, row) => { - var div_57 = root_48$2(); + var div_57 = root_49$2(); var span_9 = child(div_57); var text_28 = child(span_9); - var node_41 = sibling(span_9, 2); + var node_42 = sibling(span_9, 2); { let $0 = user_derived(() => Number(advValue(get(row).name) ?? get(row).min)); - SettingsSlider(node_41, { + SettingsSlider(node_42, { get min() { return get(row).min; }, @@ -2221,8 +2240,8 @@ function UpscaleOptionsModal($$anchor, $$props) { ); append($$anchor4, div_55); }; - if_block(node_36, ($$render) => { - if (get(advancedOpen)) $$render(consequent_35); + if_block(node_37, ($$render) => { + if (get(advancedOpen)) $$render(consequent_36); }); } template_effect(() => classes_14 = set_class(span_6, 1, "pcr-up-adv-arrow svelte-xw7bpl", null, classes_14, { open: get(advancedOpen) })); @@ -2231,16 +2250,16 @@ function UpscaleOptionsModal($$anchor, $$props) { }); append($$anchor3, fragment_6); }; - if_block(node_34, ($$render) => { - if (get(sourceGraftable) && caps().advancedDefaults || get(tileEngine) || get(engineKind) === "qwen") $$render(consequent_36); + if_block(node_35, ($$render) => { + if (get(sourceGraftable) && caps().advancedDefaults || get(tileEngine) || get(engineKind) === "qwen") $$render(consequent_37); }); } - var node_42 = sibling(node_34, 2); + var node_43 = sibling(node_35, 2); { - var consequent_37 = ($$anchor3) => { - var div_58 = root_49$2(); - var node_43 = sibling(child(div_58), 2); - SavePathInput(node_43, { + var consequent_38 = ($$anchor3) => { + var div_58 = root_50$2(); + var node_44 = sibling(child(div_58), 2); + SavePathInput(node_44, { get value() { return get(savePrefix); }, @@ -2253,19 +2272,19 @@ function UpscaleOptionsModal($$anchor, $$props) { }); append($$anchor3, div_58); }; - if_block(node_42, ($$render) => { - if (!onUseInEdit()) $$render(consequent_37); + if_block(node_43, ($$render) => { + if (!onUseInEdit()) $$render(consequent_38); }); } var div_59 = sibling(div_3, 2); - var node_44 = child(div_59); + var node_45 = child(div_59); { - var consequent_41 = ($$anchor3) => { + var consequent_42 = ($$anchor3) => { var fragment_8 = comment(); - var node_45 = first_child(fragment_8); + var node_46 = first_child(fragment_8); { - var consequent_38 = ($$anchor4) => { - var button_4 = root_51$2(); + var consequent_39 = ($$anchor4) => { + var button_4 = root_52$2(); delegated("click", button_4, function(...$$args) { var _a; (_a = onCancelRun()) == null ? void 0 : _a.apply(this, $$args); @@ -2273,26 +2292,26 @@ function UpscaleOptionsModal($$anchor, $$props) { append($$anchor4, button_4); }; var alternate_2 = ($$anchor4) => { - var fragment_9 = root_52$2(); + var fragment_9 = root_53$2(); var button_5 = first_child(fragment_9); var button_6 = sibling(button_5, 2); var text_29 = child(button_6); - var node_46 = sibling(button_6, 2); + var node_47 = sibling(button_6, 2); { - var consequent_39 = ($$anchor5) => { - var button_7 = root_53$2(); + var consequent_40 = ($$anchor5) => { + var button_7 = root_54$2(); template_effect(() => button_7.disabled = !progress().filename); delegated("click", button_7, () => onUseInEdit()(progress())); append($$anchor5, button_7); }; - var consequent_40 = ($$anchor5) => { - var button_8 = root_54$2(); + var consequent_41 = ($$anchor5) => { + var button_8 = root_55$2(); delegated("click", button_8, () => onViewResult()(progress().resultHash)); append($$anchor5, button_8); }; - if_block(node_46, ($$render) => { - if (progress().phase === "done" && onUseInEdit()) $$render(consequent_39); - else if (progress().phase === "done" && progress().resultHash && onViewResult()) $$render(consequent_40, 1); + if_block(node_47, ($$render) => { + if (progress().phase === "done" && onUseInEdit()) $$render(consequent_40); + else if (progress().phase === "done" && progress().resultHash && onViewResult()) $$render(consequent_41, 1); }); } template_effect(() => set_text(text_29, progress().phase === "done" ? "Run Again" : "Apply")); @@ -2303,28 +2322,28 @@ function UpscaleOptionsModal($$anchor, $$props) { delegated("click", button_6, () => confirm("background")); append($$anchor4, fragment_9); }; - if_block(node_45, ($$render) => { - if (get(runActive)) $$render(consequent_38); + if_block(node_46, ($$render) => { + if (get(runActive)) $$render(consequent_39); else $$render(alternate_2, -1); }); } append($$anchor3, fragment_8); }; var alternate_3 = ($$anchor3) => { - var fragment_10 = root_55$2(); + var fragment_10 = root_56$2(); var button_9 = first_child(fragment_10); - var node_47 = sibling(button_9, 2); + var node_48 = sibling(button_9, 2); { - var consequent_42 = ($$anchor4) => { - var button_10 = root_56$2(); + var consequent_43 = ($$anchor4) => { + var button_10 = root_57$2(); delegated("click", button_10, () => confirm("workflow")); append($$anchor4, button_10); }; - if_block(node_47, ($$render) => { - if (!onUseInEdit()) $$render(consequent_42); + if_block(node_48, ($$render) => { + if (!onUseInEdit()) $$render(consequent_43); }); } - var button_11 = sibling(node_47, 2); + var button_11 = sibling(node_48, 2); bind_this(button_11, ($$value) => confirmBtn = $$value, () => confirmBtn); delegated("click", button_9, function(...$$args) { var _a; @@ -2333,8 +2352,8 @@ function UpscaleOptionsModal($$anchor, $$props) { delegated("click", button_11, () => confirm("background")); append($$anchor3, fragment_10); }; - if_block(node_44, ($$render) => { - if (progress()) $$render(consequent_41); + if_block(node_45, ($$render) => { + if (progress()) $$render(consequent_42); else $$render(alternate_3, -1); }); } @@ -2359,7 +2378,7 @@ function UpscaleOptionsModal($$anchor, $$props) { append($$anchor2, div); }; if_block(node_1, ($$render) => { - if ($$props.open) $$render(consequent_43); + if ($$props.open) $$render(consequent_44); }); } append($$anchor, fragment); @@ -2375,51 +2394,52 @@ delegate([ "contextmenu", "change" ]); -var root_2$7 = from_html(``); -var root_3$7 = from_html(`
`, 1); -var root_4$7 = from_html(`
`); -var root_5$6 = from_html(`
`); -var root_7$6 = from_html(`
`); -var root_9$5 = from_html(`
Before
After
`, 1); -var root_8$6 = from_html(`
`, 1); -var root_10$5 = from_html(`
`); -var root_12$5 = from_html(` `); -var root_11$4 = from_html(`
`); -var root_6$6 = from_html(` `, 1); -var root_13$5 = from_html(``); -var root_15$4 = from_html(`
renders the painted region with this checkpoint — no render metadata needed, and PuLID / Style Reference work on any source
`); -var root_16$4 = from_html(`
re-renders the painted region with this FLUX.1 model at the denoise below — describe the content, strong on faces and eyes
`); -var root_17$3 = from_html(`
re-renders the painted region with this Krea 2 model at the denoise below — describe the content, sharp detail and clean structure
`); +var root_2$8 = from_html(``); +var root_3$8 = from_html(`
`, 1); +var root_4$8 = from_html(`
`); +var root_5$7 = from_html(`
`); +var root_7$7 = from_html(`
`); +var root_9$6 = from_html(`
Before
After
`, 1); +var root_8$7 = from_html(`
`, 1); +var root_10$6 = from_html(`
`); +var root_12$6 = from_html(` `); +var root_11$5 = from_html(`
`); +var root_6$7 = from_html(` `, 1); +var root_13$6 = from_html(``); +var root_15$5 = from_html(`
renders the painted region with this checkpoint — no render metadata needed, and PuLID / Style Reference work on any source
`); +var root_16$5 = from_html(`
re-renders the painted region with this FLUX.1 model at the denoise below — describe the content, strong on faces and eyes
`); +var root_17$4 = from_html(`
re-renders the painted region with this Krea 2 model at the denoise below — describe the content, sharp detail and clean structure
`); var root_18$3 = from_html(`
follows your instruction inside the mask only — unmasked pixels stay untouched; the region renders at full model resolution
`); -var root_14$5 = from_html(`
Engine
`); -var root_20$2 = from_html(`
Installing pack…
`); -var root_22$1 = from_html(`reference`); -var root_23$2 = from_html(`this image this image (default)`, 1); -var root_26$2 = from_html(``); -var root_25$2 = from_html(`
Start
End
`, 1); -var root_27$2 = from_html(`
Fidelity
Start
End
`, 1); -var root_21$2 = from_html(`
Strength
`, 1); -var root_19$3 = from_html(`
Condition
`); -var root_29$2 = from_html(``); -var root_28$1 = from_html(`
Mode
`); -var root_31$2 = from_html(` `); -var root_32$2 = from_html(``); -var root_30$2 = from_html(`
`, 1); -var root_34$2 = from_html(`
`); -var root_35$2 = from_html(``); -var root_36$2 = from_html(`Workflow prompt (reference) `, 1); -var root_37$2 = from_html(`
Denoise
`); -var root_39$2 = from_html(``); +var root_19$4 = from_html(``); +var root_14$6 = from_html(`
Engine
`); +var root_21$3 = from_html(`
Installing pack…
`); +var root_23$3 = from_html(`reference`); +var root_24$3 = from_html(`this image this image (default)`, 1); +var root_27$3 = from_html(``); +var root_26$3 = from_html(`
Start
End
`, 1); +var root_28$2 = from_html(`
Fidelity
Start
End
`, 1); +var root_22$2 = from_html(`
Strength
`, 1); +var root_20$3 = from_html(`
Condition
`); +var root_30$2 = from_html(``); +var root_29$3 = from_html(`
Mode
`); +var root_32$2 = from_html(` `); +var root_33$2 = from_html(``); +var root_31$2 = from_html(`
`, 1); +var root_35$2 = from_html(`
`); +var root_36$2 = from_html(``); +var root_37$2 = from_html(`Workflow prompt (reference) `, 1); +var root_38$2 = from_html(`
Denoise
`); var root_40$2 = from_html(``); -var root_38$2 = from_html(`
`); -var root_41$2 = from_html(`
Grow
Feather
Steps
CFG
Pre-filled with this engine's recipe (Turbo 8/1.0 · RAW 30/3.5 · SDXL 25/5). On the source model, 0 = inherit the image's own.
`, 1); -var root_42$2 = from_html(`Save to `, 1); -var root_43$2 = from_html(`
`); -var root_44$2 = from_html(``); -var root_46$2 = from_html(``); -var root_47$2 = from_html(``); -var root_45$2 = from_html(` `, 1); -var root_1$7 = from_html(`
`); +var root_41$2 = from_html(``); +var root_39$2 = from_html(`
`); +var root_42$2 = from_html(`
Grow
Feather
Steps
CFG
Pre-filled with this engine's recipe (Turbo 8/1.0 · RAW 30/3.5 · SDXL 25/5). On the source model, 0 = inherit the image's own.
`, 1); +var root_43$2 = from_html(`Save to `, 1); +var root_44$2 = from_html(`
`); +var root_45$2 = from_html(``); +var root_47$2 = from_html(``); +var root_48$2 = from_html(``); +var root_46$2 = from_html(` `, 1); +var root_1$8 = from_html(`
`); function InpaintModal($$anchor, $$props) { push($$props, true); let sourceUrl = prop($$props, "sourceUrl", 3, ""), width = prop($$props, "width", 3, 0), height = prop($$props, "height", 3, 0), filename = prop($$props, "filename", 3, ""), caps = prop($$props, "caps", 3, null), prefillPrompt = prop($$props, "prefillPrompt", 3, ""), referencePrompt = prop($$props, "referencePrompt", 3, ""), fetchApi = prop($$props, "fetchApi", 3, null), apiURL = prop($$props, "apiURL", 3, (p) => p), onRun = prop( @@ -2545,6 +2565,11 @@ function InpaintModal($$anchor, $$props) { return ((_b = (_a = caps()) == null ? void 0 : _a.engineModels) == null ? void 0 : _b.find((m) => m.hash === get(engineSel))) || null; }); let engineKind = user_derived(() => get(engineEntry) ? get(engineEntry).architecture === "qwen_edit" ? "qwen" : get(engineEntry).architecture === "flux" ? "flux1" : get(engineEntry).architecture === "krea2" ? "krea2" : "sdxl" : "source"); + let realism = state(false); + let realismAvailable = user_derived(() => { + var _a; + return get(engineKind) === "krea2" && /turbo/i.test(((_a = get(engineEntry)) == null ? void 0 : _a.filename) || ""); + }); let engineGroups = user_derived(() => { var _a, _b, _c, _d; return { @@ -3260,6 +3285,7 @@ function InpaintModal($$anchor, $$props) { filename: get(engineEntry).filename } : void 0, engineGen: ((_a = get(engineEntry)) == null ? void 0 : _a.gen) || void 0, + realism: get(realismAvailable) ? get(realism) : void 0, // The qwen builder crops around the mask client-side — the bbox is // known here (the painted canvas), not on the server. maskBbox: get(engineKind) === "qwen" ? maskBboxFromCanvas() : void 0, @@ -3373,8 +3399,8 @@ function InpaintModal($$anchor, $$props) { var fragment = comment(); var node_1 = first_child(fragment); { - var consequent_33 = ($$anchor2) => { - var div = root_1$7(); + var consequent_34 = ($$anchor2) => { + var div = root_1$8(); let styles; var div_1 = child(div); var div_2 = child(div_1); @@ -3383,7 +3409,7 @@ function InpaintModal($$anchor, $$props) { var node_2 = sibling(span, 2); { var consequent = ($$anchor3) => { - var button = root_2$7(); + var button = root_2$8(); delegated("click", button, close); append($$anchor3, button); }; @@ -3402,7 +3428,7 @@ function InpaintModal($$anchor, $$props) { var node_3 = sibling(div_6, 2); { var consequent_1 = ($$anchor3) => { - var fragment_1 = root_3$7(); + var fragment_1 = root_3$8(); var label = first_child(fragment_1); var input = sibling(child(label)); var div_9 = sibling(label, 2); @@ -3432,7 +3458,7 @@ function InpaintModal($$anchor, $$props) { append($$anchor3, fragment_1); }; var alternate = ($$anchor3) => { - var div_11 = root_4$7(); + var div_11 = root_4$8(); var button_5 = child(div_11); var button_6 = sibling(button_5, 2); let classes_4; @@ -3460,7 +3486,7 @@ function InpaintModal($$anchor, $$props) { var node_4 = sibling(div_13, 2); { var consequent_2 = ($$anchor3) => { - var div_14 = root_5$6(); + var div_14 = root_5$7(); let classes_6; template_effect(() => { classes_6 = set_class(div_14, 1, "pcr-ip-brush-ring svelte-ch4fw9", null, classes_6, { eraser: get(eraser) }); @@ -3475,23 +3501,23 @@ function InpaintModal($$anchor, $$props) { var node_5 = sibling(node_4, 2); { var consequent_8 = ($$anchor3) => { - var fragment_2 = root_6$6(); + var fragment_2 = root_6$7(); var node_6 = first_child(fragment_2); { var consequent_3 = ($$anchor4) => { - var div_15 = root_7$6(); + var div_15 = root_7$7(); var img_2 = child(div_15); template_effect(() => set_attribute(img_2, "src", get(progress).previewUrl)); append($$anchor4, div_15); }; var consequent_5 = ($$anchor4) => { - var fragment_3 = root_8$6(); + var fragment_3 = root_8$7(); var div_16 = first_child(fragment_3); var img_3 = child(div_16); var node_7 = sibling(div_16, 2); { var consequent_4 = ($$anchor5) => { - var fragment_4 = root_9$5(); + var fragment_4 = root_9$6(); var div_17 = first_child(fragment_4); var div_18 = child(div_17); var img_4 = child(div_18); @@ -3523,7 +3549,7 @@ function InpaintModal($$anchor, $$props) { append($$anchor4, fragment_3); }; var alternate_1 = ($$anchor4) => { - var div_20 = root_10$5(); + var div_20 = root_10$6(); var div_21 = child(div_20); var text_2 = child(div_21); template_effect(() => set_text(text_2, get(running) ? "Waiting for the sampler…" : "No output yet — paint a mask and Apply.")); @@ -3539,14 +3565,14 @@ function InpaintModal($$anchor, $$props) { var node_8 = sibling(node_6, 2); { var consequent_7 = ($$anchor4) => { - var div_22 = root_11$4(); + var div_22 = root_11$5(); var div_23 = child(div_22); var div_24 = child(div_23); let classes_7; var node_9 = sibling(div_23, 2); { var consequent_6 = ($$anchor5) => { - var span_1 = root_12$5(); + var span_1 = root_12$6(); var text_3 = child(span_1); template_effect(() => set_text(text_3, `step ${get(progress).value ?? ""}/${get(progress).max ?? ""}`)); append($$anchor5, span_1); @@ -3576,7 +3602,7 @@ function InpaintModal($$anchor, $$props) { var node_10 = child(div_25); { var consequent_9 = ($$anchor3) => { - var button_7 = root_13$5(); + var button_7 = root_13$6(); delegated("click", button_7, applySavedSetup); append($$anchor3, button_7); }; @@ -3586,8 +3612,8 @@ function InpaintModal($$anchor, $$props) { } var node_11 = sibling(node_10, 2); { - var consequent_14 = ($$anchor3) => { - var div_26 = root_14$5(); + var consequent_15 = ($$anchor3) => { + var div_26 = root_14$6(); var node_12 = sibling(child(div_26), 2); { let $0 = user_derived(() => get(installing) || get(running)); @@ -3609,15 +3635,15 @@ function InpaintModal($$anchor, $$props) { var node_13 = sibling(node_12, 2); { var consequent_10 = ($$anchor4) => { - var div_27 = root_15$4(); + var div_27 = root_15$5(); append($$anchor4, div_27); }; var consequent_11 = ($$anchor4) => { - var div_28 = root_16$4(); + var div_28 = root_16$5(); append($$anchor4, div_28); }; var consequent_12 = ($$anchor4) => { - var div_29 = root_17$3(); + var div_29 = root_17$4(); append($$anchor4, div_29); }; var consequent_13 = ($$anchor4) => { @@ -3631,17 +3657,29 @@ function InpaintModal($$anchor, $$props) { else if (get(engineKind) === "qwen") $$render(consequent_13, 3); }); } + var node_14 = sibling(node_13, 2); + { + var consequent_14 = ($$anchor4) => { + var label_2 = root_19$4(); + var input_2 = child(label_2); + bind_checked(input_2, () => get(realism), ($$value) => set(realism, $$value)); + append($$anchor4, label_2); + }; + if_block(node_14, ($$render) => { + if (get(realismAvailable)) $$render(consequent_14); + }); + } append($$anchor3, div_26); }; if_block(node_11, ($$render) => { var _a, _b; - if ((_b = (_a = caps()) == null ? void 0 : _a.engineModels) == null ? void 0 : _b.length) $$render(consequent_14); + if ((_b = (_a = caps()) == null ? void 0 : _a.engineModels) == null ? void 0 : _b.length) $$render(consequent_15); }); } - var node_14 = sibling(node_11, 2); + var node_15 = sibling(node_11, 2); { - var consequent_20 = ($$anchor3) => { - var div_31 = root_19$3(); + var consequent_21 = ($$anchor3) => { + var div_31 = root_20$3(); var select = sibling(child(div_31), 2); var option = child(select); option.value = option.__value = "none"; @@ -3651,41 +3689,41 @@ function InpaintModal($$anchor, $$props) { option_2.value = option_2.__value = "ipadapter"; var select_value; init_select(select); - var node_15 = sibling(select, 2); + var node_16 = sibling(select, 2); { - var consequent_15 = ($$anchor4) => { - var div_32 = root_20$2(); + var consequent_16 = ($$anchor4) => { + var div_32 = root_21$3(); append($$anchor4, div_32); }; - var consequent_19 = ($$anchor4) => { - var fragment_5 = root_21$2(); + var consequent_20 = ($$anchor4) => { + var fragment_5 = root_22$2(); var div_33 = first_child(fragment_5); var button_8 = child(div_33); - var node_16 = sibling(button_8, 2); + var node_17 = sibling(button_8, 2); { - var consequent_16 = ($$anchor5) => { - var img_5 = root_22$1(); + var consequent_17 = ($$anchor5) => { + var img_5 = root_23$3(); template_effect(() => set_attribute(img_5, "src", get(refThumbUrl))); append($$anchor5, img_5); }; var alternate_2 = ($$anchor5) => { - var fragment_6 = root_23$2(); + var fragment_6 = root_24$3(); var img_6 = first_child(fragment_6); template_effect(() => set_attribute(img_6, "src", sourceUrl())); append($$anchor5, fragment_6); }; - if_block(node_16, ($$render) => { - if (get(refThumbUrl)) $$render(consequent_16); + if_block(node_17, ($$render) => { + if (get(refThumbUrl)) $$render(consequent_17); else $$render(alternate_2, -1); }); } - var input_2 = sibling(node_16, 2); - bind_this(input_2, ($$value) => refInputEl = $$value, () => refInputEl); + var input_3 = sibling(node_17, 2); + bind_this(input_3, ($$value) => refInputEl = $$value, () => refInputEl); var div_34 = sibling(div_33, 2); - var node_17 = sibling(child(div_34), 2); + var node_18 = sibling(child(div_34), 2); { let $0 = user_derived(() => DEFAULT_WEIGHT[get(condition)] ?? 0.6); - SettingsSlider(node_17, { + SettingsSlider(node_18, { min: 0, max: 1.5, step: 0.05, @@ -3702,17 +3740,17 @@ function InpaintModal($$anchor, $$props) { } var div_35 = sibling(div_34, 2); var text_4 = child(div_35); - var node_18 = sibling(div_35, 2); + var node_19 = sibling(div_35, 2); { - var consequent_18 = ($$anchor5) => { + var consequent_19 = ($$anchor5) => { var fragment_7 = comment(); - var node_19 = first_child(fragment_7); + var node_20 = first_child(fragment_7); { - var consequent_17 = ($$anchor6) => { - var fragment_8 = root_25$2(); + var consequent_18 = ($$anchor6) => { + var fragment_8 = root_26$3(); var select_1 = sibling(first_child(fragment_8), 2); each(select_1, 21, () => IPA_WEIGHT_TYPES, index, ($$anchor7, wt) => { - var option_3 = root_26$2(); + var option_3 = root_27$3(); var text_5 = child(option_3); var option_3_value = {}; template_effect(() => { @@ -3724,8 +3762,8 @@ function InpaintModal($$anchor, $$props) { append($$anchor7, option_3); }); var div_36 = sibling(select_1, 2); - var node_20 = sibling(child(div_36), 2); - SettingsSlider(node_20, { + var node_21 = sibling(child(div_36), 2); + SettingsSlider(node_21, { min: 0, max: 1, step: 0.01, @@ -3738,8 +3776,8 @@ function InpaintModal($$anchor, $$props) { } }); var div_37 = sibling(div_36, 2); - var node_21 = sibling(child(div_37), 2); - SettingsSlider(node_21, { + var node_22 = sibling(child(div_37), 2); + SettingsSlider(node_22, { min: 0, max: 1, step: 0.01, @@ -3755,10 +3793,10 @@ function InpaintModal($$anchor, $$props) { append($$anchor6, fragment_8); }; var alternate_3 = ($$anchor6) => { - var fragment_9 = root_27$2(); + var fragment_9 = root_28$2(); var div_38 = first_child(fragment_9); - var node_22 = sibling(child(div_38), 2); - SettingsSlider(node_22, { + var node_23 = sibling(child(div_38), 2); + SettingsSlider(node_23, { min: 0, max: 10, step: 1, @@ -3771,8 +3809,8 @@ function InpaintModal($$anchor, $$props) { } }); var div_39 = sibling(div_38, 2); - var node_23 = sibling(child(div_39), 2); - SettingsSlider(node_23, { + var node_24 = sibling(child(div_39), 2); + SettingsSlider(node_24, { min: 0, max: 1, step: 0.01, @@ -3785,8 +3823,8 @@ function InpaintModal($$anchor, $$props) { } }); var div_40 = sibling(div_39, 2); - var node_24 = sibling(child(div_40), 2); - SettingsSlider(node_24, { + var node_25 = sibling(child(div_40), 2); + SettingsSlider(node_25, { min: 0, max: 1, step: 0.01, @@ -3800,28 +3838,28 @@ function InpaintModal($$anchor, $$props) { }); append($$anchor6, fragment_9); }; - if_block(node_19, ($$render) => { - if (get(condition) === "ipadapter") $$render(consequent_17); + if_block(node_20, ($$render) => { + if (get(condition) === "ipadapter") $$render(consequent_18); else $$render(alternate_3, -1); }); } append($$anchor5, fragment_7); }; - if_block(node_18, ($$render) => { - if (get(conditionAdvancedOpen)) $$render(consequent_18); + if_block(node_19, ($$render) => { + if (get(conditionAdvancedOpen)) $$render(consequent_19); }); } template_effect(() => set_text(text_4, `${get(conditionAdvancedOpen) ? "▾" : "▸"} Advanced`)); delegated("click", button_8, () => refInputEl == null ? void 0 : refInputEl.click()); - delegated("change", input_2, chooseReference); + delegated("change", input_3, chooseReference); delegated("click", div_35, () => { set(conditionAdvancedOpen, !get(conditionAdvancedOpen)); }); append($$anchor4, fragment_5); }; - if_block(node_15, ($$render) => { - if (get(installing)) $$render(consequent_15); - else if (get(needsReference)) $$render(consequent_19, 1); + if_block(node_16, ($$render) => { + if (get(installing)) $$render(consequent_16); + else if (get(needsReference)) $$render(consequent_20, 1); }); } template_effect( @@ -3851,18 +3889,18 @@ function InpaintModal($$anchor, $$props) { delegated("change", select, (e) => pickCondition(e.currentTarget.value)); append($$anchor3, div_31); }; - if_block(node_14, ($$render) => { + if_block(node_15, ($$render) => { var _a; - if (((_a = caps()) == null ? void 0 : _a.conditions) && get(engineKind) !== "qwen" && get(engineKind) !== "flux1" && get(engineKind) !== "krea2") $$render(consequent_20); + if (((_a = caps()) == null ? void 0 : _a.conditions) && get(engineKind) !== "qwen" && get(engineKind) !== "flux1" && get(engineKind) !== "krea2") $$render(consequent_21); }); } - var node_25 = sibling(node_14, 2); + var node_26 = sibling(node_15, 2); { - var consequent_21 = ($$anchor3) => { - var div_41 = root_28$1(); + var consequent_22 = ($$anchor3) => { + var div_41 = root_29$3(); var select_2 = sibling(child(div_41), 2); each(select_2, 21, () => MODE_ROWS, index, ($$anchor4, row) => { - var option_4 = root_29$2(); + var option_4 = root_30$2(); var text_6 = child(option_4); var option_4_value = {}; template_effect( @@ -3896,32 +3934,32 @@ function InpaintModal($$anchor, $$props) { delegated("change", select_2, (e) => pickModeKey(e.currentTarget.value)); append($$anchor3, div_41); }; - if_block(node_25, ($$render) => { - if (get(showModeRows)) $$render(consequent_21); + if_block(node_26, ($$render) => { + if (get(showModeRows)) $$render(consequent_22); }); } - var div_43 = sibling(node_25, 2); + var div_43 = sibling(node_26, 2); var div_44 = child(div_43); var text_8 = child(div_44); - var node_26 = sibling(div_44, 2); + var node_27 = sibling(div_44, 2); { - var consequent_23 = ($$anchor3) => { - var fragment_10 = root_30$2(); - var node_27 = first_child(fragment_10); + var consequent_24 = ($$anchor3) => { + var fragment_10 = root_31$2(); + var node_28 = first_child(fragment_10); { - var consequent_22 = ($$anchor4) => { - var span_2 = root_31$2(); + var consequent_23 = ($$anchor4) => { + var span_2 = root_32$2(); var text_9 = child(span_2); template_effect(() => set_text(text_9, `🧍 auto-loaded ${forcedPrefillLabel() ?? ""}'s prompt — switch below or edit`)); append($$anchor4, span_2); }; - if_block(node_27, ($$render) => { - if (forcedPrefill() && forcedPrefillLabel() && !get(showModeRows)) $$render(consequent_22); + if_block(node_28, ($$render) => { + if (forcedPrefill() && forcedPrefillLabel() && !get(showModeRows)) $$render(consequent_23); }); } - var div_45 = sibling(node_27, 2); + var div_45 = sibling(node_28, 2); each(div_45, 21, () => regionPrompts().filter((r) => r.text), (r) => r.name, ($$anchor4, r) => { - var button_9 = root_32$2(); + var button_9 = root_33$2(); let classes_8; var text_10 = child(button_9); template_effect( @@ -3937,58 +3975,58 @@ function InpaintModal($$anchor, $$props) { }); append($$anchor3, fragment_10); }; - if_block(node_26, ($$render) => { - if (regionPrompts().length && get(engineKind) !== "qwen" && !movedContent() && (!get(showModeRows) || !get(isRegionalMode))) $$render(consequent_23); + if_block(node_27, ($$render) => { + if (regionPrompts().length && get(engineKind) !== "qwen" && !movedContent() && (!get(showModeRows) || !get(isRegionalMode))) $$render(consequent_24); }); } - var node_28 = sibling(node_26, 2); - key(node_28, () => get(promptSeedKey) + ":" + get(promptRestoreNonce), ($$anchor3) => { + var node_29 = sibling(node_27, 2); + key(node_29, () => get(promptSeedKey) + ":" + get(promptRestoreNonce), ($$anchor3) => { var fragment_11 = comment(); - var node_29 = first_child(fragment_11); + var node_30 = first_child(fragment_11); { - var consequent_24 = ($$anchor4) => { - var div_46 = root_34$2(); + var consequent_25 = ($$anchor4) => { + var div_46 = root_35$2(); action(div_46, ($$node) => promptEditor == null ? void 0 : promptEditor($$node)); append($$anchor4, div_46); }; var alternate_4 = ($$anchor4) => { - var textarea = root_35$2(); + var textarea = root_36$2(); set_attribute(textarea, "placeholder", "what should appear in the painted region\n\nNegative Prompt:\noptional"); bind_value(textarea, () => get(prompt), ($$value) => set(prompt, $$value)); append($$anchor4, textarea); }; - if_block(node_29, ($$render) => { - if (mountPromptEditor()) $$render(consequent_24); + if_block(node_30, ($$render) => { + if (mountPromptEditor()) $$render(consequent_25); else $$render(alternate_4, -1); }); } append($$anchor3, fragment_11); }); - var node_30 = sibling(node_28, 2); + var node_31 = sibling(node_29, 2); { - var consequent_25 = ($$anchor3) => { - var fragment_12 = root_36$2(); + var consequent_26 = ($$anchor3) => { + var fragment_12 = root_37$2(); var textarea_1 = sibling(first_child(fragment_12), 2); bind_this(textarea_1, ($$value) => refEl = $$value, () => refEl); template_effect(() => set_value(textarea_1, referencePrompt())); append($$anchor3, fragment_12); }; - if_block(node_30, ($$render) => { - if (referencePrompt() && get(engineKind) !== "qwen") $$render(consequent_25); + if_block(node_31, ($$render) => { + if (referencePrompt() && get(engineKind) !== "qwen") $$render(consequent_26); }); } var div_47 = sibling(div_43, 2); - var node_31 = sibling(child(div_47), 2); + var node_32 = sibling(child(div_47), 2); { - var consequent_26 = ($$anchor3) => { - var div_48 = root_37$2(); - var node_32 = sibling(child(div_48), 2); + var consequent_27 = ($$anchor3) => { + var div_48 = root_38$2(); + var node_33 = sibling(child(div_48), 2); { let $0 = user_derived(() => { var _a; return ((_a = caps()) == null ? void 0 : _a.defaultDenoise) ?? 0.5; }); - SettingsSlider(node_32, { + SettingsSlider(node_33, { min: 0.05, max: 1, step: 0.01, @@ -4005,19 +4043,19 @@ function InpaintModal($$anchor, $$props) { } append($$anchor3, div_48); }; - if_block(node_31, ($$render) => { - if (get(engineKind) !== "qwen") $$render(consequent_26); + if_block(node_32, ($$render) => { + if (get(engineKind) !== "qwen") $$render(consequent_27); }); } - var node_33 = sibling(node_31, 2); + var node_34 = sibling(node_32, 2); { - var consequent_27 = ($$anchor3) => { - var div_49 = root_38$2(); + var consequent_28 = ($$anchor3) => { + var div_49 = root_39$2(); var div_50 = child(div_49); var select_3 = sibling(child(div_50), 2); let classes_9; each(select_3, 21, () => caps().samplerOptions, index, ($$anchor4, opt) => { - var option_5 = root_39$2(); + var option_5 = root_40$2(); let styles_3; var text_11 = child(option_5); var option_5_value = {}; @@ -4044,7 +4082,7 @@ function InpaintModal($$anchor, $$props) { var select_4 = sibling(child(div_51), 2); let classes_10; each(select_4, 21, () => caps().schedulerOptions, index, ($$anchor4, opt) => { - var option_6 = root_40$2(); + var option_6 = root_41$2(); let styles_4; var text_12 = child(option_6); var option_6_value = {}; @@ -4075,20 +4113,20 @@ function InpaintModal($$anchor, $$props) { bind_select_value(select_4, () => get(scheduler), ($$value) => set(scheduler, $$value)); append($$anchor3, div_49); }; - if_block(node_33, ($$render) => { + if_block(node_34, ($$render) => { var _a, _b; - if ((_b = (_a = caps()) == null ? void 0 : _a.samplerOptions) == null ? void 0 : _b.length) $$render(consequent_27); + if ((_b = (_a = caps()) == null ? void 0 : _a.samplerOptions) == null ? void 0 : _b.length) $$render(consequent_28); }); } - var div_52 = sibling(node_33, 2); + var div_52 = sibling(node_34, 2); var text_13 = child(div_52); - var node_34 = sibling(div_52, 2); + var node_35 = sibling(div_52, 2); { - var consequent_28 = ($$anchor3) => { - var fragment_13 = root_41$2(); + var consequent_29 = ($$anchor3) => { + var fragment_13 = root_42$2(); var div_53 = first_child(fragment_13); - var node_35 = sibling(child(div_53), 2); - SettingsSlider(node_35, { + var node_36 = sibling(child(div_53), 2); + SettingsSlider(node_36, { min: 0, max: 64, step: 1, @@ -4101,8 +4139,8 @@ function InpaintModal($$anchor, $$props) { } }); var div_54 = sibling(div_53, 2); - var node_36 = sibling(child(div_54), 2); - SettingsSlider(node_36, { + var node_37 = sibling(child(div_54), 2); + SettingsSlider(node_37, { min: 0, max: 128, step: 1, @@ -4115,8 +4153,8 @@ function InpaintModal($$anchor, $$props) { } }); var div_55 = sibling(div_54, 2); - var node_37 = sibling(child(div_55), 2); - SettingsSlider(node_37, { + var node_38 = sibling(child(div_55), 2); + SettingsSlider(node_38, { min: 0, max: 60, step: 1, @@ -4131,8 +4169,8 @@ function InpaintModal($$anchor, $$props) { } }); var div_56 = sibling(div_55, 2); - var node_38 = sibling(child(div_56), 2); - SettingsSlider(node_38, { + var node_39 = sibling(child(div_56), 2); + SettingsSlider(node_39, { min: 0, max: 10, step: 0.1, @@ -4148,16 +4186,16 @@ function InpaintModal($$anchor, $$props) { }); append($$anchor3, fragment_13); }; - if_block(node_34, ($$render) => { - if (get(maskAdvancedOpen)) $$render(consequent_28); + if_block(node_35, ($$render) => { + if (get(maskAdvancedOpen)) $$render(consequent_29); }); } - var node_39 = sibling(node_34, 2); + var node_40 = sibling(node_35, 2); { - var consequent_29 = ($$anchor3) => { - var fragment_14 = root_42$2(); - var node_40 = sibling(first_child(fragment_14), 2); - SavePathInput(node_40, { + var consequent_30 = ($$anchor3) => { + var fragment_14 = root_43$2(); + var node_41 = sibling(first_child(fragment_14), 2); + SavePathInput(node_41, { get value() { return get(savePrefix); }, @@ -4170,45 +4208,45 @@ function InpaintModal($$anchor, $$props) { }); append($$anchor3, fragment_14); }; - if_block(node_39, ($$render) => { - if (!onUseInEdit()) $$render(consequent_29); + if_block(node_40, ($$render) => { + if (!onUseInEdit()) $$render(consequent_30); }); } - var node_41 = sibling(div_3, 2); + var node_42 = sibling(div_3, 2); { - var consequent_30 = ($$anchor3) => { - var div_57 = root_43$2(); + var consequent_31 = ($$anchor3) => { + var div_57 = root_44$2(); var text_14 = child(div_57); template_effect(() => set_text(text_14, `⚠ ${get(errorMsg) ?? ""}`)); append($$anchor3, div_57); }; - if_block(node_41, ($$render) => { - if (get(errorMsg)) $$render(consequent_30); + if_block(node_42, ($$render) => { + if (get(errorMsg)) $$render(consequent_31); }); } - var div_58 = sibling(node_41, 2); - var node_42 = child(div_58); + var div_58 = sibling(node_42, 2); + var node_43 = child(div_58); { - var consequent_31 = ($$anchor3) => { - var button_10 = root_44$2(); + var consequent_32 = ($$anchor3) => { + var button_10 = root_45$2(); delegated("click", button_10, stopRun); append($$anchor3, button_10); }; var alternate_6 = ($$anchor3) => { - var fragment_15 = root_45$2(); + var fragment_15 = root_46$2(); var button_11 = first_child(fragment_15); var button_12 = sibling(button_11, 2); var text_15 = child(button_12); - var node_43 = sibling(button_12, 2); + var node_44 = sibling(button_12, 2); { - var consequent_32 = ($$anchor4) => { - var button_13 = root_46$2(); + var consequent_33 = ($$anchor4) => { + var button_13 = root_47$2(); template_effect(() => button_13.disabled = !get(doneState)); delegated("click", button_13, () => onUseInEdit()(get(doneState))); append($$anchor4, button_13); }; var alternate_5 = ($$anchor4) => { - var button_14 = root_47$2(); + var button_14 = root_48$2(); var text_16 = child(button_14); template_effect(() => { button_14.disabled = !get(doneState) || get(saving); @@ -4217,8 +4255,8 @@ function InpaintModal($$anchor, $$props) { delegated("click", button_14, save); append($$anchor4, button_14); }; - if_block(node_43, ($$render) => { - if (onUseInEdit()) $$render(consequent_32); + if_block(node_44, ($$render) => { + if (onUseInEdit()) $$render(consequent_33); else $$render(alternate_5, -1); }); } @@ -4231,8 +4269,8 @@ function InpaintModal($$anchor, $$props) { delegated("click", button_12, apply); append($$anchor3, fragment_15); }; - if_block(node_42, ($$render) => { - if (get(running)) $$render(consequent_31); + if_block(node_43, ($$render) => { + if (get(running)) $$render(consequent_32); else $$render(alternate_6, -1); }); } @@ -4283,7 +4321,7 @@ function InpaintModal($$anchor, $$props) { append($$anchor2, div); }; if_block(node_1, ($$render) => { - if ($$props.open) $$render(consequent_33); + if ($$props.open) $$render(consequent_34); }); } append($$anchor, fragment); @@ -4298,6 +4336,906 @@ delegate([ "contextmenu", "change" ]); +var root_2$7 = from_html(``); +var root_3$7 = from_html(``); +var root_4$7 = from_html(`
`); +var root_6$6 = from_html(`
`); +var root_8$6 = from_html(`
Source
Result
`, 1); +var root_7$6 = from_html(`
`, 1); +var root_9$5 = from_html(`
`); +var root_11$4 = from_html(` `); +var root_10$5 = from_html(`
`); +var root_5$6 = from_html(` `, 1); +var root_13$5 = from_html(`
re-diffuses the whole image with its own model at the denoise below
`); +var root_14$5 = from_html(`
re-diffuses the whole image with this SDXL checkpoint — describe the scene
`); +var root_15$4 = from_html(`
re-diffuses the whole image with this FLUX.1 model — strong on faces and eyes
`); +var root_16$4 = from_html(`
re-diffuses the whole image with this Krea 2 model — sharp detail, clean structure
`); +var root_17$3 = from_html(``); +var root_12$5 = from_html(`
Engine
`); +var root_19$3 = from_html(`
`); +var root_20$2 = from_html(``); +var root_21$2 = from_html(`Workflow prompt (reference) `, 1); +var root_23$2 = from_html(``); +var root_24$2 = from_html(``); +var root_22$1 = from_html(`
`); +var root_25$2 = from_html(`
Steps
CFG
On the source model, 0 = inherit the image's own steps/cfg.
`, 1); +var root_26$2 = from_html(`
`); +var root_27$2 = from_html(``); +var root_29$2 = from_html(``); +var root_28$1 = from_html(` `, 1); +var root_1$7 = from_html(`
`); +function I2IModal($$anchor, $$props) { + push($$props, true); + let sourceUrl = prop($$props, "sourceUrl", 3, ""), width = prop($$props, "width", 3, 0), height = prop($$props, "height", 3, 0), filename = prop($$props, "filename", 3, ""), caps = prop($$props, "caps", 3, null), prefillPrompt = prop($$props, "prefillPrompt", 3, ""), referencePrompt = prop($$props, "referencePrompt", 3, ""), apiURL = prop($$props, "apiURL", 3, (p) => p), onRun = prop( + $$props, + "onRun", + 3, + null + // (options) => tracker + ), onUseInEdit = prop( + $$props, + "onUseInEdit", + 3, + null + // (doneState) => void — hand the render back to Edit as a layer + ), elevated = prop( + $$props, + "elevated", + 3, + false + // raise z-index above the Edit modal + ), mountPromptEditor = prop( + $$props, + "mountPromptEditor", + 3, + null + // (container, initialValue, onChange, modelInfo) => Promise + ), imageKey = prop( + $$props, + "imageKey", + 3, + "" + // stable id of the image (per-image session memory) + ); + let activeTab = state("source"); + let prompt = state(""); + let denoise = state(0.55); + let sampler = state(""); + let scheduler = state(""); + let steps = state( + 0 + // engine recipe on open; 0 = inherit (source model) + ); + let cfg = state( + 0 + // engine recipe on open; 0 = inherit (source model) + ); + let advancedOpen = state(false); + let engineSel = state("source"); + let engineEntry = user_derived(() => { + var _a, _b; + return ((_b = (_a = caps()) == null ? void 0 : _a.engineModels) == null ? void 0 : _b.find((m) => m.hash === get(engineSel))) || null; + }); + let engineKind = user_derived(() => get(engineEntry) ? get(engineEntry).architecture === "flux" ? "flux1" : get(engineEntry).architecture === "krea2" ? "krea2" : "sdxl" : "source"); + let realism = state(false); + let realismAvailable = user_derived(() => { + var _a; + return get(engineKind) === "krea2" && /turbo/i.test(((_a = get(engineEntry)) == null ? void 0 : _a.filename) || ""); + }); + let engineGroups = user_derived(() => { + var _a, _b, _c; + return { + sdxl: (((_a = caps()) == null ? void 0 : _a.engineModels) || []).filter((m) => m.architecture === "sdxl"), + flux1: (((_b = caps()) == null ? void 0 : _b.engineModels) || []).filter((m) => m.architecture === "flux"), + krea2: (((_c = caps()) == null ? void 0 : _c.engineModels) || []).filter((m) => m.architecture === "krea2") + }; + }); + let engineSelectGroups = user_derived(() => { + var _a; + return [ + ...((_a = caps()) == null ? void 0 : _a.sourceUsable) !== false ? [ + { + label: "", + options: [ + { + value: "source", + label: "Source model — this image's own workflow (default)" + } + ] + } + ] : [], + ...get(engineGroups).sdxl.length ? [ + { + label: "SDXL — re-diffuse with this checkpoint", + options: get(engineGroups).sdxl.map((m) => ({ value: m.hash, label: m.displayName })) + } + ] : [], + ...get(engineGroups).flux1.length ? [ + { + label: "FLUX.1 — re-diffuse with this model", + options: get(engineGroups).flux1.map((m) => ({ value: m.hash, label: m.displayName })) + } + ] : [], + ...get(engineGroups).krea2.length ? [ + { + label: "Krea 2 — re-diffuse with this model", + options: get(engineGroups).krea2.map((m) => ({ value: m.hash, label: m.displayName })) + } + ] : [] + ]; + }); + function engineRecipe(entry) { + var _a, _b, _c, _d; + if (!entry) return { + steps: 0, + cfg: 0, + denoise: ((_b = (_a = caps()) == null ? void 0 : _a.sourceI2iRecipe) == null ? void 0 : _b.denoise) ?? ((_c = caps()) == null ? void 0 : _c.defaultDenoise) ?? 0.55, + sampler: "", + scheduler: "" + }; + const r = entry.i2i || {}; + return { + steps: r.steps ?? 0, + cfg: r.cfg ?? 0, + denoise: r.denoise ?? ((_d = caps()) == null ? void 0 : _d.defaultDenoise) ?? 0.55, + sampler: r.sampler || "", + scheduler: r.scheduler || "" + }; + } + let recSampler = user_derived(() => { + var _a; + return get(engineEntry) ? engineRecipe(get(engineEntry)).sampler : (_a = caps()) == null ? void 0 : _a.recommendedSampler; + }); + let recScheduler = user_derived(() => { + var _a; + return get(engineEntry) ? engineRecipe(get(engineEntry)).scheduler : (_a = caps()) == null ? void 0 : _a.recommendedScheduler; + }); + let recSteps = user_derived(() => engineRecipe(get(engineEntry)).steps); + let recCfg = user_derived(() => engineRecipe(get(engineEntry)).cfg); + function pickEngine(value) { + var _a, _b, _c, _d; + set(engineSel, value, true); + const entry = ((_b = (_a = caps()) == null ? void 0 : _a.engineModels) == null ? void 0 : _b.find((m) => m.hash === value)) || null; + const r = engineRecipe(entry); + if (entry) { + set(sampler, r.sampler, true); + set(scheduler, r.scheduler, true); + } else { + set(sampler, ((_c = caps()) == null ? void 0 : _c.defaultSampler) || "", true); + set(scheduler, ((_d = caps()) == null ? void 0 : _d.defaultScheduler) || "", true); + } + set(steps, r.steps, true); + set(cfg, r.cfg, true); + set(denoise, r.denoise, true); + } + function editorModelInfo() { + var _a; + if (get(engineEntry)) return { + hash: get(engineEntry).hash, + architecture: get(engineEntry).architecture + }; + return ((_a = caps()) == null ? void 0 : _a.sourceModelInfo) || null; + } + let memoryPrompt = null; + function prefillFor() { + if (typeof memoryPrompt === "string" && memoryPrompt.trim()) return memoryPrompt; + return prefillPrompt() || ""; + } + let progress = state(null); + let doneState = state(null); + let errorMsg = state(""); + let tracker = null; + let unsub = null; + let wasOpen = false; + user_effect(() => { + if ($$props.open && caps() && !wasOpen) { + wasOpen = true; + untrack(() => { + var _a, _b, _c, _d; + set(activeTab, "source"); + const mem = recallModalMemory("i2i", imageKey()); + const memEngineValid = (mem == null ? void 0 : mem.engine) === "source" ? caps().sourceUsable !== false : !!(mem == null ? void 0 : mem.engine) && !!((_a = caps().engineModels) == null ? void 0 : _a.some((m) => m.hash === mem.engine)); + set( + engineSel, + memEngineValid ? mem.engine : caps().sourceUsable === false ? ((_c = (_b = caps().engineModels) == null ? void 0 : _b[0]) == null ? void 0 : _c.hash) || "source" : "source", + true + ); + memoryPrompt = mem && typeof mem.prompt === "string" && mem.prompt.trim() ? mem.prompt : null; + set(prompt, prefillFor(), true); + const entry = ((_d = caps().engineModels) == null ? void 0 : _d.find((m) => m.hash === get(engineSel))) || null; + const r = engineRecipe(entry); + if (entry) { + set(sampler, r.sampler, true); + set(scheduler, r.scheduler, true); + } else { + set(sampler, caps().defaultSampler || "", true); + set(scheduler, caps().defaultScheduler || "", true); + } + set(steps, r.steps, true); + set(cfg, r.cfg, true); + set(denoise, r.denoise, true); + if (memEngineValid && typeof mem.denoise === "number") set(denoise, mem.denoise, true); + set(advancedOpen, false); + set(progress, null); + set(doneState, null); + set(errorMsg, ""); + requestAnimationFrame(() => { + resetView(); + }); + }); + } else if (!$$props.open) { + wasOpen = false; + } + }); + user_effect(() => { + if (!$$props.open) return; + const onKey = (e) => { + if ((e.ctrlKey || e.metaKey) && e.key === "Enter") { + e.preventDefault(); + e.stopImmediatePropagation(); + apply(); + } + }; + window.addEventListener("keydown", onKey, true); + return () => window.removeEventListener("keydown", onKey, true); + }); + let running = user_derived(() => !!get(progress) && get(progress).phase !== "done" && get(progress).phase !== "error" && get(progress).phase !== "cancelled"); + let noEngine = user_derived(() => { + var _a, _b, _c; + return ((_a = caps()) == null ? void 0 : _a.sourceUsable) === false && !((_c = (_b = caps()) == null ? void 0 : _b.engineModels) == null ? void 0 : _c.length); + }); + let outputUrl = user_derived(() => (() => { + var _a, _b; + if (((_a = get(progress)) == null ? void 0 : _a.phase) === "running" && get(progress).previewUrl) return get(progress).previewUrl; + if ((_b = get(doneState)) == null ? void 0 : _b.temp) { + const t = get(doneState).temp; + return apiURL()(`/view?filename=${encodeURIComponent(t.filename)}&subfolder=${encodeURIComponent(t.subfolder || "")}&type=${t.type || "temp"}&rand=${get(doneState).rand}`); + } + return null; + })()); + let progressPercent = user_derived(() => { + var _a; + return ((_a = get(progress)) == null ? void 0 : _a.phase) === "running" && get(progress).max ? Math.round(get(progress).value / get(progress).max * 100) : null; + }); + let viewportEl; + let zoom = state(1); + let panX = state(0); + let panY = state(0); + let panning = false; + let lastPt = null; + let compareSplit = state(false); + let splitX = state(0); + let splitDragging = false; + function resetView() { + if (!viewportEl || !width() || !height()) { + set(zoom, 1); + set(panX, 0); + set(panY, 0); + return; + } + const rect = viewportEl.getBoundingClientRect(); + set(zoom, Math.min(rect.width / width(), rect.height / height(), 1) || 1, true); + set(panX, (rect.width - width() * get(zoom)) / 2); + set(panY, (rect.height - height() * get(zoom)) / 2); + } + function onPointerDown(e) { + if (e.button > 2) return; + panning = true; + lastPt = { x: e.clientX, y: e.clientY }; + e.preventDefault(); + viewportEl.setPointerCapture(e.pointerId); + } + function onPointerMove(e) { + if (!panning) return; + set(panX, get(panX) + (e.clientX - lastPt.x)); + set(panY, get(panY) + (e.clientY - lastPt.y)); + lastPt = { x: e.clientX, y: e.clientY }; + } + function onPointerUp() { + panning = false; + lastPt = null; + } + function onWheel(e) { + e.preventDefault(); + const rect = viewportEl.getBoundingClientRect(); + const cx = e.clientX - rect.left, cy = e.clientY - rect.top; + const factor = e.deltaY < 0 ? 1.15 : 1 / 1.15; + const newZoom = Math.max(0.05, Math.min(12, get(zoom) * factor)); + set(panX, cx - (cx - get(panX)) / get(zoom) * newZoom); + set(panY, cy - (cy - get(panY)) / get(zoom) * newZoom); + set(zoom, newZoom, true); + } + function toggleCompare() { + if (!get(outputUrl) || get(running)) return; + set(compareSplit, !get(compareSplit)); + if (get(compareSplit)) { + const rect = viewportEl == null ? void 0 : viewportEl.getBoundingClientRect(); + set(splitX, rect ? rect.width / 2 : 0, true); + } + } + function onSplitDown(e) { + e.stopPropagation(); + e.preventDefault(); + splitDragging = true; + e.currentTarget.setPointerCapture(e.pointerId); + } + function onSplitMove(e) { + if (!splitDragging) return; + e.stopPropagation(); + const rect = viewportEl.getBoundingClientRect(); + set(splitX, Math.max(0, Math.min(rect.width, e.clientX - rect.left)), true); + } + function onSplitUp(e) { + splitDragging = false; + try { + e.currentTarget.releasePointerCapture(e.pointerId); + } catch { + } + } + let promptSeedKey = user_derived(() => get(engineKind)); + function promptEditor(node) { + let disposed = false; + let view = null; + (async () => { + var _a; + if (!mountPromptEditor()) return; + const v = await mountPromptEditor()( + node, + untrack(() => prefillFor()), + (text) => { + set(prompt, text, true); + }, + editorModelInfo + ); + if (disposed) { + (_a = v == null ? void 0 : v.destroy) == null ? void 0 : _a.call(v); + return; + } + view = v; + })().catch((e) => console.error("[i2i] prompt editor mount failed", e)); + return { + destroy() { + var _a; + disposed = true; + (_a = view == null ? void 0 : view.destroy) == null ? void 0 : _a.call(view); + } + }; + } + function apply() { + var _a; + if (!onRun() || get(running)) return; + set(doneState, null); + set(errorMsg, ""); + set(activeTab, "output"); + tracker = onRun()({ + prompt: get(prompt), + denoise: get(denoise), + sampler: get(sampler) || null, + scheduler: get(scheduler) || null, + steps: get(steps) > 0 ? get(steps) : void 0, + cfg: get(cfg) > 0 ? get(cfg) : void 0, + engine: get(engineKind) === "sdxl" ? "sdxl-ckpt" : get(engineKind) === "flux1" ? "flux1-unet" : get(engineKind) === "krea2" ? "krea2-unet" : "source", + engineModel: get(engineEntry) ? { + hash: get(engineEntry).hash, + filename: get(engineEntry).filename + } : void 0, + engineGen: ((_a = get(engineEntry)) == null ? void 0 : _a.gen) || void 0, + realism: get(realismAvailable) ? get(realism) : void 0 + }); + storeModalMemory("i2i", imageKey(), { + prompt: get(prompt), + engine: get(engineKind) === "source" ? "source" : get(engineSel), + denoise: get(denoise) + }); + unsub == null ? void 0 : unsub(); + unsub = tracker.subscribe((state2) => { + set(progress, state2, true); + if (state2.phase === "done") { + set(doneState, { ...state2, rand: Math.random() }, true); + set(progress, null); + } else if (state2.phase === "cancelled") { + set(progress, null); + set(activeTab, "source"); + } else if (state2.phase === "error") { + set(errorMsg, state2.message || "i2i failed", true); + set(progress, null); + set(activeTab, "source"); + } + }); + } + function stopRun() { + var _a; + (_a = tracker == null ? void 0 : tracker.cancel) == null ? void 0 : _a.call(tracker); + } + function close() { + var _a; + unsub == null ? void 0 : unsub(); + unsub = null; + tracker = null; + (_a = $$props.onCancel) == null ? void 0 : _a.call($$props); + } + function handleKeydown(e) { + if (e.key === "Escape") { + e.preventDefault(); + e.stopPropagation(); + if (get(running)) return; + close(); + } + } + var fragment = comment(); + var node_1 = first_child(fragment); + { + var consequent_21 = ($$anchor2) => { + var div = root_1$7(); + let styles; + var div_1 = child(div); + var div_2 = child(div_1); + var span = child(div_2); + var text_1 = child(span); + var node_2 = sibling(span, 2); + { + var consequent = ($$anchor3) => { + var button = root_2$7(); + delegated("click", button, close); + append($$anchor3, button); + }; + if_block(node_2, ($$render) => { + if (!get(running)) $$render(consequent); + }); + } + var div_3 = sibling(div_2, 2); + var div_4 = child(div_3); + var div_5 = child(div_4); + var div_6 = child(div_5); + var div_7 = child(div_6); + let classes; + var div_8 = sibling(div_7, 2); + let classes_1; + var div_9 = sibling(div_6, 2); + var button_1 = child(div_9); + var node_3 = sibling(button_1, 2); + { + var consequent_1 = ($$anchor3) => { + var button_2 = root_3$7(); + let classes_2; + template_effect(() => { + classes_2 = set_class(button_2, 1, "pcr-ip-tool-btn svelte-uprazu", null, classes_2, { on: get(compareSplit) }); + button_2.disabled = !get(outputUrl) || get(running); + }); + delegated("click", button_2, toggleCompare); + append($$anchor3, button_2); + }; + if_block(node_3, ($$render) => { + if (get(activeTab) === "output") $$render(consequent_1); + }); + } + var div_10 = sibling(div_5, 2); + var node_4 = child(div_10); + { + var consequent_2 = ($$anchor3) => { + var div_11 = root_4$7(); + var img = child(div_11); + template_effect(() => { + set_style(div_11, `transform: translate(${get(panX) ?? ""}px, ${get(panY) ?? ""}px) scale(${get(zoom) ?? ""});`); + set_attribute(img, "src", sourceUrl()); + set_attribute(img, "width", width()); + set_attribute(img, "height", height()); + }); + append($$anchor3, div_11); + }; + var alternate_1 = ($$anchor3) => { + var fragment_1 = root_5$6(); + var node_5 = first_child(fragment_1); + { + var consequent_3 = ($$anchor4) => { + var div_12 = root_6$6(); + var img_1 = child(div_12); + template_effect(() => set_attribute(img_1, "src", get(progress).previewUrl)); + append($$anchor4, div_12); + }; + var consequent_5 = ($$anchor4) => { + var fragment_2 = root_7$6(); + var div_13 = first_child(fragment_2); + var img_2 = child(div_13); + var node_6 = sibling(div_13, 2); + { + var consequent_4 = ($$anchor5) => { + var fragment_3 = root_8$6(); + var div_14 = first_child(fragment_3); + var div_15 = child(div_14); + var img_3 = child(div_15); + var div_16 = sibling(div_14, 6); + template_effect(() => { + set_style(div_14, `clip-path: inset(0 calc(100% - ${get(splitX) ?? ""}px) 0 0);`); + set_style(div_15, `transform: translate(${get(panX) ?? ""}px, ${get(panY) ?? ""}px) scale(${get(zoom) ?? ""});`); + set_attribute(img_3, "src", sourceUrl()); + set_attribute(img_3, "width", width()); + set_attribute(img_3, "height", height()); + set_style(div_16, `left: ${get(splitX) ?? ""}px;`); + }); + delegated("pointerdown", div_16, onSplitDown); + delegated("pointermove", div_16, onSplitMove); + delegated("pointerup", div_16, onSplitUp); + event("pointercancel", div_16, onSplitUp); + append($$anchor5, fragment_3); + }; + if_block(node_6, ($$render) => { + if (get(compareSplit)) $$render(consequent_4); + }); + } + template_effect(() => { + set_style(div_13, `transform: translate(${get(panX) ?? ""}px, ${get(panY) ?? ""}px) scale(${get(zoom) ?? ""});`); + set_attribute(img_2, "src", get(outputUrl)); + set_attribute(img_2, "width", width()); + set_attribute(img_2, "height", height()); + }); + append($$anchor4, fragment_2); + }; + var alternate = ($$anchor4) => { + var div_17 = root_9$5(); + var div_18 = child(div_17); + var text_2 = child(div_18); + template_effect(() => set_text(text_2, get(running) ? "Waiting for the sampler…" : "No output yet — set a denoise and Apply.")); + append($$anchor4, div_17); + }; + if_block(node_5, ($$render) => { + var _a; + if (get(running) && ((_a = get(progress)) == null ? void 0 : _a.previewUrl)) $$render(consequent_3); + else if (get(outputUrl)) $$render(consequent_5, 1); + else $$render(alternate, -1); + }); + } + var node_7 = sibling(node_5, 2); + { + var consequent_7 = ($$anchor4) => { + var div_19 = root_10$5(); + var div_20 = child(div_19); + var div_21 = child(div_20); + let classes_3; + var node_8 = sibling(div_20, 2); + { + var consequent_6 = ($$anchor5) => { + var span_1 = root_11$4(); + var text_3 = child(span_1); + template_effect(() => set_text(text_3, `step ${get(progress).value ?? ""}/${get(progress).max ?? ""}`)); + append($$anchor5, span_1); + }; + if_block(node_8, ($$render) => { + if (get(progressPercent) != null) $$render(consequent_6); + }); + } + template_effect(() => { + classes_3 = set_class(div_21, 1, "pcr-ip-bar-fill svelte-uprazu", null, classes_3, { indeterminate: get(progressPercent) == null }); + set_style(div_21, get(progressPercent) != null ? `width: ${get(progressPercent)}%` : ""); + }); + append($$anchor4, div_19); + }; + if_block(node_7, ($$render) => { + if (get(running)) $$render(consequent_7); + }); + } + append($$anchor3, fragment_1); + }; + if_block(node_4, ($$render) => { + if (get(activeTab) === "source") $$render(consequent_2); + else $$render(alternate_1, -1); + }); + } + bind_this(div_10, ($$value) => viewportEl = $$value, () => viewportEl); + var div_22 = sibling(div_4, 2); + var node_9 = child(div_22); + { + var consequent_13 = ($$anchor3) => { + var div_23 = root_12$5(); + var node_10 = sibling(child(div_23), 2); + SearchableSelect(node_10, { + id: "pcr-i2i-engine", + get value() { + return get(engineSel); + }, + get groups() { + return get(engineSelectGroups); + }, + popupKey: "i2i-engine", + get disabled() { + return get(running); + }, + onpick: (v) => pickEngine(v) + }); + var node_11 = sibling(node_10, 2); + { + var consequent_8 = ($$anchor4) => { + var div_24 = root_13$5(); + append($$anchor4, div_24); + }; + var consequent_9 = ($$anchor4) => { + var div_25 = root_14$5(); + append($$anchor4, div_25); + }; + var consequent_10 = ($$anchor4) => { + var div_26 = root_15$4(); + append($$anchor4, div_26); + }; + var consequent_11 = ($$anchor4) => { + var div_27 = root_16$4(); + append($$anchor4, div_27); + }; + if_block(node_11, ($$render) => { + if (get(engineKind) === "source") $$render(consequent_8); + else if (get(engineKind) === "sdxl") $$render(consequent_9, 1); + else if (get(engineKind) === "flux1") $$render(consequent_10, 2); + else if (get(engineKind) === "krea2") $$render(consequent_11, 3); + }); + } + var node_12 = sibling(node_11, 2); + { + var consequent_12 = ($$anchor4) => { + var label = root_17$3(); + var input = child(label); + bind_checked(input, () => get(realism), ($$value) => set(realism, $$value)); + append($$anchor4, label); + }; + if_block(node_12, ($$render) => { + if (get(realismAvailable)) $$render(consequent_12); + }); + } + append($$anchor3, div_23); + }; + if_block(node_9, ($$render) => { + var _a, _b; + if ((_b = (_a = caps()) == null ? void 0 : _a.engineModels) == null ? void 0 : _b.length) $$render(consequent_13); + }); + } + var div_28 = sibling(node_9, 2); + var node_13 = sibling(child(div_28), 2); + key(node_13, () => get(promptSeedKey), ($$anchor3) => { + var fragment_4 = comment(); + var node_14 = first_child(fragment_4); + { + var consequent_14 = ($$anchor4) => { + var div_29 = root_19$3(); + action(div_29, ($$node) => promptEditor == null ? void 0 : promptEditor($$node)); + append($$anchor4, div_29); + }; + var alternate_2 = ($$anchor4) => { + var textarea = root_20$2(); + set_attribute(textarea, "placeholder", "what the whole image should become\n\nNegative Prompt:\noptional"); + bind_value(textarea, () => get(prompt), ($$value) => set(prompt, $$value)); + append($$anchor4, textarea); + }; + if_block(node_14, ($$render) => { + if (mountPromptEditor()) $$render(consequent_14); + else $$render(alternate_2, -1); + }); + } + append($$anchor3, fragment_4); + }); + var node_15 = sibling(node_13, 2); + { + var consequent_15 = ($$anchor3) => { + var fragment_5 = root_21$2(); + var textarea_1 = sibling(first_child(fragment_5), 2); + template_effect(() => set_value(textarea_1, referencePrompt())); + append($$anchor3, fragment_5); + }; + if_block(node_15, ($$render) => { + if (referencePrompt()) $$render(consequent_15); + }); + } + var div_30 = sibling(div_28, 2); + var div_31 = sibling(child(div_30), 2); + var node_16 = sibling(child(div_31), 2); + { + let $0 = user_derived(() => engineRecipe(get(engineEntry)).denoise); + SettingsSlider(node_16, { + min: 0.05, + max: 1, + step: 0.01, + get value() { + return get(denoise); + }, + get savedValue() { + return get($0); + }, + onChange: (v) => { + set(denoise, v, true); + } + }); + } + var node_17 = sibling(div_31, 2); + { + var consequent_16 = ($$anchor3) => { + var div_32 = root_22$1(); + var div_33 = child(div_32); + var select = sibling(child(div_33), 2); + let classes_4; + each(select, 21, () => caps().samplerOptions, index, ($$anchor4, opt) => { + var option = root_23$2(); + let styles_1; + var text_4 = child(option); + var option_value = {}; + template_effect(() => { + styles_1 = set_style(option, "", styles_1, { color: get(opt) === get(recSampler) ? "#5ed357" : "#999" }); + set_text(text_4, `${get(opt) ?? ""}${get(opt) === get(recSampler) ? " ●" : ""}`); + if (option_value !== (option_value = get(opt))) { + option.value = (option.__value = get(opt)) ?? ""; + } + }); + append($$anchor4, option); + }); + var div_34 = sibling(div_33, 2); + var select_1 = sibling(child(div_34), 2); + let classes_5; + each(select_1, 21, () => caps().schedulerOptions, index, ($$anchor4, opt) => { + var option_1 = root_24$2(); + let styles_2; + var text_5 = child(option_1); + var option_1_value = {}; + template_effect(() => { + styles_2 = set_style(option_1, "", styles_2, { + color: get(opt) === get(recScheduler) ? "#5ed357" : "#999" + }); + set_text(text_5, `${get(opt) ?? ""}${get(opt) === get(recScheduler) ? " ●" : ""}`); + if (option_1_value !== (option_1_value = get(opt))) { + option_1.value = (option_1.__value = get(opt)) ?? ""; + } + }); + append($$anchor4, option_1); + }); + template_effect(() => { + classes_4 = set_class(select, 1, "pcr-ip-select svelte-uprazu", null, classes_4, { "at-rec": get(sampler) === get(recSampler) }); + classes_5 = set_class(select_1, 1, "pcr-ip-select svelte-uprazu", null, classes_5, { "at-rec": get(scheduler) === get(recScheduler) }); + }); + bind_select_value(select, () => get(sampler), ($$value) => set(sampler, $$value)); + bind_select_value(select_1, () => get(scheduler), ($$value) => set(scheduler, $$value)); + append($$anchor3, div_32); + }; + if_block(node_17, ($$render) => { + var _a, _b; + if ((_b = (_a = caps()) == null ? void 0 : _a.samplerOptions) == null ? void 0 : _b.length) $$render(consequent_16); + }); + } + var div_35 = sibling(node_17, 2); + var text_6 = child(div_35); + var node_18 = sibling(div_35, 2); + { + var consequent_17 = ($$anchor3) => { + var fragment_6 = root_25$2(); + var div_36 = first_child(fragment_6); + var node_19 = sibling(child(div_36), 2); + SettingsSlider(node_19, { + min: 0, + max: 60, + step: 1, + get value() { + return get(steps); + }, + get savedValue() { + return get(recSteps); + }, + onChange: (v) => { + set(steps, v, true); + } + }); + var div_37 = sibling(div_36, 2); + var node_20 = sibling(child(div_37), 2); + SettingsSlider(node_20, { + min: 0, + max: 10, + step: 0.1, + get value() { + return get(cfg); + }, + get savedValue() { + return get(recCfg); + }, + onChange: (v) => { + set(cfg, v, true); + } + }); + append($$anchor3, fragment_6); + }; + if_block(node_18, ($$render) => { + if (get(advancedOpen)) $$render(consequent_17); + }); + } + var node_21 = sibling(div_3, 2); + { + var consequent_18 = ($$anchor3) => { + var div_38 = root_26$2(); + var text_7 = child(div_38); + template_effect(() => set_text(text_7, `⚠ ${get(errorMsg) ?? ""}`)); + append($$anchor3, div_38); + }; + if_block(node_21, ($$render) => { + if (get(errorMsg)) $$render(consequent_18); + }); + } + var div_39 = sibling(node_21, 2); + var node_22 = child(div_39); + { + var consequent_19 = ($$anchor3) => { + var button_3 = root_27$2(); + delegated("click", button_3, stopRun); + append($$anchor3, button_3); + }; + var alternate_3 = ($$anchor3) => { + var fragment_7 = root_28$1(); + var button_4 = first_child(fragment_7); + var button_5 = sibling(button_4, 2); + var text_8 = child(button_5); + var node_23 = sibling(button_5, 2); + { + var consequent_20 = ($$anchor4) => { + var button_6 = root_29$2(); + template_effect(() => button_6.disabled = !get(doneState)); + delegated("click", button_6, () => onUseInEdit()(get(doneState))); + append($$anchor4, button_6); + }; + if_block(node_23, ($$render) => { + if (onUseInEdit()) $$render(consequent_20); + }); + } + template_effect(() => { + button_5.disabled = get(noEngine); + set_attribute(button_5, "title", get(noEngine) ? "This image has no render data to re-diffuse and no compatible engine model is installed." : ""); + set_text(text_8, get(doneState) ? "Re-Apply" : "Apply"); + }); + delegated("click", button_4, close); + delegated("click", button_5, apply); + append($$anchor3, fragment_7); + }; + if_block(node_22, ($$render) => { + if (get(running)) $$render(consequent_19); + else $$render(alternate_3, -1); + }); + } + action(div, ($$node) => { + var _a; + return (_a = portal) == null ? void 0 : _a($$node); + }); + template_effect(() => { + styles = set_style(div, "", styles, { "z-index": elevated() ? 10006 : null }); + set_text(text_1, `${get(running) ? "i2i Blend…" : "i2i Blend"}${filename() ? ` — ${filename()}` : ""}${width() && height() ? ` · ${width()}×${height()}` : ""}`); + classes = set_class(div_7, 1, "pcr-ip-tab svelte-uprazu", null, classes, { active: get(activeTab) === "source" }); + classes_1 = set_class(div_8, 1, "pcr-ip-tab svelte-uprazu", null, classes_1, { active: get(activeTab) === "output" }); + set_text(text_6, `${get(advancedOpen) ? "▾" : "▸"} Advanced`); + }); + delegated("keydown", div, handleKeydown); + delegated("click", div_7, () => { + if (!get(running)) set(activeTab, "source"); + }); + delegated("click", div_8, () => { + set(activeTab, "output"); + }); + delegated("click", button_1, resetView); + delegated("pointerdown", div_10, onPointerDown); + delegated("pointermove", div_10, onPointerMove); + delegated("pointerup", div_10, onPointerUp); + event("pointercancel", div_10, onPointerUp); + event("wheel", div_10, onWheel); + delegated("contextmenu", div_10, (e) => e.preventDefault()); + event("dragstart", div_10, (e) => e.preventDefault()); + delegated("click", div_35, () => { + set(advancedOpen, !get(advancedOpen)); + }); + append($$anchor2, div); + }; + if_block(node_1, ($$render) => { + if ($$props.open) $$render(consequent_21); + }); + } + append($$anchor, fragment); + pop(); +} +delegate([ + "keydown", + "click", + "pointerdown", + "pointermove", + "pointerup", + "contextmenu" +]); var root_4$6 = from_html(`
`); var root_3$6 = from_html(`
`); var root_2$6 = from_html(`
`); @@ -5127,35 +6065,36 @@ var root_45$1 = from_html(`
var root_46$1 = from_html(`
drag / stretch · Enter applies · Esc cancels
`); var root_47$1 = from_html(`
`); var root_49$1 = from_html(``); -var root_50$1 = from_html(``); -var root_51$1 = from_html(`
`); -var root_48$1 = from_html(`
`); -var root_52$1 = from_html(` `); -var root_54$1 = from_html(``); -var root_56$1 = from_html(``); -var root_58$1 = from_html(`▦`); -var root_59$1 = from_html(` `); -var root_57$1 = from_html(` `); -var root_55$1 = from_html(`
⠿
`); -var root_53$1 = from_html(`
`, 1); -var root_61$1 = from_html(`
`); -var root_60$1 = from_html(`
`); -var root_63$1 = from_html(``); -var root_64$1 = from_html(``); -var root_66$1 = from_html(``); -var root_67$1 = from_html(` `, 1); -var root_68 = from_html(``); -var root_69 = from_html(``); -var root_62$1 = from_html(`
`); -var root_71$1 = from_html(` `, 1); -var root_72$1 = from_html(` `, 1); -var root_70$1 = from_html(`
`); -var root_73$1 = from_html(`
`); -var root_74$1 = from_html(`
`); -var root_75$1 = from_html(`
⏳ Selecting subject…
`); -var root_76$1 = from_html(`
Alt-click a clean area of the image to define a source point, then paint.
`); -var root_77$1 = from_html(`
`); -var root_78$1 = from_html(`✓ Edits saved`); +var root_50$1 = from_html(``); +var root_51$1 = from_html(``); +var root_52$1 = from_html(``); +var root_48$1 = from_html(`
`); +var root_53$1 = from_html(` `); +var root_55$1 = from_html(``); +var root_57$1 = from_html(``); +var root_59$1 = from_html(`▦`); +var root_60$1 = from_html(` `); +var root_58$1 = from_html(` `); +var root_56$1 = from_html(`
⠿
`); +var root_54$1 = from_html(`
`, 1); +var root_62$1 = from_html(`
`); +var root_61$1 = from_html(`
`); +var root_64$1 = from_html(``); +var root_65$1 = from_html(``); +var root_67$1 = from_html(``); +var root_68 = from_html(` `, 1); +var root_69 = from_html(``); +var root_70$1 = from_html(``); +var root_63$1 = from_html(`
`); +var root_72$1 = from_html(` `, 1); +var root_73$1 = from_html(` `, 1); +var root_71$1 = from_html(`
`); +var root_74$1 = from_html(`
`); +var root_75$1 = from_html(`
`); +var root_76$1 = from_html(`
⏳ Selecting subject…
`); +var root_77$1 = from_html(`
Alt-click a clean area of the image to define a source point, then paint.
`); +var root_78$1 = from_html(`
`); +var root_79$1 = from_html(`✓ Edits saved`); var root_1$5 = from_html(`
Editing:
Navigator
`); function EditModal($$anchor, $$props) { push($$props, true); @@ -5191,6 +6130,12 @@ function EditModal($$anchor, $$props) { 3, null // ({compositeBlob}) => void — opens the Re-pose modal on the whole composite + ), onOpenI2i = prop( + $$props, + "onOpenI2i", + 3, + null + // ({compositeBlob, width, height}) => Promise — opens the i2i modal on the /16-snapped composite ), figureRegions = prop($$props, "figureRegions", 19, () => []), editDocHash = prop( $$props, "editDocHash", @@ -5226,7 +6171,7 @@ function EditModal($$anchor, $$props) { ); let handoffBusy = state( null - // "inpaint" | "upscale" | "repose" while the handoff modal prepares (uploads/graph build) + // "inpaint" | "upscale" | "repose" | "i2i" while the handoff modal prepares (uploads/graph build) ); async function openReposeHandoff() { if (!onOpenRepose()) return; @@ -5241,6 +6186,27 @@ function EditModal($$anchor, $$props) { set(handoffBusy, null); } } + async function openI2iHandoff() { + if (!onOpenI2i()) return; + set(errorMsg, ""); + flushActive(); + const encW = Math.max(16, Math.floor(get(width) / 16) * 16); + const encH = Math.max(16, Math.floor(get(height) / 16) * 16); + const ox = get(width) - encW >> 1, oy = get(height) - encH >> 1; + const c = document.createElement("canvas"); + c.width = encW; + c.height = encH; + drawFlattened(c.getContext("2d"), ox, oy, encW, encH, 0, 0, encW, encH); + const compositeBlob = await new Promise((r) => c.toBlob(r, "image/png")); + set(handoffBusy, "i2i"); + try { + await onOpenI2i()({ compositeBlob, width: encW, height: encH }); + } catch (e) { + set(errorMsg, `Couldn't open i2i: ${(e == null ? void 0 : e.message) || e}`); + } finally { + set(handoffBusy, null); + } + } let viewportEl; let imgEl; let paintCanvas; @@ -6661,6 +7627,14 @@ function EditModal($$anchor, $$props) { c.getContext("2d").drawImage(img, 0, 0, get(width), get(height)); insertResultLayer(c, finalMask || inpaintMaskUsed, "Inpaint"); } + async function addI2iLayerFromResult(url) { + const img = await loadImageEl(url); + const c = document.createElement("canvas"); + c.width = get(width); + c.height = get(height); + c.getContext("2d").drawImage(img, 0, 0, get(width), get(height)); + insertResultLayer(c, null, "i2i"); + } function maskBBox(maskCanvas) { const d = maskCanvas.getContext("2d").getImageData(0, 0, get(width), get(height)).data; let minX = get(width), minY = get(height), maxX = -1, maxY = -1; @@ -8176,6 +9150,7 @@ function EditModal($$anchor, $$props) { let strokeCov = null; let sourceData = null; let preStroke = null; + let selStrokeData = null; let batchBox = null; let strokeBox = null; let falloffLUT = { key: "", table: null }; @@ -8386,6 +9361,7 @@ function EditModal($$anchor, $$props) { } sourceData = backdropCanvas().getContext("2d").getImageData(0, 0, get(width), get(height)); preStroke = paintCanvas.getContext("2d").getImageData(0, 0, get(width), get(height)); + selStrokeData = get(selActive) && selMaskCanvas ? selMaskCanvas.getContext("2d").getImageData(0, 0, get(width), get(height)).data : null; batchBox = null; strokeBox = null; } @@ -8451,7 +9427,8 @@ function EditModal($$anchor, $$props) { const row = (y0 + y) * get(width) + x0; for (let x = 0; x < bw; x++) { const gi = row + x; - const cov = Math.min(strokeCov[gi], ceil); + let cov = Math.min(strokeCov[gi], ceil); + if (selStrokeData) cov *= selStrokeData[gi * 4 + 3] / 255; const i = (y * bw + x) * 4; const j = gi * 4; if (cov <= 0) { @@ -9727,13 +10704,14 @@ function EditModal($$anchor, $$props) { } var $$exports = { addLayerFromResult, + addI2iLayerFromResult, addUpscaledLayerFromResult, applyCanvasUpscale }; var fragment = comment(); var node = first_child(fragment); { - var consequent_60 = ($$anchor2) => { + var consequent_61 = ($$anchor2) => { var div = root_1$5(); var div_1 = child(div); let classes; @@ -9778,7 +10756,7 @@ function EditModal($$anchor, $$props) { var div_5 = child(div_4); var span_3 = sibling(child(div_5), 2); var text_2 = child(span_3); - template_effect(() => set_text(text_2, get(restoring) ? "Restoring saved layers…" : get(handoffBusy) === "upscale" ? "Preparing upscale…" : get(handoffBusy) === "repose" ? "Preparing Re-pose…" : "Preparing inpaint…")); + template_effect(() => set_text(text_2, get(restoring) ? "Restoring saved layers…" : get(handoffBusy) === "upscale" ? "Preparing upscale…" : get(handoffBusy) === "repose" ? "Preparing Re-pose…" : get(handoffBusy) === "i2i" ? "Preparing i2i…" : "Preparing inpaint…")); append($$anchor3, div_4); }; if_block(node_3, ($$render) => { @@ -10667,7 +11645,7 @@ function EditModal($$anchor, $$props) { var button_26 = sibling(input, 2); var node_50 = sibling(div_61, 2); { - var consequent_38 = ($$anchor3) => { + var consequent_39 = ($$anchor3) => { var div_66 = root_48$1(); var span_14 = child(div_66); var text_18 = child(span_14); @@ -10691,81 +11669,92 @@ function EditModal($$anchor, $$props) { { var consequent_36 = ($$anchor4) => { var button_28 = root_50$1(); - template_effect(() => { - button_28.disabled = !!get(handoffBusy); - set_attribute(button_28, "title", get(selActive) ? "Upscale the selected region at the quality you pick." : "Upscale the active layer's content at the quality you pick — grow the canvas to keep it 1:1, or squeeze it back in place."); - }); - delegated("click", button_28, openUpscaleHandoff); + template_effect(() => button_28.disabled = !!get(handoffBusy)); + delegated("click", button_28, openI2iHandoff); append($$anchor4, button_28); }; if_block(node_52, ($$render) => { - if (onOpenUpscale()) $$render(consequent_36); + if (onOpenI2i()) $$render(consequent_36); }); } - var node_53 = sibling(div_67, 2); + var node_53 = sibling(node_52, 2); { var consequent_37 = ($$anchor4) => { - var div_68 = root_51$1(); - var button_29 = child(div_68); - template_effect(() => button_29.disabled = !!get(handoffBusy)); - delegated("click", button_29, openReposeHandoff); - append($$anchor4, div_68); + var button_29 = root_51$1(); + template_effect(() => { + button_29.disabled = !!get(handoffBusy); + set_attribute(button_29, "title", get(selActive) ? "Upscale the selected region at the quality you pick." : "Upscale the active layer's content at the quality you pick — grow the canvas to keep it 1:1, or squeeze it back in place."); + }); + delegated("click", button_29, openUpscaleHandoff); + append($$anchor4, button_29); }; if_block(node_53, ($$render) => { - if (onOpenRepose()) $$render(consequent_37); + if (onOpenUpscale()) $$render(consequent_37); + }); + } + var node_54 = sibling(node_53, 2); + { + var consequent_38 = ($$anchor4) => { + var button_30 = root_52$1(); + template_effect(() => button_30.disabled = !!get(handoffBusy)); + delegated("click", button_30, openReposeHandoff); + append($$anchor4, button_30); + }; + if_block(node_54, ($$render) => { + if (onOpenRepose()) $$render(consequent_38); }); } template_effect(() => set_text(text_18, `Render ${get(selActive) ? "selection" : "layer"}`)); append($$anchor3, div_66); }; if_block(node_50, ($$render) => { - if (onOpenInpaint() || onOpenUpscale() || onOpenRepose()) $$render(consequent_38); + if (onOpenInpaint() || onOpenUpscale() || onOpenRepose() || onOpenI2i()) $$render(consequent_39); }); } - var div_69 = sibling(node_50, 2); - var div_70 = child(div_69); - var span_15 = child(div_70); - var button_30 = child(span_15); + var div_68 = sibling(node_50, 2); + var div_69 = child(div_68); + var span_15 = child(div_69); + var button_31 = child(span_15); let classes_8; - var button_31 = sibling(button_30, 2); + var button_32 = sibling(button_31, 2); let classes_9; - var node_54 = sibling(span_15, 2); + var node_55 = sibling(span_15, 2); { - var consequent_39 = ($$anchor3) => { - var span_16 = root_52$1(); - var button_32 = child(span_16); - var button_33 = sibling(button_32, 2); + var consequent_40 = ($$anchor3) => { + var span_16 = root_53$1(); + var button_33 = child(span_16); var button_34 = sibling(button_33, 2); var button_35 = sibling(button_34, 2); + var button_36 = sibling(button_35, 2); template_effect( ($0) => { - button_33.disabled = get(selectedIds).length < 2; - button_34.disabled = get(layers).length <= 1; - set_attribute(button_35, "title", get(selectedIds).length > 1 ? "Delete selected layers" : "Delete layer"); - button_35.disabled = $0; + button_34.disabled = get(selectedIds).length < 2; + button_35.disabled = get(layers).length <= 1; + set_attribute(button_36, "title", get(selectedIds).length > 1 ? "Delete selected layers" : "Delete layer"); + button_36.disabled = $0; }, [() => !canDeleteSelected()] ); - delegated("click", button_32, () => addLayer()); - delegated("click", button_33, mergeSelected); - delegated("click", button_34, flattenImage); - delegated("click", button_35, deleteSelectedLayers); + delegated("click", button_33, () => addLayer()); + delegated("click", button_34, mergeSelected); + delegated("click", button_35, flattenImage); + delegated("click", button_36, deleteSelectedLayers); append($$anchor3, span_16); }; - if_block(node_54, ($$render) => { - if (get(lhTab) === "layers") $$render(consequent_39); + if_block(node_55, ($$render) => { + if (get(lhTab) === "layers") $$render(consequent_40); }); } - var node_55 = sibling(div_70, 2); + var node_56 = sibling(div_69, 2); { - var consequent_43 = ($$anchor3) => { - var fragment_16 = root_53$1(); + var consequent_44 = ($$anchor3) => { + var fragment_16 = root_54$1(); var select_2 = first_child(fragment_16); each(select_2, 21, () => BLEND_MODES, ([v, label]) => v, ($$anchor4, $$item) => { var $$array_2 = user_derived(() => to_array(get($$item), 2)); let v = () => get($$array_2)[0]; let label = () => get($$array_2)[1]; - var option_4 = root_54$1(); + var option_4 = root_55$1(); var text_19 = child(option_4); var option_4_value = {}; template_effect(() => { @@ -10778,20 +11767,20 @@ function EditModal($$anchor, $$props) { }); var select_2_value; init_select(select_2); - var div_71 = sibling(select_2, 2); - each(div_71, 21, () => [...get(layers)].reverse(), (L) => L.id, ($$anchor4, L) => { + var div_70 = sibling(select_2, 2); + each(div_70, 21, () => [...get(layers)].reverse(), (L) => L.id, ($$anchor4, L) => { const i = user_derived(() => get(layers).indexOf(get(L))); - var div_72 = root_55$1(); + var div_71 = root_56$1(); let classes_10; - var span_17 = child(div_72); + var span_17 = child(div_71); let classes_11; var span_18 = sibling(span_17, 2); let classes_12; var text_20 = child(span_18); - var node_56 = sibling(span_18, 2); + var node_57 = sibling(span_18, 2); { - var consequent_40 = ($$anchor5) => { - var input_1 = root_56$1(); + var consequent_41 = ($$anchor5) => { + var input_1 = root_57$1(); autofocus(input_1); delegated("click", input_1, (e) => e.stopPropagation()); delegated("keydown", input_1, (e) => { @@ -10804,29 +11793,29 @@ function EditModal($$anchor, $$props) { append($$anchor5, input_1); }; var alternate_3 = ($$anchor5) => { - var span_19 = root_57$1(); + var span_19 = root_58$1(); var text_21 = child(span_19); - var node_57 = sibling(text_21); - { - var consequent_41 = ($$anchor6) => { - var span_20 = root_58$1(); - append($$anchor6, span_20); - }; - if_block(node_57, ($$render) => { - if (get(L).mask) $$render(consequent_41); - }); - } - var node_58 = sibling(node_57); + var node_58 = sibling(text_21); { var consequent_42 = ($$anchor6) => { - var span_21 = root_59$1(); + var span_20 = root_59$1(); + append($$anchor6, span_20); + }; + if_block(node_58, ($$render) => { + if (get(L).mask) $$render(consequent_42); + }); + } + var node_59 = sibling(node_58); + { + var consequent_43 = ($$anchor6) => { + var span_21 = root_60$1(); var text_22 = child(span_21); template_effect(() => set_text(text_22, get(L).blend)); append($$anchor6, span_21); }; var d_1 = user_derived(() => layerBlendCss(get(L))); - if_block(node_58, ($$render) => { - if (get(d_1)) $$render(consequent_42); + if_block(node_59, ($$render) => { + if (get(d_1)) $$render(consequent_43); }); } template_effect(() => set_text(text_21, get(L).name)); @@ -10836,15 +11825,15 @@ function EditModal($$anchor, $$props) { }); append($$anchor5, span_19); }; - if_block(node_56, ($$render) => { - if (get(renamingId) === get(L).id) $$render(consequent_40); + if_block(node_57, ($$render) => { + if (get(renamingId) === get(L).id) $$render(consequent_41); else $$render(alternate_3, -1); }); } - var input_2 = sibling(node_56, 2); + var input_2 = sibling(node_57, 2); template_effect( ($0) => { - classes_10 = set_class(div_72, 1, "pcr-ed-layer-row svelte-1ozzqkm", null, classes_10, $0); + classes_10 = set_class(div_71, 1, "pcr-ed-layer-row svelte-1ozzqkm", null, classes_10, $0); classes_11 = set_class(span_17, 1, "pcr-ed-layer-grip svelte-1ozzqkm", null, classes_11, { hidden: get(L).isBackground }); set_attribute(span_17, "draggable", !get(L).isBackground); classes_12 = set_class(span_18, 1, "pcr-ed-eye svelte-1ozzqkm", null, classes_12, { off: !get(L).visible }); @@ -10859,21 +11848,21 @@ function EditModal($$anchor, $$props) { }) ] ); - delegated("click", div_72, (e) => clickLayer(get(i), e)); - delegated("contextmenu", div_72, (e) => { + delegated("click", div_71, (e) => clickLayer(get(i), e)); + delegated("contextmenu", div_71, (e) => { e.preventDefault(); if (!get(selectedIds).includes(get(L).id)) clickLayer(get(i), e); set(layerMenu, { x: e.clientX, y: e.clientY, i: get(i) }, true); }); - event("dragover", div_72, (e) => { + event("dragover", div_71, (e) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; set(dragOverId, get(L).id, true); }); - event("dragleave", div_72, () => { + event("dragleave", div_71, () => { if (get(dragOverId) === get(L).id) set(dragOverId, null); }); - event("drop", div_72, (e) => { + event("drop", div_71, (e) => { e.preventDefault(); dropLayer(get(L).id); }); @@ -10899,7 +11888,7 @@ function EditModal($$anchor, $$props) { opacityDragStart = null; }); delegated("click", input_2, (e) => e.stopPropagation()); - append($$anchor4, div_72); + append($$anchor4, div_71); }); template_effect( ($0, $1) => { @@ -10923,257 +11912,257 @@ function EditModal($$anchor, $$props) { append($$anchor3, fragment_16); }; var alternate_4 = ($$anchor3) => { - var div_73 = root_60$1(); - each(div_73, 21, () => get(history2), index, ($$anchor4, h, i) => { - var div_74 = root_61$1(); + var div_72 = root_61$1(); + each(div_72, 21, () => get(history2), index, ($$anchor4, h, i) => { + var div_73 = root_62$1(); let classes_13; - var text_23 = child(div_74); + var text_23 = child(div_73); template_effect(() => { - classes_13 = set_class(div_74, 1, "pcr-ed-hist-row svelte-1ozzqkm", null, classes_13, { active: i === get(histIndex), future: i > get(histIndex) }); + classes_13 = set_class(div_73, 1, "pcr-ed-hist-row svelte-1ozzqkm", null, classes_13, { active: i === get(histIndex), future: i > get(histIndex) }); set_text(text_23, get(h).label); }); - delegated("click", div_74, () => jumpTo(i)); - append($$anchor4, div_74); + delegated("click", div_73, () => jumpTo(i)); + append($$anchor4, div_73); }); - bind_this(div_73, ($$value) => histEl = $$value, () => histEl); - append($$anchor3, div_73); + bind_this(div_72, ($$value) => histEl = $$value, () => histEl); + append($$anchor3, div_72); }; - if_block(node_55, ($$render) => { - if (get(lhTab) === "layers") $$render(consequent_43); + if_block(node_56, ($$render) => { + if (get(lhTab) === "layers") $$render(consequent_44); else $$render(alternate_4, -1); }); } - var node_59 = sibling(div_69, 2); + var node_60 = sibling(div_68, 2); { - var consequent_50 = ($$anchor3) => { - var div_75 = root_62$1(); - var button_36 = child(div_75); - var node_60 = sibling(button_36, 2); + var consequent_51 = ($$anchor3) => { + var div_74 = root_63$1(); + var button_37 = child(div_74); + var node_61 = sibling(button_37, 2); { - var consequent_44 = ($$anchor4) => { - var button_37 = root_63$1(); - delegated("click", button_37, () => { - mergeSelected(); - set(layerMenu, null); - }); - append($$anchor4, button_37); - }; var consequent_45 = ($$anchor4) => { var button_38 = root_64$1(); delegated("click", button_38, () => { - mergeDown(get(layerMenu).i); + mergeSelected(); set(layerMenu, null); }); append($$anchor4, button_38); }; - if_block(node_60, ($$render) => { - if (get(selectedIds).length > 1) $$render(consequent_44); - else if (get(layerMenu).i > 0) $$render(consequent_45, 1); + var consequent_46 = ($$anchor4) => { + var button_39 = root_65$1(); + delegated("click", button_39, () => { + mergeDown(get(layerMenu).i); + set(layerMenu, null); + }); + append($$anchor4, button_39); + }; + if_block(node_61, ($$render) => { + if (get(selectedIds).length > 1) $$render(consequent_45); + else if (get(layerMenu).i > 0) $$render(consequent_46, 1); }); } - var button_39 = sibling(node_60, 2); - var node_61 = sibling(button_39, 2); + var button_40 = sibling(node_61, 2); + var node_62 = sibling(button_40, 2); { - var consequent_47 = ($$anchor4) => { + var consequent_48 = ($$anchor4) => { var fragment_17 = comment(); - var node_62 = first_child(fragment_17); + var node_63 = first_child(fragment_17); { - var consequent_46 = ($$anchor5) => { - var button_40 = root_66$1(); - var text_24 = child(button_40); + var consequent_47 = ($$anchor5) => { + var button_41 = root_67$1(); + var text_24 = child(button_41); template_effect(() => set_text(text_24, `Add Layer Mask${get(selActive) ? " (Reveal Selection)" : ""}`)); - delegated("click", button_40, () => { + delegated("click", button_41, () => { addLayerMask(get(layerMenu).i); set(layerMenu, null); }); - append($$anchor5, button_40); + append($$anchor5, button_41); }; var alternate_5 = ($$anchor5) => { - var fragment_18 = root_67$1(); - var button_41 = first_child(fragment_18); - var button_42 = sibling(button_41, 2); - delegated("click", button_41, () => { + var fragment_18 = root_68(); + var button_42 = first_child(fragment_18); + var button_43 = sibling(button_42, 2); + delegated("click", button_42, () => { applyLayerMask(get(layerMenu).i); set(layerMenu, null); }); - delegated("click", button_42, () => { + delegated("click", button_43, () => { deleteLayerMask(get(layerMenu).i); set(layerMenu, null); }); append($$anchor5, fragment_18); }; - if_block(node_62, ($$render) => { + if_block(node_63, ($$render) => { var _a; - if (!((_a = get(layers)[get(layerMenu).i]) == null ? void 0 : _a.mask)) $$render(consequent_46); + if (!((_a = get(layers)[get(layerMenu).i]) == null ? void 0 : _a.mask)) $$render(consequent_47); else $$render(alternate_5, -1); }); } append($$anchor4, fragment_17); }; - if_block(node_61, ($$render) => { + if_block(node_62, ($$render) => { var _a; - if (!((_a = get(layers)[get(layerMenu).i]) == null ? void 0 : _a.isBackground)) $$render(consequent_47); + if (!((_a = get(layers)[get(layerMenu).i]) == null ? void 0 : _a.isBackground)) $$render(consequent_48); }); } - var node_63 = sibling(node_61, 2); + var node_64 = sibling(node_62, 2); { - var consequent_48 = ($$anchor4) => { - var button_43 = root_68(); - delegated("click", button_43, () => { + var consequent_49 = ($$anchor4) => { + var button_44 = root_69(); + delegated("click", button_44, () => { const i = get(layerMenu).i; set(layerMenu, null); if (i === get(activeIndex)) flushActive(); if (matchLayerColorsToBelow(i)) commitAction("Match Colors"); else set(errorMsg, "No usable edge band — the layer's feathered edge must overlap solid content below."); }); - append($$anchor4, button_43); + append($$anchor4, button_44); }; - if_block(node_63, ($$render) => { + if_block(node_64, ($$render) => { var _a; - if (get(layerMenu).i > 0 && ((_a = get(layers)[get(layerMenu).i]) == null ? void 0 : _a.bitmap)) $$render(consequent_48); + if (get(layerMenu).i > 0 && ((_a = get(layers)[get(layerMenu).i]) == null ? void 0 : _a.bitmap)) $$render(consequent_49); }); } - var button_44 = sibling(node_63, 2); - var node_64 = sibling(button_44, 2); + var button_45 = sibling(node_64, 2); + var node_65 = sibling(button_45, 2); { - var consequent_49 = ($$anchor4) => { - var button_45 = root_69(); - var text_25 = child(button_45); + var consequent_50 = ($$anchor4) => { + var button_46 = root_70$1(); + var text_25 = child(button_46); template_effect(() => set_text(text_25, get(selectedIds).length > 1 ? "Delete Layers" : "Delete Layer")); - delegated("click", button_45, () => { + delegated("click", button_46, () => { deleteSelectedLayers(); set(layerMenu, null); }); - append($$anchor4, button_45); + append($$anchor4, button_46); }; var d_2 = user_derived(() => canDeleteSelected()); - if_block(node_64, ($$render) => { - if (get(d_2)) $$render(consequent_49); + if_block(node_65, ($$render) => { + if (get(d_2)) $$render(consequent_50); }); } - template_effect(() => set_style(div_75, `left:${get(layerMenu).x ?? ""}px; top:${get(layerMenu).y ?? ""}px;`)); - delegated("click", button_36, () => { + template_effect(() => set_style(div_74, `left:${get(layerMenu).x ?? ""}px; top:${get(layerMenu).y ?? ""}px;`)); + delegated("click", button_37, () => { addLayer(get(layerMenu).i); set(layerMenu, null); }); - delegated("click", button_39, () => { + delegated("click", button_40, () => { duplicateLayer(get(layerMenu).i); set(layerMenu, null); }); - delegated("click", button_44, () => { + delegated("click", button_45, () => { flattenImage(); set(layerMenu, null); }); - append($$anchor3, div_75); + append($$anchor3, div_74); }; - if_block(node_59, ($$render) => { - if (get(layerMenu)) $$render(consequent_50); + if_block(node_60, ($$render) => { + if (get(layerMenu)) $$render(consequent_51); }); } - var node_65 = sibling(node_59, 2); + var node_66 = sibling(node_60, 2); { - var consequent_53 = ($$anchor3) => { - var div_76 = root_70$1(); - var node_66 = child(div_76); + var consequent_54 = ($$anchor3) => { + var div_75 = root_71$1(); + var node_67 = child(div_75); { - var consequent_51 = ($$anchor4) => { - var fragment_19 = root_71$1(); - var button_46 = first_child(fragment_19); - var button_47 = sibling(button_46, 2); - delegated("click", button_46, () => { + var consequent_52 = ($$anchor4) => { + var fragment_19 = root_72$1(); + var button_47 = first_child(fragment_19); + var button_48 = sibling(button_47, 2); + delegated("click", button_47, () => { deselect(); set(canvasMenu, null); }); - delegated("click", button_47, () => { + delegated("click", button_48, () => { selectInverse(); set(canvasMenu, null); }); append($$anchor4, fragment_19); }; - if_block(node_66, ($$render) => { - if (get(selActive)) $$render(consequent_51); + if_block(node_67, ($$render) => { + if (get(selActive)) $$render(consequent_52); }); } - var button_48 = sibling(node_66, 2); - var text_26 = child(button_48); - var button_49 = sibling(button_48, 2); + var button_49 = sibling(node_67, 2); + var text_26 = child(button_49); var button_50 = sibling(button_49, 2); - var text_27 = child(button_50); - var node_67 = sibling(button_50, 2); + var button_51 = sibling(button_50, 2); + var text_27 = child(button_51); + var node_68 = sibling(button_51, 2); { - var consequent_52 = ($$anchor4) => { - var fragment_20 = root_72$1(); - var button_51 = first_child(fragment_20); - var button_52 = sibling(button_51, 2); - delegated("click", button_51, () => { + var consequent_53 = ($$anchor4) => { + var fragment_20 = root_73$1(); + var button_52 = first_child(fragment_20); + var button_53 = sibling(button_52, 2); + delegated("click", button_52, () => { set(canvasMenu, null); layerViaCopy(false); }); - delegated("click", button_52, () => { + delegated("click", button_53, () => { set(canvasMenu, null); layerViaCopy(true); }); append($$anchor4, fragment_20); }; - if_block(node_67, ($$render) => { - if (get(selActive)) $$render(consequent_52); + if_block(node_68, ($$render) => { + if (get(selActive)) $$render(consequent_53); }); } template_effect(() => { - set_style(div_76, `left:${get(canvasMenu).x ?? ""}px; top:${get(canvasMenu).y ?? ""}px;`); + set_style(div_75, `left:${get(canvasMenu).x ?? ""}px; top:${get(canvasMenu).y ?? ""}px;`); set_text(text_26, get(selActive) ? "Free Transform Selection" : "Free Transform Layer"); - button_50.disabled = get(subjectBusy); + button_51.disabled = get(subjectBusy); set_text(text_27, get(subjectBusy) ? "Selecting Subject…" : "Select Subject"); }); - delegated("click", button_48, () => { + delegated("click", button_49, () => { set(canvasMenu, null); beginTransform(); }); - delegated("click", button_49, () => { + delegated("click", button_50, () => { selectAllRegion(); set(canvasMenu, null); }); - delegated("click", button_50, () => { + delegated("click", button_51, () => { set(canvasMenu, null); selectSubject(); }); - append($$anchor3, div_76); + append($$anchor3, div_75); }; - if_block(node_65, ($$render) => { - if (get(canvasMenu)) $$render(consequent_53); + if_block(node_66, ($$render) => { + if (get(canvasMenu)) $$render(consequent_54); }); } - var node_68 = sibling(div_6, 2); - { - var consequent_54 = ($$anchor3) => { - var div_77 = root_73$1(); - var text_28 = child(div_77); - template_effect(() => set_text(text_28, `⚠ ${get(errorMsg) ?? ""}`)); - append($$anchor3, div_77); - }; - if_block(node_68, ($$render) => { - if (get(errorMsg)) $$render(consequent_54); - }); - } - var node_69 = sibling(node_68, 2); + var node_69 = sibling(div_6, 2); { var consequent_55 = ($$anchor3) => { - var div_78 = root_74$1(); - var text_29 = child(div_78); - template_effect(() => set_text(text_29, `⏳ ${get(selSetupMsg) ?? ""}`)); - append($$anchor3, div_78); - }; - var consequent_56 = ($$anchor3) => { - var div_79 = root_75$1(); - append($$anchor3, div_79); + var div_76 = root_74$1(); + var text_28 = child(div_76); + template_effect(() => set_text(text_28, `⚠ ${get(errorMsg) ?? ""}`)); + append($$anchor3, div_76); }; if_block(node_69, ($$render) => { - if (get(selSetupMsg)) $$render(consequent_55); - else if (get(subjectBusy)) $$render(consequent_56, 1); + if (get(errorMsg)) $$render(consequent_55); }); } var node_70 = sibling(node_69, 2); - AdjustModal(node_70, { + { + var consequent_56 = ($$anchor3) => { + var div_77 = root_75$1(); + var text_29 = child(div_77); + template_effect(() => set_text(text_29, `⏳ ${get(selSetupMsg) ?? ""}`)); + append($$anchor3, div_77); + }; + var consequent_57 = ($$anchor3) => { + var div_78 = root_76$1(); + append($$anchor3, div_78); + }; + if_block(node_70, ($$render) => { + if (get(selSetupMsg)) $$render(consequent_56); + else if (get(subjectBusy)) $$render(consequent_57, 1); + }); + } + var node_71 = sibling(node_70, 2); + AdjustModal(node_71, { get open() { return get(adjustOpen); }, @@ -11189,54 +12178,54 @@ function EditModal($$anchor, $$props) { set(adjustSrcCanvas, null); } }); - var node_71 = sibling(node_70, 2); - { - var consequent_57 = ($$anchor3) => { - var div_80 = root_76$1(); - var div_81 = child(div_80); - var div_82 = child(div_81); - var text_30 = child(div_82); - var div_83 = sibling(div_82, 2); - var button_53 = child(div_83); - template_effect(() => set_text(text_30, `Could not use the ${get(tool) === "stamp" ? "clone stamp" : "healing brush"} because the area to sample from has not been defined. `)); - delegated("click", div_80, () => { - set(healDialog, false); - }); - delegated("click", button_53, () => { - set(healDialog, false); - }); - append($$anchor3, div_80); - }; - if_block(node_71, ($$render) => { - if (get(healDialog)) $$render(consequent_57); - }); - } var node_72 = sibling(node_71, 2); { var consequent_58 = ($$anchor3) => { - var div_84 = root_77$1(); - var div_85 = child(div_84); - var div_86 = child(div_85); - var text_31 = child(div_86); - var div_87 = sibling(div_86, 2); - var button_54 = child(div_87); - template_effect(() => set_text(text_31, get(hiddenEditDialog))); - delegated("click", div_84, () => { - set(hiddenEditDialog, null); + var div_79 = root_77$1(); + var div_80 = child(div_79); + var div_81 = child(div_80); + var text_30 = child(div_81); + var div_82 = sibling(div_81, 2); + var button_54 = child(div_82); + template_effect(() => set_text(text_30, `Could not use the ${get(tool) === "stamp" ? "clone stamp" : "healing brush"} because the area to sample from has not been defined. `)); + delegated("click", div_79, () => { + set(healDialog, false); }); delegated("click", button_54, () => { - set(hiddenEditDialog, null); + set(healDialog, false); }); - append($$anchor3, div_84); + append($$anchor3, div_79); }; if_block(node_72, ($$render) => { - if (get(hiddenEditDialog)) $$render(consequent_58); + if (get(healDialog)) $$render(consequent_58); }); } - var div_88 = sibling(node_72, 2); - var div_89 = child(div_88); - var node_73 = sibling(child(div_89), 2); - SavePathInput(node_73, { + var node_73 = sibling(node_72, 2); + { + var consequent_59 = ($$anchor3) => { + var div_83 = root_78$1(); + var div_84 = child(div_83); + var div_85 = child(div_84); + var text_31 = child(div_85); + var div_86 = sibling(div_85, 2); + var button_55 = child(div_86); + template_effect(() => set_text(text_31, get(hiddenEditDialog))); + delegated("click", div_83, () => { + set(hiddenEditDialog, null); + }); + delegated("click", button_55, () => { + set(hiddenEditDialog, null); + }); + append($$anchor3, div_83); + }; + if_block(node_73, ($$render) => { + if (get(hiddenEditDialog)) $$render(consequent_59); + }); + } + var div_87 = sibling(node_73, 2); + var div_88 = child(div_87); + var node_74 = sibling(child(div_88), 2); + SavePathInput(node_74, { get value() { return get(savePrefix); }, @@ -11247,21 +12236,21 @@ function EditModal($$anchor, $$props) { return fetchApi(); } }); - var node_74 = sibling(div_89, 2); + var node_75 = sibling(div_88, 2); { - var consequent_59 = ($$anchor3) => { - var span_22 = root_78$1(); + var consequent_60 = ($$anchor3) => { + var span_22 = root_79$1(); append($$anchor3, span_22); }; - if_block(node_74, ($$render) => { - if (get(editsSaved)) $$render(consequent_59); + if_block(node_75, ($$render) => { + if (get(editsSaved)) $$render(consequent_60); }); } - var button_55 = sibling(node_74, 2); - var button_56 = sibling(button_55, 2); - var text_32 = child(button_56); + var button_56 = sibling(node_75, 2); var button_57 = sibling(button_56, 2); - var text_33 = child(button_57); + var text_32 = child(button_57); + var button_58 = sibling(button_57, 2); + var text_33 = child(button_58); action(div, ($$node) => { var _a; return (_a = portal) == null ? void 0 : _a($$node); @@ -11303,13 +12292,13 @@ function EditModal($$anchor, $$props) { set_attribute(img_2, "src", get(sourceUrl)); set_style(img_2, `width: ${get(navTw) ?? ""}px; height: ${get(navTh) ?? ""}px;`); set_value(input, $3); - classes_8 = set_class(button_30, 1, "pcr-ed-lh-tab svelte-1ozzqkm", null, classes_8, { active: get(lhTab) === "layers" }); - classes_9 = set_class(button_31, 1, "pcr-ed-lh-tab svelte-1ozzqkm", null, classes_9, { active: get(lhTab) === "history" }); - button_56.disabled = !editDocHash() || !get(histIndex) || get(savingEdits) || get(saving); - set_attribute(button_56, "title", editDocHash() ? "Save your layers back onto this image — no new file, keeps the thumbnail" : "This image can't store edits in place — use Save new image"); + classes_8 = set_class(button_31, 1, "pcr-ed-lh-tab svelte-1ozzqkm", null, classes_8, { active: get(lhTab) === "layers" }); + classes_9 = set_class(button_32, 1, "pcr-ed-lh-tab svelte-1ozzqkm", null, classes_9, { active: get(lhTab) === "history" }); + button_57.disabled = !editDocHash() || !get(histIndex) || get(savingEdits) || get(saving); + set_attribute(button_57, "title", editDocHash() ? "Save your layers back onto this image — no new file, keeps the thumbnail" : "This image can't store edits in place — use Save new image"); set_text(text_32, get(savingEdits) ? "Saving…" : "Save edits"); - button_57.disabled = !get(histIndex) || get(saving) || get(savingEdits); - set_attribute(button_57, "title", get(histIndex) ? "Export the result as a new image (keeps the original)" : "Paint something first"); + button_58.disabled = !get(histIndex) || get(saving) || get(savingEdits); + set_attribute(button_58, "title", get(histIndex) ? "Export the result as a new image (keeps the original)" : "Paint something first"); set_text(text_33, get(saving) ? "Saving…" : "Save new image"); }, [ @@ -11368,19 +12357,19 @@ function EditModal($$anchor, $$props) { delegated("click", button_25, () => zoomStep(-1)); delegated("input", input, (e) => setZoomCentered(Math.exp(parseFloat(e.target.value)))); delegated("click", button_26, () => zoomStep(1)); - delegated("click", button_30, () => { + delegated("click", button_31, () => { set(lhTab, "layers"); }); - delegated("click", button_31, () => { + delegated("click", button_32, () => { set(lhTab, "history"); }); - delegated("click", button_55, close); - delegated("click", button_56, saveEdits); - delegated("click", button_57, save); + delegated("click", button_56, close); + delegated("click", button_57, saveEdits); + delegated("click", button_58, save); append($$anchor2, div); }; if_block(node, ($$render) => { - if ($$props.open) $$render(consequent_60); + if ($$props.open) $$render(consequent_61); }); } append($$anchor, fragment); @@ -13492,12 +14481,24 @@ var root_94 = from_html(`
Generation Settings
`, 1); var root_76 = from_html(`
Image Info
`, 1); var root_95 = from_html(`
Loading metadata...
`); -var root_1 = from_html(`
`, 1); +var root_1 = from_html(`
`, 1); function ImageViewer($$anchor, $$props) { var _a; push($$props, true); let images = prop($$props, "images", 23, () => []), startIndex = prop($$props, "startIndex", 3, 0), workflowId = prop($$props, "workflowId", 3, ""), apiURL = prop($$props, "apiURL", 3, (p) => p), fetchApi = prop($$props, "fetchApi", 3, null), onClose = prop($$props, "onClose", 3, () => { - }), onDelete = prop($$props, "onDelete", 3, null), onUpscale = prop($$props, "onUpscale", 3, null), onUpscalePrepare = prop($$props, "onUpscalePrepare", 3, null), onUpscaleBackground = prop($$props, "onUpscaleBackground", 3, null), onUpscaleEnlarge = prop($$props, "onUpscaleEnlarge", 3, null), onInpaintPrepare = prop($$props, "onInpaintPrepare", 3, null), onInpaintRun = prop($$props, "onInpaintRun", 3, null), onInpaintSave = prop($$props, "onInpaintSave", 3, null), onInpaintUploadReference = prop($$props, "onInpaintUploadReference", 3, null), onInpaintInstallPack = prop($$props, "onInpaintInstallPack", 3, null), onMountPromptEditor = prop($$props, "onMountPromptEditor", 3, null), onEditPrepare = prop($$props, "onEditPrepare", 3, null), onEditSave = prop($$props, "onEditSave", 3, null), onInpaintRegion = prop($$props, "onInpaintRegion", 3, null), onReposeCaps = prop( + }), onDelete = prop($$props, "onDelete", 3, null), onUpscale = prop($$props, "onUpscale", 3, null), onUpscalePrepare = prop($$props, "onUpscalePrepare", 3, null), onUpscaleBackground = prop($$props, "onUpscaleBackground", 3, null), onUpscaleEnlarge = prop($$props, "onUpscaleEnlarge", 3, null), onInpaintPrepare = prop($$props, "onInpaintPrepare", 3, null), onInpaintRun = prop($$props, "onInpaintRun", 3, null), onI2iPrepare = prop( + $$props, + "onI2iPrepare", + 3, + null + // (entry) => Promise<{data, caps}> — i2i caps (inpaint caps, no mask) + ), onI2iRun = prop( + $$props, + "onI2iRun", + 3, + null + // (prepared, options) => tracker — background full-frame i2i + ), onInpaintSave = prop($$props, "onInpaintSave", 3, null), onInpaintUploadReference = prop($$props, "onInpaintUploadReference", 3, null), onInpaintInstallPack = prop($$props, "onInpaintInstallPack", 3, null), onMountPromptEditor = prop($$props, "onMountPromptEditor", 3, null), onEditPrepare = prop($$props, "onEditPrepare", 3, null), onEditSave = prop($$props, "onEditSave", 3, null), onInpaintRegion = prop($$props, "onInpaintRegion", 3, null), onReposeCaps = prop( $$props, "onReposeCaps", 3, @@ -14786,6 +15787,9 @@ function ImageViewer($$anchor, $$props) { // the region mask handed from Edit ); let inpaintFromEdit = state(false); + let i2iModalOpen = state(false); + let i2iPrepared = state(null); + let i2iFromEdit = state(false); async function openEdit(targetHash = get(displayedHash)) { if (get(editPreparing) || !onEditPrepare() || !onEditSave()) return; if (isVideoHash(targetHash)) return; @@ -14911,6 +15915,51 @@ ${negative}` : "")); } closeInpaintFromEdit(); } + async function openI2iFromEdit({ compositeBlob, width: encW, height: encH }) { + var _a2; + if (!onI2iPrepare() || !onI2iRun() || !editEntry) return; + try { + const prepared = await onI2iPrepare()(editEntry); + if (!prepared) return; + const name = onInpaintUploadReference() ? await onInpaintUploadReference()(new File([compositeBlob], "edit-composite.png", { type: "image/png" })) : null; + set(i2iFromEdit, true); + set( + i2iPrepared, + { + ...prepared, + data: { + ...prepared.data, + input_ref: name || ((_a2 = prepared.data) == null ? void 0 : _a2.input_ref), + width: encW, + height: encH + }, + sourceUrl: URL.createObjectURL(compositeBlob) + }, + true + ); + set(i2iModalOpen, true); + } catch (e) { + console.error("[PromptChain] i2i-from-edit failed", e); + } + } + function closeI2iFromEdit() { + set(i2iModalOpen, false); + set(i2iFromEdit, false); + set(i2iPrepared, null); + } + async function handleI2iToEdit(doneState) { + var _a2; + const t = doneState == null ? void 0 : doneState.temp; + if (t && ((_a2 = get(editModalRef)) == null ? void 0 : _a2.addI2iLayerFromResult)) { + const url = apiURL()(`/view?filename=${encodeURIComponent(t.filename)}&subfolder=${encodeURIComponent(t.subfolder || "")}&type=${t.type || "temp"}&rand=${Math.random()}`); + try { + await get(editModalRef).addI2iLayerFromResult(url); + } catch (e) { + console.error("[PromptChain] add i2i layer failed", e); + } + } + closeI2iFromEdit(); + } let upscaleFromEdit = state(false); let upscaleEditCtx = state( null @@ -17013,6 +18062,83 @@ ${get(imageInfo).negative}` : "") : ""); }); } var node_82 = sibling(node_81, 2); + { + let $0 = user_derived(() => get(displayedHash) || ""); + let $1 = user_derived(() => { + var _a2; + return ((_a2 = get(i2iPrepared)) == null ? void 0 : _a2.sourceUrl) || ""; + }); + let $2 = user_derived(() => { + var _a2, _b; + return ((_b = (_a2 = get(i2iPrepared)) == null ? void 0 : _a2.data) == null ? void 0 : _b.width) || 0; + }); + let $3 = user_derived(() => { + var _a2, _b; + return ((_b = (_a2 = get(i2iPrepared)) == null ? void 0 : _a2.data) == null ? void 0 : _b.height) || 0; + }); + let $4 = user_derived(() => { + var _a2, _b; + return ((_b = (_a2 = get(i2iPrepared)) == null ? void 0 : _a2.data) == null ? void 0 : _b.filename) || ""; + }); + let $5 = user_derived(() => { + var _a2; + return (_a2 = get(i2iPrepared)) == null ? void 0 : _a2.caps; + }); + let $6 = user_derived(() => { + var _a2, _b, _c; + return ((_b = (_a2 = get(i2iPrepared)) == null ? void 0 : _a2.caps) == null ? void 0 : _b.prefillPrompt) || (((_c = get(imageInfo)) == null ? void 0 : _c.prompt) ? get(imageInfo).prompt + (get(imageInfo).negative ? ` + +Negative Prompt: +${get(imageInfo).negative}` : "") : ""); + }); + let $7 = user_derived(() => { + var _a2, _b, _c; + return ((_b = (_a2 = get(i2iPrepared)) == null ? void 0 : _a2.caps) == null ? void 0 : _b.referencePrompt) || (((_c = get(imageInfo)) == null ? void 0 : _c.prompt) ? get(imageInfo).prompt + (get(imageInfo).negative ? ` + +Negative Prompt: +${get(imageInfo).negative}` : "") : ""); + }); + I2IModal(node_82, { + get open() { + return get(i2iModalOpen); + }, + get imageKey() { + return get($0); + }, + get sourceUrl() { + return get($1); + }, + get width() { + return get($2); + }, + get height() { + return get($3); + }, + get filename() { + return get($4); + }, + get caps() { + return get($5); + }, + get prefillPrompt() { + return get($6); + }, + get referencePrompt() { + return get($7); + }, + get apiURL() { + return apiURL(); + }, + onRun: (options) => onI2iRun()(get(i2iPrepared), options), + get mountPromptEditor() { + return onMountPromptEditor(); + }, + onUseInEdit: handleI2iToEdit, + elevated: true, + onCancel: closeI2iFromEdit + }); + } + var node_83 = sibling(node_82, 2); { let $0 = user_derived(() => { var _a2; @@ -17038,9 +18164,10 @@ ${get(imageInfo).negative}` : "") : ""); let $6 = user_derived(() => onInpaintRegion() ? openInpaintFromEdit : null); let $7 = user_derived(() => onUpscalePrepare() && onUpscaleBackground() && onInpaintUploadReference() ? openUpscaleFromEdit : null); let $8 = user_derived(() => onReposeCaps() && onReposeRun() && onMountPoser() && onInpaintUploadReference() ? openReposeFromEdit : null); - let $9 = user_derived(() => get(inpaintFromEdit) && get(inpaintModalOpen) || get(upscaleFromEdit) && get(upscaleModalOpen) || get(reposeModalOpen)); + let $9 = user_derived(() => onI2iPrepare() && onI2iRun() && onInpaintUploadReference() ? openI2iFromEdit : null); + let $10 = user_derived(() => get(inpaintFromEdit) && get(inpaintModalOpen) || get(upscaleFromEdit) && get(upscaleModalOpen) || get(reposeModalOpen) || get(i2iModalOpen)); bind_this( - EditModal(node_82, { + EditModal(node_83, { get open() { return get(editModalOpen); }, @@ -17079,11 +18206,14 @@ ${get(imageInfo).negative}` : "") : ""); get onOpenRepose() { return get($8); }, + get onOpenI2i() { + return get($9); + }, get figureRegions() { return get(editFigureRegions); }, get suspended() { - return get($9); + return get($10); }, onCancel: () => { set(editModalOpen, false); @@ -17095,7 +18225,7 @@ ${get(imageInfo).negative}` : "") : ""); () => get(editModalRef) ); } - var node_83 = sibling(node_82, 2); + var node_84 = sibling(node_83, 2); { let $0 = user_derived(() => get(displayedHash) || ""); let $1 = user_derived(() => { @@ -17114,7 +18244,7 @@ ${get(imageInfo).negative}` : "") : ""); var _a2; return (_a2 = get(reposePrepared)) == null ? void 0 : _a2.caps; }); - RePoseModal(node_83, { + RePoseModal(node_84, { get open() { return get(reposeModalOpen); }, @@ -17157,7 +18287,7 @@ ${get(imageInfo).negative}` : "") : ""); } }); } - var node_84 = sibling(node_83, 2); + var node_85 = sibling(node_84, 2); { let $0 = user_derived(() => { var _a2; @@ -17183,7 +18313,7 @@ ${get(imageInfo).negative}` : "") : ""); var _a2; return ((_a2 = get(extendPrepared)) == null ? void 0 : _a2.recipeDefaults) || null; }); - ExtendModal(node_84, { + ExtendModal(node_85, { get open() { return get(extendModalOpen); }, @@ -17220,7 +18350,7 @@ ${get(imageInfo).negative}` : "") : ""); } }); } - var node_85 = sibling(node_84, 2); + var node_86 = sibling(node_85, 2); { let $0 = user_derived(() => { var _a2; @@ -17230,7 +18360,7 @@ ${get(imageInfo).negative}` : "") : ""); var _a2; return ((_a2 = get(smoothPrepared)) == null ? void 0 : _a2.frameCount) || 0; }); - SmoothModal(node_85, { + SmoothModal(node_86, { get open() { return get(smoothModalOpen); }, @@ -17252,7 +18382,7 @@ ${get(imageInfo).negative}` : "") : ""); } }); } - var node_86 = sibling(node_85, 2); + var node_87 = sibling(node_86, 2); { let $0 = user_derived(() => { var _a2; @@ -17266,7 +18396,7 @@ ${get(imageInfo).negative}` : "") : ""); var _a2; return ((_a2 = get(deleteBatch)) == null ? void 0 : _a2.length) ? `Delete ${get(deleteBatch).length}` : "Delete"; }); - ConfirmModal(node_86, { + ConfirmModal(node_87, { get open() { return get(confirmDeleteOpen); }, @@ -17283,7 +18413,7 @@ ${get(imageInfo).negative}` : "") : ""); onCancel: cancelDelete }); } - var node_87 = sibling(node_86, 2); + var node_88 = sibling(node_87, 2); { var consequent_75 = ($$anchor2) => { { @@ -17337,7 +18467,7 @@ ${get(imageInfo).negative}` : "") : ""); }); } }; - if_block(node_87, ($$render) => { + if_block(node_88, ($$render) => { if (get(lineageMenu)) $$render(consequent_75); }); } diff --git a/js/lib/upscale-background.js b/js/lib/upscale-background.js index 12ad0e9..33c4772 100644 --- a/js/lib/upscale-background.js +++ b/js/lib/upscale-background.js @@ -321,7 +321,14 @@ export function enlargeImageInBackground(inputRef, targetW, targetH, engine = "u const workflowId = globalThis.crypto?.randomUUID ? crypto.randomUUID() : `${Date.now().toString(16)}-bg`; return new Promise((resolve, reject) => { let promptId = null; + let settled = false; let lastImage = null; + // A cached enlarge executes in ~0s, so its execution_success can reach the + // listener BEFORE queuePrompt's response resolves our prompt_id. Matching a + // real event against a still-null promptId silently drops it and the promise + // hangs forever — which strands the Edit "Add to Edit" handback awaiting it. + // Buffer every event until the id is known, then replay against it. + const pending = []; const cleanup = () => { api.removeEventListener("executed", onExecuted); api.removeEventListener("execution_success", onSuccess); @@ -329,21 +336,26 @@ export function enlargeImageInBackground(inputRef, targetW, targetH, engine = "u api.removeEventListener("execution_interrupted", onError); if (promptId) externallyTrackedPrompts.delete(promptId); }; - const onExecuted = ({ detail }) => { + const settle = (fn, arg) => { if (settled) return; settled = true; cleanup(); fn(arg); }; + const takeExecuted = (detail) => { if (detail?.prompt_id !== promptId || !detail?.output?.images?.length) return; lastImage = detail.output.images[detail.output.images.length - 1]; }; - const onSuccess = ({ detail }) => { - if (detail?.prompt_id !== promptId) return; - cleanup(); - if (lastImage) resolve(lastImage); - else reject(new Error("background enlarge produced no image")); + const takeSuccess = (detail) => { + if (detail?.prompt_id !== promptId || settled) return; + if (lastImage) { settle(resolve, lastImage); return; } + // A fully-cached run can emit execution_success without re-emitting its + // executed (no image over the wire) — recover the output from history. + recoverEnlargeFromHistory(promptId).then((img) => + img ? settle(resolve, img) : settle(reject, new Error("background enlarge produced no image"))); }; - const onError = ({ detail }) => { + const takeError = (detail) => { if (detail?.prompt_id !== promptId) return; - cleanup(); - reject(new Error(detail?.exception_message || "background enlarge failed")); + settle(reject, new Error(detail?.exception_message || "background enlarge failed")); }; + const onExecuted = ({ detail }) => promptId ? takeExecuted(detail) : pending.push(["executed", detail]); + const onSuccess = ({ detail }) => promptId ? takeSuccess(detail) : pending.push(["success", detail]); + const onError = ({ detail }) => promptId ? takeError(detail) : pending.push(["error", detail]); api.addEventListener("executed", onExecuted); api.addEventListener("execution_success", onSuccess); api.addEventListener("execution_error", onError); @@ -354,16 +366,40 @@ export function enlargeImageInBackground(inputRef, targetW, targetH, engine = "u promptId = res?.prompt_id || null; if (promptId) externallyTrackedPrompts.add(promptId); disarmExternalQueue(); - if (!promptId) { cleanup(); reject(new Error("queue returned no prompt id")); } + if (!promptId) { settle(reject, new Error("queue returned no prompt id")); return; } + // Replay buffered events now the id is known — executed first so lastImage + // is set before any buffered success reads it. + for (const [kind, detail] of pending) if (kind === "executed") takeExecuted(detail); + for (const [kind, detail] of pending) { + if (kind === "success") takeSuccess(detail); + else if (kind === "error") takeError(detail); + } + pending.length = 0; }) .catch((e) => { disarmExternalQueue(); - cleanup(); - reject(new Error(e?.response?.error?.message || e?.message || "queue rejected the enlarge prompt")); + settle(reject, new Error(e?.response?.error?.message || e?.message || "queue rejected the enlarge prompt")); }); }); } +// Last output image of a finished prompt, read straight from history — the net +// for a cached enlarge whose execution_success carries no image. +async function recoverEnlargeFromHistory(promptId) { + try { + const res = await api.fetchApi(`/history/${promptId}`); + const outputs = (await res.json())?.[promptId]?.outputs || {}; + let last = null; + for (const nodeId of Object.keys(outputs)) { + const imgs = outputs[nodeId]?.images; + if (imgs?.length) last = imgs[imgs.length - 1]; + } + return last; + } catch { + return null; + } +} + // ── enlarged-base cache (NoUpscale tile engines) ─────────────────────────── // The UltraSharp/SeedVR2 enlarge ("pre-process") is deterministic, so a retry // that only changes tile dials reuses the saved base instead of re-running it. diff --git a/js/lib/upscale-from-image.js b/js/lib/upscale-from-image.js index 1df046e..a26b37a 100644 --- a/js/lib/upscale-from-image.js +++ b/js/lib/upscale-from-image.js @@ -1869,8 +1869,13 @@ function buildKrea2TileStage(graph, LG, place, opts) { if (unetFile && Array.isArray(unetOpts) && !unetOpts.includes(unetFile)) unetOpts.push(unetFile); if (!unetFile) unetFile = unetOpts.find((o) => /krea2/i.test(o)); const clip = LG.createNode("CLIPLoader"); - const clipFile = (clip?.widgets?.find((x) => x.name === "clip_name")?.options?.values || []) - .find((o) => /qwen3vl.*4b|qwen3.?vl.*4b/i.test(o)); + // Realism mode (krea2_turbo): abliterated Qwen3-VL encoder. The VAE stays + // qwen_image_vae below — a tile decoded through wan_2.1 would seam against the + // qwen-decoded surround (see the VAE note). + const clipNames = clip?.widgets?.find((x) => x.name === "clip_name")?.options?.values || []; + const clipFile = opts.realism + ? clipNames.find((o) => /huihui.*qwen3.?vl.*4b.*abliterated|qwen3.?vl.*4b.*abliterated/i.test(o)) + : clipNames.find((o) => /qwen3vl.*4b|qwen3.?vl.*4b/i.test(o)); const vae = LG.createNode("VAELoader"); // Use Krea 2's native VAE (qwen_image_vae) for the tile encode AND decode. A // re-detailed section is composited back into pixels that were generated and @@ -1882,10 +1887,14 @@ function buildKrea2TileStage(graph, LG, place, opts) { const vaeOpts = vae?.widgets?.find((x) => x.name === "vae_name")?.options?.values || []; const vaeFile = vaeOpts.find((o) => /qwen.*image.*vae/i.test(o)) || vaeOpts.find((o) => /wan.*2[._]?1.*vae/i.test(o) && !/upscale|2x/i.test(o)); + const loraNames = (opts.realism && LG.createNode("LoraLoaderModelOnly")?.widgets?.find((x) => x.name === "lora_name")?.options?.values) || []; + const projLora = opts.realism && loraNames.find((o) => /krea2_turbo_projector_scale/i.test(o)); + const realLora = opts.realism && loraNames.find((o) => /krea2-realism/i.test(o)); const posEnc = LG.createNode("CLIPTextEncode"); const negEnc = LG.createNode("CLIPTextEncode"); const usdu = LG.createNode("UltimateSDUpscaleNoUpscale"); if (!(unet && unetFile && clip && clipFile && vae && vaeFile && posEnc && negEnc && usdu)) return null; + if (opts.realism && (!projLora || !realLora)) { console.warn("[PromptChain][upscale] Realism mode needs Krea2-realism-V1 + krea2_turbo_projector_scale LoRAs installed"); return null; } place(unet, 0, 0); place(clip, 0, 180); place(vae, 0, 360); @@ -1902,7 +1911,23 @@ function buildKrea2TileStage(graph, LG, place, opts) { clip.connect(0, posEnc, inputIdx(posEnc, "clip", 0)); clip.connect(0, negEnc, inputIdx(negEnc, "clip", 0)); opts.imageNode.connect(opts.imageSlot || 0, usdu, inputIdx(usdu, "upscaled_image", 0)); - unet.connect(0, usdu, inputIdx(usdu, "model", 1)); + let modelNode = unet; + const realismLoras = []; + if (opts.realism && projLora && realLora) { + const lora1 = LG.createNode("LoraLoaderModelOnly"); + const lora2 = LG.createNode("LoraLoaderModelOnly"); + place(lora1, 0, 540); + place(lora2, 300, 540); + setNamedWidget(lora1, "lora_name", projLora); + setNamedWidget(lora1, "strength_model", 0.5); + setNamedWidget(lora2, "lora_name", realLora); + setNamedWidget(lora2, "strength_model", 0.6); + modelNode.connect(0, lora1, inputIdx(lora1, "model", 0)); + lora1.connect(0, lora2, inputIdx(lora2, "model", 0)); + modelNode = lora2; + realismLoras.push(lora1.id, lora2.id); + } + modelNode.connect(0, usdu, inputIdx(usdu, "model", 1)); posEnc.connect(0, usdu, inputIdx(usdu, "positive", 2)); negEnc.connect(0, usdu, inputIdx(usdu, "negative", 3)); vae.connect(0, usdu, inputIdx(usdu, "vae", 4)); @@ -1921,7 +1946,7 @@ function buildKrea2TileStage(graph, LG, place, opts) { if (typeof opts.seamFixDenoise === "number") setNamedWidget(usdu, "seam_fix_denoise", opts.seamFixDenoise); setNamedWidget(usdu, "force_uniform_tiles", true); setNamedWidget(usdu, "tiled_decode", false); - return { usdu, sideInputIds: [unet.id, clip.id, vae.id, posEnc.id, negEnc.id] }; + return { usdu, sideInputIds: [unet.id, clip.id, vae.id, posEnc.id, negEnc.id, ...realismLoras] }; } // The modal's tuning knobs in tile-stage shape — shared by every tile engine. @@ -2096,6 +2121,7 @@ function buildKrea2EngineGraph(data, options = {}) { imageNode: fit, unetName: options.engineModel?.filename || null, positiveText: (options.prompt || "").trim() || SDXL_TILE_NEUTRAL_POSITIVE, + realism: !!options.realism, ...tileStageOpts(options), })); } diff --git a/js/lib/viewer-bridge.js b/js/lib/viewer-bridge.js index 22371f8..2eea896 100644 --- a/js/lib/viewer-bridge.js +++ b/js/lib/viewer-bridge.js @@ -8,6 +8,7 @@ import { ensureSvelteCSS, createModuleLoader } from "./lazy-load.js"; import { prepareUpscale, buildUpscaleWorkflow } from "./upscale-from-image.js"; import { upscaleImageInBackground, enlargeImageInBackground } from "./upscale-background.js"; import { prepareInpaint, runInpaint, saveInpaintResult, uploadInputImage, inpaintRegion, sourcePromptText, sourcePromptTextFull, liveWorkflowPrompt } from "./inpaint-background.js"; +import { prepareI2I, runI2IInBackground } from "./i2i-background.js"; import { prepareEdit, saveEditedImage } from "./edit-image.js"; import { fetchReposeCaps } from "./repose-from-image.js"; import { runReposeInBackground } from "./repose-background.js"; @@ -292,6 +293,14 @@ export async function openViewer(images, startIndex = 0, workflowId = "", onDele return prepared; }, onInpaintRun: (prepared, options) => runInpaint(prepared, options), + onI2iPrepare: async (entry) => { + const prepared = await prepareI2I(entry); + if (prepared?.caps && prepared.data?.prompt) { + prepared.caps.referencePrompt = resolveReferencePrompt(prepared.data.prompt, workflowId); + } + return prepared; + }, + onI2iRun: (prepared, options) => runI2IInBackground(prepared, options), onInpaintSave: (doneState, prefix) => saveInpaintResult(doneState, prefix), onInpaintUploadReference: (file) => uploadInputImage(file), onInpaintInstallPack: (injectable) => installInpaintPack(injectable), diff --git a/js/main.js b/js/main.js index 5b45801..1ff2bce 100644 --- a/js/main.js +++ b/js/main.js @@ -148,7 +148,7 @@ let _lastPreviewUrl = null; // generating-node map never includes the video node, so hideProgress() never // fires and the preview stays stuck on "Generating…" after a video finishes. const OUTPUT_NODE_TYPES = new Set(["SaveImage", "PreviewImage", "SaveVideo", "SaveWEBM"]); -const KSAMPLER_TYPES = new Set(["KSampler", "KSamplerAdvanced", "PromptChain_RegionalDetailer", "SamplerCustomAdvanced", "UltimateSDUpscale", "UltimateSDUpscaleNoUpscale", "PromptChain_IdeogramSampler"]); +const KSAMPLER_TYPES = new Set(["KSampler", "KSamplerAdvanced", "PromptChain_RegionalDetailer", "SamplerCustomAdvanced", "UltimateSDUpscale", "UltimateSDUpscaleNoUpscale", "PromptChain_IdeogramSampler", "ClownsharKSampler_Beta"]); const IMAGE_LATENT_TYPES = new Set(["IMAGE", "LATENT"]); const LOAD_IMAGE_TYPES = new Set(["LoadImage"]); diff --git a/pyproject.toml b/pyproject.toml index 2f35424..ec0c777 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [project] name = "comfyui-promptchain" -version = "0.1.6" +version = "0.1.7" description = "Prompt-authoring and image-iteration suite for ComfyUI: structured prompts, 3D posing, model awareness, regional control, and layered editing." license = { file = "LICENSE" } requires-python = ">=3.10" diff --git a/svelte-src/components/EditModal.svelte b/svelte-src/components/EditModal.svelte index 6f9464e..d321ee6 100644 --- a/svelte-src/components/EditModal.svelte +++ b/svelte-src/components/EditModal.svelte @@ -25,6 +25,7 @@ onOpenInpaint = null, // ({compositeBlob, maskCanvas}) => void — opens the full Inpaint modal onOpenUpscale = null, // ({cropBlob, rect}) => void — opens the Upscale modal on the region crop onOpenRepose = null, // ({compositeBlob}) => void — opens the Re-pose modal on the whole composite + onOpenI2i = null, // ({compositeBlob, width, height}) => Promise — opens the i2i modal on the /16-snapped composite figureRegions = [], // [{name, text, maskUrl}] — the pose scene's region entities, for one-click figure selection editDocHash = "", // content hash of the image being edited — restores its saved layer stack ("don't-flatten" persistence) suspended = false, // an elevated modal (Inpaint/Upscale handoff) is open above — release the keyboard @@ -46,7 +47,7 @@ let savingEdits = $state(false); // "Save edits" → opened image's sidecar, in flight let editsSaved = $state(false); // "✓ Edits saved" chip; cleared by the next edit let restoring = $state(false); // loading a saved layer stack on open - let handoffBusy = $state(null); // "inpaint" | "upscale" | "repose" while the handoff modal prepares (uploads/graph build) + let handoffBusy = $state(null); // "inpaint" | "upscale" | "repose" | "i2i" while the handoff modal prepares (uploads/graph build) // Re-pose: hand the whole flattened composite to the Re-pose modal (pose a 3D // figure → background re-render into the new pose). Whole-image, not a region. @@ -60,6 +61,29 @@ finally { handoffBusy = null; } } + // i2i: re-diffuse the whole flattened composite. The render dims are snapped + // DOWN to a multiple of 16 with a centered crop — that's exactly what the VAE + // encode would do server-side (it center-crops to /8, and DiT engines patchify + // ×2 = /16), but doing it here means the result comes back at known dims and + // composites over the original without the silent 1–3px shift a raw odd size + // would cause. addI2iLayerFromResult scales the result to fill the document. + async function openI2iHandoff() { + if (!onOpenI2i) return; + errorMsg = ""; + flushActive(); + const encW = Math.max(16, Math.floor(width / 16) * 16); + const encH = Math.max(16, Math.floor(height / 16) * 16); + const ox = (width - encW) >> 1, oy = (height - encH) >> 1; + const c = document.createElement("canvas"); + c.width = encW; c.height = encH; + drawFlattened(c.getContext("2d"), ox, oy, encW, encH, 0, 0, encW, encH); + const compositeBlob = await new Promise((r) => c.toBlob(r, "image/png")); + handoffBusy = "i2i"; + try { await onOpenI2i({ compositeBlob, width: encW, height: encH }); } + catch (e) { errorMsg = `Couldn't open i2i: ${e?.message || e}`; } + finally { handoffBusy = null; } + } + // ── view state (same stage-transform pattern as the inpaint painter) ── let viewportEl; let imgEl; @@ -1329,6 +1353,17 @@ c.getContext("2d").drawImage(img, 0, 0, width, height); insertResultLayer(c, finalMask || inpaintMaskUsed, "Inpaint"); } + // i2i hands back the full-frame re-diffusion — it covers the whole document, + // so it lands as an UNCLIPPED new layer scaled to fill (the result is the + // /16-snapped render, ≤15px smaller per axis, so the fill is sub-pixel). + // Exported for the viewer to call. + export async function addI2iLayerFromResult(url) { + const img = await loadImageEl(url); + const c = document.createElement("canvas"); + c.width = width; c.height = height; + c.getContext("2d").drawImage(img, 0, 0, width, height); + insertResultLayer(c, null, "i2i"); + } // ── Region → Upscale ── function maskBBox(maskCanvas) { @@ -2738,6 +2773,7 @@ let strokeCov = null; // Float32Array width×height, current stroke coverage let sourceData = null; // ImageData of the untouched source image let preStroke = null; // paint-layer ImageData at stroke start + let selStrokeData = null; // active selection's alpha, snapshotted at stroke start (clips the brush to the selection SHAPE) let batchBox = null; // dirty region of the current pointer batch let strokeBox = null; // dirty region of the WHOLE stroke (heal resolves it at release) let falloffLUT = { key: "", table: null }; @@ -2934,6 +2970,14 @@ // preStroke = the ACTIVE layer's current pixels (the live canvas), captured // fresh so a layer switch can't leave it pointing at another layer's state. preStroke = paintCanvas.getContext("2d").getImageData(0, 0, width, height); + // Snapshot the selection SHAPE (lasso curve / marquee / object-select) once + // per stroke so the brush clips to the true mask, not its bbox. null when + // nothing is selected → paint freely. renderStrokeBatch scales coverage by + // this alpha, and restores the pre-stroke pixel verbatim where cov ≤ 0, so + // neither brush nor eraser touch anything outside the selection. + selStrokeData = (selActive && selMaskCanvas) + ? selMaskCanvas.getContext("2d").getImageData(0, 0, width, height).data + : null; batchBox = null; strokeBox = null; } @@ -3006,7 +3050,8 @@ const row = (y0 + y) * width + x0; for (let x = 0; x < bw; x++) { const gi = row + x; - const cov = Math.min(strokeCov[gi], ceil); + let cov = Math.min(strokeCov[gi], ceil); + if (selStrokeData) cov *= selStrokeData[gi * 4 + 3] / 255; // clip to the selection shape (feather-aware) const i = (y * bw + x) * 4; const j = gi * 4; if (cov <= 0) { @@ -4155,7 +4200,7 @@
- {restoring ? "Restoring saved layers…" : handoffBusy === "upscale" ? "Preparing upscale…" : handoffBusy === "repose" ? "Preparing Re-pose…" : "Preparing inpaint…"} + {restoring ? "Restoring saved layers…" : handoffBusy === "upscale" ? "Preparing upscale…" : handoffBusy === "repose" ? "Preparing Re-pose…" : handoffBusy === "i2i" ? "Preparing i2i…" : "Preparing inpaint…"}
{/if} @@ -4600,9 +4645,12 @@ - {#if onOpenInpaint || onOpenUpscale || onOpenRepose} + {#if onOpenInpaint || onOpenUpscale || onOpenRepose || onOpenI2i}
Render {selActive ? "selection" : "layer"} +
{#if onOpenInpaint} {/if} + {#if onOpenI2i} + + {/if} {#if onOpenUpscale} {/if} -
- {#if onOpenRepose} -
+ {#if onOpenRepose} -
- {/if} + {/if} +
{/if} @@ -5076,8 +5126,9 @@ } .pcr-ed-ip-prompt:focus { border-color: #c85909; } .pcr-ed-ip-row { display: flex; align-items: center; gap: 10px; margin: 4px 0; } - .pcr-ed-render-row { display: flex; gap: 8px; } - .pcr-ed-render-row .pcr-ed-ip-render { flex: 1; margin-top: 0; width: auto; white-space: nowrap; } + .pcr-ed-render-row { display: flex; flex-wrap: wrap; gap: 8px; } + /* ~50% basis → two buttons per row (a lone trailing button grows to full). */ + .pcr-ed-render-row .pcr-ed-ip-render { flex: 1 1 calc(50% - 4px); margin-top: 0; width: auto; white-space: nowrap; } .pcr-ed-ip-render { width: 100%; margin-top: 6px; padding: 7px 10px; font-size: 12.5px; color: #fff; cursor: pointer; background: #c85909; border: none; border-radius: 5px; diff --git a/svelte-src/components/I2IModal.svelte b/svelte-src/components/I2IModal.svelte new file mode 100644 index 0000000..e8280d3 --- /dev/null +++ b/svelte-src/components/I2IModal.svelte @@ -0,0 +1,648 @@ + + +{#if open} + +
+ +
+{/if} + + diff --git a/svelte-src/components/ImageViewer.svelte b/svelte-src/components/ImageViewer.svelte index 00d9d70..063b3d2 100644 --- a/svelte-src/components/ImageViewer.svelte +++ b/svelte-src/components/ImageViewer.svelte @@ -2,6 +2,7 @@ import { onDestroy } from "svelte"; import UpscaleOptionsModal from "./UpscaleOptionsModal.svelte"; import InpaintModal from "./InpaintModal.svelte"; + import I2IModal from "./I2IModal.svelte"; import EditModal from "./EditModal.svelte"; import RePoseModal from "./RePoseModal.svelte"; import ExtendModal from "./ExtendModal.svelte"; @@ -25,6 +26,8 @@ onUpscaleEnlarge = null, onInpaintPrepare = null, onInpaintRun = null, + onI2iPrepare = null, // (entry) => Promise<{data, caps}> — i2i caps (inpaint caps, no mask) + onI2iRun = null, // (prepared, options) => tracker — background full-frame i2i onInpaintSave = null, onInpaintUploadReference = null, onInpaintInstallPack = null, @@ -1517,6 +1520,11 @@ let inpaintEditMask = $state(null); // the region mask handed from Edit let inpaintFromEdit = $state(false); + // Edit → i2i handoff state (full-frame re-diffusion, no mask). + let i2iModalOpen = $state(false); + let i2iPrepared = $state(null); + let i2iFromEdit = $state(false); + async function openEdit(targetHash = displayedHash) { if (editPreparing || !onEditPrepare || !onEditSave) return; if (isVideoHash(targetHash)) return; // the editor can't open a video clip @@ -1617,6 +1625,42 @@ closeInpaintFromEdit(); } + // Edit → i2i handoff: open the i2i modal on the edited composite (already + // /16-snapped by EditModal.openI2iHandoff); its result returns to Edit as a + // new full-frame layer. Mirrors openInpaintFromEdit, minus the mask. + async function openI2iFromEdit({ compositeBlob, width: encW, height: encH }) { + if (!onI2iPrepare || !onI2iRun || !editEntry) return; + try { + const prepared = await onI2iPrepare(editEntry); + if (!prepared) return; + // Upload the composite as the render input (reuse the inpaint uploader — + // same content-addressed input/promptchain_inpaint scoping). + const name = onInpaintUploadReference + ? await onInpaintUploadReference(new File([compositeBlob], "edit-composite.png", { type: "image/png" })) + : null; + i2iFromEdit = true; + i2iPrepared = { + ...prepared, + data: { ...prepared.data, input_ref: name || prepared.data?.input_ref, width: encW, height: encH }, + sourceUrl: URL.createObjectURL(compositeBlob), + }; + i2iModalOpen = true; + } catch (e) { + console.error("[PromptChain] i2i-from-edit failed", e); + } + } + function closeI2iFromEdit() { + i2iModalOpen = false; i2iFromEdit = false; i2iPrepared = null; + } + async function handleI2iToEdit(doneState) { + const t = doneState?.temp; + if (t && editModalRef?.addI2iLayerFromResult) { + const url = apiURL(`/view?filename=${encodeURIComponent(t.filename)}&subfolder=${encodeURIComponent(t.subfolder || "")}&type=${t.type || "temp"}&rand=${Math.random()}`); + try { await editModalRef.addI2iLayerFromResult(url); } catch (e) { console.error("[PromptChain] add i2i layer failed", e); } + } + closeI2iFromEdit(); + } + // Edit → Upscale handoff: the upscale pipeline runs on the region crop, and // the render returns to Edit either 1:1 in a GROWN canvas (the document // scales to the quality the user picked) or squeezed back into the region — @@ -2950,6 +2994,26 @@ onCancel={() => { if (inpaintFromEdit) closeInpaintFromEdit(); else { inpaintModalOpen = false; inpaintPrepared = null; } }} /> + onI2iRun(i2iPrepared, options)} + mountPromptEditor={onMountPromptEditor} + onUseInEdit={handleI2iToEdit} + elevated={true} + onCancel={closeI2iFromEdit} +/> + { editModalOpen = false; editPrepared = null; editEntry = null; }} /> diff --git a/svelte-src/components/InpaintModal.svelte b/svelte-src/components/InpaintModal.svelte index 0a3a6b2..9c0183a 100644 --- a/svelte-src/components/InpaintModal.svelte +++ b/svelte-src/components/InpaintModal.svelte @@ -80,6 +80,10 @@ let engineKind = $derived(engineEntry ? (engineEntry.architecture === "qwen_edit" ? "qwen" : engineEntry.architecture === "flux" ? "flux1" : engineEntry.architecture === "krea2" ? "krea2" : "sdxl") : "source"); + // Realism mode: a krea2_turbo-only recipe (abliterated encoder + realism/bypass + // LoRAs). Inpaint keeps qwen_image_vae (seam-safe) and MaskedDetail's sampler. + let realism = $state(false); + let realismAvailable = $derived(engineKind === "krea2" && /turbo/i.test(engineEntry?.filename || "")); let engineGroups = $derived({ sdxl: (caps?.engineModels || []).filter((m) => m.architecture === "sdxl"), flux1: (caps?.engineModels || []).filter((m) => m.architecture === "flux"), @@ -792,6 +796,7 @@ : engineKind === "qwen" ? "qwen-edit" : "source", engineModel: engineEntry ? { hash: engineEntry.hash, filename: engineEntry.filename } : undefined, engineGen: engineEntry?.gen || undefined, + realism: realismAvailable ? realism : undefined, // The qwen builder crops around the mask client-side — the bbox is // known here (the painted canvas), not on the server. maskBbox: engineKind === "qwen" ? maskBboxFromCanvas() : undefined, @@ -1052,6 +1057,12 @@ {:else if engineKind === "qwen"}
follows your instruction inside the mask only — unmasked pixels stay untouched; the region renders at full model resolution
{/if} + {#if realismAvailable} + + {/if} {/if} {#if caps?.conditions && engineKind !== "qwen" && engineKind !== "flux1" && engineKind !== "krea2"} diff --git a/svelte-src/components/UpscaleOptionsModal.svelte b/svelte-src/components/UpscaleOptionsModal.svelte index fbdf678..6709c4f 100644 --- a/svelte-src/components/UpscaleOptionsModal.svelte +++ b/svelte-src/components/UpscaleOptionsModal.svelte @@ -70,6 +70,10 @@ : engineEntry.architecture === "krea2" ? "krea2" : engineEntry.architecture === "flux" ? "flux1" : "sdxl") : (engineSel === "source" ? "source" : "plain")); + // Realism mode: a krea2_turbo-only recipe (abliterated encoder + realism/bypass + // LoRAs). Tile upscale keeps qwen_image_vae (seam-safe) and USDU's sampler. + let realism = $state(false); + let realismAvailable = $derived(engineKind === "krea2" && /turbo/i.test(engineEntry?.filename || "")); let sourceGraftable = $derived(engineKind === "source" && !!caps?.graftable); // Tile engines share the USDU recipe surface (denoise/climb/tile knobs); // qwen is the whole-frame re-render with its own reduced surface. @@ -486,6 +490,7 @@ if (tileEngine && climbStage !== "ultrasharp") base.climbStage = climbStage; if (engineKind !== "source" && climbModel) base.climbModel = climbModel; if (engineKind === "source" && caps?.architecture === "flux2") base.preserveDefocus = preserveDefocus; + if (realismAvailable) base.realism = realism; if (tileEngine || engineKind === "qwen") { // Engine graphs have no source conditioning to fall back on — always // send the current text (empty → the builder's per-arch constant). @@ -831,6 +836,12 @@ {:else if engineKind === "plain"} deterministic enlargement with the climb model below — add a Restore step for degraded sources (webcam, jpeg, blur) {/if} + {#if realismAvailable} + + {/if} {/if} diff --git a/svelte-src/components/model/ModelSettingsModal.svelte b/svelte-src/components/model/ModelSettingsModal.svelte index 7bd0490..ccee6df 100644 --- a/svelte-src/components/model/ModelSettingsModal.svelte +++ b/svelte-src/components/model/ModelSettingsModal.svelte @@ -23,6 +23,7 @@ onSwitchVersion, onOpenPicker, onInjectNode, + onApplyStyleLora, onApplyTemplate, captureWorkflowGraph, readWidgetValue, @@ -423,6 +424,10 @@ onSave={handleSave} {onReopen} {onInjectNode} + {pcNode} + architecture={detectedArch} + family={currentFamily} + {onApplyStyleLora} {onClose} /> diff --git a/svelte-src/components/model/SettingsTab.svelte b/svelte-src/components/model/SettingsTab.svelte index d1da7c5..c588a66 100644 --- a/svelte-src/components/model/SettingsTab.svelte +++ b/svelte-src/components/model/SettingsTab.svelte @@ -3,6 +3,7 @@ // resolution presets, range mode, save/apply/add/edit footer. import "./model-panel-shared.css"; + import { api } from "/scripts/api.js"; import SettingsSlider from "./SettingsSlider.svelte"; import RangeSlider from "./RangeSlider.svelte"; import MultiSelectCombo from "./MultiSelectCombo.svelte"; @@ -20,6 +21,10 @@ onSave, onReopen, onInjectNode, + pcNode = null, + architecture = "", + family = "", + onApplyStyleLora = () => {}, onClose, } = $props(); @@ -250,6 +255,105 @@ // Injection menu let addMenuOpen = $state(false); let addSubmenu = $state(null); // the group (e.g. ControlNet) drilled into + + // ── Krea 2 style LoRA section (always present on Krea 2 Turbo) ────── + // A single swappable model-only LoRA + its trigger word, served from the + // scope-filtered lora catalog. Selecting downloads-if-missing then swaps the + // node + prompt trigger via onApplyStyleLora. + const showStyleSection = $derived(architecture === "krea2" && family === "krea2_turbo"); + let styleCatalog = $state([]); + let styleInstalled = $state({}); // filename → exists + let styleSelected = $state(pcNode?.properties?.pcrStyleLora || ""); + let styleStrength = $state(1.0); + let styleBusy = $state(false); + let styleStatus = $state(""); + + $effect(() => { + if (!showStyleSection) return; + let cancelled = false; + (async () => { + try { + const res = await fetch(`/promptchain/loras/catalog?architecture=${encodeURIComponent(architecture)}&family=${encodeURIComponent(family)}`); + const data = await res.json(); + if (cancelled) return; + styleCatalog = data.loras || []; + const cur = styleCatalog.find(l => l.id === styleSelected); + if (cur) styleStrength = cur.strength_default ?? 1.0; + await refreshStyleInstalled(); + } catch (e) { console.warn("[PromptChain] style catalog load failed:", e); } + })(); + return () => { cancelled = true; }; + }); + + async function refreshStyleInstalled() { + if (!styleCatalog.length) return; + try { + const res = await fetch("/promptchain/models/check-files", { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ files: styleCatalog.map(l => ({ filename: l.filename, folder: l.folder || "loras", size_bytes: l.size_bytes || 0 })) }), + }); + const data = await res.json(); + const map = {}; + for (const r of data.results || []) map[r.filename] = r.exists; + styleInstalled = map; + } catch (e) { console.warn("[PromptChain] style install check failed:", e); } + } + + // Download a missing style via the shared civitai route; resolves on the WS + // "done" event for this file (event-driven, no polling). + function downloadStyle(lora) { + return new Promise((resolve) => { + const s = lora.source || {}; + const url = s.provider === "huggingface" + ? `https://huggingface.co/${s.repo}/resolve/main/${s.path}` + : (s.download_url || s.url || ""); + if (!url) { resolve(false); return; } + const cleanup = () => { + api.removeEventListener("promptchain_download_progress", onProgress); + api.removeEventListener("promptchain_download_done", onDone); + }; + function onProgress({ detail }) { + if (detail.filename !== lora.filename) return; + styleStatus = `Downloading ${lora.label}: ${detail.progress}%`; + } + function onDone({ detail }) { + if (detail.filename !== lora.filename) return; + cleanup(); + resolve(detail.status === "completed"); + } + api.addEventListener("promptchain_download_progress", onProgress); + api.addEventListener("promptchain_download_done", onDone); + fetch("/promptchain/civitai/download", { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ url, filename: lora.filename, folder: lora.folder || "loras" }), + }).then(r => r.json()).then(d => { + if (d.error) { cleanup(); styleStatus = d.error; resolve(false); } + }).catch(err => { cleanup(); styleStatus = err.message; resolve(false); }); + }); + } + + async function onStyleChange(id) { + styleSelected = id; + if (!id) { onApplyStyleLora(null, styleStrength); styleStatus = ""; return; } + const lora = styleCatalog.find(l => l.id === id); + if (!lora) return; + styleStrength = lora.strength_default ?? styleStrength ?? 1.0; + if (!styleInstalled[lora.filename]) { + styleBusy = true; + const ok = await downloadStyle(lora); + styleBusy = false; + if (!ok) { styleStatus = styleStatus || "Download failed"; styleSelected = pcNode?.properties?.pcrStyleLora || ""; return; } + styleInstalled = { ...styleInstalled, [lora.filename]: true }; + styleStatus = ""; + } + onApplyStyleLora(lora, styleStrength); + } + + function onStyleStrength(v) { + styleStrength = v; + const lora = styleCatalog.find(l => l.id === styleSelected); + if (lora && styleInstalled[lora.filename]) onApplyStyleLora(lora, v); + } let editMenuOpen = $state(false); let injectable = $derived(injectableNodes()); @@ -517,6 +621,37 @@ {/each} {/each} + + {#if showStyleSection} +
+
Style
+
+ LoRA +
+ +
+
+ {#if styleSelected} +
+ strength + onStyleStrength(v)} /> +
+ {/if} + {#if styleStatus} +
{styleStatus}
+ {/if} +
+ {/if} {#if saveError} @@ -640,6 +775,7 @@ letter-spacing: 0; } .pcr-section-expand-toggle:hover { color: #999; } + .pcr-style-status { font-size: 11px; color: #5dcaff; } /* widget rows */ .pcr-model-panel-row {