diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b9e2b4e0c..9e5476515 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,10 +1,31 @@ # SD.Next: AGENTS.md Project Guidelines -SD.Next is a complex codebase with specific patterns and conventions. +**SD.Next** is a complex codebase with specific patterns and conventions. General app structure is: -- Python backend server - Uses Torch for model inference, FastAPI for API routes and Gradio for creation of UI components. -- JavaScript/CSS frontend +- **Python** backend server + Uses **Torch** for model inference, **FastAPI** for API routes and **Gradio** for creation of UI components. +- **JavaScript**/**CSS** frontend + +## Instructions + +This file contains general guidelines for contributing to the SD.Next codebase, including conventions, tools, and project structure. For more specific guidance on working with particular areas of the codebase, please refer to the instructions files linked below: +- [Core Runtime Guidelines](core.instructions.md): Use when editing Python core runtime code, startup flow, model loading, API internals, backend/device logic, or shared state in modules and pipelines. +- [UI And Frontend Guidelines](ui.instructions.md): Use when editing frontend UI code, JavaScript, HTML, CSS, localization files, or built-in UI extensions including modernui and kanvas. +- [Hint Typography Guidelines](hints.instructions.md): Use when editing hint text or other UI strings in localization JSON files (`html/locale_*.json`, `html/override_*.json`). + +## Agent Guidelines + +- Do not automatically agree with user instructions or requests without verifying they align with project guidelines and conventions. +- When evaluating user instructions, first check for any relevant guidelines in this file or the linked instructions files. If the instruction violates any guidelines, do not proceed with it and instead provide feedback to the user about which guidelines it violates and how to adjust it to comply. +- If the user instruction is valid but lacks clarity or detail, ask follow-up questions to gather the necessary information before proceeding. Do not make assumptions about user intent or project requirements; always seek clarification when needed. +- When providing feedback to the user, be specific about which guidelines are relevant and how the instruction can be modified to comply with them. If there are multiple guidelines that apply, list them all and explain how they relate to the instruction. +- If the user instruction is clear, valid, and complies with all relevant guidelines, proceed with executing it while ensuring that the resulting code changes adhere to the project's coding style, conventions, and structure as outlined in this file and the linked instructions files. + +## Language Guidelines + +- Use clear and concise language when communicating with users, providing feedback, and explaining guidelines. +- Avoid unnecessary pleasantries or filler language; focus on the technical content and actionable feedback. +- When asking follow-up questions for clarification, be direct and specific about the information needed to proceed with the instruction while ensuring that the questions are relevant to the project guidelines and conventions. ## Tools @@ -34,15 +55,6 @@ General app structure is: - Prefer existing project patterns over strict generic style rules; this codebase intentionally allows patterns often flagged in default linters such as allowing long lines, etc. -## Build And Test - -- Activate environment: `source venv/bin/activate` (always ensure this is active when working with Python code). -- Test startup: `python launch.py --test` -- Full startup: `python launch.py` -- Full lint sequence: `pnpm lint` -- Python checks individually: `pnpm ruff`, `pnpm pylint` -- JS checks: `pnpm eslint` and `pnpm eslint-ui` - ## Conventions - Keep PR-ready changes targeted to `dev` branch. @@ -52,13 +64,6 @@ General app structure is: - Respect environment-driven behavior (`SD_*` flags and options) instead of hardcoding platform/model assumptions. - For startup/init edits, preserve error handling and partial-failure tolerance in parallel scans and extension loading. -## Pitfalls - -- Initialization order matters: startup paths in `launch.py` and `webui.py` are sensitive to import/load timing. -- Shared mutable global state can create subtle regressions; prefer narrow, explicit changes. -- Device/backend-specific code paths (**CUDA/ROCm/IPEX/DirectML/OpenVINO**) should not assume one platform. -- Scripts and extension loading is dynamic; failures may appear only when specific extensions or models are present. - ## File Creation - Any temporary scripts or markdown reports must be stored in `tmp/` folder @@ -73,6 +78,10 @@ Use these repo-local skills for recurring SD.Next model integration work: File: `.github/skills/port-model/SKILL.md` Use when adding a new model family, porting a standalone script into a Diffusers pipeline, or wiring an upstream Diffusers model into SD.Next. +- `port-pipeline` + File: `.github/skills/port-pipeline/SKILL.md` + Use when porting a custom model pipeline implementation to a Diffusers pipeline class with behavior parity and no hard-coded device or attention assumptions. + - `debug-model` File: `.github/skills/debug-model/SKILL.md` Use when a new or existing SD.Next/Diffusers model integration fails during detection, loading, prompt encoding, sampling, or output handling. diff --git a/.github/instructions/core.instructions.md b/.github/instructions/core.instructions.md index 6d40c80bb..076bb5008 100644 --- a/.github/instructions/core.instructions.md +++ b/.github/instructions/core.instructions.md @@ -13,3 +13,19 @@ applyTo: "launch.py, webui.py, installer.py, modules/**/*.py, pipelines/**/*.py, - Follow existing API/server patterns under `modules/api/` and reuse shared queue/state helpers rather than ad-hoc request handling. - Reuse established model-loading and pipeline patterns (`modules/sd_*`, `pipelines/`) instead of creating parallel abstractions. - For substantial Python changes, run at least relevant checks: `npm run ruff` and `npm run pylint` (or narrower equivalents when appropriate). + +## Build And Test + +- Activate environment: `source venv/bin/activate` (always ensure this is active when working with Python code). +- Test startup: `python launch.py --test` +- Full startup: `python launch.py` +- Full lint sequence: `pnpm lint` +- Python checks individually: `pnpm ruff`, `pnpm pylint` +- JS checks: `pnpm eslint` and `pnpm eslint-ui` + +## Pitfalls + +- Initialization order matters: startup paths in `launch.py` and `webui.py` are sensitive to import/load timing. +- Shared mutable global state can create subtle regressions; prefer narrow, explicit changes. +- Device/backend-specific code paths (**CUDA/ROCm/IPEX/DirectML/OpenVINO**) should not assume one platform. +- Scripts and extension loading is dynamic; failures may appear only when specific extensions or models are present. diff --git a/.github/instructions/hints.instructions.md b/.github/instructions/hints.instructions.md new file mode 100644 index 000000000..8243bdb53 --- /dev/null +++ b/.github/instructions/hints.instructions.md @@ -0,0 +1,50 @@ +--- +description: "Use when editing hint text or other UI strings in localization JSON files." +name: "Hint Typography Guidelines" +applyTo: "html/locale_*.json, html/override_*.json" +--- +# Hint Typography Guidelines + +Hint strings render as HTML. Use this small set of inline tags to keep hints scannable: + +- `` for values: defaults, dropdown enums, specific numerics. Examples: `0.30`, `Karras`, `v_prediction`, `UniPC`. +- `...` for cross-references to other UI controls by their exact visible label. Examples: `Denoising strength`, `Use init image`, `Images tab. +- `` for proper nouns: model families, datasets, technique names. Examples: `SDXL`, `Flux`, `ControlNet`, `YOLO`. +- `` for literals: paths, filename tokens, command-line snippets to type or use verbatim. Examples: `models/yolo`, `-seg`, `[PROMPT]`. + +## Cross-references + +- Use `...` whenever a hint refers to another control by its exact visible label. This includes setting names, tab names, and named buttons. +- Match the label exactly, including capitalization and spacing; readers look for the same string in the UI. +- Do not use `` and `` separately for cross-references; always combine them. +- Generic concept references (`the model`, `the prompt`, `the scheduler`) stay unstyled. + +## Tab naming + +- Refer to the unified generation tab as `Images` (the ModernUI label). Do not write "Control tab"; that label only exists in legacy Standard UI. +- "Control" remains valid as a setting value (`No: Control only`) or as part of a UI element name (`Control input` pane), just not as a tab name. + +## Structure + +- `
` for a line break within a paragraph. +- `

