Merge pull request #4716 from vladmandic/dev

dev merge
pull/4725/head^2
Vladimir Mandic 2026-04-01 10:39:43 +02:00 committed by GitHub
commit 7a51017ac3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
815 changed files with 296174 additions and 109837 deletions

60
.github/copilot-instructions.md vendored Normal file
View File

@ -0,0 +1,60 @@
# SD.Next: AGENTS.md Project Guidelines
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
## Tools
- `venv` for Python environment management, activated with `source venv/bin/activate` (Linux) or `venv\Scripts\activate` (Windows).
venv MUST be activated before running any Python commands or scripts to ensure correct dependencies and environment variables.
- `python` 3.10+.
- `pyproject.toml` for Python configuration, including linting and type checking settings.
- `eslint` configured for both core and UI code.
- `pnpm` for managing JavaScript dependencies and scripts, with key commands defined in `package.json`.
- `ruff` and `pylint` for Python linting, with configurations in `pyproject.toml` and executed via `pnpm ruff` and `pnpm pylint`.
- `pre-commit` hooks which also check line-endings and other formatting issues, configured in `.pre-commit-config.yaml`.
## Project Structure
- Entry/startup flow: `webui.sh` -> `launch.py` -> `webui.py` -> modules under `modules/`.
- Install: `installer.py` takes care of installing dependencies and setting up the environment.
- Core runtime state is centralized in `modules/shared.py` (shared.opts, model state, backend/device state).
- API/server routes are under `modules/api/`.
- UI codebase is split between base JS in `javascript/` and actual UI in `extensions-builtin/sdnext-modernui/`.
- Model and pipeline logic is split between `modules/sd_*` and `pipelines/`.
- Additional plug-ins live in `scripts/` and are used only when specified.
- Extensions live in `extensions-builtin/` and `extensions/` and are loaded dynamically.
- Tests and CLI scripts are under `test/` and `cli/`, with some API smoke checks in `test/full-test.sh`.
## Code Style
- 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.
- Use conventions from `CONTRIBUTING`.
- Do not include unrelated edits or submodule changes when preparing contributions.
- Use existing CLI/API tool patterns in `cli/` and `test/` when adding automation scripts.
- 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.

View File

@ -0,0 +1,15 @@
---
description: "Use when editing Python core runtime code, startup flow, model loading, API internals, backend/device logic, or shared state in modules and pipelines."
name: "Core Runtime Guidelines"
applyTo: "launch.py, webui.py, installer.py, modules/**/*.py, pipelines/**/*.py, scripts/**/*.py, extensions-builtin/**/*.py"
---
# Core Runtime Guidelines
- Preserve startup ordering and import timing in `launch.py` and `webui.py`; avoid moving initialization steps unless required.
- Treat `modules/shared.py` as the source of truth for global runtime state (`shared.opts`, model references, backend/device flags).
- Prefer narrow changes with explicit side effects; avoid introducing new cross-module mutable globals.
- Keep platform paths neutral: do not assume CUDA-only behavior and preserve ROCm/IPEX/DirectML/OpenVINO compatibility branches.
- Keep extension and script loading resilient: when adding startup scans/hooks, preserve partial-failure tolerance and logging.
- 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).

14
.github/instructions/ui.instructions.md vendored Normal file
View File

@ -0,0 +1,14 @@
---
description: "Use when editing frontend UI code, JavaScript, HTML, CSS, localization files, or built-in UI extensions including modernui and kanvas."
name: "UI And Frontend Guidelines"
applyTo: "javascript/**/*.js, html/**/*.html, html/**/*.css, html/**/*.js, extensions-builtin/sdnext-modernui/**/*, extensions-builtin/sdnext-kanvas/**/*"
---
# UI And Frontend Guidelines
- Preserve existing UI behavior and wiring between Gradio/Python endpoints and frontend handlers; do not change payload shapes without backend alignment.
- Follow existing project lint and style patterns; prefer consistency with nearby files over introducing new frameworks or architecture.
- Keep localization-friendly UI text changes synchronized with locale resources in `html/locale_*.json` when user-facing strings are added or changed.
- Avoid bundling unrelated visual refactors with functional fixes; keep UI PRs scoped and reviewable.
- For extension UI work, respect each extension's boundaries and avoid cross-extension coupling.
- Validate JavaScript changes with `pnpm eslint`; for modern UI extension changes also run `pnpm eslint-ui`.
- Maintain mobile compatibility when touching layout or interaction behavior.

37
.gitignore vendored
View File

@ -2,6 +2,9 @@
venv/
__pycache__
.ruff_cache
.vscode/
.idea/
**/.DS_Store
/*.json
/*.yaml
/params.txt
@ -13,14 +16,11 @@ __pycache__
/data/extensions.json
/data/cache.json
/data/themes.json
config_states
/data/installer.json
/data/rocm.json
node_modules
pnpm-lock.yaml
package-lock.json
.history
cache
**/.DS_Store
tunableop_results*.csv
# all models and temp files
*.log
@ -43,31 +43,30 @@ tunableop_results*.csv
/*.mp3
/*.lnk
/*.swp
!webui.bat
!webui.sh
!package.json
!requirements.txt
# all dynamic stuff
/extensions/**/*
/outputs/**/*
/embeddings/**/*
/models/**/*
/interrogate/**/*
/train/log/**/*
/textual_inversion/**/*
/detected_maps/**/*
/tmp
/log
/cert
.vscode/
.idea/
/localizations
.*/
cache
tunableop_results*.csv
# force included
!webui.bat
!webui.sh
!package.json
!requirements.txt
!constraints.txt
!/data
!/models/VAE-approx
!/models/VAE-approx/model.pt
!/models/Reference
!/models/Reference/**/*
# copilot instructions
!/.github/
!/.github/copilot-instructions.md
!/.github/instructions/
!/.github/instructions/**/*

8
.gitmodules vendored
View File

@ -10,13 +10,9 @@
path = extensions-builtin/sd-extension-chainner
url = https://github.com/vladmandic/sd-extension-chainner
ignore = dirty
[submodule "extensions-builtin/stable-diffusion-webui-rembg"]
path = extensions-builtin/stable-diffusion-webui-rembg
url = https://github.com/vladmandic/sd-extension-rembg
ignore = dirty
[submodule "extensions-builtin/sdnext-modernui"]
path = extensions-builtin/sdnext-modernui
url = https://github.com/BinaryQuantumSoul/sdnext-modernui
[submodule "extensions-builtin/sdnext-kanvas"]
path = extensions-builtin/sdnext-kanvas
url = https://github.com/vladmandic/sdnext-kanvas
path = extensions-builtin/sdnext-kanvas
url = https://github.com/vladmandic/sdnext-kanvas

View File

@ -13,20 +13,17 @@ repos:
rev: v6.0.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: check-illegal-windows-names
- id: check-merge-conflict
- id: detect-private-key
- id: check-builtin-literals
- id: check-case-conflict
- id: check-illegal-windows-names
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: check-json
- id: check-toml
- id: check-xml
- id: check-yaml
- id: debug-statements
- id: detect-private-key
- id: end-of-file-fixer
- id: mixed-line-ending
- id: check-executables-have-shebangs
@ -41,3 +38,13 @@ repos:
.*\.md|
.github/ISSUE_TEMPLATE/.*\.yml
)$
- repo: https://github.com/oliv5/pre-commit-indents-to-spaces
rev: v0.0.3
hooks:
- id: indents-to-spaces
args: ["--spaces=4"]
types: [python]
- id: indents-to-spaces
args: ["--spaces=2"]
types: [file]
files: \.(json|js|mjs|css|html|md|yaml|toml|sh)$

284
.pylintrc
View File

@ -1,284 +0,0 @@
[MAIN]
analyse-fallback-blocks=no
clear-cache-post-run=no
extension-pkg-allow-list=
prefer-stubs=yes
extension-pkg-whitelist=
fail-on=
fail-under=10
ignore=CVS
ignore-paths=/usr/lib/.*$,
venv,
.git,
.ruff_cache,
.vscode,
modules/apg,
modules/cfgzero,
modules/control/proc,
modules/control/units,
modules/dml,
modules/facelib,
modules/flash_attn_triton_amd,
modules/ggml,
modules/hidiffusion,
modules/hijack/ddpm_edit.py,
modules/intel,
modules/intel/ipex,
modules/framepack/pipeline,
modules/onnx_impl,
modules/pag,
modules/postprocess/aurasr_arch.py,
modules/prompt_parser_xhinker.py,
modules/ras,
modules/seedvr,
modules/rife,
modules/schedulers,
modules/taesd,
modules/teacache,
modules/todo,
modules/res4lyf,
pipelines/bria,
pipelines/flex2,
pipelines/f_lite,
pipelines/hidream,
pipelines/hdm,
pipelines/meissonic,
pipelines/omnigen2,
pipelines/segmoe,
pipelines/xomni,
pipelines/chrono,
scripts/consistory,
scripts/ctrlx,
scripts/daam,
scripts/demofusion,
scripts/freescale,
scripts/infiniteyou,
scripts/instantir,
scripts/lbm,
scripts/layerdiffuse,
scripts/mod,
scripts/pixelsmith,
scripts/differential_diffusion.py,
scripts/pulid,
scripts/xadapter,
repositories,
extensions-builtin/sd-extension-chainner/nodes,
extensions-builtin/sd-webui-agent-scheduler,
extensions-builtin/sdnext-modernui/node_modules,
extensions-builtin/sdnext-kanvas/node_modules,
ignore-patterns=.*test*.py$,
.*_model.py$,
.*_arch.py$,
.*_model_arch.py*,
.*_model_arch_v2.py$,
ignored-modules=
jobs=8
limit-inference-results=100
load-plugins=
persistent=no
py-version=3.10
recursive=no
source-roots=
unsafe-load-any-extension=no
[BASIC]
argument-naming-style=snake_case
attr-naming-style=snake_case
bad-names=foo, bar, baz, toto, tutu, tata
bad-names-rgxs=
class-attribute-naming-style=any
class-const-naming-style=UPPER_CASE
class-naming-style=PascalCase
const-naming-style=snake_case
docstring-min-length=-1
function-naming-style=snake_case
good-names=i,j,k,e,ex,ok,p,x,y,id
good-names-rgxs=
include-naming-hint=no
inlinevar-naming-style=any
method-naming-style=snake_case
module-naming-style=snake_case
name-group=
no-docstring-rgx=^_
property-classes=abc.abstractproperty
variable-naming-style=snake_case
[CLASSES]
check-protected-access-in-special-methods=no
defining-attr-methods=__init__, __new__,
exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit
valid-classmethod-first-arg=cls
valid-metaclass-classmethod-first-arg=mcs
[DESIGN]
exclude-too-few-public-methods=
ignored-parents=
max-args=199
max-attributes=99
max-bool-expr=99
max-branches=199
max-locals=99
max-parents=99
max-public-methods=99
max-returns=99
max-statements=199
min-public-methods=1
[EXCEPTIONS]
overgeneral-exceptions=builtins.BaseException,builtins.Exception
[FORMAT]
expected-line-ending-format=
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
indent-after-paren=4
indent-string=' '
max-line-length=200
max-module-lines=9999
single-line-class-stmt=no
single-line-if-stmt=no
[IMPORTS]
allow-any-import-level=
allow-reexport-from-package=no
allow-wildcard-with-all=no
deprecated-modules=
ext-import-graph=
import-graph=
int-import-graph=
known-standard-library=
known-third-party=enchant
preferred-modules=
[LOGGING]
logging-format-style=new
logging-modules=logging
[MESSAGES CONTROL]
confidence=HIGH,
CONTROL_FLOW,
INFERENCE,
INFERENCE_FAILURE,
UNDEFINED
# disable=C,R,W
disable=abstract-method,
bad-inline-option,
bare-except,
broad-exception-caught,
chained-comparison,
consider-iterating-dictionary,
consider-merging-isinstance,
consider-using-dict-items,
consider-using-enumerate,
consider-using-from-import,
consider-using-generator,
consider-using-get,
consider-using-in,
consider-using-max-builtin,
consider-using-min-builtin,
consider-using-sys-exit,
cyclic-import,
dangerous-default-value,
deprecated-pragma,
duplicate-code,
file-ignored,
import-error,
import-outside-toplevel,
invalid-name,
line-too-long,
locally-disabled,
logging-fstring-interpolation,
missing-class-docstring,
missing-function-docstring,
missing-module-docstring,
no-else-raise,
no-else-return,
not-callable,
pointless-string-statement,
raw-checker-failed,
simplifiable-if-expression,
suppressed-message,
too-few-public-methods,
too-many-instance-attributes,
too-many-locals,
too-many-nested-blocks,
too-many-positional-arguments,
too-many-statements,
unidiomatic-typecheck,
unknown-option-value,
unnecessary-dict-index-lookup,
unnecessary-dunder-call,
unnecessary-lambda-assigment,
unnecessary-lambda,
unused-wildcard-import,
unpacking-non-sequence,
unsubscriptable-object,
useless-return,
use-dict-literal,
use-symbolic-message-instead,
useless-suppression,
wrong-import-position,
enable=c-extension-no-member
[METHOD_ARGS]
timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
[MISCELLANEOUS]
notes=FIXME,
XXX,
TODO
notes-rgx=
[REFACTORING]
max-nested-blocks=5
never-returning-functions=sys.exit,argparse.parse_error
[REPORTS]
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
msg-template=
reports=no
score=no
[SIMILARITIES]
ignore-comments=yes
ignore-docstrings=yes
ignore-imports=yes
ignore-signatures=yes
min-similarity-lines=4
[SPELLING]
max-spelling-suggestions=4
spelling-dict=
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
spelling-ignore-words=
spelling-private-dict-file=
spelling-store-unknown-words=no
[STRING]
check-quote-consistency=no
check-str-concat-over-line-jumps=no
[TYPECHECK]
contextmanager-decorators=contextlib.contextmanager
generated-members=numpy.*,logging.*,torch.*,cv2.*
ignore-none=yes
ignore-on-opaque-inference=yes
ignored-checks-for-mixins=no-member,
not-async-context-manager,
not-context-manager,
attribute-defined-outside-init
ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
missing-member-hint=yes
missing-member-hint-distance=1
missing-member-max-choices=1
mixin-class-rgx=.*[Mm]ixin
signature-mutators=
[VARIABLES]
additional-builtins=
allow-global-unused-variables=yes
allowed-redefined-builtins=
callbacks=cb_,
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
ignored-argument-names=_.*|^ignored_|^unused_
init-import=no
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io