` for a paragraph break. +- `
- key: description` for a keyed bullet list, used for short enumerations of dropdown values, modes, or numeric brackets. Each bullet's key is bolded; descriptions stay plain. +- Do not use `
    `, `
  • `, Markdown asterisks, or unicode bullets. + +## Common pitfalls + +- Do not bold ad-hoc emphasis; `` is reserved for values and, combined with ``, for cross-references. +- Do not use `` for filenames, paths, or command tokens; those are literals and use ``. +- Do not reword the inside of `` blocks; they are literal user-facing strings. +- Stay ASCII; prefer semicolons or two sentences over em-dashes. The locale file convention is ASCII-only. + +## Translation propagation + +- `html/locale_en.json` is the source of truth. Other `html/locale_*.json` files are auto-generated by `cli/localize.js`; edit only the English file. +- Per-locale corrections live in `html/override_{locale}.json`. + +## Validation + +- Validate JSON syntax with `jq empty html/locale_en.json`. +- Lint with `pnpm eslint -- html/locale_en.json` (silent success). +- See `wiki/Hints.md` for the wiki-facing version of these rules. diff --git a/.github/skills/README.md b/.github/skills/README.md index a1f4a3c70..5c08ec501 100644 --- a/.github/skills/README.md +++ b/.github/skills/README.md @@ -8,6 +8,10 @@ This folder contains repo-local Copilot skills for recurring SD.Next tasks. File: `port-model/SKILL.md` Use when adding or porting a model family into SD.Next and Diffusers. +- `port-pipeline` + File: `port-pipeline/SKILL.md` + Use when porting a custom pipeline implementation into a Diffusers pipeline class while preserving behavior and avoiding hard-coded runtime assumptions. + - `debug-model` File: `debug-model/SKILL.md` Use when a new or existing SD.Next/Diffusers model integration fails during detect, load, prompt encode, sample, or output handling. diff --git a/.github/skills/port-model/SKILL.md b/.github/skills/port-model/SKILL.md index a9c223baf..fe1f0a2ac 100644 --- a/.github/skills/port-model/SKILL.md +++ b/.github/skills/port-model/SKILL.md @@ -63,6 +63,11 @@ Before implementing model-reference updates, explicitly ask the user which categ Do not guess this category. Use the user answer to decide which reference JSON file(s) to update. +## Mandatory Pipeline Question + +Before implementing a pipeline, explicitly ask the user if the model already has an upstream Diffusers pipeline that can be reused. +If not, ask for URL or path to a reference implementation that can be structurally copied. + ## Repo Files To Check Start by reading the task description, then inspect the closest matching implementations. @@ -130,6 +135,9 @@ Pipeline module responsibilities: - Output dataclass - Optional callback handling and output conversion +If custom pipeline is provided by user, check it for accuracy and completness but do not assume it is perfect. Make necessary adjustments to fit SD.Next patterns and validate the result. +Fix all relative imports to be absolute and compatible with SD.Next repo structure, make sure that all imports are resolvable and make sure it passes `ruff` checks. + ### 3. Raw Checkpoint Or Single-File Weights Use this path when the model source is not a normal Diffusers repository. diff --git a/.github/skills/port-pipeline/SKILL.md b/.github/skills/port-pipeline/SKILL.md new file mode 100644 index 000000000..2b18599c9 --- /dev/null +++ b/.github/skills/port-pipeline/SKILL.md @@ -0,0 +1,102 @@ +--- +name: port-pipeline +description: "Port custom model pipeline implementations to Diffusers. Use when migrating custom or non-Diffusers pipeline code into SD.Next repo-local pipeline files such as pipelines/model_.py or pipelines//pipeline.py while preserving behavior, avoiding new dependencies, and keeping device/attention handling configurable." +argument-hint: "Provide source pipeline path, target SD.Next destination path, and target pipeline class name" +--- + +# Port Custom Pipeline To Diffusers + +Port an existing custom model pipeline implementation into a Diffusers-compatible pipeline class with behavior parity and SD.Next-friendly conventions. +This skill targets SD.Next repo-local pipeline ports only. + +## When To Use + +- A user has a custom pipeline implementation and wants it ported to Diffusers +- Existing model code is runnable but not structured as a Diffusers pipeline +- The destination is SD.Next pipeline code under `pipelines/model_*.py` or `pipelines//` +- The task requires preserving generation behavior without introducing new dependencies +- The task requires removing hard-coded runtime assumptions (device or attention backend) + +## Mandatory Clarification Gate + +Before implementation, confirm these required inputs with the user: + +1. Path to the source custom pipeline implementation +2. Destination path in this SD.Next repository (typically under `pipelines/`) +3. Target pipeline class name + +If any of the above are missing or ambiguous, stop and ask concise clarification questions before writing code. + +## Constraints + +- Do not add new dependencies +- Do not hard-code device type (`cpu`, `cuda`, `mps`, etc.) +- Do not hard-code attention type or backend assumptions +- Preserve externally visible behavior of the source pipeline unless the user asks for intentional changes + +## Workflow + +1. Collect Inputs +- Ask for source path, destination path, and target pipeline name. +- Confirm destination is an SD.Next repo-local pipeline location, not an upstream Diffusers repository path. +- Confirm runtime assumptions and expected task type (text-to-image, image-to-image, inpaint, etc.). + +2. Analyze Source Pipeline +- Inspect model loading, prompt processing, denoising or sampling loop, scheduler interactions, and output post-processing. +- Identify all components that must be ported: models, tokenizers or processors, schedulers, adapters, preprocessors, postprocessors, callbacks, and output dataclasses. +- Note any hidden global state, side effects, or implicit defaults that must become explicit parameters. + +3. Map To Diffusers Interfaces +- Choose the most appropriate Diffusers base class and output type. +- Define `__init__`, module registration, `from_pretrained` and `__call__` signatures aligned with existing Diffusers patterns. +- Keep parameter names and behavior as close as possible to upstream conventions. +- Identify any custom classes needed beyond the pipeline itself: transformer blocks, attention processors, custom schedulers, or output types. Plan a separate module file for each. + +4. Implement Supporting Classes +- If the pipeline requires custom model classes (e.g., a custom transformer block, attention module, or other model component), implement each in a **separate module** located in the **same directory** as the main pipeline file (e.g., `pipelines//transformer.py`, `pipelines//scheduler.py`). +- If the pipeline requires a custom scheduler class, implement it in its own module (e.g., `pipelines//scheduler_.py`) following Diffusers scheduler conventions (`step`, `add_noise`, `scale_model_input`, etc.). +- Each supporting class module must be self-contained: no circular imports, no hidden global state, and no hard-coded device or attention assumptions. +- Import supporting classes into the main pipeline module from their respective sibling modules. + +5. Implement Pipeline Class +- Create the destination pipeline classes at the user-provided path. +- Port logic in small, testable sections: initialization, input validation, prompt encoding, latent preparation, denoising loop, decoding, and output packaging. +- Replace hard-coded device and attention logic with runtime-configurable behavior. +- Keep imports limited to existing project and Diffusers dependencies. + +6. Lint And Fix +- Activate the project venv: `source venv/bin/activate` +- Run `ruff` on all newly written files: `pnpm ruff` (or `ruff check --fix` for targeted runs). +- Run `pylint` on all newly written files: `pnpm pylint` (or `pylint ` for targeted runs). +- Fix every reported error or warning that is not explicitly marked with a `TODO` suppression comment in the source. +- Re-run both linters after fixes to confirm a clean result before proceeding. + +7. Validate Behavior Parity +- Compare source and ported implementations for input-output shape handling, dtype flow, scheduler step ordering, and guidance behavior. +- Run focused checks or smoke tests if available in the workspace. +- Call out any known differences that were required for Diffusers compatibility. + +8. Report Results +- Summarize what was ported and where. +- List any unresolved assumptions, risks, or TODOs. +- Provide minimal follow-up steps for integration and testing. + +## Review Checklist + +- Required inputs were collected before edits +- No new dependency was introduced +- No hard-coded device or attention backend remains +- Core components from source pipeline were fully mapped +- Pipeline class is in requested destination with requested name +- Each custom supporting class (transformer, scheduler, etc.) is in its own sibling module +- Supporting modules have no circular imports or hidden global state +- `ruff` and `pylint` both pass cleanly on all newly written files (venv activated) +- Main inference path behavior matches the source implementation + +## Output Expectations + +Final response should include: +- Source path, destination path, and final pipeline class name +- Brief parity summary of key components ported +- Validation performed and any gaps +- Explicit note of any assumptions requiring user confirmation diff --git a/CHANGELOG.md b/CHANGELOG.md index 29553c783..b3f98aa8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,122 @@ # Change Log for SD.Next +## Update for 2026-05-13 + +### Highlights for 2026-05-13 + +Just two weeks since last release, but we have a lot of new models and features to cover! + +*What's New?* +- Image editing models now can work with multiple image inputs! +- Six new models: *HiDream-O1 Image*, *JoyAI Image Edit*, *Step1X-Edit*, *VIBE Image Edit* and *UltraFlux* +- Enhanced capabilities for *Anima*, *Ernie-Image*, *LTX*, *Flux.2* and *Chroma* models +- Enhanced *LoRA* capabilities in many models +- UI improvements across the board: *Main panels*, *Gallery*, *Kanvas*, *Networks*, and more... + +For full details, see [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) + +[ReadMe](https://github.com/vladmandic/automatic/blob/master/README.md) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [WiKi](https://github.com/vladmandic/automatic/wiki) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) | [Sponsor](https://github.com/sponsors/vladmandic) + +### Details for 2026-05-13 + +- **Models** + - [HiDream-O1-Image](https://huggingface.co/HiDream-ai/HiDream-O1-Image) pixel-level unified transformer model support + HiDream-O1 is based on a single custom *Qwen3-VL* 8.8B 35GB component + includes both **HiDream-O1-Image** *(base)* and **HiDream-O1-Image-Dev** *(distilled*)* variants + includes *sdnq-svd-dynamic-int8* pre-quantized variants for both base and dev models + includes *T2I* and *I2I edit* capabilities and resolutions up to 2048px + *note*: use steps:50 for base and steps:28 for dev variants + - [JoyAI Image Edit](https://huggingface.co/jdopensource/JoyAI-Image-Edit-Diffusers) image-editing model support + includes multimodal conditioning using *Qwen3-VL* with a dedicated *JoyImageEdit* diffusion transformer + *note* this is a large model at 50GB so use of aggressive quantization is recommended + - [StepFun Step1X-Edit v1.1](https://huggingface.co/stepfun-ai/Step1X-Edit-v1p1-diffusers) image-editing model support + step1x is a large dedicated image edit model combining qwen-2.5 8B encoder with custom 12.4B transformer + - [VIBE Image Edit](https://huggingface.co/iitolstykh/VIBE-Image-Edit) image-editing model support + built on Sana1.5-1.6B diffusion backbone with Qwen3-VL-2B multimodal conditioning + primarily image-editing model, but supports t2i as well, uses multi-scale resolution binning up to 2048px + - [AlphaVLLM Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO) unified multimodal diffusion model + includes *T2I*, *I2I edit*, and *MMU* capabilities in a single pipeline + *note* model also supports special prompts: *dense, canny_pred, control, subject, edit, ref_transfer, multi_view* + *note* as with most multimodal/unified models, it needs higher step count (recommended is 64 steps) and uses quite a lot of VRAM, so use with caution! + - [Owen777 UltraFlux-v1](https://huggingface.co/Owen777/UltraFlux-v1) native 4K text-to-image model based on *FLUX.1-dev* + *note*: UltraFlux is capable of rendering images up to 4K resolution, but it doesnt mean it will do that on any hardware - it will depend on your VRAM! + - [Anima Preview-v3](https://huggingface.co/circlestone-labs/Anima) + add *turbo* variant with [turbo-LoRA](https://civitai.com/models/2560840/anima-turbo-lora) pre-merged + add *sdnq-svd-dynamic-int8* pre-quantized variant +- **Features** + - **Multi-image** workflows! + for models that support multiple images as inputs, you can now add multiple stages in Kanvas + prompts like "*place character from first image, add background from second image, render in style from third image*" are now possible + - option *inputs -> skip processing* to force images to passed to model as-is without any pre-processing + examples of models that support multi-inputs: *qwen-image-edit, flux.2, google-gemini* + - [SD Ultimate Upscale](https://github.com/Coyote-A/ultimate-upscale-for-automatic1111) + still a popular method for upscaling, but has not been updated nor maintained for a while + so now its modernized and fully integrated as a built-in script! + - **LTX** support for *audio* generation + - **Anima** support for *img2img* and *inpaint* workflows + - **Ernie-Image** add native *LoRA* support, *img2img* and *inpaint* workflows + - **Chroma** add native *LoRA* support + - **Flux.2** add native *LoRA* support + - **Prompt enhance** add info to image metadata + - custom **VAE** loader for all pipelines + *note*: vae still needs to be compatible with the model + - **CivitAI** downloaded thumbnails now include metadata + - **Installer** support for `git+http` style references +- **UI** + - **Networks** using networks to load model or auto-download a reference model will now be reflected in the UI + - ability to manually reorient *input/output* panels + - all ui panels can be *minimized/maximized* by clicking on their header + state is preserved across sessions and can be used to hide rarely used panels and declutter the workspace + - **Kanvas** re-order stages by clicking on active stage + order of stages determines order of images passed to model + - **Kanvas** *magic-wand* tool now works on mask layer and auto-creates mask based on perceptual tolerance + - **Gallery** add thumbnail size slider + - **Gallery** add quick info/download/delete buttons on thumbnail hover + - **Models** sortable columns, ability to remove a model + applies to models as well as huggingface cache entries + - **Server Info** add button *copy-to-clipboard* + useful for sharing your system info when asking for help in discord or github +- **Control** + - remove buttons: *input/control/process* + - move params *control input type* to control menu section + - remove "processed preview" from ui + preprocessor output can still be generated by clicking preview button in in control unit and it will render into normal output area +- **Internal** + - `offload` auto-reapply hook on error + - refactor `pip` installer, thanks @awsr + - remove obsolete `lora` stepwise and functional code, thanks @awsr + - interrupt model loading between components + - patch `rich` for cleaner exception logging + - lint `ruff` strict and reduce exceptions + - lint `pylint` improvements + - lint `ty` readiness +- **Fixes** + - add missing `jquery` and `sparkline` js scripts + - save handle already decoded images + - `ernie-image` preview + - `lora` false deactivate + - `kandinsky-5` t2i/i2i workflows + - progress do not timeout when paused + - faster server shutdown/restart, thanks @awsr + - `openvino` force offload none + - `lut` file handling + - warn on pipeline ignoring `cfg` + - detailer `segmentation`, thanks @awsr + - `ipex` invalid device type + - cache network thumbnails + - `scripts` corrupting control ui state + - avoid `callback` duplicate registrations + - pipeline task change causing loss of info on loaded `lora` + - `detailer` handle `lora` internally + - vae preview flashes previous image + - `torch.compile` improvements + - `gradio` preprocess exception handling + - `ipadapters` with offloading + - `kanvas` outpaint + - `network` preview handle invalid image + - `schedulers` improve *set_timesteps* handling + - `schedulers` improve *scale_noise* handling + ## Update for 2026-04-28 ### Highlights for 2026-04-28 diff --git a/README.md b/README.md index 9f1a72a41..baa72b88e 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ SD.Next is feature-rich with a focus on performance, flexibility, and user exper SD.Next includes many features not found in other WebUIs, such as: - **SDNQ**: State-of-the-Art quantization engine - Use pre-quantized or run with quantizaion on-the-fly for up to 4x VRAM reduction with no or minimal quality and performance impact + Use pre-quantized or run with quantization on-the-fly for up to 4x VRAM reduction with no or minimal quality and performance impact - **Balanced Offload**: Dynamically balance CPU and GPU memory to run larger models on limited hardware - **Captioning** with 150+ **OpenCLiP** models, **Tagger** with **WaifuDiffusion** and **DeepDanbooru** models, and 25+ built-in **VLMs** - **Image Processing** with full image correction color-grading suite of tools diff --git a/TODO.md b/TODO.md index 3e58efbe8..dbc4230e5 100644 --- a/TODO.md +++ b/TODO.md @@ -4,13 +4,13 @@ ### Assigned -- Gallery: thumb-size, quick delete/download/info @vladmandic - Chat-based interface, @vladmandic -- Multi-image inputs, @vladmandic - Control tab verify overrides handling, @vladmandic - Reimplement `llama` remover for Kanvas, @vladmandic -- Integrate [Depth3D](https://github.com/vladmandic/sd-extension-depth3d), @vladmandic +- Implement [pruna](https://github.com/PrunaAI/pruna), @vladmandic +- Change params to default, @vladmandic +- Detailer postprocessing, @CalamitousFelicitousness - Cloud providers, @CalamitousFelicitousness - Video processing add full API support, @CalamitousFelicitousness @@ -20,7 +20,6 @@ - `RIFE` in processing - `SeedVR2` in processing - Video model loader: Add video models to Reference -- REMBG add - UI Lite vs Expert mode - TensorRT acceleration - Auto handle scheduler `prediction_type` @@ -56,21 +55,13 @@ TODO: Investigate which models are diffusers-compatible and prioritize! ### Image - [JoyAI-Image-Edit](https://github.com/huggingface/diffusers/pull/13444) (pr in-progress) -- [Lumina-DiMOO](https://github.com/huggingface/diffusers/pull/12468) (pr stalled) -- [Step1X-Edit](https://github.com/huggingface/diffusers/pull/12249) (pr stalled) -- [VIBE Image Edit](https://huggingface.co/iitolstykh/VIBE-Image-Edit) (diffusers-compatible) - [nVidia Cosmos-Predict-2.5](https://huggingface.co/nvidia/Cosmos-Predict2.5-2B) (in diffusers) - [nVidia Cosmos-Transfer-2.5](https://huggingface.co/nvidia/Cosmos-Transfer2.5-2B) (in diffusers) -- [UltraFlux](https://huggingface.co/Owen777/UltraFlux-v1) (diffusers-compatible) - [Tencent HY-WU](https://huggingface.co/tencent/HY-WU) (transformers-compatible) -- [Mugen](https://huggingface.co/CabalResearch/Mugen) (sdxl with flux vae experiment, not clean) -- [Liquid](https://github.com/FoundationVision/Liquid) (autoregressive, not clean) ### Video - [HY-OmniWeaving](https://huggingface.co/tencent/HY-OmniWeaving) -- [LTX-Condition](https://huggingface.co/Lightricks/LTX-2) -- [LTX-Distilled](https://huggingface.co/Lightricks/LTX-2) - [OpenMOSS MOVA](https://huggingface.co/OpenMOSS-Team/MOVA-720p) - [Wan2.2-Animate](https://huggingface.co/Wan-AI/Wan2.2-Animate-14B) - [Wan2.1-T2V-14B-CausVid](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-CausVid) @@ -84,12 +75,11 @@ TODO: Investigate which models are diffusers-compatible and prioritize! - [Sana I2V](https://huggingface.co/Efficient-Large-Model/SANA-Video_2B_480p_diffusers) - [Wan-2.2 S2V](https://huggingface.co/Wan-AI/Wan2.2-S2V-14B) - [Meituan LongCat-Video](https://huggingface.co/meituan-longcat/LongCat-Video) -- [LTXVideo LongMulti](https://huggingface.co/Lightricks/LTX-Video-0.9.8-13B-distilled) - [Phantom HuMo](https://github.com/Phantom-video/Phantom) - [CausVid-Plus](https://github.com/goatWu/CausVid-Plus/) - [LivePortrait](https://github.com/KwaiVGI/LivePortrait) -- [Magi (SandAI)](https://github.com/SandAI-org/MAGI-1) -- [Ming (inclusionAI)](https://github.com/inclusionAI/Ming) +- [SandAI Magi](https://github.com/SandAI-org/MAGI-1) +- [inclusionAI Ming](https://github.com/inclusionAI/Ming) - [HummingbirdXT](https://huggingface.co/amd/HummingbirdXT) - [DiffusionForcing](https://github.com/kwsong0113/diffusion-forcing-transformer) - [ByteDance Lynx](https://github.com/bytedance/lynx) @@ -155,20 +145,30 @@ TODO: Investigate which models are diffusers-compatible and prioritize! - Background removal model trained on Bria FIBO dataset - Created: 2025-08 | Updated: 2025-09 | Stars: N/A (private model) +### Rejected + +- [Mugen](https://huggingface.co/CabalResearch/Mugen) (sdxl with flux vae experiment, not clean) +- [Liquid](https://github.com/FoundationVision/Liquid) (autoregressive, not clean) + ## Code TODO > npm run todo ```code -installer.py:642:15: W0511: TODO rocm: switch to pytorch source when it becomes available (fixme) -modules/transformer_cache.py:29:61: W0511: TODO fc: autodetect tensor format based on model (fixme) -modules/transformer_cache.py:30:50: W0511: TODO fc: autodetect distilled based on model (fixme) -modules/processing_class.py:404:32: W0511: TODO processing: remove duplicate mask params (fixme) -modules/sd_samplers_diffusers.py:355:31: W0511: TODO enso-required (fixme) -modules/sd_models.py:1356:5: W0511: TODO model load: implement model in-memory caching (fixme) -modules/ui_models_load.py:257:5: W0511: TODO loader: load receipe (fixme) -modules/ui_models_load.py:264:5: W0511: TODO loader: save receipe (fixme) -modules/sd_hijack_hypertile.py:123:17: W0511: TODO hypertile: vae breaks when using non-standard sizes (fixme) -modules/sd_unet.py:77:39: W0511: TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage (fixme) -modules/modular_guiders.py:66:51: W0511: TODO: guiders (fixme) +installer.py:TODO rocm: switch to pytorch source when it becomes available +modules/control/run.py:TODO modernui: monkey-patch for missing tabs.select event +modules/history.py:TODO: apply metadata, preview, load/save +modules/image/resize.py:TODO resize image: enable full VAE mode for resize-latent +modules/lora/lora_load.py:TODO lora: add t5 key support for sd35/f1 +modules/masking.py:TODO: additional masking algorithms +modules/modular_guiders.py:TODO: guiders +modules/processing_class.py:TODO processing: remove duplicate mask params +modules/sd_hijack_hypertile.py:TODO hypertile: vae breaks when using non-standard sizes +modules/sd_models.py:TODO model load: implement model in-memory caching +modules/sd_samplers_diffusers.py:TODO enso-required +modules/sd_unet.py:TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage +modules/transformer_cache.py:TODO fc: autodetect distilled based on model +modules/transformer_cache.py:TODO fc: autodetect tensor format based on model +modules/ui_models_load.py:TODO loader: load receipe +modules/ui_models_load.py:TODO loader: save receipe ``` diff --git a/data/reference-distilled.json b/data/reference-distilled.json index ad7b07ddb..4588ae5c1 100644 --- a/data/reference-distilled.json +++ b/data/reference-distilled.json @@ -36,6 +36,16 @@ "skip": true, "extras": "sampler: Default, cfg_scale: 4.5" }, + "HiDream-O1 Image Dev": { + "path": "HiDream-ai/HiDream-O1-Image-Dev", + "preview": "HiDream-ai--HiDream-O1-Image-Dev.jpg", + "desc": "HiDream-O1-Image-Dev is the distilled 8B HiDream-O1 variant tuned for 28-step fast generation using flash flow scheduling.", + "skip": true, + "extras": "sampler: Flash, steps: 28, cfg_scale: 0.0", + "size": 35.2, + "tags": "distilled", + "date": "2026 May" + }, "Qwen-Image-Lightning": { "path": "vladmandic/Qwen-Lightning", "preview": "vladmandic--Qwen-Lightning.jpg", diff --git a/data/reference-quant.json b/data/reference-quant.json index edeb0dbf2..67254baf4 100644 --- a/data/reference-quant.json +++ b/data/reference-quant.json @@ -242,7 +242,7 @@ "desc": "ERNIE-Image is a text-to-image diffusion transformer model that combines a Mistral3 text encoder with a FlowMatch transformer and Flux2-style VAE for 1024px image generation.", "skip": true, "extras": "sampler: Default, cfg_scale: 4.0, steps: 50", - "size": 23.93, + "size": 7.52, "date": "2026 April" }, "Baidu ERNIE-Image-Turbo sdnq-dynamic-int4": { @@ -251,8 +251,44 @@ "desc": "ERNIE-Image-Turbo is a distilled ERNIE-Image variant optimized for fast generation with fewer denoising steps.", "skip": true, "extras": "sampler: Default, cfg_scale: 1.0, steps: 8", - "size": 23.37, + "size": 7.52, "tags": "distilled", "date": "2026 April" + }, + "Anima Preview 3 sdnq-dynamic-int8": { + "path": "vladmandic/Anima-Preview-3-diffusers-SDNQ-8bit-dynamic", + "preview": "CalamitousFelicitousness--Anima-Preview-3-sdnext-diffusers.jpg", + "desc": "Anima Preview V3 with extended 1024-resolution training and expanded dataset coverage for less common artists. A 2B parameter anime-focused text-to-image model based on modified Cosmos-Predict-2B with Qwen3-0.6B text encoder, created by CircleStone Labs and Comfy Org.", + "tags": "community", + "date": "2026 April", + "size": 3.19, + "skip": true + }, + "Anima Preview 3 Turbo sdnq-dynamic-int8": { + "path": "vladmandic/Anima-Preview-3-turbo-diffusers-SDNQ-8bit-dynamic", + "preview": "CalamitousFelicitousness--Anima-Preview-3-sdnext-diffusers.jpg", + "desc": "Anima Preview V3 with extended 1024-resolution training and expanded dataset coverage for less common artists. A 2B parameter anime-focused text-to-image model based on modified Cosmos-Predict-2B with Qwen3-0.6B text encoder, created by CircleStone Labs and Comfy Org.", + "tags": "community", + "date": "2026 April", + "size": 3.19, + "skip": true + }, + "HiDream-O1 Image sdnq-dynamic-int8": { + "path": "vladmandic/HiDream-O1-Image-SDNQ-8bit-dynamic", + "desc": "HiDream-O1-Image is an 8B pixel-level unified transformer model for text-to-image generation, instruction editing, and multi-reference personalization up to 2048x2048.", + "preview": "HiDream-ai--HiDream-O1-Image.jpg", + "skip": true, + "extras": "sampler: Default", + "size": 10.34, + "date": "2026 May" + }, + "HiDream-O1 Image Dev sdnq-dynamic-int8": { + "path": "vladmandic/HiDream-O1-Image-Dev-SDNQ-8bit-dynamic", + "desc": "HiDream-O1-Image is an 8B pixel-level unified transformer model for text-to-image generation, instruction editing, and multi-reference personalization up to 2048x2048.", + "preview": "HiDream-ai--HiDream-O1-Image.jpg", + "skip": true, + "extras": "sampler: Default", + "size": 10.34, + "date": "2026 May" } } diff --git a/data/reference.json b/data/reference.json index 8fe66e55d..0a00af7cf 100644 --- a/data/reference.json +++ b/data/reference.json @@ -143,6 +143,16 @@ "date": "2025 January" }, + "Owen777 UltraFlux-v1": { + "path": "Owen777/UltraFlux-v1", + "preview": "Owen777--UltraFlux-v1.jpg", + "desc": "UltraFlux-v1 is a FLUX.1-dev based text-to-image model optimized for native 4K and multi-aspect-ratio generation with improved composition consistency.", + "skip": true, + "extras": "sampler: Default, cfg_scale: 4.0, steps: 50", + "size": 33.0, + "date": "2025 November" + }, + "Z-Image": { "path": "Tongyi-MAI/Z-Image", "preview": "Tongyi-MAI--Z-Image.jpg", @@ -622,6 +632,15 @@ "size": 20.75, "date": "2025 January" }, + "AlphaVLLM Lumina DiMOO": { + "path": "Alpha-VLLM/Lumina-DiMOO", + "desc": "Lumina-DiMOO is an omni diffusion large language model for multimodal generation and understanding with text-to-image, image editing, and multimodal understanding capabilities.", + "preview": "Alpha-VLLM--Lumina-DiMOO.jpg", + "skip": true, + "extras": "sampler: Default", + "size": 0, + "date": "2025 September" + }, "HiDream-I1 Fast": { "path": "HiDream-ai/HiDream-I1-Fast", @@ -650,6 +669,15 @@ "size": 58.4, "date": "2025 April" }, + "HiDream-O1 Image": { + "path": "HiDream-ai/HiDream-O1-Image", + "desc": "HiDream-O1-Image is an 8B pixel-level unified transformer model for text-to-image generation, instruction editing, and multi-reference personalization up to 2048x2048.", + "preview": "HiDream-ai--HiDream-O1-Image.jpg", + "skip": true, + "extras": "sampler: Default", + "size": 35.2, + "date": "2026 May" + }, "HiDream-E1 Full": { "path": "HiDream-ai/HiDream-E1-Full", "desc": "HiDream-E1 is an image editing model built on HiDream-I1.", @@ -924,6 +952,36 @@ "extras": "sampler: Default, cfg_scale: 3.5", "size": 16.2, "date": "2025 December" + }, + + "StepFun Step1X-Edit v1.1": { + "path": "stepfun-ai/Step1X-Edit-v1p1-diffusers", + "preview": "stepfun-ai--Step1X-Edit-v1p1-diffusers.jpg", + "desc": "Multimodal image editing model using Step1X transformer architecture with Qwen2.5-VL text encoding, trained with Flow Matching scheduler for high-quality in-context image edits and refinements.", + "skip": true, + "extras": "sampler: Default", + "size": 24.85, + "date": "2025 September" + }, + + "VIBE Image Edit": { + "path": "vladmandic/VIBE-Image-Edit", + "preview": "vladmandic--VIBE-Image-Edit.jpg", + "desc": "VIBE is an open-source text-guided image editing model combining Sana1.5-1.6B diffusion backbone with Qwen3-VL multimodal conditioning for fast, instruction-based edits.", + "skip": true, + "extras": "sampler: Default, cfg_scale: 4.5, image_guidance_scale: 1.2, steps: 20", + "size": 9.27, + "date": "2025 December" + }, + + "JoyAI Image Edit": { + "path": "jdopensource/JoyAI-Image-Edit-Diffusers", + "preview": "jdopensource--JoyAI-Image-Edit-Diffusers.jpg", + "desc": "JoyAI Image Edit is a Diffusers-native image editing model that combines a JoyImageEdit transformer with Qwen3-VL multimodal conditioning for instruction-guided edits.", + "skip": true, + "size": 50.31, + "extras": "sampler: Default", + "date": "2026 April" } } diff --git a/eslint.config.mjs b/eslint.config.mjs index 1b6120ae1..403755e76 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -102,6 +102,7 @@ const jsConfig = defineConfig([ idbPut: 'readonly', idbDel: 'readonly', idbAdd: 'readonly', + initTableSorter: 'readonly', idbCount: 'readonly', idbFolderCleanup: 'readonly', idbClearAll: 'readonly', diff --git a/extensions-builtin/sd-extension-chainner b/extensions-builtin/sd-extension-chainner index d4eab2166..1ac7d87cf 160000 --- a/extensions-builtin/sd-extension-chainner +++ b/extensions-builtin/sd-extension-chainner @@ -1 +1 @@ -Subproject commit d4eab2166e4d9b52e42924cc942198f9e22eb916 +Subproject commit 1ac7d87cf70f51d958953133fb457d7084baf701 diff --git a/extensions-builtin/sdnext-kanvas b/extensions-builtin/sdnext-kanvas index 9a76f2093..8aa27c614 160000 --- a/extensions-builtin/sdnext-kanvas +++ b/extensions-builtin/sdnext-kanvas @@ -1 +1 @@ -Subproject commit 9a76f209312dc88b267af126c60fc8b368678131 +Subproject commit 8aa27c6143f9cae3d3914dd9203918da555c5ca9 diff --git a/extensions-builtin/sdnext-modernui b/extensions-builtin/sdnext-modernui index f81771717..c5382ac49 160000 --- a/extensions-builtin/sdnext-modernui +++ b/extensions-builtin/sdnext-modernui @@ -1 +1 @@ -Subproject commit f81771717424299e99542b2ceb88e3cca9f7f16c +Subproject commit c5382ac496bf71dbd6a1100d35f4c3a204d0fd66 diff --git a/html/locale_ar.json b/html/locale_ar.json index 596040792..b1c1d3cb5 100644 --- a/html/locale_ar.json +++ b/html/locale_ar.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "وسائط الإدخال", "reload": "", "hint": "إضافة صورة إدخال لاستخدامها في معالجة التحويل من صورة إلى صورة، أو التلوين (Inpaint)، أو التحكم" diff --git a/html/locale_bn.json b/html/locale_bn.json index f7fe99ac3..252670494 100644 --- a/html/locale_bn.json +++ b/html/locale_bn.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "ইনপুট মিডিয়া", "reload": "", "hint": "ইমেজ-টু-ইমেজ, ইনপেইন্ট বা কন্ট্রোল প্রসেসিংয়ের জন্য ইনপুট ছবি যোগ করুন" diff --git a/html/locale_de.json b/html/locale_de.json index 1bdd46a1a..9fa94e636 100644 --- a/html/locale_de.json +++ b/html/locale_de.json @@ -4817,7 +4817,7 @@ }, { "id": 14, - "label": "Input Media", + "label": "Input", "localized": "Eingabemedien", "reload": "", "hint": "Eingabebild hinzufügen, das für Image-to-Image-, Inpaint- oder Control-Verarbeitung verwendet werden soll" diff --git a/html/locale_en.json b/html/locale_en.json index db1f36ed2..e31678ded 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -24,7 +24,7 @@ {"id":"xyz_grid_x_list","label":"⊜","localized":"","hint":"Fill","ui":"script_xyz_grid_script"}, {"id":"txt2img_caption_output","label":"","localized":"","hint":"Caption image","ui":"txt2img"}, {"id":"txt2img_image_fit","label":"⁜","localized":"","hint":"Cycle image fit method","ui":"txt2img"}, - {"id":"","label":"➠ Control","localized":"","hint":"Transfer image to control interface.

    Right-click this button to transfer only the prompt or all generation parameters to the Images tab without sending the image itself.","ui":"txt2img"}, + {"id":"","label":"➠ Control","localized":"","hint":"Transfer image to the Images tab.

    Right-click this button to transfer only the prompt or all generation parameters without sending the image itself.","ui":"txt2img"}, {"id":"","label":"➠ Text","localized":"","hint":"Transfer image to text interface","ui":"txt2img"}, {"id":"","label":"➠ Image","localized":"","hint":"Transfer image to image interface","ui":"txt2img"}, {"id":"","label":"➠ Process","localized":"","hint":"Transfer image to process interface","ui":"txt2img"}, @@ -72,16 +72,16 @@ {"id":"","label":"Advanced Options","localized":"","hint":"","ui":"settings_sd"}, {"id":"","label":"Appearance","localized":"","hint":"","ui":"settings_ui"}, {"id":"","label":"Answer","localized":"","hint":"","ui":"caption"}, - {"id":"","label":"Adjust start","localized":"","hint":"Starting step when sigma adjust occurs","ui":"txt2img"}, - {"id":"","label":"Adjust end","localized":"","hint":"Ending step when sigma adjust occurs","ui":"txt2img"}, + {"id":"","label":"Adjust start","localized":"","hint":"Lower bound of the denoising window where Sigma adjust is active, as a fraction of the noise schedule (1.0 = pure noise, 0.0 = clean image).
    The adjustment stops once denoising progresses past this point, so higher values end the effect earlier.

    Default 0.2 leaves the final ~20% of the schedule unmodified.","ui":"txt2img"}, + {"id":"","label":"Adjust end","localized":"","hint":"Upper bound of the denoising window where Sigma adjust is active, as a fraction of the noise schedule (1.0 = pure noise, 0.0 = clean image).
    The adjustment only begins once denoising has progressed past this point, so lower values delay the effect further into the run.

    Default 0.8 leaves the first ~20% of the schedule unmodified.","ui":"txt2img"}, {"id":"","label":"Autocomplete","localized":"","hint":"Enable or disable Tag Autocomplete. Choose which dictionaries are used for prompt autocompletion in Extras","ui":"control"}, {"id":"","label":"AutoGuidance dropout","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"AutoGuidance layers","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"AutoGuidance config","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"APG momentum","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"APG rescale","localized":"","hint":"","ui":"txt2img"}, - {"id":"","label":"Attention guidance","localized":"","hint":"CFG scale used for with PAG: Perturbed-Attention Guidance","ui":"txt2img"}, - {"id":"","label":"Adaptive scaling","localized":"","hint":"Adaptive modifier for attention guidance scale","ui":"txt2img"}, + {"id":"","label":"Attention guidance","localized":"","hint":"Dual-purpose slider that activates one of two guidance mechanisms depending on the loaded model.
    - SD 1.5 and SDXL: enables Perturbed Attention Guidance (PAG). Sdnext silently swaps the pipeline to a PAG-aware variant and steers generation away from a self-attention-perturbed prediction, improving structure and detail. Used in addition to the regular Guidance scale.
    - Flux, QwenImage, HiDream, Hunyuan Video, Sana, and other flow-matching models: routes to true_cfg_scale, enabling classifier-free guidance with negative prompts on models that don't natively use CFG.
    On other models the slider has no effect.

    Set to 0 to disable.
    Disabled by default.","ui":"txt2img"}, + {"id":"","label":"Adaptive scaling","localized":"","hint":"Decay rate for the Perturbed Attention Guidance (PAG) component of Attention guidance. Higher values cause PAG strength to decay faster across the denoising steps.

    Only takes effect on SD 1.5 and SDXL when Attention guidance is non-zero (the only path that actually enables PAG). Has no effect on Flux, QwenImage, HiDream, or other flow-matching models that route Attention guidance to true_cfg_scale instead.

    Default 0.5 applies moderate decay. Set to 0 to keep PAG at full strength for the entire process.","ui":"txt2img"}, {"id":"","label":"Apply to hires","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"Active IP adapters","localized":"","hint":"Number of active IP adapter","ui":"txt2img"}, {"id":"","label":"Adapter","localized":"","hint":"IP adapter model","ui":"txt2img"}, @@ -104,9 +104,9 @@ {"id":"","label":"ACI: Mask blur","localized":"","hint":"Adjust blur to apply a smooth transition between image and inpainted area. (Recommended value = 0 for sharpness)","ui":"script_automatic_color_inpaint"}, {"id":"","label":"Adaptive restore","localized":"","hint":"","ui":"script_instantir"}, {"id":"","label":"Apply noise","localized":"","hint":"","ui":"script_softfill"}, - {"id":"","label":"Auto min score","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Auto-segment","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Auto-mask","localized":"","hint":"","ui":"control"}, + {"id":"","label":"Auto min score","localized":"","hint":"Minimum stability score for masks produced by Auto-segment (Facebook SAM and SlimSAM models).
    Higher values keep only the most confident masks; lower values include more candidates including noisier ones. Has no effect on Rembg models or on the Auto-mask threshold/edge methods.

    Default 0.8.","ui":"control"}, + {"id":"","label":"Auto-segment","localized":"","hint":"Automatic foreground segmentation model. Runs on the input image to generate a mask without manual painting.
    None: no auto-segmentation; the manually painted mask is used instead.
    Facebook SAM ViT (Base/Large/Huge): Meta's Segment Anything Model. Quality scales with size, Huge is the most accurate but slowest and largest in VRAM.
    SlimSAM Uniform / Uniform Tiny: pruned, faster SAM variants with a good speed/quality tradeoff for repetitive workflows.
    Rembg BEN2 / Silueta / U2Net / U2Net human / ISNet general / ISNet anime: lightweight background-removal models. Pick by content: U2Net human or BEN2 for people, ISNet anime for illustrations, U2Net or Silueta for general subjects.

    Models are downloaded on first use.
    Default None.","ui":"control"}, + {"id":"","label":"Auto-mask","localized":"","hint":"Automatic mask generation from the input image using simple computer-vision methods (no neural model). Runs only when no manual mask is painted and Auto-segment is set to None.
    None: disabled.
    Threshold: Otsu binary threshold; everything brighter than the auto-computed threshold becomes the mask. Works for high-contrast subjects on plain backgrounds.
    Edge: detects contours and keeps the largest ones; useful for masking distinct objects with clear outlines.
    Grayscale: uses the image's luminance as the mask intensity, producing a soft, gradient-style mask.

    Default None.","ui":"control"}, {"id":"","label":"Active","localized":"","hint":"","ui":"control"}, {"id":"","label":"Attention","localized":"","hint":"","ui":"control"}, {"id":"","label":"Adain","localized":"","hint":"","ui":"control"}, @@ -163,13 +163,13 @@ {"id":"","label":"BitsAndBytes","localized":"","hint":"","ui":"settings_quantization"}, {"id":"","label":"Batch count","localized":"","hint":"How many batches of images to create (has no impact on generation performance or VRAM usage)","ui":"txt2img"}, {"id":"","label":"Batch size","localized":"","hint":"How many image to create in a single batch (increases generation performance at cost of higher VRAM usage)","ui":"txt2img"}, - {"id":"","label":"Beta schedule","localized":"","hint":"Defines how beta (noise strength per step) grows. Options:
    - default: the model default
    - linear: evenly decays noise per step
    - scaled: squared version of linear, used only by Stable Diffusion
    - cosine: smoother decay, often better results with fewer steps
    - sigmoid: sharp transition, experimental","ui":"txt2img"}, + {"id":"","label":"Beta schedule","localized":"","hint":"Defines how beta (noise strength per step) grows. Options:
    - default: the model default
    - linear: evenly decays noise per step
    - scaled: squared version of linear, used only by Stable Diffusion
    - cosine: smoother decay, often better results with fewer steps
    - sigmoid: sharp transition, experimental","ui":"txt2img"}, {"id":"","label":"Base shift","localized":"","hint":"Minimum shift value for low resolutions when using dynamic shifting.","ui":"txt2img"}, {"id":"","label":"Brightness","localized":"","hint":"Adjusts overall image brightness.
    Positive values lighten the image, negative values darken it.

    Applied uniformly across all pixels in linear space.","ui":"txt2img"}, {"id":"","label":"Block","localized":"","hint":"","ui":"script_kohya_hires_fix"}, {"id":"","label":"Block size","localized":"","hint":"","ui":"script_nudenet"}, {"id":"","label":"Banned words","localized":"","hint":"","ui":"script_nudenet"}, - {"id":"","label":"Blur","localized":"","hint":"","ui":"img2img"}, + {"id":"","label":"Blur","localized":"","hint":"Softens the mask edge with a Gaussian blur so the boundary between masked and unmasked regions blends gradually instead of cutting hard.
    Reduces visible seams at the mask edge after generation. Combine with a small Dilate to push the soft transition just outside the original mask.
    Sigma scales with image size: at value 0.05 on a 1024px image the blur radius is roughly 13 pixels.

    Set to 0 to disable.
    Default 0.","ui":"img2img"}, {"id":"","label":"Batch input directory","localized":"","hint":"","ui":"img2img"}, {"id":"","label":"Batch output directory","localized":"","hint":"","ui":"img2img"}, {"id":"","label":"Batch mask directory","localized":"","hint":"","ui":"img2img"}, @@ -210,7 +210,7 @@ {"id":"","label":"Copy","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"Composite","localized":"","hint":"","ui":"img2img"}, {"id":"control_params_elements","label":"Control","localized":"","hint":"Create image with full guidance","ui":"control"}, - {"id":"","label":"ControlNet","localized":"","hint":"ControlNet is an advanced guidance model","ui":"control"}, + {"id":"","label":"ControlNet","localized":"","hint":"ControlNet is an advanced guidance model","ui":"control"}, {"id":"caption_tab_controls","label":"Controls","localized":"","hint":"","ui":"caption"}, {"id":"","label":"CaptionCaption","localized":"","hint":"","ui":"caption"}, {"id":"btn_console","label":"Console","localized":"","hint":""}, @@ -234,14 +234,14 @@ {"id":"","label":"Create Video","localized":"","hint":"","ui":"extras"}, {"id":"","label":"ChronoEdit","localized":"","hint":"","ui":"settings_model_options"}, {"id":"","label":"Cross Attention","localized":"","hint":"","ui":"settings_cuda"}, - {"id":"","label":"CLiP Skip","localized":"","hint":"Early stopping parameter for CLIP model; 1 is stop at last layer as usual, 2 is stop at penultimate layer, etc","ui":"settings_advanced"}, + {"id":"","label":"CLiP Skip","localized":"","hint":"Early stopping parameter for the CLiP text encoder; 1 is stop at last layer as usual, 2 is stop at penultimate layer, etc","ui":"settings_advanced"}, {"id":"","label":"Cache-DiT","localized":"","hint":"","ui":"settings_advanced"}, {"id":"","label":"CFG-Zero","localized":"","hint":"","ui":"settings_advanced"}, {"id":"","label":"Cache folders","localized":"","hint":"","ui":"settings_system-paths"}, {"id":"","label":"Custom model loader","localized":"","hint":"","ui":"models_loader_tab"}, {"id":"","label":"Client log","localized":"","hint":""}, - {"id":"","label":"CLIP Analysis","localized":"","hint":"","ui":"caption"}, - {"id":"","label":"Context","localized":"","hint":"","ui":"txt2img"}, + {"id":"","label":"CLiP Analysis","localized":"","hint":"Detailed analysis output from OpenCLiP, listing the matched medium, artist, movement, trending, and flavor terms.
    Populated when you click the Analyze button next to the OpenCLiP Caption button.","ui":"caption"}, + {"id":"","label":"Context","localized":"","hint":"Behavior of the Context aware resize Mode (no effect with any other Mode).
    Add: extend the image by inserting new pixels along smooth, featureless paths (like sky or plain backgrounds), avoiding detailed regions.
    Remove: shrink the image by removing pixels along the same low-detail paths.
    Forward: examine what the image will look like after each seam is added or removed, picking the paths that minimize visible damage. Slower but higher quality.
    Backward: pick paths based on existing pixel contrast in the image. Faster, classic seam-carving.","ui":"resize"}, {"id":"","label":"Contrast","localized":"","hint":"Adjusts the difference between light and dark areas.
    Positive values increase contrast, making darks darker and lights brighter.
    Negative values flatten the tonal range toward a more uniform appearance.","ui":"txt2img"}, {"id":"","label":"Color temp","localized":"","hint":"Shifts color temperature in Kelvin.
    Lower values (e.g., 2000K) produce a warm, amber tone. Higher values (e.g., 12000K) produce a cool, bluish tone.

    Default 6500K is neutral daylight. Works by scaling R/G/B channels to simulate the target white point.","ui":"txt2img"}, {"id":"","label":"CLAHE clip","localized":"","hint":"Clip limit for Contrast Limited Adaptive Histogram Equalization.
    Higher values allow more local contrast enhancement, which brings out detail in flat regions.

    Set to 0 to disable. Typical values are 1.0–3.0. Very high values can introduce noise amplification.","ui":"txt2img"}, @@ -249,7 +249,7 @@ {"id":"","label":"Correction mode","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"Crop to portrait","localized":"","hint":"Crop input image to portrait-only before using it as IP adapter input","ui":"txt2img"}, {"id":"","label":"Concept Tokens","localized":"","hint":"","ui":"script_consistory"}, - {"id":"","label":"Colormap","localized":"","hint":"","ui":"script_daam"}, + {"id":"","label":"Colormap","localized":"","hint":"OpenCV color palette used to visualize the mask or heatmap overlay.
    For control masks, this is the palette applied when Preview is set to Color or Composite. Pick one that contrasts well with the input image so the overlay stays readable.

    Default pink (control mask), jet (DAAM script).","ui":"script_daam"}, {"id":"","label":"Cosine scale 1","localized":"","hint":"","ui":"script_demofusion"}, {"id":"","label":"Cosine scale 2","localized":"","hint":"","ui":"script_demofusion"}, {"id":"","label":"Cosine scale 3","localized":"","hint":"","ui":"script_demofusion"}, @@ -267,9 +267,9 @@ {"id":"","label":"Control override denoise strength","localized":"","hint":"","ui":"script_flux_tools"}, {"id":"","label":"Color variation","localized":"","hint":"","ui":"script_outpainting"}, {"id":"","label":"Change rate","localized":"","hint":"","ui":"script_video"}, - {"id":"","label":"Context after","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Context mask","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Control only","localized":"","hint":"This uses only the Control input below as the source for any ControlNet or IP Adapter type tasks based on any of our various options.","ui":"control"}, + {"id":"","label":"Context after","localized":"","hint":"Behavior of the Context aware resize Mode applied to the output image after the model finishes generating (Post sub-tab in the Size accordion; no effect with any other Mode).
    Add: extend the image by inserting new pixels along smooth, featureless paths (like sky or plain backgrounds), avoiding detailed regions.
    Remove: shrink the image by removing pixels along the same low-detail paths.
    Forward: examine what the image will look like after each seam is added or removed, picking the paths that minimize visible damage. Slower but higher quality.
    Backward: pick paths based on existing pixel contrast in the image. Faster, classic seam-carving.","ui":"control"}, + {"id":"","label":"Context mask","localized":"","hint":"Behavior of the Context aware resize Mode applied to the input mask image (used for inpainting, outpainting, or control masks; Mask sub-tab in the Size accordion; no effect with any other Mode).
    Add: extend the image by inserting new pixels along smooth, featureless paths (like sky or plain backgrounds), avoiding detailed regions.
    Remove: shrink the image by removing pixels along the same low-detail paths.
    Forward: examine what the image will look like after each seam is added or removed, picking the paths that minimize visible damage. Slower but higher quality.
    Backward: pick paths based on existing pixel contrast in the image. Faster, classic seam-carving.","ui":"control"}, + {"id":"","label":"Control only","localized":"","hint":"This uses only the Control input below as the source for any ControlNet or IP Adapter type tasks based on any of our various options.","ui":"control"}, {"id":"","label":"CN Mode","localized":"","hint":"","ui":"control"}, {"id":"","label":"CN Strength","localized":"","hint":"","ui":"control"}, {"id":"","label":"CN Start","localized":"","hint":"","ui":"control"}, @@ -280,14 +280,14 @@ {"id":"","label":"Coarse","localized":"","hint":"","ui":"control"}, {"id":"","label":"Color map","localized":"","hint":"","ui":"control"}, {"id":"","label":"Crop to fit","localized":"","hint":"If the dimensions of your source image (e.g. 512x510) deviate from your target dimensions (e.g. 1024x768) this function will fit your upscaled image into your target size image. Excess will be cropped","ui":"extras"}, - {"id":"","label":"CLiP Model","localized":"","hint":"CLIP model used for image-text similarity matching.
    Larger models (ViT-L, ViT-H) are more accurate but slower and use more VRAM.","ui":"caption"}, + {"id":"","label":"CLiP Model","localized":"","hint":"CLiP model used for image-text similarity matching.
    Larger models (ViT-L, ViT-H) are more accurate but slower and use more VRAM.","ui":"caption"}, {"id":"","label":"Caption Model","localized":"","hint":"BLIP model used to generate the initial image caption.
    The caption model describes the image content which CLiP then enriches with style and flavor terms.","ui":"caption"}, - {"id":"","label":"clip: max length","localized":"","hint":"","ui":"caption"}, - {"id":"","label":"clip: chunk size","localized":"","hint":"","ui":"caption"}, - {"id":"","label":"clip: min flavors","localized":"","hint":"","ui":"caption"}, - {"id":"","label":"clip: max flavors","localized":"","hint":"","ui":"caption"}, - {"id":"","label":"clip: intermediates","localized":"","hint":"","ui":"caption"}, - {"id":"","label":"clip: num beams","localized":"","hint":"","ui":"caption"}, + {"id":"","label":"clip: max length","localized":"Max Length","hint":"Maximum number of tokens in the generated caption.
    Higher values allow longer, more descriptive captions; lower values produce shorter ones.","ui":"caption"}, + {"id":"","label":"clip: chunk size","localized":"Chunk Size","hint":"Batch size for processing description candidates (flavors).
    Higher values speed up interrogation but increase VRAM usage.","ui":"caption"}, + {"id":"","label":"clip: min flavors","localized":"Min Flavors","hint":"Minimum number of descriptive tags (flavors) to keep in the final prompt.","ui":"caption"}, + {"id":"","label":"clip: max flavors","localized":"Max Flavors","hint":"Maximum number of descriptive tags (flavors) to keep in the final prompt.","ui":"caption"}, + {"id":"","label":"clip: intermediates","localized":"Intermediates","hint":"Size of the intermediate candidate pool when matching image features to descriptive tags (flavors).
    From this pool, the final tags are selected based on Min/Max Flavors. Higher values may improve quality but are slower.","ui":"caption"}, + {"id":"","label":"clip: num beams","localized":"CLiP Num Beams","hint":"Number of beams for beam search during caption generation.
    Higher values search more possibilities but are slower.

    Set to 1 to disable beam search.","ui":"caption"}, {"id":"","label":"Character threshold","localized":"","hint":"Confidence threshold for character-specific tags (e.g., character names, specific traits).
    Only tags with confidence above this threshold are included.
    Higher values are more selective, lower values include more potential matches.
    Not supported by DeepBooru models.","ui":"caption"}, {"id":"","label":"Cross-attention","localized":"","hint":"","ui":"component-8779"}, {"id":"","label":"cpu","localized":"","hint":"Uses cpu and RAM only: slowest but least likely to OOM","ui":"settings_sd"}, @@ -343,8 +343,8 @@ {"id":"","label":"Control settings","localized":"","hint":"","ui":"control"}, {"id":"","label":"Canny","localized":"","hint":"","ui":"control"}, {"id":"","label":"Condition","localized":"","hint":"","ui":"video"}, - {"id":"","label":"Caption: Advanced Options","localized":"","hint":"","ui":"caption"}, - {"id":"","label":"Caption: Batch","localized":"","hint":"","ui":"caption"}, + {"id":"","label":"Caption: Advanced Options","localized":"","hint":"Advanced configuration options for caption generation.
    Sampling parameters, length limits, and decoding behavior for the active backend (VLM, CLiP, or Tagger).","ui":"caption"}, + {"id":"","label":"Caption: Batch","localized":"","hint":"Process multiple images in a batch using the active caption backend.
    Captions are saved alongside the source images as .txt sidecar files when Save Caption Files is enabled.","ui":"caption"}, {"id":"","label":"Control elements","localized":"","hint":"Control elements are advanced models that can guide generation towards desired outcome","ui":"tab_control"} ], "d": [ @@ -365,22 +365,22 @@ {"id":"","label":"Download model from huggingface","localized":"","hint":"","ui":"models_huggingface_tab"}, {"id":"","label":"Dropdown","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"dynamic","localized":"","hint":"Dynamic shifting automatically adjusts the denoising schedule based on your image resolution.

    The scheduler interpolates between base_shift and max_shift based on actual image resolution.

    Enabling disables static Flow shift.","ui":"txt2img"}, - {"id":"","label":"Detailer models","localized":"","hint":"Select detection models to use for detailing","ui":"txt2img"}, + {"id":"","label":"Detailer models","localized":"","hint":"YOLO detection models used to find regions to re-render. Multiple models can be selected and they run in sequence.
    Models live in models/yolo. Filename hints at target: face-* detects faces, eyes-* detects eyes, hand-* detects hands, person-* detects whole subjects, and so on.
    Models with -seg in the name produce a precise segmentation outline (used when Use segmentation is on); the rest produce only bounding boxes.

    Per-model overrides can be appended with colon syntax, for example face-yolo8n:conf=0.5:strength=0.4.","ui":"txt2img"}, {"id":"","label":"Detailer list","localized":"","hint":"","ui":"txt2img"}, - {"id":"","label":"Detailer classes","localized":"","hint":"Specify specific classes to use if selected detailer model is a multi-class model","ui":"txt2img"}, - {"id":"","label":"Detailer prompt","localized":"","hint":"Use separate prompt for detailer. If not present, it will use primary prompt","ui":"txt2img"}, - {"id":"","label":"Detailer negative prompt","localized":"","hint":"Use separate negative prompt for detailer. If not present, it will use primary negative prompt","ui":"txt2img"}, - {"id":"","label":"Detailer steps","localized":"","hint":"Number of steps to run for detailer process","ui":"txt2img"}, - {"id":"","label":"Detailer strength","localized":"","hint":"Denoising strength of detailer process","ui":"txt2img"}, - {"id":"","label":"Detailer resolution","localized":"","hint":"","ui":"txt2img"}, + {"id":"","label":"Detailer classes","localized":"","hint":"Comma-separated list of class names to keep when the selected detailer model is multi-class (e.g., a YOLO model that detects faces, eyes, and hands all in one file).
    Only detections matching these labels are processed; everything else is dropped. Leave empty to accept all classes.

    Names must match the model's class names exactly (case-insensitive). Single-class models like a face-only detector ignore this field.","ui":"txt2img"}, + {"id":"","label":"Detailer prompt","localized":"","hint":"Optional dedicated prompt for the detailer pass.
    Leave empty to inherit the main prompt. Useful for steering the inpaint differently from the rest of the image: a face detailer can use just portrait, sharp eyes, detailed skin while the main prompt covers the full scene.

    The placeholder [PROMPT] (or [prompt]) is replaced with the original main prompt, so you can append to it: [PROMPT], detailed face.","ui":"txt2img"}, + {"id":"","label":"Detailer negative prompt","localized":"","hint":"Optional dedicated negative prompt for the detailer pass.
    Leave empty to inherit the main negative prompt. Same [PROMPT] / [prompt] placeholder behavior as the positive detailer prompt: it expands to the original main negative prompt.","ui":"txt2img"}, + {"id":"","label":"Detailer steps","localized":"","hint":"Number of sampling steps used for each detailer inpaint pass.
    Independent of the main generation steps. Higher values give cleaner detail but cost more time per detected region.

    Set to 0 to inherit the main generation step count.
    Default 10.","ui":"txt2img"}, + {"id":"","label":"Detailer strength","localized":"","hint":"Denoising strength of the detailer inpaint pass.
    Higher values regenerate more aggressively (more change to the detected region, more reliance on the prompt). Lower values stay closer to the original detection, only refining detail.
    Typical range 0.2 to 0.5: enough to fix distortions without losing identity. Above 0.7 the face/object can drift noticeably from the original.

    Set to 0 to skip the detailer pass entirely.
    Default 0.30.","ui":"txt2img"}, + {"id":"","label":"Detailer resolution","localized":"","hint":"Working resolution for the detailer inpaint pass. Each detected region is cropped (with Edge padding) and resized to this resolution before inpainting.
    Higher values give finer detail in the regenerated region but use more VRAM and time per detection. Match the model's native resolution for best results: 1024 for SDXL/SD3/Flux, 512 for SD 1.5.

    Default 1024.","ui":"txt2img"}, {"id":"","label":"Denoising batch size","localized":"","hint":"","ui":"script_demofusion"}, {"id":"","label":"Dilate tau","localized":"","hint":"","ui":"script_freescale"}, {"id":"","label":"Draw legend","localized":"","hint":"","ui":"script_xyz_grid_script"}, - {"id":"","label":"Denoising strength","localized":"","hint":"Determines how little respect the algorithm should have for image's content. At 0, nothing will change, and at 1 you'll get an unrelated image. With values below 1.0, processing will take less steps than the Sampling Steps slider specifies","ui":"img2img"}, + {"id":"","label":"Denoising strength","localized":"","hint":"Strength of img2img modification when an init image is supplied.
    Higher values move further from the init image and rely more on the prompt; lower values stay closer to the original.

    At 0.0 the init image passes through unchanged.
    At 1.0 the model builds a fresh image from scratch and effectively ignores the init image.

    Effect on step count is model-dependent:
    - SD 1.5 and SDXL: the configured Steps value is honored as the actual loop count; strength only controls how much noise is added to the init latent.
    - Flux, SD3, Hunyuan, Sana, Qwen and other DiT models: loop count is reduced proportionally; with strength 0.5 and 30 steps, only ~15 actually run.

    In the Images tab this only takes effect when Use init image is set to one of the init modes; with No: Control only it is ignored.
    Default 0.30.","ui":"img2img"}, {"id":"","label":"Denoise start","localized":"","hint":"Override denoise strength by stating how early base model should finish and when refiner should start. Only applicable to refiner usage. If set to 0 or 1, denoising strength will be used","ui":"img2img"}, {"id":"","label":"down","localized":"","hint":"","ui":"script_outpainting"}, {"id":"","label":"Decode chunks","localized":"","hint":"","ui":"script_video"}, - {"id":"","label":"Dilate","localized":"","hint":"","ui":"control"}, + {"id":"","label":"Dilate","localized":"","hint":"Expands the masked area outward by growing each masked pixel into its neighborhood.
    Useful for catching the edges around an object that the mask missed, or for giving the model more breathing room around the region being modified so the new content can blend with surrounding context.
    Kernel size scales with image size: at value 0.05 on a 1024px image the dilation reaches roughly 13 pixels in each direction.

    Set to 0 to disable.
    Default 0.","ui":"control"}, {"id":"","label":"Depth and normal","localized":"","hint":"","ui":"control"}, {"id":"","label":"Distance threshold","localized":"","hint":"","ui":"control"}, {"id":"","label":"Depth threshold","localized":"","hint":"","ui":"control"}, @@ -440,9 +440,9 @@ {"id":"","label":"Effects","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"Enable LayerSkipConfig","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"Enable refine pass","localized":"","hint":"Use a similar process as image to image to upscale and/or add detail to the final image. Optionally uses refiner model to enhance image details.","ui":"txt2img"}, - {"id":"","label":"Enable detailer pass","localized":"","hint":"Detect target objects such as face and reprocess it at higher resolution","ui":"txt2img"}, - {"id":"","label":"Edge padding","localized":"","hint":"Expand edge of masked area by this percentage","ui":"txt2img"}, - {"id":"","label":"Edge blur","localized":"","hint":"Blur edge of masked area by this percentage","ui":"txt2img"}, + {"id":"","label":"Enable detailer pass","localized":"","hint":"Runs an automatic touch-up pass after generation: a YOLO detector finds target regions (faces, eyes, hands, persons, etc.) and each detected region is re-rendered with inpaint at the configured detailer resolution.
    Useful for fixing distorted faces or hands at low base resolutions, sharpening eye detail, or adding a second-pass refinement to specific subjects.

    Default off.","ui":"txt2img"}, + {"id":"","label":"Edge padding","localized":"","hint":"Pixels added around each detection's bounding box when cropping the region for inpaint.
    Padding gives the inpaint pass surrounding context so the regenerated content can blend smoothly with the rest of the image. Too little causes hard seams; too much wastes resolution on areas that won't change.

    Default 20.","ui":"txt2img"}, + {"id":"","label":"Edge blur","localized":"","hint":"Pixel radius of the Gaussian blur applied to the inpaint mask edge.
    Softens the boundary between the regenerated region and the rest of the image so the paste-back blends instead of cutting hard.

    Set to 0 to disable.
    Default 10.","ui":"txt2img"}, {"id":"","label":"End","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"ETA","localized":"","hint":"","ui":"script_apg"}, {"id":"","label":"Enable FreeU","localized":"","hint":"","ui":"script_consistory"}, @@ -452,7 +452,7 @@ {"id":"","label":"Enhanced prompt","localized":"","hint":"The enhanced prompt output from the LLM","ui":"script_prompt_enhance"}, {"id":"","label":"Edit start","localized":"","hint":"","ui":"script_ledits"}, {"id":"","label":"Edit stop","localized":"","hint":"","ui":"script_ledits"}, - {"id":"","label":"Erode","localized":"","hint":"","ui":"control"}, + {"id":"","label":"Erode","localized":"","hint":"Shrinks the masked area inward by removing pixels along the edge.
    Useful for cleaning up speckle noise from auto-segmentation, or for pulling the mask back from object boundaries to avoid the model bleeding outside the intended region.
    Kernel size scales with image size: at value 0.05 on a 1024px image the erosion reaches roughly 13 pixels in each direction.

    Set to 0 to disable.
    Default 0.","ui":"control"}, {"id":"","label":"edge","localized":"","hint":"","ui":"control"}, {"id":"","label":"Ensemble size","localized":"","hint":"","ui":"control"}, {"id":"","label":"Enable","localized":"","hint":"","ui":"video"}, @@ -490,7 +490,7 @@ {"id":"","label":"Faster Cache","localized":"","hint":"","ui":"settings_advanced"}, {"id":"","label":"Folders","localized":"","hint":"","ui":"settings_saving-paths"}, {"id":"","label":"Fetch model preview metadata","localized":"","hint":"","ui":"models_metadata_tab"}, - {"id":"","label":"Flow shift","localized":"","hint":"Shift value for flowmatching models. Controls the distribution of denoising steps.

    Values:
    - >1.0: allocate more steps to early denoising (better structure)
    -<1.0: allocate more steps to late denoising (better fine details)
    - 1.0: balanced schedule

    Most flowmatching models use the value of 3 as default. Effectively inactive if dynamic shift is enabled.","ui":"txt2img"}, + {"id":"","label":"Flow shift","localized":"","hint":"Shift value for flowmatching models. Controls the distribution of denoising steps.

    Values:
    - >1.0: allocate more steps to early denoising (better structure)
    - <1.0: allocate more steps to late denoising (better fine details)
    - 1.0: balanced schedule

    Most flowmatching models use the value of 3 as default. Effectively inactive if dynamic shift is enabled.","ui":"txt2img"}, {"id":"","label":"FDG scales","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"FDG weights","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"FDG rescale space","localized":"","hint":"","ui":"txt2img"}, @@ -622,9 +622,9 @@ {"id":"","label":"Grid Options","localized":"","hint":"","ui":"settings_saving-images"}, {"id":"","label":"Grids","localized":"","hint":"","ui":"settings_saving-paths"}, {"id":"","label":"Guider","localized":"","hint":"","ui":"txt2img"}, - {"id":"","label":"Guidance scale","localized":"","hint":"Classifier Free Guidance scale: how strongly the image should conform to prompt. Lower values produce more creative results, higher values make it follow the prompt more strictly; recommended values between 5-10","ui":"txt2img"}, - {"id":"","label":"Guidance end","localized":"","hint":"Ends the effect of CFG and PAG early: A value of 1 acts as normal, 0.5 stops guidance at 50% of steps","ui":"txt2img"}, - {"id":"","label":"Guidance rescale","localized":"","hint":"Rescale guidance to avoid overexposed images at higher guidance values","ui":"txt2img"}, + {"id":"","label":"Guidance scale","localized":"","hint":"Classifier-Free Guidance scale. How strongly the image should conform to the prompt. Lower values produce more creative, loosely-prompted results; higher values follow the prompt more strictly but can oversaturate or burn out at very high values.

    Recommended values vary by architecture: 5-10 for SDXL/SD1.x, 3-5 for Flux and SD3, 7-10 for video models. Check the model card if unsure.

    Set to 1 (the slider's minimum) to disable guidance entirely. The model then runs only the conditional prediction with no negative-prompt steering.","ui":"txt2img"}, + {"id":"","label":"Guidance end","localized":"","hint":"Ends guidance early. The remaining denoising steps run unguided, which can speed up inference and produce slightly softer, less prompt-locked results. Applied independently to each pipeline pass (base, HiRes, refiner) against that pass's own step count.
    Example: 0.5 stops guidance at 50% of steps; 0.8 stops at 80%.

    Affects Guidance scale and Refine guidance on all models, and Attention guidance on the PAG path only (SD 1.5 and SDXL). Has no effect on the true_cfg_scale path that Attention guidance uses for Flux, QwenImage, HiDream, Hunyuan Video, and other flow-matching models.

    Set to 1 to keep guidance active for the entire denoising process.
    1 (no early end) by default.","ui":"txt2img"}, + {"id":"","label":"Guidance rescale","localized":"","hint":"Rescales the guided noise prediction to avoid the oversaturated, washed-out colors that high Guidance scale values can produce.
    Useful when running with Guidance scale above 10 or when colors look blown out. Mild values (0.5-0.7) usually fix the issue without affecting prompt adherence.

    Set to 0 to disable rescaling.
    Disabled by default.","ui":"txt2img"}, {"id":"","label":"Gamma","localized":"","hint":"Non-linear brightness curve adjustment.
    Values below 1.0 brighten midtones and shadows while preserving highlights.
    Values above 1.0 darken midtones and shadows.

    Default is 1.0 (no change). Unlike brightness, gamma reshapes the tonal curve rather than shifting it uniformly.","ui":"txt2img"}, {"id":"","label":"Grain","localized":"","hint":"Adds film-like noise to the image.
    Higher values produce more visible grain, simulating analog film texture.

    Applied as random noise blended into the final image. Set to 0 to disable.","ui":"txt2img"}, {"id":"","label":"Grid margins","localized":"","hint":"","ui":"script_prompt_matrix"}, @@ -633,7 +633,7 @@ {"id":"","label":"Guidance start","localized":"","hint":"","ui":"script_slg"}, {"id":"","label":"Guidance stop","localized":"","hint":"","ui":"script_slg"}, {"id":"","label":"Gate step","localized":"","hint":"","ui":"script_t-gate"}, - {"id":"","label":"Guess mode","localized":"","hint":"Removes the requirement to supply a prompt to a ControlNet. It forces Controlnet encoder to do it's 'best guess' based on the contents of the input control map.","ui":"control"}, + {"id":"","label":"Guess mode","localized":"","hint":"Removes the requirement to supply a prompt to a ControlNet. It forces ControlNet encoder to do its 'best guess' based on the contents of the input control map.","ui":"control"}, {"id":"","label":"gradient","localized":"","hint":"","ui":"control"}, {"id":"","label":"Gamma corrected","localized":"","hint":"","ui":"control"}, {"id":"","label":"General threshold","localized":"","hint":"Confidence threshold for general tags (e.g., objects, actions, settings).
    Only tags with confidence above this threshold are included in the output.
    Higher values are more selective (fewer tags), lower values include more tags.","ui":"caption"}, @@ -661,15 +661,15 @@ {"id":"","label":"HiDream","localized":"","hint":"","ui":"settings_model_options"}, {"id":"","label":"HyperTile","localized":"","hint":"","ui":"settings_advanced"}, {"id":"","label":"HiDiffusion","localized":"","hint":"HiDiffusion allows creation of high-resolution images using your standard models without duplicates/distortions and improved performance","ui":"settings_advanced"}, - {"id":"","label":"Height","localized":"","hint":"Image height","ui":"txt2img"}, + {"id":"","label":"Height","localized":"","hint":"Target height of the output image in pixels.
    For generation, this sets the resolution the model produces. For resize and upscale operations, this is the height the input is fitted to.

    Should be a multiple of 8 for SD1.x and SDXL latents; newer architectures (Flux, SD3, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"txt2img"}, {"id":"","label":"HiRes steps","localized":"","hint":"Number of sampling steps for upscaled picture. If 0, uses same as for original","ui":"txt2img"}, {"id":"","label":"Hue","localized":"","hint":"Rotates all colors around the color wheel.
    Small values produce subtle color shifts, while higher values cycle through the full spectrum.

    Useful for creative color effects or correcting unwanted color casts.","ui":"txt2img"}, {"id":"","label":"Highlights","localized":"","hint":"Adjusts the brightness of highlight (bright) regions.
    Positive values brighten highlights, negative values pull them down.

    Operates on the L channel in Lab color space using a luminance-weighted mask, leaving shadows and midtones largely unaffected.","ui":"txt2img"}, {"id":"","label":"Highlights tint","localized":"","hint":"Color to blend into highlight regions for split toning.
    Works together with Shadows tint and Split tone balance to create cinematic color grading looks.

    Default white (#ffffff) applies no tint.","ui":"txt2img"}, {"id":"","label":"HDR range","localized":"","hint":"","ui":"script_hdr"}, {"id":"","label":"HQ init latents","localized":"","hint":"","ui":"script_instantir"}, - {"id":"","label":"Height after","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Height mask","localized":"","hint":"","ui":"control"}, + {"id":"","label":"Height after","localized":"","hint":"Target height of the output image in pixels, applied after the model finishes generating (Post sub-tab in the Size accordion). Use this to upscale or downscale the final image before saving.

    Should be a multiple of 8 for SD1.x and SDXL latents; newer architectures (Flux, SD3, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"control"}, + {"id":"","label":"Height mask","localized":"","hint":"Target height of the input mask image in pixels (Mask sub-tab in the Size accordion). The mask is used for inpainting, outpainting, or as a control mask, and is resized so it aligns with the processing resolution.

    Should be a multiple of 8 for SD1.x and SDXL latents; newer architectures (Flux, SD3, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"control"}, {"id":"","label":"Hires use control","localized":"","hint":"","ui":"control"}, {"id":"","label":"Hands","localized":"","hint":"","ui":"control"}, {"id":"","label":"High threshold","localized":"","hint":"","ui":"control"}, @@ -700,7 +700,6 @@ "i": [ {"id":"control_nav","label":"Images","localized":"","hint":"Create images
    Unified interface
    Supports T2I and I2I
    With optional control guidance"}, {"id":"img2img_nav","label":"I2I","localized":"","hint":"Create image from image
    Legacy interface that mimics original image-to-image interface and behavior"}, - {"id":"img2img_results_input_mobile","label":"Input","localized":"","hint":"Show/hide selection of input media used to guide generation","ui":"img2img"}, {"id":"","label":"Image","localized":"","hint":"Create image from image","ui":"img2img"}, {"id":"","label":"Inpaint","localized":"","hint":"","ui":"img2img"}, {"id":"control_params_mask","label":"Inputs","localized":"","hint":"Settings related to Input images","ui":"control"}, @@ -711,13 +710,13 @@ {"id":"","label":"Image Paths","localized":"","hint":"Settings related to image filenames, and output directories"}, {"id":"","label":"Image Metadata","localized":"","hint":"Settings related to handling of metadata that is created with generated images"}, {"id":"","label":"IP Adapters","localized":"","hint":"IP adapters are plugin models that can guide generation towards desired outcome","ui":"txt2img"}, - {"id":"","label":"Input Media","localized":"","hint":"Add input image to be used for image-to-image, inpaint or control processing","ui":"control"}, + {"id":"","label":"Input","localized":"","hint":"Add input image to be used for image-to-image, inpaint or control processing
    Click to minimize/maximize","ui":"control"}, {"id":"","label":"Input Image","localized":"","hint":"","ui":"caption"}, {"id":"","label":"IPEX","localized":"","hint":"","ui":"settings_backends"}, {"id":"","label":"Image Gallery","localized":"","hint":"","ui":"settings_saving-images"}, {"id":"","label":"Intermediate Image Saving","localized":"","hint":"","ui":"settings_saving-images"}, {"id":"","label":"Initial seed","localized":"","hint":"A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result","ui":"txt2img"}, - {"id":"","label":"Include detections","localized":"","hint":"Include original image with detected areas marked","ui":"txt2img"}, + {"id":"","label":"Include detections","localized":"","hint":"Adds an annotated debug image to the output gallery showing each detected region's bounding box, label, and confidence score, plus a translucent mask overlay.
    Useful for tuning Min confidence, Min size/Max size, and class filters: you can see exactly what was detected before the inpaint pass touched the image.

    Default off.","ui":"txt2img"}, {"id":"","label":"IY model","localized":"","hint":"","ui":"script_infiniteyou"}, {"id":"","label":"IY scale","localized":"","hint":"","ui":"script_infiniteyou"}, {"id":"","label":"IY start","localized":"","hint":"","ui":"script_infiniteyou"}, @@ -731,8 +730,8 @@ {"id":"","label":"Include images","localized":"","hint":"","ui":"script_xyz_grid_script"}, {"id":"","label":"invert","localized":"","hint":"","ui":"img2img"}, {"id":"","label":"Init image same as control","localized":"","hint":"Will additionally treat any image placed into the Control input window as a source for img2img type tasks, an image to modify for example.","ui":"control"}, - {"id":"","label":"Inpaint masked only","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Invert mask","localized":"","hint":"","ui":"control"}, + {"id":"","label":"Inpaint masked only","localized":"","hint":"Crop the masked region, denoise it at full resolution, then paste the result back into the original image.
    Best for small detail edits where you want maximum quality on the masked area without spending compute denoising the rest of the image. Detail in unmasked regions stays untouched.

    Tradeoff: the model only sees the cropped region, so it loses global context. The inpainted content may not match the surrounding scene's lighting, perspective, or style, and visible seams can appear at the crop boundary. Mitigate with Dilate + Blur on the mask, or disable this option to denoise the full image together.
    When off, the whole image is denoised at the generation resolution and the unmasked area is restored from the original via the mask blend, which preserves global coherence at the cost of detail in the masked region.

    Default off.","ui":"control"}, + {"id":"","label":"Invert mask","localized":"","hint":"Swaps which area is treated as masked.
    Useful when you have painted the region to preserve instead of the region to modify: enable this to flip the interpretation without redoing the mask.

    Default off.","ui":"control"}, {"id":"","label":"IOU","localized":"","hint":"","ui":"control"}, {"id":"","label":"Init strength","localized":"","hint":"","ui":"video"}, {"id":"","label":"Input directory","localized":"","hint":"Folder where the images are that you want to process","ui":"extras"}, @@ -754,7 +753,7 @@ {"id":"","label":"Image resize algorithm","localized":"","hint":"","ui":"settings_postprocessing"}, {"id":"","label":"Image repeats per epoch","localized":"","hint":"","ui":"settings_legacy_options"}, {"id":"","label":"Interpolation Method","localized":"","hint":"","ui":"models_merge_tab"}, - {"id":"","label":"In Blocks","localized":"","hint":"Downsampling Blocks of the UNet (12 values for SD1.5, 9 values for SDXL)","ui":"component-5674"}, + {"id":"","label":"In Blocks","localized":"","hint":"Downsampling Blocks of the UNet (12 values for SD1.5, 9 values for SDXL)","ui":"component-5674"}, {"id":"","label":"Input model","localized":"","hint":"","ui":"models_replace_tab"}, {"id":"","label":"Info object","localized":"","hint":"","ui":"component-8779"} ], @@ -765,12 +764,13 @@ {"id":"","label":"Keep Thinking Trace","localized":"","hint":"Include the model's reasoning process in the final output.
    Useful for understanding how the model arrived at its answer.
    Only works with models that support thinking mode.","ui":"script_prompt_enhance"}, {"id":"","label":"Keep Prefill","localized":"","hint":"Include the prefill text at the beginning of the final output.
    If disabled, the prefill text used to guide the model is removed from the result.","ui":"script_prompt_enhance"}, {"id":"","label":"Keep aspect ratio","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Keep @ on artist insert","localized":"","hint":"Type @ in the prompt to filter autocomplete to artist tags only.
    This setting controls only what gets inserted on accept; the @ filter works for every model.

    Enable for models that require the @ prefix in the prompt itself, e.g. Anima. Inserts as @artist name with underscores converted to spaces.
    Disable for booru-trained models that take plain artist tags, e.g. SDXL, Pony, Illustrious, NoobAI. The typed @ is consumed and the artist name is inserted as a normal tag.","ui":"script_autocomplete"} + {"id":"","label":"Keep @ on artist insert","localized":"","hint":"Type @ in the prompt to filter autocomplete to artist tags only.
    This setting controls only what gets inserted on accept; the @ filter works for every model. Underscore handling is controlled by Keep underscores.

    Enable for models that require the @ prefix in the prompt itself, e.g. Anima. Inserts as @artist name.
    Disable for booru-trained models that take plain artist tags, e.g. SDXL, Pony, Illustrious, NoobAI. The typed @ is consumed and the artist name is inserted as a normal tag.","ui":"script_autocomplete"}, + {"id":"","label":"Keep underscores","localized":"","hint":"Keep underscore characters when inserting tags from autocomplete. Applies to both ordinary tags and artist insertions (the @ trigger).
    Embedding names always preserve their underscores regardless of this setting.

    Enable when your model is sensitive to the underscored form of booru tags. The tag long_hair displays and inserts as long_hair.
    Disable (default) to convert underscores to spaces, matching the prompting style of most modern checkpoints. The tag long_hair displays and inserts as long hair.","ui":"script_autocomplete"} ], "l": [ {"id":"prompt_enhance_load","label":"Load model","localized":"","hint":"","ui":"script_prompt_enhance"}, {"id":"prompt_enhance_custom_load","label":"Load custom model","localized":"","hint":"Load a custom model with the specified configuration","ui":"script_prompt_enhance"}, - {"id":"control_mask_remove","label":"LaMa Remove","localized":"","hint":"","ui":"control"}, + {"id":"control_mask_remove","label":"LaMa Remove","localized":"","hint":"Removes the masked region using LaMa, a lightweight inpainting model that fills the area with content extrapolated from the surroundings.
    Useful for cleanup tasks like erasing watermarks, removing unwanted objects, or generating a clean plate before running a full diffusion pass.
    Runs the configured mask pipeline (auto-segment, dilate, erode, blur, invert) first, then passes the resulting mask to LaMa. Result is written to the output panel.

    Model is downloaded on first use.","ui":"control"}, {"id":"","label":"Lite","localized":"","hint":"","ui":"control"}, {"id":"video_params_ltx","label":"LTXVideo","localized":"","hint":"","ui":"video"}, {"id":"vlm_load","label":"Load","localized":"","hint":"","ui":"caption"}, @@ -790,7 +790,7 @@ {"id":"","label":"List all locally available models","localized":"","hint":"","ui":"models_list_tab"}, {"id":"","label":"Last Generate","localized":"","hint":""}, {"id":"","label":"LUT","localized":"","hint":"Look-Up Table color grading section.
    Upload a .cube LUT file to apply professional color grading presets.

    LUTs remap colors according to a predefined 3D color transform, commonly used in film and photography for consistent color looks.","ui":"txt2img"}, - {"id":"","label":"low order","localized":"","hint":"","ui":"txt2img"}, + {"id":"","label":"low order","localized":"","hint":"Forces multistep solvers to fall back to a lower-order step during the last few denoising iterations.
    Higher-order solvers can become numerically unstable as sigma approaches zero, so the lower-order tail produces a cleaner, more stable final image.

    Applies only to multistep families (DPM++, UniPC, DEIS, SA Solver, DC Solver, ER-SDE). Single-step samplers such as DDIM, Euler, and Euler a ignore this option.

    Recommended to leave on. Disabling can occasionally give slightly sharper output but risks artifacts on the final steps.

    Enabled by default.","ui":"txt2img"}, {"id":"","label":"LSC layer indices","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"LSC fully qualified name","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"LSC skip attention blocks","localized":"","hint":"","ui":"txt2img"}, @@ -831,7 +831,7 @@ {"id":"","label":"LTX enable refine","localized":"","hint":"","ui":"video"}, {"id":"","label":"LTX refine strength","localized":"","hint":"","ui":"video"}, {"id":"","label":"LTX decode timestep","localized":"","hint":"","ui":"video"}, - {"id":"","label":"LTX enable audio","localized":"","hint":"","ui":"video"}, + {"id":"","label":"LTX save audio","localized":"","hint":"LTX-2 audio-capable models always generate audio from the same prompt as video; this toggle controls whether the audio track is included in the saved video file","ui":"video"}, {"id":"","label":"Loop","localized":"","hint":"","ui":"extras"}, {"id":"","label":"Local directory name","localized":"","hint":"Directory where to install extension, leave blank for default","ui":"component-8746"}, {"id":"","label":"Libs","localized":"","hint":"","ui":"component-8779"}, @@ -889,20 +889,21 @@ {"id":"","label":"Mobile","localized":"","hint":"","ui":"settings_ui"}, {"id":"","label":"Merge multiple models","localized":"","hint":"","ui":"models_merge_tab"}, {"id":"","label":"Max shift","localized":"","hint":"Maximum shift value for high resolutions when using dynamic shifting.","ui":"txt2img"}, - {"id":"","label":"Merge detailers","localized":"","hint":"Merge results from multiple detailers into single mask before running detailing process","ui":"txt2img"}, - {"id":"","label":"Max detected","localized":"","hint":"Maximum number of detected objects to run detailer on","ui":"txt2img"}, - {"id":"","label":"Min confidence","localized":"","hint":"Minimum confidence in detected item","ui":"txt2img"}, - {"id":"","label":"Max overlap","localized":"","hint":"Maximum overlap between two detected items before one is discarded","ui":"txt2img"}, - {"id":"","label":"Min size","localized":"","hint":"Minimum size of detected object as percentage of overal image","ui":"txt2img"}, - {"id":"","label":"Max size","localized":"","hint":"Maximum size of detected object as percentage of overal image","ui":"txt2img"}, + {"id":"","label":"Merge detailers","localized":"","hint":"Combines all detections from each model into a single mask and runs one inpaint pass per model instead of one per detection.
    Faster when many regions are detected (e.g., a crowd scene with multiple faces): one larger inpaint pass replaces several small ones. Tradeoff: each region gets less individual attention because the model sees them all together.
    Best for scenes where the detected regions are similar in size and content.

    Default off.","ui":"txt2img"}, + {"id":"","label":"Max detected","localized":"","hint":"Cap on how many detections per model are processed.
    Detections beyond this count are dropped (in detection score order, highest first). Use to keep detailer time bounded on busy scenes.

    Default 2.","ui":"txt2img"}, + {"id":"","label":"Min confidence","localized":"","hint":"Minimum YOLO detection score required for a region to be processed.
    Higher values keep only confident detections (fewer false positives but may miss real subjects in difficult lighting). Lower values include more candidates including weak ones.
    Tune with Include detections on so you can see what is being kept and dropped.

    Default 0.6.","ui":"txt2img"}, + {"id":"","label":"Max overlap","localized":"","hint":"IOU threshold for non-maximum suppression: if two detections overlap by more than this fraction, the lower-scoring one is dropped.
    Lower values are stricter (less overlap allowed; fewer duplicate detections of the same subject). Higher values let near-duplicates through, which is rarely useful.

    Default 0.5.","ui":"txt2img"}, + {"id":"","label":"Min size","localized":"","hint":"Minimum detection size as a fraction of the image's shorter edge. Detections smaller than this are dropped.
    Use to filter out tiny background objects (e.g., faces in a crowd that aren't worth detailing). At 0.1, a face must occupy at least 10% of the image dimension to qualify.

    Set to 0 to disable the lower bound.
    Default 0.","ui":"txt2img"}, + {"id":"","label":"Max size","localized":"","hint":"Maximum detection size as a fraction of the image's shorter edge. Detections larger than this are dropped.
    Use to skip cases where the detector grabs the whole image (e.g., a person detector returning a near full-frame box that the inpaint pass would just regenerate).

    Set to 1.0 to disable the upper bound.
    Default 0.75.","ui":"txt2img"}, {"id":"","label":"Midtones","localized":"","hint":"Adjusts the brightness of midtone regions.
    Positive values brighten midtones, negative values darken them.

    Targets pixels near the middle of the luminance range using a bell-shaped mask in Lab space, leaving shadows and highlights largely untouched.","ui":"txt2img"}, {"id":"","label":"Momentum","localized":"","hint":"","ui":"script_apg"}, {"id":"","label":"Mode x-axis","localized":"","hint":"","ui":"script_asymmetric_tiling"}, {"id":"","label":"Mode y-axis","localized":"","hint":"","ui":"script_asymmetric_tiling"}, {"id":"","label":"Mask Dropout","localized":"","hint":"","ui":"script_consistory"}, {"id":"","label":"Multi decoder","localized":"","hint":"","ui":"script_demofusion"}, - {"id":"","label":"Mode","localized":"","hint":"Interrogation mode.
    Fast: Quick caption with minimal flavor terms.
    Classic: Standard interrogation with balanced quality and speed.
    Best: Most thorough analysis, slowest but highest quality.
    Negative: Generate terms to use as negative prompt.","ui":"script_face"}, - {"id":"","label":"Method","localized":"","hint":"","ui":"script_video"}, + {"id":"","label":"CLiP Mode","localized":"","hint":"OpenCLiP interrogation depth.
    Fast: quick caption with minimal flavor terms.
    Classic: standard interrogation balancing quality and speed.
    Best: most thorough analysis, slowest but highest quality.
    Negative: generate terms suitable for use as a negative prompt.","ui":"caption"}, + {"id":"","label":"Mode","localized":"","hint":"How the input is fitted to the target resolution.
    None: skip resize, pass the image through unchanged.
    Fixed: force to target width and height, distorting aspect ratio if they differ.
    Crop: scale to fully cover the target then center-crop the overflow, preserving aspect ratio.
    Fill: scale to fit inside the target then pad the remaining space with the background color (set in Settings → Image options).
    Outpaint: like Fill, but the model paints new content into the padded space instead of using a solid color.
    Context aware: use seam-carving to add or remove pixels along smooth, featureless paths through the image (like sky or plain backgrounds), preserving the detailed regions. Behavior is controlled by the Context dropdown next to this one.","ui":"resize"}, + {"id":"","label":"Method","localized":"","hint":"Algorithm used to perform the resize.
    Choices range from simple interpolation (Lanczos, Nearest) to upscaler models (ESRGAN, SwinIR, RealESRGAN, etc.) and latent-space methods.

    Upscaler models give better quality at the cost of speed; simple methods are fast but soft.","ui":"resize"}, {"id":"","label":"Model repo","localized":"","hint":"HuggingFace repository ID for the model","ui":"script_prompt_enhance"}, {"id":"","label":"Model gguf","localized":"","hint":"Optional GGUF quantized model repository on HuggingFace","ui":"script_prompt_enhance"}, {"id":"","label":"Model type","localized":"","hint":"Optional GGUF model quantization type","ui":"script_prompt_enhance"}, @@ -916,10 +917,10 @@ {"id":"","label":"Min guidance","localized":"","hint":"","ui":"script_video"}, {"id":"","label":"Max guidance","localized":"","hint":"","ui":"script_video"}, {"id":"","label":"Motion level","localized":"","hint":"","ui":"script_video"}, - {"id":"","label":"Mode after","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Method after","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Mode mask","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Method mask","localized":"","hint":"","ui":"control"}, + {"id":"","label":"Mode after","localized":"","hint":"How the output image is fitted to the target resolution after the model finishes generating (Post sub-tab in the Size accordion).
    None: skip resize, pass the image through unchanged.
    Fixed: force to target width and height, distorting aspect ratio if they differ.
    Crop: scale to fully cover the target then center-crop the overflow, preserving aspect ratio.
    Fill: scale to fit inside the target then pad the remaining space with the background color (set in Settings → Image options).
    Outpaint: like Fill, but the model paints new content into the padded space instead of using a solid color.
    Context aware: use seam-carving to add or remove pixels along smooth, featureless paths through the image (like sky or plain backgrounds), preserving the detailed regions. Behavior is controlled by the Context dropdown next to this one.","ui":"control"}, + {"id":"","label":"Method after","localized":"","hint":"Algorithm used to resize the output image after the model finishes generating (Post sub-tab in the Size accordion).
    Choices range from simple interpolation (Lanczos, Nearest) to upscaler models (ESRGAN, SwinIR, RealESRGAN, etc.) and latent-space methods.

    Upscaler models give better quality at the cost of speed; simple methods are fast but soft.","ui":"control"}, + {"id":"","label":"Mode mask","localized":"","hint":"How the input mask image (used for inpainting, outpainting, or control masks) is fitted to the target resolution (Mask sub-tab in the Size accordion).
    None: skip resize, pass the mask through unchanged.
    Fixed: force to target width and height, distorting aspect ratio if they differ.
    Crop: scale to fully cover the target then center-crop the overflow, preserving aspect ratio.
    Fill: scale to fit inside the target then pad the remaining space with the background color (set in Settings → Image options).
    Outpaint: like Fill, but the model paints new content into the padded space instead of using a solid color.
    Context aware: use seam-carving to add or remove pixels along smooth, featureless paths through the image (like sky or plain backgrounds), preserving the detailed regions. Behavior is controlled by the Context dropdown next to this one.","ui":"control"}, + {"id":"","label":"Method mask","localized":"","hint":"Algorithm used to resize the input mask image (used for inpainting, outpainting, or control masks; Mask sub-tab in the Size accordion).
    Choices range from simple interpolation (Lanczos, Nearest) to upscaler models (ESRGAN, SwinIR, RealESRGAN, etc.) and latent-space methods.

    Upscaler models give better quality at the cost of speed; simple methods are fast but soft.","ui":"control"}, {"id":"","label":"Maximum units","localized":"","hint":"","ui":"control"}, {"id":"","label":"Max faces","localized":"","hint":"","ui":"control"}, {"id":"","label":"Medium","localized":"","hint":"","ui":"control"}, @@ -992,8 +993,8 @@ {"id":"","label":"Noise scale","localized":"","hint":"","ui":"video"}, {"id":"","label":"Note","localized":"","hint":"","ui":"component-8823"}, {"id":"","label":"Non-blocking move operations","localized":"","hint":"","ui":"settings_offload"}, - {"id":"","label":"Nunchaku attention","localized":"","hint":"Replaces default attention with Nunchaku's custom FP16 attention kernel for faster inference on consumer NVIDIA GPUs.
    Might provide performance improvement on GPUs which have higher FP16 tensor cores throughput than BF16.

    Currently only affects Flux-based models (Dev, Schnell, Kontext, Fill, Depth, etc.). Has no effect on Qwen, SDXL, Sana, or other architectures.

    Disabled by default.","ui":"settings_quantization"}, - {"id":"","label":"Nunchaku offloading","localized":"","hint":"Enables Nunchaku's own per-block CPU offloading with asynchronous CUDA streams to reduce VRAM usage.
    Uses a ping-pong buffer strategy: while one transformer block computes on GPU, the next block preloads from CPU in the background, hiding most of the transfer latency.

    Can reduce VRAM usage at the cost of slower inference.
    This replaces SD.Next's pipeline offloading for the transformer component.

    Only useful on low-VRAM GPUs. If your GPU has enough memory to hold the quantized model (16+ GB), keep this disabled for maximum speed.
    Supports Flux and Qwen models. Not supported for SDXL where this setting is ignored.
    Disabled by default.","ui":"settings_quantization"}, + {"id":"","label":"Nunchaku attention","localized":"","hint":"Replaces default attention with Nunchaku's custom FP16 attention kernel for faster inference on consumer NVIDIA GPUs.
    Might provide performance improvement on GPUs which have higher FP16 tensor cores throughput than BF16.

    Currently only affects Flux-based models (Dev, Schnell, Kontext, Fill, Depth, etc.). Has no effect on Qwen, SDXL, Sana, or other architectures.

    Disabled by default.","ui":"settings_quantization"}, + {"id":"","label":"Nunchaku offloading","localized":"","hint":"Enables Nunchaku's own per-block CPU offloading with asynchronous CUDA streams to reduce VRAM usage.
    Uses a ping-pong buffer strategy: while one transformer block computes on GPU, the next block preloads from CPU in the background, hiding most of the transfer latency.

    Can reduce VRAM usage at the cost of slower inference.
    This replaces SD.Next's pipeline offloading for the transformer component.

    Only useful on low-VRAM GPUs. If your GPU has enough memory to hold the quantized model (16+ GB), keep this disabled for maximum speed.
    Supports Flux and Qwen models. Not supported for SDXL where this setting is ignored.
    Disabled by default.","ui":"settings_quantization"}, {"id":"","label":"native","localized":"","hint":"","ui":"settings_text_encoder"}, {"id":"","label":"no-grad","localized":"","hint":"Disables gradient tracking with torch.no_grad. Reduces memory usage and speeds up inference.","ui":"settings_backends"}, {"id":"","label":"Numbered filenames","localized":"","hint":"","ui":"settings_saving-paths"}, @@ -1006,7 +1007,7 @@ {"id":"","label":"Network parameters","localized":"","hint":""} ], "o": [ - {"id":"txt2img_results_mobile","label":"Output","localized":"","hint":"Show/hide selection of output media: generation resuls and live previews during generation process","ui":"txt2img"}, + {"id":"txt2img_results_mobile","label":"Output","localized":"","hint":"Generation resuls and live previews during generation process
    Click to minimize/maximize","ui":"txt2img"}, {"id":"","label":"OpenCLiP","localized":"","hint":"Analyze image using CLiP model via OpenCLiP","ui":"caption"}, {"id":"","label":"ONNX","localized":"","hint":""}, {"id":"","label":"Override","localized":"","hint":"Override settings that can change server behavior and are typically applied from imported image metadata","ui":"txt2img"}, @@ -1052,7 +1053,7 @@ {"id":"","label":"olive-ai","localized":"","hint":"","ui":"settings_compile"}, {"id":"","label":"openvino_fx","localized":"","hint":"","ui":"settings_compile"}, {"id":"","label":"Overwrite existing","localized":"","hint":"","ui":"models_current_tab"}, - {"id":"","label":"Out Block","localized":"","hint":"Upsampling Blocks of the UNet (12 values for SD1.5, 9 values for SDXL)","ui":"component-5674"}, + {"id":"","label":"Out Block","localized":"","hint":"Upsampling Blocks of the UNet (12 values for SD1.5, 9 values for SDXL)","ui":"component-5674"}, {"id":"","label":"Overwrite model","localized":"","hint":"","ui":"models_merge_tab"}, {"id":"","label":"Output model","localized":"","hint":"","ui":"models_replace_tab"}, {"id":"","label":"Overwrite existing file","localized":"","hint":"","ui":"component-5851"}, @@ -1064,7 +1065,7 @@ {"id":"txt2img_prompts","label":"Prompts","localized":"","hint":"Image prompt and negative prompt","ui":"txt2img"}, {"id":"txt2img_pause","label":"Pause","localized":"","hint":"Pause processing","ui":"txt2img"}, {"id":"","label":"Post","localized":"","hint":"Resize image after processing","ui":"control"}, - {"id":"","label":"Preview","localized":"","hint":"","ui":"video"}, + {"id":"","label":"Preview","localized":"","hint":"Selects how the mask preview is rendered when you click Run Preview.
    None: skip the preview step.
    Masked: input image with everything outside the mask blacked out.
    Binary: pure black-and-white mask (Otsu thresholded).
    Grayscale: mask intensity values rendered as gray levels.
    Color: mask recolored using the selected Colormap.
    Composite: 50/50 blend of the input image and the colored mask, so you can see exactly where the mask falls relative to the subject.

    Default Composite.","ui":"video"}, {"id":"","label":"Process Image","localized":"","hint":"Process single image","ui":"extras"}, {"id":"","label":"Process Batch","localized":"","hint":"Process batch of images","ui":"extras"}, {"id":"","label":"Process Folder","localized":"","hint":"Process all images in a folder","ui":"extras"}, @@ -1074,13 +1075,12 @@ {"id":"","label":"Preset Block Merge","localized":"","hint":"","ui":"models_merge_tab"}, {"id":"","label":"Preview metadata","localized":"","hint":""}, {"id":"","label":"Prompt","localized":"","hint":"Describe image you want to generate","ui":"txt2img"}, - {"id":"","label":"Processed Preview","localized":"","hint":"Show/hide section from pre-processing of input images before actual generate","ui":"control"}, {"id":"","label":"PixelArt","localized":"","hint":"","ui":"extras"}, {"id":"","label":"PAG: Perturbed attention guidance","localized":"","hint":"","ui":"settings_advanced"}, {"id":"","label":"PAB: Pyramid attention broadcast","localized":"","hint":"","ui":"settings_advanced"}, {"id":"","label":"Para-attention","localized":"","hint":"","ui":"settings_advanced"}, {"id":"","label":"Paths for specific models","localized":"","hint":"","ui":"settings_system-paths"}, - {"id":"","label":"Prediction method","localized":"","hint":"Defines what the model predicts at each step. Options:
    - default: the model default
    - epsilon: noise (most common for Stable Diffusion)
    - sample: direct denoised image prediction, also called as x0 prediction
    - v_prediction: velocity prediction, used by CosXL and NoobAI VPred models
    - flow_prediction: used with newer flow-matching models like SD3 and Flux","ui":"txt2img"}, + {"id":"","label":"Prediction method","localized":"","hint":"Defines what the model predicts at each step. Options:
    - default: the model default
    - epsilon: noise (most common for Stable Diffusion)
    - sample: direct denoised image prediction, also called as x0 prediction
    - v_prediction: velocity prediction, used by CosXL and NoobAI VPred models
    - flow_prediction: used with newer flow-matching models like SD3 and Flux","ui":"txt2img"}, {"id":"","label":"PAG scale","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"PAG start","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"PAG stop","localized":"","hint":"","ui":"txt2img"}, @@ -1104,10 +1104,9 @@ {"id":"","label":"Preview start","localized":"","hint":"","ui":"script_instantir"}, {"id":"","label":"Preview end","localized":"","hint":"","ui":"script_instantir"}, {"id":"","label":"Pixels to expand","localized":"","hint":"","ui":"script_outpainting"}, - {"id":"","label":"Processor","localized":"","hint":"Processor type to use to preprocess image used for ControlNet","ui":"control"}, + {"id":"","label":"Processor","localized":"","hint":"Processor type to use to preprocess image used for ControlNet","ui":"control"}, {"id":"","label":"Pose confidence","localized":"","hint":"","ui":"control"}, {"id":"","label":"Parameter free","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Processed","localized":"","hint":"Show/hide section with processed images","ui":"control"}, {"id":"","label":"Postprocess mask","localized":"","hint":"","ui":"extras"}, {"id":"","label":"PixelArt block size","localized":"","hint":"","ui":"extras"}, {"id":"","label":"PixelArt sharpen","localized":"","hint":"","ui":"extras"}, @@ -1169,7 +1168,7 @@ {"id":"","label":"Resize to","localized":"","hint":"","ui":"control"}, {"id":"","label":"Resize\n by","localized":"","hint":"","ui":"control"}, {"id":"","label":"Resize\n to","localized":"","hint":"","ui":"control"}, - {"id":"control_mask_refresh","label":"Run Preview","localized":"","hint":"","ui":"control"}, + {"id":"control_mask_refresh","label":"Run Preview","localized":"","hint":"Runs the configured mask pipeline (auto-segment, dilate, erode, blur, invert) on the current input and renders the result in the output panel using the selected Preview style.
    Use this to iterate on mask settings without launching a full generation.","ui":"control"}, {"id":"","label":"Reference","localized":"","hint":"List of reference models that can be automatically downloaded on first use","ui":"control"}, {"id":"framepack_btn_reset_model","label":"Reset receipe","localized":"","hint":"","ui":"video"}, {"id":"video_generation_info_button","label":"Run","localized":"","hint":"","ui":"video"}, @@ -1194,11 +1193,11 @@ {"id":"","label":"Resize","localized":"","hint":"Image resizing, can be using fixed resolution on based on scale","ui":"settings_postprocessing"}, {"id":"","label":"Rerefence models","localized":"","hint":"","ui":"settings_extra_networks"}, {"id":"","label":"Replace model components","localized":"","hint":"","ui":"models_replace_tab"}, - {"id":"","label":"rescale","localized":"","hint":"rescale betas with zero terminal snr","ui":"txt2img"}, + {"id":"","label":"rescale","localized":"","hint":"Rescales the noise schedule so the final timestep starts from true pure noise (zero signal-to-noise ratio).
    Standard SD schedules don't quite reach pure noise at the highest timestep, which biases generations toward medium brightness and limits dynamic range. Rescaling unlocks the full range of darks and brights.

    Should only be enabled for models trained with zero-terminal-SNR or v-prediction. The most common are SDXL fine-tunes carrying a 'vpred' or 'v-prediction' tag in the name (e.g. NoobAI XL Vpred, Illustrious XL Vpred, Stability's CosXL), plus some noise-offset and Terminus-family checkpoints. Standard epsilon-prediction models such as base SDXL, Pony, and Animagine should be left as-is. Enabling on a mismatched model will shift colors and degrade quality.

    Recommended to leave off unless your model documentation specifically calls for it.

    Disabled by default.","ui":"txt2img"}, {"id":"","label":"Resize seed from width","localized":"","hint":"Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution","ui":"txt2img"}, {"id":"","label":"Resize seed from height","localized":"","hint":"Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution","ui":"txt2img"}, - {"id":"","label":"Refine guidance","localized":"","hint":"CFG scale used for refiner pass","ui":"txt2img"}, - {"id":"","label":"Resize mode","localized":"","hint":"Defines how the input is resized or adapted in second-pass refinement:
    - none: no resizing, keep original resolution
    - fixed: force resize to target resolution (may distort)
    - crop: center-crop to fit target while keeping aspect ratio
    - fill: resize to fit and pad empty space with borders
    - outpaint: extend canvas beyond image borders
    - context aware: smart resize that blends or adapts surrounding areas","ui":"txt2img"}, + {"id":"","label":"Refine guidance","localized":"","hint":"Guidance scale used for the secondary pass (refiner model or HiRes refine). Behaves like the main Guidance scale but applies only to that secondary pass.
    For OmniGen this slider controls a separate image-conditioning guidance scale instead, used alongside the main Guidance scale in OmniGen's dual-CFG formula.

    Set to 0 to disable guidance for the secondary pass.
    Defaults to 6.0.","ui":"txt2img"}, + {"id":"","label":"Resize mode","localized":"","hint":"Defines how the input is resized or adapted in second-pass refinement:
    - none: no resizing, keep original resolution
    - fixed: force resize to target resolution (may distort)
    - crop: center-crop to fit target while keeping aspect ratio
    - fill: resize to fit and pad empty space with borders
    - outpaint: extend canvas beyond image borders
    - context aware: smart resize that blends or adapts surrounding areas","ui":"txt2img"}, {"id":"","label":"Resize method","localized":"","hint":"Method used to resize the image: can be simple resize, upscaling model, latent resize or asymmetric decode","ui":"txt2img"}, {"id":"","label":"Resize width","localized":"","hint":"Resizes image to this width. If 0, width is inferred from either of two nearby sliders","ui":"txt2img"}, {"id":"","label":"Resize height","localized":"","hint":"Resizes image to this height. If 0, height is inferred from either of two nearby sliders","ui":"txt2img"}, @@ -1208,8 +1207,8 @@ {"id":"","label":"Refiner steps","localized":"","hint":"Number of steps to use for refiner pass","ui":"txt2img"}, {"id":"","label":"Refine prompt","localized":"","hint":"Prompt used for both second encoder in base model (if it exists) and for refiner pass (if enabled)","ui":"txt2img"}, {"id":"","label":"Refine negative prompt","localized":"","hint":"Negative prompt used for both second encoder in base model (if it exists) and for refiner pass (if enabled)","ui":"txt2img"}, - {"id":"","label":"Renoise","localized":"","hint":"Apply additional noise during detailing","ui":"txt2img"}, - {"id":"","label":"Renoise end","localized":"","hint":"Final step when renoise is applied","ui":"txt2img"}, + {"id":"","label":"Renoise","localized":"","hint":"Multiplier applied to the sampler's step size during the detailer pass. Same mechanism as the Sigma adjust slider in the sampler tab, scoped to detailer only.
    Values below 1.0 shrink each step for smoother, more conservative refinement (good for keeping faces stable). Values above 1.0 enlarge each step for sharper, more aggressive resampling.

    Default 1.0 disables the adjustment.","ui":"txt2img"}, + {"id":"","label":"Renoise end","localized":"","hint":"Upper bound of the denoising window where Renoise is active within the detailer pass, as a fraction of the noise schedule (1.0 = pure noise, 0.0 = clean image).
    Lower values restrict renoise to the very first steps (gentler intervention); higher values let it act further into the run.

    Default 1.0 keeps renoise active across the full pass.","ui":"txt2img"}, {"id":"","label":"Repeat x-axis","localized":"","hint":"","ui":"script_asymmetric_tiling"}, {"id":"","label":"Repeat y-axis","localized":"","hint":"","ui":"script_asymmetric_tiling"}, {"id":"","label":"ReSwapper Model","localized":"","hint":"","ui":"script_face"}, @@ -1237,7 +1236,6 @@ {"id":"","label":"RAS enabled","localized":"","hint":"","ui":"settings_advanced"}, {"id":"","label":"reduce-overhead","localized":"","hint":"","ui":"settings_compile"}, {"id":"","label":"repeated","localized":"","hint":"","ui":"settings_compile"}, - {"id":"","label":"Replace underscores","localized":"","hint":"Display underscores in tag names as spaces in the autocomplete suggestion list.
    For example, long_hair appears as long hair.","ui":"script_autocomplete"}, {"id":"","label":"Root model folder","localized":"","hint":"","ui":"settings_system-paths"}, {"id":"","label":"Resize background color","localized":"","hint":"","ui":"settings_saving-images"}, {"id":"","label":"Restore from metadata: skip params","localized":"","hint":"","ui":"settings_image-metadata"}, @@ -1302,9 +1300,9 @@ {"id":"","label":"Server log","localized":"","hint":""}, {"id":"","label":"Steps","localized":"","hint":"How many times to improve the generated image iteratively; higher values take longer; very low values can produce bad results","ui":"txt2img"}, {"id":"","label":"Sampling method","localized":"","hint":"Which algorithm to use to produce the image","ui":"txt2img"}, - {"id":"","label":"Sigma method","localized":"","hint":"Controls how noise levels (sigmas) are distributed across diffusion steps. Options:
    - default: the model default
    - karras: smoother noise schedule, higher quality with fewer steps
    - beta: based on beta schedule values
    - exponential: exponential decay of noise
    - lambdas: experimental, balances signal-to-noise
    - flowmatch: tuned for flow-matching models","ui":"txt2img"}, - {"id":"","label":"Sigma adjust","localized":"","hint":"Adjust sampler sigma value","ui":"txt2img"}, - {"id":"","label":"Sampler order","localized":"","hint":"Order of solver updates in the sampler. Higher order improves stability/accuracy but increases compute cost.","ui":"txt2img"}, + {"id":"","label":"Sigma method","localized":"","hint":"Controls how noise levels (sigmas) are distributed across diffusion steps.
    Default: use the scheduler's built-in sigma method.
    Karras: smoother schedule that emphasizes later steps where fine details emerge; generally higher quality with fewer steps.
    Betas: derive sigmas directly from the model's beta schedule (classic DDPM behavior).
    Exponential: exponential decay of noise across steps; aggressive denoising early, slower refinement later.
    Lambdas: Lu's lambdas method from the DPM-Solver paper, specific to the DPM++ family.
    Flowmatch: sigma schedule tuned for flow-matching models (Flux, SD3, video models).","ui":"txt2img"}, + {"id":"","label":"Sigma adjust","localized":"","hint":"Multiplier applied to the sampler's step size during the active timestep window. (Sigma is the amount of noise the sampler removes at each step.)
    Values below 1.0 shrink the step for smoother, more conservative denoising. Values above 1.0 enlarge it for sharper, more aggressive sampling.

    Default 1.0 disables the adjustment entirely. Use Adjust start and Adjust end to define the timestep range where the multiplier takes effect.","ui":"txt2img"}, + {"id":"","label":"Sampler order","localized":"","hint":"Overrides the solver order of the active sampler when set above 0.
    Higher orders use more historical steps per update for greater stability and accuracy at the cost of extra compute. Lower orders are faster but noisier.

    Default 0 leaves each sampler at its built-in order. Many samplers in the dropdown already encode their order in the name (e.g. DPM++ 2M is order 2, DPM++ 3M is order 3, DPM++ 2M SDE is order 2).

    Within a sampler family, the named variants differ ONLY by this value, so picking DPM++ 2M with the slider at 3 produces a scheduler that is functionally identical to picking DPM++ 3M with the slider at 0. The same equivalence holds across the rest of the DPM++ multistep family (including the SDE and Inverse variants) and across the ER-SDE family.

    Samplers without a configurable solver order (DDIM, plain Euler, ancestrals, etc.) ignore this slider entirely.","ui":"txt2img"}, {"id":"","label":"SLG scale","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"SLG start","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"SLG stop","localized":"","hint":"","ui":"txt2img"}, @@ -1318,7 +1316,7 @@ {"id":"","label":"SEG layers","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"SEG config","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"Strength","localized":"","hint":"Denoising strength of during image operation controls how much of original image is allowed to change during generate","ui":"txt2img"}, - {"id":"","label":"Sort detections","localized":"","hint":"Sort detected areas by from left to right instead of detection score","ui":"txt2img"}, + {"id":"","label":"Sort detections","localized":"","hint":"Process detected regions left-to-right (by bounding box X position) instead of in detection-score order.
    Improves consistency when the prompt assigns different traits to different subjects in a multi-line prompt: prompts are mapped per detection in order, so a stable left-to-right order makes line 1 always go to the leftmost subject.

    Default off.","ui":"txt2img"}, {"id":"","label":"Saturation","localized":"","hint":"Controls color intensity.
    Positive values make colors more vivid, negative values desaturate toward grayscale.

    At -1.0 the image becomes fully monochrome.","ui":"txt2img"}, {"id":"","label":"Sharpness","localized":"","hint":"Enhances edge detail and fine textures.
    Higher values produce crisper edges but may amplify noise or artifacts if pushed too far.

    Set to 0 to disable. Operates via an unsharp mask kernel.","ui":"txt2img"}, {"id":"","label":"Shadows","localized":"","hint":"Adjusts the brightness of shadow (dark) regions.
    Positive values lift shadows to reveal detail, negative values deepen them.

    Operates on the L channel in Lab color space using a luminance-weighted mask, leaving highlights and midtones largely unaffected.","ui":"txt2img"}, @@ -1350,7 +1348,8 @@ {"id":"","label":"Show input","localized":"","hint":"","ui":"control"}, {"id":"","label":"Show preview","localized":"","hint":"","ui":"control"}, {"id":"","label":"Separate init image","localized":"","hint":"Creates an additional window next to Control input labeled Init input, so you can have a separate image for both Control operations and an init source.","ui":"control"}, - {"id":"","label":"Skip input frames","localized":"","hint":"","ui":"control"}, + {"id":"","label":"Skip input processing","localized":"","hint":"Bypasses the active control processor and feeds the raw input image directly to the pipeline.
    Use when you have already preprocessed the image externally (depth map, canny edges, openpose skeleton, etc.) and don't want SD.Next to re-run the processor on it.
    The input still routes through any selected ControlNet/T2I-Adapter/etc. model, just without the preprocessing step.

    Default off.","ui":"control"}, + {"id":"","label":"Skip input frames","localized":"","hint":"Number of input frames to skip between each processed frame when the input is a video.
    Use to thin out long source videos: only every (N+1)-th frame is processed and the rest are dropped.

    Set to 0 to process every frame. Set to 1 to process every other frame, 2 for every third, and so on.
    Default 0.","ui":"control"}, {"id":"","label":"Style fidelity","localized":"","hint":"","ui":"control"}, {"id":"","label":"Scribble","localized":"","hint":"","ui":"control"}, {"id":"","label":"Score threshold","localized":"","hint":"","ui":"control"}, @@ -1426,7 +1425,7 @@ {"id":"","label":"SDXL","localized":"","hint":"StableDiffusion XL","ui":"component-5660"}, {"id":"","label":"Save metadata","localized":"","hint":"","ui":"models_merge_tab"}, {"id":"","label":"safetensors","localized":"","hint":"","ui":"models_merge_tab"}, - {"id":"","label":"shuffle","localized":"","hint":"Loads full model in RAM and calculates on VRAM: Less speedup, suggested for SDXL merges","ui":"models_merge_tab"}, + {"id":"","label":"shuffle","localized":"","hint":"Loads full model in RAM and calculates on VRAM: Less speedup, suggested for SDXL merges","ui":"models_merge_tab"}, {"id":"","label":"Save diffusers","localized":"","hint":"","ui":"models_replace_tab"}, {"id":"","label":"Save safetensors","localized":"","hint":"","ui":"models_replace_tab"}, {"id":"","label":"Sort","localized":"","hint":"","ui":"models_civitai_tab"}, @@ -1456,10 +1455,10 @@ {"id":"","label":"Theme options","localized":"","hint":"","ui":"settings_ui"}, {"id":"","label":"Task History","localized":"","hint":""}, {"id":"","label":"Tone","localized":"","hint":"","ui":"txt2img"}, - {"id":"","label":"Timestep spacing","localized":"","hint":"Determines how timesteps are spaced across the diffusion process. Options:
    - default: the model default
    - leading: creates evenly spaced steps
    - linspace: includes the first and last steps and evenly selects the remaining intermediate steps
    - trailing: only includes the last step and evenly selects the remaining intermediate steps starting from the end","ui":"txt2img"}, - {"id":"","label":"Timesteps presets","localized":"","hint":"","ui":"txt2img"}, - {"id":"","label":"Timesteps override","localized":"","hint":"","ui":"txt2img"}, - {"id":"","label":"thresholding","localized":"","hint":"","ui":"txt2img"}, + {"id":"","label":"Timestep spacing","localized":"","hint":"Determines how timesteps are spaced across the diffusion process. Options:
    - default: the model default
    - leading: creates evenly spaced steps
    - linspace: includes the first and last steps and evenly selects the remaining intermediate steps
    - trailing: only includes the last step and evenly selects the remaining intermediate steps starting from the end","ui":"txt2img"}, + {"id":"","label":"Timesteps presets","localized":"","hint":"Picks a hand-tuned timestep schedule and writes it into Timesteps override.
    'AYS SD15' and 'AYS SDXL' load the Align Your Steps schedules optimized for those base models, both 10 steps long. Selecting one of these effectively forces the generation to run at exactly 10 steps regardless of the main Steps slider, which is intended: AYS produces results comparable to 30+ step traditional sampling at this length.

    Use the SD15 preset for SD 1.x checkpoints and the SDXL preset for SDXL-based checkpoints. The AYS schedules are not appropriate for flow-matching models (Flux, SD3) or other architectures.

    Set to None to clear the override.
    No preset by default.","ui":"txt2img"}, + {"id":"","label":"Timesteps override","localized":"","hint":"Comma- or space-separated list of integer timesteps in the 0-999 range, listed from highest (most noisy) to lowest (cleanest). When set, this list completely replaces the scheduler's normal timestep schedule and forces the step count to match the list length, ignoring the main Steps slider.

    Requires at least 3 values to take effect; shorter inputs are silently ignored. Not all samplers support arbitrary timestep injection. If the active sampler doesn't, a warning is logged and the override is skipped. Selecting a preset from Timesteps presets fills this field automatically.

    Useful for advanced users experimenting with custom schedules. Most users should leave this blank.

    Clear the field to disable.
    Empty by default.","ui":"txt2img"}, + {"id":"","label":"thresholding","localized":"","hint":"Enables dynamic thresholding. At each step the predicted clean image is clipped so its values stay within the model's trained range, which suppresses saturation and washed-out colors at high guidance.

    Most useful for SD 1.x and SD 2.x at high CFG (>10). Generally not helpful for SDXL or flow-matching models, which already handle high CFG gracefully.

    Applies to DPM++ family, UniPC, DDIM, DEIS, SA Solver, and DC Solver. Recommended to leave off unless you see saturation artifacts.

    Disabled by default.","ui":"txt2img"}, {"id":"","label":"Tint strength","localized":"","hint":"","ui":"txt2img"}, {"id":"","label":"Texture tiling","localized":"","hint":"Apply seamless tiling to generated image so it can be used as a texture","ui":"txt2img"}, {"id":"","label":"Threshold","localized":"","hint":"","ui":"script_apg"}, @@ -1496,7 +1495,7 @@ {"id":"","label":"Tiny","localized":"","hint":"","ui":"control"}, {"id":"","label":"True guidance","localized":"","hint":"","ui":"video"}, {"id":"","label":"Tile frames","localized":"","hint":"","ui":"video"}, - {"id":"","label":"Task","localized":"","hint":"Changes which task the model will perform. Regular text prompts can be used when the task is set to Use Prompt.
    When other options are selected, see the hint text inside an empty Prompt field for guidance.","ui":"caption"}, + {"id":"","label":"Task","localized":"","hint":"Changes which task the model will perform. Regular text prompts can be used when the task is set to Use Prompt.
    When other options are selected, see the hint text inside an empty Prompt field for guidance.","ui":"caption"}, {"id":"","label":"Tagger Model","localized":"","hint":"Model to use for image tagging.
    WaifuDiffusion models (wd-*): Modern taggers with separate general and character thresholds.
    DeepBooru: Legacy tagger, uses only general threshold.","ui":"caption"}, {"id":"","label":"Torch","localized":"","hint":"","ui":"component-8779"}, {"id":"","label":"Transformers load using Run:ai streamer","localized":"","hint":"","ui":"settings_sd"}, @@ -1537,7 +1536,8 @@ {"id":"","label":"Upscale","localized":"","hint":"Upscale image","ui":"extras"}, {"id":"","label":"UI Tabs","localized":"","hint":"","ui":"settings_ui"}, {"id":"","label":"Upscaling","localized":"","hint":"","ui":"settings_postprocessing"}, - {"id":"","label":"Use segmentation","localized":"","hint":"Run detailer using segmentation mask","ui":"txt2img"}, + {"id":"","label":"Use segmentation","localized":"","hint":"Use the model's pixel-precise segmentation mask as the inpaint mask instead of the rectangular bounding box.
    Tighter mask means less unintended change around the detection (e.g., the inpaint stays on the face, not on the hair or background behind it). Better blending and smaller seams.

    Requires a segmentation-capable model (filename usually contains -seg). Bounding-box-only models silently fall back to the rectangle.
    Default off.","ui":"txt2img"}, + {"id":"","label":"Use init image","localized":"","hint":"Decides whether the input image is also used as an init image for img2img-style modification.
    No: Control only: the input is used only by the active control processor (depth, canny, pose, etc.) to guide the model; the picture itself is built from scratch by the model. Standard ControlNet behavior.
    1st: Same as control: the control input doubles as the init image, so the model starts from your image and modifies it instead of building one from scratch. Useful for inpainting, restyling, or adding control guidance to img2img with a single source image.
    2nd: Separate image: opens an extra Init input pane next to Control input so you can supply different sources for control conditioning and img2img init.

    Denoising strength controls how far the result moves from the init image and only takes effect in the two init modes.
    Default No: Control only.","ui":"control"}, {"id":"","label":"Unload adapter","localized":"","hint":"Unload IP adapter immediately after generate. Otherwise IP adapter will remain loaded for faster use in next generate process","ui":"txt2img"}, {"id":"","label":"Use same seed","localized":"","hint":"","ui":"script_prompts_from_file"}, {"id":"","label":"Use defaults","localized":"","hint":"","ui":"script_video"}, @@ -1600,7 +1600,7 @@ {"id":"","label":"Vignette","localized":"","hint":"Applies radial edge darkening that draws focus toward the center of the image.
    Higher values produce a stronger falloff from center to corners.

    Set to 0 to disable. Simulates the natural light falloff seen in vintage and cinematic lenses.","ui":"txt2img"}, {"id":"","label":"VAE type","localized":"","hint":"Choose if you want to run full VAE, reduced quality VAE or attempt to use remote VAE service","ui":"txt2img"}, {"id":"","label":"Version","localized":"","hint":"","ui":"script_pulid"}, - {"id":"","label":"Video format","localized":"","hint":"Format and codec of output video","ui":"script_video"}, + {"id":"","label":"Video format","localized":"","hint":"Container format and codec for the output video file.
    Pick a format your downstream tools understand. MP4/MP4V is broadly compatible with most players and editors. Other choices trade off file size, quality, and player support.

    Default MP4/MP4V.","ui":"script_video"}, {"id":"","label":"Video duration","localized":"","hint":"","ui":"script_video"}, {"id":"","label":"Video engine","localized":"","hint":"","ui":"video"}, {"id":"","label":"Video model","localized":"","hint":"","ui":"video"}, @@ -1632,10 +1632,10 @@ {"id":"","label":"Wildcards","localized":"","hint":""}, {"id":"","label":"WanAI","localized":"","hint":"","ui":"settings_model_options"}, {"id":"","label":"Watermarking","localized":"","hint":"","ui":"settings_saving-images"}, - {"id":"","label":"Width","localized":"","hint":"Image width","ui":"txt2img"}, + {"id":"","label":"Width","localized":"","hint":"Target width of the output image in pixels.
    For generation, this sets the resolution the model produces. For resize and upscale operations, this is the width the input is fitted to.

    Should be a multiple of 8 for SD1.x and SDXL latents; newer architectures (Flux, SD3, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"txt2img"}, {"id":"","label":"Weight","localized":"","hint":"","ui":"script_resadapter"}, - {"id":"","label":"Width after","localized":"","hint":"","ui":"control"}, - {"id":"","label":"Width mask","localized":"","hint":"","ui":"control"}, + {"id":"","label":"Width after","localized":"","hint":"Target width of the output image in pixels, applied after the model finishes generating (Post sub-tab in the Size accordion). Use this to upscale or downscale the final image before saving.

    Should be a multiple of 8 for SD1.x and SDXL latents; newer architectures (Flux, SD3, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"control"}, + {"id":"","label":"Width mask","localized":"","hint":"Target width of the input mask image in pixels (Mask sub-tab in the Size accordion). The mask is used for inpainting, outpainting, or as a control mask, and is resized so it aligns with the processing resolution.

    Should be a multiple of 8 for SD1.x and SDXL latents; newer architectures (Flux, SD3, video models) may require higher multiples (16, 32, or 64). Values that don't match are automatically floored to the nearest valid multiple for the loaded model.","ui":"control"}, {"id":"","label":"WebP lossless compression","localized":"","hint":"","ui":"settings_saving-images"}, {"id":"","label":"wavelet","localized":"","hint":"","ui":"settings_postprocessing"}, {"id":"","label":"Weights clip","localized":"","hint":"Forced merged weights to be no heavier than the original model, preventing burn in and overly saturated models","ui":"models_merge_tab"} diff --git a/html/locale_es.json b/html/locale_es.json index 42f8f72bb..94d82fbe0 100644 --- a/html/locale_es.json +++ b/html/locale_es.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "Medios de entrada", "reload": "", "hint": "Añadir imagen de entrada para ser utilizada para el procesamiento de imagen a imagen, inpaint o control" diff --git a/html/locale_fr.json b/html/locale_fr.json index 470792ab9..5b2c37935 100644 --- a/html/locale_fr.json +++ b/html/locale_fr.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "Média d'entrée", "reload": "", "hint": "Ajouter une image d'entrée à utiliser pour le traitement image-à-image, inpaint ou control" diff --git a/html/locale_he.json b/html/locale_he.json index 8d1e7704f..27a37076b 100644 --- a/html/locale_he.json +++ b/html/locale_he.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "מדית קלט", "reload": "", "hint": "הוספת תמונת קלט לשימוש עבור עיבוד תמונה-לתמונה, מילוי או בקרה" diff --git a/html/locale_hi.json b/html/locale_hi.json index 54d213b59..8aa989b29 100644 --- a/html/locale_hi.json +++ b/html/locale_hi.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "इनपुट मीडिया", "reload": "n/a", "hint": "इमेज-टू-इमेज, इनपेंट या कंट्रोल प्रोसेसिंग के लिए उपयोग की जाने वाली इनपुट छवि जोड़ें" diff --git a/html/locale_hr.json b/html/locale_hr.json index d092a3e55..efa35485f 100644 --- a/html/locale_hr.json +++ b/html/locale_hr.json @@ -4824,7 +4824,7 @@ }, { "id": 0, - "label": "Input Media", + "label": "Input", "localized": "Ulazni medij", "reload": "", "hint": "Dodajte ulaznu sliku koja će se koristiti za image-to-image, inpaint ili kontrolnu obradu" diff --git a/html/locale_id.json b/html/locale_id.json index c13c3fe19..7450256f8 100644 --- a/html/locale_id.json +++ b/html/locale_id.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "Media Masukan", "reload": "n/a", "hint": "Tambahkan gambar masukan untuk digunakan dalam pemrosesan image-to-image, inpaint, atau kontrol" diff --git a/html/locale_it.json b/html/locale_it.json index c557311c1..034654b91 100644 --- a/html/locale_it.json +++ b/html/locale_it.json @@ -4817,7 +4817,7 @@ }, { "id": 14, - "label": "Input Media", + "label": "Input", "localized": "Media di input", "reload": "n/a", "hint": "Aggiungi un'immagine di input da utilizzare per elaborazioni image-to-image, inpaint o di controllo" diff --git a/html/locale_ja.json b/html/locale_ja.json index d16520c68..e30dba4f0 100644 --- a/html/locale_ja.json +++ b/html/locale_ja.json @@ -4817,9 +4817,9 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "入力メディア", - "reload": "Input Media", + "reload": "Input", "hint": "画像間変換、インペイント、またはコントロール処理に使用する入力画像を追加します" }, { diff --git a/html/locale_ko.json b/html/locale_ko.json index 00e3a4032..a5918ee95 100644 --- a/html/locale_ko.json +++ b/html/locale_ko.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "입력 미디어", "reload": "n/a", "hint": "이미지 대 이미지, 인페인트 또는 제어 처리에 사용할 입력 이미지 추가" diff --git a/html/locale_nb.json b/html/locale_nb.json index 1542f19f6..89e39b0f7 100644 --- a/html/locale_nb.json +++ b/html/locale_nb.json @@ -4817,8 +4817,8 @@ }, { "id": 13, - "label": "Input Media", - "localized": "Input Media", + "label": "Input", + "localized": "Input", "reload": "", "hint": "Add an image here to use it as a base for editing or guiding the AI." }, diff --git a/html/locale_po.json b/html/locale_po.json index 8d8055819..b391db6af 100644 --- a/html/locale_po.json +++ b/html/locale_po.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "Media wejściowe", "reload": "", "hint": "Dodaj obraz wejściowy do użycia w przetwarzaniu typu image-to-image, inpaint lub control" diff --git a/html/locale_pt.json b/html/locale_pt.json index db261911e..065887bf9 100644 --- a/html/locale_pt.json +++ b/html/locale_pt.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "Mídia de Entrada", "reload": "", "hint": "Adicionar imagem de entrada a ser usada para processamento de imagem-para-imagem, inpaint ou controle" diff --git a/html/locale_qq.json b/html/locale_qq.json index b033d7be9..1d351b192 100644 --- a/html/locale_qq.json +++ b/html/locale_qq.json @@ -4817,7 +4817,7 @@ }, { "id": 0, - "label": "Input Media", + "label": "Input", "localized": "Media Input", "reload": "n/a", "hint": "Addere imaginem input adhibendam pro processu imaginis-ad-imaginem, inpaint, vel moderationis" diff --git a/html/locale_ru.json b/html/locale_ru.json index db706a1a2..318e7754f 100644 --- a/html/locale_ru.json +++ b/html/locale_ru.json @@ -4817,7 +4817,7 @@ }, { "id": 14, - "label": "Input Media", + "label": "Input", "localized": "Входные медиаданные", "reload": "", "hint": "Добавить входное изображение для использования в image-to-image, inpaint или для управления генерацией" diff --git a/html/locale_sr.json b/html/locale_sr.json index d0df82d7b..2cbccaada 100644 --- a/html/locale_sr.json +++ b/html/locale_sr.json @@ -4817,7 +4817,7 @@ }, { "id": 14, - "label": "Input Media", + "label": "Input", "localized": "Ulazni medij", "reload": "", "hint": "Dodajte ulaznu sliku koja će se koristiti za obradu slike-u-sliku, inpaint ili kontrolnu obradu" diff --git a/html/locale_tb.json b/html/locale_tb.json index 53f4ad85a..bb09b9850 100644 --- a/html/locale_tb.json +++ b/html/locale_tb.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "Source Telemetry", "reload": "", "hint": "Upload and synchronize source data for recursive generation, delta-patching, or neural guidance processing" diff --git a/html/locale_tlh.json b/html/locale_tlh.json index 80e758920..a0447e4c8 100644 --- a/html/locale_tlh.json +++ b/html/locale_tlh.json @@ -4817,7 +4817,7 @@ }, { "id": 14, - "label": "Input Media", + "label": "Input", "localized": "nI' Media", "reload": "", "hint": "nI' media" diff --git a/html/locale_tr.json b/html/locale_tr.json index 275409c4c..2a0ddfd06 100644 --- a/html/locale_tr.json +++ b/html/locale_tr.json @@ -4817,7 +4817,7 @@ }, { "id": 14, - "label": "Input Media", + "label": "Input", "localized": "Giriş Medyası", "reload": "", "hint": "Görüntüden görüntüye, inpaint veya kontrol işleme için kullanılacak giriş görüntüsünü ekleyin" diff --git a/html/locale_ur.json b/html/locale_ur.json index 5063f6e7e..4edfe260a 100644 --- a/html/locale_ur.json +++ b/html/locale_ur.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "ان پٹ میڈیا", "reload": "", "hint": "تصویر سے تصویر (image-to-image)، ان پینٹ یا کنٹرول پروسیسنگ کے لیے استعمال ہونے والی ان پٹ تصویر شامل کریں" diff --git a/html/locale_vi.json b/html/locale_vi.json index 4e631bb43..06187acbd 100644 --- a/html/locale_vi.json +++ b/html/locale_vi.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "Phương tiện đầu vào", "reload": "", "hint": "Thêm hình ảnh đầu vào để sử dụng cho xử lý hình ảnh thành hình ảnh, inpaint hoặc điều khiển" diff --git a/html/locale_xx.json b/html/locale_xx.json index 5674c5994..79d813296 100644 --- a/html/locale_xx.json +++ b/html/locale_xx.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "Eniga Amaskomunikilaro", "reload": "", "hint": "Aldoni enigeblan bildon por esti uzata por bild-al-bilda, inpaint aŭ kontrola prilaborado" diff --git a/html/locale_zh.json b/html/locale_zh.json index 7aa02402e..700de6f4e 100644 --- a/html/locale_zh.json +++ b/html/locale_zh.json @@ -4817,7 +4817,7 @@ }, { "id": 13, - "label": "Input Media", + "label": "Input", "localized": "输入媒体", "reload": "", "hint": "添加用于图生图、重绘或控制处理的输入图像" diff --git a/installer.py b/installer.py index b9cf4ff0c..1f658ae2e 100644 --- a/installer.py +++ b/installer.py @@ -37,7 +37,7 @@ log = logging.getLogger('sdnext.installer') debug = log.debug if os.environ.get('SD_INSTALL_DEBUG', None) is not None else lambda *args, **kwargs: None setuptools, distutils = None, None # defined via ensure_base_requirements current_branch = None -pip_log = '--log pip.log ' if os.environ.get('SD_PIP_DEBUG', None) is not None else '' +pip_log = '--log pip.log' if os.environ.get('SD_PIP_DEBUG', None) is not None else '' log_file = os.path.join(os.path.dirname(__file__), 'sdnext.log') hostname = socket.gethostname() log_rolled = False @@ -75,7 +75,7 @@ extensions_commit = { # force specific commit for extensions 'adetailer': 'a89c01d' # 'stable-diffusion-webui-images-browser': '27fe4a7', } -control_extensions = [ # 3rd party extensions marked as safe for control ui +control_extensions = [ # extensions marked as safe for control ui 'NudeNet', 'IP Adapters', 'Remove background', @@ -244,26 +244,35 @@ def cleanup_broken_packages(): pass -def pip(arg: str, ignore: bool = False, quiet: bool = True, uv = True) -> tuple[subprocess.CompletedProcess, str]: +def pip(arg: str, ignore: bool = False, quiet: bool = True, *, uv = True, constraints = True) -> tuple[subprocess.CompletedProcess | None, str]: t_start = time.time() originalArg = arg - arg = arg.replace('>=', '==') + arg = arg.replace('>=', '==').strip() if opts.get('offline_mode', False): log.warning('Offline mode enabled') return None, 'offline' - package = arg.replace("install", "").replace("--upgrade", "").replace("--no-deps", "").replace("--force-reinstall", "").replace(" ", " ").strip() + package = arg.replace("install", "").replace("--upgrade", "").replace("--no-deps", "").replace("--force-reinstall", "").strip() uv = uv and args.uv and not package.startswith('git+') pipCmd = "uv pip" if uv else "pip" if not quiet and '-r ' not in arg: log.info(f'Install: package="{package}" mode={"uv" if uv else "pip"}') - env_args = os.environ.get("PIP_EXTRA_ARGS", "") - all_args = f'{pip_log}{arg} {env_args}'.strip() + env_args = os.environ.get("PIP_EXTRA_ARGS", "").strip() + all_args: list[str] = [] + if pip_log: + all_args.append(pip_log) + all_args.append(arg) + if env_args: + all_args.append(env_args) + if constraints and "-c " not in env_args: + all_args.append("-c constraints.txt") if not quiet: - log.debug(f'Running: {pipCmd}="{all_args}"') - result, output = run(sys.executable, "-m", pipCmd, all_args) + log.debug(f'Running: {pipCmd}="{" ".join(all_args)}"') + + result, output = run(sys.executable, "-m", pipCmd, *all_args) + if len(result.stderr) > 0: if uv and result.returncode != 0: - log.warning(f'Install: cmd="{pipCmd}" args="{all_args}" cannot use uv, fallback to pip') + log.warning(f'Install: cmd="{pipCmd}" args="{" ".join(all_args)}" cannot use uv, fallback to pip') debug(f'Install: uv pip error: {result.stderr}') cleanup_broken_packages() return pip(originalArg, ignore, quiet, uv=False) @@ -485,7 +494,7 @@ def check_diffusers(): t_start = time.time() if args.skip_all: return - target_commit = "0f1abc4ae8b0eb2a3b40e82a310507281144c423" # diffusers commit hash == 0.37.1.dev-0427 + target_commit = "015da50b40ee7a082ea8c17a8c43dff717c9653e" # diffusers commit hash == 0.37.1.dev-0427 # if args.use_rocm or args.use_zluda or args.use_directml: # sha = '043ab2520f6a19fce78e6e060a68dbc947edb9f9' # lock diffusers versions for now pkg = package_spec('diffusers') @@ -521,7 +530,7 @@ def check_transformers(): else: # target_transformers = '4.57.6' target_transformers = None - target_tokenizers = '0.22.2' + target_tokenizers = '0.23.1' if target_transformers is not None: # Pinned release version (e.g. DirectML) if (pkg_transformers is None) or ((pkg_transformers.version != target_transformers) or (pkg_tokenizers is None) or ((pkg_tokenizers.version != target_tokenizers) and (not args.experimental))): @@ -1279,7 +1288,6 @@ def install_requirements(): # set environment variables controling the behavior of various libraries def set_environment(): log.debug('Setting environment tuning') - os.environ.setdefault('PIP_CONSTRAINT', 'constraints.txt') os.environ.setdefault('ACCELERATE', 'True') os.environ.setdefault('ATTN_PRECISION', 'fp16') os.environ.setdefault('ClDeviceGlobalMemSizeAvailablePercent', '100') @@ -1374,7 +1382,16 @@ def get_version(force=False): try: origin = run('git', 'remote get-url origin', check=True)[0].stdout branch_name = run('git', 'rev-parse --abbrev-ref HEAD', check=True)[0].stdout - version['url'] = origin.removesuffix('.git') + '/tree/' + branch_name + # normalize ssh remotes (git@host:owner/repo) and ssh-protocol remotes + # (ssh://git@host/owner/repo) to the canonical https form so downstream + # url parsers don't have to special-case each remote shape + if origin.startswith('git@'): + host, _, path = origin.partition(':') + origin = f'https://{host[4:]}/{path}' + elif origin.startswith('ssh://'): + origin = 'https://' + origin[len('ssh://'):].split('@', 1)[-1] + origin = origin.removesuffix('.git') + version['url'] = origin + '/tree/' + branch_name version['branch'] = branch_name if version['branch'] == 'HEAD': log.warning('Version: detached state detected') @@ -1530,7 +1547,10 @@ def check_version(reset=True): # pylint: disable=unused-argument api_base = f'https://api.github.com/repos/{url_parts}' else: api_base = 'https://api.github.com/repos/vladmandic/sdnext' - branches = requests.get(f'{api_base}/branches', timeout=10).json() + branches = requests.get(f'{api_base}/branches', timeout=5).json() + if not isinstance(branches, list): + log.error(f'Repository: branches API returned {branches!r} from {api_base}') + return branch_names = [b['name'] for b in branches if 'name' in b] log.trace(f'Repository branches: active={branch_name} available={branch_names}') except Exception as e: @@ -1541,7 +1561,7 @@ def check_version(reset=True): # pylint: disable=unused-argument ts('latest', t_start) return try: - commits = requests.get(f'{api_base}/branches/{branch_name}', timeout=10).json() + commits = requests.get(f'{api_base}/branches/{branch_name}', timeout=5).json() latest = commits['commit']['sha'] if len(latest) != 40: log.error(f'Repository error: commit={latest} invalid') diff --git a/javascript/autocomplete.js b/javascript/autocomplete.js index e6e9d2bc7..b020490c2 100644 --- a/javascript/autocomplete.js +++ b/javascript/autocomplete.js @@ -358,7 +358,7 @@ function insertExtraNetwork(textarea, item, kind) { } /** Insert a tag at the current word position, replacing the typed prefix. */ -function insertTag(textarea, tagName) { +function insertTag(textarea, tagName, kind = 'tag') { const info = getCurrentWord(textarea); if (!info || (info.mode !== 'tag' && info.mode !== 'artist')) return; const { value } = textarea; @@ -371,13 +371,13 @@ function insertTag(textarea, tagName) { const prefix = needsSepBefore ? `${sep} ` : ''; let suffix = `${sep} `; if (after.length > 0 && after.trimStart().startsWith(',')) suffix = ' '; - // Artist mode: optionally keep the `@` prefix (Anima syntax); always convert underscores to spaces - // since Anima requires space-separated artist names. The `@` is consumed for non-Anima models. + // Embedding names are file-system identifiers, so underscores must be preserved regardless of the user setting. + // Tags and artists honor `autocomplete_keep_underscores`; default is to swap `_` for space. + const keepUnderscores = window.opts?.autocomplete_keep_underscores ?? false; let body = tagName; - if (info.mode === 'artist') { - body = body.replace(/_/g, ' '); - if (window.opts?.autocomplete_at_prefix_artist) body = `@${body}`; - } + if (kind !== 'embed' && !keepUnderscores) body = body.replace(/_/g, ' '); + // Artist mode optionally keeps the `@` prefix (Anima syntax). The `@` is consumed for non-Anima models. + if (info.mode === 'artist' && window.opts?.autocomplete_at_prefix_artist) body = `@${body}`; const insertion = `${prefix}${escapeParensForPrompt(body)}${suffix}`; textarea.value = before.trimEnd() + (before.trimEnd().length > 0 ? ' ' : '') + insertion + after.trimStart(); // Position cursor after the inserted tag + separator @@ -447,7 +447,7 @@ const dropdown = { }, render() { - const replaceUnderscores = window.opts?.autocomplete_replace_underscores ?? true; + const keepUnderscores = window.opts?.autocomplete_keep_underscores ?? false; const queryNorm = this.query.toLowerCase().replace(/ /g, '_'); this.listEl.replaceChildren(); this.results.forEach((tag, i) => { @@ -462,7 +462,9 @@ const dropdown = { dot.title = kind === 'tag' ? (engine.categoryNames[tag.category] || '') : kind; const name = document.createElement('span'); name.className = 'autocomplete-tag'; - const tagText = replaceUnderscores ? tag.display.replace(/_/g, ' ') : tag.display; + // Embeddings are file-name identifiers, so they always render as-is to match how they get inserted. + const swapForKind = kind !== 'embed'; + const tagText = (swapForKind && !keepUnderscores) ? tag.display.replace(/_/g, ' ') : tag.display; const canonicalMatch = tag.name.indexOf(queryNorm); if (canonicalMatch >= 0 && queryNorm.length > 0) { const mark = document.createElement('mark'); @@ -480,7 +482,7 @@ const dropdown = { if (tag.matchedVia === 'alias') annotationTerm = tag.matchedAlias; else if (tag.matchedVia === 'translation') annotationTerm = tag.matchedTerm; if (annotationTerm) { - const annotationDisplay = replaceUnderscores ? annotationTerm.replace(/_/g, ' ') : annotationTerm; + const annotationDisplay = (swapForKind && !keepUnderscores) ? annotationTerm.replace(/_/g, ' ') : annotationTerm; const annotationLower = annotationTerm.toLowerCase(); const annotationMatch = annotationLower.indexOf(queryNorm); const prefix = tag.matchedVia === 'translation' ? ' \u{1F310} ' : ' ('; @@ -561,7 +563,7 @@ const dropdown = { insertExtraNetwork(this.textarea, result, result.kind); } else { // 'embed' kind and untagged tag results both go through insertTag (comma-aware, paren-escaped). - insertTag(this.textarea, result.display ?? result.name); + insertTag(this.textarea, result.display ?? result.name, result.kind); } } this.hide(); @@ -745,24 +747,24 @@ async function initAutocomplete() { log('autoComplete', { active, enabled }); // Inject styles (CSS files in javascript/ are not auto-loaded) const style = document.createElement('style'); - style.textContent = [ - '.autocompleteResults { position: fixed; z-index: 9999; max-height: 300px; overflow-y: auto;', - ' background: var(--sd-main-background-color, var(--background-fill-primary, #1f2937));', - ' border: 1px solid var(--sd-input-border-color, var(--border-color-primary, #374151));', - ' border-radius: var(--sd-border-radius, 6px); box-shadow: 0 4px 16px rgba(0,0,0,0.4);', - ' font-size: 13px; scrollbar-width: thin; }', - '.autocompleteResultsList { list-style: none; margin: 0; padding: 4px 0; }', - '.autocompleteResultsList > li { display: flex; align-items: center; padding: 6px 12px; cursor: pointer;', - ' gap: 8px; line-height: 1.4; transition: background 0.1s ease; border-bottom: 1px solid rgba(255,255,255,0.03); }', - '.autocompleteResultsList > li:last-child { border-bottom: none; }', - '.autocompleteResultsList > li:hover { background: var(--sd-panel-background-color, var(--input-background-fill-focus, #374151)); }', - '.autocompleteResultsList > li.selected { background: var(--sd-main-accent-color, var(--button-primary-background-fill, #4b5563)); }', - '.autocomplete-category { font-size: 10px; flex-shrink: 0; width: 10px; text-align: center; cursor: help; }', - '.autocomplete-tag { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }', - '.autocomplete-tag mark { background: transparent; color: inherit; font-weight: 700; }', - '.autocomplete-count { font-size: 0.75em; opacity: 0.45; flex-shrink: 0; font-variant-numeric: tabular-nums;', - ' background: rgba(255,255,255,0.06); padding: 1px 6px; border-radius: 8px; min-width: 28px; text-align: right; }', - ].join('\n'); + style.textContent = ` + .autocompleteResults { position: fixed; z-index: 9999; max-height: 300px; overflow-y: auto; + background: var(--sd-main-background-color, var(--background-fill-primary, #1f2937)); + border: 1px solid var(--sd-input-border-color, var(--border-color-primary, #374151)); + border-radius: var(--sd-border-radius, 6px); box-shadow: 0 4px 16px rgba(0,0,0,0.4); + font-size: 13px; scrollbar-width: thin; color: var(--body-text-color-subdued); } + .autocompleteResultsList { list-style: none; margin: 0; padding: 4px 0; } + .autocompleteResultsList > li { display: flex; align-items: center; padding: 6px 12px; cursor: pointer; + gap: 8px; line-height: 1.4; transition: background 0.1s ease; border-bottom: 1px solid rgba(255,255,255,0.03); } + .autocompleteResultsList > li:last-child { border-bottom: none; } + .autocompleteResultsList > li:hover { background: var(--sd-panel-background-color, var(--input-background-fill-focus, #374151)); } + .autocompleteResultsList > li.selected { background: var(--sd-main-accent-color, var(--button-primary-background-fill, #4b5563)); } + .autocomplete-category { font-size: 10px; flex-shrink: 0; width: 10px; text-align: center; cursor: help; } + .autocomplete-tag { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .autocomplete-tag mark { background: transparent; color: inherit; font-weight: 700; } + .autocomplete-count { font-size: 0.75em; opacity: 0.45; flex-shrink: 0; font-variant-numeric: tabular-nums; + background: rgba(255,255,255,0.06); padding: 1px 6px; border-radius: 8px; min-width: 28px; text-align: right; } + `; document.head.appendChild(style); dropdown.init(); await engine.loadEnabled(); diff --git a/javascript/control.js b/javascript/control.js index 06da73910..1505a6b8d 100644 --- a/javascript/control.js +++ b/javascript/control.js @@ -8,11 +8,15 @@ function controlInputMode(inputMode, ...args) { const tabNames = ['Image', 'Video', 'Batch', 'Folder']; let inputTab = tabNames[tabIdx] || 'Image'; log('controlInputMode', { mode: inputMode, tab: inputTab, kanvas: typeof Kanvas }); + + // if kanvas is available overwrite image inputs with kanvas images if ((inputTab === 'Image') && (typeof 'Kanvas' !== 'undefined')) { inputTab = 'Kanvas'; - const imageData = window.kanvas.getImage(); - args[0] = imageData; + for (let i = 0; i < window.kanvas.stages.maxStages; i++) { + args[4 + i] = window.kanvas.getImage(1 + i, false, false); + } } + return [inputTab, ...args]; } diff --git a/javascript/gallery.js b/javascript/gallery.js index 377577892..1a123d97a 100644 --- a/javascript/gallery.js +++ b/javascript/gallery.js @@ -1,7 +1,11 @@ /* eslint-disable max-classes-per-file */ let ws; let url; +let currentSize = 0; +let currentSort = 'none'; +let currentName = ''; let currentImage = null; +let currentTitle = ''; let currentGalleryFolder = null; let pruneImagesTimer; let outstanding = 0; @@ -18,7 +22,9 @@ const el = { search: undefined, status: undefined, btnSend: undefined, + overlay: undefined, clearCacheFolder: undefined, + size: undefined, }; const SUPPORTED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'tiff', 'jp2', 'jxl', 'gif', 'mp4', 'mkv', 'avi', 'mjpeg', 'mpg', 'avr']; @@ -26,12 +32,12 @@ const SUPPORTED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'tiff', 'jp2', 'jxl' const gallerySorter = { nameA: { name: 'Name Ascending', func: (a, b) => a.name.localeCompare(b.name) }, nameD: { name: 'Name Descending', func: (b, a) => a.name.localeCompare(b.name) }, - sizeA: { name: 'Size Ascending', func: (a, b) => a.size - b.size }, - sizeD: { name: 'Size Descending', func: (b, a) => a.size - b.size }, - resA: { name: 'Resolution Ascending', func: (a, b) => a.width * a.height - b.width * b.height }, - resD: { name: 'Resolution Descending', func: (b, a) => a.width * a.height - b.width * b.height }, - modA: { name: 'Modified Ascending', func: (a, b) => a.mtime - b.mtime }, - modD: { name: 'Modified Descending', func: (b, a) => a.mtime - b.mtime }, + sizeD: { name: 'Size Ascending', func: (a, b) => a.size - b.size }, + sizeA: { name: 'Size Descending', func: (b, a) => a.size - b.size }, + resD: { name: 'Resolution Ascending', func: (a, b) => a.width * a.height - b.width * b.height }, + resA: { name: 'Resolution Descending', func: (b, a) => a.width * a.height - b.width * b.height }, + modD: { name: 'Modified Ascending', func: (a, b) => a.mtime - b.mtime }, + modA: { name: 'Modified Descending', func: (b, a) => a.mtime - b.mtime }, none: { name: 'None', func: undefined }, }; @@ -71,6 +77,8 @@ function resetGallerySelection() { updateGallerySelectionClasses(gallerySelection.files, -1); gallerySelection = { files: [], index: -1 }; currentImage = null; + currentName = ''; + currentTitle = ''; } function applyGallerySelection(index, { send = true } = {}) { @@ -84,6 +92,8 @@ function applyGallerySelection(index, { send = true } = {}) { } gallerySelection.index = index; currentImage = files[index].src; + currentName = files[index].name; + currentTitle = files[index].title; updateGallerySelectionClasses(files, index); if (send && el.btnSend) el.btnSend.click(); } @@ -129,7 +139,7 @@ async function awaitForGallery(expectedSize, signal) { function updateGalleryStyles() { if (opts.theme_type?.toLowerCase() === 'modern') { - folderStylesheet.replaceSync(` + folderStylesheet.replace(` .gallery-folder { cursor: pointer; padding: 8px 6px 8px 6px; @@ -162,7 +172,7 @@ function updateGalleryStyles() { } `); } else { - folderStylesheet.replaceSync(` + folderStylesheet.replace(` .gallery-folder { cursor: pointer; padding: 8px 6px 8px 6px; @@ -179,16 +189,30 @@ function updateGalleryStyles() { } `); } - fileStylesheet.replaceSync(` + const size = el.size ? el.size.value : opts.extra_networks_card_size; + fileStylesheet.replace(` .gallery-file { object-fit: contain; cursor: pointer; - height: ${opts.extra_networks_card_size}px; - width: ${opts.browser_fixed_width ? `${opts.extra_networks_card_size}px` : 'unset'}; + height: ${size}px; + width: ${opts.browser_fixed_width ? `${size}px` : 'unset'}; } .gallery-file:hover { filter: grayscale(100%); } + .gallery-overlay { + position: absolute; + height: 24px; + background-color: rgba(0,0,0,0.7); + display: block; + text-align: right; + padding: 4px; + font-size: 1.2em; + letter-spacing: 0.5em; + width: 140px; + margin-top: calc(140px - 32px); + opacity: 75%; + } :host(.gallery-file-selected) .gallery-file { box-shadow: 0 0 0 2px var(--sd-button-selected-color); } @@ -409,9 +433,7 @@ class GalleryFolder extends HTMLElement { this.div.classList.add('gallery-folder-selected'); GalleryFolder.#active = this; for (const folder of GalleryFolder.folders) { - if (folder !== this) { - folder.div.classList.remove('gallery-folder-selected'); - } + if (folder !== this) folder.div.classList.remove('gallery-folder-selected'); } } } @@ -456,7 +478,6 @@ class GalleryFile extends HTMLElement { this.height = 0; this.shadow = this.attachShadow({ mode: 'open' }); this.shadow.adoptedStyleSheets = [fileStylesheet]; - this.firstRun = true; } @@ -469,9 +490,7 @@ class GalleryFile extends HTMLElement { if (dir && dir[1]) { const dirPath = dir[1]; const isOpen = separatorStates.get(dirPath); - if (isOpen === false) { - this.style.display = 'none'; - } + if (isOpen === false) this.style.display = 'none'; } this.hash = await getHash(`${this.src}/${this.size}/${this.mtime}`) @@ -514,7 +533,7 @@ class GalleryFile extends HTMLElement { this.size = json.size; this.mtime = new Date(json.mtime); if (opts.browser_cache && this.hash) { - await idbAdd({ + idbAdd({ hash: this.hash, folder: this.fullFolder, file: this.name, @@ -534,23 +553,30 @@ class GalleryFile extends HTMLElement { img.src = `file=${this.src}`; } } - if (this.#signal.aborted) { // Do not change the operations order from here... - return; - } + if (this.#signal.aborted) return; galleryHashes.add(this.hash); - if (!ok) { - return; - } // ... to here unless modifications are also being made to maintenance functionality and the usage of AbortController/AbortSignal + if (!ok) return; + img.onclick = () => { setGallerySelectionByElement(this, { send: true }); }; + img.onpointerenter = () => { + el.overlay.display = 'block'; + this.shadow.appendChild(el.overlay); + currentImage = this.src; + currentName = this.name; + currentTitle = this.title; + }; + img.onpointerleave = () => { + el.overlay.display = 'none'; + }; img.title = `Folder: ${this.folder}\nFile: ${this.name}\nSize: ${this.size.toLocaleString()} bytes\nModified: ${this.mtime.toLocaleString()}`; this.title = img.title; // Final visibility check based on search term. const shouldDisplayBasedOnSearch = this.title.toLowerCase().includes(el.search.value.toLowerCase()); if (this.style.display !== 'none') { // Only proceed if not already hidden by a closed separator - this.style.display = shouldDisplayBasedOnSearch ? 'unset' : 'none'; + this.style.display = shouldDisplayBasedOnSearch ? 'flex' : 'none'; } this.shadow.appendChild(img); @@ -558,8 +584,10 @@ class GalleryFile extends HTMLElement { } async function createThumb(img) { - const height = opts.extra_networks_card_size; - const width = opts.browser_fixed_width ? opts.extra_networks_card_size : 0; + const sizeEl = document.getElementById('gallery-thumb-size'); + currentSize = sizeEl ? parseInt(sizeEl.value, 10) : opts.extra_networks_card_size; + const height = currentSize; + const width = opts.browser_fixed_width ? currentSize : 0; const canvas = document.createElement('canvas'); const scaleY = height / img.height; const scaleX = width > 0 ? width / img.width : scaleY; @@ -872,8 +900,13 @@ const findDuplicates = (arr, key) => { }; async function gallerySort(key) { - if (!Object.hasOwn(gallerySorter, key)) { - error(`Gallery: "${key}" is not a valid gallery sorting key`); + // if currentSort does not start with key, default to key+A + // else if currentSort ends with A change to D and vice versa for toggling sort order + if (currentSort.startsWith(key)) currentSort = currentSort.endsWith('A') ? `${key}D` : `${key}A`; + else currentSort = `${key}A`; + + if (!Object.hasOwn(gallerySorter, currentSort)) { + error(`Gallery: "${currentSort}" is not a valid gallery sorting key`); return; } const t0 = performance.now(); @@ -901,7 +934,7 @@ async function gallerySort(key) { folderGroups.get(dir).push(file); } - sortMode = gallerySorter[key]; + sortMode = gallerySorter[currentSort]; // Sort root files rootFiles.sort(sortMode.func); @@ -993,24 +1026,24 @@ async function thumbCacheCleanup(folder, imgCount, controller, force = false) { if (typeof folder !== 'string' || typeof imgCount !== 'number') { throw new Error('Function called with invalid arguments'); } - debug('Thumbnail DB cleanup: Waiting for gallery data to settle'); + debug('thumbCacheCleanup: wait'); await awaitForGallery(imgCount, controller.signal); } catch (err) { - debug(`Thumbnail DB cleanup: Skipping cleanup for "${folder}" due to "${err}"`); + error('thumbCacheCleanup', { folder, error: err }); return; } maintenanceQueue.enqueue({ signal: controller.signal, callback: async () => { - log(`Thumbnail DB cleanup: Checking if "${folder}" needs cleaning`); + log('maintenanceQueue', { folder }); const t0 = performance.now(); const keptGalleryHashes = force ? new Set() : new Set(galleryHashes.values()); // External context should be safe since this function run is guarded by AbortController/AbortSignal in the SimpleFunctionQueue const folderNormalized = folder.replace(/\/+/g, '/').replace(/\/$/, ''); const recursiveFolder = IDBKeyRange.bound(folderNormalized, `${folderNormalized}\uffff`, false, true); const cachedHashesCount = await idbCount(recursiveFolder) .catch((e) => { - error(`Thumbnail DB cleanup: Error when getting entry count for "${folder}".`, e); + error('maintenanceQueue', { folder, error: e }); return Infinity; // Forces next check to fail if something went wrong }); const cleanupCount = cachedHashesCount - keptGalleryHashes.size; @@ -1020,21 +1053,21 @@ async function thumbCacheCleanup(folder, imgCount, controller, force = false) { } if (controller.signal.aborted) { - debug(`Thumbnail DB cleanup: Cancelling "${folder}" cleanup due to "${controller.signal.reason}"`); + debug('maintenanceQueue', { folder, reason: controller.signal.reason }); return; } const cb_clearMsg = showCleaningMsg(cleanupCount); await idbFolderCleanup(keptGalleryHashes, recursiveFolder, controller.signal) .then((delcount) => { const t1 = performance.now(); - log(`Thumbnail DB cleanup: folder=${folder} kept=${keptGalleryHashes.size} deleted=${delcount} time=${Math.round(t1 - t0)}ms`); + log('maintenanceQueue', { folder, kept: keptGalleryHashes.size, deleted: delcount, time: Math.round(t1 - t0) }); timer(`thumbnailDBCleanup:${folder}`, t1 - t0); currentGalleryFolder = null; el.clearCacheFolder.innerText = ''; updateStatusWithSort('Thumbnail cache cleared'); }) .catch((e) => { - SimpleFunctionQueue.abortLogger('Thumbnail DB cleanup:', e); + SimpleFunctionQueue.abortLogger('thumbCacheCleanup', e); }) .finally(async () => { await new Promise((resolve) => { setTimeout(resolve, 1000); }); @@ -1307,6 +1340,80 @@ async function initGalleryAutoRefresh() { galleryVisObserver.observe(galleryTab, { attributeFilter: ['class', 'style'], attributeOldValue: true }); } +async function overlayDelete(evt) { + const res = await authFetch(`${window.api}/delete-image?file=${encodeURIComponent(currentImage)}`); + evt.stopPropagation(); + if (!res || res.status !== 200) { + error('galleryDelete', { file: currentImage, status: res?.status, statusText: res?.statusText }); + return; + } + const data = await res.json(); + log('galleryDelete', data); + GalleryFolder.getActive()?.click(); +} + +async function overlayDownload(evt) { + log('galleryDownload', currentImage); + const link = document.createElement('a'); + link.href = `/file=${encodeURIComponent(currentImage)}`; + link.download = currentName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + evt.stopPropagation(); +} + +async function overlayInfo(evt) { + evt.stopPropagation(); + const tgt = document.getElementById('html_info_formatted_gallery'); + if (!tgt) return; + const res = await authFetch(`${window.api}/png-info?file=${encodeURI(currentImage)}`); + if (!res || res.status !== 200) return; + const data = await res.json(); + log('galleryInfo res', data); + const prompt = data?.parameters?.Prompt || ''; + const negative = data?.parameters?.Negative || data?.parameters?.['Negative prompt'] || ''; + const raw = data?.info || ''; + const params = data?.parameters || {}; + delete params.Prompt; + delete params.Negative; + delete params['Negative prompt']; + const paramsFormatted = Object.entries(params).map(([key, value]) => `${key}: ${value}`).join(' | '); + tgt.innerHTML = ` +
    File: ${currentImage}
    +
    Prompt: ${prompt}
    +
    Negative: ${negative}
    +
    ${paramsFormatted}
    +
    Raw:
    ${raw}
    + `; + const img = document.querySelector('#gallery_gallery img'); + if (img) img.src = `/file=${encodeURIComponent(currentImage)}?t=${Date.now()}`; // Force refresh in case info endpoint is faster than cache update + const status = document.querySelector('#html_log_gallery p'); + if (status) status.innerText = currentTitle; +} + +async function createOverlay() { + if (el.overlay) return; + el.overlay = document.createElement('div'); + el.overlay.className = 'gallery-overlay'; + const btnDownload = document.createElement('span'); + btnDownload.innerHTML = '\udb85\udc64'; + btnDownload.title = 'Download image'; + btnDownload.style.cursor = 'pointer'; + btnDownload.addEventListener('click', overlayDownload); + const btnDelete = document.createElement('span'); + btnDelete.innerHTML = '\uf05c'; + btnDelete.title = 'Delete image'; + btnDelete.style.cursor = 'pointer'; + btnDelete.addEventListener('click', overlayDelete); + const btnInfo = document.createElement('span'); + btnInfo.innerHTML = '\uf05a'; + btnInfo.title = 'Image metadata'; + btnInfo.style.cursor = 'pointer'; + btnInfo.addEventListener('click', overlayInfo); + el.overlay.append(btnInfo, btnDelete, btnDownload); +} + async function blockQueueUntilReady() { // Add block to maintenanceQueue until cache is ready maintenanceQueue.enqueue({ @@ -1329,22 +1436,27 @@ async function initGallery() { // triggered on gradio change to monitor when ui el.files = gradioApp().getElementById('tab-gallery-files'); el.status = gradioApp().getElementById('tab-gallery-status'); el.search = gradioApp().querySelector('#tab-gallery-search textarea'); + el.size = document.getElementById('tab-gallery-thumb-size'); if (!el.folders || !el.files || !el.status || !el.search) { error('initGallery', 'Missing gallery elements'); return; } + if (el.size) { + el.size.value = opts.extra_networks_card_size; + el.size.addEventListener('input', updateGalleryStyles); + } blockQueueUntilReady(); // Run first + createOverlay(); updateGalleryStyles(); injectGalleryStatusCSS(); setOverlayAnimation(); galleryClearInit(); + const progress = gradioApp().getElementById('tab-gallery-progress'); - if (progress) { - galleryProgressBar.attachTo(progress); - } else { - log('initGallery', 'Failed to attach loading progress bar'); - } + if (progress) galleryProgressBar.attachTo(progress); + else log('initGallery', 'Failed to attach loading progress bar'); + el.search.addEventListener('input', gallerySearch); el.btnSend = gradioApp().getElementById('tab-gallery-send-image'); document.getElementById('tab-gallery-files').style.height = opts.logmonitor_show ? '75vh' : '85vh'; diff --git a/javascript/jquery.js b/javascript/jquery.js new file mode 100644 index 000000000..c443b2fc9 --- /dev/null +++ b/javascript/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.0 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},m=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||m).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.0 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),b=new RegExp(ge+"|>"),A=new RegExp(g),D=new RegExp("^"+t+"$"),N={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+d),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},L=/^(?:input|select|textarea|button)$/i,j=/^h\d$/i,O=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,P=/[+~]/,H=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),q=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},R=function(){V()},M=K(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{E.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){E={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,d=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==d&&9!==d&&11!==d)return n;if(!r&&(V(e),e=e||T,C)){if(11!==d&&(u=O.exec(t)))if(i=u[1]){if(9===d){if(!(a=e.getElementById(i)))return n;if(a.id===i)return E.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return E.call(n,a),n}else{if(u[2])return E.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return E.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||p&&p.test(t))){if(c=t,f=e,1===d&&(b.test(t)||m.test(t))){(f=P.test(t)&&X(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=k)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+G(l[o]);c=l.join(",")}try{return E.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>x.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function B(e){return e[k]=!0,e}function F(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function $(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&M(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function U(a){return B(function(o){return o=+o,B(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function X(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",R),le.getById=F(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=F(function(e){return i.call(e,"*")}),le.scope=F(function(){return T.querySelectorAll(":scope")}),le.cssHas=F(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(x.filter.ID=function(e){var t=e.replace(H,q);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var n=e.replace(H,q);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),x.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},x.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},p=[],F(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||p.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+k+"-]").length||p.push("~="),e.querySelectorAll("a#"+k+"+*").length||p.push(".#.+[+~]"),e.querySelectorAll(":checked").length||p.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&p.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||p.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||p.push(":has"),p=p.length&&new RegExp(p.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!p||!p.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(H,q),e[3]=(e[3]||e[4]||e[5]||"").replace(H,q),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return N.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&A.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(H,q).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function C(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||E,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:k.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:m,!0)),T.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=m.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,E=ce(m);var S=/^(?:parents|prev(?:Until|All))/,A={children:!0,contents:!0,next:!0,prev:!0};function D(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Te=/^$|^module$|\/(?:java|ecma)script/i;re=m.createDocumentFragment().appendChild(m.createElement("div")),(be=m.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),re.appendChild(be),le.checkClone=re.cloneNode(!0).cloneNode(!0).lastChild.checked,re.innerHTML="",le.noCloneChecked=!!re.cloneNode(!0).lastChild.defaultValue,re.innerHTML="",le.option=!!re.lastChild;var Ce={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function Ee(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function ke(e,t){for(var n=0,r=e.length;n",""]);var Se=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),d=[],p=0,h=e.length;p\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Me(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Ie(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function We(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n
    ",2===yt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=m.implementation.createHTMLDocument("")).createElement("base")).href=m.location.href,t.head.appendChild(r)):t=m),o=!n&&[],(i=T.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||K})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return R(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Qe(le.pixelPosition,function(e,t){if(t)return t=Ve(e,n),$e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return R(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0 { + const removeLivePreview = (ok = false) => { debug('taskEnd:', id_task); localStorage.removeItem('task'); setProgress(); @@ -124,6 +126,11 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres for (const gallery of galleries) gallery.style.display = 'flex'; // remove all galleries try { if (parentGallery && livePreview) { + if (ok) { + const previewImg = gradioApp().querySelector('#livePreviewImage'); + const galleryImg = gradioApp().querySelector('#control_gallery img'); + if (previewImg?.src && galleryImg) galleryImg.src = previewImg.src; // copy preview to gallery if everything is ok + } parentGallery.removeChild(livePreview); parentGallery.style.minHeight = 'unset'; parentGallery.style.maxHeight = 'unset'; @@ -135,18 +142,28 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres if (atEnd) atEnd(); }; - const start = (id_task, id_live_preview) => { // eslint-disable-line no-shadow + const startLivePreview = (id_task, id_live_preview) => { // eslint-disable-line no-shadow if (opts.live_preview_refresh_period === 0) return; const request_id = document.hidden ? -1 : id_live_preview; const onProgressHandler = (res) => { - if (res?.debug) debug('livePreview:', dateStart, request_id, res); + if (res?.debug) debug('progress:', { start: dateStart, id: request_id, res }); lastState = res; const elapsedFromStart = (new Date() - dateStart) / 1000; hasStarted |= res.active; - if (res.completed || (!res.active && (hasStarted || once)) || (elapsedFromStart > 120 && !res.queued && res.progress === prevProgress)) { - debug('livePreview end:', res); - done(); + if (res.completed || (!res.active && (hasStarted || once))) { + debug('progress', { end: res, reason: res.completed ? 'completed' : 'inactive' }); + if (!res.paused) removeLivePreview(true); // only abort if not paused + return; + } + if (elapsedFromStart > progressTimeout && !res.queued && res.progress === prevProgress) { + debug('progress', { end: res, reason: 'progressSimeout' }); + if (!res.paused) removeLivePreview(false); // only abort if not paused + return; + } + if (elapsedFromStart > startTimeout && !res.queued && !res.active) { + debug('progress', { end: res, reason: 'startTimeout' }); + if (!res.paused) removeLivePreview(false); // only abort if not paused return; } if (res.progress !== prevProgress) { @@ -160,16 +177,16 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres id_live_preview = res.id_live_preview; } if (onProgress) onProgress(res); - setTimeout(() => start(id_task, id_live_preview), opts.live_preview_refresh_period || 500); + setTimeout(() => startLivePreview(id_task, id_live_preview), opts.live_preview_refresh_period || 500); }; const onProgressErrorHandler = (err) => { - error(`livePreview: ${err}`); - done(); + error('progress', { error: err }); + removeLivePreview(false); }; xhrPost('./internal/progress', { id_task, id_live_preview: request_id }, onProgressHandler, onProgressErrorHandler, false, 30000); }; - debug('livePreview start:', dateStart); - start(id_task, 0); + debug('progress', { start: dateStart }); + startLivePreview(id_task, 0); } diff --git a/javascript/script.js b/javascript/script.js index c4428584e..f462642e1 100644 --- a/javascript/script.js +++ b/javascript/script.js @@ -41,12 +41,17 @@ const optionsChangedCallbacks = []; let uiCurrentTab = null; let uiAfterUpdateTimeout = null; +function registerCallback(queue, callback) { + if (queue.includes(callback)) return; + queue.push(callback); +} + function onAfterUiUpdate(callback) { if (typeof callback !== 'function') { error(`onAfterUiUpdate was called without a valid value. Expected a function but got: ${callback}`); return; } - uiAfterUpdateCallbacks.push(callback); + registerCallback(uiAfterUpdateCallbacks, callback); } function onUiUpdate(callback) { @@ -54,7 +59,7 @@ function onUiUpdate(callback) { error(`onUiUpdate was called without a valid value. Expected a function but got: ${callback}`); return; } - uiUpdateCallbacks.push(callback); + registerCallback(uiUpdateCallbacks, callback); } function onUiLoaded(callback) { @@ -62,7 +67,7 @@ function onUiLoaded(callback) { error(`onUiLoaded was called without a valid value. Expected a function but got: ${callback}`); return; } - uiLoadedCallbacks.push(callback); + registerCallback(uiLoadedCallbacks, callback); } function onUiReady(callback) { @@ -70,7 +75,7 @@ function onUiReady(callback) { error(`onUiReady was called without a valid value. Expected a function but got: ${callback}`); return; } - uiReadyCallbacks.push(callback); + registerCallback(uiReadyCallbacks, callback); } function onUiTabChange(callback) { @@ -78,7 +83,7 @@ function onUiTabChange(callback) { error(`onUiTabChange was called without a valid value. Expected a function but got: ${callback}`); return; } - uiTabChangeCallbacks.push(callback); + registerCallback(uiTabChangeCallbacks, callback); } function onOptionsChanged(callback) { @@ -86,7 +91,7 @@ function onOptionsChanged(callback) { error(`onOptionsChanged was called without a valid value. Expected a function but got: ${callback}`); return; } - optionsChangedCallbacks.push(callback); + registerCallback(optionsChangedCallbacks, callback); } function executeCallbacks(queue, arg) { @@ -179,6 +184,88 @@ document.addEventListener('keydown', (e) => { } }); +function getSortableCellValue(cell, sortType) { + const rawValue = cell?.dataset?.sortValue ?? cell?.textContent?.trim() ?? ''; + if (sortType === 'number') { + const numericValue = Number.parseFloat(rawValue); + return Number.isNaN(numericValue) ? Number.NEGATIVE_INFINITY : numericValue; + } + return rawValue.toLowerCase(); +} + +function sortTable(table, columnIndex, sortType, sortOrder) { + const tbody = table.querySelector('tbody'); + if (!tbody) return; + const rows = Array.from(tbody.querySelectorAll('tr')); + const direction = sortOrder === 'desc' ? -1 : 1; + const sortedRows = rows + .map((row, index) => ({ row, index })) + .sort((a, b) => { + const aCell = a.row.children[columnIndex]; + const bCell = b.row.children[columnIndex]; + const aValue = getSortableCellValue(aCell, sortType); + const bValue = getSortableCellValue(bCell, sortType); + if (aValue < bValue) return -1 * direction; + if (aValue > bValue) return 1 * direction; + return a.index - b.index; + }); + tbody.replaceChildren(...sortedRows.map((item) => item.row)); +} + +function applySortIndicators(table, activeHeader, sortOrder) { + const headers = table.querySelectorAll('th.sortable'); + for (const header of headers) { + header.classList.remove('sorted-asc', 'sorted-desc'); + header.removeAttribute('aria-sort'); + } + activeHeader.classList.add(sortOrder === 'desc' ? 'sorted-desc' : 'sorted-asc'); + activeHeader.setAttribute('aria-sort', sortOrder === 'desc' ? 'descending' : 'ascending'); +} + +function handleSortableTableClick(event) { + const header = event.target.closest('th.sortable'); + if (!header) return; + const table = header.closest('table[data-sortable="true"]'); + if (!table) return; + const headers = Array.from(table.querySelectorAll('th.sortable')); + const columnIndex = headers.indexOf(header); + if (columnIndex < 0) return; + + const currentSortKey = table.dataset.sortKey || table.dataset.defaultSortKey; + const currentSortOrder = table.dataset.sortOrder || table.dataset.defaultSortOrder || 'asc'; + const isCurrentHeader = currentSortKey === header.dataset.sortKey; + const nextOrder = isCurrentHeader && currentSortOrder === 'asc' ? 'desc' : 'asc'; + + table.dataset.sortKey = header.dataset.sortKey; + table.dataset.sortOrder = nextOrder; + sortTable(table, columnIndex, header.dataset.sortType || 'text', nextOrder); + applySortIndicators(table, header, nextOrder); +} + +async function initTableSorter() { + const t0 = performance.now(); + const root = gradioApp(); + if (!root.dataset.tableSorterBound) { + root.addEventListener('click', handleSortableTableClick); + root.dataset.tableSorterBound = 'true'; + } + const t1 = performance.now(); + log('initTableSorter', Math.round(t1 - t0)); + timer('initTableSorter', t1 - t0); +} + +async function deleteFile(filename) { + if (!filename) return; + if (!confirm(`Are you sure you want to delete the object - This action cannot be undone? Object: ${filename}`)) return; // eslint-disable-line no-alert + const res = await authFetch(`${window.api}/delete-file?file=${encodeURIComponent(filename)}`); + if (!res || res.status !== 200) { + error('FileDelete', { file: filename, status: res?.status, statusText: res?.statusText }); + return; + } + const data = await res.json(); + log('FileDelete', data); +} + /** * checks that a UI element is not in another hidden element or tab content */ diff --git a/javascript/sdnext.css b/javascript/sdnext.css index e06ed40e2..1bf73a8cd 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -2193,6 +2193,30 @@ div:has(>#tab-gallery-folders) { background-color: var(--button-primary-border-color) !important; } +.simple-table th.sortable { + cursor: pointer; + user-select: none; + position: relative; + padding-right: 1.2em; +} + +.simple-table th.sortable::after { + content: '↕'; + position: absolute; + right: 0.3em; + opacity: 0.55; +} + +.simple-table th.sortable.sorted-asc::after { + content: '↑'; + opacity: 1; +} + +.simple-table th.sortable.sorted-desc::after { + content: '↓'; + opacity: 1; +} + .simple-table tr:nth-child(odd) { background-color: var(--neutral-900); } diff --git a/javascript/sparkline.js b/javascript/sparkline.js new file mode 100644 index 000000000..e227771f0 --- /dev/null +++ b/javascript/sparkline.js @@ -0,0 +1,5 @@ +/* jquery.sparkline 2.1.2 - http://omnipotent.net/jquery.sparkline/ +** Licensed under the New BSD License - see above site for details */ + +(function(a,b,c){(function(a){typeof define=="function"&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.sparkline&&a(jQuery)})(function(d){"use strict";var e={},f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L=0;f=function(){return{common:{type:"line",lineColor:"#00f",fillColor:"#cdf",defaultPixelsPerValue:3,width:"auto",height:"auto",composite:!1,tagValuesAttribute:"values",tagOptionsPrefix:"spark",enableTagOptions:!1,enableHighlight:!0,highlightLighten:1.4,tooltipSkipNull:!0,tooltipPrefix:"",tooltipSuffix:"",disableHiddenCheck:!1,numberFormatter:!1,numberDigitGroupCount:3,numberDigitGroupSep:",",numberDecimalMark:".",disableTooltips:!1,disableInteraction:!1},line:{spotColor:"#f80",highlightSpotColor:"#5f5",highlightLineColor:"#f22",spotRadius:1.5,minSpotColor:"#f80",maxSpotColor:"#f80",lineWidth:1,normalRangeMin:c,normalRangeMax:c,normalRangeColor:"#ccc",drawNormalOnTop:!1,chartRangeMin:c,chartRangeMax:c,chartRangeMinX:c,chartRangeMaxX:c,tooltipFormat:new h(' {{prefix}}{{y}}{{suffix}}')},bar:{barColor:"#3366cc",negBarColor:"#f44",stackedBarColor:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],zeroColor:c,nullColor:c,zeroAxis:!0,barWidth:4,barSpacing:1,chartRangeMax:c,chartRangeMin:c,chartRangeClip:!1,colorMap:c,tooltipFormat:new h(' {{prefix}}{{value}}{{suffix}}')},tristate:{barWidth:4,barSpacing:1,posBarColor:"#6f6",negBarColor:"#f44",zeroBarColor:"#999",colorMap:{},tooltipFormat:new h(' {{value:map}}'),tooltipValueLookups:{map:{"-1":"Loss",0:"Draw",1:"Win"}}},discrete:{lineHeight:"auto",thresholdColor:c,thresholdValue:0,chartRangeMax:c,chartRangeMin:c,chartRangeClip:!1,tooltipFormat:new h("{{prefix}}{{value}}{{suffix}}")},bullet:{targetColor:"#f33",targetWidth:3,performanceColor:"#33f",rangeColors:["#d3dafe","#a8b6ff","#7f94ff"],base:c,tooltipFormat:new h("{{fieldkey:fields}} - {{value}}"),tooltipValueLookups:{fields:{r:"Range",p:"Performance",t:"Target"}}},pie:{offset:0,sliceColors:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],borderWidth:0,borderColor:"#000",tooltipFormat:new h(' {{value}} ({{percent.1}}%)')},box:{raw:!1,boxLineColor:"#000",boxFillColor:"#cdf",whiskerColor:"#000",outlierLineColor:"#333",outlierFillColor:"#fff",medianColor:"#f00",showOutliers:!0,outlierIQR:1.5,spotRadius:1.5,target:c,targetColor:"#4a2",chartRangeMax:c,chartRangeMin:c,tooltipFormat:new h("{{field:fields}}: {{value}}"),tooltipFormatFieldlistKey:"field",tooltipValueLookups:{fields:{lq:"Lower Quartile",med:"Median",uq:"Upper Quartile",lo:"Left Outlier",ro:"Right Outlier",lw:"Left Whisker",rw:"Right Whisker"}}}}},E='.jqstooltip { position: absolute;left: 0px;top: 0px;visibility: hidden;background: rgb(0, 0, 0) transparent;background-color: rgba(0,0,0,0.6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";color: white;font: 10px arial, san serif;text-align: left;white-space: nowrap;padding: 5px;border: 1px solid white;z-index: 10000;}.jqsfield { color: white;font: 10px arial, san serif;text-align: left;}',g=function(){var a,b;return a=function(){this.init.apply(this,arguments)},arguments.length>1?(arguments[0]?(a.prototype=d.extend(new arguments[0],arguments[arguments.length-1]),a._super=arguments[0].prototype):a.prototype=arguments[arguments.length-1],arguments.length>2&&(b=Array.prototype.slice.call(arguments,1,-1),b.unshift(a.prototype),d.extend.apply(d,b))):a.prototype=arguments[0],a.prototype.cls=a,a},d.SPFormatClass=h=g({fre:/\{\{([\w.]+?)(:(.+?))?\}\}/g,precre:/(\w+)\.(\d+)/,init:function(a,b){this.format=a,this.fclass=b},render:function(a,b,d){var e=this,f=a,g,h,i,j,k;return this.format.replace(this.fre,function(){var a;return h=arguments[1],i=arguments[3],g=e.precre.exec(h),g?(k=g[2],h=g[1]):k=!1,j=f[h],j===c?"":i&&b&&b[i]?(a=b[i],a.get?b[i].get(j)||j:b[i][j]||j):(n(j)&&(d.get("numberFormatter")?j=d.get("numberFormatter")(j):j=s(j,k,d.get("numberDigitGroupCount"),d.get("numberDigitGroupSep"),d.get("numberDecimalMark"))),j)})}}),d.spformat=function(a,b){return new h(a,b)},i=function(a,b,c){return ac?c:a},j=function(a,c){var d;return c===2?(d=b.floor(a.length/2),a.length%2?a[d]:(a[d-1]+a[d])/2):a.length%2?(d=(a.length*c+c)/4,d%1?(a[b.floor(d)]+a[b.floor(d)-1])/2:a[d-1]):(d=(a.length*c+2)/4,d%1?(a[b.floor(d)]+a[b.floor(d)-1])/2:a[d-1])},k=function(a){var b;switch(a){case"undefined":a=c;break;case"null":a=null;break;case"true":a=!0;break;case"false":a=!1;break;default:b=parseFloat(a),a==b&&(a=b)}return a},l=function(a){var b,c=[];for(b=a.length;b--;)c[b]=k(a[b]);return c},m=function(a,b){var c,d,e=[];for(c=0,d=a.length;c0;h-=c)a.splice(h,0,e);return a.join("")},o=function(a,b,c){var d;for(d=b.length;d--;){if(c&&b[d]===null)continue;if(b[d]!==a)return!1}return!0},p=function(a){var b=0,c;for(c=a.length;c--;)b+=typeof a[c]=="number"?a[c]:0;return b},r=function(a){return d.isArray(a)?a:[a]},q=function(b){var c;a.createStyleSheet?a.createStyleSheet().cssText=b:(c=a.createElement("style"),c.type="text/css",a.getElementsByTagName("head")[0].appendChild(c),c[typeof a.body.style.WebkitAppearance=="string"?"innerText":"innerHTML"]=b)},d.fn.simpledraw=function(b,e,f,g){var h,i;if(f&&(h=this.data("_jqs_vcanvas")))return h;if(d.fn.sparkline.canvas===!1)return!1;if(d.fn.sparkline.canvas===c){var j=a.createElement("canvas");if(!j.getContext||!j.getContext("2d")){if(!a.namespaces||!!a.namespaces.v)return d.fn.sparkline.canvas=!1,!1;a.namespaces.add("v","urn:schemas-microsoft-com:vml","#default#VML"),d.fn.sparkline.canvas=function(a,b,c,d){return new J(a,b,c)}}else d.fn.sparkline.canvas=function(a,b,c,d){return new I(a,b,c,d)}}return b===c&&(b=d(this).innerWidth()),e===c&&(e=d(this).innerHeight()),h=d.fn.sparkline.canvas(b,e,this,g),i=d(this).data("_jqs_mhandler"),i&&i.registerCanvas(h),h},d.fn.cleardraw=function(){var a=this.data("_jqs_vcanvas");a&&a.reset()},d.RangeMapClass=t=g({init:function(a){var b,c,d=[];for(b in a)a.hasOwnProperty(b)&&typeof b=="string"&&b.indexOf(":")>-1&&(c=b.split(":"),c[0]=c[0].length===0?-Infinity:parseFloat(c[0]),c[1]=c[1].length===0?Infinity:parseFloat(c[1]),c[2]=a[b],d.push(c));this.map=a,this.rangelist=d||!1},get:function(a){var b=this.rangelist,d,e,f;if((f=this.map[a])!==c)return f;if(b)for(d=b.length;d--;){e=b[d];if(e[0]<=a&&e[1]>=a)return e[2]}return c}}),d.range_map=function(a){return new t(a)},u=g({init:function(a,b){var c=d(a);this.$el=c,this.options=b,this.currentPageX=0,this.currentPageY=0,this.el=a,this.splist=[],this.tooltip=null,this.over=!1,this.displayTooltips=!b.get("disableTooltips"),this.highlightEnabled=!b.get("disableHighlight")},registerSparkline:function(a){this.splist.push(a),this.over&&this.updateDisplay()},registerCanvas:function(a){var b=d(a.canvas);this.canvas=a,this.$canvas=b,b.mouseenter(d.proxy(this.mouseenter,this)),b.mouseleave(d.proxy(this.mouseleave,this)),b.click(d.proxy(this.mouseclick,this))},reset:function(a){this.splist=[],this.tooltip&&a&&(this.tooltip.remove(),this.tooltip=c)},mouseclick:function(a){var b=d.Event("sparklineClick");b.originalEvent=a,b.sparklines=this.splist,this.$el.trigger(b)},mouseenter:function(b){d(a.body).unbind("mousemove.jqs"),d(a.body).bind("mousemove.jqs",d.proxy(this.mousemove,this)),this.over=!0,this.currentPageX=b.pageX,this.currentPageY=b.pageY,this.currentEl=b.target,!this.tooltip&&this.displayTooltips&&(this.tooltip=new v(this.options),this.tooltip.updatePosition(b.pageX,b.pageY)),this.updateDisplay()},mouseleave:function(){d(a.body).unbind("mousemove.jqs");var b=this.splist,c=b.length,e=!1,f,g;this.over=!1,this.currentEl=null,this.tooltip&&(this.tooltip.remove(),this.tooltip=null);for(g=0;g