View File

@ -1,109 +0,0 @@
line-length = 250
indent-width = 4
target-version = "py310"
exclude = [
"venv",
".git",
".ruff_cache",
".vscode",
"modules/cfgzero",
"modules/facelib",
"modules/flash_attn_triton_amd",
"modules/hidiffusion",
"modules/intel/ipex",
"modules/pag",
"modules/schedulers",
"modules/teacache",
"modules/seedvr",
"modules/control/proc",
"modules/control/units",
"modules/control/units/xs_pipe.py",
"modules/postprocess/aurasr_arch.py",
"pipelines/meissonic",
"pipelines/omnigen2",
"pipelines/hdm",
"pipelines/segmoe",
"pipelines/xomni",
"pipelines/chrono",
"scripts/lbm",
"scripts/daam",
"scripts/xadapter",
"scripts/pulid",
"scripts/instantir",
"scripts/freescale",
"scripts/consistory",
"repositories",
"extensions-builtin/Lora",
"extensions-builtin/sd-extension-chainner/nodes",
"extensions-builtin/sd-webui-agent-scheduler",
"extensions-builtin/sdnext-modernui/node_modules",
]
[lint]
select = [
"F",
"E",
"W",
"C",
"B",
"I",
"YTT",
"ASYNC",
"RUF",
"AIR",
"NPY",
"C4",
"T10",
"EXE",
"ISC",
"ICN",
"RSE",
"TCH",
"TID",
"INT",
"PLE",
]
ignore = [
"B006", # Do not use mutable data structures for argument defaults
"B008", # Do not perform function call in argument defaults
"B905", # Strict zip() usage
"C420", # Unnecessary dict comprehension for iterable; use `dict.fromkeys` instead
"C408", # Unnecessary `dict` call
"I001", # Import block is un-sorted or un-formatted
"E402", # Module level import not at top of file
"E501", # Line too long
"E721", # Do not compare types, use `isinstance()`
"E731", # Do not assign a `lambda` expression, use a `def`
"E741", # Ambiguous variable name
"F401", # Imported by unused
"EXE001", # file with shebang is not marked executable
"NPY002", # replace legacy random
"RUF005", # Consider iterable unpacking
"RUF008", # Do not use mutable default values for dataclass
"RUF010", # Use explicit conversion flag
"RUF012", # Mutable class attributes
"RUF013", # PEP 484 prohibits implicit `Optional`
"RUF015", # Prefer `next(...)` over single element slice
"RUF046", # Value being cast to `int` is already an integer
"RUF059", # Unpacked variables are not used
"RUF051", # Prefer pop over del
]
fixable = ["ALL"]
unfixable = []
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
[format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
docstring-code-format = false
[lint.mccabe]
max-complexity = 150

60
AGENTS.md Normal file
View File

@ -0,0 +1,60 @@
# SD.Next: AGENTS.md Project Guidelines
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
## Tools
- `venv` for Python environment management, activated with `source venv/bin/activate` (Linux) or `venv\Scripts\activate` (Windows).
venv MUST be activated before running any Python commands or scripts to ensure correct dependencies and environment variables.
- `python` 3.10+.
- `pyproject.toml` for Python configuration, including linting and type checking settings.
- `eslint` configured for both core and UI code.
- `pnpm` for managing JavaScript dependencies and scripts, with key commands defined in `package.json`.
- `ruff` and `pylint` for Python linting, with configurations in `pyproject.toml` and executed via `pnpm ruff` and `pnpm pylint`.
- `pre-commit` hooks which also check line-endings and other formatting issues, configured in `.pre-commit-config.yaml`.
## Project Structure
- Entry/startup flow: `webui.sh` -> `launch.py` -> `webui.py` -> modules under `modules/`.
- Install: `installer.py` takes care of installing dependencies and setting up the environment.
- Core runtime state is centralized in `modules/shared.py` (shared.opts, model state, backend/device state).
- API/server routes are under `modules/api/`.
- UI codebase is split between base JS in `javascript/` and actual UI in `extensions-builtin/sdnext-modernui/`.
- Model and pipeline logic is split between `modules/sd_*` and `pipelines/`.
- Additional plug-ins live in `scripts/` and are used only when specified.
- Extensions live in `extensions-builtin/` and `extensions/` and are loaded dynamically.
- Tests and CLI scripts are under `test/` and `cli/`, with some API smoke checks in `test/full-test.sh`.
## Code Style
- 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.
- Use conventions from `CONTRIBUTING`.
- Do not include unrelated edits or submodule changes when preparing contributions.
- Use existing CLI/API tool patterns in `cli/` and `test/` when adding automation scripts.
- 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.

View File

@ -1,28 +1,211 @@
# Change Log for SD.Next
## Update for 2026-02-07
## Update for 2026-04-01
- **Upscalers**
- add support for [spandrel](https://github.com/chaiNNer-org/spandrel)
upscaling engine with suport for new upscaling model families
- add two new ai upscalers: *RealPLKSR NomosWebPhoto* and *RealPLKSR AnimeSharpV2*
- add two new interpolation methods: *HQX* and *ICB*
### Highlights for 2026-04-01
This release brings massive code refactoring to modernize codebase and removal of some obsolete features. Leaner & Faster!
And since its a bit quieter period when it comes to new models, notable additions would be : *FireRed-Image-Edit*, *SkyWorks-UniPic-3* and new versions of *Anima-Preview*, *Flux-Klein-KV* image models and *LTX 2.3* video model
If you're on Windows platform, we have a brand new [All-in-one Installer & Launcher](https://github.com/vladmandic/sdnext-launcher): simply download [exe or zip](https://github.com/vladmandic/sdnext-launcher/releases) and done!
And we have a new (optional) React-based **UI** [Enso](https://github.com/CalamitousFelicitousness/enso)!
*What else*? Really a lot!
New color grading module, updated localization with new languages and improved translations, new CivitAI integration module, new finetunes loader, several new upscalers, improvements to LLM/VLM in captioning and prompt enhance, a lot of new control preprocessors, new realtime server info panel, some new UI themes
And major work on API hardening: *security, rate limits, secrets handling, new endpoints*, etc.
But also many smaller quality-of-life improvements - for full details, see [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md)
*Note*: Purely due to size of changes, clean install is recommended!
Just how big? Some stats: *~530 commits over 880 files*
[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-04-01
- **Models**
- [Google Flash 3.1 Image](https://ai.google.dev/gemini-api/docs/models/gemini-3-flash-preview) a.k.a. *Nano Banana 2*
- [FireRed Image Edit](https://huggingface.co/FireRedTeam/FireRed-Image-Edit-1.0) *1.0 and 1.1*
*Note*: FireRed is a fine-tune of Qwen-Image-Edit regardless of its claim as a new base-model
- [Skyworks UniPic-3](https://huggingface.co/Skywork/Unipic3), *Consistency and DMD* variants to reference/community section
*Note*: UniPic-3 is a fine-tune of Qwen-Image-Edit with new distillation regardless of its claim of major changes
- [Anima Preview-v2](https://huggingface.co/circlestone-labs/Anima)
- [FLUX.2-Klein-KV](https://huggingface.co/black-forest-labs/FLUX.2-klein-9b-kv), thanks @liutyi
- [LTX-Video 2.3](https://huggingface.co/Lightricks/LTX-2.3) in *Full and Distilled* variants and in both original *FP16 and SDNQ-4bit* quantiztion
*note* ltx-2.3 is a massive 22B parameters and full model is very large (72GB) so use of pre-quantized variant (32GB) is highly recommended
- **Image manipulation**
- new **Color grading** module
apply basic corrections to your images: brightness,contrast,saturation,shadows,highlights
move to professional photo corrections: hue,gamma,sharpness,temperature
correct tone: shadows,midtones,highlights
add effects: vignette,grain
apply professional lut-table using .cube file
*hint* color grading is available as step during generate or as processing item for already existing images
- **Upscaling**
add support for [spandrel](https://github.com/chaiNNer-org/spandrel) engine with suport for new upscaling model families
add two new ai upscalers: *RealPLKSR NomosWebPhoto* and *RealPLKSR AnimeSharpV2*
add two new **interpolation** methods: *HQX* and *ICB*
use high-quality [sharpfin](https://github.com/drhead/Sharpfin) accelerated library
extend `chainner` support for additional models
- update **Latent corrections** *(former HDR Corrections)*
expand allowed models
- **Captioning / Prompt Enhance**
- new models: **Qwen-3.5**, **Mistral-3** in multiple variations
- new models: multiple *heretic* and *abliterated* finetunes for **Qwen, Gemma, Mistral**
- **captioning** and **prompt enhance**: add support for all cloud-based Gemini models
*3.1/3.0/2.5 pro/flash/flash-lite*
- improve captioning and prompt enhance memory handling/offloading
- **Features**
- pipelines: add **ZImageInpaint**, thanks @CalamitousFelicitousness
- add `--remote` command line flag that reduces client/server chatter and improves link stability
for long-running generates, useful when running on remote servers
- **Secrets** handling: new `secrets.json` and special handling for tokens/keys/passwords
used to be treated like any other `config.json` param which can cause security issues
- **Control**: many new **pre-processors**
*anyline, depth_anything v2, dsine, lotus, marigold normals, oneformer, rtmlib pose, sam2, stablenormal, teed, vitpose*
- pipelines: add **ZImageInpaint**
- rewritten **CivitAI** module
browse/discover mode with sort, period, type/base dropdowns; URL paste; subfolder sorting; auto-browse; dynamic dropdowns
- **HiRes**: allow using different lora in refiner prompt
- **Nunchaku** models are now listed in networks tab as reference models
instead of being used implicitly via quantization
- improve image **Metadata** parser for foreign metadata (e.g. XMP)
- new **CeeTeeDees image-to-image batch inference**, thanks @resonantsky
available in *main interface -> scripts*
- **Compute**
- **ROCm** advanced configuration and tuning, thanks @resonantsky
see *main interface -> scripts -> rocm advanced config*
- **ROCm** support for additional AMD GPUs: `gfx103X`, thanks @crashingalexsan
- **Cuda** update to `torch=2.11` with `cuda=13.0`
- **Ipex** update to `torch==2.11`
- **ROCm/Linux** update to `torch==2.11` with `rocm==7.2`
- **OpenVINO** update to `torch==2.11` and `openvino==2026.0`
- *note* **Cuda** `torch==2.10` removed support for `rtx1000` series and older GPUs
use following before first startup to force installation of `torch==2.9.1` with `cuda==12.6`:
> `set TORCH_COMMAND='torch==2.9.1 torchvision==0.24.1 torchaudio==2.9.1 --index-url https://download.pytorch.org/whl/cu126'`
- *note* **Cuda** `cuda==13.0` requires newer nVidia drivers
use following before first startup to force installation of `torch==2.11.0` with `cuda==12.68`:
> `set TORCH_COMMAND='torch torchvision --index-url https://download.pytorch.org/whl/cu128`
- update installer and support `nunchaku==1.2.1`
- **UI**
- ui: **themes** add *CTD-NT64Light* and *CTD-NT64Dark*, thanks @resonantsky
- ui: **gallery** add option to auto-refresh gallery, thanks @awsr
- **Enso** new React-based UI, developed by @CalamitousFelicitousness!
with WYSIWYG infinite canvas workspace, command palette, and numerous quality of life improvements across the board
enable using `--enso` flag and access using `/enso` endpoint (e.g. <http://localhost:7860/enso>)
see [Enso Docs](https://vladmandic.github.io/sdnext-docs/Enso/) and [Enso Home](https://github.com/CalamitousFelicitousness/enso) for details
*note* Enso is work-in-progress and alpha-ready
- legacy panels **T2I** and **I2I** are disabled by default
you can re-enable them in *settings -> ui -> hide legacy tabs*
- new panel: **Server Info** with detailed runtime informaton
- rename **Scripts** to **Extras** and reorganize to split internal functionality vs external extensions
- **Networks** add **UNet/DiT**
- **Localization** improved translation quality and new translations locales:
*en, en1, en2, en3, en4, hr, es, it, fr, de, pt, ru, zh, ja, ko, hi, ar, bn, ur, id, vi, tr, sr, po, he, xx, yy, qq, tlh*
yes, this now includes stuff like *latin, esperanto, arabic, hebrew, klingon* and a lot more!
and also introduce some pseudo-locales such as: *techno-babbel*, *for-n00bs*
*hint*: click on locale icon in bottom-left corner to cycle through available locales, or set default in *settings -> ui*
- **Server settings** new section in *settings*
- **Kanvas** add paste image from clipboard
- **Themes** add *CTD-NT64Light*, *CTD-NT64Medium* and *CTD-NT64Dark*, thanks @resonantsky
- **Themes** add *Vlad-Neomorph*
- **Gallery** add option to auto-refresh gallery, thanks @awsr
- **Token counters** add per-section display for supported models, thanks @awsr
- **Colour grading** add hints for all the functions
- **Docs / Wiki**
- updates to to compute sections: *AMD-ROCm, AMD-MIOpen, ZLUDA, OpenVINO, nVidia*
- updates to core sections: *Installation, Python, Schedulers, Launcher, SDNQ, Video*
- added Enso page
- **API**
- prototype **v2 API** (`/sdapi/v2/`)
job-based generation with queue, per-job WebSocket progress, file uploads with TTL, model/network enumeration
and a plethora of other improvements *(work-in-progress)*
for the time being ships with Enso, which must be enabled wih `--enso` flag on startup for v2 API to be available
- **rate limiting**: global for all endpoints, guards against abuse and denial-of-service type of attacks
configurable in *settings -> server settings*
- new `/sdapi/v1/upload` endpoint with support for both POST with form-data or PUT using raw-bytes
- new `/sdapi/v1/torch` endpoint for torch info (backend, version, etc.)
- new `/sdapi/v1/gpu` endpoint for GPU info
- new `/sdapi/v1/rembg` endpoint for background removal
- new `/sdapi/v1/unet` endpoint to list available unets/dits
- use rate limiting for api logging
- **Obsoleted**
- removed support for additional quantization engines: *BitsAndBytes, TorchAO, Optimum-Quanto, NNCF*
*note*: SDNQ is quantization engine of choice for SD.Next
- removed `flux_enhance` script
- **Internal**
- refactor: reorganize `cli` scripts
- `python==3.13` full support
- `python==3.14` initial support
see [docs](https://vladmandic.github.io/sdnext-docs/Python/) for details
- remove hard-dependnecies:
`clip, numba, skimage, torchsde, omegaconf, antlr, patch-ng, patch-ng, astunparse, addict, inflection, jsonmerge, kornia`,
`resize-right, voluptuous, yapf, sqlalchemy, invisible-watermark, pi-heif, ftfy, blendmodes, PyWavelets, imp`
these are now installed on-demand when needed
- bump `huggingface_hub==1.5.0`
- bump `transformers==5.3.0`
- installer introduce `constraints.txt`
- refactor to/from *image/tensor* logic
- refactor reorganize `cli` scripts
- refactor move tests to dedicated `/test/`
- refactor all image handling to `modules/image/`
- refactor many params that were server-global are now ui params that are handled per-request
*schedulers, todo, tome, etc.*
- refactor error handling during `torch.compile`
- refactor move `rebmg` to core instead of extensions
- remove face restoration
- unified command line parsing
- reinstall `nunchaku` with `--reinstall` flag
- use explicit icon image references in `gallery`, thanks @awsr
- launch use threads to async execute non-critical tasks
- switch from deprecated `pkg_resources` to `importlib`
- modernize typing and type annotations
- improve `pydantic==2.x` compatibility
- refactor entire logging into separate `modules/logger`
- replace `timestamp` based startup checks with state caching
- split monolithic `shared` module and introduce `ui_definitions`
- modularize all imports and avoid re-imports
- use `threading` for deferable operatios
- use `threading` for io-independent parallel operations
- remove requirements: `clip`, `open-clip`
- add new build of `insightface`, thanks @hameerabbasi
- reduce use of generators with ui interactor
- better subprocess execute, thanks @awsr
- better wslopen handling, thanks @awsr
- refactor for PEP-484 compliance, thanks @awsr
- detect active `venv`
- **Obsolete**
- remove `normalbae` pre-processor
- remove `dwpose` pre-processor
- remove `hdm` model support
- remove `xadapter` script
- remove `codeformer` and `gfpgan` face restorers
- **Checks**
- switch to `pyproject.toml` for tool configs
- update `lint` rules, thanks @awsr
- add `ty` to optional lint tooling
- add `pyright` to optional lint tooling
- **Fixes**
- fix: add metadata restore to always-on scripts
- fix: improve wildcard weights parsing, thanks @Tillerz
- fix: ui gallery cace recursive cleanup, thanks @awsr
- fix: `anima` model detection
- fix: lora unwanted unload
- fix: improve preview error handler
- ui `gallery` cache recursive cleanup, thanks @awsr
- ui main results pane sizing
- ui connection monitor
- handle `clip` installer doing unwanted `setuptools` update
- cleanup for `uv` installer fallback
- add `metadata` restore to always-on scripts
- improve `wildcard` weights parsing, thanks @Tillerz
- model detection for `anima`
- handle `lora` unwanted unload
- improve `preview` error handler
- handle `gallery` over remote/unsecure connections
- fix `ltx2-i2v`
- handle missing `preview` image
- kandinsky 5 t2i/i2i model type detection
- kanvas notify core on image size change
- command arg `--reinstall` stricter enforcement
- handle `api` state reset
- processing upscaler refresh button
- simplify and validate `rembg` dependencies
- improve video generation progress tracking
- handle startup with bad `scripts` more gracefully
- thread-safety for `error-limiter`, thanks @awsr
- add `lora` support for flux2-klein
- fix `lora` change when used with `sdnq`
- multiple `sdnq` fixes
- handle `taesd` init errors
## Update for 2026-02-04
@ -143,7 +326,7 @@ For full list of changes, see full changelog.
available in both *original* and *sdnq-dynamic prequantized* variants
thanks @CalamitousFelicitousness
*note*: model requires pre-release versions of `transformers` package:
> pip install --upgrade git+https://github.com/huggingface/transformers.git
> pip install --upgrade git+<https://github.com/huggingface/transformers.git>
> ./webui.sh --experimental
- [Nunchaku Z-Image Turbo](https://huggingface.co/nunchaku-tech/nunchaku-z-image-turbo)
nunchaku optimized z-image turbo
@ -304,7 +487,7 @@ Plus a lot of internal improvements and fixes
**Z-Image** is a powerful and highly efficient image generation model with 6B parameters and using Qwen-3 as text encoder
unlike most of new models that are far larger, Z-Image architecture allows it to run with good performance even on mid-range hardware
*note*: initial release is *Turbo* variant only with *Base* and *Edit* variants to follow
- [Kandinsky 5.0 Lite]() is a new 6B model using Qwen-2.5 as text encoder
- [Kandinsky 5.0 Lite](https://huggingface.co/kandinskylab/Kandinsky-5.0-I2V-Lite-5s-Diffusers) is a new 6B model using Qwen-2.5 as text encoder
it comes in text-to-image and image-edit variants
- **Google Gemini Nano Banana** [2.5 Flash](https://blog.google/products/gemini/gemini-nano-banana-examples/) and [3.0 Pro](https://deepmind.google/models/gemini-image/pro/)
first cloud-based model directly supported in SD.Next UI
@ -397,9 +580,11 @@ Plus a lot of internal improvements and fixes
- control: safe load non-sparse controlnet
- control: fix marigold preprocessor with bfloat16
- auth: fix password being shown in clear text during login
- github: better handling of forks
- firefox: remove obsolete checks, thanks @awsr
- runai streamer: cleanup logging, thanks @CalamitousFelicitousness
- gradio: event handlers, thanks @awsr
- seedvr: handle non-cuda environments, thanks @resonantsky
## Update for 2025-11-06

113
README.md
View File

@ -1,8 +1,14 @@
<div align="center">
<img src="https://github.com/vladmandic/sdnext/raw/master/html/logo-transparent.png" width=200 alt="SD.Next">
<img src="https://github.com/vladmandic/sdnext/raw/master/html/logo-transparent.png" width=200 alt="SD.Next: AI art generator logo">
# SD.Next: All-in-one WebUI for AI generative image and video creation and captioning
# SD.Next: All-in-one WebUI
SD.Next is a powerful, open-source WebUI app for AI image and video generation, built on Stable Diffusion and supporting dozens of advanced models. Create, caption, and process images and videos with a modern, cross-platform interface—perfect for artists, researchers, and AI enthusiasts.
![Stars](https://img.shields.io/github/stars/vladmandic/sdnext?style=social)
![Forks](https://img.shields.io/github/forks/vladmandic/sdnext?style=social)
![Contributors](https://img.shields.io/github/contributors/vladmandic/sdnext)
![Last update](https://img.shields.io/github/last-commit/vladmandic/sdnext?svg=true)
![License](https://img.shields.io/github/license/vladmandic/sdnext?svg=true)
[![Discord](https://img.shields.io/discord/1101998836328697867?logo=Discord&svg=true)](https://discord.gg/VjvR2tabEX)
@ -17,61 +23,63 @@
## Table of contents
- [Documentation](https://vladmandic.github.io/sdnext-docs/)
- [SD.Next Features](#sdnext-features)
- [Model support](#model-support)
- [Platform support](#platform-support)
- [SD.Features](#features--capabilities)
- [Supported AI Models](#supported-ai-models)
- [Supported Platforms & Hardware](#supported-platforms--hardware)
- [Getting started](#getting-started)
## SD.Next Features
### Screenshot: Desktop interface
All individual features are not listed here, instead check [ChangeLog](CHANGELOG.md) for full list of changes
- Fully localized:
**English | Chinese | Russian | Spanish | German | French | Italian | Portuguese | Japanese | Korean**
- Desktop and Mobile support!
- Multiple [diffusion models](https://vladmandic.github.io/sdnext-docs/Model-Support/)!
- Multi-platform!
▹ **Windows | Linux | MacOS | nVidia CUDA | AMD ROCm | Intel Arc / IPEX XPU | DirectML | OpenVINO | ONNX+Olive | ZLUDA**
<div align="center">
<img src="https://github.com/vladmandic/sdnext/raw/dev/html/screenshot-robot.jpg" alt="SD.Next: AI art generator desktop interface screenshot" width="90%">
</div>
### Screenshot: Mobile interface
<div align="center">
<img src="https://github.com/user-attachments/assets/ced9fe0c-d2c2-46d1-94a7-8f9f2307ce38" alt="SD.Next: AI art generator mobile interface screenshot" width="35%">
</div>
</div>
<br>
## Features & Capabilities
SD.Next is feature-rich with a focus on performance, flexibility, and user experience. Key features include:
- [Multi-platform](#platform-support!
- Many [diffusion models](https://vladmandic.github.io/sdnext-docs/Model-Support/)!
- Fully localized to ~15 languages and with support for many [UI themes](https://vladmandic.github.io/sdnext-docs/Themes/)!
- [Desktop](#screenshot-desktop-interface) and [Mobile](#screenshot-mobile-interface) support!
- Platform specific auto-detection and tuning performed on install
- Optimized processing with latest `torch` developments with built-in support for model compile and quantize
Compile backends: *Triton | StableFast | DeepCache | OneDiff | TeaCache | etc.*
Quantization methods: *SDNQ | BitsAndBytes | Optimum-Quanto | TorchAO / LayerWise*
- **Interrogate/Captioning** with 150+ **OpenCLiP** models and 20+ built-in **VLMs**
- Built in installer with automatic updates and dependency management
<br>
### Unique features
**Desktop** interface
<div align="center">
<img src="https://github.com/user-attachments/assets/d6119a63-6ee5-4597-95f6-29ed0701d3b5" alt="screenshot-modernui-desktop" width="90%">
</div>
**Mobile** interface
<div align="center">
<img src="https://github.com/user-attachments/assets/ced9fe0c-d2c2-46d1-94a7-8f9f2307ce38" alt="screenshot-modernui-mobile" width="35%">
</div>
For screenshots and information on other available themes, see [Themes](https://vladmandic.github.io/sdnext-docs/Themes/)
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
- **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
<br>
## Model support
## Supported AI Models
SD.Next supports broad range of models: [supported models](https://vladmandic.github.io/sdnext-docs/Model-Support/) and [model specs](https://vladmandic.github.io/sdnext-docs/Models/)
## Platform support
## Supported Platforms & Hardware
- *nVidia* GPUs using **CUDA** libraries on both *Windows and Linux*
- *AMD* GPUs using **ROCm** libraries on *Linux*
Support will be extended to *Windows* once AMD releases ROCm for Windows
- *AMD* GPUs using **ROCm** libraries on both *Linux and Windows*
- *AMD* GPUs on Windows using **ZLUDA** libraries
- *Intel Arc* GPUs using **OneAPI** with *IPEX XPU* libraries on both *Windows and Linux*
- Any *CPU/GPU* or device compatible with **OpenVINO** libraries on both *Windows and Linux*
- Any GPU compatible with *DirectX* on *Windows* using **DirectML** libraries
This includes support for AMD GPUs that are not supported by native ROCm libraries
- Any GPU or device compatible with **OpenVINO** libraries on both *Windows and Linux*
- *Apple M1/M2* on *OSX* using built-in support in Torch with **MPS** optimizations
- *ONNX/Olive*
- *AMD* GPUs on Windows using **ZLUDA** libraries
Plus Docker container recipes for: [CUDA, ROCm, Intel IPEX and OpenVINO](https://vladmandic.github.io/sdnext-docs/Docker/)
Plus **Docker** container recipes for: [CUDA, ROCm, Intel IPEX and OpenVINO](https://vladmandic.github.io/sdnext-docs/Docker/)
## Getting started
@ -84,21 +92,37 @@ Plus Docker container recipes for: [CUDA, ROCm, Intel IPEX and OpenVINO](https:/
> And for platform specific information, check out
> [WSL](https://vladmandic.github.io/sdnext-docs/WSL/) | [Intel Arc](https://vladmandic.github.io/sdnext-docs/Intel-ARC/) | [DirectML](https://vladmandic.github.io/sdnext-docs/DirectML/) | [OpenVINO](https://vladmandic.github.io/sdnext-docs/OpenVINO/) | [ONNX & Olive](https://vladmandic.github.io/sdnext-docs/ONNX-Runtime/) | [ZLUDA](https://vladmandic.github.io/sdnext-docs/ZLUDA/) | [AMD ROCm](https://vladmandic.github.io/sdnext-docs/AMD-ROCm/) | [MacOS](https://vladmandic.github.io/sdnext-docs/MacOS-Python/) | [nVidia](https://vladmandic.github.io/sdnext-docs/nVidia/) | [Docker](https://vladmandic.github.io/sdnext-docs/Docker/)
### Quick Start
```shell
git clone https://github.com/vladmandic/sdnext
cd sdnext
./webui.sh # Linux/Mac
webui.bat # Windows
webui.ps1 # PowerShell
```
> [!WARNING]
> If you run into issues, check out [troubleshooting](https://vladmandic.github.io/sdnext-docs/Troubleshooting/) and [debugging](https://vladmandic.github.io/sdnext-docs/Debug/) guides
## Community & Support
If you're unsure how to use a feature, best place to start is [Docs](https://vladmandic.github.io/sdnext-docs/) and if its not there,
check [ChangeLog](https://vladmandic.github.io/sdnext-docs/CHANGELOG/) for when feature was first introduced as it will always have a short note on how to use it
And for any question, reach out on [Discord](https://discord.gg/VjvR2tabEX) or open an [issue](https://github.com/vladmandic/sdnext/issues) or [discussion](https://github.com/vladmandic/sdnext/discussions)
### Contributing
Please see [Contributing](CONTRIBUTING) for details on how to contribute to this project
And for any question, reach out on [Discord](https://discord.gg/VjvR2tabEX) or open an [issue](https://github.com/vladmandic/sdnext/issues) or [discussion](https://github.com/vladmandic/sdnext/discussions)
### Credits
## License & Credits
- SD.Next is licensed under the [Apache License 2.0](LICENSE.txt)
- Main credit goes to [Automatic1111 WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) for the original codebase
- Additional credits are listed in [Credits](https://github.com/AUTOMATIC1111/stable-diffusion-webui/#credits)
- Licenses for modules are listed in [Licenses](html/licenses.html)
### Evolution
## Evolution
<a href="https://star-history.com/#vladmandic/sdnext&Date">
<picture width=640>
@ -109,9 +133,4 @@ And for any question, reach out on [Discord](https://discord.gg/VjvR2tabEX) or o
- [OSS Stats](https://ossinsight.io/analyze/vladmandic/sdnext#overview)
### Docs
If you're unsure how to use a feature, best place to start is [Docs](https://vladmandic.github.io/sdnext-docs/) and if its not there,
check [ChangeLog](https://vladmandic.github.io/sdnext-docs/CHANGELOG/) for when feature was first introduced as it will always have a short note on how to use it
<br>

105
TODO.md
View File

@ -2,29 +2,32 @@
## Internal
- Update: `transformers==5.0.0`, owner @CalamitousFelicitousness
- Deploy: Create executable for SD.Next
- Feature: implement `unload_auxiliary_models`
- Feature: RIFE update
- Feature: RIFE in processing
- Feature: SeedVR2 in processing
- Feature: Add video models to `Reference`
- Deploy: Lite vs Expert mode
- Engine: [mmgp](https://github.com/deepbeepmeep/mmgp)
- Engine: [sharpfin](https://github.com/drhead/sharpfin) instead of `torchvision`
- Engine: `TensorRT` acceleration
- Feature: Auto handle scheduler `prediction_type`
- Feature: Cache models in memory
- Feature: Control tab add overrides handling
- Feature: JSON image metadata
- Validate: Control tab add overrides handling
- Feature: Integrate natural language image search
[ImageDB](https://github.com/vladmandic/imagedb)
- Feature: LoRA add OMI format support for SD35/FLUX.1, on-hold
- Feature: Multi-user support
- Feature: Remote Text-Encoder support, sidelined for the moment
- Feature: Settings profile manager
- Feature: Video tab add full API support
- Refactor: Unify *huggingface* and *diffusers* model folders
- Refactor: Move `nunchaku` models to refernce instead of internal decision, owner @CalamitousFelicitousness
- Refactor: [GGUF](https://huggingface.co/docs/diffusers/main/en/quantization/gguf)
- Refactor: move sampler options to settings to config
- Refactor: remove `CodeFormer`, owner @CalamitousFelicitousness
- Refactor: remove `GFPGAN`, owner @CalamitousFelicitousness
- Reimplement `llama` remover for Kanvas, pending end-to-end review of `Kanvas`
- Reimplement `llama` remover for Kanvas
- Integrate: [Depth3D](https://github.com/vladmandic/sd-extension-depth3d)
## OnHold
- Feature: LoRA add OMI format support for SD35/FLUX.1, on-hold
- Feature: Remote Text-Encoder support, sidelined for the moment
## Modular
@ -42,8 +45,10 @@
TODO: Investigate which models are diffusers-compatible and prioritize!
### Image-Base
- [Chroma Zeta](https://huggingface.co/lodestones/Zeta-Chroma): Image and video generator for creative effects and professional filters
- [Chroma Radiance](https://huggingface.co/lodestones/Chroma1-Radiance): Pixel-space model eliminating VAE artifacts for high visual fidelity
- [Bria FIBO](https://huggingface.co/briaai/FIBO): Fully JSON based
- [Liquid](https://github.com/FoundationVision/Liquid): Unified vision-language auto-regressive generation paradigm
- [Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO): Foundational multi-modal generation and understanding via discrete diffusion
- [nVidia Cosmos-Predict-2.5](https://huggingface.co/nvidia/Cosmos-Predict2.5-2B): Physics-aware world foundation model for consistent scene prediction
@ -51,14 +56,20 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
- [Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO): foundational multi-modal multi-task generation and understanding
### Image-Edit
- [Bria FIBO-Edit](https://huggingface.co/briaai/Fibo-Edit-RMBG): Fully JSON-based instruction-following image editing framework
- [Meituan LongCat-Image-Edit-Turbo](https://huggingface.co/meituan-longcat/LongCat-Image-Edit-Turbo):6B instruction-following image editing with high visual consistency
- [VIBE Image-Edit](https://huggingface.co/iitolstykh/VIBE-Image-Edit): (Sana+Qwen-VL)Fast visual instruction-based image editing framework
- [LucyEdit](https://github.com/huggingface/diffusers/pull/12340):Instruction-guided video editing while preserving motion and identity
- [Step1X-Edit](https://github.com/stepfun-ai/Step1X-Edit):Multimodal image editing decoding MLLM tokens via DiT
- [OneReward](https://github.com/bytedance/OneReward):Reinforcement learning grounded generative reward model for image editing
- [ByteDance DreamO](https://huggingface.co/ByteDance/DreamO): image customization framework for IP adaptation and virtual try-on
- [nVidia Cosmos-Transfer-2.5](https://github.com/huggingface/diffusers/pull/13066)
### Video
- [LTX-Condition](https://github.com/huggingface/diffusers/pull/13058)
- [LTX-Distilled](https://github.com/huggingface/diffusers/pull/12934)
- [OpenMOSS MOVA](https://huggingface.co/OpenMOSS-Team/MOVA-720p): Unified foundation model for synchronized high-fidelity video and audio
- [Wan family (Wan2.1 / Wan2.2 variants)](https://huggingface.co/Wan-AI/Wan2.2-Animate-14B): MoE-based foundational tools for cinematic T2V/I2V/TI2V
example: [Wan2.1-T2V-14B-CausVid](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-CausVid)
@ -81,6 +92,7 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
- [Ming (inclusionAI)](https://github.com/inclusionAI/Ming): Unified multimodal model for processing text, audio, image, and video
### Other/Unsorted
- [DiffusionForcing](https://github.com/kwsong0113/diffusion-forcing-transformer): Full-sequence diffusion with autoregressive next-token prediction
- [Self-Forcing](https://github.com/guandeh17/Self-Forcing): Framework for improving temporal consistency in long-horizon video generation
- [SEVA](https://github.com/huggingface/diffusers/pull/11440): Stable Virtual Camera for novel view synthesis and 3D-consistent video
@ -97,8 +109,7 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
- [ReNO](https://github.com/ExplainableML/ReNO): Reward-based Noise Optimization to improve text-to-image quality during inference
### Not Planned
- [Bria FIBO](https://huggingface.co/briaai/FIBO): Fully JSON based
- [Bria FiboEdit](https://github.com/huggingface/diffusers/commit/d7a1c31f4f85bae5a9e01cdce49bd7346bd8ccd6): Fully JSON based
- [LoRAdapter](https://github.com/CompVis/LoRAdapter): Not recently updated
- [SD3 UltraEdit](https://github.com/HaozheZhao/UltraEdit): Based on SD3
- [PowerPaint](https://github.com/open-mmlab/PowerPaint): Based on SD15
@ -109,53 +120,29 @@ TODO: Investigate which models are diffusers-compatible and prioritize!
- [DenseDiffusion](https://github.com/naver-ai/DenseDiffusion): Based on SD15
- [IC-Light](https://github.com/lllyasviel/IC-Light): Based on SD15
## Migration
### Asyncio
- Policy system is deprecated and will be removed in Python 3.16
[Python 3.14 removalsasyncio](https://docs.python.org/3.14/whatsnew/3.14.html#id10)
https://docs.python.org/3.14/library/asyncio-policy.html
Affected files:
[`webui.py`](webui.py)
[`cli/sdapi.py`](cli/sdapi.py)
Migration:
[asyncio.run](https://docs.python.org/3.14/library/asyncio-runner.html#asyncio.run)
[asyncio.Runner](https://docs.python.org/3.14/library/asyncio-runner.html#asyncio.Runner)
### rmtree
- `onerror` deprecated and replaced with `onexc` in Python 3.12
``` python
def excRemoveReadonly(func, path, exc: BaseException):
import stat
shared.log.debug(f'Exception during cleanup: {func} {path} {type(exc).__name__}')
if func in (os.rmdir, os.remove, os.unlink) and isinstance(exc, PermissionError):
shared.log.debug(f'Retrying cleanup: {path}')
os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
func(path)
# ...
try:
shutil.rmtree(found.path, ignore_errors=False, onexc=excRemoveReadonly)
```
## Code TODO
> npm run todo
- fc: autodetect distilled based on model
- fc: autodetect tensor format based on model
- hypertile: vae breaks when using non-standard sizes
- install: switch to pytorch source when it becomes available
- loader: load receipe
- loader: save receipe
- lora: add other quantization types
- lora: add t5 key support for sd35/f1
- lora: maybe force imediate quantization
- model load: force-reloading entire model as loading transformers only leads to massive memory usage
- model load: implement model in-memory caching
- modernui: monkey-patch for missing tabs.select event
- modules/lora/lora_extract.py:188:9: W0511: TODO: lora: support pre-quantized flux
- modules/modular_guiders.py:65:58: W0511: TODO: guiders
- processing: remove duplicate mask params
- resize image: enable full VAE mode for resize-latent
```code
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_apply.py:TODO lora: add other quantization types
modules/lora/lora_apply.py:TODO lora: maybe force imediate quantization
modules/lora/lora_extract.py:TODO: lora: support pre-quantized flux
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
modules/video_models/video_save.py:TODO audio set time-base
```

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
"""
use clip to interrogate image(s)
use clip to caption image(s)
"""
import io
@ -43,64 +43,63 @@ def print_summary():
log.info({ 'keyword stats': keywords })
async def interrogate(f):
async def caption(f):
if not filetype.is_image(f):
log.info({ 'interrogate skip': f })
log.info({ 'caption skip': f })
return
json = Map({ 'image': encode(f) })
log.info({ 'interrogate': f })
log.info({ 'caption': f })
# run clip
json.model = 'clip'
res = await sdapi.post('/sdapi/v1/interrogate', json)
caption = ""
res = await sdapi.post('/sdapi/v1/caption', json)
result = ""
style = ""
if 'caption' in res:
caption = res.caption
log.info({ 'interrogate caption': caption })
if ', by' in caption:
style = caption.split(', by')[1].strip()
log.info({ 'interrogate style': style })
for word in caption.split(' '):
result = res.caption
log.info({ 'caption result': result })
if ', by' in result:
style = result.split(', by')[1].strip()
log.info({ 'caption style': style })
for word in result.split(' '):
if word not in exclude:
stats['captions'][word] = stats['captions'][word] + 1 if word in stats['captions'] else 1
else:
log.error({ 'interrogate clip error': res })
# run booru
json.model = 'deepdanbooru'
res = await sdapi.post('/sdapi/v1/interrogate', json)
log.error({ 'caption clip error': res })
# run tagger (DeepBooru)
tagger_req = Map({'image': json.image, 'model': 'deepbooru', 'show_scores': True})
res = await sdapi.post('/sdapi/v1/tagger', tagger_req)
keywords = {}
if 'caption' in res:
for term in res.caption.split(', '):
term = term.replace('(', '').replace(')', '').replace('\\', '').split(':')
if len(term) < 2:
continue
keywords[term[0]] = term[1]
keywords = dict(sorted(keywords.items(), key=lambda x:x[1], reverse=True))
for word in keywords.items():
stats['keywords'][word[0]] = stats['keywords'][word[0]] + 1 if word[0] in stats['keywords'] else 1
log.info({ 'interrogate keywords': keywords })
if 'scores' in res and res.scores:
keywords = dict(sorted(res.scores.items(), key=lambda x: x[1], reverse=True))
for word in keywords:
stats['keywords'][word] = stats['keywords'][word] + 1 if word in stats['keywords'] else 1
log.info({'caption keywords': keywords})
elif 'tags' in res:
for tag in res.tags.split(', '):
stats['keywords'][tag] = stats['keywords'][tag] + 1 if tag in stats['keywords'] else 1
log.info({'caption tags': res.tags})
else:
log.error({ 'interrogate booru error': res })
return caption, keywords, style
log.error({'caption tagger error': res})
return result, keywords, style
async def main():
sys.argv.pop(0)
await sdapi.session()
if len(sys.argv) == 0:
log.error({ 'interrogate': 'no files specified' })
log.error({ 'caption': 'no files specified' })
for arg in sys.argv:
if os.path.exists(arg):
if os.path.isfile(arg):
await interrogate(arg)
await caption(arg)
elif os.path.isdir(arg):
for root, _dirs, files in os.walk(arg):
for f in files:
_caption, _keywords, _style = await interrogate(os.path.join(root, f))
_caption, _keywords, _style = await caption(os.path.join(root, f))
else:
log.error({ 'interrogate unknown file type': arg })
log.error({ 'caption unknown file type': arg })
else:
log.error({ 'interrogate file missing': arg })
log.error({ 'caption file missing': arg })
await sdapi.close()
print_summary()

View File

@ -24,7 +24,7 @@ def auth():
return None
def get(endpoint: str, dct: dict = None):
def get(endpoint: str, dct: dict | None = None):
req = requests.get(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -30,7 +30,7 @@ def auth():
return None
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -23,7 +23,7 @@ def auth():
return None
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -24,7 +24,7 @@ def auth():
return None
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -29,7 +29,7 @@ def auth():
return None
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -79,7 +79,7 @@ def generate(x: int, y: int): # pylint: disable=redefined-outer-name
return images
def merge(images: list[Image.Image], horizontal: bool, labels: list[str] = None):
def merge(images: list[Image.Image], horizontal: bool, labels: list[str] | None = None):
rows = 1 if horizontal else len(images)
cols = math.ceil(len(images) / rows)
w = max([i.size[0] for i in images])

View File

@ -29,7 +29,7 @@ def auth():
return None
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -24,7 +24,7 @@ def auth():
return None
def get(endpoint: str, dct: dict = None):
def get(endpoint: str, dct: dict | None = None):
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
@ -32,7 +32,7 @@ def get(endpoint: str, dct: dict = None):
return req.json()
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -28,7 +28,7 @@ def auth():
return None
def post(endpoint: str, payload: dict = None):
def post(endpoint: str, payload: dict | None = None):
if 'sdapi' not in endpoint:
endpoint = f'sdapi/v1/{endpoint}'
if 'http' not in endpoint:

View File

@ -26,7 +26,7 @@ def auth():
return None
def get(endpoint: str, dct: dict = None):
def get(endpoint: str, dct: dict | None = None):
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
@ -34,7 +34,7 @@ def get(endpoint: str, dct: dict = None):
return req.json()
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -26,7 +26,7 @@ def auth():
return None
def get(endpoint: str, dct: dict = None):
def get(endpoint: str, dct: dict | None = None):
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
@ -34,7 +34,7 @@ def get(endpoint: str, dct: dict = None):
return req.json()
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -81,7 +81,7 @@ async function main() {
} else {
const json = await res.json();
console.log('result:', json.info);
for (const i in json.images) { // eslint-disable-line guard-for-in
for (const i in json.images) {
const file = args.output || `/tmp/test-${i}.jpg`;
const data = atob(json.images[i]);
fs.writeFileSync(file, data, 'binary');

0
cli/api-samplers.py Normal file → Executable file
View File

View File

@ -41,7 +41,7 @@ async function main() {
} else {
const json = await res.json();
console.log('result:', json.info);
for (const i in json.images) { // eslint-disable-line guard-for-in
for (const i in json.images) {
const f = `/tmp/test-${i}.jpg`;
fs.writeFileSync(f, atob(json.images[i]), 'binary');
console.log('image saved:', f);

View File

@ -28,7 +28,7 @@ def auth():
return None
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -24,7 +24,7 @@ def auth():
return None
def get(endpoint: str, dct: dict = None):
def get(endpoint: str, dct: dict | None = None):
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
@ -32,7 +32,7 @@ def get(endpoint: str, dct: dict = None):
return req.json()
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -24,7 +24,7 @@ def auth():
return None
def get(endpoint: str, dct: dict = None):
def get(endpoint: str, dct: dict | None = None):
req = requests.get(f'{sd_url}{endpoint}', json=dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }
@ -32,7 +32,7 @@ def get(endpoint: str, dct: dict = None):
return req.json()
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -30,7 +30,7 @@ def auth():
return None
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -24,7 +24,7 @@ def auth():
return None
def get(endpoint: str, dct: dict = None):
def get(endpoint: str, dct: dict | None = None):
req = requests.get(f'{sd_url}{endpoint}', json = dct, timeout=300, verify=False, auth=auth())
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -99,10 +99,10 @@ def search_civitai(
types:str = '', # (Checkpoint, TextualInversion, Hypernetwork, AestheticGradient, LORA, Controlnet, Poses)
sort:str = '', # (Highest Rated, Most Downloaded, Newest)
period:str = '', # (AllTime, Year, Month, Week, Day)
nsfw:bool = None, # optional:bool
nsfw:bool | None = None, # optional:bool
limit:int = 0,
base:list[str] = [], # list
token:str = None,
token:str | None = None,
exact:bool = True,
):
import requests
@ -113,6 +113,11 @@ def search_civitai(
return []
t0 = time.time()
import re
url_match = re.match(r'https?://civitai\.com/models/(\d+)', query.strip())
if url_match:
query = url_match.group(1)
log.info(f'CivitAI: extracted model id={query} from URL')
dct = { 'query': query }
if len(tag) > 0:
dct['tag'] = tag
@ -164,7 +169,7 @@ def search_civitai(
return exact_models if len(exact_models) > 0 else models
def models_to_dct(all_models:list, model_id:int=None):
def models_to_dct(all_models:list, model_id:int | None=None):
dct = []
for model in all_models:
if model_id is not None and model.id != model_id:

0
cli/download-file.py Normal file → Executable file
View File

View File

@ -33,7 +33,7 @@ def pil_to_b64(img: Image, size: int, quality: int):
return f'data:image/jpeg;base64,{b64encoded}'
def post(endpoint: str, dct: dict = None):
def post(endpoint: str, dct: dict | None = None):
req = requests.post(endpoint, json = dct, timeout=300, verify=False)
if req.status_code != 200:
return { 'error': req.status_code, 'reason': req.reason, 'url': req.url }

View File

@ -24,10 +24,7 @@
{
"upscaler_1": "SwinIR_4x",
"upscaler_2": "None",
"upscaling_resize": 0,
"gfpgan_visibility": 0,
"codeformer_visibility": 0,
"codeformer_weight": 0.5
"upscaling_resize": 0
},
"options":
{

View File

@ -65,8 +65,6 @@ def exif(info, i = None, op = 'generate'):
seed = ', '.join([str(x) for x in seed]) # int list to str list to single str
template = '{prompt} | negative {negative_prompt} | seed {s} | steps {steps} | cfgscale {cfg_scale} | sampler {sampler_name} | batch {batch_size} | timestamp {job_timestamp} | model {model} | vae {vae}'.format(s = seed, model = sd.options['sd_model_checkpoint'], vae = sd.options['sd_vae'], **info) # pylint: disable=consider-using-f-string
if op == 'upscale':
template += ' | faces gfpgan' if sd.upscale.gfpgan_visibility > 0 else ''
template += ' | faces codeformer' if sd.upscale.codeformer_visibility > 0 else ''
template += ' | upscale {resize}x {upscaler}'.format(resize = sd.upscale.upscaling_resize, upscaler = sd.upscale.upscaler_1) if sd.upscale.upscaler_1 != 'None' else '' # pylint: disable=consider-using-f-string
template += ' | upscale {resize}x {upscaler}'.format(resize = sd.upscale.upscaling_resize, upscaler = sd.upscale.upscaler_2) if sd.upscale.upscaler_2 != 'None' else '' # pylint: disable=consider-using-f-string
if op == 'grid':
@ -309,7 +307,6 @@ def args(): # parse cmd arguments
sd.generate.height = params.height if params.height > 0 else sd.generate.height
sd.generate.steps = params.steps if params.steps > 0 else sd.generate.steps
sd.upscale.upscaling_resize = params.upscale if params.upscale > 0 else sd.upscale.upscaling_resize
sd.upscale.codeformer_visibility = 1 if params.detailer else sd.upscale.codeformer_visibility
sd.options.sd_vae = params.vae if params.vae != '' else sd.options.sd_vae
sd.options.sd_model_checkpoint = params.model if params.model != '' else sd.options.sd_model_checkpoint
sd.upscale.upscaler_1 = 'SwinIR_4x' if params.upscale > 1 else sd.upscale.upscaler_1

0
cli/git-clone.py Normal file → Executable file
View File

View File

@ -14,7 +14,6 @@ if __name__ == "__main__":
full=True,
limit=100,
sort="downloads",
direction=-1,
)
res = sorted(res, key=lambda x: x.id)
exact = [m for m in res if keyword.lower() in m.id.lower()]

View File

@ -19,7 +19,7 @@ class ImageDB:
def __init__(self,
name:str='db',
fmt:str='json',
cache_dir:str=None,
cache_dir:str | None=None,
dtype:torch.dtype=torch.float16,
device:torch.device=torch.device('cpu'),
model:str='openai/clip-vit-large-patch14', # 'facebook/dinov2-small'
@ -123,8 +123,8 @@ class ImageDB:
self.df = rec
self.index.add(embed)
def search(self, filename: str = None, metadata: str = None, embed: np.ndarray = None, k=10, d=1.0): # search by filename/metadata/prompt-embed/image-embed
def dct(record: pd.DataFrame, mode: str, distance: float = None):
def search(self, filename: str | None = None, metadata: str | None = None, embed: np.ndarray = None, k=10, d=1.0): # search by filename/metadata/prompt-embed/image-embed
def dct(record: pd.DataFrame, mode: str, distance: float | None = None):
if distance is not None:
return {'type': mode, 'filename': record[1]['filename'], 'metadata': record[1]['metadata'], 'distance': round(distance, 2)}
else:

0
cli/install-stablefast.py Normal file → Executable file
View File

View File

@ -1,67 +0,0 @@
#!/usr/bin/env node
// script used to localize sdnext ui and hints to multiple languages using google gemini ai
const fs = require('node:fs');
const { GoogleGenerativeAI } = require('@google/generative-ai');
const api_key = process.env.GOOGLE_AI_API_KEY;
const model = 'gemini-2.5-flash';
const prompt = `Translate attached JSON from English to {language} using following rules: fields id, label and reload should be preserved from original, field localized should be a translated version of field label and field hint should be translated in-place.
if field is less than 3 characters, do not translate it and keep it as is.
Every JSON entry should have id, label, localized, reload and hint fields.
Output should be pure JSON without any additional text. To better match translation, context of the text is related to Stable Diffusion and topic of Generative AI.`;
const languages = {
hr: 'Croatian',
de: 'German',
es: 'Spanish',
fr: 'French',
it: 'Italian',
pt: 'Portuguese',
zh: 'Chinese',
ja: 'Japanese',
ko: 'Korean',
ru: 'Russian',
};
const chunkLines = 100;
async function localize() {
if (!api_key || api_key.length < 10) {
console.error('localize: set GOOGLE_AI_API_KEY env variable with your API key');
process.exit();
}
const genAI = new GoogleGenerativeAI(api_key);
const instance = genAI.getGenerativeModel({ model });
const raw = fs.readFileSync('html/locale_en.json');
const json = JSON.parse(raw);
for (const locale of Object.keys(languages)) {
const lang = languages[locale];
const target = prompt.replace('{language}', lang).trim();
const output = {};
const fn = `html/locale_${locale}.json`;
for (const section of Object.keys(json)) {
const data = json[section];
output[section] = [];
for (let i = 0; i < data.length; i += chunkLines) {
let markdown;
try {
const chunk = data.slice(i, i + chunkLines);
const result = await instance.generateContent([target, JSON.stringify(chunk)]);
markdown = result.response.text();
const text = markdown.replaceAll('```', '').replace(/^.*\n/, '');
const parsed = JSON.parse(text);
output[section].push(...parsed);
console.log(`localize: locale=${locale} lang=${lang} section=${section} chunk=${chunk.length} output=${output[section].length} fn=${fn}`);
} catch (err) {
console.error('localize:', err);
console.error('localize input:', { target, section, i });
console.error('localize output:', { markdown });
}
}
const txt = JSON.stringify(output, null, 2);
fs.writeFileSync(fn, txt);
}
}
}
localize();

View File

@ -13,9 +13,8 @@ def get_nvidia_smi(output='dict'):
if smi is None:
log.error("nvidia-smi not found")
return None
result = subprocess.run(f'"{smi}" -q -x', shell=True, check=False, env=os.environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
xml = result.stdout.decode(encoding="utf8", errors="ignore")
d = xmltodict.parse(xml)
result = subprocess.run(f'"{smi}" -q -x', shell=True, check=False, env=os.environ, capture_output=True, text=True)
d = xmltodict.parse(result.stdout)
if 'nvidia_smi_log' in d:
d = d['nvidia_smi_log']
if 'gpu' in d and 'supported_clocks' in d['gpu']:

View File

@ -6,7 +6,6 @@ import base64
import numpy as np
import mediapipe as mp
from PIL import Image, ImageOps
from pi_heif import register_heif_opener
from skimage.metrics import structural_similarity as ssim
from scipy.stats import beta
@ -22,7 +21,7 @@ all_images_by_type = {}
class Result():
def __init__(self, typ: str, fn: str, tag: str = None, requested: list = []):
def __init__(self, typ: str, fn: str, tag: str | None = None, requested: list = []):
self.type = typ
self.input = fn
self.output = ''
@ -126,11 +125,9 @@ def reset():
all_images = []
def upscale_restore_image(res: Result, upscale: bool = False, restore: bool = False):
def upscale_restore_image(res: Result, upscale: bool = False):
kwargs = util.Map({
'image': encode(res.image),
'codeformer_visibility': 0.0,
'codeformer_weight': 0.0,
})
if res.image.width >= options.process.target_size and res.image.height >= options.process.target_size:
upscale = False
@ -138,25 +135,21 @@ def upscale_restore_image(res: Result, upscale: bool = False, restore: bool = Fa
kwargs.upscaler_1 = 'SwinIR_4x'
kwargs.upscaling_resize = 2
res.ops.append('upscale')
if restore:
kwargs.codeformer_visibility = 1.0
kwargs.codeformer_weight = 0.2
res.ops.append('restore')
if upscale or restore:
if upscale:
result = sdapi.postsync('/sdapi/v1/extra-single-image', kwargs)
if 'image' not in result:
res.message = 'failed to upscale/restore image'
res.message = 'failed to upscale image'
else:
res.image = Image.open(io.BytesIO(base64.b64decode(result['image'])))
return res
def interrogate_image(res: Result, tag: str = None):
def caption_image(res: Result, tag: str | None = None):
caption = ''
tags = []
for model in options.process.interrogate_model:
for model in options.process.caption_model:
json = util.Map({ 'image': encode(res.image), 'model': model })
result = sdapi.postsync('/sdapi/v1/interrogate', json)
result = sdapi.postsync('/sdapi/v1/caption', json)
if model == 'clip':
caption = result.caption if 'caption' in result else ''
caption = caption.split(',')[0].replace(' a ', ' ').strip()
@ -176,7 +169,7 @@ def interrogate_image(res: Result, tag: str = None):
tags = tags[:options.process.tag_limit]
res.caption = caption
res.tags = tags
res.ops.append('interrogate')
res.ops.append('caption')
return res
@ -267,7 +260,6 @@ def file(filename: str, folder: str, tag = None, requested = []):
res = Result(fn = filename, typ='unknown', tag=tag, requested = requested)
# open image
try:
register_heif_opener()
res.image = Image.open(filename)
if res.image.mode == 'RGBA':
res.image = res.image.convert('RGB')
@ -309,13 +301,13 @@ def file(filename: str, folder: str, tag = None, requested = []):
if res.image is None:
return res
# post processing steps
res = upscale_restore_image(res, 'upscale' in requested, 'restore' in requested)
res = upscale_restore_image(res, 'upscale' in requested)
if res.image.width < options.process.target_size or res.image.height < options.process.target_size:
res.message = f'low resolution: [{res.image.width}, {res.image.height}]'
res.image = None
return res
if 'interrogate' in requested:
res = interrogate_image(res, tag)
if 'caption' in requested:
res = caption_image(res, tag)
if 'resize' in requested:
res = resize_image(res)
if 'square' in requested:

View File

@ -130,9 +130,9 @@ process = Map({
'body_pad': 0.2, # pad body image percentage
'body_model': 2, # body model to use 0/low 1/medium 2/high
# similarity detection settings
# interrogate settings
'interrogate': False, # interrogate images
'interrogate_model': ['clip', 'deepdanbooru'], # interrogate models
# caption settings
'caption': False, # caption images
'caption_model': ['clip', 'deepdanbooru'], # caption models
'tag_limit': 5, # number of tags to extract
# validations
# tbd

View File

@ -93,7 +93,7 @@ def resultsync(req: requests.Response):
return res
async def get(endpoint: str, json: dict = None):
async def get(endpoint: str, json: dict | None = None):
global sess # pylint: disable=global-statement
sess = sess if sess is not None else await session()
try:
@ -105,7 +105,7 @@ async def get(endpoint: str, json: dict = None):
return {}
def getsync(endpoint: str, json: dict = None):
def getsync(endpoint: str, json: dict | None = None):
try:
req = requests.get(f'{sd_url}{endpoint}', json=json, verify=False, auth=authsync()) # pylint: disable=missing-timeout
res = resultsync(req)
@ -115,7 +115,7 @@ def getsync(endpoint: str, json: dict = None):
return {}
async def post(endpoint: str, json: dict = None):
async def post(endpoint: str, json: dict | None = None):
global sess # pylint: disable=global-statement
# sess = sess if sess is not None else await session()
if sess and not sess.closed:
@ -130,7 +130,7 @@ async def post(endpoint: str, json: dict = None):
return {}
def postsync(endpoint: str, json: dict = None):
def postsync(endpoint: str, json: dict | None = None):
req = requests.post(f'{sd_url}{endpoint}', json=json, verify=False, auth=authsync()) # pylint: disable=missing-timeout
res = resultsync(req)
return res

0
cli/search-docs.py Normal file → Executable file
View File

3
constraints.txt Normal file
View File

@ -0,0 +1,3 @@
fastapi==0.124.4
numpy==2.1.2
Pillow==10.4.0

View File

@ -1,11 +1,18 @@
{
"Google Gemini 2.5 Flash Nano Banana": {
"Google Gemini 2.5 Flash Nano Banana": {
"path": "gemini-2.5-flash-image",
"desc": "Gemini can generate and process images conversationally. You can prompt Gemini with text, images, or a combination of both allowing you to create, edit, and iterate on visuals with unprecedented control.",
"preview": "gemini-2.5-flash-image.jpg",
"tags": "cloud",
"skip": true
},
"Google Gemini 3.1 Flash Nano Banana": {
"path": "gemini-3.1-flash-image-preview",
"desc": "Gemini can generate and process images conversationally. You can prompt Gemini with text, images, or a combination of both allowing you to create, edit, and iterate on visuals with unprecedented control.",
"preview": "gemini-3.1-flash-image-preview.jpg",
"tags": "cloud",
"skip": true
},
"Google Gemini 3.0 Pro Nano Banana": {
"path": "gemini-3-pro-image-preview",
"desc": "Built on Gemini 3. Create and edit images with studio-quality levels of precision and control",

View File

@ -106,20 +106,22 @@
"desc": "Pony V7 is a versatile character generation model based on AuraFlow architecture. It supports a wide range of styles and species types (humanoid, anthro, feral, and more) and handles character interactions through natural language prompts.",
"extras": "",
"tags": "community",
"date": "October September"
"date": "2025 October"
},
"ShuttleAI Shuttle 3.0 Diffusion": {
"path": "shuttleai/shuttle-3-diffusion",
"desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition",
"preview": "shuttleai--shuttle-3-diffusion.jpg",
"tags": "community",
"date": "2024 November",
"skip": true
},
"ShuttleAI Shuttle 3.1 Aesthetic": {
"path": "shuttleai/shuttle-3.1-aesthetic",
"desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition",
"preview": "shuttleai--shuttle-3_1-aestetic.jpg",
"preview": "shuttleai--shuttle-3.1-aesthetic.jpg",
"tags": "community",
"date": "2024 November",
"skip": true
},
"ShuttleAI Shuttle Jaguar": {
@ -127,13 +129,55 @@
"desc": "Shuttle uses Flux.1 Schnell as its base. It can produce images similar to Flux Dev or Pro in just 4 steps, and it is licensed under Apache 2. The model was partially de-distilled during training. When used beyond 10 steps, it enters refiner mode enhancing image details without altering the composition",
"preview": "shuttleai--shuttle-jaguar.jpg",
"tags": "community",
"date": "2025 January",
"skip": true
},
"Anima": {
"Anima Preview 1": {
"path": "CalamitousFelicitousness/Anima-sdnext-diffusers",
"preview": "CalamitousFelicitousness--Anima-sdnext-diffusers.png",
"preview": "CalamitousFelicitousness--Anima-sdnext-diffusers.jpg",
"desc": "Modified Cosmos-Predict-2B that replaces the T5-11B text encoder with Qwen3-0.6B. Anima is a 2 billion parameter text-to-image model created via a collaboration between CircleStone Labs and Comfy Org. It is focused mainly on anime concepts, characters, and styles, but is also capable of generating a wide variety of other non-photorealistic content. The model is designed for making illustrations and artistic images, and will not work well at realism.",
"tags": "community",
"date": "2026 January",
"skip": true
},
"Anima Preview 2": {
"path": "CalamitousFelicitousness/Anima-Preview-2-sdnext-diffusers",
"preview": "CalamitousFelicitousness--Anima-Preview-2-sdnext-diffusers.jpg",
"desc": "Anima Preview V2 with improved hyperparameters, extended medium-resolution training for more character knowledge, and a regularization dataset for better natural language comprehension. A 2B parameter anime-focused text-to-image model based on modified Cosmos-Predict-2B with Qwen3-0.6B text encoder.",
"tags": "community",
"date": "2026 March",
"skip": true
},
"FireRed Image Edit 1.0": {
"path": "FireRedTeam/FireRed-Image-Edit-1.0",
"preview": "FireRedTeam--FireRed-Image-Edit-1.0.jpg",
"desc": "FireRed-Image-Edit is a general-purpose image editing model that delivers high-fidelity and consistent editing across a wide range of scenarios. FireRed is a fine-tune of Qwen-Image-Edit.",
"tags": "community",
"date": "2026 February",
"skip": true
},
"FireRed Image Edit 1.1": {
"path": "FireRedTeam/FireRed-Image-Edit-1.1",
"preview": "FireRedTeam--FireRed-Image-Edit-1.1.jpg",
"desc": "FireRed-Image-Edit is a general-purpose image editing model that delivers high-fidelity and consistent editing across a wide range of scenarios. FireRed is a fine-tune of Qwen-Image-Edit.",
"tags": "community",
"date": "2026 February",
"skip": true
},
"Skywork UniPic3": {
"path": "Skywork/Unipic3",
"preview": "Skywork--Unipic3.jpg",
"desc": "UniPic3 is an image editing and multi-image composition model based. It is a fine-tune of Qwen-Image-Edit.",
"tags": "community",
"date": "2026 February",
"skip": true
},
"Skywork/Unipic3-DMD": {
"path": "Skywork/Unipic3-DMD",
"preview": "Skywork--Unipic3-DMD.jpg",
"desc": "UniPic3-DMD-Model is a few-step image editing and multi-image composition model trained using Distribution Matching Distillation (DMD) and is a fine-tune of Qwen-Image-Edit.",
"tags": "community",
"date": "2026 February",
"skip": true
}
}

View File

@ -170,7 +170,7 @@
"tags": "distilled",
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
"size": 8.5,
"date": "2025 January"
"date": "2026 January"
},
"Black Forest Labs FLUX.2 Klein 9B": {
"path": "black-forest-labs/FLUX.2-klein-9B",
@ -180,6 +180,25 @@
"tags": "distilled",
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
"size": 18.5,
"date": "2025 January"
"date": "2026 January"
},
"Black Forest Labs FLUX.2 Klein 9B KV": {
"path": "black-forest-labs/FLUX.2-klein-9b-kv",
"preview": "black-forest-labs--FLUX.2-klein-9b-kv.jpg",
"desc": "FLUX.2 klein 9B KV is an optimized variant of FLUX.2 klein 9B with KV-cache support for accelerated multi-reference editing. This variant caches key-value pairs from reference images during the first denoising step, eliminating redundant computation in subsequent steps for significantly faster multi-image editing workflows.",
"skip": true,
"tags": "distilled",
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
"size": 18.5,
"date": "2026 March"
},
"Meituan LongCat Image-Edit Turbo": {
"path": "meituan-longcat/LongCat-Image-Edit-Turbo",
"preview": "meituan-longcat--LongCat-Image-Edit.jpg",
"desc": "LongCat-Image-Edit-Turbo, the distilled version of LongCat-Image-Edit. It achieves high-quality image editing with only 8 NFEs (Number of Function Evaluations) , offering extremely low inference latency.",
"skip": true,
"extras": "",
"size": 27.30,
"date": "2026 February"
}
}

View File

@ -0,0 +1,209 @@
{
"FLUX.1-Dev Nunchaku SVDQuant": {
"path": "black-forest-labs/FLUX.1-dev",
"subfolder": "nunchaku",
"preview": "black-forest-labs--FLUX.1-dev.jpg",
"desc": "Nunchaku SVDQuant quantization of FLUX.1-dev transformer with INT4 and SVD rank 32",
"skip": true,
"nunchaku": ["Model", "TE"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"FLUX.1-Schnell Nunchaku SVDQuant": {
"path": "black-forest-labs/FLUX.1-schnell",
"subfolder": "nunchaku",
"preview": "black-forest-labs--FLUX.1-schnell.jpg",
"desc": "Nunchaku SVDQuant quantization of FLUX.1-schnell transformer with INT4 and SVD rank 32",
"skip": true,
"nunchaku": ["Model", "TE"],
"tags": "nunchaku",
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
"size": 0,
"date": "2025 June"
},
"FLUX.1-Kontext Nunchaku SVDQuant": {
"path": "black-forest-labs/FLUX.1-Kontext-dev",
"subfolder": "nunchaku",
"preview": "black-forest-labs--FLUX.1-Kontext-dev.jpg",
"desc": "Nunchaku SVDQuant quantization of FLUX.1-Kontext-dev transformer with INT4 and SVD rank 32",
"skip": true,
"nunchaku": ["Model", "TE"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"FLUX.1-Krea Nunchaku SVDQuant": {
"path": "black-forest-labs/FLUX.1-Krea-dev",
"subfolder": "nunchaku",
"preview": "black-forest-labs--FLUX.1-Krea-dev.jpg",
"desc": "Nunchaku SVDQuant quantization of FLUX.1-Krea-dev transformer with INT4 and SVD rank 32",
"skip": true,
"nunchaku": ["Model", "TE"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"FLUX.1-Fill Nunchaku SVDQuant": {
"path": "black-forest-labs/FLUX.1-Fill-dev",
"subfolder": "nunchaku",
"preview": "black-forest-labs--FLUX.1-Fill-dev.jpg",
"desc": "Nunchaku SVDQuant quantization of FLUX.1-Fill-dev transformer for inpainting",
"skip": true,
"hidden": true,
"nunchaku": ["Model", "TE"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"FLUX.1-Depth Nunchaku SVDQuant": {
"path": "black-forest-labs/FLUX.1-Depth-dev",
"subfolder": "nunchaku",
"preview": "black-forest-labs--FLUX.1-Depth-dev.jpg",
"desc": "Nunchaku SVDQuant quantization of FLUX.1-Depth-dev transformer for depth-conditioned generation",
"skip": true,
"hidden": true,
"nunchaku": ["Model", "TE"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"Shuttle Jaguar Nunchaku SVDQuant": {
"path": "shuttleai/shuttle-jaguar",
"subfolder": "nunchaku",
"preview": "shuttleai--shuttle-jaguar.jpg",
"desc": "Nunchaku SVDQuant quantization of Shuttle Jaguar transformer",
"skip": true,
"nunchaku": ["Model", "TE"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"Qwen-Image Nunchaku SVDQuant": {
"path": "Qwen/Qwen-Image",
"subfolder": "nunchaku",
"preview": "Qwen--Qwen-Image.jpg",
"desc": "Nunchaku SVDQuant quantization of Qwen-Image transformer with INT4 and SVD rank 128",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"Qwen-Lightning (8-step) Nunchaku SVDQuant": {
"path": "vladmandic/Qwen-Lightning",
"subfolder": "nunchaku",
"preview": "vladmandic--Qwen-Lightning.jpg",
"desc": "Nunchaku SVDQuant quantization of Qwen-Lightning (8-step distilled) transformer with INT4 and SVD rank 128",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"extras": "steps: 8",
"size": 0,
"date": "2025 June"
},
"Qwen-Lightning (4-step) Nunchaku SVDQuant": {
"path": "vladmandic/Qwen-Lightning",
"subfolder": "nunchaku-4step",
"preview": "vladmandic--Qwen-Lightning.jpg",
"desc": "Nunchaku SVDQuant quantization of Qwen-Lightning (4-step distilled) transformer with INT4 and SVD rank 128",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"extras": "steps: 4",
"size": 0,
"date": "2025 June"
},
"Qwen-Image-Edit Nunchaku SVDQuant": {
"path": "Qwen/Qwen-Image-Edit",
"subfolder": "nunchaku",
"preview": "Qwen--Qwen-Image-Edit.jpg",
"desc": "Nunchaku SVDQuant quantization of Qwen-Image-Edit transformer with INT4 and SVD rank 128",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"Qwen-Lightning-Edit (8-step) Nunchaku SVDQuant": {
"path": "vladmandic/Qwen-Lightning-Edit",
"subfolder": "nunchaku",
"preview": "vladmandic--Qwen-Lightning-Edit.jpg",
"desc": "Nunchaku SVDQuant quantization of Qwen-Lightning-Edit (8-step distilled editing) transformer with INT4 and SVD rank 128",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"extras": "steps: 8",
"size": 0,
"date": "2025 June"
},
"Qwen-Lightning-Edit (4-step) Nunchaku SVDQuant": {
"path": "vladmandic/Qwen-Lightning-Edit",
"subfolder": "nunchaku-4step",
"preview": "vladmandic--Qwen-Lightning-Edit.jpg",
"desc": "Nunchaku SVDQuant quantization of Qwen-Lightning-Edit (4-step distilled editing) transformer with INT4 and SVD rank 128",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"extras": "steps: 4",
"size": 0,
"date": "2025 June"
},
"Qwen-Image-Edit-2509 Nunchaku SVDQuant": {
"path": "Qwen/Qwen-Image-Edit-2509",
"subfolder": "nunchaku",
"preview": "Qwen--Qwen-Image-Edit-2509.jpg",
"desc": "Nunchaku SVDQuant quantization of Qwen-Image-Edit-2509 transformer with INT4 and SVD rank 128",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"size": 0,
"date": "2025 September"
},
"Sana 1.6B 1k Nunchaku SVDQuant": {
"path": "Efficient-Large-Model/Sana_1600M_1024px_BF16_diffusers",
"subfolder": "nunchaku",
"preview": "Efficient-Large-Model--Sana_1600M_1024px_BF16_diffusers.jpg",
"desc": "Nunchaku SVDQuant quantization of Sana 1.6B 1024px transformer with INT4 and SVD rank 32",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"Z-Image-Turbo Nunchaku SVDQuant": {
"path": "Tongyi-MAI/Z-Image-Turbo",
"subfolder": "nunchaku",
"preview": "Tongyi-MAI--Z-Image-Turbo.jpg",
"desc": "Nunchaku SVDQuant quantization of Z-Image-Turbo transformer with INT4 and SVD rank 128",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"extras": "sampler: Default, cfg_scale: 1.0, steps: 9",
"size": 0,
"date": "2025 June"
},
"SDXL Base Nunchaku SVDQuant": {
"path": "stabilityai/stable-diffusion-xl-base-1.0",
"subfolder": "nunchaku",
"preview": "stabilityai--stable-diffusion-xl-base-1.0.jpg",
"desc": "Nunchaku SVDQuant quantization of SDXL Base 1.0 UNet with INT4 and SVD rank 32",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"size": 0,
"date": "2025 June"
},
"SDXL Turbo Nunchaku SVDQuant": {
"path": "stabilityai/sdxl-turbo",
"subfolder": "nunchaku",
"preview": "stabilityai--sdxl-turbo.jpg",
"desc": "Nunchaku SVDQuant quantization of SDXL Turbo UNet with INT4 and SVD rank 32",
"skip": true,
"nunchaku": ["Model"],
"tags": "nunchaku",
"extras": "sampler: Default, cfg_scale: 1.0, steps: 4",
"size": 0,
"date": "2025 June"
}
}

View File

@ -25,11 +25,13 @@ const jsConfig = defineConfig([
ecmaVersion: 'latest',
},
globals: { // Set per project
...globals.node,
...globals.builtin,
...globals.browser,
...globals.jquery,
panzoom: 'readonly',
authFetch: 'readonly',
initServerInfo: 'readonly',
log: 'readonly',
debug: 'readonly',
error: 'readonly',
@ -93,6 +95,7 @@ const jsConfig = defineConfig([
initGPU: 'readonly',
startGPU: 'readonly',
disableNVML: 'readonly',
hash: 'readonly',
idbGet: 'readonly',
idbPut: 'readonly',
idbDel: 'readonly',
@ -127,6 +130,7 @@ const jsConfig = defineConfig([
camelcase: 'off',
'default-case': 'off',
'max-classes-per-file': 'warn',
'guard-for-in': 'off',
'no-await-in-loop': 'off',
'no-bitwise': 'off',
'no-continue': 'off',
@ -144,6 +148,7 @@ const jsConfig = defineConfig([
'prefer-rest-params': 'off',
'prefer-template': 'warn',
'promise/no-nesting': 'off',
'@typescript-eslint/no-for-in-array': 'off',
radix: 'off',
'@stylistic/brace-style': [
'error',
@ -333,6 +338,7 @@ export default defineConfig([
'**/exifr.js',
'**/jquery.js',
'**/sparkline.js',
'**/sha256.js',
'**/iframeResizer.min.js',
]),
...jsConfig,

@ -1 +1 @@
Subproject commit 2a7005fbcf8985644b66121365fa7228a65f34b0
Subproject commit d4eab2166e4d9b52e42924cc942198f9e22eb916

@ -1 +1 @@
Subproject commit ddf821483c8bcdac4868f15a9a838b45b4dd30ad
Subproject commit 006f08f499bbe69c484f0f1cc332bbf0e75526c2

@ -1 +1 @@
Subproject commit 79cae1944646e57cfbfb126a971a04e44e45d776
Subproject commit 1e840033b040d8915ddfb5dbf62c80f411bcec0a

@ -1 +1 @@
Subproject commit 357985697de0a457e401bccbc41132322648d0ba
Subproject commit c7af727f31758c9fc96cf0429bcf3608858a15e8

@ -1 +0,0 @@
Subproject commit 3ec8f9eb796be519a98de985bac03645d0040ede

View File

@ -4,46 +4,6 @@
#licenses pre { margin: 1em 0 2em 0;}
</style>
<h2><a href="https://github.com/sczhou/CodeFormer/blob/master/LICENSE">CodeFormer</a></h2>
<small>Parts of CodeFormer code had to be copied to be compatible with GFPGAN.</small>
<pre>
S-Lab License 1.0
Copyright 2022 S-Lab
Redistribution and use for non-commercial purpose in source and
binary forms, with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
In the event that redistribution and/or use for commercial purpose in
source or binary forms, with or without modification is required,
please contact the contributor(s) of the work.
</pre>
<h2><a href="https://github.com/victorca25/iNNfer/blob/main/LICENSE">ESRGAN</a></h2>
<small>Code for architecture and reading models copied.</small>

11298
html/locale_ar.json Normal file

File diff suppressed because it is too large Load Diff

11298
html/locale_bn.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

11298
html/locale_he.json Normal file

File diff suppressed because it is too large Load Diff

11298
html/locale_hi.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

11298
html/locale_id.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

11298
html/locale_nb.json Normal file

File diff suppressed because it is too large Load Diff

11298
html/locale_po.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

11298
html/locale_qq.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

11298
html/locale_sr.json Normal file

File diff suppressed because it is too large Load Diff

11298
html/locale_tb.json Normal file

File diff suppressed because it is too large Load Diff

11298
html/locale_tlh.json Normal file

File diff suppressed because it is too large Load Diff

11298
html/locale_tr.json Normal file

File diff suppressed because it is too large Load Diff

11298
html/locale_ur.json Normal file

File diff suppressed because it is too large Load Diff

11298
html/locale_vi.json Normal file

File diff suppressed because it is too large Load Diff

11298
html/locale_xx.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

7
html/logo-dark.svg Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<!-- Generator: Adobe Illustrator 30.2.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 105) -->
<path d="M333.62,470.26l-9.4,4.63-39.05-21.52,16.16,32.8-9.4,4.63-24.4-49.52,9.4-4.63,39.09,21.59-16.19-32.87,9.4-4.63,24.4,49.52h-.01ZM442.86,365.07h0ZM334.71,418.37l6.05,12.27h.01l22.32-11.01,4.03,8.18-22.32,11,6.26,12.7,38.07-18.76,3.55-23.51-18.59-10.28-39.38,19.41h0ZM457.12,338.1l-205,101,31.64,65.17,205.12-100.53-31.76-65.64h0ZM423.12,426.16l-21.57-12.47-2.75,24.46-11.34,5.59h0l-41.79,20.59-24.4-49.52,43.06-21.21.19.07-.16-.09h-.01.01l11.54-5.68,19.04,10.95,2.2-21.42,11.34-5.59-4.76,31.42,30.96,17.21-11.55,5.69h-.01ZM433.46,369.7l-14.85,7.32-4.03-8.18,39.11-19.27,4.03,8.18-14.86,7.32,20.37,41.34-9.4,4.63-20.37-41.34h0Z" fill="#231f20"/>
<polygon points="248.11 427.73 453.11 326.73 453.11 113.73 248.11 7.73 248.11 117.73 333.11 167.73 333.11 267.73 248.11 317.73 248.11 427.73" fill="#231f20"/>
<polygon points="23.11 150.73 23.11 275.73 103.11 319.73 103.11 331.73 23.11 375.73 23.11 500.73 228.11 386.73 228.11 261.73 143.11 221.73 143.11 209.73 228.11 161.73 228.11 36.73 23.11 150.73" fill="#231f20"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

7
html/logo-light.svg Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<!-- Generator: Adobe Illustrator 30.2.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 105) -->
<path d="M333.62,470.26l-9.4,4.63-39.05-21.52,16.16,32.8-9.4,4.63-24.4-49.52,9.4-4.63,39.09,21.59-16.19-32.87,9.4-4.63,24.4,49.52h-.01ZM442.86,365.07h0ZM334.71,418.37l6.05,12.27h.01l22.32-11.01,4.03,8.18-22.32,11,6.26,12.7,38.07-18.76,3.55-23.51-18.59-10.28-39.38,19.41h0ZM457.12,338.1l-205,101,31.64,65.17,205.12-100.53-31.76-65.64h0ZM423.12,426.16l-21.57-12.47-2.75,24.46-11.34,5.59h0l-41.79,20.59-24.4-49.52,43.06-21.21.19.07-.16-.09h-.01.01l11.54-5.68,19.04,10.95,2.2-21.42,11.34-5.59-4.76,31.42,30.96,17.21-11.55,5.69h-.01ZM433.46,369.7l-14.85,7.32-4.03-8.18,39.11-19.27,4.03,8.18-14.86,7.32,20.37,41.34-9.4,4.63-20.37-41.34h0Z" fill="#fff"/>
<polygon points="248.11 427.73 453.11 326.73 453.11 113.73 248.11 7.73 248.11 117.73 333.11 167.73 333.11 267.73 248.11 317.73 248.11 427.73" fill="#fff"/>
<polygon points="23.11 150.73 23.11 275.73 103.11 319.73 103.11 331.73 23.11 375.73 23.11 500.73 228.11 386.73 228.11 261.73 143.11 221.73 143.11 209.73 228.11 161.73 228.11 36.73 23.11 150.73" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

19
html/logo-robot.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 473 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

BIN
html/screenshot-robot.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

File diff suppressed because it is too large Load Diff

View File

@ -24,9 +24,9 @@ async function authFetch(url, options = {}) {
let res;
try {
res = await fetch(url, options);
if (!res.ok) error('fetch', { status: res.status, url, user, token });
if (!res.ok) error('fetch', { status: res?.status || 503, url, user, token });
} catch (err) {
error('fetch', { status: res.status, url, user, token, error: err });
error('fetch', { status: res?.status || 503, url, user, token, error: err });
}
return res;
}

View File

@ -1,7 +1,7 @@
String.prototype.format = function (args) { // eslint-disable-line no-extend-native, func-names
let thisString = '';
for (let charPos = 0; charPos < this.length; charPos++) thisString += this[charPos];
for (const key in args) { // eslint-disable-line guard-for-in
for (const key in args) {
const stringKey = `{${key}}`;
thisString = thisString.replace(new RegExp(stringKey, 'g'), args[key]);
}
@ -11,6 +11,9 @@ String.prototype.format = function (args) { // eslint-disable-line no-extend-nat
let selectedURL = '';
let selectedName = '';
let selectedType = '';
let selectedBase = '';
let selectedModelId = '';
let selectedVersionId = '';
function clearModelDetails() {
const el = gradioApp().getElementById('model-details') || gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
@ -84,7 +87,7 @@ async function modelCardClick(id) {
data = data[0]; // assuming the first item is the one we want
const versionsHTML = data.versions.map((v) => modelVersionsHTML.format({
url: `<div class="link" onclick="startCivitDownload('${v.files[0]?.url}', '${v.files[0]?.name}', '${data.type}')"> \udb80\uddda </div>`,
url: `<div class="link" onclick="startCivitDownload('${v.files[0]?.url}', '${v.files[0]?.name}', '${data.type}', '${v.base || ''}', ${data.id}, ${v.id})"> \udb80\uddda </div>`,
name: v.name || 'unknown',
type: v.files[0]?.type || 'unknown',
base: v.base || 'unknown',
@ -113,11 +116,14 @@ async function modelCardClick(id) {
el.innerHTML = modelHTML;
}
function startCivitDownload(url, name, type) {
log('startCivitDownload', { url, name, type });
function startCivitDownload(url, name, type, base, modelId, versionId) {
log('startCivitDownload', { url, name, type, base, modelId, versionId });
selectedURL = [url];
selectedName = [name];
selectedType = [type];
selectedBase = [base || ''];
selectedModelId = [modelId || 0];
selectedVersionId = [versionId || 0];
const civitDownloadBtn = gradioApp().getElementById('civitai_download_btn');
if (civitDownloadBtn) civitDownloadBtn.click();
}
@ -128,20 +134,44 @@ function startCivitAllDownload(evt) {
selectedURL = [];
selectedName = [];
selectedType = [];
selectedBase = [];
selectedModelId = [];
selectedVersionId = [];
for (const version of versions) {
const parsed = version.querySelector('td:nth-child(1) div')?.getAttribute('onclick')?.match(/startCivitDownload\('([^']+)', '([^']+)', '([^']+)'\)/);
if (!parsed || parsed.length < 4) continue;
const parsed = version.querySelector('td:nth-child(1) div')?.getAttribute('onclick')?.match(/startCivitDownload\('([^']+)', '([^']+)', '([^']+)', '([^']*)', (\d+), (\d+)\)/);
if (!parsed || parsed.length < 7) continue;
selectedURL.push(parsed[1]);
selectedName.push(parsed[2]);
selectedType.push(parsed[3]);
selectedBase.push(parsed[4]);
selectedModelId.push(parseInt(parsed[5], 10));
selectedVersionId.push(parseInt(parsed[6], 10));
}
const civitDownloadBtn = gradioApp().getElementById('civitai_download_btn');
if (civitDownloadBtn) civitDownloadBtn.click();
}
function downloadCivitModel(modelUrl, modelName, modelType, modelPath, civitToken, innerHTML) {
log('downloadCivitModel', { modelUrl, modelName, modelType, modelPath, civitToken });
function downloadCivitModel(modelUrl, modelName, modelType, modelBase, mId, vId, modelPath, civitToken, innerHTML) {
log('downloadCivitModel', { modelUrl, modelName, modelType, modelBase, mId, vId, modelPath, civitToken });
const el = gradioApp().getElementById('civitai_models_output') || gradioApp().getElementById('models_outcome');
const currentHTML = el?.innerHTML || '';
return [selectedURL, selectedName, selectedType, modelPath, civitToken, currentHTML];
return [selectedURL, selectedName, selectedType, selectedBase, selectedModelId, selectedVersionId, modelPath, civitToken, currentHTML];
}
let civitMutualExcludeBound = false;
function civitaiMutualExclude() {
if (civitMutualExcludeBound) return;
const searchEl = gradioApp().querySelector('#civit_search_text textarea');
const tagEl = gradioApp().querySelector('#civit_search_tag textarea');
if (!searchEl || !tagEl) return;
civitMutualExcludeBound = true;
searchEl.addEventListener('input', () => {
tagEl.closest('.gradio-textbox')?.classList.toggle('disabled-look', !!searchEl.value.trim());
});
tagEl.addEventListener('input', () => {
searchEl.closest('.gradio-textbox')?.classList.toggle('disabled-look', !!tagEl.value.trim());
});
}
onUiLoaded(civitaiMutualExclude);

View File

@ -171,6 +171,12 @@ async function filterExtraNetworksForTab(searchTerm) {
.toLowerCase()
.includes('quantized') ? '' : 'none';
});
} else if (searchTerm === 'nunchaku/') {
cards.forEach((elem) => {
elem.style.display = elem.dataset.tags
.toLowerCase()
.includes('nunchaku') ? '' : 'none';
});
} else if (searchTerm === 'local/') {
cards.forEach((elem) => {
elem.style.display = elem.dataset.name

View File

@ -5,10 +5,7 @@ let currentImage = null;
let currentGalleryFolder = null;
let pruneImagesTimer;
let outstanding = 0;
let lastSort = 0;
let lastSortName = 'None';
let gallerySelection = { files: [], index: -1 };
const galleryHashes = new Set();
let maintenanceController = new AbortController();
const folderStylesheet = new CSSStyleSheet();
const fileStylesheet = new CSSStyleSheet();
@ -26,6 +23,31 @@ const el = {
const SUPPORTED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'tiff', 'jp2', 'jxl', 'gif', 'mp4', 'mkv', 'avi', 'mjpeg', 'mpg', 'avr'];
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 },
none: { name: 'None', func: undefined },
};
let sortMode = gallerySorter.none;
async function getHash(str) {
let hex = '';
const strBuf = new TextEncoder().encode(str);
let hashBuf;
if (crypto?.subtle?.digest) hashBuf = await crypto.subtle.digest('SHA-256', strBuf);
else hashBuf = hash(strBuf).buffer; // from sha256.js
const view = new DataView(hashBuf);
for (let i = 0; i < hashBuf.byteLength; i += 4) hex += (`00000000${view.getUint32(i).toString(16)}`).slice(-8);
return hex;
}
function getVisibleGalleryFiles() {
if (!el.files) return [];
return Array.from(el.files.children).filter((node) => node.name && node.offsetParent);
@ -100,7 +122,8 @@ async function awaitForOutstanding(num, signal) {
* @param {AbortSignal} signal - AbortController signal
*/
async function awaitForGallery(expectedSize, signal) {
while (galleryHashes.size < expectedSize && !signal.aborted) await new Promise((resolve) => { setTimeout(resolve, 500); }); // longer interval because it's a low priority check
// eslint-disable-next-line no-use-before-define
while (Math.max(galleryHashes.size, galleryHashes.fallback) < expectedSize && !signal.aborted) await new Promise((resolve) => { setTimeout(resolve, 500); }); // longer interval because it's a low priority check
signal.throwIfAborted();
}
@ -174,6 +197,25 @@ function updateGalleryStyles() {
// Classes
class HashSet extends Set {
constructor(val) {
super(val);
this.fallback = 0;
}
add(value) {
++this.fallback;
super.add(value);
}
clear() {
this.fallback = 0;
super.clear();
}
}
const galleryHashes = new HashSet();
class SimpleProgressBar {
#container = document.createElement('div');
#progress = document.createElement('div');
@ -432,7 +474,11 @@ class GalleryFile extends HTMLElement {
}
}
this.hash = await getHash(`${this.src}/${this.size}/${this.mtime}`); // eslint-disable-line no-use-before-define
this.hash = await getHash(`${this.src}/${this.size}/${this.mtime}`)
.catch((err) => {
error('getHash:', err);
return null;
});
const cachedData = (this.hash && opts.browser_cache) ? await idbGet(this.hash).catch(() => undefined) : undefined;
const img = document.createElement('img');
img.className = 'gallery-file';
@ -467,7 +513,7 @@ class GalleryFile extends HTMLElement {
this.height = json.height;
this.size = json.size;
this.mtime = new Date(json.mtime);
if (opts.browser_cache) {
if (opts.browser_cache && this.hash) {
await idbAdd({
hash: this.hash,
folder: this.fullFolder,
@ -642,19 +688,6 @@ async function addSeparators() {
const gallerySendImage = (_images) => [currentImage]; // invoked by gradio button
async function getHash(str, algo = 'SHA-256') {
try {
let hex = '';
const strBuf = new TextEncoder().encode(str);
const hash = await crypto.subtle.digest(algo, strBuf);
const view = new DataView(hash);
for (let i = 0; i < hash.byteLength; i += 4) hex += (`00000000${view.getUint32(i).toString(16)}`).slice(-8);
return hex;
} catch {
return undefined;
}
}
/**
* Helper function to update status with sort mode
* @param {...string|[string, string]} messages - Each can be either a string to use as-is, or an array of a string label and value
@ -662,7 +695,7 @@ async function getHash(str, algo = 'SHA-256') {
*/
function updateStatusWithSort(...messages) {
if (!el.status) return;
messages.unshift(['Sort', lastSortName]);
messages.unshift(['Sort', sortMode.name]);
const fragment = document.createDocumentFragment();
for (let i = 0; i < messages.length; i++) {
const div = document.createElement('div');
@ -837,11 +870,14 @@ const findDuplicates = (arr, key) => {
});
};
async function gallerySort(btn) {
async function gallerySort(key) {
if (!Object.hasOwn(gallerySorter, key)) {
error(`Gallery: "${key}" is not a valid gallery sorting key`);
return;
}
const t0 = performance.now();
const arr = Array.from(el.files.children).filter((node) => node.name); // filter out separators
if (arr.length === 0) return; // no files to sort
if (btn) lastSort = btn.charCodeAt(0);
const fragment = document.createDocumentFragment();
// Helper to get directory path from a file node
@ -864,60 +900,17 @@ async function gallerySort(btn) {
folderGroups.get(dir).push(file);
}
// Sort function based on current sort mode
let sortFn;
switch (lastSort) {
case 61789: // name asc
lastSortName = 'Name Ascending';
sortFn = (a, b) => a.name.localeCompare(b.name);
break;
case 61790: // name dsc
lastSortName = 'Name Descending';
sortFn = (a, b) => b.name.localeCompare(a.name);
break;
case 61792: // size asc
lastSortName = 'Size Ascending';
sortFn = (a, b) => a.size - b.size;
break;
case 61793: // size dsc
lastSortName = 'Size Descending';
sortFn = (a, b) => b.size - a.size;
break;
case 61794: // resolution asc
lastSortName = 'Resolution Ascending';
sortFn = (a, b) => a.width * a.height - b.width * b.height;
break;
case 61795: // resolution dsc
lastSortName = 'Resolution Descending';
sortFn = (a, b) => b.width * b.height - a.width * a.height;
break;
case 61662:
lastSortName = 'Modified Ascending';
sortFn = (a, b) => a.mtime - b.mtime;
break;
case 61661:
lastSortName = 'Modified Descending';
sortFn = (a, b) => b.mtime - a.mtime;
break;
default:
lastSortName = 'None';
sortFn = null;
break;
}
sortMode = gallerySorter[key];
// Sort root files
if (sortFn) {
rootFiles.sort(sortFn);
}
rootFiles.sort(sortMode.func);
rootFiles.forEach((node) => fragment.appendChild(node));
// Sort folder names alphabetically, then sort files within each folder
const sortedFolderNames = Array.from(folderGroups.keys()).sort((a, b) => a.localeCompare(b));
for (const folderName of sortedFolderNames) {
const files = folderGroups.get(folderName);
if (sortFn) {
files.sort(sortFn);
}
files.sort(sortMode.func);
files.forEach((node) => fragment.appendChild(node));
}
@ -942,7 +935,7 @@ async function gallerySort(btn) {
}
const t1 = performance.now();
log(`gallerySort: char=${lastSort} len=${arr.length} time=${Math.floor(t1 - t0)} sort=${lastSortName}`);
log(`gallerySort: sort=${sortMode.name} len=${arr.length} time=${Math.floor(t1 - t0)}`);
updateStatusWithSort(['Images', arr.length.toLocaleString()], `${iconStopwatch} ${Math.floor(t1 - t0).toLocaleString()}ms`);
refreshGallerySelection();
}
@ -1353,6 +1346,7 @@ async function initGallery() { // triggered on gradio change to monitor when ui
monitorGalleries();
updateFolders();
initGalleryAutoRefresh();
[
'browser_folders',
'outdir_samples',

View File

@ -30,7 +30,7 @@ async function updateGPU() {
const gpuEl = document.getElementById('gpu');
const gpuTable = document.getElementById('gpu-table');
try {
const res = await authFetch(`${window.api}/gpu`);
const res = await authFetch(`${window.api}/gpu-smi`);
if (!res.ok) {
clearInterval(gpuInterval);
gpuEl.style.display = 'none';

View File

@ -82,7 +82,6 @@ async function logMonitor() {
for (const line of lines) addLogLine(line);
if (!logConnected) {
logConnected = true;
monitorConnection();
xhrPost(`${window.api}/log`, { debug: 'connected' });
}
} else {

Some files were not shown because too many files have changed in this diff Show More