diff --git a/.gitattributes b/.gitattributes index 6cf175e7c5a..eaada5155bf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,5 +3,40 @@ # For more info see: https://www.aleksandrhovhannisyan.com/blog/crlf-vs-lf-normalizing-line-endings-in-git/ * text=auto docker/** text eol=lf + +# The frontend formatters read the file on disk, so a CRLF working tree makes `format:check` fail +# for every file -- including ones nobody touched. That leaves contributors on Windows unable to run +# the same check CI runs, which is how formatting errors reach CI in the first place. +# +# Only the working copy changes: these files are already stored with LF (`* text=auto` normalizes on +# commit), so nothing in history is rewritten. +# +# It is not automatic on an existing checkout, and nothing warns you. Git applies `eol=lf` when it +# writes a file, and a pull only writes files whose content changed -- so a CRLF working tree +# becomes a mixed one: the files this revision touched are LF, the thousands it did not stay CRLF, +# and `format:check` keeps failing, now on files nobody edited. Rewrite the rest once, from the +# repository root, with nothing you want to keep uncommitted (this discards working-copy changes; +# `git stash` first if you have any): +# +# git rm --cached -r . +# git reset --hard +# +# `rm --cached` empties the index without touching disk; `reset --hard` then rewrites both from +# HEAD, which is the only step that applies these rules to files whose content did not change. +# `git status` is clean afterwards on a checkout that was already converted. +invokeai/frontend/**/*.ts text eol=lf +invokeai/frontend/**/*.tsx text eol=lf +invokeai/frontend/**/*.mts text eol=lf +invokeai/frontend/**/*.js text eol=lf +invokeai/frontend/**/*.jsx text eol=lf +invokeai/frontend/**/*.mjs text eol=lf +invokeai/frontend/**/*.cjs text eol=lf +invokeai/frontend/**/*.json text eol=lf +invokeai/frontend/**/*.webmanifest text eol=lf +invokeai/frontend/**/*.css text eol=lf +invokeai/frontend/**/*.html text eol=lf +invokeai/frontend/**/*.md text eol=lf +invokeai/frontend/**/*.yaml text eol=lf +invokeai/frontend/**/*.yml text eol=lf tests/test_model_probe/stripped_models/** filter=lfs diff=lfs merge=lfs -text tests/model_identification/stripped_models/** filter=lfs diff=lfs merge=lfs -text diff --git a/.github/AGENTS.md b/.github/AGENTS.md index 9f701a7e33d..976b2b0c1ac 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -1,24 +1,11 @@ -# Agent Instructions - -## Package Management - -This project uses **pnpm** exclusively for package management in the frontend (`invokeai/frontend/web/`). - -- ✅ Use `pnpm` commands (e.g., `pnpm install`, `pnpm run`) -- ❌ Never use `npm` or `yarn` commands -- ❌ Never suggest creating or using `package-lock.json` or `yarn.lock` -- ✅ The lock file is `pnpm-lock.yaml` - -Use the following pnpm commands for typical operations: - -- pnpm -C invokeai/frontend/web install -- pnpm -C invokeai/frontend/web build -- pnpm -C invokeai/frontend/web lint:tsc -- pnpm -C invokeai/frontend/web lint:dpdm -- pnpm -C invokeai/frontend/web lint:eslint -- pnpm -C invokeai/frontend/web lint:prettier - -## Project Structure - -- Backend: Python in `invokeai/` -- Frontend: TypeScript/React in `invokeai/frontend/web/` (uses pnpm) +# CI and contributions + +- Read affected package guidance before changing checks. +- Preserve PR, main-push, merge-group, manual-dispatch, and reusable-workflow events. `github.base_ref` is not always populated outside PRs. +- Change detection must include workflows and relevant shared actions/configuration; test-only and tooling-only changes must trigger owning checks. +- Preserve tool versions, lockfiles, reproducible installs, hardware/platform matrices, and pinned action revisions. +- Prefer package completion commands over duplicating them in YAML. For webv2's `pnpm check:release`, install Chromium and required Linux dependencies first. +- Preserve failure diagnostics with `always()` where appropriate; keep generated review/performance artifacts out of Git. +- Match hooks to CI's Ruff version/configuration; validation must not rewrite formatting. +- Follow [the PR template](pull_request_template.md); keep summaries short and omit subagent counts, tiers, and process narration. +- Workflow changes do not configure hosted branch protection or install local hooks; verify before claiming either. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5750d976313..7f125079502 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -27,4 +27,5 @@ /invokeai/frontend/install @lstein /invokeai/frontend/merge @lstein @blessedcoolant /invokeai/frontend/training @lstein @blessedcoolant -/invokeai/frontend/web @blessedcoolant @lstein @dunkeroni @Pfannkuchensack @jpphoto +/invokeai/frontend/webv1 @blessedcoolant @lstein @dunkeroni @Pfannkuchensack @jpphoto +/invokeai/frontend/webv2 @blessedcoolant @lstein @dunkeroni @Pfannkuchensack @jpphoto diff --git a/.github/actions/install-frontend-deps/action.yml b/.github/actions/install-frontend-deps/action.yml index 1e6d3e6be80..57421c59a4e 100644 --- a/.github/actions/install-frontend-deps/action.yml +++ b/.github/actions/install-frontend-deps/action.yml @@ -1,12 +1,17 @@ name: install frontend dependencies description: Installs frontend dependencies with pnpm, with caching +inputs: + working-directory: + description: Directory to install dependencies in + required: false + default: invokeai/frontend/webv2 runs: using: 'composite' steps: - name: setup node 22 uses: actions/setup-node@v6 with: - node-version: '22' + node-version-file: '.nvmrc' - name: setup pnpm uses: pnpm/action-setup@v6 @@ -30,4 +35,4 @@ runs: - name: install frontend dependencies run: pnpm install --prefer-frozen-lockfile shell: bash - working-directory: invokeai/frontend/web + working-directory: ${{ inputs.working-directory }} diff --git a/.github/pr_labels.yml b/.github/pr_labels.yml index fdf11a470fb..2c258c5c044 100644 --- a/.github/pr_labels.yml +++ b/.github/pr_labels.yml @@ -10,7 +10,8 @@ python: - changed-files: - all-globs-to-any-file: - 'invokeai/**' - - '!invokeai/frontend/web/**' + - '!invokeai/frontend/webv1/**' + - '!invokeai/frontend/webv2/**' python-tests: - changed-files: @@ -56,4 +57,6 @@ frontend-deps: frontend: - changed-files: - - any-glob-to-any-file: 'invokeai/frontend/web/**' + - any-glob-to-any-file: + - 'invokeai/frontend/webv1/**' + - 'invokeai/frontend/webv2/**' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 84633de6ce1..c63bc70eb0b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,6 @@ ## Summary - + ## Related Issues / Discussions @@ -8,16 +8,22 @@ ## QA Instructions - + -## Merge Plan +## Review - + + +## Compatibility / Rollout + + ## Checklist - [ ] _The PR has a short but descriptive title, suitable for a changelog_ -- [ ] _Tests added / updated (if applicable)_ -- [ ] _❗Changes to a redux slice have a corresponding migration_ +- [ ] _Meaningful regression coverage added / updated where needed; obsolete tests/code removed_ +- [ ] _Persisted-state and API changes include required migrations / compatibility validation_ +- [ ] _Relevant performance/efficiency opportunities considered; material claims have evidence_ +- [ ] _Material review findings resolved and relevant checks rerun_ - [ ] _Documentation added / updated (if applicable)_ - [ ] _Updated `What's New` copy (if doing a release after this PR)_ diff --git a/.github/workflows/build-container.yml b/.github/workflows/build-container.yml index cad5c94ca13..f09465ab796 100644 --- a/.github/workflows/build-container.yml +++ b/.github/workflows/build-container.yml @@ -9,7 +9,7 @@ on: - 'invokeai/**' - 'docker/Dockerfile' - 'docker/docker-entrypoint.sh' - - 'workflows/build-container.yml' + - '.github/workflows/build-container.yml' tags: - 'v*.*.*' workflow_dispatch: @@ -100,7 +100,13 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build container - timeout-minutes: 40 + # The rocm image's venv layer is ~7.2GB compressed - roughly 60% larger than + # cuda's and 9x cpu's - and BuildKit gzips a single layer on one thread, so + # the export alone is ~6.5 min on a healthy runner. The whole job normally + # lands at ~17 min, but every phase roughly doubles on a slow runner, which + # blew through the old 40 min cap four times (2026-07-28, 2026-09-17 x2, + # 2026-09-22). + timeout-minutes: 90 id: docker_build uses: docker/build-push-action@v7 with: @@ -109,7 +115,21 @@ jobs: platforms: ${{ env.PLATFORMS }} build-args: | GPU_DRIVER=${{ matrix.gpu-driver }} - push: ${{ github.ref == 'refs/heads/main' || github.ref_type == 'tag' || github.event.inputs.push-to-registry }} + # `outputs` rather than `push` so we can set the compression level: BuildKit + # defaults to gzip -6, which runs at ~19MB/s over that one big layer. gzip -1 + # is 2-3x faster for ~15% larger layers, which is a good trade when the + # bottleneck is CI wall-clock rather than registry bandwidth. An empty + # `outputs` emits no --output at all, which is what a non-pushing build did + # before. + # + # The `== 'true'` is load-bearing. `github.event.inputs.*` is always a string, + # and every non-empty string is truthy, so a bare reference to an unchecked + # boolean input evaluates as TRUE. `push:` got away with it because the action + # ran that input through core.getBooleanInput, which maps "false" to false; + # `outputs:` is read with getInputList and passes any non-empty string straight + # through to --output. Without the comparison, a manual run from a non-main + # branch with the box unchecked would publish that branch to ghcr. + outputs: ${{ (github.ref == 'refs/heads/main' || github.ref_type == 'tag' || github.event.inputs.push-to-registry == 'true') && 'type=image,push=true,compression=gzip,compression-level=1' || '' }} provenance: mode=max sbom: true tags: ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index b1680b71216..88ace849eac 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -78,12 +78,12 @@ jobs: with: version: '0.11.28' # When updating this, make sure the version in launcher's package.json gets updated too. enable-cache: true - python-version: '3.11' + python-version: '3.12' - name: setup python uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.12' # generate_docs_json.py only needs the invokeai package importable # (pydantic + invokeai.app/backend). Skip the [test] extra to keep CI fast. @@ -107,6 +107,10 @@ jobs: working-directory: docs # Checks + - name: test docs tooling + run: pnpm test + working-directory: docs + - name: verify generated docs data run: pnpm run check-docs-data working-directory: docs diff --git a/.github/workflows/frontend-checks.yml b/.github/workflows/frontend-checks.yml index b36fbeb650b..bb00170406e 100644 --- a/.github/workflows/frontend-checks.yml +++ b/.github/workflows/frontend-checks.yml @@ -30,67 +30,49 @@ on: type: boolean default: true -defaults: - run: - working-directory: invokeai/frontend/web +# One run per pull request: a new push cancels the superseded run. The PR number is used rather +# than the head branch, which two forks can share. Pushes to main and merge groups fall back to +# the run id and never cancel each other. The group names the workflow literally +# because `github.workflow` is the caller's name inside a reusable workflow, and release.yml calls +# several of these at once. +concurrency: + group: frontend-checks-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true jobs: - frontend-checks: + frontend-webv2-checks: runs-on: ubuntu-latest timeout-minutes: 10 # expected run time: <2 min + defaults: + run: + working-directory: invokeai/frontend/webv2 steps: - uses: actions/checkout@v6 - - name: Fail if package-lock.json is added/modified (pnpm only) - shell: bash - working-directory: . - run: | - set -euo pipefail - git fetch --no-tags --prune --depth=1 origin "${{ github.base_ref }}" - if git diff --name-only "origin/${{ github.base_ref }}...HEAD" | grep -E '(^|/)package-lock\.json$'; then - echo "::error::package-lock.json was added or modified. This repo uses pnpm only." - exit 1 - fi - - - name: check for changed frontend files + - name: check for changed webv2 files if: ${{ inputs.always_run != true }} id: changed-files - # Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks. - # See: - # - CVE-2025-30066 - # - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised - # - https://github.com/tj-actions/changed-files/issues/2463 uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 with: files_yaml: | - frontend: - - 'invokeai/frontend/web/**' + webv2: + - '.github/workflows/frontend-checks.yml' + - '.nvmrc' + - '.github/actions/install-frontend-deps/**' + - 'invokeai/frontend/webv2/**' - name: install dependencies - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} + if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }} uses: ./.github/actions/install-frontend-deps + with: + working-directory: invokeai/frontend/webv2 - - name: tsc - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:tsc' - shell: bash - - - name: dpdm - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:dpdm' - shell: bash - - - name: eslint - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:eslint' - shell: bash - - - name: prettier - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:prettier' + - name: lint + if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }} + run: 'pnpm run lint' shell: bash - - name: knip - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm lint:knip' + - name: build + if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }} + run: 'pnpm run build' shell: bash diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml index abb1fb8419f..347b2d4d305 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/frontend-tests.yml @@ -30,36 +30,109 @@ on: type: boolean default: true -defaults: - run: - working-directory: invokeai/frontend/web +# One run per pull request: a new push cancels the superseded run. The PR number is used rather +# than the head branch, which two forks can share. Pushes to main and merge groups fall back to +# the run id and never cancel each other. The group names the workflow literally +# because `github.workflow` is the caller's name inside a reusable workflow, and release.yml calls +# several of these at once. +concurrency: + group: frontend-tests-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true jobs: - frontend-tests: + frontend-webv2-tests: runs-on: ubuntu-latest - timeout-minutes: 10 # expected run time: <2 min + timeout-minutes: 20 # Includes browser suites, performance sampling, project files, and accessibility. + defaults: + run: + working-directory: invokeai/frontend/webv2 steps: - uses: actions/checkout@v6 - - name: check for changed frontend files + - name: check for changed webv2 files if: ${{ inputs.always_run != true }} id: changed-files - # Pinned to the _hash_ for v45.0.9 to prevent supply-chain attacks. - # See: - # - CVE-2025-30066 - # - https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised - # - https://github.com/tj-actions/changed-files/issues/2463 uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 with: files_yaml: | - frontend: - - 'invokeai/frontend/web/**' + webv2: + - '.github/workflows/frontend-tests.yml' + - '.nvmrc' + - '.github/actions/install-frontend-deps/**' + - 'invokeai/frontend/webv2/**' - name: install dependencies - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} + if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }} uses: ./.github/actions/install-frontend-deps + with: + working-directory: invokeai/frontend/webv2 + + - name: install Chromium + if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }} + run: 'pnpm exec playwright install --with-deps chromium' + shell: bash + + # A pull request into main, or a merge group, is compared with the base branch's + # own measurements, restored from the cache every push to main fills below: the exact base + # commit when it has an entry, otherwise the most recent main entry (a main job that failed or + # is still running leaves no hole). Anything else compares against the committed baseline. The + # gate also honors deliberately re-recorded byte baselines. It logs which reference it used, + # and the restored key is passed through so reports can name it. + - name: restore performance reference + id: perf-ref + if: ${{ (steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true) && ((github.event_name == 'pull_request' && github.base_ref == 'main') || (github.event_name == 'merge_group' && github.event.merge_group.base_ref == 'refs/heads/main')) }} + uses: actions/cache/restore@v5 + with: + path: invokeai/frontend/webv2/artifacts/perf-reference + key: webv2-perf-reference-${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }} + restore-keys: webv2-perf-reference- + + - name: note a missing performance reference + if: ${{ steps.perf-ref.outcome == 'success' && steps.perf-ref.outputs.cache-matched-key == '' }} + run: 'echo "::warning::No performance reference was restored; byte budgets are compared against the committed baseline."' + shell: bash - - name: vitest - if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }} - run: 'pnpm test:no-watch' + - name: release completion gates + if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }} + run: 'pnpm check:release' shell: bash + env: + WEBV2_PERF_REFERENCE_DIR: ${{ steps.perf-ref.outputs.cache-matched-key != '' && 'artifacts/perf-reference' || '' }} + WEBV2_PERF_REFERENCE_LABEL: ${{ steps.perf-ref.outputs.cache-matched-key }} + + # Recorded even when the gate failed: a main that has outgrown the committed baseline is + # still the right thing for a pull request to be compared with. A main job that ran the gate + # but produced no reference (a failure before the browser stage) records nothing and says so; + # one that left webv2 untouched has nothing to record. Pull requests based on either restore + # the most recent main entry instead. + - name: stage performance reference + id: perf-ref-stage + if: ${{ always() && github.event_name == 'push' && github.ref == 'refs/heads/main' && (steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true) }} + run: | + if [ -f artifacts/architecture-performance/build-reference.json ] && [ -f artifacts/architecture-performance/browser-reference.json ]; then + mkdir -p artifacts/perf-reference + cp artifacts/architecture-performance/build-reference.json artifacts/architecture-performance/browser-reference.json artifacts/perf-reference/ + echo "ready=true" >> "$GITHUB_OUTPUT" + else + echo "ready=false" >> "$GITHUB_OUTPUT" + echo "::warning::No performance reference recorded for ${{ github.sha }}; pull requests based on it fall back to the most recent main reference." + fi + shell: bash + + - name: save performance reference + if: ${{ always() && github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.perf-ref-stage.outputs.ready == 'true' }} + uses: actions/cache/save@v5 + with: + path: invokeai/frontend/webv2/artifacts/perf-reference + key: webv2-perf-reference-${{ github.sha }} + + - name: upload architecture review artifacts + if: ${{ always() && (steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true) }} + uses: actions/upload-artifact@v6 + with: + name: webv2-architecture-review + path: | + invokeai/frontend/webv2/artifacts/architecture + invokeai/frontend/webv2/artifacts/architecture-performance + invokeai/frontend/webv2/artifacts/accessibility + if-no-files-found: ignore diff --git a/.github/workflows/openapi-checks.yml b/.github/workflows/openapi-checks.yml index 3160d5a3de0..e4299cf57c4 100644 --- a/.github/workflows/openapi-checks.yml +++ b/.github/workflows/openapi-checks.yml @@ -31,6 +31,15 @@ on: type: boolean default: true +# One run per pull request: a new push cancels the superseded run. The PR number is used rather +# than the head branch, which two forks can share. Pushes to main and merge groups fall back to +# the run id and never cancel each other. The group names the workflow literally +# because `github.workflow` is the caller's name inside a reusable workflow, and release.yml calls +# several of these at once. +concurrency: + group: openapi-checks-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + jobs: openapi-checks: env: @@ -69,7 +78,14 @@ jobs: files_yaml: | src: - 'pyproject.toml' + - '.github/workflows/openapi-checks.yml' + - '.nvmrc' + - '.github/actions/install-frontend-deps/**' + - 'scripts/generate_openapi_schema.py' - 'invokeai/**' + # Shared contracts are generated from Python; neither UI package owns these artifacts. + - '!invokeai/frontend/webv1/**' + - '!invokeai/frontend/webv2/**' - name: setup uv if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} @@ -77,13 +93,13 @@ jobs: with: version: '0.11.28' # When updating this, make sure the version in launcher's package.json gets updated too. enable-cache: true - python-version: '3.11' + python-version: '3.12' - name: setup python if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: install dependencies if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} @@ -94,21 +110,23 @@ jobs: - name: install frontend dependencies if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} uses: ./.github/actions/install-frontend-deps + with: + working-directory: invokeai/frontend/api - name: copy schema if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} - run: cp invokeai/frontend/web/openapi.json invokeai/frontend/web/openapi_orig.json + run: cp invokeai/frontend/api/openapi.json invokeai/frontend/api/openapi_orig.json shell: bash - name: generate schema if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} - run: cd invokeai/frontend/web && uv run ../../../scripts/generate_openapi_schema.py > openapi.json && pnpm prettier --write openapi.json + run: cd invokeai/frontend/api && uv run ../../../scripts/generate_openapi_schema.py > openapi.json && pnpm format:openapi shell: bash - name: compare files if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} run: | - if ! diff invokeai/frontend/web/openapi.json invokeai/frontend/web/openapi_orig.json; then + if ! diff invokeai/frontend/api/openapi.json invokeai/frontend/api/openapi_orig.json; then echo "Files are different!"; exit 1; fi diff --git a/.github/workflows/python-checks.yml b/.github/workflows/python-checks.yml index 091de818ed8..388a9e4b6e1 100644 --- a/.github/workflows/python-checks.yml +++ b/.github/workflows/python-checks.yml @@ -32,6 +32,15 @@ on: type: boolean default: true +# One run per pull request: a new push cancels the superseded run. The PR number is used rather +# than the head branch, which two forks can share. Pushes to main and merge groups fall back to +# the run id and never cancel each other. The group names the workflow literally +# because `github.workflow` is the caller's name inside a reusable workflow, and release.yml calls +# several of these at once. +concurrency: + group: python-checks-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + jobs: python-checks: env: @@ -55,10 +64,11 @@ jobs: with: files_yaml: | python: + # ruff config and the classifier check both read pyproject.toml. - 'pyproject.toml' - - 'invokeai/**' - - '!invokeai/frontend/web/**' - - 'tests/**' + - '.github/workflows/python-checks.yml' + # ruff lints every tracked .py file from the repository root, wherever it lives. + - '**/*.py' - name: setup uv if: ${{ steps.changed-files.outputs.python_any_changed == 'true' || inputs.always_run == true }} diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 25b749295e2..c39e9a3da3b 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -30,16 +30,22 @@ on: type: boolean default: true +# One run per pull request: a new push cancels the superseded run. The PR number is used rather +# than the head branch, which two forks can share. Pushes to main and merge groups fall back to +# the run id and never cancel each other. The group names the workflow literally +# because `github.workflow` is the caller's name inside a reusable workflow, and release.yml calls +# several of these at once. concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + group: python-tests-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true jobs: matrix: strategy: + # Let the other platforms finish when one runner fails, so a rerun repeats only the failed job. + fail-fast: false matrix: python-version: - - '3.11' - '3.12' platform: - linux-cpu @@ -50,17 +56,37 @@ jobs: os: ubuntu-24.04 extra-index-url: 'https://download.pytorch.org/whl/cpu' github-env: $GITHUB_ENV + pytest-workers: logical + timeout-minutes: 30 - platform: macos-default os: macOS-14 github-env: $GITHUB_ENV + pytest-workers: logical + timeout-minutes: 30 - platform: windows-cpu os: windows-2022 github-env: $env:GITHUB_ENV + # Half the vCPUs, not one worker each. The tests that spawn helper processes -- + # ffmpeg decoders, installer subprocesses -- bring their own threads, which the + # per-worker `OMP_NUM_THREADS=1` below does not cap, so a full-width run leaves this + # runner with no spare core and the deadline-sensitive tests miss their budgets + # non-deterministically. Two workers keep that headroom. + pytest-workers: '2' + # Roughly 1.5x the pytest step's four-worker wall time (~13.5 min), plus the job's + # ~1.5 min of checkout and install, plus margin. + timeout-minutes: 45 name: 'py${{ matrix.python-version }}: ${{ matrix.platform }}' runs-on: ${{ matrix.os }} - timeout-minutes: 30 # expected run time: 10-16 min, depending on platform + # Per platform: expected run time is 8-14 min on linux and macos, longer on windows, which + # runs fewer workers. See the matrix. + timeout-minutes: ${{ matrix.timeout-minutes }} env: PIP_USE_PEP517: '1' + # One compute thread per worker. Torch otherwise sizes its thread pools to the whole + # machine, so one worker per vCPU puts vCPUs^2 threads on a runner that fight for the same + # cores. This caps torch only; helper processes the tests spawn thread themselves. + OMP_NUM_THREADS: '1' + MKL_NUM_THREADS: '1' steps: - name: checkout @@ -80,8 +106,29 @@ jobs: files_yaml: | python: - 'pyproject.toml' + # The lockfile pins pytest-xdist, which this workflow's pytest command depends on. + - 'uv.lock' + - '.github/workflows/python-tests.yml' - 'invokeai/**' - - '!invokeai/frontend/web/**' + - '!invokeai/frontend/webv1/**' + - '!invokeai/frontend/webv2/**' + # Re-includes must follow the exclusions above. + # test_imports.py walks every package, including the frontend ones. + - 'invokeai/frontend/webv1/**/*.py' + - 'invokeai/frontend/webv2/**/*.py' + # test_workflow_graph_builder.py validates these editor fixtures against the backend. + - 'invokeai/frontend/webv2/src/features/workflow/core/fixtures/**' + # Cross-stack generation contracts: the frontend writes these, Python validates them + # against the invocation and architecture registries. A frontend-only change that + # regenerates one would otherwise pass vitest while skipping the backend half. + # `__fixtures__/` does not exist yet -- it arrives with the graph-builder fixtures. + # A pattern matching nothing costs nothing here, and listing it now means the branch + # that adds the directory does not also have to remember to add this line. + - 'invokeai/frontend/webv2/src/features/generation/core/__snapshots__/**' + - 'invokeai/frontend/webv2/src/features/generation/core/__fixtures__/**' + - 'invokeai/frontend/webv2/src/features/video/core/__snapshots__/**' + # test_ideogram4_frontend_bounds.py reads the Ideogram 4 control bounds from here. + - 'invokeai/frontend/webv2/src/features/generation/core/settings.ts' - 'tests/**' # tests/ covers a few of these (e.g. test_check_pins.py, test_docs_json_export.py) - 'scripts/**' @@ -103,4 +150,8 @@ jobs: - name: run pytest if: ${{ steps.changed-files.outputs.python_any_changed == 'true' || inputs.always_run == true }} - run: uv run --no-sync pytest + # `logical`, not `auto`: xdist's `auto` asks psutil for *physical* cores, which is half + # the vCPUs a standard runner exposes. Much of the suite waits on timers and polls, so + # one worker per vCPU is the right level where the runner has the headroom for it. The + # matrix carries the count, because windows needs a lower one. + run: uv run --no-sync pytest -n ${{ matrix.pytest-workers }} --max-worker-restart=0 diff --git a/.github/workflows/typegen-checks.yml b/.github/workflows/typegen-checks.yml index 7bea61680a7..89227660fdf 100644 --- a/.github/workflows/typegen-checks.yml +++ b/.github/workflows/typegen-checks.yml @@ -31,6 +31,15 @@ on: type: boolean default: true +# One run per pull request: a new push cancels the superseded run. The PR number is used rather +# than the head branch, which two forks can share. Pushes to main and merge groups fall back to +# the run id and never cancel each other. The group names the workflow literally +# because `github.workflow` is the caller's name inside a reusable workflow, and release.yml calls +# several of these at once. +concurrency: + group: typegen-checks-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + jobs: typegen-checks: env: @@ -69,7 +78,14 @@ jobs: files_yaml: | src: - 'pyproject.toml' + - '.github/workflows/typegen-checks.yml' + - '.nvmrc' + - '.github/actions/install-frontend-deps/**' + - 'scripts/generate_openapi_schema.py' - 'invokeai/**' + # Shared contracts are generated from Python; neither UI package owns these artifacts. + - '!invokeai/frontend/webv1/**' + - '!invokeai/frontend/webv2/**' - name: setup uv if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} @@ -77,13 +93,13 @@ jobs: with: version: '0.11.28' # When updating this, make sure the version in launcher's package.json gets updated too. enable-cache: true - python-version: '3.11' + python-version: '3.12' - name: setup python if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.12' - name: install dependencies if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} @@ -94,21 +110,33 @@ jobs: - name: install frontend dependencies if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} uses: ./.github/actions/install-frontend-deps + with: + working-directory: invokeai/frontend/api + + - name: lint contract package + if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} + run: pnpm -C invokeai/frontend/api lint + shell: bash + + - name: test contract generation + if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} + run: pnpm -C invokeai/frontend/api test + shell: bash - name: copy schema if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} - run: cp invokeai/frontend/web/src/services/api/schema.ts invokeai/frontend/web/src/services/api/schema_orig.ts + run: cp invokeai/frontend/api/schema.ts invokeai/frontend/api/schema_orig.ts shell: bash - name: generate schema if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} - run: cd invokeai/frontend/web && uv run ../../../scripts/generate_openapi_schema.py | pnpm typegen + run: set -o pipefail; cd invokeai/frontend/api && uv run ../../../scripts/generate_openapi_schema.py | pnpm typegen shell: bash - name: compare files if: ${{ steps.changed-files.outputs.src_any_changed == 'true' || inputs.always_run == true }} run: | - if ! diff invokeai/frontend/web/src/services/api/schema.ts invokeai/frontend/web/src/services/api/schema_orig.ts; then + if ! diff invokeai/frontend/api/schema.ts invokeai/frontend/api/schema_orig.ts; then echo "Files are different!"; exit 1; fi diff --git a/.github/workflows/uv-lock-checks.yml b/.github/workflows/uv-lock-checks.yml index 5f7d71e34f0..e64cef1297c 100644 --- a/.github/workflows/uv-lock-checks.yml +++ b/.github/workflows/uv-lock-checks.yml @@ -34,6 +34,15 @@ on: type: boolean default: true +# One run per pull request: a new push cancels the superseded run. The PR number is used rather +# than the head branch, which two forks can share. Pushes to main and merge groups fall back to +# the run id and never cancel each other. The group names the workflow literally +# because `github.workflow` is the caller's name inside a reusable workflow, and release.yml calls +# several of these at once. +concurrency: + group: uv-lock-checks-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + jobs: uv-lock-checks: env: diff --git a/.gitignore b/.gitignore index 33967877853..bcd02dde490 100644 --- a/.gitignore +++ b/.gitignore @@ -135,6 +135,17 @@ env/ venv/ ENV/ +# Generated stuff +invokeai.yaml +invokeai.example.yaml + +/models/ +/databases/ +/configs/ +/nodes/ +/outputs/ +/fonts/ + # Spyder project settings .spyderproject .spyproject @@ -169,7 +180,7 @@ cython_debug/ .DS_Store # Let the frontend manage its own gitignore -!invokeai/frontend/web/* +!invokeai/frontend/webv1/* # Scratch folder .scratch/ @@ -187,6 +198,14 @@ installer/InvokeAI-Installer/ .aider* .claude/ +.superpowers/ + +# Temporary agent planning artifacts. +**/.agents/plans/ +**/.codex/plans/ +**/.claude/plans/ +**/docs/plans/ +**/docs/superpowers/ # Weblate configuration file weblate.ini diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f128557bc08..0ff644f3b6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,25 +2,18 @@ repos: - repo: local hooks: - - id: black - name: black + - id: ruff-check + name: ruff check stages: [pre-commit] language: system - entry: black + entry: uv tool run ruff@0.11.2 check --force-exclude types: [python] - - id: flake8 - name: flake8 + - id: ruff-format + name: ruff format check stages: [pre-commit] language: system - entry: flake8 - types: [python] - - - id: isort - name: isort - stages: [pre-commit] - language: system - entry: isort + entry: uv tool run ruff@0.11.2 format --check --force-exclude types: [python] - id: uvlock @@ -29,4 +22,4 @@ repos: language: system entry: uv lock files: ^pyproject\.toml$ - pass_filenames: false \ No newline at end of file + pass_filenames: false diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..ce309621bdc --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,87 @@ +# Working on InvokeAI + +## Communication + +- Give plain, concise updates led by outcomes, blockers, and evidence. Distinguish verified results, assumptions, and unavailable checks. +- Ask only when a decision cannot be inferred safely from the repository or request; continue authorized work without repeated permission requests. + +## Find the right owner + +Before editing, read `AGENTS.md` along each target path; scoped rules supplement this file. Keep policy in `AGENTS.md`. + +| Area | Guidance and source of truth | +| --- | --- | +| Active frontend | [webv2/AGENTS.md](invokeai/frontend/webv2/AGENTS.md), its `ARCHITECTURE.md` and executable architecture policy | +| API, services, invocations | [app/AGENTS.md](invokeai/app/AGENTS.md) | +| Inference and model management | [backend/AGENTS.md](invokeai/backend/AGENTS.md) | +| Python tests | [tests/AGENTS.md](tests/AGENTS.md); also read guidance for the production code under test | +| Legacy frontend | [webv1/AGENTS.md](invokeai/frontend/webv1/AGENTS.md) | +| Shared generated API contracts | [api/AGENTS.md](invokeai/frontend/api/AGENTS.md) | +| CI and contribution tooling | [.github/AGENTS.md](.github/AGENTS.md) | +| Documentation | `docs/README.md` and `docs/package.json`; keep documentation about the implemented product current | + +Ordinary frontend work targets **webv2**, served by default; `--webv2` remains a compatibility alias and `--web-legacy` selects legacy web. Verify build/launch targets for UI investigations. The shared `frontend/api` package owns generated OpenAPI/type artifacts. + +## Engineering standard + +- Inspect implementations, callers, contracts, and tests before designing changes. Define observable outcomes and validation; reproduce bugs where feasible, fix root causes, and reconsider designs that accumulate patches. +- Keep ownership and interfaces clear. Account for edge cases, failures, data lifecycles, and operational limits. +- Every function, dependency, fallback, and test must earn its place. Add abstractions to translate contracts, isolate dependencies, own lifecycles, prevent drift, or simplify callers; avoid pass-through wrappers, speculative extensibility, redundant validation/configuration, and abstractions justified only by similar-looking code. +- Remove obsolete code, tests, and scaffolding; preserve unrelated work. Comment only non-obvious intent, constraints, or trade-offs. +- Test meaningful behavior through its owning interface with independent expectations that catch plausible regressions. Avoid trivial constant/type/re-export assertions, incidental snapshots, duplicate coverage, and mock choreography. Prefer real lightweight dependencies and isolated fixtures; use doubles for expensive/external systems. Optimize test value, not count. +- Keep dependency/lockfile changes intentional. Never weaken assertions, gates, architecture policy, or performance budgets to pass; explain existing failures and verification limits. +- Keep architecture/reference docs aligned with implementation. Skills are optional helpers: adapt them to this stack without importing personal paths or generic ceremony. + +## Performance and efficiency + +- Inspect affected hot paths and immediate callers for repeated work, renders/subscriptions, waterfalls, queries, serialization/copies, unbounded collections, and retained resources. +- Implement clear, low-risk improvements in scope; larger optimizations must serve the task. Briefly note unrelated opportunities without expanding scope. +- Measure nontrivial optimizations and performance claims with existing budgets and representative fixtures, query counts, profiling, or focused benchmarks. Report material before/after results and limits. +- Preserve correctness and clarity. Require understood invalidation, lifetimes, ordering, and cost for caching, memoization, concurrency, or batching. Do not invent optimizations or benchmarks for a checklist. + +## Product quality + +Maintain a polished desktop experience: deliberate layouts, responsive interactions, cohesive accessible controls, and complete loading/empty/error/recovery states. Verify changed interactions in the browser; automated checks alone do not establish visual quality. + +## Milestones and code review rules + +A milestone is one coherent, commit-ready unit. Run relevant checks and self-review every change. Choose review depth by the complete task's behavior, blast radius, and failure consequences—not file/line counts or directory alone. Do not split risky work to avoid review. + +| Review depth | When to use it | +| --- | --- | +| **Self-review; no subagents** | Local, low-risk edits with straightforward verification: copy, docs, styling, or an input minimum without shared validation, persistence, or API contract changes. | +| **One independent reviewer** | Substantive features, fixes, or refactors with bounded impact and no high-risk trigger. | +| **Three independent reviewers** | Behavior changes affecting authentication/authorization, account isolation, persisted-data compatibility/migrations, destructive operations, concurrency/resource lifecycles, shared API compatibility, inference numerical/device/memory behavior, or substantial architecture across owners. | + +Review focuses: correctness/spec conformance; architecture/operational safety/performance/unnecessary complexity; test value/gaps/product quality, including UI accessibility. One reviewer covers all; three divide them. User instructions override defaults. Select the tier without asking permission; briefly explain spawning. Required checks and browser verification apply at every tier. + +1. Review once per coherent candidate after checks. Supply the same fixed base, acceptance criteria, and complete task-owned diff (staged, unstaged, new files); identify unrelated changes. Use focused briefs and entry points, not full conversation inheritance. +2. Reviewers inspect independently, including necessary callers/contracts. They are read-only: no edits, commits, or recursive delegation. Findings need locations, impact, and failure scenarios or engineering costs; style preferences alone are not blockers. +3. Resolve all material findings, add useful regression coverage, and rerun affected checks. A clean review is final for an unchanged candidate. For subsequent edits, seek blocker-only follow-up from affected reviewers on changes and consequences. Broaden review and reassess the tier only when scope/risk materially changes. +4. Do not declare readiness with material findings or unexplained failing gates. Report resolved findings, passed/failed/unavailable checks, and remaining limitations. PRs omit subagent counts, tiers, and process narration. + +If required independent review is unavailable, self-review the corresponding focuses and disclose this to the user; ordinary low-risk self-review needs no such disclaimer. Never label self-review independent. Implementation delegation is optional for independently useful tasks with clear ownership. + +## Commands and environments + +- Use **pnpm 10**, never npm/yarn, with package-owned lockfiles and `pnpm -C ...`. Use `.nvmrc`'s Node version; consult package scripts, not root Makefile assumptions about webv2. +- Preserve the existing Python/accelerator environment and `uv.lock`. Fresh test setup: `uv sync --locked --extra test`; do not routinely replace accelerator backends. +- Root Python checks: `uv tool run ruff@0.11.2 check ` and `uv tool run ruff@0.11.2 format --check `. Use 120-column formatting, absolute imports, and configured vendored-code exclusions. +- Focused tests: `uv run --no-sync pytest `; full CI: `uv run --no-sync pytest -n logical`. Hardware/quiet-machine timing tests use `-m slow` on development machines; excluded by default and CI. See `tests/AGENTS.md` for isolation rules. +- `-n logical` is sized for CI runners that own the machine. On a development box cap workers (`-n 4`, or plain `pytest`), set `OMP_NUM_THREADS` when a run must stay light, and do not start a suite while a generation is running. +- `uv run --no-sync mypy scripts/invokeai-web.py` uses current exclusions; it is an optional diagnostic, not a CI gate or comprehensive type coverage. +- Select checks by behavior; run required milestone gates before review. Repeat unchanged checks only for a reason. +- After test dependencies are available, install hooks per checkout: `uv run --no-sync pre-commit install`. Committing configuration does not install hooks. + +## Scratch files and repository hygiene + +- Never commit plans, work logs, investigation notes, review transcripts, or handoffs under any filename. Optional scratch belongs in `.scratch/agents//`; do not relabel it as durable docs. +- Version durable documentation of implemented architecture, decisions, APIs, and setup. +- Inspect staged diffs and new files for planning material. Ignore rules neither remove tracked files nor prevent force-adds. Never force-add planning artifacts or introduce `package-lock.json`/`yarn.lock`. + +## Commits and pull requests + +- Readiness does not authorize commits/pushes; follow existing user authorization without automatic per-subtask commits. +- Use Conventional Commits: short outcome-focused subject, then one to three factual lines of material changes without repetition or process narration. +- Use the configured Git author; no agent attribution or co-author trailers. +- Follow `.github/pull_request_template.md`: ordered headings, all applicable sections and inline guidance, omit only conditional sections, and tick only true checklist items. diff --git a/Makefile b/Makefile index 9830f7167fc..b3a656e9a63 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ # simple Makefile with scripts that are otherwise hard to remember # to use, run from the repo root `make ` +SHELL := /bin/bash + default: help help: @@ -13,8 +15,12 @@ help: @echo "test Run the unit tests." @echo "frontend-install Install the pnpm modules needed for the frontend" @echo "frontend-build Build the frontend for localhost:9090" + @echo "frontend-legacy-install Install legacy frontend dependencies" + @echo "frontend-legacy-build Build the legacy frontend for --web-legacy" @echo "frontend-test Run the frontend test suite once" @echo "frontend-dev Run the frontend in developer mode on localhost:5173" + @echo "frontend-legacy-dev Run the legacy frontend development server" + @echo "frontend-api-install Install shared API generation dependencies" @echo "frontend-openapi Generate the OpenAPI schema" @echo "frontend-typegen Generate types for the frontend from the OpenAPI schema" @echo "frontend-lint Run frontend checks and fixable lint/format steps" @@ -50,36 +56,55 @@ test: # Install the pnpm modules needed for the front end frontend-install: - rm -rf invokeai/frontend/web/node_modules - cd invokeai/frontend/web && pnpm install + rm -rf invokeai/frontend/webv2/node_modules + cd invokeai/frontend/webv2 && pnpm install # Build the frontend frontend-build: - cd invokeai/frontend/web && pnpm build + cd invokeai/frontend/webv2 && pnpm build + +# Compatibility aliases and explicit legacy commands +frontendv2-install: frontend-install + +frontend-legacy-install: + rm -rf invokeai/frontend/webv1/node_modules + cd invokeai/frontend/webv1 && pnpm install + +# Build the legacy frontend +frontendv2-build: frontend-build + +frontend-legacy-build: + cd invokeai/frontend/webv1 && pnpm build # Run the frontend test suite once frontend-test: - cd invokeai/frontend/web && pnpm run test:run + cd invokeai/frontend/webv2 && pnpm test # Run the frontend in dev mode frontend-dev: - cd invokeai/frontend/web && pnpm dev + cd invokeai/frontend/webv2 && pnpm dev + +# Run the legacy frontend in dev mode +frontendv2-dev: frontend-dev + +frontend-legacy-dev: + cd invokeai/frontend/webv1 && pnpm run dev + +# Install the shared contract generation tools +frontend-api-install: + pnpm -C invokeai/frontend/api install --frozen-lockfile # Generate the OpenAPI Schema for the app frontend-openapi: - cd invokeai/frontend/web && \ + cd invokeai/frontend/api && \ python ../../../scripts/generate_openapi_schema.py > openapi.json && \ - pnpm prettier --write openapi.json + pnpm format:openapi frontend-typegen: - cd invokeai/frontend/web && python ../../../scripts/generate_openapi_schema.py | pnpm typegen + set -o pipefail; cd invokeai/frontend/api && python ../../../scripts/generate_openapi_schema.py | pnpm typegen frontend-lint: - cd invokeai/frontend/web/src && \ - pnpm lint:tsc && \ - pnpm lint:dpdm && \ - pnpm lint:eslint --fix && \ - pnpm lint:prettier --write + pnpm -C invokeai/frontend/webv2 lint # Tag the release wheel: diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000000..ab2eb3c0f81 --- /dev/null +++ b/NOTICE @@ -0,0 +1 @@ +Copyright 2022-2026 InvokeAI Contributors diff --git a/README.md b/README.md index f16ae5ad325..bae19b658d6 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ We very much thank the following sponsors: Invoke is a combined effort of [passionate and talented people from across the world][contributors]. We thank them for their time, hard work and effort. -Original portions of the software are Copyright © 2024 by respective contributors. +Copyright 2022-2026 InvokeAI Contributors. See [NOTICE](NOTICE). [features docs]: https://invoke.ai/ [faq]: https://invoke.ai/troubleshooting/faq/ diff --git a/docker/Dockerfile b/docker/Dockerfile index a9a983e3587..f04a840b556 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -8,10 +8,17 @@ ENV PATH="$PNPM_HOME:$PATH" RUN corepack use pnpm@10.x && corepack enable WORKDIR /build -COPY invokeai/frontend/web/ ./ +COPY invokeai/frontend/webv2/ ./ RUN --mount=type=cache,target=/pnpm/store \ pnpm install --frozen-lockfile -RUN npx vite build +RUN pnpm exec vite build + +FROM web-builder AS legacy-web-builder +WORKDIR /legacy-build +COPY invokeai/frontend/webv1/ ./ +RUN --mount=type=cache,target=/pnpm/store \ + pnpm install --frozen-lockfile +RUN pnpm exec vite build ## Backend --------------------------------------- @@ -66,10 +73,8 @@ WORKDIR ${INVOKEAI_SRC} # Install project's dependencies as a separate layer so they aren't rebuilt every commit. # bind-mount instead of copy to defer adding sources to the image until next layer. # -# NOTE: on arm64 (aarch64), torch/torchvision resolve to PyPI CPU wheels regardless of -# GPU_DRIVER — the WHL indexes carry aarch64 torch wheels (+cpu/+cu128) but no aarch64 -# torchvision wheels, so the pinned pairs only apply on x86_64. Use GPU_DRIVER=cpu on arm64. -# CUDA-on-arm64 (e.g. Grace/GH200) would need a torchvision-specific workaround; see pyproject.toml. +# NOTE: on arm64 (aarch64), torch/torchvision resolve to the PyPI CPU wheels (2.7.1) regardless of +# GPU_DRIVER -- the pinned WHL-index pairs only apply on x86_64 (see pyproject.toml). Use GPU_DRIVER=cpu on arm64. # x86_64/CUDA is the default RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ @@ -95,7 +100,8 @@ ENTRYPOINT ["/opt/invokeai/docker-entrypoint.sh"] CMD ["invokeai-web"] # --link requires buldkit w/ dockerfile syntax 1.4, does not work with podman -COPY --link --from=web-builder /build/dist ${INVOKEAI_SRC}/invokeai/frontend/web/dist +COPY --link --from=web-builder /build/dist ${INVOKEAI_SRC}/invokeai/frontend/webv2/dist +COPY --link --from=legacy-web-builder /legacy-build/dist ${INVOKEAI_SRC}/invokeai/frontend/webv1/dist # add sources last to minimize image changes on code changes COPY invokeai ${INVOKEAI_SRC}/invokeai diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index ebb59d36115..402e590adc2 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -1,4 +1,5 @@ // @ts-check +import { fileURLToPath } from 'node:url'; import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; @@ -7,6 +8,7 @@ import starlightLinksValidator from 'starlight-links-validator'; import starlightLlmsText from 'starlight-llms-txt'; import starlightChangelogs from 'starlight-changelogs'; import { rehypePrefixBaseToRootLinks } from './plugins/rehype-prefix-base-to-root-links.mjs'; +import { remarkSortPrefixSlug } from './plugins/remark-sort-prefix-slug.mjs'; import starlightContextualMenu from 'starlight-contextual-menu'; // Configs @@ -34,6 +36,7 @@ export default defineConfig({ site, base: base || undefined, markdown: { + remarkPlugins: [[remarkSortPrefixSlug, { docsDir: fileURLToPath(new URL('./src/content/docs', import.meta.url)) }]], rehypePlugins: [[rehypePrefixBaseToRootLinks, { base }]], }, integrations: [ @@ -69,6 +72,7 @@ export default defineConfig({ './src/styles/custom.css', ], sidebar: sidebarConfig, + routeMiddleware: './src/route-data.ts', components: { ThemeProvider: './src/lib/components/ForceDarkTheme.astro', ThemeSelect: './src/lib/components/EmptyComponent.astro', diff --git a/docs/package.json b/docs/package.json index 29d0786e8ea..1839a673f1b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,6 +9,7 @@ "check-docs-data": "pnpm run generate-docs-data && git diff --exit-code -- src/generated", "check-deploy-output": "node ./scripts/verify-deploy-output.mjs", "check-redirects": "node ./scripts/validate-redirect-targets.mjs", + "test": "node --test \"src/**/*.test.mjs\"", "build": "astro build", "preview": "astro preview", "astro": "astro" @@ -18,6 +19,7 @@ "@fontsource-variable/inter": "^5.2.8", "@fontsource-variable/roboto-mono": "^5.2.9", "astro": "^6.3.7", + "github-slugger": "^2.0.0", "mermaid": "^11.15.0", "rehype-external-links": "^3.0.0", "sharp": "^0.34.5", diff --git a/docs/plugins/remark-sort-prefix-slug.mjs b/docs/plugins/remark-sort-prefix-slug.mjs new file mode 100644 index 00000000000..17a5e6faa6d --- /dev/null +++ b/docs/plugins/remark-sort-prefix-slug.mjs @@ -0,0 +1,21 @@ +import path from 'node:path'; + +import { generateDocsId, getSortPrefix } from '../src/lib/sort-prefix.mjs'; + +/** + * starlight-links-validator derives each page's URL from its file path and ignores the docs loader's + * `generateId`, so it cannot find pages whose path carries a sorting prefix. It does honour a + * frontmatter `slug`, so give those pages their real id as one while rendering. + */ +export function remarkSortPrefixSlug({ docsDir }) { + return (_tree, file) => { + const filePath = file.history[0]; + if (!filePath) return; + const entry = path.relative(docsDir, filePath).split(path.sep).join('/'); + if (entry.startsWith('..') || !entry.split('/').some((segment) => getSortPrefix(segment) !== undefined)) return; + + file.data.astro ??= {}; + const frontmatter = (file.data.astro.frontmatter ??= {}); + if (!frontmatter.slug) frontmatter.slug = generateDocsId({ entry, data: frontmatter }); + }; +} diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index a2daac1ce72..fe3fbb05994 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: astro: specifier: ^6.3.7 version: 6.3.7(@types/node@24.12.3)(jiti@2.6.1)(rollup@4.60.4)(yaml@2.9.0) + github-slugger: + specifier: ^2.0.0 + version: 2.0.0 mermaid: specifier: ^11.15.0 version: 11.15.0 diff --git a/docs/scripts/validate-redirect-targets.mjs b/docs/scripts/validate-redirect-targets.mjs index 1ec6e7f0898..a1990c45483 100644 --- a/docs/scripts/validate-redirect-targets.mjs +++ b/docs/scripts/validate-redirect-targets.mjs @@ -2,6 +2,8 @@ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'; import { dirname, join, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { generateDocsId } from '../src/lib/sort-prefix.mjs'; + const docsRoot = join(dirname(fileURLToPath(import.meta.url)), '..'); const contentRoot = join(docsRoot, 'src', 'content', 'docs'); const redirectsFile = join(docsRoot, 'src', 'config', 'redirects.ts'); @@ -31,8 +33,8 @@ const collectDocsRoutes = (dir, routes = new Set()) => { continue; } - const relativePath = relative(contentRoot, entryPath).replace(/\\/g, '/').replace(/\.mdx?$/, ''); - const route = relativePath.endsWith('/index') ? relativePath.slice(0, -'/index'.length) : relativePath; + // Frontmatter slugs are not read, as before. + const route = generateDocsId({ entry: relative(contentRoot, entryPath).replace(/\\/g, '/'), data: {} }); routes.add(normalizeRoute(route)); const segments = route.split('/').filter(Boolean); diff --git a/docs/src/config/redirects.ts b/docs/src/config/redirects.ts index 8c2b3e69c7f..8b6c46d58ba 100644 --- a/docs/src/config/redirects.ts +++ b/docs/src/config/redirects.ts @@ -8,32 +8,35 @@ const redirects: RedirectsConfig = { '/installation': '/start-here/installation', '/installation/docker': '/configuration/docker', '/installation/manual': '/start-here/manual', - '/installation/models': '/concepts/models', - '/installation/patchmatch': '/configuration/patchmatch', + '/installation/models': '/users-guide/models/introduction', + '/installation/patchmatch': '/configuration/optimization/patchmatch', '/installation/quick_start': '/start-here/installation', '/installation/requirements': '/start-here/system-requirements', '/configuration': '/configuration/invokeai-yaml', - '/features/low-vram/': '/configuration/low-vram-mode/', - '/features/lasso-tool': '/features/canvas/lasso-tool', - '/features/shapes-tool': '/features/canvas/shapes-tool', + '/features/low-vram/': '/configuration/optimization/low-vram-mode/', + '/features/lasso-tool': '/users-guide/canvas/canvas-guide', + '/features/shapes-tool': '/users-guide/canvas/canvas-guide', + '/features/video-generation': '/users-guide/video-generation/introduction', '/faq': '/troubleshooting/faq', - '/help/SAMPLER_CONVERGENCE': '/concepts/parameters', - '/help/diffusion': '/concepts/diffusion', - '/help/gettingStartedWithAI': '/concepts/image-generation', - '/nodes/NODES': '/features/workflows/editor-interface', + '/help/SAMPLER_CONVERGENCE': '/users-guide/image-generation/parameters', + '/help/diffusion': '/users-guide/image-generation/advanced-topics/diffusion', + '/help/gettingStartedWithAI': '/users-guide/image-generation/image-generation', + '/nodes/NODES': '/users-guide/workflows/introduction', '/nodes/NODES_MIGRATION_V3_V4': '/development/guides/api-development', - '/nodes/comfyToInvoke': '/features/workflows/comfyui-migration', - '/nodes/communityNodes': '/features/workflows/community-nodes', + '/nodes/comfyToInvoke': '/users-guide/workflows/comfyui-migration', + '/nodes/communityNodes': '/users-guide/workflows/community-nodes', '/nodes/contributingNodes': '/development/guides/creating-nodes', - '/nodes/detailedNodes/faceTools': '/features/workflows/face-tools', + '/nodes/detailedNodes/faceTools': '/users-guide/workflows/face-tools', '/nodes/invocation-api': '/development/guides/api-development', '/contributing/ARCHITECTURE': '/development/architecture/overview', '/contributing/DOWNLOAD_QUEUE': '/development/architecture/model-manager', - '/contributing/HOTKEYS': '/features/hotkeys', + '/contributing/HOTKEYS': '/users-guide/workbench/hotkeys', '/contributing/INVOCATIONS': '/development/architecture/invocations', '/contributing/LOCAL_DEVELOPMENT': '/development/setup/dev-environment', '/contributing/MODEL_MANAGER': '/development/architecture/model-manager', - '/contributing/NEW_MODEL_INTEGRATION': '/development/guides/models', + '/contributing/NEW_MODEL_INTEGRATION': '/contributing/new-model-integration', + // Superseded by the contributor guide, which tracks the current architecture. + '/development/guides/models': '/contributing/new-model-integration', '/contributing/PR-MERGE-POLICY': '/development/process/pr-merge-policy', '/contributing/TESTS': '/development/guides/tests', '/contributing/contribution_guides/development': '/development', @@ -44,6 +47,54 @@ const redirects: RedirectsConfig = { '/contributing/frontend/state-management': '/development/front-end/state-management', '/contributing/frontend/workflows': '/development/front-end/workflows', + // Pages moved by the v7 documentation reorganization. + '/concepts/diffusion': '/users-guide/image-generation/advanced-topics/diffusion', + '/concepts/dynamic-prompting': '/users-guide/image-generation/prompting-guide', + '/concepts/image-generation': '/users-guide/image-generation/image-generation', + '/concepts/models': '/users-guide/models/introduction', + '/concepts/nodes-workflows': '/users-guide/workflows/introduction', + '/concepts/parameters': '/users-guide/image-generation/parameters', + '/concepts/prompt-syntax': '/users-guide/image-generation/prompting-guide', + '/concepts/prompting-guide': '/users-guide/image-generation/prompting-guide', + '/concepts/video-generation': '/users-guide/video-generation/introduction', + '/configuration/fp8-storage': '/configuration/optimization/fp8-storage', + '/configuration/low-vram-mode': '/configuration/optimization/low-vram-mode', + '/configuration/patchmatch': '/configuration/optimization/patchmatch', + '/configuration/sdnq-quantization': '/configuration/optimization/sdnq-quantization', + '/features/canvas/bounding-box': '/users-guide/canvas/canvas-guide', + '/features/canvas/canvas-projects': '/users-guide/projects/introduction', + '/features/canvas/gradient-tool': '/users-guide/canvas/canvas-guide', + '/features/canvas/lasso-tool': '/users-guide/canvas/canvas-guide', + '/features/canvas/layers-and-drops': '/users-guide/canvas/introduction', + '/features/canvas/run-workflow': '/users-guide/canvas/canvas-guide', + '/features/canvas/shapes-tool': '/users-guide/canvas/canvas-guide', + '/features/canvas/text-tool': '/users-guide/canvas/canvas-guide', + '/features/custom-node-manager': '/users-guide/workflows/custom-node-manager', + '/features/external-models': '/users-guide/models/external-models/introduction', + '/features/external-models/alibabacloud': '/users-guide/models/external-models/alibabacloud', + '/features/external-models/gemini': '/users-guide/models/external-models/gemini', + '/features/external-models/openai': '/users-guide/models/external-models/openai', + '/features/external-models/seedream': '/users-guide/models/external-models/seedream', + '/features/gallery': '/users-guide/gallery/introduction', + '/features/hidiffusion': '/users-guide/image-generation/advanced-topics/hidiffusion', + '/features/hotkeys': '/users-guide/workbench/hotkeys', + '/features/image-storage-maintenance': '/users-guide/gallery/image-storage-maintenance', + '/features/intermediates': '/users-guide/image-generation/intermediates', + '/features/krea-2': '/users-guide/models/local-models/krea-2', + '/features/multi-user-mode/admin-guide': '/configuration/multi-user-mode/admin-guide', + '/features/multi-user-mode/api-guide': '/configuration/multi-user-mode/api-guide', + '/features/multi-user-mode/user-guide': '/configuration/multi-user-mode/user-guide', + '/features/pid-decode': '/users-guide/image-generation/advanced-topics/pid-decode', + '/features/prompt-tools': '/users-guide/image-generation/prompt-tools', + '/features/video-workflows': '/users-guide/video-generation/video-workflows', + '/features/workflows': '/users-guide/workflows/introduction', + '/features/workflows/adding-nodes': '/users-guide/workflows/introduction', + '/features/workflows/comfyui-migration': '/users-guide/workflows/comfyui-migration', + '/features/workflows/community-nodes': '/users-guide/workflows/community-nodes', + '/features/workflows/custom-node-manager': '/users-guide/workflows/custom-node-manager', + '/features/workflows/editor-interface': '/users-guide/workflows/introduction', + '/features/workflows/face-tools': '/users-guide/workflows/face-tools', + '/features/workflows/loop-nodes': '/users-guide/workflows/loop-nodes', }; function createRedirects(base: string): RedirectsConfig { diff --git a/docs/src/config/sidebar.ts b/docs/src/config/sidebar.ts index a2636472d41..d6672239d23 100644 --- a/docs/src/config/sidebar.ts +++ b/docs/src/config/sidebar.ts @@ -16,23 +16,16 @@ const sidebar: SidebarConfig = [ label: 'Configuration', items: [ { - autogenerate: { directory: 'configuration' }, - }, - ], - }, - { - label: 'Concepts', - items: [ - { - autogenerate: { directory: 'concepts' }, + autogenerate: { directory: 'configuration', collapsed: true }, }, ], + collapsed: true, }, { - label: 'Features', + label: 'Users Guide', items: [ { - autogenerate: { directory: 'features' }, + autogenerate: { directory: 'Users Guide', collapsed: true }, }, ], }, diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts index 5cba9253694..dc15607ef98 100644 --- a/docs/src/content.config.ts +++ b/docs/src/content.config.ts @@ -1,11 +1,23 @@ import { defineCollection } from 'astro:content'; +import { z } from 'astro/zod'; import { docsLoader, i18nLoader } from '@astrojs/starlight/loaders'; import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; import { changelogsLoader } from 'starlight-changelogs/loader'; +import { generateDocsId } from './lib/sort-prefix.mjs'; + export const collections = { - docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), + docs: defineCollection({ + loader: docsLoader({ generateId: generateDocsId }), + schema: docsSchema({ + extend: z.object({ + // Docs directories, as URL paths (e.g. `users-guide/models/local-models`), whose sidebar groups also list + // this page. See `src/route-data.ts`. + alsoListedIn: z.array(z.string()).optional(), + }), + }), + }), i18n: defineCollection({ loader: i18nLoader(), schema: i18nSchema() }), changelogs: defineCollection({ loader: changelogsLoader([ diff --git a/docs/src/content/docs/Users Guide/01.Workbench/01.introduction.mdx b/docs/src/content/docs/Users Guide/01.Workbench/01.introduction.mdx new file mode 100644 index 00000000000..5a10b7a21d4 --- /dev/null +++ b/docs/src/content/docs/Users Guide/01.Workbench/01.introduction.mdx @@ -0,0 +1,198 @@ +--- +title: Guided tour +description: How the Invoke workbench is laid out, how to arrange panels in the rails and the center, and how to save favourite arrangements as layout presets. +lastUpdated: 2026-09-29 +--- + +import WorkbenchLayout from './assets/workbench-layout.svg'; + +Invoke's main window, the **workbench**, is built from panels that you can arrange to suit the job in front of you. Put the tools you use together side by side, move the gallery to whichever side you prefer, float the Preview over the canvas, and save the result as a **layout preset** so you can return to it with one click. + +## The parts of the workbench + + + +Hover over a numbered circle, or tap it on a touchscreen, for a short description. The table below describes each one in full. + +| | Part | What it does | +| --- | --- | --- | +| 1 | **App menu** | Links to Projects, the Model Manager, Fonts, the Queue, Settings and help. | +| 2 | **Project switcher** | Shows the active project and switches between projects. See [Working with Projects](/users-guide/projects/introduction/). | +| 3 | **Layout preset tabs** | One tab per saved layout. A dot on the active tab means the layout has unsaved changes; its arrow opens the preset menu. | +| 4 | **Save as new preset** | Saves the current arrangement as a new preset. | +| 5 | **Invoke controls** | The number of iterations, the **Invoke** button, and routing, which chooses where the settings come from and where results go. | +| 6 | **Rails** | A strip of icons down each edge of the window, one icon per panel placed on that side. | +| 7 | **Rail menu (…)** | Adds panels to a rail or removes them. | +| 8 | **Side panels** | Show the selected panel from the neighbouring rail. | +| 9 | **Resize handle** | Drag to make a side panel wider or narrower, or to collapse it. | +| 10 | **Center panel** | The main work area: Preview, Canvas, Gallery, Workflow editor or Image Map. | +| 11 | **Center view menu** | Switches what the center shows, and adds or closes center views. | +| 12 | **Floating window** | A panel detached from its rail, which you can move and resize anywhere. | +| 13 | **Floating panel's rail slot** | A dashed icon that marks where a floating panel came from. Click it to dock the panel again. | +| 14 | **Status bar** | Small status chips: server connection, queue, gallery, autosave and notifications. | + +### Rails and side panels + +The **left rail** is for panels you *create* with, such as **Generate**, **Upscale** and **Video**. The **right rail** is for panels you *inspect* results with, such as **Gallery**, **Layers** and **Queue**. Each rail can hold several panels, but its side panel shows one at a time: + +- Click a rail icon to show that panel. Hover over an icon to see its name. +- Click the icon of the panel that is already showing to collapse the side panel and give the center more room. Click it again to bring the panel back at the same width. +- Drag the **resize handle** between a side panel and the center to change its width. Dragging well past its narrowest size collapses it. + +Side panels shrink as needed when the window gets narrow, so the center always keeps a usable width. + +### The center panel + +The center panel shows one **view** at a time. The button in its top-left corner (for example **Preview**) names the current view; click it to open the center view menu: + +- **Center Views** lists the views already placed in the center. Choose one to switch to it. +- **Add To Center** lists other panels that can be shown in the center. +- **Close** removes the current view. The center always keeps at least one view. + +Press Z to swap the Preview into the center and back to the view you had before. It is a quick way to check a result while you work on the canvas or a workflow. + +### The status bar and bottom panel + +The status bar along the bottom holds small chips. Hover over **Server Status**, the queue chip or **Autosave** for details; click **Notifications** to see recent messages. Some chips, such as **Gallery**, open a **bottom panel** above the status bar. Drag the top edge of the bottom panel to resize it. + +### Floating windows + +**Preview** and **Image Map** can float in their own windows above the rest of the workbench. Click **Float Window** in the panel's header to detach it. Then: + +- Drag the title bar to move the window, and drag the corner grip to resize it. +- Double-click the title bar, or use **Collapse**, to shrink the window to just its title bar. +- **Maximize** enlarges the window; **Restore** returns it to its previous size. +- **Dock to panel** returns the panel to the rail it came from. Clicking its dashed rail icon does the same. + +A floating Preview is handy on the **Edit** layout: keep the canvas in the center and watch results in a small window beside your work. + +## Arranging panels + +### Available panels + +Each panel can be placed only where it makes sense: + +| Panel | Left rail | Right rail | Center | Bottom | Can float | +| --- | :---: | :---: | :---: | :---: | :---: | +| Generate, Upscale, Video | ✓ | | | | | +| Workflow | ✓ | | ✓ | ✓ | | +| Canvas | | | ✓ | | | +| Gallery | ✓ | ✓ | ✓ | ✓ | | +| Image Map | ✓ | ✓ | ✓ | | ✓ | +| Preview | | ✓ | ✓ | | ✓ | +| Project | ✓ | ✓ | | | | +| Layers, Queue | | ✓ | | | | +| Diagnostics | | ✓ | | ✓ | | + +A panel can appear in more than one place at once, for example the Gallery in the right rail *and* in the center, but only once in each place. The **Project** panel isn't in any built-in layout; add it to a rail to keep the project's name, sync status and details in view. + +### Adding and removing panels + +Click the **…** button at the bottom of a rail, or right-click an empty part of the rail, to open its menu (**Create widgets** on the left, **Inspect widgets** on the right). It lists every panel that rail can hold, with a check mark beside those already placed. Choose a panel to add it, or choose a checked panel to remove it. + +To remove a panel quickly, right-click its rail icon and choose **Remove**. Removing a panel only takes it out of the layout. Its contents and settings are kept, and it comes back as you left it when you add it again. + +Center views are added and closed from the [center view menu](#the-center-panel). + +### Moving panels between rails + +Drag a rail icon to reorder it within its rail, or drop it on the other rail to move the panel to the other side. The side panel opens to show the panel you moved. You can also drag an icon onto the status bar to tuck the panel into the bottom panel, and drag a chip back up to a rail. + +While you drag, each place shows **Drop here** if the panel can go there, **Unavailable** if that panel isn't allowed there, or **Already placed** if it is there already. + +On a touchscreen, press and hold an icon briefly before dragging. + +### Layout shortcuts + +| Keys | Action | +| --- | --- | +| T or O | Collapse or expand the left panel | +| G | Collapse or expand the right panel | +| F | Collapse or expand all side and bottom panels at once, for a clear view of the center | +| Z | Swap the Preview into or out of the center | +| Shift+R | Revert to the active preset's saved layout | +| Alt+1 to Alt+4 | Switch to the Compose, Edit, Video or Automate preset | + +These shortcuts don't fire while you are typing in a text field. You can change any of them; see [Hotkeys](/users-guide/workbench/hotkeys/). + +## Layout presets + +A **layout preset** is a saved arrangement of panels. The tabs in the middle of the top bar switch between them, so you can move from generating to canvas editing to building workflows without rearranging anything by hand. + +### Built-in presets + +Invoke comes with four presets: + +| Preset | Center | Left rail | Right rail | +| --- | --- | --- | --- | +| **Compose**: generate images from text | Preview (Gallery also available) | Generate, Upscale | Gallery, Image Map, Queue | +| **Edit**: paint and composite on the canvas | Canvas (Preview also available) | Generate, Upscale | Layers, Preview | +| **Video**: generate video | Preview | Video, Upscale | Gallery, Queue | +| **Automate**: build and run workflows | Workflow editor (Preview also available) | Workflow | Queue, Preview, Gallery, Image Map | + +Each preset also sets where **Invoke** takes its settings from and where results go. For example, Compose generates from the Generate panel into the Gallery, while Edit works from and into the Canvas. A source or destination you have locked from the routing control next to **Invoke** is left alone. + +New projects open with Compose unless you choose a different starting point when you create them. + +### Switching presets + +Click a preset's tab, or press Alt+1 to Alt+4 for the built-in presets. Switching changes only the arrangement: every panel keeps its contents, so a half-written prompt or a scrolled gallery is exactly as you left it. + +When the window is narrow, the tabs show only their icons. Hover over one to see its name. + +### Changing a preset + +Once you move, add or remove panels, the active preset's tab shows a dot to say the layout no longer matches the saved preset. Nothing is lost if you switch away: the arrangement you had stays with the project. + +Click the arrow on the active tab, or right-click any tab, to open the preset menu: + +- **Save changes** stores the current arrangement in the preset, so it opens that way from now on. +- **Revert to saved layout** puts the panels back as the preset saved them. Shift+R does the same. +- **Edit preset…** changes the preset's name, icon and default routing. +- **Delete preset…** removes a preset you created. +- **Manage presets** opens the list of all presets. +- On a tab that isn't active, **Switch to this layout** switches to it. + +You can also save changes to the built-in presets. Your version replaces the original for your account only, and you can restore the original at any time from **Manage presets**. + +### Creating your own preset + +1. Arrange the panels the way you like them. +2. Click the **+** button to the right of the preset tabs (**Save this layout as a new preset**). +3. In **Save as new preset**, enter a **Preset name** and choose an **Icon** for its tab. +4. Choose the **Default source**, the panel **Invoke** reads its settings from (such as Generate or Workflow), and the **Default destination**, **Canvas** or **Gallery**. +5. Click **Save preset**. + +The new preset captures the whole arrangement, including floating windows and their positions. It becomes the active preset and gets a tab at the end of the row. + +### Managing presets + +**Manage presets** lists every preset in tab order. From there you can: + +- drag presets by their grip to change the order of the tabs; +- click the pencil to edit a preset's name, icon and routing; +- restore a built-in preset you have changed to its original arrangement. Changed built-ins are marked with a dot; +- delete presets you created. Built-in presets can't be deleted. + +You can also reorder tabs by dragging them in the top bar. With the keyboard, focus a tab, press Space, move it with the arrow keys, and press Space again to drop it. + +Deleting a preset only removes the saved preset. Projects that were using it keep their current arrangement. + +## What is saved where + +- **The current arrangement belongs to the project.** Each project remembers its own panels, sizes, center view and floating windows, so switching projects also switches layouts. See [Working with Projects](/users-guide/projects/introduction/). +- **Presets belong to your account.** Custom presets, changes to built-in presets and the order of the tabs are available in all of your projects. With [multiple users](/configuration/multi-user-mode/user-guide/), each person has their own presets. + +To start over, press Shift+R, or use **Reset layout** under **Data & workspace** in Settings. Both put the current project's panels back as the active preset saved them. + +## Tips + +- **Build a preset for each kind of work.** A "Portraits" preset might keep Generate and the Gallery side by side with a floating Preview; an "Inpainting" preset might keep Layers and Preview on the right of the canvas. +- **Keep the center for the work itself.** Use F to hide all side panels when you need the whole window for the canvas, and press it again to bring them back. +- **Use the Gallery in the center for triage.** Add the Gallery to the center to review a large batch at full width, then press Z to jump between it and the Preview. +- **Save built-in changes you like.** If you always move the Queue to the left, save that change into the preset rather than repeating it in each project. +- **Highlight the focused area.** **Highlight focused regions** in Settings (on by default) outlines the part of the workbench that keyboard shortcuts will act on. diff --git a/docs/src/content/docs/Users Guide/01.Workbench/02.hotkeys.mdx b/docs/src/content/docs/Users Guide/01.Workbench/02.hotkeys.mdx new file mode 100644 index 00000000000..6200e875326 --- /dev/null +++ b/docs/src/content/docs/Users Guide/01.Workbench/02.hotkeys.mdx @@ -0,0 +1,200 @@ +--- +title: Hotkey shortcuts +description: Learn how to use and customize hotkeys in InvokeAI, and how developers can interact with the hotkey system. +lastUpdated: 2026-02-19 +--- + +import { Tabs, TabItem, Steps, Card, CardGrid, Icon } from '@astrojs/starlight/components'; + +InvokeAI allows you to customize all keyboard shortcuts (hotkeys) to match your workflow preferences. This guide covers how to use and customize hotkeys as a user, as well as providing technical documentation for developers. + +## User Guide + + + + See all available keyboard shortcuts organized by category in one place. + + + Change any shortcut to your preference, or assign multiple key combinations to the same action. + + + Built-in validation prevents invalid combinations. + + + Your custom hotkeys are safely stored and restored across sessions. + + + +### Opening the Hotkeys Modal + +Press Shift + ? or click the **keyboard icon** in the application to open the Hotkeys Modal. + +### Managing Hotkeys + + + + - Browse all available hotkeys organized by category (App, Canvas, Gallery, Workflows, etc.). + - Search for specific hotkeys using the search bar. + - See the current key combination for each action. + + + + 1. Click the **pencil** button by the hotkey you want to change, or click the **plus** button to add a new one. + 2. Enter your new hotkey combination in the editor. + - Use modifier buttons for quick-insert (Mod, Ctrl, Shift, Alt). + - Check the live preview to see how your hotkey will look. + 3. Click the **checkmark** or press Enter to save. + + + + - **Reset a single hotkey:** Click the counter-clockwise arrow icon next to customized hotkeys. + - **Reset all hotkeys:** In Edit Mode, click the **Reset All to Default** button at the bottom. + + + +### Hotkey Format Reference + +When customizing hotkeys, use the following formats: + +- **Valid Modifiers:** `mod` (Ctrl on Windows/Linux, Cmd on Mac), `ctrl`, `shift`, `alt` +- **Valid Keys:** Letters (`a-z`), Numbers (`0-9`), Function keys (`f1-f12`), Special keys (`enter`, `space`, `tab`, `backspace`, `delete`, `escape`), Arrow keys (`up`, `down`, `left`, `right`) +- **Multiple alternatives:** Separate with commas (e.g., `mod+enter, ctrl+enter`) + +:::tip + **Valid Hotkeys:** `mod+s`, `ctrl+shift+p`, `f5, mod+r`
+ **Invalid Hotkeys:** `mod+` (no key after modifier), `shift+ctrl+` (ends with modifier) +::: + +--- + +## Developer Guide + +The hotkeys system allows developers to centrally define, manage, and validate hotkeys throughout the application. It is built on top of `react-hotkeys-hook`. + +### Architecture + +The customizable hotkeys feature comprises the following components: + + + + **Hotkeys State Slice (`hotkeysSlice.ts`)** + - Stores custom hotkey mappings in Redux state. + - Persisted to IndexedDB using `redux-remember`. + - Provides actions to change, reset individual, or reset all hotkeys. + + + **`useHotkeyData` Hook (`useHotkeyData.ts`)** + - Defines all default hotkeys and merges them with custom hotkeys from the store. + - Returns the effective hotkeys to be used. + - Provides platform-specific key translations. + + + - **`HotkeyEditor.tsx`**: Inline editor with live preview, validation, and modifier insertion. + - **`HotkeysModal.tsx`**: The modal interface supporting View/Edit modes, searching, and categories. + + + +### Adding New Hotkeys + +To add a new hotkey to the system, follow these steps: + + +1. **Add Translation Strings** + In `invokeai/frontend/webv1/public/locales/en.json`: + ```json + { + "hotkeys": { + "app": { + "myAction": { + "title": "My Action", + "desc": "Description of what this hotkey does" + } + } + } + } + ``` + +2. **Register the Hotkey** + In `invokeai/frontend/webv1/src/features/system/components/HotkeysModal/useHotkeyData.ts`: + ```typescript + // Inside the appropriate category builder function + addHotkey('app', 'myAction', ['mod+k']); // Default binding + ``` + +3. **Use the Hotkey in Components** + ```tsx + import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; + + const MyComponent = () => { + const handleAction = useCallback(() => { + // Your action here + }, []); + + // Automatically uses custom hotkeys if configured + useRegisteredHotkeys({ + id: 'myAction', + category: 'app', // 'app', 'canvas', 'viewer', 'gallery', 'workflows' + callback: handleAction, + options: { enabled: true, preventDefault: true }, + dependencies: [handleAction] + }); + + // ... + }; + ``` + + +### Common Patterns + + + + Only enable hotkeys when certain conditions are met: + ```typescript + useRegisteredHotkeys({ + id: 'save', + category: 'app', + callback: handleSave, + options: { + enabled: hasUnsavedChanges && !isLoading, // Only when valid + preventDefault: true + }, + dependencies: [hasUnsavedChanges, isLoading, handleSave] + }); + ``` + + + Ensure hotkeys are only active when a specific region is focused: + ```tsx + import { useFocusRegion } from 'common/hooks/focus'; + + const MyComponent = () => { + const focusRegionRef = useFocusRegion('myRegion'); + + // Hotkey only works when this region has focus + useRegisteredHotkeys({ + id: 'myAction', + category: 'app', + callback: handleAction, + options: { enabled: true } + }); + + return
...
; + }; + ``` +
+ + Provide multiple alternatives for the same action: + ```typescript + // In useHotkeyData.ts + addHotkey('canvas', 'redo', ['mod+shift+z', 'mod+y']); // Two alternatives + ``` + +
+ +:::caution[Best Practices] +- **Use `mod` instead of `ctrl`**: Automatically maps to Cmd on Mac, Ctrl elsewhere. +- **Provide descriptive translations**: Help users understand what each hotkey does. +- **Avoid conflicts**: Check existing hotkeys before adding new ones. +- **Check enabled state**: Only activate hotkeys when the action is available. +- **Use dependencies correctly**: Ensure callbacks are stable with `useCallback`. +::: diff --git a/docs/src/content/docs/Users Guide/01.Workbench/assets/workbench-layout.svg b/docs/src/content/docs/Users Guide/01.Workbench/assets/workbench-layout.svg new file mode 100644 index 00000000000..0278c501111 --- /dev/null +++ b/docs/src/content/docs/Users Guide/01.Workbench/assets/workbench-layout.svg @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + My Project + + + + T + Compose + + + Edit + Video + Automate + + + + 1 + + + Invoke + + + + + + 0 + + + + + + + + + + + + + + + + + Generate + + Model + + Prompt + + + + + LEFT PANEL + + + + + + + + + + + + Preview + + + + + + + + CENTER PANEL + + + + + + Image Map + + + + + + + + + + + + + + + + + Gallery + + + + + + + + RIGHT PANEL + + + + + + + + + + + + + + + + Server Status + Queue idle + Gallery + Autosave: Saved + + Notifications + + + + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + 5 + + + + 6 + + + + 6 + + + + 7 + + + + 8 + + + + 8 + + + + 9 + + + + 10 + + + + 11 + + + + 12 + + + + 13 + + + + 14 + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/content/docs/Users Guide/02.Projects/introduction.mdx b/docs/src/content/docs/Users Guide/02.Projects/introduction.mdx new file mode 100644 index 00000000000..e2abf4e2ff1 --- /dev/null +++ b/docs/src/content/docs/Users Guide/02.Projects/introduction.mdx @@ -0,0 +1,170 @@ +--- +title: Working with Projects +description: Keep each piece of work in its own project, with its canvas, workflows, layout and board, and switch between them without losing your place. +lastUpdated: 2026-09-29 +sidebar: + order: 0 +--- +import { Image } from 'astro:assets'; +import projectSwitcher from '../assets/project-switcher.webp'; + +A **project** is a self-contained workspace. Everything you arrange while working on one idea stays together in it: the canvas and its layers, the workflows you have open, the panel layout, your prompt history, and a gallery board that collects the project's images and videos. Open the project again later, on the same computer or another one, and it comes back exactly as you left it. + +Invoke saves projects automatically on the server, so there is no Save button to remember. You can keep several projects open at once and switch between them in a keystroke. + +## Why use projects + +- **Pick up where you left off.** Closing the browser, reloading the page or moving to another computer does not lose your arrangement. The Home page offers your most recent project with a single **Resume** button. +- **Keep unrelated work apart.** A character study, a product shoot and a video experiment can each have their own canvas, workflows, layout and board, instead of competing for one workspace. +- **Switch without waiting.** Generations keep running when you switch to another project. Their results still land on the board of the project that started them. +- **Find results easily.** Each project's images and videos collect on its own board, named after the project, rather than piling up in one long gallery. +- **Back up and share work.** A project can be exported as a single `.invk` file that includes its images and videos, and imported on any Invoke installation. + +## What a project keeps + +| Saved with each project | Shared by all your projects | +| --- | --- | +| Canvas layers and settings | Installed models | +| Open workflows, and which one is active | Your workflow library | +| Panel layout and floating widgets | Saved layout presets | +| Prompt history, and the Invoke routing (source and destination) | Preferences, theme and hotkeys | +| Widget settings, such as gallery filters | Boards other than the project's own | +| Project settings: **Use CPU noise**, **Antialias progress images** and the Preview's progress image toggle | Generate recipes | +| The project's board and the images and videos on it | | + +In **Settings**, a badge next to each option says where it is stored: **Saved with this project**, **Saved with this widget in this project**, or **Saved in your preferences** for options that follow you across projects. + +Undo history is not saved. It lasts only while the project is open in your browser, so reloading the page or closing the project starts a fresh history. + +## Starting a project + +The **Home** page is the first thing you see when Invoke opens. It shows the project you edited most recently under **Pick up where you left off**, a row of **Recent projects**, and tiles for starting something new. + +To create a project, click **New project**. The arrow beside it (**Choose how to start**) offers starting points that open the project with a layout suited to the task: + +| Start | Opens with | +| --- | --- | +| **Generate from text** | The Compose layout, ready to generate | +| **Edit on canvas** | The Edit layout, with the canvas in front | +| **Upscale an image** | The Compose layout, set up for upscaling | +| **Generate a video** | The Video layout | +| **Build a workflow** | The Automate layout, for the workflow editor | + +The starting point only chooses the initial layout. Every tool remains available, and you can [rearrange the workspace](/users-guide/workbench/introduction/) at any time. + +New projects are called **Project Name #1**, **Project Name #2** and so on. Give yours a meaningful name early (see [Renaming](#renaming-duplicating-and-deleting)), because the name also labels the project's board in the gallery. + +## Switching between projects + +The name of the active project appears in the top bar. Click it, or press Ctrl+P (⌘+P on macOS), to open the project switcher. It lists: + +- **Open projects**: every project open in this session, with a progress ring on any that is generating. Choose one to switch to it. +- **Recent**: up to five other projects you edited lately. +- **New project** and **Open project**, which lists every saved project that is not already open. + +The project switcher open from the top bar, showing project details and commands, the open project, recent projects, New project and Open project. + +The switcher also carries commands for the active project: **Rename…**, **Details**, **Export**, **Close** and **Delete project…**. **Details** opens the **Project** widget, which shows the project's name, when it was last saved and whether it is in sync with the server. + +The shortcut works even while you are typing in a prompt box. You can rebind it in the hotkey settings, where it is called **Open project switcher**. + +### Closing a project + +**Close** removes a project from the open list without deleting anything. It stays in your library, ready to reopen from the Projects page or the switcher. Closing the last open project returns you to Home. A project that is still generating cannot be closed until its queued runs finish. + +## Where your images go + +Every project owns a private board, created with the project and named after it. In the gallery it appears near the top, after **Uncategorized**, and always shows the current project name. + +New results go to whichever board is selected in the gallery, so you can send a batch somewhere else when you want to. To send them to one board while you browse others, choose **Auto-add to this board** from that board's menu; see [Choosing where new media goes](/users-guide/gallery/introduction/#choosing-where-new-media-goes). When a project is first created, imported or duplicated, its own board is selected. If you choose a date view instead of a specific board, results still go to the project board. After that, the project remembers your choice of destination. + +The destination is fixed when you press **Invoke**. Switching to a different project or board while a job runs does not redirect its results. + +Boards belonging to your other projects are hidden from the gallery to keep it focused. To see them, turn on **Other projects' boards** in the gallery's board filters. The setting is remembered separately for each project. + +A project board is managed through its project: renaming the project renames the board, and the board has no rename, archive or delete commands of its own. Its menu offers **Export project (.invk)** instead. + +See [the gallery](/users-guide/gallery/introduction/) for more about boards. + +## Saving and syncing + +Changes save to the server automatically, about half a second after you stop making them. If the connection drops, your browser keeps the unsaved changes and Invoke retries until the save goes through. The Project widget and the workflow toolbar show the current state, for example **Project saved to the server** or **Saving project…**. + +Because projects live on the server, the same project can be open in two browser tabs or on two computers. If both change it before either has saved, Invoke shows a **Project changed elsewhere** banner and lets you decide: + +- **Save as new project** keeps your version as a separate copy, so nothing is lost. +- **Use server version** discards this tab's edits in favour of the saved version. +- **Export local draft** downloads your version as a file before you choose. + +A similar banner appears if a project you have open is deleted from another tab or computer. Your work stays available until you choose what to do with it. + +:::tip +To avoid conflicts, work on a project in one tab at a time. Use the switcher to move between projects within that tab. +::: + +## Managing your projects + +The **Projects** page lists everything you have saved. Reach it from the Home page (**View all projects**), from the **Projects** section alongside **Home**, or through the **Projects** entry in the top-left app menu. + +- **Search projects…** filters by name. +- **Sort by** orders the list by **Last edited** (the default), **Date created** or **Name**. +- **Grid view** and **List view** switch between cover cards and a compact list. +- Projects are grouped under **Pinned**, **Open now**, **Today**, **Earlier this week**, **Earlier this month** and **Older**. Searching or sorting by name shows a single list instead. + +Each project's cover is chosen automatically from its newest result, or from its top canvas layer when it has no results yet. + +Right-click a project, or use its **Actions** button, for **Open**, **Pin**, **Rename…**, **Duplicate**, **Export**, **Delete intermediates…** and **Delete…**. Pinned projects stay at the top of the list. Your sort order, view and pins are remembered. + +### Renaming, duplicating and deleting + +**Rename…** changes the project name and the name of its board. + +**Duplicate** creates a complete copy called "*name* copy", including copies of every image and video on the project's board. Use it to try a different direction without disturbing the original. + +**Delete…** removes the project permanently. There is no trash to recover it from. + +- The project's canvas, workflows, layout and settings are deleted. +- Its board is deleted too, but **the images and videos on it are kept**: they move to **Uncategorized** in the gallery. Deleting a project never empties your gallery. +- A project that is still generating cannot be deleted until its runs finish. + +If you only want to tidy the switcher, **Close** the project instead of deleting it. + +Generation also leaves temporary files behind in each project. **Delete intermediates…** reclaims that space safely; see [Intermediates](/users-guide/image-generation/intermediates/). + +## Exporting and importing + +**Export** packages a project into a single `.invk` file named after it. The file contains the project itself, every image and video on its board, and any other media the project uses, such as reference images, so it opens complete on another installation. Temporary intermediates are left out. + +Before exporting, Invoke asks whether to **Include font files** used by text layers. It is off by default. Leave it off unless you have permission to redistribute the fonts; recipients can install or replace missing fonts themselves. + +If some media is no longer available, the export still completes and reports how many items it could not include. A project with changes that have not reached the server yet cannot be exported until it reconnects. + +To bring a project in, click **Import…** on the Home or Projects page, or use **Open project** in the switcher, and choose a `.invk` file. Import always creates a new project, so importing the same file twice gives you two independent copies, and it never overwrites an existing project. The imported project opens straight away. Files from older versions of Invoke that saved canvas projects differently cannot be imported. + +Export is also the simplest way to back up a project you care about, or to hand one to someone else. There is no other way to share a project, because projects belong to a single account. + +## Workflows in a project + +A project can hold several workflows. Each is saved as part of the project and changes automatically along with it; your [workflow library](/users-guide/workflows/editor-reference/#your-workflows) changes only when you save a workflow to it on purpose. + +In the Workflow Library dialog, **This project** lists the project's own workflows, while **Browse** and **Yours** list bundled templates and your saved library. Opening a library workflow adds a copy to the current project. **Fork into new project** copies a workflow into a brand-new project of its own. + +## Making the most of projects + +- **One project per piece of work.** Create a project for each client job, series or experiment, rather than doing everything in one. Each keeps its own layout and board, so switching tasks means switching projects. +- **Name projects as you create them.** Names make the Projects page searchable and label the boards in your gallery. +- **Pin what you're actively working on.** Pinned projects stay at the top of the Projects page no matter how many others you create. +- **Duplicate before a big change.** A duplicate is cheap insurance when you want to explore a different direction. +- **Queue work, then switch.** Start a long render or a batch, switch to another project with Ctrl+P, and keep working. The progress ring in the switcher shows when it is done, and the results wait on the original project's board. To see only the active project's jobs in the Queue widget, set **Show jobs from** to **Active project** in Settings. +- **Close projects you're not using.** It keeps the switcher short and costs nothing: the project stays in your library. +- **Export milestones.** Exporting a `.invk` file gives you a self-contained snapshot to archive or share. +- **Clean up occasionally.** Long-running projects accumulate temporary files; run **Delete intermediates…** from the project's menu to reclaim disk space. + +## Things to know + +- **Projects belong to one account.** With [multiple users](/configuration/multi-user-mode/user-guide/) enabled, each person sees only their own projects, and there is no project sharing. Export and import a `.invk` file to pass a project to someone else. +- **Undo history is not saved** with the project; it is cleared when you reload or close it. +- **Invoke needs the server to open projects.** If it cannot reach the server, a **Projects are temporarily unavailable** screen offers to retry and to export any unsaved work your browser is holding. +- **Newer projects need a newer Invoke.** A project saved by a newer version of Invoke shows **Requires a newer Invoke** and cannot be opened until you upgrade. +- **Size limits.** A project file can be up to 2 GB, with up to 20,000 files. Export reports a project that is too large to package. +- **Clear saved data…** under **Data & workspace** in Settings deletes *all* of your projects and settings at once, and cannot be undone. diff --git a/docs/src/content/docs/Users Guide/03.Models/01.introduction.mdx b/docs/src/content/docs/Users Guide/03.Models/01.introduction.mdx new file mode 100644 index 00000000000..f1d0e614935 --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/01.introduction.mdx @@ -0,0 +1,257 @@ +--- +title: Managing media generation models +description: Introduction to the Model Manager +--- + +Models are the "secret sauce" that drive image and video +generation. This page provides a brief introduction to these models +and describes how you can manage them using InvokeAI. + +## Types of Models + +There are literally thousands of models available, each +specialized for a different task. These include: + +| Type | Description | +|---|---| +| **Text-to-Image** | Given a text prompt, generate a still image. | +| **Image-to-Image** | Given a starting image and a text prompt, generate a new image. | +| **Image Editing** | Use a text prompt to modify an image or combine several images according to instructions. | +| **Video Generation** | Generate a video from a text prompt (text-to-video), a starting image (image-to-video) or another video (video-to-video). These modes can be combined in multiple ways. | +| **Concept** | Small add-on models that modify a larger image or video generation model to achieve special effects, such as adding a particular style, theme or character. The most common of these are LoRA models. | +| **ControlNet** | Models that provide spatial guidance to an image generation model while it works, helping to precisely control where elements of the image appear. A typical example is a pose model that transfers the pose of a stick figure onto a photorealistic subject. | + +In addition to models directly involved in image and video +manipulation, InvokeAI supports several support models: + +| Type | Description | +|---|---| +| **Image Segmenters** | Identify and isolate individual objects in an image, which is often the first step in precisely editing it. | +| **Vision Models** | Describe what they "see" in an image. Used to classify, cluster and tag images, and to turn an image into a text prompt that can be reused. | +| **Large Language Models** | Generate text from text. InvokeAI uses them for prompt enhancement, which expands a simple prompt into one that achieves better results. | + +## Local vs External Models + +A model can be installed locally, in which case it executes on your +own hardware (CPU and graphics card), or it can be hosted +externally. In the latter case InvokeAI will send your request out to +the hosted image or video generation service and return the result. To +use an external model you will typically need to subscribe to the +hosting service. + +Locally-hosted models, also known as "open-weights" models, produce +excellent quality images and videos with decent performance. They cost +nothing to run beyond the initial hardware investment and the +electricity it uses. However, some of the more advanced models may +strain or exceed local hardware resources; some are 50-100 GB in +size. In this case using an externally-hosted model may be your best +choice. + +:::note[Model Licensing] + Note that some models carry license terms that limit their use in + commercial applications or on public servers. It's your responsibility + to adhere to the license terms. The [Model Families](/users-guide/models/model-families/#model-families) + table below summarizes each family's license, and each model's page + has a **License** section with the details. +::: + +--- + +## Model Manager + +The InvokeAI Model Manager is the central location for managing both +local and external models. For those just getting started with +InvokeAI, it provides a series of recommended starter models that are +organized into a series of bundles. + +To access the model manager, go to the InvokeAI icon at the upper +left of the screen, pull down its menu, and select "Model Manager." It +has two sections: a list of installed models on the left (initially +empty), and a panel on the right that allows you to search and install +bundles, individual starter models, and other locally-installable and +external models. + +If it isn't selected already, click **"+ Add Models"** to display the +list of starter models and bundles. + +### Local Bundles + +We suggest that you start by installing a bundle. You will see a +series of chips across the top of the panel, each one corresponding to +a different family (or "base") of generation model. Clicking on the +chip will show you all the starter models in that group. To install +all the recommended models for that family, click "Install bundle." +You may also cherry-pick individual models within the bundle. + +Some models are quite large and may take a while to download. During +download and installation, a status bar will appear at the bottom of +the right panel. Click on this bar in order to see the progress of +individual models. + +### External Models + +To add an externally-hosted model, select the **"API Keys"** section of +the Model Manager's right panel. This will display a list of model +providers. Where prompted, enter the API key for access to the +provider using your subscription. See your provider's account page for +information on how to generate these keys. Once a valid API key has +been provided, the list of models hosted by that service will appear +as selections in the Image Generation panel. + +:::note[Gated Downloads] + The API Keys section is also used to provide keys for services that + require authentication to download certain models, such as those + covered by restrictive licenses. If you try to install a model + hosted by HuggingFace or Civitai and you get a permission denied + error, you will need to enter an API key for your account on the + service. You may also need to accept the license terms for the + model in question on the host's download page. +::: + +## Installing Models by Hand + +There is a search box at the top of the **"+ Add Models"** section +that has multiple uses. You can: + +* Type a search term to find starter models that you can install + immediately. +* Paste in the download URL of a model you wish to install from + Civitai, HuggingFace, or another download site. +* Paste in the HuggingFace repository ID of a model you wish to install. +* If you've already downloaded the model and it is living on the + InvokeAI backend filesystem, type in or cut and paste the path to the + folder containing the model you wish to install. + +If you type in a URL or HuggingFace repository ID, a **Pull** button +will appear. Pressing it will initiate the download and install. If +you type in a file path, the button will change to **Scan**. Pressing +it will scan the indicated folder and list the installable +models. Click the model(s) you wish to install. + +## Editing a Model + +Once a model is installed, it will appear in the left-hand panel. The +panel has controls that let you search and sort the models in various +ways, including by model type and architecture. + +Most models have default parameters that you can customize, for +example step count, image dimensions and CFG. When you click on a +model, an edit panel appears to the right where you can edit the +model's default parameters, change its name, add descriptive notes, +and even choose a preview image for the model. For models that take +prompt trigger terms, such as some LoRAs, you can add the terms so +that they appear as shortcuts in the Image generation prompt box. + +## Deleting a Model + +Right click on the model in the left panel and choose **"Delete +model"**. It will be removed from the list of models InvokeAI knows +about. If the model was downloaded and installed by the Model Manager, +its weights will be deleted from your file system. If, on the other +hand, you downloaded the model by hand and then imported it into +InvokeAI, the weights will be left alone. + +--- + +## Model formats and families + +[Model Formats and Families](/users-guide/models/model-families/) explains the [formats](/users-guide/models/model-families/#checkpoints-vs-diffusers-formats) models come in, the [components](/users-guide/models/model-families/#submodels) that make up a model, and the [model families](/users-guide/models/model-families/#model-families) InvokeAI supports, with their licenses and links to each family's page. + +--- + +## Advanced Topics + +This section covers several model installation issues that you may +encounter along the way. + +### HuggingFace repos that contain multiple models + +HuggingFace repos can be structured in any way. Some model authors +include multiple models within the same folder. In this situation, you +may need to provide some additional information to identify the model +you want, by adding `:subfolder_name` to the repo ID. + +:::note[Example] + Say you have a repo ID `monster-labs/control_v1p_sd15_qrcode_monster`, and the model you want is inside the `v2` subfolder. + + Add `:v2` to the repo ID and use that when installing the model: `monster-labs/control_v1p_sd15_qrcode_monster:v2` +::: + +### How models are identified + +InvokeAI identifies a model from the file itself, and falls back to its name only where the contents are the same: + +- **Standalone 16-channel VAEs.** FLUX.1 and SD3 use the same VAE network with different settings. A diffusers `vae/` folder is identified by its `config.json`, so FLUX.1-dev's folder is filed as FLUX.1 and SD3.5's as SD3. A single file such as `sd3_vae.safetensors` is identified by its name, folder or download source; if none of them names a model family, it is filed as FLUX.1. If a VAE lands under the wrong base, change **Base** in the model's **Edit** view. A `vae/` folder installed before this detection existed can be fixed with **Re-identify model**. +- **FLUX.2 Klein 9B vs. Klein 9B Base.** The two are identical in structure, so the file or repo name decides, and they ship different default step counts (4 against 28). Names that say "distilled" are read as Klein 9B even when they also contain "base". If you installed `Winnougan/Klein9b-Distilled-Base-INT8-Convrot` by repo ID before this was fixed, it was filed as Klein 9B Base; remove and reinstall it. + +## Editing model metadata + +Every model has an editable **Source URL** field alongside its name +and description. Use it to record where a model came from — for +example a Civitai or HuggingFace page — independent of how it was +originally installed. The URL is editable from the model's **Edit** +view and appears as a clickable link in the model header once +set. Models without a URL simply hide the field. + +This is purely metadata: the URL has no effect on loading and is not +used to refresh or reinstall the model. It is mainly useful for going +back to the model's documentation, license, or example prompts later. + +## Bulk actions in the Model Manager + +The Model Manager supports multi-selection for batch operations. + +- **Select multiple models** by clicking with **Ctrl** (Windows / Linux) or **Cmd** (macOS) held, or by using the checkboxes on each row. A sticky header at the top shows the current selection count and is always visible while you scroll. +- Select the bulk action you wish to execute. These actions appear as buttons at the top of the list when one or more models are selected. + - **Delete** — removes every selected model in a single confirmation step. Partial failures (e.g. permission issues) are reported per-model in the result toast. + - **Re-identify** — re-probes every selected model, updating fields that depend on the file contents (type, base, format, variant, etc.). This is the bulk version of the per-model reidentify action. + +:::caution[Re-identify resets custom settings] +Reidentifying a model re-derives its configuration from the file on disk. Any custom settings you've adjusted on those models — default settings, descriptions, trigger phrases — may be overwritten. The confirmation modal warns you about this before running. +::: + +Both actions handle partial failures: if some models succeed and others fail, the toast lists succeeded and failed counts and the list view updates immediately for the ones that worked. + +## Finding orphaned models + +If a model file is deleted or moved outside the Model Manager, its +database entry sticks around. To find these orphaned entries: + +1. Open the Model Manager. +2. Click the "..." menu at the top right of the installed model list, and pick **"Clean up orphaned models..."** +3. If there are orphaned models, a dialogue box appears that lists them and offers to remove their database entries. + +Orphaned models are automatically excluded from selection dropdowns +(main model, LoRA, VAE, etc.), so you cannot accidentally pick one for +generation. + +## Exporting and Importing Model Settings + +Each installed model has an **Export Settings** and **Import Settings** action in the Model Manager. Use these to back up a model's configuration, move it to another install, or share a curated setup with someone else. + +### What gets exported + +The exported `.json` file captures the configuration you have set on the model, not the model weights themselves: + +- `default_settings` — steps, CFG / guidance, scheduler, dimensions, FP8 storage toggle, VAE precision, etc. +- `trigger_phrases` — for LoRAs and similar. +- `cpu_only` — for encoder-type models. +- `name`, `description`, `source_url` — the model's identifying metadata. +- `cover_image` — the model's thumbnail, embedded as a base64 data URL. + +Fields you have not set are omitted from the file. The format is forward and backward compatible: older clients ignore newer fields, and a file produced by a newer version still imports cleanly into an older one (it just skips the fields it does not understand). + +### Importing + +Importing applies the JSON to the currently selected model: + +- `default_settings`, `trigger_phrases`, `cpu_only`, `name`, `description`, and `source_url` are applied via the normal model update path. Any field that the target model type does not support (e.g. `cpu_only` on a model that has no such setting) is listed in a "skipped" toast — everything else still applies. +- `cover_image` is uploaded and set as the model's thumbnail. + +Imports are validated before they run. The file is rejected if `source_url` is not an `http(s)://` URL or if `cover_image` is not a valid image data URL — so a malformed or hand-edited file cannot quietly poison a model's configuration. + +### Typical workflows + +- **Back up a model you've spent time tuning** so you can restore its settings after a reinstall, or roll back after experimenting. +- **Copy settings between two installs of the same model** — e.g. between a desktop and a workstation. +- **Share a curated setup** (name, description, thumbnail, default steps / CFG / scheduler, trigger phrases) for a model you have configured well. diff --git a/docs/src/content/docs/Users Guide/03.Models/02.model-families.mdx b/docs/src/content/docs/Users Guide/03.Models/02.model-families.mdx new file mode 100644 index 00000000000..f6809c3f6d4 --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/02.model-families.mdx @@ -0,0 +1,135 @@ +--- +title: Model Formats and Families +description: An overview of the models InvokeAI supports — checkpoint, Diffusers and quantized formats, the components that make up a model, and the model families with their licenses. +lastUpdated: 2026-09-30 +sidebar: + order: 2 +--- + +There are a bewildering number of model families, types and formats. This page provides a brief overview of the models that InvokeAI supports: the formats they come in, the components that make up a model, and the families of models, each with its own page under **Local Models**. + +## Checkpoints vs Diffusers Formats + +Model weights are distributed in two main layouts, and InvokeAI +installs both: + +* **Diffusers** -- A folder, usually a HuggingFace repository, laid + out the way the HuggingFace `diffusers` library expects. Each + component of the model (the denoiser, the VAE, the text encoders, + the tokenizers and the scheduler settings) lives in its own + subfolder with its own configuration file. A Diffusers install is + self-contained: everything the model needs comes with it. Install + one by pasting its repository ID, such as + `stabilityai/stable-diffusion-xl-base-1.0`, into the Model Manager. +* **Checkpoint** (single file) -- One file, usually `.safetensors` + (older models may use `.ckpt` or `.pt`), holding the weights of one + or more components. This is the usual format on Civitai. Single-file + checkpoints for Stable Diffusion 1.x and SDXL generally include the + VAE and text encoders. Checkpoints for most newer architectures + contain only the transformer, so you also have to install the VAE + and text encoder that go with them, either separately or as part of + the family's starter bundle. + +Newer models are often distributed in **quantized** forms, which +store the weights at reduced precision to save disk space and +VRAM. InvokeAI recognizes several of these, including **GGUF** files +(named for their quantization level, such as `Q4_K_M` or `Q8_0`), +**fp8**, **int8** and **nf4** builds. Like other single-file +checkpoints, a quantized file usually carries only the transformer +and needs separately installed companions. Lower precision trades a +little image quality for a large saving in memory; each model family's +page lists the builds it supports. + +### Quantized formats + +InvokeAI recognizes the format from the file's contents, so you install them like any other model: + +- **GGUF** (`.gguf`, llama.cpp quantization such as Q4_K or Q8_0): weights stay packed in memory and are unpacked layer by layer during generation. Supported for the FLUX.1, FLUX.2, Z-Image, Krea-2, Qwen-Image and Wan transformers, and for the T5, Qwen3, Qwen3-VL, Mistral and Gemma 2 text encoders. +- **NF4** (bitsandbytes): 4-bit, CUDA only. +- **SDNQ**: see [SDNQ Quantization](/configuration/optimization/sdnq-quantization/). +- **int8** (`int8_convrot`, from ComfyUI repackages): the weights stay int8 in memory on every device, including Apple Silicon, and need no setting to turn on. Supported for FLUX.1, FLUX.2 Klein, Z-Image, Krea-2, Ideogram 4, MiniMax H3, LTX-2 and the PiD decoders, and for the Qwen3 8B, Qwen3-VL and LTX-2 Gemma encoders. +- **nvfp4** (4-bit, in ComfyUI's checkpoint format, such as Comfy-Org's builds; other nvfp4 exports such as AWQ are refused): the weights stay packed in memory and are unpacked per layer, so no special GPU is needed. Supported for the Z-Image, Krea-2, Qwen-Image and LTX-2 transformers and for the Qwen3, Qwen2.5-VL and FLUX.2 Mistral encoders; the Ideogram 4 nvfp4 files are refused at install. +- **fp8** (plain or `fp8_scaled`): InvokeAI keeps the weights in fp8 when [FP8 Storage](/configuration/optimization/fp8-storage/) is on, which installation does for you, or when `fp8_compute` is enabled. +- **MXFP8** (fp8 with one scale per block of 32 weights): loaded by unpacking to BF16. Turn FP8 Storage off for these; see the [FP8 Storage caution](/configuration/optimization/fp8-storage/). + +Text encoders for Qwen-Image, Z-Image, Krea-2 and Ideogram 4 bring their tokenizer and configuration with InvokeAI, so single-file and GGUF encoders load without network access. + +### Converting to Diffusers + +A single-file Stable Diffusion 1.x, 2.x or SDXL model can be converted +to Diffusers format with the **Convert to diffusers** action in the +model's detail view. Conversion replaces the single file with an +equivalent Diffusers folder. Converting is optional: InvokeAI runs +the single-file checkpoint directly. + +## Submodels + +What we call "a model" is really a pipeline of several cooperating +networks, called submodels or components. The prompt is split into +tokens and turned into numbers by the text encoder. The denoiser then +starts from random noise and, over a series of steps, gradually shapes +it into an image guided by those numbers. It works in a compressed +"latent" space rather than on pixels, so at the end the VAE decodes +the result into the image you see. + +A Diffusers install carries all of these components together. With +single-file and quantized installs you often pick some of them +yourself. They appear as separate models in the Model Manager, and +generation panels show slots for them (for example **VAE**, **Text +encoder** or **Model components**) when the selected model needs +them. Several families share components: FLUX.2 and Ideogram 4 use +the same VAE, for example, so one download can serve both. + +| Component | What it does | +|---|---| +| **Denoiser** (UNet or transformer) | The core of the model and by far the largest part. At each step it predicts how to remove noise from the latent image, steered by the encoded prompt. Older models (SD 1.x, SDXL) use a convolutional **UNet**; newer ones use a **diffusion transformer** (DiT). Some models use two: the Wan 2.2 A14B models hand off from a high-noise expert to a low-noise expert partway through, and Ideogram 4 runs a conditional and an unconditional transformer side by side. | +| **VAE** (variational autoencoder) | Translates between pixels and latents. The encoder half compresses an input image (for image-to-image, inpainting or a video's first frame) into latents; the decoder half turns the finished latents back into pixels. A different VAE can change color and fine detail. | +| **Tokenizer** | Splits the prompt into tokens, the word pieces the text encoder understands. It is a small vocabulary file rather than a neural network, and it always comes with its text encoder. | +| **Text encoder** | Turns the tokens into embeddings that tell the denoiser what to draw. Older models use CLIP; newer ones use large language models such as T5, Qwen3, Mistral or Gemma, which understand long, detailed prompts much better. These can be as large as the denoiser, and several families let you run them from a separately installed, quantized file. SDXL and FLUX.1 combine two encoders. | +| **Image encoder** | Turns a reference image into embeddings the denoiser can use, the way a text encoder does for text. Used by IP-Adapters and FLUX Redux (CLIP Vision or SigLIP) to carry the style or content of a reference image. Vision-language text encoders such as Qwen-VL read reference images directly, which is how image-editing models like Qwen Image Edit see the image they edit. | +| **Scheduler** | The algorithm that decides how much noise to remove at each step. It is configuration rather than a network; you can pick a different scheduler for many models in the generation settings. | +| **Audio VAE** | Video-with-audio models only (LTX-2, MiniMax H3). The audio counterpart of the VAE: it encodes and decodes the latent form of the soundtrack that is denoised alongside the video. | +| **Vocoder** | LTX-2 only. Converts the decoded audio spectrogram into a playable waveform. MiniMax H3's audio VAE produces a waveform directly and needs no vocoder. | +| **Connectors** | LTX-2 only. A small network that adapts the Gemma text encoder's output, taken from every layer, into the form the LTX-2 transformer expects. | +| **Latent upsampler** | LTX-2 only. Enlarges the latent video between the two passes of two-stage generation, so the second pass can refine at higher resolution. | + +## Model Families + +Every main model belongs to a family, also called its **base** or +architecture. The family determines which components, LoRAs, +ControlNets and other add-ons work with the model: a LoRA trained for +SDXL cannot be used with FLUX.1, for example. InvokeAI detects the +family when you install a model, shows it as a colored badge in the +Model Manager, and only offers add-ons that match the selected main +model. + +Families differ widely in size, speed and specialty. Older families +such as SD 1.5 and SDXL run on modest hardware and have huge +libraries of community fine-tunes, LoRAs and ControlNets. Newer +families follow long prompts more faithfully and render text better, +but need more VRAM. Many newer families also come in distilled +"Turbo" or "Lightning" variants that trade some flexibility for much +faster generation. + +| Family | Generates | Highlights | License | Details | +|---|---|---|---|---| +| **Stable Diffusion 1.x / 2.x** | Images | The original open model family. Small and fast, with the largest library of fine-tunes, LoRAs, ControlNets and IP-Adapters. Best at 512×512. | OpenRAIL-M; varies by fine-tune | [SD 1.5 / 2.x](/users-guide/models/local-models/stable-diffusion-1-2/) | +| **SDXL** | Images | Higher-resolution successor to SD 1.5 (1024×1024) with a similarly rich ecosystem of add-ons. An optional refiner model, used in the workflow editor, polishes fine detail. | OpenRAIL++-M; varies by fine-tune | [SDXL](/users-guide/models/local-models/sdxl/) | +| **Stable Diffusion 3.5** | Images | Medium and Large transformer models from Stability AI, with three text encoders. | Community, free under $1M revenue | [SD 3.5](/users-guide/models/local-models/sd-3/) | +| **FLUX.1** | Images | Dev, Schnell, Kontext (image editing) and Krea variants, plus Fill (inpainting, workflow editor only) and Redux, with ControlNets and IP-Adapters. | schnell: Apache 2.0; others non-commercial | [FLUX.1](/users-guide/models/local-models/flux-1/) | +| **FLUX.2** | Images | Klein 4B and 9B, and the much larger [dev]. Generates and edits images from reference images. | Klein 4B: Apache 2.0; Klein 9B and [dev] non-commercial | [FLUX.2](/users-guide/models/local-models/flux-2/) | +| **CogView4** | Images | THUDM's 6B text-to-image model with a GLM text encoder. Basic support, without LoRAs, ControlNets or reference images. | Apache 2.0 | [CogView4](/users-guide/models/local-models/cogview4/) | +| **Z-Image** | Images | 6B model in a fast, few-step Turbo variant and a Base variant. Understands English and Chinese prompts, with ControlNets. | Apache 2.0 | [Z-Image](/users-guide/models/local-models/z-image/) | +| **ERNIE-Image** | Images | Baidu's 8B text-to-image model, in standard and distilled Turbo variants. | Apache 2.0 | [ERNIE-Image](/users-guide/models/local-models/ernie-image/) | +| **Qwen Image** | Images | Text-to-image (2512) and instruction-based image editing (Edit 2511), with fast Lightning variants. | Apache 2.0 | [Qwen Image](/users-guide/models/local-models/qwen-image/) | +| **Krea-2** | Images | 12B model in fast Turbo and undistilled Raw variants, with training-free style reference. | Community, free under $1M revenue | [Krea-2](/users-guide/models/local-models/krea-2/) | +| **Ideogram 4** | Images | Structured prompts that place described elements in regions of the image. | Non-commercial | [Ideogram 4](/users-guide/models/local-models/ideogram4/) | +| **Anima** | Images | Compact 2B anime-focused model, with ControlNet-LLLite adapters for inpainting and sketch guidance. | Non-commercial | [Anima](/users-guide/models/local-models/anima/) | +| **Wan 2.2** | Video, images | Text-to-video and image-to-video, including first/last-frame interpolation. Can also generate single images. | Apache 2.0 | [Wan 2.2](/users-guide/video-generation/wan-22/) | +| **LTX-2** | Video with audio | Video with a synchronized soundtrack, audio-to-video and video-to-audio, keyframes and extension. | Community, free under $10M revenue | [LTX-2](/users-guide/video-generation/ltx-2/) | +| **MiniMax H3** | Video with audio | Video with a stereo soundtrack, from first/last frames or from up to 12 reference images and videos. | Community, with conditions | [MiniMax H3](/users-guide/video-generation/minimax-h3/) | + +The video families are described in the [Video +Generation](/users-guide/video-generation/introduction/) section, and +are also listed under Local Models in the sidebar. + diff --git a/docs/src/content/docs/Users Guide/03.Models/External Models/01.introduction.mdx b/docs/src/content/docs/Users Guide/03.Models/External Models/01.introduction.mdx new file mode 100644 index 00000000000..91d83a792a0 --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/External Models/01.introduction.mdx @@ -0,0 +1,58 @@ +--- +title: Using external models +--- + +External models let you generate images in Invoke by calling third-party image generation APIs instead of running a model locally. This is useful when: + +- You don't have the GPU or VRAM to run a model locally. +- You want access to closed-source models (e.g. GPT Image, Gemini). +- You need a specific provider capability (very high resolutions, fast batches, bilingual text rendering, etc.). + +External models appear in the model picker alongside locally installed models. Generations are routed to the provider's API, billed against your provider account, and the resulting images are imported back into Invoke like any other generation. + +## Supported Providers + +- [Google Gemini](/users-guide/models/external-models/gemini/) — Gemini 2.5 Flash Image, Gemini 3 Pro Image Preview, Gemini 3.1 Flash Image Preview +- [OpenAI](/users-guide/models/external-models/openai/) — GPT Image 1 / 1.5 / 1-mini, DALL·E 3 +- [BytePlus Seedream](/users-guide/models/external-models/seedream/) — Seedream 5.0, 5.0 Lite, 4.5, 4.0 +- [Alibaba Cloud DashScope](/users-guide/models/external-models/alibabacloud/) — Qwen Image 2.0 / 2.0 Pro / Max / Edit Max, Wan 2.6 T2I + +## Configuring API Keys + +External provider credentials are stored in a dedicated `api_keys.yaml` file alongside `invokeai.yaml` in your Invoke root directory. + +```yaml +# api_keys.yaml +external_gemini_api_key: "your-gemini-api-key" +external_openai_api_key: "your-openai-api-key" + +# Optional: override the provider base URL (e.g. for a compatible proxy or regional endpoint) +external_gemini_base_url: "https://generativelanguage.googleapis.com" +external_openai_base_url: "https://api.openai.com" +``` + +Restart Invoke after editing `api_keys.yaml` so the new values are picked up. + +!!! warning "Keep your keys private" + `api_keys.yaml` contains secrets. Do not commit it to version control and do not share it with other users of your machine. + +## Installing External Models + +External models are listed in the starter models dialog under their provider. Install them like any other starter model — Invoke records a model reference but does not download weights (there are no weights to download). + +Once installed, external models show up everywhere a model can be selected. Choose one, set the usual parameters (prompt, dimensions, num images, etc.), and invoke as normal. + +## Capabilities and Settings Visibility + +Each external model declares its own **capabilities** — for example: + +- Which generation modes it supports (`txt2img`, `img2img`). Inpainting is not currently supported by any external provider. +- Whether it accepts reference images, and how many. +- Which aspect ratios and resolutions it allows. +- Whether it supports a negative prompt, seed, or batch size > 1. + +Invoke uses these capabilities to drive the UI: only the settings a given model actually supports will be shown in the parameters panel. If a field you expect is missing, it's because the selected model does not support it. + +## Costs and Rate Limits + +External providers charge for each request. Check the provider's pricing page before running large batches. Rate-limit errors from the provider are surfaced in Invoke as generation failures — wait a moment and try again, or lower your concurrent batch size. diff --git a/docs/src/content/docs/features/External Models/alibabacloud.mdx b/docs/src/content/docs/Users Guide/03.Models/External Models/alibabacloud.mdx similarity index 100% rename from docs/src/content/docs/features/External Models/alibabacloud.mdx rename to docs/src/content/docs/Users Guide/03.Models/External Models/alibabacloud.mdx diff --git a/docs/src/content/docs/features/External Models/gemini.mdx b/docs/src/content/docs/Users Guide/03.Models/External Models/gemini.mdx similarity index 100% rename from docs/src/content/docs/features/External Models/gemini.mdx rename to docs/src/content/docs/Users Guide/03.Models/External Models/gemini.mdx diff --git a/docs/src/content/docs/features/External Models/openai.mdx b/docs/src/content/docs/Users Guide/03.Models/External Models/openai.mdx similarity index 100% rename from docs/src/content/docs/features/External Models/openai.mdx rename to docs/src/content/docs/Users Guide/03.Models/External Models/openai.mdx diff --git a/docs/src/content/docs/features/External Models/seedream.mdx b/docs/src/content/docs/Users Guide/03.Models/External Models/seedream.mdx similarity index 100% rename from docs/src/content/docs/features/External Models/seedream.mdx rename to docs/src/content/docs/Users Guide/03.Models/External Models/seedream.mdx diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/anima.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/anima.mdx new file mode 100644 index 00000000000..3ca2830fbac --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/anima.mdx @@ -0,0 +1,118 @@ +--- +title: Anima +description: Generate anime-style images with Anima, a 2B Cosmos Predict2 diffusion transformer with an LLM adapter, including its schedulers, regional prompting and ControlNet-LLLite adapters. +lastUpdated: 2026-09-30 +sidebar: + order: 1 +--- + +Anima is a **2B-parameter**, anime-focused text-to-image model built on NVIDIA's **Cosmos Predict2** +diffusion transformer. Instead of a large text encoder, it pairs a small **Qwen3 0.6B** encoder with a +built-in **LLM adapter** that translates the encoder's output for the transformer. It decodes with the +16-channel Wan 2.1 / Qwen Image VAE. + +InvokeAI supports Anima for text-to-image, and on the Canvas for image-to-image, inpainting, outpainting +and regional prompts. + +## License + +Anima is released under the **CircleStone Labs Non-Commercial License**, and, because it is built on Cosmos-Predict2, the NVIDIA Open Model License also applies. The weights may not be used commercially; the model card says images you generate may be. The download is not gated. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +Anima is small by current standards: the transformer is a ~4.5 GB download, the Qwen3 encoder ~1.2 GB +and the VAE ~200 MB. On low-VRAM GPUs, +[Low-VRAM mode](/configuration/optimization/low-vram-mode/) and +[FP8 Storage](/configuration/optimization/fp8-storage/) both apply to Anima. + +## Installing + +Install the **Anima** bundle from the Model Manager's Starter Models. It contains: + +- **Anima Base 1.0** — the main model (single-file checkpoint, ~4.5 GB) +- **Anima Qwen3 0.6B Text Encoder** (~1.2 GB) +- **Anima QwenImage VAE** (~200 MB) +- **Anima LLLite Inpainting** and **Anima LLLite Sketch** — ControlNet-LLLite adapters (see + [below](#controlnet-lllite-adapters)) + +Installing *Anima Base 1.0* on its own also installs the encoder and VAE as dependencies. Anima is +distributed as a single file only, so the three components are always separate models: + +| Component | Model | +| ------------ | -------------------------------------------------------------- | +| Transformer | the Anima checkpoint (Cosmos Predict2 DiT + LLM adapter) | +| Text encoder | a **Qwen3 0.6B** encoder | +| VAE | the 16-channel Wan 2.1 / Qwen Image VAE | + +Select the **Qwen3 Encoder** and **VAE** in the **Components** section next to the model; both are +required. Only the 0.6B Qwen3 encoder is offered — the larger Qwen3 encoders used by Z-Image and FLUX.2 +are not compatible. The same VAE file may be installed under the Anima, Qwen Image or Wan base, and all +three are listed. FLUX VAEs are **not** compatible. + +Anima also needs a T5-XXL tokenizer, which ships with InvokeAI; no T5 model has to be installed. + +## Generation settings + +Selecting an Anima model applies these defaults: + +- **Steps:** 35 +- **CFG Scale:** 4.5 (the minimum is 1, which turns CFG off) +- **Scheduler:** Euler +- **Size:** 1024×1024; width and height snap to multiples of 8. + +The **Scheduler** menu offers Anima's own set: **Euler**, **Heun (2nd order)**, **DPM++ 2M**, +**DPM++ 2M SDE**, **ER-SDE** and **LCM**. + +The **negative prompt** is used only when CFG Scale is above 1; at CFG 1 it is ignored. + +## Regional prompting + +On the Canvas, **Regional Guidance** layers with a positive prompt work with Anima: each region's prompt is +steered to its masked area, alongside the global prompt. The restriction is applied on alternating +transformer blocks, leaving the others unrestricted to keep the image coherent. Regional negative prompts and regional +reference images are not supported. + +In the workflow editor, the **Prompt - Anima** node has an optional **mask** input, and **Denoise - +Anima** accepts a collection of conditionings for its positive and negative inputs. + +## LoRA + +Anima LoRAs (Kohya / LyCORIS format) are supported. They apply to the transformer and, where the LoRA +includes text-encoder layers, to the Qwen3 encoder. In the workflow editor use **Apply LoRA - Anima** or +**Apply LoRA Collection - Anima**. + +## ControlNet-LLLite adapters + +Anima supports kohya-ss's **ControlNet-LLLite** adapters, small models (8–66 MB) that condition the +transformer on an image. These are available **in the workflow editor only**: add an **Anima +ControlNet-LLLite** node, give it the conditioning image and a control model, and connect it to the +**Control LLLite** input of **Denoise - Anima**. Several adapters can be combined by collecting them, but +each adapter model may be used only once per generation. Each node has **Weight**, **Begin Step +Percent** and **End Step Percent** settings. + +| Starter model | Purpose | +| ----------------------------------- | -------------------------------------------------------------- | +| **Anima LLLite Inpainting** | Conditions on the masked image during inpainting/outpainting. Requires the node's **mask** input (white = area to inpaint). | +| **Anima LLLite Sketch** | Mixed scribble / HED / lineart / grayscale conditioning | +| **Anima LLLite Depth (Preview3)** | Depth | +| **Anima LLLite Scribble (Preview3)**| Scribble | +| **Anima LLLite Lineart (Preview3)** | Lineart | +| **Anima LLLite Pose (Preview3)** | Pose | + +:::caution[Preview3 adapters] +The four *Preview3* adapters were trained on an earlier Anima build and give reduced quality on Anima +Base 1.0; the pose adapter is notably weak. Only the Inpainting and Sketch adapters are in the bundle. +::: + +## Workflow nodes + +| Node | Purpose | +| ---------------------------- | --------------------------------------------------------- | +| **Main Model - Anima** | Loads the transformer, Qwen3 encoder and VAE | +| **Prompt - Anima** | Encodes a prompt, with an optional regional mask | +| **Denoise - Anima** | Runs sampling; accepts img2img latents, masks and LLLite adapters | +| **Image to Latents - Anima** | VAE encode | +| **Latents to Image - Anima** | VAE decode | +| **Anima ControlNet-LLLite** | Configures one ControlNet-LLLite adapter | diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/cogview4.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/cogview4.mdx new file mode 100644 index 00000000000..89481e9721f --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/cogview4.mdx @@ -0,0 +1,64 @@ +--- +title: CogView4 +description: Generate images with the CogView4-6B text-to-image model from THUDM. +lastUpdated: 2026-09-30 +sidebar: + order: 2 +--- + +CogView4 is THUDM's 6B-parameter diffusion-transformer text-to-image model. It uses a **GLM** language +model as its text encoder and a 16-channel VAE. InvokeAI supports the base **CogView4-6B** checkpoint. +CogView4 support is basic: plain generation and Canvas image editing work, but the adapters and +controls that other families offer are not available. + +## License + +CogView4-6B is released under **Apache 2.0**, which allows commercial use. The download is not gated. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +The Diffusers download is about **31 GB**, including the GLM text encoder and VAE. See +[System Requirements](/start-here/system-requirements/) for general guidance, and the +[Low VRAM guide](/configuration/optimization/low-vram-mode/) if you run out of memory. + +## Installing + +Install **CogView4** from the Model Manager's starter models. Its source is `THUDM/CogView4-6B`. This +family has no starter bundle. + +CogView4 installs in **Diffusers** format only, as a single folder that contains the transformer, GLM +text encoder, tokenizer and VAE. No other models are needed, and the components cannot be replaced +individually. Single-file and GGUF CogView4 models are not recognized. + +## Generation settings + +When you select CogView4, InvokeAI applies the defaults from the model's reference example: + +| Setting | Default | +| ---------- | ----------- | +| Steps | 50 | +| CFG Scale | 3.5 | +| Resolution | 1024 × 1024 | + +- **CFG Scale** applies true classifier-free guidance. +- **Width and height** must be multiples of **32**. +- **Negative prompts** are supported and always used. +- **No scheduler choice.** CogView4 uses its own flow-matching sampler, so the Scheduler control is + hidden. +- **Prompt weighting** (Compel syntax) does not apply to CogView4. See + [Prompt Syntax](/users-guide/image-generation/advanced-prompt-syntax/#attention-weighting). The GLM encoder reads up to **1024 + tokens**, so long, descriptive prompts are fine. + +## Canvas + +On the Canvas, CogView4 supports text-to-image, image-to-image, inpainting and outpainting. LoRAs, +ControlNet, reference images, regional guidance and PiD decoding are **not** available for CogView4. + +## Workflow editor + +The CogView4 nodes are **Main Model - CogView4**, **Prompt - CogView4**, **Denoise - CogView4**, +**Image to Latents - CogView4** and **Latents to Image - CogView4**. Each prompt needs its own **Prompt - +CogView4** node. Connect them to the denoise node's positive and negative conditioning inputs. Both are +required. diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/ernie-image.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/ernie-image.mdx new file mode 100644 index 00000000000..6fb48f7d83c --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/ernie-image.mdx @@ -0,0 +1,107 @@ +--- +title: ERNIE-Image +description: Generate images with Baidu's ERNIE-Image and ERNIE-Image Turbo, from the Diffusers pipelines or the single-file transformers. +lastUpdated: 2026-09-30 +sidebar: + order: 3 +--- + +ERNIE-Image is Baidu's 8B single-stream diffusion-transformer text-to-image family. It encodes prompts +with a **Ministral 3B** text encoder and decodes with the 32-channel **FLUX.2 VAE**. Two checkpoints are +supported: + +- **ERNIE-Image** is the undistilled model. It runs at **50 steps** with **CFG 4.0** and supports negative + prompts. +- **ERNIE-Image Turbo** is the distilled variant with the same architecture, tuned for **8 steps** with + **CFG disabled** (CFG Scale `1.0`). + +ERNIE-Image is **text-to-image only**. It can be used in the Generate tab and in the workflow editor, but +not for image-to-image, inpainting or outpainting on the Canvas. + +## License + +ERNIE-Image, ERNIE-Image Turbo and the Ministral 3B text encoder are released under **Apache 2.0**, which allows commercial use. None of the downloads is gated. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +See the [System Requirements](/start-here/system-requirements/) table. For scale: the single-file +transformer alone is about 16 GB and the standalone Ministral 3B encoder about 7.7 GB. On smaller +cards, [Low-VRAM mode](/configuration/optimization/low-vram-mode/) lets InvokeAI stream models that +don't fit entirely in VRAM. + +## Installing + +The easiest path is the **ERNIE-Image** bundle in the Model Manager. It installs **ERNIE-Image Turbo** as a +Diffusers pipeline. The undistilled ERNIE-Image is left out of the bundle because of its size, but it can +be installed individually from the starter models. + +ERNIE-Image needs three components: + +| Component | Diffusers install | Single-file install | +| --------------------------- | ----------------- | -------------------- | +| Transformer | bundled | the `.safetensors` file | +| Text encoder (Ministral 3B) | bundled | installed separately | +| VAE (FLUX.2) | bundled | installed separately | + +The starter models: + +| Starter | Source | Also installs | +| ------------------------------- | ----------------------------------------- | -------------------------------------- | +| ERNIE-Image | `baidu/ERNIE-Image` (Diffusers) | nothing, everything is bundled | +| ERNIE-Image Turbo | `baidu/ERNIE-Image-Turbo` (Diffusers) | nothing, everything is bundled | +| ERNIE-Image (single file) | Comfy-Org single file (~16 GB) | Ministral 3B encoder + FLUX.2 VAE | +| ERNIE-Image Turbo (single file) | Comfy-Org single file (~16 GB) | Ministral 3B encoder + FLUX.2 VAE | + +The Diffusers pipelines also carry a prompt-enhancer model (see [Prompt enhancer](#prompt-enhancer)); the +single files do not. GGUF and quantized single files are not supported. + +When a single-file ERNIE-Image model is selected, the **Components** section next to the model asks for: + +- **Mistral Encoder**: the **ERNIE-Image Ministral Encoder** starter. Ministral 3B is a different + architecture from the Mistral Small 3 encoder FLUX.2 uses, and the two are not interchangeable. The + prompt enhancer from the Diffusers folder has the same shape as the encoder but is refused as one, with + a message naming the file to install instead. +- **VAE**: a FLUX.2 VAE. ERNIE-Image uses the same VAE as FLUX.2, which is why it installs under the + FLUX.2 base. + +:::note[Turbo is recognized by name] +ERNIE-Image and ERNIE-Image Turbo share an architecture, so InvokeAI can't tell them apart from the +weights. A model whose name contains **turbo** gets the Turbo defaults (8 steps, CFG 1.0); any other name +gets the base defaults. If you install a Turbo checkpoint under another name, adjust the steps and CFG in +its Default Settings. +::: + +## Generation settings + +Selecting an ERNIE-Image model applies these defaults: + +| Model | Steps | CFG Scale | Scheduler | Size | +| ----------------- | ----- | --------- | --------- | --------- | +| ERNIE-Image | 50 | 4.0 | Euler | 1024×1024 | +| ERNIE-Image Turbo | 8 | 1.0 | Euler | 1024×1024 | + +- **CFG Scale**: `1.0` turns CFG off. Values below `1.0` are not accepted. +- **Negative prompt**: only used when CFG Scale is above `1.0`. With Turbo at `1.0` it has no effect. +- **Schedulers**: *Euler* (the default), *Heun (2nd order)* and *LCM*. +- **Resolution**: width and height must be multiples of 16. + +LoRA, ControlNet, reference images and regional prompting are not available for ERNIE-Image. + +## Prompt enhancer + +The Diffusers ERNIE-Image pipelines include a prompt-enhancer language model that rewrites a short prompt +into a more detailed one, sized for the target width and height. The Generate tab does not use it. To use +it, build a workflow: + +1. In **Main Model - ERNIE-Image**, leave **Use Prompt Enhancer** on (the default) and select a Diffusers + ERNIE-Image model. +2. Connect its **Prompt Enhancer** output to a **Prompt Enhancer - ERNIE-Image** node, and give that node + your prompt and the target width and height. **Temperature** (default `0.6`) and **Top P** (default + `0.95`) control how freely it rewrites. +3. Feed the rewritten prompt into **Prompt - ERNIE-Image**, then on to **Denoise - ERNIE-Image** and + **Latents to Image - ERNIE-Image**. + +If no prompt enhancer is connected, for example with a single-file model, the node passes the prompt +through unchanged. The rewritten prompt is written to the server log. diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/flux-1.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/flux-1.mdx new file mode 100644 index 00000000000..746621ae2f8 --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/flux-1.mdx @@ -0,0 +1,162 @@ +--- +title: FLUX.1 +description: Generate and edit images with FLUX.1 dev, schnell, Krea dev and Kontext, plus FLUX Fill, Redux, IP-Adapter, ControlNets and Control LoRAs. +lastUpdated: 2026-09-30 +sidebar: + order: 4 +--- + +FLUX.1 is Black Forest Labs' family of rectified-flow transformer text-to-image models. It uses two +text encoders (T5-XXL and CLIP-L) and a 16-channel VAE. InvokeAI detects three variants on install: + +- **FLUX.1 dev** — guidance-distilled. Uses a **Guidance** value rather than classifier-free guidance. + **FLUX.1 Krea dev** and **FLUX.1 Kontext dev** are dev-architecture models and are handled the same way. +- **FLUX.1 schnell** — timestep-distilled for **4 steps**. It ignores guidance. +- **FLUX.1 Fill dev** — an inpainting/outpainting model (shown as *FLUX Dev - Fill*). It cannot do + text-to-image. + +The family is supported by a set of add-on models: ControlNets, Control LoRAs, an IP-Adapter, FLUX Redux +and PiD super-resolution decoders. + +## License + +| Model | License | Commercial use | +|---|---|---| +| FLUX.1 schnell | Apache 2.0 | Allowed | +| FLUX.1 dev, Kontext dev, Krea dev, Fill and Redux | FLUX.1 [dev] Non-Commercial License | The weights may not be used commercially. Images you generate may be, except to train a competing model. | +| T5 and CLIP text encoders | Apache 2.0 / MIT | Allowed | + +The starter models download from ungated copies where possible. The FLUX Fill, Redux and Kontext models, and the control LoRAs, come from Black Forest Labs' own gated repositories: accept the license on each model's HuggingFace page and add your HuggingFace token under **API Keys** in the Model Manager. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +See the [System Requirements](/start-here/system-requirements/) table. FLUX.1 needs about **10 GB of +VRAM** and **32 GB of system RAM** at 1024×1024. Download totals with dependencies are roughly **12 GB** +for the NF4 and GGUF Q4 builds, **18 GB** for the int8 build and **33 GB** for bfloat16. The int8 +transformer occupies about 11.5 GB on any supported GPU, so it streams on a 10 GB card. + +To fit a full-precision checkpoint in less memory, see [FP8 Storage](/configuration/optimization/fp8-storage/) +and [Low-VRAM mode](/configuration/optimization/low-vram-mode/). + +## Installing + +The simplest path is the **FLUX.1 dev** bundle in the Model Manager. It installs: + +- FLUX.1 schnell (quantized) and FLUX.1 dev (quantized) +- FLUX.1 Kontext dev (quantized) and FLUX.1 Krea dev (quantized) +- FLUX Fill and FLUX Redux +- the FLUX VAE, the int8 T5 encoder and the CLIP-L encoder +- the InstantX Union ControlNet, the Canny and Depth Control LoRAs, and the XLabs IP-Adapter + +Individual starter models for the main model: + +| Starter model | Format | Notes | +| --- | --- | --- | +| FLUX.1 dev / FLUX.1 schnell | bfloat16 single file | ~33 GB with dependencies (bf16 T5) | +| FLUX.1 dev (quantized) / schnell (quantized) | bitsandbytes NF4 | ~12 GB with dependencies (int8 T5) | +| FLUX.1 dev (int8) | int8 single file (community repack) | ~18 GB with dependencies; half the memory of bf16 on every supported GPU | +| FLUX.1 schnell (SDNQ uint4 + SVD) | [SDNQ](/configuration/optimization/sdnq-quantization/) Diffusers pipeline | ~15 GB, self-contained | +| FLUX.1 Kontext dev (quantized) | GGUF Q4_K_M | ~12 GB with dependencies | +| FLUX.1 Krea dev / Krea dev (quantized) | single file / GGUF Q4_K_M | ~29 GB / ~12 GB with dependencies | +| FLUX Fill | single file | Inpainting model | + +Except for the self-contained SDNQ pipeline, a FLUX.1 main model is only the transformer, and needs three +components. Installing a starter model installs them as dependencies: + +| Component | Starter options | +| --- | --- | +| VAE | `FLUX.1-schnell_ae` (works with every FLUX.1 variant) | +| T5 encoder | bfloat16 (~9.5 GB), bitsandbytes int8 (~5 GB), GGUF Q6_K (~3.9 GB, near-lossless), GGUF Q3_K_S (~2.1 GB, lower quality) | +| CLIP embed | `clip-vit-large-patch14` (~250 MB) | + +Select them in the **Components** section next to the model. The SDNQ pipeline carries its own encoders +and VAE, so its component pickers stay empty. + +Some Black Forest Labs repositories are gated on HuggingFace. If a download asks for an access token, +accept the license on the model's HuggingFace page and add your HuggingFace token under **API Keys** in +the Model Manager. + +## Generation settings + +Selecting a FLUX.1 model applies these defaults: + +| Variant | Steps | Guidance | Scheduler | Size | +| --- | --- | --- | --- | --- | +| dev (incl. Krea, Kontext) | 28 | 3.5 | Euler | 1024×1024 | +| schnell | 4 | — (ignored) | Euler | 1024×1024 | +| Fill | 50 | 30 | Euler | 1024×1024 | + +- **Guidance** — the slider next to Steps sets FLUX's distilled guidance, not CFG. Higher values follow the + prompt more closely and give less varied images. +- **Scheduler** — Euler, Heun (2nd order; better quality at about twice the time per step) or LCM (for few + steps). +- **Size** — width and height must be multiples of 16. +- **Negative prompt** — FLUX.1 has no negative prompt in the Generate tab or on the Canvas. + +Text-to-image, image-to-image, inpainting and outpainting all work with dev and schnell models on the +Canvas. Regional guidance is supported, and a regional layer can +also carry a FLUX Redux reference image. + +## Reference images + +A FLUX.1 model accepts up to **five** reference images. Three kinds are available: + +- **FLUX Redux** — image variation from a reference. Pick a FLUX Redux model on the reference image card + and set **Image influence** from *Lowest* to *Highest* (default). Redux needs the SigLIP image encoder, + which installs with it. +- **IP-Adapter** — the *Standard Reference (XLabs FLUX IP-Adapter v2)* starter model references an image + more loosely. It uses the CLIP ViT-L image encoder, installed as a dependency. +- **Kontext** — when the selected model is FLUX.1 Kontext dev, reference images are passed to Kontext for + instruction-style editing: describe the change you want in the prompt. + +:::note[Kontext is recognized by name] +The Generate tab treats a FLUX.1 model as Kontext only if its name contains "kontext". If you rename a +Kontext model in the Model Manager, keep that word in the name. +::: + +## ControlNets and Control LoRAs + +On the Canvas, a control layer can use either kind of FLUX.1 structural control: + +- **ControlNet** — the *FLUX.1-dev-Controlnet-Union* starter model (InstantX) covers canny, tile, depth, + blur, pose, gray and low-quality inputs. XLabs FLUX ControlNets are also recognized. +- **Control LoRA** — *Hard Edge Detection (Canny)* and *Depth Map* are Black Forest Labs' official Control + LoRAs. A Control LoRA cannot be combined with a FLUX Fill model. + +## FLUX Fill + +FLUX Fill regenerates a masked region of an image. The Generate tab and Canvas refuse a Fill model (they +report that it does not support text-to-image), so use it in the workflow editor: connect a +**FLUX Fill Conditioning** node (image plus inpainting mask) to the **Fill Conditioning** input of +**FLUX Denoise**, and connect the VAE to its `controlnet_vae` input. A guidance of about 30 is +recommended. + +## LoRA + +FLUX.1 LoRAs can be applied from the Generate tab or with the **Apply LoRA - FLUX** and **Apply LoRA +Collection - FLUX** nodes. They patch the transformer and, where the LoRA includes text-encoder layers, the +CLIP and T5 encoders. + +## PiD super-resolution decode + +FLUX.1 latents can be decoded with a PiD decoder, which produces a 4× super-resolved image. Install *PiD +Decoder FLUX (2K)* or *PiD Decoder FLUX (2K to 4K)*; see [PiD Super-Resolution +Decode](/users-guide/image-generation/advanced-topics/pid-decode/). + +## Workflow nodes + +| Node | Purpose | +| --- | --- | +| **Main Model - FLUX** | Loads the transformer, T5, CLIP and VAE | +| **Prompt - FLUX** | Encodes the prompt | +| **FLUX Denoise** | Runs the diffusion; has inputs for ControlNet, Control LoRA, IP-Adapter, Redux, Kontext and Fill conditioning | +| **Latents to Image - FLUX** / **Image to Latents - FLUX** | VAE decode / encode | +| **Kontext Conditioning - FLUX** | Wraps a reference image for Kontext | +| **FLUX Kontext Image Prep** | Resizes the first image to a preferred Kontext resolution and joins up to ten images side by side | +| **FLUX Redux**, **FLUX IP-Adapter**, **FLUX ControlNet**, **Control LoRA - FLUX**, **FLUX Fill Conditioning** | Conditioning for the features above | + +In workflows, **FLUX Denoise** also exposes **CFG Scale** with a negative conditioning input (CFG is off at +the default of 1.0), and a **DyPE** preset for resolutions above the model's native size: *Auto +(>1536px)*, *Area (auto)*, *4K Optimized* or *Manual*. diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/flux-2.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/flux-2.mdx new file mode 100644 index 00000000000..e44f067adcd --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/flux-2.mdx @@ -0,0 +1,170 @@ +--- +title: FLUX.2 +description: Generate and edit images with FLUX.2 Klein (4B and 9B, distilled and Base) and FLUX.2 [dev], including multi-reference image editing. +lastUpdated: 2026-09-30 +sidebar: + order: 5 +--- + +FLUX.2 is Black Forest Labs' second-generation rectified-flow transformer family. It uses a 32-channel VAE +and a large language model as its text encoder. InvokeAI supports these variants and detects them on +install: + +| Variant | Text encoder | Notes | +| --- | --- | --- | +| **Klein 4B** | Qwen3 4B | Distilled for **4 steps** | +| **Klein 9B** | Qwen3 8B | Distilled for **4 steps** | +| **Klein 4B Base** / **Klein 9B Base** | Qwen3 4B / Qwen3 8B | Undistilled foundation models; **28 steps** | +| **FLUX.2 [dev]** | Mistral Small 3.1 | 32B guidance-distilled transformer; **28 steps**; non-commercial license | + +Every variant can take reference images for image editing (see [Reference images](#reference-images)). + +:::note[Klein vs. Klein Base] +A Klein model and its Base model have identical architectures, so InvokeAI tells them apart by the file or +repository name: a name containing "base" (and not "distilled") is identified as Base. If a model was +identified wrongly, change its **Variant** in the model's edit form in the Model Manager. It matters: +Base models need 28 steps, distilled ones 4. +::: + +## License + +| Model | License | Commercial use | Gated | +|---|---|---|---| +| FLUX.2 Klein 4B | Apache 2.0 | Allowed | No | +| FLUX.2 Klein 9B | FLUX Non-Commercial License | The weights may not be used commercially; generated images may, except to train a competing model | Yes, on Black Forest Labs' repositories | +| FLUX.2 [dev] | FLUX [dev] Non-Commercial License | As for Klein 9B | Yes, on Black Forest Labs' repositories | + +The Qwen3 and Mistral text encoders are Apache 2.0. Some repackaged copies show a different license in their metadata; the original model's license is the one that applies. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +See the [System Requirements](/start-here/system-requirements/) table: + +- **Klein 4B** — 12 GB VRAM and 16 GB RAM at 1024×1024; the FP8 build works with 8 GB+. The GGUF Q4 build + runs on 6–8 GB. +- **Klein 9B** — 12 GB VRAM and 32 GB RAM. The int8 build is 9.5 GB on any supported GPU. The FP8 build stays + that small only on an RTX 40-series (or newer) card with `fp8_compute` enabled; otherwise it expands to + about 18 GB. The Qwen3 8B encoder is about 15 GB and streams on a 12 GB card. +- **FLUX.2 [dev]** — the full Diffusers pipeline is about 80 GB. The NF4 Diffusers build runs on about 18 GB + of VRAM with offloading, and the GGUF Q3_K_M transformer fits about 12 GB with offloading. + +See also [FP8 Storage](/configuration/optimization/fp8-storage/) and [SDNQ +quantization](/configuration/optimization/sdnq-quantization/). + +## Installing + +The **FLUX.2 Klein** bundle in the Model Manager is the quickest start. It installs *FLUX.2 Klein 4B (GGUF +Q4)* together with the FLUX.2 VAE and the Qwen3 4B encoder. + +A FLUX.2 model needs three components: + +| Component | Diffusers install | Single-file / GGUF / FP8 / int8 install | +| --- | --- | --- | +| Transformer | bundled | the checkpoint itself | +| VAE (FLUX.2) | bundled | installed separately | +| Text encoder | bundled | Klein 4B: Qwen3 4B · Klein 9B: Qwen3 8B · [dev]: Mistral | + +Starter models that are not Diffusers pipelines install the VAE and the matching encoder as dependencies. +Pick them in the **Components** section next to the model. If you have a FLUX.2 Diffusers model of the +matching variant installed, InvokeAI uses its encoder and VAE automatically, so you don't need standalone +ones. + +### Klein starter models + +| Starter model | Format | Size | +| --- | --- | --- | +| FLUX.2 Klein 4B (Diffusers) / 9B (Diffusers) | Diffusers pipeline | ~16 GB / ~35 GB | +| FLUX.2 Klein 4B | bf16 single file | ~8 GB | +| FLUX.2 Klein 4B (FP8) / 9B (FP8) | FP8 single file | ~4 GB / ~9.5 GB | +| FLUX.2 Klein 9B (int8) | int8 single file (community repack) | ~9.5 GB, same size in memory on every supported GPU | +| FLUX.2 Klein 4B (GGUF Q4 / Q8) | GGUF | ~2.6 GB / ~4.3 GB | +| FLUX.2 Klein 9B (GGUF Q4 / Q8) | GGUF | ~5.8 GB / ~10 GB | +| FLUX.2 Klein 4B (SDNQ dynamic 4-bit) / 9B (SDNQ dynamic 4-bit + SVD) | SDNQ Diffusers pipeline | ~5 GB / ~13 GB, self-contained | + +Standalone Qwen3 encoders are available as starter models too: *FLUX.2 Klein Qwen3 4B Encoder* (~8 GB) and +its *FP4 mixed* build (~3.6 GB download), and *FLUX.2 Klein Qwen3 8B Encoder* (~16 GB) with *FP4 mixed* +(~6.3 GB download) and *int8* (~9.4 GB) builds. A 4B encoder only works with Klein 4B models, an 8B encoder +only with Klein 9B. + +When you install an FP8 checkpoint, FP8 Storage is switched on for it automatically, which keeps the file's +fp8 weights instead of expanding them. + +### FLUX.2 [dev] starter models + +| Starter model | Format | Size | +| --- | --- | --- | +| FLUX.2 [dev] (Diffusers) | Diffusers pipeline | ~80 GB | +| FLUX.2 [dev] (Diffusers, NF4) | NF4-quantized transformer and text encoder | ~18 GB VRAM with offload | +| FLUX.2 [dev] Transformer (GGUF Q3_K_M / Q4_K_M / Q5_K_M / Q6_K / Q8_0) | GGUF | ~15.9 / 20 / 24 / 27.9 / 35.5 GB | + +The GGUF transformers need a separate Mistral encoder. Q3_K_M and Q4_K_M install the GGUF Q4 encoder, the +larger quants the GGUF Q8 encoder. Other encoder starters are the Comfy-Org builds — *FP8* (~18 GB, best +quality for its size), *BF16* (~35.6 GB) and *FP4 mixed* (~12.3 GB download) — and a GGUF *IQ4_XS* build +(~11.1 GB). + +:::caution[License and access] +FLUX.2 [dev] is released under a non-commercial license. Some Black Forest Labs repositories are gated on +HuggingFace: if a download asks for an access token, accept the license on the model's HuggingFace page and +add your HuggingFace token under **API Keys** in the Model Manager. +::: + +## Generation settings + +Selecting a FLUX.2 model applies these defaults: + +| Variant | Steps | Guidance | Scheduler | Size | +| --- | --- | --- | --- | --- | +| Klein 4B / 9B | 4 | — | Euler | 1024×1024 | +| Klein 4B Base / 9B Base | 28 | — | Euler | 1024×1024 | +| [dev] | 28 | 3.5 | Euler | 1024×1024 | + +- **Guidance** — the slider next to Steps sets FLUX.2 [dev]'s distilled guidance (0–20). It has no effect on + Klein models. +- **Scheduler** — Euler, Heun (2nd order; better quality at about twice the time per step) or LCM (for few + steps). +- **Size** — width and height must be multiples of 16. +- **Negative prompt** — not available in the Generate tab or on the Canvas. + +Text-to-image, image-to-image, inpainting and outpainting all work on the Canvas. FLUX.2 has no ControlNet +or IP-Adapter support. + +## Reference images + +Every FLUX.2 model can edit or combine images from up to **five** reference images. Add them on the +Generate tab or the Canvas and describe the result you want in the prompt. No extra model is needed. + +In the workflow editor, wrap each image in a **Kontext Conditioning - FLUX** node and connect it (or a +collection of them) to the **Reference Images** input of **FLUX2 Denoise**. + +## Regional guidance + +Regional guidance layers with a prompt are supported on the Canvas. Regional reference images are not. +Regional prompts are **ignored when reference images are attached**; InvokeAI logs a warning when that +happens. + +## LoRA + +FLUX.2 LoRAs are labelled with the variant they were trained for, and the Generate tab only offers LoRAs +that match the selected model's variant. LoRAs patch the transformer and, where they include text-encoder +layers, the Qwen3 or Mistral encoder. In workflows use **Apply LoRA - Flux2 Klein** or **Apply LoRA - +FLUX.2 [dev]** (and their *Collection* versions). + +## PiD super-resolution decode + +FLUX.2 Klein latents can be decoded with a PiD decoder for a 4× super-resolved image. Install *PiD Decoder +FLUX.2 (2K)* or *PiD Decoder FLUX.2 (2K to 4K)*; see [PiD Super-Resolution +Decode](/users-guide/image-generation/advanced-topics/pid-decode/). + +## Workflow nodes + +| Node | Purpose | +| --- | --- | +| **Main Model - Flux2 Klein** / **Main Model - FLUX.2 [dev]** | Loads the transformer, encoder and VAE | +| **Prompt - Flux2 Klein** / **Prompt - FLUX.2 [dev]** | Encodes the prompt | +| **FLUX2 Denoise** | Runs the diffusion; takes the VAE and optional reference images | +| **Latents to Image - FLUX2** / **Image to Latents - FLUX2** | VAE decode / encode | + +**FLUX2 Denoise** also has a **CFG Scale** and a negative conditioning input, which the Generate tab leaves +off (CFG 1.0). diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/ideogram4.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/ideogram4.mdx new file mode 100644 index 00000000000..35d32a74a11 --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/ideogram4.mdx @@ -0,0 +1,91 @@ +--- +title: Ideogram 4 +description: Generate images with the Ideogram 4 text-to-image models. +lastUpdated: 2026-09-30 +sidebar: + order: 6 +--- +## Ideogram 4 + +Ideogram 4 is an open-weight text-to-image model with a distinctive **structured JSON prompt**: instead of a single sentence, the model is trained to read an overall scene description plus a list of regions, each with a bounding box and its own text. Invoke assembles this JSON for you. + +### License + +Ideogram 4 is released under the **Ideogram 4 Non-Commercial License**. The weights may not be used commercially, and outputs may not be used to build competing models. Ideogram claims no rights in images you generate, but the license does not explicitly grant commercial use of them. The ideogram-ai repositories are gated; the Comfy-Org copy is not, but the same license applies. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +### Installing Ideogram 4 + +The weights are gated on HuggingFace under a **non-commercial license**. Open the model page, accept the terms, and make sure your HuggingFace token is [set up in the config file](/configuration/invokeai-yaml/) before installing. Two bundled builds are available: + +- [`ideogram-ai/ideogram-4-nf4`](https://huggingface.co/ideogram-ai/ideogram-4-nf4) — nf4 quantized, **CUDA only**, fits in 24 GB VRAM. Recommended for NVIDIA GPUs. +- [`ideogram-ai/ideogram-4-fp8`](https://huggingface.co/ideogram-ai/ideogram-4-fp8) — fp8 quantized, runs on **any device**, with higher memory use. + +Paste either repo ID into the Model Manager's HuggingFace / URL field to install. Each folder carries everything Ideogram 4 needs. + +#### Single files + +Comfy-Org's [`Comfy-Org/Ideogram-4`](https://huggingface.co/Comfy-Org/Ideogram-4) repackage is ungated (the same non-commercial license still applies) and ships the parts separately. Ideogram 4 runs **two** transformers — a conditional branch and an unconditional one, both active at every step — so a single-file install is four models, and each starter installs all four together: + +- `diffusion_models/ideogram4_.safetensors` — the conditional branch, selected as the model +- `diffusion_models/ideogram4_unconditional_.safetensors` — the unconditional branch +- `text_encoders/qwen3vl_8b_fp8_scaled.safetensors` — the Qwen3-VL **8B** encoder (not the 4B one Krea-2 uses). A llama.cpp GGUF of the 8B encoder works as well; see [Qwen3-VL GGUF encoders](/users-guide/models/local-models/krea-2/#qwen3-vl-gguf-encoders). +- `vae/flux2-vae.safetensors` — the 32-channel VAE, shared with FLUX.2, which is why it installs under the FLUX.2 base + +Pick the unconditional branch, the encoder and the VAE in the **Components** section next to the model — and pick the unconditional branch of the *same* build as the model. The `nvfp4` repackage in that repo is **not** supported yet and is refused at install. + +Which branch a file holds is read from the file itself, and from the filename for a build that does not record it, so keep these names as published. + +Two builds of the transformers are supported, and they behave differently in memory: + +| | download, per branch | resident, per branch | +|---|---|---| +| **Ideogram 4 (single file, int8)** — `int8_convrot` | 8.9 GB | **8.9 GB**, on every device | +| **Ideogram 4 (single file, fp8)** — `fp8_scaled` | 8.6 GB | **8.7 GB** with `fp8_compute` *or* FP8 Storage; 17.3 GB with neither | + +Both branches are resident at once, so double those figures for the pair. + +:::note[Which to pick] +**On an NVIDIA Ada card (RTX 40-series) or newer, with `fp8_compute` enabled**, take the fp8 build: the two are a wash on memory, and fp8 is the faster one because its weights stay quantized *and* run on the fp8 tensor cores. Measured at 1024px on a 4090, 12 steps: **35.6 s** for the fp8 pair against **45.7 s** for the int8 pair. + +**Without the fp8 matmul, the fp8 build still works — through FP8 Storage.** That setting keeps the file's own fp8 weights and per-tensor scales instead of expanding them, and installation switches it on for these files by itself. Measured on a 4090 with `fp8_compute` off, 1024px: **40.6 s**, 22.9 GB peak, 8.7 GB resident per branch — against **45.7 s** for the int8 pair. With *neither* the matmul nor Storage the weights are expanded to bf16 on load, 17.3 GB per branch, and a 24 GB card runs out of memory during the first step. + +**Use int8 where FP8 Storage is not available.** It requires a CUDA or Intel XPU device; on anything else the fp8 build falls back to the expanded path, while int8 keeps its stored codes and dequantizes them per forward on any device with no setting to turn on. + +On a 24 GB NVIDIA card without the fp8 matmul (RTX 30-series), the int8 pair plus the 8B encoder still adds up to more than the card holds, so the bundled [nf4 build](#installing-ideogram-4) remains the smaller option there. +::: + +### Memory use during generation + +Ideogram 4 keeps both transformer branches loaded for the whole generation. With the single-file builds on a CUDA or ROCm GPU (with partial loading on, the default), InvokeAI checks whether the pair fits next to the working memory a generation needs. If it does not, it keeps only part of the second branch on the GPU and streams the rest in each step. On a 24 GB card with the fp8 pair this keeps about 2.4 GB free instead of 1.2 GB. The bundled nf4 and fp8 folders are not affected. Three things follow: + +- A streamed layer computes on a slightly different path, so images at the same seed can differ from those generated with both branches fully on the GPU. +- If generations still run out of memory, raise `device_working_mem_gb` in `invokeai.yaml` (for example to `8`). +- If you set `max_cache_vram_gb`, InvokeAI does not limit the second branch this way, because the cache already plans from that limit. + +### Prompting Ideogram 4 + +When an Ideogram 4 model is selected, Invoke builds the structured JSON prompt automatically: + +- The **positive prompt** becomes the overall scene description. +- Each enabled **Regional Guidance** layer on the Canvas contributes one element: its drawn box becomes the region's bounding box and its prompt becomes that region's description. Draw a box where you want something and describe it there. +- To drive the model directly, paste a **raw JSON** object into the prompt box — anything starting with `{` is passed through unchanged. + +The exact JSON that was encoded is stored in the image metadata as **Structured Caption**, and can be recalled straight back into the prompt box from the metadata viewer. + +:::note[No negative prompt] +Ideogram 4 does not use a negative prompt — it has a dedicated unconditional branch instead, so the negative prompt box has no effect. +::: + +### Generation settings + +- **Sampler Preset** — the primary quality/speed control. `Quality (48 steps)`, `Default (20 steps)`, and `Turbo (12 steps)` each bundle a step count, a guidance schedule, and the schedule shift. +- **Advanced** overrides (all optional, leave on *Auto* to use the preset's values): **Steps**, **Guidance Scale**, **Schedule Shift (mu)**, and a **Color Palette** that biases the generated colors. + +:::caution[Built-in safety filter] +Ideogram 4 ships with a **content safety filter baked into the released model weights**. When it triggers, the model outputs a placeholder image reading *"Image blocked by safety filter"* instead of your image. This is the model's own filter — it is **not** Invoke's NSFW checker (it fires even with the NSFW checker off) and cannot be disabled from Invoke. + +The filter is known to **false-positive on completely benign prompts**, and the quantized builds can trigger it more readily. Invoke always sends the model a structured JSON prompt, which tends to trip the filter less than plain text; if you hit it, rephrasing or adjusting the prompt usually clears it. +::: + diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/krea-2.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/krea-2.mdx new file mode 100644 index 00000000000..65e9e74b6d8 --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/krea-2.mdx @@ -0,0 +1,171 @@ +--- +title: Krea-2 +description: Generate images with the Krea-2 text-to-image models (Turbo and Raw), including the GGUF / single-file workflow, the conditioning enhancers and training-free style reference. +lastUpdated: 2026-09-30 +sidebar: + order: 7 +--- + +Krea-2 is a ~12B single-stream diffusion-transformer text-to-image family. InvokeAI supports both +published checkpoints: + +- **Krea-2-Turbo** — distilled for fast, low-step generation. Runs at **8 steps** with **CFG disabled** + (CFG Scale `1.0`). This is the recommended checkpoint for everyday use. +- **Krea-2-Raw** — the undistilled base checkpoint. Runs at **more steps (~28)** with **CFG enabled** + (CFG Scale ~`5.5`, equivalent to the reference pipeline's guidance `4.5`) and supports negative prompts. It is primarily intended as a base for + finetuning / LoRA training, but full inference is supported. + +The variant is detected automatically on install, and selecting a Krea-2 model sets sensible defaults +(steps, CFG, 1024×1024) for that variant. + +## License + +Krea-2 Turbo and Raw are released under the **Krea 2 Community License**. Commercial use of the model and its images is allowed only for companies with less than $1M in annual revenue; above that, an Enterprise license is needed. Redistributed copies must add "Krea" to the model name. The full Diffusers repositories are gated: accept the license on the model's HuggingFace page and add your HuggingFace token under **API Keys** in the Model Manager. The quantized GGUF and NVFP4 copies are not gated, but the same license applies. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +Krea-2 is a large model. See the [System Requirements](/start-here/system-requirements/) table for +details. In short, on a 24 GB card enable **FP8** in the model's Default Settings to fit 1024² (with a +LoRA). For lower VRAM, use a smaller transformer build: + +| Transformer build | Transformer size | Notes | +| --------------------------- | ---------------- | ----------------------------------------------------------- | +| NVFP4 (starter model) | ≈ 7.2 GiB | stays packed, unpacked per layer on any GPU | +| GGUF Q4_K_M (starter model) | ≈ 7 GB | stays packed, unpacked per layer | +| GGUF Q8_0 (starter model) | ≈ 13 GB | near-full quality | +| int8 (`int8_convrot`) | ≈ 12.6 GiB | stays int8 on every device, no FP8 setting needed | +| fp8 / `fp8_scaled` | ≈ 12.6 GB | with FP8 Storage (switched on at install) or `fp8_compute` | +| MXFP8 | ≈ 24 GB | unpacked to BF16 at load; prefer the fp8, NVFP4 or GGUF build | + +The Qwen3-VL encoder and the small VAE come on top of these sizes. The encoder takes about 8.5 GB as bf16 +(the starter bundle's encoder), about half that as fp8, and about 2.8 GB as a Q4_K_M +[GGUF Qwen3-VL encoder](#qwen3-vl-gguf-encoders). + +## Installing + +The easiest path is the **Krea-2 launchpad bundle** in the Model Manager, which installs the models and +their dependencies together. + +Krea-2 needs three components: + +| Component | Diffusers install | GGUF / single-file install | +| ----------------------- | ----------------------- | ------------------------------------ | +| Transformer | bundled in the pipeline | the `.gguf` / single-file checkpoint (GGUF, fp8, int8, NVFP4, MXFP8) | +| VAE (Qwen-Image) | bundled | installed separately | +| Text encoder (Qwen3-VL) | bundled | installed separately | + +- **Diffusers** (e.g. `krea/Krea-2-Turbo`, `krea/Krea-2-Raw`): a single ~26 GB install that bundles the + VAE and text encoder. Nothing else is required. +- **GGUF / single-file**: the checkpoint ships **only the transformer**. You must also install a + standalone **Qwen-Image VAE** and a **Qwen3-VL encoder** (both included in the launchpad bundle). + +When you select a GGUF/single-file Krea-2 model, InvokeAI auto-selects an installed VAE and Qwen3-VL +encoder if you have them. If none are installed, you'll be prompted to pick them (in the model dropdowns) +before you can generate. Selecting a Diffusers Krea-2 model clears those standalone selections and uses +the bundled components. + +:::note[Qwen3-VL encoder] +Single-file Qwen3-VL encoders bundle only the weights. Their tokenizer and architecture config ship +with InvokeAI, so these encoders load without network access. A single-file fp8 encoder is kept +resident in fp8 (roughly half the VRAM of the bf16 encoder). +::: + +### Qwen3-VL GGUF encoders + +The Qwen3-VL encoder can also be a llama.cpp GGUF, for the 4B encoder Krea-2 uses and for the 8B encoder +Ideogram 4 uses. A Q4_K_M 4B file is about 2.5 GB and takes about 2.8 GB of VRAM, against 8.5 GB for +the bf16 safetensors encoder, so encoder and transformer fit on the GPU together. Images match the bf16 +encoder closely; the remaining difference is the quantization of the encoder. + +- Use a Qwen3-VL GGUF, such as the files in `Qwen/Qwen3-VL-4B-Instruct-GGUF`. A plain Qwen3 GGUF has the + same shapes but conditions wrongly, so InvokeAI identifies the encoder from the file's + `qwen3vl` architecture tag and does not install a plain Qwen3 file as one. +- The `mmproj-*.gguf` file next to it holds the vision part and is not needed. Installing it is refused + with a message saying so. +- LoRAs with text-encoder layers still apply to a quantized encoder. + +## Conditioning enhancers + +Two optional, off-by-default toggles are available under **Advanced Options** (below CFG Scale). They +transform the text conditioning and are especially useful for the distilled Turbo checkpoint: + +- **Conditioning Rebalance** — per-layer weighting of the text embedding to improve prompt adherence. +- **Seed Variance Enhancer** — injects controlled noise into the conditioning to restore per-seed + diversity (the distilled model otherwise produces near-identical images across seeds), trading some + prompt adherence for variety. + +Both are recorded in image metadata and can be recalled. +When enabled on the canvas, the same enhancer chain is applied independently to the global prompt and +each positive regional prompt before their conditionings are collected. + +## Multiple conditionings + +In the workflow editor, the **Denoise - Krea-2** node accepts one conditioning or a collection for both +its positive and negative conditioning inputs. Multiple independently encoded conditionings are concatenated +after padding tokens are removed. + +The **Text Encoder - Krea-2** node also accepts an optional mask. A masked conditioning applies to that +image region; an unmasked conditioning applies to the background not covered by any regional mask. If +regional masks cover the full image, an unmasked conditioning falls back to the full image instead of being +ignored. Krea-2 uses restricted attention on alternating main transformer blocks, leaving the other blocks +unrestricted to preserve image-wide coherence. Positive regional prompts are available on the canvas. In +workflows, masked conditioning collections can also be supplied to the negative input when CFG is enabled. +Canvas regional negative prompts, auto-negative, and regional reference images are not supported. + +:::caution[Regional prompting memory] +Regional prompting builds a dense boolean attention mask whose memory grows quadratically with the combined +text and image token count. Invoke reserves memory for retained masks, construction scratch space, and the +dtype-sized additive attention bias created by SDPA. Fused memory-efficient attention support for dense masks +still depends on the GPU, PyTorch build, dtype, and sequence shape. When no fused kernel is available, PyTorch +falls back to math attention, which may use substantially more VRAM and can cause an out-of-memory error. +Reducing image resolution or disabling regional prompting is the practical fallback. Automated CUDA coverage +is skipped on unsupported hardware and cannot exercise every GPU/backend combination. +::: + +## Style reference + +Krea-2 can transfer the *look* of a reference image — palette, texture, rendering — while the prompt keeps +driving the content. There is **no adapter model and no LoRA**: the reference's attention keys and values +are spliced into the target's, so it works with any Krea-2 checkpoint out of the box. + +On the canvas, add a **Reference Image** while a Krea-2 model is selected, pick an image, and set **Style +Strength**. In the workflow editor the same thing is the **Style Reference - Krea-2** node, feeding the +**Style Reference** input of **Denoise - Krea-2**. + +- **Style Strength** is the one knob you need. `1.0` is the recommended setting; the slider goes to `2.0` + for a heavier effect. `0` disables the reference entirely and costs nothing. +- The remaining node inputs (block range, key/value scaling, AdaIN strengths) are for tuning and should be + left at their defaults. Style Strength already modulates several of them. +- Style Strength is recorded in image metadata, so you can see what a given image was generated with. It has + no recall button of its own — the reference image itself is not part of the metadata. + +:::caution[One reference, matching size, roughly 2x runtime] +- **Exactly one** reference image is used. If several are enabled on the canvas, only the first is applied + and the others are flagged with a warning. +- The reference must be encoded at **the same width and height as the denoise node** — its image tokens are + appended to the target's. On the canvas this is wired up for you. In the workflow editor, set the same + width and height on both nodes, or the denoise node will refuse the reference. +- Every step runs **one extra transformer pass** for the reference, so generation takes roughly **twice as + long**. +- The reference's keys and values are retained for the whole step: about **0.5 GB at 1024²** and **1.7 GB at + 2560×1440**. At 1440p that no longer fits on a 24 GB card alongside the model. +::: + +## LoRA + +Krea-2 LoRAs (diffusers PEFT format) are supported and apply to both the transformer and — where the +LoRA includes text-encoder layers — the Qwen3-VL encoder. On quantized builds (GGUF, int8, NVFP4) the LoRA +is applied alongside the packed weights rather than merged into them. + +## Attention backend + +On CUDA, Krea-2 picks the fastest available attention kernel by itself (Flash Attention where the PyTorch +build has it, then cuDNN, then memory-efficient attention). For benchmarking or support questions you +can force one kernel with the environment variable `INVOKE_KREA2_SDPA_BACKEND`: `flash`, `cudnn`, +`efficient` or `math`, or `default` for the automatic order. A forced kernel that the GPU or build does +not support raises an error instead of quietly falling back, and an unknown value stops InvokeAI at startup. + +Any value, `default` included, also turns on per-step timing in the log, which synchronizes the GPU after +every step and slows generation slightly. Unset the variable for normal use. diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/qwen-image.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/qwen-image.mdx new file mode 100644 index 00000000000..36f80a60b3a --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/qwen-image.mdx @@ -0,0 +1,146 @@ +--- +title: Qwen Image +description: Generate and edit images with the Qwen Image 2512 text-to-image model and the Qwen Image Edit 2511 reference-image editing model, including GGUF installs and the Lightning LoRAs. +lastUpdated: 2026-09-30 +sidebar: + order: 8 +--- + +Qwen Image is a diffusion-transformer image family from the Qwen team that encodes prompts with the +**Qwen2.5-VL 7B** vision-language model and decodes with a 16-channel VAE. InvokeAI supports two +variants: + +- **Qwen Image** (e.g. *Qwen Image 2512*) — text-to-image generation. +- **Qwen Image Edit** (e.g. *Qwen Image Edit 2511*) — text-guided editing driven by one or more + **reference images**. Because its text encoder is a vision-language model, it reads the reference + images alongside your instruction ("replace the background with a beach", "put this jacket on the + person in image 2"). + +The variant is detected automatically on install. Both variants can also run text-to-image, +image-to-image, inpainting and outpainting on the Canvas; only the Edit variant accepts reference images. + +## License + +Qwen Image 2512, Qwen Image Edit 2511, the Lightning LoRAs and the Qwen2.5-VL text encoder are released under **Apache 2.0**, which allows commercial use. None of the downloads is gated. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +Qwen Image is a large model: the full Diffusers pipeline is a ~40 GB download. To run it on a +consumer GPU, use a **GGUF** transformer together with the standalone VAE and fp8 text encoder: + +| Transformer build | Download | +| ----------------- | -------- | +| Q2_K | ~7.5 GB | +| Q4_K_M | ~13 GB | +| Q6_K | ~17 GB | +| Q8_0 | ~22 GB | + +The Qwen2.5-VL encoder is loaded in addition to the transformer (~7 GB for the fp8 build). If your GPU +cannot hold both, enable [Low-VRAM mode](/configuration/optimization/low-vram-mode/). Single-file +checkpoints that ship scaled fp8 weights can be kept in fp8 with +[FP8 Storage](/configuration/optimization/fp8-storage/). + +## Installing + +The easiest path is the **Qwen Image** bundle in the Model Manager's Starter Models. It installs the +standalone VAE and fp8 encoder, the Diffusers and the Q4_K_M / Q8_0 GGUF builds of both *Qwen Image +2512* and *Qwen Image Edit 2511*, and the Lightning LoRAs for each. The other GGUF builds (Q2_K, Q6_K) +are available as individual starter models. + +Qwen Image needs three components: + +| Component | Diffusers install | GGUF / single-file install | +| ------------------------- | ----------------------- | -------------------------------------- | +| Transformer | bundled in the pipeline | the `.gguf` / `.safetensors` file | +| VAE (Qwen Image VAE) | bundled | installed separately | +| Text encoder (Qwen2.5-VL) | bundled | installed separately | + +- **Diffusers** (`Qwen/Qwen-Image-2512`, `Qwen/Qwen-Image-Edit-2511`): a single ~40 GB install that + includes everything. +- **GGUF** (from `unsloth/Qwen-Image-2512-GGUF` and `unsloth/Qwen-Image-Edit-2511-GGUF`): the file + contains only the transformer. The GGUF starter models install the **Qwen Image VAE** (~250 MB) and + the **Qwen2.5-VL Encoder (fp8 scaled)** (~7 GB) as dependencies, so you never need the full pipeline. +- **Single-file `.safetensors`** checkpoints (bf16/fp16, and ComfyUI-style fp8-scaled or nvfp4 builds) + can be installed by path or URL; like GGUF, they need a standalone VAE and encoder. + +Three Qwen2.5-VL encoder builds are available as starter models: **fp8 scaled** (~7 GB, the one the +bundle installs), **NVFP4** (~5.7 GB download, about 6.7 GB once loaded), and **Diffusers** (~16 GB, +full precision). The single-file encoders ship with their tokenizer and config inside InvokeAI. + +When a GGUF or single-file Qwen Image model is selected, pick the **Qwen VL Encoder** and **VAE** in the +**Components** section next to the model. Alternatively, set **Component source** to an installed +Diffusers Qwen Image model and InvokeAI takes the missing VAE and encoder from it. A Diffusers main +model needs neither. + +:::note[Edit or Generate?] +For single-file and GGUF installs, the Edit variant is recognized from a marker tensor in the file, or +from the word "edit" in the filename for converters that don't write the marker. Keep the published +filenames when installing a converted Edit checkpoint. +::: + +## Generation settings + +Selecting a Qwen Image model applies these defaults: + +- **Steps:** 40 +- **CFG Scale:** 4 +- **Size:** 1024×1024; width and height snap to multiples of 16. + +There is no scheduler choice — Qwen Image always uses its own flow-matching schedule, so the +**Scheduler** control is hidden. + +The **negative prompt** is used only when CFG Scale is above 1; at CFG 1 it is ignored. + +## Lightning LoRAs (4 and 8 steps) + +The *Qwen Image Lightning* and *Qwen Image Edit Lightning* starter LoRAs are distillation LoRAs that cut +generation to **4** or **8** steps. Use the one that matches your model variant, and set: + +- **Steps:** 4 or 8, matching the LoRA +- **CFG Scale:** 1 + +The LoRAs are trained for a fixed schedule shift of `3`. In the workflow editor, set **Shift** on the +**Denoise - Qwen Image** node to `3.0` when using them; leave it empty for the base model's default +schedule. The Generate tab does not expose this setting. + +## Image editing with reference images + +With a **Qwen Image Edit** model selected, add up to **five** reference images, +then describe the change you want in the prompt. The text-to-image variant does not accept reference +images. + +- All reference images are given to the Qwen2.5-VL encoder together with your prompt, so you can refer + to them in the instruction. +- The **first** reference image is also encoded by the VAE and supplied to the transformer as a + pixel-level reference. + +In the workflow editor, reference images go into the **Reference Images** input of **Prompt - Qwen +Image**; connect an **Image to Latents - Qwen Image** node to the **Reference Latents** input of +**Denoise - Qwen Image** to add the pixel-level reference. + +## LoRA + +Qwen Image LoRAs (diffusers PEFT, Kohya and LoKR formats) are supported and apply to the transformer. +In the workflow editor use **Apply LoRA - Qwen Image** or **Apply LoRA Collection - Qwen Image**. + +## PiD super-resolution decode + +Qwen Image works with [PiD Decode](/users-guide/image-generation/advanced-topics/pid-decode/), which replaces the VAE +decode with a 4× super-resolving pixel diffusion. The *PiD Decoder Qwen-Image (2K to 4K)* and *PiD 1.5 +Decoder Qwen-Image (2K to 4K)* decoders (including an int8 build of the latter) are starter models. + +## Workflow nodes + +| Node | Purpose | +| ------------------------------------------- | ------------------------------------------------------------------- | +| **Main Model - Qwen Image** | Loads the transformer, VAE and Qwen VL encoder (or a component source) | +| **Prompt - Qwen Image** | Encodes the prompt and any reference images | +| **Denoise - Qwen Image** | Runs sampling; accepts reference latents, img2img latents and masks | +| **Image to Latents - Qwen Image** | VAE encode | +| **Latents to Image - Qwen Image** | VAE decode | +| **Latents to Image - Qwen-Image + PiD (4x SR)** | PiD super-resolution decode | + +The **Prompt - Qwen Image** node also has a **Quantization** option (`none`, `int8`, `nf4`) that +quantizes the Qwen2.5-VL encoder on load to reduce its VRAM use. diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/sd-3.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/sd-3.mdx new file mode 100644 index 00000000000..0cdf4949972 --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/sd-3.mdx @@ -0,0 +1,100 @@ +--- +title: Stable Diffusion 3.5 +description: Generate images with Stability AI's Stable Diffusion 3.5 Medium and Large models, including text encoder options and PiD super-resolution decoding. +lastUpdated: 2026-09-30 +sidebar: + order: 11 +--- + +Stable Diffusion 3.5 is Stability AI's diffusion-transformer text-to-image family. It encodes prompts +with three text encoders at once: **CLIP-L**, **CLIP-G** and **T5**. It uses a 16-channel VAE. InvokeAI +supports two checkpoints: + +- **SD3.5 Medium**: the smaller model and the one InvokeAI's default settings are tuned for. +- **SD3.5 Large**: the larger model. + +Both appear in InvokeAI under the **SD 3.x** base. + +## License + +SD 3.5 Medium and Large are released under the **Stability AI Community License**: free for individuals and organizations with less than $1M in annual revenue, above which an Enterprise license is needed. The downloads are gated: accept the license on the model's HuggingFace page and add your HuggingFace token under **API Keys** in the Model Manager. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +The Diffusers downloads are about **16 GB** for Medium and **28 GB** for Large, including the text +encoders and VAE. Medium is the better choice on smaller cards. See +[System Requirements](/start-here/system-requirements/) for general guidance, and the +[Low VRAM guide](/configuration/optimization/low-vram-mode/) if you run out of memory. + +## Installing + +Both models are **starter models** in the Model Manager. Search for **SD3.5 Medium** or **SD3.5 Large**. +This family has no starter bundle. + +| Starter model | Source | Size | +| ------------- | ---------------------------------------- | ------ | +| SD3.5 Medium | `stabilityai/stable-diffusion-3.5-medium` | ~16 GB | +| SD3.5 Large | `stabilityai/stable-diffusion-3.5-large` | ~28 GB | + +SD3 models install in **Diffusers** format only. Each Diffusers folder contains everything the model +needs: the transformer, both CLIP encoders, the T5 encoder and the VAE. InvokeAI does not recognize +single-file or GGUF SD3 main models. + +:::note[Gated downloads] +If the download fails with a permission error, open the model's HuggingFace page, accept its license +terms, and add your HuggingFace token in the Model Manager's API Keys section. See +[Model Manager](/users-guide/models/introduction/#external-models) for details. +::: + +### Optional component overrides + +By default, every component comes from the main model. In the **Components** section next to the model, +you can replace any of them with a model you have installed separately: + +- **T5 Encoder** +- **CLIP L** +- **CLIP G** +- **VAE**: only SD3 VAEs are offered + +## Generation settings + +When you select an SD3 model, InvokeAI applies these defaults, which come from the SD3.5 Medium +reference settings: + +| Setting | Default | +| ---------- | ----------- | +| Steps | 40 | +| CFG Scale | 4.5 | +| Resolution | 1024 × 1024 | + +- **CFG Scale** applies true classifier-free guidance. Stability's reference setting for **Large** is + about **28 steps at CFG 3.5**. Use that as a starting point when you switch to Large. +- **Width and height** must be multiples of **16**. +- **Negative prompts** are supported and always used. +- **No scheduler choice.** SD3 always uses its own flow-matching sampler, so the Scheduler control is + hidden. +- **Prompt weighting** (Compel syntax) does not apply to SD3. See [Prompt Syntax](/users-guide/image-generation/advanced-prompt-syntax/#attention-weighting). + +## Canvas + +On the Canvas, SD3 supports text-to-image, image-to-image, inpainting and outpainting. ControlNet, +reference images and regional guidance are **not** available for SD3. LoRAs are not supported either. + +## PiD super-resolution decode + +SD3 works with **PiD** decoding. PiD replaces the VAE decode with a 4× super-resolving pixel diffusion +pass. Two decoders are available as starter models: **PiD Decoder SD3 (2K)** and **PiD Decoder SD3 +(2K to 4K)**. Each is about 5 GB and installs the shared Gemma-2 caption encoder as a dependency. See +[PiD Super-Resolution Decode](/users-guide/image-generation/advanced-topics/pid-decode/). + +## Workflow editor + +The SD3 nodes are **Main Model - SD3**, **Prompt - SD3**, **Denoise - SD3**, **Image to Latents - SD3** +and **Latents to Image - SD3**. **Latents to Image - SD3 + PiD (4x SR)** decodes with PiD instead of the +VAE. + +The **T5Encoder** input of **Prompt - SD3** is optional. SD3 was trained with text-encoder dropout, so +leaving T5 unconnected still produces images and saves the time and memory of loading T5. When T5 is connected, it reads at most **256 tokens** of the +prompt. diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/sdxl.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/sdxl.mdx new file mode 100644 index 00000000000..84a8e26b810 --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/sdxl.mdx @@ -0,0 +1,206 @@ +--- +title: SDXL +description: Generate images with Stable Diffusion XL models, including the fp16-fix VAE, ControlNet, T2I-Adapter, IP-Adapter reference images, PiD decoding and the SDXL Refiner. +lastUpdated: 2026-09-30 +sidebar: + order: 9 +--- + +Stable Diffusion XL (SDXL) is a latent-diffusion UNet model trained at **1024×1024**, with two CLIP +text encoders. It has a large ecosystem of community fine-tunes, LoRAs, ControlNets and IP-Adapters, +and runs comfortably on mid-range GPUs. + +SDXL was released together with a separate **SDXL Refiner** model — a second pass that polishes the +last denoising steps of an SDXL image. InvokeAI treats the refiner as its own model family and +supports it in the workflow editor (see [SDXL Refiner](#sdxl-refiner)). + +InvokeAI identifies SDXL models automatically on install, including their **variant** (normal or +inpainting). + +## License + +| Model | License | Commercial use | +|---|---|---| +| SDXL base and Refiner | CreativeML OpenRAIL++-M | Allowed, subject to the license's use restrictions | +| Dreamshaper XL v2 Turbo, RealVisXL V5.0 | OpenRAIL++ | Allowed, subject to the license's use restrictions | +| Juggernaut XL v9 | CreativeML OpenRAIL-M | Allowed, but its card says it may not be served behind a paid API without a license from RunDiffusion | + +None of the starter downloads is gated. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +The [System Requirements](/start-here/system-requirements/) table lists **8 GB VRAM** and 16 GB RAM as +the minimum for SDXL at 1024×1024. On smaller cards, see +[Low-VRAM mode](/configuration/optimization/low-vram-mode/). For large SDXL checkpoints, +[FP8 Storage](/configuration/optimization/fp8-storage/) can reduce VRAM use further on NVIDIA GPUs. + +## Installing + +The easiest path is the **SDXL** bundle in the Model Manager's starter models. It installs: + +- **Juggernaut XL v9** — a photograph-focused main model +- **sdxl-vae-fp16-fix** — an SDXL VAE that works at fp16 precision (see [VAE](#vae)) +- Two **IP-Adapters** for reference images — Standard and Precise — plus their image encoder +- **ControlNets**: Hard Edge Detection (canny), Depth Map, Soft Edge Detection (softedge), + Pose Detection (openpose), Contour Detection (scribble) and Tile +- **SwinIR**, a 4× upscaler used by the Upscale tool + +Other SDXL starter models can be installed individually: + +| Starter model | Notes | +| --- | --- | +| Dreamshaper XL v2 Turbo | A turbo model — see [Turbo models](#turbo-models) | +| Architecture (RealVisXL5) | Photorealistic, with architecture among its use cases | +| SDXL Refiner | The original Stability AI refiner (`stabilityai/stable-diffusion-xl-refiner-1.0`) | +| Alien Style, Noodles Style | Style LoRAs; trigger with `alienzkin` and `noodlez` respectively | +| Multi-Guidance Detection (Union Pro) | A single ControlNet that supports 10+ control types | +| QRCode Monster (SDXL) | ControlNet for scannable, stylized QR codes | +| T2I-Adapters: Hard Edge Detection (canny), Lineart, Sketch | Lighter-weight alternatives to ControlNet | +| PiD Decoder SDXL (2K to 4K) | 4× super-resolution decoder — see [PiD](#pid-decoding) | + +Every SDXL main model in the starter list installs **sdxl-vae-fp16-fix** as a dependency. + +SDXL main models are self-contained: the UNet, both text encoders and the VAE come in one install. Both +formats are supported: + +- **Diffusers** folders (for example a HuggingFace repo ID such as `RunDiffusion/Juggernaut-XL-v9`). +- **Single-file checkpoints** (`.safetensors` / `.ckpt`), the format most community fine-tunes are + published in. + +No HuggingFace token is needed for the starter models. + +## Generation settings + +Selecting an SDXL model applies these defaults, which you can override globally or per model in the +Model Manager's **Default Settings**: + +| Setting | SDXL default | +| --- | --- | +| Width × Height | 1024 × 1024 | +| Steps | 30 | +| CFG Scale | 7 | +| Scheduler | Euler Ancestral | + +- **Resolution** — dimensions must be multiples of 8. An image size equivalent to 1024×1024 in pixel + count is recommended; use the aspect-ratio control to change shape at roughly the same area. +- **Negative prompt** — fully supported. +- **Schedulers** — the full standard list (DDIM, DPM++ 2M Karras, DPM++ SDE Karras, Euler, Euler + Ancestral, UniPC, and so on) is available. +- **Prompt syntax** — SDXL prompts support + [Compel weighting and prompt functions](/users-guide/image-generation/advanced-prompt-syntax/#attention-weighting). The prompt is + sent to both of SDXL's text encoders. + +The **Advanced** section of the Generate panel adds these controls for SDXL: + +- **VAE** and **VAE Precision** — see [VAE](#vae). +- **Color Compensation** — adjusts the input image to reduce color shifts during inpainting and + image-to-image. This control is available only for SDXL. +- **Seamless Tiling** — generates images that tile without visible seams, on the X axis, Y axis or both. +- **HiDiffusion** — improves structure at higher resolutions (it is most noticeable at 1536px and + above). See [HiDiffusion](/users-guide/image-generation/advanced-topics/hidiffusion/). +- **PiD** — see [PiD decoding](#pid-decoding). + +CLIP Skip and CFG Rescale are not offered for SDXL. + +### Turbo models + +Turbo fine-tunes need far fewer steps and a much lower CFG than the defaults. For +**Dreamshaper XL v2 Turbo**, the starter model's description recommends **CFG Scale 2**, **4–8 steps** +and the **DPM++ SDE Karras** scheduler. Set these as the model's Default Settings in the Model Manager so they +are applied whenever you select it. + +## VAE + +The original SDXL VAE is prone to numeric overflow at fp16, which can produce black images. There are +two remedies: + +- Select **sdxl-vae-fp16-fix** in the **VAE** field (or set it as the model's default VAE in the Model + Manager). It is installed alongside every SDXL starter model. +- Or set **VAE Precision** to **fp32**, at the cost of more memory and time for decoding. + +## Inpainting models + +SDXL models with an inpainting variant are detected automatically and can be used to fill masked areas +and extend images on the Canvas. There is no SDXL inpainting model among the starter models. + +## LoRAs and embeddings + +- **LoRAs** for SDXL, including LyCORIS variants, are supported. Add them in the LoRA section of the + Generate panel. LoRAs made for a different model family are not applied. +- **Textual-inversion embeddings** for SDXL are supported in the positive and negative prompts. Type `<` + in a prompt box to pick from the compatible embeddings you have installed. + +## Control layers + +SDXL supports **ControlNet** and **T2I-Adapter** models as control layers on the Canvas. A control layer +guides composition from an image — edges, depth, pose, line art and so on. The +**Multi-Guidance Detection (Union Pro)** ControlNet handles many control types with one model. See +[Adding a gallery image to a layer](/users-guide/canvas/introduction/#adding-a-gallery-image-to-a-layer) for creating a control layer from an image. + +## Reference images (IP-Adapter) + +SDXL supports up to **five reference images**, using IP-Adapter models: + +- **Standard Reference (IP Adapter ViT-H)** +- **Precise Reference (IP Adapter Plus ViT-H)** — references the image with higher precision. + +Each reference image has a **Mode** — **Style**, **Composition** or **Both** — and a **Weight**. Under +**Advanced**, you can set the range of steps the reference is active for and, in Style mode, choose a +**Style variant** (Standard, Strong or Precise). + +The starter IP-Adapters carry their own image encoder. The **CLIP Vision** selector next to the model +matters only for IP-Adapters installed from a single checkpoint file; if the chosen encoder is not +installed, it is downloaded on first use. + +## Regional guidance + +SDXL supports **regional guidance** on the Canvas: each region can have its own positive prompt, its +own negative prompt, and its own IP-Adapter reference images. + +## PiD decoding + +SDXL supports NVIDIA's **PiD** decoder, which replaces the VAE decode with a 4× super-resolution decode. +Install **PiD Decoder SDXL (2K to 4K)** from the starter models — only this 2K-to-4K preset exists for +SDXL — and enable **PiD** in the Advanced section before generating. See +[PiD Decode](/users-guide/image-generation/advanced-topics/pid-decode/) for details and limitations. In the workflow +editor, the corresponding node is **Latents to Image - SDXL + PiD (4x SR)**. + +## Upscaling + +The **Upscale** tool supports SDXL main models. It requires an SDXL **Tile** or **Union** ControlNet +(the starter **Tile** ControlNet is part of the bundle), and the bundle's **SwinIR** model can serve as +the upscaler. + +## SDXL Refiner + +The refiner is a separate model that runs as a second pass over an SDXL latent. It cannot generate an +image on its own. + +- **Installing** — install **SDXL Refiner** from the starter models, or any refiner checkpoint or + Diffusers folder; InvokeAI recognizes refiners automatically and lists them under their own + **SDXL Refiner** family. +- **Using it** — the refiner is not selectable in the Generate panel or on the Canvas. Use it in the + workflow editor. + +A refiner workflow runs two denoise passes over the same latents: + +1. Load the base model with **Main Model - SDXL** and prompt it with **Prompt - SDXL**. In its + **Denoise - SD1.5, SDXL** node, set **Denoising End** below 1 (for example `0.8`) so the base model + stops before the last steps. +2. Load the refiner with **Refiner Model - SDXL** and prompt it with **Prompt - SDXL Refiner** — one + node for the positive and one for the negative conditioning. This node takes a **Style** prompt and an + **Aesthetic Score** (default `6`). +3. Feed the base pass's latents into a second **Denoise - SD1.5, SDXL** node that uses the refiner's + UNet, with **Denoising Start** set to the value where the base pass stopped, then decode with + **Latents to Image - SD1.5, SDXL**. + +The refiner uses SDXL's VAE. + +## Workflows + +The workflow library includes **Text to Image - SDXL** and **MultiDiffusion SDXL**. Other nodes that +work with SDXL include **Apply LoRA - SDXL**, **Apply LoRA Collection - SDXL**, +**ControlNet - SD1.5, SD2, SDXL**, **T2I-Adapter - SD1.5, SDXL**, **IP-Adapter - SD1.5, SDXL**, +**Apply Seamless - SD1.5, SDXL** and **Tiled Multi-Diffusion Denoise - SD1.5, SDXL**. diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/stable-diffusion-1-2.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/stable-diffusion-1-2.mdx new file mode 100644 index 00000000000..7ad26980564 --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/stable-diffusion-1-2.mdx @@ -0,0 +1,183 @@ +--- +title: Stable Diffusion 1.5 / 2.x +description: Generate images with Stable Diffusion 1.5 and legacy SD 2.x models, including inpainting models, ControlNet, T2I-Adapter, IP-Adapter reference images, LoRAs and embeddings. +lastUpdated: 2026-09-30 +sidebar: + order: 10 +--- + +Stable Diffusion 1.x and 2.x are the original latent-diffusion UNet models. They are small and fast +by current standards, run on modest hardware, and have the largest library of community fine-tunes, +LoRAs, embeddings and ControlNets. + +- **SD 1.x** (in practice, **SD 1.5** and its fine-tunes) is trained at **512×512** and uses a single + CLIP text encoder. It is fully supported: text-to-image, image-to-image, inpainting and outpainting, + control layers, reference images, regional guidance and upscaling. +- **SD 2.x** is treated as **legacy**. Models you install yourself load and generate, but InvokeAI + ships no SD 2.x starter models and some Canvas features are not available for it (see + [SD 2.x](#sd-2x)). + +InvokeAI identifies the family, the **variant** (normal or inpainting; SD 2.x also has a depth +variant) and the **prediction type** (epsilon or v-prediction) automatically on install. + +## License + +| Model | License | Commercial use | +|---|---|---| +| SD 1.5 base, Dreamshaper 8, CyberRealistic v4.1, ReV Animated | CreativeML OpenRAIL-M | Allowed, subject to the license's use restrictions | +| Deliberate v5 | CC BY-NC-ND 4.0 | Companies and services must contact the author; the author places no restrictions on private individuals | + +None of the starter downloads is gated. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +SD 1.5 is the lightest local image model InvokeAI supports: the +[System Requirements](/start-here/system-requirements/) table lists **4 GB VRAM** and 8 GB RAM as the +minimum at 512×512. On smaller cards, see [Low-VRAM mode](/configuration/optimization/low-vram-mode/). + +## Installing + +The easiest path is the **Stable Diffusion 1.5** bundle in the Model Manager's starter models. It +installs: + +- **Dreamshaper 8** — a popular, versatile main model +- **EasyNegative** — a textual-inversion embedding for the negative prompt +- Three **IP-Adapters** for reference images — Standard, Precise and Face — plus their image encoder +- A set of **ControlNets**: Hard Edge Detection (canny), Inpainting, Line Drawing (mlsd), Depth Map, + Lighting Detection (Normals), Segmentation Map, Lineart, Lineart Anime, Pose Detection (openpose), + Contour Detection (scribble), Soft Edge Detection (softedge), Remix (shuffle) and Tile +- **SwinIR**, a 4× upscaler used by the Upscale tool + +Other SD 1.5 starter models can be installed individually: + +| Starter model | Notes | +| --- | --- | +| CyberRealistic v4.1 | Photorealistic; installs its companion **CyberRealistic Negative v3** embedding | +| ReV Animated | Fantasy and anime styles | +| Dreamshaper 8 (inpainting) | Inpainting version of Dreamshaper 8 | +| Deliberate v5 | Versatile general-purpose model | +| Deliberate v5 (inpainting) | Inpainting version of Deliberate v5 | +| QRCode Monster v2 (SD1.5) | ControlNet for scannable, stylized QR codes | +| T2I-Adapters: Hard Edge Detection (canny), Sketch, Depth Map | Lighter-weight alternatives to ControlNet | + +SD 1.x and 2.x main models are self-contained: the UNet, text encoder and VAE all come in one install. +Both formats are supported: + +- **Diffusers** folders (for example a HuggingFace repo ID such as `Lykon/dreamshaper-8`). +- **Single-file checkpoints** (`.safetensors` / `.ckpt`), the format most community fine-tunes are + published in. + +No HuggingFace token is needed for the starter models. + +If a model was identified incorrectly, open it in the Model Manager and correct its **Variant** or +**Prediction Type**. + +## Generation settings + +Selecting an SD 1.5 model applies these defaults, which you can override globally or per model in the +Model Manager's **Default Settings**: + +| Setting | SD 1.5 default | +| --- | --- | +| Width × Height | 512 × 512 | +| Steps | 30 | +| CFG Scale | 7 | +| Scheduler | Euler Ancestral | + +- **Resolution** — dimensions must be multiples of 8. An image size equivalent to 512×512 in pixel + count is recommended. For larger output, use the Upscale tool, or try + [HiDiffusion](/users-guide/image-generation/advanced-topics/hidiffusion/). +- **Negative prompt** — fully supported. +- **Schedulers** — the full standard list (DDIM, DPM++ 2M Karras, Euler, Euler Ancestral, UniPC, and + so on) is available. +- **Prompt syntax** — SD prompts support [Compel weighting and prompt functions](/users-guide/image-generation/advanced-prompt-syntax/#attention-weighting). + +The **Advanced** section of the Generate panel adds these SD-specific controls: + +- **CLIP Skip** — skips the last layers of the text encoder (up to 12 for SD 1.5). Some fine-tunes, + especially anime models, are designed to be used with it. +- **CFG Rescale** — intended for models trained with zero-terminal SNR; a value around `0.7` is + suggested for those. Leave it at 0 for ordinary models. +- **Seamless Tiling** — generates images that tile without visible seams, on the X axis, Y axis or both. +- **VAE** and **VAE Precision** — override the model's bundled VAE, and choose fp32 or fp16 for + encoding and decoding. VAE Precision is fp32 unless the model's default settings in the Model + Manager say fp16. +- **HiDiffusion** — improves structure at higher resolutions. See + [HiDiffusion](/users-guide/image-generation/advanced-topics/hidiffusion/). + +## Inpainting models + +Models with an inpainting variant (such as **Dreamshaper 8 (inpainting)** and +**Deliberate v5 (inpainting)**) are detected automatically. They are made for filling masked areas and +extending images on the Canvas, and usually blend new content into the surrounding image better than a +normal model does. The **Inpainting** ControlNet is an alternative that works with ordinary SD 1.5 +models. + +## LoRAs and embeddings + +- **LoRAs** for SD 1.x (and SD 2.x), including LyCORIS variants, are supported. Add them in the LoRA + section of the Generate panel. LoRAs made for a different model family are not applied. +- **Textual-inversion embeddings** are supported in the positive and negative prompts. Type `<` in a + prompt box to pick from the compatible embeddings you have installed — for example, `EasyNegative` + in the negative prompt. + +## Control layers + +SD 1.5 supports **ControlNet** and **T2I-Adapter** models as control layers on the Canvas. A control +layer guides composition from an image — edges, depth, pose, line art and so on. See +[Adding a gallery image to a layer](/users-guide/canvas/introduction/#adding-a-gallery-image-to-a-layer) for creating a control layer from an image. + +## Reference images (IP-Adapter) + +SD 1.5 supports up to **five reference images**, using IP-Adapter models: + +- **Standard Reference** — references an image with looser precision. +- **Precise Reference** — references an image more precisely. +- **Face Reference** — a precise reference adapted for faces. + +Each reference image has a **Mode** — **Style**, **Composition** or **Both** — and a **Weight**. Under +**Advanced**, you can set the range of steps the reference is active for and, in Style mode, choose a +**Style variant** (Standard, Strong or Precise). + +The starter IP-Adapters carry their own image encoder. The **CLIP Vision** selector next to the model +matters only for IP-Adapters installed from a single checkpoint file; if the chosen encoder is not +installed, it is downloaded on first use. + +## Regional guidance + +SD 1.5 supports **regional guidance** on the Canvas: each region can have its own positive prompt, its +own negative prompt, and its own IP-Adapter reference images. + +## Upscaling + +The **Upscale** tool supports SD 1.5 main models. It requires a **Tile** ControlNet for SD 1.5 (the +starter **Tile** ControlNet is part of the bundle), and the bundle's **SwinIR** model can serve as the +upscaler. + +## Workflows + +The workflow library includes **Text to Image - SD1.5** and **MultiDiffusion SD1.5**. In the workflow +editor, SD 1.x and 2.x models are loaded with the **Main Model - SD1.5, SD2** node and prompted with +**Prompt - SD1.5**. Other nodes that work with them include **Denoise - SD1.5, SDXL**, +**Apply LoRA - SD1.5**, **ControlNet - SD1.5, SD2, SDXL**, **T2I-Adapter - SD1.5, SDXL**, +**IP-Adapter - SD1.5, SDXL**, **Apply CLIP Skip - SD1.5, SDXL** and **Apply Seamless - SD1.5, SDXL**. + +## SD 2.x + +SD 2.x models (SD 2.0 and 2.1 and their fine-tunes) are supported for models you install yourself, +from a Diffusers folder or a single-file checkpoint. There are no SD 2.x starter models. + +- **Defaults** — selecting an SD 2.x model applies 768×768, 30 steps, CFG 7 and Euler Ancestral. That + suits the 768px v-prediction checkpoints. For the 512px `-base` checkpoints, change the size to + 512×512, or set it as the model's default in the Model Manager. +- **Prediction type** — SD 2.x checkpoints may be epsilon or v-prediction. InvokeAI detects this on + install from the checkpoint or its scheduler config. If a model's output looks wrong, check its + **Prediction Type** in the Model Manager. +- **CLIP Skip** goes up to 24, and **CFG Rescale**, **Seamless Tiling**, the negative prompt and + regional prompts work as for SD 1.5. +- **Not available for SD 2.x on the Canvas**: control layers, reference images (IP-Adapter), + HiDiffusion and the Upscale tool. In the workflow editor, the **ControlNet - SD1.5, SD2, SDXL** node + does accept SD 2.x ControlNets. +- **LoRAs and embeddings** made for SD 2.x are supported. diff --git a/docs/src/content/docs/Users Guide/03.Models/Local Models/z-image.mdx b/docs/src/content/docs/Users Guide/03.Models/Local Models/z-image.mdx new file mode 100644 index 00000000000..55b00e7582a --- /dev/null +++ b/docs/src/content/docs/Users Guide/03.Models/Local Models/z-image.mdx @@ -0,0 +1,144 @@ +--- +title: Z-Image +description: Generate images with Z-Image Turbo and Z-Image Base, including the GGUF, NVFP4 and SDNQ builds, ControlNets and regional prompting. +lastUpdated: 2026-09-30 +sidebar: + order: 12 +--- + +Z-Image is a 6B-parameter text-to-image family from Tongyi-MAI that understands both English and Chinese +prompts. It encodes prompts with a **Qwen3 4B** text encoder and decodes with the same 16-channel VAE as +FLUX.1. InvokeAI supports two variants: + +- **Z-Image Turbo** is distilled for fast, low-step generation with **CFG disabled** (CFG Scale `1.0`). + This is the checkpoint the starter models install. +- **Z-Image Base** is the undistilled foundation model. It needs more steps, and supports CFG and negative + prompts. + +A Diffusers pipeline's variant is detected on install. Single-file, GGUF and SDNQ files are registered as +**Turbo**. If you install a Base checkpoint in one of those formats, change its **Variant** to *Z-Image +Base* in the Model Manager so the right defaults and schedulers apply. + +Z-Image works in the Generate tab, on the Canvas (text-to-image, image-to-image, inpainting and +outpainting) and in the workflow editor. + +## License + +Z-Image Turbo, Z-Image (Base) and their Qwen3 text encoder are released under **Apache 2.0**, which allows commercial use. None of the downloads is gated. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +## Hardware + +See the [System Requirements](/start-here/system-requirements/) table. In short: a Q4_K GGUF or NVFP4 +transformer runs in about 8 GB of VRAM, while the Q8 and full-precision (BF16) builds want 16 GB or more. +The full Diffusers pipeline is roughly a 33 GB download. + +For more headroom you can enable [FP8 Storage](/configuration/optimization/fp8-storage/) on a +full-precision Z-Image model, use an [SDNQ](/configuration/optimization/sdnq-quantization/) build, or turn +on [Low-VRAM mode](/configuration/optimization/low-vram-mode/). + +## Installing + +The easiest path is the **Z-Image Turbo** bundle in the Model Manager. It installs the Q4_K GGUF +transformer, the quantized Qwen3 encoder, the FLUX VAE and both Z-Image ControlNets. + +Z-Image needs three components: + +| Component | Diffusers / SDNQ pipeline | GGUF / single-file install | +| --------------------- | ------------------------- | ------------------------------------ | +| Transformer | bundled | the `.gguf` / `.safetensors` file | +| Text encoder (Qwen3 4B) | bundled | installed separately | +| VAE (FLUX.1) | bundled | installed separately (any FLUX VAE) | + +The starter models: + +| Starter | Format | Size | Also installs | +| ---------------------------------- | --------------------------- | ------- | ---------------------------------------------- | +| Z-Image Turbo | Diffusers pipeline | ~33 GB | nothing, everything is bundled | +| Z-Image Turbo (quantized) | GGUF Q4_K | ~4 GB | Qwen3 encoder (GGUF Q6_K, ~3.3 GB) + FLUX VAE | +| Z-Image Turbo (Q8) | GGUF Q8_0 | ~6.6 GB | Qwen3 encoder (GGUF Q6_K) + FLUX VAE | +| Z-Image Turbo (NVFP4) | Comfy-Org nvfp4 single file | ~4.5 GB | Qwen3 encoder (FP4 mixed, ~3.5 GB) + FLUX VAE | +| Z-Image Turbo (SDNQ uint4 + SVD) | SDNQ Diffusers pipeline | ~5 GB | nothing, everything is bundled | + +A full-precision **Z-Image Qwen3 Text Encoder** (~8 GB) is also available as a separate starter. + +The NVFP4 build keeps its quantized layers packed in memory and decodes them on the fly, so it needs about +a third of the full-precision transformer's memory. + +### Choosing components + +When the selected Z-Image model is not a self-contained pipeline, the **Components** section next to the +model shows three slots: + +- **Component source**: a Diffusers (or SDNQ) Z-Image pipeline to borrow the encoder and VAE from. +- **Qwen3 Encoder**: a standalone Qwen3 4B encoder. +- **VAE**: Z-Image decodes with the FLUX VAE, so FLUX-base VAEs are listed here. + +You need either a component source or both a Qwen3 encoder and a VAE before you can generate. + +## Generation settings + +Selecting a Z-Image model applies these defaults: + +| Variant | Steps | CFG Scale | Scheduler | Size | +| ------------- | ----- | --------- | --------- | --------- | +| Z-Image Turbo | 9 | 1.0 | Euler | 1024×1024 | +| Z-Image Base | 50 | 4.0 | Euler | 1024×1024 | + +- **CFG Scale**: `1.0` turns CFG off, which is what Turbo is trained for. Values below `1.0` are not + accepted. +- **Negative prompt**: only used when CFG Scale is above `1.0`. With Turbo at `1.0` it has no effect. +- **Schedulers**: *Euler* (the default and recommended), *Heun (2nd order)* (better quality, about twice as + slow) and *LCM*. LCM works with Turbo only and is not offered for Z-Image Base. +- **Resolution**: width and height must be multiples of 16. +- **Prompt weighting**: Compel-style [prompt syntax](/users-guide/image-generation/advanced-prompt-syntax/#attention-weighting) does not + apply to Z-Image. Write prompts in plain language. + +In the workflow editor, **Denoise - Z-Image** also has a **Shift** input that overrides the timestep +shift. Leave it blank to have it calculated from the image size, which is the recommended setting. + +## ControlNet + +Two Z-Image ControlNets are available as starters (both are part of the bundle): + +- **Z-Image ControlNet Union** handles Canny, HED, Depth, Pose and MLSD control images. +- **Z-Image ControlNet Tile** is useful for upscaling and adding detail. + +On the Canvas, add a **Control Layer** while a Z-Image model is selected. The layer uses a Z-Image +ControlNet with a default weight of `0.75`. The weight ranges from `0` to `2`, and `0.65` to `0.80` is the +recommended range. **Only one** Z-Image control layer can be used per generation. + +In the workflow editor, the same thing is the **Z-Image ControlNet** node feeding the **Control** input of +**Denoise - Z-Image**. That node also needs the **VAE** input connected, which it uses to encode the +control image. + +## Regional prompting + +Z-Image supports **Regional Guidance** layers on the Canvas with **positive** prompts. Regional negative +prompts, auto-negative and regional reference images are not supported for Z-Image. + +In the workflow editor, **Prompt - Z-Image** takes an optional mask, and **Denoise - Z-Image** accepts +either one conditioning or a collection of them. + +## LoRA + +Z-Image LoRAs are supported in Kohya, PEFT/Diffusers and LyCORIS formats. They are applied to both the +transformer and the Qwen3 encoder. In the workflow editor, use **Apply LoRA - Z-Image** or **Apply LoRA +Collection - Z-Image**. + +## Seed Variance Enhancer + +Z-Image Turbo tends to produce similar images across different seeds. The **Seed Variance Enhancer - +Z-Image** node, available in the workflow editor only, adds reproducible, seed-based noise to the prompt +conditioning to increase variety: + +- **Strength**: `0` is off, `0.1` (the default) is subtle, `0.5` is strong. +- **Randomize Percent**: the share of embedding values that receive noise (default `50`). + +Connect it between **Prompt - Z-Image** and the denoise node's positive conditioning. + +## PiD decoding + +Z-Image has no PiD decoder of its own and uses the **FLUX** PiD decoders, since it shares FLUX.1's VAE. See +[PiD Decode](/users-guide/image-generation/advanced-topics/pid-decode/). diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/01.introduction.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/01.introduction.mdx new file mode 100644 index 00000000000..5d5d0eb28b5 --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/01.introduction.mdx @@ -0,0 +1,85 @@ +--- +title: Generating and modifying still images +description: Get started with the Generate panel — choose a model, write a prompt, generate a first image, and send results to the Gallery or the Canvas. +lastUpdated: 2026-09-30 +sidebar: + order: 0 +--- +import routingMenu from '../assets/routing-menu.webp'; + +import { Steps } from '@astrojs/starlight/components'; +import { Image } from 'astro:assets'; +import generatePanel from '../assets/generate-panel-overview.png'; + +The **Generate** panel is where you create still images from a text prompt. This page gets you started: choosing a model, writing a prompt and generating a first image, and choosing whether results go to the Gallery or the Canvas. The [Generate Panel Reference](/users-guide/image-generation/generate-panel/) then describes every section of the panel in detail. + +The Generate panel sits in the left rail of the **Compose** layout (**Alt+1**), where finished images appear in the Preview in the center of the screen. It is also in the **Edit** layout (**Alt+2**), next to the Canvas. Press **1** to bring it to the front, or **Alt+A** to jump straight to its prompt. See [The Workbench](/users-guide/workbench/introduction/) for how layouts and panels work. + +The panel is divided into sections: the model picker and prompts at the top, followed by **Size**, **Guidance**, **Render**, **Components** and **Advanced**. Which sections and fields you see depends on the selected model, and each section header shows a summary of its settings, so you can keep sections you rarely change collapsed. + +The Generate panel with an SDXL model selected, a prompt and negative prompt entered, one concept added, and the Size, Guidance and Render sections open. + +## Selecting a model + +The **Model** picker at the top of the panel lists the image generation models you have installed, grouped by model family. Type in **Search models…** to find one by name, family or description, or use the family chips to narrow the list. If the list is empty, install a model first; see [Managing media generation models](/users-guide/models/introduction/). + +When you switch models, the panel keeps your prompt and settings and adjusts only what the new model cannot use: it snaps the size to the new model's grid, and turns off or removes concepts, reference images and options the model does not support. If anything would be lost, a **Switch model?** dialog lists it before you confirm. Switching does not load the new model's recommended settings; use [Reset all to model defaults](/users-guide/image-generation/generate-panel/#resetting-to-defaults) for that. + +### Model components + +Many newer models are installed as a single file that holds only the main network, and need companion models, such as a text encoder and a VAE, to generate. For these models the panel shows a **Components** section with a slot for each companion model. A Diffusers install of the same model carries its own components and leaves the slots on **Model default**. [Submodels](/users-guide/models/model-families/#submodels) explains what each component does, and each model family's page under [Local Models](/users-guide/models/model-families/#model-families) lists the ones it needs. + +If a required component is missing, **Invoke** is disabled and its tooltip names the slot to fill, for example *"Generate needs a VAE for FLUX models."* Pick an installed model in the slot, or install the family's starter bundle from the Model Manager, which includes everything the family needs. + +## Your first image + + + +1. **Write a prompt.** Type a description of the image into the **Prompt** box, for example *a lighthouse on a rocky coast at sunset, dramatic clouds, oil painting*. Describe the subject, the setting, the lighting and the style you want. The [Prompting Guide](/users-guide/image-generation/prompting-guide/) has advice on writing prompts that work. + +2. **Choose how many images.** The **Iterations** field in the top bar, next to **Invoke**, sets how many images one press of Invoke produces. [Seed](/users-guide/image-generation/generate-panel/#seed) explains how their seeds are chosen. + +3. **Press Invoke** in the top bar, or press **Ctrl+Enter** (**Cmd+Enter** on macOS). The job joins the queue, and the top bar shows its progress. The first run with a model takes longer while the model loads. + +4. **See the result.** The finished image appears in the Preview and is saved to the Gallery. To stop a run, click the cancel button in the queue area of the top bar, or press **Shift+X**. + + + +Hover over **Invoke** to see what will be queued, for example *1 prompt × 4 iterations → 4 generations*. When Invoke is disabled, the same tooltip lists what is stopping it. + +## Routing images to the Gallery or the Canvas + +Every press of **Invoke** has a *route*: a **source**, the panel whose settings are used, and a **destination**, where the results go. Images can go to one of two destinations: + +* **Gallery** — the image is saved to the Gallery, on the board selected there, and shown in the Preview. +* **Canvas** — the image lands in the Canvas's staging area, where you can compare the results and accept one onto the canvas as a new layer. + +Each layout preset starts with the route that suits its purpose. The **Compose** preset routes the Generate panel to the **Gallery**. The **Edit** preset routes to the **Canvas**, using the Canvas's layers and bounding box as the source, so results come back to the canvas for further editing. Switching presets switches to the new preset's route, unless the routing is locked. + +The routing control next to **Invoke** in the top bar shows the current source and destination as a pair of icons. Click it to change either one: + +* **Source** lists the panels in the current layout that can run a generation, such as Generate, Canvas, Upscale and Video. +* **Destination** chooses **Gallery** or **Canvas**. +* **Lock routing** keeps the route fixed. A small dot on the control shows that it is locked. + +The routing menu next to Invoke, with Source (Generate, Canvas, Upscale, Video, Workflow), Destination (Canvas or Gallery) and Lock routing. + +The route also follows your work. When you change a setting in a different panel, that panel becomes the source, and the destination changes to match: the Canvas sends results to the Canvas, and the other panels send them to the Gallery. For example, editing the prompt in the Generate panel while you are in the Edit layout sends the next results to the Gallery. Lock the routing if you want results to keep going to one place while you work in several panels. To send a single run to the other destination without changing the route, press **Alt+Ctrl+Enter** (**Alt+Cmd+Enter** on macOS). + +The route is saved with the project, so each project remembers where its results go. See [Projects](/users-guide/projects/introduction/#what-a-project-keeps) for everything a project keeps. + +## Going further + +The [Generate Panel Reference](/users-guide/image-generation/generate-panel/) describes the rest of the panel in detail: + +* **[Image size](/users-guide/image-generation/generate-panel/#setting-the-image-size)** — width, height, aspect ratio and the model's optimal size. +* **[Render settings](/users-guide/image-generation/generate-panel/#render-settings)** — steps, CFG or guidance, scheduler, seed, the negative prompt, advanced options, and **Reset all to model defaults**. +* **[Concepts](/users-guide/image-generation/generate-panel/#adding-concepts)** — add LoRAs that teach the model a style, character or object. +* **[Reference images](/users-guide/image-generation/generate-panel/#adding-reference-images)** — guide the image by example instead of by words. +* **[Prompt tools](/users-guide/image-generation/generate-panel/#prompt-tools)** — expand a prompt, write one from an image, and reuse earlier prompts. + +The [Prompting Guide](/users-guide/image-generation/prompting-guide/) covers writing prompts that work, prompt templates and dynamic prompts. diff --git a/docs/src/content/docs/concepts/diffusion.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/Advanced Topics/diffusion.mdx similarity index 99% rename from docs/src/content/docs/concepts/diffusion.mdx rename to docs/src/content/docs/Users Guide/04.Image Generation/Advanced Topics/diffusion.mdx index 27030b2af4a..a82d6a510df 100644 --- a/docs/src/content/docs/concepts/diffusion.mdx +++ b/docs/src/content/docs/Users Guide/04.Image Generation/Advanced Topics/diffusion.mdx @@ -2,7 +2,7 @@ title: Diffusion lastUpdated: 2026-02-20 sidebar: - order: 5 + order: 10 --- import { Card, CardGrid, Steps, Tabs, TabItem } from '@astrojs/starlight/components'; diff --git a/docs/src/content/docs/features/hidiffusion.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/Advanced Topics/hidiffusion.mdx similarity index 98% rename from docs/src/content/docs/features/hidiffusion.mdx rename to docs/src/content/docs/Users Guide/04.Image Generation/Advanced Topics/hidiffusion.mdx index 8158f4980a1..fdb0755f024 100644 --- a/docs/src/content/docs/features/hidiffusion.mdx +++ b/docs/src/content/docs/Users Guide/04.Image Generation/Advanced Topics/hidiffusion.mdx @@ -1,5 +1,7 @@ --- title: HiDiffusion +sidebar: + order: 12 --- # HiDiffusion diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/Advanced Topics/pid-decode.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/Advanced Topics/pid-decode.mdx new file mode 100644 index 00000000000..82fd9acea17 --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/Advanced Topics/pid-decode.mdx @@ -0,0 +1,104 @@ +--- +title: PiD Super-Resolution Decode +lastUpdated: 2026-08-06 +sidebar: + order: 11 +--- + +import { Steps, Aside, Tabs, TabItem } from '@astrojs/starlight/components' + +**PiD** (Pixel Diffusion Decoder) is an alternative way to turn a model's latents into an image. Instead of the usual VAE decode, it runs a short pixel-space diffusion that produces a **4× super-resolved** result in a single, few-step pass — so a 512×512 generation comes out as a detailed 2048×2048 image. + +Because it decodes in pixel space and is conditioned on your prompt, PiD often recovers finer texture and edge detail than a plain VAE decode followed by an upscaler. + + + +## Supported models + +PiD works with these base models: + +| Base model | PiD decoder to install | +|---|---| +| FLUX.1 | PiD Decoder FLUX | +| FLUX.2 Klein (4B / 9B) | PiD Decoder FLUX.2 | +| Stable Diffusion 3 | PiD Decoder SD3 | +| SDXL | PiD Decoder SDXL | +| Z-Image / Z-Image Turbo | **PiD Decoder FLUX** (Z-Image shares FLUX.1's VAE) | +| Qwen-Image | PiD Decoder Qwen-Image | + + + +## What you need to install + +PiD needs two extra models, both available in **Model Manager → Starter Models**: + + +1. A **PiD Decoder** for your base model (e.g. *PiD Decoder FLUX (2K)*). Some bases offer a *2K* and a *2K-to-4K* preset; SDXL and Qwen-Image ship only the *2K-to-4K* preset. +2. The **Gemma 2 2B (PiD caption encoder)** — PiD uses it to condition the decode on your prompt. It installs automatically as a dependency of any PiD decoder, and is shared across all of them. + + +A first-generation PiD decoder download is roughly 5 GB, a PiD 1.5 decoder download roughly 2.8 GB (1.6 GB for its int8 build), and the shared Gemma-2 encoder is roughly 5 GB. Once loaded, a decoder runs in full precision and takes about 5.6 GB in either generation; the PiD 1.5 int8 build keeps its transformer layers in int8 and takes about 2 GB. + + + +The Gemma-2 caption encoder can also be a single-file **GGUF** (e.g. `gemma-2-2b-it-Q4_K_M.gguf`) that you install manually: InvokeAI loads it natively, keeping the large projection weights quantized instead of expanding them in memory, which cuts the encoder's footprint noticeably. Only Gemma-2-**2b** GGUFs are accepted — PiD's caption projection is fixed to their 2304-dim hidden state. + +## Enabling PiD + +Open the **Generation** settings for a supported model and expand the advanced options. You'll find a **PiD** control with three modes: + + + + Standard VAE decode. No PiD models required. + + + Generate at the requested size, decode 4× with PiD, then downscale the result back to the requested size. This is the safe default and works everywhere — the output matches your bounding box exactly, so it composites cleanly on the Canvas. + + + Treat the requested dimensions as the **4× target**: the image is generated at target ÷ 4 and PiD's full 4× output is used directly (no downscale), preserving all of the added detail. Great when you want a large, highly-detailed result. + + + +When PiD mode is not *Off*, pick your **PiD Decoder** and **Gemma-2 Encoder** below the mode selector. The **PiD Steps** control (default 4) sets how many decode steps run — the released checkpoints are trained for 4. + +PiD is available in both the **Generate** tab (text-to-image) and on the **Canvas** (image-to-image), in both Fit and Native modes. + +## Running PiD on an existing image + +Everything above describes PiD as a *decode* step: it needs the latent the model just produced, so it only exists inside a generation. If you want to push an image you already have through PiD, the Workflow editor has a separate **PiD Upscale (4x)** node. + +It encodes your image with the FLUX VAE and hands the resulting latent to the PiD decoder, giving a 4× result in one pass — the PiD analogue of an ESRGAN / SUPIR upscale. Connect a FLUX VAE, a **FLUX** PiD decoder and the Gemma-2 encoder, and optionally give it a caption describing the image (the decoder is caption-conditioned; an empty caption is the cheapest option but usually the weaker result). + + + +## Tips & limitations + +On GPUs with limited VRAM, enable the experimental PiD memory optimizations in `invokeai.yaml`, then restart InvokeAI: + +```yaml +pid_memory_optimization: true +``` + +This setting applies to every supported PiD decoder. It reduces peak activation and sampler memory by processing parts of the pixel pathway in chunks and by running the sampler's intermediates in float32 instead of float64 — measured on an RTX 4090, a 2048px decode peaks at ~1.5 GB of activations instead of ~3.7 GB. Decoding speed is roughly unchanged. + +It is disabled by default because it **changes the decoded image**. The combined optimized path is not guaranteed to be bit-exact with the default path: chunking can change GPU kernel and reduction choices, while the float32 sampler math differs from the default float64 path. The few-step sampler amplifies that into a small but real difference (~43 dB PSNR — visually indistinguishable, numerically not identical). A seed that reproduced an image with the setting off will not reproduce it exactly with the setting on, and because this is a server-wide `invokeai.yaml` option it is not recorded in image metadata — each decode logs that it ran optimized instead. + +- **Turn off "Scale Before Processing"** on the Canvas when using PiD — PiD already decodes at 4×, so pre-scaling would inflate the work and is blocked. +- **Inpaint / Outpaint** are not supported with PiD yet; use text-to-image or image-to-image. +- **SDXL Refiner** cannot be combined with PiD — disable one of them. +- PiD's memory use scales with the *output* resolution. A 2048px output needs only a little more headroom than a normal decode, but Native mode at large target sizes (e.g. a 4096px result) is significantly heavier. +- Turbo variants (e.g. Z-Image Turbo) work as usual — the low step count / no-CFG only affects generation; PiD's own step count is separate. + + diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/advanced-prompt-syntax.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/advanced-prompt-syntax.mdx new file mode 100644 index 00000000000..6ab400146e6 --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/advanced-prompt-syntax.mdx @@ -0,0 +1,138 @@ +--- +title: Advanced Prompt Syntax +description: Fine control over prompts — attention weighting, blending and conjunctions for SD 1.5, SD 2.x and SDXL, and the advanced dynamic prompt syntax for multiple choices, weights, variables and comments. +lastUpdated: 2026-09-30 +sidebar: + order: 3 +--- + +Most prompts need nothing more than plain text, perhaps with a [prompt template](/users-guide/image-generation/prompting-guide/#prompt-templates) or a few [dynamic prompt](/users-guide/image-generation/prompting-guide/#dynamic-prompts) alternatives. This page covers the syntax for finer control: making parts of a prompt count for more or less, blending prompts, and the advanced forms of dynamic prompts. + +## Attention weighting + +Attention weighting makes parts of the prompt count for more or less than the rest. It uses Compel syntax, which applies to **SD 1.5, SD 2.x and SDXL**. Newer model families, including FLUX, Z-Image, CogView4 and Qwen Image, do not use it, and the symbols are passed to the model as ordinary text. + +:::note[Compatibility note] + If a weighted prompt seems to be ignored, check which model you are using. The syntax in this section only affects SD 1.5, SD 2.x and SDXL. +::: + +| Syntax | Effect | +|---|---| +| `trees+` / `fog-` | Increase or decrease a single word | +| `(golden hour light)+` | Weight a phrase | +| `(cinematic lighting)1.25` | Use an exact numeric weight | +| `(portrait with (blue eyes)1.3)1.1` | Nest weights | +| `("portrait photo", "oil painting").blend(0.7, 0.3)` | Blend prompts | +| `("red silk dress", "studio portrait", "soft rim light").and()` | Encode clauses separately | +| `colored pencil \(medium\)` | Use literal parentheses | + +### Weighting with `+` and `-` + +Append `+` to increase influence, or `-` to reduce it. + +```text +freckles+ +background crowd- +(soft rim light)++ +``` + +- Single words can be weighted directly. Wrap phrases of several words in parentheses. +- Each additional `+` or `-` compounds, in steps of roughly 10%. +- One or two steps is usually enough. Extreme weighting can overpower the rest of the prompt. + +### Numeric weights + +Use numeric weights when you want precise control instead of repeated plus or minus markers. + +```text +(cinematic lighting)1.25 +(background crowd)0.8 +``` + +`1` is neutral, values above `1` increase emphasis, and values between `0` and `1` reduce it. Wrap the weighted phrase in parentheses. + +Groups can be nested, and the weights combine. In `(portrait with (blue eyes)1.3)1.1`, the outer group strengthens the whole phrase, and `blue eyes` gets even more emphasis. + +### Adjusting weights with the keyboard + +In a prompt field, select text and press **Ctrl+Up** or **Ctrl+Down** to raise or lower its attention. With no selection, the shortcut targets the word at the cursor. Selecting a group's parentheses or weight targets its contents. + +- A selection inside a group changes only the selected words. A selection crossing a group boundary changes the selected words on both sides. +- Symbolic weights move one `+` or `-` step. Numeric weights change by `0.1` of effective attention, including inherited group weights. +- The numeric attention preference controls new weights; existing group weights keep their style. +- Adjacent content with matching weights merges, and the adjusted content stays selected for the next keypress. +- Inside prompt functions, the shortcut adjusts prompt arguments and preserves function parameters. Use **Ctrl+Z** to undo a weight change. + +### Blending with `.blend()` + +Use `.blend()` to mix the meaning of two or more prompts. + +```text +("portrait photo, 85mm lens", "oil painting, visible brushstrokes").blend(0.7, 0.3) +``` + +Provide one weight for each prompt. Keeping the weights near a total of `1` makes the result easier to reason about, and quoting each prompt is safest, especially when they contain commas. + +### Combining clauses with `.and()` + +Use `.and()` when you want separate prompt clauses encoded individually instead of as one long comma-separated sentence. + +```text +("red silk dress", "studio portrait", "soft rim light").and() +``` + +This can behave differently from `red silk dress, studio portrait, soft rim light`. If a normal prompt keeps collapsing ideas together, `.and()` is worth testing. + +### Literal parentheses + +Unescaped parentheses are treated as weighting syntax. To include actual parentheses in the text, escape them with backslashes: + +```text +colored pencil \(medium\) +A bear \(with razor-sharp teeth\) in a forest +``` + +## Advanced dynamic prompt syntax + +These additions to the [dynamic prompt](/users-guide/image-generation/prompting-guide/#dynamic-prompts) syntax give finer control over how variations are built. + +### Choosing more than one option + +Put a number and `$$` at the start of a group to pick several different options from it. They are joined with a comma: + +```text +portrait, {2$$rim light|fog|rain} +``` + +gives prompts such as `portrait, rim light,fog` and `portrait, fog,rain`. To join them differently, put the separator between two `$$`: + +```text +portrait, {2$$ and $$rim light|fog|rain} +``` + +gives prompts such as `portrait, rim light and fog`. A range picks a varying number of options: `{1-3$$rim light|fog|rain}` picks one, two or three. + +### Weighting options + +In **Random sample** mode, you can make some options more likely than others by putting a weight and `::` in front of them: + +```text +a {0.7::red|0.2::green|0.1::blue} balloon +``` + +makes red balloons most common. Weights are ignored in All combinations mode, where every option appears once. + +### Variables + +A variable gives a group a name, so you can define it once and use it in several places. `${name={...}}` defines it and `${name}` uses it: + +```text +${color={red|blue}} a ${color} hat with a ${color} scarf +``` + +Each use varies on its own, like a group written out in full, so this gives all four combinations from `a red hat with a red scarf` to `a blue hat with a blue scarf`. Variables are most useful for long lists you would otherwise repeat; for lists you use in many prompts, a [wildcard](/users-guide/image-generation/prompting-guide/#wildcards) is more convenient. + +### Comments + +Everything after a `#` is treated as a comment and left out of the prompt. Avoid `#` in ordinary prompts, since the rest of the prompt after it is dropped. + diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/generate-panel.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/generate-panel.mdx new file mode 100644 index 00000000000..85cf0b38227 --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/generate-panel.mdx @@ -0,0 +1,138 @@ +--- +title: Generate Panel Reference +description: Every section of the Generate panel in detail — size, render settings, negative prompts, advanced options, resetting to defaults, concepts, reference images and the prompt tools. +lastUpdated: 2026-09-30 +sidebar: + order: 1 +--- + +import { Steps } from '@astrojs/starlight/components'; + +This page describes each section of the **Generate** panel in detail. For a first walkthrough — choosing a model, generating a first image, and sending results to the Gallery or the Canvas — see [Generating and modifying still images](/users-guide/image-generation/introduction/). + +## Setting the image size + +The **Size** section sets the width and height of the image. + +* **Width** and **Height** can be set by dragging, by typing a value, or by dragging the corner of the small size preview. Sizes run from 64 to 4096 pixels and snap to a multiple the model requires, usually 8, 16 or 32. +* **Aspect ratio** offers common shapes: 21:9, 16:9, 3:2, 4:3, 1:1, 3:4, 2:3, 9:16 and 9:21, or **Free**. Choosing a ratio locks it, so changing the width changes the height to match. The lock button between the two fields locks or unlocks the current ratio. +* **Swap width and height** turns a landscape image into a portrait one and back. +* Each model has an optimal image area, typically about one megapixel for newer models and less for SD 1.5. The line under the fields shows the image's size in megapixels, marks the recommended size, and estimates the run time from your recent runs. **Set optimal size** resizes the image to the model's optimal area at the current aspect ratio. + +Models produce their best results near their optimal size. Much larger images can show repeated subjects or distorted anatomy. To make a large image, generate at the optimal size and then upscale. + +## Render settings + +The **Render** section controls how the image is generated. Each slider has a mark at the model's recommended value. To reset a single field, double-click it or right-click it and choose **Reset to model default**. + +### Steps + +**Steps** is the number of denoising passes the model makes. More steps add detail and refine the image, up to a point where further steps change little and only take longer. Distilled "Turbo" and "Lightning" models are designed for a handful of steps (often 4 to 9), while standard models usually need 25 to 50. The model default is a good place to start. + +### CFG and Guidance + +**CFG** (classifier-free guidance) sets how strictly the model follows your prompt. Higher values follow the prompt more literally but can look harsh or oversaturated; lower values are looser and more natural. Typical values are 3 to 7 for most models, and 1 for distilled models, which are trained to work without CFG. Some models, including FLUX, show **Guidance** instead: a value built into the model that plays the same role, typically around 3.5. The model's page under Local Models gives recommended values. + +### Scheduler + +The **Scheduler** is the algorithm that decides how much noise to remove at each step. Schedulers differ in speed, in how many steps they need, and in the character of the result. Euler, the DPM++ family and UniPC are good general-purpose choices; LCM is only for LCM and similar distilled models. The list of schedulers depends on the model, and some models use a fixed schedule and do not show this field. **Use model default scheduler** switches back to the model's own choice. + +### Seed + +The **Seed** sets the starting noise for the image. The same model, prompt, seed and settings reproduce the same image, which lets you change one setting at a time and compare the results. + +* **New seed** picks a random seed. +* **Seed mode** sets how seeds are chosen: **Random** picks a fresh starting seed each time you press Invoke, **Fixed** reuses the seed in the field, and **Increment** and **Decrement** use successive seeds, counting up or down by one. A line under the field shows the seeds the next batch will use. +* **Recent seeds** shows thumbnails of recent images. Click one to reuse its seed; the mode switches to Fixed. + +### Negative prompt + +A negative prompt lists what you do *not* want in the image, such as *blurry, watermark, extra fingers*. Models that support one show a **+ Negative prompt…** button under the prompt; click it to open the box, and click **Turn off negative prompt** to hide it again without losing the text. + +Not every model uses a negative prompt. SD 1.5, SDXL, SD 3.5 and CogView4 always use it. Several newer models, including Z-Image, Qwen Image and Krea-2, use it only when CFG is above 1, and the box says so. FLUX.1, FLUX.2 and Ideogram 4 do not use a negative prompt, and the box is not shown for them. + +### Advanced settings + +The **Advanced** section appears for models with extra options, and its header counts the settings you have changed. Depending on the model it can include: + +* **VAE** and **VAE precision** — for SD 1.5 and SDXL, choose a different VAE or decode at FP32 precision. Other families set their VAE in the Components section. +* **CLIP skip** and **CFG rescale** — SD 1.5 and SD 2.x only. +* **HiDiffusion** — improves structure at large sizes for SD 1.5 and SDXL; see [HiDiffusion](/users-guide/image-generation/advanced-topics/hidiffusion/). +* **Color compensation** — SDXL only. +* **PiD Decode** — decodes the image at up to four times its size; see [PiD Super-Resolution Decode](/users-guide/image-generation/advanced-topics/pid-decode/). +* **Seamless tiling** — makes the image tile along the X axis, the Y axis, or both. + +### Resetting to defaults + +Each model has recommended settings, which you can change in the Model Manager. **Reset all to model defaults**, the ↺ button in the Generate panel's title bar, applies them: size, steps, CFG or guidance, scheduler and VAE settings. Your prompts are left alone. The button is disabled when your settings already match the defaults. + +To return to the settings of an earlier image instead, right-click it in the Gallery or Preview and open **Recall Metadata**. **Recall All** restores its model, prompts, seed, size and render settings; **Remix Image** does the same but keeps a new seed; and **Use Prompt**, **Use Seed** and **Use Size** recall one thing at a time. + +To keep a combination of settings for later, use **Presets** in the panel's title bar to save the current settings under a name and apply them again. + +## Adding concepts + +Concepts, also called LoRAs, are small add-on models that teach the main model a style, a character, an object or a look. They are added in the **Guidance** section. + + + +1. Click **Add concept** and choose a LoRA. The list only shows LoRAs made for the selected model's family. + +2. Adjust its **Weight**, which sets how strongly it acts. The default is the LoRA's own recommended weight, or 0.75. Values between 0.5 and 1 are typical; higher values can overpower the image, and negative values push the image away from the concept. + +3. Add any trigger words the LoRA needs to your prompt. They are shown under the LoRA's name, and the **+** button in the prompt toolbar inserts them for you (see [Prompt Triggers](/users-guide/image-generation/prompt-tools/#prompt-triggers)). + + + +You can add as many concepts as you like and combine them. Use the switch on each row to turn a concept off without removing it, and the trash button to remove it. If you change to a model from a different family, its concepts are marked **Incompatible** or removed. + +## Adding reference images + +A reference image guides the new image by example rather than by words: its style, its composition, a person or an object in it. Reference images are added at the top of the **Guidance** section, and only appear for models that support them. + +To add one, click **Add reference image** and pick images from the Gallery or upload new ones, or drag images from the Gallery onto the section. You can also right-click any image and choose **Use as Reference Image**. Most models accept up to five. The order of the references matters to some models; use the arrow buttons on each card to reorder them. + +Hovering over a reference's thumbnail offers **Crop**, to use only part of the image, and **Use size**, to set the generation's size to match the reference. The switch on each card turns a reference off without removing it. + +What a reference does, and what options it has, depends on the model family: + +| Model family | How references work | Options | +|---|---|---| +| **SD 1.5, SDXL** | An IP-Adapter model carries the reference's look into the image. | **Mode**: **Style**, **Composition** or **Both**. **Weight**. Under **Advanced**: the IP-Adapter model, a **Style variant** and the **Active steps** range. | +| **FLUX.1** | An IP-Adapter, or a **FLUX Redux** model, which transfers the reference's content. | IP-Adapter options as above, or **Image influence** (Lowest to Highest) for Redux. | +| **FLUX.1 Kontext** | Kontext edits or recombines the reference images according to your prompt. | None beyond the model. | +| **FLUX.2, Qwen Image Edit** | The model reads the references directly and uses them as your prompt instructs, for example *put the jacket from the first image on the person in the second*. | None. | + +Reference images require an extra model for SD 1.5, SDXL and FLUX.1 (an IP-Adapter or FLUX Redux model). These are available as starter models in the Model Manager, and an installed IP-Adapter is selected automatically. + +## Prompt tools + +The toolbar above the prompt box holds tools that help you write prompts. From left to right: **Templates**, **Dynamic**, **Add prompt trigger**, **Expand prompt**, **Image to prompt** and **Prompt history**. + +### Prompt enhancement + +**Expand prompt** turns a short prompt into a detailed description using a local language model. Write a few words, open the tool, pick a Text LLM and a **System prompt** (the instructions that tell the language model how to rewrite), and click **Expand**. The expanded text replaces your prompt, and you can edit it before pressing Invoke. Several system prompts ship with InvokeAI, some tuned for particular model families, and you can write your own. + +You need a Text LLM model installed; if there is none, the tool offers to open the Model Manager. See [Prompt Tools](/users-guide/image-generation/prompt-tools/#expand-prompt) for suitable models and for managing system prompts. + +### Image to prompt + +**Image to prompt** writes a prompt that describes an existing image, using a vision-language model (LLaVA OneVision). Select an image in the Gallery or Preview, or drag one onto the prompt box, then open the tool, choose the model and click **Generate Prompt**. The description replaces your prompt. This is useful for recreating an image you like in another model, or for starting from a close description and editing it. See [Prompt Tools](/users-guide/image-generation/prompt-tools/#image-to-prompt). + +### Prompt history + +Every prompt you submit with Invoke is kept in the project's **Prompt history**, along with its negative prompt, up to the last 100. Open it from the clock button to search your earlier prompts, click **Use prompt** to bring one back, or delete entries you no longer need. **Clear** empties the history at once, without asking. + +To step through the history without opening it, press **Alt+Up** and **Alt+Down** while the prompt box has focus. Stepping past the newest entry brings back what you were typing. The history belongs to the project and is shared by the Generate, Video and Upscale panels. + +## More prompting features + +These features change what the prompt says or how the model reads it. Prompt triggers are described on the [Prompt Tools](/users-guide/image-generation/prompt-tools/) page, and the others in the [Prompting Guide](/users-guide/image-generation/prompting-guide/). + +| Feature | What it does | +|---|---| +| **[Prompt templates](/users-guide/image-generation/prompting-guide/#prompt-templates)** | Wrap your prompt in a reusable style, such as a photographic or concept-art look, without retyping it. Includes 18 built-in templates, and you can create, import and export your own. | +| **[Dynamic prompts](/users-guide/image-generation/prompting-guide/#dynamic-prompts)** | Write alternatives such as `a {red\|green\|blue} car` to generate one image per combination, or a random sample of them. Wildcards let you reuse named lists of values. | +| **[Attention weighting](/users-guide/image-generation/advanced-prompt-syntax/#attention-weighting)** | Make parts of the prompt count for more or less, for example `(red car)++` or `(red car)1.3`, and blend or combine prompts. Select text and press **Ctrl+Up** or **Ctrl+Down** to adjust its weight. Applies to SD 1.5, SD 2.x and SDXL. | +| **[Prompt triggers](/users-guide/image-generation/prompt-tools/#prompt-triggers)** | Insert the trigger phrases of your model and concepts, compatible embeddings and wildcards from a searchable menu. | + diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/image-generation.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/image-generation.mdx new file mode 100644 index 00000000000..0410b3a4299 --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/image-generation.mdx @@ -0,0 +1,58 @@ +--- +title: Concepts and Terms +description: A short glossary of the ideas and terms you meet when generating images — models, prompts, steps, CFG, schedulers, seeds, latents, LoRAs, ControlNets and more — with links to the guides that cover them. +lastUpdated: 2026-09-30 +sidebar: + order: 6 +--- + +New to AI image generation? This page explains the terms you will meet in InvokeAI, in plain language, and points to the guides that cover each one in depth. To start generating straight away, see [Generating and modifying still images](/users-guide/image-generation/introduction/). + +## How an image is made + +Most image models in InvokeAI are **diffusion models**. A diffusion model starts from random noise and, over a series of **steps**, gradually removes the noise until an image remains, steered at every step by your prompt. The work happens on a compressed version of the image, the **latents**, which is decoded into pixels at the end. The [Diffusion overview](/users-guide/image-generation/advanced-topics/diffusion/) explains the process in more detail. + +## Glossary + +**Model.** The trained network that generates images. Models come in families, such as SD 1.5, SDXL, FLUX and Z-Image, which differ in size, speed, quality and the add-ons they accept. See [Managing media generation models](/users-guide/models/introduction/). + +**Prompt.** The text that describes the image you want. Many models also accept a **negative prompt** listing what you do not want. See the [Prompting Guide](/users-guide/image-generation/prompting-guide/). + +**Steps.** How many denoising passes the model makes. More steps refine detail up to a point, after which they change little. Standard models typically use 25 to 50 steps; distilled "Turbo" and "Lightning" models are designed for 4 to 10. + +**CFG (classifier-free guidance) or Guidance.** How strictly the model follows the prompt. Higher values follow it more literally; too high looks harsh or oversaturated. Distilled models usually run at a CFG of 1. + +**Scheduler.** The method that decides how much noise to remove at each step. Different schedulers trade speed, step count and character of the result. Some model families use a fixed schedule. + +**Seed.** The number that sets the starting noise. The same model, prompt, seed and settings reproduce the same image, which makes it easy to see what a single change does. + +**Latents.** The compressed form of an image that diffusion models work on. The **VAE** (variational autoencoder) converts between latents and pixels. + +**Text encoder.** The part of a model that turns the prompt into numbers the model understands. Older models use CLIP; newer ones use large language models, which understand long, detailed prompts. + +**Image to image.** Generating from an existing image instead of from pure noise. **Denoising strength** sets how much of the original is kept. + +**Inpainting and outpainting.** Regenerating part of an image (inpainting) or extending it beyond its edges (outpainting), done on the [Canvas](/users-guide/canvas/introduction/). + +**LoRA (concept).** A small add-on model that teaches the main model a style, character or object. Added in the Generate panel's Guidance section. See [Adding concepts](/users-guide/image-generation/generate-panel/#adding-concepts). + +**Embedding (textual inversion).** A trained word that stands for a style or concept, inserted into the prompt as ``. + +**Trigger phrase.** A word or phrase a LoRA or model needs in the prompt to produce its effect. See [Prompt Tools](/users-guide/image-generation/prompt-tools/#prompt-triggers). + +**ControlNet and control layers.** Add-on models that guide the composition with an image such as edges, depth or a pose. See [Control layers](/users-guide/canvas/introduction/#control-layers). + +**Reference image (IP-Adapter, Redux).** An image that guides the result by example, for its style, composition or content. See [Adding reference images](/users-guide/image-generation/generate-panel/#adding-reference-images). + +**Distilled model.** A model trained to produce good results in very few steps, usually at a CFG of 1, trading some flexibility for speed. Often named *Turbo*, *Lightning* or *Schnell*. + +**Quantized model.** A model stored at reduced precision, such as GGUF, fp8, int8 or nf4, to save memory and disk space at a small cost in quality. See [Checkpoints vs Diffusers formats](/users-guide/models/model-families/#checkpoints-vs-diffusers-formats). + +**Workflow.** A graph of processing steps that you build yourself in the workflow editor. See [Automating with Workflows](/users-guide/workflows/introduction/). + +## Learn more + +- [Generating and modifying still images](/users-guide/image-generation/introduction/) — the Generate panel, step by step. +- [Prompting Guide](/users-guide/image-generation/prompting-guide/) — writing prompts that work, and the tools that extend them. +- [Parameter Selection Guide](/users-guide/image-generation/parameters/) — choosing steps, CFG, schedulers and seeds. +- [This presentation](https://docs.google.com/presentation/d/1IO78i8oEXFTZ5peuHHYkVF-Y3e2M6iM5tCnc-YBfcCM/edit?usp=sharing) by one of the maintainers (@lstein) introduces the ideas behind AI image generation. diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/intermediates.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/intermediates.mdx new file mode 100644 index 00000000000..bf2fc124698 --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/intermediates.mdx @@ -0,0 +1,57 @@ +--- +title: Intermediates +description: Reclaim disk space by deleting the temporary images and videos generation leaves behind, per project and per account. +sidebar: + order: 7 +--- + +Generation produces intermediate images and videos: canvas composites, filter previews, staging candidates and other temporary files that were never saved to the gallery. The **Intermediates** section of Settings lists them per project, shows how much space deleting them would reclaim, and deletes them safely. It is also reachable from a project's actions menu, from the Users page (administrators) and from **Data & workspace**. + +## What the page shows + +Each row is one of your projects, or **Unassigned** for intermediates without a known project. Deleted projects' leftovers appear under Unassigned. A row shows the project's cover, its **Used** count (intermediates a cleanup keeps) and its **Unused** count (intermediates nothing needs any more), with the measured reclaimable size underneath. Sizes are measured in the background after upgrading; the section says so while measurement is still running and never counts an unmeasured file as empty. + +Select rows, or **Select all**, and press **Delete**. Select all covers every project the current search matches, including projects on other pages; untick a row to leave it out (up to 1,000 rows). A selection that matches more than 10,000 rows is refused; narrow the search. The confirmation explains why kept items are kept: referenced by a saved document, used by queued or running work, or created recently. + +Administrators start on their own account. **Show everyone** expands the view to every account; they can search accounts as well as project names and can open one account from the Users page. Opening the section from a project action shows that project directly, even when it is outside the first results page. + +## Safe and force deletion + +A normal delete removes only safe items. It always keeps: + +- anything a pending, waiting or running job produced or uses as an input, including cached images and videos reused from an earlier job and completed child-workflow media until the root workflow finishes; +- anything created in the last 30 minutes, so an upload the browser is about to reference or enqueue is never collected, and cached media for 30 minutes after the job that reused it ends; +- anything the open webv2 editor still references, including unsaved edits and available undo history; the browser refreshes this temporary hold while it is open; +- anything a saved document still names: a project or library workflow (canvas staging candidates, cropped reference images), the legacy editor's saved canvas and reference images, or a project set aside for repair when project boards were introduced; +- anything that is no longer an intermediate. + +The advanced option in the confirmation, **also delete items that saved projects or workflows still reference**, turns it into a force delete. The confirmation lists the documents it would break (the first 200, with a count of any others); those projects will show missing images and undo may no longer restore them. Confirming acknowledges exactly those documents: a document saved after the check keeps its media, while a document on the list may lose more of its references if it names further items while the deletion runs. A force delete never touches active, browser-held or recent work and cannot be undone; it asks for an acknowledgement and for the word `DELETE` to be typed. A regular user's force delete also keeps anything another account's document names, such as a shared workflow. The same applies when an administrator loses administrator rights while their force delete runs. A force delete that would break more than 10,000 documents is refused; narrow the selection. + +Every confirmation is based on a fresh server check that fixes the moment recency is judged at. Files created after the check are never included, and every batch re-checks its targets at the moment of deletion: an intermediate that was promoted, referenced or picked up by a job in the meantime is kept. Items that became unused after the check are collected too, so the deleted count can exceed the estimate. + +An open editor refreshes its browser hold every five minutes and when the tab becomes visible again. It releases names removed from the current projects and undo history, and releases the whole hold when the tab closes or you sign out. If the tab crashes instead, the hold expires 15 minutes after its last refresh. Each account holds media for at most eight open editors (tabs) at once; past that, the editor refreshed longest ago stops protecting its media until its next refresh. A single editor holds up to 50,000 images and 50,000 videos. Reopen an unsaved browser-only draft before deleting old intermediates. + +## Progress and results + +Cleanup runs on the server, so you can navigate away while it works. The page reports deleted, kept and failed items separately, along with files whose records are gone but whose bytes could not be removed yet; those are purged on the next server start and are not counted as reclaimed. Reloading the page, or opening the section with no deletion shown, picks up a deletion that is still running. When a deletion stops early or leaves failed items, **Delete again** repeats the same request, in the same mode; the check runs afresh, so it picks up exactly what remains. + +Running deletions live in the server's memory. If the server restarts while one runs, the section still showing it reports that its status is no longer known; after a reload the interrupted run is simply gone. Either way, refresh the list and delete again if anything remains. The server keeps each account's last few finished deletions until it restarts. + +The server accepts one running deletion per account and at most eight at a time. Wait for one to finish before confirming another delete if a limit is reached. Cleanup is refused while [image storage maintenance](/users-guide/gallery/image-storage-maintenance/) is active. + +## Permissions + +| Action | Regular user | Administrator | +| --- | --- | --- | +| Delete a project's intermediates | Own projects | Any project | +| Delete an account's intermediates | Own account | Any account | +| Delete everyone's intermediates | No | Yes | +| Force delete referenced intermediates | Own files that only your own documents name | Any authorized scope | + +## Legacy endpoint + +The older `DELETE /api/v1/images/intermediates` route remains available to administrators. It applies the same safety policy as a normal delete, so it keeps referenced, active and recent images and returns the number of images it deleted. It does not touch videos. `GET /api/v1/images/intermediates` counts exactly the images that route would delete, so the legacy button settles at zero after it runs. + +## Limitations + +- Canvas uploads made before the server accepted the project they belong to are stored without a project and are never re-associated with it. They are still protected by saved references and browser holds, but they appear under **Unassigned**. diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/parameters.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/parameters.mdx new file mode 100644 index 00000000000..4a46b97db0a --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/parameters.mdx @@ -0,0 +1,174 @@ +--- +title: Parameter Selection Guide +description: How to choose steps, CFG or guidance, schedulers, seeds, size and denoising strength for the model you are using, with a historical study of sampler convergence. +lastUpdated: 2026-09-30 +sidebar: + order: 5 +--- + +import { Card, CardGrid, Steps } from '@astrojs/starlight/components'; + +The right settings depend far more on the model than on any general rule. This guide explains how to choose them, starting from the model's own recommendations. For where each setting lives in the Generate panel, see [Render settings](/users-guide/image-generation/generate-panel/#render-settings). + +## Start from the model's defaults + +Each model has recommended settings, which are shown as marks on the sliders and which you can edit in the Model Manager. When you change models, InvokeAI keeps your current settings if they are compatible, so after switching models click **Reset all to model defaults** (the ↺ button in the Generate panel's title bar) to start from the new model's recommendations. Each model family's page under [Local Models](/users-guide/models/model-families/#model-families) lists its typical values. + +## Steps + +More steps refine an image up to a point; beyond it, an image changes little and generation only takes longer. + +* **Distilled models** — Turbo, Lightning and Schnell variants, and models used with a Lightning or Turbo LoRA — are trained for a specific small number of steps, typically 4 to 10. Using many more does not help and can make results worse. +* **Standard models** usually need 25 to 50 steps. SD 1.5 and SDXL work well at about 30. +* To explore ideas quickly, generate a batch at fewer steps, then recall the seed of the image you like and generate it again at the full step count (see [Seeds](#seeds) below). + +## CFG and guidance + +**CFG** sets how strictly the model follows the prompt. Higher values follow it more literally but look harsher; lower values are looser and more natural. + +* SD 1.5 and SDXL: about 5 to 8. +* Newer standard models, such as SD 3.5, Qwen Image, ERNIE-Image and Krea-2 Raw: about 3.5 to 6. +* Distilled models: 1. At a CFG of 1 the negative prompt has no effect, so several model families hide or ignore it. +* FLUX.1 and some other families show **Guidance** instead, a value built into the model; about 3.5 for FLUX.1 dev. + +If an image looks oversaturated or "burned", lower CFG; if it ignores parts of the prompt, raise it slightly or improve the prompt. + +## Schedulers + +The scheduler decides how much noise is removed at each step. Which ones are offered depends on the model: + +* **SD 1.5, SD 2.x and SDXL** offer the full list. **DPM++ 2M Karras** and **Euler** are good general-purpose choices; **Euler Ancestral** and the other *Ancestral* and *SDE* schedulers add variety and keep changing the image as steps increase; **UniPC** and **DPM++ 3M** reach good results in fewer steps. **LCM** and **TCD** are only for LCM- and TCD-distilled models. +* **Flow models**, such as FLUX.1, FLUX.2, Z-Image and ERNIE-Image, offer **Euler**, **Heun (2nd order)**, which is slower per step but can need fewer steps, and **LCM** for distilled models. +* **Anima** has its own short list, starting with **Euler**. +* Some families, including SD 3.5, Qwen Image, CogView4, Krea-2 and Ideogram 4, use a fixed schedule and do not show the field. + +**Use model default scheduler** switches back to the model's own choice. + +## Seeds + +The seed sets the starting noise, so the same model, prompt, seed and settings reproduce the same image. + +* While exploring, leave **Seed mode** on **Random** to get a different image every time. +* To refine a result, set the seed to **Fixed**, for example by clicking the image's thumbnail under **Recent seeds**, and then change one setting at a time so you can see exactly what it does. +* To polish a promising draft, recall its seed with **Use Seed** from the image's **Recall Metadata** menu and generate it again at more steps or a larger size. + +## Size + +Each model has an optimal image area, around one megapixel for most current models and less for SD 1.5. Results are best near that size; much larger images can repeat subjects or distort anatomy. Use **Set optimal size** in the Size section, and upscale afterwards for larger images. + +## Denoising strength + +For image-to-image, inpainting and outpainting on the Canvas, **Denoising strength** sets how much the existing image may change. Around 30% to 50% keeps the composition and colors while changing details; 70% and above (the default is 75%) reworks the image substantially, keeping only its broad layout. + +## Appendix: sampler convergence on SD 1.x (historical) + +:::note[Historical study] +This study was made in 2022 with Stable Diffusion 1.4 and the command-line interface of the time. Its prompts use that interface's flags: `-s` for steps, `-C` for CFG, `-S` for the seed, `-W` and `-H` for the size, and `-n` for the number of images; `K_LMS`, `K_HEUN`, `K_EULER_A` and so on are the samplers that are now called LMS, Heun, Euler Ancestral and so on. The specific numbers do not carry over to current models, but the general lesson does: images converge as steps increase, and a quick low-step preview is often a good guide to the final result. +::: + + + - Results converge as steps are increased, except with the ancestral samplers (`K_DPM_2_A` and `K_EULER_A`). Often at 100 steps or more, but sometimes not until 700. + - Producing a batch of candidate images at low step counts (8 to 30) can save hours of computation. + - `K_HEUN` and `K_DPM_2` converge in fewer steps, but are slower per step. + - `K_DPM_2_A` and `K_EULER_A` incorporate a lot of creativity and variability. + + +### Sampler speed + +*(Tested on an M1 Max with 64 GB, at 512×512, averaged over 3 samples.)* + +| Sampler | it/s | +| :--- | :--- | +| `DDIM` | 1.89 | +| `PLMS` | 1.86 | +| `K_EULER` | 1.86 | +| `K_LMS` | **1.91** (fastest) | +| `K_EULER_A` | 1.86 | +| `K_HEUN` | 0.95 *(slower)* | +| `K_DPM_2` | 0.95 *(slower)* | +| `K_DPM_2_A` | 0.95 *(slower)* | + +### Sampler Results by Subject + +Let's start by choosing a prompt and using it with each of our 8 samplers, running it for 10, 20, 30, 40, 50 and 100 steps. + +#### Anime +> `"an anime girl" -W512 -H512 -C7.5 -S3031912972` + +![Anime Comparison Grid](https://user-images.githubusercontent.com/50542132/191868725-7f7af991-e254-4c1f-83e7-bed8c9b2d34f.png) + +Immediately, you can notice results tend to converge — that is, as `-s` (step) values increase, images look more and more similar until there comes a point where the image no longer changes. + +You can also notice how `DDIM` and `PLMS` eventually tend to converge to K-sampler results as steps are increased. Among K-samplers, `K_HEUN` and `K_DPM_2` seem to require the fewest steps to converge, and even at low step counts they are good indicators of the final result. Finally, `K_DPM_2_A` and `K_EULER_A` seem to do a bit of their own thing and don't keep much similarity with the rest of the samplers. + +#### Nature +Now, these results seem interesting, but do they hold for other topics? Let's try! + +> `"valley landscape wallpaper, d&d art, fantasy, painted, 4k, high detail, sharp focus, washed colors, elaborate excellent painted illustration" -W512 -H512 -C7.5 -S1458228930` + +![Nature Comparison Grid](https://user-images.githubusercontent.com/50542132/191868763-b151c69e-0a72-4cf1-a151-5a64edd0c93e.png) + +With nature, you can see how initial results are even more indicative of the final result — more so than with characters/people. `K_HEUN` and `K_DPM_2` are again the quickest indicators, almost right from the start. Results also converge faster (e.g. `K_HEUN` converged at `-s21`). + +#### Food +> `"a hamburger with a bowl of french fries" -W512 -H512 -C7.5 -S4053222918` + +![Food Comparison Grid](https://user-images.githubusercontent.com/50542132/191868898-98801a62-885f-4ea1-aee8-563503522aa9.png) + +Again, `K_HEUN` and `K_DPM_2` take the fewest number of steps to be good indicators of the final result. `K_DPM_2_A` and `K_EULER_A` seem to incorporate a lot of creativity/variability, capable of producing rotten hamburgers, but also of adding lettuce to the mix. And they're the only samplers that produced an actual 'bowl of fries'! + +#### Animals +> `"grown tiger, full body" -W512 -H512 -C7.5 -S3721629802` + +![Animal Comparison Grid](https://user-images.githubusercontent.com/50542132/191868870-9e3b7d82-b909-429f-893a-13f6ec343454.png) + +`K_HEUN` and `K_DPM_2` once again require the least number of steps to be indicative of the final result (around `-s30`), while other samplers are still struggling with several tails or malformed back legs. + +It also takes longer to converge (for comparison, `K_HEUN` required around 150 steps to converge). This is normal, as producing human/animal faces/bodies is one of the things the model struggles the most with. For these topics, running for more steps will often increase coherence within the composition. + +#### People +> `"Ultra realistic photo, (Miranda Bloom-Kerr), young, stunning model, blue eyes, blond hair, beautiful face, intricate, highly detailed, smooth, art by artgerm and greg rutkowski and alphonse mucha, stained glass" -W512 -H512 -C7.5 -S2131956332`. *(This time, we will go up to 300 steps).* + +![People Comparison Grid 1](https://user-images.githubusercontent.com/50542132/191871743-6802f199-0ffd-4986-98c5-df2d8db30d18.png) + +Observing the results, it again takes longer for all samplers to converge (`K_HEUN` took around 150 steps), but we can observe good indicative results much earlier (see: `K_HEUN`). Conversely, `DDIM` and `PLMS` are still undergoing moderate changes (see: lace around her neck), even at `-s300`. + +In fact, as we can see in this other experiment, some samplers can take 700+ steps to converge when generating people. + +![People Comparison Grid 2](https://user-images.githubusercontent.com/50542132/191992123-7e0759d6-6220-42c4-a961-88c7071c5ee6.png) + +Note also the point of convergence may not be the most desirable state (e.g. you might prefer an earlier version of the face that is more rounded), but it will probably be the most coherent regarding arms/hands/face attributes. You can always merge different images with a photo editing tool and pass it through `img2img` to smoothen the composition. + +--- + +### Batch Generation Speedup + +This realization about convergence is very useful because it means you don't need to create a batch of 100 images (`-n100`) at `-s100` just to choose your favorite 2 or 3 images. + +You can produce the same 100 images at `-s10` to `-s30` using a K-sampler (since they converge faster), get a rough idea of the final result, choose your 2 or 3 favorite ones, and then run `-s100` on those specific images to polish details. This technique is **3-8x as quick**. + +:::tip[Time Savings Example] + Assuming 60 seconds per 100 steps: + + - **Method A:** 60s * 100 images = **6000s** (100 images at `-s100`, manually picking 3 favorites). Total time: **1 hour and 40 minutes.** + - **Method B:** 6s * 100 images + 60s * 3 images = **780s** (100 images at `-s10`, manually picking 3 favorites, and running those 3 at `-s100` to polish details). Total time: **13 minutes.** +::: + +### Three Key Takeaways + +Finally, it is relevant to mention that, in general, there are 3 important moments in the process of image formation as steps increase: + + +1. **The Indicator Stage:** + The earliest point at which an image becomes a good indicator of the final result. This is useful for batch generation at low step values to preview outputs before committing to higher steps. +2. **The Coherence Stage:** + The point at which an image becomes coherent, even if different from the final converged result. This is useful for low-step batch generation where quality is improved via other techniques (like inpainting) rather than raw step count. +3. **The Convergence Stage:** + The point at which an image fully converges and stops changing. + + +:::note[Workflow Dictates Strategy] + Remember that your workflow/strategy should define your optimal number of steps, even for the same prompt and seed. For example, if you seek full convergence, you may run `K_LMS` for `-s200`. However, running `K_LMS` for `-s20` (taking one-tenth the time) may perform just as well if your workflow includes adding small missing details via `img2img`. +::: + +![Low Step Sampler Comparison](https://user-images.githubusercontent.com/50542132/192046823-2714cb29-bbf3-4eb1-9213-e27a0963905c.png) diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/prompt-tools.md b/docs/src/content/docs/Users Guide/04.Image Generation/prompt-tools.md new file mode 100644 index 00000000000..2f9d9f68109 --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/prompt-tools.md @@ -0,0 +1,117 @@ +--- +title: Prompt Tools +sidebar: + order: 4 +lastUpdated: 2026-09-30 +--- + +The prompt toolbar holds tools that help you write prompts. **Expand Prompt** and **Image to Prompt** use local language models to write or rewrite a prompt for you; if no compatible model is installed, the tool's popover says so and offers an **Open Model Manager** button. **Prompt triggers** insert the trigger phrases, embeddings and wildcards your models need. + +## Expand Prompt + +Takes your short prompt and expands it into a detailed, vivid description suitable for image generation. + +**How to use:** + +1. Type a brief prompt (e.g. "a cat in a garden") +2. Click **Expand prompt** (the pencil-and-sparkles button) in the prompt toolbar +3. Select a Text LLM model from the dropdown. The panel remembers your choice for the project. +4. Choose a **System prompt**, the instructions that tell the model how to rewrite your prompt +5. Click **Expand** +6. Your prompt is replaced with the expanded version, which you can edit before generating + +### System prompts + +InvokeAI ships with a **Default** system prompt and several tuned for particular model families, such as FLUX.2, Z-Image, Qwen Image and Krea-2. To add your own, click the gear button next to the System prompt selector (**Manage system prompts**) and choose **New**. A system prompt has a **Name**, the **Instructions** given to the language model, and an optional **Max output length** in tokens (300 by default). + +You can duplicate, edit and delete your own system prompts. Shared system prompts, including the built-in ones, are marked **Shared**. On a multi-user installation only administrators can edit them, and changes apply to every user. + +**Compatible models:** Any HuggingFace model with a `ForCausalLM` architecture, plus Gemma-4 checkpoints (`Gemma4ForConditionalGeneration`), which can also read an image. Recommended options: + +| Model | Size | HuggingFace ID | +|-------|------|----------------| +| Qwen2.5 1.5B Instruct | ~3 GB | `Qwen/Qwen2.5-1.5B-Instruct` | +| Phi-3 Mini Instruct | ~7.5 GB | `microsoft/Phi-3-mini-4k-instruct` | +| TinyLlama Chat | ~2 GB | `TinyLlama/TinyLlama-1.1B-Chat-v1.0` | + +Install by pasting the HuggingFace ID into the Model Manager. The model is automatically detected as a **Text LLM** type. + +### Prompt enhancement for LTX-2.5 + +LTX-2.5 was trained on long, single-paragraph audio-visual captions, and its own text encoder is not trained to write them. Lightricks pairs the model with a small dedicated enhancer, and InvokeAI offers the same pairing through Expand Prompt: + +* Install **LTX-2.5 Prompt Enhancer (Gemma-4 E2B)** from the starter models (`google/gemma-4-E2B-it`, ~10 GB). It is optional and not part of the LTX-2.5 bundle. +* Two system prompts ship with InvokeAI: **LTX-2.5 Text-to-Video** and **LTX-2.5 Image-to-Video**, the release's own texts. + +In the Video panel with an LTX-2 model selected, Expand Prompt preselects the enhancer — when it was installed from the starter models or as `google/gemma-4-E2B-it`; otherwise the popover points you to the starter models — and the matching system prompt. With a **first frame** set it shows the frame with **Describe from the first frame** ticked and uses the Image-to-Video prompt, so the caption begins from what the image shows. Untick it, or pick a model that cannot read images (the popover says so), and it uses the Text-to-Video prompt instead. Any model or system prompt you choose in the popover overrides the suggestion. + +The rewritten prompt replaces yours in the prompt box, where you can edit it before pressing Invoke; the prompt that is generated and recorded is exactly what the box holds. + +### Reasoning ("thinking") models + +Reasoning models such as Qwen3 or the DeepSeek-R1 distills normally write out a chain of thought before their answer. Prompt expansion returns the generated text as-is, so that reasoning would end up in your prompt. InvokeAI therefore renders the chat template with thinking disabled, which makes these models answer directly. Models whose chat template does not support the switch are unaffected, and models that always reason (with no way to turn it off) are not suitable for prompt expansion. + +## Image to Prompt + +Upload an image and generate a descriptive prompt from it using a vision-language model. + +**How to use:** + +1. Select an image in the Gallery or Preview, or drag an image from the Gallery onto the prompt box (**Describe this image**), which opens the tool for you +2. Click **Image to prompt** in the prompt toolbar if it is not already open +3. Select a LLaVA OneVision model from the dropdown +4. Click **Generate Prompt** +5. The generated description replaces your prompt + +**Compatible models:** LLaVA OneVision models (already supported by InvokeAI). + +## Replacing your prompt + +Both tools replace the text in the prompt box. To get an earlier prompt back, open **Prompt history** in the prompt toolbar: every prompt you have submitted with Invoke is kept there. + +## Prompt triggers + +Some models only produce their effect when the prompt contains a particular word or phrase. A LoRA trained on a character may need the character's name; a style LoRA may need a keyword such as `pixel art style`. These **trigger phrases** are easy to forget, so InvokeAI collects them, together with embeddings and wildcards, in a menu you can insert from while writing a prompt. + +### Inserting a trigger + +Click the **+** button in the prompt toolbar (**Add prompt trigger**). It is available on the positive and negative prompts in the Generate, Video and Upscale panels. The menu lists, in this order: + +| Group | What it contains | Inserted as | +|---|---|---| +| **Wildcards** | Your [wildcards](/users-guide/image-generation/prompting-guide/#wildcards) (positive prompt only). | `__name__` | +| *The selected main model's name* | The main model's trigger phrases. | the phrase | +| *Each enabled concept's name* | The trigger phrases of each LoRA that is switched on in the Guidance section. Disabled concepts are skipped. | the phrase | +| **Compatible embeddings** | Every installed embedding (textual inversion) that works with the selected main model. | `` | + +Type in **Search prompt triggers** to filter the list, then click an item. It is inserted at the cursor exactly as shown, without extra spaces or commas, so add any separators you need. + +If the menu is empty, none of your installed models have trigger phrases and no embeddings or wildcards are available. The **Open Model Manager** button takes you to where trigger phrases are set. The **+** button is disabled while you are previewing the merged prompt of a [template](/users-guide/image-generation/prompting-guide/#prompt-templates). + +Two kinds of triggers can also be completed as you type: + +* Type `<` in either prompt to list compatible **embeddings**. +* Type `__` (two underscores) in the positive prompt to list **wildcards**. + +Use the arrow keys to choose, **Enter** or **Tab** to insert, and **Escape** to close the list. Trigger phrases of models and LoRAs are only offered in the **+** menu. + +### Defining trigger phrases + +Trigger phrases belong to a model and are set in the **Model Manager**. Select a main model or a LoRA and find the **Trigger Phrases** field in its detail view. Type a phrase and press **Enter** to add it; each phrase can be up to 200 characters, and duplicates are refused. Changes are saved as you make them. + +Trigger phrases come from the model's creator, so check the model's download page (Civitai or HuggingFace) for the words it expects. Two things fill them in for you: + +* When you install a LoRA that has a companion `.json` file next to it with an `activation text` entry, as some download tools create, those phrases become the LoRA's trigger phrases. +* Trigger phrases are included when you [export and import a model's settings](/users-guide/models/introduction/#exporting-and-importing-model-settings). + +The Guidance section of the Generate panel shows each LoRA's trigger phrases under its name as a reminder. Adding a LoRA does not insert them into your prompt; use the **+** menu for that. + +## Text LLM workflow nodes + +The workflow editor provides **Text LLM** and **Text LLM (with System Prompt Preset)** nodes for automated pipelines. Both accept a prompt, model, maximum token count, and seed, then output generated text as a string. The preset variant reads its system prompt from the System Prompts library. + +Using the same seed reproduces sampling when the model, prompt, settings, hardware, and software versions remain the same. Results may differ across devices or software versions. The prompt area's **Expand Prompt** tool chooses a fresh seed for each request so repeated expansions can vary. + +API callers can pass an optional seed to **Expand Prompt**; the response returns the effective seed for replay. They can also pass an `image_name` to condition the rewrite on a stored image, which requires a Text LLM whose `supports_images` is true. + +When a saved `1.0.0` Text LLM workflow is migrated, its new seed defaults to `0`, so repeated runs become deterministic. Connect a random integer node to **seed** if the workflow should vary between runs. diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/prompting-guide.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/prompting-guide.mdx new file mode 100644 index 00000000000..1aaa8a63fba --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/prompting-guide.mdx @@ -0,0 +1,324 @@ +--- +title: Prompting Guide +description: Write prompts that work, then go further with prompt templates, dynamic prompts and wildcards. +lastUpdated: 2026-09-30 +sidebar: + order: 2 +--- +import { Image } from 'astro:assets'; +import templatesPopover from '../assets/prompt-templates-popover.webp'; +import templateMerged from '../assets/prompt-template-merged.webp'; + +import { Card, CardGrid, Steps } from '@astrojs/starlight/components'; + +The prompt is your main way of telling a model what to create. This guide starts with writing good prompts, which is all most images need. It then covers the tools that save typing and add variety: prompt templates, dynamic prompts and wildcards. [Advanced Prompt Syntax](/users-guide/image-generation/advanced-prompt-syntax/) covers the syntax for fine control. + +For the tools that write or rewrite a prompt for you, **Expand prompt** and **Image to prompt**, and for inserting trigger phrases, see [Prompt Tools](/users-guide/image-generation/prompt-tools/). + +## Writing a prompt + +Prompting works best when you describe the image clearly, then refine only the parts that matter. Most good prompts cover a few of these: + + + + Start with the main thing you want to see: a character, object, scene, or action. + + + Add the visual language: photograph, watercolor, oil painting, 3D render, anime illustration, and so on. + + + Describe the camera angle, framing, lighting, environment, color palette, or mood that will shape the image. + + + Add a few high-value details such as fabric texture, shallow depth of field, natural skin texture, or painterly brushwork. + + + +A simple pattern that works well is: + +`subject, style or medium, lighting or composition, a few important details` + +Not every prompt needs every category. Start simple, then add detail only when the model needs more direction. + +### Positive and negative prompts + +The **positive prompt** describes what you want the model to create. Put the most important idea early and keep the wording concrete. + +The **negative prompt** lists what you do not want. Use it to remove recurring problems or unwanted traits, and keep it short and targeted rather than pasting a giant list into every generation. Good negative prompts name specific failure modes: `blurry`, `distorted hands`, `low detail`, `extra limbs`. + +Not every model uses a negative prompt. See [Negative prompt](/users-guide/image-generation/generate-panel/#negative-prompt) in the Generate panel guide for which models do. + +:::tip[Negative prompts are strong] + A negative term can suppress nearby concepts too. If you negate something broad like `green` or `moss`, you may also weaken grass, foliage, or other related ideas. +::: + +### A practical workflow + + + 1. **Start with the core image.** Write the clearest version of the image you want before adding stylistic extras. + + 2. **Add style and composition.** Once the subject is right, add medium, lens, lighting, mood, background, or framing details. + + 3. **Test with a fixed seed.** When you are learning what a prompt change does, set the seed mode to **Fixed** so you can compare results directly. + + 4. **Change one thing at a time.** If you add five new terms at once, you will not know which one helped. + + 5. **Escalate only when needed.** If the result is close but one element is too weak or too strong, use [attention weighting](/users-guide/image-generation/advanced-prompt-syntax/#attention-weighting). If you want lots of variations, use [dynamic prompts](#dynamic-prompts). + + +Here is the same idea refined in stages: + +```text +portrait of a woman + +portrait of a woman, studio photograph, soft key light + +portrait of a woman, studio photograph, soft key light, 85mm lens, shallow depth of field, natural skin texture +``` + +### Write for the model you are using + +The same prompt can behave very differently across models. + +- Newer models with large language-model text encoders, such as FLUX, Qwen Image, Z-Image and Krea-2, understand full sentences and long, detailed descriptions. Describe the scene as you would to a person, including how things relate to each other and any text that should appear in the image. +- Older models such as SD 1.5 and SDXL use CLIP text encoders trained on short captions, and respond best to short, comma-separated phrases. +- Photo-oriented models respond well to camera, lens, lighting and texture language. Illustration models often respond better to medium, art direction and shape language. +- Specialty models and concepts may expect specific [trigger phrases](/users-guide/image-generation/prompt-tools/#prompt-triggers) from their model card. +- If a prompt works beautifully on one model and poorly on another, that does not always mean the prompt is bad. The model may just speak a different visual language. + +### Common mistakes + +- Packing too many unrelated ideas into one prompt. +- Using long generic quality-word lists before you know the base prompt works. +- Treating the negative prompt as a trash can for every bad outcome. +- Expecting identical behavior across models, schedulers, and workflows. +- Changing prompt, model, seed, and settings all at once while troubleshooting. + +### Example prompts + +**Photographic portrait** + +```text +editorial portrait of a woman in a charcoal coat, studio photograph, soft key light, subtle rim light, 85mm lens, shallow depth of field, natural skin texture +``` + +Negative: `blurry, low detail, waxy skin, extra fingers` + +**Environment concept art** + +```text +ancient stone temple built into a cliffside, fantasy concept art, misty sunrise, towering scale, moss-covered stairs, cinematic atmosphere +``` + +Negative: `flat lighting, low contrast, muddy details` + +**Product-style render** + +```text +sleek ceramic teapot on a matte stone surface, product photography, clean studio lighting, soft shadow, high detail, minimal background +``` + +Negative: `cluttered background, distortion, duplicate objects` + +**Stylized illustration** + +```text +fox courier crossing a rainy city street, storybook illustration, bold shapes, glowing shop signs, reflective pavement, warm and cool color contrast +``` + +Negative: `photorealistic, dull colors, low detail` + +## Prompt templates + +A prompt template is a reusable piece of prompt text, such as a photographic or artistic style, that wraps whatever you type. Apply **Photography (General)** once, and every prompt you write is sent as a photograph without you retyping the style words. Templates are available in the **Generate** panel. + +### Applying a template + +Click **Templates** in the positive prompt's toolbar. The popover lists the templates available to you in three groups: + +* **Your templates** — templates you have created. Only these can be edited or deleted. +* **Shared templates** — templates other users have shared (multi-user installations only). +* **Built-in templates** — the templates that ship with InvokeAI. + +The Prompt templates popover listing the built-in Photography templates, each with a preview image and its prompt text. + +Each row shows the template's preview image, its name and its prompt text. Type in **Search templates** to filter by name or prompt text, and click a template to apply it. + +While a template is applied, the toolbar button shows its name and a tag with the name appears under the positive prompt. To stop using the template, click the tag's close button (**Stop using this template**). + +Applying a template never changes the text in your prompt box. The template is combined with your prompt when you press **Invoke**, so you can switch templates, or remove one, without losing what you wrote. + +### How the template and your prompt combine + +A template's text usually contains the placeholder `{prompt}`, which marks where your own prompt goes. With the template + +```text +{prompt}. photography. f/2.8 macro photo, bokeh, photorealism +``` + +the prompt `a red fox in the snow` is sent as + +```text +a red fox in the snow. photography. f/2.8 macro photo, bokeh, photorealism +``` + +* Only the first `{prompt}` is replaced. Any further copies stay in the text as written. +* A template without `{prompt}` is added to the end of your prompt, after a space. +* Templates can carry negative text too. It is combined with your negative prompt by the same rules, for models that use a negative prompt. + +While a template is applied, an eye button in the prompt toolbar (**Show the merged prompt**) shows the prompt exactly as it will be sent, with the template's words distinguished from yours. The prompt boxes are read-only in this view. Click the text, or the button again (**Back to editing your prompt**), to return to editing. + +The merged prompt view: the prompt "a red fox in the snow" followed by the Photography (General) template's words, with the template's negative prompt below and the template tag under the prompt. + +### Built-in templates + +InvokeAI ships with 18 templates covering common styles: + +* **Photography** — General, Studio Lighting, Landscape, Portrait, and Black and White +* **Concept Art** — Fantasy, Sci-Fi, Character and Painterly +* **Architectural Visualization**, **Environment Art**, **Interior Design (Visualization)**, **Product Rendering** and **Vehicles** +* **Sketch**, **Line Art**, **Anime** and **Illustration** + +Built-in templates can be applied but not edited. To adapt one, create your own template from its text. + +### Creating and editing templates + +Click **New template** at the top of the popover. The editor has four fields: + +* **Name** — up to 128 characters. Templates you create are saved to your account. +* **Prompt** — the template's positive text. The **`{prompt}`** button next to the field inserts the placeholder. +* **Negative** — optional negative text, which may also contain `{prompt}`. +* **Preview image** — an optional thumbnail for the list. Pick an image from the gallery, upload a file or drag one in. + +Click **Save** to store the template. To change or remove one of your templates later, use the pencil and trash buttons on its row. Deleting a template cannot be undone. If you delete, or someone else deletes, a template you are currently using, it keeps shaping your prompt until you stop using it. + +To reuse the prompt of an image you like, right-click it in the Gallery or Preview and choose **Use as Prompt Template**. The New template editor opens with the image's positive and negative prompts filled in. Replace the part that describes the subject with `{prompt}`, name the template and save it. + +### Importing and exporting templates + +The popover's **Import** and **Export** buttons move templates in and out as files. In a multi-user installation they are available to administrators only. + +* **Import** reads a `.csv` or `.json` file. A CSV file needs the columns `name`, `prompt` and `negative_prompt`; a JSON file is a list of objects with the same keys. Every template needs a name. Imported templates become your own templates. +* **Export** downloads every user-created template as `prompt_templates.csv`, with the same three columns. Built-in templates are not included. + +Preview images are not part of either file. + +On a multi-user installation, templates you create are private to your account. Templates that other users have shared appear under **Shared templates**, where you can apply them but not change them. + +### Templates and image metadata + +An image's metadata records the prompt that was actually used, with the template already combined into it. It does not record which template was applied. When you recall the prompts from an image, you get that combined text, and any template you currently have applied is removed so that the style is not applied twice. + +## Dynamic prompts + +Dynamic prompts expand a single prompt into many variations. Write the parts you want to vary as a list of alternatives, and one press of **Invoke** generates an image for each variation. They are useful for brainstorming, exploring styles and subjects, and testing a model without rewriting the same prompt by hand. + +Dynamic prompts work in the **Generate** panel and on the Canvas. Only the positive prompt is expanded; the negative prompt is used as written for every variation. + +### Basic syntax + +Put alternatives inside braces and separate them with `|`: + +```text +a {red|green|blue} balloon +``` + +This expands into three prompts: `a red balloon`, `a green balloon` and `a blue balloon`. Several groups in one prompt multiply: + +```text +a {red|green} {balloon|kite} +``` + +gives `a red balloon`, `a red kite`, `a green balloon` and `a green kite`. Groups can also be nested: `a {cat|{small|large} dog}` gives `a cat`, `a small dog` and `a large dog`. More syntax is described under [Advanced dynamic prompt syntax](/users-guide/image-generation/advanced-prompt-syntax/#advanced-dynamic-prompt-syntax). + +### Previewing and generating + +Click **Dynamic** in the prompt toolbar to open the Dynamic Prompts popover. When the prompt contains dynamic syntax, the button shows how many prompts it expands into. + +The **Preview** tab lists the expanded prompts, numbered and highlighted, and shows the total as *prompts × iterations → generations*. The Invoke button's tooltip shows the same count. Only the first 200 prompts are listed; the rest are summarized as *and N more*. + +Click a prompt in the list (**Use this prompt**) to replace your prompt with that one variation, for example to refine it on its own. If a template is applied, it is folded into the chosen prompt and the template itself is removed, since its words are now part of your prompt. + +If the prompt cannot be expanded, for example because a brace is not closed or a wildcard has no values, the Preview tab shows the error and Invoke is blocked until you fix it. + + + +1. **Write the prompt** with the parts you want to vary in braces. + +2. **Open Dynamic** and check the Preview list. Make sure the combinations make sense before you generate a large batch. + +3. **Set the mode and the number of prompts** (see below). + +4. **Press Invoke.** Each expanded prompt is generated once per iteration, so 6 prompts with **Iterations** set to 2 give 12 images. + + + +### All combinations or a random sample + +The **Mode** menu in the Preview tab sets how prompts are chosen: + + + + Lists every combination in order, up to **Max prompts**. This is the default, and the best choice when you want to compare every option. + + + Draws **Number of prompts** variations at random, honoring any weights. The same prompt can be drawn more than once. Click **Shuffle the sample** to draw a new sample. + + + +The number of prompts can be set from 1 to 10,000 and is 100 by default. Combinations grow quickly, so this limit keeps an innocent-looking prompt from queuing thousands of images: `a {red|green|blue} balloon in {morning mist|golden hour|rain}` already has nine combinations. + +:::tip[Start small] + Preview a handful of variations first. Once they look useful, raise the limit or the number of iterations for a larger batch. +::: + +### Seeds + +By default, every variation within one iteration shares the same seed. Because only the prompt changes between the images, this makes it easy to see what each option does. + +Turn on **New seed for every image** to give each image its own seed instead, for the widest variety. When the panel's **Seed mode** is **Fixed**, every image uses the same seed and this switch is unavailable. + +### Dynamic prompts and other features + +* **Prompt templates.** Expansion runs on your prompt after the [template](#prompt-templates) has been applied, so a template can contain dynamic syntax too. +* **Image metadata.** Each image records the expanded prompt that produced it and its own seed. The original dynamic prompt and the dynamic prompt settings are not recorded. +* **Workflows.** The workflow editor has a **Dynamic Prompt** node, and batch generators for random and combinatorial expansion. + +## Wildcards + +A wildcard is a named list of values that you can use in any prompt. Write the name between double underscores, and it behaves like a [dynamic prompt](#dynamic-prompts) group of all its values: + +```text +a __colors__ balloon +``` + +With a `colors` wildcard containing `red`, `green` and `blue`, this is the same as `a {red|green|blue} balloon`. Wildcards save retyping long lists and keep them consistent between prompts. + +Values can contain dynamic syntax, including other wildcards. Names can be nested with `/`, such as `animals/dogs`, and `__animals/*__` combines every wildcard under `animals`. + +### Managing wildcards + +Wildcards are managed on the **Wildcards** tab of the **Dynamic** popover. + +* **New wildcard** opens the editor. Give the wildcard a **Name** (letters, numbers, underscores and hyphens, up to 128 characters) and its **Values**, one per line. Blank lines are ignored, and `#` starts a comment. +* Click a wildcard to insert it at the cursor (**Insert into the prompt**). You can also type `__` in the prompt, or use the **+** [prompt trigger](/users-guide/image-generation/prompt-tools/#prompt-triggers) menu. +* Use the pencil and trash buttons on a wildcard to edit or delete it. +* **Search wildcards** finds wildcards by name or value. + +Wildcards belong to your account. On a multi-user installation, each user has their own set. + +### Importing and exporting wildcards + +**Import** reads wildcards from files you already have, such as collections downloaded for other tools: + +* **From files…** imports one or more files. A `.txt` file (or a file without an extension) becomes one wildcard named after the file, with one value per line. +* **From a folder…** imports every wildcard file in a folder. Subfolders become part of the name, so `animals/dogs.txt` becomes `animals/dogs`. +* A `.yaml`, `.yml` or `.json` file can hold many wildcards. Nested keys become the name, joined with `/`, and each list becomes a wildcard's values. + +If some wildcards already exist in your library, or cannot be imported, the **Import wildcards** dialog lists them. For each existing name you can **Skip** it, **Replace** it, or **Keep both**, which imports the new one under a numbered name. + +**Export** downloads all your wildcards as `wildcards.yaml` (**As YAML**) or `wildcards.json` (**As JSON**), in the same nested format. + +## Advanced syntax + +For finer control, [Advanced Prompt Syntax](/users-guide/image-generation/advanced-prompt-syntax/) covers [attention weighting](/users-guide/image-generation/advanced-prompt-syntax/#attention-weighting) — making parts of a prompt count for more or less, and blending prompts, on SD 1.5, SD 2.x and SDXL — and the [advanced dynamic prompt syntax](/users-guide/image-generation/advanced-prompt-syntax/#advanced-dynamic-prompt-syntax) for choosing several options, weighting options, variables and comments. diff --git a/docs/src/content/docs/Users Guide/04.Image Generation/upscale.mdx b/docs/src/content/docs/Users Guide/04.Image Generation/upscale.mdx new file mode 100644 index 00000000000..3179292b737 --- /dev/null +++ b/docs/src/content/docs/Users Guide/04.Image Generation/upscale.mdx @@ -0,0 +1,46 @@ +--- +title: Upscaling +lastUpdated: 2026-09-30 +sidebar: + order: 8 +--- + +The **Upscale** panel enlarges an image and adds detail in two passes. First a Spandrel upscaling model (such as a Real-ESRGAN variant) enlarges the image. Then a main model runs a short image-to-image pass over the result to invent detail that fits your prompt. + +Open it from the left rail. It is available in the Compose, Edit and Video layouts. + +## Setting up an upscale + +The panel is split into sections, top to bottom: + +- **Source & treatment** + - **Source image**: pick an image from the gallery, drag one in, or upload one. + - **Spandrel model**: the first-pass upscaler. Choose one suited to your image: photography, illustration or linework. + - **Scale**: the output size is the source size multiplied by this value. The panel shows the resulting input and output size and warns when the output is large enough to need much more time and VRAM. + - **Treatment preset**, or **Creativity** and **Structure** directly. Higher Creativity invents more detail and gives the prompt more influence. Higher Structure keeps the source composition more strictly; it is only shown for models that use a Tile ControlNet. +- **Detail guidance**: the prompt. It is the project prompt shared with Generate. For upscaling, describe the medium, texture and style rather than new content. +- **Generation**: the **Main model** for the detail pass (**SD1.5**, **SDXL** or **FLUX.1**), steps, CFG scale, scheduler, seed, and LoRAs that match the main model. +- **Advanced**: the components the main model needs (see below), tile size and tile overlap, VAE and VAE precision, and CLIP skip for SD1.5. + +## What each model family needs + +| | SD1.5 / SDXL | FLUX.1 | +|---|---|---| +| Tile ControlNet | Required: a Tile or Union ControlNet for the same family, under **Advanced** | Not used; the Structure slider is hidden | +| Negative prompt | Available | Not used; FLUX.1 ignores it, so it is hidden and not recorded | +| Other components | Taken from the main model | Every FLUX.1 model except an SDNQ pipeline needs a **T5 Encoder**, **CLIP Embed** and **VAE**, all under **Advanced**. SDNQ pipelines bring their own. FLUX Fill models cannot be used | +| Detail pass | Tile by tile | One pass over the whole image | + +The panel says what is missing before you can queue, for example "Select a compatible Tile or Union ControlNet." or "This model needs a T5 encoder selected." + +:::note[FLUX.1 output size] +FLUX.1 needs sizes that are a multiple of 16. When the enlarged image is not, the panel shrinks it slightly to the nearest fitting size before the detail pass — for example 2040 px becomes 2032 px. +::: + +## Memory + +For SD1.5 and SDXL the detail pass works in tiles, so the output can be much larger than what the model generates in one go. Larger tiles can improve continuity but need more VRAM; more tile overlap reduces seams but costs time and memory. + +For FLUX.1 the detail pass runs over the whole image at once, so its memory use grows with the output size. There, **Tile size** only sets the tiles for the VAE encode and decode, and **Tile overlap** has no effect. + +For all model families, encoding and decoding the enlarged image with the VAE runs in tiles. See [Low-VRAM mode](/configuration/optimization/low-vram-mode/) if you still run out of memory. diff --git a/docs/src/content/docs/Users Guide/05.Video Generation/introduction.mdx b/docs/src/content/docs/Users Guide/05.Video Generation/introduction.mdx new file mode 100644 index 00000000000..31c4889c384 --- /dev/null +++ b/docs/src/content/docs/Users Guide/05.Video Generation/introduction.mdx @@ -0,0 +1,285 @@ +--- +title: Generating and modifying short videos +description: Generate short videos — with audio on MiniMax H3 and LTX-2 — from the Video panel. Covers the panel's controls, the Wan 2.2, MiniMax H3 and LTX-2 model families, audio-to-video and video-to-audio, and how to make longer videos. +lastUpdated: 2026-09-25 +sidebar: + order: 0 +--- + +import { Image } from 'astro:assets'; +import videoOverview from '../assets/video-panel-overview.png'; +import videoStartEnd from '../assets/video-panel-start-end-images.png'; +import videoRender from '../assets/video-panel-model-section.png'; +import { LinkCard, CardGrid, Steps } from '@astrojs/starlight/components'; + +InvokeAI generates short MP4 clips from a text prompt, one or two images, an existing video, a soundtrack — or combinations of those — using three local model families: + +* **[Wan 2.2](../wan-22)** — the Alibaba Wan-AI family: three transformer variants covering text-to-video, image-to-video, first-to-last-frame interpolation, and video extension, with a 4-step **Lightning** fast path. +* **[MiniMax H3](../minimax-h3)** — two task transformers, **FL2VA** (first/last-frame-to-video-plus-audio) and **Ref2VA** (reference-to-video-plus-audio), that between them cover every conditioning mode *and* generate a synchronized stereo soundtrack, with a **Turbo** fast path. A [hybrid](/users-guide/video-generation/minimax-h3/#the-hybrid-fl2va-quality-with-ref2va-conditioning) runs the two together. +* **[LTX-2](../ltx-2)** — Lightricks' 22B dual-stream transformer, which generates picture and stereo soundtrack together. It covers text-to-video, first frame, last frame, first-to-last interpolation and video extension, and — because it models both streams — can also generate a **picture for an existing soundtrack** (audio-to-video) or a **soundtrack for an existing picture** (video-to-audio). It ships in a **Dev** build you steer with guidance and a **Distilled** build that renders in eight fixed steps, and Dev has an optional eight-step **Distilled** LoRA fast path. + +The easiest way to use any of them is the **Video panel** described on this page — no node graph required. The [bundled video workflows](/users-guide/video-generation/video-workflows/) expose the same pipelines in the workflow editor for graph-level control. + +:::caution[Experimental] +Video generation is a prototype feature. Panel behavior, workflows, and starter-model packaging may change between releases. The underlying models are also new — expect rough edges in coherence and artifacts at longer durations. +::: + +--- + +## The Video panel + +Open the **Video** entry in the editor's left rail (next to Generate and Upscale). The panel is a complete video pipeline: type a prompt, pick a model, press **Invoke**, and the finished MP4 appears in the gallery. + +The Video panel with a Wan 2.2 image-to-video model selected, a prompt, first and last frame images, and the Initial Video and Dimensions sections. + +### Quick start + + + +1. **Install a video model.** Open the **Model Manager** and install a starter bundle: **Wan 2.2 Text-to-Video**, **Wan 2.2 Image-to-Video**, **MiniMax H3**, or **LTX-2.5**. See [the models](#the-models) below for what each contains and what fits your hardware. + +2. **Open the Video panel** and pick your model in the **Model** section. The panel reshapes itself around the selection — sections and fields appear, disappear, and re-range to match what the model can actually do. + +3. **Type a prompt** describing the scene and, importantly, the *motion* you want across the clip. + +4. **Optionally add conditioning media** — a first frame, a last frame, an initial video to extend, or, on LTX-2, a conditioning clip whose soundtrack or picture the generation completes (see below). + +5. **Press Invoke.** The MP4 lands in the gallery and plays in the viewer. On MiniMax H3 and LTX-2 it arrives with a generated audio track. + + + +### The panel adapts to the model + +There is no mode switch. The panel infers what to generate from **which inputs you have filled in**, and each model family supports a different set of combinations: + +| Inputs you set | What runs | Wan T2V‑A14B | Wan I2V‑A14B | Wan TI2V‑5B | MiniMax H3 | LTX-2 | +|---|---|---|---|---|---|---| +| Prompt only | Text to video | ✓ | — | ✓ | ✓ | ✓ | +| First Frame | Video starts on your image | — | ✓ | ✓ | ✓ | ✓ | +| Last Frame only | Video ends on your image | — | — | — | ✓ | ✓ | +| First + Last Frame | Interpolates between the two | — | ✓ | — | ✓ | ✓ | +| Initial Video | Extends the clip | — | ✓ | ✓ | ✓ | ✓ | +| Initial Video + Last Frame | Extends toward a destination image | — | ✓ | — | ✓ | ✓ | +| Conditioning Clip, *its soundtrack* | Generates a picture for the audio (audio-to-video) | — | — | — | — | ✓ | +| Conditioning Clip, *its picture* | Generates a soundtrack for the video (video-to-audio) | — | — | — | — | ✓ | + +MiniMax H3's **Ref2VA** transformer has a mode of its own — reference-conditioned generation — covered [with the model](/users-guide/video-generation/minimax-h3/). + +If you pick a model that can't run the current combination, the Invoke button tells you exactly why instead of failing mid-run. + +### Prompt + +The prompt toolbar carries most of the helpers from the image Generate panel — prompt triggers, prompt expansion, image-to-prompt, and history (prompt templates are only available in Generate). On LTX-2, **Expand Prompt** starts from the enhancer LTX-2.5 was released with and, when a first frame is set, describes the video from that frame — see [prompt enhancement for LTX-2.5](/users-guide/image-generation/prompt-tools/#prompt-enhancement-for-ltx-25). The **negative prompt** behaves per family: + +* **Wan** — used only when CFG is greater than 1 (the field says so). With the Lightning fast path on (CFG 1), it is ignored by the graph. +* **MiniMax H3** — hidden entirely. H3 is guidance-distilled and has no CFG, so a negative prompt has nothing to act on. +* **LTX-2** — offered on the Dev build, where it is pre-filled with the release's own default list of artifact terms, and hidden on the Distilled build, which runs no guidance. On Dev it is also hidden while the [Distilled fast path](/users-guide/video-generation/ltx-2/#the-distilled-lora-fast-path-on-dev) is on, since that run uses no guidance; turn the toggle off to get it back. With CFG and Audio CFG both at 1 it is dropped from the graph, along with the 12B prompt encode it would have cost. + +### Start & End Images + +Two drop targets: **First Frame** and **Last Frame**. Drag an image from the gallery (thumbnail or the preview's frame), or upload one. + +* A **first frame** anchors the video's opening image (image-to-video). +* A **last frame** anchors where it ends. Combined with a first frame, the model interpolates between the two. On MiniMax H3 and LTX-2 a last frame also works **alone** — the video builds toward your image. +* Setting a first frame **disables the Initial Video section**, and vice-versa: a clip can start from a still or continue a video, not both. The disabled section says which input to clear. + +The Start & End Images section with a first frame and a last frame set, each with Remove image. + +### Initial Video + +Drop or upload an existing video to **extend** it. The section shows the clip with a live preview, plus: + +* **Start Frame / End Frame sliders** to trim the kept range — small thumbnails show the exact frames at both bounds while you drag. +* The new segment continues from the trimmed clip's end frame and is joined on with a smooth cross-fade. +* Add a **Last Frame** image alongside the initial video to steer where the continuation lands (not available on TI2V‑5B). +* On Wan and LTX-2, the extension **inherits the source clip's frame rate** and the FPS field locks accordingly. MiniMax H3 always generates at its fixed 24 fps. +* On LTX-2 a **Context Frames** control appears under the clip: how many of the source's closing frames — picture *and* sound — the continuation is given, and how long the cross-fade is. Its help text shows how much new material the run will add. See [extending with LTX-2](/users-guide/video-generation/ltx-2/#extending-with-ltx-2). + +Run extend repeatedly — each output can be the next run's initial video — to grow a clip well past a single generation's length. See [making longer videos](#making-longer-videos). + +![The Initial Video section with a clip loaded: its thumbnail and name, Remove video, and the Trim controls with Start Frame and End Frame thumbnails and values.](../assets/video-panel-initial-video.png) + +### Conditioning Clip (LTX-2 only) + +LTX-2 offers the ability to add a soundtrack to a video, or add a +video to a soundtrack. Please see [LTX-2](../ltx-2#conditioning-clip) +for more details. + +### Dimensions + +* **Aspect Ratio** presets (21:9 through 9:21) drive text-to-video output. As soon as conditioning media is set, **its aspect ratio takes over** and the preset locks — the readout names the source ("from the first frame"). +* **Target Resolution** picks the size tier within that ratio: + * Wan — **480p** and **720p** (native), **1080p** (extrapolated beyond the training distribution; expect softer results). + * MiniMax H3 — **768 highres** (native: short edge 768) and **768 lowres** (fast preview: long edge 768). + * LTX-2 — **512p**, **704p** (default) and **768p** in one pass; **1024p** and **1536p** are [two-stage](/users-guide/video-generation/ltx-2/#two-stage-presets), and much slower. + The panel derives exact pixel dimensions automatically, snapped to each model's grid — you never do the multiple-of-16/32 math yourself. +* **Frames** sets clip length: + * Wan — any 4·n+1 count from 5 to 161; default **81** (5 seconds at the default 16 fps). The training distribution is 81; longer values degrade coherence. + * MiniMax H3 — a snapped slider over the model's fixed grid (90 to 345 in steps of 17); default **124** (about 5 seconds at 24 fps). + * LTX-2 — any 8·n+1 count; the slider covers 9 to 241 and the field accepts up to 481. Default **121** (5 seconds at 24 fps). A conditioning clip sets it for you. +* **FPS** — editable on Wan (default 16) and LTX-2 (default 24, range 1–60), locked on both while extending (see above); fixed at 24 on MiniMax H3. On LTX-2 the frame rate also sets how much audio is generated, and a conditioning clip in the picture role supplies its own. + +### Fast rendering modes + +Each of the models has a fast mode that significantly reduces the +number of steps needed to generate a final video, at the cost of loss +of diversity. We use the names that the model authors prefer, so fast +mode is variously called "Lightning," "Turbo," and "Distilled." + +* **Wan 2.2** A _Lightning (fast)_ toggle appears in the Render + section of the Video Panel when any of the A14B models are + chosen. Activating this toggle does several things. It loads two + bundled "lightning" LoRAs (one each for the high and low noise + render phases), it reduces the default steps from 40 to 4, and it + sets CFG to 1. You may, if you wish, swap out the lightning LoRAs + for other accelerated LoRAs. + +* **MiniMax H3** A _Turbo (fast)_ toggle appears in the Render + section. Activating this loads a bundled "Turbo" LoRA and reduces + the number of steps to 8. There are two different LoRAs: one for + H3 FL2VA and one for H3 Ref2VA. + +* **LTX-2** We bundle a distilled LTX-2.5 transformer model with the + starter models. Select this model will reduce the step + requirements to 8. Alternatively, you can select the Dev + transformer and activate fast mode using a toggle labeled + _Distilled (fast)_ in the Render section of the panel. This will + load an LTX-2 distilled LoRA and reduce steps to 8. The former is + fast but less flexible. The latter produces higher quality videos + and gives you the flexibility to adjust the distillation strength + or fine-tune the model. Note that the distilled LoRA is not + installed with the LTX-2 bundle, but can be found among Starter + Models by searching for "LTX-2.5 Distilled LoRA." + +The Render section of the Video panel for Wan 2.2: the Lightning (fast) switch, Steps, CFG, CFG (Low Noise) and Seed. + +### Concepts (LoRAs) + +Add your own style/subject LoRAs, just like the image panel — on all three families, including LTX-2. Compatibility is enforced per family — an A14B LoRA can't run on TI2V‑5B (the tensor shapes differ), and the panel says so rather than silently dropping it. + +### Model Components + +What appears here depends on the model: + +* **Wan single-file (GGUF/checkpoint) mains** need their supporting pieces: either a **Component Source** (any Diffusers Wan install, which supplies the text encoder and VAE) or a standalone **VAE** and **Wan T5 Encoder**. A14B mains also get a **Transformer (Low Noise)** slot for the second expert — without it, the high-noise expert runs the whole schedule. +* **Wan Diffusers mains** bundle everything; the section stays collapsed. +* **LTX-2** transformer mains always need two components: **Model components**, an LTX-2 components install supplying the VAEs, vocoder, text connectors and latent upscaler, and a **Gemma-4 encoder** — no LTX-2 model carries its own text encoder. +* **MiniMax H3** single-file transformer mains need **Model components** — a Diffusers H3 install that supplies the tokenizer, processor, and VAEs — plus a single-file **Text encoder** when that install carries no text-encoder weights (the starter bundle's does not). A **Ref2VA** main also offers the optional **Hybrid quality base (FL2VA)** slot and, once a base is picked, a **Ref2VA AdaLN from block** slider — see [the hybrid](/users-guide/video-generation/minimax-h3/#the-hybrid-fl2va-quality-with-ref2va-conditioning). + +If a Wan A14B pairing looks mis-wired — for example a low-noise-tagged file in the main slot — the panel shows a **non-blocking advisory** beneath the slots with a one-click **Swap experts** action. The tags come from a filename heuristic and your explicit wiring always wins, so deliberate cross-wiring still runs (the backend logs the same warning at generation time). + +![The Model Components section with a high-noise expert in the Transformer (Low Noise) slot, and the advisory "The filename tags suggest these two experts are swapped" with a Swap experts button.](../assets/video-panel-expert-advisory.png) + +### Recalling parameters + +Video generations record their parameters as metadata. Select a video in the gallery and use **Recall parameters** (in the viewer's header or the context menu) to load its prompt, model, dimensions, frames, LoRAs, and conditioning back into the Video panel — the same round-trip the image panel offers. Media that no longer exists is cleared rather than silently substituted. The MiniMax H3 hybrid is part of the record too: recalling a hybrid video restores its base and start block, and recalling a video made without the hybrid clears the slot. LTX-2 records carry their guidance scales, extend context length and conditioning clip with its role. + +The record travels **inside the MP4**, the way an image's does inside its PNG. Download a video, upload it to any Invoke install — yours or someone else's — and **Recall parameters** works there too: models are matched by content hash when the other install's model keys differ, and conditioning media that only existed in the original gallery is simply left empty. The [Media Metadata](/development/architecture/media-metadata/) reference documents the record's fields and versioning. + +--- + +## Making longer videos + +Every family is trained on short clips — Wan on 81 frames (~5 s), H3 on up to 345 (~14 s), LTX-2 on 121 (~5 s at 24 fps). Asking for more frames in one generation pushes the temporal positional encoding out of distribution and coherence degrades. The way to go longer is to **extend**: generate a clip, then continue it, repeatedly. + +Extension is available on all three families. LTX-2's continues the soundtrack as well as the picture; see [extending with LTX-2](/users-guide/video-generation/ltx-2/#extending-with-ltx-2) for its Context Frames control. + +### From the Video panel + + + +1. **Generate your opening clip** (any mode). + +2. **Drag it from the gallery into the Initial Video field.** Trim off any weak tail with the End Frame slider — the last frames of a generation are occasionally its worst. + +3. Optionally set a **Last Frame** image to steer where the continuation lands. + +4. **Invoke.** The panel renders the continuation and joins it to your source with a cross-fade, delivering one combined MP4. On MiniMax H3 and LTX-2 the soundtrack is carried across the join too. + +5. **Repeat** with the new output as the next initial video. + + + +### Quality drift across iterations + +Each iteration's source is itself a generation output, so artifacts compound — by the 4th or 5th extension you may see softening or color drift. Mitigations: + +1. **Trim the bridge point a few frames back from the end** — boundary frames are the most artifact-prone. +2. **Refresh a bridge frame with a low-strength img2img pass** (SDXL or FLUX at ~0.2 strength) before using it as a first frame for the next segment. +3. **Don't chain more than 4–5 segments** without refreshing in between. + +Also, be aware that the extended video only has access to information present in the bridge frames (the end of the previous video — a single frame on Wan, a few on LTX-2). So if a character in the first video has their face obscured at that point, the character's face may be completely different in the extended part of the video. + +### In the workflow editor + +The **Extend Video** workflows (Wan, MiniMax H3 and LTX-2) expose the same machinery as graphs, including the `Concatenate Videos` node's transition modes (`cut`, `crossfade`, `fade_through_black`) and `Frame from Video` for manual frame-bridging chains. See the [Video Workflows guide](/users-guide/video-generation/video-workflows/). + +--- + +## The Models + +More information on the capabilities and limitations of each model can be found in the detail pages below. + + + + + + + + +--- + +## Troubleshooting + +### OOM errors + +Video denoise is memory-intensive — attention scales roughly as `(T_lat × H/16 × W/16)²`, so resolution and frame count both quadratically affect peak VRAM. + +For Wan, add `wan_memory_optimization: true` to `invokeai.yaml` and restart to target about 2 GiB of resident transformer weights when `enable_partial_loading` is enabled, lower denoise activation memory, and stream untiled VAE decode directly to MP4. This can make generation substantially slower and requires enough system RAM for offloaded weights. + +* **Drop resolution before frame count.** 1280×720 → 832×480 is a ~2.4× memory drop; on H3, switch Target Resolution to **768 lowres**. +* **TI2V-5B before A14B.** TI2V-5B Q4_K_M peaks around ~6–8 GB at 832×480, versus ~12–14 GB for A14B Q4_K_M. +* **OOM at the *reference image encoder* step** is usually allocator fragmentation from a previous run. Restart the server and try again; if it recurs reproducibly, file an issue. + +### Late-frame artifacts (text, color blobs) + +If a video looks great for most of its duration but the last ~20% develops text, watermarks, or floating colored shapes, that's the model's training-data prior leaking through as temporal coherence weakens. It's most common on TI2V-5B. + +* On Wan (with CFG > 1), add to the negative prompt: `text, watermark, logo, subtitles, chinese characters, kanji, ticker, banner` +* Describe the *action* you want through the clip, not just the static scene +* Stay at the default frame count; longer pushes temporal RoPE out of distribution + +### The Invoke button is disabled + +The panel validates before enqueueing, and the button's tooltip lists every reason: an unsupported input combination for the selected model, a missing required component (Wan single-file mains need a VAE and encoder or a component source), a LoRA targeting the wrong Wan family, conditioning media whose aspect ratio H3 can't reach (outside 1:4–4:1), an LTX-2 conditioning clip paired with a two-stage resolution, or an LTX-2 extension whose Context Frames is at least Frames, exceeds the trimmed source's length, or is too large for the source's resolution. Fix what it names and the button re-arms. + +### TI2V-5B VAE state-dict load error + +If decode fails with `size mismatch for ... AutoencoderKLWan`, the **wrong VAE** is wired for the transformer: TI2V-5B needs the 48-channel Wan 2.2 VAE, the A14B variants the 16-channel Wan 2.1 VAE. In the panel, pick the matching VAE in Model Components (the selector already filters to compatible channel counts when the model reports them). + +### Preview images don't appear + +1. **A video is already loaded in the viewer** — the progress preview overlays it. If missing entirely, hard-refresh the browser (`Ctrl+Shift+R` / `Cmd+Shift+R`). +2. **`Show progress in viewer` is disabled** — check the gallery settings (gear icon). + +### Pipeline runs but the final MP4 is glitchy + +Almost always a **VAE mismatch** (16-ch vs 48-ch on Wan) or a scheduler override. Use the auto-selected scheduler and the family-matched VAE. + +--- + +## Acknowledgements + +Wan 2.2 model family by the Alibaba Wan-AI team; Lightning distillation LoRAs by [lightx2v](https://huggingface.co/lightx2v/Wan2.2-Lightning); GGUF quantizations by [QuantStack](https://huggingface.co/QuantStack). MiniMax H3 by [MiniMax AI](https://huggingface.co/MiniMaxAI/MiniMax-H3); int8 single-file repacks by [Comfy-Org](https://huggingface.co/Comfy-Org/MiniMax-H3); Turbo distillation LoRAs by [larryvrh](https://huggingface.co/larryvrh/MiniMax-H3-Turbo-Lora) and [LightX2V](https://huggingface.co/lightx2v/Minimax-h3-Turbo). The FL2VA/Ref2VA hybrid follows the per-tensor analysis and reference implementation in [scottmudge/ComfyUI_MinimaxH3HybridLoader](https://github.com/scottmudge/ComfyUI_MinimaxH3HybridLoader). LTX-2.5 by [Lightricks](https://huggingface.co/Lightricks/LTX-2.5); per-component int8 repacks and distilled LoRA by [DeepBeepMeep](https://huggingface.co/DeepBeepMeep/LTX-2). diff --git a/docs/src/content/docs/Users Guide/05.Video Generation/ltx-2.mdx b/docs/src/content/docs/Users Guide/05.Video Generation/ltx-2.mdx new file mode 100644 index 00000000000..e6052884e3a --- /dev/null +++ b/docs/src/content/docs/Users Guide/05.Video Generation/ltx-2.mdx @@ -0,0 +1,144 @@ +--- +title: LTX-2 +lastUpdated: 2026-09-29 +alsoListedIn: + - users-guide/models/local-models +sidebar: + order: 3 +--- + +LTX-2 is Lightricks' 22B **dual-stream** transformer: picture and soundtrack are generated *together*, by the same model, at every denoising step — not dubbed on afterwards. It ships in two builds that share every other component: + +* **Dev** — the guided build. It samples a 30-step schedule and exposes four guidance controls (see below). +* **Distilled** — guidance-distilled to a fixed **eight-step** schedule. It runs one model evaluation per step where Dev runs up to four, which makes it roughly fifteen times cheaper per clip. Guidance has no effect on it, so the panel hides those controls and shows the step count as fixed. + +Both builds support every LTX-2 mode — text, first frame, last frame, first-to-last, extend, audio-to-video and video-to-audio — and your own LTX-2 LoRAs. + +What to know in practice: + +* **Frames are 8·n + 1** (the video VAE encodes the first frame alone, then groups of eight): 9 to 481, default 121 — five seconds at the default 24 fps, up to twenty. Time and memory grow roughly linearly with the frame count, so a 481-frame clip costs about four times a default one. +* **Frame rate is yours to set** (1–60 fps). It changes the clip's duration, and with it how much audio is generated — the two streams share one clock. +* **Canvas presets pin the short edge**, with the long edge following your aspect ratio: 512p, 704p (default) and 768p render in one pass and snap to a multiple of 32; 1024p and 1536p are [two-stage](#two-stage-presets) and snap to 64, because the first pass runs at half of them. +* **Image-to-video re-compresses your frame** as a single H.264 frame before encoding it. That is deliberate: the model was trained on frames that had been through a video codec, and a pristine PNG is far enough out of that distribution that the clip visibly drifts away from it over the first second. +* **The negative prompt** only reaches the model through classifier-free guidance, so it is available on Dev and hidden on Distilled. It is pre-filled with the release's own default, a long list of artifact and audio-defect terms. + +#### Advanced guidance (Dev only) + +Beyond the usual **CFG**, LTX-2 exposes three more terms in a collapsed **Advanced guidance** section. Each one costs an extra model evaluation per step, so turning one off is a real saving: + +* **Audio CFG** (default 7) — how hard the soundtrack follows the prompt. The release guides audio far harder than picture. +* **STG** (default 1) — spatio-temporal guidance. Steers away from a pass with one attention block skipped, which sharpens motion. 0 turns it off. +* **Modality guidance** (default 3) — steers away from a pass with the audio/video cross-attention disabled, tightening how well the two streams agree. 1 turns it off. + +#### Two-stage presets + +The **1024p** and **1536p** target resolutions are *two-stage*. LTX-2 generates at half the canvas, doubles the latent with a dedicated x2 upscaler, then runs a second, shorter denoise over it. The upscaler alone produces a soft latent; the refine pass is what resolves it, so the output is larger without going soft. The soundtrack skips the upscaler, having no spatial extent, but is carried through the second pass so the two streams stay on one trajectory. + +The refine pass is the expensive half: it runs at four times the base pass's token count. Dev evaluates the model four times per step, so a Dev refine step costs four times a Distilled one — which is why the Dev refine runs a smaller step budget than its base pass rather than the same one. Expect minutes for Distilled at 1024p and hours for Dev at 1536p. + +A first frame is anchored on both passes, encoded once at each canvas. The refine pass re-noises every frame including the first, so an anchor that was only applied to the base pass would not survive into the output. The same holds for the opening of an extension. A [conditioning clip](#conditioning-clip), which holds a whole stream fixed, cannot run two-stage at all. + +#### The Distilled LoRA (fast path on Dev) + +The **LTX-2.5 Distilled LoRA** turns the Dev transformer into an eight-step, guidance-free model like the Distilled build. With it installed, the Dev model's **Distilled** toggle defaults on: it adds the LoRA, sets eight steps and turns every guidance scale off, so each step is one model evaluation instead of four. Turn it off to return to the guided 30-step recipe. + +Why use it rather than the Distilled transformer? It keeps a single ~19 GB transformer on disk that can run either way. The LoRA is large for a LoRA (~8.9 GB) because distillation changes the whole network. The toggle is only offered on Dev: the Distilled build already is that model. + +#### Last frames and keyframes + +A **Last Frame** works alone or with a first frame, as on MiniMax H3. Unlike a first frame, which directly replaces the clip's opening, a last frame is added to the model's input as an extra *keyframe* positioned at the clip's final frame, and the generated frames are steered toward it. A last frame can also be combined with an **Initial Video** to steer where an extension lands. + +#### Conditioning Clip + +With an LTX-2 model selected, the panel offers a **Conditioning Clip** slot. Drop a clip and choose, under **Use from this clip**, which half of it the model is given — it generates the other half: + +* **Its soundtrack — generate the picture** (audio-to-video). The clip's audio is held fixed and LTX-2 generates video to match it, down to lip movement. The output carries the **original recording**, not a re-synthesis of it. +* **Its picture — generate the soundtrack** (video-to-audio). The clip's frames are held fixed and LTX-2 generates a soundtrack for them. + +The clip decides the run's length: in the soundtrack role the Frames control locks to however many frames the audio covers at the panel's FPS; in the picture role both Frames and FPS come from the clip (FPS stays the panel's if the gallery doesn't know the clip's frame rate). Counts snap *down* to LTX-2's 8·n + 1 grid, so up to seven trailing frames of a clip are dropped. Trim the clip beforehand if you only want part of it. + +A few rules follow from how it works: + +* The clip excludes every other conditioning input — clear the first frame, last frame and initial video to use one. +* Only the single-pass resolutions (512p, 704p, 768p) can run; with 1024p or 1536p selected, the Invoke button says to pick another. A [two-stage](#two-stage-presets) refine pass re-noises everything, which would undo the held stream. +* In the picture role, the output carries **your original footage** at its own resolution with the new soundtrack. The model itself sees the clip resized to the generation canvas, whose aspect ratio the panel takes from the clip, so the soundtrack is generated for the whole frame. +* To give LTX-2 a bare soundtrack — a song, a voice recording — upload the audio file to the gallery. It becomes a clip whose frames draw the waveform, and dropping it here selects the soundtrack role automatically. + +Describe the scene you want in the prompt either way; for audio-to-video, saying who or what is making the sound ("a woman singing to the camera") is what lets the model tie picture to audio. + +#### Prompt Enhancement + +InvokeAI ships with an optional LTX-2 prompt enhancement model +designed specifically to produce improved results with this video +model. It is not bundled, but can be installed from the starter models +by searching for "LTX-2.5 Prompt Enhancer (Gemma-4 E2B)". Once +installed, type your prompt and press the "sparkle pencil" icon above +and to the right of the prompt text box. Select the LTX prompt +enhancer model, and choose the system prompt "LTX-2.5 +Text-to-Video". This will expand the prompt by adding cinematic +instructions and colorful detail. You may edit the resulting prompt as +you wish. + +#### Auto duration + +LTX-2.5 ships with a small **duration head** that reads your prompt, as the model will see it, and predicts how long the shot it describes should run. With a duration head in the **Duration head** slot of **Model components**, an **Auto duration** switch appears above Frames: + +* With it on, the head chooses the clip's length, and **Frames becomes Max frames** — the longest clip it may choose. That number still sizes the run's memory, so it stays editable, and its help text shows the range in seconds. Raise it to allow longer shots. +* The head chooses between one second (or the shortest clip LTX-2 makes) and Max frames, at the panel's FPS, and lands on the 8·n + 1 frame grid. If Max frames is too short to leave it a choice, the clip simply runs at that length. +* It is off for audio-to-video, video-to-audio and extensions, where the source clip decides the length. +* The length that ran is what the video records, so recalling it reproduces that exact clip; recall turns Auto duration off for that reason. + +The duration head is a separate, optional 3.6 MB install (**LTX-2.5 Duration Head** in the starter models). It is fetched from Lightricks' own repository, which is license-gated: the install needs a Hugging Face token from an account that has accepted the LTX-2.5 license. Once installed it is selected automatically; the switch stays off until you turn it on. + +#### Extending with LTX-2 + +LTX-2 continues a clip from its **closing frames and closing sound**, not from a single still. The final *Context Frames* of the trimmed source (default **17**, about 0.7 s at 24 fps) are held fixed at the start of the new clip, picture and audio together, so the model sees the motion and sound it is continuing. The same span is then cross-faded to join the new clip to the source, so the join blends two copies of the same moment rather than two different ones. + +Context comes out of the frame budget: the extension adds **Frames − Context Frames** new frames, and the Context Frames help text shows that number. Trade-offs when choosing it: + +* **Longer context carries more of the source.** 17 frames is enough for motion and the *tone* of a sound, but only about a syllable of it: extending a song at 17 tends to flatten the continuation into a sustained note. Raising context to 33–49 restores rhythm and cadence. +* **Raise Frames with it.** At 121 frames, context 17 adds 104 new frames; context 49 adds only 72. +* **The ceiling depends on the source.** The join must fit the overlap in memory at the source's own resolution, so large sources cap Context Frames lower. The control's maximum already reflects this. + +Context Frames steps along the same 8·n + 1 grid as Frames (9, 17, 25, 33 …). + +#### Audio-to-video and video-to-audio + +LTX-2 generates picture and soundtrack as one sequence, so it can hold either stream fixed and generate only the other — the same mechanism a first frame uses to hold one image fixed, applied to a whole stream. The panel exposes this as the [Conditioning Clip](#conditioning-clip): + +* **Audio-to-video** encodes the clip's soundtrack and keeps it fixed at every step. Because the model has always learned picture and sound together, it lines the video up with the audio — a face speaking or singing to camera gets matching lip movement. The output contains the original recording, so nothing is lost from the audio. The two modes are mirror images: each returns the stream you supplied untouched. +* **Video-to-audio** keeps the clip's frames fixed and generates a soundtrack. The output is your original footage, frame for frame, with the generated soundtrack muxed in. + +Both run on the single-pass canvases only. Guidance applies as usual on Dev; in particular **Audio CFG** is the one that steers a video-to-audio soundtrack toward the prompt. + +#### License + +LTX-2.5 is released under the **LTX-2.x Community License**: free for commercial and production use by entities with less than $10M in annual revenue, above which a paid commercial agreement is needed. The optional Duration Head comes from Lightricks' gated repository (see [Installing LTX-2](#installing-ltx-2)). The Gemma-4 E2B prompt enhancer is Apache 2.0. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +#### Installing LTX-2 + +The **LTX-2.5** starter bundle installs the working set: + +* **LTX-2.5 Dev Transformer (int8)** and **LTX-2.5 Distilled Transformer (int8)** (~18 GiB each) — single-file transformer builds. **One of these is the model you select in the Video panel**, and which one you pick decides the schedule. +* **LTX-2.5 Components** — the video VAE, audio VAE, vocoder, text connectors and the x2 latent upscaler, which go in the panel's **Model components** slot. The upscaler is what the two-stage presets need; a hand-assembled components folder without it fails when the refine pass begins, after the first pass has already run. +* **LTX-2.5 Text Encoder (Gemma-4 12B, int8)** — the Lightricks-tuned Gemma-4 text tower, which goes in the **Gemma-4 encoder** slot. No LTX-2 model carries text-encoder weights, so this one is required rather than an override. + +Available separately in the starter models list: + +* **LTX-2.5 Distilled LoRA** (~8.9 GB) — the [fast path](#the-distilled-lora-fast-path-on-dev) for the Dev transformer. Not needed if you use the Distilled transformer. +* **LTX-2.5 Dev Transformer (bf16)** (~38 GB) and **LTX-2.5 Text Encoder (Gemma-4 12B, bf16)** (~24 GB) — unquantized builds, for a 48 GB+ card or partial loading. +* **LTX-2.5 Duration Head** (3.6 MB) — enables [Auto duration](#auto-duration). License-gated: needs a Hugging Face token whose account has accepted the LTX-2.5 license. +* **LTX-2.5 Prompt Enhancer (Gemma-4 E2B) (~10 GB) - Enables prompt enhancement. + +LTX-2 does not condition on the last hidden state of its text encoder the way most models do: it stacks the hidden states of *every* Gemma layer and runs them through a separate connector model. That is why the encoder is a large, separate download. + +Measured on a 48 GB card at 1248×704×121 frames, the int8 Dev transformer is resident at ~18 GiB and the denoise working set peaks at ~2.8 GiB, with all four guidance passes running. The Distilled build at the same size needs the same memory and about a fifteenth of the time. + +:::caution[License] +LTX-2.5 is distributed under the **LTX-2.x Community License**, which is free for entities under $10M in annual revenue. Review the terms at [huggingface.co/Lightricks/LTX-2.5](https://huggingface.co/Lightricks/LTX-2.5) before installing. +::: + +--- + diff --git a/docs/src/content/docs/Users Guide/05.Video Generation/minimax-h3.mdx b/docs/src/content/docs/Users Guide/05.Video Generation/minimax-h3.mdx new file mode 100644 index 00000000000..7cb68085a19 --- /dev/null +++ b/docs/src/content/docs/Users Guide/05.Video Generation/minimax-h3.mdx @@ -0,0 +1,106 @@ +--- +title: MiniMax H3 +lastUpdated: 2026-09-29 +alsoListedIn: + - users-guide/models/local-models +sidebar: + order: 2 +--- + +MiniMax H3 creates high-quality videos with sound at a fixed framerate +of 24 fps with a duration of up to ~15s. The FL2VA variant supports pure +text prompting, as well as text prompting plus conditioning with a +first frame and/or last frame image. The Ref2VA variant supports pure +text prompting as well as up to 9 reference images and 3 reference +videos. The reference videos can be used to guide the generated +media's video, audio, or both video and audio. + +### The H3 Models + +MiniMax H3 ships as two task transformers that share every other component. **FL2VA** — *first/last-frame to video with audio* — covers text-to-video, first frame, last frame, both, and extend. **Ref2VA** — *reference to video with audio* — conditions a new clip on **reference media**: up to 3 videos and 9 images, in an order you choose (order changes the result). Every generation from either includes a **jointly generated stereo soundtrack**, muxed into the MP4 as AAC. + +The panel switches task by transformer: select the Ref2VA single-file transformer as the **Model** and the conditioning sections become the ordered **References** list; select the FL2VA transformer to get the five frame/extend modes back. A video reference can contribute its image track, its soundtrack, or both — soundtrack-only references must be paired with at least one visual reference. Image references default to a high-detail 2048 px encoding ("Max"); the "Match generation size" option is several times faster at some fidelity cost. + +What makes it different from Wan in practice: + +* **Guidance-distilled** — there is no CFG and no negative + prompt. Prompt adherence is baked in; the panel hides both + controls. + +* **Fixed 24 fps** and a fixed frame grid: 90–345 frames + in steps of 17 (about 3.8 to 14.4 seconds), default 124. + +* **Two fixed resolutions** "768 highres" pins the short edge to 768 + (native quality); "768 lowres" pins the *long* edge to 768 for fast + previews. + +* **Aspect ratios** from 1:4 to 4:1 are supported, wider than the + preset list. + +* **Turbo fast path** — step-distillation LoRAs render in ~6–8 steps instead of ~50. The panel's Turbo toggle + defaults to **on** when one is installed (8 steps). + +* **Conditioning text encoder** is a truncated **Qwen3-VL-32B** vision-language + model, which is why H3 follows detailed scene descriptions well. + +#### The hybrid: FL2VA quality with Ref2VA conditioning + +The two transformers are not equal. FL2VA produces the cleaner output; Ref2VA is the only one that takes references, but a known training defect degrades what it generates — video and audio artifacts that FL2VA does not show. A tensor-by-tensor comparison of the two checkpoints shows they differ almost only in their per-block **AdaLN modulation projections** — the small time-conditioning layers that tell each block how far along the denoise it is. Attention, MLPs, and output heads are near-identical. + +The **hybrid** exploits that: it runs the FL2VA transformer with Ref2VA's AdaLN projections swapped in for the upper part of the network. References still route (the task is decided by those projections), while everything else keeps FL2VA's quality. + +To use it: + +1. Select the **Ref2VA** transformer as the **Model**, as for any reference generation. +2. Under **Model Components**, pick the FL2VA transformer in **Hybrid quality base (FL2VA)**. The slot lists only files of the same kind as the model — pruned with pruned, full with full — because the projections have different shapes otherwise. +3. Leave **Ref2VA AdaLN from block** at its default of **25** to start. H3 has 50 blocks (0–49); blocks from the chosen one through 49 keep Ref2VA's projections, earlier blocks use FL2VA's. Lower values follow the references more closely at more of Ref2VA's quality cost; higher values are cleaner but weaker on adherence. + +Nothing is merged or written to disk: at generation time the loader loads the FL2VA base and the selected AdaLN tensors are read from the Ref2VA file and swapped in for the duration of the run. On the pruned starter repacks that costs about 40 MB of RAM; full-size pairs cost far more (roughly 520 MB per selected block). The **Turbo** toggle keys off the model, so it picks the Ref2VA Turbo LoRA (8 steps with the LightX2V release); the FL2VA Turbo can be added by hand under **Concepts** as an experiment, since neither distillation saw the hybrid's mixed path. Your own LoRAs apply on top of the hybrid as usual. + +The hybrid is an experiment adopted from the community — see the acknowledgements below. Early renders with the defaults were free of the artifacts Ref2VA alone produced, but judge it on your own results rather than treating it as a fixed improvement. + +#### Ref2VA Prompt Enhancement + +Unlike FL2VA, Ref2VA was trained with a [specific structured prompt +format](https://app.artificialstudio.ai/articles/prompts-for-minimax-h3-examples-tutorial). This +format is particularly important to follow when you have multiple +character reference images and videos and you wish to assign specific +references to different characters in your video. + +To help you with this, InvokeAI provides an optional prompt +enhancer. To use it, you will need to install one of the optional text +LLMs, such as "Qwen2.5-1.5B-instruct." After installing the model, +enter your unenhanced prompt and click on the "sparkle pencil" +icon. Select the LLM you installed, then select "MiniMax H3 Ref2VA +Structured Prompt" from the System prompt menu. This will fill in a +template structured prompt which you can then tweak. + +When you use reference images and videos in a Ref2VA generation, you +may refer to them within the prompt as <Picture 1>, <Video +1> and so forth. These labels appear adjacent to the reference +image or video. + +#### License + +MiniMax H3 is released under the **MiniMax H3 Community License**. Commercial use is allowed, with conditions: products earning more than $20M a year need written authorization from MiniMax, commercial products must show "MiniMax H3" in their interface, use is restricted in some territories (see the note under [Installing MiniMax H3](#installing-minimax-h3)), and outputs may not be used to improve other AI models. The downloads are not gated. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +#### Installing MiniMax H3 + +The **MiniMax H3** starter bundle (~85 GB) installs the working set: + +* **MiniMax H3 FL2VA Transformer (int8, pruned)** and **MiniMax H3 Ref2VA Transformer (int8, pruned)** (~21 GB each) — single-file transformer builds. **One of these is the model you select in the Video panel**, and the one you pick decides the task: FL2VA for the frame/extend modes, Ref2VA for reference conditioning (with FL2VA optionally in the hybrid base slot). +* **MiniMax H3 Components** (~11 GB) — the Diffusers-format install of the tokenizer, processor, and video + audio VAEs, which goes in the panel's **Model components** slot — plus **MiniMax H3 Text Encoder (int8)** (~27 GB), the single-file build for its **Text encoder** slot (the Components install carries no text-encoder weights). The bundled templates wire them the same way. +* **MiniMax H3 Turbo LoRA**, **MiniMax H3 LightX2V Turbo LoRA**, and **MiniMax H3 LightX2V Ref2V Turbo LoRA** — independent step-distillation LoRAs; the Turbo toggle picks the one matching the selected task (the Ref2V build is trained against the Ref2VA transformer only). The earlier 4-step *MiniMax H3 Ref2V Turbo LoRA* (v0.1) is no longer a starter: it pans the camera rightward whenever a video reference is used. If you still have it installed, the toggle prefers the LightX2V release once that is installed too. + +Reference conditioning adds rows to every denoising step roughly linearly: a few references typically cost ~1–2 GiB of extra activation memory, but a full-detail 2048×2048 image reference alone adds ~4096 rows (about 1 GiB), and three full-length video references can triple the packed sequence. Prefer "Match generation size" for image references and trimmed clips for video references on smaller cards. + +H3 is the heaviest local video option — With 16 GB VRAM, `enable_partial_loading` enabled and 32 GB of system RAM it can create a full 345 frame video (about 14s) at 768x768 resolution, but for larger or longer videos a high-VRAM GPU (24 GB+) is strongly recommended. + +:::caution[License restrictions] +MiniMax H3 is distributed under the **MiniMax H3 Community License**, which restricts use by territory (currently excluding the US, EU, UK, and South Korea) — and the restriction **extends to model outputs**. Review the terms at [huggingface.co/MiniMaxAI/MiniMax-H3](https://huggingface.co/MiniMaxAI/MiniMax-H3) before installing. The Turbo LoRAs themselves are Apache 2.0. +::: + +--- + diff --git a/docs/src/content/docs/Users Guide/05.Video Generation/video-workflows.mdx b/docs/src/content/docs/Users Guide/05.Video Generation/video-workflows.mdx new file mode 100644 index 00000000000..ef81fc732ca --- /dev/null +++ b/docs/src/content/docs/Users Guide/05.Video Generation/video-workflows.mdx @@ -0,0 +1,256 @@ +--- +title: Video Workflows +description: A plain-language guide to the ready-made Wan 2.2, MiniMax H3 and LTX-2 video workflows that ship with InvokeAI — which one to pick, and how to run it. +lastUpdated: 2026-09-25 +--- + +import { Card, CardGrid, Steps, LinkButton } from '@astrojs/starlight/components'; + +InvokeAI ships **twenty-six ready-made video workflows** — ten for **Wan 2.2**, six for **MiniMax H3** and ten for **LTX-2** — so you can start generating video without wiring up nodes yourself. Each one is a complete recipe — open it from the **Workflows** library, pick your models, type a prompt (and/or drop in an image), and press **Invoke**. + +This page helps you choose the right workflow and run it. For how the models work under the hood, VRAM details, and troubleshooting, see the [Video Generation guide](/users-guide/video-generation/introduction/). + +:::tip[There's an easier way] +Most of what these workflows do is also available with no graph at all: the **Video panel** in the editor's left rail infers the right pipeline from whatever you fill in — prompt, first/last frame, initial video — and handles model constraints automatically. Start with the [Video Generation guide](/users-guide/video-generation/introduction/) if you just want to make a video; come here when you want graph-level control. +::: + +:::caution[Experimental] +Video generation is a prototype feature. These workflows and the models behind them are new — expect rough edges, and check the [Video Generation guide](/users-guide/video-generation/introduction/) if something misbehaves. +::: + +--- + +## Before you start + +**1. Install the video models.** Open the **Model Manager** and install a starter bundle: **Wan 2.2 Text-to-Video**, **Wan 2.2 Image-to-Video**, **MiniMax H3**, and/or **LTX-2.5**. The [Video Generation guide explains the bundles and what fits your GPU](/users-guide/video-generation/introduction/#the-models). + +**2. You'll select models when you open a workflow.** The shipped workflows come with their model slots **left empty on purpose** — every InvokeAI install stores models a little differently, so a workflow can't point at yours automatically. When you open one, look at its **Notes** panel (the note icon in the workflow editor): it lists exactly which model to pick in each slot. Drop those in once and you're set. + +:::tip +Whenever a workflow won't run because a model field is empty, open its **Notes** — the shopping list of models to select is right there. +::: + +--- + +## Which workflow should I use? + +The Wan workflows come in three families — **Text to Video**, **Image to Video**, and **Extend Video** — each with a high-quality option, a "concept LoRA" variant, and a lighter low-VRAM option — plus an **Interpolate** workflow that bridges two images and an **Extend Video to Image** variant that steers a continuation toward a target frame. The six **MiniMax H3** workflows cover the same conditioning modes — plus reference-conditioned generation — and add sound. The ten **LTX-2** workflows cover every one of those modes with sound, add **audio-to-video** and **video-to-audio**, and include a two-stage high-resolution text-to-video and a LoRA-collection variant. + +| I want to… | Use | Notes | +|---|---|---| +| Make a clip from a text description | **Text to Video - Wan 2.2 Lightning** | Best quality, fast. Start here. | +| Make a clip **with a soundtrack** | **Text to Video - LTX-2** or **Text to Video - MiniMax H3** | Both generate synchronized stereo audio with every video. | +| Make a **high-resolution** clip | **Text to Video - LTX-2 Two-Stage** | Generates at half size, upscales, then refines at 1024p or 1536p. Slow. | +| Animate an existing image | **Image to Video - Wan 2.2 Lightning**, **First Frame to Video - MiniMax H3** or **First Frame to Video - LTX-2** | Your image becomes the first frame. | +| Make a video that *ends on* an image | **Last Frame to Video - MiniMax H3** or **Last Frame to Video - LTX-2** | The video builds toward your image. | +| Make a video between two images | **Interpolate 2 Images to Video - Wan 2.2 Lightning**, **First and Last Frame to Video - MiniMax H3** or **First and Last Frame to Video - LTX-2** | Provide a start and end image; the model interpolates between them. | +| Make a video longer than one generation | **Extend Video - Wan 2.2 Lightning**, **Extend Video - MiniMax H3** or **Extend Video - LTX-2** | Continues an existing video and stitches the pieces together. H3 and LTX-2 continue the soundtrack too. | +| Make a clip **in the style/likeness of reference media** | **Reference to Video - MiniMax H3** | Conditions on up to 3 reference videos and 9 images (Ref2VA transformer required); reference order changes the result. | +| Extend a video toward a specific end frame | **Extend Video to Image - Wan 2.2 Lightning** or **Extend Video to Image - LTX-2** | Continues a video and interpolates the new part to a target image. | +| Make a picture **for a soundtrack** — a song, a voice recording | **Audio to Video - LTX-2** | The audio is kept as-is; the video is generated to match it, including lip movement. | +| Add a **soundtrack to a silent or existing clip** | **Video to Audio - LTX-2** | Your footage is kept as-is; a new stereo soundtrack is generated for it. | +| Add my own style/subject LoRAs | the **w/ Concept LoRAs** variant of a Wan workflow, or **Text to Video - LTX-2 w/ LoRA Collection** | Same as the base workflow, plus your LoRAs. | +| Run on a smaller GPU (≈12–16 GB) | a **TI2V-5B (Low Quality)** workflow | Smaller model, lower memory, lower quality. | + +If you're not sure, start with **Text to Video - Wan 2.2 Lightning** to get a feel for it, then move to Image-to-Video and Extend — or straight to **Text to Video - MiniMax H3** if you want audio and have the hardware for it. + +--- + +## Bundled Wan 2.2 workflows + +### Text to Video + + + + Type a prompt, get a ~5-second clip. Uses the high-quality A14B model with the Lightning speed-up (just 4 steps). **The recommended starting point.** + + + The same workflow, plus extra slots for your own concept LoRAs (a style or character you've trained or downloaded). Every LoRA slot must be filled — if you don't have concept LoRAs to add, use the plain version instead. + + + Uses the smaller TI2V-5B model. Lower quality and slower per result, but fits comfortably on 12–16 GB GPUs. Good for quick drafts or smaller cards. + + + +### Image to Video + + + + Drop in a starting image and a prompt; the model animates outward from your image as the first frame. Uses the A14B model + Lightning. **The recommended image-to-video starting point.** + + + Same as above, with slots for your own concept LoRAs. + + + Image-to-video on the smaller TI2V-5B model. Lower quality, lower VRAM — the low-memory option for animating an image. + + + +### Interpolate Between Two Images + + + + Provide a **start image** and an **end image**; the model generates a clip that begins on the first and animates smoothly to the second (first-last-frame interpolation). Great for morphing between two stills or bridging two shots. Uses I2V-A14B + Lightning (4-step). + + + +:::caution[The "Concept LoRAs" variants need a LoRA in every slot] +A **concept LoRA** is an add-on that teaches the model a specific style, character, or subject. The *w/ Concept LoRAs* workflows are the same as their plain counterparts but add extra LoRA slots. **Every LoRA slot is required** — if any slot, including the extra concept ones, is left empty, the workflow won't run. Only use these variants when you actually have concept LoRAs to apply; otherwise use the plain version. +::: + +:::note[About the "TI2V-5B (Low Quality)" variants] +"Low quality" here means *lower than the A14B Lightning workflows*, in exchange for running on much less video memory. On a 12–16 GB GPU they may be your best (or only) option, and they're handy everywhere for fast drafts. +::: + +### Extend Video + +The Wan models are trained on short (~5 second) clips, so the way to make something longer is to **continue** a video and stitch the pieces together. These workflows do that for you. + + + + Provide an existing video and use the sliders to choose where to continue from. The workflow generates a new clip starting from that point and joins it onto the original with a smooth transition. Run it repeatedly to keep growing the video. + + + Same as above, with slots for your own concept LoRAs so the continuation keeps your style or subject. + + + Continue a video **toward a target image**. Provide a starting video and a destination image; the new segment interpolates from the video's last frame to your image (FLF2V), then joins onto the original with a smooth transition. Combines the Extend and Interpolate ideas — useful for steering where a continuation ends up. + + + +:::tip[Using "Extend Video to Image"] +This variant takes **two inputs** — the source video and a destination image: + +1. Drop your **video** into the video input and use the **start/end frame sliders** to pick the slice to continue from. +2. Drop a **destination image** into the image input. The generated continuation interpolates from the video's last selected frame to this image, so the extended clip *ends on your image*. +3. **Invoke.** The new segment is concatenated onto the source with a cross-fade — tune the join (transition type and frame count) on the **Concatenate Videos** node. + +Because the endpoint is pinned to your image, it's ideal for bridging a clip to a known next shot or landing on a specific final frame. +::: + +--- + +## Bundled MiniMax H3 workflows + +One model family, six workflows, every conditioning mode — and every output is an MP4 with a **jointly generated stereo soundtrack**. All default to a **Turbo** step-distillation LoRA (6 steps for FL2VA with the original Turbo LoRA, 8 with either LightX2V release); H3 has no CFG or negative prompt to tune, and always renders at 24 fps. **Reference to Video** needs the Ref2VA transformer selected in the model loader; the other five use FL2VA. + + + + Type a prompt, get a clip **with sound**. H3's vision-language text encoder follows detailed scene descriptions well — describe the audio you expect, too. + + + Drop in a starting image; the video animates outward from it, soundtrack included. + + + The reverse — provide an **ending** image and the video builds toward it. Unique to H3; no Wan workflow can do this. + + + Provide both; the model interpolates a clip (with audio) that starts on the first image and lands on the second. + + + Continue an existing video with a new H3 segment, joined on with a smooth transition. The continuation renders at H3's fixed 24 fps. + + + +:::note[Selecting the H3 models] +The H3 workflows use the **MiniMax H3 Model Loader**, which takes three selections from the **MiniMax H3** starter bundle: the **MiniMax H3 Components** install as the main model, plus the single-file **FL2VA Transformer (int8, pruned)** and **Text Encoder (int8)** in its override fields. Each workflow's **Notes** panel lists them. H3 is the heaviest local video option — see [the hardware notes](/users-guide/video-generation/minimax-h3/#installing-minimax-h3). +::: + +:::caution[License restrictions] +MiniMax H3 is distributed under a restrictive community license that excludes certain territories (currently the US, EU, UK, and South Korea) and extends to outputs. See the [Video Generation guide's license note](/users-guide/video-generation/minimax-h3/#installing-minimax-h3) before installing. +::: + +--- + +## Bundled LTX-2 workflows + +Every LTX-2 workflow outputs an MP4 with a **jointly generated stereo soundtrack** (or, for audio-to-video, your own). Each works with either transformer: the **Dev** build runs the guided 30-step recipe, and the **Distilled** build a fixed 8 steps that ignore the Steps and CFG fields. The canvas comes from your image, source video or clip where there is one — except in Audio to Video, which generates the picture — and otherwise the **Width** and **Height** fields set only the aspect ratio. **Target Resolution** sets the size. + + + + Type a prompt, get a ~5-second clip with sound. Describe the audio you expect, not just the picture. + + + The high-resolution version: generates at half the canvas, doubles it with LTX-2's latent upscaler, then refines it at full size. Defaults to 1024p (1792×1024 at 16:9); 1536p also works, but 512p–768p fail here — use the plain Text to Video workflow for those. Much slower — minutes on Distilled, far longer on Dev. + + + Text-to-video plus a list of your own LTX-2 LoRAs, each with its own weight. Leave the list empty to run the base model. + + + Your image becomes the opening frame; the video and its soundtrack continue from it. + + + Provide an **ending** image and the video builds toward it. + + + Provide both; the clip starts on the first image and lands on the second. Give the two images the same aspect ratio. + + + Continue an existing clip. The continuation starts from the source's last few frames **and its closing sound**, and joins onto it with a cross-fade over the same span, so the picture and the soundtrack both carry across the join. **Context Frames** sets how much of the source is carried in. + + + The same, with a **Last Frame** image: the continuation ends on your image. Give it the source clip's aspect ratio. + + + Drop in a clip — or an audio file uploaded to the gallery — and LTX-2 generates a picture for its soundtrack, down to lip movement. The soundtrack decides the length, and the output carries the original recording. Say in the prompt who or what is making the sound. + + + Drop in a clip and LTX-2 generates a soundtrack for it. The clip decides the length and frame rate, and the output is your original footage with the new audio. + + + +:::tip[Tuning the LTX-2 workflows] +* **Use a single-pass resolution** (512p, 704p or 768p) everywhere except the Two-Stage workflow. The other workflows have no refine pass, so 1024p and 1536p render *smaller* than 768p. Audio-to-video and video-to-audio cannot run two-stage at all. +* **Number of Frames** must be on LTX-2's 8·n + 1 grid (9, 17, … 121, … 241); unlike the Video panel, the workflows do not snap other values, and the run fails at the Denoise node. The frame rate sets how much audio is generated, as well as the clip's duration. +* **Extending:** **Context Frames** defaults to 17 (about 0.7 s at 24 fps) — enough for motion, but only about one syllable of sound. For singing or other rhythmic material, raise it along the same grid (25, 33, 41, 49 …). **Number of Frames** *includes* the context, so the extension adds Number of Frames − Context Frames new frames; raise both together. +* **The Distilled LoRA** (the 8-step fast path for the Dev transformer) goes in the LoRA Collection workflow's list. Also set **Schedule** to `distilled`, **Steps** to 8 and **CFG** to 1 in the form — the `auto` schedule follows the checkpoint, not the LoRA. + +The **Notes** panel of each lists the models to select in the form's **Model** section: an LTX-2.5 transformer for **Model**, the LTX-2.5 Components install for **Components**, and the Gemma-4 text encoder for **Text Encoder**. +::: + +--- + +## Running a workflow + + + +1. **Open the Workflows tab** and load one of the workflows from the library. + +2. **Read the Notes** (note icon) and select the models it lists — the main model, VAE, and text encoder, plus the Lightning LoRA pair on the A14B workflows, the H3 loader's three selections plus the Turbo LoRA on the MiniMax workflows, or the LTX-2 loader's three selections on the LTX-2 workflows. + +3. **Set your inputs:** + - *Text to Video* — type a **positive prompt** describing the scene and motion. + - *Image to Video / First (or Last) Frame to Video* — drop a starting (or ending) **image** into the image field, and optionally add a prompt. + - *Extend Video* — provide a **starting video** and trim it with the **Start Frame** and **End Frame** sliders (they appear once the video is loaded); the continuation picks up from the end of the trimmed range. + +4. **(Optional) adjust resolution.** The defaults are chosen to balance speed and memory. If you hit an out-of-memory error, lower the resolution preset — see the [VRAM tips](#which-one-fits-my-gpu) below. + +5. **Press Invoke.** The finished MP4 appears in the gallery and plays inline in the viewer — with its audio track, on H3. + + + +:::tip +Leave the **frame count** and **steps** at their defaults to start. They're already tuned for each workflow (for example, the Lightning workflows use only 4 steps and the H3 workflows 6, which is what makes them fast). +::: + +--- + +## Which one fits my GPU? + +A rough guide — exact numbers depend on resolution and your system. See [Video Generation → OOM errors](/users-guide/video-generation/introduction/#oom-errors) for the full picture. + +| Your GPU | Best choice | +|---|---| +| 24 GB and up | Any Wan workflow at 720p; the MiniMax H3 workflows (with `enable_partial_loading` and generous system RAM). | +| 16–24 GB | A14B Lightning workflows at 480p; TI2V-5B at up to 720p. | +| 12–16 GB | The **TI2V-5B (Low Quality)** workflows. | + +If you get an **out-of-memory** error, lower the resolution first (it helps far more than reducing the frame count), and switch to a TI2V-5B workflow if you're still over budget. On H3, switch the resolution to **768 lowres** for drafts. + +--- + +## Going further + +These workflows are starting points. Once you're comfortable, the [Video Generation guide](/users-guide/video-generation/introduction/) covers the **Video panel** (the no-graph way to run all of this), the model variants, how image conditioning works, extending clips into longer videos, recommended parameters, and troubleshooting. + +Read the Video Generation guide diff --git a/docs/src/content/docs/Users Guide/05.Video Generation/wan-2.2.mdx b/docs/src/content/docs/Users Guide/05.Video Generation/wan-2.2.mdx new file mode 100644 index 00000000000..36f41e1dad6 --- /dev/null +++ b/docs/src/content/docs/Users Guide/05.Video Generation/wan-2.2.mdx @@ -0,0 +1,64 @@ +--- +title: Wan 2.2 +lastUpdated: 2026-09-29 +alsoListedIn: + - users-guide/models/local-models +sidebar: + order: 1 +--- + +Wan 2.2 creates short videos from a text-only prompt, or a prompt plus +starting images for the first and/or last frames. Unlike LTX-2 or +MiniMax H3, Wan cannot generate a soundtrack (but you can use LTX-2's +[conditioning mode](../introduction#conditioning-clip-ltx-2-only) to add one). + +### License + +Wan 2.2 (T2V-A14B, I2V-A14B and TI2V-5B), its text encoder and the Lightning LoRAs are released under **Apache 2.0**, which allows commercial use. None of the downloads is gated. + +This is a summary, not legal advice. Read the full license on the model's HuggingFace page before using a model commercially. + +### The Wan Models + +Wan 2.2 ships three transformer variants, plus a shared text encoder and two VAEs. All share the same diffusion-style sampling but differ in size, conditioning, and intended task. + +| Variant | Task | Params | VAE | Conditioning | +|---|---|---|---|---| +| **T2V-A14B** | Text → Video | 14B × 2 experts | A14B VAE (16-ch, 8× spatial) | Text only | +| **I2V-A14B** | Image+Text → Video | 14B × 2 experts | A14B VAE (16-ch, 8× spatial) | Text + reference image (36-channel concat) | +| **TI2V-5B** | Text → Video OR Image+Text → Video | 5B (single) | Wan 2.2-VAE (48-ch, 16× spatial) | Text, optionally with reference image (first-frame mask blend) | + +* **T2V-A14B** generates videos from a text prompt alone. Best motion coherence and prompt-following of the three. +* **I2V-A14B** locks the first frame to a reference image you supply, and also handles first-to-last interpolation and extension. Best subject-preservation. +* **TI2V-5B** is the small single-expert variant. It does both text-to-video and image-to-video at substantially lower VRAM, with somewhat less stable long-range coherence. + +#### High-noise and low-noise transformers (A14B variants only) + +The A14B models are a **mixture-of-experts (MoE)** pair — *two* 14B transformers per variant. The denoise loop swaps between them at a model-defined boundary timestep: + +* **High-noise expert** runs early, when the latents are still mostly noise: composition, layout, broad motion. +* **Low-noise expert** runs late, when the latents are close to clean: detail and texture. + +InvokeAI handles the swap automatically. In the Video panel, a single-file A14B main takes its partner through the **Transformer (Low Noise)** component slot; a Diffusers A14B main carries both experts internally. **TI2V-5B is single-expert** — no swap, no low-noise CFG, no second slot. + +#### Lightning (the Wan fast path) + +The stock A14B variants need ~40–50 denoise steps. The **Lightning distillation LoRAs** collapse that to **4 steps** at CFG 1 with minimal quality loss — about a 10× speedup. There's a pair per variant (one per expert); the panel's Lightning toggle wires both automatically. + +:::tip +**TI2V-5B has no Lightning LoRA.** Its smaller size makes each step cheap; run it at 40–50 steps for a similar wall-clock to A14B + Lightning. +::: + +#### Installing Wan models + +Two **starter bundles**: + +* **Wan 2.2 Text-to-Video** (~36 GB) — UMT5-XXL text encoder, both VAEs, TI2V-5B Q4_K_M, T2V-A14B Q4_K_M (high + low), T2V Lightning (high + low). +* **Wan 2.2 Image-to-Video** (~32 GB) — UMT5-XXL, A14B VAE, I2V-A14B Q4_K_M (high + low), I2V Lightning (high + low). + +The bundles are independent; installing both totals ~56 GB (shared components are deduplicated). Higher-quality Q8_0 quantizations and full Diffusers builds are available a-la-carte in the starter models list. + +:::tip +**On a 12 GB VRAM card**: install just the Text-to-Video bundle and use TI2V-5B — it covers both text-to-video and image-to-video and fits comfortably. The A14B variants will technically run via aggressive offloading but are slow and prone to OOM. +::: + diff --git a/docs/src/content/docs/Users Guide/06.Gallery/gallery-reference.mdx b/docs/src/content/docs/Users Guide/06.Gallery/gallery-reference.mdx new file mode 100644 index 00000000000..5ed412125c4 --- /dev/null +++ b/docs/src/content/docs/Users Guide/06.Gallery/gallery-reference.mdx @@ -0,0 +1,107 @@ +--- +title: Gallery Reference +description: Details of the Gallery — the board menu, archiving, downloading and deleting boards, the media menu, Gallery settings and keyboard shortcuts. +lastUpdated: 2026-09-30 +sidebar: + order: 2 +--- + +import { Image } from 'astro:assets'; +import boardMenu from '../assets/gallery-board-menu.png'; +import deleteBoard from '../assets/gallery-delete-board.png'; +import imageMenu from '../assets/gallery-image-menu.png'; +import gallerySettings from '../assets/gallery-settings.png'; + +This page describes the Gallery's menus, settings and shortcuts in detail. For an introduction to boards, searching, starring, uploading, and moving and deleting media, see [Organizing your media](/users-guide/gallery/introduction/). + +## Managing boards + +### The board menu + +Hover over a board and click its **⋮** button, or right-click it, to open its menu: + +The board menu with Auto-add to this board, Download Board, Rename board, Archive Board and Delete Board. + +* **Auto-add to this board** — makes this the board that receives new results, marked **Auto**, whichever board is selected. On the auto-add board, the item is **Stop auto-adding here**, which goes back to sending results to the selected board. Not offered for date boards or archived boards. See [Choosing where new media goes](/users-guide/gallery/introduction/#choosing-where-new-media-goes). +* **Download Board** — downloads the board (see [below](#downloading-a-board)). +* **Rename board** — opens a dialog to change the name. +* **Archive Board** / **Unarchive Board** — see [below](#archiving-a-board). +* **Delete Board** — see [below](#deleting-a-board). + +Project boards are managed through their project: their menu has **Export project (.invk)**, **Auto-add to this board** and **Download Board** instead, and renaming the project renames its board. **Uncategorized** offers only **Auto-add to this board** and **Download Board**, and date boards have no menu. If the auto-add board is deleted, results go back to following the selected board. + +### Archiving a board + +Archiving hides a board you no longer use without deleting anything. Choose **Archive Board** from its menu, and the board disappears from the list. To see archived boards again, turn on **Archived boards** in the **Filter & sort boards** menu; they appear in their own **Archived** group, where **Unarchive Board** returns them to the list. + +Archived boards are not included in [semantic search](/users-guide/gallery/introduction/#searching-by-content). + +### Downloading a board + +**Download Board** packages the board's images, from both its Media and Uploads tabs, into a `.zip` file. A notice appears while the archive is prepared, and the browser saves the file when it is ready. Videos are not included, and the menu item says how many will be left out, for example **Download Board (1 video omitted)**. To save a video, download it on its own from its menu. + +### Deleting a board + +Choose **Delete Board** from the board's menu. The confirmation dialog shows how many images, videos and uploads the board holds, and offers two choices: + +The Delete board dialog with Cancel, Delete Board Only and Delete Board and Media buttons. + +* **Delete Board Only** — removes the board and moves everything on it to **Uncategorized**. +* **Delete Board and Media** — removes the board and **permanently deletes** its images and videos. + +:::danger +**Delete Board and Media** cannot be undone. +::: + +Queued or running jobs that were sending results to the deleted board send them to **Uncategorized** instead. Project boards cannot be deleted from the Gallery; delete the project instead. + +On a multi-user installation, only a board's owner or an administrator can delete it. When an owner deletes a shared board with its media, only their own media is deleted and other people's items move to Uncategorized; an administrator's deletion removes everything. If some files cannot be removed, those items are kept in Uncategorized and a warning says how many. + +## The media menu + +Right-click an item, or several selected items, for its menu. + +The context menu for an image, with quick-action icons at the top followed by Load Workflow, Recall Metadata, Send to Upscale, Send to Video, Use as Reference Image, Use as Prompt Template, Select for Compare, New from Image, Change Board and Delete Image. + +For an image, the menu offers: + +* **Quick actions** (the icons at the top) — open in a new tab, copy to the clipboard, download, open in the Preview, and star. +* **Load Workflow** and **Recall Metadata** — restore the workflow or the settings that made the image. Recall Metadata includes **Recall All**, **Remix Image**, **Use Prompt**, **Use Seed**, **Use Size** and **Use CLIP Skip**; see [Resetting to defaults](/users-guide/image-generation/generate-panel/#resetting-to-defaults). These are unavailable for images without generation metadata. +* **Send to Upscale**, **Send to Video**, **Use as Reference Image**, **Use as Prompt Template** and **Select for Compare** — use the image elsewhere in InvokeAI. +* **New from Image** — start a new canvas from the image, or add it to the current canvas as a new layer. +* **Change Board** and **Delete Image**. + +A video's menu has the equivalent actions for video, including **Extend in Video**, **Use as Reference Video** and **Use as Conditioning Clip**. With several items selected, the menu offers the actions that apply to all of them, such as **Star All**, **Download Selection**, **Change Board** and **Delete Selection**. + +Every image generated by InvokeAI also stores its generation metadata inside the file, so the prompt and settings travel with it when you download it. + +## Gallery settings + +The gear button in the Gallery header opens its settings: + +The Gallery settings popover with Image size, Thumbnails, Always show dimensions, Show in-progress section and Pagination. + +* **Image size** — the size of the thumbnails. +* **Thumbnails** — **Square** crops thumbnails to squares; **Aspect** shows each item's full shape. +* **Always show dimensions** — shows each item's width and height on its thumbnail, not only on hover. +* **Show in-progress section** — shows generations that are still running at the top of the grid. +* **Pagination** — **Infinite** scrolling or numbered **Pages**. + +**All Gallery settings…** at the bottom opens the full settings. They also include **Send new results to the selected board** (under **Destination**), which is on by default; turn it off to keep sending results to the current destination board, which then becomes the **Auto** board. The full settings also include the sort order and the board visibility and sorting options from the [Filter & sort boards](/users-guide/gallery/introduction/#finding-filtering-and-sorting-boards) menu. All Gallery settings are saved with the project. + +## Keyboard shortcuts + +These work while the Gallery has focus: + +| Shortcut | Action | +|---|---| +| Arrow keys | Move through the grid | +| **Shift**-click / **Ctrl**-click | Select a range / add or remove an item | +| **Ctrl+A** | Select everything loaded on the page | +| **Escape** | Clear the selection | +| **.** (period) | Star or unstar the selection | +| **Delete** / **Backspace** | Delete the selection | +| **R** | Remix the selected image: recall its settings, except the seed | + +On macOS, use **Cmd** in place of **Ctrl**. See [Hotkeys](/users-guide/workbench/hotkeys/) for all shortcuts. + diff --git a/docs/src/content/docs/features/image-storage-maintenance.mdx b/docs/src/content/docs/Users Guide/06.Gallery/image-storage-maintenance.mdx similarity index 100% rename from docs/src/content/docs/features/image-storage-maintenance.mdx rename to docs/src/content/docs/Users Guide/06.Gallery/image-storage-maintenance.mdx diff --git a/docs/src/content/docs/Users Guide/06.Gallery/introduction.mdx b/docs/src/content/docs/Users Guide/06.Gallery/introduction.mdx new file mode 100644 index 00000000000..b84176b780a --- /dev/null +++ b/docs/src/content/docs/Users Guide/06.Gallery/introduction.mdx @@ -0,0 +1,154 @@ +--- +title: Organizing your media +description: Use the Gallery to organize images and videos into boards, choose where new results go, search by metadata or by content, upload media, and move, star, download or delete it. +lastUpdated: 2026-09-30 +sidebar: + order: 1 +--- + +import { Image } from 'astro:assets'; +import overview from '../assets/gallery-overview.webp'; +import createBoard from '../assets/gallery-create-board.png'; +import boardFilters from '../assets/gallery-board-filters.png'; +import searchHelp from '../assets/gallery-search-help.png'; +import semanticSearch from '../assets/gallery-semantic-search.png'; +import selection from '../assets/gallery-selection.webp'; + +The **Gallery** holds every image and video you generate or upload. It is organized into **boards**: the always-present **Uncategorized** board, a board for each project, and boards you create yourself. From the Gallery you choose where new results go, find media by its prompt or by what it shows, and move, star, download or delete it. + +The Gallery with the Concept Art board selected: the board list at the top, the Media and Uploads tabs, the search field, and a grid of images with starred items pinned at the top. + +## The Gallery at a glance + +The Gallery has three parts: + +* **The header** shows the selected board after *Gallery /*. Click the board name to hide or show the board list. The gear button opens the [Gallery settings](/users-guide/gallery/gallery-reference/#gallery-settings). +* **The board list** has a search field (**Search or create boards**), a **Filter & sort boards** button, and the boards themselves, grouped under **Boards** and, when turned on, **By Date** and **Archived**. Each group can be collapsed. +* **The media area** shows the contents of the selected board, on two tabs: + * **Media** — images and videos generated in InvokeAI. + * **Uploads** — images, videos and audio you have uploaded. + + Above the grid are the **Search items** field, the **Starred only** filter, the sort order (**Newest** or **Oldest**) and the upload button. + +When the Gallery is narrow, or docked in a side rail, the board list sits above the media grid. When it is wide, the board list becomes a column on the left. Drag the divider between them to resize the list. + +Each board shows its cover image, its name and a count such as **76 | 12**: 76 items on the Media tab and 12 on the Uploads tab. A project's board is marked **Project**. On a multi-user installation, other people's boards show their owner's name. + +## Boards + +### Creating a board + +Type a name into **Search or create boards**. If no board has that name, a **Create board "…"** row appears at the end of the list; click it, or press **Enter**. You can also type the name and click the **+** button next to the **Boards** heading. + +Typing Seascapes into the board search field shows a Create board "Seascapes" row. + +The new board is selected straight away, so it also becomes the destination for new results. A board's cover is chosen automatically: its newest starred item, or its newest item if nothing is starred. + +### Choosing where new media goes + +**By default, new results go to the board that is selected in the Gallery**: select a board, press **Invoke**, and the results land on that board. The destination is fixed when you press Invoke, so you can select another board and queue more work while earlier jobs are still running, without redirecting them. + +* A new project starts with its own board selected. See [Projects](/users-guide/projects/introduction/#where-your-images-go). +* If you select a date board (see below), results go to the project's board, because date boards are only a view. +* Uploads go to the selected board. +* If you archive or delete the selected board, the selection, and with it the destination, moves to **Uncategorized**. + +To keep sending results to one board while you browse others, make it the **auto-add board**: choose **Auto-add to this board** from its menu. The board is marked **Auto**, and new generations, Canvas saves, pasted media and the results of workflow nodes whose **Board** is set to **Auto** all go there, whichever board you are viewing. Choose **Stop auto-adding here** from the same menu to go back to following the selected board. The setting behind this is **Send new results to the selected board**, in the [Gallery settings](/users-guide/gallery/gallery-reference/#gallery-settings). + +### Finding, filtering and sorting boards + +Typing in **Search or create boards** narrows the list to boards whose names contain the text. Selecting a board clears the search. + +The **Filter & sort boards** button beside the search field controls which boards are listed and in what order: + +The Filter & sort boards menu with Board visibility options (Date boards, Archived boards, Other projects' boards) and Sort by options. + +* **Board visibility** + * **Date boards** — adds a **By Date** group with one board per day on which you created media. Date boards are read-only views: you cannot upload to them, drop media on them, or rename or delete them. + * **Archived boards** — adds an **Archived** group listing the boards you have archived. + * **Other projects' boards** — shows the boards of your other projects, which are hidden by default to keep the list focused. The current project's board is always shown. +* **Sort by** — **Created** (the default) or **Name**, in descending or ascending order. **Uncategorized** and the current project's board always stay at the top. + +These choices are saved with the project, so each project can list boards differently. + +### Renaming, archiving, downloading and deleting boards + +Right-click a board, or hover over it and click its **⋯** button, to rename, archive, download or delete it. [Managing boards](/users-guide/gallery/gallery-reference/#managing-boards) in the Gallery Reference describes each action, including what deleting a board does to its media. + +## Finding media in a board + +Search, the starred filter and the sort order apply to the selected board and the current tab (Media or Uploads). + +### Searching by metadata + +Type into **Search items** to find media whose generation metadata contains the text, such as a word from the prompt, a model name or a seed. The search is not case-sensitive and matches the text exactly as typed, spaces included. Media without metadata, such as most uploads, can only be found by date. + +To limit results by date, add a date token. The **?** button in the search field shows the syntax: + +The Search syntax help, listing the from:, to: and date: tokens with examples. + +* `from:` — on or after a day, e.g. `from:2026-07-14` +* `to:` — on or before a day, e.g. `to:yesterday` +* `date:` — a single day, e.g. `date:today` + +Days can be written as `YYYY-MM-DD`, `today`, `yesterday`, or relative to today: `7d`, `2w` and `1m` mean 7 days, 2 weeks and 1 month ago. For example, `lighthouse from:2w` finds media from the last two weeks whose metadata mentions a lighthouse. A token that cannot be read is shown in red. + +### Searching by content + +When image indexing is turned on, the Gallery can also search by what media *shows*, whatever the prompt said: find "sunset on the beach" even in images that were never prompted with those words. This **semantic search** uses the same index as the [Image Map](/users-guide/image-map/introduction/). To turn indexing on, install the `DFN2B-CLIP-ViT-L-14-39B` model from the starter models and restart the backend; see [Activating the Image Map](/users-guide/image-map/introduction/#activating-the-image-map). When indexing is turned off, the semantic search button is not shown. + +A semantic search for "a wizard holding a torch", with the most similar images first. + +* **Search by text.** Click the sparkle button at the right end of the search field (**Search semantically**). The field changes to **Describe what to find…**; describe what you are looking for. Any text already in the field is used straight away. +* **Search by image.** Drag an image onto the search field: a thumbnail from the Gallery, an image from the Preview, an image file from your computer, or an image from another browser tab. The Gallery then shows the media most similar to it, labelled **Similar to …**. + +Results are ranked by similarity, most similar first, up to 500 items from the selected board. Videos are included. The Media and Uploads tabs, the sort order, the starred filter and date tokens do not apply to ranked results. + +To leave semantic search, click the **✕** in the search field, click the sparkle button again (**Exit semantic search**), or switch tabs. If the index model is not installed, the Gallery says so when you try to search. Archived boards are not indexed. + +### Sorting and the starred filter + +The sort menu orders media by creation time, **Newest** first (the default) or **Oldest** first. The star button next to it (**Starred only**) shows only starred items. Images and videos are always shown together. + +## Starring favorites + +Star an item to keep it at hand. Hover over a thumbnail and click the star in its corner, choose **Star image** (or **Star video**) from its menu, or select items and press **.** (period). Starred thumbnails keep a yellow star. + +Starred items are gathered in a **Starred** section at the top of the board, above the rest of the media, which can be collapsed. If there are more than fit, **Show all** switches on the **Starred only** filter. Starring also affects the board's cover, which is its newest starred item. + +## Uploading media + +Click the upload button at the right of the tab bar (**Upload media to …**) and choose files, or drag files from your computer onto the media grid. An empty board also shows **Drop media here or click to upload**. Uploads go to the selected board, on its **Uploads** tab. You cannot upload to a date board. + +You can upload: + +* **Images** — PNG, JPEG and WebP. +* **Videos** — stored and played as H.264/AAC MP4. H.264 video in another container, such as an iPhone `.mov` recorded as "Most Compatible", is repackaged almost instantly. Other formats, such as HEVC, ProRes, VP9 and `.wmv`, are converted, which takes roughly as long as the clip on a typical CPU. 10-bit HDR footage is converted to 8-bit without tone mapping, so it will look flatter than the original. +* **Audio** — MP3, M4A, WAV, FLAC, OGG and similar files become a video that shows the waveform. It behaves like any other video, for example as the soundtrack for [audio-to-video](/users-guide/video-generation/introduction/) generation. + +You can also paste an image or video from the clipboard while no text field has focus. A dialog asks whether to upload it to the Gallery, add it to the Canvas, or use it as a reference image. + +## Selecting, moving and deleting media + +Click an item to select it. **Shift**-click selects a range, and **Ctrl**-click (**Cmd**-click on macOS) adds or removes single items. **Ctrl+A** selects everything loaded on the page, and **Escape** clears the selection. + +While anything is selected, a bar at the bottom of the Gallery shows how many items are selected, with buttons to star them, move them to another board, download them, or delete them. + +Three selected images with the selection bar at the bottom of the Gallery showing 3 selected. + +### Moving media to another board + +Drag items onto a board in the board list; if the list is hidden, it opens while you drag. You can also use **Change Board** in an item's menu, or the move button in the selection bar. Moving never copies: each item is on one board at a time. **Remove from Board** in the **Change Board** menu moves an item to **Uncategorized**. + +### Deleting media + +Press **Delete** (or **Backspace**), choose **Delete Image**, **Delete Video** or **Delete Selection** from the menu, or use the delete button in the selection bar. Deleting permanently removes the items from every board and from disk, and cannot be undone. A confirmation asks first; you can turn it off with **Confirm image deletion** in Settings. + +Deleting does not check whether an image is still used elsewhere, for example on a canvas or as a reference image, so check before you delete media you are still working with. If deleting is interrupted, InvokeAI cleans up any leftover files the next time it starts. + +## Related pages + +* [Gallery Reference](/users-guide/gallery/gallery-reference/) — the board and media menus, Gallery settings and keyboard shortcuts. +* [Image Map](/users-guide/image-map/introduction/) — explore your whole gallery as a map of similar images. +* [Image Storage Maintenance](/users-guide/gallery/image-storage-maintenance/) — for administrators: reorganize how image files are stored on disk. +* [Intermediates](/users-guide/image-generation/intermediates/) — reclaim space used by temporary files that never reached the Gallery. diff --git a/docs/src/content/docs/Users Guide/07.Canvas/01.introduction.mdx b/docs/src/content/docs/Users Guide/07.Canvas/01.introduction.mdx new file mode 100644 index 00000000000..a54eda569b0 --- /dev/null +++ b/docs/src/content/docs/Users Guide/07.Canvas/01.introduction.mdx @@ -0,0 +1,178 @@ +--- +title: Crafting images with the Canvas +description: An introduction to the Canvas — layers and masks, sending generations to the Canvas, inpainting, outpainting, regional prompts, control layers, drawing and adding gallery images. +lastUpdated: 2026-09-30 +--- + +import { Image } from 'astro:assets'; +import { Steps } from '@astrojs/starlight/components'; +import editLayout from '../assets/canvas-edit-layout.webp'; +import addLayerMenu from '../assets/canvas-add-layer-menu.webp'; +import inpaint from '../assets/canvas-inpaint.webp'; +import outpaint from '../assets/canvas-outpaint.webp'; +import regional from '../assets/canvas-regional-guidance.webp'; +import controlLayer from '../assets/canvas-control-layer.webp'; +import inpaintResult from '../assets/canvas-inpaint-result.webp'; + +The **Canvas** is where you build and refine an image in layers. You can paint, repaint part of an image (inpainting), extend it beyond its edges (outpainting), guide different regions with their own prompts, and steer the composition with control images, generating as many times as you like and keeping only the results you want. + +The Canvas lives in the **Edit** layout (**Alt+2**): the Canvas is in the center, the **Generate** panel on the left supplies the model, prompt and settings, and the **Layers** panel on the right lists the canvas's layers and their settings. + +The Edit layout: the Generate panel on the left, the Canvas with a mountain landscape in the center, and the Layers panel with its color picker, layer list and Properties on the right. + +This page introduces the main ideas and tasks. The [Canvas guide](/users-guide/canvas/canvas-guide/) and the [Layers panel guide](/users-guide/canvas/layers-panel/) describe every tool, menu and setting. + +## Canvas concepts + +### Layers + +A canvas is a stack of layers. Each layer has a type, which decides what it contributes to a generation: + +| Layer type | What it is for | +|---|---| +| **Raster layer** | Ordinary image content: pictures, painting, shapes, gradients and text. The visible raster content inside the generation frame is the starting image for a generation. | +| **Inpaint mask** | Marks the area to regenerate. Everything outside the mask is kept. | +| **Regional guidance** | Marks an area that gets its own prompt, and optionally its own reference image, so different parts of the image can be described differently. | +| **Control layer** | Holds a guide image, such as edges, a depth map or a pose, that a control model uses to shape the composition. | + +The Layers panel groups layers by type, in the stacks **Inpaint Masks**, **Regional Guidance**, **Control Layers** and **Raster Layers**. Within a stack, higher layers are drawn over lower ones. Add a layer with the **+** button above the layer list: + +The Add layer menu with Inpaint mask, Regional guidance, Regional reference image, Control layer, Raster layer and New group. + +Each layer row has a dot on the left that switches the layer on or off for generation. Masks, regional guidance and control layers also have an eye button, which only hides them from view: a hidden mask still affects generation. **Shift+H** hides all of them at once so you can see the image underneath. + +### Settings live in the Properties pane + +Each layer's settings, such as a regional prompt or a control layer's model, are in the **Properties** tab at the bottom of the Layers panel. Select the layer and click **Properties**: the heading at the top of the tab confirms which layer you are editing, for example *Layer: Regional Guidance 1*. The settings are in its **Layer** section. The Layer section comes after the settings of the active tool, so **you may need to scroll the Properties pane down to see it**. Dragging the divider above the Properties pane upward, or collapsing the color picker, gives it more room. + +### The generation frame + +The dashed rectangle on the canvas is the **generation frame**, also called the bounding box. Only the area inside it is generated: what lies inside is the model's starting point, and the result is placed back in the same spot. The frame's size is the generation size, and it stays in step with the **Size** section of the Generate panel. Move and resize it with the **Generation frame** tool (press **C**). See [The generation frame](/users-guide/canvas/canvas-guide/#the-generation-frame) for details. + +### What Invoke does + +When the Canvas is the source of a generation, InvokeAI looks at what is inside the generation frame and chooses the kind of generation for you: + +| Inside the frame | Generation | +|---|---| +| No visible raster content | **Text to image**: a new image from the prompt alone. | +| Raster content covering the whole frame, no inpaint mask | **Image to image**: the content is repainted. **Denoising strength** in the Generate panel sets how much it changes. | +| Raster content covering the frame, and an inpaint mask | **Inpainting**: only the masked area is regenerated. | +| Raster content that covers only part of the frame | **Outpainting**: the empty part is filled in, blending into the existing content. | + +## Sending generations to the Canvas + +In the Edit layout, **Invoke** uses the Canvas as its source and sends the results back to the Canvas. The routing control next to Invoke shows this. Editing the canvas switches the route back to the Canvas automatically, unless the routing is locked. See [Routing images to the Gallery or the Canvas](/users-guide/image-generation/introduction/#routing-images-to-the-gallery-or-the-canvas). + +When the source is the Canvas, the Generate panel adds three sections at the bottom: + +* **Denoising strength** — how much image-to-image, inpainting and outpainting may change the existing content. The default is 75%. +* **Scale before processing** — generate at a different size, for example the model's optimal size, and scale the result back to the frame. +* **Compositing** — how inpainted and outpainted areas are filled and blended: **Infill method**, **Coherence pass mode**, **Coherence edge size**, **Coherence min denoise** and **Mask blur**. + +Results do not go straight onto the canvas. They appear in the **staging area**, a bar at the bottom of the canvas where you can step through the candidates, then **Accept to Layer** the one you want (it becomes a new raster layer), **Save to gallery**, or **Discard**. While results are staged, the canvas tools are locked except for **View**. + +## Inpainting + +Inpainting regenerates part of an image and leaves the rest untouched: remove an object, fix a face, or change what is in a corner. + +An inpaint mask, shown as a red overlay, painted over a rock in a mountain landscape. + + + +1. **Add an inpaint mask**, or select the one a new canvas starts with, in the **Inpaint Masks** stack. + +2. **Paint the mask.** Choose the **Brush** (**B**) and paint over the area to regenerate, or draw it with the **Shape** tool (**R**). The mask shows as a tinted overlay. The **Eraser** (**E**) removes mask, and **[** and **]** change the brush size. + +3. **Describe the result** in the Generate panel's prompt: describe the whole picture as it should look, or just what should appear in the masked area. + +4. **Press Invoke**, then pick a result in the staging area and click **Accept to Layer**. + + + +Inpainting a plate of sushi: on the left, an inpaint mask over one piece; on the right, the result in the staging area, where that piece has become salmon-roe sushi, ready for Accept to Layer. + +**Denoising strength** controls how much the masked area may change: lower values keep its colors and shapes, higher values replace it. Two optional modifiers, added from the mask's right-click menu, give finer control: **Add noise** adds noise inside the mask before generating, and **Add denoise limit** caps how strongly it can be repainted. The mask's **Invert mask** button, in its Properties, swaps the masked and unmasked areas. + +## Outpainting + +Outpainting extends an image beyond its edges. + +The generation frame extended to the right of the landscape image, leaving an empty area to be filled. + + + +1. **Choose the Generation frame tool** (press **C**). + +2. **Extend the frame** past the edge of the image by dragging its handles, or type a new width and height in the **Frame** section of the Properties pane. Part of the frame now covers empty canvas. + +3. **Press Invoke.** The empty area is filled first, using the **Infill method** in the Generate panel's **Compositing** section, and then generated so that it blends into the existing image. + + + +Include some of the existing image in the frame, since the model uses it to continue the scene. You can combine outpainting with an inpaint mask to regenerate part of the existing image at the same time. + +## Regional prompts and reference images + +Regional guidance gives one area of the image its own prompt. For example, keep the main prompt for the landscape and add birds only in the sky. + +A regional guidance layer, shown in green over the sky, with the prompt "a flock of birds soaring over the peaks" in the Layer section of the Properties pane. + + + +1. **Add a Regional guidance layer** with **+ → Regional guidance**. + +2. **Paint the region** with the **Brush**, or draw it with the **Shape** tool. It shows as a tinted overlay. + +3. **Enter the region's prompt.** With the layer selected, open **Properties** and scroll down to its **Layer** section. Type in **Positive prompt**, and optionally **Negative prompt**. The prompt is saved when you click away from the field. + +4. **Press Invoke.** The main prompt still describes the whole image; the regional prompt applies inside the region. + + + +To guide a region with an image instead of, or as well as, words, choose **Add reference image** from the regional guidance layer's right-click menu, or add a **Regional reference image** layer from the **+** menu. The reference appears as a row under the layer; select that row to choose its image, model and weight in Properties. + +Regional guidance works with SD 1.5, SDXL, FLUX.1, FLUX.2, Krea-2, Z-Image and Anima. Regional negative prompts are available for SD 1.5 and SDXL, and regional reference images for SD 1.5, SDXL (IP-Adapter) and FLUX.1 (FLUX Redux). With other models, regional layers are skipped, and the layer's Properties say so. For a reference image that applies to the whole picture, use **Add reference image** in the Generate panel instead. + +## Control layers + +A control layer shapes the composition with a guide image: the edges of a sketch, the depth of a scene, or a person's pose. A control model makes the generated image follow it. + +A control layer's settings in the Layer section of Properties: Adapter type ControlNet, Model Depth Map, and Weight 0.75. + + + +1. **Put the guide image on a control layer.** A control layer's image is its own pixels; there is no separate image picker. Do one of these: + * Right-click a raster layer and choose **Copy to → Copy to control layer**, or **Convert to → Convert to control layer**. + * Drag an image from the Gallery onto the canvas and drop it on **Add as Control Layer** (see [below](#adding-a-gallery-image-to-a-layer)). + * Add an empty **Control layer** from the **+** menu and draw on it. + +2. **Choose the model.** Select the layer, open **Properties** and scroll to its **Layer** section. Choose the **Adapter type** (ControlNet, T2I Adapter, Control LoRA or Z-Image ControlNet) and the **Model**. A compatible installed model is chosen for you when the layer is created. + +3. **Process the image, if needed.** Most control models expect a processed image, such as an edge map or a depth map, rather than a photograph. Choosing a model starts its recommended **Filter** automatically; you can also click **Filter**, pick a filter, and **Apply** it. + +4. **Adjust the influence** with **Weight** (how strongly the guide is followed), **Step range** (which part of the generation it applies to) and, for ControlNet, **Control mode**. Then press Invoke. + + + +Control layers work with SD 1.5, SDXL, FLUX.1 and Z-Image, and need a control model for the same family, installed from the Model Manager's starter models. If the **Model** list is empty, no compatible model is installed for the model selected in the Generate panel. A control layer without a model stops Invoke with the message *Select a control model before generating.* + +## Drawing on a raster layer + +You can paint directly on the canvas, for example to sketch a composition for image-to-image, or to touch up a result. + +1. **Select a raster layer** in the Layers panel, or add one with **+ → Raster layer**. If you start painting with no suitable layer selected, a new raster layer is created for you. +2. **Pick a color** in the color picker at the top of the Layers panel. Its two chips are the foreground color, used by the brush, and the background color. **X** swaps them and **D** resets them to black and white. Hold **Alt** to pick a color from the canvas. +3. **Choose the Brush** (**B**) and paint. Its **Size**, **Opacity** and **Hardness** are in the Properties pane; **[** and **]** change the size. The **Eraser** (**E**) erases. + +The tool strip also has shape, gradient and text tools, which are described in the [Canvas guide](/users-guide/canvas/canvas-guide/). + +## Adding a gallery image to a layer + +There are three ways to bring an existing image onto the canvas: + +* **Drag it from the Gallery.** With the Gallery and the Canvas both on screen (for example, the Compose layout with its center view switched to Canvas), drag an image onto the canvas. Drop zones appear: **Add as Raster Layer**, **Add as Control Layer**, **Add as Regional Reference Image**, **Add as Inpaint Mask** and **Add as Resized Control Layer**, which scales the image to the model's optimal size. +* **Use the image's menu.** Right-click an image in the Gallery or Preview and choose **New from Image → New Layer from Image**, then the kind of layer. **New from Image → New Canvas from Image** instead starts a new project whose canvas holds the image, sized to fit it. +* **Paste it.** Copy an image, click the canvas and press **Ctrl+V** (**Cmd+V** on macOS). It is added as a new raster layer at the generation frame. + +A new raster layer from an image is placed at its full size at the top-left of the generation frame. Use the **Move** (**V**) or **Transform** (**Ctrl+T**) tool to position and scale it. diff --git a/docs/src/content/docs/Users Guide/07.Canvas/02.canvas-guide.mdx b/docs/src/content/docs/Users Guide/07.Canvas/02.canvas-guide.mdx new file mode 100644 index 00000000000..687f7e515e3 --- /dev/null +++ b/docs/src/content/docs/Users Guide/07.Canvas/02.canvas-guide.mdx @@ -0,0 +1,215 @@ +--- +title: Canvas guide +description: A detailed guide to the Canvas — its tools and their options, navigation, the header buttons, the generation frame, selections, transform, filters, the staging area, menus, settings and keyboard shortcuts. +lastUpdated: 2026-09-30 +--- + +import { Image } from 'astro:assets'; +import toolbar from '../assets/canvas-toolbar.webp'; +import frameProps from '../assets/canvas-frame-properties.webp'; +import contextMenu from '../assets/canvas-context-menu.webp'; +import canvasSettings from '../assets/canvas-settings.webp'; + +This page describes the Canvas itself: its tools, controls and menus. For an introduction to working with the Canvas, see [Crafting images with the Canvas](/users-guide/canvas/introduction/). The Layers panel, where layers and their settings are managed, has its own [guide](/users-guide/canvas/layers-panel/). + +Each project has one canvas. It opens in the center of the **Edit** layout (**Alt+2**), or press **2** to bring it to the front. + +The Canvas with its tool strip on the left and the header buttons along the top: zoom, fit buttons, undo and redo, save, new canvas, settings and more. + +## Tools + +The tool strip on the left edge of the canvas holds the drawing and editing tools. A tool's options appear in the **Tool** section of the **Properties** tab in the Layers panel, not on the canvas. + +| Tool | Key | What it does | +|---|---|---| +| **View** | **H** | Pan by dragging. Hold **Space** to pan from any tool. | +| **Move** | **V** | Move the selected layers. **Shift** locks the direction. Dragging inside a selection lifts the selected pixels so you can move them; **Enter** places them. | +| **Transform** | **Ctrl+T** | Scale and rotate the selected layer with handles. See [Transforming layers](#transforming-layers). | +| **Generation frame** | **C** | Move and resize the generation frame. Tap **C** to switch to it, or hold **C** to use it briefly. | +| **Brush** | **B** | Paint on the selected raster layer, or paint a mask or region on the selected inpaint mask or regional guidance layer. | +| **Eraser** | **E** | Erase paint or mask. | +| **Shape** | **R** | Draw a rectangle, ellipse, triangle, star, polygon or freehand shape on a raster layer, or add a shaped area to the selected inpaint mask or regional guidance layer. Press **R** again to cycle through the shapes. | +| **Gradient** | **G** | Create a linear or radial gradient. | +| **Text** | **T** | Add editable text. | +| **Marquee select** / **Lasso select** | **U** / **L** | Select an area of pixels. Press **U** again to switch between rectangle and ellipse, or **L** again for freehand and polygon. | + +The Shape and Select buttons hold several tools. Click to use the current one, or press and hold (or right-click) the button to choose another. To pick a color from the canvas, hold **Alt** with any painting tool and click. While you pick, a round **loupe** follows the pointer: it magnifies the pixels around it, boxes the pixel that will be picked in the center, and shows that pixel's RGB values. The pointer itself is hidden, so aim with the boxed pixel. You can drag to sample as you go; the color is set when you release. + +### Tool options + +* **Brush** — **Size**, **Opacity** and **Hardness**; the **Foreground** color; and, under **Dynamics**, whether pen pressure affects the width or the opacity. **[** and **]** make the brush smaller or larger, and so does **Ctrl**+scroll. +* **Eraser** — **Size**, **Opacity** and **Hardness**. +* **Shape** — the **Kind** of shape; **Place on**, which draws on the **Selected layer** or on a **New layer** of its own; **Fill** and **Stroke**, each with a color; and the stroke **Width**. **Shift** constrains the proportions. For polygons, click to place points and double-click, press **Enter** or click the first point to close the shape. With an inpaint mask or regional guidance layer selected, the shape is added to the mask or region, which makes shapes a quick way to mark rectangular or geometric areas; the colors are ignored. +* **Gradient** — **Kind** (Linear or Radial), **Angle**, and the color **Stops**, which can be added and removed or follow the foreground and background colors. Dragging creates a gradient layer the size of the generation frame, or repositions the selected gradient layer. +* **Text** — the font **Family**, **Style** or **Weight**, **Size**, **Line height**, **Align** and **Fill** color. Click the canvas to start typing; **Ctrl+Enter** or clicking elsewhere finishes the text, and **Escape** cancels it. The text stays editable as a text layer. +* **Marquee select** and **Lasso select** — the **Shape** and the selection **Mode** (Replace, Add, Subtract or Intersect). **Shift** adds to the selection and **Alt** subtracts from it. The **Actions** fill or erase the selection, lift it to a **New layer**, **Invert** it, select everything, or **Deselect**. + +### Fonts + +The Text tool offers built-in font families, plus any fonts you add. To add fonts, open **Fonts** in the Launchpad and use **Add Fonts** to upload `.ttf`, `.otf`, `.woff` or `.woff2` files. Uploaded fonts are private to your account (**My fonts**); administrators can also upload shared fonts for everyone on the server. Select a font in the library to preview it and see its variation axes and named instances. Variable fonts expose those axes in the Text tool's options. + +Fonts placed in the server's fonts directory are also available to everyone. It is `fonts` in the InvokeAI root folder by default and can be changed with `fonts_dir` in `invokeai.yaml`. It may contain subfolders. Refresh the app after adding or removing files there. + +If a project uses a font that is not installed, a dialog lets you retry, upload the font, or choose a replacement. You can keep editing with a marked fallback, but generating or saving the affected text needs the font. When you export a project, **Include font files** bundles the fonts it uses, if you have the right to redistribute them. + +Selections restrict painting: while an area is selected, brush strokes stay inside it. **Ctrl+A** selects all, **Ctrl+D** deselects and **Ctrl+Shift+I** inverts the selection. + +:::note[Changed from v6] +In InvokeAI 6, the Lasso drew directly into an inpaint mask, and created one if needed. In v7, the Lasso and Marquee make pixel selections on any layer instead, and the selection's **Fill** and **Erase** actions apply to raster layers only. To mask an irregular area, select it with the Lasso, select the inpaint mask or regional guidance layer, and paint over the area with a large Brush: the strokes stay inside the selection. +::: + +## Navigating the canvas + +* **Zoom** with the mouse wheel, which zooms around the pointer, or choose a level from the zoom menu in the header (25% to 500%). +* **Pan** with the View tool, by holding **Space**, or by dragging with the middle mouse button. +* **Fit to view** (header) fits the generation frame and all layers into the window. + +## The header + +The buttons along the top of the canvas, from left to right: + +* **Zoom level** and **Fit to view**. +* **Fit frame to layers** (**Shift+N**) — resizes the generation frame to enclose all layers. +* **Fit frame to masks** (**Shift+B**) — resizes the generation frame to enclose the inpaint masks. +* **Undo canvas edit** (**Ctrl+Z**) and **Redo canvas edit** (**Ctrl+Shift+Z** or **Ctrl+Y**). +* **Save Canvas to Gallery** — saves the visible canvas as an image on the Gallery's selected board. Its arrow offers **Save BBox to Gallery**, which saves only what is inside the generation frame. +* **New session → New canvas** — clears all layers and starts a fresh canvas with an empty inpaint mask. This cannot be undone. +* **Canvas settings** — see [below](#canvas-settings). + +## The generation frame + +The generation frame is the area that is generated. With the **Generation frame** tool selected, drag inside the frame to move it and drag its handles to resize it. **Shift** keeps the proportions, **Ctrl** resizes from the center, and **Alt** turns off snapping to the model's grid. + +The Generation frame tool's options: Position X and Y, and the Frame's Size and Aspect with a lock button. + +The tool's options show the frame's **Position** and **Size**, and an **Aspect** menu for common aspect ratios, with a lock. The frame's size is the generation size, so it matches the **Size** section of the Generate panel, and it snaps to the sizes the selected model supports. + +### What the model sees + +When you press **Invoke**, the enabled raster content inside the frame is combined into the image the model starts from. Content outside the frame is not included, even though it remains visible on the canvas. Control layers and regional guidance are likewise read only inside the frame. + +This makes placement important. If the model needs to continue a shape, match a texture or keep part of a subject, include enough of it inside the frame to give the model context. Moving the frame changes which part of the image the next generation can use. + +The frame is not a mask: everything inside it can change in image-to-image. To regenerate only part of the area, add an [inpaint mask](/users-guide/canvas/introduction/#inpainting). + +### Frame size and processing size + +The frame is the final footprint of the generation on the canvas. With **Scale before processing** (in the Generate panel's canvas sections) set to **Auto** or **Manual**, InvokeAI generates at a different resolution, such as the model's optimal size, and then resizes the result to fit the frame. Choose a frame large enough to include the context the model needs, while keeping it within a size the model and your GPU memory handle well. + +## Transforming layers + +Choose **Transform** from a layer's menu, or press **Ctrl+T**, to scale and rotate the selected layer: + +* Drag a corner or edge handle to scale. **Shift** scales proportionally and **Alt** scales from the center. +* Drag just outside a corner to rotate. **Shift** rotates in 15° steps. +* Drag inside to move. + +Press **Enter** or click **Apply** to finish, or **Escape** or **Cancel** to undo the transform. The **Transform** tab of the Layers panel sets the position, scale and rotation numerically. A negative scale flips the layer. + +To crop a layer to the generation frame, choose **Crop layer to bbox** from its menu. **Fit to bbox** scales a layer to fit inside the frame. + +## Filters and object selection + +**Filter**, in a layer's menu or its Properties, processes a raster or control layer. Starting it opens the Layers panel's **Properties** tab, headed *Operation: Filter*, where you choose and preview the filter. The filters are grouped by purpose: + +* **Edge detection** — Canny, HED, Lineart, Lineart Anime, MLSD, PiDiNet and Scribble. +* **Depth & normal** — Depth Anything and Normal map. +* **Pose & face** — DW Openpose and Mediapipe Face. +* **Color & tone** — Color map and Adjust image. +* **Blur & noise** — Content shuffle, Blur and Noise. +* **Upscale** — an upscaling model. + +Choose a filter and its settings in Properties, click **Process** (or turn on **Process automatically**) to preview the result, then **Apply** it. The arrow next to Apply offers **Apply as** a new raster or control layer instead. Edge, depth and pose filters produce the guide images that control layers need. + +**Select object**, in a layer's menu, finds an object automatically with a segmentation model. Its settings open in the **Properties** tab, headed *Operation: Select Object*. Click the object to add points (Shift-click marks areas to exclude), or describe it by text, then **Process** and **Apply**, or save the result as a selection or a new layer. + +## Running a workflow on a layer + +**Run workflow**, in a raster or control layer's menu, processes the layer with the project's active workflow, the one open in the workflow editor. In the **Run workflow from layer** dialog, choose: + +* **Workflow image input** — the workflow's image field that receives the layer. +* **Workflow image output** — the node whose image is the result. +* **Destination** — **Canvas staging**, **Replace layer**, **Copy as raster layer**, or **Gallery**. + +Then click **Run**. If the workflow has no image input and output it can connect, the dialog says so. The layer must be enabled, unlocked and not empty. + +## The staging area + +When the Canvas is the destination, results appear in the staging area at the bottom of the canvas, not directly on it. The staging bar lets you: + +* Step through the results with the arrows, or with **←** and **→**, and show or hide the result thumbnails. +* **Accept to Layer** — add the current result as a new raster layer and close the staging area. The arrow next to it offers **Keep as Hidden Layer**, which adds the result as a disabled layer and keeps staging open. +* **Save to gallery** the current result, **Discard** it (**Delete**), or **Discard All**. +* Hide the preview of the current result to compare it with the canvas underneath. +* **Auto-switch** — show each new result as it arrives (**Newest**), follow the one in progress (**In progress**), or stay on the current one (**Off**). +* **Cancel** the running generation. + +Right-click a result's thumbnail to recall its settings into the Generate panel. While results are staged or a canvas generation is running, only the View tool works. + +## The canvas menu + +Right-click the canvas to open the menu of the selected layer, the same menu as in the Layers panel (see the [Layers panel guide](/users-guide/canvas/layers-panel/#the-layer-menu)), with two extra submenus: + +The canvas right-click menu for a raster layer, with the layer actions followed by a Canvas section with Save to Gallery and Create From BBox. + +* **Save to Gallery** — **Save Canvas to Gallery** or **Save BBox to Gallery**. +* **Create From BBox** — copy what is inside the generation frame into a **New Global Reference Image**, **New Regional Reference Image**, **New Control Layer** or **New Raster Layer**. + +With no layer selected, the menu has only these two submenus. + +## Canvas settings + +The gear button in the header opens the canvas settings. They are saved with the project. + +The Canvas settings popover with Behavior, Display and Grid options. + +* **Behavior** + * **Invert scroll for brush size** — reverses **Ctrl**+scroll brush sizing. + * **Clip strokes to bbox** — keeps painting inside the generation frame. + * **Output only generated regions** — when on, an inpainting or outpainting result contains only the regenerated area, with the rest transparent; when off, it includes the whole frame. +* **Display** + * **Show progress on canvas** — shows the image forming while it generates. + * **Show bbox overlay** — dims everything outside the generation frame. + * **Show generation frame** — shows the frame's dashed outline. + * **Checkerboard background** — shows transparent areas as a checkerboard. +* **Grid** + * **Show grid** — shows the model's size grid. + * **Snap to grid** — snaps the generation frame, moves and transforms to the grid. Hold **Alt** to override it. + * **Rule of thirds** — shows composition guides inside the frame. + +## Saving and sharing a canvas + +The canvas belongs to its project and is saved with it automatically. To keep a finished image, use **Save Canvas to Gallery** or **Save BBox to Gallery**, or accept results and save individual layers with **Save layer to uploads**. To share a whole canvas, with its layers, masks and settings, export the project as an `.invk` file; see [Projects](/users-guide/projects/introduction/#exporting-and-importing). + +## Copying and pasting + +* **Ctrl+C** copies the selected pixels, and **Ctrl+X** cuts them. +* **Ctrl+V** pastes an image from the clipboard as a new raster layer at the generation frame. +* **Ctrl+J** duplicates the selected layer, or copies the selected pixels to a new layer. + +## Keyboard shortcuts + +These shortcuts work while the canvas has focus. On macOS, use **Cmd** in place of **Ctrl**. + +| Shortcut | Action | +|---|---| +| **H**, **V**, **Ctrl+T**, **C**, **B**, **E**, **R**, **G**, **T**, **U**, **L** | Select a tool (see [Tools](#tools)) | +| Hold **Space** | Pan temporarily | +| Hold **Alt** | Pick a color | +| **[** / **]** | Smaller / larger brush or eraser | +| **X** / **D** | Swap colors / reset colors to black and white | +| **Ctrl+Z** / **Ctrl+Shift+Z**, **Ctrl+Y** | Undo / redo | +| **Ctrl+A** / **Ctrl+D** / **Ctrl+Shift+I** | Select all / deselect / invert the selection | +| **Ctrl+C** / **Ctrl+X** / **Ctrl+V** | Copy / cut / paste | +| **Delete** | Erase the selection, or delete the selected layer | +| **Shift+C** | Clear the selected mask | +| Arrow keys (**Shift**: 10 px) | Nudge the selected layer | +| **Ctrl+J** / **Ctrl+E** | Duplicate layer / merge layer down | +| **Ctrl+G** / **Ctrl+Shift+G** | Group / ungroup layers | +| **Ctrl+]** / **Ctrl+[** (add **Shift** for front / back) | Move layer forward / backward | +| **Alt+[** / **Alt+]** | Previous / next layer, or staged result | +| **Shift+H** | Hide control, guidance and mask layers | +| **Shift+N** / **Shift+B** | Fit the generation frame to the layers / to the masks | +| **Enter** / **Escape** | Apply / cancel a transform, text or filter | + +Some canvas commands listed in **Settings → Hotkeys** are marked *Pending*; they are not available yet. diff --git a/docs/src/content/docs/Users Guide/07.Canvas/03.layers-panel.mdx b/docs/src/content/docs/Users Guide/07.Canvas/03.layers-panel.mdx new file mode 100644 index 00000000000..4a54244c2e9 --- /dev/null +++ b/docs/src/content/docs/Users Guide/07.Canvas/03.layers-panel.mdx @@ -0,0 +1,179 @@ +--- +title: Layers panel guide +description: A detailed guide to the Canvas Layers panel — the color picker, managing layers, history, the layer menu, and every layer type's settings in the Properties pane. +lastUpdated: 2026-09-30 +--- + +import { Image } from 'astro:assets'; +import layersPanel from '../assets/layers-panel.webp'; +import layersContext from '../assets/layers-context-menu.webp'; +import history from '../assets/layers-history.webp'; + +The **Layers** panel sits to the right of the Canvas in the **Edit** layout. It has three parts, from top to bottom: + +1. **Color** — the color picker and swatches used by the painting tools. +2. **Layers** and **History** — the canvas's layers, or its undo history. +3. **Properties**, **Transform** and **Overview** — the settings of the active tool and of the selected layer. + +Drag the dividers between the parts to resize them, and use the chevron at the right of the Color and Properties tabs to collapse them. The sizes are saved with the project. + +The Layers panel: the color wheel at the top, the layer list grouped into Inpaint Masks, Regional Guidance, Control Layers and Raster Layers, and the Properties pane showing a control layer's Adapter type, Model and Weight. + +For an introduction to the layer types, see [Crafting images with the Canvas](/users-guide/canvas/introduction/#canvas-concepts). + +## Color + +The **Color** tab edits the two painting colors: the **Foreground color**, which the brush and text use, and the **Background color**. Click a chip to choose which one you are editing. + +* The color wheel, or a box picker via **Switch to box picker**, and a field for the value. The format button cycles through hex, RGB, HSL and HSB. +* **Swap foreground and background colors** (**X**) and **Reset colors to black and white** (**D**). +* **Pick a color from the canvas** — the eyedropper. You can also hold **Alt** on the canvas. A magnifying loupe shows the pixel under the pointer and its RGB values; see the [Canvas guide](/users-guide/canvas/canvas-guide/#tools). +* When an inpaint mask or regional guidance layer is selected, a third chip, **Mask tint**, edits the color of its overlay. + +The **Swatches** tab has built-in **Swatches**, your **Recent colors**, and the **Project palette**: click **+** (**Add current color to palette**) to save a color with the project, and right-click a swatch to remove it. + +## Layers + +### The layer list + +Layers are grouped into four stacks: **Inpaint Masks**, **Regional Guidance**, **Control Layers** and **Raster Layers**. Within a stack, higher layers are drawn over lower ones. Click a stack's header to collapse it. Its buttons add a **New layer** of that type and, for masks, regional guidance and control layers, **Hide all** or **Show all**. The Raster Layers header also has **Merge visible** and **Export to PSD**. + +Each row shows, from left to right: + +* A colored stripe, if you gave the layer a color label. +* A dot that turns the layer on or off (**Enable/disable layer**). A disabled layer takes no part in generation. +* A thumbnail, the layer type and the name. +* An eye button (**Show/hide layer**) on masks, regional guidance and control layers. It only hides the layer from view; a hidden layer still affects generation. +* A lock button (**Toggle lock**), which prevents editing. + +Rows can have child rows, shown with a chevron: adjustments and regenerate regions on raster layers, noise and denoise limits on inpaint masks, and reference images on regional guidance layers. Select a child row to edit it in Properties. + +### Working with layers + +* **Add** a layer with the **+** button next to the Layers and History tabs, or by right-clicking empty space in the list. The new layer is placed above the selected one and selected. +* **Select** a layer by clicking it. **Ctrl**-click adds or removes layers from the selection, and **Shift**-click selects a range. +* **Rename** a layer by double-clicking its name or pressing **F2**. **Enter** saves and **Escape** cancels. +* **Reorder** layers by dragging them, or with **Alt+↑** and **Alt+↓**. Layers can only move within their own stack. +* **Blend mode** and **Opacity**, in the row above the list, apply to the selected layer. There are 16 blend modes, from Normal and Multiply to Color and Luminosity. +* **Group** layers into folders with **Group selected layers**, or **Ctrl+G** on the canvas. **Alt+→** moves a layer into the group above it and **Alt+←** moves it out. A group's dot, eye and lock apply to everything in it. +* **Filter layers** at the bottom of the list shows only the layers whose names match. +* The buttons beside the filter **Duplicate selected layers**, **Group selected layers** and **Delete selected layers**. + +## History + +The **History** tab lists the canvas's edits, oldest first, with the current step highlighted and undone steps in italics. Click a step to go back or forward to it, or use the **Undo** and **Redo** buttons (**Ctrl+Z**, and **Ctrl+Shift+Z** or **Ctrl+Y**). + +The History tab listing the canvas's recent edits, such as Brush stroke, Regional guidance and Positive prompt. + +The history keeps the last 64 steps. It lasts only while the project is open in your browser: reloading the page, or starting a new canvas, clears it. + +## The layer menu + +Right-click a layer, or select it and press **Shift+F10**, for its menu. Right-clicking the canvas opens the same menu for the selected layer. + +The menu of a raster layer, with Transform, Rename layer, Fit to bbox, Add adjustment, Add regenerate region, Filter, Select object, Merge down, Copy to, Convert to, Crop layer to bbox, Save layer to uploads, Disable layer, Lock layer, Color label and Delete layer. + +The icons at the top **Arrange** the layer (move it to the front, forward, backward or to the back), **Duplicate layer** and **Group layers**. Below them: + +* **For every layer:** + * **Rename layer** and **Fit to bbox** (scale the layer to fit inside the generation frame). + * **Merge down**. + * **Copy to** — including **Copy layer to clipboard**, and copies to other layer types. + * **Crop layer to bbox** and **Save layer to uploads** (save it to the Gallery's Uploads). + * **Disable layer**, **Lock layer** and **Color label**. + * **Delete layer**. +* **Raster layers:** + * **Transform**. + * **Add adjustment** — Brightness/Contrast, Exposure, Levels, Curves, Saturation, Hue or Invert. Adjustments are non-destructive and appear as child rows. + * **Add regenerate region** — treats the layer's own shape as an inpaint mask. + * **Filter**, **Select object** and **Run workflow** — process the layer; see the [Canvas guide](/users-guide/canvas/canvas-guide/#filters-and-object-selection). + * **Boolean operations** — combine the layer with the one below: intersect, cut out, cut away or exclude. + * **Convert to** — make it a control layer, inpaint mask or regional guidance layer. Text, shape and gradient layers can also be rasterized into plain pixels. +* **Control layers:** **Transform**, **Filter**, **Select object**, **Run workflow**, **Enable transparent preview**, and **Convert to raster layer**. +* **Inpaint masks:** **Add noise**, **Add denoise limit**, **Extract masked area** (copy the masked part of the image to a new layer), and **Hide layer**. +* **Regional guidance:** **Enable auto-negative**, **Add reference image**, and **Hide layer**. + +When several layers are selected, the menu also offers **Merge selected layers**. + +## Properties + +A heading at the top of the **Properties** tab names what it is editing: **Layer:** or **Group:** followed by the selected layer's or group's name, **Operation:** followed by the operation's name while a Filter or Select Object operation runs, or *No layer selected*. It stays in view as you scroll, so you can always tell which layer the settings below belong to. + +Below the heading, the tab shows up to three sections, in this order: + +1. The running operation's settings — while a **Filter** or **Select Object** operation is running. Starting either from a layer's menu opens the Layers panel on this tab. +2. **Tool** — the options of the active canvas tool; see the [Canvas guide](/users-guide/canvas/canvas-guide/#tool-options). +3. **Layer** — the settings of the selected layer. + +:::tip[Scroll down for the layer's settings] +The **Layer** section comes after the tool options, so on most screens it starts below the visible part of the pane. Scroll the Properties pane down to reach it, or drag the divider above the pane upward to make it taller. +::: + +The Layer section is dimmed while results are being staged or a canvas generation is running. + +### Raster layer + +* **Lock transparency** — paint only where the layer already has pixels, leaving transparent areas transparent, like Photoshop's "Lock transparent pixels". Use it to recolor a shape without spilling paint around it, or to refine a subject painted on a transparent layer without growing its outline. Brushes, shapes and pen input all respect the lock. +* **Filter** — start the Filter operation. + +Adjustment child rows each have their own settings, such as **Brightness** and **Contrast**, **Exposure**, **Levels** and **Curves** with a channel selector, **Saturation**, and **Hue**. A regenerate region has a **Fill color** and **Fill style**. + +### Control layer + +* **Adapter type** — ControlNet, T2I Adapter, Control LoRA or Z-Image ControlNet, depending on the model selected in the Generate panel. +* **Model** — the installed control models of that type for the same model family. If the list is empty, install one from the starter models. +* **Weight** — how strongly the guide is followed (0 to 2; the default is 0.75 for ControlNet). +* **Step range** — the part of the generation, from start to finish, in which the guide applies. +* **Control mode** (ControlNet only) — **Balanced**, **More prompt**, **More control** or **Unbalanced**. +* **Transparent preview** — shows the layer semi-transparently over the image. +* **Filter** — process the layer's image into a guide, such as an edge or depth map. + +The control image is the layer's own pixels. To use a picture, copy or convert a raster layer, or drop a gallery image on **Add as Control Layer**. + +### Inpaint mask + +* **Fill color** and **Fill style** (Solid, Grid, Crosshatch, Diagonal, Horizontal or Vertical) — how the mask is drawn on screen. +* **Invert mask** — swap the masked and unmasked areas. + +Its child rows, added from the layer menu, set **Noise** (noise added inside the mask before generating, 25% by default) and **Denoise limit** (the most the masked area may change, 80% by default). + +### Regional guidance + +* **Positive prompt** and **Negative prompt** — the prompts for this region. Each is saved when you click away from the field. The negative prompt is shown only for models that use one. +* **Auto-Negative** — also uses the region's positive prompt as a negative prompt outside the region. +* **Fill color**, **Fill style** and **Invert mask**, as for inpaint masks. + +If the selected model does not support regional guidance, a message says that the layer is skipped during generation. + +### Regional reference image + +A regional guidance layer's reference image is a child row. Select it to set: + +* The image: **Choose image** from the Gallery, drop a gallery image on it, or **Upload** a file. +* **Model** — an IP-Adapter (SD 1.5, SDXL) or FLUX Redux (FLUX.1) model. +* For IP-Adapter, **Method** (Full, Style, Composition, Style (strong) or Style (precise)) and **Weight**. For FLUX Redux, **Image influence**. + +## Transform and Overview + +The **Transform** tab sets the selected layer's **Position**, **Scale** and **Rotation** as numbers. Each change is a separate undoable step. + +The **Overview** tab shows a thumbnail of the whole canvas with the visible area outlined. Click it to move the view. + +## Keyboard shortcuts + +In the layer list: + +| Shortcut | Action | +|---|---| +| **↑** / **↓**, **Home** / **End** | Move between layers | +| **→** / **←** | Expand / collapse, or go to the parent group | +| **Enter** or **Space** | Select | +| **F2** | Rename | +| **Delete** / **Backspace** | Delete the layer | +| **Ctrl+C** | Copy the layer to the clipboard | +| **Alt+↑** / **Alt+↓** | Move the layer up / down | +| **Alt+→** / **Alt+←** | Move into the group above / out of its group | +| **Shift+F10** | Open the layer menu | +| **Ctrl+Z** / **Ctrl+Shift+Z** | Undo / redo | + +On macOS, use **Cmd** in place of **Ctrl**. More layer shortcuts, such as duplicate, merge and arrange, work while the canvas has focus; see the [Canvas guide](/users-guide/canvas/canvas-guide/#keyboard-shortcuts). diff --git a/docs/src/content/docs/Users Guide/08.Workflows/01.introduction.mdx b/docs/src/content/docs/Users Guide/08.Workflows/01.introduction.mdx new file mode 100644 index 00000000000..ebe5ac3f5cf --- /dev/null +++ b/docs/src/content/docs/Users Guide/08.Workflows/01.introduction.mdx @@ -0,0 +1,170 @@ +--- +title: Automating with Workflows +description: Why and when to use workflows, how nodes and connections work, a tour of the workflow editor, and a walkthrough that generates a subject with SDXL, composites it onto a background, and builds a simple form for it. +lastUpdated: 2026-09-30 +--- + +import { Image } from 'astro:assets'; +import { Steps } from '@astrojs/starlight/components'; +import EditorTour from './assets/workflow-editor-tour.svg'; +import compositorNodes from './assets/workflow-compositor-nodes.webp'; +import formView from './assets/workflow-form-view.webp'; +import formEdit from './assets/workflow-form-edit.webp'; +import compositorResult from './assets/workflow-compositor-result.webp'; + +The Generate panel, the Canvas and the Video panel each cover a common job well. **Workflows** are for everything else. A workflow is a recipe of processing steps that you assemble yourself: generate an image, then upscale it, cut out its subject, place it on a background, run it through a second model, repeat it for a list of prompts, and so on. Once built, a workflow runs with one press of **Invoke** and can be reused, adjusted and shared. + +Use a workflow when you want to: + +* **Chain steps together** that would otherwise take several trips through different panels, such as generating an image and then compositing it onto another. +* **Use features the panels do not expose**, including community-contributed nodes. +* **Repeat a process** exactly, on new prompts or images, without redoing it by hand. +* **Share a process** with others as a single file. + +## Nodes and connections + +A workflow is made of **nodes** connected into a graph. Each node performs one operation, such as loading a model, encoding a prompt, denoising, decoding an image, or resizing it. Everything InvokeAI does is built from nodes, including what the Generate panel does behind the scenes. + +A node has: + +* **Outputs**, listed at the top of the node, with connection points on its right edge. +* **Inputs**, listed below the outputs, with connection points on its left edge. Most inputs also have a control for entering a value directly, such as a text box, number or model picker. + +You build a workflow by dragging from an output of one node to an input of another. The connection passes the output's value along, and the input's own control is replaced by *Provided by connection*. Connection points and lines are colored by the kind of value they carry, such as images, latents, conditioning, numbers or text, and only compatible points can be connected. An input marked with a red **\*** is required: the workflow cannot run until it has a value or a connection. + +When you press **Invoke**, InvokeAI works out the order to run the nodes from their connections, runs them, and saves the images from the nodes you chose to the Gallery. + +:::tip[New to diffusion?] +Workflows expose the individual steps of image generation. The [Diffusion overview](/users-guide/image-generation/advanced-topics/diffusion/) explains what those steps do, which makes it much easier to build workflows of your own. +::: + +## A tour of the workflow editor + +Workflows are built and run in the **Automate** layout (**Alt+4**). The editor fills the center, the workflow's form is on the left, and the Gallery and Queue are on the right. + + + +Hover over a numbered circle, or tap it on a touchscreen, for a short description. + +| | Part | What it does | +|---|---|---| +| 1 | **Workflow panel** | The workflow's **form**: its most important settings as simple controls, like a Generate panel for this workflow. | +| 2 | **View** / **Edit** | **View** uses the form. **Edit** lets you design the form, edit the workflow's details (name, description, author, tags, notes), and inspect the selected node. | +| 3 | **Editor toolbar** | Tools for panning, box and lasso selection, and erasing nodes or connections; **Zoom in**, **Zoom out** and **Fit view**; **Download workflow as PNG**; and **Node opacity**. | +| 4 | **Workflow name** | The active workflow. Click it to open the **Workflows** library. | +| 5 | **Workflow actions** | **Add node** (**+**), **Save to library…**, the save status, settings, and the **⋯** menu with **Workflow details…**, **Rename workflow…**, **Import workflow JSON…**, **Export workflow JSON**, **New workflow** and more. | +| 6 | **The graph** | The workflow's nodes and connections. Drag nodes by their title bar; scroll to zoom; drag empty space to pan. | +| 7 | **Minimap** | An overview of the whole graph for finding your way around large workflows. | +| 8 | **Gallery and Queue** | Where results appear, with the Queue, Preview and Image Map on the right-hand rail. | +| 9 | **Automate** | The layout preset for workflows. | +| 10 | **Invoke** | Runs the active workflow. The routing control beside it shows the source (**Workflow**) and the destination (**Gallery**). | + +### The essentials + +* **Add a node** by pressing **Shift+A** or **Space** in the editor, or with the **+** button in its header, then search for it by name. +* **Connect nodes** by dragging from an output on the right of one node to an input on the left of another. +* **Choose which images are kept.** Only nodes with **Save to Gallery** checked, at the bottom of image nodes, send their images to the Gallery. +* **Manage workflows** from the **Workflows** library: click the workflow's name in the editor's header. + +The [Workflow Editor Reference](/users-guide/workflows/editor-reference/) covers all of this in detail, along with connectors, the library, common node patterns, community nodes and keyboard shortcuts. + +## Walkthrough: put a generated subject on a background + +The Generate panel can make a picture of a fox, but it cannot then cut the fox out and place it on a photo of your choice. This walkthrough builds a workflow that does both: it generates a subject with SDXL on a plain white background, then uses the **Image Compositor** node to remove the white and composite the subject onto a background image. Finally, it gives the workflow a simple form. + +You need an SDXL model and an SDXL VAE, such as the *sdxl-vae-fp16-fix* starter model, installed from the Model Manager, and a background image in the Gallery. + +### 1. Start from the SDXL text-to-image workflow + + + +1. Press **Alt+4** for the Automate layout. + +2. Click the workflow's name at the top of the editor to open the **Workflows** library, go to **Browse**, and open **Text to Image - SDXL**. A copy is added to your project and becomes the active workflow. + +3. In the **Main Model - SDXL** node, choose your SDXL model in **Model**, and in the **VAE Model** node choose an SDXL VAE. + +4. In the **Positive Prompt** node, describe the subject on a plain background, for example *a red fox sitting, full body, on a plain white background*. A plain, evenly lit background is what lets the Compositor cut the subject out cleanly. + + + +This workflow already contains the SDXL text-to-image chain: the model loader, positive and negative prompts, random seed and noise, **Denoise - SD1.5, SDXL**, and **Latents to Image - SD1.5, SDXL**, which turns the result into an image. + +### 2. Add the Image Compositor + + + +1. Press **Space** and type *compositor*, then click **Image Compositor** to add it. Drag it to the right of **Latents to Image**. + +2. Connect the **Image** output of **Latents to Image** to the **Image Subject** input of **Image Compositor**. + +3. In **Image Background**, click **Choose image** and pick a background from the Gallery, drag one onto it from the Gallery, or **Upload** a file. + +4. Check **Save to Gallery** at the bottom of **Image Compositor**, so the finished composite is saved. Uncheck it on **Latents to Image**, unless you also want to keep the plain subject. + + + +The Latents to Image node connected to the Image Compositor node, which has a mountain landscape as its Image Background. Save to Gallery is unchecked on Latents to Image and checked on Image Compositor. + +### 3. Run it + +Press **Invoke**. The nodes light up in turn as they run, and the composited image appears in the Gallery. Press Invoke again for a new variation: the workflow's **Random Seed** node picks a new seed each time. + +The default settings rarely give a clean cut-out on the first try. SDXL models usually add a soft shadow under the subject, even with *shadow* in the negative prompt. The Compositor's default flood fill, which spreads inward from the corners of the subject image, stops where the white turns to the shadow's light grey, so the shadow and sometimes a pale halo are pasted onto the background. To clean them up, set **Chroma Key** to `white` and raise **Threshold** to about 80, as in the example below, and run it again. Every near-white pixel then becomes transparent, including the faint shadow. + +:::caution[Watch the light parts of the subject] +A chroma key also removes near-white areas *inside* the subject, such as a fox's white chest or the highlights in its eyes. If holes appear, lower **Threshold** until they close, accepting a little shadow, or describe a subject without white parts. +::: + +After a run: the Latents to Image node's latest output shows the fox on a plain white background, and the Current Image node shows the fox composited onto the mountain landscape. The Image Compositor uses Chroma Key white and Threshold 80. + +In summary, the **Image Compositor**'s settings are: + +* **Threshold** (default 50) sets how different from the background color a pixel must be to count as part of the subject. Raise it if a white halo or shadow remains around the subject; lower it if parts of the subject disappear. +* **Chroma Key** removes a specific color everywhere instead of flood-filling from the corners. Enter a color such as `white` or `#00ff00`. This also removes faint shadows and background showing through gaps in the subject. Leave it empty to use the flood fill. +* **Fill Y** (on by default) scales the subject to the height of the background; **Fill X** scales it to the width. Keep at least one of them on unless the two images are exactly the same size. With both on, the subject is stretched to the background's shape. +* **X Offset** and **Y Offset** move the subject, in pixels. Positive values move it right and **up**. A subject moved past an edge reappears on the opposite side. + +### 4. Build a form for the workflow + +A workflow with a dozen nodes is not the most convenient thing to use day to day. The **form** in the Workflow panel on the left gathers the settings you actually change into one simple panel, like the Generate panel. + +To add a setting to the form, click the **pin** icon at the right end of its row in the node (**Expose in Linear UI form**). Only settings with their own control can be pinned; connected inputs cannot. + + + +1. The SDXL workflow already puts its **Positive Prompt**, **Negative Prompt**, **Model**, VAE and board settings in the form. + +2. In **Image Compositor**, pin **Image Background**, **Threshold**, **X Offset** and **Y Offset**. The pins turn blue, and the settings appear at the end of the form. + +3. In the Workflow panel, click **Edit**, then **Form**. Each setting is a card: + * Drag a card's title bar to reorder it. + * Type in **Field label** to give it a clearer name, such as *Background* for Image Background. + * Click **✕** (**Remove from form**) to take out settings you do not need, such as the VAE and board. + * **Add form element** adds a **Heading**, **Text**, a **Divider**, or a row or column container to group settings. + +4. Click **View** to use the form. + + + +The Workflow panel in Edit mode, with the Form tab listing the pinned settings as cards and the Node Inspector below. + +In **View**, the form works like any generation panel: change the prompt or the background and press **Invoke**. The form is part of the workflow, so it is saved with it, exported with it, and shown to anyone who opens your workflow. + +The finished form in View mode: Positive Prompt, Negative Prompt, Model, the Image Background with a mountain landscape, Threshold, X Offset and Y Offset. + +### 5. Save and share it + +Use **Details** in the Workflow panel's Edit mode to give the workflow a name and description, then click **Save to library…** in the editor's header to keep it in your library for other projects. To share it, choose **Export workflow JSON** from the **⋯** menu. + +## Learn more + +* [Workflow Editor Reference](/users-guide/workflows/editor-reference/) — nodes, connections, the library, common node patterns and shortcuts in detail. +* [Loop Nodes](/users-guide/workflows/loop-nodes/) — repeat steps over lists of prompts, images or values. +* [Face Tools Nodes](/users-guide/workflows/face-tools/) — find, mask and edit faces in a workflow. +* [Community Nodes](/users-guide/workflows/community-nodes/) and the [Custom Node Manager](/users-guide/workflows/custom-node-manager/) — add nodes written by the community. +* [Migrating from ComfyUI](/users-guide/workflows/comfyui-migration/) — how familiar ComfyUI nodes map to InvokeAI's. diff --git a/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-compositor-nodes.webp b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-compositor-nodes.webp new file mode 100644 index 00000000000..c87fd000b75 Binary files /dev/null and b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-compositor-nodes.webp differ diff --git a/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-compositor-result.webp b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-compositor-result.webp new file mode 100644 index 00000000000..9afb98a0817 Binary files /dev/null and b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-compositor-result.webp differ diff --git a/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-editor-tour.svg b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-editor-tour.svg new file mode 100644 index 00000000000..5c3b250cc65 --- /dev/null +++ b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-editor-tour.svg @@ -0,0 +1,43 @@ + + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + + + + + + + + + + + \ No newline at end of file diff --git a/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-form-edit.webp b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-form-edit.webp new file mode 100644 index 00000000000..9e6373d8344 Binary files /dev/null and b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-form-edit.webp differ diff --git a/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-form-view.webp b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-form-view.webp new file mode 100644 index 00000000000..a34517fd6b7 Binary files /dev/null and b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-form-view.webp differ diff --git a/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-node-picker.webp b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-node-picker.webp new file mode 100644 index 00000000000..b86cd870f1d Binary files /dev/null and b/docs/src/content/docs/Users Guide/08.Workflows/assets/workflow-node-picker.webp differ diff --git a/docs/src/content/docs/features/Workflows/comfyui-migration.mdx b/docs/src/content/docs/Users Guide/08.Workflows/comfyui-migration.mdx similarity index 100% rename from docs/src/content/docs/features/Workflows/comfyui-migration.mdx rename to docs/src/content/docs/Users Guide/08.Workflows/comfyui-migration.mdx diff --git a/docs/src/content/docs/features/Workflows/community-nodes.mdx b/docs/src/content/docs/Users Guide/08.Workflows/community-nodes.mdx similarity index 99% rename from docs/src/content/docs/features/Workflows/community-nodes.mdx rename to docs/src/content/docs/Users Guide/08.Workflows/community-nodes.mdx index 66ca6bbdf64..b7eb65e6435 100644 --- a/docs/src/content/docs/features/Workflows/community-nodes.mdx +++ b/docs/src/content/docs/Users Guide/08.Workflows/community-nodes.mdx @@ -2,7 +2,7 @@ title: Community Nodes --- -These are nodes that have been developed by the community, for the community. If you're not sure what a node is, you can learn more about nodes [here](/concepts/nodes-workflows/). +These are nodes that have been developed by the community, for the community. If you're not sure what a node is, you can learn more about nodes [here](/users-guide/workflows/introduction/). If you'd like to submit a node for the community, please refer to the [node creation overview](/development/guides/creating-nodes/). diff --git a/docs/src/content/docs/features/Workflows/custom-node-manager.mdx b/docs/src/content/docs/Users Guide/08.Workflows/custom-node-manager.mdx similarity index 100% rename from docs/src/content/docs/features/Workflows/custom-node-manager.mdx rename to docs/src/content/docs/Users Guide/08.Workflows/custom-node-manager.mdx diff --git a/docs/src/content/docs/Users Guide/08.Workflows/editor-reference.mdx b/docs/src/content/docs/Users Guide/08.Workflows/editor-reference.mdx new file mode 100644 index 00000000000..ea8c46f8aad --- /dev/null +++ b/docs/src/content/docs/Users Guide/08.Workflows/editor-reference.mdx @@ -0,0 +1,98 @@ +--- +title: Workflow Editor Reference +description: Details of the workflow editor — adding and connecting nodes, connectors, which results reach the Gallery, managing workflows and the library, common node patterns, community nodes and keyboard shortcuts. +lastUpdated: 2026-09-30 +sidebar: + order: 2 +--- + +import { Image } from 'astro:assets'; +import nodePicker from './assets/workflow-node-picker.webp'; + +This page describes working in the workflow editor in detail. For an introduction to workflows, a tour of the editor and a step-by-step walkthrough, see [Automating with Workflows](/users-guide/workflows/introduction/). + +## Working in the editor + +### Adding and connecting nodes + +To add a node, press **Shift+A** or **Space** in the editor, or click **+** (**Add node**) in the editor's header. Type to search: every word you type must appear in the node's name, type, tags or category. Nodes are grouped by category, each with a short description and the node pack it comes from; nodes still in development are marked with a hammer icon (*Beta*). Click a node to add it near the middle of the view. + +The node picker searching for "compositor", showing the Image Compositor node in the Image category. + +A quicker way to add a node that is already connected: drag from a connection point and release it over empty space. The picker opens showing only the nodes that can connect to that point, and the new node is wired up for you. + +To connect two nodes, drag from an output on the right of one node to an input on the left of another. Only compatible connection points accept the connection. An input takes one connection, but an output can feed as many inputs as you like. To reconnect, drag the end of a connection to another point; to disconnect, drag it away and release it over empty space, or select the connection and press **Delete**. + +Right-click a node to **Copy**, **Paste**, **Duplicate**, **Collapse** or **Delete** it, and double-click its title to rename it. The chevron in a node's title bar collapses it to keep a large graph tidy. + +The picker's **Utility** group has three special nodes: + +* **Notes** — a sticky note for explaining a workflow to others, or to yourself later. +* **Current Image** — shows the image being generated and the latest result, for keeping an eye on long workflows. +* **Connector** — a small dot for rerouting connections (see below). + +#### Connectors + +Connectors tidy up the wiring of a large graph. They pass a value straight through and are removed before the workflow runs, so they cannot change what it does. To add one, right-click empty space in the editor and choose **Add connector**, then connect to and from it. You can wire a connector's output first and its input later; it only accepts a source whose type matches what it feeds. Deleting a connector joins its incoming connection directly to everything it fed. + +A node's border shows its state: red when a required input is missing or the node failed, highlighted with a progress strip while it runs, and green when it has finished. After a run, **Latest output** on an image node shows its result. + +### Where results go + +Only nodes whose **Save to Gallery** box is checked, at the bottom of nodes that produce images, send their images to the Gallery. The images of other nodes are intermediate results that are used and then discarded. New nodes start with **Save to Gallery** turned off, so check it on the node that makes your final image. + +An image node's **Board** field chooses where its saved images go. **Auto**, the default, uses the Gallery's destination: the selected board, or the auto-add board if one is set (see [Choosing where new media goes](/users-guide/gallery/introduction/#choosing-where-new-media-goes)). **None** saves to Uncategorized, and you can also pick a specific board. Images from nodes that have no Board field go to Uncategorized. + +Beside it, **Use Cache** lets InvokeAI reuse a node's previous result when its inputs have not changed, which speeds up repeated runs. On a multi-user installation, only administrators see this option. + +### Your workflows + +Each project keeps its own workflows, and one of them is **active**: the one shown in the editor and run by Invoke. Changes are saved with the project automatically. Click the workflow's name to open the **Workflows** library: + +* **This project** — the project's workflows. Open one to make it active, or start a **New workflow**. +* **Browse** — the workflows that ship with InvokeAI, such as *Text to Image - SDXL*, *Flux Text to Image* and the video workflows. Opening one adds a copy to your project. +* **Yours** — workflows you have saved to your library with **Save to library…**, for use in other projects. + +Opening a library workflow adds an independent copy to the project; the library's version is untouched. If the project already has a copy, opening the template asks what to do: **Open copy** switches to the existing copy, **Add new copy** makes another one, and **Replace copy** replaces the copy's graph with the library version while keeping its name. A replace can be undone in one step. + +Changes to a project's workflows never change the library by themselves. **Save to library…**, in the editor's header, saves the workflow as a new library template with the name you give it, including its form and its current settings as defaults. For a workflow opened from one of your own templates, **Update library template** replaces that template instead. If the template has changed since you opened it, you can save as a new template instead, or review the current one and replace it deliberately. Replacing a template also affects workflows that use it through the **Call Saved Workflow** node. + +Add comma-separated tags, such as `portrait, SDXL, upscaling`, in the workflow's **Details**; they become filters in the library. Each workflow also keeps its own undo history while the project is open. + +:::note[Changed from v6] +In InvokeAI 6, editing a workflow opened from the library saved every change straight back to the library. Workflows now belong to their project and autosave there; the library changes only when you choose **Save to library…** or **Update library template**. +::: + +To share a workflow, use **Export workflow JSON** from the **⋯** menu; others can load it with **Import workflow JSON…**. An image made by a workflow also carries the workflow in its metadata: right-click the image and choose **Load Workflow**. **Download workflow as PNG** in the editor toolbar saves a picture of the whole graph, at up to twice screen resolution, for documentation or sharing. Closing the workflow editor while the picture is being prepared cancels the export. + +## Common node patterns + +A few groups of nodes appear in almost every image workflow. Knowing what they do makes it easier to read the bundled workflows and to build your own. + +* **Model, prompts and denoising.** A model loader, such as **Main Model - SDXL**, provides the model's parts: the UNet or transformer that denoises, the text encoders (CLIP) and the VAE. Prompt nodes turn text into *conditioning* using the text encoders. The denoise node needs both positive and negative conditioning, even if the negative prompt is empty. +* **Noise and seeds.** Text-to-image starts from random noise, made by **Create Latent Noise** from a **Seed**, **Width** and **Height**. Its **Noise Type** must match the model family: **SD** for SD 1.5 and SDXL, or the matching family for FLUX, FLUX.2, SD3, CogView4, Z-Image or Anima. Type a seed for repeatable results, or connect a **Random Integer** node to the seed for a new one on every run. +* **Images and latents.** Models work on compressed *latents*, not pixels. **Image to Latents** encodes an image with a VAE, for image-to-image; **Latents to Image** decodes the result back into pixels. +* **Scaling.** **Scale Image** and **Resize Image** resize pixels, **Scale Latents** and **Resize Latents** resize latents, and **Image-to-Image** runs an upscaling model. Latents are already compressed, so enlarging the image and then refining it often gives more detail than enlarging the latents. +* **Control and LoRAs.** A **ControlNet** node turns a control image, usually prepared by a processing node such as **Canny Edge Detection** or **Depth Anything Depth Estimation**, into a control input for the denoise node. **Apply LoRA** nodes (one per model family, such as **Apply LoRA - SDXL**) add a LoRA to the model's UNet and text encoders. +* **Iteration and batches.** **Iterate** runs the rest of the graph once for each item in a collection, and **Collect** gathers several values into a collection. For example, **Random Range** makes a list of seeds and **Iterate** feeds them one at a time to the noise node, generating one image per seed. For more complex repetition, see [Loop Nodes](/users-guide/workflows/loop-nodes/). + +## Adding community nodes + +The community has written many more nodes, from image effects to prompt tools. The easiest way to install a node pack is the [Custom Node Manager](/users-guide/workflows/custom-node-manager/): paste the pack's Git URL and it is downloaded and loaded without restarting. To install one by hand, clone or download it into the `nodes` folder of your InvokeAI root, then click **Reload** in the Custom Node Manager or restart InvokeAI. `git clone` is the better choice, because `git pull` then updates the pack. The [Community Nodes](/users-guide/workflows/community-nodes/) page lists available packs. + +## Keyboard shortcuts + +These work while the workflow editor has focus. On macOS, use **Cmd** in place of **Ctrl**. + +| Shortcut | Action | +|---|---| +| **Shift+A** or **Space** | Add a node | +| **Ctrl+C** / **Ctrl+V** | Copy / paste nodes, with the connections between them | +| **Ctrl+D** | Duplicate the selected nodes | +| **Ctrl+A** | Select all nodes | +| **Delete** / **Backspace** | Delete the selected nodes | +| **Ctrl+Z** / **Ctrl+Shift+Z**, **Ctrl+Y** | Undo / redo | +| **Shift**-drag | Box-select nodes | +| Hold **Ctrl** while dragging | Snap to the grid | +| **Ctrl+Enter** | Invoke | + diff --git a/docs/src/content/docs/features/Workflows/face-tools.mdx b/docs/src/content/docs/Users Guide/08.Workflows/face-tools.mdx similarity index 100% rename from docs/src/content/docs/features/Workflows/face-tools.mdx rename to docs/src/content/docs/Users Guide/08.Workflows/face-tools.mdx diff --git a/docs/src/content/docs/Users Guide/08.Workflows/loop-nodes.mdx b/docs/src/content/docs/Users Guide/08.Workflows/loop-nodes.mdx new file mode 100644 index 00000000000..69bd4d75c61 --- /dev/null +++ b/docs/src/content/docs/Users Guide/08.Workflows/loop-nodes.mdx @@ -0,0 +1,239 @@ +--- +title: Loop Nodes +description: Repeat workflow steps over a collection and optionally carry values from one iteration to the next. +sidebar: + order: 4 +lastUpdated: 2026-09-28 +--- + +Loop nodes let a workflow repeat the same group of steps for every item in a collection. For example, a workflow can +process every image found by a detector, try several seeds, or build a collection of results one item at a time. + +The main loop uses two nodes: + +- **For** starts the loop and provides one collection item at a time. +- **ForReturn** marks the end of the loop body and sends the result back to the loop. + +## The Basic Pattern + +Connect the nodes in this order: + +```text +Collection producer -> For.collection +For.loop_linkage - - - - - - - - - - - - - - - - - > ForReturn.loop_linkage +For.item -> work nodes -> ForReturn.output +For.output_collection -> nodes after the loop +``` + +The `For` node has one iteration for each item in its `collection`. During an iteration, its `item`, `index`, `total`, +and `state` outputs contain values for that iteration. The work nodes run, then `ForReturn` closes that iteration. + +The `For` node does not send its per-item `item` output directly to nodes after the loop. To run a node once after all +iterations finish, connect that node to `For.output_collection` or `For.final_state`. + +You can connect `For.output_collection` directly to a branch input on an `If`. If that branch is selected, its consumer +runs in the parent frame after the loop finishes; you do not need a `For.final_state` edge just to make it run. + +The editor draws a dashed green boundary around the loop body. This is a visual guide showing which nodes repeat; the +boundary does not carry data. The dashed green line between `For.loop_linkage` and `ForReturn.loop_linkage` is a +required association, not a data value. The remaining values move through the ports and edges you connect. + +The loop is not executable until its two boundary nodes are paired. Connect `For.loop_linkage` directly to the matching +`ForReturn.loop_linkage`. When you add `ForReturn` from an iteration-output connection in the node picker, the editor +can create this association for you. Replacing either boundary node requires pairing the replacement again. + +Deleting a connector preserves its through-connections by reconnecting the upstream source to surviving downstream +targets. Deleting a complete loop-linkage alias collapses it to a direct `loop_linkage` edge; ordinary connector paths +remain ordinary data edges. + +### A Simple Example + +To process every image in a collection: + +```text +Image collection -> For.collection +For.item -> Image Processor.image +Image Processor.image -> ForReturn.output +For.output_collection -> Save or display node +``` + +The processor runs once per image. The final `output_collection` contains the processed images in the same order as the +input collection. + +`ForReturn.output` may be disconnected when the result is carried through loop state instead. A body still needs a +dependency into `ForReturn`, such as a state update or a scalar `continue_condition`; there is no separate completion +port for effects-only bodies. + +## What The For Outputs Mean + +| Output | Meaning | Use it for | +| ------------------- | ------------------------------------------------- | ------------------------------------------- | +| `loop_linkage` | The association port for the matching `ForReturn` | Pairing the loop boundaries; it is not data | +| `item` | The current collection item | The work done during this iteration | +| `index` | The zero-based position of the current item | Labels, counters, or position-based logic | +| `total` | The collection length | Progress or position-based logic | +| `state` | The current loop state | Values carried from earlier iterations | +| `output_collection` | All values returned through `ForReturn.output` | Work that should happen after the loop | +| `final_state` | The state from the last completed iteration | Reading accumulated values after the loop | + +The `item`, `index`, `total`, and `state` outputs belong inside the loop body. The `output_collection` and +`final_state` outputs become available after the loop finishes. `loop_linkage` is only for pairing the two boundary +nodes. + +Collection Cartesian products are limited to 100,000 item pairs. A product larger than 100,000 pairs fails with an +error instead of expanding without bound. + +## Returning Results + +`ForReturn` closes the loop body. It has four useful inputs: + +- **Output** adds one value to `For.output_collection` for the current iteration. Null (`None`) values are retained, so + collection positions continue to match iteration order. +- **State** supplies the state for the next iteration. +- **Continue Condition** controls whether another item should be processed. It continues by default; connecting `False` + stops after the current iteration. +- **Loop Linkage** pairs this node with its owning `For`. It must be connected directly to `For.loop_linkage`; it does + not provide a value to the loop body. + +The `Loop Linkage` connection is separate from the body data connections. It tells the engine which `ForReturn` closes +which `For`, which matters when loops are nested or several loop boundaries are present in one workflow. + +## Carrying Values With State + +State is an optional set of named values that travels from one iteration to the next. It is useful for a running sum, a +changing parameter, or several values that must be updated together. + +The state helper nodes make this easier: + +- **Empty Loop State** creates a blank state. +- **Get Loop State Value** reads a value by name. Its `default` input is used when that name has not been set yet. +- **Set Loop State Value** returns a copy of the state with one named value changed. +- **Merge Loop State Values** returns a copy with several named values changed. + +A running value usually looks like this: + +```text +For.state -> Get Loop State Value.state +For.item -> Add or other calculation +Get.value + For.item -> calculation +calculation -> Set Loop State Value.value +Set Loop State Value.state -> ForReturn.state +For.final_state -> Get Loop State Value.state (after the loop) +``` + +Set the `default` on `Get Loop State Value` when the value does not exist during the first iteration. For example, use +`0` for the initial value of a running sum. + +State is separate from `output_collection`: + +- Use `output` when each iteration produces a result that should be collected automatically. +- Use state when the loop needs to remember or update a value while it runs. +- Use both when the loop needs per-item results and an accumulator. + +## Making Collections For A Loop + +`For` accepts one collection input. Use collection-producing nodes before it when the values need to be calculated. + +### Range Nodes + +- **Integer Range** creates values from `start` up to, but not including, `stop`. +- **Integer Range of Size** creates a requested number of values. +- **Random Range** creates a collection of random integers using its seed and bounds. + +For example: + +```text +Integer Range of Size -> For.collection +For.item -> work nodes +``` + +This is useful when the loop should run a known number of times. The integer itself can be used as a seed, index, or +ordinary numeric input. + +### Combining Collections + +These nodes make the collection relationship explicit before the loop: + +- **Concatenate Collections** puts every item from the first collection before every item from the second. The two + collections may have different lengths. +- **Zip Collections** pairs items by position. Both collections must have the same length; a mismatch is an error. +- **Cartesian Product of Collections** creates every possible pair, one item from each collection. The collections may + have different lengths. Products larger than 100,000 pairs fail with an error. + +Choose based on the relationship between the items: + +```text +Concatenate: first phase, then second phase +Zip: first[0] with second[0], first[1] with second[1], ... +Cartesian: every item in first with every item in second +``` + +An empty input to `Concatenate` is harmless. An empty input to `Zip` is valid when both inputs are empty. An empty +input to `Cartesian Product` produces no pairs. + +## For And Iterate + +Both `For` and `Iterate` repeat work over a collection, but they serve different purposes: + +- Use **Iterate** when a normal workflow needs to expand a collection into separate executions and **Collect** when + those executions should be gathered again. +- Use **For** when the repeated work has a clear beginning and end, needs loop state, may stop early, or should expose + a final collection and final state. + +An `Iterate` can also be used inside a `For` for a bounded inner pass. The inner values must be gathered by one +`Collect` before the outer `ForReturn`: + +```text +For.item -> preparation -> Iterate.collection +Iterate.item -> inner work -> Collect.item +Collect.collection -> ForReturn.output +``` + +The outer loop waits for the inner `Iterate` and `Collect` to finish, then returns one collection for that outer item. +One exact serial two-`Iterate` body is also supported: `For.item` feeds a preparation node and the first `Iterate`; its +item feeds a second preparation node and `Iterate`, followed by one work node, one `Collect`, and `ForReturn`. For this +bounded pattern, use a non-empty literal outer collection and one consumer of `For.output_collection`. Other +arrangements, such as sibling internal `Iterate` nodes or an independent external `Iterate` feeding a `For` body, are +unsupported. + +## Nested For Loops + +A `For` can contain another `For`. The inner loop must finish before the outer loop returns its current item: + +```text +OuterFor.item -> InnerFor.collection +InnerFor.item -> inner work -> InnerForReturn.output +InnerFor.output_collection -> OuterForReturn.output +``` + +Each `For` must have its own matching `ForReturn`. The editor and saved workflow keep those pairings separate, so state +from an inner loop does not automatically become state for the outer loop. Connect values explicitly when they need to +cross a loop boundary. + +Multiple inner loops can be used as siblings when their final collections feed an ordinary collection-combining node. +That node defines whether those collections are concatenated, zipped, or combined as a Cartesian product. The workflow +does not choose one of those meanings automatically. + +## Empty Collections And Early Stop + +An empty collection is a successful loop with zero body iterations: + +- `output_collection` is empty. +- `final_state` is the provided initial state, or an empty state. +- No body node runs. + +Set `ForReturn.continue_condition` to `False` when the loop should stop after the current item. The results and state +from completed iterations remain available through the normal final outputs. + +## Common Wiring Mistakes + +- Connect the collection to `For.collection`, not to `For.item`. +- Connect `For.loop_linkage` directly to the matching `ForReturn.loop_linkage`. +- Connect body work to `ForReturn.output` when its results should be collected. +- Connect after-loop work to `For.output_collection` or `For.final_state`, not to `For.item` or `For.state`. +- Give each nested `For` its own `ForReturn`. +- Use `Zip Collections` only when the input lengths must match. +- Use `Cartesian Product of Collections` carefully: large inputs can produce many pairs. + +For the execution and validation rules behind these connections, see the contributor reference: +[`Loop Nodes Architecture`](/contributing/loop-nodes/). diff --git a/docs/src/content/docs/Users Guide/09.Image Map/01.introduction.mdx b/docs/src/content/docs/Users Guide/09.Image Map/01.introduction.mdx new file mode 100644 index 00000000000..cc68e013932 --- /dev/null +++ b/docs/src/content/docs/Users Guide/09.Image Map/01.introduction.mdx @@ -0,0 +1,87 @@ +--- +title: Image Map Indexing and Search +--- + +## Image Map + +The optional Image Map feature dynamically creates a scatterplot of +your generated and uploaded images and videos. Once its model is +installed, Image Map automatically indexes every image or video in +your gallery. + +![ImageMap Zoomed Out](./assets/imagemap-1.png) + +Images and videos that are similar to each other are grouped together +in colored clusters and labeled using an autotagging system. When the +Image Map is activated you also get the benefit of semantic search in +the image gallery. This lets you search your galleries by image +content (i.e. find "sunset on the beach" even for images that weren't +prompted with those exact words). You can also perform image similarity +searches against your own, or third party images. + +### Activating the Image Map + +To use the Image Map you will need to install the image encoder model +`DFN2B-CLIP-ViT-L-14-39B` from the starter models section of the Model +Manager. Once installed, you will need to restart the backend in order +to initiate the indexing process. If you have a large image +collection, initial indexing may take a while -- expect a gallery of +20,000 images to take about 15 minutes to index. After this, the index +is updated incrementally in the background. + +### Inactivating the Image Map + +If you wish to turn off Image Map indexing and hide its panel +entirely, add the line `image_index_enabled: false` to +`invokeai.yaml`. + +### Image Map Navigation + +You can explore the map by using the mouse, scrollwheel or touchscreen +gestures to pan and zoom. Hovering over a spot will display a thumbnail +of the image it corresponds to, along with information on which +cluster the image belongs to, the tags assigned to the cluster, and +the tags assigned to the image itself. + +![ImageMap Zoomed In](./assets/imagemap-2.png) + +When you click on a spot, one of two things can happen depending on +the Image Map mode you are in. In one mode clicking on a spot loads +its image into the preview, and navigates to where the image lives in +the gallery. In the other mode, clicking on a spot will load its +entire cluster into the gallery, and load the clicked image into the +preview. You can change the mode by selecting the "click" icon in the +upper right of the image map panel: ![Selection Mode](./assets/imagemap-3.png) + +If you select an image in the gallery, a yellow target will appear on +the Image Map to show its location, and the target will move around as +you navigate the gallery. + +### Activating/inactivating tags + +You can show and hide the tags by clicking on the "label" icon in the +upper right-hand of the panel. + +### Floating the Image Map Window + +When you open the Image Map in the right rail, a floating window icon +will appear at the far right of its title bar. Click on this to pop +the window out. You can then move it around the screen and resize the +floating window by dragging its bottom right corner. + +### Semantic search + +If Image Map indexing is active, then an AI search mode is added to +the gallery. Press the sparkle button at the right end of the gallery's +search box and describe what you are looking for; any text already in +the box is used straight away. This will display the images and video +stills whose content is most closely related to your description. See +[Searching by content](/users-guide/gallery/introduction/#searching-by-content) +for details. + +You can also search by image similarity. Drag and drop an image (from +the gallery, preview, or another browser tab) onto the search box. All +similar images will be shown in the gallery. + +To exit search mode, click the "X" to the right of the search box. + diff --git a/docs/src/content/docs/Users Guide/09.Image Map/02.customizing.mdx b/docs/src/content/docs/Users Guide/09.Image Map/02.customizing.mdx new file mode 100644 index 00000000000..9c5515b6f12 --- /dev/null +++ b/docs/src/content/docs/Users Guide/09.Image Map/02.customizing.mdx @@ -0,0 +1,28 @@ +--- +title: Customizing the Image Map +--- + +### Adding custom tags + +The default labeling vocabulary contains about 1200 terms that are +frequently used to tag photographic images. To add additional custom +terms, click on the Settings icon. You can then add up to 500 +supplementary terms that will be applied to clusters and individual +images. It may take up to a minute for tags to update after this +operation. + +### Changing the cluster strength + +The clustering algorithm uses a cluster strength coefficient to +control the size and number of identified clusters. Higher +coefficients lead to larger and fewer unclustered images. By default, +InvokeAI tries to balance the coefficient so that not more than 10% of +the images are unclustered. Usually this is good enough, but if you +wish to adjust the cluster strength manually, you can do so by opening +up the Image Map Settings dialogue, and adjusting the "Clustering +Strength" parameter. Make small incremental changes. If the value is +too high, most of your images will coalesce into a single monster +cluster. If the value is too low, most of your images will be +unclustered singletons. The Image Map displays these in gray. + +Click "auto" to return to the default values. \ No newline at end of file diff --git a/docs/src/content/docs/Users Guide/09.Image Map/assets/imagemap-1.png b/docs/src/content/docs/Users Guide/09.Image Map/assets/imagemap-1.png new file mode 100644 index 00000000000..637f3f4b399 Binary files /dev/null and b/docs/src/content/docs/Users Guide/09.Image Map/assets/imagemap-1.png differ diff --git a/docs/src/content/docs/Users Guide/09.Image Map/assets/imagemap-2.png b/docs/src/content/docs/Users Guide/09.Image Map/assets/imagemap-2.png new file mode 100644 index 00000000000..0f171dc6117 Binary files /dev/null and b/docs/src/content/docs/Users Guide/09.Image Map/assets/imagemap-2.png differ diff --git a/docs/src/content/docs/Users Guide/09.Image Map/assets/imagemap-3.png b/docs/src/content/docs/Users Guide/09.Image Map/assets/imagemap-3.png new file mode 100644 index 00000000000..4195c840449 Binary files /dev/null and b/docs/src/content/docs/Users Guide/09.Image Map/assets/imagemap-3.png differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-add-layer-menu.webp b/docs/src/content/docs/Users Guide/assets/canvas-add-layer-menu.webp new file mode 100644 index 00000000000..11316ccbaf8 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-add-layer-menu.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-context-menu.webp b/docs/src/content/docs/Users Guide/assets/canvas-context-menu.webp new file mode 100644 index 00000000000..94b7d947dab Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-context-menu.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-control-layer.webp b/docs/src/content/docs/Users Guide/assets/canvas-control-layer.webp new file mode 100644 index 00000000000..1edc7b9f957 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-control-layer.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-edit-layout.webp b/docs/src/content/docs/Users Guide/assets/canvas-edit-layout.webp new file mode 100644 index 00000000000..9db28bf6c8f Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-edit-layout.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-frame-properties.webp b/docs/src/content/docs/Users Guide/assets/canvas-frame-properties.webp new file mode 100644 index 00000000000..ad866165698 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-frame-properties.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-inpaint-result.webp b/docs/src/content/docs/Users Guide/assets/canvas-inpaint-result.webp new file mode 100644 index 00000000000..4bf71bcbb19 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-inpaint-result.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-inpaint.webp b/docs/src/content/docs/Users Guide/assets/canvas-inpaint.webp new file mode 100644 index 00000000000..e27bd54afc9 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-inpaint.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-outpaint.webp b/docs/src/content/docs/Users Guide/assets/canvas-outpaint.webp new file mode 100644 index 00000000000..3f272eb2449 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-outpaint.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-regional-guidance.webp b/docs/src/content/docs/Users Guide/assets/canvas-regional-guidance.webp new file mode 100644 index 00000000000..ca2f5ad9cf0 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-regional-guidance.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-settings.webp b/docs/src/content/docs/Users Guide/assets/canvas-settings.webp new file mode 100644 index 00000000000..d9601e4d524 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-settings.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/canvas-toolbar.webp b/docs/src/content/docs/Users Guide/assets/canvas-toolbar.webp new file mode 100644 index 00000000000..4dfef314bfc Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/canvas-toolbar.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-board-filters.png b/docs/src/content/docs/Users Guide/assets/gallery-board-filters.png new file mode 100644 index 00000000000..22a0a07395a Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-board-filters.png differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-board-menu.png b/docs/src/content/docs/Users Guide/assets/gallery-board-menu.png new file mode 100644 index 00000000000..5e072fcca75 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-board-menu.png differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-create-board.png b/docs/src/content/docs/Users Guide/assets/gallery-create-board.png new file mode 100644 index 00000000000..f2db8bcdced Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-create-board.png differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-delete-board.png b/docs/src/content/docs/Users Guide/assets/gallery-delete-board.png new file mode 100644 index 00000000000..559865cf0e6 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-delete-board.png differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-image-menu.png b/docs/src/content/docs/Users Guide/assets/gallery-image-menu.png new file mode 100644 index 00000000000..dbdb3801712 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-image-menu.png differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-overview.webp b/docs/src/content/docs/Users Guide/assets/gallery-overview.webp new file mode 100644 index 00000000000..ef9e5000fc2 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-overview.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-search-help.png b/docs/src/content/docs/Users Guide/assets/gallery-search-help.png new file mode 100644 index 00000000000..4dea863b609 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-search-help.png differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-selection.webp b/docs/src/content/docs/Users Guide/assets/gallery-selection.webp new file mode 100644 index 00000000000..3e9a8339f86 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-selection.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-semantic-search.png b/docs/src/content/docs/Users Guide/assets/gallery-semantic-search.png new file mode 100644 index 00000000000..a373e3123b3 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-semantic-search.png differ diff --git a/docs/src/content/docs/Users Guide/assets/gallery-settings.png b/docs/src/content/docs/Users Guide/assets/gallery-settings.png new file mode 100644 index 00000000000..ae60cc253fd Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/gallery-settings.png differ diff --git a/docs/src/content/docs/Users Guide/assets/generate-panel-overview.png b/docs/src/content/docs/Users Guide/assets/generate-panel-overview.png new file mode 100644 index 00000000000..0d2eb7be1e1 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/generate-panel-overview.png differ diff --git a/docs/src/content/docs/Users Guide/assets/layers-context-menu.webp b/docs/src/content/docs/Users Guide/assets/layers-context-menu.webp new file mode 100644 index 00000000000..ec96ca1fff7 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/layers-context-menu.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/layers-history.webp b/docs/src/content/docs/Users Guide/assets/layers-history.webp new file mode 100644 index 00000000000..a922b933c9c Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/layers-history.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/layers-panel.webp b/docs/src/content/docs/Users Guide/assets/layers-panel.webp new file mode 100644 index 00000000000..2033ffb7c01 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/layers-panel.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/project-switcher.webp b/docs/src/content/docs/Users Guide/assets/project-switcher.webp new file mode 100644 index 00000000000..cb9b843af3f Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/project-switcher.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/prompt-template-merged.webp b/docs/src/content/docs/Users Guide/assets/prompt-template-merged.webp new file mode 100644 index 00000000000..931937baf8a Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/prompt-template-merged.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/prompt-templates-popover.webp b/docs/src/content/docs/Users Guide/assets/prompt-templates-popover.webp new file mode 100644 index 00000000000..38eb6c3436d Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/prompt-templates-popover.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/routing-menu.webp b/docs/src/content/docs/Users Guide/assets/routing-menu.webp new file mode 100644 index 00000000000..5c600f3f29f Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/routing-menu.webp differ diff --git a/docs/src/content/docs/Users Guide/assets/video-panel-expert-advisory.png b/docs/src/content/docs/Users Guide/assets/video-panel-expert-advisory.png new file mode 100644 index 00000000000..017b070a9c9 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/video-panel-expert-advisory.png differ diff --git a/docs/src/content/docs/Users Guide/assets/video-panel-initial-video.png b/docs/src/content/docs/Users Guide/assets/video-panel-initial-video.png new file mode 100644 index 00000000000..c8bd6795264 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/video-panel-initial-video.png differ diff --git a/docs/src/content/docs/Users Guide/assets/video-panel-model-section.png b/docs/src/content/docs/Users Guide/assets/video-panel-model-section.png new file mode 100644 index 00000000000..2706001e52a Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/video-panel-model-section.png differ diff --git a/docs/src/content/docs/Users Guide/assets/video-panel-overview.png b/docs/src/content/docs/Users Guide/assets/video-panel-overview.png new file mode 100644 index 00000000000..6efd759d891 Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/video-panel-overview.png differ diff --git a/docs/src/content/docs/Users Guide/assets/video-panel-start-end-images.png b/docs/src/content/docs/Users Guide/assets/video-panel-start-end-images.png new file mode 100644 index 00000000000..60bc41b1e0a Binary files /dev/null and b/docs/src/content/docs/Users Guide/assets/video-panel-start-end-images.png differ diff --git a/docs/src/content/docs/concepts/dynamic-prompting.mdx b/docs/src/content/docs/concepts/dynamic-prompting.mdx deleted file mode 100644 index c345fea5fe4..00000000000 --- a/docs/src/content/docs/concepts/dynamic-prompting.mdx +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: Dynamic Prompting -lastUpdated: 2026-03-30 -sidebar: - order: 4 ---- - -import { Card, CardGrid, Steps, LinkCard } from '@astrojs/starlight/components'; - -Dynamic prompting expands a single prompt into many prompt variations. It is useful for brainstorming, prompt exploration, and batch testing without rewriting the same prompt by hand. - -## Basic syntax - -Put alternatives inside braces and separate them with `|`. - -```text -a {red|green|blue} balloon -``` - -This can expand into: - -```text -a red balloon -a green balloon -a blue balloon -``` - -You can use more than one dynamic group in the same prompt: - -```text -a {red|green} {balloon|kite} -``` - -That creates a set of prompt combinations such as `a red balloon`, `a red kite`, `a green balloon`, and `a green kite`. - -## Select more than one option with `$$` - -Prefix a group with a number and `$$` to choose multiple distinct options from the same set. - -```text -portrait, {2$$rim light|fog|rain|neon reflections} -``` - -Possible results include: - -```text -portrait, rim light, fog -portrait, fog, rain -portrait, rim light, neon reflections -``` - -This is useful when you want controlled variety without writing every combination by hand. - -## Random vs combinatorial expansion - - - - Walks the possible prompt combinations systematically until `Max Prompts` is reached. - - - Samples prompt variations instead of enumerating every combination. A seed can make random expansion repeatable. - - - -InvokeAI supports both modes, but where you can choose them depends on the workflow. - -- In the current linear UI, dynamic prompt preview is driven from the positive prompt and currently follows the standard combinatorial expansion path. -- In node and backend contexts, random and combinatorial generation are exposed more explicitly. - -## Max Prompts - -`Max Prompts` limits how many expanded prompts InvokeAI will generate. - -This matters because combinations grow quickly. For example: - -```text -a {red|green|blue} balloon in {morning mist|golden hour|rain} -``` - -Even this small prompt already has nine possible combinations. - -:::tip[Start small] - Preview a handful of prompt variants first. Once the combinations look useful, increase `Max Prompts` for a larger batch. -::: - -## Seed Behaviour - -In the current UI, the `Seed Behaviour` setting controls how seeds are reused across expanded prompts. - - - - Uses one seed per iteration, so prompt variants in the same iteration share a seed. This is useful when you want to compare prompt wording more directly. - - - Uses a different seed for every generated image. This is useful when you want the widest possible variety. - - - -## Using dynamic prompting in the linear UI - - - 1. **Put dynamic prompt syntax in the positive prompt** - - In the current linear UI, dynamic prompt expansion is driven from the positive prompt. - - 2. **Open the preview** - - Use `Show Dynamic Prompts` or the prompts preview to inspect the expanded list before you generate. - - 3. **Set `Max Prompts`** - - Keep the expansion under control before launching a large batch. - - 4. **Choose the right seed behavior** - - Use `Seed per Iteration` for easier comparison, or `Seed per Image` for more variety. - - 5. **Generate a small batch first** - - Sanity-check the combinations before scaling up. - - -:::note[Current linear UI behavior] - The linear UI currently exposes `Max Prompts`, preview, and seed behavior. It does not expose a separate random-versus-combinatorial mode switch in the main positive prompt flow. -::: - -## Tips - -- Keep each option group internally compatible. -- Be careful with multiple groups, because the number of combinations grows quickly. -- Review the expanded prompt list before launching a large batch. -- Use dynamic prompting for variation, not to avoid thinking through the base prompt. -- When one specific term needs more emphasis, use [Prompting Syntax](../prompt-syntax) instead of adding more dynamic groups. diff --git a/docs/src/content/docs/concepts/image-generation.mdx b/docs/src/content/docs/concepts/image-generation.mdx deleted file mode 100644 index 6aa25f4b34c..00000000000 --- a/docs/src/content/docs/concepts/image-generation.mdx +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: Image Generation -lastUpdated: 2026-03-30 -sidebar: - order: 1 ---- - -import { Card, CardGrid, Steps, LinkCard } from '@astrojs/starlight/components'; - -:::tip[New to image generation with AI?] - You're in the right place! This is a high-level walkthrough of some of the concepts and terms you'll see as you start using Invoke. Please note, this is not an exhaustive guide and may be out of date due to the rapidly changing nature of the space. -::: - -## Using InvokeAI - -### Prompt Crafting - -Prompts are the basis of using InvokeAI, providing the models directions on what to generate. As a general rule of thumb, the more detailed your prompt is, the better your result will be. - - - To get started, here's an easy template to use for structuring your prompts: - **Subject, Style, Quality, Aesthetic** - - - **Subject:** What your image will be about. E.g. “a futuristic city with trains”, “penguins floating on icebergs”, “friends sharing beers”. - - **Style:** The style or medium in which your image will be in. E.g. “photograph”, “pencil sketch”, “oil paints”, or “pop art”, “cubism”, “abstract”. - - **Quality:** A particular aspect or trait that you would like to see emphasized in your image. E.g. "award-winning", "featured in relevant set of high quality works", "professionally acclaimed". Many people often use "masterpiece". - - **Aesthetics:** The visual impact and design of the artwork. This can be colors, mood, lighting, setting, etc. - - -There are two prompt boxes: **Positive Prompt** & **Negative Prompt**. - -- A **Positive Prompt** includes words you want the model to reference when creating an image. -- A **Negative Prompt** is for anything you want the model to eliminate when creating an image. It doesn’t always interpret things exactly the way you would, but helps control the generation process. Always try to include a few terms - you can typically use lower quality image terms like “blurry” or “distorted” with good success. - -**Some example prompts you can try on your own:** - -- *A detailed oil painting of a tranquil forest at sunset with vibrant colors and soft, golden light filtering through the trees* -- *friends sharing beers in a busy city, realistic colored pencil sketch, twilight, masterpiece, bright, lively* - -### Advanced Prompting - - - - - - - -### Generation Workflows - -Invoke offers a number of different workflows for interacting with models to produce images. Each is extremely powerful on its own, but together provide you an unparalleled way of producing high quality creative outputs that align with your vision. - - - - Focuses on the key workflow of using a prompt to generate a new image. It includes other features that help control the generation process as well. - - - Provide an image as a reference (called the “initial image”), which provides more guidance around color and structure to the AI as it generates a new image. - - - An advanced AI-first image editing tool. Drag an image onto the canvas to regenerate elements, edit content or colors (**inpainting**), or extend the image with consistency and clarity (**outpainting**). - - - -### Improving Image Quality - - - 1. **Fine-tuning your prompt:** - - The more specific you are, the closer the image will turn out to what is in your head. Adding more details in the Positive or Negative Prompt can help add or remove parts of the image. You can also use advanced techniques like upweighting and downweighting to control the influence of specific words. Learn more in the [Prompting Guide](../prompting-guide) and [Prompting Syntax](../prompt-syntax). - - :::tip - If you're seeing poor results, try adding the things you don't like about the image to your negative prompt. E.g. *distorted, low quality, unrealistic, etc.* - ::: - - 2. **Explore different models:** - - Other models can produce different results due to the data they've been trained on. Each model has specific language and settings it works best with; a model's documentation is your friend here. Play around with some and see what works best for you! - - 3. **Increasing Steps:** - - The number of steps used controls how much time the model is given to produce an image, and depends on the "Scheduler" used. More steps tends to mean better results, but will take longer. We recommend at least 30 steps for most. - - 4. **Tweak and Iterate:** - - Remember, it's best to change one thing at a time so you know what is working and what isn't. Sometimes you just need to try a new image, and other times using a new prompt might be the ticket. - *For testing, consider turning off the "random" Seed. Using the same seed with the same settings will produce the same image, which makes it the perfect way to learn exactly what your changes are doing.* - - 5. **Explore Advanced Settings:** - - InvokeAI has a full suite of tools available to allow you complete control over your image creation process. Check out our [features docs](../../features/gallery) if you want to learn more. - - -## Terms & Concepts - -:::note - If you're interested in learning more, check out [this presentation](https://docs.google.com/presentation/d/1IO78i8oEXFTZ5peuHHYkVF-Y3e2M6iM5tCnc-YBfcCM/edit?usp=sharing) from one of our maintainers (@lstein). -::: - -### Stable Diffusion - -Stable Diffusion is a deep learning, text-to-image model that is the foundation of the capabilities found in InvokeAI. Since the release of Stable Diffusion, there have been many subsequent models created based on Stable Diffusion that are designed to generate specific types of images. - -### Prompts - -Prompts provide the models directions on what to generate. As a general rule of thumb, the more detailed your prompt is, the better your result will be. - -### Models - -Models are the magic that power InvokeAI. These files represent the output of training a machine on understanding massive amounts of images - providing them with the capability to generate new images using just a text description of what you'd like to see. - -Invoke offers a simple way to download several different models upon installation, but many more can be discovered online, including at [civitai.com](https://civitai.com). Each model can produce a unique style of output, based on the images it was trained on. - -:::note - Models that contain "inpainting" in the name are designed for use with the inpainting feature of the Unified Canvas. -::: - -### Schedulers & Steps - -**Schedulers** guide the process of removing noise (de-noising) from data. They determine: -1. The number of steps to take to remove the noise. -2. Whether the steps are random (stochastic) or predictable (deterministic). -3. The specific method (algorithm) used for de-noising. - -**Steps** represent the number of de-noising iterations each generation goes through. Schedulers can be intricate and there's often a balance to strike between how quickly they can de-noise data and how well they can do it. It's typically advised to experiment with different schedulers to see which one gives the best results. - -### Additional Concepts - - - - LoRAs are like a smaller, more focused version of models, intended to focus on training a better understanding of how a specific character, style, or concept looks. - - - Like LoRAs, embeddings assist with more easily prompting for certain characters, styles, or concepts. They are trained to update the relationship between a specific word (known as the "trigger") and the intended output. - - - ControlNets are neural network models that are able to extract key features from an existing image and use these features to guide the output of the image generation model. - - - A Variational Auto-Encoder (VAE) is an encode/decode model that translates the "latents" image produced during the image generation process to the large pixel images that we see. - - diff --git a/docs/src/content/docs/concepts/models.mdx b/docs/src/content/docs/concepts/models.mdx deleted file mode 100644 index ada15da80e8..00000000000 --- a/docs/src/content/docs/concepts/models.mdx +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Models -sidebar: - order: 8 ---- - -## Checkpoint and Diffusers Models - -The model checkpoint files (`*.ckpt`) are the Stable Diffusion "secret sauce". They are the product of training the AI on millions of captioned images gathered from multiple sources. - -Originally there was only a single Stable Diffusion weights file, which many people named `model.ckpt`. - -Today, there are thousands of models, fine tuned to excel at specific styles, genres, or themes. - -:::tip[Model Formats] - We also have two more popular model formats, both created by [HuggingFace](https://huggingface.co/): - - - `safetensors`: Single file, like `.ckpt` files. Prevents malware from lurking in a model. - - `diffusers`: Splits the model components into separate files, allowing very fast loading. - - InvokeAI supports all three formats. -::: - -## Starter Models - -When you first start InvokeAI, you'll see a popup prompting you to install some starter models from the Model Manager. Click the `Starter Models` tab to see the list. - -You'll find a collection of popular and high-quality models available for easy download. - -Some models carry license terms that limit their use in commercial applications or on public servers. It's your responsibility to adhere to the license terms. - -## Other Models - -There are a few ways to install other models: - -- **URL or Local Path**: Provide the path to a model on your computer, or a direct link to the model. Some sites require you to use an API token to download models, which you can [set up in the config file]. You can also paste a HuggingFace Repo ID here directly — it is detected and routed to the HuggingFace installer automatically. -- **HuggingFace**: Paste a HF Repo ID to install it. If there are multiple models in the repo, you'll get a list to choose from. Repo IDs look like this: `XpucT/Deliberate`. There is a copy button on each repo to copy the ID. -- **Scan Folder**: Scan a local folder for models. You can install all of the detected models in one click. - -### Diffusers models in HF repo subfolders - -HuggingFace repos can be structured in any way. Some model authors include multiple models within the same folder. - -In this situation, you may need to provide some additional information to identify the model you want, by adding `:subfolder_name` to the repo ID. - -:::note[Example] - Say you have a repo ID `monster-labs/control_v1p_sd15_qrcode_monster`, and the model you want is inside the `v2` subfolder. - - Add `:v2` to the repo ID and use that when installing the model: `monster-labs/control_v1p_sd15_qrcode_monster:v2` -::: - -[set up in the config file]: ../../configuration/invokeai-yaml - -## Ideogram 4 - -Ideogram 4 is an open-weight text-to-image model with a distinctive **structured JSON prompt**: instead of a single sentence, the model is trained to read an overall scene description plus a list of regions, each with a bounding box and its own text. Invoke assembles this JSON for you. - -### Installing Ideogram 4 - -The weights are gated on HuggingFace under a **non-commercial license**. Open the model page, accept the terms, and make sure your HuggingFace token is [set up in the config file] before installing. Two builds are available: - -- [`ideogram-ai/ideogram-4-nf4`](https://huggingface.co/ideogram-ai/ideogram-4-nf4) — nf4 quantized, **CUDA only**, fits in 24 GB VRAM. Recommended for NVIDIA GPUs. -- [`ideogram-ai/ideogram-4-fp8`](https://huggingface.co/ideogram-ai/ideogram-4-fp8) — fp8 quantized, runs on **any device**, with higher memory use. - -Paste either repo ID into the Model Manager's HuggingFace / URL field to install. - -### Prompting Ideogram 4 - -When an Ideogram 4 model is selected, Invoke builds the structured JSON prompt automatically: - -- The **positive prompt** becomes the overall scene description. -- Each enabled **Regional Guidance** layer on the Canvas contributes one element: its drawn box becomes the region's bounding box and its prompt becomes that region's description. Draw a box where you want something and describe it there. -- To drive the model directly, paste a **raw JSON** object into the prompt box — anything starting with `{` is passed through unchanged. - -The exact JSON that was encoded is stored in the image metadata as **Structured Caption**, and can be recalled straight back into the prompt box from the metadata viewer. - -:::note[No negative prompt] -Ideogram 4 does not use a negative prompt — it has a dedicated unconditional branch instead, so the negative prompt box has no effect. -::: - -### Generation settings - -- **Sampler Preset** — the primary quality/speed control. `Quality (48 steps)`, `Default (20 steps)`, and `Turbo (12 steps)` each bundle a step count, a guidance schedule, and the schedule shift. -- **Advanced** overrides (all optional, leave on *Auto* to use the preset's values): **Steps**, **Guidance Scale**, **Schedule Shift (mu)**, and a **Color Palette** that biases the generated colors. - -:::caution[Built-in safety filter] -Ideogram 4 ships with a **content safety filter baked into the released model weights**. When it triggers, the model outputs a placeholder image reading *"Image blocked by safety filter"* instead of your image. This is the model's own filter — it is **not** Invoke's NSFW checker (it fires even with the NSFW checker off) and cannot be disabled from Invoke. - -The filter is known to **false-positive on completely benign prompts**, and the quantized builds can trigger it more readily. Invoke always sends the model a structured JSON prompt, which tends to trip the filter less than plain text; if you hit it, rephrasing or adjusting the prompt usually clears it. -::: - -## Editing model metadata - -Every model has an editable **Source URL** field alongside its name and description. Use it to record where a model came from — for example a Civitai or HuggingFace page — independent of how it was originally installed. The URL is editable from the model's **Edit** view and appears as a clickable link in the model header once set. Models without a URL simply hide the field. - -This is purely metadata: the URL has no effect on loading and is not used to refresh or reinstall the model. It is mainly useful for going back to the model's documentation, license, or example prompts later. - -## Bulk actions in the Model Manager - -The Model Manager supports multi-selection for batch operations. - -- **Select multiple models** by clicking with **Ctrl** (Windows / Linux) or **Cmd** (macOS) held, or by using the checkboxes on each row. A sticky header at the top shows the current selection count and is always visible while you scroll. -- Open the **Actions** dropdown for the selection. The available actions are: - - **Delete Models** — removes every selected model in a single confirmation step. Partial failures (e.g. permission issues) are reported per-model in the result toast. - - **Reidentify Models** — re-probes every selected model, updating fields that depend on the file contents (type, base, format, variant, etc.). This is the bulk version of the per-model reidentify action. - -:::caution[Reidentify resets custom settings] -Reidentifying a model re-derives its configuration from the file on disk. Any custom settings you've adjusted on those models — default settings, descriptions, trigger phrases — may be overwritten. The confirmation modal warns you about this before running. -::: - -Both actions handle partial failures: if some models succeed and others fail, the toast lists succeeded and failed counts and the list view updates immediately for the ones that worked. - -## Finding orphaned models - -If a model file is deleted or moved outside the Model Manager, its database entry sticks around. To find these orphaned entries: - -1. Open the Model Manager. -2. Open the **type filter** dropdown and pick **Missing Files**. -3. The list now shows only models whose files are no longer present on disk. Each one also displays a **Missing Files** badge in its row. - -Orphaned models are automatically excluded from selection dropdowns (main model, LoRA, VAE, etc.), so you cannot accidentally pick one for generation. Use the [bulk delete action](#bulk-actions-in-the-model-manager) to clean them out in one step. - -## Synchronizing orphaned model directories - -The **Missing Files** filter finds database records whose files are gone. InvokeAI also has a separate sync workflow for the opposite situation: model directories that still exist on disk but are not referenced in the database. - -This can happen after a failed import, a manual database edit, or deleting a model record while leaving files behind. The sync workflow scans the models directory for top-level folders containing model files with common model extensions, including `.safetensors`, `.ckpt`, `.pt`, `.pth`, `.bin`, `.onnx`, and `.gguf`. - -To review these directories: - -1. In multi-user mode, sign in as an administrator. In single-user mode, the Model Manager controls are available by default. -2. Open the Model Manager. -3. Click **Sync Models** to scan for orphaned model directories. -4. Review each reported relative directory path, contained model files, and total size before deleting anything. - -:::caution[Deletion removes directories] -Deleting an orphaned model directory removes the entire reported directory from disk. The server deletes it directly with recursive directory deletion, so make sure the directory contains only files you intend to remove. -::: - -Only administrators can use this workflow in multi-user mode. The underlying API is `/api/v2/models/sync/orphaned`; API results also include the absolute path for each reported directory. - -## Exporting and Importing Model Settings - -Each installed model has an **Export Settings** and **Import Settings** action in the Model Manager. Use these to back up a model's configuration, move it to another install, or share a curated setup with someone else. - -### What gets exported - -The exported `.json` file captures the configuration you have set on the model, not the model weights themselves: - -- `default_settings` — steps, CFG / guidance, scheduler, dimensions, FP8 storage toggle, VAE precision, etc. -- `trigger_phrases` — for LoRAs and similar. -- `cpu_only` — for encoder-type models. -- `name`, `description`, `source_url` — the model's identifying metadata. -- `cover_image` — the model's thumbnail, embedded as a base64 data URL. - -Fields you have not set are omitted from the file. The format is forward and backward compatible: older clients ignore newer fields, and a file produced by a newer version still imports cleanly into an older one (it just skips the fields it does not understand). - -### Importing - -Importing applies the JSON to the currently selected model: - -- `default_settings`, `trigger_phrases`, `cpu_only`, `name`, `description`, and `source_url` are applied via the normal model update path. Any field that the target model type does not support (e.g. `cpu_only` on a model that has no such setting) is listed in a "skipped" toast — everything else still applies. -- `cover_image` is uploaded and set as the model's thumbnail. - -Imports are validated before they run. The file is rejected if `source_url` is not an `http(s)://` URL or if `cover_image` is not a valid image data URL — so a malformed or hand-edited file cannot quietly poison a model's configuration. - -### Typical workflows - -- **Back up a model you've spent time tuning** so you can restore its settings after a reinstall, or roll back after experimenting. -- **Copy settings between two installs of the same model** — e.g. between a desktop and a workstation. -- **Share a curated setup** (name, description, thumbnail, default steps / CFG / scheduler, trigger phrases) for a model you have configured well. diff --git a/docs/src/content/docs/concepts/nodes-workflows.mdx b/docs/src/content/docs/concepts/nodes-workflows.mdx deleted file mode 100644 index 019d999bb35..00000000000 --- a/docs/src/content/docs/concepts/nodes-workflows.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Nodes and Workflows -sidebar: - order: 7 ---- - -import { Card, CardGrid } from '@astrojs/starlight/components'; - -## What are Nodes? - -A **Node** is simply a single operation that takes in inputs and returns outputs. Multiple nodes can be linked together to create more complex functionality. All InvokeAI features are added through nodes. - -With nodes, you can easily extend the image generation capabilities of InvokeAI and build workflows that suit your specific needs. - -### Anatomy of a Node - -Individual nodes are made up of the following: - - - - Edge points on the **left side** of the node window where you connect outputs from other nodes. - - - Edge points on the **right side** of the node window where you connect to inputs on other nodes. - - - Various options which are either manually configured, or overridden by connecting an output from another node to the input. - - diff --git a/docs/src/content/docs/concepts/parameters.mdx b/docs/src/content/docs/concepts/parameters.mdx deleted file mode 100644 index c4882de919e..00000000000 --- a/docs/src/content/docs/concepts/parameters.mdx +++ /dev/null @@ -1,143 +0,0 @@ ---- -title: Generation Parameters -lastUpdated: 2026-02-20 -sidebar: - order: 6 ---- - -import { Card, CardGrid, Steps } from '@astrojs/starlight/components'; - -# Sampler Convergence - -As features keep increasing, making the right choices for your needs can become increasingly difficult. What sampler to use? And for how many steps? Do you change the CFG value? Do you use prompt weighting? Do you allow variations? - -Even once you have a result, do you blend it with other images? Pass it through `img2img`? With what strength? Do you use inpainting to correct small details? Outpainting to extend cropped sections? - -The purpose of this series of documents is to help you better understand these tools, so you can make the best out of them. Feel free to contribute with your own findings! - -In this document, we will talk about **sampler convergence**. - - - Looking for a short version? Here is the summary: - - - Results converge as steps (`-s`) are increased (except for `K_DPM_2_A` and `K_EULER_A`). Often at ≥ `-s100`, but may require ≥ `-s700`. - - Producing a batch of candidate images at low (`-s8` to `-s30`) step counts can save you hours of computation. - - `K_HEUN` and `K_DPM_2` converge in fewer steps (but are slower per step). - - `K_DPM_2_A` and `K_EULER_A` incorporate a lot of creativity and variability. - - -## Sampler Performance Overview - - - - *(Tested on M1 Max 64GB, 512x512, 3 sample average)* - - | Sampler | it/s | - | :--- | :--- | - | `DDIM` | 1.89 | - | `PLMS` | 1.86 | - | `K_EULER` | 1.86 | - | `K_LMS` | **1.91** (Fastest) | - | `K_EULER_A` | 1.86 | - | `K_HEUN` | 0.95 *(Slower)* | - | `K_DPM_2` | 0.95 *(Slower)* | - | `K_DPM_2_A` | 0.95 *(Slower)* | - - - - For most use cases, `K_LMS`, `K_HEUN` and `K_DPM_2` are the best choices. - - While `K_HEUN` and `K_DPM_2` run half as fast, they tend to converge twice as quickly as `K_LMS`. - - At very low steps (≤ `-s8`), `K_HEUN` and `K_DPM_2` are not recommended. Use `K_LMS` instead. - - For high variability between steps, use `K_EULER_A` (which runs twice as fast as `K_DPM_2_A`). - - - ---- - -## Sampler Results by Subject - -Let's start by choosing a prompt and using it with each of our 8 samplers, running it for 10, 20, 30, 40, 50 and 100 steps. - -### Anime -> `"an anime girl" -W512 -H512 -C7.5 -S3031912972` - -![Anime Comparison Grid](https://user-images.githubusercontent.com/50542132/191868725-7f7af991-e254-4c1f-83e7-bed8c9b2d34f.png) - -Immediately, you can notice results tend to converge — that is, as `-s` (step) values increase, images look more and more similar until there comes a point where the image no longer changes. - -You can also notice how `DDIM` and `PLMS` eventually tend to converge to K-sampler results as steps are increased. Among K-samplers, `K_HEUN` and `K_DPM_2` seem to require the fewest steps to converge, and even at low step counts they are good indicators of the final result. Finally, `K_DPM_2_A` and `K_EULER_A` seem to do a bit of their own thing and don't keep much similarity with the rest of the samplers. - -### Nature -Now, these results seem interesting, but do they hold for other topics? Let's try! - -> `"valley landscape wallpaper, d&d art, fantasy, painted, 4k, high detail, sharp focus, washed colors, elaborate excellent painted illustration" -W512 -H512 -C7.5 -S1458228930` - -![Nature Comparison Grid](https://user-images.githubusercontent.com/50542132/191868763-b151c69e-0a72-4cf1-a151-5a64edd0c93e.png) - -With nature, you can see how initial results are even more indicative of the final result — more so than with characters/people. `K_HEUN` and `K_DPM_2` are again the quickest indicators, almost right from the start. Results also converge faster (e.g. `K_HEUN` converged at `-s21`). - -### Food -> `"a hamburger with a bowl of french fries" -W512 -H512 -C7.5 -S4053222918` - -![Food Comparison Grid](https://user-images.githubusercontent.com/50542132/191868898-98801a62-885f-4ea1-aee8-563503522aa9.png) - -Again, `K_HEUN` and `K_DPM_2` take the fewest number of steps to be good indicators of the final result. `K_DPM_2_A` and `K_EULER_A` seem to incorporate a lot of creativity/variability, capable of producing rotten hamburgers, but also of adding lettuce to the mix. And they're the only samplers that produced an actual 'bowl of fries'! - -### Animals -> `"grown tiger, full body" -W512 -H512 -C7.5 -S3721629802` - -![Animal Comparison Grid](https://user-images.githubusercontent.com/50542132/191868870-9e3b7d82-b909-429f-893a-13f6ec343454.png) - -`K_HEUN` and `K_DPM_2` once again require the least number of steps to be indicative of the final result (around `-s30`), while other samplers are still struggling with several tails or malformed back legs. - -It also takes longer to converge (for comparison, `K_HEUN` required around 150 steps to converge). This is normal, as producing human/animal faces/bodies is one of the things the model struggles the most with. For these topics, running for more steps will often increase coherence within the composition. - -### People -> `"Ultra realistic photo, (Miranda Bloom-Kerr), young, stunning model, blue eyes, blond hair, beautiful face, intricate, highly detailed, smooth, art by artgerm and greg rutkowski and alphonse mucha, stained glass" -W512 -H512 -C7.5 -S2131956332`. *(This time, we will go up to 300 steps).* - -![People Comparison Grid 1](https://user-images.githubusercontent.com/50542132/191871743-6802f199-0ffd-4986-98c5-df2d8db30d18.png) - -Observing the results, it again takes longer for all samplers to converge (`K_HEUN` took around 150 steps), but we can observe good indicative results much earlier (see: `K_HEUN`). Conversely, `DDIM` and `PLMS` are still undergoing moderate changes (see: lace around her neck), even at `-s300`. - -In fact, as we can see in this other experiment, some samplers can take 700+ steps to converge when generating people. - -![People Comparison Grid 2](https://user-images.githubusercontent.com/50542132/191992123-7e0759d6-6220-42c4-a961-88c7071c5ee6.png) - -Note also the point of convergence may not be the most desirable state (e.g. you might prefer an earlier version of the face that is more rounded), but it will probably be the most coherent regarding arms/hands/face attributes. You can always merge different images with a photo editing tool and pass it through `img2img` to smoothen the composition. - ---- - -## Batch Generation Speedup - -This realization about convergence is very useful because it means you don't need to create a batch of 100 images (`-n100`) at `-s100` just to choose your favorite 2 or 3 images. - -You can produce the same 100 images at `-s10` to `-s30` using a K-sampler (since they converge faster), get a rough idea of the final result, choose your 2 or 3 favorite ones, and then run `-s100` on those specific images to polish details. This technique is **3-8x as quick**. - -:::tip[Time Savings Example] - Assuming 60 seconds per 100 steps: - - - **Method A:** 60s * 100 images = **6000s** (100 images at `-s100`, manually picking 3 favorites). Total time: **1 hour and 40 minutes.** - - **Method B:** 6s * 100 images + 60s * 3 images = **780s** (100 images at `-s10`, manually picking 3 favorites, and running those 3 at `-s100` to polish details). Total time: **13 minutes.** -::: - -## Three Key Takeaways - -Finally, it is relevant to mention that, in general, there are 3 important moments in the process of image formation as steps increase: - - -1. **The Indicator Stage:** - The earliest point at which an image becomes a good indicator of the final result. This is useful for batch generation at low step values to preview outputs before committing to higher steps. -2. **The Coherence Stage:** - The point at which an image becomes coherent, even if different from the final converged result. This is useful for low-step batch generation where quality is improved via other techniques (like inpainting) rather than raw step count. -3. **The Convergence Stage:** - The point at which an image fully converges and stops changing. - - -:::note[Workflow Dictates Strategy] - Remember that your workflow/strategy should define your optimal number of steps, even for the same prompt and seed. For example, if you seek full convergence, you may run `K_LMS` for `-s200`. However, running `K_LMS` for `-s20` (taking one-tenth the time) may perform just as well if your workflow includes adding small missing details via `img2img`. -::: - -![Low Step Sampler Comparison](https://user-images.githubusercontent.com/50542132/192046823-2714cb29-bbf3-4eb1-9213-e27a0963905c.png) diff --git a/docs/src/content/docs/concepts/prompt-syntax.mdx b/docs/src/content/docs/concepts/prompt-syntax.mdx deleted file mode 100644 index 5d26267c57a..00000000000 --- a/docs/src/content/docs/concepts/prompt-syntax.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -title: Prompting Syntax -lastUpdated: 2026-03-30 -sidebar: - order: 3 ---- - -import { Card, LinkCard, CardGrid } from '@astrojs/starlight/components'; - - - - - - - -InvokeAI supports Compel-style prompt weighting and prompt functions for `SD 1.5` and `SDXL` text conditioning workflows. Recent model families, including `FLUX`, `Z-Image`, `CogView4`, and `Qwen Image`, bypass Compel and do not use the syntax documented on this page. This page documents syntax for those Compel-based workflows only. If you want general advice on writing better prompts, start with [Prompting Guide](../prompting-guide). - -:::note[Compatibility note] - If a weighted prompt seems to be ignored, check whether you are using an `SD 1.5` or `SDXL` workflow. Compel syntax on this page does not apply to newer model families such as `FLUX`, `Z-Image`, `CogView4`, and `Qwen Image`. -::: - -## Quick reference - - - - Increase a single word: `trees+` - - Decrease a single word: `fog-` - - Weight a phrase: `(golden hour light)+` - - Use an exact numeric weight: `(cinematic lighting)1.25` - - Nest weights: `(portrait with (blue eyes)1.3)1.1` - - Blend prompts: `("portrait photo", "oil painting").blend(0.7, 0.3)` - - Conjoin clauses: `("red silk dress", "studio portrait", "soft rim light").and()` - - Escape literal parentheses: `colored pencil \(medium\)` - - -## Attention weighting with `+` and `-` - -Append `+` to increase influence, or `-` to reduce it. - -```text -freckles+ -background crowd- -(soft rim light)++ -``` - -Rules of thumb: - -- Single words can be weighted directly. -- Multi-word phrases should be wrapped in parentheses. -- Each additional `+` compounds upward. -- Each additional `-` compounds downward in roughly 10% steps. - -:::tip[Start small] - One or two steps is usually enough. Extreme weighting can overpower the rest of the prompt. -::: - -## Numeric weights - -Use numeric weights when you want precise control instead of repeated plus or minus markers. - -```text -(cinematic lighting)1.25 -(background crowd)0.8 -(sharp focus)1.1 -``` - -Guidelines: - -- `1` is neutral. -- Values greater than `1` increase emphasis. -- Values between `0` and `1` reduce emphasis. -- Wrap the weighted phrase in parentheses. - -## Grouping and nesting - -You can group phrases and apply weight to the whole group, then nest another weighted phrase inside it. - -```text -(portrait with (blue eyes)1.3)1.1 -``` - -In this example, the outer group strengthens the whole phrase, and the inner group gives `blue eyes` even more emphasis. - -## Blend prompts with `.blend()` - -Use `.blend()` to mix the meaning of two or more prompts. - -```text -("portrait photo, 85mm lens", "oil painting, visible brushstrokes").blend(0.7, 0.3) -``` - -This is most useful for combining concepts or styles that you want balanced deliberately. - -Tips: - -- Provide one weight for each prompt argument. -- Keeping the weights near a total of `1` makes the result easier to reason about. -- Quoted arguments are the safest choice, especially when the prompts contain commas. - -## Combine clauses with `.and()` - -Use `.and()` when you want separate prompt clauses encoded individually instead of as one long comma-separated sentence. - -```text -("red silk dress", "studio portrait", "soft rim light").and() -``` - -This can behave differently from: - -```text -red silk dress, studio portrait, soft rim light -``` - -If a normal prompt keeps collapsing ideas together, `.and()` is worth testing. - -## Escape literal parentheses - -Unescaped parentheses are treated as prompt syntax. If you want actual parentheses in the text, escape them with backslashes. - -```text -colored pencil \(medium\) -portrait \(realistic\) (high quality)1.2 -A bear \(with razor-sharp teeth\) in a forest -``` - -Use unescaped parentheses only when you mean grouping or weighting. - -## Related pages - -- For practical prompt-writing advice, read [Prompting Guide](../prompting-guide). -- For prompt expansion and permutations, read [Dynamic Prompting](../dynamic-prompting). diff --git a/docs/src/content/docs/concepts/prompting-guide.mdx b/docs/src/content/docs/concepts/prompting-guide.mdx deleted file mode 100644 index 3dbb27a9fbc..00000000000 --- a/docs/src/content/docs/concepts/prompting-guide.mdx +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: Prompting Guide -lastUpdated: 2026-03-30 -sidebar: - order: 2 ---- - -import { Card, CardGrid, Steps, LinkCard } from '@astrojs/starlight/components'; - - - - - - - -Prompting in InvokeAI works best when you describe the image clearly, then refine only the parts that matter. This page focuses on practical prompt-writing habits. - - - - Start with the main thing you want to see: a character, object, scene, or action. - - - Add the visual language: photograph, watercolor, oil painting, 3D render, anime illustration, and so on. - - - Describe the camera angle, framing, lighting, environment, color palette, or mood that will shape the image. - - - Add a few high-value quality cues such as fabric texture, shallow depth of field, natural skin texture, or painterly brushwork. - - - -A simple pattern that works well is: - -`subject, style or medium, lighting or composition, a few important details` - -Not every prompt needs every category. Start simple, then add detail only when the model needs more direction. - -## Positive and negative prompts - - - - Use the positive prompt to describe what you want the model to create. Put the most important idea early and keep the wording concrete. - - - Use the negative prompt to remove recurring problems or unwanted traits. Keep it short and targeted instead of pasting a giant list into every generation. - - - -Good negative prompts usually name specific failure modes: `blurry`, `distorted hands`, `low detail`, `extra limbs`. - -:::tip[Negative prompts are strong] - A negative term can suppress nearby concepts too. If you negate something broad like `green` or `moss`, you may also weaken grass, foliage, or other related ideas. -::: - -## A practical prompting workflow - - - 1. Start with the core image - - Write the clearest version of the image you want before adding stylistic extras. - - 2. Add style and composition - - Once the subject is right, add medium, lens, lighting, mood, background, or framing details. - - 3. Test with a fixed seed - - When you are learning what a prompt change does, keep the seed stable so you can compare results directly. - - 4. Change one thing at a time - - If you add five new terms at once, you will not know which one helped. - - 5. Escalate only when needed - - If the result is close but one element is too weak or too strong, move to [Prompting Syntax](../prompt-syntax) for weighting. If you want lots of variations, use [Dynamic Prompting](../dynamic-prompting). - - -Here is the same idea refined in stages: - -```text -portrait of a woman - -portrait of a woman, studio photograph, soft key light - -portrait of a woman, studio photograph, soft key light, 85mm lens, shallow depth of field, natural skin texture -``` - -## Write for the model you are using - -The same prompt can behave very differently across models. - -- Photo-oriented models respond well to camera, lens, lighting, and texture language. -- Illustration models often respond better to medium, art direction, and shape language. -- Specialty models may expect specific trigger words, subjects, or styles from their own model card. -- If a prompt works beautifully on one model and poorly on another, that does not always mean the prompt is bad. The model may just speak a different visual language. - -## When advanced syntax helps - -Reach for advanced syntax when a normal comma-separated prompt is almost right, but you need more control. - -- Use [Prompting Syntax](../prompt-syntax) when one term needs more or less influence. -- Use `.blend()` when you want to mix concepts or styles deliberately. -- Use `.and()` when you want separate prompt clauses encoded individually. -- Use [Dynamic Prompting](../dynamic-prompting) when you want many prompt variations from one template. - -## Common mistakes - -- Packing too many unrelated ideas into one prompt. -- Using long generic quality-word lists before you know the base prompt works. -- Treating the negative prompt as a trash can for every bad outcome. -- Expecting identical behavior across models, schedulers, and workflows. -- Changing prompt, model, seed, and settings all at once while troubleshooting. - -## Example prompts - -### Photographic portrait - -**Positive prompt** - -```text -editorial portrait of a woman in a charcoal coat, studio photograph, soft key light, subtle rim light, 85mm lens, shallow depth of field, natural skin texture -``` - -**Negative prompt** - -```text -blurry, low detail, waxy skin, extra fingers -``` - -### Environment concept art - -**Positive prompt** - -```text -ancient stone temple built into a cliffside, fantasy concept art, misty sunrise, towering scale, moss-covered stairs, cinematic atmosphere -``` - -**Negative prompt** - -```text -flat lighting, low contrast, muddy details -``` - -### Product-style render - -**Positive prompt** - -```text -sleek ceramic teapot on a matte stone surface, product photography, clean studio lighting, soft shadow, high detail, minimal background -``` - -**Negative prompt** - -```text -cluttered background, distortion, duplicate objects -``` - -### Stylized illustration - -**Positive prompt** - -```text -fox courier crossing a rainy city street, storybook illustration, bold shapes, glowing shop signs, reflective pavement, warm and cool color contrast -``` - -**Negative prompt** - -```text -photorealistic, dull colors, low detail -``` diff --git a/docs/src/content/docs/configuration/01.invokeai-yaml.mdx b/docs/src/content/docs/configuration/01.invokeai-yaml.mdx new file mode 100644 index 00000000000..9e12d25c143 --- /dev/null +++ b/docs/src/content/docs/configuration/01.invokeai-yaml.mdx @@ -0,0 +1,340 @@ +--- +title: YAML Config +sidebar: + order: 1 +--- + +import { FileTree } from '@astrojs/starlight/components' +import SettingsDocs from '@lib/components/SettingsDocs.astro' + +Runtime settings, including the location of files and directories, memory usage, and performance, are managed via the `invokeai.yaml` config file or environment variables. A subset of settings may be set via commandline arguments. + +Settings sources are used in this order: + +- CLI args +- Environment variables +- `invokeai.yaml` settings +- Fallback: defaults + +### InvokeAI Root Directory + +On startup, InvokeAI searches for its "root" directory. This is the directory that contains models, images, the database, and so on. It also contains a configuration file called `invokeai.yaml`. + + + - models/ + - outputs/ + - databases/ + - workflow_thumbnails/ + - style_presets/ + - nodes/ + - configs/ + - invokeai.example.yaml + - **invokeai.yaml** + + +InvokeAI searches for the root directory in this order: + +1. The `--root ` CLI arg. +2. The environment variable INVOKEAI_ROOT. +3. The directory containing the currently active virtual environment. +4. Fallback: a directory in the current user's home directory named `invokeai`. + +### InvokeAI Configuration File + +Inside the root directory, we read settings from the `invokeai.yaml` file. + +It has two sections - one for internal use and one for user settings: + +```yaml +# Internal metadata - do not edit: +schema_version: 4.0.2 + +# Put user settings here - see https://invoke.ai/configuration/invokeai-yaml/: +host: 0.0.0.0 # serve the app on your local network +models_dir: D:\invokeai\models # store models on an external drive +precision: float16 # always use fp16 precision +``` + +The settings in this file will override the defaults. You only need +to change this file if the default for a particular setting doesn't +work for you. + +You'll find an example file next to `invokeai.yaml` that shows the default values. + +Some settings, like [Model Marketplace API Keys], require the YAML +to be formatted correctly. Here is a [basic guide to YAML files]. + +#### Custom Config File Location + +You can use any config file with the `--config` CLI arg. Pass in the path to the `invokeai.yaml` file you want to use. + +Note that environment variables will trump any settings in the config file. + +#### Model Marketplace API Keys + +Some model marketplaces require an API key to download models. You can provide a URL pattern and appropriate token in your `invokeai.yaml` file to provide that API key. + +The pattern can be any valid regex (you may need to surround the pattern with quotes): + +```yaml +remote_api_tokens: + # Any URL containing `models.com` will automatically use `your_models_com_token` + - url_regex: models.com + token: your_models_com_token + # Any URL matching this contrived regex will use `some_other_token` + - url_regex: '^[a-z]{3}whatever.*\.com$' + token: some_other_token +``` + +The provided token will be added as a `Bearer` token to the network requests to download the model files. As far as we know, this works for all model marketplaces that require authorization. + +:::tip[Hugging face Models] +If you get an error when installing a HF model using a URL instead of repo id, you may need to [set up a HF API token](https://huggingface.co/settings/tokens) and add an entry for it under `remote_api_tokens`. Use `huggingface.co` for `url_regex`. +::: + +#### Model Hashing + +Models are hashed during installation, providing a stable identifier for models across all platforms. Hashing is a one-time operation. + +```yaml +hashing_algorithm: blake3_single # default value +``` + +You might want to change this setting, depending on your system: + +- `blake3_single` (default): Single-threaded - best for spinning HDDs, still OK for SSDs +- `blake3_multi`: Parallelized, memory-mapped implementation - best for SSDs, terrible for spinning disks +- `random`: Skip hashing entirely - fastest but of course no hash + +During the first startup after upgrading to v4, all of your models will be hashed. This can take a few minutes. + +Most common algorithms are supported, like `md5`, `sha256`, and `sha512`. These are typically much, much slower than either of the BLAKE3 variants. + +#### Path Settings + +These options set the paths of various directories and files used by InvokeAI. Any user-defined paths should be absolute paths. + +#### Multi-GPU Generation + +On a machine with more than one GPU, InvokeAI can run several generation sessions at the same time — one per GPU — instead of processing the queue one job at a time. In multi-user deployments with the default `session_queue_mode: round_robin`, jobs are distributed fairly across users, so a single user's large batch cannot monopolize every GPU while others wait. + +This is controlled by the `generation_devices` setting: + +```yaml +generation_devices: auto # default value +``` + +| Value | Behavior | +| -------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| `auto` | Use every available CUDA GPU, running one generation session per GPU concurrently. This is the default. Exception: if the legacy `device` setting is pinned to a specific device, `auto` uses only that device (see Notes below). | +| `[cuda:0,cuda:1]` | Use the specific devices listed, one session per device. Useful for reserving a GPU for other work. | +| `[cuda:0]` | Use a single specific device. Generation runs serially, as it did before multi-GPU support. | + +Each entry in the list must be one of `cpu`, `cuda`, `mps`, or `cuda:N`, where `N` is a zero-based device number (`cuda:0` is the first GPU, `cuda:1` the second, and so on). The list may not be empty — use `auto` (or a single-device list) instead. + +```yaml +# Use the first and third GPUs, leaving the second free for other tasks +generation_devices: [cuda:0, cuda:2] +``` + +Notes: + +- If the legacy `device` setting is pinned to a specific device (e.g. `device: cuda:1`), `auto` resolves to that single device rather than every GPU. This preserves the behavior of installs that pinned `device` before multi-GPU support existed (for example, to keep generation off a display GPU). An explicit `generation_devices` list overrides `device`. +- On a system without a CUDA GPU, `auto` resolves to the single best available device (`mps` on Apple Silicon, otherwise `cpu`), so generation runs serially. +- Each active GPU gets its own model cache, but the CPU (system RAM) copy of each model's weights is shared between the caches, so a model loaded on several GPUs is held in RAM only once. Running many GPUs in parallel can still increase RAM usage (different GPUs may hold different models), so ensure you have ample system memory before enabling a large device list. +- When a GPU finishes a job, it prefers queued jobs whose models it already has loaded (among the same user's equal-priority jobs) to avoid slow model reloads. Set `session_queue_mode: FIFO` to disable this reordering and enforce strict submission order. +- Duplicate entries are ignored; `[cuda:0, cuda:0]` is treated as `[cuda:0]`. +- You can restrict which physical GPUs InvokeAI sees with the `CUDA_VISIBLE_DEVICES` environment variable. When set, `auto` only enumerates the visible subset, and `cuda:N` indices refer to positions within that subset. + +During parallel generation, the progress display shows one progress bar per active session, stacked vertically, each disappearing as its session completes. + +#### Text Encoder Offload to Idle GPUs + +When more than one GPU is configured for generation but not all of them are busy, InvokeAI can run a session's text/prompt encoder on a currently-idle GPU instead of the GPU running its denoise pipeline. This avoids evicting the denoise model from VRAM just to make room for the encoder, and lets the cached encoder be reused across generations — making repeated generations noticeably smoother. + +This is controlled by the `offload_text_encoders_to_idle_gpus` setting: + +```yaml +offload_text_encoders_to_idle_gpus: true # default value +``` + +| Value | Behavior | +| ------- | ---------------------------------------------------------------------------------------------------------------- | +| `true` | Run text encoders on an idle GPU when one is available. This is the default. | +| `false` | Always run text encoders on the same GPU as the rest of the pipeline (the behavior before this feature existed). | + +Notes: + +- This has no effect unless at least two `generation_devices` are configured. On a single device — or when every GPU is already busy with its own session — encoders run on the session's own GPU, exactly as if the setting were `false`. +- It is purely a placement optimization and does not change generated images. +- A borrowed GPU is used exclusively for the encoder while it runs, so it never interferes with a generation session running on that same GPU. + +#### Prompt Tools and Image Indexing on Multiple GPUs + +**Expand Prompt**, **Image to Prompt** and image embedding for the image map and search by image run outside the generation queue. With two or more GPUs configured, each such request takes an idle GPU for as long as it runs — the first idle one in `generation_devices` order — so it no longer competes for memory with a generation running on another GPU. While a GPU is lent this way, queued generations start on the other GPUs rather than waiting for it, and these requests never take every GPU at once, so the queue always has one to start on. When no GPU can be taken, the request runs on the default device as it did before. There is no setting for this, and a single-GPU install behaves as it always has. + +Background indexing of new images already waits until no generation is running, so it simply takes the first idle GPU; it is search by image that benefits during a generation. + +#### Image Subfolder Strategy + +By default, generated images are stored in a single flat directory under `outputs/images/`. The `image_subfolder_strategy` setting lets you organize newly-created images into subfolders automatically. You can edit this setting in `invokeai.yaml` or, as an admin user, in the Settings panel. + +```yaml +image_subfolder_strategy: flat # default value +``` + +Available strategies: + +| Strategy | Example Path | Description | +| -------- | -------------------------------------- | ------------------------------------------------------------------------------------------------- | +| `flat` | `outputs/images/abc123.png` | Store images directly in the images directory. | +| `date` | `outputs/images/2026/03/17/abc123.png` | Organize images by creation date. | +| `type` | `outputs/images/general/abc123.png` | Organize images by image category. | +| `hash` | `outputs/images/ab/abc123.png` | Use the first two characters of the image UUID for filesystem performance with large collections. | + +Changing this setting only affects newly-created images. Existing images remain in their current locations unless you run [Image Storage Maintenance](/users-guide/gallery/image-storage-maintenance/). + +#### Response Compression + +API responses (JSON, HTML, JS, CSS, SVG) are gzipped before being sent. Already-compressed responses — PNG, WebP, JPEG, MP4 — are passed through untouched, since compressing them costs CPU and returns a body no smaller than the original. + +Compression runs on the server's event loop, which means it blocks *everything else* for its duration: no other request is served and no progress event is delivered while it runs. The `http_compression_level` setting controls that trade-off: + +```yaml +http_compression_level: 9 # default value +``` + +| Value | Behavior | +| ------ | ---------------------------------------------------------------------------------------------- | +| `0` | No compression. Responses are sent as-is and the compression middleware is not installed. | +| `1` | Fastest compression, slightly larger output. | +| `2`–`8` | Progressively slower, marginally smaller. | +| `9` | Smallest output, by far the slowest. This is the default. | + +The setting can also be given as the `INVOKEAI_HTTP_COMPRESSION_LEVEL` environment variable. + +If the UI feels sluggish while a large library is being browsed, lowering this is one of the cheapest wins available. Measured on the image-name list of a 200,000-image library (8.48 MB of JSON): + +| Level | Time | Output | +| ----- | ------: | --------: | +| `1` | 16.4 ms | 6.1% of input | +| `6` | 36.1 ms | 5.9% of input | +| `9` | 90.2 ms | 5.7% of input | + +Level 9 spends 5.5× the event-loop time to save 0.4 percentage points of bandwidth. On a locally-served install the bandwidth is free and the stall is not, so `http_compression_level: 1` is usually the better setting there — the default stays at `9` only so that upgrading does not silently change how anyone's install behaves. + +:::tip[Behind a reverse proxy] +If you serve InvokeAI through nginx, Caddy, or similar, set `http_compression_level: 0` and let the proxy compress instead. The proxy does that work in its own process rather than on InvokeAI's event loop, and it avoids compressing the same bytes twice. +::: + +#### Database Durability + +Every write to InvokeAI's SQLite database is committed to disk before the request returns. How hard that commit is pushed to the physical disk is controlled by `db_synchronous`: + +```yaml +db_synchronous: full # default value +``` + +| Value | Behavior | +| -------- | ----------------------------------------------------------------------------------------------------------- | +| `full` | Every commit is flushed to disk before it is acknowledged. What InvokeAI has always used, and SQLite's default on standard builds. | +| `normal` | Commits are acknowledged without waiting for the flush. Much faster; a power loss may lose the last few. | + +The setting can also be given as the `INVOKEAI_DB_SYNCHRONOUS` environment variable. + +**`normal` cannot corrupt the database, as long as WAL is in effect.** InvokeAI runs SQLite in WAL mode, and WAL guarantees a consistent database either way. What `normal` gives up is the *most recent transactions* if the machine loses power or the OS crashes mid-write — a just-written image record or a queue status. The image file itself survives on disk, but nothing rebuilds the row, and the gallery is driven by the database: the image is simply gone from it. The gallery orphan scan does not heal this — it *archives* record-less files out of `outputs/` so you can delete them or re-import them, which takes the separate `invokeai/frontend/install/import_images.py` script. + +Running the database on a network share is not a supported configuration -- SQLite's locking is unreliable over NFS and SMB, and this is worth avoiding for reasons well beyond durability. Keep `db_dir` on local storage. + +The safeguard exists anyway, because WAL needs shared memory and a few setups cannot provide it: a database on a network share, some container volume drivers, and SQLite builds compiled without WAL all leave a rollback journal in place instead — and on a rollback journal, `normal` *can* corrupt the database on power loss. InvokeAI reads back the journal mode it actually got, so in that case it ignores `normal`, uses `full`, and logs a warning naming the mode. An in-memory database (`use_memory_db`) has no durability to trade and is unaffected. + +`db_synchronous` applies to the application's own database connection. The standalone scripts that open the database directly — `scripts/gallery_maintenance.py` and `invokeai/frontend/install/import_images.py` — leave SQLite's own default in place, so the measurements below describe the running app, not those scripts. + +Measured on a copy of a real library, 300 single-row inserts each committed on its own: + +| Setting | Median commit | p95 commit | +| -------- | ------------: | ---------: | +| `full` | 0.427 ms | 0.811 ms | +| `normal` | 0.035 ms | 0.121 ms | + +That is 12x shorter at the median. It matters more than the raw numbers suggest, because all database work is serialised through a single lock: a shorter write is also a shorter time during which every read is blocked. + +The default stays at `full`, which is what InvokeAI has always run with, so upgrading does not weaken anyone's durability. It is now applied explicitly, which does tighten it on the uncommon SQLite build compiled with `SQLITE_DEFAULT_WAL_SYNCHRONOUS=1`, where WAL connections previously started at `normal`. If you generate in large batches on a machine with reliable power, `normal` is the cheaper setting. + +#### Logging + +Several different log handler destinations are available, and multiple destinations are supported by providing a list: + +```yaml +log_handlers: + - console + - syslog=localhost + - file=/var/log/invokeai.log +``` + +- `console` is the default. It prints log messages to the command-line window from which InvokeAI was launched. + +- `syslog` is only available on Linux and Macintosh systems. It uses + the operating system's "syslog" facility to write log file entries + locally or to a remote logging machine. `syslog` offers a variety + of configuration options: + +```yaml +syslog=/dev/log` - log to the /dev/log device +syslog=localhost` - log to the network logger running on the local machine +syslog=localhost:512` - same as above, but using a non-standard port +syslog=fredserver,facility=LOG_USER,socktype=SOCK_DRAM` +- Log to LAN-connected server "fredserver" using the facility LOG_USER and datagram packets. +``` + +- `http` can be used to log to a remote web server. The server must be + properly configured to receive and act on log messages. The option + accepts the URL to the web server, and a `method` argument + indicating whether the message should be submitted using the GET or + POST method. + +```yaml +http=http://my.server/path/to/logger,method=POST +``` + +The `log_format` option provides several alternative formats: + +- `color` - default format providing time, date and a message, using text colors to distinguish different log severities +- `plain` - same as above, but monochrome text only +- `syslog` - the log level and error message only, allowing the syslog system to attach the time and date +- `legacy` - a format similar to the one used by the legacy 2.3 InvokeAI releases. + +### Environment Variables + +All settings may be set via environment variables by prefixing `INVOKEAI_` +to the variable name. For example, `INVOKEAI_HOST` would set the `host` +setting. + +For non-primitive values, pass a JSON-encoded string: + +```sh +export INVOKEAI_REMOTE_API_TOKENS='[{"url_regex":"modelmarketplace", "token": "12345"}]' +``` + +We suggest using `invokeai.yaml`, as it is more user-friendly. + +### CLI Args + +A subset of settings may be specified using CLI args: + +- `--root`: specify the root directory +- `--config`: override the default `invokeai.yaml` file location + +### Low-VRAM Mode + +See the [Low-VRAM mode docs][low-vram] for details on enabling this feature. + +### All Settings + +The full settings reference is below. Additional explanations for selected settings appear earlier on this page. + + + +[basic guide to yaml files]: https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/ +[Model Marketplace API Keys]: #model-marketplace-api-keys +[low-vram]: /configuration/optimization/low-vram-mode/ diff --git a/docs/src/content/docs/configuration/02.docker.mdx b/docs/src/content/docs/configuration/02.docker.mdx new file mode 100644 index 00000000000..9ab015bf304 --- /dev/null +++ b/docs/src/content/docs/configuration/02.docker.mdx @@ -0,0 +1,95 @@ +--- +title: Docker +--- + +import { Aside, Tabs, TabItem } from '@astrojs/starlight/components' + +import SystemRequirementsLink from '@components/SystemRequirmentsLink.astro' + + + +:::note[Operating Systems and GPU Support] + + + Docker Desktop on Windows [includes GPU support](https://www.docker.com/blog/wsl-2-gpu-support-for-docker-desktop-on-nvidia-gpus/). + + + Docker can not access the GPU on macOS, so your generation speeds will be slow. Use the [launcher](../../start-here/installation) instead. + + + Configure Docker to access your machine's GPU. + Follow the [NVIDIA](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) or [AMD](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/how-to/docker.html) documentation. + + +::: + +## TL;DR + +Ensure your Docker setup is able to use your GPU. The CUDA image needs an Nvidia driver from the R580 series or newer on the host and a GPU with compute capability 7.5 or newer (GTX 16xx, RTX 20xx and later). Then: + +```bash +docker run --runtime=nvidia --gpus=all --publish 9090:9090 ghcr.io/invoke-ai/invokeai +``` + +Once the container starts up, open [http://localhost:9090](http://localhost:9090) in your browser, install some models, and start generating. + +## Build-It-Yourself + +All the docker materials are located inside the [docker](https://github.com/invoke-ai/InvokeAI/tree/main/docker) directory in the Git repo. + +```bash +cd docker +cp .env.sample .env +docker compose up +``` + +We also ship the `run.sh` convenience script. See the `docker/README.md` file for detailed instructions on how to customize the docker setup to your needs. + +### Prerequisites + +#### Install [Docker](https://github.com/santisbon/guides#docker) + +On the [Docker Desktop app](https://docs.docker.com/get-docker/), go to `Preferences` -> `Resources` -> `Advanced`. Increase the CPUs and Memory to avoid this [Issue](https://github.com/invoke-ai/InvokeAI/issues/342). You may need to increase Swap and Disk image size too. + +### Setup + +Set up your environment variables. In the `docker` directory, make a copy of `.env.sample` and name it `.env`. Make changes as necessary. + +Any environment variables supported by InvokeAI can be set here - please see the [configuration docs](/configuration/invokeai-yaml/) for further detail. + +At the very least, you might want to set the `INVOKEAI_ROOT` environment variable +to point to the location where you wish to store your InvokeAI models, configuration, and outputs. + +| Environment Variable | Default value | Description | +| --- | --- | --- | +| `INVOKEAI_ROOT` | `~/invokeai` | **Required** - the location of your InvokeAI root directory. It will be created if it does not exist. | +| `HUGGING_FACE_HUB_TOKEN` | | InvokeAI will work without it, but some of the integrations with HuggingFace (like downloading from models from private repositories) may not work | +| `GPU_DRIVER` | `cuda` | Optionally change this to `rocm` to build the image for AMD GPUs, or `cpu` for CPU-only builds (required on ARM64 / `aarch64` hosts such as the Raspberry Pi 5). NOTE: Use the `build.sh` script to build the image for this to take effect. | + +#### Build the Image + +Use the standard `docker compose build` command from within the `docker` directory. + +If using an AMD GPU: +a: set the `GPU_DRIVER=rocm` environment variable in `docker-compose.yml` and continue using `docker compose build` as usual, or +b: set `GPU_DRIVER=rocm` in the `.env` file and use the `build.sh` script, provided for convenience + +#### Run the Container + +Use the standard `docker compose up` command, and generally the `docker compose` [CLI](https://docs.docker.com/compose/reference/) as usual. + +Once the container starts up (and configures the InvokeAI root directory if this is a new installation), you can access InvokeAI at [http://localhost:9090](http://localhost:9090) + +## Troubleshooting / FAQ + +
+ "I am running Windows under WSL2, and am seeing a 'no such file or directory' error." + + Your `docker-entrypoint.sh` might have has Windows (CRLF) line endings, depending how you cloned the repository. + To solve this, change the line endings in the `docker-entrypoint.sh` file to `LF`. You can do this in VSCode + (`Ctrl+P` and search for "line endings"), or by using the `dos2unix` utility in WSL. + Finally, you may delete `docker-entrypoint.sh` followed by `git pull; git checkout docker/docker-entrypoint.sh` + to reset the file to its most recent version. + For more information on this issue, see [Docker Desktop documentation](https://docs.docker.com/desktop/troubleshoot/topics/#avoid-unexpected-syntax-errors-use-unix-style-line-endings-for-files-in-containers) + +
diff --git a/docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx b/docs/src/content/docs/configuration/Multi-User Mode/admin-guide.mdx similarity index 98% rename from docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx rename to docs/src/content/docs/configuration/Multi-User Mode/admin-guide.mdx index ebca75fe0a0..a29e3cdd527 100644 --- a/docs/src/content/docs/features/Multi-User Mode/admin-guide.mdx +++ b/docs/src/content/docs/configuration/Multi-User Mode/admin-guide.mdx @@ -175,7 +175,7 @@ scripts. #### **Via the Web Frontend:** -Please see the Multi-User Guide's section on [Adding and Modifying Users](./user-guide#adding-and-modifying-users) +Please see the Multi-User Guide's section on [Adding and Modifying Users](/configuration/multi-user-mode/user-guide/#adding-and-modifying-users) for a walk-through. #### **Via Command Line Scripts:** @@ -243,7 +243,7 @@ the queue by user; use your browser's find-in-page to scan by name if needed. ## Model Management As an administrator, you have full access to the [Model -Manager](/concepts/models) and can install, edit and delete +Manager](/users-guide/models/introduction/) and can install, edit and delete models just as in single-user mode. Unprivileged users, however, can view the models previously installed, but cannot add or modify them. @@ -710,7 +710,7 @@ InvokeAI remains under its existing license. Multi-user mode does not change lic ### Support - **General Documentation**: [InvokeAI Docs](https://invoke.ai/) -- **User Guide**: [For Users](/features/multi-user-mode/user-guide/) -- **API Guide**: [For Developers](/features/multi-user-mode/api-guide/) +- **User Guide**: [For Users](/configuration/multi-user-mode/user-guide/) +- **API Guide**: [For Developers](/configuration/multi-user-mode/api-guide/) - **Discord**: [Join Community](https://discord.gg/ZmtBAhwWhy) - **GitHub Issues**: [Report Problems](https://github.com/invoke-ai/InvokeAI/issues) diff --git a/docs/src/content/docs/features/Multi-User Mode/api-guide.mdx b/docs/src/content/docs/configuration/Multi-User Mode/api-guide.mdx similarity index 99% rename from docs/src/content/docs/features/Multi-User Mode/api-guide.mdx rename to docs/src/content/docs/configuration/Multi-User Mode/api-guide.mdx index 36083388daf..2a33baba35e 100644 --- a/docs/src/content/docs/features/Multi-User Mode/api-guide.mdx +++ b/docs/src/content/docs/configuration/Multi-User Mode/api-guide.mdx @@ -1227,8 +1227,8 @@ allow_headers: ## Additional Resources -- [User Guide](./user-guide/) — For end users -- [Administrator Guide](./admin-guide/) — For administrators +- [User Guide](/configuration/multi-user-mode/user-guide/) — For end users +- [Administrator Guide](/configuration/multi-user-mode/admin-guide/) — For administrators - [GitHub Repository](https://github.com/invoke-ai/InvokeAI) — Source code --- diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-1.png b/docs/src/content/docs/configuration/Multi-User Mode/assets/admin-add-user-1.png similarity index 100% rename from docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-1.png rename to docs/src/content/docs/configuration/Multi-User Mode/assets/admin-add-user-1.png diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-2.png b/docs/src/content/docs/configuration/Multi-User Mode/assets/admin-add-user-2.png similarity index 100% rename from docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-2.png rename to docs/src/content/docs/configuration/Multi-User Mode/assets/admin-add-user-2.png diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-3.png b/docs/src/content/docs/configuration/Multi-User Mode/assets/admin-add-user-3.png similarity index 100% rename from docs/src/content/docs/features/Multi-User Mode/assets/admin-add-user-3.png rename to docs/src/content/docs/configuration/Multi-User Mode/assets/admin-add-user-3.png diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/admin-setup.png b/docs/src/content/docs/configuration/Multi-User Mode/assets/admin-setup.png similarity index 100% rename from docs/src/content/docs/features/Multi-User Mode/assets/admin-setup.png rename to docs/src/content/docs/configuration/Multi-User Mode/assets/admin-setup.png diff --git a/docs/src/content/docs/features/Multi-User Mode/assets/user-login-1.png b/docs/src/content/docs/configuration/Multi-User Mode/assets/user-login-1.png similarity index 100% rename from docs/src/content/docs/features/Multi-User Mode/assets/user-login-1.png rename to docs/src/content/docs/configuration/Multi-User Mode/assets/user-login-1.png diff --git a/docs/src/content/docs/features/Multi-User Mode/user-guide.mdx b/docs/src/content/docs/configuration/Multi-User Mode/user-guide.mdx similarity index 98% rename from docs/src/content/docs/features/Multi-User Mode/user-guide.mdx rename to docs/src/content/docs/configuration/Multi-User Mode/user-guide.mdx index f333be851b8..8e96cd8465d 100644 --- a/docs/src/content/docs/features/Multi-User Mode/user-guide.mdx +++ b/docs/src/content/docs/configuration/Multi-User Mode/user-guide.mdx @@ -319,7 +319,7 @@ Yes. You can mark any workflow as shared (public), which makes it visible to all ### Can I use the API with multi-user mode? -Yes, but you'll need to authenticate with a JWT token. See the [API Guide](./api-guide/) for details. +Yes, but you'll need to authenticate with a JWT token. See the [API Guide](/configuration/multi-user-mode/api-guide/) for details. ### What happens if I forget my password? @@ -362,5 +362,5 @@ When reporting an issue, include: ## Additional Resources -- [Administrator Guide](./admin-guide/) — For administrators managing users and the system -- [API Guide](./api-guide/) — For developers using the InvokeAI API +- [Administrator Guide](/configuration/multi-user-mode/admin-guide/) — For administrators managing users and the system +- [API Guide](/configuration/multi-user-mode/api-guide/) — For developers using the InvokeAI API diff --git a/docs/src/content/docs/configuration/Optimization/fp8-storage.mdx b/docs/src/content/docs/configuration/Optimization/fp8-storage.mdx new file mode 100644 index 00000000000..090d7036c19 --- /dev/null +++ b/docs/src/content/docs/configuration/Optimization/fp8-storage.mdx @@ -0,0 +1,200 @@ +--- +title: FP8 Storage & Compute +sidebar: + order: 3 +--- + +import { Steps } from '@astrojs/starlight/components'; + +FP8 Storage cuts a model's VRAM footprint roughly in half by keeping weights on the GPU in 8-bit floating-point format (`float8_e4m3fn`). During inference, each layer's weights are cast on-the-fly up to the compute precision (FP16/BF16), and the stored FP8 weights are put back after the forward pass — so quality is largely preserved. + +It pairs well with [Low-VRAM mode](/configuration/optimization/low-vram-mode/): low-VRAM mode streams layers between RAM and VRAM, while FP8 Storage shrinks the layers themselves. + +:::caution[For full precision models only] +FP8 Storage is meant for **full precision** checkpoints (FP16 / BF16 / FP32). It does nothing for **GGUF**, **NF4** and [**SDNQ**](/configuration/optimization/sdnq-quantization/) checkpoints, which carry their own storage precision, so the Model Manager does not show the toggle for them (see [Where the toggle is shown](#where-the-toggle-is-shown)). **int8** and **nvfp4** single-file checkpoints keep their quantized layers as they are — the loader returns a module type the FP8 layer cast does not touch — so there the toggle reaches only the layers the build ships in full precision, and on an **Ideogram 4** int8 file nothing at all. On single-file checkpoints that ship *scaled* fp8 layers, the toggle keeps those layers in FP8 with their own scale instead of unpacking them; that is what FLUX.1, FLUX.2, Z-Image, Anima, Krea-2, Qwen-Image and Ideogram 4 all do. **MXFP8 is the exception, and there the toggle costs quality:** those files carry one exponent per 32-element block, which no GPU before Blackwell can apply, so the layers are unpacked at load and the toggle then re-encodes them as fp8 *without* any scale. Blocks whose scale sits far below 1 lose their weights to underflow. Leave it off for an MXFP8 checkpoint — and because installing switches it on for every checkpoint with FP8 weights, MXFP8 included, turn it off under **Default Settings** after installing one. If your model is already quantized, use its full-precision variant to get the full effect of FP8 Storage. Checkpoints that store FP8 weights are the exception: InvokeAI loads them at BF16 unless FP8 Storage is on, so they get it automatically when installed (see [When installing](#when-installing)). +::: + +## Requirements + +- **A CUDA or Intel XPU device.** Before first use InvokeAI runs a small FP8 test on the device. If the device, driver or PyTorch build cannot store and upcast `float8_e4m3fn` (some older AMD ROCm GPUs, for example), the test fails, InvokeAI logs a warning and loads the model without FP8. CPU and MPS are never used for FP8 Storage. +- **Recent PyTorch.** The `float8_e4m3fn` dtype was added in PyTorch 2.1 — InvokeAI's bundled versions satisfy this. + +There is no hardware requirement for FP8 *compute* — InvokeAI casts back to FP16/BF16 for math. This means FP8 Storage works on GPUs that do not natively support FP8 matmul (e.g. RTX 30-series), at a small per-step throughput cost. + +## Hardware support tiers + +InvokeAI's FP8 path stores weights in FP8 and casts them back to BF16/FP16 on each forward pass via its own `register_forward_pre_hook` / `register_forward_hook` wrappers (the same skip list as diffusers' `apply_layerwise_casting`, but applied to every `nn.Module` — including diffusers `ModelMixin` subclasses — so it composes correctly with InvokeAI's `CustomLinear` and partial loading). The practical benefit of toggling FP8 Storage depends on what your GPU can do natively. There are three tiers: + +### RTX 30-series and older Ampere workstation cards — VRAM win only + +The toggle works as advertised: the UNet / transformer drops by roughly 50% on the GPU. Per-step latency is the same or marginally slower because every forward pass adds an FP8 → BF16 cast on entry. On exit the stored FP8 weights are simply put back, which costs no extra cast or allocation. This is the **largest target group**: 3090 owners squeezing FLUX into 24 GB benefit the most. + +### RTX 40-series, RTX 50-series, and Hopper — VRAM win, plus a compute win via a separate setting + +These GPUs have native FP8 tensor cores. FP8 *Storage* on its own still only buys the ~50% VRAM reduction, because the forward pass runs in BF16 — the hook casts weights back up to compute precision before each layer. To actually use the tensor cores you need the separate [FP8 Compute](#fp8-compute) setting. Note that on a checkpoint which already ships FP8 weights, FP8 Storage does something different from what this section describes — see [Checkpoints that are already FP8](#checkpoints-that-are-already-fp8). + +### Older CUDA cards — still a VRAM win + +`float8_e4m3fn` is a pure storage dtype in PyTorch and works on any CUDA device, so pre-Ampere cards (GTX 16-series, RTX 20-series, etc.) get the same ~50% VRAM reduction as Ampere. There are no native FP8 tensor cores on these GPUs, so the throughput trade-off is the same as on the 30-series: cast in, compute in BF16/FP16, restore the FP8 weights. + +### MPS and CPU — no-op + +FP8 Storage is only used on CUDA and Intel XPU devices. On CPU PyTorch *technically* supports FP8 dtypes, but the cast operations are software-emulated and end up costing more than the memory savings buy back. If you toggle it on CPU or MPS, the loader skips the cast and returns the model unchanged with no log line. On a CUDA or XPU device whose FP8 test fails, the log shows `FP8 storage probe failed on ... not using FP8.` once. + +## Enabling FP8 Storage + +FP8 Storage is a **per-model setting**, configured from the Model Manager: + + +1. Open the **Model Manager**. +2. Select a model (Main, ControlNet, or T2I-Adapter). The toggle only appears for models where it has an effect (see [Where the toggle is shown](#where-the-toggle-is-shown)). +3. Under **Default Settings**, toggle **FP8 storage**. +4. Click **Save Defaults**. + + +The setting takes effect on the next load. If the model is already in the cache, InvokeAI evicts the cached copy automatically so the new setting applies — even if a generation is currently using the model (the eviction is deferred until the generation finishes). + +### When installing + +- **Checkpoints that already store FP8 weights get FP8 Storage automatically.** InvokeAI reads the weight types from the file while identifying the model, not its name, so a full-precision file with "fp8" in its name is not affected. Only the denoiser counts: an all-in-one checkpoint with an FP8 text encoder beside a full-precision UNet or transformer is left alone. Without this, an FP8 checkpoint would be loaded at BF16 and take twice the VRAM. +- **"Scaled" FP8 checkpoints are included.** Their weights only mean something together with the per-tensor scale stored beside them, and the loader keeps them in exactly that form, so the setting costs no precision at all — it simply stops InvokeAI from unpacking the file to twice its size. Two exceptions: + - **MXFP8** checkpoints are switched on as well, but for them the setting costs quality (see the callout at the top). Turn it off after installing one. + - **Wan** checkpoints in ComfyUI's `fp8_scaled` format: the Wan loader cannot keep them packed yet. It declines the setting, logs why, and loads the model at BF16, even though the toggle shows as on. +- **For any other model, tick FP8 storage next to the install source** (a file path, URL or HuggingFace repo, and in folder scan results). It applies to the models you install from there that support it. For a model whose loader cannot use FP8 Storage the request is dropped, so the model is not recorded with a setting that does nothing. + +Either way the setting stays editable under **Default Settings** afterwards. Re-identifying a model resets its default settings, including this one, to what identification chooses. + +:::tip[When to enable] + Enable FP8 Storage on large models that don't fit comfortably in VRAM — FLUX dev/Klein, large SDXL checkpoints, ControlNet-XL adapters. For smaller SD1 / SD2 models, the savings are negligible and not worth the small precision trade-off. +::: + +## What FP8 Storage applies to + +FP8 Storage is **only** applied to layers where the precision trade-off is acceptable: + +| Model type | FP8 applied? | +| --- | --- | +| Main models: SD1, SD2, SDXL, SDXL Refiner, SD3, CogView4 | Yes | +| FLUX.1, FLUX.2 (all variants) | Yes | +| Z-Image, Anima, Krea-2, Qwen-Image, ERNIE-Image | Yes | +| Ideogram 4 (single file) | Yes — both transformer branches | +| LTX-2 | Yes | +| Wan (checkpoint and diffusers) | Yes — except ComfyUI `fp8_scaled` files, which load at BF16 | +| ControlNet (SD1, SD2, SDXL), T2I-Adapter | Yes | +| VAE | No — visible decode-quality regression | +| Text encoders, tokenizers | No — small models, no benefit | +| Anima ControlNet-LLLite | No — adapter is only tens of MB | +| FLUX and Z-Image ControlNet | No — not implemented yet | +| MiniMax H3 | No — not implemented yet | +| Ideogram 4 (diffusers folder) | No — published only as nf4 or fp8 | +| GGUF, NF4 and SDNQ models | No — already quantized | +| LoRA, ControlLoRA | No — patched into base, not run alone | + +Within a supported model, **norm layers, position/patch embeddings, and `proj_in`/`proj_out` are skipped** so precision-sensitive tiny learned scalars (e.g. FLUX `RMSNorm.scale`) aren't crushed to FP8. This mirrors the diffusers default skip list. + +**On top of that, each model's own declared exclusions are honored.** Architectures list their precision-sensitive modules themselves (diffusers calls these `_skip_layerwise_casting_patterns` and `_keep_in_fp32_modules`), and those layers stay at compute precision too. This matters where the generic patterns don't fit the architecture's naming, and it is not a nicety: Z-Image and Anima both name their timestep-embedding MLP `t_embedder`, which no generic pattern matches. Cast to FP8, Z-Image fails outright and Anima renders a heavily dithered image with no fine detail. + +The cost is a slightly smaller saving on the models that declare a lot. Measured against the generic defaults alone: + +| Model | Weights kept at compute precision | Saving given up | +| ------------------------- | ------------------------------------------------ | --------------- | +| Krea-2 | 39 M (`time_embed`) | ~38 MiB | +| Anima | 19 M (`t_embedder`, `x_embedder`, `final_layer`) | ~18 MiB | +| Wan 14B | 232 M (`condition_embedder`, `patch_embedding`) | ~221 MiB | +| FLUX.1, Qwen-Image | 0 | 0 | + +Measured on Wan 2.2 TI2V-5B (fp16 single file, 512², 20 steps, RTX 4090), FP8 Storage takes the transformer from 9.5 GB to 4.9 GB resident, at 21.4 s instead of 18.4 s per video. + +### Where the toggle is shown + +The Model Manager shows the **FP8 Storage** toggle only for models in the "Yes" rows above. For the others it is hidden, because the setting would not change anything. A model installed before this rule may still have FP8 Storage stored as on. The setting stays in the database and does nothing; when such a model loads, the log says so once: `FP8 Storage is set for '' but does nothing here: `. + +## Quality trade-offs + +FP8 Storage is **near-lossless** for most workloads because: + +- Norms and embeddings (the precision-sensitive layers) are skipped. +- The actual matmul still happens in FP16/BF16 — FP8 is only the on-GPU storage format. +- On a checkpoint that is already FP8, the weights are not re-encoded at all: they stay exactly as published and are unpacked with their own scale before each layer, so there is no added rounding, and no re-quantization to undo. + +That said, some artifacts have been reported on: + +- **VAEs** — never cast (the toggle has no effect on VAE submodels). +- **Heavy LoRA stacks** — patching is unaffected, but very precision-sensitive LoRAs may show slight drift. Compare a side-by-side if your workflow depends on subtle LoRA behavior. + +If you see unexpected quality regressions, disable FP8 Storage on the affected model and re-run. + +## Combining with Low-VRAM mode + +**FP8 Storage + partial loading**: fully supported. FP8 Storage shrinks the layers; partial loading streams them between RAM and VRAM as needed. Use both on tight VRAM budgets. + +**FP8 Compute + partial loading** is a different story — it still works, but it costs both speed and reproducibility. See [FP8 Compute](#fp8-compute) below. + +(For how FP8 Storage interacts with GGUF / NF4 / SDNQ / int8 / nvfp4 checkpoints, see the callout at the top of this page.) + +## FP8 Compute + +Everything above describes FP8 *Storage*, which changes how weights are **stored** while the math still runs in BF16. `fp8_compute` is a separate, global setting in `invokeai.yaml` that also does the **math** in FP8, on GPUs that have hardware for it. That makes generation faster, not just smaller. + +It is for models that were **already saved in FP8** by whoever published them — you'll often see these labelled "fp8" or "fp8_scaled" in the filename. Without either setting InvokeAI unpacks them back to BF16 while loading, which is why such a checkpoint has FP8 Storage switched on for it when it is installed. With `fp8_compute` on they stay as they are and run directly on the GPU's FP8 hardware; with FP8 Storage on they also stay as they are, but are unpacked layer by layer during the forward pass instead. + +### Checkpoints that are already FP8 + +For these, FP8 Storage does not re-encode anything — it simply stops InvokeAI from unpacking them. That matters because the file stores a per-tensor scale alongside each weight, and re-encoding the unpacked result would throw that scale away: on FLUX.2 Klein 4B roughly 3% of the weights would round to zero, for a file that was already one byte per weight. What it costs instead is a little speed, since each layer is unpacked on use. Measured on a 4090 with `fp8_compute` off, the Ideogram 4 fp8 pair generates 1024px in 40.6 s against 35.6 s on the FP8 tensor cores, and holds 8.7 GB per branch instead of the 17.3 GB the unpacked weights need — which is the difference between fitting on a 24 GB card and running out of memory during the first step. + +The loader reports what it kept, naming the setting that kept it — for example `Ideogram 4: kept 208 layer(s) in fp8 (scaled fp8 checkpoint, kept for FP8 Storage, dequantized per forward)`. Some loaders then hand the model to the layer cast anyway, which declines it and logs `FP8 storage skipped ... already fp8`; on FLUX.1, FLUX.2, Z-Image and Anima you therefore see both lines for the same model. Both are the intended outcome, not a failure. + +Not every part of a model stays in FP8 — the small, precision-sensitive pieces are always unpacked. Those are a tiny share of the weights, so you still get nearly the full VRAM saving. + +You need an FP8-capable GPU: RTX 40-series or newer, the datacenter cards of those generations, or an AMD GPU whose ROCm build supports FP8 matmul (MI300, for example). InvokeAI checks your card the first time it needs to — by actually trying a small FP8 operation rather than going by the model name — so a card that can't do it quietly falls back to the normal path instead of failing partway through a generation. + +:::danger[Reproducibility requires the model to be fully in VRAM] +With `fp8_compute` enabled, **the same seed only reproduces the same image if the model is 100% resident in VRAM.** + +FP8 math only happens for the parts of the model that are actually on the GPU. Anything still sitting in system RAM takes the normal path instead, which gives slightly different numbers — and *which* parts that affects depends on how much of the model happened to fit at that moment, which changes from run to run. + +Measured on a 24 GB card with a ~12 GB model at 88–95% loaded: two runs with an identical seed and identical settings differed in **98.7% of all pixels**. With the model fully loaded, repeated runs came out **identical**. + +To get repeatable output, check the model's load line in the log for `VRAM: … (100.0%)`. If it is below that, free up VRAM (lower resolution, fewer models loaded at once, a smaller text encoder) or set `enable_partial_loading: false`. +::: + +Keeping the whole model on the GPU is worth it for speed as well: on the same 24 GB card, having to stream the last 5–12% of the model over PCIe cost **+47% per step** (1.03 → 1.51 s/it at 1024², 8 steps). + +### When the model asks for full precision + +Some FP8 models come with a note from whoever made them, marking certain layers as ones that should not use FP8 math. InvokeAI follows those notes by default, so those layers run the slower way. On a model that marks a lot of layers, this can eat much of the FP8 Compute speedup. + +Setting `fp8_compute_full_precision_hints: false` ignores the notes and runs everything on the FP8 hardware. It is faster, but you are overriding the model author's judgement about which layers are sensitive — so compare a few images before sticking with it. + +## Troubleshooting + +### "I toggled FP8 Storage but VRAM usage didn't change" + +The cache eviction is immediate for idle models, but **deferred until the next unlock** if the model is mid-generation. Wait for the current generation to finish, then start a new one — the next load will use the new setting. + +If VRAM still hasn't dropped: + +- Check the InvokeAI log for `FP8 layerwise casting enabled for `. If the line isn't there, the model is on the exclusion list (VAE, text encoder, LoRA — see table above), or it is a Wan `fp8_scaled` checkpoint, for which the loader logs why it declined. For a checkpoint that is already FP8 the expected line is `FP8 storage skipped ... already fp8` instead: nothing needed re-encoding. +- Confirm you are on a CUDA or XPU device, and look for `FP8 storage probe failed` in the log. FP8 Storage is not used on CPU and MPS. + +### Quality regression on a specific model + +Disable FP8 Storage for that model in Model Manager and reload. If quality is restored, the model has FP8-sensitive layers that fall outside the default skip list. Please open an issue with the model name and a side-by-side comparison. + +### "RuntimeError: ... float8_e4m3fn ..." + +You're on a PyTorch version that predates FP8 support. Reinstall InvokeAI using the official launcher — the bundled torch version supports FP8. + +### Reporting an FP8 issue + +If FP8 Storage misbehaves — crash, quality regression, OOM that shouldn't happen — please [open a GitHub issue](https://github.com/invoke-ai/InvokeAI/issues/new/choose) and include: + +- **What you did**: the workflow / generation step that triggered the problem, and whether it reproduces every time. +- **Model**: exact name and variant (e.g. "FLUX.2 Klein 9B Diffusers", "SDXL Base 1.0 single-file"), and whether the file is a full-precision checkpoint or already quantized (GGUF / NF4 / SDNQ / int8 / nvfp4 / scaled fp8 / MXFP8). +- **LoRAs**: whether any LoRAs (or ControlLoRAs) are stacked on the model, and how many. +- **Other toggles**: Low-VRAM mode on/off, any `cpu_only` text encoder setting, configured VRAM limit. +- **GPU**: model and VRAM size (e.g. "RTX 3090 24 GB", "RTX 4070 Ti 12 GB"). +- **OS**: Windows or Linux, plus driver / CUDA version if you have it. +- **Logs**: the InvokeAI log around the failure — in particular the `FP8 layerwise casting enabled for ` line (or its absence) and any traceback. + +A side-by-side image comparison (FP8 on vs. FP8 off, same seed) is extremely useful for quality regressions. diff --git a/docs/src/content/docs/configuration/Optimization/low-vram-mode.mdx b/docs/src/content/docs/configuration/Optimization/low-vram-mode.mdx new file mode 100644 index 00000000000..dd4345b2979 --- /dev/null +++ b/docs/src/content/docs/configuration/Optimization/low-vram-mode.mdx @@ -0,0 +1,249 @@ +--- +title: Low-VRAM mode +sidebar: + order: 2 +--- + +As of v5.6.0, Invoke has a low-VRAM mode. It works on systems with dedicated GPUs (Nvidia GPUs on Windows/Linux and AMD GPUs on Linux). + +This allows you to generate even if your GPU doesn't have enough VRAM to hold full models. Most users should be able to run even the beefiest models - like the ~24GB unquantised FLUX dev model. + +## Enabling Low-VRAM mode + +Low-VRAM mode is **enabled by default** via the `enable_partial_loading: true` setting in `invokeai.yaml`. No action is required to turn it on. + +**Windows users should also [disable the Nvidia sysmem fallback](#disabling-nvidia-sysmem-fallback-windows-only)**. + +It is possible to fine-tune the settings for best performance or if you still get out-of-memory errors (OOMs). + +If you want to disable partial loading (e.g. on systems with plenty of VRAM where full loading is faster), add this line to your `invokeai.yaml` and restart Invoke: + +```yaml +enable_partial_loading: false +``` + +:::tip[How to find `invokeai.yaml`] + The `invokeai.yaml` configuration file lives in your install directory. To access it, run the **Invoke Community Edition** launcher and click the install location. This will open your install directory in a file explorer window. + + You'll see `invokeai.yaml` there and can edit it with any text editor. After making changes, restart Invoke. + + If you don't see `invokeai.yaml`, launch Invoke once. It will create the file on its first startup. +::: + +## Details and fine-tuning + +Low-VRAM mode and related workload-specific optimizations include: + +- Partial model loading (`enable_partial_loading`) +- PyTorch CUDA allocator config (`pytorch_cuda_alloc_conf`) +- Dynamic RAM and VRAM cache sizes (`max_cache_ram_gb`, `max_cache_vram_gb`) +- Working memory (`device_working_mem_gb`) +- Keeping a RAM weight copy (`keep_ram_copy_of_weights`) +- Tiled VAE decode and encode (`auto_tiled_decode`, `force_tiled_decode`) +- Wan video memory optimization (`wan_memory_optimization`) +- PiD decode activation chunking (`pid_memory_optimization`) + +Read on to learn about these features and understand how to fine-tune them for your system and use-cases. + +### Wan video memory optimization + +Wan video generation has an additional opt-in memory optimization: + +```yaml +wan_memory_optimization: true +``` + +This reserves VRAM so partial-load Wan transformer weights target about 2 GiB resident and stream remaining layers from RAM when partial model loading is enabled (the default). The explicit residency trim may be a no-op when cache admission already reaches that target. If `enable_partial_loading: false`, the activation, timestep, and VAE optimizations still apply, but transformer weights remain fully resident and the 2 GiB residency target is unavailable. It also chunks pointwise transformer activations, compacts TI2V per-token timestep conditioning, and streams untiled VAE decode chunks directly to MP4. It reduces peak VRAM during both denoise and decode, but generation can be substantially slower and requires enough system RAM for offloaded weights. Spatially tiled VAE decode continues to use its existing full-tile path. + +The optimized BF16 transformer path can produce small numerical differences because chunked matrix operations accumulate in a different order. Same-seed output is not guaranteed to be bit-identical; set `wan_memory_optimization: false` for the baseline path. + +Developers with a CUDA or ROCm device can validate Wan VAE memory estimates with `python scripts/calibrate_wan_vae_working_memory.py --vae `. The script reports allocated and reserved memory deltas; its implied scaling constant uses allocated memory to match the shipped estimator, while reserved memory shows allocator headroom. Use `--tiling` to measure the spatially tiled full-decode fallback; it overrides streaming mode. Use `--tile-size ` to override the VAE's default tile size. + +Direct MP4 streaming keeps the VAE cache lock while chunks are decoded and written. Wan's causal decoder state and weights must remain live for the sequence; releasing the lock would require a separate bounded decode and encode queue. + +### Partial model loading + +Invoke's partial model loading works by streaming model "layers" between RAM and VRAM as they are needed. + +When an operation needs layers that are not in VRAM, but there isn't enough room to load them, inactive layers are offloaded to RAM to make room. + +#### Enabling partial model loading + +Partial model loading is enabled by default. The corresponding setting in `invokeai.yaml` is: + +```yaml +enable_partial_loading: true +``` + +Set it to `false` to disable partial loading. + +### PyTorch CUDA allocator config + +The PyTorch CUDA allocator's behavior can be configured using the `pytorch_cuda_alloc_conf` config. Tuning the allocator configuration can help to reduce the peak reserved VRAM. The optimal configuration is dependent on many factors (e.g. device type, VRAM, CUDA driver version, etc.), but switching from PyTorch's native allocator to using CUDA's built-in allocator works well on many systems. To try this, add the following line to your `invokeai.yaml` file: + +```yaml +pytorch_cuda_alloc_conf: "backend:cudaMallocAsync" +``` + +When the setting is left unset, PyTorch's defaults apply, with one exception: with a ROCm build of PyTorch on Windows, Invoke uses `expandable_segments:True`. There, Windows does not fail an allocation it cannot fit into contiguous VRAM. It moves the allocation into much slower system memory instead, and fragmented VRAM makes that happen even when enough VRAM is free. Setting `pytorch_cuda_alloc_conf` yourself, or any of the `PYTORCH_ALLOC_CONF` / `PYTORCH_CUDA_ALLOC_CONF` / `PYTORCH_HIP_ALLOC_CONF` environment variables, replaces this default. + +A more complete explanation of the available configuration options is [here](https://pytorch.org/docs/stable/notes/cuda.html#optimizing-memory-usage-with-pytorch-cuda-alloc-conf). + +### Dynamic RAM and VRAM cache sizes + +Loading models from disk is slow and can be a major bottleneck for performance. Invoke uses two model caches - RAM and VRAM - to reduce loading from disk to a minimum. + +By default, Invoke manages these caches' sizes dynamically for best performance. + +#### Fine-tuning cache sizes + +Prior to v5.6.0, the cache sizes were static, and for best performance, many users needed to manually fine-tune the `ram` and `vram` settings in `invokeai.yaml`. + +As of v5.6.0, the caches are dynamically sized. The `ram` and `vram` settings are no longer used, and new settings are added to configure the cache. + +**Most users will not need to fine-tune the cache sizes.** + +But, if your GPU has enough VRAM to hold models fully, you might get a perf boost by manually setting the cache sizes in `invokeai.yaml`: + +```yaml +# The default max cache RAM size is logged on InvokeAI startup. It is determined based on your system RAM / VRAM. +# You can override the default value by setting `max_cache_ram_gb`. +# Increasing `max_cache_ram_gb` will increase the amount of RAM used to cache inactive models, resulting in faster model +# reloads for the cached models. +# As an example, if your system has 32GB of RAM and no other heavy processes, setting the `max_cache_ram_gb` to 28GB +# might be a good value to achieve aggressive model caching. +max_cache_ram_gb: 28 + +# The default max cache VRAM size is adjusted dynamically based on the amount of available VRAM (taking into +# consideration the VRAM used by other processes; with a ROCm build on Windows, the video-memory budget Windows grants +# Invoke, beyond which it would move allocations into system memory). +# You can override the default value by setting `max_cache_vram_gb`. +# CAUTION: Most users should not manually set this value. See warning below. +max_cache_vram_gb: 16 +``` + +:::caution[Max safe value for `max_cache_vram_gb`] + Most users should not manually configure the `max_cache_vram_gb`. This configuration value caps model-cache residency; `device_working_mem_gb` and operation-specific reservations (e.g. VAE decode) are still subtracted from that cap for every model-cache operation, not only when Wan memory optimization is enabled. A cap below the active working-memory reservation can force aggressive model offloading. + + For users who wish to configure `max_cache_vram_gb`, the max safe value can be determined by subtracting `device_working_mem_gb` from your GPU's VRAM. As described below, the default for `device_working_mem_gb` is 3GB. + + For example, if you have a 12GB GPU, the max safe value for `max_cache_vram_gb` is `12GB - 3GB = 9GB`. + + If you had increased `device_working_mem_gb` to 4GB, then the max safe value for `max_cache_vram_gb` is `12GB - 4GB = 8GB`. + + Most users who override `max_cache_vram_gb` are doing so because they wish to use significantly less VRAM, and should be setting `max_cache_vram_gb` to a value significantly less than the 'max safe value'. +::: + +### Working memory + +Invoke cannot use _all_ of your VRAM for model caching and loading. It requires some VRAM to use as working memory for various operations. + +Invoke reserves 3GB VRAM as working memory by default, which is enough for most use-cases. However, it is possible to fine-tune this setting if you still get OOMs. + +#### Fine-tuning working memory + +You can increase the working memory size in `invokeai.yaml` to prevent OOMs: + +```yaml +# The default is 3GB - bump it up to 4GB to prevent OOMs. +device_working_mem_gb: 4 +``` + +:::tip[Operations may request more working memory] + For some operations, we can determine VRAM requirements in advance and allocate additional working memory to prevent OOMs. + + VAE decoding is one such operation. This operation converts the generation process's output into an image. For large image outputs, this might use more than the default working memory size of 3GB. + + During this decoding step, Invoke calculates how much VRAM will be required to decode and requests that much VRAM from the model manager. If the amount exceeds the working memory size, the model manager will offload cached model layers from VRAM until there's enough VRAM to decode. + + Once decoding completes, the model manager "reclaims" the extra VRAM allocated as working memory for future model loading operations. +::: + +### Keeping a RAM weight copy + +Invoke has the option of keeping a RAM copy of all model weights, even when they are loaded onto the GPU. This optimization is _on_ by default, and enables faster model switching and LoRA patching. Disabling this feature will reduce the average RAM load while running Invoke (peak RAM likely won't change), at the cost of slower model switching and LoRA patching. If you have limited RAM, you can disable this optimization: + +```yaml +# Set to false to reduce the average RAM usage at the cost of slower model switching and LoRA patching. +keep_ram_copy_of_weights: false +``` + +### Tiled VAE decode and encode + +Without tiling, the memory a VAE needs grows with the square of the image size. For FLUX.1, an untiled 3072² encode peaks at about 18.8 GiB on an RTX 4090, and a tiled one at 0.8 GiB, while tiled memory stays roughly flat as the image grows. Tiling costs some speed on smaller images. + +Invoke tiles large decodes by itself: when an untiled decode would claim most of the memory the GPU keeps resident, it decodes in tiles instead, for the FLUX.1, Z-Image, Qwen-Image, Krea-2, Anima and Wan decodes. Because a tiled decode is not pixel-identical to an untiled one, this only kicks in for images too large for the card: for Z-Image on a 16GB GPU, for example, from about 1850x1850 on Nvidia and from about 1500x1500 on AMD, whose convolution library needs more memory. On a ROCm build under Windows the comparison uses the video-memory budget Windows grants Invoke, which is lower than the card's total and lower again while another program uses the GPU. Anima tiles a little earlier, because its untiled decode pushes the Anima model itself out of VRAM on 8GB cards. + +To always decode untiled unless you ask for tiling, turn this off. A decode too large for the card then runs out of memory: the FLUX.1 and Z-Image decodes retry in tiles, the others fail, and on Windows the driver may move the decode into system memory and run it very slowly instead. Anima is not affected by this setting: there, tiling a large decode is what keeps the Anima model in VRAM, and turning it off would only make those decodes slower. + +```yaml +auto_tiled_decode: false +``` + +- **Always tile:** set `force_tiled_decode: true` in `invokeai.yaml`. It covers the VAE decode and encode for SD1.5/SDXL, FLUX.1, Z-Image and Qwen-Image, the Anima encode, and the Krea-2 style reference. Other model families (for example SD3, FLUX.2, Wan and LTX-2) ignore it. +- **Per node:** the SD, FLUX.1, Z-Image and Qwen-Image **Latents to Image** and **Image to Latents** nodes, and **Image to Latents - Anima**, have **Tiled** and **Tile Size** fields for workflows. The [Upscale panel](/users-guide/image-generation/upscale/) turns tiling on by itself. +- **Automatic retry:** if a FLUX.1, Z-Image or Anima decode, or a FLUX.1 encode, runs out of memory, InvokeAI retries it once with tiling and logs that it did. + +A tiled decode is not pixel-identical to a single-pass one. The VAE looks at the whole image at once, so the difference is spread over the image rather than limited to the tile seams; it is small, but the same seed will not reproduce an untiled result exactly. + +### PiD decode optimization + +PiD decodes directly into high-resolution pixels, so its activation and sampler memory can exceed the working memory needed by a normal VAE decode. Partial model loading reduces memory used by PiD's weights, but does not reduce these full-resolution intermediates. + +To reduce PiD's peak VRAM use, enable its experimental memory optimization in `invokeai.yaml` and restart InvokeAI: + +```yaml +pid_memory_optimization: true +``` + +This setting processes parts of the PiD pixel pathway in chunks and uses float32 instead of float64 sampler intermediates. It applies to every supported PiD decoder. Measured on an RTX 4090, peak activation memory for a 2048px decode drops from ~3.7 GB to ~1.5 GB (1024px: ~0.9 GB to ~0.5 GB). + +`device_working_mem_gb` sets a floor for the reservation. To realize the full PiD savings at 1024px-2048px, lower that setting as needed for your GPU. + +It is disabled by default because it is **not output-preserving**. The combined optimized path is not guaranteed to be bit-exact: chunking can change GPU kernel and reduction choices, while the float32 sampler differs from the default float64 path. The few-step sampler amplifies the difference into a slightly different image. The delta is small — around 43 dB PSNR, visually indistinguishable in side-by-side comparisons — but it is real, so the same seed and workflow will not reproduce an unoptimized decode exactly. Decoding speed is roughly unchanged: chunking costs about 4%, which the cheaper sampler math largely offsets. + +When the setting is active, each decode logs the resolution, the patch-token count and whether chunking actually engaged — worth checking, since the option is server-wide and never recorded in image metadata. See [PiD Super-Resolution Decode](/users-guide/image-generation/advanced-topics/pid-decode/) for supported models and usage. + +### Disabling Nvidia sysmem fallback (Windows only) + +On Windows, Nvidia GPUs are able to use system RAM when their VRAM fills up via **sysmem fallback**. While it sounds like a good idea on the surface, in practice it causes massive slowdowns during generation. + +It is strongly suggested to disable this feature: + +- Open the **NVIDIA Control Panel** app. +- Expand **3D Settings** on the left panel. +- Click **Manage 3D Settings** in the left panel. +- Find **CUDA - Sysmem Fallback Policy** in the right panel and set it to **Prefer No Sysmem Fallback**. + +![cuda-sysmem-fallback](../assets/cuda-sysmem-fallback.png) + +:::tip[Invoke does the same thing, but better] + If the sysmem fallback feature sounds familiar, that's because Invoke's partial model loading strategy is conceptually very similar - use VRAM when there's room, else fall back to RAM. + + Unfortunately, the Nvidia implementation is not optimized for applications like Invoke and does more harm than good. +::: + +### AMD GPUs on Windows (ROCm) + +Invoke does not officially support ROCm on Windows (see the [System Requirements](/start-here/system-requirements/)). If you run a ROCm build of PyTorch there anyway, Windows behaves as if the Nvidia sysmem fallback were always on: an allocation that does not fit is moved into much slower system memory instead of failing, and it stays there. Task Manager shows it as shared GPU memory. Invoke works around this where it can: + +- The model cache plans against the video-memory budget Windows grants Invoke, not against the card's total VRAM. +- The PyTorch allocator defaults to `expandable_segments:True` (see [PyTorch CUDA allocator config](#pytorch-cuda-allocator-config)), so fragmented VRAM does not force allocations out. +- Decodes too large for the card are tiled before they start (see [Tiled VAE decode and encode](#tiled-vae-decode-and-encode)); waiting for an out-of-memory error does not work there, because none comes. +- When part of Invoke's GPU memory stays in system memory across two generations, the log says so. Other programs using the same GPU, like games or browsers, can push Invoke's memory out as well; close them. If it keeps happening, lower the image size, set `max_cache_vram_gb` lower or `device_working_mem_gb` higher, and restart Invoke to get the memory back into VRAM. + +## Troubleshooting + +### Windows page file + +Invoke has high virtual memory (a.k.a. 'committed memory') requirements. This can cause issues on Windows if the page file size limits are hit. (See this issue for the technical details on why this happens: https://github.com/invoke-ai/InvokeAI/issues/7563). + +If you run out of page file space, InvokeAI may crash. Often, these crashes will happen with one of the following errors: + +- InvokeAI exits with Windows error code `3221225477` +- InvokeAI crashes without an error, but `eventvwr.msc` reveals an error with code `0xc0000005` (the hex equivalent of `3221225477`) + +If you are running out of page file space, try the following solutions: + +- Make sure that you have sufficient disk space for the page file to grow. Watch your disk usage as Invoke runs. If it climbs near 100% leading up to the crash, then this is very likely the source of the issue. Clear out some disk space to resolve the issue. +- Make sure that your page file is set to "System managed size" (this is the default) rather than a custom size. Under the "System managed size" policy, the page file will grow dynamically as needed. diff --git a/docs/src/content/docs/configuration/patchmatch.mdx b/docs/src/content/docs/configuration/Optimization/patchmatch.mdx similarity index 100% rename from docs/src/content/docs/configuration/patchmatch.mdx rename to docs/src/content/docs/configuration/Optimization/patchmatch.mdx diff --git a/docs/src/content/docs/configuration/sdnq-quantization.mdx b/docs/src/content/docs/configuration/Optimization/sdnq-quantization.mdx similarity index 92% rename from docs/src/content/docs/configuration/sdnq-quantization.mdx rename to docs/src/content/docs/configuration/Optimization/sdnq-quantization.mdx index ff19ee14e8d..70a10fffe39 100644 --- a/docs/src/content/docs/configuration/sdnq-quantization.mdx +++ b/docs/src/content/docs/configuration/Optimization/sdnq-quantization.mdx @@ -35,7 +35,7 @@ Typical reductions vs. the bfloat16 baseline: | FLUX.2 Klein 9B (dynamic + SVD) | ~18 GB | ~13 GB | ~11 GB | | Z-Image Turbo | ~12 GB | ~5 GB | ~5 GB | -The actual peak VRAM depends on resolution, batch size, attention backend, and whether [Low-VRAM mode](/configuration/low-vram-mode/) is enabled. +The actual peak VRAM depends on resolution, batch size, attention backend, and whether [Low-VRAM mode](/configuration/optimization/low-vram-mode/) is enabled. ## Installing SDNQ models @@ -103,7 +103,10 @@ If you need higher quality and have the VRAM, the static `Disty0/FLUX.2-klein-9B | **SDNQ uint4 + SVD** | ~50 % | ~50 % | ✅ sidecar | Full diffusers pipeline | | **GGUF Q4_K_M** | ~30 % | ~30 % | ✅ sidecar | Single-file transformer + separate encoders/VAE | | **BnB NF4** | ~50 % | ~50 % | ✅ sidecar | Single-file transformer + separate encoders/VAE | -| **FP8 storage** | ~50 % | ~50 % | ✅ direct | Any full-precision model (toggle in Model Manager) | +| **int8** (`int8_convrot`) | ~50 % | ~50 % | ✅ sidecar | Single-file transformer or encoder, stays int8 on every device | +| **nvfp4** | ~35 % | ~35 % | ✅ sidecar | Single-file transformer or encoder, stays packed on every GPU | +| **fp8 / fp8_scaled** checkpoint | ~50 % | ~50 % | ✅ sidecar | Single file; stays fp8 with FP8 Storage (on after install) or `fp8_compute` | +| **FP8 storage** | ~50 % | ~50 % | ✅ sidecar | Full-precision models whose loader supports it ([details](/configuration/optimization/fp8-storage/#where-the-toggle-is-shown)) | The headline differences: @@ -111,7 +114,7 @@ The headline differences: - **SDNQ has the cleanest dynamic-precision story**: GGUF picks one bit-width per file; SDNQ dynamic-mixed exports tune precision per layer. - **GGUF is more memory-efficient** at the same nominal bit-width because it uses smaller groups. SDNQ trades that for the SVD correction option. -For low VRAM (~6–8 GB), GGUF Q4 is still the best fit. For 12–16 GB cards that can host a FLUX-class model, SDNQ is the simplest "install one thing, get a working pipeline" option. +For low VRAM (~6–8 GB), GGUF Q4 is still the best fit for most models; for Z-Image, the NVFP4 and int8 builds fit there too. For 12–16 GB cards that can host a FLUX-class model, SDNQ is the simplest "install one thing, get a working pipeline" option. ## Using a pipeline without standalone components diff --git a/docs/src/content/docs/configuration/docker.mdx b/docs/src/content/docs/configuration/docker.mdx deleted file mode 100644 index 17f6e108eb6..00000000000 --- a/docs/src/content/docs/configuration/docker.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Docker ---- - -import { Aside, Tabs, TabItem } from '@astrojs/starlight/components' - -import SystemRequirementsLink from '@components/SystemRequirmentsLink.astro' - - - -:::note[Operating Systems and GPU Support] - - - Docker Desktop on Windows [includes GPU support](https://www.docker.com/blog/wsl-2-gpu-support-for-docker-desktop-on-nvidia-gpus/). - - - Docker can not access the GPU on macOS, so your generation speeds will be slow. Use the [launcher](../../start-here/installation) instead. - - - Configure Docker to access your machine's GPU. - Follow the [NVIDIA](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) or [AMD](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/how-to/docker.html) documentation. - - -::: - -## TL;DR - -Ensure your Docker setup is able to use your GPU. Then: - -```bash -docker run --runtime=nvidia --gpus=all --publish 9090:9090 ghcr.io/invoke-ai/invokeai -``` - -Once the container starts up, open [http://localhost:9090](http://localhost:9090) in your browser, install some models, and start generating. - -## Build-It-Yourself - -All the docker materials are located inside the [docker](https://github.com/invoke-ai/InvokeAI/tree/main/docker) directory in the Git repo. - -```bash -cd docker -cp .env.sample .env -docker compose up -``` - -We also ship the `run.sh` convenience script. See the `docker/README.md` file for detailed instructions on how to customize the docker setup to your needs. - -### Prerequisites - -#### Install [Docker](https://github.com/santisbon/guides#docker) - -On the [Docker Desktop app](https://docs.docker.com/get-docker/), go to `Preferences` -> `Resources` -> `Advanced`. Increase the CPUs and Memory to avoid this [Issue](https://github.com/invoke-ai/InvokeAI/issues/342). You may need to increase Swap and Disk image size too. - -### Setup - -Set up your environment variables. In the `docker` directory, make a copy of `.env.sample` and name it `.env`. Make changes as necessary. - -Any environment variables supported by InvokeAI can be set here - please see the [configuration docs](/configuration/invokeai-yaml/) for further detail. - -At the very least, you might want to set the `INVOKEAI_ROOT` environment variable -to point to the location where you wish to store your InvokeAI models, configuration, and outputs. - -| Environment Variable | Default value | Description | -| --- | --- | --- | -| `INVOKEAI_ROOT` | `~/invokeai` | **Required** - the location of your InvokeAI root directory. It will be created if it does not exist. | -| `HUGGING_FACE_HUB_TOKEN` | | InvokeAI will work without it, but some of the integrations with HuggingFace (like downloading from models from private repositories) may not work | -| `GPU_DRIVER` | `cuda` | Optionally change this to `rocm` to build the image for AMD GPUs, or `cpu` for CPU-only builds (required on ARM64 / `aarch64` hosts such as the Raspberry Pi 5). NOTE: Use the `build.sh` script to build the image for this to take effect. | - -#### Build the Image - -Use the standard `docker compose build` command from within the `docker` directory. - -If using an AMD GPU: -a: set the `GPU_DRIVER=rocm` environment variable in `docker-compose.yml` and continue using `docker compose build` as usual, or -b: set `GPU_DRIVER=rocm` in the `.env` file and use the `build.sh` script, provided for convenience - -#### Run the Container - -Use the standard `docker compose up` command, and generally the `docker compose` [CLI](https://docs.docker.com/compose/reference/) as usual. - -Once the container starts up (and configures the InvokeAI root directory if this is a new installation), you can access InvokeAI at [http://localhost:9090](http://localhost:9090) - -## Troubleshooting / FAQ - -
- "I am running Windows under WSL2, and am seeing a 'no such file or directory' error." - - Your `docker-entrypoint.sh` might have has Windows (CRLF) line endings, depending how you cloned the repository. - To solve this, change the line endings in the `docker-entrypoint.sh` file to `LF`. You can do this in VSCode - (`Ctrl+P` and search for "line endings"), or by using the `dos2unix` utility in WSL. - Finally, you may delete `docker-entrypoint.sh` followed by `git pull; git checkout docker/docker-entrypoint.sh` - to reset the file to its most recent version. - For more information on this issue, see [Docker Desktop documentation](https://docs.docker.com/desktop/troubleshoot/topics/#avoid-unexpected-syntax-errors-use-unix-style-line-endings-for-files-in-containers) - -
diff --git a/docs/src/content/docs/configuration/fp8-storage.mdx b/docs/src/content/docs/configuration/fp8-storage.mdx deleted file mode 100644 index 56d73198ed0..00000000000 --- a/docs/src/content/docs/configuration/fp8-storage.mdx +++ /dev/null @@ -1,143 +0,0 @@ ---- -title: FP8 Storage -sidebar: - order: 3 ---- - -import { Steps } from '@astrojs/starlight/components'; - -FP8 Storage cuts a model's VRAM footprint roughly in half by keeping weights on the GPU in 8-bit floating-point format (`float8_e4m3fn`). During inference, each layer's weights are cast on-the-fly back up to the compute precision (FP16/BF16), then cast back to FP8 after the forward pass — so quality is largely preserved. - -It pairs well with [Low-VRAM mode](/configuration/low-vram-mode/): low-VRAM mode streams layers between RAM and VRAM, while FP8 Storage shrinks the layers themselves. - -:::caution[For full precision models only] -FP8 Storage only applies to **full precision** checkpoints (FP16 / BF16 / FP32). It is **silently a no-op** for already-quantized formats — **GGUF**, **NF4**, **int8**, and [**SDNQ**](/configuration/sdnq-quantization/) checkpoints carry their own storage precision and the loader returns a different module type that the FP8 layer cast does not touch. If your model is already quantized, the toggle has no effect; use the full-precision variant of the model if you want to enable FP8 Storage. -::: - -## Requirements - -- **Nvidia GPU on Windows or Linux.** FP8 Storage uses CUDA tensor types and is silently disabled on CPU and MPS. -- **CUDA 12.x and recent PyTorch.** The `float8_e4m3fn` dtype was added in PyTorch 2.1 — InvokeAI's bundled versions satisfy this. - -There is no hardware requirement for FP8 *compute* — InvokeAI casts back to FP16/BF16 for math. This means FP8 Storage works on GPUs that do not natively support FP8 matmul (e.g. RTX 30-series), at a small per-step throughput cost. - -## Hardware support tiers - -InvokeAI's FP8 path stores weights in FP8 and casts them back to BF16/FP16 on each forward pass via its own `register_forward_pre_hook` / `register_forward_hook` wrappers (the same skip list as diffusers' `apply_layerwise_casting`, but applied to every `nn.Module` — including diffusers `ModelMixin` subclasses — so it composes correctly with InvokeAI's `CustomLinear` and partial loading). The practical benefit of toggling FP8 Storage depends on what your GPU can do natively. There are three tiers: - -### RTX 30-series and older Ampere workstation cards — VRAM win only - -The toggle works as advertised: the UNet / transformer drops by roughly 50% on the GPU. Per-step latency is the same or marginally slower because every forward pass adds an FP8 → BF16 cast on entry and a BF16 → FP8 cast on exit. This is the **largest target group**: 3090 owners squeezing FLUX into 24 GB benefit the most. - -### RTX 40-series, RTX 50-series, and Hopper — VRAM win today, compute win possible later - -These GPUs have native FP8 tensor cores. The toggle still buys you the same ~50% VRAM reduction today, because the forward pass still runs in BF16 — the hook casts weights back up to compute precision before each layer. If InvokeAI later wires up a true FP8 matmul path (e.g. via `torchao`), the same toggle will *also* unlock compute speedups on this hardware. Until then, treat the benefit as "VRAM only, same as Ampere". - -### Older CUDA cards — still a VRAM win - -`float8_e4m3fn` is a pure storage dtype in PyTorch and works on any CUDA device, so pre-Ampere cards (GTX 16-series, RTX 20-series, etc.) get the same ~50% VRAM reduction as Ampere. There are no native FP8 tensor cores on these GPUs, so the throughput trade-off is the same as on the 30-series: cast in, compute in BF16/FP16, cast back out. - -### MPS and CPU — no-op - -FP8 Storage is silently disabled on anything that is not CUDA. On CPU PyTorch *technically* supports FP8 dtypes, but the cast operations are software-emulated and end up costing more than the memory savings buy back, so InvokeAI gates the entire path on `device.type == "cuda"`. If you toggle it on CPU or MPS, the loader skips the cast and returns the model unchanged with no log line. - -## Enabling FP8 Storage - -FP8 Storage is a **per-model setting**, configured from the Model Manager: - - -1. Open the **Model Manager**. -2. Select a model (Main, ControlNet, or T2I-Adapter). -3. Under **Default Settings**, toggle **FP8 Storage (Save VRAM)**. -4. Click **Save**. - - -The setting takes effect on the next load. If the model is already in the cache, InvokeAI evicts the cached copy automatically so the new setting applies — even if a generation is currently using the model (the eviction is deferred until the generation finishes). - -:::tip[When to enable] - Enable FP8 Storage on large models that don't fit comfortably in VRAM — FLUX dev/Klein, large SDXL checkpoints, ControlNet-XL adapters. For smaller SD1 / SD2 models, the savings are negligible and not worth the small precision trade-off. -::: - -## What FP8 Storage applies to - -FP8 Storage is **only** applied to layers where the precision trade-off is acceptable: - -| Model type | FP8 applied? | -| ---------------------------------- | ----------------------------------------------- | -| Main models (SD1, SD2, SDXL) | Yes | -| FLUX.1 / FLUX.2 Klein | Yes | -| Z-Image, Anima, Krea-2, Qwen-Image | Yes | -| ControlNet, T2I-Adapter | Yes | -| Wan | Not yet — the switch is shown but has no effect | -| VAE | No — visible decode-quality regression | -| Text encoders, tokenizers | No — small models, no benefit | -| Anima ControlNet-LLLite | No — adapter is only tens of MB | -| LoRA, ControlLoRA | No — patched into base, not run alone | - -Within a supported model, **norm layers, position/patch embeddings, and `proj_in`/`proj_out` are skipped** so precision-sensitive tiny learned scalars (e.g. FLUX `RMSNorm.scale`) aren't crushed to FP8. This mirrors the diffusers default skip list. - -**On top of that, each model's own declared exclusions are honored.** Architectures list their precision-sensitive modules themselves (diffusers calls these `_skip_layerwise_casting_patterns` and `_keep_in_fp32_modules`), and those layers stay at compute precision too. This matters where the generic patterns don't fit the architecture's naming, and it is not a nicety: Z-Image and Anima both name their timestep-embedding MLP `t_embedder`, which no generic pattern matches. Cast to FP8, Z-Image fails outright and Anima renders a heavily dithered image with no fine detail. - -The cost is a slightly smaller saving on the models that declare a lot. Measured against the generic defaults alone: - -| Model | Weights kept at compute precision | Saving given up | -| ------------------------- | ------------------------------------------------ | --------------- | -| Krea-2 | 39 M (`time_embed`) | ~38 MiB | -| Anima | 19 M (`t_embedder`, `x_embedder`, `final_layer`) | ~18 MiB | -| Wan 14B (not applied yet) | 232 M (`condition_embedder`, `patch_embedding`) | ~221 MiB | -| FLUX.1, Qwen-Image | 0 | 0 | - -Wan is listed for completeness only: no Wan loader applies FP8 Storage yet, so the switch has no effect on Wan models and there is nothing to budget for today. - -## Quality trade-offs - -FP8 Storage is **near-lossless** for most workloads because: - -- Norms and embeddings (the precision-sensitive layers) are skipped. -- The actual matmul still happens in FP16/BF16 — FP8 is only the on-GPU storage format. - -That said, some artifacts have been reported on: - -- **VAEs** — never cast (the toggle has no effect on VAE submodels). -- **Heavy LoRA stacks** — patching is unaffected, but very precision-sensitive LoRAs may show slight drift. Compare a side-by-side if your workflow depends on subtle LoRA behavior. - -If you see unexpected quality regressions, disable FP8 Storage on the affected model and re-run. - -## Combining with Low-VRAM mode - -**FP8 + partial loading**: fully supported. FP8 Storage shrinks the layers; partial loading streams them between RAM and VRAM as needed. Use both on tight VRAM budgets. - -(For why FP8 Storage doesn't stack on top of GGUF / NF4 / int8 checkpoints, see the callout at the top of this page.) - -## Troubleshooting - -### "I toggled FP8 Storage but VRAM usage didn't change" - -The cache eviction is immediate for idle models, but **deferred until the next unlock** if the model is mid-generation. Wait for the current generation to finish, then start a new one — the next load will use the new setting. - -If VRAM still hasn't dropped: - -- Check the InvokeAI log for `FP8 layerwise casting enabled for `. If the line isn't there, the model is on the exclusion list, or its loader does not apply FP8 yet (VAE, text encoder, LoRA, Wan — see table above). -- Confirm you are on CUDA. FP8 Storage is silently disabled on CPU and MPS. - -### Quality regression on a specific model - -Disable FP8 Storage for that model in Model Manager and reload. If quality is restored, the model has FP8-sensitive layers that fall outside the default skip list. Please open an issue with the model name and a side-by-side comparison. - -### "RuntimeError: ... float8_e4m3fn ..." - -You're on a PyTorch version that predates FP8 support. Reinstall InvokeAI using the official launcher — the bundled torch version supports FP8. - -### Reporting an FP8 issue - -If FP8 Storage misbehaves — crash, quality regression, OOM that shouldn't happen — please [open a GitHub issue](https://github.com/invoke-ai/InvokeAI/issues/new/choose) and include: - -- **What you did**: the workflow / generation step that triggered the problem, and whether it reproduces every time. -- **Model**: exact name and variant (e.g. "FLUX.2 Klein 9B Diffusers", "SDXL Base 1.0 single-file"), and whether the file is a full-precision checkpoint or already quantized (GGUF / NF4 / int8). -- **LoRAs**: whether any LoRAs (or ControlLoRAs) are stacked on the model, and how many. -- **Other toggles**: Low-VRAM mode on/off, any `cpu_only` text encoder setting, configured VRAM limit. -- **GPU**: model and VRAM size (e.g. "RTX 3090 24 GB", "RTX 4070 Ti 12 GB"). -- **OS**: Windows or Linux, plus driver / CUDA version if you have it. -- **Logs**: the InvokeAI log around the failure — in particular the `FP8 layerwise casting enabled for ` line (or its absence) and any traceback. - -A side-by-side image comparison (FP8 on vs. FP8 off, same seed) is extremely useful for quality regressions. diff --git a/docs/src/content/docs/configuration/invokeai-yaml.mdx b/docs/src/content/docs/configuration/invokeai-yaml.mdx deleted file mode 100644 index a29ade7a97e..00000000000 --- a/docs/src/content/docs/configuration/invokeai-yaml.mdx +++ /dev/null @@ -1,300 +0,0 @@ ---- -title: YAML Config -sidebar: - order: 1 ---- - -import { FileTree } from '@astrojs/starlight/components' -import SettingsDocs from '@lib/components/SettingsDocs.astro' - -Runtime settings, including the location of files and directories, memory usage, and performance, are managed via the `invokeai.yaml` config file or environment variables. A subset of settings may be set via commandline arguments. - -Settings sources are used in this order: - -- CLI args -- Environment variables -- `invokeai.yaml` settings -- Fallback: defaults - -### InvokeAI Root Directory - -On startup, InvokeAI searches for its "root" directory. This is the directory that contains models, images, the database, and so on. It also contains a configuration file called `invokeai.yaml`. - - - - models/ - - outputs/ - - databases/ - - workflow_thumbnails/ - - style_presets/ - - nodes/ - - configs/ - - invokeai.example.yaml - - **invokeai.yaml** - - -InvokeAI searches for the root directory in this order: - -1. The `--root ` CLI arg. -2. The environment variable INVOKEAI_ROOT. -3. The directory containing the currently active virtual environment. -4. Fallback: a directory in the current user's home directory named `invokeai`. - -### InvokeAI Configuration File - -Inside the root directory, we read settings from the `invokeai.yaml` file. - -It has two sections - one for internal use and one for user settings: - -```yaml -# Internal metadata - do not edit: -schema_version: 4.0.2 - -# Put user settings here - see https://invoke.ai/configuration/invokeai-yaml/: -host: 0.0.0.0 # serve the app on your local network -models_dir: D:\invokeai\models # store models on an external drive -precision: float16 # always use fp16 precision -``` - -The settings in this file will override the defaults. You only need -to change this file if the default for a particular setting doesn't -work for you. - -You'll find an example file next to `invokeai.yaml` that shows the default values. - -Some settings, like [Model Marketplace API Keys], require the YAML -to be formatted correctly. Here is a [basic guide to YAML files]. - -#### Custom Config File Location - -You can use any config file with the `--config` CLI arg. Pass in the path to the `invokeai.yaml` file you want to use. - -Note that environment variables will trump any settings in the config file. - -#### Model Marketplace API Keys - -Some model marketplaces require an API key to download models. You can provide a URL pattern and appropriate token in your `invokeai.yaml` file to provide that API key. - -The pattern can be any valid regex (you may need to surround the pattern with quotes): - -```yaml -remote_api_tokens: - # Any URL containing `models.com` will automatically use `your_models_com_token` - - url_regex: models.com - token: your_models_com_token - # Any URL matching this contrived regex will use `some_other_token` - - url_regex: '^[a-z]{3}whatever.*\.com$' - token: some_other_token -``` - -The provided token will be added as a `Bearer` token to the network requests to download the model files. As far as we know, this works for all model marketplaces that require authorization. - -:::tip[Hugging face Models] -If you get an error when installing a HF model using a URL instead of repo id, you may need to [set up a HF API token](https://huggingface.co/settings/tokens) and add an entry for it under `remote_api_tokens`. Use `huggingface.co` for `url_regex`. -::: - -#### Model Hashing - -Models are hashed during installation, providing a stable identifier for models across all platforms. Hashing is a one-time operation. - -```yaml -hashing_algorithm: blake3_single # default value -``` - -You might want to change this setting, depending on your system: - -- `blake3_single` (default): Single-threaded - best for spinning HDDs, still OK for SSDs -- `blake3_multi`: Parallelized, memory-mapped implementation - best for SSDs, terrible for spinning disks -- `random`: Skip hashing entirely - fastest but of course no hash - -During the first startup after upgrading to v4, all of your models will be hashed. This can take a few minutes. - -Most common algorithms are supported, like `md5`, `sha256`, and `sha512`. These are typically much, much slower than either of the BLAKE3 variants. - -#### Path Settings - -These options set the paths of various directories and files used by InvokeAI. Any user-defined paths should be absolute paths. - -#### Multi-GPU Generation - -On a machine with more than one GPU, InvokeAI can run several generation sessions at the same time — one per GPU — instead of processing the queue one job at a time. In multi-user deployments with the default `session_queue_mode: round_robin`, jobs are distributed fairly across users, so a single user's large batch cannot monopolize every GPU while others wait. - -This is controlled by the `generation_devices` setting: - -```yaml -generation_devices: auto # default value -``` - -| Value | Behavior | -| -------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -| `auto` | Use every available CUDA GPU, running one generation session per GPU concurrently. This is the default. Exception: if the legacy `device` setting is pinned to a specific device, `auto` uses only that device (see Notes below). | -| `[cuda:0,cuda:1]` | Use the specific devices listed, one session per device. Useful for reserving a GPU for other work. | -| `[cuda:0]` | Use a single specific device. Generation runs serially, as it did before multi-GPU support. | - -Each entry in the list must be one of `cpu`, `cuda`, `mps`, or `cuda:N`, where `N` is a zero-based device number (`cuda:0` is the first GPU, `cuda:1` the second, and so on). The list may not be empty — use `auto` (or a single-device list) instead. - -```yaml -# Use the first and third GPUs, leaving the second free for other tasks -generation_devices: [cuda:0, cuda:2] -``` - -Notes: - -- If the legacy `device` setting is pinned to a specific device (e.g. `device: cuda:1`), `auto` resolves to that single device rather than every GPU. This preserves the behavior of installs that pinned `device` before multi-GPU support existed (for example, to keep generation off a display GPU). An explicit `generation_devices` list overrides `device`. -- On a system without a CUDA GPU, `auto` resolves to the single best available device (`mps` on Apple Silicon, otherwise `cpu`), so generation runs serially. -- Each active GPU gets its own model cache, but the CPU (system RAM) copy of each model's weights is shared between the caches, so a model loaded on several GPUs is held in RAM only once. Running many GPUs in parallel can still increase RAM usage (different GPUs may hold different models), so ensure you have ample system memory before enabling a large device list. -- When a GPU finishes a job, it prefers queued jobs whose models it already has loaded (among the same user's equal-priority jobs) to avoid slow model reloads. Set `session_queue_mode: FIFO` to disable this reordering and enforce strict submission order. -- Duplicate entries are ignored; `[cuda:0, cuda:0]` is treated as `[cuda:0]`. -- You can restrict which physical GPUs InvokeAI sees with the `CUDA_VISIBLE_DEVICES` environment variable. When set, `auto` only enumerates the visible subset, and `cuda:N` indices refer to positions within that subset. - -During parallel generation, the progress display shows one progress bar per active session, stacked vertically, each disappearing as its session completes. - -#### Text Encoder Offload to Idle GPUs - -When more than one GPU is configured for generation but not all of them are busy, InvokeAI can run a session's text/prompt encoder on a currently-idle GPU instead of the GPU running its denoise pipeline. This avoids evicting the denoise model from VRAM just to make room for the encoder, and lets the cached encoder be reused across generations — making repeated generations noticeably smoother. - -This is controlled by the `offload_text_encoders_to_idle_gpus` setting: - -```yaml -offload_text_encoders_to_idle_gpus: true # default value -``` - -| Value | Behavior | -| ------- | ---------------------------------------------------------------------------------------------------------------- | -| `true` | Run text encoders on an idle GPU when one is available. This is the default. | -| `false` | Always run text encoders on the same GPU as the rest of the pipeline (the behavior before this feature existed). | - -Notes: - -- This has no effect unless at least two `generation_devices` are configured. On a single device — or when every GPU is already busy with its own session — encoders run on the session's own GPU, exactly as if the setting were `false`. -- It is purely a placement optimization and does not change generated images. -- A borrowed GPU is used exclusively for the encoder while it runs, so it never interferes with a generation session running on that same GPU. - -#### Image Subfolder Strategy - -By default, generated images are stored in a single flat directory under `outputs/images/`. The `image_subfolder_strategy` setting lets you organize newly-created images into subfolders automatically. You can edit this setting in `invokeai.yaml` or, as an admin user, in the Settings panel. - -```yaml -image_subfolder_strategy: flat # default value -``` - -Available strategies: - -| Strategy | Example Path | Description | -| -------- | -------------------------------------- | ------------------------------------------------------------------------------------------------- | -| `flat` | `outputs/images/abc123.png` | Store images directly in the images directory. | -| `date` | `outputs/images/2026/03/17/abc123.png` | Organize images by creation date. | -| `type` | `outputs/images/general/abc123.png` | Organize images by image category. | -| `hash` | `outputs/images/ab/abc123.png` | Use the first two characters of the image UUID for filesystem performance with large collections. | - -Changing this setting only affects newly-created images. Existing images remain in their current locations unless you run [Image Storage Maintenance](/features/image-storage-maintenance/). - -#### Response Compression - -API responses (JSON, HTML, JS, CSS, SVG) are gzipped before being sent. Already-compressed responses — PNG, WebP, JPEG, MP4 — are passed through untouched, since compressing them costs CPU and returns a body no smaller than the original. - -Compression runs on the server's event loop, which means it blocks *everything else* for its duration: no other request is served and no progress event is delivered while it runs. The `http_compression_level` setting controls that trade-off: - -```yaml -http_compression_level: 9 # default value -``` - -| Value | Behavior | -| ------ | ---------------------------------------------------------------------------------------------- | -| `0` | No compression. Responses are sent as-is and the compression middleware is not installed. | -| `1` | Fastest compression, slightly larger output. | -| `2`–`8` | Progressively slower, marginally smaller. | -| `9` | Smallest output, by far the slowest. This is the default. | - -The setting can also be given as the `INVOKEAI_HTTP_COMPRESSION_LEVEL` environment variable. - -If the UI feels sluggish while a large library is being browsed, lowering this is one of the cheapest wins available. Measured on the image-name list of a 200,000-image library (8.48 MB of JSON): - -| Level | Time | Output | -| ----- | ------: | --------: | -| `1` | 16.4 ms | 6.1% of input | -| `6` | 36.1 ms | 5.9% of input | -| `9` | 90.2 ms | 5.7% of input | - -Level 9 spends 5.5× the event-loop time to save 0.4 percentage points of bandwidth. On a locally-served install the bandwidth is free and the stall is not, so `http_compression_level: 1` is usually the better setting there — the default stays at `9` only so that upgrading does not silently change how anyone's install behaves. - -:::tip[Behind a reverse proxy] -If you serve InvokeAI through nginx, Caddy, or similar, set `http_compression_level: 0` and let the proxy compress instead. The proxy does that work in its own process rather than on InvokeAI's event loop, and it avoids compressing the same bytes twice. -::: - -#### Logging - -Several different log handler destinations are available, and multiple destinations are supported by providing a list: - -```yaml -log_handlers: - - console - - syslog=localhost - - file=/var/log/invokeai.log -``` - -- `console` is the default. It prints log messages to the command-line window from which InvokeAI was launched. - -- `syslog` is only available on Linux and Macintosh systems. It uses - the operating system's "syslog" facility to write log file entries - locally or to a remote logging machine. `syslog` offers a variety - of configuration options: - -```yaml -syslog=/dev/log` - log to the /dev/log device -syslog=localhost` - log to the network logger running on the local machine -syslog=localhost:512` - same as above, but using a non-standard port -syslog=fredserver,facility=LOG_USER,socktype=SOCK_DRAM` -- Log to LAN-connected server "fredserver" using the facility LOG_USER and datagram packets. -``` - -- `http` can be used to log to a remote web server. The server must be - properly configured to receive and act on log messages. The option - accepts the URL to the web server, and a `method` argument - indicating whether the message should be submitted using the GET or - POST method. - -```yaml -http=http://my.server/path/to/logger,method=POST -``` - -The `log_format` option provides several alternative formats: - -- `color` - default format providing time, date and a message, using text colors to distinguish different log severities -- `plain` - same as above, but monochrome text only -- `syslog` - the log level and error message only, allowing the syslog system to attach the time and date -- `legacy` - a format similar to the one used by the legacy 2.3 InvokeAI releases. - -### Environment Variables - -All settings may be set via environment variables by prefixing `INVOKEAI_` -to the variable name. For example, `INVOKEAI_HOST` would set the `host` -setting. - -For non-primitive values, pass a JSON-encoded string: - -```sh -export INVOKEAI_REMOTE_API_TOKENS='[{"url_regex":"modelmarketplace", "token": "12345"}]' -``` - -We suggest using `invokeai.yaml`, as it is more user-friendly. - -### CLI Args - -A subset of settings may be specified using CLI args: - -- `--root`: specify the root directory -- `--config`: override the default `invokeai.yaml` file location - -### Low-VRAM Mode - -See the [Low-VRAM mode docs][low-vram] for details on enabling this feature. - -### All Settings - -The full settings reference is below. Additional explanations for selected settings appear earlier on this page. - - - -[basic guide to yaml files]: https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/ -[Model Marketplace API Keys]: #model-marketplace-api-keys -[low-vram]: /configuration/low-vram-mode diff --git a/docs/src/content/docs/configuration/low-vram-mode.mdx b/docs/src/content/docs/configuration/low-vram-mode.mdx deleted file mode 100644 index f2b970eca6b..00000000000 --- a/docs/src/content/docs/configuration/low-vram-mode.mdx +++ /dev/null @@ -1,218 +0,0 @@ ---- -title: Low-VRAM mode -sidebar: - order: 2 ---- - -As of v5.6.0, Invoke has a low-VRAM mode. It works on systems with dedicated GPUs (Nvidia GPUs on Windows/Linux and AMD GPUs on Linux). - -This allows you to generate even if your GPU doesn't have enough VRAM to hold full models. Most users should be able to run even the beefiest models - like the ~24GB unquantised FLUX dev model. - -## Enabling Low-VRAM mode - -Low-VRAM mode is **enabled by default** via the `enable_partial_loading: true` setting in `invokeai.yaml`. No action is required to turn it on. - -**Windows users should also [disable the Nvidia sysmem fallback](#disabling-nvidia-sysmem-fallback-windows-only)**. - -It is possible to fine-tune the settings for best performance or if you still get out-of-memory errors (OOMs). - -If you want to disable partial loading (e.g. on systems with plenty of VRAM where full loading is faster), add this line to your `invokeai.yaml` and restart Invoke: - -```yaml -enable_partial_loading: false -``` - -:::tip[How to find `invokeai.yaml`] - The `invokeai.yaml` configuration file lives in your install directory. To access it, run the **Invoke Community Edition** launcher and click the install location. This will open your install directory in a file explorer window. - - You'll see `invokeai.yaml` there and can edit it with any text editor. After making changes, restart Invoke. - - If you don't see `invokeai.yaml`, launch Invoke once. It will create the file on its first startup. -::: - -## Details and fine-tuning - -Low-VRAM mode and related workload-specific optimizations include: - -- Partial model loading (`enable_partial_loading`) -- PyTorch CUDA allocator config (`pytorch_cuda_alloc_conf`) -- Dynamic RAM and VRAM cache sizes (`max_cache_ram_gb`, `max_cache_vram_gb`) -- Working memory (`device_working_mem_gb`) -- Keeping a RAM weight copy (`keep_ram_copy_of_weights`) -- Wan video memory optimization (`wan_memory_optimization`) -- PiD decode activation chunking (`pid_memory_optimization`) - -Read on to learn about these features and understand how to fine-tune them for your system and use-cases. - -### Wan video memory optimization - -Wan video generation has an additional opt-in memory optimization: - -```yaml -wan_memory_optimization: true -``` - -This reserves VRAM so partial-load Wan transformer weights target about 2 GiB resident and stream remaining layers from RAM when partial model loading is enabled (the default). The explicit residency trim may be a no-op when cache admission already reaches that target. If `enable_partial_loading: false`, the activation, timestep, and VAE optimizations still apply, but transformer weights remain fully resident and the 2 GiB residency target is unavailable. It also chunks pointwise transformer activations, compacts TI2V per-token timestep conditioning, and streams untiled VAE decode chunks directly to MP4. It reduces peak VRAM during both denoise and decode, but generation can be substantially slower and requires enough system RAM for offloaded weights. Spatially tiled VAE decode continues to use its existing full-tile path. - -The optimized BF16 transformer path can produce small numerical differences because chunked matrix operations accumulate in a different order. Same-seed output is not guaranteed to be bit-identical; set `wan_memory_optimization: false` for the baseline path. - -Developers with a CUDA or ROCm device can validate Wan VAE memory estimates with `python scripts/calibrate_wan_vae_working_memory.py --vae `. The script reports allocated and reserved memory deltas; its implied scaling constant uses allocated memory to match the shipped estimator, while reserved memory shows allocator headroom. Use `--tiling` to measure the spatially tiled full-decode fallback; it overrides streaming mode. Use `--tile-size ` to override the VAE's default tile size. - -Direct MP4 streaming keeps the VAE cache lock while chunks are decoded and written. Wan's causal decoder state and weights must remain live for the sequence; releasing the lock would require a separate bounded decode and encode queue. - -### Partial model loading - -Invoke's partial model loading works by streaming model "layers" between RAM and VRAM as they are needed. - -When an operation needs layers that are not in VRAM, but there isn't enough room to load them, inactive layers are offloaded to RAM to make room. - -#### Enabling partial model loading - -Partial model loading is enabled by default. The corresponding setting in `invokeai.yaml` is: - -```yaml -enable_partial_loading: true -``` - -Set it to `false` to disable partial loading. - -### PyTorch CUDA allocator config - -The PyTorch CUDA allocator's behavior can be configured using the `pytorch_cuda_alloc_conf` config. Tuning the allocator configuration can help to reduce the peak reserved VRAM. The optimal configuration is dependent on many factors (e.g. device type, VRAM, CUDA driver version, etc.), but switching from PyTorch's native allocator to using CUDA's built-in allocator works well on many systems. To try this, add the following line to your `invokeai.yaml` file: - -```yaml -pytorch_cuda_alloc_conf: "backend:cudaMallocAsync" -``` - -A more complete explanation of the available configuration options is [here](https://pytorch.org/docs/stable/notes/cuda.html#optimizing-memory-usage-with-pytorch-cuda-alloc-conf). - -### Dynamic RAM and VRAM cache sizes - -Loading models from disk is slow and can be a major bottleneck for performance. Invoke uses two model caches - RAM and VRAM - to reduce loading from disk to a minimum. - -By default, Invoke manages these caches' sizes dynamically for best performance. - -#### Fine-tuning cache sizes - -Prior to v5.6.0, the cache sizes were static, and for best performance, many users needed to manually fine-tune the `ram` and `vram` settings in `invokeai.yaml`. - -As of v5.6.0, the caches are dynamically sized. The `ram` and `vram` settings are no longer used, and new settings are added to configure the cache. - -**Most users will not need to fine-tune the cache sizes.** - -But, if your GPU has enough VRAM to hold models fully, you might get a perf boost by manually setting the cache sizes in `invokeai.yaml`: - -```yaml -# The default max cache RAM size is logged on InvokeAI startup. It is determined based on your system RAM / VRAM. -# You can override the default value by setting `max_cache_ram_gb`. -# Increasing `max_cache_ram_gb` will increase the amount of RAM used to cache inactive models, resulting in faster model -# reloads for the cached models. -# As an example, if your system has 32GB of RAM and no other heavy processes, setting the `max_cache_ram_gb` to 28GB -# might be a good value to achieve aggressive model caching. -max_cache_ram_gb: 28 - -# The default max cache VRAM size is adjusted dynamically based on the amount of available VRAM (taking into -# consideration the VRAM used by other processes). -# You can override the default value by setting `max_cache_vram_gb`. -# CAUTION: Most users should not manually set this value. See warning below. -max_cache_vram_gb: 16 -``` - -:::caution[Max safe value for `max_cache_vram_gb`] - Most users should not manually configure the `max_cache_vram_gb`. This configuration value caps model-cache residency; `device_working_mem_gb` and operation-specific reservations (e.g. VAE decode) are still subtracted from that cap for every model-cache operation, not only when Wan memory optimization is enabled. A cap below the active working-memory reservation can force aggressive model offloading. - - For users who wish to configure `max_cache_vram_gb`, the max safe value can be determined by subtracting `device_working_mem_gb` from your GPU's VRAM. As described below, the default for `device_working_mem_gb` is 3GB. - - For example, if you have a 12GB GPU, the max safe value for `max_cache_vram_gb` is `12GB - 3GB = 9GB`. - - If you had increased `device_working_mem_gb` to 4GB, then the max safe value for `max_cache_vram_gb` is `12GB - 4GB = 8GB`. - - Most users who override `max_cache_vram_gb` are doing so because they wish to use significantly less VRAM, and should be setting `max_cache_vram_gb` to a value significantly less than the 'max safe value'. -::: - -### Working memory - -Invoke cannot use _all_ of your VRAM for model caching and loading. It requires some VRAM to use as working memory for various operations. - -Invoke reserves 3GB VRAM as working memory by default, which is enough for most use-cases. However, it is possible to fine-tune this setting if you still get OOMs. - -#### Fine-tuning working memory - -You can increase the working memory size in `invokeai.yaml` to prevent OOMs: - -```yaml -# The default is 3GB - bump it up to 4GB to prevent OOMs. -device_working_mem_gb: 4 -``` - -:::tip[Operations may request more working memory] - For some operations, we can determine VRAM requirements in advance and allocate additional working memory to prevent OOMs. - - VAE decoding is one such operation. This operation converts the generation process's output into an image. For large image outputs, this might use more than the default working memory size of 3GB. - - During this decoding step, Invoke calculates how much VRAM will be required to decode and requests that much VRAM from the model manager. If the amount exceeds the working memory size, the model manager will offload cached model layers from VRAM until there's enough VRAM to decode. - - Once decoding completes, the model manager "reclaims" the extra VRAM allocated as working memory for future model loading operations. -::: - -### Keeping a RAM weight copy - -Invoke has the option of keeping a RAM copy of all model weights, even when they are loaded onto the GPU. This optimization is _on_ by default, and enables faster model switching and LoRA patching. Disabling this feature will reduce the average RAM load while running Invoke (peak RAM likely won't change), at the cost of slower model switching and LoRA patching. If you have limited RAM, you can disable this optimization: - -```yaml -# Set to false to reduce the average RAM usage at the cost of slower model switching and LoRA patching. -keep_ram_copy_of_weights: false -``` - -### PiD decode optimization - -PiD decodes directly into high-resolution pixels, so its activation and sampler memory can exceed the working memory needed by a normal VAE decode. Partial model loading reduces memory used by PiD's weights, but does not reduce these full-resolution intermediates. - -To reduce PiD's peak VRAM use, enable its experimental memory optimization in `invokeai.yaml` and restart InvokeAI: - -```yaml -pid_memory_optimization: true -``` - -This setting processes parts of the PiD pixel pathway in chunks and uses float32 instead of float64 sampler intermediates. It applies to every supported PiD decoder. Measured on an RTX 4090, peak activation memory for a 2048px decode drops from ~3.7 GB to ~1.5 GB (1024px: ~0.9 GB to ~0.5 GB). - -`device_working_mem_gb` sets a floor for the reservation. To realize the full PiD savings at 1024px-2048px, lower that setting as needed for your GPU. - -It is disabled by default because it is **not output-preserving**: neither change is bit-exact, and the few-step sampler amplifies the difference into a slightly different image. The delta is small — around 43 dB PSNR, visually indistinguishable in side-by-side comparisons — but it is real, so the same seed and workflow will not reproduce an unoptimized decode exactly. Decoding speed is roughly unchanged: chunking costs about 4%, which the cheaper sampler math largely offsets. - -When the setting is active, each decode logs the resolution, the patch-token count and whether chunking actually engaged — worth checking, since the option is server-wide and never recorded in image metadata. See [PiD Super-Resolution Decode](/features/pid-decode/) for supported models and usage. - -### Disabling Nvidia sysmem fallback (Windows only) - -On Windows, Nvidia GPUs are able to use system RAM when their VRAM fills up via **sysmem fallback**. While it sounds like a good idea on the surface, in practice it causes massive slowdowns during generation. - -It is strongly suggested to disable this feature: - -- Open the **NVIDIA Control Panel** app. -- Expand **3D Settings** on the left panel. -- Click **Manage 3D Settings** in the left panel. -- Find **CUDA - Sysmem Fallback Policy** in the right panel and set it to **Prefer No Sysmem Fallback**. - -![cuda-sysmem-fallback](./assets/cuda-sysmem-fallback.png) - -:::tip[Invoke does the same thing, but better] - If the sysmem fallback feature sounds familiar, that's because Invoke's partial model loading strategy is conceptually very similar - use VRAM when there's room, else fall back to RAM. - - Unfortunately, the Nvidia implementation is not optimized for applications like Invoke and does more harm than good. -::: - -## Troubleshooting - -### Windows page file - -Invoke has high virtual memory (a.k.a. 'committed memory') requirements. This can cause issues on Windows if the page file size limits are hit. (See this issue for the technical details on why this happens: https://github.com/invoke-ai/InvokeAI/issues/7563). - -If you run out of page file space, InvokeAI may crash. Often, these crashes will happen with one of the following errors: - -- InvokeAI exits with Windows error code `3221225477` -- InvokeAI crashes without an error, but `eventvwr.msc` reveals an error with code `0xc0000005` (the hex equivalent of `3221225477`) - -If you are running out of page file space, try the following solutions: - -- Make sure that you have sufficient disk space for the page file to grow. Watch your disk usage as Invoke runs. If it climbs near 100% leading up to the crash, then this is very likely the source of the issue. Clear out some disk space to resolve the issue. -- Make sure that your page file is set to "System managed size" (this is the default) rather than a custom size. Under the "System managed size" policy, the page file will grow dynamically as needed. diff --git a/docs/src/content/docs/contributing/call-saved-workflow.md b/docs/src/content/docs/contributing/call-saved-workflow.md index d96f8c8564d..817dd110893 100644 --- a/docs/src/content/docs/contributing/call-saved-workflow.md +++ b/docs/src/content/docs/contributing/call-saved-workflow.md @@ -2,39 +2,56 @@ title: Call Saved Workflow Architecture --- -## Goal +## Callable Workflow Contract -`CallSavedWorkflowInvocation` should become an engine-native workflow call boundary, not a frontend-only dynamic node -and not a compile-time graph inliner. +`CallSavedWorkflowInvocation` is an engine-native workflow call boundary, not a frontend-only dynamic node and not a +compile-time graph inliner. -The long-term feature goal is: +The callable-workflow contract is: - A parent workflow can call a saved workflow selected by ID. - The call node redraws in the editor based on the selected workflow's exposed form fields. - Parent values and inbound connections bind to those exposed fields as call arguments. - Execution suspends at the call node, runs the selected workflow as a dependent workflow execution, captures explicit return values, and then resumes the parent workflow. -- The architecture must work for Invoke frontend graphs and for externally submitted graphs that use the same node type. - -This document records the current state, the target architecture, and the execution contract needed to continue -development later. - -## Implementation Priority +- The architecture must work for Invoke frontend graphs and for externally submitted graphs that use the same node + type. + +This document describes the callable-workflow architecture and execution contract. Saved-workflow calls use a dedicated +queue boundary for durable rows and queue transitions, while invocation and graph state use the lifecycle +effect/dependency seam to declare, persist, and resume the call. + +The callable node, saved-workflow, and queue interaction contract is stable. Internal execution references, tokens, +effects, frames, and scheduler records are persistence-only implementation metadata. Version-2 +`dump_execution_state()` rebuilds execution references and ordinary output tokens, retains frame-scoped activation +tokens and compact dispatch metadata needed for active recovery, and omits terminal saved-workflow lifecycle effects +and completed child dependencies. The active child queue row is the recovery authority: attached child state remains +available in memory but is omitted from the persisted parent snapshot, while terminal child state is omitted entirely. +Ordinary model serialization and public schemas exclude these four internal ledgers. Existing fields, +requiredness, statuses, events, and client behavior remain compatible. Frontend application behavior is outside this +backend architecture. No code under `invokeai/frontend/...`, including generated `openapi.json` or `schema.ts`, is part +of this execution behavior; the existing frontend/backend external interface is stable. + +The internal generic boundary is implemented in `invokeai.app.services.shared.execution_engine`. It provides +frame-scoped gates, ordered streams, continuations, and capability-bound child dependency records. These records are +used as adapters around the existing queue and materialization paths; they do not add frontend handles, queue statuses, +event payloads, or author-time graph fields. + +## Design Principles Favor the architecturally correct design over the fastest implementation path. -The work may still proceed incrementally, but each increment should satisfy all of the following: +Changes to this area should satisfy all of the following: - testable in isolation - compatible with the long-term architecture described here - non-breaking to existing code and existing workflow execution behavior -Speed is not the primary goal for this phase. The primary goal is to move toward the durable design without introducing -throwaway execution semantics that would need to be unwound later. +The durable design is preferred over throwaway execution semantics. -## Current State +## Current Behavior -Implemented already in the branch: +The callable-workflow feature provides: - A real invocation exists: `call_saved_workflow`. - A real return node exists: `workflow_return`. @@ -50,7 +67,17 @@ Implemented already in the branch: - Incompatible or no-longer-exposed inbound edges are removed in the editor. - Backend validation exists for `workflow_id` existence and access rights. -Implemented runtime scaffolding: +The runtime uses an additive execution-effects seam: + +- the runner invokes `invoke_internal_with_effects()` and applies its result through `GraphExecutionState.apply()`; +- stable execution references, frames, output tokens, and accepted effects are persisted with the runtime state; +- `emit` and `close_stream` are dispatchable by default; +- lifecycle recording for `spawn`, `await`, and `fail` is capability-gated and returns a validated child handle where + applicable; queue mutation remains in the queue adapter; +- saved workflow calls continue through `WorkflowCallCoordinator` and `WorkflowCallQueueLifecycle`, with a generic + child dependency record validating their relationship and aggregation. + +Runtime state: - `GraphExecutionState` now persists workflow-call runtime state: - `workflow_call_stack` @@ -67,15 +94,19 @@ Implemented runtime scaffolding: - child sessions carry a `workflow_call_parent` reference back to the parent call relationship - `GraphExecutionState.next()` returns no runnable node while the parent session is waiting on a child workflow call. - `GraphExecutionState.is_complete()` stays false while waiting. -- `DefaultSessionRunner.run_node()` now treats `call_saved_workflow` as a call boundary instead of a normal executable - node. -- On boundary entry, the runner: - - validates the selected workflow - - builds a workflow call frame - - converts the saved workflow JSON into a backend `Graph` - - validates and applies parent call arguments to the child graph - - creates a child `GraphExecutionState` - - attaches that child session to the waiting parent session +- `DefaultSessionRunner.run_node()` invokes `call_saved_workflow` through the same effect-aware invocation path as + other nodes. The invocation records one capability-bound `spawn_execution` effect and one matching `await` effect; + authorization and input collection happen before those effects are recorded. +- `GraphExecutionState.apply()` persists the pending lifecycle effects without completing the call node. The queue + adapter then consumes the effects to: + - build a workflow call frame + - convert the saved workflow JSON into a backend `Graph` + - validate and apply parent call arguments to the child graph + - create child `GraphExecutionState` instances and durable child queue rows + - attach those child sessions to the waiting parent session +- When the generic child dependency reaches a terminal result, the queue adapter projects the legacy workflow-call + fields, resumes the parent through `GraphExecutionState.apply()`, and completes the call node with ordered return + values. A capability-gated `fail` effect records a parent failure without creating child rows. - Workflow-call runtime responsibilities are now split: - `WorkflowCallCoordinator` handles call-specific setup: - build the child graph @@ -83,7 +114,7 @@ Implemented runtime scaffolding: - create the child `GraphExecutionState` - suspend the parent and enqueue the child queue item - `WorkflowCallQueueLifecycle` handles queue-visible parent/child lifecycle: - - run child queue items + - dispatch child queue items through the normal session runner - resume waiting parents after child success - complete the parent call node with the child `workflow_return` values - fail suspended parents after child failure and cascade that failure upward through parent call chains @@ -101,16 +132,21 @@ Implemented runtime scaffolding: - `root_item_id` - `workflow_call_depth` - child workflow executions are now inserted as their own pending queue rows using those columns +- Child queue rows and the parent's complete waiting-session projection are published in one durable queue transaction. + Concurrent child completions update the latest parent session transactionally, so a sibling cannot overwrite another + sibling's completion. - Parent queue items now enter a real `waiting` status while suspended on a child workflow execution. - `_on_after_run_session()` no longer completes queue items whose sessions are incomplete but waiting. - Dynamic call arguments now execute end-to-end in the current runner path: - - literal dynamic values are serialized into a hidden `workflow_inputs` payload on the parent node at graph-build time + - literal dynamic values are serialized into a hidden `workflow_inputs` payload on the parent node at graph-build + time - stale hidden `workflow_inputs` values from recalled graphs are ignored unless a matching current dynamic field exists - existing dynamic input values are preserved across refresh only while the exposed field type remains compatible; if the selected child workflow changes the exposed field type at the same node/field path, the caller input resets to the child workflow's current initial value - - connected dynamic values are accepted as special call-boundary edges and are resolved from parent results at runtime + - connected dynamic values are accepted as special call-boundary edges and are resolved from parent results at + runtime - both are validated against the child workflow's exposed form interface before being applied to the child graph - Queue lifecycle semantics now exist for workflow-call chains: - parent queue items are suspended in `waiting` while a child queue row runs @@ -130,20 +166,20 @@ Implemented runtime scaffolding: - child queue rows keep `Cancel` - child queue rows hide `Retry` - child queue-row creation is now fail-clean: - - if call-boundary setup fails after some child rows have already been inserted, those child rows are deleted before - the parent invocation is failed + - if call-boundary setup fails after some child rows have already been inserted, those child rows are deleted + before the parent invocation is failed - child queue-row fan-out is bounded by remaining queue capacity, not just the global queue-size setting: - a workflow call that would exceed the remaining pending capacity now fails instead of silently truncating or over-enqueuing child rows - child insertion rechecks pending capacity in the same database transaction as the insert -Implemented conversion helper: +Workflow conversion: - `workflow_graph_builder.py` converts saved workflow JSON into an executable backend `Graph`. - It currently supports the invocation-node subset needed for this feature. - It flattens connector nodes and omits explicit destination field values when a connection exists, matching frontend graph-build semantics. -- It now serves as the first explicit callable-workflow compatibility gate: +- It enforces the callable-workflow boundary: - the selected workflow must contain exactly one `workflow_return` node - connected batch child inputs produced by ordinary non-generator upstream nodes still fail early with a clear unsupported-feature error @@ -162,7 +198,7 @@ Implemented conversion helper: - workflow library list items now surface an explicit unsupported badge and localized reason without blocking normal workflow viewing or editing -What is still not implemented: +Current callable-workflow limitations: - connected batch child inputs whose batch values are produced by ordinary non-generator upstream nodes are still not supported and must fail with a clear domain error @@ -170,21 +206,17 @@ What is still not implemented: with a clear domain error - broader child-workflow compatibility coverage still needs to be expanded from real unsupported shapes rather than trying to interpret every frontend-only workflow representation through the current graph-builder path -- the current workflow-call queue lifecycle is still implemented through dedicated workflow-call runtime classes rather - than a fully generalized parent/child scheduler model +- the queue lifecycle remains implemented by dedicated workflow-call runtime classes, but each waiting call also + registers an internal `ChildDependencyRecord` with an engine-issued capability. The record validates exact parent + identity, ordered all-of aggregation, resource limits, and idempotent child terminal events before the existing queue + adapter resumes, fails, or cancels the parent -Conclusion: +The editor contract, parent call boundary, child execution, argument forwarding, explicit return capture, suspended +parent status, queue-visible child rows, and upward failure cascade are all handled by the current runtime. -- the editor contract is largely in place -- the parent-side runtime call boundary is in place -- child execution, argument forwarding, explicit child return capture, suspended parent status, queue-visible child - rows, and upward failure cascade now work -- the remaining major runtime work is to harden and generalize the parent/child scheduler model rather than prove the - basic call boundary +## Architecture -## Architectural Direction - -Use the architecture that is more likely to be kept long-term: +The implementation uses the following architecture: - `call_saved_workflow` is a call boundary. - The parent graph does not inline the full child workflow into itself at queue time. @@ -193,7 +225,7 @@ Use the architecture that is more likely to be kept long-term: - The child workflow returns explicit outputs to the parent. - The parent resumes once the child returns successfully. -This is preferred over full graph expansion because it: +This keeps execution at the call boundary instead of expanding the full child graph because it: - avoids execution-graph blowup - preserves workflow boundaries @@ -201,9 +233,9 @@ This is preferred over full graph expansion because it: - supports explicit return values - keeps externally submitted graphs viable as long as they use the same node type and contract -## Non-Goals For The Next Phase +## Current Limitations -These should not be the first implementation target: +The current implementation does not provide: - full inline graph expansion of called workflows - unlimited nested workflow call support @@ -241,8 +273,8 @@ Each dynamic input must have: - a default value if defined by the child workflow - a user-facing label and description when available -Current fast-path identity is based on child `nodeId + fieldName`. That is acceptable short-term in the editor, but a -longer-term stable interface ID would be better if child workflows are frequently duplicated or refactored. +Current fast-path identity is based on child `nodeId + fieldName`. A stable interface ID may be useful if child +workflows are frequently duplicated. ### 3. Input Binding At Runtime @@ -259,39 +291,50 @@ Argument values may come from: - parent literal field values - resolved inbound connections into the call node's dynamic inputs -For batch-aware child workflows, the parent call boundary should still pass normal exposed form inputs. Batching should -emerge from the child workflow's own internal batch nodes or generators, not from a separate caller-side batch protocol. +For batch-aware child workflows, the parent call boundary passes normal exposed form inputs. Batching emerges from the +child workflow's own internal batch nodes or generators, not from a separate caller-side batch protocol. ### 4. Child Workflow Execution The child workflow runs as its own dependent execution context, not as an inlined copy of the parent graph. -Desired semantics: +Execution semantics: - parent execution pauses at the call node - child execution runs with inherited context where appropriate - child workflow finishes or fails - parent resumes only if child execution succeeds -This implies the queue/session/runtime layer needs an explicit parent-child execution relationship. +The queue/session/runtime layer now implements an explicit parent-child execution relationship through runtime state, +durable queue metadata, and queue-visible child rows. + +Child snapshot semantics are explicit: + +- the saved workflow is resolved and converted to a child `Graph` when the call is created +- the child queue row persists that graph snapshot and its inputs; resume/recovery uses that snapshot rather than + resolving the saved workflow again +- an active parent retains the child session in memory while it is waiting, but its persisted snapshot keeps only + compact lifecycle metadata because the child queue row is authoritative +- queue pruning is the boundary that removes the child row's postmortem snapshot; retry creates a new execution and + therefore resolves the saved workflow again under the retrying user's authorization Current limitation: -- the temporary `workflow_graph_builder.py` path still reconstructs only the ordinary invocation subset of child - workflows +- the `workflow_graph_builder.py` path currently reconstructs only the ordinary invocation subset of child workflows - direct batch-special child workflows now bypass that path and use queue batch expansion instead - generator-backed batch child workflows now bypass that path too when the batch is fed directly by a supported generator node -- connected batch child inputs produced by ordinary non-generator upstream nodes are still not supported and should fail - early with a clear unsupported-feature error -- the current queue-visible child execution path still relies on `WorkflowCallCoordinator` to resume or fail parents - directly rather than a more general queue scheduler abstraction -- the current implementation is still an intermediate architecture step, but it is now materially closer to the intended - durable parent/child model than the earlier inline-runner path +- connected batch child inputs produced by ordinary non-generator upstream nodes are still not supported and fail early + with a clear unsupported-feature error +- the current queue-visible child execution path still relies on `WorkflowCallCoordinator` and + `WorkflowCallQueueLifecycle` for durable row creation and parent transitions; generic lifecycle effects and + `ChildDependencyRecord` supply the invocation intent, identity, ordered aggregation, idempotency, and terminal + decision without changing the public queue contract ### 4a. Queue Lifecycle Contract -The current queue-visible implementation uses the following lifecycle contract: +The current queue-visible implementation uses the following lifecycle contract. The generic child record is an internal +validation and aggregation seam; it does not add queue columns, statuses, events, or frontend handles. - root or parent queue items may enter `waiting` while suspended on a child workflow call - child workflow executions are represented as real queue rows with explicit parent/child relationship metadata @@ -312,8 +355,8 @@ The current queue-visible implementation uses the following lifecycle contract: must receive only the retry item ids for their own roots, while admins can still observe the full retried set - workflow live-update sockets join workflow event rooms in both authenticated multiuser mode and unauthenticated single-user mode; the frontend relies on those events to invalidate workflow library data and clear deleted saved - workflow selections; in single-user mode, workflow CRUD events emit only to the admin room to avoid duplicate delivery - to sockets that are also joined to `user:system` + workflow selections; in single-user mode, workflow CRUD events emit only to the admin room to avoid duplicate + delivery to sockets that are also joined to `user:system` - a public-to-private transition emits a schema-defined `workflow_access_revoked` event to shared-workflow subscribers; non-owner, non-admin clients clear references to that workflow while owners and admins retain access - the saved-workflow node picker queries owned/default workflows and public shared workflows separately, merges them by @@ -325,8 +368,8 @@ The current queue-visible implementation uses the following lifecycle contract: - workflow-call child enqueue events use the same owner-aware redaction as ordinary status transitions, even though they do not pass through `_set_queue_item_status` -This is now part of the intended user-facing contract, even though the orchestration still lives in -`WorkflowCallCoordinator`. +`WorkflowCallCoordinator` owns child setup and enqueueing. `WorkflowCallQueueLifecycle` owns parent resume and terminal +propagation, using the session queue service for durable row transitions. ### 4b. Batch Child Workflows @@ -354,7 +397,8 @@ Current semantics: - the workflow call creates one child queue row per expanded batch session - supported generator value shapes are resolved into concrete batch items before queue batch expansion - declared generator counts are rejected before resolution when they exceed remaining child capacity -- cartesian expansion size is computed arithmetically before session generation rather than by materializing the product +- cartesian expansion size is computed arithmetically before session generation rather than by materializing the + product - batch outputs may feed a named `workflow_return_value.value` directly; each expanded child returns one value for that key - parent resume waits for all child rows tied to that workflow call @@ -459,8 +503,8 @@ Retry behavior: - retry is root-oriented - child queue rows should not be directly retried from the UI -- backend retry of a child id should normalize to the root workflow call chain rather than create an isolated child-only - rerun +- backend retry of a child id should normalize to the root workflow call chain rather than create an isolated + child-only rerun ### 5. Return Values @@ -495,8 +539,8 @@ Batch return aggregation: - when a called workflow expands into multiple child queue rows, each child row produces its own named return map - the parent aggregates those child maps as `dict[str, list[Any]]` -- each key maps to child values in child enqueue order, preserving positional correspondence with batch inputs even when - child executions complete out of order +- each key maps to child values in child enqueue order, preserving positional correspondence with batch inputs even + when child executions complete out of order - duplicate keys within a single child return map are still invalid; repeated keys across batch children are the normal aggregation path @@ -505,9 +549,9 @@ Batch return aggregation: If child execution fails: - the call node fails -- the parent workflow fails unless a later design adds explicit error-handling semantics +- the parent workflow fails -For the first implementation, failure propagation should be simple and strict. +Failure propagation is simple and strict. ### 7. Access Control @@ -519,9 +563,9 @@ This matters even if the parent workflow was authored in a context where the chi ### 8. Recursion And Nesting -Nested and recursive `call_saved_workflow` execution should be allowed, but bounded. +Nested and recursive `call_saved_workflow` execution is allowed, but bounded. -Initial implementation should enforce: +The runtime enforces: - nested workflow calls are allowed - recursive workflow calls are allowed @@ -531,16 +575,15 @@ Initial implementation should enforce: This allows legitimate recursive or conditionally terminating workflow structures while still preventing unbounded call growth. -## Where The Runtime Work Belongs +## Runtime Ownership -The goal is to support externally submitted graphs, not only frontend-authored graphs. Therefore the authoritative -execution logic must live in Python. +Externally submitted graphs and frontend-authored graphs use the same Python execution path. The backend owns the +authoritative execution logic. -Recommended high-level design: +The runtime design is: - a backend `GraphExpander` or broader graph-preparation service may still exist as an abstraction point -- but for this feature, the preferred long-term runtime model is not full graph expansion -- instead, the runtime needs a call-execution mechanism in the Python execution stack +- the runtime uses a call-execution mechanism in the Python execution stack rather than full graph expansion Relevant existing path: @@ -551,16 +594,17 @@ Relevant existing path: Current insertion points already used: -- `DefaultSessionRunner.run_node()` detects `call_saved_workflow` and enters boundary state +- `DefaultSessionRunner.run_node()` supplies the call capability and inputs, invokes the node through the effect-aware + path, persists its pending lifecycle effects through `GraphExecutionState.apply()`, then dispatches the queue adapter - `GraphExecutionState` stores the waiting/call-stack state and attached child session -- `WorkflowCallCoordinator` currently establishes the call boundary and enqueues child workflow executions as real queue - rows +- `WorkflowCallCoordinator` currently establishes the call boundary and enqueues child workflow executions as real + queue rows - `WorkflowCallQueueLifecycle` currently resumes or fails parents when those child rows complete - child queue items already carry stable parent/child identifiers in both runtime objects and durable queue columns -Next runtime work still needed: +Queue lifecycle boundary: -- keep `WorkflowCallQueueLifecycle` as the bounded workflow-call lifecycle component for this PR +- `WorkflowCallQueueLifecycle` remains the bounded workflow-call lifecycle component - the current workflow-call feature is the only caller of parent/child queue semantics - replacing it with a generalized queue dependency scheduler now would add regression risk without unlocking a concrete user workflow @@ -595,7 +639,7 @@ The dedicated child-workflow return nodes are implemented. Responsibilities: - guarantee that only one such node exists per workflow - behave as a normal node in the editor, with singularity enforced by both frontend and Python validation/runtime code -This should remain the canonical reusable return mechanism for any future subworkflow call behavior. +This is the canonical reusable return mechanism for subworkflow calls. ### Execution Relationship Tracking @@ -608,8 +652,7 @@ Session/runtime state records: ### Workflow Return Value Flow -The workflow return value should not be persisted back into the saved workflow record and should not be derived from -frontend state. +The workflow return value is not persisted back into the saved workflow record and is not derived from frontend state. The intended runtime flow is: @@ -623,17 +666,13 @@ The intended runtime flow is: 1. The parent `call_saved_workflow` node is completed with that returned named value map. 1. The parent graph resumes. -## Named Return Implementation Status - -Named returns are implemented for backend invocation behavior, caller-side extraction, runtime propagation, and batch -aggregation. Remaining work is limited to incremental frontend UX cleanup and any future expansion of supported batch -shapes. +## Named Return Contract -### Stage 1: Backend Return Contract +Named returns cover backend invocation behavior, caller-side extraction, runtime propagation, and batch aggregation. -Status: implemented in backend invocation tests. +### Backend Return Contract -Goal: +Current behavior: - establish the named return data model and invocation primitives @@ -641,22 +680,21 @@ Contract: - `WorkflowReturnValueField` stores one `key: str` and one `value: Any` - `workflow_return_value` creates a single `WorkflowReturnValueField` from a key and connected value -- `workflow_return` accepts either one `WorkflowReturnValueField` member or a list of `WorkflowReturnValueField` members +- `workflow_return` accepts either one `WorkflowReturnValueField` member or a list of `WorkflowReturnValueField` + members - `WorkflowReturnOutput` exposes `values: dict[str, Any]` - duplicate keys in one non-batch `workflow_return` execution are invalid and must fail clearly -Tests first: +Validation: - `workflow_return_value` emits the requested key/value pair - `workflow_return` emits a named value map from one or more return members - duplicate keys in one `workflow_return` execution are rejected - empty returns are valid only if that remains an intentional callable-workflow contract -### Stage 2: Caller-Side Extraction Primitive +### Caller-Side Extraction Primitive -Status: implemented in backend invocation tests. - -Goal: +Current behavior: - let the calling workflow extract a named return value without relying on collection position @@ -664,19 +702,17 @@ Contract: - `workflow_return_get` accepts the named return map and a key - `workflow_return_get` outputs the selected value as `Any` -- missing keys fail clearly unless a later version intentionally adds default-value support +- missing keys fail clearly -Tests first: +Validation: - extracting an existing key returns the stored value - extracting a missing key fails with a useful message - extracted `Any` values can feed typed downstream nodes through the existing connection compatibility rules -### Stage 3: Runtime Propagation - -Status: implemented in backend runtime tests. +### Runtime Propagation -Goal: +Current behavior: - carry named return maps through queue-visible child execution and parent resume @@ -687,17 +723,15 @@ Contract: - failed child execution behavior is unchanged - cancel/retry lifecycle behavior is unchanged -Tests first: +Validation: - a called workflow returning `{image: image_value}` completes the parent `call_saved_workflow` output with that key - a caller-side extraction node can consume that output after parent resume - missing or invalid `workflow_return` nodes still fail with the existing clear errors -### Stage 4: Batch Return Aggregation +### Batch Return Aggregation -Status: implemented in backend runtime tests. - -Goal: +Current behavior: - define named returns for child workflows that expand into multiple queue rows @@ -712,19 +746,18 @@ Contract: - if a non-batch workflow wants multiple images under one key, it must collect those images into a single list value before returning that key -Tests first: +Validation: - a batched child returning `{image: image_value}` from each child row produces `{image: [image_1, image_2, ...]}` - sibling failure still cancels remaining siblings and fails the parent - duplicate keys inside one child row are rejected rather than silently aggregated -### Stage 5: Frontend Schema, UI, And Docs +### Frontend Schema, UI, And Docs -Status: mostly implemented. Schema/type generation includes the backend nodes and fields; editor connection coverage and -localized UI strings are in place for the current node wiring. Future UX cleanup should be driven by concrete user -testing rather than added as speculative work. +Existing schema/type generation includes the backend nodes and fields; editor connection coverage and localized UI +strings are in place for the current node wiring. -Goal: +Current behavior: - make named returns usable and visible in the editor @@ -735,14 +768,14 @@ Contract: - `call_saved_workflow` exposes the named return map output - users can wire that output to `workflow_return_get` -Tests first: +Validation: - frontend connection/type tests cover return-value collection wiring - frontend connection/type tests cover wiring one `workflow_return_value.value` directly to `workflow_return.values` - frontend connection/type tests cover `call_saved_workflow.values -> workflow_return_get.values` - docs describe how a called workflow creates named returns and how a caller extracts them -## Frontend Responsibilities In The Long-Term Design +## Frontend Responsibilities The frontend remains responsible for editor-time behavior: @@ -756,15 +789,9 @@ The frontend remains responsible for editor-time behavior: inputs during backend compatibility evaluation, so workflows that are valid once the caller supplies exposed values are not disabled prematurely -Potential future optimization: - -- add a backend endpoint that returns a normalized callable workflow interface -- this would let the frontend avoid re-parsing full saved workflow payloads to redraw the node -- it would also give the frontend a backend-authoritative interface hash for drift detection +## Validation Coverage -## Tests Needed Going Forward - -Already covered: +Coverage includes: - workflow-call stack and waiting state on `GraphExecutionState` - depth-limit enforcement @@ -779,35 +806,24 @@ Already covered: - literal and connected dynamic call arguments are applied to the child graph at runtime - non-exposed dynamic call arguments are rejected at runtime - child `workflow_return` output is captured and becomes the parent `call_saved_workflow` output -- named `workflow_return` values can be constructed, propagated to the parent, extracted by key, and batch-aggregated as - `dict[str, list[Any]]` +- named `workflow_return` values can be constructed, propagated to the parent, extracted by key, and batch-aggregated + as `dict[str, list[Any]]` - child workflows without a `workflow_return` node fail cleanly when called - child execution events now include stable workflow-call relationship metadata on the child `SessionQueueItem` - parent-child resume and failure propagation through queue-visible child rows - nested runtime execution with bounded stack depth - direct and generator-backed batch-special child workflows through queue child-row expansion -- compatibility metadata for required exposed inputs, missing/multiple returns, supported named batch-return shapes, and - unsupported batch input wiring +- compatibility metadata for required exposed inputs, missing/multiple returns, supported named batch-return shapes, + and unsupported batch input wiring -Still needed in later increments: +Supported extension policy: - focused coverage for any newly supported batch or generator shape when its contract changes -- possible migration from dedicated workflow-call queue lifecycle handling to a more general scheduler or - queue-lifecycle model only if another feature needs reusable dependent queue items - -## Recommended Immediate Next Step - -The next incremental step should be: - -- stop adding feature slices unless they close a concrete correctness gap or unlock a realistic user workflow -- stabilize the current branch with review, targeted test runs, and cleanup of stale design-doc language -- treat migration from `WorkflowCallQueueLifecycle` to a generalized parent/child queue lifecycle as a larger - architecture slice, not as small follow-on busywork +- a more general scheduler or queue-lifecycle model is warranted only if another feature needs reusable dependent queue + items; the current queue lifecycle remains authoritative -The current branch is at the point where: +## Operational Guidance -- parent call-boundary state exists -- child execution state can be created from the selected saved workflow -- child execution, argument forwarding, explicit return propagation, suspended parent status, queue-visible child rows, - and upward failure cascade work through the current coordinator + queue path -- but long-term generalized parent/child scheduling semantics are still missing +The queue path owns durable child rows, parent waiting/resume transitions, cancellation, retry, recovery, and events. +The generic child record validates identity, ordered aggregation, resource limits, and idempotent terminal events; it +does not replace the queue lifecycle. diff --git a/docs/src/content/docs/contributing/external-providers.md b/docs/src/content/docs/contributing/external-providers.md index 13dfd2df5b3..ab62fb886e4 100644 --- a/docs/src/content/docs/contributing/external-providers.md +++ b/docs/src/content/docs/contributing/external-providers.md @@ -118,7 +118,7 @@ If your model uses a provider that is not already integrated: `ExternalGenerationService`. 5. Add starter model entries using `source="external:///"`. 6. Optional UI ordering tweak: - `invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx` + `invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx` (`PROVIDER_SORT_ORDER`). ## 4) Optional Manual Installation diff --git a/docs/src/content/docs/contributing/loop-nodes.md b/docs/src/content/docs/contributing/loop-nodes.md new file mode 100644 index 00000000000..90a5717fc3c --- /dev/null +++ b/docs/src/content/docs/contributing/loop-nodes.md @@ -0,0 +1,278 @@ +--- +title: Loop Nodes Architecture +--- + +This page records the implementation contract for the collection-based `For` and `ForReturn` nodes. The durable +contract is shared by the backend graph executor, saved workflow format, and workflow editor. + +## Current execution-engine shape + +The author-time graph contract is unchanged. `GraphExecutionState` remains the execution façade: it owns graph +validation, prepared execution-node identity, frame and iteration-path metadata, persistence, and the boundary between +invocation results and scheduling. + +Readiness is split by admitted topology: + +- `ExecutionPlan` and `ExecutionScheduler` provide opaque, deterministic node readiness and completion. They do not + choose successors by node type or accept literal successor-node IDs. +- `_GenericGraphSchedulerAdapter` projects existing graph state into that scheduler for admitted fresh graphs and for + the ordinary static-DAG/legacy compatibility path. +- `_GenericForPlanner`, the bounded `Iterate` planners, and the frame-scoped `If` dependency code supply the + control-flow-specific preparation and activation rules for their exact admitted shapes. +- `_ExecutionMaterializer` remains the compatibility owner for unsupported topologies and legacy snapshots. +- `CallSavedWorkflowInvocation` declares one capability-bound `spawn_execution` plus matching `await` effect (or a + capability-bound `fail`). `GraphExecutionState` persists the pending lifecycle effects and generic child dependency; + `WorkflowCallCoordinator` and `WorkflowCallQueueLifecycle` remain the durable owner of saved-workflow child queue + rows, statuses, events, authorization, capacity, cancellation, recovery, retry, and parent/child projection. + +Control-flow nodes remain concrete `BaseInvocation` subclasses. They do not inherit from a new control-flow engine +hierarchy, and they do not return a literal next-node ID. Internal planners and records may share implementation +helpers, while successor readiness is selected from frame-scoped dependencies and effects. + +Ownership is topology-dependent: admitted fresh `If` branches require matching activation tokens; bounded planners own +admitted loop expansion and continuation. Compatibility adapters own the remaining shapes. Execution ledgers are +internal persistence data, excluded from ordinary model serialization and public schemas. Version-2 +`dump_execution_state()` rebuilds references and ordinary output tokens, retains activation tokens and effects needed +for resume, and omits terminal lifecycle records and completed child dependencies. They add no author-time ports or +frontend/backend external fields. + +## Core contract + +`For` is a bounded collection loop, not a general `While` node. Its source is one `collection: list[Any]` input. Each +iteration exposes `item`, `index`, `total`, and `state`; the final execution surface exposes `output_collection` and +`final_state`. `ForReturn` closes one iteration and may provide an output item, updated state, and a +`continue_condition`. + +Loop state is explicit `LoopState` graph data. It is copied and serialized with normal invocation inputs and results; +it is not stored in transient process-local context. If a return omits state, the previous state carries forward. A +missing or `None` continue condition continues; `False` finalizes the current loop after recording its output and +state. + +The loop is sequential. A body failure or cancellation stops later iterations and does not release partial final-scoped +outputs. An empty collection is successful: no body node runs, `output_collection` is empty, and `final_state` is the +provided initial state or an empty state. + +## Durable loop linkage + +Every `For` and `ForReturn` pair has one canonical runtime `loop_linkage` edge: + +```text +For.loop_linkage - - - - - - - - - - - - - - - - - > ForReturn.loop_linkage +For.item -> body path -> ForReturn.output +``` + +This edge is an association, not executable data flow. It is excluded from ordinary input propagation, cycle detection, +and scheduling. The backend requires exactly one outgoing linkage for every `For`, exactly one incoming linkage for +every `ForReturn`, and the exact `For.loop_linkage` to `ForReturn.loop_linkage` endpoints. Default edges using the +reserved `loop_linkage` field are invalid. + +Authoring workflow JSON may represent the association through a one-to-one connector alias: + +```text +For.loop_linkage -> connector.in -> connector.out -> ForReturn.loop_linkage +``` + +Every connector on that path must have exactly one input and one output. The path cannot branch, be reused as ordinary +data flow, or terminate at a different node. Graph construction canonicalizes a complete valid alias to one direct +runtime `loop_linkage` edge. No loop identity or body metadata is inferred from arbitrary topology or migrated from +unrelated fields. + +## Body and output scopes + +Iteration-scoped outputs (`item`, `index`, `total`, and `state`) define the loop body. Final-scoped outputs +(`output_collection` and `final_state`) are available only after the matching loop context completes. Body nodes must +terminate at the linked `ForReturn`; they cannot escape directly to after-loop nodes. Final outputs cannot feed back +into the loop body. + +Downstream consumers, including a selected `If` branch, may consume `output_collection` directly. Its parent-frame scope +is preserved through branch selection, so a `final_state` edge is not needed just to make those consumers run. + +`ForReturn.output` and `ForReturn.state` are scheduler-facing result fields and are hidden as downstream editor +outputs. They are still retained in execution results for aggregation, persistence, and resume. Ordinary state helper +nodes (`state_empty`, `state_get`, `state_set`, and `state_merge`) carry explicit `LoopState` values through the body. + +## Supported nested shapes + +Nested `For` boundaries are supported recursively when each inner boundary has its own direct linkage and matching +`ForReturn`. The inner final collection may feed the parent return directly or through an ordinary parent-scoped +continuation. Independent inner loops must all feed one explicit fan-in continuation; collection concatenation, +zipping, or Cartesian semantics come from the connected collection operation, not from loop scheduling. + +A bounded single internal `Iterate` is supported for the canonical outer-`For` shape: one ordinary preparation node +converts `For.item` to the inner collection, one ordinary body node consumes `Iterate.item`, and one `Collect` +collapses that item dimension before the parent `ForReturn`: + +```text +For.item -> preparation -> Iterate.collection +Iterate.item -> body -> Collect.item +Collect.collection -> ForReturn.output +``` + +The fresh generic scheduler admits this exact shape when the outer collection is a non-empty literal or one of the +supported input-driven outer-collection variants, and there is one final consumer of `For.output_collection`. Each +outer frame is isolated, including when the preparation node produces an empty inner collection. A checkpoint after a +nested iterator boundary restores the generic class-drain state and continues the same frame/stream order. Valid +topologies outside these bounds retain compatibility ownership; escaped body paths fail graph validation. + +The exact serial two-`Iterate` body is also supported: + +```text +For.item -> preparation1 -> Iterate1.collection +Iterate1.item -> preparation2 -> Iterate2.collection +Iterate2.item -> body -> Collect.item +Collect.collection -> ForReturn.output +``` + +Generic admission requires a non-empty literal outer collection, those two ordinary preparation nodes, one ordinary +body node, and one ordinary `For.output_collection` consumer: nine nodes and nine edges including linkage. This does +not admit sibling iterators, fan-in, or additional control-flow nodes. + +Unsupported shapes, including independent iterator-derived body inputs, mixed nested `For`/`Iterate` bodies, escaping +body paths, ambiguous returns, and arbitrary cyclic graphs, are rejected before execution. + +## Generic `For` routing matrix + +`_GenericForPlanner` owns these fresh shapes. `_ExecutionMaterializer` remains the compatibility facade for every other +shape and for explicitly forced compatibility runs: + +- One flat `For`/`ForReturn`: literal empty or non-empty collection, supported non-empty input producer, or the exact + four-node/four-edge empty input producer with no downstream consumer. +- Canonical two-level nested `For` with a non-empty literal outer collection or supported non-empty input producer; + empty input-driven outer results remain compatibility-owned. +- Exact two-sibling nested `For` fan-in through `CollectionConcat`, with a non-empty literal outer collection. +- Exact serial three-level nested `For`, including its statically non-empty `CollectionConcat` producer variant. +- Exact serial four-level nested `For`. +- Exact bounded outer `For`/`Iterate`/`Collect`. +- Exact serial two-`Iterate` nested outer-`For` shape with one ordinary final consumer. + +Valid shapes outside this admission matrix retain compatibility ownership, including legacy snapshots, unsupported +input producers, extra nodes or consumers, fan-out, nested `For` depth five or greater, unsupported sibling or deeper +shapes, and saved-workflow or `If`-containing `For` graphs. Malformed linkage, invalid output-scope edges, and +unsupported mixed bodies fail graph validation; a compatibility route does not make them executable. No invocation +emits or requires a literal successor node ID. This matrix changes no invocation, API, saved-workflow, frontend, or +generated-schema contract. + +## Persistence and validation + +Prepared execution nodes, source/prepared mappings, iteration paths, results, indegrees, and finalized loop contexts +are persisted through `GraphExecutionState`. Runtime-only queues and metadata are rebuilt when state is rehydrated. +Finalization is keyed by the loop source and its parent iteration path so nested or repeated contexts cannot mix output +collections or state. + +The execution-engine seam is additive to this loop contract. The session runner applies each result through +`GraphExecutionState.apply()` using a stable execution reference and records frame-aware output tokens; accepted +`For`/`ForReturn` continuation state is mirrored by a typed, frame-scoped `ContinuationRecord`. `ForInvocation` and +`ForReturnInvocation` also declare one validated, frame-scoped `continuation` effect per prepared invocation. The `For` +effect starts the `for` continuation with the current iteration and state; the `ForReturn` effect completes it with +output, state, and the continue decision. These effects are persisted under the invocation reference. Session-built +effect references carry graph-state, durable-frame, iteration-path, and workflow-call-depth identity, and stale or +cross-scope effects are rejected before mutation. They never encode `loop_linkage` as a data token. A fresh graph with +exactly one static flat `For`/`ForReturn` pair, including an empty literal collection, a supported non-empty +input-driven collection producer, or the exact bounded empty input-driven producer shape, now uses the generic +scheduler adapter for readiness and continuation transitions. Graph state owns the generic continuation boundary. +`_GenericForPlanner` owns admitted For-specific preparation, materializes the next body iteration, preserves frame +paths and carried state, and finalizes nested completion. It shares only low-level execution-node construction +mechanics with the compatibility materializer. An empty literal collection completes through the existing synthetic +terminal `For` result without running the body or `ForReturn`. The generic scheduler remains opaque and never receives +a literal successor node ID. The compatibility continuation bridge is retained only for unsupported loop shapes and +explicitly legacy-loaded snapshots. Empty input-driven shapes outside the exact four-node/four-edge bounded topology, +unsupported input-driven outer collections, five-level-or-deeper or unsupported sibling nested loops, and unsupported +mixed control flow remain on the compatibility scheduler; the exact fresh two-sibling `For`/`ForReturn` +`CollectionConcat` fan-in shape, the narrow canonical two-level, and exact fresh three-level and four-level serial +nested-`For` shapes (one literal outer collection, or the exact statically non-empty `CollectionConcat` producer for +the three-level case, one direct child at each level, no continuation nodes) and the exact bounded +outer-`For`/`Iterate`/`Collect` shape are now generic-routed. This additive effect seam does not claim generic +scheduling for the remaining shapes. `Iterate` records ordered item tokens in a closed `StreamBuffer`; an empty +`Iterate` records an explicit empty close. A direct `Iterate.item` consumer waits for the canonical stream to close, +then `Collect` consumes its ordered values; a missing stream falls back to materialized results for legacy snapshots. +The exact fresh four-node `literal collection source -> Iterate -> one ordinary body -> Collect` shape and any number +of ordinary downstream consumers from `Collect.collection` use a private graph-state planner for prepared-copy +expansion, iteration paths, and the empty-stream barrier. The exact fresh +two-source/two-`Iterate`/shared-`Collect.item` and exact fresh three-source/three-`Iterate`/shared-`Collect.item` +fan-in shapes also use the planner for independent stream expansion, closure gating, and deterministic source-ordered +hydration. The exact fresh two-branch body-mediated shape, `source_a -> Iterate_a -> body_a -> Collect.item` plus +`source_b -> Iterate_b -> body_b -> Collect.item`, is also planner-owned: source IDs are ordered lexically, branch +order is preserved, and `Collect` waits for both streams to close, including empty streams. This is a private bounded +planner case with exactly seven nodes and six ordinary edges: two inputless ordinary sources, two `Iterate` nodes, two +ordinary bodies, and one `Collect`. It has no `Collect.collection` input, downstream consumer, or extra topology. Three +or more body-mediated branches, fan-in with four or more direct branches, unsupported nested or input-driven iterator +topologies, and mixed control flow outside the bounded per-item `Iterate`/`If`/`Collect` topology remain on the +compatibility materializer. The exact admitted serial nested-`Iterate` chains and producer-driven bounded +`For`/`Iterate`/`Collect` variants use the generic planner described above. Direct `Iterate`/`Collect`-only graphs and +the exact bounded nested shape use the generic scheduler adapter: its readiness predicate waits for the canonical +stream to close, and completion mirrors Iterate outputs into that ledger before releasing Collect. Rehydration restores +the active class-drain boundary so nested stream order is preserved across dump/load. For the exact fresh direct shape, +the planner owns expansion, downstream admission, and empty closure atomically: a failed expansion leaves no partial +prepared copies to be resumed. Versioned queue checkpoints and retries preserve the existing durable execution-state +boundary and receive fresh execution identities. Materialization still owns those responsibilities for fallback shapes. +Mixed control-flow outside the exact bounded admitted topology and queue lifecycle remain compatibility-owned. +`loop_linkage` remains association metadata and never becomes a data token. + +The supported flat-loop behavior preserves generic/compatibility parity for successful execution, carried and replaced +state, early break, empty and input-driven fallback routing, `None` output items, body/return failure, partial +rehydration, frame identity, and SQLite cancellation/retry isolation. Continuation effects are normalized to JSON +values and validated before mutation: exact duplicate effects in one batch are retained once, while conflicting +terminal payloads (including type-distinct JSON values) are rejected. Before scheduler completion, +`ForInvocationOutput` and `ForReturnInvocationOutput` continuation fields must match their prepared nodes; the `For` +item must match its prepared collection item, and effects must independently match those values. Current versioned +snapshots must include an execution-effect ledger: every ledger key must name an executed prepared node, and pending +nodes, unknown references, missing executed markers, missing continuation effects, and malformed ownership are +rejected. Persisted `For` outputs are checked against prepared iteration data and, after finalization, the +authoritative returned collection and final state. Unversioned legacy snapshots retain their compatibility loader. A +`For` start effect must match the prepared index, collection total, and state; malformed effects leave the graph state +unchanged. Unsupported shapes retain the compatibility continuation/materialization owner. + +`GraphExecutionState.complete()` remains the compatibility entry point for callers that already have a node result. Its +first JSON-safe completion uses the same validated, atomic ledger path as `apply()`, including output tokens and +synthetic `For`/`ForReturn` continuation effects. It still permits historical idempotent result replacement for an +already-applied node; arbitrary in-memory values that cannot be represented in the JSON ledger remain a scheduler-only +compatibility case and cannot be persisted. When an explicitly unversioned, partially completed legacy snapshot is +loaded, missing flat-loop continuation buckets are synthesized before a versioned re-save so the next load remains +valid. A loaded terminal legacy state may retain its empty in-memory ledger for compatibility, but +`dump_execution_state()` upgrades missing loop buckets in the versioned snapshot. + +`IfInvocation` now declares the same seam for branch activation: it emits one frame-scoped activation token for the +selected branch, and the graph state validates and persists it after invocation. Fresh graphs with one ordinary-node +`If`, the exact one-level nested shape with two `If` nodes where the inner value feeds one outer branch and has no +other consumer, the exact bounded three-`If` chain with direct inner-to-middle-to-outer branch edges, the exact +four-`If` nested chain, or exactly two, exactly three, or exactly four independent ordinary-node sibling `If`s compile +opaque, frame-local activation dependencies in graph state. The three- and four-`If` admissions require `default` +edges, all branch inputs (`condition`, `true_input`, `false_input`) on each `If`. The three-`If` chain permits one +extra `middle If.value` edge to an ordinary leaf consumer with no outputs; that leaf inherits only the outer branch +dependency. Other inner/middle outputs must be direct nested branch edges, and the four-`If` chain permits no extra +output fan-out. Five-or-more nested `If`s, other fan-out, mixed shapes outside the bounded per-item +`Iterate`/`If`/`Collect` topology, loop-containing `For`/`ForReturn`, saved-workflow, legacy, and five-or-more sibling +shapes use the compatibility scheduler with the dedicated `_IfActivationController` fallback. Legacy snapshots retain +their generic compatibility projection. Legacy skipped-state projection remains only for old snapshots. Fresh +materialization prepares the condition boundary, resolves the activation token, and attaches only the selected branch +input; unselected branch nodes remain unprepared. The generic and compatibility adapters consume the same decisions and +append-only execution edges; no type-specific branch scheduler prunes edges or propagates skips. Legacy skipped-state +metadata remains only for snapshots that already contain the old projection. `apply()` replaces the activation token by +stable identity, and activation effects are excluded from stream handling. Graph state derives branch membership for +the supported fresh shape; the fallback controller retains it for unsupported and legacy shapes. Author-time activation +ports and literal successor IDs remain absent, while unsupported loop shapes and saved-workflow control flow remain on +their compatibility paths until differential coverage proves their generic replacements. + +The engine keeps compatibility materialization and queue readiness for loop-containing graphs outside the routing +matrix and for existing snapshots. Admitted fresh `For` graphs use the generic opaque plan/scheduler plus +`_GenericForPlanner`; `_ExecutionNodeBuilder` supplies shared low-level graph mutation mechanics. Ordinary static DAGs, +supported fresh `If` graphs, direct `Iterate`/`Collect`-only graphs, and the exact bounded mixed +`Iterate`/`If`/`Collect` graph use the generic adapter; unsupported or legacy shapes retain their compatibility routes. +No activation, stream, or continuation ports are added to author-time graph JSON. Exact fresh serial nested-`Iterate` +chains through eight levels use the generic planner for ordered frame expansion, empty-stream closure, checkpoint +rehydration, source completion, and failure parity. Nine-level or deeper chains, five-level-or-deeper or unsupported +sibling nested loops, and unsupported mixed shapes remain compatibility-owned. The exact fresh two-sibling +`For`/`ForReturn` `CollectionConcat` fan-in shape, the exact fresh three-level serial nested-`For` shape with its +bounded static `CollectionConcat` producer, and the exact fresh four-level serial nested-`For` shape are +generic-routed. The execution engine does not modify any file under `invokeai/frontend/...`, including generated +schemas; the existing frontend/backend external interface remains stable. Branch-membership analysis remains internal, +while fresh execution no longer creates skipped-state projection and old snapshots retain it for compatibility. + +The frontend and backend validate the same boundary rules. Saved workflows preserve node types, field handles, and the +direct linkage edge. The current invocation templates provide output-scope metadata when a workflow is loaded. The +editor's boundary overlay and contextual `ForReturn` picker are presentation aids; they do not replace whole-graph +validation. + +Collection helpers are ordinary explicit nodes: `CollectionConcat` preserves left-to-right order and accepts unequal +lengths, `CollectionZip` requires equal lengths, and `CollectionCartesian` produces deterministic +left-major/right-minor pairs with a 100,000-pair limit. They do not add implicit loop dimensions. diff --git a/docs/src/content/docs/contributing/new-model-integration.mdx b/docs/src/content/docs/contributing/new-model-integration.mdx index c20c933bc83..44811bfc1b2 100644 --- a/docs/src/content/docs/contributing/new-model-integration.mdx +++ b/docs/src/content/docs/contributing/new-model-integration.mdx @@ -1,1234 +1,970 @@ --- title: New Model Type Integration Checklist -description: A step-by-step checklist for integrating a new foundational model type into InvokeAI, from backend model manager to frontend UI. -lastUpdated: 2026-06-26 +description: A step-by-step checklist for integrating a new model architecture into InvokeAI, from the model manager to the webv2 frontend and the tests that enforce completeness. +lastUpdated: 2026-09-25 --- import { Steps, FileTree } from '@astrojs/starlight/components'; -This guide describes all the steps required to integrate a new model type into InvokeAI, from the backend model manager up to the React frontend. +This guide covers every step needed to add a new model architecture to InvokeAI: + +- the model manager, which identifies and loads the files +- the architecture declaration +- the invocations that form the generation graph +- the webv2 frontend +- the tests and CI gates that enforce completeness :::note -The code examples use a hypothetical `NewModel` architecture. The implementations of FLUX.1, FLUX.2 Klein, SD3, SDXL, and Z-Image in the InvokeAI codebase serve as excellent real-world references. +The examples use a hypothetical architecture, `NewModel`, with the base value `new-model`. These real implementations follow the current patterns end to end: + +- **Krea-2**: a single-stream DiT with a Qwen3-VL encoder, installable as Diffusers, single-file or GGUF. +- **Z-Image**: variants with different defaults, LoRA and control. +- **Qwen-Image**: reference images and an edit variant. ::: ---- +:::caution[Frontend scope] +Frontend work targets **webv2** (`invokeai/frontend/webv2`) only. The legacy UI in `invokeai/frontend/webv1` gets no graph builders or UI for new architectures. Shared generated OpenAPI artifacts live in `invokeai/frontend/api`, which CI checks; see [Tests and CI gates](#13-tests-and-ci-gates). +::: -## 1. Backend: Model Manager +:::tip[Start with the scaffolder] +Add the `BaseModelType` member first (step 2.1), then run: - -1. **Add `BaseModelType`** +```sh +python scripts/new_architecture.py --base new-model --name NewModel # dry run +python scripts/new_architecture.py --base new-model --name NewModel --write +``` - Declare your new model in the base model taxonomy. +It writes three files and refuses to overwrite existing ones: - ```python title="invokeai/backend/model_manager/taxonomy.py" ins={10} - class BaseModelType(str, Enum): - # Existing types - StableDiffusion1 = "sd-1" - StableDiffusion2 = "sd-2" - StableDiffusionXL = "sdxl" - Flux = "flux" - Flux2 = "flux2" # FLUX.2 Klein - SD3 = "sd-3" - ZImage = "z-image" - NewModel = "newmodel" # NEW - ``` +- the architecture declaration, `architectures/defs/new_model.py` +- the invocation package, `app/invocations/new_model/__init__.py` +- the starter-model module, `starter_models/new_model.py` -2. **Add Variant Type (if needed)** +It then prints two lists: - If your model comes in different structural variants (e.g., different parameter counts or distilled versions), define a variant enum. +- **Three edits it cannot scaffold:** `taxonomy.py`, `conditioning_data.py` and `metadata.py`. +- **Every backend module that names five or more `BaseModelType` members.** These modules dispatch on base, so each one needs checking. The list is derived on each run. It has false positives, such as the `UIType` migration code in `fields.py`. It cannot see the hand-maintained lists this guide covers, and it lists no frontend work. - ```python title="invokeai/backend/model_manager/taxonomy.py" - # Examples of existing variants: - class FluxVariantType(str, Enum): - Schnell = "schnell" - Dev = "dev" - DevFill = "dev_fill" - - class Flux2VariantType(str, Enum): - Klein4B = "klein_4b" # Qwen3 4B encoder - Klein9B = "klein_9b" # Qwen3 8B distilled - Klein9BBase = "klein_9b_base" - - # NEW (if needed): - class NewModelVariantType(str, Enum): - VariantA = "variant_a" - VariantB = "variant_b" - ``` +The generated declaration contains every required facet, but only one placeholder stops the app from booting: an all-zero latent→RGB projection, which `LatentSpace` rejects when the module is imported. The other placeholders are valid values and **boot without complaint**: -3. **Define Default Settings** - - Provide default generation parameters for the UI to use when this model is selected. - - ```python title="invokeai/backend/model_manager/configs/main.py" ins={9-10} - class MainModelDefaultSettings: - @staticmethod - def from_base(base: BaseModelType, variant: AnyVariant | None = None): - match base: - case BaseModelType.Flux2: - if variant == Flux2VariantType.Klein9BBase: - return MainModelDefaultSettings(steps=28, cfg_scale=1.0, ...) - return MainModelDefaultSettings(steps=4, cfg_scale=1.0, ...) - case BaseModelType.NewModel: # NEW - return MainModelDefaultSettings(steps=20, cfg_scale=7.0, ...) - ``` - +- `BasicConditioningInfo` as the conditioning class +- 1024×1024 default settings +- `txt2img` as the only mode +- a dimension grid of 8 -:::tip[Checklist: Model Manager]{icon="approve-check"} -- [ ] Extend `BaseModelType` enum (`taxonomy.py`) -- [ ] Create variant enum if needed (`taxonomy.py`) -- [ ] Update `AnyVariant` union (`taxonomy.py`) -- [ ] Add default settings in `from_base()` (`configs/main.py`) +Treat every `TODO` in the file as mandatory. ::: --- -## 2. Backend: Model Configs +## 1. Upstream dependencies -InvokeAI needs to know how to identify your model from a `.safetensors` file or a diffusers folder. +Most architectures load their transformer, VAE and text encoder through classes from `diffusers` and `transformers`. If the pinned releases lack the new model's classes, handle the dependency first: - -1. **Create Main Model Config** +- **Wait for a stable release** that contains the classes. Do not pin a git commit; `pyproject.toml` pins `diffusers` to an exact version. +- **Bump in a separate change.** A new release can change the numerical behavior of every existing architecture. The change has four parts: + - Update `pyproject.toml` and `uv.lock`, including raised transitive minimums such as `huggingface-hub`. + - Update `tests/backend/model_manager/load/test_diffusers_0XX_compatibility.py`. It asserts the exact pinned version and the classes InvokeAI imports. + - Run the full test suite. + - Run smoke generations for the existing architectures. +- **Check the `transformers` requirement in the model card against the pin.** Model cards often name the version the authors tested, not a hard minimum. Verify that the encoder and processor behave correctly on the pinned version before bumping it. - Define the configuration schemas for your model format(s). +--- - ```python title="invokeai/backend/model_manager/configs/main.py" - # Checkpoint Format - @ModelConfigFactory.register - class Main_Checkpoint_NewModel_Config(Checkpoint_Config_Base): - type: Literal[ModelType.Main] = ModelType.Main - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel - format: Literal[ModelFormat.Checkpoint] = ModelFormat.Checkpoint - variant: NewModelVariantType = NewModelVariantType.VariantA +## 2. Taxonomy - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict) -> Self: - if not cls._validate_is_newmodel(mod): - raise NotAMatchError("Not a NewModel") - variant = cls._get_variant_or_raise(mod) - return cls(..., variant=variant) - - # Diffusers Format - @ModelConfigFactory.register - class Main_Diffusers_NewModel_Config(Diffusers_Config_Base): - type: Literal[ModelType.Main] = ModelType.Main - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel - format: Literal[ModelFormat.Diffusers] = ModelFormat.Diffusers +File: `invokeai/backend/model_manager/taxonomy.py` + + +1. **Add the `BaseModelType` member.** + + ```python title="invokeai/backend/model_manager/taxonomy.py" + class BaseModelType(str, Enum): + ... + Krea2 = "krea-2" + """Indicates the model is associated with the Krea 2 model architecture, including Krea-2-Turbo.""" + NewModel = "new-model" + """Indicates the model is associated with the NewModel architecture.""" ``` -2. **Detection Helper Functions** + The value is stored in every user's model database, so it cannot be renamed later. It also names the declaration module, with `-` replaced by `_`, so it must yield a valid Python module name. Use lowercase letters, digits and `-` only, and no dots: `qwen-image-2-1`, not `qwen-image-2.1`. - Write helpers to inspect the state dictionary keys and shapes to uniquely identify your architecture. +2. **Add a variant enum (if needed).** - ```python title="invokeai/backend/model_manager/configs/main.py" - def _is_newmodel(state_dict: dict) -> bool: - """Detect if state dict belongs to NewModel architecture.""" - # Example FLUX.2 Klein detection: - # - context_embedder.weight shape[1] > 4096 (Qwen3 vs T5) - # - img_in.weight shape[1] == 128 (32 latent channels × 4) - required_keys = ["transformer_blocks.0.attn.to_q.weight", ...] - return all(key in state_dict for key in required_keys) - - def _get_newmodel_variant(state_dict: dict) -> NewModelVariantType: - """Determine variant from state dict.""" - # Example FLUX.2: context_in_dim distinguishes Klein 4B/9B - context_dim = state_dict["context_embedder.weight"].shape[1] - if context_dim == 7680: - return NewModelVariantType.VariantA - return NewModelVariantType.VariantB - ``` + Add one only when models of the architecture differ in ways that matter at load or generation time, for example distilled versus base. Variant strings are resolved *without* the base (`configs/factory.py`), so every value must be unique across all variant enums. That is why `Krea2VariantType.Turbo` is `"krea2_turbo"` and not `"turbo"`. -3. **VAE Config (if custom VAE)** + ```python title="invokeai/backend/model_manager/taxonomy.py" + class NewModelVariantType(str, Enum): + """NewModel variants.""" - ```python title="invokeai/backend/model_manager/configs/vae.py" - @ModelConfigFactory.register - class VAE_Checkpoint_NewModel_Config(VAE_Checkpoint_Base): - type: Literal[ModelType.VAE] = ModelType.VAE - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel + Turbo = "new_model_turbo" + """Distilled: few steps, CFG off.""" - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, ...) -> Self: - if not _is_newmodel_vae(mod.state_dict): - raise NotAMatchError() - return cls(...) - - def _is_newmodel_vae(state_dict: dict) -> bool: - # Example FLUX.2: Check for BN layers (bn.running_mean) - return "encoder.bn.running_mean" in state_dict + Base = "new_model_base" + """Undistilled base model: more steps, CFG on.""" ``` -4. **Text Encoder Config (if custom encoder)** - - ```python title="invokeai/backend/model_manager/configs/[encoder_type].py" - def _has_newmodel_encoder_keys(state_dict: dict) -> bool: - """Check if state dict contains NewModel encoder keys.""" - required_keys = ["model.layers.0.", "model.embed_tokens.weight"] - return any( - key.startswith(indicator) or key == indicator - for key in state_dict.keys() - for indicator in required_keys - if isinstance(key, str) - ) - - @ModelConfigFactory.register - class NewModelEncoder_Checkpoint_Config(Checkpoint_Config_Base): - """Configuration for single-file NewModel Encoder models.""" + List the enum in all three places: + - `AnyVariant` in `taxonomy.py` + - `variant_type_adapter` in `taxonomy.py` + - `ModelRecordChanges.variant` in `invokeai/app/services/model_records/model_records_base.py` - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - type: Literal[ModelType.NewModelEncoder] = Field(default=ModelType.NewModelEncoder) - format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + `tests/backend/architectures/test_variants.py` checks that the three agree and that the values are unique. - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict) -> Self: - raise_if_not_file(mod) - raise_for_override_fields(cls, override_fields) +3. **Add encoder types (only for a new text encoder).** - if not _has_newmodel_encoder_keys(mod.load_state_dict()): - raise NotAMatchError("state dict does not look like a NewModel encoder") + There is no generic text-encoder type. Each encoder family has its own `ModelType` member, and a `ModelFormat` member for its folder layout. Existing examples are `Qwen3Encoder`, `Qwen3VLEncoder`, `QwenVLEncoder`, `MistralEncoder` and `T5Encoder`. - return cls(**override_fields) + Reuse an existing type whenever the encoder is a model InvokeAI already supports. Krea-2, Ideogram 4 and MiniMax H3 all use `ModelType.Qwen3VLEncoder`, told apart by `Qwen3VLVariantType`. Check whether the encoder weights are a stock checkpoint before adding a type. + - @ModelConfigFactory.register - class NewModelEncoder_Diffusers_Config(Config_Base): - """Configuration for NewModel Encoder in diffusers directory format.""" +:::tip[Checklist: Taxonomy]{icon="approve-check"} +- [ ] `BaseModelType` member with a module-safe value +- [ ] Variant enum with globally unique values (if needed) +- [ ] Variant enum listed in `AnyVariant`, `variant_type_adapter` and `ModelRecordChanges.variant` +- [ ] Encoder `ModelType` + `ModelFormat` only if no existing encoder type fits +::: - base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any) - type: Literal[ModelType.NewModelEncoder] = Field(default=ModelType.NewModelEncoder) - format: Literal[ModelFormat.Diffusers] = Field(default=ModelFormat.Diffusers) +--- - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict) -> Self: - raise_if_not_dir(mod) - raise_for_override_fields(cls, override_fields) +## 3. Architecture declaration + +File: `invokeai/backend/architectures/defs/new_model.py` + +Each architecture declares what it is in exactly one `register(...)` call. Modules under `defs/` are discovered automatically, so there is no list to edit. + +At boot, `validate()` runs from `ApiDependencies.initialize`. It refuses to start the app in two cases: +- a `BaseModelType` has no module +- a module omits a required facet + +It checks only that the facets are *present*, not that their values are right. + +| Facet | Required | What it declares | Read by | +| --- | --- | --- | --- | +| `LatentSpaceFacet` | yes | Latent channels, spatial compression, latent→RGB projection | Step previews (`app/util/step_callback.py`) | +| `ConditioningFacet` | yes | The `*ConditioningInfo` class the text encoder writes | The safe-globals list for conditioning deserialization | +| `DefaultSettingsFacet` | yes | Steps, CFG or guidance, scheduler and size per variant (`None` is the fallback); optional `by_name_hint` | Stored on the model config at identification; prefills the UI | +| `ModalityFacet` | yes | Modes (`txt2img`, `img2img`, `inpaint`, `outpaint`, video modes) and `metadata_slug` | Must equal `GENERATION_MODES`; which canvas tools the UI offers | +| `FeaturesFacet` | yes | Negative prompt, `dimension_grid`, guidance label and range, scheduler set, control kinds, reference images, regional guidance, and more | Served to webv2 at `GET /api/v2/models/capabilities` | +| `VaeFacet` | no | VAE bases (and channel counts) the architecture decodes with | Model loader VAE field, VAE picker, loader validation | +| `VariantFacet` | no | Variant enum per `ModelType` | Variant consistency tests | +| `UNetDownscaleFacet` | no | UNet downscale factor | SD-family UNets only | + +The facet classes live in `invokeai/backend/architectures/facets/`. Modeled on `defs/krea_2.py`: + +```python title="invokeai/backend/architectures/defs/new_model.py" +"""What the new-model architecture declares.""" + +from invokeai.backend.architectures.facets.conditioning import ConditioningFacet +from invokeai.backend.architectures.facets.default_settings import DefaultSettingsFacet +from invokeai.backend.architectures.facets.features import FeaturesFacet, NegativePrompt +from invokeai.backend.architectures.facets.latent_space import FLUX2_32, LatentSpaceFacet +from invokeai.backend.architectures.facets.modality import ModalityFacet +from invokeai.backend.architectures.facets.vae import VaeCompatibility, VaeFacet +from invokeai.backend.architectures.facets.variant import VariantFacet +from invokeai.backend.architectures.registry import register +from invokeai.backend.model_manager.configs.default_settings import MainModelDefaultSettings +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, NewModelVariantType +from invokeai.backend.stable_diffusion.diffusion.conditioning_data import NewModelConditioningInfo + +register( + BaseModelType.NewModel, + # NewModel decodes with the FLUX.2 VAE, so it shares that latent space. + LatentSpaceFacet(FLUX2_32), + ConditioningFacet(NewModelConditioningInfo), + DefaultSettingsFacet( + { + # Values from the model card. + NewModelVariantType.Base: MainModelDefaultSettings( + scheduler="euler", steps=28, cfg_scale=4.0, width=1024, height=1024 + ), + # Turbo, and any model whose variant is unknown. + None: MainModelDefaultSettings(scheduler="euler", steps=8, cfg_scale=1.0, width=1024, height=1024), + } + ), + ModalityFacet(frozenset({"txt2img", "img2img", "inpaint", "outpaint"}), metadata_slug="new_model"), + FeaturesFacet( + negative_prompt=NegativePrompt(visible=True, usage="cfg-gated"), + dimension_grid=16, # equals `multiple_of` on new_model_denoise.width/height + guidance_label="CFG", + scheduler_set="flow", + ), + VaeFacet(frozenset({VaeCompatibility(BaseModelType.Flux2)})), + # Variant values must be globally unique; see taxonomy.py. + VariantFacet({ModelType.Main: NewModelVariantType}), +) +``` - # Check for text_encoder config - config_path = mod.path / "text_encoder" / "config.json" - if not config_path.exists(): - raise NotAMatchError(f"config file not found: {config_path}") +Things to get right: + +- **Latent space.** If the architecture uses a VAE InvokeAI already supports, reuse its shared space from `facets/latent_space.py`: + - `FLUX_16`: FLUX.1, Z-Image + - `FLUX2_32`: FLUX.2, ERNIE-Image, Ideogram 4 + - `WAN21_16`: Wan 2.1, Qwen-Image, Krea-2, Anima + - `WAN22_48` + + Otherwise, declare a new `LatentSpace` in that file. Compute its projection with `scripts/generate_vae_linear_approximation.py` rather than guessing it. +- **Grid and guidance** are pinned to the denoise node by tests: + - `dimension_grid` must equal the `multiple_of` on the node's width and height. + - `guidance_min` and `guidance_max` must match the `ge` and `le` of the guidance field. + - A constraint that the node only enforces inside `invoke()`, per variant, goes in `dimension_grid_by_variant`. +- **`VaeFacet`** replaces the default rule, under which an architecture accepts only its own base. List every accepted base explicitly, including the architecture's own if it applies. Z-Image, for example, accepts only FLUX VAEs. +- **Mode strings** are persisted in image metadata and cannot be renamed. `metadata_slug` is usually the base value with `_` (`z_image`), but not always (`krea2`). +- **Default settings** are copied onto the model config when a model is identified (`configs/factory.py`), so model configs need no code for them. Use `by_name_hint` only for sub-models that nothing on disk can tell apart. + +:::tip[Checklist: Architecture declaration]{icon="approve-check"} +- [ ] `defs/.py` with one `register(...)` call +- [ ] All five required facets carry real values, not scaffolder placeholders +- [ ] `VaeFacet` / `VariantFacet` where applicable +- [ ] New `LatentSpace` computed with `generate_vae_linear_approximation.py`, if the VAE is new +::: - raise_for_class_name(config_path, {"NewModelForCausalLM"}) +--- - return cls(**override_fields) - ``` +## 4. Model configs - Examples of existing implementations: +Folder: `invokeai/backend/model_manager/configs/` - - `t5_encoder.py` — T5 Encoder for FLUX.1, SD3 - - `qwen3_encoder.py` — Qwen3 Encoder for FLUX.2 Klein, Z-Image - - `clip_embed.py` — CLIP Encoder for SDXL, SD3 +Configs identify a model on disk. Every non-abstract subclass of `Config_Base` registers itself through `Config_Base.__init_subclass__`; there is no decorator. -5. **Update `AnyModelConfig` Union** + +1. **Main model configs** (`configs/main.py`) - Register your new configs so the application knows to check them when scanning directories. + One class per format. Each combines a format mixin, `Main_Config_Base` and `Config_Base`: - ```python title="invokeai/backend/model_manager/configs/factory.py" ins={3-5} - AnyModelConfig = Annotated[ - # ... existing configs - Main_Checkpoint_NewModel_Config | - Main_Diffusers_NewModel_Config | - VAE_Checkpoint_NewModel_Config, - Discriminator(...) - ] - ``` - + ```python title="invokeai/backend/model_manager/configs/main.py" + class Main_Diffusers_NewModel_Config(Diffusers_Config_Base, Main_Config_Base, Config_Base): + """Model config for NewModel diffusers models.""" -:::tip[Checklist: Model Configs]{icon="approve-check"} -- [ ] Create main checkpoint config (`configs/main.py`) -- [ ] Create main diffusers config (`configs/main.py`) -- [ ] Create detection helper functions (`_is_newmodel()`, `_get_variant()`) -- [ ] Create VAE config if custom VAE (`configs/vae.py`) -- [ ] Create text encoder config if custom encoder -- [ ] Update `AnyModelConfig` union (`configs/factory.py`) -::: + base: Literal[BaseModelType.NewModel] = Field(BaseModelType.NewModel) + variant: NewModelVariantType = Field() ---- + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_dir(mod) + raise_for_override_fields(cls, override_fields) + # The pipeline class name implies the base. + raise_for_class_name(common_config_paths(mod.path), {"NewModelPipeline"}) -## 3. Backend: Model Loader + # `_get_variant` reads whatever distinguishes the variants, e.g. a flag in model_index.json. + variant = override_fields.pop("variant", None) or cls._get_variant(mod) + repo_variant = override_fields.pop("repo_variant", None) or cls._get_repo_variant_or_raise(mod) + return cls(**override_fields, variant=variant, repo_variant=repo_variant) -Loaders convert the files on disk (described by the config) into PyTorch models in memory. - -1. **Create Model Loader** - - ```python title="invokeai/backend/model_manager/load/model_loaders/[newmodel].py" - @ModelLoaderRegistry.register( - base=BaseModelType.NewModel, - type=ModelType.Main, - format=ModelFormat.Checkpoint - ) - class NewModelLoader(ModelLoader): - def _load_model(self, config: AnyModelConfig, submodel_type: SubModelType | None) -> AnyModel: - # Load and convert state dict - state_dict = self._load_state_dict(config.path) - - # If format conversion needed (e.g., BFL → Diffusers): - if self._is_bfl_format(state_dict): - state_dict = self._convert_bfl_to_diffusers(state_dict) - - # Instantiate model - model = NewModelTransformer(config=model_config) - model.load_state_dict(state_dict) - return model - ``` + class Main_Checkpoint_NewModel_Config(Checkpoint_Config_Base, Main_Config_Base, Config_Base): + """Model config for NewModel single-file checkpoints.""" -2. **VAE Loader (if custom VAE)** - - ```python title="invokeai/backend/model_manager/load/model_loaders/[newmodel].py" - @ModelLoaderRegistry.register( - base=BaseModelType.NewModel, - type=ModelType.VAE, - format=ModelFormat.Checkpoint - ) - class NewModelVAELoader(ModelLoader): - def _load_model(self, config, submodel_type) -> AnyModel: - # Example FLUX.2: AutoencoderKLFlux2 with BN layers - from diffusers import AutoencoderKLFlux2 - vae = AutoencoderKLFlux2.from_single_file(config.path) - return vae - ``` + base: Literal[BaseModelType.NewModel] = Field(default=BaseModelType.NewModel) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + variant: NewModelVariantType = Field() -3. **Text Encoder Loader (if custom encoder)** - - ```python title="invokeai/backend/model_manager/load/model_loaders/[newmodel].py" - @ModelLoaderRegistry.register( - base=BaseModelType.Any, - type=ModelType.NewModelEncoder, - format=ModelFormat.Checkpoint - ) - class NewModelEncoderLoader(ModelLoader): - """Load single-file NewModel Encoder models.""" - - def _load_model(self, config: AnyModelConfig, submodel_type: SubModelType | None) -> AnyModel: - match submodel_type: - case SubModelType.TextEncoder: - return self._load_text_encoder(config) - case SubModelType.Tokenizer: - # Load tokenizer from HuggingFace or local path - return AutoTokenizer.from_pretrained("org/newmodel-base") - - raise ValueError(f"Unsupported submodel: {submodel_type}") - - def _load_text_encoder(self, config: AnyModelConfig) -> AnyModel: - from safetensors.torch import load_file - from transformers import NewModelConfig, NewModelForCausalLM - - # Load state dict and determine model configuration - sd = load_file(config.path) - - # Detect model architecture from weights - layer_count = self._count_layers(sd) - hidden_size = sd["model.embed_tokens.weight"].shape[1] - - # Create model with detected configuration - model_config = NewModelConfig( - hidden_size=hidden_size, - num_hidden_layers=layer_count, - # ... other config parameters - ) + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_file(mod) + raise_for_override_fields(cls, override_fields) - with accelerate.init_empty_weights(): - model = NewModelForCausalLM(model_config) + state_dict = mod.load_state_dict() + if not _has_new_model_keys(state_dict): + raise NotAMatchError("state dict does not look like a NewModel model") + if _has_ggml_tensors(state_dict): + raise NotAMatchError("state dict looks like GGUF quantized") - model.load_state_dict(sd, assign=True) - return model + variant = override_fields.pop("variant", None) or _get_new_model_variant_from_name(mod.path.name) + return cls(**override_fields, variant=variant) ``` - -:::tip[Checklist: Model Loader]{icon="approve-check"} -- [ ] Create and register main model loader -- [ ] Create VAE loader if custom VAE -- [ ] Create text encoder loader if custom encoder -- [ ] Implement state dict conversion if needed (different formats) -- [ ] Implement submodel loading (Diffusers format) -::: + If GGUF files exist, add a `Main_GGUF_NewModel_Config` with `format: Literal[ModelFormat.GGUFQuantized]` that *requires* `_has_ggml_tensors`. ---- - -## 4. Backend: Invocations + The identification helpers are in `configs/identification_utils.py`: + - `NotAMatchError`: "not this config" + - `InvalidMatchError`: "this config, but the file is broken" + - `raise_if_not_file` / `raise_if_not_dir` + - `raise_for_override_fields` + - `raise_for_class_name` + - `common_config_paths` + - `state_dict_has_any_keys_*` -Invocations expose your PyTorch functions as isolated execution nodes in InvokeAI's graph. +2. **Detection helpers** - -1. **Model Loader Invocation** + Detect the architecture from keys that only it has, and from shapes where keys are shared. Strip ComfyUI prefixes such as `model.diffusion_model.` from each key first, with `_strip_comfyui_key_prefix` in `configs/main.py`. It reads the shared `COMFYUI_KEY_PREFIXES`, so detection and the loaders agree on which prefixes exist. Exclude LoRA suffixes, so that a LoRA for the architecture is not mistaken for a main model. - ```python title="invokeai/app/invocations/[newmodel]_model_loader.py" - @invocation("newmodel_model_loader", title="NewModel Loader", ...) - class NewModelModelLoaderInvocation(BaseInvocation): - model: ModelIdentifierField = InputField(description="Main model") - vae_model: ModelIdentifierField | None = InputField(default=None) - encoder_model: ModelIdentifierField | None = InputField(default=None) - - def invoke(self, context: InvocationContext) -> NewModelLoaderOutput: - # Load transformer - transformer = self.model.model_copy( - update={"submodel_type": SubModelType.Transformer} - ) - # Load VAE (from main model or separately) - if self.vae_model: - vae = self.vae_model.model_copy(...) - else: - vae = self.model.model_copy( - update={"submodel_type": SubModelType.VAE} - ) - return NewModelLoaderOutput(transformer=transformer, vae=vae, ...) + ```python title="invokeai/backend/model_manager/configs/main.py" + def _has_new_model_keys(state_dict: dict[str | int, Any]) -> bool: + """True for NewModel transformer weights, False for its LoRAs and for every other architecture.""" + # `txt_in.text_norm` is unique to NewModel; `img_in` alone is shared with Qwen-Image and Krea-2. + ... ``` -2. **Text Encoder Invocation** + **Configs must exclude each other.** Identification iterates `Config_Base.CONFIG_CLASSES`, which is a set, so the order in the `AnyModelConfig` union decides nothing. When two configs match the same file, `matches_sort_key` breaks the tie, which amounts to chance. For every existing config whose heuristic the new files could satisfy, add a negative check on one side or both. The same applies to LoRA and VAE configs, whose heuristics are often loose, for example "any key starting with `transformer_blocks.`". Cover each exclusion with a detection test. - ```python title="invokeai/app/invocations/[newmodel]_text_encoder.py" - @invocation("newmodel_text_encode", title="NewModel Text Encoder", ...) - class NewModelTextEncoderInvocation(BaseInvocation): - prompt: str = InputField() - encoder: EncoderField = InputField() - - def invoke(self, context: InvocationContext) -> ConditioningOutput: - # 1. Tokenize the prompt - with context.models.load(self.encoder.tokenizer) as tokenizer: - input_ids = tokenizer( - self.prompt, - return_tensors="pt", - padding="max_length", - max_length=256, - truncation=True - ).input_ids - - # 2. Run encoder and extract hidden states - # Example FLUX.2 Klein/Z-Image: Extract specific layers and stack them - # Different models use different layer extraction strategies: - # - Some use the final hidden state only - # - Others stack multiple intermediate layers for richer representations - with context.models.load(self.encoder.text_encoder) as encoder: - outputs = encoder(input_ids, output_hidden_states=True) - hidden_states = outputs.hidden_states - - # Stack layers 9, 18, 27 to create combined text embedding - # This captures features at different abstraction levels - # Shape: (batch, seq_len, hidden_size) -> (batch, seq_len, hidden_size * 3) - stacked_embeddings = torch.cat([ - hidden_states[9], - hidden_states[18], - hidden_states[27] - ], dim=-1) - - # 3. Create conditioning data structure - # The stacked embeddings become the text conditioning that guides denoising - conditioning_data = ConditioningFieldData( - conditionings=[ - BasicConditioningInfo(embeds=stacked_embeddings) - ] - ) +3. **VAE config (only for a new VAE)** (`configs/vae.py`) - # 4. Save conditioning to context and return reference - conditioning_name = context.conditioning.save(conditioning_data) - return ConditioningOutput( - conditioning=ConditioningField(conditioning_name=conditioning_name) - ) - ``` + `VAE_Checkpoint_Config_Base` and `VAE_Diffusers_Config_Base` detect the plain `AutoencoderKL` VAEs: SD 1, SD 2 (single files only) and SDXL at 4 latent channels, FLUX.1 and SD3 at 16. Within one latent width the weights cannot tell these bases apart, so `_VAE_FAMILIES` groups them, and an explicit `base` override chooses within a family but never across one. For 16 channels, a folder's `config.json` decides by `scaling_factor`/`shift_factor`, and a config that matches neither is not filed at all. A 16-channel single file goes to the backbone named in its file name, folder or install source (`configs/backbone_names.py`), else to FLUX.1. At 4 channels the older heuristics still apply: the file name for a single file, the SDXL config values or name for a folder. A new architecture that reuses one of these networks with its own constants (CogView 4 would) belongs in that table and in the diffusers constants check, not in a new detector. -3. **Denoise Invocation** + Newer architectures with their own network subclass `Checkpoint_Config_Base, Config_Base` directly, as `VAE_Checkpoint_Flux2_Config` does: - ```python title="invokeai/app/invocations/[newmodel]_denoise.py" - @invocation("newmodel_denoise", title="NewModel Denoise", ...) - class NewModelDenoiseInvocation(BaseInvocation): - # Standard Fields - latents: LatentsField | None = InputField(default=None) - positive_conditioning: ConditioningField = InputField() - negative_conditioning: ConditioningField | None = InputField(default=None) + ```python title="invokeai/backend/model_manager/configs/vae.py" + class VAE_Checkpoint_NewModel_Config(Checkpoint_Config_Base, Config_Base): + """Model config for NewModel VAE checkpoints.""" - # Model Fields - transformer: TransformerField = InputField() + type: Literal[ModelType.VAE] = Field(default=ModelType.VAE) + format: Literal[ModelFormat.Checkpoint] = Field(default=ModelFormat.Checkpoint) + base: Literal[BaseModelType.NewModel] = Field(default=BaseModelType.NewModel) + cpu_only: bool | None = Field(default=None, description="Whether this model should run on CPU only") - # Denoise Parameters - denoising_start: float = InputField(default=0.0, ge=0, le=1) - denoising_end: float = InputField(default=1.0, ge=0, le=1) - steps: int = InputField(default=20, ge=1) - cfg_scale: float = InputField(default=7.0) + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_file(mod) + raise_for_override_fields(cls, override_fields) + if not _is_new_model_vae(mod.load_state_dict()): + raise NotAMatchError("state dict does not look like a NewModel VAE") + return cls(**override_fields) + ``` - # Image-to-Image / Inpainting - denoise_mask: DenoiseMaskField | None = InputField(default=None) + Then exclude the new VAE in `VAE_Checkpoint_Config_Base._validate_looks_like_vae`, and in every family detector it could also satisfy. Wan, Qwen-Image and Anima VAEs share one layout family. - # Scheduler (if model-specific) - scheduler: Literal["euler", "heun", "lcm"] = InputField(default="euler") +4. **Text encoder config (only for a new encoder type)** - def invoke(self, context: InvocationContext) -> LatentsOutput: - # 1. Generate noise - noise = get_noise_newmodel(seed, height, width, ...) - - # 2. Pack latents (if needed) - x = pack_newmodel(latents) - - # 3. Compute schedule - timesteps = get_schedule_newmodel(num_steps, denoising_start, denoising_end) - - # 4. Denoising loop - x = denoise( - model=transformer, - x=x, - timesteps=timesteps, - conditioning=conditioning, - cfg_scale=self.cfg_scale, - inpaint_extension=inpaint_extension, # For inpainting - ) + Create `configs/.py` with `base=BaseModelType.Any`, the new `ModelType`, and one config per format: folder (its own `ModelFormat`), single-file checkpoint, and GGUF if it exists. `qwen3_encoder.py`, `qwen3_vl_encoder.py` and `mistral_encoder.py` are complete examples. + +5. **`AnyModelConfig` union** (`configs/factory.py`) - # 5. Unpack latents - latents = unpack_newmodel(x) + Add each new config to the union, in the shape the other entries use: - return LatentsOutput(latents=latents) + ```python title="invokeai/backend/model_manager/configs/factory.py" + Annotated[Main_Diffusers_NewModel_Config, Main_Diffusers_NewModel_Config.get_tag()], ``` + + +:::tip[Checklist: Model configs]{icon="approve-check"} +- [ ] Main configs per format (Diffusers, checkpoint, GGUF) +- [ ] Detection helper and variant detection +- [ ] Mutual exclusion with every existing config the new files could match, with tests +- [ ] VAE config and exclusions in the existing VAE detectors (if the VAE is new) +- [ ] Encoder configs (if the encoder type is new) +- [ ] Every new config in the `AnyModelConfig` union +::: -4. **VAE Encode Invocation** +--- - ```python title="invokeai/app/invocations/[newmodel]_vae_encode.py" - @invocation("newmodel_vae_encode", title="Image to Latents - NewModel", ...) - class NewModelVaeEncodeInvocation(BaseInvocation): - image: ImageField = InputField() - vae: VAEField = InputField() +## 5. Model loaders - def invoke(self, context: InvocationContext) -> LatentsOutput: - image = context.images.get_pil(self.image.image_name) - image_tensor = image_resized_to_grid_as_tensor(image.convert("RGB")) +Folder: `invokeai/backend/model_manager/load/model_loaders/` - with context.models.load(self.vae.vae) as vae: - latent_dist = vae.encode(image_tensor) - latents = latent_dist.mode() # Deterministic +Loaders turn a config into an in-memory model. Every module in this folder is imported automatically. A loader registers for a `(base, type, format)` triple. Lookup falls back to `base=BaseModelType.Any`, which is how encoder loaders serve every architecture. - return LatentsOutput(latents=latents) + +1. **Diffusers-format main model** + + Subclass `GenericDiffusersLoader`. It resolves each submodel's class from `model_index.json`. Override `_load_model` only for what the pipeline gets wrong: tokenizer quirks, config patches, dtype, or dropping unused modules. + + ```python title="invokeai/backend/model_manager/load/model_loaders/new_model.py" + @ModelLoaderRegistry.register(base=BaseModelType.NewModel, type=ModelType.Main, format=ModelFormat.Diffusers) + class NewModelDiffusersModel(GenericDiffusersLoader): + """Loads NewModel submodels from a diffusers pipeline folder.""" + + def _load_model(self, config: AnyModelConfig, submodel_type: Optional[SubModelType] = None) -> AnyModel: + if submodel_type is None: + raise Exception("A submodel type must be provided when loading main pipelines.") + model_path = Path(config.path) + load_class = self.get_hf_load_class(model_path, submodel_type) + dtype = TorchDevice.choose_bfloat16_safe_dtype(TorchDevice.choose_torch_device()) + result: AnyModel = load_class.from_pretrained(model_path / submodel_type.value, torch_dtype=dtype) + return self._apply_fp8_layerwise_casting(result, config, submodel_type) ``` -5. **VAE Decode Invocation** +2. **Single-file and GGUF main models** - ```python title="invokeai/app/invocations/[newmodel]_vae_decode.py" - @invocation("newmodel_vae_decode", title="Latents to Image - NewModel", ...) - class NewModelVaeDecodeInvocation(BaseInvocation): - latents: LatentsField = InputField() - vae: VAEField = InputField() + Subclass `ModelLoader`, build the transformer under `accelerate.init_empty_weights()`, convert the keys to the diffusers layout, and load with `assign=True`. `Krea2CheckpointModel` and `Krea2GGUFCheckpointModel` in `krea2.py` show the full pattern: + - stripping the ComfyUI prefix with `CheckpointPrefix.detect(sd).strip(sd)` (`invokeai/backend/model_manager/checkpoint_prefix.py`) rather than a hand-written loop + - reserving RAM before materializing, exactly once: build the empty model first, take the fp8 and nvfp4 side channels out of the state dict, then reserve, and only then fold or widen anything. A fold before the reservation lands on a cache that was never asked for the room. For fp8, nvfp4 and dense files the reservation is `reserve_for_load` (`invokeai/backend/quantization/load_plan.py`). + - quantized formats: ComfyUI `fp8_scaled`, `int8_convrot` and `nvfp4` (helpers in `invokeai/backend/quantization/`), and GGUF through `gguf_sd_loader`. For int8, call `install_int8_convrot_layers` instead of its individual steps. It makes the reservation itself through its `reserve` argument, so do not also call `reserve_for_load`; the steps' order matters, and skipping the first one loads a mixed checkpoint's fp8 weights without their scale. + - a loader that handles no quantization side channel (a VAE, an adapter, a non-strict `load_state_dict`) calls `reject_quantized_side_channel` so a quantized file is refused rather than loaded wrongly + - opting into FP8 storage with `_apply_fp8_layerwise_casting` - def invoke(self, context: InvocationContext) -> ImageOutput: - latents = context.tensors.load(self.latents.latents_name) + **Declare FP8 Storage support at registration.** The Model Manager only shows the FP8 Storage toggle where `fp8_storage_verdict(base, type, format)` (`invokeai/backend/model_manager/load/fp8_capability.py`) says it works. A loader that does not apply the cast says so where it registers, with a reason: `fp8_storage=Unimplemented("...")` for work not done yet, `NotApplicable("...")` for a deliberate decision. `tests/backend/model_manager/load/test_fp8_capability.py` walks the registry and fails for a loader that neither casts nor declares. - with context.models.load(self.vae.vae) as vae: - # Example FLUX.2: BN denormalization before decode - if hasattr(vae, "bn"): - latents = self._bn_denormalize(latents, vae) + Community single-file checkpoints often use key names that differ from diffusers, for example fused projections. Verify the conversion numerically against the diffusers module on a small config. - image = vae.decode(latents).sample - image = (image / 2 + 0.5).clamp(0, 1) +3. **VAE loader (only for a new VAE)** - return ImageOutput(image=image) - ``` + Build the diffusers VAE class and load the converted state dict. `vae.py` and `flux.py` handle existing VAEs from native and ComfyUI layouts. + +4. **Text encoder loader (only for a new encoder type)** + + Register with `base=BaseModelType.Any` and the encoder's `ModelType`, and serve both `SubModelType.TextEncoder` and `SubModelType.Tokenizer`. Vendor tokenizers and configs the loader needs, as `invokeai/backend/qwen2_5_vl/` does, so a single-file install loads without network access. Add the vendored files to `[tool.setuptools.package-data]` in `pyproject.toml`, or a wheel install fails with `FileNotFoundError`. -:::tip[Checklist: Invocations]{icon="approve-check"} -- [ ] Model loader invocation (`[newmodel]_model_loader.py`) -- [ ] Text encoder invocation (`[newmodel]_text_encoder.py`) -- [ ] Denoise invocation (`[newmodel]_denoise.py`) -- [ ] VAE encode invocation (`[newmodel]_vae_encode.py`) -- [ ] VAE decode invocation (`[newmodel]_vae_decode.py`) -- [ ] Define output classes (e.g., `NewModelLoaderOutput`) -- [ ] Define field classes if needed (e.g., `NewModelEncoderField`) +:::tip[Checklist: Model loaders]{icon="approve-check"} +- [ ] Loader per registered `(base, type, format)` +- [ ] Key conversion for single-file and GGUF layouts, verified numerically against diffusers +- [ ] Quantized formats via the existing `invokeai/backend/quantization/` helpers +- [ ] FP8 Storage: the loader casts, or declares `Unimplemented` / `NotApplicable` at registration +- [ ] VAE and encoder loaders (if new) ::: --- -## 5. Backend: Sampling and Denoise +## 6. Conditioning plumbing -This is where the actual mathematical implementation of the model lives. +A text encoder saves a `ConditioningFieldData` to disk, and the denoise node reads it back. Each architecture has its own conditioning types, and they must be threaded through five places: -1. **Sampling Utilities** - - ```python title="invokeai/backend/[newmodel]/sampling_utils.py" - def get_noise_newmodel( - num_samples: int, - height: int, - width: int, - seed: int, - device: torch.device, - dtype: torch.dtype, - ) -> torch.Tensor: - """Generate noise for NewModel. - - Example FLUX.2: 32 latent channels (vs 16 for FLUX.1) - """ - latent_channels = 32 # Model-specific - latent_h = height // 8 - latent_w = width // 8 - - generator = torch.Generator(device=device).manual_seed(seed) - return torch.randn( - (num_samples, latent_channels, latent_h, latent_w), - generator=generator, - device=device, - dtype=dtype, - ) - - def pack_newmodel(x: torch.Tensor) -> torch.Tensor: - """Pack latents for transformer input. - - Example FLUX: 2×2 patches → (B, H/2*W/2, C*4) - """ - return rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2) +1. **Info class and union** (`invokeai/backend/stable_diffusion/diffusion/conditioning_data.py`) - def unpack_newmodel(x: torch.Tensor, height: int, width: int) -> torch.Tensor: - """Unpack transformer output to latents.""" - return rearrange( - x, "b (h w) (c ph pw) -> b c (h ph) (w pw)", - h=height // 16, w=width // 16, ph=2, pw=2 - ) - - def get_schedule_newmodel( - num_steps: int, - denoising_start: float = 0.0, - denoising_end: float = 1.0, - ) -> list[float]: - """Create timestep schedule. + Add a dataclass with a `to(device, dtype)` method, and add it to the `ConditioningFieldData.conditionings` union. The `ConditioningFacet` then puts it on the safe-globals list that deserialization needs. A class missing from that list fails with an `UnpicklingError` inside the denoise node, after the encoder has already run. - Example FLUX.2 Klein: Linear schedule from 1.0 → 0.0 - """ - start_step = int(num_steps * denoising_start) - end_step = int(num_steps * denoising_end) + ```python title="invokeai/backend/stable_diffusion/diffusion/conditioning_data.py" + @dataclass + class NewModelConditioningInfo: + """NewModel text conditioning.""" - sigmas = torch.linspace(1.0, 0.0, num_steps + 1) - return sigmas[start_step:end_step + 1].tolist() + prompt_embeds: torch.Tensor + """Shape: (batch_size, seq_len, hidden_size).""" - def generate_img_ids_newmodel(batch_size: int, height: int, width: int) -> torch.Tensor: - """Generate position IDs for transformer. + prompt_embeds_mask: torch.Tensor | None = None + """Shape: (batch_size, seq_len). True for valid tokens.""" - Example FLUX.2: 4D position IDs (T, H, W, L) - """ - # Model-specific position encoding - pass + def to(self, device: torch.device | None = None, dtype: torch.dtype | None = None): + self.prompt_embeds = self.prompt_embeds.to(device=device, dtype=dtype) + if self.prompt_embeds_mask is not None: + self.prompt_embeds_mask = self.prompt_embeds_mask.to(device=device) + return self ``` - If the architecture supports external noise, prefer extending the standard `invokeai/app/invocations/noise.py` node's `noise_type` selector instead of adding a brand new noise node. Only add a dedicated noise invocation when the architecture's noise tensor rank or layout cannot be expressed by the standard node. - -2. **Denoise Function** - - ```python title="invokeai/backend/[newmodel]/denoise.py" - def denoise( - model: nn.Module, - img: torch.Tensor, - img_ids: torch.Tensor, - txt: torch.Tensor, - txt_ids: torch.Tensor, - timesteps: list[float], - cfg_scale: list[float], - neg_txt: torch.Tensor | None = None, - neg_txt_ids: torch.Tensor | None = None, - scheduler: Any = None, - inpaint_extension: RectifiedFlowInpaintExtension | None = None, - step_callback: Callable | None = None, - ) -> torch.Tensor: - """Main denoising loop. - - Example FLUX.2 Klein: - - No guidance_embeds (unlike FLUX.1 Dev) - - Supports Euler, Heun, LCM schedulers - - Integration with RectifiedFlowInpaintExtension - """ - total_steps = len(timesteps) - 1 - - for step_index in range(total_steps): - t_curr = timesteps[step_index] - t_prev = timesteps[step_index + 1] - - # CFG - if cfg_scale[step_index] > 1.0 and neg_txt is not None: - pred_pos = model(img, t_curr, txt, txt_ids, img_ids) - pred_neg = model(img, t_curr, neg_txt, neg_txt_ids, img_ids) - pred = pred_neg + cfg_scale[step_index] * (pred_pos - pred_neg) - else: - pred = model(img, t_curr, txt, txt_ids, img_ids) - - # Scheduler step or manual Euler - if scheduler is not None: - img = scheduler.step(pred, t_curr, img).prev_sample - else: - # Manual Euler: x = x + (t_prev - t_curr) * pred - img = img + (t_prev - t_curr) * pred - - # Inpainting merge - if inpaint_extension is not None: - img = inpaint_extension.merge_intermediate_latents_with_init_latents(img, t_prev) - - # Progress callback - if step_callback: - step_callback(PipelineIntermediateState(step=step_index + 1, ...)) - - return img - ``` +2. **Field** (`invokeai/app/invocations/fields.py`): `NewModelConditioningField` with `conditioning_name` and an optional `mask` for regional prompting. Add node field descriptions to `FieldDescriptions` in the same file. -3. **Scheduler (if model-specific)** +3. **Output** (`invokeai/app/invocations/primitives.py`): `NewModelConditioningOutput`, registered with `@invocation_output("new_model_conditioning_output")`. - ```python title="invokeai/backend/[newmodel]/schedulers.py" - # Existing schedulers in invokeai/backend/flux/schedulers.py: - # - FlowMatchEulerDiscreteScheduler - # - FlowMatchHeunDiscreteScheduler - # - FlowMatchLCMScheduler +4. **Encoder field** (`invokeai/app/invocations/model.py`, only for a new encoder type): for example `Qwen3VLEncoderField`, with `tokenizer`, `text_encoder` and `loras`. - NEWMODEL_SCHEDULER_MAP = { - "euler": FlowMatchEulerDiscreteScheduler, - "heun": FlowMatchHeunDiscreteScheduler, - "lcm": FlowMatchLCMScheduler, - } - ``` +5. **Public API** (`invokeai/invocation_api/__init__.py`): export the info class, and list it in `__all__`. -:::tip[Checklist: Sampling and Denoise]{icon="approve-check"} -- [ ] Noise generation (`get_noise_newmodel()`) -- [ ] Pack/unpack functions (if transformer-based) -- [ ] Schedule generation (`get_schedule_newmodel()`) -- [ ] Position ID generation (if needed) -- [ ] Implement denoise loop -- [ ] Scheduler integration -- [ ] Inpaint extension integration -- [ ] Progress callbacks -::: - --- -## 6. Frontend: Graph Building +## 7. Invocations -The UI doesn't know about Python functions; it only knows how to build graphs of Invocations. +Invocations expose the architecture as nodes in the generation graph. - -1. **Create Graph Builder** +:::note[Where node files go] +Architecture nodes live in `invokeai/app/invocations/new_model/`, one package per architecture. Nodes that several architectures share are grouped by role instead: - ```typescript title="invokeai/frontend/web/src/features/nodes/util/graph/generation/buildNewModelGraph.ts" - export const buildNewModelGraph = async (arg: GraphBuilderArg): Promise => { - const { state, manager } = arg; - const { model } = state.params; - - const g = new Graph(); - - // 1. Model Loader - const modelLoader = g.addNode({ - id: NEWMODEL_MODEL_LOADER, - type: 'newmodel_model_loader', - model: Graph.getModelMetadataField(model), - }); +| Nodes | Package | +| --- | --- | +| VAE encode and decode, latents to image or video | `invocations/vae/` | +| Text encoders | `invocations/text_encoder/` | +| PiD decode and upscale | `invocations/pid/` | - // 2. Text Encoder - const positivePrompt = g.addNode({ - id: POSITIVE_CONDITIONING, - type: 'newmodel_text_encode', - prompt: positivePromptText, - }); - g.addEdge(modelLoader, 'encoder', positivePrompt, 'encoder'); - - // 3. Denoise Node - const denoise = g.addNode({ - id: NEWMODEL_DENOISE, - type: 'newmodel_denoise', - steps, - cfg_scale: cfg, - scheduler: newmodelScheduler, - denoising_start: 0, - denoising_end: 1, - }); - g.addEdge(modelLoader, 'transformer', denoise, 'transformer'); - g.addEdge(positivePrompt, 'conditioning', denoise, 'positive_conditioning'); +Generic nodes (noise, primitives, image operations, metadata) stay at the top level. Keep the architecture prefix in file names even inside the package (`new_model_denoise.py`). - // 4. VAE Decode - const l2i = g.addNode({ - id: NEWMODEL_VAE_DECODE, - type: 'newmodel_vae_decode', - }); - g.addEdge(modelLoader, 'vae', l2i, 'vae'); - g.addEdge(denoise, 'latents', l2i, 'latents'); - - // 5. Generation Mode Handling - let canvasOutput: Invocation = l2i; - - switch (generationMode) { - case 'txt2img': - canvasOutput = addTextToImage({ g, state, denoise, l2i }); - g.upsertMetadata({ generation_mode: 'newmodel_txt2img' }); - break; - case 'img2img': - const i2l = g.addNode({ type: 'newmodel_vae_encode' }); - canvasOutput = await addImageToImage({ g, state, manager, denoise, l2i, i2l, ... }); - g.upsertMetadata({ generation_mode: 'newmodel_img2img' }); - break; - case 'inpaint': - canvasOutput = await addInpaint({ g, state, manager, denoise, l2i, i2l, ... }); - g.upsertMetadata({ generation_mode: 'newmodel_inpaint' }); - break; - case 'outpaint': - canvasOutput = await addOutpaint({ g, state, manager, denoise, l2i, i2l, ... }); - g.upsertMetadata({ generation_mode: 'newmodel_outpaint' }); - break; - } - - return { g, noise, denoise, posCond: positivePrompt, ... }; - }; - ``` - -2. **Register Graph Builder** - - ```typescript title="invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts" ins={13-14} - switch (base) { - case 'sd-1': - case 'sd-2': - case 'sdxl': - return buildSD1Graph(arg); - case 'flux': - return buildFLUXGraph(arg); - case 'flux2': - return buildFLUXGraph(arg); // FLUX.2 uses the same builder - case 'sd-3': - return buildSD3Graph(arg); - case 'z-image': - return buildZImageGraph(arg); - case 'newmodel': // NEW - return buildNewModelGraph(arg); - } - ``` +Packages are discovered automatically. A directory of node modules without an `__init__.py` raises an `ImportError` at startup, and `tests/app/invocations/test_node_discovery.py` checks that files on disk and imported modules agree. +::: -3. **Update Type Definitions** + +1. **Model loader** (`new_model/new_model_model_loader.py`) - ```typescript title="invokeai/frontend/web/src/features/nodes/util/graph/types.ts" - // Add node types: - export type ImageOutputNodes = - | 'l2i' | 'flux_vae_decode' | 'flux2_vae_decode' - | 'sd3_l2i' | 'newmodel_vae_decode'; + Outputs the submodel identifiers. The transformer always comes from the main model; the VAE and encoder come from standalone models if selected, otherwise from the Diffusers main model. The field hints `ui_model_base` and `ui_model_type` drive the model pickers. The VAE field must use `accepted_vae_bases()`, which `tests/backend/architectures/test_vae.py` enforces. - export type LatentToImageNodes = - | 'l2i' | 'flux_vae_decode' | 'flux2_vae_decode' - | 'sd3_l2i' | 'newmodel_vae_decode'; + ```python title="invokeai/app/invocations/new_model/new_model_model_loader.py" + @invocation_output("new_model_model_loader_output") + class NewModelModelLoaderOutput(BaseInvocationOutput): + transformer: TransformerField = OutputField(description=FieldDescriptions.transformer, title="Transformer") + qwen3_vl_encoder: Qwen3VLEncoderField = OutputField( + description=FieldDescriptions.qwen3_vl_encoder, title="Qwen3-VL Encoder" + ) + vae: VAEField = OutputField(description=FieldDescriptions.vae, title="VAE") - export type ImageToLatentsNodes = - | 'i2l' | 'flux_vae_encode' | 'flux2_vae_encode' - | 'sd3_i2l' | 'newmodel_vae_encode'; - export type DenoiseLatentsNodes = - | 'denoise_latents' | 'flux_denoise' | 'flux2_denoise' - | 'sd3_denoise' | 'newmodel_denoise'; + @invocation("new_model_model_loader", title="Main Model - NewModel", tags=["model", "new_model"], + category="model", version="1.0.0", classification=Classification.Prototype) + class NewModelModelLoaderInvocation(BaseInvocation): + model: ModelIdentifierField = InputField( + description=FieldDescriptions.main_model, input=Input.Direct, + ui_model_base=BaseModelType.NewModel, ui_model_type=ModelType.Main, title="Transformer", + ) + vae_model: Optional[ModelIdentifierField] = InputField( + default=None, description="Standalone VAE model.", input=Input.Direct, + ui_model_base=accepted_vae_bases(BaseModelType.NewModel), ui_model_type=ModelType.VAE, title="VAE", + ) + qwen3_vl_encoder_model: Optional[ModelIdentifierField] = InputField( + default=None, description="Standalone Qwen3-VL Encoder model.", input=Input.Direct, + ui_model_type=ModelType.Qwen3VLEncoder, title="Qwen3-VL Encoder", + ) - export type MainModelLoaderNodes = - | 'main_model_loader' | 'flux_model_loader' | 'flux2_klein_model_loader' - | 'sd3_model_loader' | 'newmodel_model_loader'; + def invoke(self, context: InvocationContext) -> NewModelModelLoaderOutput: + transformer = self.model.model_copy(update={"submodel_type": SubModelType.Transformer}) + # Validate each standalone component (base, type, variant, accepts_vae(...)) with an error + # that names what the user picked; fall back to the Diffusers main model's submodels. + ... ``` -4. **Update Generation Mode Utilities** +2. **Text encoder** (`text_encoder/new_model_text_encoder.py`) - Update `addImageToImage.ts`, `addInpaint.ts`, and `addOutpaint.ts` to recognize your denoise node type. + Loads tokenizer and encoder through `context.models.load(...)`, applies LoRAs to the encoder if the architecture has any, extracts the hidden states the transformer was trained on, and saves the conditioning: - ```typescript title="invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts" - // Extend the type check: - if ( - denoise.type === 'cogview4_denoise' || - denoise.type === 'flux_denoise' || - denoise.type === 'flux2_denoise' || - denoise.type === 'newmodel_denoise' // NEW - ) { - // Rectified flow models: denoising_start instead of noise - } - ``` - + ```python title="invokeai/app/invocations/text_encoder/new_model_text_encoder.py" + @invocation("new_model_text_encoder", title="Prompt - NewModel", tags=["prompt", "conditioning", "new_model"], + category="conditioning", version="1.0.0", classification=Classification.Prototype, + idle_gpu_offloadable=True) + class NewModelTextEncoderInvocation(BaseInvocation): + prompt: str = InputField(description="Text prompt.", ui_component=UIComponent.Textarea) + mask: TensorField | None = InputField(default=None, description="Regional mask.", input=Input.Connection) + qwen3_vl_encoder: Qwen3VLEncoderField = InputField( + title="Qwen3-VL Encoder", description=FieldDescriptions.qwen3_vl_encoder, input=Input.Connection + ) -:::tip[Checklist: Graph Building]{icon="approve-check"} -- [ ] Create graph builder (`buildNewModelGraph.ts`) -- [ ] Register graph builder in `useEnqueueCanvas` -- [ ] Update type definitions (`types.ts`) -- [ ] Extend node type unions (`ImageOutputNodes`, etc.) -- [ ] Update `addImageToImage.ts` -- [ ] Update `addInpaint.ts` -- [ ] Update `addOutpaint.ts` -::: + @torch.no_grad() + def invoke(self, context: InvocationContext) -> NewModelConditioningOutput: + prompt_embeds, prompt_mask = self._encode(context) # template, layer choice, padding + conditioning_data = ConditioningFieldData( + conditionings=[NewModelConditioningInfo(prompt_embeds=prompt_embeds.cpu(), prompt_embeds_mask=prompt_mask)] + ) + conditioning_name = context.conditioning.save(conditioning_data) + return NewModelConditioningOutput.build(conditioning_name, mask=self.mask) + ``` ---- + Copy the reference pipeline's encoding exactly and cover it with a test: the prompt template, the prefix tokens it drops, the hidden-state layer (and whether it is before or after the final norm), the padding side and the maximum length. Small deviations here degrade images without failing. -## 7. Frontend: State Management +3. **Denoise** (`new_model/new_model_denoise.py`) - -1. **Add Parameter State** - - ```typescript title="invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts" - // Extend state interface: - interface ParamsState { - // Existing fields - fluxScheduler: 'euler' | 'heun' | 'lcm'; - zImageScheduler: 'euler' | 'heun' | 'lcm'; - - // NEW: NewModel specific parameters - newmodelScheduler: 'euler' | 'heun' | 'lcm'; - newmodelVaeModel: ParameterVAEModel | null; - newmodelEncoderModel: ParameterModel | null; - } - - // Initial state: - const initialState: ParamsState = { - newmodelScheduler: 'euler', - newmodelVaeModel: null, - newmodelEncoderModel: null, - }; + ```python title="invokeai/app/invocations/new_model/new_model_denoise.py" + @invocation("new_model_denoise", title="Denoise - NewModel", tags=["image", "new_model"], + category="image", version="1.0.0", classification=Classification.Prototype) + class NewModelDenoiseInvocation(BaseInvocation): + latents: Optional[LatentsField] = InputField(default=None, description=FieldDescriptions.latents, + input=Input.Connection) + denoise_mask: Optional[DenoiseMaskField] = InputField(default=None, description=FieldDescriptions.denoise_mask, + input=Input.Connection) + denoising_start: float = InputField(default=0.0, ge=0, le=1, description=FieldDescriptions.denoising_start) + denoising_end: float = InputField(default=1.0, ge=0, le=1, description=FieldDescriptions.denoising_end) + transformer: TransformerField = InputField(description=FieldDescriptions.transformer, input=Input.Connection) + positive_conditioning: NewModelConditioningField | list[NewModelConditioningField] = InputField( + description=FieldDescriptions.positive_cond, input=Input.Connection + ) + negative_conditioning: NewModelConditioningField | list[NewModelConditioningField] | None = InputField( + default=None, description=FieldDescriptions.negative_cond, input=Input.Connection + ) + cfg_scale: float | list[float] = InputField(default=1.0, description=FieldDescriptions.cfg_scale) + # `multiple_of` must equal FeaturesFacet.dimension_grid. + width: int = InputField(default=1024, gt=0, multiple_of=16, description="Width of the generated image.") + height: int = InputField(default=1024, gt=0, multiple_of=16, description="Height of the generated image.") + steps: int = InputField(default=8, gt=0, description=FieldDescriptions.steps) + seed: int = InputField(default=0, description="Randomness seed for reproducibility.") - // Reducers: - reducers: { - setNewmodelScheduler: (state, action: PayloadAction<'euler' | 'heun' | 'lcm'>) => { - state.newmodelScheduler = action.payload; - }, - newmodelVaeModelSelected: (state, action: PayloadAction) => { - state.newmodelVaeModel = action.payload; - }, - newmodelEncoderModelSelected: (state, action: PayloadAction) => { - state.newmodelEncoderModel = action.payload; - }, - } + def invoke(self, context: InvocationContext) -> LatentsOutput: + # 1. Noise from the seed; init latents for img2img; clip the schedule by denoising_start/end. + # 2. Load the transformer with a working-memory estimate; apply LoRA patches. + # 3. Loop: model prediction, optional CFG, scheduler step, + # RectifiedFlowInpaintExtension merge when a denoise_mask is set, step callback. + # 4. Save and return the latents. + ... ``` -2. **Create Selectors** + Report progress with `context.util.sd_step_callback(state, BaseModelType.NewModel)`; it renders previews through the architecture's `LatentSpaceFacet`. The denoise node is also where these are checked: interrupts (`context.util.is_canceled()`), inpainting, img2img through `denoising_start`, and a working-memory estimate passed to `model_on_device(working_mem_bytes=...)`. - ```typescript title="invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts" - export const selectNewmodelScheduler = createSelector( - selectParamsSlice, - (params) => params.newmodelScheduler - ); +4. **VAE encode and decode** (`vae/new_model_image_to_latents.py`, `vae/new_model_latents_to_image.py`) - export const selectNewmodelVaeModel = createSelector( - selectParamsSlice, - (params) => params.newmodelVaeModel - ); + Apply the VAE's latent normalization, scaling and shift or per-channel mean and std, exactly as the reference pipeline does. Handle tiling and give a working-memory estimate. The estimate helpers live in `invokeai/backend/util/vae_working_memory.py`, with calibration scripts in `scripts/calibrate_*_working_memory.py`. Node types conventionally end in `_i2l` and `_l2i`. - export const selectNewmodelEncoderModel = createSelector( - selectParamsSlice, - (params) => params.newmodelEncoderModel - ); - ``` +5. **LoRA loaders** (if LoRA is supported, see [Optional features](#12-optional-features)) + + `new_model_lora_loader` and `new_model_lora_collection_loader`. webv2 uses the collection loader. -:::tip[Checklist: State Management]{icon="approve-check"} -- [ ] Extend state interface for model-specific parameters -- [ ] Define initial state -- [ ] Create reducer actions -- [ ] Create selectors -- [ ] Export actions +:::tip[Checklist: Invocations]{icon="approve-check"} +- [ ] Package `invocations/new_model/` with `__init__.py` +- [ ] Model loader with `ui_model_base` / `ui_model_type`, and `accepted_vae_bases()` on the VAE field +- [ ] Text encoder matching the reference encoding, with a test +- [ ] Denoise: grid equals `dimension_grid`, guidance bounds equal the facet, img2img, inpaint, previews, cancel, working memory +- [ ] VAE encode and decode with the correct normalization ::: --- -## 8. Frontend: Parameter Recall - -Ensure users can extract parameters from previously generated images. - -```typescript title="invokeai/frontend/web/src/features/metadata/parsing.tsx" -// Add parameter recall handlers: -const recallNewmodelScheduler = (metadata: CoreMetadata) => { - if (metadata.scheduler) { - dispatch(setNewmodelScheduler(metadata.scheduler)); - } -}; - -const recallNewmodelVaeModel = async (metadata: CoreMetadata) => { - if (metadata.vae) { - const vaeModel = await fetchModelConfig(metadata.vae); - dispatch(newmodelVaeModelSelected(vaeModel)); - } -}; - -const recallNewmodelEncoderModel = async (metadata: CoreMetadata) => { - if (metadata.encoder_model) { - const encoderModel = await fetchModelConfig(metadata.encoder_model); - dispatch(newmodelEncoderModelSelected(encoderModel)); - } -}; -``` +## 8. Sampling -:::tip[Checklist: Parameter Recall]{icon="approve-check"} -- [ ] Recall handlers for each model-specific parameter -- [ ] Model config fetching for submodels -- [ ] Dispatch actions for state updates -::: +- **Backend package (optional).** Put reusable math in `invokeai/backend/new_model/`: noise, packing, position IDs, schedules and text encoding. FLUX, FLUX.2 and Krea-2 have one; Qwen-Image and Z-Image keep the loop in the denoise node. Use a package once code is shared between nodes or needs its own tests. +- **Schedulers.** Flow-matching architectures use diffusers' `FlowMatchEulerDiscreteScheduler`, or the shared maps in `invokeai/backend/flux/schedulers.py`: `FLUX_SCHEDULER_MAP`, `ZIMAGE_SCHEDULER_MAP` and `ERNIE_IMAGE_SCHEDULER_MAP`, each with name values and labels. Declare which set the UI offers in `FeaturesFacet.scheduler_set`. Set `scheduler_applies_to_graph=True` only if the denoise node really takes a scheduler field. Reproduce the reference sigma schedule and shift (`mu`, time-shift type, terminal shift) exactly, and test it against the reference scheduler. +- **External noise (optional).** Only if the denoise node accepts a noise tensor: extend `LatentNoiseType` and its shape and grid logic in `invokeai/app/invocations/latent_noise.py`, and extend `noise_type` in `invocations/noise.py`. Validate with `validate_noise_tensor_shape`. +- **Inpainting.** Rectified-flow models use `RectifiedFlowInpaintExtension` (`invokeai/backend/rectified_flow/rectified_flow_inpaint_extension.py`). +- **Previews.** `LatentSpaceFacet` provides them; there is nothing to add in the step callback. --- -## 9. Metadata and Generation Modes +## 9. Metadata and generation modes + +File: `invokeai/app/invocations/metadata.py` -1. **Add Generation Modes** +1. **Add the mode strings to `GENERATION_MODES`.** - ```python title="invokeai/app/invocations/metadata.py" ins={11-14} + ```python title="invokeai/app/invocations/metadata.py" GENERATION_MODES = Literal[ - # Existing modes - "txt2img", "img2img", "inpaint", "outpaint", - "sdxl_txt2img", "sdxl_img2img", "sdxl_inpaint", "sdxl_outpaint", - "flux_txt2img", "flux_img2img", "flux_inpaint", "flux_outpaint", - "flux2_txt2img", "flux2_img2img", "flux2_inpaint", "flux2_outpaint", - "sd3_txt2img", "sd3_img2img", "sd3_inpaint", "sd3_outpaint", - # NEW: - "newmodel_txt2img", - "newmodel_img2img", - "newmodel_inpaint", - "newmodel_outpaint", + ... + "new_model_txt2img", + "new_model_img2img", + "new_model_inpaint", + "new_model_outpaint", ] ``` -2. **Extend `CoreMetadata` (if needed)** + They must equal the `ModalityFacet` declaration: `_` for each mode. `tests/backend/architectures/test_modality.py` compares the two. webv2 writes `_txt2img`, and the canvas derives the other modes by replacing `txt2img`, so declare all four image modes if the canvas supports them. - ```python title="invokeai/app/invocations/metadata.py" - @invocation_output("core_metadata_output") - class CoreMetadataOutput(BaseInvocationOutput): - # Existing fields - model: ModelIdentifierField | None = None - steps: int | None = None - cfg_scale: float | None = None - - # NEW: Model-specific metadata fields - newmodel_encoder: ModelIdentifierField | None = None - newmodel_custom_param: float | None = None - ``` - +2. **Metadata fields (if needed).** -:::tip[Checklist: Metadata and Generation Modes]{icon="approve-check"} -- [ ] Add generation modes to `GENERATION_MODES` -- [ ] Extend `CoreMetadata` if model-specific fields needed -- [ ] Set metadata in graph builder (`g.upsertMetadata({...})`) -::: + `CoreMetadataInvocation` (`core_metadata`) accepts extra fields, so architecture-specific keys such as the chosen encoder or VAE need no declaration. Declare a field only if it needs validation. Adding one requires a node version bump and follows the `CORE_METADATA_VERSION` rules documented in [Media Metadata](/development/architecture/media-metadata/). + --- -## 10. Starter Models +## 10. Starter models -To allow users to easily download your model from the Model Manager UI, add it to the starter models list. +Folder: `invokeai/backend/model_manager/starter_models/` -```python title="invokeai/backend/model_manager/starter_models.py" -# Main Model -newmodel_main = StarterModel( - name="NewModel Main", - base=BaseModelType.NewModel, - source="organization/newmodel-main", # HuggingFace repo - description="NewModel main transformer. ~10GB", - type=ModelType.Main, -) +The catalogue is a package with one module per architecture: +- `types.py` holds `StarterModel`. +- `common.py` holds encoders and upscalers that several architectures share. Import a shared dependency from there instead of declaring a second copy; if an encoder you need is declared in another architecture's module, move it to `common.py`. + +```python title="invokeai/backend/model_manager/starter_models/new_model.py" +"""NewModel starter models.""" -# VAE (if separate) -newmodel_vae = StarterModel( +from invokeai.backend.model_manager.starter_models.common import qwen3_vl_encoder_4b +from invokeai.backend.model_manager.starter_models.types import StarterModel +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType, NewModelVariantType + +new_model_vae = StarterModel( name="NewModel VAE", base=BaseModelType.NewModel, - source="organization/newmodel::vae", # Submodel syntax - description="NewModel VAE. ~500MB", + source="org/NewModel::vae/diffusion_pytorch_model.safetensors", # a file inside a repo + description="NewModel VAE. ~300MB", type=ModelType.VAE, ) -# Text Encoder (if separate) -newmodel_encoder = StarterModel( - name="NewModel Encoder", - base=BaseModelType.Any, - source="organization/newmodel::text_encoder+tokenizer", - description="NewModel text encoder. ~5GB", - type=ModelType.TextEncoder, +new_model_turbo = StarterModel( + name="NewModel Turbo", + base=BaseModelType.NewModel, + source="org/NewModel-Turbo", # a whole diffusers repo + description="NewModel Turbo, full diffusers pipeline. ~20GB", + type=ModelType.Main, + variant=NewModelVariantType.Turbo, ) -# Quantized variants -newmodel_fp8 = StarterModel( - name="NewModel (FP8)", +new_model_turbo_gguf_q4_k_m = StarterModel( + name="NewModel Turbo (Q4_K_M GGUF)", base=BaseModelType.NewModel, - source="https://huggingface.co/org/newmodel-fp8/resolve/main/model.safetensors", - description="FP8 quantized version. ~5GB", + source="https://huggingface.co/org/NewModel-Turbo-GGUF/resolve/main/new_model_turbo-Q4_K_M.gguf", + description="GGUF ships only the transformer; the VAE and encoder are installed as dependencies. ~7GB", type=ModelType.Main, - dependencies=[newmodel_vae, newmodel_encoder], # Dependencies! + format=ModelFormat.GGUFQuantized, + variant=NewModelVariantType.Turbo, + dependencies=[new_model_vae, qwen3_vl_encoder_4b], ) - -# Add to STARTER_MODELS list: -STARTER_MODELS: list[StarterModel] = [ - # ... existing models - newmodel_main, - newmodel_vae, - newmodel_encoder, - newmodel_fp8, -] ``` -:::tip[Checklist: Starter Models]{icon="approve-check"} -- [ ] Define main model StarterModel -- [ ] Define VAE StarterModel if separate -- [ ] Define text encoder StarterModel if separate -- [ ] Define quantized variants (FP8, GGUF, etc.) -- [ ] Set dependencies correctly -- [ ] Add to `STARTER_MODELS` list +Then import the models in the package's `__init__.py`: + +- **Add them to `STARTER_MODELS`.** It is a curated order, the order the install dialog shows, so insert them where they belong instead of appending. +- **Optionally add a `STARTER_BUNDLES` entry** keyed by the base. +- **Keep `source` values unique.** The module asserts that. + +Put license restrictions in the description; the Ideogram 4 and MiniMax H3 descriptions are examples. + +:::tip[Checklist: Starter models]{icon="approve-check"} +- [ ] `starter_models/new_model.py` +- [ ] Main model per format worth offering (Diffusers, single-file, quantized), with `format=` and `variant=` +- [ ] VAE and encoder dependencies, shared ones from `common.py` +- [ ] Placed in `STARTER_MODELS`; bundle in `STARTER_BUNDLES` if useful +- [ ] License notes in descriptions ::: --- -## 11. Optional Features +## 11. Frontend (webv2) -### ControlNet Support +webv2 does not keep per-architecture tables of its own. Grid, default settings, negative-prompt behavior, guidance label and range, scheduler set, VAE acceptance, control kinds and reference-image limits all come from the backend's `FeaturesFacet`, `DefaultSettingsFacet` and `VaeFacet`, served at `GET /api/v2/models/capabilities`. What webv2 owns is the graph topology and the per-family UI. -**Backend Config** — `invokeai/backend/model_manager/configs/controlnet.py`: +Paths below are relative to `invokeai/frontend/webv2/src/features/generation/core/` unless stated otherwise. -```python title="invokeai/backend/model_manager/configs/controlnet.py" -@ModelConfigFactory.register -class ControlNet_Checkpoint_NewModel_Config(ControlNet_Checkpoint_Base): - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel + +1. **Regenerate the capabilities fixture** - @classmethod - def from_model_on_disk(cls, mod: ModelOnDisk, ...) -> Self: - if not _is_newmodel_controlnet(mod.state_dict): - raise NotAMatchError() - return cls(...) -``` + ```sh + REGEN_CAPABILITIES_FIXTURE=1 uv run --no-sync pytest tests/backend/architectures/test_capabilities_fixture.py + ``` -**Backend Invocation** — `invokeai/app/invocations/[newmodel]_controlnet.py`: + This rewrites `__fixtures__/architectureCapabilities.json`. The mock backend and the unit tests read it. -```python title="invokeai/app/invocations/[newmodel]_controlnet.py" -@invocation("newmodel_controlnet", ...) -class NewModelControlNetInvocation(BaseInvocation): - image: ImageField = InputField() - controlnet_model: ControlNetField = InputField() - control_weight: float = InputField(default=1.0) +2. **Mark the base as buildable** - def invoke(self, context) -> ControlNetOutput: - # Compute ControlNet conditioning - pass -``` + - Add it to `KnownGenerationModelBase` in `contracts.ts`. + - Add it to `SUPPORTED_GENERATE_BASES` in `supportedBases.ts`. + - Update the pinned list in `supportedBases.test.ts`. -**Frontend Graph:** +3. **Write the graph builder** (`graph.ts`) -```typescript title="buildNewModelGraph.ts" -const { controlNets } = await addControlNets({ g, manager, denoise }); -``` + Add `buildNewModelGraph` and register it in `GRAPH_BUILDERS`, which `satisfies Record`, so the compiler reports a missing entry. `buildZImageGraph` and `buildKrea2Graph` are good templates: -### IP-Adapter / Reference Images + ```ts title="invokeai/frontend/webv2/src/features/generation/core/graph.ts" + const buildNewModelGraph = ( + settings: GenerateSettings, + model: MainModelConfig, + outputIsIntermediate: boolean, + projectSettings: GenerationProjectSettings + ): BackendGraphContract => { + const vaeModel = getCompatibleVae(settings, model); + const graph: BackendGraphContract = { edges: [], id: createId('new_model_graph'), nodes: {} }; + const { negativePrompt, positivePrompt, seed } = addPromptAndSeedNodes(graph); + const useCfg = settings.cfgScale > 1; -**Backend Invocation** — `invokeai/app/invocations/[newmodel]_ip_adapter.py`: + const modelLoader = addNode(graph, { + id: 'model_loader', + model, + type: 'new_model_model_loader', + vae_model: vaeModel ?? undefined, + }); + const posCond = addNode(graph, { id: 'pos_cond', type: 'new_model_text_encoder' }); + const posCondCollect = addNode(graph, { id: 'pos_cond_collect', type: 'collect' }); + const size = getDenoiseSize(settings, model); + const denoise = addNode(graph, { + cfg_scale: settings.cfgScale, + denoising_end: 1, + denoising_start: 0, + height: size.height, + id: 'denoise_latents', + steps: settings.steps, + type: 'new_model_denoise', + width: size.width, + }); + // Creates the `canvas_output` node. + const output = addImageOutputNode(graph, 'new_model_l2i', outputIsIntermediate); + + addEdge(graph, modelLoader, 'transformer', denoise, 'transformer'); + addEdge(graph, modelLoader, 'qwen3_vl_encoder', posCond, 'qwen3_vl_encoder'); + addEdge(graph, positivePrompt, 'value', posCond, 'prompt'); + addEdge(graph, posCond, 'conditioning', posCondCollect, 'item'); + addEdge(graph, posCondCollect, 'collection', denoise, 'positive_conditioning'); + // …the same chain for `negative_prompt` → `neg_cond` → `neg_cond_collect` when useCfg… + addEdge(graph, seed, 'value', denoise, 'seed'); + addEdge(graph, denoise, 'latents', output, 'latents'); + addEdge(graph, modelLoader, 'vae', output, 'vae'); + addMetadata(graph, output, settings, model, 'new_model_txt2img', projectSettings, { + vae: vaeModel ?? undefined, + }); + return graph; + }; + ``` -```python title="invokeai/app/invocations/[newmodel]_ip_adapter.py" -@invocation("newmodel_ip_adapter", ...) -class NewModelIPAdapterInvocation(BaseInvocation): - image: ImageField = InputField() - ip_adapter_model: IPAdapterField = InputField() - weight: float = InputField(default=1.0) -``` + The builder produces the **txt2img** graph only; the canvas grafts img2img, inpaint and outpaint onto it. That depends on these conventions: + - Node ids `positive_prompt`, `negative_prompt`, `seed`, `model_loader`, `pos_cond`, `neg_cond`, `pos_cond_collect`, `neg_cond_collect` and `denoise_latents`. + - An output node `canvas_output` whose `vae` input has an edge (`requireVaeSource`). + - `addDecodeOutput` if the architecture supports PiD decoding, otherwise `addImageOutputNode`. -**Frontend Graph:** + Validation that only one architecture needs belongs in `getModelFamilyValidationReasons` (`baseGenerationPolicies.ts`). -```typescript title="buildNewModelGraph.ts" -const { ipAdapters } = await addIPAdapters({ g, manager, denoise }); -``` +4. **Component pickers** (encoder, VAE, Diffusers component source) -### LoRA Support + - Add a `case` to `getBaseComponentSectionPolicy` in `baseGenerationPolicies.ts`. Its `default` returns an empty policy silently. + - Add encoder filters in `componentCompatibility.ts`. If a main model can bring its own components, update `isBundledMainForBase`. VAE acceptance comes from the backend. + - A **new** settings key must be added in all of these places: + - `GenerateSettings` (`types.ts`) + - `GenerateComponentValueKey`, `COMPONENT_SETTING_LABELS`, `getComponentPolicyContext` and `getDefaultGenerateSettings` (`baseGenerationPolicies.ts`) + - `normalizeGenerateSettings` and `cloneGenerateWidgetValues` (`settings.ts`) -**Backend Config** — `invokeai/backend/model_manager/configs/lora.py`: +5. **Canvas** -```python title="invokeai/backend/model_manager/configs/lora.py" -@ModelConfigFactory.register -class LoRA_LyCORIS_NewModel_Config(LoRA_LyCORIS_Base): - base: Literal[BaseModelType.NewModel] = BaseModelType.NewModel -``` + - Map the encode node in `CANVAS_I2L_NODE_TYPES` (`canvas/compileCanvasGraph.ts`). The map is `Partial`, so a missing entry only fails at runtime. + - If the architecture uses the `strength^0.2` denoising-start curve, add it to `canvasDenoisingStart`. + - Add a case to `BASE_CASES` in `canvas/compileCanvasGraph.test.ts`. -**Backend Model Loader Integration:** +6. **Recall** (`src/workbench/image-actions/`) -```python title="invokeai/app/invocations/[newmodel]_model_loader.py" -class NewModelModelLoaderOutput(BaseInvocationOutput): - transformer: TransformerField # TransformerField already contains loras: list[LoRAField] -``` + - In `imageRecall.ts`, add the component keys the builder writes into metadata to `hasComponentModels` and to the component patch. + - Add the base to `GUIDANCE_BASES` in `recallParameters.ts` if it takes the shared guidance slider. -**Frontend Graph:** + Nothing checks recall against what the builder writes, so test it. -```typescript title="buildNewModelGraph.ts" -const { loras } = await addLoRAs({ g, manager, denoise, modelLoader }); -``` +7. **Model registry and labels** -### Scheduler UI - -**Frontend Component** — `invokeai/frontend/web/src/features/parameters/components/NewModelScheduler.tsx`: - -```typescript title="invokeai/frontend/web/src/features/parameters/components/NewModelScheduler.tsx" -export const NewModelSchedulerSelect = () => { - const dispatch = useAppDispatch(); - const scheduler = useAppSelector(selectNewmodelScheduler); - - return ( - {optionsList.map((option: string) => ( ))} diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowIntegration/WorkflowFormPreview.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/CanvasWorkflowIntegration/WorkflowFormPreview.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowIntegration/WorkflowFormPreview.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/CanvasWorkflowIntegration/WorkflowFormPreview.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowIntegration/useCanvasWorkflowIntegrationExecute.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/CanvasWorkflowIntegration/useCanvasWorkflowIntegrationExecute.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowIntegration/useCanvasWorkflowIntegrationExecute.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/CanvasWorkflowIntegration/useCanvasWorkflowIntegrationExecute.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowIntegration/useFilteredWorkflows.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/CanvasWorkflowIntegration/useFilteredWorkflows.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowIntegration/useFilteredWorkflows.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/CanvasWorkflowIntegration/useFilteredWorkflows.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowIntegration/workflowHasImageField.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/CanvasWorkflowIntegration/workflowHasImageField.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/CanvasWorkflowIntegration/workflowHasImageField.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/CanvasWorkflowIntegration/workflowHasImageField.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayer.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayer.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayer.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerBadges.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerBadges.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerBadges.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerBadges.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapter.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapter.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapter.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapter.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterControlMode.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterControlMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterControlMode.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterControlMode.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerEntityList.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerEntityList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerEntityList.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerEntityList.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItems.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItems.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItems.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItems.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsConvertToSubMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsConvertToSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsConvertToSubMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsConvertToSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsCopyToSubMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsCopyToSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsCopyToSubMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsCopyToSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsTransparencyEffect.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsTransparencyEffect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsTransparencyEffect.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerMenuItemsTransparencyEffect.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerSettings.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerSettings.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerSettings.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerSettingsEmptyState.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerSettingsEmptyState.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerSettingsEmptyState.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ControlLayer/ControlLayerSettingsEmptyState.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/Filter.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/Filter.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/Filter.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterAdjustImage.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterAdjustImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterAdjustImage.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterAdjustImage.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterBlur.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterBlur.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterBlur.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterBlur.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterCannyEdgeDetection.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterCannyEdgeDetection.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterCannyEdgeDetection.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterCannyEdgeDetection.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterColorMap.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterColorMap.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterColorMap.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterColorMap.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterContentShuffle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterContentShuffle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterContentShuffle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterContentShuffle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterDWOpenposeDetection.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterDWOpenposeDetection.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterDWOpenposeDetection.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterDWOpenposeDetection.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterDepthAnythingDepthEstimation.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterDepthAnythingDepthEstimation.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterDepthAnythingDepthEstimation.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterDepthAnythingDepthEstimation.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterHEDEdgeDetection.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterHEDEdgeDetection.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterHEDEdgeDetection.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterHEDEdgeDetection.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterLineartEdgeDetection.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterLineartEdgeDetection.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterLineartEdgeDetection.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterLineartEdgeDetection.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterMLSDDetection.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterMLSDDetection.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterMLSDDetection.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterMLSDDetection.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterMediaPipeFaceDetection.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterMediaPipeFaceDetection.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterMediaPipeFaceDetection.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterMediaPipeFaceDetection.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterNoise.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterNoise.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterNoise.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterNoise.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterPiDiNetEdgeDetection.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterPiDiNetEdgeDetection.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterPiDiNetEdgeDetection.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterPiDiNetEdgeDetection.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterSettings.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterSettings.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterSettings.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterSpandrel.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterSpandrel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterSpandrel.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterSpandrel.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterTypeSelect.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterTypeSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/FilterTypeSelect.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/FilterTypeSelect.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Filters/types.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/Filters/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Filters/types.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/Filters/types.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUD.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/HUD/CanvasHUD.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUD.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/HUD/CanvasHUD.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItem.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/HUD/CanvasHUDItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItem.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/HUD/CanvasHUDItem.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItemBbox.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/HUD/CanvasHUDItemBbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItemBbox.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/HUD/CanvasHUDItemBbox.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItemScaledBbox.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/HUD/CanvasHUDItemScaledBbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/HUD/CanvasHUDItemScaledBbox.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/HUD/CanvasHUDItemScaledBbox.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMask.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMask.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMask.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMask.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskDeleteModifierButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskDeleteModifierButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskDeleteModifierButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskDeleteModifierButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskDenoiseLimitSlider.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskDenoiseLimitSlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskDenoiseLimitSlider.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskDenoiseLimitSlider.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskList.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskList.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskList.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItems.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItems.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItems.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItems.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsAddModifiers.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsAddModifiers.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsAddModifiers.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsAddModifiers.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsConvertToSubMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsConvertToSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsConvertToSubMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsConvertToSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsCopyToSubMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsCopyToSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsCopyToSubMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsCopyToSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsExtractMaskedArea.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsExtractMaskedArea.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsExtractMaskedArea.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskMenuItemsExtractMaskedArea.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskNoiseSlider.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskNoiseSlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskNoiseSlider.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskNoiseSlider.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskSettings.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InpaintMask/InpaintMaskSettings.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InpaintMask/InpaintMaskSettings.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/InvokeCanvasComponent.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/InvokeCanvasComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/InvokeCanvasComponent.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/InvokeCanvasComponent.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/LoadCanvasProjectConfirmationAlertDialog.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/LoadCanvasProjectConfirmationAlertDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/LoadCanvasProjectConfirmationAlertDialog.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/LoadCanvasProjectConfirmationAlertDialog.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/ParamDenoisingStrength.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/ParamDenoisingStrength.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayer.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayer.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayer.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerAdjustmentsPanel.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerAdjustmentsPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerAdjustmentsPanel.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerAdjustmentsPanel.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesAdjustmentsEditor.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesAdjustmentsEditor.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesAdjustmentsEditor.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesAdjustmentsEditor.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesAdjustmentsGraph.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesAdjustmentsGraph.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesAdjustmentsGraph.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerCurvesAdjustmentsGraph.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerEntityList.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerEntityList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerEntityList.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerEntityList.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerExportPSDButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerExportPSDButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerExportPSDButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerExportPSDButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerIsTransparencyLockedToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerIsTransparencyLockedToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerIsTransparencyLockedToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerIsTransparencyLockedToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItems.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItems.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItems.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItems.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsAdjustments.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsAdjustments.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsAdjustments.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsAdjustments.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsBooleanSubMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsBooleanSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsBooleanSubMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsBooleanSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsConvertToSubMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsConvertToSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsConvertToSubMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsConvertToSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsCopyToSubMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsCopyToSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsCopyToSubMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerMenuItemsCopyToSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerSimpleAdjustmentsEditor.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerSimpleAdjustmentsEditor.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerSimpleAdjustmentsEditor.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RasterLayer/RasterLayerSimpleAdjustmentsEditor.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/IPAdapterMenuItemPullBbox.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/IPAdapterMenuItemPullBbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/IPAdapterMenuItemPullBbox.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/IPAdapterMenuItemPullBbox.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/IPAdapterMenuItems.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/IPAdapterMenuItems.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/IPAdapterMenuItems.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/IPAdapterMenuItems.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/IPAdapterMethod.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/IPAdapterMethod.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/IPAdapterMethod.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/IPAdapterMethod.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/Krea2StyleStrength.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/Krea2StyleStrength.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/Krea2StyleStrength.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/Krea2StyleStrength.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageHeader.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageHeader.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageImage.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageImage.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageImage.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageList.helpers.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageList.helpers.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageList.helpers.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageList.helpers.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageList.helpers.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageList.helpers.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageList.helpers.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageList.helpers.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageList.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageList.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageList.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageModel.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageModel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageModel.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageModel.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageNoImageState.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageNoImageState.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageNoImageState.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageNoImageState.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageNoImageStateWithCanvasOptions.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageNoImageStateWithCanvasOptions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageNoImageStateWithCanvasOptions.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageNoImageStateWithCanvasOptions.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImagePreview.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImagePreview.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageSettings.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageSettings.tsx similarity index 97% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageSettings.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageSettings.tsx index 8e924bee3aa..ec59e7696c2 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageSettings.tsx +++ b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageSettings.tsx @@ -41,6 +41,7 @@ import { isFLUXReduxConfig, isIPAdapterConfig, isKrea2ReferenceImageConfig, + isMiniMaxH3ReferenceImageConfig, isQwenImageReferenceImageConfig, isWanReferenceImageConfig, } from 'features/controlLayers/store/types'; @@ -140,12 +141,13 @@ const RefImageSettingsContent = memo(() => { const isFLUX = useAppSelector(selectIsFLUX); const isExternalModel = !!mainModelConfig && isExternalApiModelConfig(mainModelConfig); - // FLUX.2 Klein, Qwen Image Edit, Wan 2.2, Krea-2 and external API models do not require a ref image + // FLUX.2 Klein, Qwen Image Edit, Wan 2.2, Krea-2, MiniMax H3 and external API models do not require a ref image // model selection. const showModelSelector = !isFlux2ReferenceImageConfig(config) && !isQwenImageReferenceImageConfig(config) && !isWanReferenceImageConfig(config) && + !isMiniMaxH3ReferenceImageConfig(config) && !isKrea2ReferenceImageConfig(config) && !isExternalModel; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageWarningTooltipContent.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageWarningTooltipContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageWarningTooltipContent.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/RefImageWarningTooltipContent.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/useRefImageDnd.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/useRefImageDnd.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/useRefImageDnd.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/useRefImageDnd.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/useRefImageEntity.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/useRefImageEntity.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RefImage/useRefImageEntity.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/RefImage/useRefImageEntity.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidance.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidance.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidance.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidance.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceAddPromptsIPAdapterButtons.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceAddPromptsIPAdapterButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceAddPromptsIPAdapterButtons.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceAddPromptsIPAdapterButtons.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceEntityList.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceEntityList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceEntityList.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceEntityList.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettings.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettings.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettings.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettingsEmptyState.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettingsEmptyState.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettingsEmptyState.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettingsEmptyState.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapters.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapters.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapters.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapters.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItems.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAddPromptsAndIPAdapter.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAddPromptsAndIPAdapter.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAddPromptsAndIPAdapter.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAddPromptsAndIPAdapter.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAutoNegative.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAutoNegative.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAutoNegative.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsAutoNegative.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsConvertToSubMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsConvertToSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsConvertToSubMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsConvertToSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsCopyToSubMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsCopyToSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsCopyToSubMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceMenuItemsCopyToSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceNegativePrompt.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceNegativePrompt.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceNegativePrompt.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceNegativePrompt.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidancePositivePrompt.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidancePositivePrompt.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidancePositivePrompt.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidancePositivePrompt.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceRefImageImage.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceRefImageImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceRefImageImage.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceRefImageImage.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalReferenceImageModel.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalReferenceImageModel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalReferenceImageModel.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/RegionalGuidance/RegionalReferenceImageModel.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SaveCanvasProjectDialog.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SaveCanvasProjectDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SaveCanvasProjectDialog.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SaveCanvasProjectDialog.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObject.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObject.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectActionButtons.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectActionButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectActionButtons.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectActionButtons.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInfoTooltip.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectInfoTooltip.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInfoTooltip.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectInfoTooltip.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInputTypeButtons.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectInputTypeButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInputTypeButtons.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectInputTypeButtons.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInvert.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectInvert.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInvert.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectInvert.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectModel.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectModel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectModel.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectModel.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectPointType.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectPointType.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectPointType.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectPointType.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectPrompt.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectPrompt.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectPrompt.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectPrompt.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectSaveAsMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectSaveAsMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectSaveAsMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/SelectObject/SelectObjectSaveAsMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsBboxOverlaySwitch.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsBboxOverlaySwitch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsBboxOverlaySwitch.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsBboxOverlaySwitch.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearHistoryButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsClearHistoryButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearHistoryButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsClearHistoryButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsGridSize.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsGridSize.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsGridSize.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsGridSize.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsIsolatedLayerPreviewSwitch.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsIsolatedLayerPreviewSwitch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsIsolatedLayerPreviewSwitch.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsIsolatedLayerPreviewSwitch.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsIsolatedStagingPreviewSwitch.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsIsolatedStagingPreviewSwitch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsIsolatedStagingPreviewSwitch.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsIsolatedStagingPreviewSwitch.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsLogDebugInfo.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsLogDebugInfo.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsLogDebugInfo.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsLogDebugInfo.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsOutputOnlyMaskedRegionsCheckbox.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsOutputOnlyMaskedRegionsCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsOutputOnlyMaskedRegionsCheckbox.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsOutputOnlyMaskedRegionsCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPreserveMaskCheckbox.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsPreserveMaskCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPreserveMaskCheckbox.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsPreserveMaskCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPressureSensitivity.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsPressureSensitivity.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPressureSensitivity.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsPressureSensitivity.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRuleOfThirdsGuideSwitch.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsRuleOfThirdsGuideSwitch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRuleOfThirdsGuideSwitch.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsRuleOfThirdsGuideSwitch.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsSaveAllImagesToGalleryCheckbox.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsSaveAllImagesToGalleryCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsSaveAllImagesToGalleryCheckbox.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsSaveAllImagesToGalleryCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsShowHUDSwitch.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsShowHUDSwitch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsShowHUDSwitch.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsShowHUDSwitch.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsShowProgressOnCanvasSwitch.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsShowProgressOnCanvasSwitch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsShowProgressOnCanvasSwitch.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Settings/CanvasSettingsShowProgressOnCanvasSwitch.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemCircularProgress.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemCircularProgress.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemCircularProgress.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemCircularProgress.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemNumber.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemNumber.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemNumber.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemNumber.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemPreviewMini.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemPreviewMini.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemPreviewMini.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemPreviewMini.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemProgressImage.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemProgressImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemProgressImage.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemProgressImage.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemStatusLabel.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemStatusLabel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemStatusLabel.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/QueueItemStatusLabel.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaAutoSwitchButtons.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaAutoSwitchButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaAutoSwitchButtons.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaAutoSwitchButtons.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaItemsList.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaItemsList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaItemsList.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaItemsList.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbar.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbar.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbar.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbar.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarAcceptButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarAcceptButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarAcceptButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarAcceptButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardAllButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardAllButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardAllButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardAllButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardSelectedButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardSelectedButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardSelectedButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardSelectedButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarImageCountButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarImageCountButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarImageCountButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarImageCountButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarMenu.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarMenu.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarMenu.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarMenuNewLayerFromImage.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarMenuNewLayerFromImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarMenuNewLayerFromImage.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarMenuNewLayerFromImage.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarNextButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarNextButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarNextButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarNextButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarPrevButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarPrevButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarPrevButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarPrevButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarSaveSelectedToGalleryButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarSaveSelectedToGalleryButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarSaveSelectedToGalleryButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarSaveSelectedToGalleryButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarToggleShowResultsButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarToggleShowResultsButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarToggleShowResultsButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarToggleShowResultsButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarToggleThumbnailsButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarToggleThumbnailsButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarToggleThumbnailsButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/StagingAreaToolbarToggleThumbnailsButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/__mocks__/mockStagingAreaApp.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/__mocks__/mockStagingAreaApp.ts similarity index 90% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/__mocks__/mockStagingAreaApp.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/__mocks__/mockStagingAreaApp.ts index e0b56d5a439..0318aa4b87b 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/__mocks__/mockStagingAreaApp.ts +++ b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/__mocks__/mockStagingAreaApp.ts @@ -130,8 +130,13 @@ export const createMockImageDTO = (overrides: Partial = {}): ImageDTO ...overrides, }); +// `invocation` is the union of every registered invocation type — hundreds of members, and it +// grows with each new node. Carrying it into this parameter type makes `merge` distribute the +// union across the result and TypeScript gives up with TS2590 ("union type too complex"). It is +// omitted here rather than made optional: an intersection that merely mentions the union is +// enough to blow the limit, and no caller overrides `invocation`. export const createMockProgressEvent = ( - overrides: PartialDeep = {} + overrides: PartialDeep> = {} ): S['InvocationProgressEvent'] => merge( { @@ -165,6 +170,7 @@ export const createMockQueueItemStatusChangedEvent = ( status: 'completed', error_type: null, error_message: null, + error_traceback: null, } as S['QueueItemStatusChangedEvent'], overrides ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/context.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/context.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/context.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/context.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/shared.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/shared.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/shared.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/shared.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/state.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/state.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/state.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/state.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/state.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/state.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/StagingArea/state.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/StagingArea/state.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Text/CanvasTextOverlay.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Text/CanvasTextOverlay.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Text/CanvasTextOverlay.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Text/CanvasTextOverlay.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Text/CanvasTextOverlay.utils.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/Text/CanvasTextOverlay.utils.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Text/CanvasTextOverlay.utils.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/Text/CanvasTextOverlay.utils.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Text/CanvasTextOverlay.utils.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/Text/CanvasTextOverlay.utils.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Text/CanvasTextOverlay.utils.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/Text/CanvasTextOverlay.utils.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Text/TextToolOptions.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Text/TextToolOptions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Text/TextToolOptions.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Text/TextToolOptions.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/GradientIcons.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/GradientIcons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/GradientIcons.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/GradientIcons.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/PinnedFillColorPickerOverlay.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/PinnedFillColorPickerOverlay.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/PinnedFillColorPickerOverlay.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/PinnedFillColorPickerOverlay.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolBboxButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolBboxButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolBrushButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolBrushButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolChooser.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolChooser.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolChooser.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolChooser.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolEraserButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolEraserButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolGradientButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolGradientButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientClipToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolGradientClipToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientClipToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolGradientClipToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientModeToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolGradientModeToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientModeToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolGradientModeToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolLassoButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolLassoButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolLassoButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolLassoButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolLassoModeToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolLassoModeToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolLassoModeToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolLassoModeToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolMoveButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolMoveButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolOptionsRowContainer.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolOptionsRowContainer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolOptionsRowContainer.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolOptionsRowContainer.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolShapeTypeToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolShapeTypeToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolShapeTypeToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolShapeTypeToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolShapesButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolShapesButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolShapesButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolShapesButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolTextButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolTextButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolTextButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolTextButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolViewButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolViewButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolWidthPicker.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolWidthPicker.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolWidthPicker.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/ToolWidthPicker.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/hooks.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/hooks.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/hooks.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/hooks.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/toolOptionIconSize.ts b/invokeai/frontend/webv1/src/features/controlLayers/components/Tool/toolOptionIconSize.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Tool/toolOptionIconSize.ts rename to invokeai/frontend/webv1/src/features/controlLayers/components/Tool/toolOptionIconSize.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbar.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbar.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbar.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbar.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarNewSessionMenuButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarNewSessionMenuButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarNewSessionMenuButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarNewSessionMenuButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarProjectMenuButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarProjectMenuButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarProjectMenuButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarProjectMenuButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarRedoButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarRedoButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarRedoButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarRedoButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarSaveToGalleryButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarSaveToGalleryButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarSaveToGalleryButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarSaveToGalleryButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarScale.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarScale.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarScale.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarScale.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarSnapshotMenuButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarSnapshotMenuButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarSnapshotMenuButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarSnapshotMenuButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarUndoButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarUndoButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarUndoButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Toolbar/CanvasToolbarUndoButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Transform/Transform.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Transform/Transform.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformFitToBboxButtons.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Transform/TransformFitToBboxButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformFitToBboxButtons.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Transform/TransformFitToBboxButtons.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformSmoothingControls.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/Transform/TransformSmoothingControls.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformSmoothingControls.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/Transform/TransformSmoothingControls.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/BeginEndStepPct.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/BeginEndStepPct.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/BeginEndStepPct.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/BeginEndStepPct.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityAddOfTypeButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityAddOfTypeButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityAddOfTypeButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityAddOfTypeButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityDeleteButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityDeleteButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityDeleteButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityDeleteButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityEnabledToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityEnabledToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityEnabledToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityEnabledToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityHeader.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityHeader.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityHeader.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityHeader.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityHeaderCommonActions.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityHeaderCommonActions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityHeaderCommonActions.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityHeaderCommonActions.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityHeaderWarnings.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityHeaderWarnings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityHeaderWarnings.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityHeaderWarnings.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityIsBookmarkedForQuickSwitchToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityIsBookmarkedForQuickSwitchToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityIsBookmarkedForQuickSwitchToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityIsBookmarkedForQuickSwitchToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityIsLockedToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityIsLockedToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityIsLockedToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityIsLockedToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsArrange.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsArrange.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsArrange.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsArrange.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsCopyToClipboard.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsCopyToClipboard.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsCopyToClipboard.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsCopyToClipboard.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsCropToBbox.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsCropToBbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsCropToBbox.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsCropToBbox.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsDelete.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsDelete.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsDelete.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsDelete.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsDuplicate.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsFilter.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsFilter.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsFilter.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsFilter.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsMergeDown.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsMergeDown.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsMergeDown.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsMergeDown.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsRunWorkflow.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsRunWorkflow.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsRunWorkflow.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsRunWorkflow.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsSave.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsSave.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsSave.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsSave.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsSelectObject.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsSelectObject.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsSelectObject.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsSelectObject.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsTransform.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsTransform.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMenuItemsTransform.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMenuItemsTransform.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMergeVisibleButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMergeVisibleButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMergeVisibleButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityMergeVisibleButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityPreviewImage.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityPreviewImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityPreviewImage.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityPreviewImage.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntitySettingsWrapper.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntitySettingsWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntitySettingsWrapper.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntitySettingsWrapper.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityTitle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityTitle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityTitle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityTitle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityTitleEdit.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityTitleEdit.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityTitleEdit.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityTitleEdit.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityTypeIsHiddenToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityTypeIsHiddenToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityTypeIsHiddenToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasEntityTypeIsHiddenToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasNonRasterLayersIsHiddenToggle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasNonRasterLayersIsHiddenToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/CanvasNonRasterLayersIsHiddenToggle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/CanvasNonRasterLayersIsHiddenToggle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/FLUXReduxImageInfluence.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/FLUXReduxImageInfluence.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/FLUXReduxImageInfluence.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/FLUXReduxImageInfluence.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/IPAdapterCLIPVisionModel.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/IPAdapterCLIPVisionModel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/IPAdapterCLIPVisionModel.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/IPAdapterCLIPVisionModel.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/MaskFillStyle.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/MaskFillStyle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/MaskFillStyle.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/MaskFillStyle.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/PullBboxIntoRefImageIconButton.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/PullBboxIntoRefImageIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/PullBboxIntoRefImageIconButton.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/PullBboxIntoRefImageIconButton.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/Weight.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/Weight.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/Weight.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/Weight.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/icons.tsx b/invokeai/frontend/webv1/src/features/controlLayers/components/common/icons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/components/common/icons.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/components/common/icons.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/contexts/CanvasEntityStateGate.tsx b/invokeai/frontend/webv1/src/features/controlLayers/contexts/CanvasEntityStateGate.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/contexts/CanvasEntityStateGate.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/contexts/CanvasEntityStateGate.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/contexts/CanvasManagerProviderGate.tsx b/invokeai/frontend/webv1/src/features/controlLayers/contexts/CanvasManagerProviderGate.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/contexts/CanvasManagerProviderGate.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/contexts/CanvasManagerProviderGate.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/contexts/EntityAdapterContext.tsx b/invokeai/frontend/webv1/src/features/controlLayers/contexts/EntityAdapterContext.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/contexts/EntityAdapterContext.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/contexts/EntityAdapterContext.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/contexts/EntityIdentifierContext.ts b/invokeai/frontend/webv1/src/features/controlLayers/contexts/EntityIdentifierContext.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/contexts/EntityIdentifierContext.ts rename to invokeai/frontend/webv1/src/features/controlLayers/contexts/EntityIdentifierContext.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/contexts/RefImageIdContext.ts b/invokeai/frontend/webv1/src/features/controlLayers/contexts/RefImageIdContext.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/contexts/RefImageIdContext.ts rename to invokeai/frontend/webv1/src/features/controlLayers/contexts/RefImageIdContext.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/addLayerHooks.ts similarity index 97% rename from invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/addLayerHooks.ts index 1997fd66881..2c10a4d161a 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts +++ b/invokeai/frontend/webv1/src/features/controlLayers/hooks/addLayerHooks.ts @@ -30,6 +30,7 @@ import type { FluxKontextReferenceImageConfig, IPAdapterConfig, Krea2ReferenceImageConfig, + MiniMaxH3ReferenceImageConfig, QwenImageReferenceImageConfig, RegionalGuidanceIPAdapterConfig, T2IAdapterConfig, @@ -41,6 +42,7 @@ import { initialFluxKontextReferenceImage, initialIPAdapter, initialKrea2ReferenceImage, + initialMiniMaxH3ReferenceImage, initialQwenImageReferenceImage, initialRegionalGuidanceIPAdapter, initialT2IAdapter, @@ -90,7 +92,8 @@ export const getDefaultRefImageConfig = ( | Flux2ReferenceImageConfig | QwenImageReferenceImageConfig | WanReferenceImageConfig - | Krea2ReferenceImageConfig => { + | Krea2ReferenceImageConfig + | MiniMaxH3ReferenceImageConfig => { const state = getState(); const mainModelConfig = selectMainModelConfig(state); @@ -113,6 +116,11 @@ export const getDefaultRefImageConfig = ( return deepClone(initialWanReferenceImage); } + // MiniMax H3 first-frame conditioning uses the main model's own VAE + vision context + if (base === 'minimax-h3') { + return deepClone(initialMiniMaxH3ReferenceImage); + } + // Krea-2 transfers style training-free via shared-KV reference attention - no adapter model needed if (base === 'krea-2') { return deepClone(initialKrea2ReferenceImage); diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/canvasMergeHotkeyUtils.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/canvasMergeHotkeyUtils.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/canvasMergeHotkeyUtils.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/canvasMergeHotkeyUtils.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/canvasMergeHotkeyUtils.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/canvasMergeHotkeyUtils.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/canvasMergeHotkeyUtils.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/canvasMergeHotkeyUtils.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/copyHooks.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/copyHooks.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/copyHooks.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/copyHooks.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/saveCanvasHooks.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/saveCanvasHooks.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/saveCanvasHooks.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/saveCanvasHooks.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useAutoFitBBoxToMasks.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useAutoFitBBoxToMasks.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useAutoFitBBoxToMasks.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useAutoFitBBoxToMasks.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasDeleteLayerHotkey.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasEntityQuickSwitchHotkey.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasEntityQuickSwitchHotkey.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasEntityQuickSwitchHotkey.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasEntityQuickSwitchHotkey.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasFilterHotkey.tsx b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasFilterHotkey.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasFilterHotkey.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasFilterHotkey.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasInvertMaskHotkey.tsx b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasInvertMaskHotkey.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasInvertMaskHotkey.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasInvertMaskHotkey.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasIsBusy.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasIsBusy.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasIsBusy.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasIsBusy.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasMergeDownHotkey.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasMergeDownHotkey.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasMergeDownHotkey.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasMergeDownHotkey.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasMergeVisibleHotkey.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasMergeVisibleHotkey.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasMergeVisibleHotkey.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasMergeVisibleHotkey.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasProjectLoad.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasProjectLoad.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasProjectLoad.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasProjectLoad.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasProjectSave.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasProjectSave.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasProjectSave.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasProjectSave.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasResetLayerHotkey.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasSelectBboxToolHotkey.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasSelectBboxToolHotkey.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasSelectBboxToolHotkey.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasSelectBboxToolHotkey.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasSnapshots.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasSnapshots.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasSnapshots.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasSnapshots.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasToggleBboxHotkey.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasToggleBboxHotkey.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasToggleBboxHotkey.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasToggleBboxHotkey.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasToggleNonRasterLayersHotkey.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasToggleNonRasterLayersHotkey.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasToggleNonRasterLayersHotkey.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasToggleNonRasterLayersHotkey.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasTransformHotkey.tsx b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasTransformHotkey.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasTransformHotkey.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasTransformHotkey.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useCanvasUndoRedoHotkeys.tsx diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useDeferredModelLoadingInvocationProgressMessage.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useDeferredModelLoadingInvocationProgressMessage.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useDeferredModelLoadingInvocationProgressMessage.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useDeferredModelLoadingInvocationProgressMessage.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityFilter.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityFilter.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityFilter.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityFilter.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsBookmarkedForQuickSwitch.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsBookmarkedForQuickSwitch.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsBookmarkedForQuickSwitch.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsBookmarkedForQuickSwitch.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsEmpty.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsEmpty.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsEmpty.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsEmpty.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsEnabled.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsEnabled.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsEnabled.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsEnabled.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsLocked.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsLocked.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsLocked.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsLocked.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsSelected.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsSelected.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityIsSelected.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityIsSelected.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntitySegmentAnything.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntitySegmentAnything.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntitySegmentAnything.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntitySegmentAnything.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTitle.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTitle.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTitle.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTitle.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTransform.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTransform.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTransform.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeCount.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeCount.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeCount.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeCount.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeInformationalPopover.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeInformationalPopover.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeInformationalPopover.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeInformationalPopover.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeIsHidden.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeIsHidden.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeIsHidden.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeIsHidden.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeString.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeString.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeString.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeString.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeTitle.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeTitle.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeTitle.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useEntityTypeTitle.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useExportCanvasToPSD.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useExportCanvasToPSD.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useExportCanvasToPSD.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useExportCanvasToPSD.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useInvertMask.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useInvertMask.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useInvertMask.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useInvertMask.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useInvokeCanvas.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useInvokeCanvas.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useInvokeCanvas.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useInvokeCanvas.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts similarity index 94% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts index 4c4617f1b81..e77777ddb8c 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts +++ b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts @@ -17,7 +17,7 @@ export const useIsEntityTypeEnabled = (entityType: CanvasEntityType) => { const isExternal = useAppSelector(selectIsExternal); // TODO(psyche): consider using a constant to define which entity types are supported by which model, - // see invokeai/frontend/web/src/features/modelManagerV2/models.ts for ref + // see invokeai/frontend/webv1/src/features/modelManagerV2/models.ts for ref const isEntityTypeEnabled = useMemo(() => { switch (entityType) { case 'regional_guidance': diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useIsUncommittedCanvasTextSessionActive.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useIsUncommittedCanvasTextSessionActive.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useIsUncommittedCanvasTextSessionActive.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useIsUncommittedCanvasTextSessionActive.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useNextPrevEntity.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useNextPrevEntity.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useNextPrevEntity.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useNextPrevEntity.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useNextRenderableEntityIdentifier.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useNextRenderableEntityIdentifier.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useNextRenderableEntityIdentifier.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useNextRenderableEntityIdentifier.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useSaveLayerToAssets.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useSaveLayerToAssets.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useSaveLayerToAssets.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useSaveLayerToAssets.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useVisibleEntityCountByType.ts b/invokeai/frontend/webv1/src/features/controlLayers/hooks/useVisibleEntityCountByType.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useVisibleEntityCountByType.ts rename to invokeai/frontend/webv1/src/features/controlLayers/hooks/useVisibleEntityCountByType.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasBackgroundModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasBackgroundModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasBackgroundModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasBackgroundModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasCacheModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasCacheModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasCacheModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasCacheModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasCompositionGuideModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasCompositionGuideModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasCompositionGuideModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasCompositionGuideModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasCompositorModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasCompositorModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasCompositorModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasCompositorModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterBase.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterBase.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterBase.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterBase.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterControlLayer.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterControlLayer.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterControlLayer.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterControlLayer.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterInpaintMask.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterInpaintMask.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterInpaintMask.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterInpaintMask.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRasterLayer.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRasterLayer.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRasterLayer.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRasterLayer.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRegionalGuidance.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRegionalGuidance.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRegionalGuidance.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRegionalGuidance.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityBufferObjectRenderer.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityBufferObjectRenderer.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityBufferObjectRenderer.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityBufferObjectRenderer.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityFilterer.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityFilterer.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityFilterer.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityFilterer.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityTransformer.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityTransformer.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityTransformer.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/CanvasEntityTransformer.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/types.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/types.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntity/types.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityRendererModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntityRendererModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityRendererModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasEntityRendererModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasManager.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasManager.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasModuleBase.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasModuleBase.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasModuleBase.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasModuleBase.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectBrushLine.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectBrushLine.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectBrushLine.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectBrushLine.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectBrushLineWithPressure.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectBrushLineWithPressure.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectBrushLineWithPressure.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectBrushLineWithPressure.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectEraserLine.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectEraserLine.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectEraserLine.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectEraserLine.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectEraserLineWithPressure.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectEraserLineWithPressure.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectEraserLineWithPressure.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectEraserLineWithPressure.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectGradient.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectGradient.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectGradient.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectGradient.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectImage.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectImage.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectImage.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectImage.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectLasso.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectLasso.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectLasso.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectLasso.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectOval.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectOval.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectOval.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectOval.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectPolygon.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectPolygon.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectPolygon.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectPolygon.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectRect.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectRect.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/CanvasObjectRect.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/CanvasObjectRect.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/needsPixelBbox.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/needsPixelBbox.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/needsPixelBbox.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/needsPixelBbox.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/needsPixelBbox.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/needsPixelBbox.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/needsPixelBbox.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/needsPixelBbox.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/types.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasObject/types.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasObject/types.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasSegmentAnythingModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasSegmentAnythingModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasSegmentAnythingModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasSegmentAnythingModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStageModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasStageModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasStageModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasStageModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasStagingAreaModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasStagingAreaModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApiModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasStateApiModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApiModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasStateApiModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasBboxToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasBboxToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasBboxToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasBboxToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasBrushToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasBrushToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasBrushToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasBrushToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasColorPickerToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasColorPickerToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasColorPickerToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasColorPickerToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasEraserToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasEraserToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasEraserToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasEraserToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasGradientToolModule.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasGradientToolModule.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasGradientToolModule.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasGradientToolModule.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasGradientToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasGradientToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasGradientToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasGradientToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasLassoToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasLassoToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasLassoToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasLassoToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasMoveToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasMoveToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasMoveToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasMoveToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasShapeToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasShapeToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasShapeToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasShapeToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasTextToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasTextToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasTextToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasTextToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasViewToolModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasViewToolModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasViewToolModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/CanvasViewToolModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/gradientBufferState.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/gradientBufferState.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/gradientBufferState.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/gradientBufferState.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/shapeCompositeOperation.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/shapeCompositeOperation.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/shapeCompositeOperation.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/shapeCompositeOperation.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/shapeCompositeOperation.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/shapeCompositeOperation.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/shapeCompositeOperation.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/shapeCompositeOperation.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/toolHotkeys.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/toolHotkeys.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/toolHotkeys.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/toolHotkeys.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/toolHotkeys.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/toolHotkeys.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/toolHotkeys.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/toolHotkeys.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/transparencyLocking.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/transparencyLocking.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/transparencyLocking.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/transparencyLocking.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/transparencyLocking.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/transparencyLocking.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/transparencyLocking.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasTool/transparencyLocking.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasWorkerModule.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasWorkerModule.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/CanvasWorkerModule.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/CanvasWorkerModule.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/cursors/zoom-drag-cursor.svg b/invokeai/frontend/webv1/src/features/controlLayers/konva/cursors/zoom-drag-cursor.svg similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/cursors/zoom-drag-cursor.svg rename to invokeai/frontend/webv1/src/features/controlLayers/konva/cursors/zoom-drag-cursor.svg diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/cursors/zoomDragCursor.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/cursors/zoomDragCursor.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/cursors/zoomDragCursor.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/cursors/zoomDragCursor.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/filters.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/filters.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/filters.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/filters.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/patterns/getPatternSVG.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/getPatternSVG.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/patterns/getPatternSVG.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/getPatternSVG.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-crosshatch.svg b/invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-crosshatch.svg similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-crosshatch.svg rename to invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-crosshatch.svg diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-diagonal.svg b/invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-diagonal.svg similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-diagonal.svg rename to invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-diagonal.svg diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-grid.svg b/invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-grid.svg similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-grid.svg rename to invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-grid.svg diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-horizontal.svg b/invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-horizontal.svg similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-horizontal.svg rename to invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-horizontal.svg diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-vertical.svg b/invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-vertical.svg similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/patterns/pattern-vertical.svg rename to invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/pattern-vertical.svg diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparency-checkerboard-pattern.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/transparency-checkerboard-pattern.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparency-checkerboard-pattern.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/transparency-checkerboard-pattern.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparent_bg.png b/invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/transparent_bg.png similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparent_bg.png rename to invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/transparent_bg.png diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparent_bg_dark.png b/invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/transparent_bg_dark.png similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparent_bg_dark.png rename to invokeai/frontend/webv1/src/features/controlLayers/konva/patterns/transparent_bg_dark.png diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/pressure.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/pressure.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/pressure.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/pressure.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/pressure.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/pressure.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/pressure.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/pressure.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/util.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/util.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/util.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/util.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/util.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/util.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/util.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/util.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/worker.ts b/invokeai/frontend/webv1/src/features/controlLayers/konva/worker.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/konva/worker.ts rename to invokeai/frontend/webv1/src/features/controlLayers/konva/worker.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/actions.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/actions.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/actions.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/actions.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSettingsSlice.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/canvasSettingsSlice.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/canvasSettingsSlice.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/canvasSettingsSlice.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSettingsSlice.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/canvasSettingsSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/canvasSettingsSlice.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/canvasSettingsSlice.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/canvasSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/canvasSlice.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/canvasStagingAreaSlice.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/canvasStagingAreaSlice.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/canvasStagingAreaSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/canvasStagingAreaSlice.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasTextSlice.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/canvasTextSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/canvasTextSlice.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/canvasTextSlice.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasWorkflowIntegrationSlice.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/canvasWorkflowIntegrationSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/canvasWorkflowIntegrationSlice.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/canvasWorkflowIntegrationSlice.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/ephemeral.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/ephemeral.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/ephemeral.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/ephemeral.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/filters.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/filters.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/filters.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/filters.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/lorasSlice.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/lorasSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/lorasSlice.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/lorasSlice.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/paramsSlice.test.ts similarity index 89% rename from invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/paramsSlice.test.ts index 2be7b2d4cfa..d318f2d647f 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts +++ b/invokeai/frontend/webv1/src/features/controlLayers/store/paramsSlice.test.ts @@ -180,11 +180,10 @@ describe('paramsSlice selectors for external models', () => { * `animaT5EncoderModel`, since removed from the schema. * - v4: the narrowest v4 blob is not a release at all — it is the one written by the build that did * the bump, `1aeb05bbf0` (97 keys). Releases writing v4 start at v6.14.0-rc1. - * - v5: the current version, reached by the FLUX.2 [dev] merge `f10d2a4f5a`, which is also the - * build that wrote the narrowest v5 blob. Pinning the fixture at the bump commit is what keeps - * the invariant below meaningful for the current tier: the version steps can never cover it (a - * v5 blob matches no branch), so every key added since the bump has to carry a zod default, and - * this entry is what proves it does. + * - v5: reached by the FLUX.2 [dev] merge `f10d2a4f5a`, which is also the build that wrote the + * narrowest v5 blob. It is historical now: the v5 -> v8 steps must seed every required field + * that this fixture predates, while any field added to the current v8 tier still needs a zod + * default because no migration step can cover a blob already at that version. */ const RELEASE_PARAMS_KEYS = { 'v6.0.0a1': { @@ -903,12 +902,18 @@ describe('paramsSliceConfig persisted state migration', () => { delete v2State.hiDiffusionWindowAttnEnabled; delete v2State.hiDiffusionT1Ratio; delete v2State.hiDiffusionT2Ratio; + delete v2State.minimaxH3DurationSeconds; + delete v2State.minimaxH3OutputMode; + delete v2State.minimaxH3TransformerModel; + delete v2State.minimaxH3TextEncoderModel; const result = migrate?.(v2State) as ReturnType; // v2 migrates all the way through the current chain (v2 -> v3 adds Qwen fields, - // v3 -> v4 adds Krea-2 and PiD fields). - expect(result._version).toBe(5); + // v3 -> v4 adds Krea-2 and PiD fields, v5 -> v6 adds MiniMax H3 fields, v6 -> v7 adds the + // MiniMax H3 single-file transformer override, v7 -> v8 adds the single-file text encoder + // override). + expect(result._version).toBe(8); expect(result.qwenImageVaeModel).toBeNull(); expect(result.qwenImageQwenVLEncoderModel).toBeNull(); expect(result.hiDiffusionEnabled).toBe(false); @@ -946,7 +951,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(v3State) as ReturnType & Record; - expect(result._version).toBe(5); + expect(result._version).toBe(8); expect((result.flux2VaeModel as { key: string } | null)?.key).toBe('klein-vae'); // The new standalone dev Mistral encoder slot must be seeded, not left undefined. expect(result.flux2DevMistralEncoderModel).toBeNull(); @@ -977,10 +982,14 @@ describe('paramsSliceConfig persisted state migration', () => { delete v3State.krea2RebalanceEnabled; delete v3State.krea2RebalanceMultiplier; delete v3State.krea2RebalanceWeights; + delete v3State.minimaxH3DurationSeconds; + delete v3State.minimaxH3OutputMode; + delete v3State.minimaxH3TransformerModel; + delete v3State.minimaxH3TextEncoderModel; const result = migrate?.(v3State) as ReturnType; - expect(result._version).toBe(5); + expect(result._version).toBe(8); expect(result.krea2VaeModel).toBeNull(); expect(result.krea2Qwen3VlEncoderModel).toBeNull(); expect(result.krea2SeedVarianceEnabled).toBe(false); @@ -994,6 +1003,37 @@ describe('paramsSliceConfig persisted state migration', () => { expect(result.dimensions).toMatchObject({ width: 640, height: 896 }); }); + it('seeds the Wan fields for a released-build v3 blob that predates the Wan merge', () => { + expect(migrate).toBeDefined(); + + const initial = getInitialParamsState(); + // Released v6.13.x builds wrote v3 blobs before the Wan fields existed. They're nullable + // with no default, so if the v3 -> v4 step didn't seed them, parse() would throw and the + // whole slice would be wiped on upgrade. + const v3State: Record = { + ...initial, + _version: 3, + positivePrompt: 'a fluffy cat', + }; + delete v3State.wanTransformerLowNoise; + delete v3State.wanComponentSource; + delete v3State.wanVaeModel; + delete v3State.wanT5EncoderModel; + delete v3State.wanGuidanceScaleLowNoise; + delete v3State.flux2VaeModel; + delete v3State.flux2DevMistralEncoderModel; + + const result = migrate?.(v3State) as ReturnType; + + expect(result._version).toBe(8); + expect(result.wanTransformerLowNoise).toBeNull(); + expect(result.wanComponentSource).toBeNull(); + expect(result.wanVaeModel).toBeNull(); + expect(result.wanT5EncoderModel).toBeNull(); + expect(result.wanGuidanceScaleLowNoise).toBeNull(); + expect(result.positivePrompt).toBe('a fluffy cat'); + }); + it('migrates a v4 blob written by main (PiD fields, no flux2 fields) without wiping it', () => { expect(migrate).toBeDefined(); @@ -1014,7 +1054,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(mainV4State) as ReturnType & Record; - expect(result._version).toBe(5); + expect(result._version).toBe(8); expect((result.flux2VaeModel as { key: string } | null)?.key).toBe('klein-vae'); expect(result.flux2DevMistralEncoderModel).toBeNull(); // main's own v4 values must survive untouched. @@ -1042,7 +1082,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(devV4State) as ReturnType & Record; - expect(result._version).toBe(5); + expect(result._version).toBe(8); // The branch's own v4 values must survive untouched. expect((result.flux2VaeModel as { key: string } | null)?.key).toBe('flux2-vae'); expect(result.pidMode).toBe('off'); @@ -1052,6 +1092,108 @@ describe('paramsSliceConfig persisted state migration', () => { expect(result.positivePrompt).toBe('a fluffy cat'); }); + it('backfills the MiniMax H3 fields when migrating from v5 and preserves existing params', () => { + // main shipped its own v4 -> v5 (the FLUX.2 [dev] VAE/encoder merge) before this branch + // landed, so the H3 step runs as v5 -> v6. A v5 blob written by a released build has no H3 + // keys, and they are required with no default -- if this step did not run, zParamsState.parse() + // would throw and the whole slice would be wiped on upgrade. + expect(migrate).toBeDefined(); + + const initial = getInitialParamsState(); + const v5State: Record = { + ...initial, + _version: 5, + positivePrompt: 'preserve this prompt', + seed: 4242, + dimensions: { ...initial.dimensions, width: 1344, height: 768 }, + }; + delete v5State.minimaxH3DurationSeconds; + delete v5State.minimaxH3OutputMode; + + const result = migrate?.(v5State) as ReturnType; + + expect(result._version).toBe(8); + expect(result.minimaxH3DurationSeconds).toBe(5); + expect(result.minimaxH3OutputMode).toBe('video'); + expect(result.positivePrompt).toBe('preserve this prompt'); + expect(result.seed).toBe(4242); + expect(result.dimensions).toMatchObject({ width: 1344, height: 768 }); + }); + + it('carries a v4 blob through both the flux2 and MiniMax H3 steps in one pass', () => { + // The regression this guards: with both steps written as v4 -> v5, the flux2 block sets + // _version = 5 and the H3 block never runs, so the parse below throws and the slice is wiped. + expect(migrate).toBeDefined(); + + const initial = getInitialParamsState(); + const v4State: Record = { + ...initial, + _version: 4, + positivePrompt: 'preserve this prompt', + }; + delete v4State.minimaxH3DurationSeconds; + delete v4State.minimaxH3OutputMode; + delete v4State.flux2VaeModel; + delete v4State.flux2DevMistralEncoderModel; + delete v4State.minimaxH3TransformerModel; + delete v4State.minimaxH3TextEncoderModel; + + const result = migrate?.(v4State) as ReturnType; + + expect(result._version).toBe(8); + expect(result.minimaxH3DurationSeconds).toBe(5); + expect(result.minimaxH3OutputMode).toBe('video'); + expect(result.flux2DevMistralEncoderModel).toBeNull(); + expect(result.minimaxH3TransformerModel).toBeNull(); + expect(result.minimaxH3TextEncoderModel).toBeNull(); + expect(result.positivePrompt).toBe('preserve this prompt'); + }); + + it('backfills the MiniMax H3 transformer override when migrating from v5 and preserves existing params', () => { + expect(migrate).toBeDefined(); + + const initial = getInitialParamsState(); + const v5State: Record = { + ...initial, + _version: 5, + positivePrompt: 'preserve this prompt', + seed: 777, + minimaxH3DurationSeconds: 10, + }; + delete v5State.minimaxH3TransformerModel; + delete v5State.minimaxH3TextEncoderModel; + + const result = migrate?.(v5State) as ReturnType; + + expect(result._version).toBe(8); + expect(result.minimaxH3TransformerModel).toBeNull(); + expect(result.minimaxH3DurationSeconds).toBe(10); + expect(result.positivePrompt).toBe('preserve this prompt'); + expect(result.seed).toBe(777); + }); + + it('backfills the MiniMax H3 text encoder override when migrating from v6 and preserves existing params', () => { + expect(migrate).toBeDefined(); + + const initial = getInitialParamsState(); + const v6State: Record = { + ...initial, + _version: 6, + positivePrompt: 'preserve this prompt', + seed: 888, + minimaxH3DurationSeconds: 12, + }; + delete v6State.minimaxH3TextEncoderModel; + + const result = migrate?.(v6State) as ReturnType; + + expect(result._version).toBe(8); + expect(result.minimaxH3TextEncoderModel).toBeNull(); + expect(result.minimaxH3DurationSeconds).toBe(12); + expect(result.positivePrompt).toBe('preserve this prompt'); + expect(result.seed).toBe(888); + }); + it('backfills the ERNIE-Image fields from their zod defaults without a version bump', () => { // The ERNIE-Image fields are additive with `.default()`, so there is no migration branch for // them. A persisted state written before they existed must still parse -- if it throws, the @@ -1099,7 +1241,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(blob) as ReturnType; - expect(result._version).toBe(5); + expect(result._version).toBe(8); expect(result.positivePrompt).toBe('a fluffy cat'); expect(result.seed).toBe(42); expect(result.shouldRandomizeSeed).toBe(false); @@ -1132,6 +1274,7 @@ describe('paramsSliceConfig persisted state migration', () => { // values would be needed to close that, which is a bigger change than this suite. for (const release of Object.keys(RELEASE_PARAMS_KEYS) as (keyof typeof RELEASE_PARAMS_KEYS)[]) { const { version } = RELEASE_PARAMS_KEYS[release]; + const currentVersion: number = getInitialParamsState()._version; const blob = buildReleaseBlob(release); applyParamsVersionMigrations(blob); @@ -1147,7 +1290,7 @@ describe('paramsSliceConfig persisted state migration', () => { expect( backfilled, - version === getInitialParamsState()._version + version === currentVersion ? `Keys missing from a blob written at ${release}, the commit that bumped _version to ${version}. ` + `A blob already at the current version matches no branch in the migration chain, so no step can ` + `seed these — each needs a zod default, or upgrading throws in zParamsState.parse() and wipes ` + @@ -1265,7 +1408,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(blob) as ReturnType; - expect(result._version).toBe(5); + expect(result._version).toBe(8); expect(result.dimensions).toEqual(getInitialParamsState().dimensions); expect(result.positivePrompt).toBe('a fluffy cat'); expect(result.seed).toBe(7); @@ -1282,7 +1425,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(blob) as ReturnType; - expect(result._version).toBe(5); + expect(result._version).toBe(8); expect(result.positivePromptHistory).toEqual([]); expect(result.qwenImageVaeModel).toBeNull(); expect(result.wanVaeModel).toBeNull(); @@ -1292,18 +1435,18 @@ describe('paramsSliceConfig persisted state migration', () => { it('never repairs _version, so version detection cannot be bypassed', () => { // `_version` is the input to the version steps, so the net must leave it alone. If it repaired // it, any blob whose version is not the current literal — including one written by a *newer* - // build — would be silently stamped v5 having run no step, and its stale field values would be + // build — would be silently stamped v8 having run no step, and its stale field values would be // accepted as current. Deliberately not routed through migrate(): the version steps normalise // `_version` before the net ever sees it, so only calling the net directly tests the guard. // The blob is otherwise complete (the current tier's key set), so `_version` is the only thing // the parse below can object to. - const blob = buildReleaseBlob('f10d2a4f5a', { _version: 6, positivePrompt: 'a fluffy cat' }); + const blob: Record = { ...getInitialParamsState(), _version: 9, positivePrompt: 'a fluffy cat' }; const { backfilled, reset } = repairParamsState(blob); expect(backfilled).toEqual([]); expect(reset).toEqual([]); - expect(blob._version).toBe(6); + expect(blob._version).toBe(9); // Still fatal, which is the correct outcome for a downgrade: that slice really was written by a // schema this build does not know. expect(() => zParamsState.parse(blob)).toThrow(); @@ -1319,7 +1462,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(blob) as ReturnType; - expect(result._version).toBe(5); + expect(result._version).toBe(8); expect(result.positivePrompt).toBe('a fluffy cat'); expect(result.seed).toBe(7); expect(result.dimensions).toBeDefined(); @@ -1336,7 +1479,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(v3State) as ReturnType; - expect(result._version).toBe(5); + expect(result._version).toBe(8); expect(result.wanTransformerLowNoise).toBeNull(); expect(result.wanComponentSource).toBeNull(); expect(result.wanVaeModel).toBeNull(); @@ -1358,7 +1501,7 @@ describe('paramsSliceConfig persisted state migration', () => { const result = migrate?.(v2State) as ReturnType; - expect(result._version).toBe(5); + expect(result._version).toBe(8); expect(result.fluxScheduler).toBe('euler'); expect(result.zImageScheduler).toBe('euler'); expect(result.colorCompensation).toBe(false); @@ -1411,14 +1554,14 @@ describe('paramsSliceConfig persisted state migration', () => { // reach for as long as 4 was current: a v4 blob matched no branch, so no step could seed it and // a zod default was the only option. The PiD fields (3f5588f21f, one day after the bump) are the // case that dev builds actually hit; the ERNIE-Image and HiDiffusion fields followed the same - // route. main's v4 -> v5 step now also seeds the PiD fields, but the defaults are what covers - // the same gap on the current tier, which by definition still has no step. + // route. The v4 -> v5 step now seeds the PiD fields; the defaults cover the analogous gap on + // the current v8 tier, which by definition still has no step. const blob = buildReleaseBlob('1aeb05bbf0', { positivePrompt: 'a fluffy cat', seed: 42 }); expect('pidMode' in blob).toBe(false); expect('hiDiffusionEnabled' in blob).toBe(false); applyParamsVersionMigrations(blob); - expect(blob._version).toBe(5); + expect(blob._version).toBe(8); // The value assertions below cannot, on their own, prove the defaults exist: three mechanisms // produce the identical values, so any two can hide the third being reverted. Parsing directly diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/paramsSlice.ts similarity index 95% rename from invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/paramsSlice.ts index 6f95038f36f..f2b04dac67b 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts +++ b/invokeai/frontend/webv1/src/features/controlLayers/store/paramsSlice.ts @@ -493,6 +493,34 @@ const slice = createSlice({ wanGuidanceScaleLowNoiseChanged: (state, action: PayloadAction) => { state.wanGuidanceScaleLowNoise = action.payload; }, + minimaxH3DurationSecondsChanged: (state, action: PayloadAction) => { + const result = zParamsState.shape.minimaxH3DurationSeconds.safeParse(action.payload); + if (!result.success) { + return; + } + state.minimaxH3DurationSeconds = result.data; + }, + minimaxH3OutputModeChanged: (state, action: PayloadAction<'video' | 'image'>) => { + const result = zParamsState.shape.minimaxH3OutputMode.safeParse(action.payload); + if (!result.success) { + return; + } + state.minimaxH3OutputMode = result.data; + }, + minimaxH3TransformerModelSelected: (state, action: PayloadAction) => { + const result = zParamsState.shape.minimaxH3TransformerModel.safeParse(action.payload); + if (!result.success) { + return; + } + state.minimaxH3TransformerModel = result.data; + }, + minimaxH3TextEncoderModelSelected: (state, action: PayloadAction) => { + const result = zParamsState.shape.minimaxH3TextEncoderModel.safeParse(action.payload); + if (!result.success) { + return; + } + state.minimaxH3TextEncoderModel = result.data; + }, vaePrecisionChanged: (state, action: PayloadAction) => { state.vaePrecision = action.payload; }, @@ -844,6 +872,8 @@ const resetState = (state: ParamsState): ParamsState => { newState.wanVaeModel = oldState.wanVaeModel; newState.wanT5EncoderModel = oldState.wanT5EncoderModel; newState.wanGuidanceScaleLowNoise = oldState.wanGuidanceScaleLowNoise; + newState.minimaxH3TransformerModel = oldState.minimaxH3TransformerModel; + newState.minimaxH3TextEncoderModel = oldState.minimaxH3TextEncoderModel; return newState; }; @@ -926,6 +956,10 @@ export const { wanVaeModelSelected, wanT5EncoderModelSelected, wanGuidanceScaleLowNoiseChanged, + minimaxH3DurationSecondsChanged, + minimaxH3OutputModeChanged, + minimaxH3TransformerModelSelected, + minimaxH3TextEncoderModelSelected, setClipSkip, shouldUseCpuNoiseChanged, setColorCompensation, @@ -1159,6 +1193,42 @@ export const applyParamsVersionMigrations = (state: any): void => { state.gemma2EncoderModel = state.gemma2EncoderModel ?? null; state.pidSteps = state.pidSteps ?? 4; } + + if (state._version === 5) { + // v5 -> v6, add the MiniMax H3 duration and output-mode fields. These steps follow the + // earlier versioned migrations above so released v4 and v5 blobs both advance safely. + state._version = 6; + state.minimaxH3DurationSeconds = state.minimaxH3DurationSeconds ?? 5; + state.minimaxH3OutputMode = state.minimaxH3OutputMode ?? 'video'; + } + + if (state._version === 6) { + // v6 -> v7, add the MiniMax H3 single-file transformer override. + state._version = 7; + state.minimaxH3TransformerModel = null; + } + + if (state._version === 7) { + // v7 -> v8, add the MiniMax H3 single-file text encoder override. + state._version = 8; + state.minimaxH3TextEncoderModel = null; + } + + if (!('hiDiffusionEnabled' in state)) { + state.hiDiffusionEnabled = false; + } + if (!('hiDiffusionRauNetEnabled' in state)) { + state.hiDiffusionRauNetEnabled = true; + } + if (!('hiDiffusionWindowAttnEnabled' in state)) { + state.hiDiffusionWindowAttnEnabled = true; + } + if (!('hiDiffusionT1Ratio' in state)) { + state.hiDiffusionT1Ratio = 0.4; + } + if (!('hiDiffusionT2Ratio' in state)) { + state.hiDiffusionT2Ratio = 0.0; + } }; export const paramsSliceConfig: SliceConfig = { @@ -1179,6 +1249,7 @@ export const paramsSliceConfig: SliceConfig = { `These need a zod default or a seed in the migration chain.` ); } + if (reset.length > 0) { log.warn( { reset }, @@ -1208,6 +1279,7 @@ export const selectIsExternal = createParamsSelector((params) => params.model?.b export const selectIsQwenImage = createParamsSelector((params) => params.model?.base === 'qwen-image'); export const selectIsKrea2 = createParamsSelector((params) => params.model?.base === 'krea-2'); export const selectIsWan = createParamsSelector((params) => params.model?.base === 'wan'); +export const selectIsMiniMaxH3 = createParamsSelector((params) => params.model?.base === 'minimax-h3'); export const selectIsFluxKontext = createParamsSelector((params) => { if (params.model?.base === 'flux' && params.model?.name.toLowerCase().includes('kontext')) { return true; @@ -1252,6 +1324,10 @@ export const selectWanComponentSource = createParamsSelector((params) => params. export const selectWanVaeModel = createParamsSelector((params) => params.wanVaeModel); export const selectWanT5EncoderModel = createParamsSelector((params) => params.wanT5EncoderModel); export const selectWanGuidanceScaleLowNoise = createParamsSelector((params) => params.wanGuidanceScaleLowNoise); +export const selectMiniMaxH3DurationSeconds = createParamsSelector((params) => params.minimaxH3DurationSeconds); +export const selectMiniMaxH3OutputMode = createParamsSelector((params) => params.minimaxH3OutputMode); +export const selectMiniMaxH3TransformerModel = createParamsSelector((params) => params.minimaxH3TransformerModel); +export const selectMiniMaxH3TextEncoderModel = createParamsSelector((params) => params.minimaxH3TextEncoderModel); export const selectCFGScale = createParamsSelector((params) => params.cfgScale); export const selectGuidance = createParamsSelector((params) => params.guidance); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/refImagesSlice.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/refImagesSlice.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/refImagesSlice.ts similarity index 98% rename from invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/refImagesSlice.ts index 1cad8d7042b..12937962c67 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/refImagesSlice.ts +++ b/invokeai/frontend/webv1/src/features/controlLayers/store/refImagesSlice.ts @@ -24,6 +24,7 @@ import { isFLUXReduxConfig, isIPAdapterConfig, isKrea2ReferenceImageConfig, + isMiniMaxH3ReferenceImageConfig, isQwenImageReferenceImageConfig, isWanReferenceImageConfig, zRefImagesState, @@ -147,13 +148,14 @@ const slice = createSlice({ return; } - // FLUX.2, Qwen Image Edit, Wan and Krea-2 reference images don't have a model field - they use + // FLUX.2, Qwen Image Edit, Wan, Krea-2 and MiniMax H3 reference images don't have a model field - they use // built-in support if ( isFlux2ReferenceImageConfig(entity.config) || isQwenImageReferenceImageConfig(entity.config) || isWanReferenceImageConfig(entity.config) || - isKrea2ReferenceImageConfig(entity.config) + isKrea2ReferenceImageConfig(entity.config) || + isMiniMaxH3ReferenceImageConfig(entity.config) ) { return; } diff --git a/invokeai/frontend/web/src/features/controlLayers/store/selectors.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/selectors.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/selectors.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/selectors.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/types.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/types.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/types.test.ts diff --git a/invokeai/frontend/webv1/src/features/controlLayers/store/types.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/types.ts new file mode 100644 index 00000000000..ee6ea9a2025 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/controlLayers/store/types.ts @@ -0,0 +1,1314 @@ +import { deepClone } from 'common/util/deepClone'; +import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEntity/types'; +import { zMainModelBase, zModelIdentifierField } from 'features/nodes/types/common'; +import { + zParameterAnimaScheduler, + zParameterCanvasCoherenceMode, + zParameterCFGRescaleMultiplier, + zParameterCFGScale, + zParameterCLIPEmbedModel, + zParameterCLIPGEmbedModel, + zParameterCLIPLEmbedModel, + zParameterControlLoRAModel, + zParameterErnieImageScheduler, + zParameterFluxDypeExponent, + zParameterFluxDypePreset, + zParameterFluxDypeScale, + zParameterFluxScheduler, + zParameterGuidance, + zParameterIdeogram4SamplerPreset, + zParameterImageDimension, + zParameterMaskBlurMethod, + zParameterModel, + zParameterNegativePrompt, + zParameterPositivePrompt, + zParameterPrecision, + zParameterScheduler, + zParameterSDXLRefinerModel, + zParameterSeed, + zParameterSteps, + zParameterStrength, + zParameterT5EncoderModel, + zParameterVAEModel, + zParameterZImageScheduler, +} from 'features/parameters/types/parameterSchemas'; +import type { JsonObject } from 'type-fest'; +import { z } from 'zod'; + +const zId = z.string().min(1); +const zName = z.string().min(1).nullable(); + +export const zImageWithDims = z.object({ + image_name: z.string(), + width: z.number().int().positive(), + height: z.number().int().positive(), +}); +export type ImageWithDims = z.infer; + +const zCropBox = z.object({ + x: z.number().min(0), + y: z.number().min(0), + width: z.number().positive(), + height: z.number().positive(), +}); +// This new schema is an extension of zImageWithDims, with an optional crop field. +// +// When we added cropping support to certain entities (e.g. Ref Images, video Starting Frame Image), we changed +// their schemas from using zImageWithDims to this new schema. To support loading pre-existing entities that +// were created before cropping was supported, we can use zod's preprocess to transform old data into the new format. +// Its essentially a data migration step. +// +// This parsing happens currently in two places: +// - Recalling metadata. +// - Loading/rehydrating persisted client state from storage. +const zCroppableImageWithDims = z.preprocess( + (val) => { + try { + const imageWithDims = zImageWithDims.parse(val); + const migrated = { original: { image: deepClone(imageWithDims) } }; + return migrated; + } catch { + return val; + } + }, + z.object({ + original: z.object({ image: zImageWithDims }), + crop: z + .object({ + box: zCropBox, + ratio: z.number().gt(0).nullable(), + image: zImageWithDims, + }) + .optional(), + }) +); +export type CroppableImageWithDims = z.infer; + +const zImageWithDimsDataURL = z.object({ + dataURL: z.string(), + width: z.number().int().positive(), + height: z.number().int().positive(), +}); + +const zBeginEndStepPct = z + .tuple([z.number().gte(0).lte(1), z.number().gte(0).lte(1)]) + .refine(([begin, end]) => begin < end, { + message: 'Begin must be less than end', + }); + +const zControlModeV2 = z.enum(['balanced', 'more_prompt', 'more_control', 'unbalanced']); +export type ControlModeV2 = z.infer; +export const isControlModeV2 = (v: unknown): v is ControlModeV2 => zControlModeV2.safeParse(v).success; + +const zCLIPVisionModelV2 = z.enum(['ViT-H', 'ViT-G', 'ViT-L']); +export type CLIPVisionModelV2 = z.infer; +export const isCLIPVisionModelV2 = (v: unknown): v is CLIPVisionModelV2 => zCLIPVisionModelV2.safeParse(v).success; + +const zIPMethodV2 = z.enum(['full', 'style', 'composition', 'style_strong', 'style_precise']); +export type IPMethodV2 = z.infer; +export const isIPMethodV2 = (v: unknown): v is IPMethodV2 => zIPMethodV2.safeParse(v).success; + +const _zTool = z.enum(['brush', 'eraser', 'move', 'rect', 'lasso', 'gradient', 'view', 'bbox', 'colorPicker', 'text']); +export type Tool = z.infer; + +const zPoints = z.array(z.number()).refine((points) => points.length % 2 === 0, { + message: 'Must have an even number of coordinate components', +}); +const zPointsWithPressure = z.array(z.number()).refine((points) => points.length % 3 === 0, { + message: 'Must have a number of components divisible by 3', +}); + +const zRgbColor = z.object({ + r: z.number().int().min(0).max(255), + g: z.number().int().min(0).max(255), + b: z.number().int().min(0).max(255), +}); +export type RgbColor = z.infer; +export const zRgbaColor = zRgbColor.extend({ + a: z.number().min(0).max(1), +}); +export type RgbaColor = z.infer; +export const RGBA_BLACK: RgbaColor = { r: 0, g: 0, b: 0, a: 1 }; +export const RGBA_WHITE: RgbaColor = { r: 255, g: 255, b: 255, a: 1 }; + +const zOpacity = z.number().gte(0).lte(1); + +const _zDimensions = z.object({ + width: z.number().int().positive(), + height: z.number().int().positive(), +}); +export type Dimensions = z.infer; + +const zCoordinate = z.object({ + x: z.number(), + y: z.number(), +}); +export type Coordinate = z.infer; +const _zCoordinateWithPressure = z.object({ + x: z.number(), + y: z.number(), + pressure: z.number(), +}); +export type CoordinateWithPressure = z.infer; + +const SAM_POINT_LABELS = { + background: -1, + neutral: 0, + foreground: 1, +} as const; + +const zSAMPointLabel = z.nativeEnum(SAM_POINT_LABELS); +export type SAMPointLabel = z.infer; + +export const zSAMPointLabelString = z.enum(['background', 'neutral', 'foreground']); +export type SAMPointLabelString = z.infer; + +export const zSAMModel = z.enum(['SAM1', 'SAM2']); +export type SAMModel = z.infer; + +/** + * A mapping of SAM point labels (as numbers) to their string representations. + */ +export const SAM_POINT_LABEL_NUMBER_TO_STRING: Record = { + '-1': 'background', + 0: 'neutral', + 1: 'foreground', +}; + +/** + * A mapping of SAM point labels (as strings) to their numeric representations. + */ +export const SAM_POINT_LABEL_STRING_TO_NUMBER: Record = { + background: -1, + neutral: 0, + foreground: 1, +}; + +const _zSAMPoint = z.object({ + x: z.number().int().gte(0), + y: z.number().int().gte(0), + label: zSAMPointLabel, +}); +type SAMPoint = z.infer; +export type SAMPointWithId = SAMPoint & { id: string }; + +const zRect = z.object({ + x: z.number(), + y: z.number(), + width: z.number().min(1), + height: z.number().min(1), +}); +export type Rect = z.infer; + +const _zRectWithRotation = zRect.extend({ + rotation: z.number(), +}); +export type RectWithRotation = z.infer; + +const zCanvasBrushLineState = z.object({ + id: zId, + type: z.literal('brush_line'), + strokeWidth: z.number().min(1), + /** + * Points without pressure are in the format [x1, y1, x2, y2, ...] + */ + points: zPoints, + color: zRgbaColor, + clip: zRect.nullable(), + globalCompositeOperation: z.string().optional(), +}); +export type CanvasBrushLineState = z.infer; + +const zCanvasBrushLineWithPressureState = z.object({ + id: zId, + type: z.literal('brush_line_with_pressure'), + strokeWidth: z.number().min(1), + /** + * Points with pressure are in the format [x1, y1, pressure1, x2, y2, pressure2, ...] + */ + points: zPointsWithPressure, + color: zRgbaColor, + pressureAffectsWidth: z.boolean().default(true), + pressureAffectsOpacity: z.boolean().default(false), + clip: zRect.nullable(), + globalCompositeOperation: z.string().optional(), +}); +export type CanvasBrushLineWithPressureState = z.infer; + +const zCanvasEraserLineState = z.object({ + id: zId, + type: z.literal('eraser_line'), + strokeWidth: z.number().min(1), + /** + * Points without pressure are in the format [x1, y1, x2, y2, ...] + */ + points: zPoints, + clip: zRect.nullable(), +}); +export type CanvasEraserLineState = z.infer; + +const zCanvasEraserLineWithPressureState = z.object({ + id: zId, + type: z.literal('eraser_line_with_pressure'), + strokeWidth: z.number().min(1), + /** + * Points with pressure are in the format [x1, y1, pressure1, x2, y2, pressure2, ...] + */ + points: zPointsWithPressure, + pressureAffectsWidth: z.boolean().default(true), + clip: zRect.nullable(), +}); +export type CanvasEraserLineWithPressureState = z.infer; + +const zCanvasShapeCompositeOperation = z.enum(['source-over', 'source-atop', 'destination-out']); + +const zCanvasRectState = z.object({ + id: zId, + type: z.literal('rect'), + rect: zRect, + color: zRgbaColor, + compositeOperation: zCanvasShapeCompositeOperation.default('source-over'), + clip: zRect.nullable().default(null), +}); +export type CanvasRectState = z.infer; + +const zCanvasLassoCompositeOperation = z.enum(['source-over', 'destination-out']); + +const zCanvasLassoState = z.object({ + id: zId, + type: z.literal('lasso'), + /** + * Points in the format [x1, y1, x2, y2, ...]. + * The lasso tool always commits a closed contour. + */ + points: zPoints, + compositeOperation: zCanvasLassoCompositeOperation.default('source-over'), +}); +export type CanvasLassoState = z.infer; + +const zCanvasOvalState = z.object({ + id: zId, + type: z.literal('oval'), + rect: zRect, + color: zRgbaColor, + compositeOperation: zCanvasShapeCompositeOperation.default('source-over'), + clip: zRect.nullable().default(null), +}); +export type CanvasOvalState = z.infer; + +const zCanvasPolygonState = z.object({ + id: zId, + type: z.literal('polygon'), + points: zPoints, + color: zRgbaColor, + compositeOperation: zCanvasShapeCompositeOperation.default('source-over'), + previewPoint: zCoordinate.optional(), +}); +export type CanvasPolygonState = z.infer; + +const zCanvasShapeState = z.union([zCanvasRectState, zCanvasOvalState, zCanvasPolygonState]); +type CanvasShapeState = z.infer; + +// Gradient state includes clip metadata so the tool can optionally clip to drag gesture. +const zCanvasLinearGradientState = z.object({ + id: zId, + type: z.literal('gradient'), + gradientType: z.literal('linear'), + rect: zRect, + start: zCoordinate, + end: zCoordinate, + clipCenter: zCoordinate, + clipRadius: z.number().min(0), + clipAngle: z.number(), + clipEnabled: z.boolean().default(true), + bboxRect: zRect, + fgColor: zRgbaColor, + bgColor: zRgbaColor, + globalCompositeOperation: z.string().optional(), +}); +const zCanvasRadialGradientState = z.object({ + id: zId, + type: z.literal('gradient'), + gradientType: z.literal('radial'), + rect: zRect, + center: zCoordinate, + radius: z.number().min(0), + clipCenter: zCoordinate, + clipRadius: z.number().min(0), + clipEnabled: z.boolean().default(true), + bboxRect: zRect, + fgColor: zRgbaColor, + bgColor: zRgbaColor, + globalCompositeOperation: z.string().optional(), +}); +const zCanvasGradientState = z.discriminatedUnion('gradientType', [ + zCanvasLinearGradientState, + zCanvasRadialGradientState, +]); +export type CanvasGradientState = z.infer; + +const zCanvasImageState = z.object({ + id: zId, + type: z.literal('image'), + image: z.union([zImageWithDims, zImageWithDimsDataURL]), + usePixelBbox: z.boolean().optional(), +}); +export type CanvasImageState = z.infer; + +const zCanvasObjectState = z.union([ + zCanvasImageState, + zCanvasBrushLineState, + zCanvasEraserLineState, + zCanvasShapeState, + zCanvasLassoState, + zCanvasBrushLineWithPressureState, + zCanvasEraserLineWithPressureState, + zCanvasGradientState, +]); +export type CanvasObjectState = z.infer; + +const zIPAdapterConfig = z.object({ + type: z.literal('ip_adapter'), + image: zCroppableImageWithDims.nullable(), + model: zModelIdentifierField.nullable(), + weight: z.number().gte(-1).lte(2), + beginEndStepPct: zBeginEndStepPct, + method: zIPMethodV2, + clipVisionModel: zCLIPVisionModelV2, +}); +export type IPAdapterConfig = z.infer; + +const zRegionalGuidanceIPAdapterConfig = z.object({ + type: z.literal('ip_adapter'), + image: zImageWithDims.nullable(), + model: zModelIdentifierField.nullable(), + weight: z.number().gte(-1).lte(2), + beginEndStepPct: zBeginEndStepPct, + method: zIPMethodV2, + clipVisionModel: zCLIPVisionModelV2, +}); +export type RegionalGuidanceIPAdapterConfig = z.infer; + +const zFLUXReduxImageInfluence = z.enum(['lowest', 'low', 'medium', 'high', 'highest']); +export const isFLUXReduxImageInfluence = (v: unknown): v is FLUXReduxImageInfluence => + zFLUXReduxImageInfluence.safeParse(v).success; +export type FLUXReduxImageInfluence = z.infer; +const zFLUXReduxConfig = z.object({ + type: z.literal('flux_redux'), + image: zCroppableImageWithDims.nullable(), + model: zModelIdentifierField.nullable(), + imageInfluence: zFLUXReduxImageInfluence.default('highest'), +}); +export type FLUXReduxConfig = z.infer; +const zRegionalGuidanceFLUXReduxConfig = z.object({ + type: z.literal('flux_redux'), + image: zImageWithDims.nullable(), + model: zModelIdentifierField.nullable(), + imageInfluence: zFLUXReduxImageInfluence.default('highest'), +}); +type RegionalGuidanceFLUXReduxConfig = z.infer; + +const zFluxKontextReferenceImageConfig = z.object({ + type: z.literal('flux_kontext_reference_image'), + image: zCroppableImageWithDims.nullable(), + model: zModelIdentifierField.nullable(), +}); +export type FluxKontextReferenceImageConfig = z.infer; + +// FLUX.2 Klein has built-in reference image support - no separate model needed +const zFlux2ReferenceImageConfig = z.object({ + type: z.literal('flux2_reference_image'), + image: zCroppableImageWithDims.nullable(), +}); +export type Flux2ReferenceImageConfig = z.infer; + +// Qwen Image Edit has built-in reference image support - no separate model needed +const zQwenImageReferenceImageConfig = z.object({ + type: z.literal('qwen_image_reference_image'), + image: zCroppableImageWithDims.nullable(), +}); +export type QwenImageReferenceImageConfig = z.infer; + +// Wan 2.2 I2V uses the model's own VAE to encode a single reference image - +// no separate adapter model needed. Only consumed by the I2V variant of Wan +// 2.2 (A14B). T2V / TI2V variants ignore the ref image at graph build time. +const zWanReferenceImageConfig = z.object({ + type: z.literal('wan_reference_image'), + image: zCroppableImageWithDims.nullable(), +}); +export type WanReferenceImageConfig = z.infer; + +// Krea-2 transfers style training-free, by splicing the reference's attention keys/values into the +// target's - no adapter model needed. styleStrength is the only knob surfaced here; the rest of the +// tuning parameters live on the krea2_style_reference node and keep their defaults. +const zKrea2ReferenceImageConfig = z.object({ + type: z.literal('krea2_reference_image'), + image: zCroppableImageWithDims.nullable(), + styleStrength: z.number().gte(0).lte(2).default(1), +}); +export type Krea2ReferenceImageConfig = z.infer; + +// MiniMax H3 first-frame conditioning uses the model's own VAE + vision +// context - no separate adapter model needed. Consumed only in video output +// mode (the first enabled ref image becomes the video's first frame). +const zMiniMaxH3ReferenceImageConfig = z.object({ + type: z.literal('minimax_h3_reference_image'), + image: zCroppableImageWithDims.nullable(), +}); +export type MiniMaxH3ReferenceImageConfig = z.infer; + +const zCanvasEntityBase = z.object({ + id: zId, + name: zName, + isEnabled: z.boolean(), + isLocked: z.boolean(), +}); + +export const zRefImageState = z.object({ + id: zId, + isEnabled: z.boolean().default(true), + config: z.discriminatedUnion('type', [ + zIPAdapterConfig, + zFLUXReduxConfig, + zFluxKontextReferenceImageConfig, + zFlux2ReferenceImageConfig, + zQwenImageReferenceImageConfig, + zWanReferenceImageConfig, + zMiniMaxH3ReferenceImageConfig, + zKrea2ReferenceImageConfig, + ]), +}); +export type RefImageState = z.infer; + +export const isIPAdapterConfig = (config: RefImageState['config']): config is IPAdapterConfig => + config.type === 'ip_adapter'; + +export const isFLUXReduxConfig = (config: RefImageState['config']): config is FLUXReduxConfig => + config.type === 'flux_redux'; + +export const isFluxKontextReferenceImageConfig = ( + config: RefImageState['config'] +): config is FluxKontextReferenceImageConfig => config.type === 'flux_kontext_reference_image'; + +export const isFlux2ReferenceImageConfig = (config: RefImageState['config']): config is Flux2ReferenceImageConfig => + config.type === 'flux2_reference_image'; + +export const isQwenImageReferenceImageConfig = ( + config: RefImageState['config'] +): config is QwenImageReferenceImageConfig => config.type === 'qwen_image_reference_image'; + +export const isWanReferenceImageConfig = (config: RefImageState['config']): config is WanReferenceImageConfig => + config.type === 'wan_reference_image'; + +export const isKrea2ReferenceImageConfig = (config: RefImageState['config']): config is Krea2ReferenceImageConfig => + config.type === 'krea2_reference_image'; + +export const isMiniMaxH3ReferenceImageConfig = ( + config: RefImageState['config'] +): config is MiniMaxH3ReferenceImageConfig => config.type === 'minimax_h3_reference_image'; + +const zFillStyle = z.enum(['solid', 'grid', 'crosshatch', 'diagonal', 'horizontal', 'vertical']); +export type FillStyle = z.infer; +export const isFillStyle = (v: unknown): v is FillStyle => zFillStyle.safeParse(v).success; +const zFill = z.object({ style: zFillStyle, color: zRgbColor }); + +const zRegionalGuidanceRefImageState = z.object({ + id: zId, + config: z.discriminatedUnion('type', [zRegionalGuidanceIPAdapterConfig, zRegionalGuidanceFLUXReduxConfig]), +}); +export type RegionalGuidanceRefImageState = z.infer; + +export const isRegionalGuidanceIPAdapterConfig = ( + config: RegionalGuidanceRefImageState['config'] +): config is RegionalGuidanceIPAdapterConfig => config.type === 'ip_adapter'; + +export const isRegionalGuidanceFLUXReduxConfig = ( + config: RegionalGuidanceRefImageState['config'] +): config is RegionalGuidanceFLUXReduxConfig => config.type === 'flux_redux'; + +const zCanvasRegionalGuidanceState = zCanvasEntityBase.extend({ + type: z.literal('regional_guidance'), + position: zCoordinate, + opacity: zOpacity, + objects: z.array(zCanvasObjectState), + fill: zFill, + positivePrompt: zParameterPositivePrompt.nullable(), + negativePrompt: zParameterNegativePrompt.nullable(), + referenceImages: z.array(zRegionalGuidanceRefImageState), + autoNegative: z.boolean(), +}); +export type CanvasRegionalGuidanceState = z.infer; + +const zCanvasInpaintMaskState = zCanvasEntityBase.extend({ + type: z.literal('inpaint_mask'), + position: zCoordinate, + fill: zFill, + opacity: zOpacity, + objects: z.array(zCanvasObjectState), + noiseLevel: z.number().gte(0).lte(1).optional(), + denoiseLimit: z.number().gte(0).lte(1).optional(), +}); +export type CanvasInpaintMaskState = z.infer; + +const zControlNetConfig = z.object({ + type: z.literal('controlnet'), + model: zModelIdentifierField.nullable(), + weight: z.number().gte(-1).lte(2), + beginEndStepPct: zBeginEndStepPct, + controlMode: zControlModeV2, +}); +export type ControlNetConfig = z.infer; + +const zT2IAdapterConfig = z.object({ + type: z.literal('t2i_adapter'), + model: zModelIdentifierField.nullable(), + weight: z.number().gte(-1).lte(2), + beginEndStepPct: zBeginEndStepPct, +}); +export type T2IAdapterConfig = z.infer; + +const zControlLoRAConfig = z.object({ + type: z.literal('control_lora'), + weight: z.number().gte(-1).lte(2), + model: zModelIdentifierField.nullable(), +}); +export type ControlLoRAConfig = z.infer; + +const zZImageControlConfig = z.object({ + type: z.literal('z_image_control'), + model: zModelIdentifierField.nullable(), + weight: z.number().gte(0).lte(2), // control_context_scale, recommended 0.65-0.80 + beginEndStepPct: zBeginEndStepPct, +}); +export type ZImageControlConfig = z.infer; + +const zAnimaLLLiteConfig = z.object({ + type: z.literal('anima_lllite'), + model: zModelIdentifierField.nullable(), + weight: z.number().gte(0).lte(2), + beginEndStepPct: zBeginEndStepPct, +}); +export type AnimaLLLiteConfig = z.infer; + +/** + * All simple params normalized to `[-1, 1]` except sharpness `[0, 1]`. + * + * - Brightness: -1 (darken) to 1 (brighten) + * - Contrast: -1 (decrease contrast) to 1 (increase contrast) + * - Saturation: -1 (desaturate) to 1 (saturate) + * - Temperature: -1 (cooler/blue) to 1 (warmer/yellow) + * - Tint: -1 (greener) to 1 (more magenta) + * - Sharpness: 0 (no sharpening) to 1 (maximum sharpening) + */ +export const zSimpleAdjustmentsConfig = z.object({ + brightness: z.number().gte(-1).lte(1), + contrast: z.number().gte(-1).lte(1), + saturation: z.number().gte(-1).lte(1), + temperature: z.number().gte(-1).lte(1), + tint: z.number().gte(-1).lte(1), + sharpness: z.number().gte(0).lte(1), +}); +export type SimpleAdjustmentsConfig = z.infer; + +const zUint8 = z.number().int().min(0).max(255); +const zChannelPoints = z.array(z.tuple([zUint8, zUint8])).min(2); +const zChannelName = z.enum(['master', 'r', 'g', 'b']); +const zCurvesAdjustmentsConfig = z.record(zChannelName, zChannelPoints); +export type ChannelName = z.infer; +export type ChannelPoints = z.infer; +export type CurvesAdjustmentsConfig = z.infer; + +/** + * The curves adjustments are stored as LUTs in the Konva node attributes. Konva will use these values when applying + * the filter. + */ +export const zCurvesAdjustmentsLUTs = z.record(zChannelName, z.array(zUint8)); + +const zRasterLayerAdjustments = z.object({ + version: z.literal(1), + enabled: z.boolean(), + collapsed: z.boolean(), + mode: z.enum(['simple', 'curves']), + simple: zSimpleAdjustmentsConfig, + curves: zCurvesAdjustmentsConfig, +}); +export type RasterLayerAdjustments = z.infer; + +/** + * Available global composite operations (blend modes) for layers. + * These are the standard Canvas 2D composite operations. + * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation + * NOTE: All of these are supported by canvas layers, but not all are supported by CSS blend modes (live rendering). + */ +const COMPOSITE_OPERATIONS = [ + 'darken', + 'multiply', + 'color-burn', + 'lighten', + 'screen', + 'color-dodge', + 'lighter', + 'overlay', + 'soft-light', + 'hard-light', + 'difference', + 'exclusion', + 'xor', + 'hue', + 'saturation', + 'color', + 'luminosity', + 'source-over', + 'source-in', + 'source-out', + 'source-atop', + 'destination-over', + 'destination-in', + 'destination-out', + 'destination-atop', + 'copy', +] as const; + +export type CompositeOperation = (typeof COMPOSITE_OPERATIONS)[number]; + +// Subset of color blend modes for UI selection. All are supported by both Konva and CSS. +export const COLOR_BLEND_MODES: CompositeOperation[] = [ + 'source-over', + 'darken', + 'multiply', + 'color-burn', + 'lighten', + 'screen', + 'color-dodge', + 'overlay', + 'soft-light', + 'hard-light', + 'difference', + 'hue', + 'saturation', + 'color', + 'luminosity', +]; + +const zCanvasRasterLayerState = zCanvasEntityBase.extend({ + type: z.literal('raster_layer'), + position: zCoordinate, + opacity: zOpacity, + objects: z.array(zCanvasObjectState), + // Optional per-layer color adjustments (simple + curves). When undefined, no adjustments are applied. + adjustments: zRasterLayerAdjustments.optional(), + // Optional per-layer composite operation. When undefined, defaults to 'source-over'. + globalCompositeOperation: z.enum(COMPOSITE_OPERATIONS).optional(), + // When true, brush strokes only paint where existing pixels are non-transparent (preserve alpha). + isTransparencyLocked: z.boolean().optional(), +}); +export type CanvasRasterLayerState = z.infer; + +const zCanvasControlLayerState = zCanvasRasterLayerState.extend({ + type: z.literal('control_layer'), + withTransparencyEffect: z.boolean(), + controlAdapter: z.discriminatedUnion('type', [ + zControlNetConfig, + zT2IAdapterConfig, + zControlLoRAConfig, + zZImageControlConfig, + zAnimaLLLiteConfig, + ]), +}); +export type CanvasControlLayerState = z.infer; + +const zBoundingBoxScaleMethod = z.enum(['none', 'auto', 'manual']); +export type BoundingBoxScaleMethod = z.infer; +export const isBoundingBoxScaleMethod = (v: unknown): v is BoundingBoxScaleMethod => + zBoundingBoxScaleMethod.safeParse(v).success; + +const _zCanvasEntityState = z.discriminatedUnion('type', [ + zCanvasRasterLayerState, + zCanvasControlLayerState, + zCanvasRegionalGuidanceState, + zCanvasInpaintMaskState, +]); +export type CanvasEntityState = z.infer; + +const zCanvasEntityType = z.union([ + zCanvasRasterLayerState.shape.type, + zCanvasControlLayerState.shape.type, + zCanvasRegionalGuidanceState.shape.type, + zCanvasInpaintMaskState.shape.type, +]); +export type CanvasEntityType = z.infer; + +export const zCanvasEntityIdentifer = z.object({ + id: zId, + type: zCanvasEntityType, +}); +export type CanvasEntityIdentifier = { id: string; type: T }; + +export const zLoRA = z.object({ + id: z.string(), + isEnabled: z.boolean(), + model: zModelIdentifierField, + weight: z.number(), +}); +export type LoRA = z.infer; + +export const zAspectRatioID = z.enum([ + 'Free', + '8:1', + '4:1', + '21:9', + '16:9', + '3:2', + '5:4', + '4:3', + '1:1', + '3:4', + '4:5', + '2:3', + '9:16', + '1:4', + '9:21', + '1:8', +]); +export type AspectRatioID = z.infer; +export const isAspectRatioID = (v: unknown): v is AspectRatioID => zAspectRatioID.safeParse(v).success; +export const ASPECT_RATIO_MAP: Record, { ratio: number; inverseID: AspectRatioID }> = { + '8:1': { ratio: 8 / 1, inverseID: '1:8' }, + '4:1': { ratio: 4 / 1, inverseID: '1:4' }, + '21:9': { ratio: 21 / 9, inverseID: '9:21' }, + '16:9': { ratio: 16 / 9, inverseID: '9:16' }, + '3:2': { ratio: 3 / 2, inverseID: '2:3' }, + '5:4': { ratio: 5 / 4, inverseID: '4:5' }, + '4:3': { ratio: 4 / 3, inverseID: '4:3' }, + '1:1': { ratio: 1, inverseID: '1:1' }, + '3:4': { ratio: 3 / 4, inverseID: '4:3' }, + '4:5': { ratio: 4 / 5, inverseID: '5:4' }, + '2:3': { ratio: 2 / 3, inverseID: '3:2' }, + '9:16': { ratio: 9 / 16, inverseID: '16:9' }, + '1:4': { ratio: 1 / 4, inverseID: '4:1' }, + '9:21': { ratio: 9 / 21, inverseID: '21:9' }, + '1:8': { ratio: 1 / 8, inverseID: '8:1' }, +}; + +const zAspectRatioConfig = z.object({ + id: zAspectRatioID, + value: z.number().gt(0), + isLocked: z.boolean(), +}); +type AspectRatioConfig = z.infer; + +export const DEFAULT_ASPECT_RATIO_CONFIG: AspectRatioConfig = { + id: '1:1', + value: 1, + isLocked: false, +}; + +const zBboxState = z.object({ + rect: z.object({ + x: z.number().int(), + y: z.number().int(), + width: zParameterImageDimension, + height: zParameterImageDimension, + }), + aspectRatio: zAspectRatioConfig, + scaledSize: z.object({ + width: zParameterImageDimension, + height: zParameterImageDimension, + }), + scaleMethod: zBoundingBoxScaleMethod, + modelBase: zMainModelBase, +}); + +const zDimensionsState = z.object({ + width: zParameterImageDimension, + height: zParameterImageDimension, + aspectRatio: zAspectRatioConfig, +}); + +export const MAX_POSITIVE_PROMPT_HISTORY = 100; +const zPromptHistoryItem = z.union([ + zParameterPositivePrompt.transform((positivePrompt) => ({ positivePrompt, negativePrompt: null })), + z.object({ + positivePrompt: zParameterPositivePrompt, + negativePrompt: zParameterNegativePrompt, + }), +]); +export type PromptHistoryItem = z.infer; +const zPositivePromptHistory = z + .array(zPromptHistoryItem) + .transform((arr) => arr.slice(0, MAX_POSITIVE_PROMPT_HISTORY)); + +export const zInfillMethod = z.enum(['patchmatch', 'lama', 'cv2', 'color', 'tile']); +export type InfillMethod = z.infer; + +const zPidMode = z.enum(['off', 'fit', 'native']); +export type PidMode = z.infer; + +export const zParamsState = z.object({ + _version: z.literal(8), + maskBlur: z.number(), + maskBlurMethod: zParameterMaskBlurMethod, + canvasCoherenceMode: zParameterCanvasCoherenceMode, + canvasCoherenceMinDenoise: zParameterStrength, + canvasCoherenceEdgeSize: z.number(), + infillMethod: zInfillMethod, + infillTileSize: z.number(), + infillPatchmatchDownscaleSize: z.number(), + infillColorValue: zRgbaColor, + cfgScale: zParameterCFGScale, + cfgRescaleMultiplier: zParameterCFGRescaleMultiplier, + guidance: zParameterGuidance, + img2imgStrength: zParameterStrength, + optimizedDenoisingEnabled: z.boolean(), + // Added after the `_version` 3 -> 4 bump, so while 4 was current no migration step could seed them + // — a blob already at v4 matched no branch in the chain. Without defaults they are required, and + // every persisted blob in existence fails the parse in `migrate()`, wiping the user's whole params + // slice on upgrade. The defaults are still what covers the current tier, which has no step either. + hiDiffusionEnabled: z.boolean().default(false), + hiDiffusionRauNetEnabled: z.boolean().default(true), + hiDiffusionWindowAttnEnabled: z.boolean().default(true), + hiDiffusionT1Ratio: z.number().default(0.4), + hiDiffusionT2Ratio: z.number().default(0.0), + iterations: z.number(), + scheduler: zParameterScheduler, + fluxScheduler: zParameterFluxScheduler, + fluxDypePreset: zParameterFluxDypePreset, + fluxDypeScale: zParameterFluxDypeScale, + fluxDypeExponent: zParameterFluxDypeExponent, + zImageScheduler: zParameterZImageScheduler, + zImageShift: z.number().min(0).max(3).nullable(), + // Defaults make these resilient to rehydration of persisted state saved before the fields existed. + ernieImageScheduler: zParameterErnieImageScheduler.default('euler'), + ernieImageUsePromptEnhancer: z.boolean().default(true), + ideogram4SamplerPreset: zParameterIdeogram4SamplerPreset.default('V4_QUALITY_48'), + // Optional advanced overrides of the Ideogram 4 sampler preset (null = use the preset's value). + // Backend requires steps >= 2 (a polish and a main step). `.catch(null)` normalizes a stale/invalid + // persisted or recalled value (e.g. 1 from an older build) to null (= use preset) instead of letting an + // out-of-range value reach the graph or breaking the whole persisted params slice on rehydrate. + ideogram4Steps: z.number().int().min(2).max(100).nullable().catch(null).default(null), + ideogram4GuidanceScale: z.number().min(1).max(20).nullable().default(null), + ideogram4Mu: z.number().min(-4).max(4).nullable().default(null), + // Hex colors (#RRGGBB) injected into the JSON caption's style_description.color_palette. + ideogram4ColorPalette: z.array(z.string()).default([]), + upscaleScheduler: zParameterScheduler, + upscaleCfgScale: zParameterCFGScale, + seed: zParameterSeed, + shouldRandomizeSeed: z.boolean(), + steps: zParameterSteps, + model: zParameterModel.nullable(), + vae: zParameterVAEModel.nullable(), + vaePrecision: zParameterPrecision, + fluxVAE: zParameterVAEModel.nullable(), + seamlessXAxis: z.boolean(), + seamlessYAxis: z.boolean(), + clipSkip: z.number(), + shouldUseCpuNoise: z.boolean(), + colorCompensation: z.boolean(), + positivePrompt: zParameterPositivePrompt, + positivePromptHistory: zPositivePromptHistory, + negativePrompt: zParameterNegativePrompt, + refinerModel: zParameterSDXLRefinerModel.nullable(), + refinerSteps: z.number(), + refinerCFGScale: z.number(), + refinerScheduler: zParameterScheduler, + refinerPositiveAestheticScore: z.number(), + refinerNegativeAestheticScore: z.number(), + refinerStart: z.number(), + t5EncoderModel: zParameterT5EncoderModel.nullable(), + clipEmbedModel: zParameterCLIPEmbedModel.nullable(), + clipLEmbedModel: zParameterCLIPLEmbedModel.nullable(), + clipGEmbedModel: zParameterCLIPGEmbedModel.nullable(), + controlLora: zParameterControlLoRAModel.nullable(), + // Z-Image model components - can use separate models or extract from a Diffusers source + zImageVaeModel: zParameterVAEModel.nullable(), // Optional: Separate FLUX VAE + zImageQwen3EncoderModel: zModelIdentifierField.nullable(), // Optional: Separate Qwen3 Encoder + zImageQwen3SourceModel: zParameterModel.nullable(), // Diffusers Z-Image model (fallback for VAE/Encoder) + // Anima model components - uses Qwen3 0.6B + bundled T5-XXL tokenizer + QwenImage VAE + animaVaeModel: zParameterVAEModel.nullable(), // Optional: Separate QwenImage/FLUX VAE for Anima + animaQwen3EncoderModel: zModelIdentifierField.nullable(), // Optional: Separate Qwen3 0.6B Encoder for Anima + animaScheduler: zParameterAnimaScheduler, + animaLLLiteModel: zModelIdentifierField.nullable().default(null), // Optional: ControlNet-LLLite inpaint adapter for Anima + animaLLLiteWeight: z.number().min(-10).max(10).default(1), + // FLUX.2 VAE shared by Klein and [dev] — both use the same 32-channel AutoencoderKLFlux2 pool, + // so a single slot avoids losing the selection when switching a GGUF between the two. + flux2VaeModel: zParameterVAEModel.nullable(), // Optional: Separate FLUX.2 VAE (Klein + [dev]) + // Flux2 Klein text encoder - uses Qwen3 instead of CLIP+T5 + kleinQwen3EncoderModel: zModelIdentifierField.nullable(), // Optional: Separate Qwen3 Encoder for Klein + // Flux2 [dev] text encoder - uses Mistral Small 3.1 (24B) + flux2DevMistralEncoderModel: zModelIdentifierField.nullable(), // Optional: Standalone Mistral encoder for [dev] + // PiD (Pixel Diffusion Decoder) - optional 4x super-resolution decode replacing the VAE decode. + // - 'off': regular VAE decode + // - 'fit': PiD decodes 4x internally, then downscales back to the bbox (compositing-safe; works in canvas/inpaint) + // - 'native': PiD's full 4x output IS the result; the user-facing dimensions are the target, generation runs at target / 4 + // These four landed *after* the `_version` 3 -> 4 bump, so for as long as 4 was the current + // version no migration step could reach them: a blob already at v4 (dev builds from that window) + // matched no branch in the chain. They carry zod defaults for the same reason the ERNIE-Image + // fields above do — without one they would be required, and their absence would fail the parse in + // `migrate()` and wipe the whole slice. That is the rule for any field added after the last bump, + // which is why it still applies now that the v4 -> v5 step also seeds these. + pidMode: zPidMode.default('off'), + pidDecoderModel: zModelIdentifierField.nullable().default(null), // PiD decoder checkpoint (matched to the main model's base) + gemma2EncoderModel: zModelIdentifierField.nullable().default(null), // Gemma-2 caption encoder required by PiD + pidSteps: z.number().int().min(1).max(4).default(4), // PiD distill steps: student schedule has only 4 transitions, so 1-4 + // Qwen Image Edit model components - GGUF transformer needs a Diffusers source for VAE/encoder + qwenImageComponentSource: zParameterModel.nullable(), // Diffusers model providing VAE + text encoder + qwenImageVaeModel: zParameterVAEModel.nullable(), // Optional: Standalone Qwen Image VAE checkpoint + qwenImageQwenVLEncoderModel: zModelIdentifierField.nullable(), // Optional: Standalone Qwen2.5-VL encoder + qwenImageQuantization: z.enum(['none', 'int8', 'nf4']), // BitsAndBytes quantization for Qwen VL encoder + qwenImageShift: z.number().nullable(), // Sigma schedule shift override (e.g. 3.0 for Lightning LoRAs) + // Wan 2.2 model components — A14B GGUF needs a paired second-expert transformer + // plus a Diffusers source for VAE/T5 unless standalone VAE/encoder models are wired. + wanTransformerLowNoise: zParameterModel.nullable(), // A14B GGUF only: second-expert transformer + wanComponentSource: zParameterModel.nullable(), // Diffusers Wan model providing VAE + UMT5-XXL + wanVaeModel: zParameterVAEModel.nullable(), // Optional: Standalone Wan VAE checkpoint + wanT5EncoderModel: zModelIdentifierField.nullable(), // Optional: Standalone UMT5-XXL encoder + wanGuidanceScaleLowNoise: z.number().nullable(), // Optional: separate CFG for low-noise expert (A14B). null = same as primary + // MiniMax H3 joint audio-video generation (fixed 24 fps; frame counts snap to the 17n+5 grid, + // so the effective duration ceiling is 345 frames = 14.375 s). + minimaxH3DurationSeconds: z.number().int().min(5).max(14), + minimaxH3OutputMode: z.enum(['video', 'image']), + minimaxH3TransformerModel: zParameterModel.nullable(), // Optional: single-file transformer override (e.g. pruned int8) + minimaxH3TextEncoderModel: zParameterModel.nullable(), // Optional: single-file truncated Qwen3-VL encoder override (e.g. int8) + // Z-Image Seed Variance Enhancer settings + zImageSeedVarianceEnabled: z.boolean(), + zImageSeedVarianceStrength: z.number().min(0).max(2), + zImageSeedVarianceRandomizePercent: z.number().min(1).max(100), + // Krea-2 standalone submodels (optional; used when the transformer is a single-file checkpoint/GGUF + // that has no bundled VAE / Qwen3-VL encoder. When null, they are extracted from the diffusers model.) + krea2VaeModel: zParameterVAEModel.nullable(), + krea2Qwen3VlEncoderModel: zModelIdentifierField.nullable(), + // Krea-2 conditioning enhancers (optional; both default off so stock behaviour is unchanged) + krea2SeedVarianceEnabled: z.boolean(), + krea2SeedVarianceStrength: z.number().min(0).max(2), + krea2SeedVarianceRandomizePercent: z.number().min(0).max(100), + krea2RebalanceEnabled: z.boolean(), + krea2RebalanceMultiplier: z.number().min(0).max(20), + krea2RebalanceWeights: z.string(), + imageSize: z.string().nullable().default(null), + // OpenAI-specific external options + openaiQuality: z.enum(['auto', 'high', 'medium', 'low']).default('auto'), + openaiBackground: z.enum(['auto', 'transparent', 'opaque']).default('auto'), + openaiInputFidelity: z.enum(['low', 'high']).nullable().default(null), + // Gemini-specific external options + geminiTemperature: z.number().min(0).max(2).nullable().default(null), + geminiThinkingLevel: z.enum(['minimal', 'high']).nullable().default(null), + // Seedream-specific external options + seedreamWatermark: z.boolean().default(false), + seedreamOptimizePrompt: z.boolean().default(false), + dimensions: zDimensionsState, +}); +export type ParamsState = z.infer; +export const getInitialParamsState = (): ParamsState => ({ + _version: 8, + maskBlur: 16, + maskBlurMethod: 'box', + canvasCoherenceMode: 'Gaussian Blur', + canvasCoherenceMinDenoise: 0, + canvasCoherenceEdgeSize: 16, + infillMethod: 'lama', + infillTileSize: 32, + infillPatchmatchDownscaleSize: 1, + infillColorValue: { r: 0, g: 0, b: 0, a: 1 }, + cfgScale: 7.5, + cfgRescaleMultiplier: 0, + guidance: 4, + img2imgStrength: 0.75, + optimizedDenoisingEnabled: true, + hiDiffusionEnabled: false, + hiDiffusionRauNetEnabled: true, + hiDiffusionWindowAttnEnabled: true, + hiDiffusionT1Ratio: 0.4, + hiDiffusionT2Ratio: 0.0, + iterations: 1, + scheduler: 'dpmpp_3m_k', + fluxScheduler: 'euler', + fluxDypePreset: 'off', + fluxDypeScale: 2.0, + fluxDypeExponent: 2.0, + zImageScheduler: 'euler', + zImageShift: null, + ernieImageScheduler: 'euler', + ernieImageUsePromptEnhancer: true, + ideogram4SamplerPreset: 'V4_QUALITY_48', + ideogram4Steps: null, + ideogram4GuidanceScale: null, + ideogram4Mu: null, + ideogram4ColorPalette: [], + upscaleScheduler: 'kdpm_2', + upscaleCfgScale: 2, + seed: 0, + shouldRandomizeSeed: true, + steps: 30, + model: null, + vae: null, + vaePrecision: 'fp32', + fluxVAE: null, + seamlessXAxis: false, + seamlessYAxis: false, + clipSkip: 0, + shouldUseCpuNoise: true, + colorCompensation: false, + positivePrompt: '', + positivePromptHistory: [], + negativePrompt: null, + refinerModel: null, + refinerSteps: 20, + refinerCFGScale: 7.5, + refinerScheduler: 'euler', + refinerPositiveAestheticScore: 6, + refinerNegativeAestheticScore: 2.5, + refinerStart: 0.8, + t5EncoderModel: null, + clipEmbedModel: null, + clipLEmbedModel: null, + clipGEmbedModel: null, + controlLora: null, + zImageVaeModel: null, + zImageQwen3EncoderModel: null, + zImageQwen3SourceModel: null, + animaVaeModel: null, + animaQwen3EncoderModel: null, + animaScheduler: 'euler', + animaLLLiteModel: null, + animaLLLiteWeight: 1, + flux2VaeModel: null, + kleinQwen3EncoderModel: null, + flux2DevMistralEncoderModel: null, + pidMode: 'off', + pidDecoderModel: null, + gemma2EncoderModel: null, + pidSteps: 4, + qwenImageComponentSource: null, + qwenImageVaeModel: null, + qwenImageQwenVLEncoderModel: null, + qwenImageQuantization: 'none' as const, + qwenImageShift: null, + wanTransformerLowNoise: null, + wanComponentSource: null, + wanVaeModel: null, + wanT5EncoderModel: null, + wanGuidanceScaleLowNoise: null, + minimaxH3DurationSeconds: 5, + minimaxH3OutputMode: 'video', + minimaxH3TransformerModel: null, + minimaxH3TextEncoderModel: null, + zImageSeedVarianceEnabled: false, + zImageSeedVarianceStrength: 0.1, + zImageSeedVarianceRandomizePercent: 50, + krea2VaeModel: null, + krea2Qwen3VlEncoderModel: null, + krea2SeedVarianceEnabled: false, + krea2SeedVarianceStrength: 0.1, + krea2SeedVarianceRandomizePercent: 50, + krea2RebalanceEnabled: false, + krea2RebalanceMultiplier: 4, + krea2RebalanceWeights: '1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.5,5.0,1.1,4.0,1.0', + imageSize: null, + openaiQuality: 'auto', + openaiBackground: 'auto', + openaiInputFidelity: null, + geminiTemperature: null, + geminiThinkingLevel: null, + seedreamWatermark: false, + seedreamOptimizePrompt: false, + dimensions: { + width: 512, + height: 512, + aspectRatio: deepClone(DEFAULT_ASPECT_RATIO_CONFIG), + }, +}); + +const zInpaintMasks = z.object({ + isHidden: z.boolean(), + entities: z.array(zCanvasInpaintMaskState), +}); +const zRasterLayers = z.object({ + isHidden: z.boolean(), + entities: z.array(zCanvasRasterLayerState), +}); +const zControlLayers = z.object({ + isHidden: z.boolean(), + entities: z.array(zCanvasControlLayerState), +}); +const zRegionalGuidance = z.object({ + isHidden: z.boolean(), + entities: z.array(zCanvasRegionalGuidanceState), +}); +export const zCanvasState = z.object({ + _version: z.literal(3), + selectedEntityIdentifier: zCanvasEntityIdentifer.nullable(), + bookmarkedEntityIdentifier: zCanvasEntityIdentifer.nullable(), + inpaintMasks: zInpaintMasks, + rasterLayers: zRasterLayers, + controlLayers: zControlLayers, + regionalGuidance: zRegionalGuidance, + bbox: zBboxState, +}); +export type CanvasState = z.infer; +export const getInitialCanvasState = (): CanvasState => ({ + _version: 3, + selectedEntityIdentifier: null, + bookmarkedEntityIdentifier: null, + inpaintMasks: { isHidden: false, entities: [] }, + rasterLayers: { isHidden: false, entities: [] }, + controlLayers: { isHidden: false, entities: [] }, + regionalGuidance: { isHidden: false, entities: [] }, + bbox: { + rect: { x: 0, y: 0, width: 512, height: 512 }, + aspectRatio: deepClone(DEFAULT_ASPECT_RATIO_CONFIG), + scaleMethod: 'auto', + scaledSize: { width: 512, height: 512 }, + modelBase: 'sd-1', + }, +}); + +export const zRefImagesState = z.object({ + selectedEntityId: z.string().nullable(), + isPanelOpen: z.boolean(), + entities: z.array(zRefImageState), +}); +export type RefImagesState = z.infer; +export const getInitialRefImagesState = (): RefImagesState => ({ + selectedEntityId: null, + isPanelOpen: false, + entities: [], +}); + +export const zCanvasReferenceImageState_OLD = zCanvasEntityBase.extend({ + type: z.literal('reference_image'), + ipAdapter: z.discriminatedUnion('type', [zIPAdapterConfig, zFLUXReduxConfig]), +}); + +export const zCanvasMetadata = z.object({ + inpaintMasks: z.array(zCanvasInpaintMaskState), + rasterLayers: z.array(zCanvasRasterLayerState), + controlLayers: z.array(zCanvasControlLayerState), + regionalGuidance: z.array(zCanvasRegionalGuidanceState), + // referenceImages: z.array(zRefImageState), +}); +export type CanvasMetadata = z.infer; + +export type StageAttrs = { + x: Coordinate['x']; + y: Coordinate['y']; + width: Dimensions['width']; + height: Dimensions['height']; + scale: number; +}; + +export type EntityIdentifierPayload< + T extends JsonObject | void = void, + U extends CanvasEntityType = CanvasEntityType, +> = T extends void + ? { + entityIdentifier: CanvasEntityIdentifier; + } + : { + entityIdentifier: CanvasEntityIdentifier; + } & T; + +export type EntityMovedToPayload = EntityIdentifierPayload<{ position: Coordinate }>; +export type EntityMovedByPayload = EntityIdentifierPayload<{ offset: Coordinate }>; +export type EntityBrushLineAddedPayload = EntityIdentifierPayload<{ + brushLine: CanvasBrushLineState | CanvasBrushLineWithPressureState; +}>; +export type EntityEraserLineAddedPayload = EntityIdentifierPayload<{ + eraserLine: CanvasEraserLineState | CanvasEraserLineWithPressureState; +}>; +export type EntityLassoAddedPayload = EntityIdentifierPayload<{ lasso: CanvasLassoState }>; +export type EntityShapeAddedPayload = EntityIdentifierPayload<{ shape: CanvasShapeState }>; +export type EntityGradientAddedPayload = EntityIdentifierPayload<{ gradient: CanvasGradientState }>; +export type EntityRasterizedPayload = EntityIdentifierPayload<{ + imageObject: CanvasImageState; + position: Coordinate; + replaceObjects: boolean; + isSelected?: boolean; +}>; + +/** + * A helper type to remove `[index: string]: any;` from a type. + * This is useful for some Konva types that include `[index: string]: any;` in addition to statically named + * properties, effectively widening the type signature to `Record`. For example, `LineConfig`, + * `RectConfig`, `ImageConfig`, etc all include `[index: string]: any;` in their type signature. + * TODO(psyche): Fix this upstream. + */ +// export type RemoveIndexString = { +// [K in keyof T as string extends K ? never : K]: T[K]; +// }; + +export type GenerationMode = 'txt2img' | 'img2img' | 'inpaint' | 'outpaint'; + +export type CanvasEntityStateFromType = Extract; + +export function isRasterLayerEntityIdentifier( + entityIdentifier: CanvasEntityIdentifier +): entityIdentifier is CanvasEntityIdentifier<'raster_layer'> { + return entityIdentifier.type === 'raster_layer'; +} + +export function isControlLayerEntityIdentifier( + entityIdentifier: CanvasEntityIdentifier +): entityIdentifier is CanvasEntityIdentifier<'control_layer'> { + return entityIdentifier.type === 'control_layer'; +} + +export function isInpaintMaskEntityIdentifier( + entityIdentifier: CanvasEntityIdentifier +): entityIdentifier is CanvasEntityIdentifier<'inpaint_mask'> { + return entityIdentifier.type === 'inpaint_mask'; +} + +export function isRegionalGuidanceEntityIdentifier( + entityIdentifier: CanvasEntityIdentifier +): entityIdentifier is CanvasEntityIdentifier<'regional_guidance'> { + return entityIdentifier.type === 'regional_guidance'; +} + +export function isFilterableEntityIdentifier( + entityIdentifier: CanvasEntityIdentifier +): entityIdentifier is CanvasEntityIdentifier<'raster_layer'> | CanvasEntityIdentifier<'control_layer'> { + return isRasterLayerEntityIdentifier(entityIdentifier) || isControlLayerEntityIdentifier(entityIdentifier); +} + +export function isSegmentableEntityIdentifier( + entityIdentifier: CanvasEntityIdentifier +): entityIdentifier is CanvasEntityIdentifier<'raster_layer'> | CanvasEntityIdentifier<'control_layer'> { + return isRasterLayerEntityIdentifier(entityIdentifier) || isControlLayerEntityIdentifier(entityIdentifier); +} + +export function isTransformableEntityIdentifier( + entityIdentifier: CanvasEntityIdentifier +): entityIdentifier is + | CanvasEntityIdentifier<'raster_layer'> + | CanvasEntityIdentifier<'control_layer'> + | CanvasEntityIdentifier<'inpaint_mask'> + | CanvasEntityIdentifier<'regional_guidance'> { + return ( + isRasterLayerEntityIdentifier(entityIdentifier) || + isControlLayerEntityIdentifier(entityIdentifier) || + isInpaintMaskEntityIdentifier(entityIdentifier) || + isRegionalGuidanceEntityIdentifier(entityIdentifier) + ); +} + +export function isSaveableEntityIdentifier( + entityIdentifier: CanvasEntityIdentifier +): entityIdentifier is CanvasEntityIdentifier<'raster_layer'> | CanvasEntityIdentifier<'control_layer'> { + return isRasterLayerEntityIdentifier(entityIdentifier) || isControlLayerEntityIdentifier(entityIdentifier); +} + +export const getEntityIdentifier = ( + entity: Extract +): CanvasEntityIdentifier => { + return { id: entity.id, type: entity.type }; +}; + +export const isMaskEntityIdentifier = ( + entityIdentifier: CanvasEntityIdentifier +): entityIdentifier is CanvasEntityIdentifier<'inpaint_mask' | 'regional_guidance'> => { + return isInpaintMaskEntityIdentifier(entityIdentifier) || isRegionalGuidanceEntityIdentifier(entityIdentifier); +}; + +// Ideally, we'd type `adapter` as `CanvasEntityAdapterBase`, but the generics make this tricky. `CanvasEntityAdapter` +// is a union of all entity adapters and is functionally identical to `CanvasEntityAdapterBase`. +export type LifecycleCallback = (adapter: CanvasEntityAdapter) => Promise; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/util.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/util.ts similarity index 98% rename from invokeai/frontend/web/src/features/controlLayers/store/util.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/util.ts index b4a4ce75bfd..8cf4d172f2b 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/util.ts +++ b/invokeai/frontend/webv1/src/features/controlLayers/store/util.ts @@ -17,6 +17,7 @@ import type { ImageWithDims, IPAdapterConfig, Krea2ReferenceImageConfig, + MiniMaxH3ReferenceImageConfig, QwenImageReferenceImageConfig, RasterLayerAdjustments, RefImageState, @@ -129,6 +130,11 @@ export const initialWanReferenceImage: WanReferenceImageConfig = { type: 'wan_reference_image', image: null, }; +export const initialMiniMaxH3ReferenceImage: MiniMaxH3ReferenceImageConfig = { + type: 'minimax_h3_reference_image', + image: null, +}; + export const initialKrea2ReferenceImage: Krea2ReferenceImageConfig = { type: 'krea2_reference_image', image: null, diff --git a/invokeai/frontend/web/src/features/controlLayers/store/validators.krea2.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/validators.krea2.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/validators.krea2.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/validators.krea2.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/validators.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/validators.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/store/validators.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/validators.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/store/validators.ts b/invokeai/frontend/webv1/src/features/controlLayers/store/validators.ts similarity index 95% rename from invokeai/frontend/web/src/features/controlLayers/store/validators.ts rename to invokeai/frontend/webv1/src/features/controlLayers/store/validators.ts index e008cd2e24f..cbe291c4176 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/validators.ts +++ b/invokeai/frontend/webv1/src/features/controlLayers/store/validators.ts @@ -196,12 +196,13 @@ export const getGlobalReferenceImageWarnings = ( const { config } = entity; - // FLUX.2, Qwen Image Edit, Wan and Krea-2 reference images don't require a model - it's built-in + // FLUX.2, Qwen Image Edit, Wan, Krea-2 and MiniMax H3 reference images don't require a model - it's built-in if ( config.type !== 'flux2_reference_image' && config.type !== 'qwen_image_reference_image' && config.type !== 'wan_reference_image' && - config.type !== 'krea2_reference_image' + config.type !== 'krea2_reference_image' && + config.type !== 'minimax_h3_reference_image' ) { if (!('model' in config) || !config.model) { // No model selected @@ -213,10 +214,15 @@ export const getGlobalReferenceImageWarnings = ( } if (!entity.config.image) { - // No image selected - for Qwen Image Edit and Wan, an image is optional at the - // entity level. Wan I2V *requires* one but enforcement happens at graph-build - // time so the warning doesn't fire on T2V/TI2V variants that ignore ref images. - if (config.type !== 'qwen_image_reference_image' && config.type !== 'wan_reference_image') { + // No image selected - for Qwen Image Edit, Wan and MiniMax H3, an image is optional + // at the entity level. Wan I2V *requires* one but enforcement happens at graph-build + // time so the warning doesn't fire on T2V/TI2V variants that ignore ref images. For + // MiniMax H3 an empty ref image simply means plain text-to-video. + if ( + config.type !== 'qwen_image_reference_image' && + config.type !== 'wan_reference_image' && + config.type !== 'minimax_h3_reference_image' + ) { warnings.push(WARNINGS.IP_ADAPTER_NO_IMAGE_SELECTED); } } diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textCommit.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textCommit.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textCommit.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textCommit.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textCommit.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textCommit.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textCommit.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textCommit.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textConstants.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textConstants.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textConstants.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textConstants.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textConstants.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textConstants.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textConstants.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textConstants.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textHotkeys.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textHotkeys.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textHotkeys.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textHotkeys.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textHotkeys.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textHotkeys.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textHotkeys.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textHotkeys.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textRenderer.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textRenderer.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textRenderer.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textRenderer.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textRenderer.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textRenderer.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textRenderer.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textRenderer.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textSessionMachine.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textSessionMachine.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textSessionMachine.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textSessionMachine.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textSessionMachine.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textSessionMachine.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textSessionMachine.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textSessionMachine.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textUserFonts.test.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textUserFonts.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textUserFonts.test.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textUserFonts.test.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/text/textUserFonts.ts b/invokeai/frontend/webv1/src/features/controlLayers/text/textUserFonts.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/text/textUserFonts.ts rename to invokeai/frontend/webv1/src/features/controlLayers/text/textUserFonts.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/util/canvasProjectFile.ts b/invokeai/frontend/webv1/src/features/controlLayers/util/canvasProjectFile.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/util/canvasProjectFile.ts rename to invokeai/frontend/webv1/src/features/controlLayers/util/canvasProjectFile.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/util/getScaledBoundingBoxDimensions.ts b/invokeai/frontend/webv1/src/features/controlLayers/util/getScaledBoundingBoxDimensions.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/util/getScaledBoundingBoxDimensions.ts rename to invokeai/frontend/webv1/src/features/controlLayers/util/getScaledBoundingBoxDimensions.ts diff --git a/invokeai/frontend/web/src/features/controlLayers/util/simplify.ts b/invokeai/frontend/webv1/src/features/controlLayers/util/simplify.ts similarity index 100% rename from invokeai/frontend/web/src/features/controlLayers/util/simplify.ts rename to invokeai/frontend/webv1/src/features/controlLayers/util/simplify.ts diff --git a/invokeai/frontend/webv1/src/features/cropper/README.md b/invokeai/frontend/webv1/src/features/cropper/README.md new file mode 100644 index 00000000000..310d50c5c47 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/cropper/README.md @@ -0,0 +1,11 @@ +# Image cropper + +This is a simple image cropping canvas app built with KonvaJS ("native" Konva, _not_ the react bindings). + +The editor implementation is here: invokeai/frontend/webv1/src/features/cropper/lib/editor.ts + +It is rendered in a modal. + +Currently, the crop functionality is only exposed for reference images. These are the kind of images that most often need cropping (i.e. for FLUX Kontext, which is sensitive to the size/aspect ratio of its ref images). All ref image state is enriched to include a ref to the original image, the cropped image, and the crop attributes. + +The functionality could be extended to all images in the future, but there are some questions around whether we consider gallery images immutable. If so, we can't crop them in place. Do we instead add a new cropped image to the gallery? Or do we add a field to the image metadata that points to a cropped version of the image? diff --git a/invokeai/frontend/web/src/features/cropper/components/CropImageEditor.tsx b/invokeai/frontend/webv1/src/features/cropper/components/CropImageEditor.tsx similarity index 100% rename from invokeai/frontend/web/src/features/cropper/components/CropImageEditor.tsx rename to invokeai/frontend/webv1/src/features/cropper/components/CropImageEditor.tsx diff --git a/invokeai/frontend/web/src/features/cropper/components/CropImageModal.tsx b/invokeai/frontend/webv1/src/features/cropper/components/CropImageModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/cropper/components/CropImageModal.tsx rename to invokeai/frontend/webv1/src/features/cropper/components/CropImageModal.tsx diff --git a/invokeai/frontend/web/src/features/cropper/lib/editor.ts b/invokeai/frontend/webv1/src/features/cropper/lib/editor.ts similarity index 100% rename from invokeai/frontend/web/src/features/cropper/lib/editor.ts rename to invokeai/frontend/webv1/src/features/cropper/lib/editor.ts diff --git a/invokeai/frontend/web/src/features/cropper/store/index.ts b/invokeai/frontend/webv1/src/features/cropper/store/index.ts similarity index 100% rename from invokeai/frontend/web/src/features/cropper/store/index.ts rename to invokeai/frontend/webv1/src/features/cropper/store/index.ts diff --git a/invokeai/frontend/web/src/features/customNodes/CustomNodesInstallLog.tsx b/invokeai/frontend/webv1/src/features/customNodes/CustomNodesInstallLog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/customNodes/CustomNodesInstallLog.tsx rename to invokeai/frontend/webv1/src/features/customNodes/CustomNodesInstallLog.tsx diff --git a/invokeai/frontend/web/src/features/customNodes/CustomNodesInstallPane.tsx b/invokeai/frontend/webv1/src/features/customNodes/CustomNodesInstallPane.tsx similarity index 100% rename from invokeai/frontend/web/src/features/customNodes/CustomNodesInstallPane.tsx rename to invokeai/frontend/webv1/src/features/customNodes/CustomNodesInstallPane.tsx diff --git a/invokeai/frontend/web/src/features/customNodes/CustomNodesList.tsx b/invokeai/frontend/webv1/src/features/customNodes/CustomNodesList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/customNodes/CustomNodesList.tsx rename to invokeai/frontend/webv1/src/features/customNodes/CustomNodesList.tsx diff --git a/invokeai/frontend/web/src/features/customNodes/InstallFromGitForm.tsx b/invokeai/frontend/webv1/src/features/customNodes/InstallFromGitForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/customNodes/InstallFromGitForm.tsx rename to invokeai/frontend/webv1/src/features/customNodes/InstallFromGitForm.tsx diff --git a/invokeai/frontend/web/src/features/customNodes/ScanNodesForm.tsx b/invokeai/frontend/webv1/src/features/customNodes/ScanNodesForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/customNodes/ScanNodesForm.tsx rename to invokeai/frontend/webv1/src/features/customNodes/ScanNodesForm.tsx diff --git a/invokeai/frontend/web/src/features/customNodes/useCustomNodesInstallLog.test.ts b/invokeai/frontend/webv1/src/features/customNodes/useCustomNodesInstallLog.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/customNodes/useCustomNodesInstallLog.test.ts rename to invokeai/frontend/webv1/src/features/customNodes/useCustomNodesInstallLog.test.ts diff --git a/invokeai/frontend/web/src/features/customNodes/useCustomNodesInstallLog.ts b/invokeai/frontend/webv1/src/features/customNodes/useCustomNodesInstallLog.ts similarity index 100% rename from invokeai/frontend/web/src/features/customNodes/useCustomNodesInstallLog.ts rename to invokeai/frontend/webv1/src/features/customNodes/useCustomNodesInstallLog.ts diff --git a/invokeai/frontend/web/src/features/customNodes/useIsCustomNodesEnabled.test.ts b/invokeai/frontend/webv1/src/features/customNodes/useIsCustomNodesEnabled.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/customNodes/useIsCustomNodesEnabled.test.ts rename to invokeai/frontend/webv1/src/features/customNodes/useIsCustomNodesEnabled.test.ts diff --git a/invokeai/frontend/web/src/features/customNodes/useIsCustomNodesEnabled.ts b/invokeai/frontend/webv1/src/features/customNodes/useIsCustomNodesEnabled.ts similarity index 100% rename from invokeai/frontend/web/src/features/customNodes/useIsCustomNodesEnabled.ts rename to invokeai/frontend/webv1/src/features/customNodes/useIsCustomNodesEnabled.ts diff --git a/invokeai/frontend/webv1/src/features/deleteImageModal/README.md b/invokeai/frontend/webv1/src/features/deleteImageModal/README.md new file mode 100644 index 00000000000..674abee31a1 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/deleteImageModal/README.md @@ -0,0 +1,7 @@ +# Delete image modal + +When users delete images, we show a confirmation dialog to prevent accidental deletions. Users can opt out of this, but we still check if dleeting an image would screw up their workspace and prompt if so. + +For example, if an image is currently set as a field in the workflow editor, we warn the user that deleting it will remove it from the node. We warn them even if they have opted out of the confirmation dialog. + +These "image usage" checks are done using redux selectors/util functions. See invokeai/frontend/webv1/src/features/deleteImageModal/store/state.ts diff --git a/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx b/invokeai/frontend/webv1/src/features/deleteImageModal/components/DeleteImageButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx rename to invokeai/frontend/webv1/src/features/deleteImageModal/components/DeleteImageButton.tsx diff --git a/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageModal.tsx b/invokeai/frontend/webv1/src/features/deleteImageModal/components/DeleteImageModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageModal.tsx rename to invokeai/frontend/webv1/src/features/deleteImageModal/components/DeleteImageModal.tsx diff --git a/invokeai/frontend/web/src/features/deleteImageModal/components/ImageUsageMessage.tsx b/invokeai/frontend/webv1/src/features/deleteImageModal/components/ImageUsageMessage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/deleteImageModal/components/ImageUsageMessage.tsx rename to invokeai/frontend/webv1/src/features/deleteImageModal/components/ImageUsageMessage.tsx diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/state.test.ts b/invokeai/frontend/webv1/src/features/deleteImageModal/store/state.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/deleteImageModal/store/state.test.ts rename to invokeai/frontend/webv1/src/features/deleteImageModal/store/state.test.ts diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/state.ts b/invokeai/frontend/webv1/src/features/deleteImageModal/store/state.ts similarity index 100% rename from invokeai/frontend/web/src/features/deleteImageModal/store/state.ts rename to invokeai/frontend/webv1/src/features/deleteImageModal/store/state.ts diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/types.ts b/invokeai/frontend/webv1/src/features/deleteImageModal/store/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/deleteImageModal/store/types.ts rename to invokeai/frontend/webv1/src/features/deleteImageModal/store/types.ts diff --git a/invokeai/frontend/web/src/features/deleteVideoModal/components/DeleteVideoModal.tsx b/invokeai/frontend/webv1/src/features/deleteVideoModal/components/DeleteVideoModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/deleteVideoModal/components/DeleteVideoModal.tsx rename to invokeai/frontend/webv1/src/features/deleteVideoModal/components/DeleteVideoModal.tsx diff --git a/invokeai/frontend/web/src/features/deleteVideoModal/store/state.test.ts b/invokeai/frontend/webv1/src/features/deleteVideoModal/store/state.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/deleteVideoModal/store/state.test.ts rename to invokeai/frontend/webv1/src/features/deleteVideoModal/store/state.test.ts diff --git a/invokeai/frontend/web/src/features/deleteVideoModal/store/state.ts b/invokeai/frontend/webv1/src/features/deleteVideoModal/store/state.ts similarity index 100% rename from invokeai/frontend/web/src/features/deleteVideoModal/store/state.ts rename to invokeai/frontend/webv1/src/features/deleteVideoModal/store/state.ts diff --git a/invokeai/frontend/web/src/features/dnd/DndDragPreviewMultipleImage.tsx b/invokeai/frontend/webv1/src/features/dnd/DndDragPreviewMultipleImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dnd/DndDragPreviewMultipleImage.tsx rename to invokeai/frontend/webv1/src/features/dnd/DndDragPreviewMultipleImage.tsx diff --git a/invokeai/frontend/web/src/features/dnd/DndDragPreviewSingleImage.tsx b/invokeai/frontend/webv1/src/features/dnd/DndDragPreviewSingleImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dnd/DndDragPreviewSingleImage.tsx rename to invokeai/frontend/webv1/src/features/dnd/DndDragPreviewSingleImage.tsx diff --git a/invokeai/frontend/web/src/features/dnd/DndDropOverlay.tsx b/invokeai/frontend/webv1/src/features/dnd/DndDropOverlay.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dnd/DndDropOverlay.tsx rename to invokeai/frontend/webv1/src/features/dnd/DndDropOverlay.tsx diff --git a/invokeai/frontend/web/src/features/dnd/DndDropTarget.tsx b/invokeai/frontend/webv1/src/features/dnd/DndDropTarget.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dnd/DndDropTarget.tsx rename to invokeai/frontend/webv1/src/features/dnd/DndDropTarget.tsx diff --git a/invokeai/frontend/web/src/features/dnd/DndImage.tsx b/invokeai/frontend/webv1/src/features/dnd/DndImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dnd/DndImage.tsx rename to invokeai/frontend/webv1/src/features/dnd/DndImage.tsx diff --git a/invokeai/frontend/web/src/features/dnd/DndImageIcon.tsx b/invokeai/frontend/webv1/src/features/dnd/DndImageIcon.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dnd/DndImageIcon.tsx rename to invokeai/frontend/webv1/src/features/dnd/DndImageIcon.tsx diff --git a/invokeai/frontend/web/src/features/dnd/DndListDropIndicator.tsx b/invokeai/frontend/webv1/src/features/dnd/DndListDropIndicator.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dnd/DndListDropIndicator.tsx rename to invokeai/frontend/webv1/src/features/dnd/DndListDropIndicator.tsx diff --git a/invokeai/frontend/web/src/features/dnd/FullscreenDropzone.tsx b/invokeai/frontend/webv1/src/features/dnd/FullscreenDropzone.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dnd/FullscreenDropzone.tsx rename to invokeai/frontend/webv1/src/features/dnd/FullscreenDropzone.tsx diff --git a/invokeai/frontend/webv1/src/features/dnd/README.md b/invokeai/frontend/webv1/src/features/dnd/README.md new file mode 100644 index 00000000000..1bdfd67a025 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/dnd/README.md @@ -0,0 +1,41 @@ +# Drag and drop + +Dnd functionality is implemented with https://github.com/atlassian/pragmatic-drag-and-drop, the successor to https://github.com/atlassian/react-beautiful-dnd + +It uses the native HTML5 drag and drop API and is very performant, though a bit more involved to set up. The library doesn't expose a react API, but rather a set of utility functions to hook into the drag and drop events. + +## Implementation + +The core of our implementation is in invokeai/frontend/webv1/src/features/dnd/dnd.ts + +We support dragging and dropping of single or multiple images within the app. We have "dnd source" and "dnd target" abstractions. + +A dnd source is is anything that provides the draggable payload/data. Currently, that's either an image DTO or list of image names along with their origin board. + +A dnd target is anything that can accept a drop of that payload. Targets have their own data. For example, a target might be a board with a board ID, or a canvas layer with a layer ID. + +The library has a concept of draggable elements (dnd sources), droppable elements (dnd targets), and dnd monitors. The monitors are invisible elements that track drag events and provide information about the current drag operation. + +The library is a bit to wrap your head around but once you understand the concepts, it's very nice to work with and super flexible. + +## Type safety + +Native drag events do not have any built-in type safety. We inject a unique symbol into the sources and targets and check that via typeguard functions. This gives us confidence that the payload is what we expect it to be and not some other data that might have been dropped from outside the app or some other source. + +## Defining sources and targets + +These are strictly typed in the dnd.ts file. Follow the examples there to define new sources and targets. + +Targets are more complicated - they get an isValid callback (which is called with the currently-dragged source to determine if it can accept the drop) and a handler callback (which is called when the drop is made). + +Both isValid and handler get the source data, target data, and the redux getState/dispatch functions. They can do whatever they need to do to determine if the drop is valid and to handle the drop. + +Typically the isValid function just uses the source type guard function, and the handler function dispatches one or more redux actions to update the state. + +## Other uses of Dnd + +We use the same library for other dnd things: + +- When dragging over some tabbed interface, hovering the tab for a moment will switch to it. See invokeai/frontend/webv1/src/common/hooks/useCallbackOnDragEnter.ts for a hook that implements this functionality. +- Reordering of canvas layer lists. See invokeai/frontend/webv1/src/features/controlLayers/components/CanvasEntityList/CanvasEntityGroupList.tsx and invokeai/frontend/webv1/src/features/controlLayers/components/CanvasEntityList/useCanvasEntityListDnd.ts +- Adding node fields to a workflow form builder and restructuring the form. This gets kinda complicated, as the form builder supports arbitrary nesting of containers with stacking of elements. See invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/dnd-hooks.ts diff --git a/invokeai/frontend/web/src/features/dnd/dnd.ts b/invokeai/frontend/webv1/src/features/dnd/dnd.ts similarity index 100% rename from invokeai/frontend/web/src/features/dnd/dnd.ts rename to invokeai/frontend/webv1/src/features/dnd/dnd.ts diff --git a/invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.test.ts b/invokeai/frontend/webv1/src/features/dnd/fullscreenDropzoneAccept.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.test.ts rename to invokeai/frontend/webv1/src/features/dnd/fullscreenDropzoneAccept.test.ts diff --git a/invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.ts b/invokeai/frontend/webv1/src/features/dnd/fullscreenDropzoneAccept.ts similarity index 100% rename from invokeai/frontend/web/src/features/dnd/fullscreenDropzoneAccept.ts rename to invokeai/frontend/webv1/src/features/dnd/fullscreenDropzoneAccept.ts diff --git a/invokeai/frontend/web/src/features/dnd/types.ts b/invokeai/frontend/webv1/src/features/dnd/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/dnd/types.ts rename to invokeai/frontend/webv1/src/features/dnd/types.ts diff --git a/invokeai/frontend/web/src/features/dnd/useDndMonitor.ts b/invokeai/frontend/webv1/src/features/dnd/useDndMonitor.ts similarity index 100% rename from invokeai/frontend/web/src/features/dnd/useDndMonitor.ts rename to invokeai/frontend/webv1/src/features/dnd/useDndMonitor.ts diff --git a/invokeai/frontend/web/src/features/dnd/util.ts b/invokeai/frontend/webv1/src/features/dnd/util.ts similarity index 100% rename from invokeai/frontend/web/src/features/dnd/util.ts rename to invokeai/frontend/webv1/src/features/dnd/util.ts diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/README.md b/invokeai/frontend/webv1/src/features/dynamicPrompts/README.md similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/README.md rename to invokeai/frontend/webv1/src/features/dynamicPrompts/README.md diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx b/invokeai/frontend/webv1/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx rename to invokeai/frontend/webv1/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx b/invokeai/frontend/webv1/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx rename to invokeai/frontend/webv1/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx b/invokeai/frontend/webv1/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx rename to invokeai/frontend/webv1/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx b/invokeai/frontend/webv1/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx rename to invokeai/frontend/webv1/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx b/invokeai/frontend/webv1/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx rename to invokeai/frontend/webv1/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/hooks/useDynamicPromptsModal.ts b/invokeai/frontend/webv1/src/features/dynamicPrompts/hooks/useDynamicPromptsModal.ts similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/hooks/useDynamicPromptsModal.ts rename to invokeai/frontend/webv1/src/features/dynamicPrompts/hooks/useDynamicPromptsModal.ts diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/hooks/useDynamicPromptsWatcher.tsx b/invokeai/frontend/webv1/src/features/dynamicPrompts/hooks/useDynamicPromptsWatcher.tsx similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/hooks/useDynamicPromptsWatcher.tsx rename to invokeai/frontend/webv1/src/features/dynamicPrompts/hooks/useDynamicPromptsWatcher.tsx diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/store/dynamicPromptsSlice.ts b/invokeai/frontend/webv1/src/features/dynamicPrompts/store/dynamicPromptsSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/store/dynamicPromptsSlice.ts rename to invokeai/frontend/webv1/src/features/dynamicPrompts/store/dynamicPromptsSlice.ts diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/util/getShouldProcessPrompt.ts b/invokeai/frontend/webv1/src/features/dynamicPrompts/util/getShouldProcessPrompt.ts similarity index 100% rename from invokeai/frontend/web/src/features/dynamicPrompts/util/getShouldProcessPrompt.ts rename to invokeai/frontend/webv1/src/features/dynamicPrompts/util/getShouldProcessPrompt.ts diff --git a/invokeai/frontend/webv1/src/features/gallery/README.md b/invokeai/frontend/webv1/src/features/gallery/README.md new file mode 100644 index 00000000000..e9a12b01043 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/gallery/README.md @@ -0,0 +1,77 @@ +# Gallery Overview + +The gallery renders a scrollable grid of images. The image sizes adapt to the viewport size, and the user can scroll to any part of their gallery. It supports keyboard navigation, multi-select and context menus. Images can be dragged from the gallery to use them in other parts of the app (they are not removed from the gallery). + +There is some basic ordering and searching support. + +## Boards + +Boards act as folders for images. + +- Users can create any number of boards. +- Each image can be assigned to at most one board. +- There is a default "no board" board, labeled "Uncategorized". +- User-created boards can be deleted. The no-board board cannot be deleted. +- When deleting a board, users can choose to either delete all images in the board, or move them to the no-board board. +- User-created boards can be renamed. The no-board board cannot be renamed. +- Boards cannot be nested. +- Boards can be archived, which hides them from the board list. +- There is no way to show all images at once. The gallery view always shows images for a specific board. +- Boards can be selected to show their images in the panel below the boards list; the gallery grid. +- Boards can be set as the "auto-add" board. New images will be added to this board as they are generated. + +## Image viewer + +Clicking an image in the gallery opens it in the image viewer, which presents a larger view of the image, along with a variety of image actions. + +The image viewer is rendered in one of the main/center panel tabs. + +### Image actions + +A handful of common actions are available as buttons in the image viewer header, matching the context menu actions. + +See invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/README.md + +### Progress viewer + +During generation, we might get "progress images" showing a low-res version of the image at each step in the denoising process. If these are available, the user can open a progress viewer overlay to see the image at each step. + +Socket subscriptions and related logic for handling progress images are in the image viewer context. See invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/context.tsx + +### Metadata viewer + +The user can enable a metadata overlay to view the image metadata. This is rendered as a semi-transparent overlay on top of the image. + +"Metadata" refers to key-value pairs of various settings. For example, the prompt, number of steps and model used to generate the image. This metadata is embedded into the image file itself, but also stored in the database for searching and filtering. + +Images also have the execution graph embedded in them. This isn't stored in the database, as it can be large and complex. Instead, we extract it from the image when needed. + +Metadata can be recalled, and the graph can be loaded into the workflow editor. + +### Image comparison + +Users can hold Alt when click an image in the gallery to select it as the "comparison" image. The comparison image is shown alongside the current image in the image viewer with a couple modes (slider, side-by-side, hover-to-swap). + +## Data fetching + +The gallery uses a windowed list to only render the images that are currently visible in the viewport. + +It starts by loading a list of all image names for the selected board or view settings. react-virtuoso reports on the currently-visible range of images (plus some "overscan"). We then fetch the full image DTOs only for those images, which are cached by RTK Query. As the user scrolls, the visible range changes and we fetch more image DTOs as needed. + +This affords a nice UX, where the user can scroll to any part of their gallery. The scrollbar size never changes. + +We've tried some other approaches in the past, but they all had significant UX or implementation issues: + +### Infinite scroll + +Load an initial chunk of images, then load more as the user scrolls to the bottom. + +The scrollbar continually shrinks as more images are loaded. + +This yields a poor UX, as the user cannot easily scroll to a specific part of their gallery. It's also pretty complicated to implement within RTK Query, though since we switched, RTK Query now supports infinite queries. It might be easier to do this today. + +### Traditional pagination + +Show a fixed number of images per page, with pagination controls. + +This is a poor UX, as the user cannot easily scroll to a specific part of their gallery. Gallerys are often very large, and the page size changes depending on the viewport size. The gallery is also constantly inserting new images at the top of the list, which means we are constanty invalidating the current page's query cache and the page numbers are not stable. diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/AutoAddBadge.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/AutoAddBadge.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/AutoAddBadge.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/AutoAddBadge.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardContextMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardContextMenu.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AddBoardButton.test.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/AddBoardButton.test.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AddBoardButton.test.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/AddBoardButton.test.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AddBoardButton.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/AddBoardButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AddBoardButton.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/AddBoardButton.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardEditableTitle.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardEditableTitle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardEditableTitle.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardEditableTitle.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardTooltip.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardsList.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsListWrapper.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardsListWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsListWrapper.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardsListWrapper.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/BoardsSearch.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/VirtualBoardItem.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/VirtualBoardItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/VirtualBoardItem.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/VirtualBoardItem.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/VirtualBoardSection.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/VirtualBoardSection.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/VirtualBoardSection.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsList/VirtualBoardSection.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsListSortControls.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsListSortControls.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsListSortControls.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsListSortControls.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsSettingsPopover.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsSettingsPopover.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/BoardsSettingsPopover.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/BoardsSettingsPopover.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/DeleteBoardModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/DeleteBoardModal.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/DeleteBoardModal.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/NoBoardBoardContextMenu.tsx b/invokeai/frontend/webv1/src/features/gallery/components/Boards/NoBoardBoardContextMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/NoBoardBoardContextMenu.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/NoBoardBoardContextMenu.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/getMediaDeletionSummary.test.ts b/invokeai/frontend/webv1/src/features/gallery/components/Boards/getMediaDeletionSummary.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/getMediaDeletionSummary.test.ts rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/getMediaDeletionSummary.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/getMediaDeletionSummary.ts b/invokeai/frontend/webv1/src/features/gallery/components/Boards/getMediaDeletionSummary.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/Boards/getMediaDeletionSummary.ts rename to invokeai/frontend/webv1/src/features/gallery/components/Boards/getMediaDeletionSummary.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/BoardsListPanelContent.tsx b/invokeai/frontend/webv1/src/features/gallery/components/BoardsListPanelContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/BoardsListPanelContent.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/BoardsListPanelContent.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/ImageContextMenu.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/ImageContextMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/ImageContextMenu.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/ImageContextMenu.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoard.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoard.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoard.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoard.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoardVideo.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoardVideo.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoardVideo.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemChangeBoardVideo.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemCopy.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemCopy.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemCopy.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemCopy.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteImage.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteImage.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteImage.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteVideo.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteVideo.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteVideo.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDeleteVideo.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownload.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownload.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownload.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownload.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownloadVideo.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownloadVideo.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownloadVideo.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemDownloadVideo.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemFiltersSubMenu.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemFiltersSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemFiltersSubMenu.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemFiltersSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemLoadWorkflow.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemLoadWorkflow.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemLoadWorkflow.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemLoadWorkflow.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemLocateInGalery.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemLocateInGalery.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemLocateInGalery.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemLocateInGalery.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemMetadataRecallActionsCanvasGenerateTabs.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemMetadataRecallActionsCanvasGenerateTabs.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemMetadataRecallActionsCanvasGenerateTabs.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemMetadataRecallActionsCanvasGenerateTabs.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemMetadataRecallActionsUpscaleTab.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemMetadataRecallActionsUpscaleTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemMetadataRecallActionsUpscaleTab.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemMetadataRecallActionsUpscaleTab.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemNewCanvasFromImageSubMenu.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemNewCanvasFromImageSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemNewCanvasFromImageSubMenu.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemNewCanvasFromImageSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemNewLayerFromImageSubMenu.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemNewLayerFromImageSubMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemNewLayerFromImageSubMenu.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemNewLayerFromImageSubMenu.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTab.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTab.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTab.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTabVideo.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTabVideo.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTabVideo.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInNewTabVideo.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInViewer.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInViewer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInViewer.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemOpenInViewer.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemSelectForCompare.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemSelectForCompare.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemSelectForCompare.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemSelectForCompare.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemSendToUpscale.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemSendToUpscale.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemSendToUpscale.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemSendToUpscale.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemStarUnstar.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemStarUnstar.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemStarUnstar.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemStarUnstar.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemUseAsPromptTemplate.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemUseAsPromptTemplate.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemUseAsPromptTemplate.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemUseAsPromptTemplate.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemUseAsRefImage.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemUseAsRefImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemUseAsRefImage.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MenuItems/ContextMenuItemUseAsRefImage.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItems.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItems.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItems.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItems.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItemsVideos.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItemsVideos.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItemsVideos.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/MultipleSelectionMenuItemsVideos.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/README.md b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/README.md similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/README.md rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/README.md diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/SingleSelectionMenuItems.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/SingleSelectionMenuItems.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/SingleSelectionMenuItems.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/SingleSelectionMenuItems.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ContextMenu/VideoContextMenu.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/VideoContextMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ContextMenu/VideoContextMenu.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ContextMenu/VideoContextMenu.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryImageGrid.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GalleryImageGrid.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GalleryImageGrid.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GalleryImageGrid.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryImageGridPaged.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GalleryImageGridPaged.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GalleryImageGridPaged.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GalleryImageGridPaged.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryPanel.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GalleryPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GalleryPanel.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GalleryPanel.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/AlwaysShowImageSizeCheckbox.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/AlwaysShowImageSizeCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/AlwaysShowImageSizeCheckbox.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/AlwaysShowImageSizeCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/AutoAssignBoardCheckbox.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/AutoAssignBoardCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/AutoAssignBoardCheckbox.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/AutoAssignBoardCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/AutoSwitchCheckbox.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/AutoSwitchCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/AutoSwitchCheckbox.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/AutoSwitchCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/GallerySettingsPopover.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/GallerySettingsPopover.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/GallerySettingsPopover.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/GallerySettingsPopover.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/ImageMinimumWidthSlider.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/ImageMinimumWidthSlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/ImageMinimumWidthSlider.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/ImageMinimumWidthSlider.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/ShowArchivedBoardsCheckbox.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/ShowArchivedBoardsCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/ShowArchivedBoardsCheckbox.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/ShowArchivedBoardsCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/ShowStarredFirstCheckbox.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/ShowStarredFirstCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/ShowStarredFirstCheckbox.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/ShowStarredFirstCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/ShowVirtualBoardsCheckbox.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/ShowVirtualBoardsCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/ShowVirtualBoardsCheckbox.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/ShowVirtualBoardsCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/SortDirectionCombobox.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/SortDirectionCombobox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/SortDirectionCombobox.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/SortDirectionCombobox.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/UsePagedGalleryViewCheckbox.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/UsePagedGalleryViewCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover/UsePagedGalleryViewCheckbox.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GallerySettingsPopover/UsePagedGalleryViewCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryUploadButton.tsx b/invokeai/frontend/webv1/src/features/gallery/components/GalleryUploadButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/GalleryUploadButton.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/GalleryUploadButton.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryImage.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemDeleteIconButton.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemDeleteIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemDeleteIconButton.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemDeleteIconButton.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemHoverIcons.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemHoverIcons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemHoverIcons.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemHoverIcons.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemOpenInViewerIconButton.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemOpenInViewerIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemOpenInViewerIconButton.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemOpenInViewerIconButton.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemPlayBadge.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemPlayBadge.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemPlayBadge.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemPlayBadge.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemSizeBadge.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemSizeBadge.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemSizeBadge.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemSizeBadge.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemStarIconButton.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemStarIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemStarIconButton.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemStarIconButton.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemVideoStarIconButton.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemVideoStarIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryItemVideoStarIconButton.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryItemVideoStarIconButton.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryPaginationPaged.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryPaginationPaged.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryPaginationPaged.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryPaginationPaged.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GallerySearch.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GallerySearch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GallerySearch.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GallerySearch.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GallerySelectionCountTag.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GallerySelectionCountTag.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GallerySelectionCountTag.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GallerySelectionCountTag.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryVideoItem.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryVideoItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryVideoItem.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryVideoItem.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryVideoThumbnail.test.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryVideoThumbnail.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryVideoThumbnail.test.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryVideoThumbnail.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryVideoThumbnail.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryVideoThumbnail.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryVideoThumbnail.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/GalleryVideoThumbnail.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/JumpToPaged.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/JumpToPaged.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/JumpToPaged.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/JumpToPaged.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/galleryItemContainerSX.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/galleryItemContainerSX.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/galleryItemContainerSX.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/galleryItemContainerSX.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/useGallerySearchTerm.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/useGallerySearchTerm.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageGrid/useGallerySearchTerm.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageGrid/useGallerySearchTerm.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/DataViewer.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/DataViewer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/DataViewer.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/DataViewer.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx similarity index 89% rename from invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx index 934f4291464..fa383e2ac36 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx +++ b/invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.test.tsx @@ -45,6 +45,18 @@ describe('IMAGE_METADATA_ACTION_HANDLERS', () => { } }); + it('includes every MiniMax H3 metadata handler in the recall parameters UI', () => { + const handlers = [ + ImageMetadataHandlers.MiniMaxH3DurationSeconds, + ImageMetadataHandlers.MiniMaxH3OutputMode, + ImageMetadataHandlers.MiniMaxH3TransformerModel, + ImageMetadataHandlers.MiniMaxH3TextEncoderModel, + ]; + for (const handler of handlers) { + expect(IMAGE_METADATA_ACTION_HANDLERS).toContain(handler); + } + }); + it('has no un-triaged handler missing from the recall parameters UI', () => { // Guard against the list drifting behind the handler registry again: every new handler must either be // listed above or be added here with a reason. The second group is a snapshot of pre-existing gaps — diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx similarity index 97% rename from invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx index c6699cdf7ec..e854e00a631 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx +++ b/invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx @@ -78,6 +78,10 @@ export const IMAGE_METADATA_ACTION_HANDLERS: ImageMetadataActionHandler[] = [ ImageMetadataHandlers.ZImageQwen3SourceModel, ImageMetadataHandlers.AnimaVAEModel, ImageMetadataHandlers.AnimaQwen3EncoderModel, + ImageMetadataHandlers.MiniMaxH3DurationSeconds, + ImageMetadataHandlers.MiniMaxH3OutputMode, + ImageMetadataHandlers.MiniMaxH3TransformerModel, + ImageMetadataHandlers.MiniMaxH3TextEncoderModel, ImageMetadataHandlers.Krea2VAEModel, ImageMetadataHandlers.Krea2Qwen3VlEncoderModel, ImageMetadataHandlers.Krea2SeedVarianceEnabled, diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataGraphTabContent.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataGraphTabContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataGraphTabContent.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataGraphTabContent.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/VideoMetadataViewer.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/VideoMetadataViewer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/VideoMetadataViewer.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageMetadataViewer/VideoMetadataViewer.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CompareToolbar.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CompareToolbar.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/CompareToolbar.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CompareToolbar.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.test.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentImagePreview.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.test.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentImagePreview.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentVideoButtons.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentVideoButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentVideoButtons.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentVideoButtons.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentVideoPreview.test.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentVideoPreview.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentVideoPreview.test.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentVideoPreview.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentVideoPreview.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentVideoPreview.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentVideoPreview.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/CurrentVideoPreview.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparison.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparison.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparison.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparison.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonDroppable.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonDroppable.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonDroppable.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonDroppable.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonLabel.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonLabel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonLabel.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonLabel.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSideBySide.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonSideBySide.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSideBySide.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonSideBySide.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageViewer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageViewer.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerPanel.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageViewerPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerPanel.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageViewerPanel.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerToolbar.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageViewerToolbar.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerToolbar.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ImageViewerToolbar.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/NoContentForViewer.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/NoContentForViewer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/NoContentForViewer.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/NoContentForViewer.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressImage2.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ProgressImage2.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressImage2.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ProgressImage2.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressImageTiles.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ProgressImageTiles.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressImageTiles.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ProgressImageTiles.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressIndicator2.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ProgressIndicator2.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressIndicator2.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ProgressIndicator2.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleProgressButton.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ToggleProgressButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleProgressButton.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/ToggleProgressButton.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/VideoPlayButtonOverlay.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/VideoPlayButtonOverlay.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/VideoPlayButtonOverlay.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/VideoPlayButtonOverlay.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/common.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/common.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/common.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/common.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/context.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/context.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/context.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/context.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/selectedItemReveal.test.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/selectedItemReveal.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/selectedItemReveal.test.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/selectedItemReveal.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/selectedItemReveal.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/selectedItemReveal.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/selectedItemReveal.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/selectedItemReveal.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/useSelectedItemReveal.test.tsx b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/useSelectedItemReveal.test.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/useSelectedItemReveal.test.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/useSelectedItemReveal.test.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/useSelectedItemReveal.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/useSelectedItemReveal.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/useSelectedItemReveal.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/useSelectedItemReveal.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/useToggleMetadataHotkey.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/useToggleMetadataHotkey.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/useToggleMetadataHotkey.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/useToggleMetadataHotkey.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/viewerProgressLifecycle.test.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/viewerProgressLifecycle.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/viewerProgressLifecycle.test.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/viewerProgressLifecycle.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/viewerProgressLifecycle.ts b/invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/viewerProgressLifecycle.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/ImageViewer/viewerProgressLifecycle.ts rename to invokeai/frontend/webv1/src/features/gallery/components/ImageViewer/viewerProgressLifecycle.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/NextPrevItemButtons.tsx b/invokeai/frontend/webv1/src/features/gallery/components/NextPrevItemButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/NextPrevItemButtons.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/NextPrevItemButtons.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/getItemIndex.tsx b/invokeai/frontend/webv1/src/features/gallery/components/getItemIndex.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/getItemIndex.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/getItemIndex.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/getItemsPerPage.ts b/invokeai/frontend/webv1/src/features/gallery/components/getItemsPerPage.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/getItemsPerPage.ts rename to invokeai/frontend/webv1/src/features/gallery/components/getItemsPerPage.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/getItemsPerRow.ts b/invokeai/frontend/webv1/src/features/gallery/components/getItemsPerRow.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/getItemsPerRow.ts rename to invokeai/frontend/webv1/src/features/gallery/components/getItemsPerRow.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/scrollIntoView.tsx b/invokeai/frontend/webv1/src/features/gallery/components/scrollIntoView.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/scrollIntoView.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/scrollIntoView.tsx diff --git a/invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.test.ts b/invokeai/frontend/webv1/src/features/gallery/components/use-gallery-image-names.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.test.ts rename to invokeai/frontend/webv1/src/features/gallery/components/use-gallery-image-names.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.ts b/invokeai/frontend/webv1/src/features/gallery/components/use-gallery-image-names.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/use-gallery-image-names.ts rename to invokeai/frontend/webv1/src/features/gallery/components/use-gallery-image-names.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/useNextPrevItemNavigation.ts b/invokeai/frontend/webv1/src/features/gallery/components/useNextPrevItemNavigation.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/useNextPrevItemNavigation.ts rename to invokeai/frontend/webv1/src/features/gallery/components/useNextPrevItemNavigation.ts diff --git a/invokeai/frontend/web/src/features/gallery/components/useScrollableGallery.tsx b/invokeai/frontend/webv1/src/features/gallery/components/useScrollableGallery.tsx similarity index 100% rename from invokeai/frontend/web/src/features/gallery/components/useScrollableGallery.tsx rename to invokeai/frontend/webv1/src/features/gallery/components/useScrollableGallery.tsx diff --git a/invokeai/frontend/web/src/features/gallery/contexts/ImageDTOContext.ts b/invokeai/frontend/webv1/src/features/gallery/contexts/ImageDTOContext.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/contexts/ImageDTOContext.ts rename to invokeai/frontend/webv1/src/features/gallery/contexts/ImageDTOContext.ts diff --git a/invokeai/frontend/web/src/features/gallery/contexts/VideoDTOContext.ts b/invokeai/frontend/webv1/src/features/gallery/contexts/VideoDTOContext.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/contexts/VideoDTOContext.ts rename to invokeai/frontend/webv1/src/features/gallery/contexts/VideoDTOContext.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useClearStylePresetWithToast.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useClearStylePresetWithToast.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useClearStylePresetWithToast.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useClearStylePresetWithToast.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useCreateStylePresetFromMetadata.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useCreateStylePresetFromMetadata.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useCreateStylePresetFromMetadata.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useCreateStylePresetFromMetadata.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useDeleteImage.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useDeleteImage.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useDeleteImage.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useDeleteImage.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useEditImage.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useEditImage.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useEditImage.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useEditImage.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useHasImages.test.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useHasImages.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useHasImages.test.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useHasImages.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useHasImages.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useHasImages.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useHasImages.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useHasImages.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useLoadWorkflow.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useLoadWorkflow.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useLoadWorkflow.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useLoadWorkflow.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.test.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useRangeBasedImageFetching.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.test.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useRangeBasedImageFetching.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useRangeBasedImageFetching.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useRangeBasedImageFetching.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useRangeBasedImageFetching.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRecallAllImageMetadata.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useRecallAllImageMetadata.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useRecallAllImageMetadata.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useRecallAllImageMetadata.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRecallCLIPSkip.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useRecallCLIPSkip.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useRecallCLIPSkip.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useRecallCLIPSkip.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRecallDimensions.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useRecallDimensions.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useRecallDimensions.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useRecallDimensions.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRecallPrompts.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useRecallPrompts.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useRecallPrompts.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useRecallPrompts.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRecallRemix.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useRecallRemix.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useRecallRemix.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useRecallRemix.ts diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useRecallSeed.ts b/invokeai/frontend/webv1/src/features/gallery/hooks/useRecallSeed.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/hooks/useRecallSeed.ts rename to invokeai/frontend/webv1/src/features/gallery/hooks/useRecallSeed.ts diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySelectionSource.test.ts b/invokeai/frontend/webv1/src/features/gallery/store/gallerySelectionSource.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/store/gallerySelectionSource.test.ts rename to invokeai/frontend/webv1/src/features/gallery/store/gallerySelectionSource.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySelectionSource.ts b/invokeai/frontend/webv1/src/features/gallery/store/gallerySelectionSource.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/store/gallerySelectionSource.ts rename to invokeai/frontend/webv1/src/features/gallery/store/gallerySelectionSource.ts diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts b/invokeai/frontend/webv1/src/features/gallery/store/gallerySelectors.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/store/gallerySelectors.ts rename to invokeai/frontend/webv1/src/features/gallery/store/gallerySelectors.ts diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.test.ts b/invokeai/frontend/webv1/src/features/gallery/store/gallerySlice.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/store/gallerySlice.test.ts rename to invokeai/frontend/webv1/src/features/gallery/store/gallerySlice.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/webv1/src/features/gallery/store/gallerySlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts rename to invokeai/frontend/webv1/src/features/gallery/store/gallerySlice.ts diff --git a/invokeai/frontend/web/src/features/gallery/store/selectCachedGalleryItemNames.ts b/invokeai/frontend/webv1/src/features/gallery/store/selectCachedGalleryItemNames.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/store/selectCachedGalleryItemNames.ts rename to invokeai/frontend/webv1/src/features/gallery/store/selectCachedGalleryItemNames.ts diff --git a/invokeai/frontend/web/src/features/gallery/store/types.test.ts b/invokeai/frontend/webv1/src/features/gallery/store/types.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/store/types.test.ts rename to invokeai/frontend/webv1/src/features/gallery/store/types.test.ts diff --git a/invokeai/frontend/webv1/src/features/gallery/store/types.ts b/invokeai/frontend/webv1/src/features/gallery/store/types.ts new file mode 100644 index 00000000000..98e03d683cd --- /dev/null +++ b/invokeai/frontend/webv1/src/features/gallery/store/types.ts @@ -0,0 +1,64 @@ +import type { ImageCategory } from 'services/api/types'; +import z from 'zod'; + +const zGalleryView = z.enum(['images', 'assets']); +export type GalleryView = z.infer; +const zBoardId = z.string(); +// TS hack to get autocomplete for "none" but accept any string +export type BoardId = 'none' | (string & {}); +const zComparisonMode = z.enum(['slider', 'side-by-side', 'hover']); +export type ComparisonMode = z.infer; +const zComparisonFit = z.enum(['contain', 'fill']); +export type ComparisonFit = z.infer; +const zOrderDir = z.enum(['ASC', 'DESC']); +export type OrderDir = z.infer; +const zBoardRecordOrderBy = z.enum(['created_at', 'board_name']); +export type BoardRecordOrderBy = z.infer; + +// Mirrors the backend's IMAGE_CATEGORIES / ASSETS_CATEGORIES +// (image_records_common.py). 'other' is in neither: it is the private category +// for images a canvas layer owns, which are layer pixels rather than gallery +// content. The list endpoint filters by whatever categories the client sends, +// so leaving 'other' here would list those images in the assets view while the +// board counts — which come from the backend constant — excluded them. +export const IMAGE_CATEGORIES: ImageCategory[] = ['general']; +export const ASSETS_CATEGORIES: ImageCategory[] = ['control', 'mask', 'user']; + +export const zGalleryState = z.object({ + selection: z.array(z.string()), + shouldAutoSwitch: z.boolean(), + autoAssignBoardOnClick: z.boolean(), + autoAddBoardId: zBoardId, + galleryImageMinimumWidth: z.number(), + selectedBoardId: zBoardId, + galleryView: zGalleryView, + boardSearchText: z.string(), + starredFirst: z.boolean(), + orderDir: zOrderDir, + searchTerm: z.string(), + alwaysShowImageSizeBadge: z.boolean(), + imageToCompare: z.string().nullable(), + comparisonMode: zComparisonMode, + comparisonFit: zComparisonFit, + shouldShowArchivedBoards: z.boolean(), + showVirtualBoards: z.boolean(), + virtualBoardsSectionOpen: z.boolean(), + boardsListOrderBy: zBoardRecordOrderBy, + boardsListOrderDir: zOrderDir, +}); + +export type GalleryState = z.infer; + +const VIRTUAL_BOARD_ID_PREFIX = 'by_date:'; + +export const isVirtualBoardId = (id: string): boolean => id.startsWith(VIRTUAL_BOARD_ID_PREFIX); + +export const getDateFromVirtualBoardId = (id: string): string => id.replace(VIRTUAL_BOARD_ID_PREFIX, ''); + +/** + * The polymorphic gallery treats selection as `string[]` of names. The kind is recoverable from + * the filename extension since the backend names images with `.png` and videos with `.mp4` (see + * SimpleNameService). Centralizing the discriminator here so callers don't have to know about + * the extension contract. + */ +export const isVideoName = (name: string): boolean => name.toLowerCase().endsWith('.mp4'); diff --git a/invokeai/frontend/web/src/features/gallery/store/virtualBoardGalleryConsumers.test.ts b/invokeai/frontend/webv1/src/features/gallery/store/virtualBoardGalleryConsumers.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/store/virtualBoardGalleryConsumers.test.ts rename to invokeai/frontend/webv1/src/features/gallery/store/virtualBoardGalleryConsumers.test.ts diff --git a/invokeai/frontend/web/src/features/gallery/videoReviewRegressions.test.ts b/invokeai/frontend/webv1/src/features/gallery/videoReviewRegressions.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/gallery/videoReviewRegressions.test.ts rename to invokeai/frontend/webv1/src/features/gallery/videoReviewRegressions.test.ts diff --git a/invokeai/frontend/web/src/features/imageActions/README.md b/invokeai/frontend/webv1/src/features/imageActions/README.md similarity index 100% rename from invokeai/frontend/web/src/features/imageActions/README.md rename to invokeai/frontend/webv1/src/features/imageActions/README.md diff --git a/invokeai/frontend/web/src/features/imageActions/actions.ts b/invokeai/frontend/webv1/src/features/imageActions/actions.ts similarity index 100% rename from invokeai/frontend/web/src/features/imageActions/actions.ts rename to invokeai/frontend/webv1/src/features/imageActions/actions.ts diff --git a/invokeai/frontend/web/src/features/lora/components/LoRACard.tsx b/invokeai/frontend/webv1/src/features/lora/components/LoRACard.tsx similarity index 100% rename from invokeai/frontend/web/src/features/lora/components/LoRACard.tsx rename to invokeai/frontend/webv1/src/features/lora/components/LoRACard.tsx diff --git a/invokeai/frontend/web/src/features/lora/components/LoRAList.tsx b/invokeai/frontend/webv1/src/features/lora/components/LoRAList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/lora/components/LoRAList.tsx rename to invokeai/frontend/webv1/src/features/lora/components/LoRAList.tsx diff --git a/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx b/invokeai/frontend/webv1/src/features/lora/components/LoRASelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx rename to invokeai/frontend/webv1/src/features/lora/components/LoRASelect.tsx diff --git a/invokeai/frontend/web/src/features/metadata/parsing.test.ts b/invokeai/frontend/webv1/src/features/metadata/parsing.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/metadata/parsing.test.ts rename to invokeai/frontend/webv1/src/features/metadata/parsing.test.ts diff --git a/invokeai/frontend/web/src/features/metadata/parsing.test.tsx b/invokeai/frontend/webv1/src/features/metadata/parsing.test.tsx similarity index 100% rename from invokeai/frontend/web/src/features/metadata/parsing.test.tsx rename to invokeai/frontend/webv1/src/features/metadata/parsing.test.tsx diff --git a/invokeai/frontend/web/src/features/metadata/parsing.tsx b/invokeai/frontend/webv1/src/features/metadata/parsing.tsx similarity index 96% rename from invokeai/frontend/web/src/features/metadata/parsing.tsx rename to invokeai/frontend/webv1/src/features/metadata/parsing.tsx index 24402b94ec6..cd36e158cc3 100644 --- a/invokeai/frontend/web/src/features/metadata/parsing.tsx +++ b/invokeai/frontend/webv1/src/features/metadata/parsing.tsx @@ -21,6 +21,10 @@ import { kleinQwen3EncoderModelSelected, krea2Qwen3VlEncoderModelSelected, krea2VaeModelSelected, + minimaxH3DurationSecondsChanged, + minimaxH3OutputModeChanged, + minimaxH3TextEncoderModelSelected, + minimaxH3TransformerModelSelected, negativePromptChanged, openaiBackgroundChanged, openaiInputFidelityChanged, @@ -1138,6 +1142,111 @@ const WanGuidanceScaleLowNoise: SingleMetadataHandler = { }; //#endregion WanGuidanceScaleLowNoise +//#region MiniMaxH3DurationSeconds +const MiniMaxH3DurationSeconds: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'MiniMaxH3DurationSeconds', + parse: (metadata, _store) => { + const raw = getProperty(metadata, 'minimax_h3_duration_seconds'); + if (raw === undefined) { + // Reject when the key is absent so the handler is not rendered for non-H3 media. + return Promise.reject(); + } + const parsed = z.number().int().min(5).max(14).parse(raw); + return Promise.resolve(parsed); + }, + recall: (value, store) => { + store.dispatch(minimaxH3DurationSecondsChanged(value)); + }, + i18nKey: 'parameters.minimaxH3DurationSeconds', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => , +}; +//#endregion MiniMaxH3DurationSeconds + +//#region MiniMaxH3OutputMode +const MiniMaxH3OutputMode: SingleMetadataHandler<'video' | 'image'> = { + [SingleMetadataKey]: true, + type: 'MiniMaxH3OutputMode', + parse: (metadata, _store) => { + const raw = getProperty(metadata, 'minimax_h3_output_mode'); + if (raw === undefined) { + return Promise.reject(); + } + const parsed = z.enum(['video', 'image']).parse(raw); + return Promise.resolve(parsed); + }, + recall: (value, store) => { + store.dispatch(minimaxH3OutputModeChanged(value)); + }, + i18nKey: 'parameters.minimaxH3OutputMode', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps<'video' | 'image'>) => , +}; +//#endregion MiniMaxH3OutputMode + +//#region MiniMaxH3TransformerModel +const MiniMaxH3TransformerModel: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'MiniMaxH3TransformerModel', + parse: async (metadata, store) => { + const raw = getProperty(metadata, 'minimax_h3_transformer_model'); + if (raw === undefined) { + // The graph builder only writes this key when a single-file transformer override was + // used; reject when absent so the handler is not rendered (and recall-all skips it). + return Promise.reject(); + } + if (raw === null) { + return Promise.resolve(null); + } + // Validate the single-file transformer is still installed - recall-all must skip silently + // (not clobber or error) when it has since been deleted. + const parsed = await parseModelIdentifier(raw, store, 'main'); + assert(parsed.type === 'main' && parsed.base === 'minimax-h3'); + return Promise.resolve(parsed); + }, + recall: (value, store) => { + store.dispatch(minimaxH3TransformerModelSelected(value)); + }, + i18nKey: 'modelManager.minimaxH3TransformerModel', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => ( + + ), +}; +//#endregion MiniMaxH3TransformerModel + +//#region MiniMaxH3TextEncoderModel +const MiniMaxH3TextEncoderModel: SingleMetadataHandler = { + [SingleMetadataKey]: true, + type: 'MiniMaxH3TextEncoderModel', + parse: async (metadata, store) => { + const raw = getProperty(metadata, 'minimax_h3_text_encoder_model'); + if (raw === undefined) { + // The graph builder only writes this key when a single-file text-encoder override was + // used; reject when absent so the handler is not rendered (and recall-all skips it). + return Promise.reject(); + } + if (raw === null) { + return Promise.resolve(null); + } + // Validate the single-file encoder is still installed - recall-all must skip silently + // (not clobber or error) when it has since been deleted. + const parsed = await parseModelIdentifier(raw, store, 'qwen3_vl_encoder'); + assert(parsed.type === 'qwen3_vl_encoder' && parsed.base === 'minimax-h3'); + return Promise.resolve(parsed); + }, + recall: (value, store) => { + store.dispatch(minimaxH3TextEncoderModelSelected(value)); + }, + i18nKey: 'modelManager.minimaxH3TextEncoderModel', + LabelComponent: MetadataLabel, + ValueComponent: ({ value }: SingleMetadataValueProps) => ( + + ), +}; +//#endregion MiniMaxH3TextEncoderModel + //#region ZImageShift const ZImageShift: SingleMetadataHandler = { [SingleMetadataKey]: true, @@ -2481,6 +2590,10 @@ export const ImageMetadataHandlers = { WanVaeModel, WanT5EncoderModel, WanGuidanceScaleLowNoise, + MiniMaxH3DurationSeconds, + MiniMaxH3OutputMode, + MiniMaxH3TransformerModel, + MiniMaxH3TextEncoderModel, ZImageShift, Ideogram4SamplerPreset, Ideogram4Steps, diff --git a/invokeai/frontend/web/src/features/metadata/util/modelFetchingHelpers.ts b/invokeai/frontend/webv1/src/features/metadata/util/modelFetchingHelpers.ts similarity index 100% rename from invokeai/frontend/web/src/features/metadata/util/modelFetchingHelpers.ts rename to invokeai/frontend/webv1/src/features/metadata/util/modelFetchingHelpers.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useBuildModelsToInstall.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useBuildModelsToInstall.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useBuildModelsToInstall.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useBuildModelsToInstall.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useControlAdapterModelDefaultSettings.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useControlAdapterModelDefaultSettings.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useControlAdapterModelDefaultSettings.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useControlAdapterModelDefaultSettings.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useCpuOnlyModelSettings.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useCpuOnlyModelSettings.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useCpuOnlyModelSettings.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useCpuOnlyModelSettings.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useInstallModel.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useInstallModel.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useInstallModel.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useInstallModel.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useIsModelManagerEnabled.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useIsModelManagerEnabled.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useIsModelManagerEnabled.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useIsModelManagerEnabled.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useLoRAModelDefaultSettings.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useLoRAModelDefaultSettings.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useLoRAModelDefaultSettings.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useLoRAModelDefaultSettings.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useMainModelDefaultSettings.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useMainModelDefaultSettings.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useMainModelDefaultSettings.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useMainModelDefaultSettings.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterBundleInstall.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useStarterBundleInstall.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterBundleInstall.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useStarterBundleInstall.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterBundleInstallStatus.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useStarterBundleInstallStatus.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterBundleInstallStatus.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useStarterBundleInstallStatus.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterModelsToast.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useStarterModelsToast.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/hooks/useStarterModelsToast.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/hooks/useStarterModelsToast.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/models.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/models.ts similarity index 91% rename from invokeai/frontend/web/src/features/modelManagerV2/models.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/models.ts index 12d8b095178..f2e318dccb2 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/models.ts +++ b/invokeai/frontend/webv1/src/features/modelManagerV2/models.ts @@ -9,9 +9,11 @@ import { isExternalApiModelConfig, isFluxReduxModelConfig, isGemma2EncoderModelConfig, + isGemma4EncoderModelConfig, isIPAdapterModelConfig, isLLaVAModelConfig, isLoRAModelConfig, + isLTX2DurationHeadModelConfig, isMistralEncoderModelConfig, isNonRefinerMainModelConfig, isPiDDecoderModelConfig, @@ -110,11 +112,21 @@ const MODEL_CATEGORIES: Record = { i18nKey: 'modelManager.gemma2Encoder', filter: isGemma2EncoderModelConfig, }, + gemma4_encoder: { + category: 'gemma4_encoder', + i18nKey: 'modelManager.gemma4Encoder', + filter: isGemma4EncoderModelConfig, + }, pid_decoder: { category: 'pid_decoder', i18nKey: 'modelManager.pidDecoder', filter: isPiDDecoderModelConfig, }, + ltx2_duration_head: { + category: 'ltx2_duration_head', + i18nKey: 'modelManager.ltx2DurationHead', + filter: isLTX2DurationHeadModelConfig, + }, control_lora: { category: 'control_lora', i18nKey: 'modelManager.controlLora', @@ -199,6 +211,8 @@ export const MODEL_BASE_TO_COLOR: Record = { external: 'orange', anima: 'invokePurple', wan: 'cyan', + 'minimax-h3': 'yellow', + 'ltx-2': 'teal', unknown: 'red', }; @@ -225,6 +239,7 @@ export const MODEL_TYPE_TO_LONG_NAME: Record = { qwen3_vl_encoder: 'Qwen3-VL Encoder', wan_t5_encoder: 'Wan T5 Encoder', gemma2_encoder: 'Gemma-2 Encoder', + gemma4_encoder: 'Gemma-4 Encoder', clip_embed: 'CLIP Embed', siglip: 'SigLIP', flux_redux: 'FLUX Redux', @@ -232,6 +247,7 @@ export const MODEL_TYPE_TO_LONG_NAME: Record = { text_llm: 'Text LLM', external_image_generator: 'External Image Generator', pid_decoder: 'PiD Decoder', + ltx2_duration_head: 'LTX-2 Duration Head', unknown: 'Unknown', }; @@ -256,6 +272,8 @@ export const MODEL_BASE_TO_LONG_NAME: Record = { external: 'External', anima: 'Anima', wan: 'Wan 2.2', + 'minimax-h3': 'MiniMax H3', + 'ltx-2': 'LTX-2', unknown: 'Unknown', }; @@ -280,6 +298,8 @@ export const MODEL_BASE_TO_SHORT_NAME: Record = { external: 'External', anima: 'Anima', wan: 'Wan', + 'minimax-h3': 'MiniMax H3', + 'ltx-2': 'LTX-2', unknown: 'Unknown', }; @@ -302,6 +322,10 @@ export const MODEL_VARIANT_TO_LONG_NAME: Record = { gigantic: 'CLIP G', generate: 'Qwen Image', edit: 'Qwen Image Edit', + fl2va: 'MiniMax H3 FL2VA', + ref2va: 'MiniMax H3 Ref2VA', + ltx2_dev: 'LTX-2 Dev', + ltx2_distilled: 'LTX-2 Distilled', t2v_a14b: 'Wan 2.2 T2V A14B', i2v_a14b: 'Wan 2.2 I2V A14B', ti2v_5b: 'Wan 2.2 TI2V 5B', @@ -310,7 +334,10 @@ export const MODEL_VARIANT_TO_LONG_NAME: Record = { qwen3_4b: 'Qwen3 4B', qwen3_8b: 'Qwen3 8B', qwen3_06b: 'Qwen3 0.6B', + qwen3_vl_4b: 'Qwen3-VL 4B (Krea-2)', + qwen3_vl_8b: 'Qwen3-VL 8B (Ideogram 4)', cow_mistral3_small: 'cow-mistral3-small (FLUX.2)', + ministral3_3b: 'Ministral 3B (ERNIE-Image)', mistral3_24b: 'Mistral Small 3 (24B, FLUX.2)', res2k_sr4x: 'PiD 2K (4x SR)', res2kto4k_sr4x: 'PiD 4K (4x SR Upscale)', @@ -334,6 +361,7 @@ export const MODEL_FORMAT_TO_LONG_NAME: Record = { qwen3_vl_encoder: 'Qwen3-VL Encoder', wan_t5_encoder: 'Wan T5 Encoder (UMT5-XXL)', gemma2_encoder: 'Gemma-2 Encoder', + gemma4_encoder: 'Gemma-4 Encoder', bnb_quantized_int8b: 'BNB Quantized (int8b)', bnb_quantized_nf4b: 'BNB Quantized (nf4b)', gguf_quantized: 'GGUF Quantized', @@ -350,6 +378,7 @@ export const SUPPORTS_REF_IMAGES_BASE_MODELS: BaseModelType[] = [ 'flux2', 'qwen-image', 'wan', + 'minimax-h3', 'krea-2', ]; diff --git a/invokeai/frontend/web/src/features/modelManagerV2/store/installModelsStore.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/store/installModelsStore.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/store/installModelsStore.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/store/installModelsStore.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/store/modelManagerV2Slice.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/store/modelManagerV2Slice.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/store/modelManagerV2Slice.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/store/modelManagerV2Slice.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HFToken.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HFToken.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HFToken.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HFToken.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadForm.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadForm.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadForm.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueBadge.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueBadge.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueBadge.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueBadge.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelResultItemActions.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ModelResultItemActions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelResultItemActions.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ModelResultItemActions.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderForm.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderForm.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderForm.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResultItem.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResultItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResultItem.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResultItem.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleTooltipContent.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleTooltipContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleTooltipContent.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleTooltipContent.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleTooltipContentCompact.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleTooltipContentCompact.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleTooltipContentCompact.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleTooltipContentCompact.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResultItem.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResultItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResultItem.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResultItem.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/InstallModels.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/InstallModels.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManager.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManager.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManager.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManager.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/BulkDeleteModelsModal.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/BulkDeleteModelsModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/BulkDeleteModelsModal.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/BulkDeleteModelsModal.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/BulkReidentifyModelsModal.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/BulkReidentifyModelsModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/BulkReidentifyModelsModal.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/BulkReidentifyModelsModal.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/FetchingModelsLoader.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/FetchingModelsLoader.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/FetchingModelsLoader.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/FetchingModelsLoader.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/MissingModelsContext.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/MissingModelsContext.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/MissingModelsContext.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/MissingModelsContext.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFilterMenu.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFilterMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFilterMenu.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFilterMenu.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx similarity index 96% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx index 7b62a728df2..e299dbd292a 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx +++ b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx @@ -20,6 +20,7 @@ const FORMAT_NAME_MAP: Record = { qwen3_vl_encoder: 'qwen3_vl_encoder', wan_t5_encoder: 'wan_t5_encoder', gemma2_encoder: 'gemma2_encoder', + gemma4_encoder: 'gemma4_encoder', bnb_quantized_int8b: 'bnb_quantized_int8b', bnb_quantized_nf4b: 'quantized', gguf_quantized: 'gguf', @@ -46,6 +47,7 @@ const FORMAT_COLOR_MAP: Record = { qwen3_vl_encoder: 'base', wan_t5_encoder: 'base', gemma2_encoder: 'base', + gemma4_encoder: 'base', bnb_quantized_int8b: 'base', bnb_quantized_nf4b: 'base', gguf_quantized: 'base', diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelList.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListBulkActions.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListBulkActions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListBulkActions.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListBulkActions.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListNavigation.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListWrapper.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListWrapper.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListWrapper.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsDialog.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsDialog.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsDialog.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPane.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPane.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/ControlAdapterModelDefaultSettings.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/ControlAdapterModelDefaultSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/ControlAdapterModelDefaultSettings.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/ControlAdapterModelDefaultSettings.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/DefaultFp8StorageControlAdapter.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/DefaultFp8StorageControlAdapter.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/DefaultFp8StorageControlAdapter.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/DefaultFp8StorageControlAdapter.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/DefaultPreprocessor.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/DefaultPreprocessor.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/DefaultPreprocessor.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ControlAdapterModelDefaultSettings/DefaultPreprocessor.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/CpuOnlyModelSettings/CpuOnlyModelSettings.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/CpuOnlyModelSettings/CpuOnlyModelSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/CpuOnlyModelSettings/CpuOnlyModelSettings.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/CpuOnlyModelSettings/CpuOnlyModelSettings.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/EncoderModelSettings/EncoderModelSettings.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/EncoderModelSettings/EncoderModelSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/EncoderModelSettings/EncoderModelSettings.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/EncoderModelSettings/EncoderModelSettings.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/BaseModelSelect.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/BaseModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/BaseModelSelect.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/BaseModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelFormatSelect.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelFormatSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelFormatSelect.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelFormatSelect.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelTypeSelect.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelTypeSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelTypeSelect.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelTypeSelect.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelVariantSelect.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelVariantSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelVariantSelect.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelVariantSelect.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/PredictionTypeSelect.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/PredictionTypeSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/PredictionTypeSelect.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Fields/PredictionTypeSelect.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeight.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeight.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeight.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeight.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeightMax.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeightMax.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeightMax.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeightMax.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeightMin.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeightMin.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeightMin.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/DefaultWeightMin.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/LoRAModelDefaultSettings.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/LoRAModelDefaultSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/LoRAModelDefaultSettings.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/LoRAModelDefaultSettings/LoRAModelDefaultSettings.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgRescaleMultiplier.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgRescaleMultiplier.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgRescaleMultiplier.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgRescaleMultiplier.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgScale.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgScale.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgScale.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultCfgScale.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultFp8Storage.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultFp8Storage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultFp8Storage.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultFp8Storage.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultGuidance.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultGuidance.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultGuidance.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultGuidance.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultScheduler.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultScheduler.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultScheduler.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultScheduler.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultSteps.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultSteps.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultSteps.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultSteps.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVae.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVae.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVae.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVae.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVaePrecision.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVaePrecision.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVaePrecision.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultVaePrecision.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx similarity index 98% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx index 7f8c7de862a..1fc36265a35 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx +++ b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx @@ -58,7 +58,7 @@ export const MainModelDefaultSettings = memo(({ modelConfig }: Props) => { // Already-quantized weights cannot also be stored as FP8 — the backend refuses it (see // `_should_use_fp8`), so offering the switch would be a control that silently does nothing. - // Keep in sync with `_QUANTIZED_MODEL_FORMATS` in `load_default.py`. + // Keep in sync with `QUANTIZED_MODEL_FORMATS` in `taxonomy.py`. const isQuantized = useMemo(() => { return ['gguf_quantized', 'bnb_quantized_nf4b', 'bnb_quantized_int8b', 'sdnq_quantized'].includes( modelConfig.format diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelDeleteButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelDeleteButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelDeleteButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelDeleteButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEditButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelEditButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEditButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelEditButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelFooter.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelFooter.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelFooter.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelFooter.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelHeader.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelReidentifyButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelReidentifyButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelReidentifyButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelReidentifyButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelSettingsExportButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelSettingsExportButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelSettingsExportButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelSettingsExportButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelSettingsImportButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelSettingsImportButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelSettingsImportButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelSettingsImportButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelUpdatePathButton.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelUpdatePathButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelUpdatePathButton.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelUpdatePathButton.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/RelatedModels.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/RelatedModels.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/RelatedModels.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/RelatedModels.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/SettingToggle.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/SettingToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/SettingToggle.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/SettingToggle.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/TriggerPhrases.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/VAEModelSettings/VAEModelSettings.tsx b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/VAEModelSettings/VAEModelSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/VAEModelSettings/VAEModelSettings.tsx rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/VAEModelSettings/VAEModelSettings.tsx diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/isExternalModel.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/isExternalModel.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/isExternalModel.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/isExternalModel.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/modelSettingsIO.test.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/modelSettingsIO.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/modelSettingsIO.test.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/modelSettingsIO.test.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/modelSettingsIO.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/modelSettingsIO.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/modelSettingsIO.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/subpanels/ModelPanel/modelSettingsIO.ts diff --git a/invokeai/frontend/web/src/features/modelManagerV2/util/getApiErrorDetail.ts b/invokeai/frontend/webv1/src/features/modelManagerV2/util/getApiErrorDetail.ts similarity index 100% rename from invokeai/frontend/web/src/features/modelManagerV2/util/getApiErrorDetail.ts rename to invokeai/frontend/webv1/src/features/modelManagerV2/util/getApiErrorDetail.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx b/invokeai/frontend/webv1/src/features/nodes/components/NodeEditor.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/NodeEditor.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/Flow.tsx similarity index 98% rename from invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/Flow.tsx index 7f0e9d544a2..3c4676fbb2c 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx +++ b/invokeai/frontend/webv1/src/features/nodes/components/flow/Flow.tsx @@ -63,8 +63,7 @@ import { useTranslation } from 'react-i18next'; import { PiPlugsConnectedBold, PiTrashBold } from 'react-icons/pi'; import CustomConnectionLine from './connectionLines/CustomConnectionLine'; -import InvocationCollapsedEdge from './edges/InvocationCollapsedEdge'; -import InvocationDefaultEdge from './edges/InvocationDefaultEdge'; +import { edgeTypes } from './edgeTypes'; import ConnectorNode from './nodes/Connector/ConnectorNode'; import CurrentImageNode from './nodes/CurrentImage/CurrentImageNode'; import InvocationNodeWrapper from './nodes/Invocation/InvocationNodeWrapper'; @@ -73,11 +72,6 @@ import type { WorkflowContextMenuState } from './workflowContextMenu'; import { getWorkflowContextMenuState } from './workflowContextMenu'; import { isWorkflowHotkeyEnabled, shouldIgnoreWorkflowCopyHotkey } from './workflowHotkeys'; -const edgeTypes = { - collapsed: InvocationCollapsedEdge, - default: InvocationDefaultEdge, -} as const; - const nodeTypes = { invocation: InvocationNodeWrapper, connector: ConnectorNode, diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx diff --git a/invokeai/frontend/webv1/src/features/nodes/components/flow/edgeTypes.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/edgeTypes.ts new file mode 100644 index 00000000000..a3fab8e5c6b --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/components/flow/edgeTypes.ts @@ -0,0 +1,8 @@ +import InvocationCollapsedEdge from './edges/InvocationCollapsedEdge'; +import InvocationDefaultEdge from './edges/InvocationDefaultEdge'; + +export const edgeTypes = { + collapsed: InvocationCollapsedEdge, + default: InvocationDefaultEdge, + loop_linkage: InvocationDefaultEdge, +} as const; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/buildEdgeSelectors.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/edges/util/buildEdgeSelectors.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/edges/util/buildEdgeSelectors.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/edges/util/buildEdgeSelectors.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/getEdgeColor.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/edges/util/getEdgeColor.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/edges/util/getEdgeColor.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/edges/util/getEdgeColor.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Connector/ConnectorNode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Connector/ConnectorNode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Connector/ConnectorNode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Connector/ConnectorNode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/CallSavedWorkflowNode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/CallSavedWorkflowNode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/CallSavedWorkflowNode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/CallSavedWorkflowNode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeClassificationIcon.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeClassificationIcon.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeClassificationIcon.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeClassificationIcon.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeCollapsedHandles.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeCollapsedHandles.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeCollapsedHandles.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeCollapsedHandles.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeFooter.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeHeader.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeHeader.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeHeader.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeHeader.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeInfoIcon.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeInfoIcon.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeInfoIcon.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeInfoIcon.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeNotesTextarea.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeNotesTextarea.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeNotesTextarea.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeNotesTextarea.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeUnknownFallback.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeUnknownFallback.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeUnknownFallback.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeUnknownFallback.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/SaveToGalleryCheckbox.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/SaveToGalleryCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/SaveToGalleryCheckbox.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/SaveToGalleryCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/UseCacheCheckbox.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/UseCacheCheckbox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/UseCacheCheckbox.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/UseCacheCheckbox.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.test.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.test.ts similarity index 99% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.test.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.test.ts index 769de694a07..f24f606d162 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.test.ts +++ b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.test.ts @@ -55,6 +55,7 @@ const buildWorkflowResponse = (overrides?: { opened_at: null, user_id: 'user-1', is_public: false, + revision: 1, thumbnail_url: null, workflow: { id: 'workflow-1', diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/callSavedWorkflowFormUtils.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/context.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/context.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/context.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/context.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldInput.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldInput.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldInput.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldInput.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldInputAndSlider.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldInputAndSlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldInputAndSlider.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldInputAndSlider.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldSlider.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldSlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldSlider.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/FloatFieldSlider.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/useFloatField.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/useFloatField.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/useFloatField.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/FloatField/useFloatField.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldAddRemoveFormRoot.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldAddRemoveFormRoot.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldAddRemoveFormRoot.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldAddRemoveFormRoot.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldDescriptionPopover.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldDescriptionPopover.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldDescriptionPopover.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldDescriptionPopover.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldEditModeNodes.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldEditModeNodes.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldEditModeNodes.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldEditModeNodes.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldGate.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldGate.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldGate.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldGate.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldHandle.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldHandle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldHandle.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldHandle.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldResetToDefaultValueIconButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldResetToDefaultValueIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldResetToDefaultValueIconButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldResetToDefaultValueIconButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTooltipContent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTooltipContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTooltipContent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTooltipContent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldWrapper.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldWrapper.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldWrapper.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInput.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInput.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInput.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInput.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInputAndSlider.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInputAndSlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInputAndSlider.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldInputAndSlider.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldSlider.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldSlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldSlider.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/IntegerFieldSlider.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/VideoFrameIndexFieldInput.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/VideoFrameIndexFieldInput.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/VideoFrameIndexFieldInput.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/VideoFrameIndexFieldInput.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/useIntegerField.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/useIntegerField.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/useIntegerField.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/IntegerField/useIntegerField.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/NodeFieldElementResetToInitialValueIconButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/NodeFieldElementResetToInitialValueIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/NodeFieldElementResetToInitialValueIconButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/NodeFieldElementResetToInitialValueIconButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldGate.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldGate.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldGate.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldGate.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldHandle.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldHandle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldHandle.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldHandle.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldNodesEditorView.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldNodesEditorView.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldNodesEditorView.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldNodesEditorView.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTitle.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTitle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTitle.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTitle.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTooltipContent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTooltipContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTooltipContent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTooltipContent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldWrapper.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldWrapper.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldWrapper.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldDropdown.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldDropdown.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldDropdown.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldDropdown.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldInput.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldInput.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldInput.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldInput.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldTextarea.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldTextarea.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldTextarea.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/StringFieldTextarea.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/useStringField.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/useStringField.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/useStringField.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/StringField/useStringField.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/BoardFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/BoardFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/BoardFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/BoardFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/BooleanFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/BooleanFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/BooleanFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/BooleanFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ColorFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ColorFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ColorFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ColorFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/EnumFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/EnumFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/EnumFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/EnumFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatFieldCollectionInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatFieldCollectionInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatFieldCollectionInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatFieldCollectionInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorArithmeticSequenceSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorArithmeticSequenceSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorArithmeticSequenceSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorArithmeticSequenceSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorFieldComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorFieldComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorFieldComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorFieldComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorLinearDistributionSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorLinearDistributionSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorLinearDistributionSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorLinearDistributionSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorParseStringSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorParseStringSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorParseStringSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorParseStringSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorUniformRandomDistributionSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorUniformRandomDistributionSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorUniformRandomDistributionSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorUniformRandomDistributionSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/GeneratorTextareaWithFileUpload.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/GeneratorTextareaWithFileUpload.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/GeneratorTextareaWithFileUpload.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/GeneratorTextareaWithFileUpload.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldCollectionInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldCollectionInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldCollectionInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldCollectionInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageGeneratorFieldComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageGeneratorFieldComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageGeneratorFieldComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageGeneratorFieldComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageGeneratorImagesFromBoardSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageGeneratorImagesFromBoardSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageGeneratorImagesFromBoardSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageGeneratorImagesFromBoardSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerFieldCollectionInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerFieldCollectionInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerFieldCollectionInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerFieldCollectionInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorArithmeticSequenceSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorArithmeticSequenceSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorArithmeticSequenceSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorArithmeticSequenceSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorFieldComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorFieldComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorFieldComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorFieldComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorLinearDistributionSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorLinearDistributionSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorLinearDistributionSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorLinearDistributionSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorParseStringSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorParseStringSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorParseStringSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorParseStringSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorUniformRandomDistributionSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorUniformRandomDistributionSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorUniformRandomDistributionSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IntegerGeneratorUniformRandomDistributionSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LoRAFieldCollectionInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LoRAFieldCollectionInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LoRAFieldCollectionInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LoRAFieldCollectionInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelFieldCombobox.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelFieldCombobox.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelFieldCombobox.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelFieldCombobox.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SavedWorkflowFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SavedWorkflowFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SavedWorkflowFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SavedWorkflowFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SchedulerFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SchedulerFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SchedulerFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SchedulerFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringFieldCollectionInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringFieldCollectionInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringFieldCollectionInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringFieldCollectionInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorDynamicPromptsCombinatorialSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorDynamicPromptsCombinatorialSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorDynamicPromptsCombinatorialSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorDynamicPromptsCombinatorialSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorDynamicPromptsRandomSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorDynamicPromptsRandomSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorDynamicPromptsRandomSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorDynamicPromptsRandomSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorFieldComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorFieldComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorFieldComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorFieldComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorParseStringSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorParseStringSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorParseStringSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StringGeneratorParseStringSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StylePresetFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StylePresetFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StylePresetFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/StylePresetFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SystemPromptFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SystemPromptFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SystemPromptFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SystemPromptFieldInputComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VideoFieldInputComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VideoFieldInputComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VideoFieldInputComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VideoFieldInputComponent.tsx diff --git a/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/savedWorkflowFieldUtils.test.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/savedWorkflowFieldUtils.test.ts new file mode 100644 index 00000000000..f116e260c7f --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/savedWorkflowFieldUtils.test.ts @@ -0,0 +1,209 @@ +import type { WorkflowRecordListItemWithThumbnailDTO } from 'services/api/types'; +import { describe, expect, it } from 'vitest'; + +import { + buildSavedWorkflowOptions, + getSavedWorkflowDisplayState, + getSavedWorkflowListItemFromRecord, + getSavedWorkflowPickerOwnedQueryArg, + getSavedWorkflowPickerSharedQueryArg, + getSavedWorkflowSelectionOption, + getSavedWorkflowSelectionState, + mergeSavedWorkflowPickerItems, + MISSING_WORKFLOW_OPTION_VALUE, + shouldFetchNextSavedWorkflowPickerPage, +} from './savedWorkflowFieldUtils'; + +const workflows: WorkflowRecordListItemWithThumbnailDTO[] = [ + { + workflow_id: 'workflow-a', + name: 'Alpha Workflow', + created_at: '', + updated_at: '', + opened_at: null, + description: '', + tags: '', + is_public: false, + revision: 1, + thumbnail_url: null, + category: 'user', + user_id: 'user-a', + call_saved_workflow_compatibility: { + is_callable: true, + reason: 'ok', + message: null, + }, + }, + { + workflow_id: 'workflow-b', + name: 'Beta Workflow', + created_at: '', + updated_at: '', + opened_at: null, + description: '', + tags: '', + is_public: true, + revision: 1, + thumbnail_url: null, + category: 'default', + user_id: 'system', + call_saved_workflow_compatibility: { + is_callable: false, + reason: 'missing_workflow_return', + message: 'The workflow must contain exactly one workflow_return node.', + }, + }, +]; + +describe('savedWorkflowFieldUtils', () => { + it('builds combobox options from visible workflows', () => { + expect(buildSavedWorkflowOptions(workflows)).toEqual([ + { label: 'Alpha Workflow', value: 'workflow-a', isDisabled: false }, + { label: 'Beta Workflow', value: 'workflow-b', isDisabled: true }, + ]); + }); + + it('returns an unselected state for the default empty value', () => { + const selectionState = getSavedWorkflowSelectionState(workflows, ''); + expect(selectionState).toEqual({ status: 'unselected' }); + expect(getSavedWorkflowSelectionOption(selectionState)).toBeNull(); + }); + + it('returns a selected state for a valid workflow id', () => { + const selectionState = getSavedWorkflowSelectionState(workflows, 'workflow-b'); + expect(selectionState).toEqual({ status: 'selected', workflow: workflows[1] }); + expect(getSavedWorkflowSelectionOption(selectionState)).toEqual({ + label: 'Beta Workflow', + value: 'workflow-b', + }); + }); + + it('returns a missing state for a stale or inaccessible workflow id', () => { + const selectionState = getSavedWorkflowSelectionState(workflows, 'missing-workflow'); + expect(selectionState).toEqual({ status: 'missing', workflowId: 'missing-workflow' }); + expect(getSavedWorkflowSelectionOption(selectionState)).toEqual({ + label: MISSING_WORKFLOW_OPTION_VALUE, + value: MISSING_WORKFLOW_OPTION_VALUE, + }); + }); + + it('builds display state for an already-selected unsupported workflow', () => { + const selectionState = getSavedWorkflowSelectionState(workflows, 'workflow-b'); + + expect(getSavedWorkflowDisplayState(selectionState)).toEqual({ + selection: 'selected', + statusLabelKey: null, + badges: ['unsupported', 'default'], + compatibilityMessageKey: 'workflows.savedWorkflowCompatibility.missingWorkflowReturn', + }); + }); + + it('builds display state for missing and unselected workflows', () => { + expect(getSavedWorkflowDisplayState({ status: 'unselected' })).toEqual({ + selection: 'unselected', + statusLabelKey: 'nodes.savedWorkflowChoose', + badges: [], + compatibilityMessageKey: null, + }); + + expect(getSavedWorkflowDisplayState({ status: 'missing', workflowId: 'missing-workflow' })).toEqual({ + selection: 'missing', + statusLabelKey: 'nodes.savedWorkflowMissing', + badges: [], + compatibilityMessageKey: null, + }); + }); + + it('uses a fetched selected workflow when it is not present in the first list page', () => { + const selectedWorkflow = getSavedWorkflowListItemFromRecord({ + workflow_id: 'workflow-z', + name: 'Zeta Workflow', + created_at: '', + updated_at: '', + opened_at: null, + user_id: 'user-z', + is_public: true, + revision: 1, + thumbnail_url: null, + call_saved_workflow_compatibility: { + is_callable: true, + reason: 'ok', + message: null, + }, + workflow: { + id: 'workflow-z', + name: 'Zeta Workflow', + author: '', + description: 'A workflow outside the first page', + version: '', + contact: '', + tags: 'paged', + notes: '', + exposedFields: [], + meta: { + category: 'user', + version: '1.0.0', + }, + nodes: [], + edges: [], + form: null, + }, + }); + + const selectionState = getSavedWorkflowSelectionState(workflows, 'workflow-z', selectedWorkflow); + + expect(selectionState).toEqual({ status: 'selected', workflow: selectedWorkflow }); + expect(getSavedWorkflowDisplayState(selectionState)).toEqual({ + selection: 'selected', + statusLabelKey: null, + badges: ['shared'], + compatibilityMessageKey: null, + }); + }); + + it('queries owned/default workflows and shared public workflows separately', () => { + expect(getSavedWorkflowPickerOwnedQueryArg('landscape')).toMatchObject({ + page: 0, + per_page: undefined, + query: 'landscape', + categories: ['user', 'default'], + is_public: undefined, + callable: true, + }); + expect(getSavedWorkflowPickerSharedQueryArg('landscape')).toMatchObject({ + page: 0, + per_page: undefined, + query: 'landscape', + categories: ['user'], + is_public: true, + callable: true, + }); + }); + + it('merges paged owned and shared workflow picker results without duplicates', () => { + const ownedWorkflow = workflows[0]; + const defaultWorkflow = workflows[1]; + if (!ownedWorkflow || !defaultWorkflow) { + throw new Error('Expected workflow fixtures'); + } + const sharedWorkflow = { + ...ownedWorkflow, + workflow_id: 'workflow-shared', + name: 'Shared Workflow', + is_public: true, + revision: 1, + }; + + expect(mergeSavedWorkflowPickerItems([ownedWorkflow], [defaultWorkflow, sharedWorkflow], [ownedWorkflow])).toEqual([ + ownedWorkflow, + defaultWorkflow, + sharedWorkflow, + ]); + }); + + it('fetches more workflow picker pages only when a query has another idle page', () => { + expect(shouldFetchNextSavedWorkflowPickerPage({ hasNextPage: true, isFetching: false })).toBe(true); + expect(shouldFetchNextSavedWorkflowPickerPage({ hasNextPage: false, isFetching: false })).toBe(false); + expect(shouldFetchNextSavedWorkflowPickerPage({ hasNextPage: true, isFetching: true })).toBe(false); + }); +}); diff --git a/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/savedWorkflowFieldUtils.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/savedWorkflowFieldUtils.ts new file mode 100644 index 00000000000..a64407fde4e --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/savedWorkflowFieldUtils.ts @@ -0,0 +1,179 @@ +import type { ComboboxOption } from '@invoke-ai/ui-library'; +import { + getWorkflowCallCompatibilityState, + type WorkflowCallCompatibilityMessageKey, +} from 'features/workflowLibrary/util/workflowCallCompatibility'; +import type { S, WorkflowRecordListItemWithThumbnailDTO } from 'services/api/types'; + +export const MISSING_WORKFLOW_OPTION_VALUE = '__missing_workflow__'; +type SavedWorkflowBadge = 'unsupported' | 'default' | 'shared'; + +type SavedWorkflowSelectionState = + | { status: 'unselected' } + | { status: 'selected'; workflow: WorkflowRecordListItemWithThumbnailDTO } + | { status: 'missing'; workflowId: string }; + +export const buildSavedWorkflowOptions = (workflows: WorkflowRecordListItemWithThumbnailDTO[]): ComboboxOption[] => { + return workflows.map((workflow) => ({ + label: workflow.name, + value: workflow.workflow_id, + isDisabled: workflow.call_saved_workflow_compatibility?.is_callable === false, + })); +}; + +const baseSavedWorkflowPickerQueryArg = { + page: 0, + per_page: undefined, + order_by: 'name', + direction: 'ASC', + tags: [] as string[], + has_been_opened: undefined, + callable: true, +} as const; + +export const getSavedWorkflowPickerOwnedQueryArg = (query = '') => ({ + ...baseSavedWorkflowPickerQueryArg, + query, + categories: ['user', 'default'] as ('user' | 'default')[], + is_public: undefined, +}); + +export const getSavedWorkflowPickerSharedQueryArg = (query = '') => ({ + ...baseSavedWorkflowPickerQueryArg, + query, + categories: ['user'] as ('user' | 'default')[], + is_public: true, +}); + +export const mergeSavedWorkflowPickerItems = ( + ...workflowLists: WorkflowRecordListItemWithThumbnailDTO[][] +): WorkflowRecordListItemWithThumbnailDTO[] => { + const workflowsById = new Map(); + for (const workflows of workflowLists) { + for (const workflow of workflows) { + if (!workflowsById.has(workflow.workflow_id)) { + workflowsById.set(workflow.workflow_id, workflow); + } + } + } + return Array.from(workflowsById.values()); +}; + +export const shouldFetchNextSavedWorkflowPickerPage = ({ + hasNextPage, + isFetching, +}: { + hasNextPage: boolean; + isFetching: boolean; +}) => hasNextPage && !isFetching; + +export const getSavedWorkflowSelectionState = ( + workflows: WorkflowRecordListItemWithThumbnailDTO[], + workflowId: string, + selectedWorkflow?: WorkflowRecordListItemWithThumbnailDTO +): SavedWorkflowSelectionState => { + if (!workflowId) { + return { status: 'unselected' }; + } + + const workflow = workflows.find((workflow) => workflow.workflow_id === workflowId); + if (workflow) { + return { status: 'selected', workflow }; + } + + if (selectedWorkflow?.workflow_id === workflowId) { + return { status: 'selected', workflow: selectedWorkflow }; + } + + return { status: 'missing', workflowId }; +}; + +export const getSavedWorkflowListItemFromRecord = ( + workflow: S['WorkflowRecordWithThumbnailDTO'] +): WorkflowRecordListItemWithThumbnailDTO => ({ + workflow_id: workflow.workflow_id, + name: workflow.name, + created_at: workflow.created_at, + updated_at: workflow.updated_at, + opened_at: workflow.opened_at, + user_id: workflow.user_id, + is_public: workflow.is_public, + revision: workflow.revision, + description: workflow.workflow.description, + category: workflow.workflow.meta.category, + tags: workflow.workflow.tags, + thumbnail_url: workflow.thumbnail_url, + call_saved_workflow_compatibility: workflow.call_saved_workflow_compatibility, +}); + +export const getSavedWorkflowSelectionOption = (selectionState: SavedWorkflowSelectionState): ComboboxOption | null => { + if (selectionState.status === 'unselected') { + return null; + } + + if (selectionState.status === 'selected') { + return { + label: selectionState.workflow.name, + value: selectionState.workflow.workflow_id, + }; + } + + return { + label: MISSING_WORKFLOW_OPTION_VALUE, + value: MISSING_WORKFLOW_OPTION_VALUE, + }; +}; + +type SavedWorkflowDisplayState = + | { + selection: 'unselected' | 'missing'; + statusLabelKey: 'nodes.savedWorkflowChoose' | 'nodes.savedWorkflowMissing'; + badges: SavedWorkflowBadge[]; + compatibilityMessageKey: null; + } + | { + selection: 'selected'; + statusLabelKey: null; + badges: SavedWorkflowBadge[]; + compatibilityMessageKey: WorkflowCallCompatibilityMessageKey | null; + }; + +export const getSavedWorkflowDisplayState = ( + selectionState: SavedWorkflowSelectionState +): SavedWorkflowDisplayState => { + if (selectionState.status === 'unselected') { + return { + selection: 'unselected', + statusLabelKey: 'nodes.savedWorkflowChoose', + badges: [], + compatibilityMessageKey: null, + }; + } + + if (selectionState.status === 'missing') { + return { + selection: 'missing', + statusLabelKey: 'nodes.savedWorkflowMissing', + badges: [], + compatibilityMessageKey: null, + }; + } + + const compatibilityState = getWorkflowCallCompatibilityState(selectionState.workflow); + const badges: SavedWorkflowBadge[] = []; + if (compatibilityState.isUnsupported) { + badges.push('unsupported'); + } + if (selectionState.workflow.category === 'default') { + badges.push('default'); + } else if (selectionState.workflow.is_public) { + badges.push('shared'); + } + + return { + selection: 'selected', + statusLabelKey: null, + badges, + compatibilityMessageKey: compatibilityState.messageKey, + }; +}; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/types.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/types.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/types.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/videoFieldErrors.test.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/videoFieldErrors.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/videoFieldErrors.test.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/videoFieldErrors.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/videoFieldErrors.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/videoFieldErrors.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/videoFieldErrors.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/videoFieldErrors.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Notes/NotesNode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Notes/NotesNode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Notes/NotesNode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/Notes/NotesNode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/InvocationNodeTitle.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/InvocationNodeTitle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/InvocationNodeTitle.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/InvocationNodeTitle.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeCollapseButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/NodeCollapseButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeCollapseButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/NodeCollapseButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NonInvocationNodeTitle.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/NonInvocationNodeTitle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NonInvocationNodeTitle.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/NonInvocationNodeTitle.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NonInvocationNodeWrapper.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/NonInvocationNodeWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NonInvocationNodeWrapper.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/NonInvocationNodeWrapper.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/shared.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/shared.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/shared.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/nodes/common/shared.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/AutoLayoutPopover.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/BottomLeftPanel/AutoLayoutPopover.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/AutoLayoutPopover.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/BottomLeftPanel/AutoLayoutPopover.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/BottomLeftPanel.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/BottomLeftPanel/BottomLeftPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/BottomLeftPanel.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/BottomLeftPanel/BottomLeftPanel.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/SaveWorkflowButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/SaveWorkflowButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/SaveWorkflowButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/SaveWorkflowButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopCenterPanel.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/TopCenterPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopCenterPanel.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/TopCenterPanel.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopLeftPanel.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/TopLeftPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopLeftPanel.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/TopLeftPanel.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopRightPanel.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/TopRightPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopRightPanel.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/TopRightPanel.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/UpdateNodesButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/UpdateNodesButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/UpdateNodesButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopPanel/UpdateNodesButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/ReloadSchemaButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopRightPanel/ReloadSchemaButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/ReloadSchemaButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopRightPanel/ReloadSchemaButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/workflowContextMenu.test.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/workflowContextMenu.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/workflowContextMenu.test.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/workflowContextMenu.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/workflowContextMenu.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/workflowContextMenu.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/workflowContextMenu.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/workflowContextMenu.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/workflowHotkeys.test.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/workflowHotkeys.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/workflowHotkeys.test.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/workflowHotkeys.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/workflowHotkeys.ts b/invokeai/frontend/webv1/src/features/nodes/components/flow/workflowHotkeys.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/flow/workflowHotkeys.ts rename to invokeai/frontend/webv1/src/features/nodes/components/flow/workflowHotkeys.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/EditModeLeftPanelContent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/EditModeLeftPanelContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/EditModeLeftPanelContent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/EditModeLeftPanelContent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowNameAndActions.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowNameAndActions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowNameAndActions.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowNameAndActions.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/SaveWorkflowButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowListMenu/SaveWorkflowButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/SaveWorkflowButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowListMenu/SaveWorkflowButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuTrigger.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuTrigger.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuTrigger.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuTrigger.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowName.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowName.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowName.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowName.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowViewEditToggleButton.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowViewEditToggleButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowViewEditToggleButton.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowViewEditToggleButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowsTabLeftPanel.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowsTabLeftPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowsTabLeftPanel.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/WorkflowsTabLeftPanel.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/AutosizeTextarea.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/AutosizeTextarea.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/AutosizeTextarea.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/AutosizeTextarea.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElementSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/ContainerElementSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElementSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/ContainerElementSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DividerElement.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DividerElement.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DividerElement.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DividerElement.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DividerElementComponent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DividerElementComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DividerElementComponent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DividerElementComponent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DividerElementEditMode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DividerElementEditMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DividerElementEditMode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DividerElementEditMode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DividerElementViewMode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DividerElementViewMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DividerElementViewMode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DividerElementViewMode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DndListDropIndicator.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DndListDropIndicator.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/DndListDropIndicator.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/DndListDropIndicator.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/FormElementEditModeContent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/FormElementEditModeContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/FormElementEditModeContent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/FormElementEditModeContent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/FormElementEditModeHeader.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/FormElementEditModeHeader.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/FormElementEditModeHeader.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/FormElementEditModeHeader.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElement.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElement.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElement.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElement.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementContent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElementContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementContent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElementContent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementContentEditable.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElementContentEditable.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementContentEditable.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElementContentEditable.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementEditMode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElementEditMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementEditMode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElementEditMode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementViewMode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElementViewMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementViewMode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/HeadingElementViewMode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElement.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElement.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElement.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElement.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementDescriptionEditable.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementDescriptionEditable.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementDescriptionEditable.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementDescriptionEditable.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementEditMode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementEditMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementEditMode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementEditMode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementFloatSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementFloatSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementFloatSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementFloatSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementIntegerSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementIntegerSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementIntegerSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementIntegerSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementLabel.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementLabel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementLabel.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementLabel.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementLabelEditable.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementLabelEditable.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementLabelEditable.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementLabelEditable.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementStringDropdownSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementStringDropdownSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementStringDropdownSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementStringDropdownSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementStringSettings.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementStringSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementStringSettings.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementStringSettings.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElement.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElement.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElement.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElement.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementContent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElementContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementContent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElementContent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementContentEditable.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElementContentEditable.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementContentEditable.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElementContentEditable.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementEditMode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElementEditMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementEditMode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElementEditMode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementViewMode.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElementViewMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementViewMode.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/TextElementViewMode.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/WorkflowBuilder.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/WorkflowBuilder.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/WorkflowBuilder.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/WorkflowBuilder.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/WorkflowBuilderMenu.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/WorkflowBuilderMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/WorkflowBuilderMenu.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/WorkflowBuilderMenu.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/center-or-closest-edge.ts b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/center-or-closest-edge.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/center-or-closest-edge.ts rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/center-or-closest-edge.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/contexts.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/contexts.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/contexts.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/contexts.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/dnd-hooks.ts b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/dnd-hooks.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/dnd-hooks.ts rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/dnd-hooks.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/form-manipulation.test.ts b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/form-manipulation.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/form-manipulation.test.ts rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/form-manipulation.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/form-manipulation.ts b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/form-manipulation.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/form-manipulation.ts rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/form-manipulation.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/use-add-remove-form-element.ts b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/use-add-remove-form-element.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/use-add-remove-form-element.ts rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/use-add-remove-form-element.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/use-element.ts b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/use-element.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/use-element.ts rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/use-element.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/use-reset-all-node-fields.ts b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/use-reset-all-node-fields.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/use-reset-all-node-fields.ts rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/builder/use-reset-all-node-fields.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/ImageOutputPreview.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/ImageOutputPreview.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/ImageOutputPreview.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/ImageOutputPreview.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTabEditableNodeTitle.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorTabEditableNodeTitle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTabEditableNodeTitle.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorTabEditableNodeTitle.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/NodeTemplateGate.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/NodeTemplateGate.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/NodeTemplateGate.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/NodeTemplateGate.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/WorkflowNodeInspectorPanel.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/WorkflowNodeInspectorPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/WorkflowNodeInspectorPanel.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/inspector/WorkflowNodeInspectorPanel.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/EmptyState.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/EmptyState.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/EmptyState.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/EmptyState.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/ViewModeLeftPanelContent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/ViewModeLeftPanelContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/ViewModeLeftPanelContent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/ViewModeLeftPanelContent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowInfoTooltipContent.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/WorkflowInfoTooltipContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowInfoTooltipContent.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/WorkflowInfoTooltipContent.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowWarning.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/WorkflowWarning.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowWarning.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/WorkflowWarning.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowWarningTooltip.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/WorkflowWarningTooltip.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowWarningTooltip.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/viewMode/WorkflowWarningTooltip.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowGeneralTab.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowGeneralTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowGeneralTab.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowGeneralTab.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowJSONTab.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowJSONTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowJSONTab.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowJSONTab.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DeleteWorkflow.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DeleteWorkflow.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DeleteWorkflow.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DeleteWorkflow.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DownloadWorkflow.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DownloadWorkflow.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DownloadWorkflow.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DownloadWorkflow.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/EditWorkflow.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/EditWorkflow.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/EditWorkflow.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/EditWorkflow.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/ViewWorkflow.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/ViewWorkflow.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/ViewWorkflow.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/ViewWorkflow.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.utils.test.ts b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.utils.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.utils.test.ts rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.utils.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.utils.ts b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.utils.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.utils.ts rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal.utils.ts diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibrarySideNav.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibrarySideNav.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibrarySideNav.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibrarySideNav.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryTopNav.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryTopNav.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryTopNav.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryTopNav.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowList.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowList.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowList.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowListItem.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowListItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowListItem.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowListItem.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowSearch.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowSearch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowSearch.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowSearch.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowSortControl.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowSortControl.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowSortControl.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowSortControl.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowPanel.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowPanel.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowPanel.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailEditor.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailEditor.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailEditor.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailEditor.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx b/invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx similarity index 100% rename from invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx rename to invokeai/frontend/webv1/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useAutoLayout.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useAutoLayout.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useAutoLayout.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useAutoLayout.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupColorToken.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useBatchGroupColorToken.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupColorToken.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useBatchGroupColorToken.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupId.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useBatchGroupId.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useBatchGroupId.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useBatchGroupId.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useBuildNode.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useBuildNode.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useConnection.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useConnection.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useConnection.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useConnection.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldConnectionState.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useFieldConnectionState.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useFieldConnectionState.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useFieldConnectionState.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useGetNodesNeedUpdate.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useGetNodesNeedUpdate.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldDefaultValue.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldDefaultValue.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldDefaultValue.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldDefaultValue.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldErrors.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldErrors.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldErrors.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldErrors.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldInitialFormValue.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldInitialFormValue.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldInitialFormValue.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldInitialFormValue.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldInstance.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldInstance.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldInstance.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldInstance.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldInstanceExists.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldInstanceExists.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldInstanceExists.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldInstanceExists.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldIsAddedToForm.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldIsAddedToForm.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldIsAddedToForm.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldIsAddedToForm.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldIsConnected.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldIsConnected.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldIsConnected.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldIsConnected.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldIsInvalid.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldIsInvalid.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldIsInvalid.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldIsInvalid.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldNameSafe.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldNameSafe.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldNameSafe.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldNameSafe.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldNamesByStatus.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldNamesByStatus.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldNamesByStatus.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldNamesByStatus.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldTemplateExists.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldTemplateExists.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldTemplateExists.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldTemplateExists.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldTemplateOrThrow.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldTemplateOrThrow.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldTemplateOrThrow.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldTemplateOrThrow.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldTemplateSafe.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldTemplateSafe.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldTemplateSafe.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldTemplateSafe.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldTemplateTitleOrThrow.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldTemplateTitleOrThrow.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldTemplateTitleOrThrow.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldTemplateTitleOrThrow.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldUserDescriptionSafe.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldUserDescriptionSafe.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldUserDescriptionSafe.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldUserDescriptionSafe.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useInputFieldUserTitleSafe.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldUserTitleSafe.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useInputFieldUserTitleSafe.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useInputFieldUserTitleSafe.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useIsBatchNode.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useIsBatchNode.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useIsBatchNode.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useIsBatchNode.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useIsValidConnection.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useIsValidConnection.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useMouseOverNode.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useMouseOverNode.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useMouseOverNode.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useMouseOverNode.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeClassification.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeClassification.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeCopyPaste.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeCopyPaste.ts similarity index 94% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeCopyPaste.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeCopyPaste.ts index 5c86a0b8f08..75c845c22ba 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeCopyPaste.ts +++ b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeCopyPaste.ts @@ -134,11 +134,11 @@ const _pasteSelection = (withEdgesToCopiedNodes?: boolean) => { ); return; } - } else if (e.type === 'default') { + } else if (e.type === 'default' || e.type === 'loop_linkage') { const { type, source, sourceHandle, target, targetHandle } = e; - // Our "default" type edges always have handles, but the reactflow types don't seem to inherit this typing, so we - // need a type guard here. But these _should_ always be present. + // Our handle-bearing edges do not inherit this typing from React Flow, so + // we need a type guard here. These edges should always have handles. if (!sourceHandle || !targetHandle) { log.warn( { edge: { type, source, sourceHandle, target, targetHandle } }, @@ -165,7 +165,7 @@ const _pasteSelection = (withEdgesToCopiedNodes?: boolean) => { return; } } else { - // All our edges should be either "collapsed" or "default" type, so if we get here, something is wrong + // All our edges should be "collapsed", "default", or "loop_linkage". const { type } = e; log.warn({ edge: { type } }, `Invalid edge type, cannot paste`); return; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeExecutionState.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeExecutionState.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeExecutionState.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeExecutionState.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeHasGalleryOutput.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeHasGalleryOutput.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeHasGalleryOutput.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeHasGalleryOutput.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeIsIntermediate.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeIsIntermediate.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeIsIntermediate.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeIsIntermediate.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeIsInvalid.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeIsInvalid.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeIsInvalid.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeIsInvalid.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeNeedsUpdate.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeNeedsUpdate.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeNotes.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeNotes.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeNotes.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeNotes.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodePack.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodePack.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodePack.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodePack.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateOrThrow.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeTemplateOrThrow.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateOrThrow.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeTemplateOrThrow.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateSafe.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeTemplateSafe.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateSafe.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeTemplateSafe.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitleSafe.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeTemplateTitleSafe.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitleSafe.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeTemplateTitleSafe.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeType.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeType.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeType.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeType.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeUserTitleSafe.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeUserTitleSafe.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeUserTitleSafe.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeUserTitleSafe.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeVersion.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useNodeVersion.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useNodeVersion.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useNodeVersion.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldName.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useOutputFieldName.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldName.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useOutputFieldName.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useOutputFieldNames.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useOutputFieldNames.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldTemplate.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useOutputFieldTemplate.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldTemplate.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useOutputFieldTemplate.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldTemplateExists.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useOutputFieldTemplateExists.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldTemplateExists.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useOutputFieldTemplateExists.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/usePrettyFieldType.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/usePrettyFieldType.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/usePrettyFieldType.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/usePrettyFieldType.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useUseCache.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useUseCache.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useUseCache.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useUseCache.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useWithFooter.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useWithFooter.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useWithFooter.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useWithFooter.ts diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useZoomToNode.ts b/invokeai/frontend/webv1/src/features/nodes/hooks/useZoomToNode.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/hooks/useZoomToNode.ts rename to invokeai/frontend/webv1/src/features/nodes/hooks/useZoomToNode.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/nodesSlice.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/nodesSlice.test.ts rename to invokeai/frontend/webv1/src/features/nodes/store/nodesSlice.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/webv1/src/features/nodes/store/nodesSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts rename to invokeai/frontend/webv1/src/features/nodes/store/nodesSlice.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/reactFlowInstance.ts b/invokeai/frontend/webv1/src/features/nodes/store/reactFlowInstance.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/reactFlowInstance.ts rename to invokeai/frontend/webv1/src/features/nodes/store/reactFlowInstance.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/selectors.ts b/invokeai/frontend/webv1/src/features/nodes/store/selectors.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/selectors.ts rename to invokeai/frontend/webv1/src/features/nodes/store/selectors.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/types.ts b/invokeai/frontend/webv1/src/features/nodes/store/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/types.ts rename to invokeai/frontend/webv1/src/features/nodes/store/types.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/areTypesEqual.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/areTypesEqual.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/areTypesEqual.test.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/areTypesEqual.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/areTypesEqual.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/areTypesEqual.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/areTypesEqual.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/areTypesEqual.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/connectorTopology.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/connectorTopology.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/connectorTopology.test.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/connectorTopology.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/connectorTopology.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/connectorTopology.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/connectorTopology.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/connectorTopology.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/fieldValidators.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/fieldValidators.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/fieldValidators.test.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/fieldValidators.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/fieldValidators.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/fieldValidators.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/fieldValidators.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/fieldValidators.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/findUnoccupiedPosition.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/findUnoccupiedPosition.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/getCollectItemType.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/getCollectItemType.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/getCollectItemType.test.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/getCollectItemType.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/getCollectItemType.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/getCollectItemType.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/getCollectItemType.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/getCollectItemType.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/getFirstValidConnection.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/getFirstValidConnection.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/getFirstValidConnection.test.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/getFirstValidConnection.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/getFirstValidConnection.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/getFirstValidConnection.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/getFirstValidConnection.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/getFirstValidConnection.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/getHasCycles.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/getHasCycles.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/getHasCycles.test.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/getHasCycles.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/getHasCycles.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/getHasCycles.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/getHasCycles.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/getHasCycles.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/makeConnectionErrorSelector.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/makeConnectionErrorSelector.ts similarity index 97% rename from invokeai/frontend/web/src/features/nodes/store/util/makeConnectionErrorSelector.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/makeConnectionErrorSelector.ts index fa10fcc3e6f..3a13708ec08 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/makeConnectionErrorSelector.ts +++ b/invokeai/frontend/webv1/src/features/nodes/store/util/makeConnectionErrorSelector.ts @@ -8,7 +8,7 @@ import type { AnyEdge } from 'features/nodes/types/invocation'; /** * Creates a selector that validates a pending connection. * - * NOTE: The logic here must be duplicated in `invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts` + * NOTE: The logic here must be duplicated in `invokeai/frontend/webv1/src/features/nodes/hooks/useIsValidConnection.ts` * TODO: Figure out how to do this without duplicating all the logic * * @param templates The invocation templates diff --git a/invokeai/frontend/webv1/src/features/nodes/store/util/reactFlowUtil.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/reactFlowUtil.test.ts new file mode 100644 index 00000000000..29d0453d39f --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/store/util/reactFlowUtil.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from 'vitest'; + +import { connectionToEdge } from './reactFlowUtil'; + +describe('connectionToEdge', () => { + it('creates a default edge with the expected id and endpoints', () => { + expect( + connectionToEdge({ + source: 'source-node', + sourceHandle: 'value', + target: 'target-node', + targetHandle: 'a', + }) + ).toEqual({ + type: 'default', + source: 'source-node', + sourceHandle: 'value', + target: 'target-node', + targetHandle: 'a', + id: 'reactflow__edge-source-nodevalue-target-nodea', + }); + }); + + // `connectionToEdge` is the only edge constructor this editor has, and it is + // used by reconnect and by fresh connection drags. Now that loop_linkage edges + // load here, dragging one back onto the same handle must not silently turn it + // into a data edge: webv2 infers the type from the handle names, and the + // backend rejects a loop_linkage field wired by a default edge. + it('creates a loop_linkage edge when both endpoints are loop linkage handles', () => { + expect( + connectionToEdge({ + source: 'for-node', + sourceHandle: 'loop_linkage', + target: 'for-return-node', + targetHandle: 'loop_linkage', + }) + ).toMatchObject({ type: 'loop_linkage' }); + }); +}); diff --git a/invokeai/frontend/web/src/features/nodes/store/util/reactFlowUtil.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/reactFlowUtil.ts similarity index 92% rename from invokeai/frontend/web/src/features/nodes/store/util/reactFlowUtil.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/reactFlowUtil.ts index 3eaece154fb..1e1fdd1dc62 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/reactFlowUtil.ts +++ b/invokeai/frontend/webv1/src/features/nodes/store/util/reactFlowUtil.ts @@ -24,7 +24,7 @@ export const connectionToEdge = (connection: Connection): AnyEdge => { const { source, sourceHandle, target, targetHandle } = connection; assert(source && sourceHandle && target && targetHandle, 'Invalid connection'); return { - type: 'default', + type: sourceHandle === 'loop_linkage' && targetHandle === 'loop_linkage' ? 'loop_linkage' : 'default', source, sourceHandle, target, diff --git a/invokeai/frontend/web/src/features/nodes/store/util/testUtils.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/testUtils.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/testUtils.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/testUtils.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/validateConnection.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/validateConnection.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/validateConnection.test.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/validateConnection.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/validateConnection.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/validateConnection.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/validateConnection.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/validateConnection.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/validateConnectionTypes.test.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/validateConnectionTypes.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/validateConnectionTypes.test.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/validateConnectionTypes.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/util/validateConnectionTypes.ts b/invokeai/frontend/webv1/src/features/nodes/store/util/validateConnectionTypes.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/util/validateConnectionTypes.ts rename to invokeai/frontend/webv1/src/features/nodes/store/util/validateConnectionTypes.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/workflowLibraryModal.ts b/invokeai/frontend/webv1/src/features/nodes/store/workflowLibraryModal.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/workflowLibraryModal.ts rename to invokeai/frontend/webv1/src/features/nodes/store/workflowLibraryModal.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/workflowLibrarySlice.ts b/invokeai/frontend/webv1/src/features/nodes/store/workflowLibrarySlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/workflowLibrarySlice.ts rename to invokeai/frontend/webv1/src/features/nodes/store/workflowLibrarySlice.ts diff --git a/invokeai/frontend/web/src/features/nodes/store/workflowSettingsSlice.ts b/invokeai/frontend/webv1/src/features/nodes/store/workflowSettingsSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/store/workflowSettingsSlice.ts rename to invokeai/frontend/webv1/src/features/nodes/store/workflowSettingsSlice.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts b/invokeai/frontend/webv1/src/features/nodes/types/common.test-d.ts similarity index 96% rename from invokeai/frontend/web/src/features/nodes/types/common.test-d.ts rename to invokeai/frontend/webv1/src/features/nodes/types/common.test-d.ts index 373f95f6f59..470bad268d3 100644 --- a/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts +++ b/invokeai/frontend/webv1/src/features/nodes/types/common.test-d.ts @@ -15,6 +15,7 @@ import type { zFlux2VariantType, zFluxVariantType, zKrea2VariantType, + zLTX2VariantType, zModelFormat, zModelVariantType, zQwen3VariantType, @@ -55,6 +56,7 @@ describe('Common types', () => { test('ZImageVariantType', () => assert, S['ZImageVariantType']>>()); test('Krea2VariantType', () => assert, S['Krea2VariantType']>>()); test('Qwen3VariantType', () => assert, S['Qwen3VariantType']>>()); + test('LTX2VariantType', () => assert, S['LTX2VariantType']>>()); test('ModelFormat', () => assert, S['ModelFormat']>>()); // Misc types diff --git a/invokeai/frontend/web/src/features/nodes/types/common.ts b/invokeai/frontend/webv1/src/features/nodes/types/common.ts similarity index 93% rename from invokeai/frontend/web/src/features/nodes/types/common.ts rename to invokeai/frontend/webv1/src/features/nodes/types/common.ts index c38cd493b52..1fb3bb84b6c 100644 --- a/invokeai/frontend/web/src/features/nodes/types/common.ts +++ b/invokeai/frontend/webv1/src/features/nodes/types/common.ts @@ -120,6 +120,8 @@ export const zBaseModelType = z.enum([ 'external', 'anima', 'wan', + 'minimax-h3', + 'ltx-2', 'unknown', ]); export type BaseModelType = z.infer; @@ -138,6 +140,7 @@ export const zMainModelBase = z.enum([ 'ideogram-4', 'anima', 'wan', + 'minimax-h3', ]); type MainModelBase = z.infer; export const isMainModelBase = (base: unknown): base is MainModelBase => zMainModelBase.safeParse(base).success; @@ -162,12 +165,14 @@ export const zModelType = z.enum([ 'qwen3_vl_encoder', 'wan_t5_encoder', 'gemma2_encoder', + 'gemma4_encoder', 'clip_embed', 'siglip', 'flux_redux', 'prompt_enhancer', 'external_image_generator', 'pid_decoder', + 'ltx2_duration_head', 'unknown', ]); export type ModelType = z.infer; @@ -181,11 +186,17 @@ export const zSubModelType = z.enum([ 'tokenizer', 'tokenizer_2', 'tokenizer_3', + 'processor', 'pe', 'pe_tokenizer', 'vae', 'vae_decoder', 'vae_encoder', + 'audio_vae', + 'vocoder', + 'connectors', + 'latent_upsampler', + 'temporal_latent_upsampler', 'scheduler', 'safety_checker', ]); @@ -202,7 +213,12 @@ const zWanVariantType = z.enum(['t2v_a14b', 'i2v_a14b', 'ti2v_5b']); * targets. A14B = inner_dim 5120 (both T2V and I2V), 5B = inner_dim 3072. */ const zWanLoRAVariantType = z.enum(['a14b', '5b']); export const zQwen3VariantType = z.enum(['qwen3_4b', 'qwen3_8b', 'qwen3_06b']); -const zMistralVariantType = z.enum(['cow_mistral3_small', 'mistral3_24b']); +/** Qwen3-VL (vision-language) encoders: Krea-2 takes the 4B, Ideogram 4 the 8B. */ +const zQwen3VLVariantType = z.enum(['qwen3_vl_4b', 'qwen3_vl_8b']); +const zMiniMaxH3VariantType = z.enum(['fl2va', 'ref2va']); +/** LTX-2 transformers: the guided ('dev') and the step-distilled release of one architecture. */ +export const zLTX2VariantType = z.enum(['ltx2_dev', 'ltx2_distilled']); +const zMistralVariantType = z.enum(['cow_mistral3_small', 'mistral3_24b', 'ministral3_3b']); const zPiDDecoderVariantType = z.enum(['res2k_sr4x', 'res2kto4k_sr4x']); export const zAnyModelVariant = z.union([ zModelVariantType, @@ -215,6 +231,9 @@ export const zAnyModelVariant = z.union([ zWanVariantType, zWanLoRAVariantType, zQwen3VariantType, + zQwen3VLVariantType, + zMiniMaxH3VariantType, + zLTX2VariantType, zMistralVariantType, zPiDDecoderVariantType, ]); @@ -236,6 +255,7 @@ export const zModelFormat = z.enum([ 'qwen3_vl_encoder', 'wan_t5_encoder', 'gemma2_encoder', + 'gemma4_encoder', 'bnb_quantized_int8b', 'bnb_quantized_nf4b', 'gguf_quantized', diff --git a/invokeai/frontend/web/src/features/nodes/types/constants.ts b/invokeai/frontend/webv1/src/features/nodes/types/constants.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/constants.ts rename to invokeai/frontend/webv1/src/features/nodes/types/constants.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/error.ts b/invokeai/frontend/webv1/src/features/nodes/types/error.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/error.ts rename to invokeai/frontend/webv1/src/features/nodes/types/error.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/extraInputs.ts b/invokeai/frontend/webv1/src/features/nodes/types/extraInputs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/extraInputs.ts rename to invokeai/frontend/webv1/src/features/nodes/types/extraInputs.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/field.test.ts b/invokeai/frontend/webv1/src/features/nodes/types/field.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/field.test.ts rename to invokeai/frontend/webv1/src/features/nodes/types/field.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/field.ts b/invokeai/frontend/webv1/src/features/nodes/types/field.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/field.ts rename to invokeai/frontend/webv1/src/features/nodes/types/field.ts diff --git a/invokeai/frontend/webv1/src/features/nodes/types/invocation.test.ts b/invokeai/frontend/webv1/src/features/nodes/types/invocation.test.ts new file mode 100644 index 00000000000..02a04c99c66 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/types/invocation.test.ts @@ -0,0 +1,216 @@ +/** + * Regression tests for PR #9162 review: + * The `MetadataExtraField` catch-all (value: `z.any()`) must only preserve undeclared "extra" + * input values for node types that accept extras (pydantic `extra='allow'`, e.g. `core_metadata`). + * + * Workflow inputs are parsed WITHOUT their field template (see `zInvocationNodeData.inputs`), so a + * global catch-all would let any stale/malformed connection-only value survive parsing for ANY + * node and later leak into the backend graph via `buildNodesGraph`. Scoping the catch-all to + * extra-accepting node types prevents that. + */ + +import { describe, expect, it } from 'vitest'; + +import { zInvocationNodeData } from './invocation'; + +const buildNodeData = (type: string, inputs: Record) => ({ + id: 'node-1', + version: '1.0.0', + nodePack: 'invokeai', + label: '', + notes: '', + type, + isOpen: true, + isIntermediate: false, + useCache: true, + inputs, +}); + +// A value that matches no stateful field-instance schema: the `type` discriminator rules out the +// generator/color/model/image schemas, and it is neither a primitive nor a collection. Pre-PR such a +// value coerced to `undefined` via the stateless branch; the `MetadataExtraField` catch-all is what +// preserves it, so it is the ideal probe for the scoping boundary. +const opaqueValue = { type: 'connection-payload', payload: 123 }; + +describe('zInvocationNodeData: extra-input scoping', () => { + it('drops a stale value on a NON-extra node (coerced to undefined)', () => { + const parsed = zInvocationNodeData.parse( + buildNodeData('some_stateless_node', { + unet: { name: 'unet', label: '', description: '', value: opaqueValue }, + }) + ); + // The key is preserved (it's a real input on the node) but its value is NOT — it falls through + // to the stateless branch which coerces to undefined, so it cannot leak into the backend graph. + expect(parsed.inputs.unet).toBeDefined(); + expect(parsed.inputs.unet?.value).toBeUndefined(); + }); + + it('drops a stale array-of-records value on a NON-extra node (would match a metadata passthrough instance)', () => { + // The metadata pass-through instances (LoRA/ControlNet/...) accept `array(record(string, any))`. + // On a non-extra node they must NOT be used, so a stale array-of-objects value is coerced away. + const parsed = zInvocationNodeData.parse( + buildNodeData('some_stateless_node', { + metadata: { name: 'metadata', label: '', description: '', value: [{ foo: 'bar' }] }, + }) + ); + expect(parsed.inputs.metadata).toBeDefined(); + expect(parsed.inputs.metadata?.value).toBeUndefined(); + }); + + it('preserves a metadata passthrough value (loras) on core_metadata', () => { + const loras = [{ model: { key: 'k', hash: 'h', name: 'n', base: 'z-image', type: 'lora' }, weight: 0.75 }]; + const parsed = zInvocationNodeData.parse( + buildNodeData('core_metadata', { + loras: { name: 'loras', label: '', description: '', value: loras }, + }) + ); + expect(parsed.inputs.loras?.value).toEqual(loras); + }); + + it('preserves an undeclared extra value on core_metadata (extra=allow)', () => { + const parsed = zInvocationNodeData.parse( + buildNodeData('core_metadata', { + some_extra: { name: 'some_extra', label: '', description: '', value: opaqueValue }, + }) + ); + expect(parsed.inputs.some_extra?.value).toEqual(opaqueValue); + }); + + it('preserves primitive extras on core_metadata', () => { + const parsed = zInvocationNodeData.parse( + buildNodeData('core_metadata', { + z_image_seed_variance_enabled: { + name: 'z_image_seed_variance_enabled', + label: '', + description: '', + value: false, + }, + z_image_seed_variance_strength: { + name: 'z_image_seed_variance_strength', + label: '', + description: '', + value: 0.1, + }, + }) + ); + expect(parsed.inputs.z_image_seed_variance_enabled?.value).toBe(false); + expect(parsed.inputs.z_image_seed_variance_strength?.value).toBe(0.1); + }); + + it('normalizes webv2 dynamic input templates', () => { + const parsed = zInvocationNodeData.parse({ + ...buildNodeData('call_saved_workflow', {}), + dynamicInputTemplates: { + board: { + batch: false, + cardinality: 'SINGLE', + description: 'Child board', + fieldKind: 'internal', + input: 'any', + name: 'board', + required: false, + title: 'Board', + type: { batch: false, cardinality: 'SINGLE', name: 'BoardField' }, + uiChoiceLabels: null, + uiComponent: null, + uiHidden: false, + uiModelBase: null, + uiModelFormat: null, + uiModelType: null, + uiOrder: null, + }, + }, + }); + + expect(parsed.dynamicInputTemplates.board).toMatchObject({ + fieldKind: 'input', + name: 'board', + title: 'Board', + type: { name: 'BoardField' }, + ui_hidden: false, + }); + }); + + it('accepts legacy-compatible enum dynamic templates from webv2', () => { + const parsed = zInvocationNodeData.parse({ + ...buildNodeData('call_saved_workflow', {}), + dynamicInputTemplates: { + literal: { + batch: false, + cardinality: 'SINGLE', + default: '2', + description: 'Literal value', + fieldKind: 'input', + input: 'any', + name: 'literal', + options: ['2'], + required: false, + title: 'Literal', + type: { batch: false, cardinality: 'SINGLE', name: 'EnumField' }, + uiHidden: false, + }, + }, + }); + + expect(parsed.dynamicInputTemplates.literal).toMatchObject({ default: '2', options: ['2'] }); + }); + + it('accepts an enum dynamic template whose optional default is unset', () => { + // webv2 leaves `default` unset for an optional enum whose backend default is + // null, so the backend receives `None` rather than an invented choice. JSON + // drops the undefined key, so the template arrives here without it. Legacy's + // `zEnumFieldInputTemplate.default` is a required `z.string()`, and + // `zWorkflowV3.parse` throws rather than dropping one template, so the whole + // workflow fails to open in this editor. + const parsed = zInvocationNodeData.parse({ + ...buildNodeData('call_saved_workflow', {}), + dynamicInputTemplates: { + fidelity: { + description: 'Input fidelity', + exclusiveMaximum: null, + exclusiveMinimum: null, + fieldKind: 'input', + input: 'any', + maximum: null, + minimum: null, + multipleOf: null, + name: 'fidelity', + options: ['low', 'high'], + required: false, + title: 'Input Fidelity', + type: { batch: false, cardinality: 'SINGLE', name: 'EnumField' }, + uiChoiceLabels: null, + uiComponent: null, + uiHidden: false, + uiModelBase: null, + uiModelFormat: null, + uiModelType: null, + uiOrder: null, + }, + }, + }); + + expect(parsed.dynamicInputTemplates.fidelity).toMatchObject({ default: 'low', options: ['low', 'high'] }); + }); + + it('accepts an optional enum dynamic template with no options at the legacy boundary', () => { + const parsed = zInvocationNodeData.parse({ + ...buildNodeData('call_saved_workflow', {}), + dynamicInputTemplates: { + empty: { + description: 'Empty enum', + fieldKind: 'input', + input: 'any', + name: 'empty', + options: [], + required: false, + title: 'Empty enum', + type: { batch: false, cardinality: 'SINGLE', name: 'EnumField' }, + uiHidden: false, + }, + }, + }); + + expect(parsed.dynamicInputTemplates.empty).toMatchObject({ default: '', options: [] }); + }); +}); diff --git a/invokeai/frontend/webv1/src/features/nodes/types/invocation.ts b/invokeai/frontend/webv1/src/features/nodes/types/invocation.ts new file mode 100644 index 00000000000..f25b0909491 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/types/invocation.ts @@ -0,0 +1,307 @@ +import type { Edge, Node } from '@xyflow/react'; +import { z } from 'zod'; + +import { zClassification, zProgressImage } from './common'; +import { nodeAcceptsExtraInputs } from './extraInputs'; +import type { FieldInputInstance } from './field'; +import { zFieldInputInstance, zFieldInputInstanceWithExtras, zFieldInputTemplate, zFieldOutputTemplate } from './field'; +import { zSemVer } from './semver'; + +const zWebv2DynamicInputTemplateSource = z.object({ + default: z.unknown().optional(), + description: z.string(), + exclusiveMaximum: z.number().nullable().optional(), + exclusiveMinimum: z.number().nullable().optional(), + fieldKind: z.enum(['input', 'internal']), + input: z.enum(['connection', 'direct', 'any']), + maximum: z.number().nullable().optional(), + minimum: z.number().nullable().optional(), + multipleOf: z.number().nullable().optional(), + name: z.string(), + options: z.array(z.unknown()).nullable().optional(), + required: z.boolean(), + title: z.string(), + type: z.object({ + batch: z.boolean(), + cardinality: z.enum(['SINGLE', 'COLLECTION', 'SINGLE_OR_COLLECTION']), + name: z.string(), + originalType: z.unknown().optional(), + }), + uiChoiceLabels: z.record(z.string(), z.string()).nullable().optional(), + uiComponent: z.enum(['slider', 'textarea', 'video-frame-index']).nullable().optional(), + uiHidden: z.boolean(), + uiModelBase: z.array(z.string()).nullable().optional(), + uiModelFormat: z.array(z.string()).nullable().optional(), + uiModelType: z.array(z.string()).nullable().optional(), + uiOrder: z.number().int().nullable().optional(), +}); + +const zWebv2DynamicInputTemplate = z.preprocess((value) => { + const result = zWebv2DynamicInputTemplateSource.safeParse(value); + if (!result.success) { + return value; + } + + const template = result.data; + const options = template.options?.map(String); + const defaultValue = + template.type.name === 'EnumField' && (template.default === undefined || template.default === null) + ? (options?.[0] ?? '') + : template.default; + return { + ...template, + fieldKind: 'input' as const, + labels: template.uiChoiceLabels ?? undefined, + exclusiveMaximum: template.exclusiveMaximum ?? undefined, + exclusiveMinimum: template.exclusiveMinimum ?? undefined, + maximum: template.maximum ?? undefined, + minimum: template.minimum ?? undefined, + multipleOf: template.multipleOf ?? undefined, + options: options ?? undefined, + default: + template.type.name === 'EnumField' && defaultValue !== undefined && defaultValue !== null + ? String(defaultValue) + : defaultValue, + originalType: template.type.originalType, + type: template.type, + ui_choice_labels: template.uiChoiceLabels ?? undefined, + ui_component: template.uiComponent ?? undefined, + ui_hidden: template.uiHidden, + ui_model_base: template.uiModelBase ?? undefined, + ui_model_format: template.uiModelFormat ?? undefined, + ui_model_type: template.uiModelType ?? undefined, + ui_order: template.uiOrder ?? undefined, + ui_type: undefined, + }; +}, zFieldInputTemplate); + +const zCompatibleFieldInputTemplate = z.union([zFieldInputTemplate, zWebv2DynamicInputTemplate]); + +// #region InvocationTemplate +const _zInvocationTemplate = z.object({ + type: z.string(), + title: z.string(), + description: z.string(), + tags: z.array(z.string().min(1)), + inputs: z.record(z.string(), zFieldInputTemplate), + outputs: z.record(z.string(), zFieldOutputTemplate), + outputType: z.string().min(1), + version: zSemVer, + useCache: z.boolean(), + nodePack: z.string().min(1).default('invokeai'), + classification: zClassification, + category: z.string().default('other'), +}); +export type InvocationTemplate = z.infer; +// #endregion + +// #region NodeData +export const zInvocationNodeData = z + .object({ + id: z.string().trim().min(1), + version: zSemVer, + nodePack: z.string().min(1).default('invokeai'), + label: z.string(), + notes: z.string(), + type: z.string().trim().min(1), + // Parsed per-input in the transform below so that the input-instance schema can be chosen based + // on the node type (extras are only accepted for nodes that declare `extra='allow'`). + inputs: z.record(z.string(), z.unknown()), + dynamicInputTemplates: z.record(z.string(), zCompatibleFieldInputTemplate).default({}), + isOpen: z.boolean(), + isIntermediate: z.boolean(), + useCache: z.boolean(), + }) + .transform((data, ctx) => { + // Undeclared "extra" inputs (pydantic `extra='allow'`, e.g. `core_metadata`) carry arbitrary + // values that must round-trip. Only nodes known to accept extras get the permissive + // `MetadataExtraField` catch-all; every other node uses the strict instance union so stale or + // malformed saved values are coerced away (via the stateless branch) rather than preserved and + // later leaked into the backend graph by `buildNodesGraph`. + const instanceSchema = nodeAcceptsExtraInputs(data.type) ? zFieldInputInstanceWithExtras : zFieldInputInstance; + const inputs: Record = {}; + for (const [name, rawInput] of Object.entries(data.inputs)) { + const dynamicTemplate = data.dynamicInputTemplates[name]; + const rawValue = + rawInput && typeof rawInput === 'object' && !Array.isArray(rawInput) + ? (rawInput as { value?: unknown }).value + : undefined; + const normalizedInput = + dynamicTemplate?.type.name === 'EnumField' && (typeof rawValue === 'number' || typeof rawValue === 'boolean') + ? { ...(rawInput as Record), value: String(rawValue) } + : rawInput; + const result = instanceSchema.safeParse(normalizedInput); + if (!result.success) { + ctx.addIssue({ + code: 'custom', + message: `Invalid input "${name}": ${result.error.message}`, + path: ['inputs', name], + }); + return z.NEVER; + } + inputs[name] = result.data; + } + return { ...data, inputs }; + }); + +export const zNotesNodeData = z.object({ + id: z.string().trim().min(1), + type: z.literal('notes'), + label: z.string(), + isOpen: z.boolean(), + notes: z.string(), +}); +export const zConnectorNodeData = z.object({ + id: z.string().trim().min(1), + type: z.literal('connector'), + label: z.string(), + isOpen: z.boolean(), +}); +const zCurrentImageNodeData = z.object({ + id: z.string().trim().min(1), + type: z.literal('current_image'), + label: z.string(), + isOpen: z.boolean(), +}); + +export type NotesNodeData = z.infer; +export type InvocationNodeData = z.infer; +export type ConnectorNodeData = z.infer; +type CurrentImageNodeData = z.infer; + +const zInvocationNodeValidationSchema = z.looseObject({ + type: z.literal('invocation'), + data: zInvocationNodeData, +}); +const zInvocationNode = z.custom>( + (val) => zInvocationNodeValidationSchema.safeParse(val).success +); +export type InvocationNode = z.infer; + +const zNotesNodeValidationSchema = z.looseObject({ + type: z.literal('notes'), + data: zNotesNodeData, +}); +const zNotesNode = z.custom>((val) => zNotesNodeValidationSchema.safeParse(val).success); +export type NotesNode = z.infer; + +const zConnectorNodeValidationSchema = z.looseObject({ + type: z.literal('connector'), + data: zConnectorNodeData, +}); +const zConnectorNode = z.custom>( + (val) => zConnectorNodeValidationSchema.safeParse(val).success +); +export type ConnectorNode = z.infer; + +const zCurrentImageNodeValidationSchema = z.looseObject({ + type: z.literal('current_image'), + data: zCurrentImageNodeData, +}); +const zCurrentImageNode = z.custom>( + (val) => zCurrentImageNodeValidationSchema.safeParse(val).success +); +export type CurrentImageNode = z.infer; + +export const zAnyNode = z.union([zInvocationNode, zNotesNode, zConnectorNode, zCurrentImageNode]); +export type AnyNode = z.infer; + +export const isInvocationNode = (node?: AnyNode | null): node is InvocationNode => + Boolean(node && node.type === 'invocation'); +export const isNotesNode = (node?: AnyNode | null): node is NotesNode => Boolean(node && node.type === 'notes'); +export const isConnectorNode = (node?: AnyNode | null): node is ConnectorNode => + Boolean(node && node.type === 'connector'); +// #endregion + +// #region NodeExecutionState +export const zNodeStatus = z.enum(['PENDING', 'IN_PROGRESS', 'COMPLETED', 'FAILED']); +const zNodeError = z.object({ + error_type: z.string(), + error_message: z.string(), + error_traceback: z.string(), +}); +const _zNodeExecutionState = z.object({ + nodeId: z.string().trim().min(1), + status: zNodeStatus, + progress: z.number().nullable(), + progressImage: zProgressImage.nullable(), + outputs: z.array(z.any()), + error: zNodeError.nullable(), +}); +export type NodeExecutionState = z.infer; +// #endregion + +// #region Edges +const zDefaultInvocationNodeEdgeValidationSchema = z.looseObject({ + type: z.literal('default'), +}); +const zDefaultInvocationNodeEdge = z.custom, 'default'>>( + (val) => zDefaultInvocationNodeEdgeValidationSchema.safeParse(val).success +); +export type DefaultInvocationNodeEdge = z.infer; + +const zLoopLinkageInvocationNodeEdgeValidationSchema = z.looseObject({ + type: z.literal('loop_linkage'), +}); +const zLoopLinkageInvocationNodeEdge = z.custom, 'loop_linkage'>>( + (val) => zLoopLinkageInvocationNodeEdgeValidationSchema.safeParse(val).success +); + +const zInvocationNodeEdgeCollapsedData = z.object({ + count: z.number().int().min(1), +}); +const zInvocationNodeEdgeCollapsedValidationSchema = z.looseObject({ + type: z.literal('default'), + data: zInvocationNodeEdgeCollapsedData, +}); +type InvocationNodeEdgeCollapsedData = z.infer; + +const zCollapsedInvocationNodeEdge = z.custom>( + (val) => zInvocationNodeEdgeCollapsedValidationSchema.safeParse(val).success +); +export type CollapsedInvocationNodeEdge = z.infer; +export const zAnyEdge = z.union([ + zDefaultInvocationNodeEdge, + zLoopLinkageInvocationNodeEdge, + zCollapsedInvocationNodeEdge, +]); +export type AnyEdge = z.infer; +// #endregion + +export const isBatchNodeType = (type: string) => + ['image_batch', 'string_batch', 'integer_batch', 'float_batch'].includes(type); + +export const isGeneratorNodeType = (type: string) => + ['image_generator', 'string_generator', 'integer_generator', 'float_generator'].includes(type); + +export const isBatchNode = (node: InvocationNode) => isBatchNodeType(node.data.type); +const isGeneratorNode = (node: InvocationNode) => isGeneratorNodeType(node.data.type); + +export const isExecutableNode = (node: InvocationNode) => { + return !isBatchNode(node) && !isGeneratorNode(node); +}; + +export const getInvocationNodeInputTemplate = ( + nodeData: Pick & Partial>, + template: InvocationTemplate, + fieldName: string +) => { + return nodeData.dynamicInputTemplates?.[fieldName] ?? template.inputs[fieldName]; +}; + +export const getInvocationNodeTemplateWithDynamicInputs = ( + nodeData: Pick & Partial>, + template: InvocationTemplate +): InvocationTemplate => { + if (!nodeData.dynamicInputTemplates || Object.keys(nodeData.dynamicInputTemplates).length === 0) { + return template; + } + + return { + ...template, + inputs: { + ...template.inputs, + ...nodeData.dynamicInputTemplates, + }, + }; +}; diff --git a/invokeai/frontend/web/src/features/nodes/types/openapi.ts b/invokeai/frontend/webv1/src/features/nodes/types/openapi.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/openapi.ts rename to invokeai/frontend/webv1/src/features/nodes/types/openapi.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/semver.ts b/invokeai/frontend/webv1/src/features/nodes/types/semver.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/semver.ts rename to invokeai/frontend/webv1/src/features/nodes/types/semver.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/v1/fieldTypeMap.ts b/invokeai/frontend/webv1/src/features/nodes/types/v1/fieldTypeMap.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/v1/fieldTypeMap.ts rename to invokeai/frontend/webv1/src/features/nodes/types/v1/fieldTypeMap.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/v1/workflowV1.ts b/invokeai/frontend/webv1/src/features/nodes/types/v1/workflowV1.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/v1/workflowV1.ts rename to invokeai/frontend/webv1/src/features/nodes/types/v1/workflowV1.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/v2/common.ts b/invokeai/frontend/webv1/src/features/nodes/types/v2/common.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/v2/common.ts rename to invokeai/frontend/webv1/src/features/nodes/types/v2/common.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/v2/field.ts b/invokeai/frontend/webv1/src/features/nodes/types/v2/field.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/v2/field.ts rename to invokeai/frontend/webv1/src/features/nodes/types/v2/field.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/v2/invocation.ts b/invokeai/frontend/webv1/src/features/nodes/types/v2/invocation.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/v2/invocation.ts rename to invokeai/frontend/webv1/src/features/nodes/types/v2/invocation.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/v2/semver.ts b/invokeai/frontend/webv1/src/features/nodes/types/v2/semver.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/v2/semver.ts rename to invokeai/frontend/webv1/src/features/nodes/types/v2/semver.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/v2/workflow.ts b/invokeai/frontend/webv1/src/features/nodes/types/v2/workflow.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/v2/workflow.ts rename to invokeai/frontend/webv1/src/features/nodes/types/v2/workflow.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/workflow.test-d.ts b/invokeai/frontend/webv1/src/features/nodes/types/workflow.test-d.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/types/workflow.test-d.ts rename to invokeai/frontend/webv1/src/features/nodes/types/workflow.test-d.ts diff --git a/invokeai/frontend/web/src/features/nodes/types/workflow.ts b/invokeai/frontend/webv1/src/features/nodes/types/workflow.ts similarity index 98% rename from invokeai/frontend/web/src/features/nodes/types/workflow.ts rename to invokeai/frontend/webv1/src/features/nodes/types/workflow.ts index bfb7b92b18f..0dac4044102 100644 --- a/invokeai/frontend/web/src/features/nodes/types/workflow.ts +++ b/invokeai/frontend/webv1/src/features/nodes/types/workflow.ts @@ -57,10 +57,13 @@ const zWorkflowEdgeDefault = zWorkflowEdgeBase.extend({ targetHandle: z.string().trim().min(1), hidden: z.boolean().optional(), }); +const zWorkflowEdgeLoopLinkage = zWorkflowEdgeDefault.extend({ + type: z.literal('loop_linkage'), +}); const zWorkflowEdgeCollapsed = zWorkflowEdgeBase.extend({ type: z.literal('collapsed'), }); -const zWorkflowEdge = z.union([zWorkflowEdgeDefault, zWorkflowEdgeCollapsed]); +const zWorkflowEdge = z.union([zWorkflowEdgeDefault, zWorkflowEdgeLoopLinkage, zWorkflowEdgeCollapsed]); // #endregion // #region Workflow Builder diff --git a/invokeai/frontend/web/src/features/nodes/util/constrainNumber.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/constrainNumber.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/constrainNumber.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/constrainNumber.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/constrainNumber.ts b/invokeai/frontend/webv1/src/features/nodes/util/constrainNumber.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/constrainNumber.ts rename to invokeai/frontend/webv1/src/features/nodes/util/constrainNumber.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocPostProcessingGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/buildAdHocPostProcessingGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocPostProcessingGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/buildAdHocPostProcessingGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildLinearBatchConfig.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/buildLinearBatchConfig.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildLinearBatchConfig.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/buildLinearBatchConfig.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/buildNodesGraph.test.ts similarity index 91% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/buildNodesGraph.test.ts index ea772a16bff..f8b71ab1939 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.test.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/graph/buildNodesGraph.test.ts @@ -376,4 +376,33 @@ describe('buildNodesGraph', () => { const graphNode = graph.nodes['img_resize-1'] as Record | undefined; expect(graphNode?.metadata).toBeUndefined(); }); + + // Loading a webv2 for-loop workflow now works, but the run graph drops the + // linkage: `buildNodesGraph` keeps only `type === 'default'` edges, and the + // backend then refuses the run with "must have exactly one loop linkage". + // The filter is edge-type based, so any two executable nodes demonstrate it. + it('keeps loop_linkage edges in the run graph', () => { + const source = buildNode(addTemplate); + const target = buildNode(addTemplate); + const linkage = { ...buildEdge(source.id, 'loop_linkage', target.id, 'loop_linkage'), type: 'loop_linkage' }; + const nextState = deepClone(nodesSliceConfig.getInitialState()); + + nextState.nodes = [source, target]; + nextState.edges = [linkage as (typeof nextState.edges)[number]]; + + const rootState = { + gallery: { autoAddBoardId: 'none' }, + nodes: { future: [], past: [], present: nextState }, + } as never; + + const graph = buildNodesGraph(rootState, templates); + + expect(graph.edges).toContainEqual( + expect.objectContaining({ + type: 'loop_linkage', + destination: { field: 'loop_linkage', node_id: target.id }, + source: { field: 'loop_linkage', node_id: source.id }, + }) + ); + }); }); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/buildNodesGraph.ts similarity index 98% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/buildNodesGraph.ts index 47e4d779c2d..315850fcf29 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/graph/buildNodesGraph.ts @@ -126,7 +126,7 @@ export const buildNodesGraph = (state: RootState, templates: Templates): Require // skip out the "dummy" edges between collapsed nodes const flattenedEdges = edges - .filter((edge) => edge.type === 'default') + .filter((edge) => edge.type === 'default' || edge.type === 'loop_linkage') .flatMap((edge) => { const targetNode = nodes.find((node) => node.id === edge.target); if (!targetNode || !isInvocationNode(targetNode) || !isExecutableNode(targetNode)) { @@ -185,6 +185,7 @@ export const buildNodesGraph = (state: RootState, templates: Templates): Require // Format the edges and add to the edges array edgesAccumulator.push({ + ...(edge.type === 'loop_linkage' ? { type: 'loop_linkage' as const } : {}), source: { node_id: source, field: sourceHandle, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/filters/buildPBRFilterGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/filters/buildPBRFilterGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/filters/buildPBRFilterGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/filters/buildPBRFilterGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/Graph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/Graph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/Graph.ts similarity index 98% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/Graph.ts index db94b946e8f..a80adcda64b 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/Graph.ts @@ -125,7 +125,9 @@ export class Graph { }); } - Object.assign(node, changes); + // Keep the runtime mutation generic without asking TypeScript to expand the full + // generated invocation union for Object.assign's inferred intersection type. + Object.assign(node as object, changes as object); return node; } diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addAnimaLoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addAnimaLoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addAnimaLoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addAnimaLoRAs.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addControlAdapters.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlAdapters.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addControlAdapters.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFLUXFill.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFLUXFill.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXLoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFLUXLoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXLoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFLUXLoRAs.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXRedux.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFLUXRedux.ts similarity index 97% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXRedux.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFLUXRedux.ts index 10ae0b66c99..a46d5c40d6d 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXRedux.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFLUXRedux.ts @@ -44,7 +44,7 @@ export const addFLUXReduxes = ({ entities, g, collector, model }: AddFLUXReduxAr * - downsampling_function: the function used to downsample the image. Defaults to 'area'. Dunno about how it affects the image. * - weight: 0 to 1. the conditioning is multiplied by the square of this value. 1 means no change. * - * See invokeai/app/invocations/flux_redux.py for more details. + * See invokeai/app/invocations/flux/flux_redux.py for more details. */ export const IMAGE_INFLUENCE_TO_SETTINGS: Record< FLUXReduxImageInfluence, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFlux2DevLoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFlux2DevLoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addFlux2DevLoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFlux2DevLoRAs.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFlux2KleinLoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFlux2KleinLoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addFlux2KleinLoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addFlux2KleinLoRAs.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addIPAdapters.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addIPAdapters.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addIPAdapters.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addIPAdapters.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addImageToImage.ts similarity index 95% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addImageToImage.ts index dc91405d1a2..90d406f39ea 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addImageToImage.ts @@ -26,7 +26,8 @@ type AddImageToImageArg = { l2i: Invocation; i2l: Invocation; noise?: Invocation<'noise'>; - denoise: Invocation; + // MiniMax H3's denoise node has no denoising_start/end, so it cannot do img2img. + denoise: Invocation>; vaeSource: Invocation; }; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addInpaint.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addInpaint.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addKrea2LoRAs.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addKrea2LoRAs.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addKrea2LoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addKrea2LoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addKrea2LoRAs.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addLoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addLoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addLoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addLoRAs.ts diff --git a/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addMiniMaxH3LoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addMiniMaxH3LoRAs.ts new file mode 100644 index 00000000000..b26e26a7ed4 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addMiniMaxH3LoRAs.ts @@ -0,0 +1,65 @@ +import type { RootState } from 'app/store/store'; +import { getPrefixedId } from 'features/controlLayers/konva/util'; +import { zModelIdentifierField } from 'features/nodes/types/common'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import type { Invocation, S } from 'services/api/types'; + +/** + * Add MiniMax H3 LoRA wiring to the graph between the model loader and the + * denoise node. + * + * Each enabled H3 LoRA becomes a ``lora_selector`` feeding a ``collect`` + * node, which fans into a ``minimax_h3_lora_collection_loader`` that rewrites + * the model loader's transformer output with the ``loras`` list populated. + * There is no variant/expert routing (H3 has a single fl2va transformer); + * the backend loader re-validates every LoRA's base server-side. + */ +export const addMiniMaxH3LoRAs = ( + state: RootState, + g: Graph, + denoise: Invocation<'minimax_h3_denoise'>, + modelLoader: Invocation<'minimax_h3_model_loader'> +): void => { + const enabledLoRAs = state.loras.loras.filter((l) => l.isEnabled && l.model.base === 'minimax-h3'); + + if (enabledLoRAs.length === 0) { + return; + } + + const loraMetadata: S['LoRAMetadataField'][] = []; + + const loraCollector = g.addNode({ + id: getPrefixedId('lora_collector'), + type: 'collect', + }); + const loraCollectionLoader = g.addNode({ + type: 'minimax_h3_lora_collection_loader', + id: getPrefixedId('minimax_h3_lora_collection_loader'), + }); + + g.addEdge(loraCollector, 'collection', loraCollectionLoader, 'loras'); + g.addEdge(modelLoader, 'transformer', loraCollectionLoader, 'transformer'); + g.deleteEdgesTo(denoise, ['transformer']); + g.addEdge(loraCollectionLoader, 'transformer', denoise, 'transformer'); + + for (const lora of enabledLoRAs) { + const { weight } = lora; + const parsedModel = zModelIdentifierField.parse(lora.model); + + const loraSelector = g.addNode({ + type: 'lora_selector', + id: getPrefixedId('lora_selector'), + lora: parsedModel, + weight, + }); + + loraMetadata.push({ + model: parsedModel, + weight, + }); + + g.addEdge(loraSelector, 'lora', loraCollector, 'item'); + } + + g.upsertMetadata({ loras: loraMetadata }); +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addNSFWChecker.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addNSFWChecker.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addNSFWChecker.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addNSFWChecker.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addOutpaint.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addOutpaint.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addPidDecode.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addPidDecode.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addPidDecode.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addPidDecode.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addQwenImageLoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addQwenImageLoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addQwenImageLoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addQwenImageLoRAs.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addRegions.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addRegions.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addRegions.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addRegions.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addRegions.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addRegions.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addRegions.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addRegions.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLRefiner.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addSDXLRefiner.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLRefiner.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addSDXLRefiner.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSeamless.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addSeamless.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addSeamless.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addSeamless.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addTextToImage.ts similarity index 89% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addTextToImage.ts index a948fd67621..de29d17333d 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addTextToImage.ts @@ -33,13 +33,18 @@ export const addTextToImage = ({ | 'ernie_image_vae_decode' | 'anima_l2i' | 'wan_l2i' + | 'minimax_h3_latents_to_image' > => { - denoise.denoising_start = 0; - denoise.denoising_end = 1; + if (denoise.type !== 'minimax_h3_denoise') { + // MiniMax H3's denoise node has no denoising_start/end fields (txt2img/t2v only). + denoise.denoising_start = 0; + denoise.denoising_end = 1; + } const { originalSize, scaledSize } = getOriginalAndScaledSizesForTextToImage(state); if ( + denoise.type === 'minimax_h3_denoise' || denoise.type === 'cogview4_denoise' || denoise.type === 'qwen_image_denoise' || denoise.type === 'flux_denoise' || diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addWanLoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addWanLoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addWanLoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addWanLoRAs.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addWatermarker.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addWatermarker.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addWatermarker.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addWatermarker.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addZImageLoRAs.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addZImageLoRAs.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/addZImageLoRAs.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/addZImageLoRAs.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildAnimaGraph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildAnimaGraph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildAnimaGraph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildAnimaGraph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildAnimaGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildAnimaGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildAnimaGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildAnimaGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildCogView4Graph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildCogView4Graph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildCogView4Graph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildCogView4Graph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildErnieImageGraph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildErnieImageGraph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildErnieImageGraph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildErnieImageGraph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildErnieImageGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildErnieImageGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildErnieImageGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildErnieImageGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildExternalGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildExternalGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildFLUXGraph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildFLUXGraph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildFLUXGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildFLUXGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildHiDiffusionGraph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildHiDiffusionGraph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildHiDiffusionGraph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildHiDiffusionGraph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildIdeogram4Graph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildIdeogram4Graph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildIdeogram4Graph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Prompt.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildIdeogram4Prompt.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Prompt.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildIdeogram4Prompt.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Prompt.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildIdeogram4Prompt.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildIdeogram4Prompt.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildIdeogram4Prompt.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.regions.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildKrea2Graph.regions.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.regions.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildKrea2Graph.regions.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildKrea2Graph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildKrea2Graph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildKrea2Graph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildKrea2Graph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildKrea2Graph.ts diff --git a/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildMiniMaxH3Graph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildMiniMaxH3Graph.ts new file mode 100644 index 00000000000..6b4dfab3a37 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildMiniMaxH3Graph.ts @@ -0,0 +1,265 @@ +import { logger } from 'app/logging/logger'; +import { getPrefixedId } from 'features/controlLayers/konva/util'; +import { selectMainModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; +import { selectRefImagesSlice } from 'features/controlLayers/store/refImagesSlice'; +import { selectCanvasMetadata } from 'features/controlLayers/store/selectors'; +import { isMiniMaxH3ReferenceImageConfig } from 'features/controlLayers/store/types'; +import { getGlobalReferenceImageWarnings } from 'features/controlLayers/store/validators'; +import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; +import { zImageField } from 'features/nodes/types/common'; +import { addMiniMaxH3LoRAs } from 'features/nodes/util/graph/generation/addMiniMaxH3LoRAs'; +import { addNSFWChecker } from 'features/nodes/util/graph/generation/addNSFWChecker'; +import { addTextToImage } from 'features/nodes/util/graph/generation/addTextToImage'; +import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker'; +import { Graph } from 'features/nodes/util/graph/generation/Graph'; +import { + getOriginalAndScaledSizesForTextToImage, + selectCanvasOutputFields, +} from 'features/nodes/util/graph/graphBuilderUtils'; +import type { GraphBuilderArg, GraphBuilderReturn, ImageOutputNodes } from 'features/nodes/util/graph/types'; +import { UnsupportedGenerationModeError } from 'features/nodes/util/graph/types'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; +import type { Invocation } from 'services/api/types'; +import { isNonRefinerMainModelConfig } from 'services/api/types'; +import { assert } from 'tsafe'; + +const log = logger('system'); + +/** MiniMax H3 generates at a fixed 24 fps. */ +const MINIMAX_H3_FPS = 24; +/** Legal frame counts are 17n+5. 124 frames is the 5s+ video minimum; 345 (14.375s) is the + * largest grid point within the model's 15s ceiling. The 5-frame minimum block is reserved + * for the still-image output mode. */ +const MINIMAX_H3_MIN_VIDEO_FRAMES = 124; +const MINIMAX_H3_MAX_VIDEO_FRAMES = 345; +const MINIMAX_H3_IMAGE_FRAMES = '5' as const; + +/** + * Snap a duration in seconds to the nearest legal MiniMax H3 frame count (17n+5), clamped to + * the video range [124, 345]. + */ +const snapMiniMaxH3DurationToFrames = (durationSeconds: number): number => { + // The top slider stop (14 s) maps to the model's true ceiling (345 frames = 14.375 s): + // nearest-grid rounding alone would top out at 328 and leave the last 0.7 s unreachable. + if (durationSeconds >= 14) { + return MINIMAX_H3_MAX_VIDEO_FRAMES; + } + const targetFrames = durationSeconds * MINIMAX_H3_FPS; + const n = Math.round((targetFrames - 5) / 17); + const frames = n * 17 + 5; + return Math.min(Math.max(frames, MINIMAX_H3_MIN_VIDEO_FRAMES), MINIMAX_H3_MAX_VIDEO_FRAMES); +}; + +/** + * Build a graph for MiniMax H3 (Hailuo 3.0) generation. + * + * H3 is a joint audio-video model; the linear UI exposes two output modes: + * - 'video': text-to-video / first-frame-to-video with a muxed stereo soundtrack. Generate + * tab only. + * - 'image': a minimum-length (5-frame) clip decoded to a single gallery image. This is the + * text-to-image mode and also works as the canvas txt2img path. + * + * The checkpoint is guidance-distilled: no negative prompt, no CFG - there is exactly one + * prompt node. When a first-frame reference image is wired, the SAME image and canvas + * dimensions MUST reach both the text encoder (vision context) and the frame-conditioning + * node - the backend denoise node enforces this coupling. + */ +export const buildMiniMaxH3Graph = async (arg: GraphBuilderArg): Promise => { + const { generationMode, state, manager } = arg; + + log.debug({ generationMode, manager: manager?.id }, 'Building MiniMax H3 graph'); + + const model = selectMainModelConfig(state); + assert(model, 'No model selected'); + assert(model.base === 'minimax-h3', 'Selected model is not a MiniMax H3 model'); + const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig); + assert(modelConfig.base === 'minimax-h3'); + + const params = selectParamsSlice(state); + const { minimaxH3OutputMode } = params; + // The H3 denoise node requires steps >= 2 (N sigma grid points = N-1 model evaluations); + // the shared Steps slider allows 1, so clamp rather than 422 at enqueue. + const steps = Math.max(2, params.steps); + + if (generationMode !== 'txt2img') { + throw new UnsupportedGenerationModeError( + 'MiniMax H3 supports text-to-video, first-frame-to-video and text-to-image only. ' + + 'Canvas img2img / inpaint / outpaint are not supported.' + ); + } + + const g = new Graph(getPrefixedId('minimax_h3_graph')); + + // Optional single-file overrides (e.g. pruned int8 transformer, truncated int8 text + // encoder): each replaces its folder counterpart; everything else still comes from the + // folder install. + const transformerModel = params.minimaxH3TransformerModel; + const textEncoderModel = params.minimaxH3TextEncoderModel; + const modelLoader = g.addNode({ + type: 'minimax_h3_model_loader', + id: getPrefixedId('minimax_h3_model_loader'), + model, + transformer_model: transformerModel ?? undefined, + text_encoder_model: textEncoderModel ?? undefined, + }); + + const positivePrompt = g.addNode({ + id: getPrefixedId('positive_prompt'), + type: 'string', + }); + // Guidance-distilled: no negative prompt, no CFG, one text encoder. + const posCond = g.addNode({ + type: 'minimax_h3_text_encoder', + id: getPrefixedId('pos_prompt'), + }); + + const seed = g.addNode({ + id: getPrefixedId('seed'), + type: 'integer', + }); + + const denoise = g.addNode({ + type: 'minimax_h3_denoise', + id: getPrefixedId('denoise_latents'), + steps, + }); + + g.addEdge(modelLoader, 'transformer', denoise, 'transformer'); + g.addEdge(modelLoader, 'text_encoder', posCond, 'text_encoder'); + g.addEdge(positivePrompt, 'value', posCond, 'prompt'); + g.addEdge(posCond, 'conditioning', denoise, 'positive_conditioning'); + g.addEdge(seed, 'value', denoise, 'seed'); + + // H3 LoRAs (e.g. the Turbo step-distillation LoRA) rewire the transformer edge through a + // collection loader. Works with both the folder transformer and the single-file overrides. + addMiniMaxH3LoRAs(state, g, denoise, modelLoader); + + g.upsertMetadata({ + model: Graph.getModelMetadataField(modelConfig), + steps, + minimax_h3_output_mode: minimaxH3OutputMode, + }); + if (transformerModel) { + g.upsertMetadata({ minimax_h3_transformer_model: transformerModel }); + } + if (textEncoderModel) { + g.upsertMetadata({ minimax_h3_text_encoder_model: textEncoderModel }); + } + g.addEdgeToMetadata(seed, 'value', 'seed'); + g.addEdgeToMetadata(positivePrompt, 'value', 'positive_prompt'); + + // First-frame conditioning (video mode only): the first enabled MiniMax H3 reference image + // becomes the video's first frame. The image feeds BOTH the text encoder (vision context) + // and the frame-conditioning node (VAE condition rows) - the backend requires the pair. + const refEntity = + minimaxH3OutputMode === 'video' + ? selectRefImagesSlice(state).entities.find( + (entity) => + entity.isEnabled && + isMiniMaxH3ReferenceImageConfig(entity.config) && + entity.config.image !== null && + getGlobalReferenceImageWarnings(entity, modelConfig).length === 0 + ) + : undefined; + + if (minimaxH3OutputMode === 'video') { + if (selectActiveTab(state) !== 'generate') { + throw new UnsupportedGenerationModeError('MiniMax H3 video generation runs on the Generate tab.'); + } + + const { originalSize } = getOriginalAndScaledSizesForTextToImage(state); + const num_frames = snapMiniMaxH3DurationToFrames(params.minimaxH3DurationSeconds); + + g.updateNode(denoise, { + width: originalSize.width, + height: originalSize.height, + // The node takes the frame count as a grid-aligned choice value, not a free integer. + num_frames: `${num_frames}` as Invocation<'minimax_h3_denoise'>['num_frames'], + }); + // The keyframe vision context is prepared on the same canvas as the condition rows. + g.updateNode(posCond, { + width: originalSize.width, + height: originalSize.height, + }); + + const l2v = g.addNode({ + type: 'minimax_h3_latents_to_video', + id: getPrefixedId('l2v'), + }); + g.addEdge(modelLoader, 'vae', l2v, 'vae'); + g.addEdge(modelLoader, 'audio_vae', l2v, 'audio_vae'); + g.addEdge(denoise, 'video_latents', l2v, 'video_latents'); + g.addEdge(denoise, 'audio_latents', l2v, 'audio_latents'); + + if (refEntity) { + assert(isMiniMaxH3ReferenceImageConfig(refEntity.config) && refEntity.config.image); + const refImageField = zImageField.parse( + refEntity.config.image.crop?.image ?? refEntity.config.image.original.image + ); + const frameCond = g.addNode({ + type: 'minimax_h3_frame_conditioning', + id: getPrefixedId('minimax_h3_frame_conditioning'), + first_image: refImageField, + width: originalSize.width, + height: originalSize.height, + }); + g.addEdge(modelLoader, 'vae', frameCond, 'vae'); + g.addEdge(frameCond, 'frame_conditioning', denoise, 'frame_conditioning'); + g.updateNode(posCond, { first_image: refImageField }); + g.upsertMetadata({ generation_mode: 'minimax_h3_i2v' }); + } else { + g.upsertMetadata({ generation_mode: 'minimax_h3_t2v' }); + } + + g.upsertMetadata({ + width: originalSize.width, + height: originalSize.height, + minimax_h3_duration_seconds: params.minimaxH3DurationSeconds, + }); + + g.updateNode(l2v, selectCanvasOutputFields(state)); + g.setMetadataReceivingNode(l2v); + + return { + g, + seed, + positivePrompt, + }; + } + + // Image output mode: a 5-frame (minimum block) clip decoded to a single image. Works on + // both the Generate tab and canvas txt2img. + g.updateNode(denoise, { num_frames: MINIMAX_H3_IMAGE_FRAMES }); + + const l2i = g.addNode({ + type: 'minimax_h3_latents_to_image', + id: getPrefixedId('l2i'), + frame_index: 0, + }); + g.addEdge(modelLoader, 'vae', l2i, 'vae'); + g.addEdge(denoise, 'video_latents', l2i, 'video_latents'); + + let canvasOutput: Invocation = addTextToImage({ g, state, denoise, l2i }); + g.upsertMetadata({ generation_mode: 'minimax_h3_txt2img' }); + + if (state.system.shouldUseNSFWChecker) { + canvasOutput = addNSFWChecker(g, canvasOutput); + } + if (state.system.shouldUseWatermarker) { + canvasOutput = addWatermarker(g, canvasOutput); + } + + g.updateNode(canvasOutput, selectCanvasOutputFields(state)); + + if (selectActiveTab(state) === 'canvas') { + g.upsertMetadata(selectCanvasMetadata(state)); + } + + g.setMetadataReceivingNode(canvasOutput); + + return { + g, + seed, + positivePrompt, + }; +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildQwenImageGraph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildQwenImageGraph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildQwenImageGraph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildQwenImageGraph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildQwenImageGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildQwenImageGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildQwenImageGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildQwenImageGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildSD1Graph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildSD1Graph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD3Graph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildSD3Graph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD3Graph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildSD3Graph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDNegativePromptGraph.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildSDNegativePromptGraph.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDNegativePromptGraph.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildSDNegativePromptGraph.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildSDXLGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildSDXLGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildWanGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildWanGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildWanGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildWanGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildZImageGraph.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildZImageGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/buildZImageGraph.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/buildZImageGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/randDeviceMetadata.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/generation/randDeviceMetadata.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/generation/randDeviceMetadata.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/generation/randDeviceMetadata.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/graphBuilderUtils.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts rename to invokeai/frontend/webv1/src/features/nodes/util/graph/graphBuilderUtils.ts diff --git a/invokeai/frontend/webv1/src/features/nodes/util/graph/types.ts b/invokeai/frontend/webv1/src/features/nodes/util/graph/types.ts new file mode 100644 index 00000000000..a3cfb9b19f1 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/util/graph/types.ts @@ -0,0 +1,113 @@ +import type { RootState } from 'app/store/store'; +import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; +import type { GenerationMode } from 'features/controlLayers/store/types'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import type { Invocation } from 'services/api/types'; + +export type ImageOutputNodes = + | 'l2i' + | 'img_nsfw' + | 'img_watermark' + | 'img_resize' + | 'invokeai_img_blend' + | 'apply_mask_to_image' + | 'flux_pid_decode' + | 'flux2_pid_decode' + | 'sd3_pid_decode' + | 'sdxl_pid_decode' + | 'z_image_pid_decode' + | 'qwen_image_pid_decode' + | 'flux_vae_decode' + | 'flux2_vae_decode' + | 'sd3_l2i' + | 'cogview4_l2i' + | 'qwen_image_l2i' + | 'z_image_l2i' + | 'ernie_image_vae_decode' + | 'ideogram4_l2i' + | 'anima_l2i' + | 'wan_l2i' + | 'minimax_h3_latents_to_image'; + +export type LatentToImageNodes = + | 'l2i' + | 'flux_vae_decode' + | 'flux2_vae_decode' + | 'sd3_l2i' + | 'cogview4_l2i' + | 'qwen_image_l2i' + | 'z_image_l2i' + | 'ernie_image_vae_decode' + | 'anima_l2i' + | 'wan_l2i' + | 'minimax_h3_latents_to_image'; + +export type ImageToLatentsNodes = + | 'i2l' + | 'flux_vae_encode' + | 'flux2_vae_encode' + | 'sd3_i2l' + | 'cogview4_i2l' + | 'qwen_image_i2l' + | 'z_image_i2l' + | 'anima_i2l' + | 'wan_i2l'; + +export type DenoiseLatentsNodes = + | 'denoise_latents' + | 'flux_denoise' + | 'flux2_denoise' + | 'sd3_denoise' + | 'cogview4_denoise' + | 'qwen_image_denoise' + | 'z_image_denoise' + | 'ernie_image_denoise' + | 'krea2_denoise' + | 'anima_denoise' + | 'wan_denoise' + | 'minimax_h3_denoise'; + +/** + * Denoise nodes that support masked denoising (inpaint/outpaint). ERNIE-Image's denoise node + * has no `denoise_mask` input (it is text-to-image only), and MiniMax H3's denoise node has + * neither `denoise_mask` nor `denoising_start/end` (txt2img/t2v only), so they are excluded. + */ +export type MaskableDenoiseNodes = Exclude; + +export type MainModelLoaderNodes = + | 'main_model_loader' + | 'sdxl_model_loader' + | 'flux_model_loader' + | 'flux2_klein_model_loader' + | 'flux2_dev_model_loader' + | 'sd3_model_loader' + | 'cogview4_model_loader' + | 'qwen_image_model_loader' + | 'z_image_model_loader' + | 'ernie_image_model_loader' + | 'krea2_model_loader' + | 'anima_model_loader' + | 'wan_model_loader' + | 'minimax_h3_model_loader'; + +export type VaeSourceNodes = 'seamless' | 'vae_loader'; + +export type GraphBuilderArg = { + generationMode: GenerationMode; + state: RootState; + manager: CanvasManager | null; +}; + +export type GraphBuilderReturn = { + g: Graph; + seed?: Invocation<'integer'>; + positivePrompt: Invocation<'string'>; + negativePrompt?: Invocation<'string'>; +}; + +export class UnsupportedGenerationModeError extends Error { + constructor(message: string) { + super(message); + this.name = this.constructor.name; + } +} diff --git a/invokeai/frontend/web/src/features/nodes/util/node/buildConnectorNode.ts b/invokeai/frontend/webv1/src/features/nodes/util/node/buildConnectorNode.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/node/buildConnectorNode.ts rename to invokeai/frontend/webv1/src/features/nodes/util/node/buildConnectorNode.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/node/buildCurrentImageNode.ts b/invokeai/frontend/webv1/src/features/nodes/util/node/buildCurrentImageNode.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/node/buildCurrentImageNode.ts rename to invokeai/frontend/webv1/src/features/nodes/util/node/buildCurrentImageNode.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts b/invokeai/frontend/webv1/src/features/nodes/util/node/buildInvocationNode.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts rename to invokeai/frontend/webv1/src/features/nodes/util/node/buildInvocationNode.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/node/buildNotesNode.ts b/invokeai/frontend/webv1/src/features/nodes/util/node/buildNotesNode.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/node/buildNotesNode.ts rename to invokeai/frontend/webv1/src/features/nodes/util/node/buildNotesNode.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/node/getSortedFilteredFieldNames.ts b/invokeai/frontend/webv1/src/features/nodes/util/node/getSortedFilteredFieldNames.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/node/getSortedFilteredFieldNames.ts rename to invokeai/frontend/webv1/src/features/nodes/util/node/getSortedFilteredFieldNames.ts diff --git a/invokeai/frontend/webv1/src/features/nodes/util/node/nodeUpdate.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/node/nodeUpdate.test.ts new file mode 100644 index 00000000000..46157a8a30e --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/util/node/nodeUpdate.test.ts @@ -0,0 +1,345 @@ +import type { InvocationTemplate } from 'features/nodes/types/invocation'; +import { buildInvocationNode } from 'features/nodes/util/node/buildInvocationNode'; +import { getConnectedInputNames, updateNode } from 'features/nodes/util/node/nodeUpdate'; +import { describe, expect, it } from 'vitest'; + +const imageCollectionOutput = { + collection: { + fieldKind: 'output', + name: 'collection', + title: 'Collection', + description: 'The output images', + type: { + name: 'ImageField', + cardinality: 'COLLECTION', + batch: false, + }, + ui_hidden: false, + }, +} satisfies InvocationTemplate['outputs']; + +const oldImageCollectionTemplate = { + title: 'Image Collection Primitive', + type: 'image_collection', + version: '1.0.1', + tags: ['primitives', 'image', 'collection'], + description: 'A collection of image primitive values', + outputType: 'image_collection_output', + inputs: { + collection: { + name: 'collection', + title: 'Collection', + required: false, + description: 'The collection of image values', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { + name: 'ImageField', + cardinality: 'COLLECTION', + batch: false, + }, + default: undefined, + }, + }, + outputs: imageCollectionOutput, + useCache: true, + nodePack: 'invokeai', + classification: 'stable', + category: 'primitives', +} satisfies InvocationTemplate; + +const oldestImageCollectionTemplate = { + ...oldImageCollectionTemplate, + version: '1.0.0', +} satisfies InvocationTemplate; + +const currentImageCollectionTemplate = { + ...oldImageCollectionTemplate, + version: '1.0.2', + inputs: { + collection: { + name: 'collection', + title: 'Collection', + required: false, + description: 'An optional image collection to append to', + fieldKind: 'input', + input: 'connection', + ui_hidden: false, + type: { + name: 'ImageField', + cardinality: 'COLLECTION', + batch: false, + }, + default: undefined, + }, + images: { + name: 'images', + title: 'Images', + required: false, + description: 'The images to append to the collection', + fieldKind: 'input', + input: 'direct', + ui_hidden: false, + type: { + name: 'ImageField', + cardinality: 'COLLECTION', + batch: false, + }, + default: undefined, + }, + }, +} satisfies InvocationTemplate; + +const textLLMInputs = { + prompt: { + name: 'prompt', + title: 'Prompt', + required: false, + description: 'Input text prompt.', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { name: 'StringField', cardinality: 'SINGLE', batch: false }, + default: '', + }, + system_prompt: { + name: 'system_prompt', + title: 'System Prompt', + required: false, + description: 'System prompt that guides the model behavior.', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { name: 'StringField', cardinality: 'SINGLE', batch: false }, + default: '', + }, + text_llm_model: { + name: 'text_llm_model', + title: 'Text LLM Model', + required: true, + description: 'The text language model to use for text generation.', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { name: 'ModelIdentifierField', cardinality: 'SINGLE', batch: false }, + default: undefined, + }, + max_tokens: { + name: 'max_tokens', + title: 'Max Tokens', + required: false, + description: 'Maximum number of tokens to generate.', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { name: 'IntegerField', cardinality: 'SINGLE', batch: false }, + default: 300, + }, +} satisfies InvocationTemplate['inputs']; + +const textLLMOutput = { + value: { + fieldKind: 'output', + name: 'value', + title: 'Value', + description: 'Generated text', + type: { name: 'StringField', cardinality: 'SINGLE', batch: false }, + ui_hidden: false, + }, +} satisfies InvocationTemplate['outputs']; + +const oldTextLLMTemplate = { + title: 'Text LLM', + type: 'text_llm', + version: '1.0.0', + tags: ['llm', 'text'], + description: 'Run a text language model.', + outputType: 'string_output', + inputs: textLLMInputs, + outputs: textLLMOutput, + useCache: true, + nodePack: 'invokeai', + classification: 'beta', + category: 'llm', +} satisfies InvocationTemplate; + +const currentTextLLMTemplate = { + ...oldTextLLMTemplate, + version: '1.1.0', + inputs: { + ...textLLMInputs, + seed: { + name: 'seed', + title: 'Seed', + required: false, + description: 'Seed for random number generation.', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { name: 'IntegerField', cardinality: 'SINGLE', batch: false }, + default: 0, + }, + }, +} satisfies InvocationTemplate; + +const oldMiniMaxH3DenoiseTemplate = { + title: 'Denoise - MiniMax H3', + type: 'minimax_h3_denoise', + version: '1.2.0', + tags: ['latents', 'video', 'audio', 'minimax'], + description: 'Run the MiniMax H3 joint audio-video denoising loop.', + outputType: 'minimax_h3_denoise_output', + inputs: { + num_frames: { + name: 'num_frames', + title: 'Number of Frames', + required: false, + description: 'Number of output frames at the fixed 24 fps.', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { name: 'IntegerField', cardinality: 'SINGLE', batch: false }, + default: 124, + }, + }, + outputs: {}, + useCache: true, + nodePack: 'invokeai', + classification: 'prototype', + category: 'latents', +} satisfies InvocationTemplate; + +const currentMiniMaxH3DenoiseTemplate = { + ...oldMiniMaxH3DenoiseTemplate, + version: '1.3.0', + inputs: { + num_frames: { + name: 'num_frames', + title: 'Number of Frames', + required: false, + description: 'Only the video VAE 17n+5 grid points are offered.', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { name: 'EnumField', cardinality: 'SINGLE', batch: false }, + default: '124', + options: ['5', '90', '107', '124', '141', '345'], + ui_choice_labels: { '124': '124 frames - 5.17 s' }, + }, + }, +} satisfies InvocationTemplate; + +describe('updateNode', () => { + it('treats loop linkage edges as connected inputs during migration', () => { + expect( + getConnectedInputNames('for-return', [ + { target: 'for-return', targetHandle: 'loop_linkage', type: 'loop_linkage' }, + ]) + ).toEqual(new Set(['loop_linkage'])); + }); + it('adds the seeded default when updating a stored text_llm node', () => { + const node = buildInvocationNode({ x: 0, y: 0 }, oldTextLLMTemplate); + node.data.inputs.prompt!.value = 'a cat'; + + const updated = updateNode(node, currentTextLLMTemplate, { connectedInputNames: new Set() }); + + expect(updated.data.version).toBe('1.1.0'); + expect(updated.data.inputs.prompt?.value).toBe('a cat'); + expect(updated.data.inputs.seed?.value).toBe(0); + }); + + it('moves old image_collection direct collection values to the new images field', () => { + const node = buildInvocationNode({ x: 0, y: 0 }, oldImageCollectionTemplate); + const images = [{ image_name: 'first' }, { image_name: 'second' }]; + const collectionInput = node.data.inputs.collection; + if (!collectionInput) { + throw new Error('Expected collection input'); + } + collectionInput.value = images; + + const updated = updateNode(node, currentImageCollectionTemplate, { connectedInputNames: new Set() }); + + expect(updated.data.version).toBe('1.0.2'); + expect(updated.data.inputs.images?.value).toEqual(images); + expect(updated.data.inputs.collection?.value).toEqual([]); + }); + + it('moves 1.0.0 image_collection direct collection values to the new images field', () => { + const node = buildInvocationNode({ x: 0, y: 0 }, oldestImageCollectionTemplate); + const images = [{ image_name: 'first' }]; + const collectionInput = node.data.inputs.collection; + if (!collectionInput) { + throw new Error('Expected collection input'); + } + collectionInput.value = images; + + const updated = updateNode(node, currentImageCollectionTemplate, { connectedInputNames: new Set() }); + + expect(updated.data.version).toBe('1.0.2'); + expect(updated.data.inputs.images?.value).toEqual(images); + expect(updated.data.inputs.collection?.value).toEqual([]); + }); + + it('preserves old image_collection direct collection values when collection is connected', () => { + const node = buildInvocationNode({ x: 0, y: 0 }, oldImageCollectionTemplate); + const images = [{ image_name: 'stale' }]; + const collectionInput = node.data.inputs.collection; + if (!collectionInput) { + throw new Error('Expected collection input'); + } + collectionInput.value = images; + + const updated = updateNode(node, currentImageCollectionTemplate, { + connectedInputNames: new Set(['collection']), + }); + + expect(updated.data.inputs.images?.value).toBeUndefined(); + expect(updated.data.inputs.collection?.value).toEqual(images); + }); + + it('converts a stored minimax_h3_denoise frame count to the matching choice value', () => { + const node = buildInvocationNode({ x: 0, y: 0 }, oldMiniMaxH3DenoiseTemplate); + const numFrames = node.data.inputs.num_frames; + if (!numFrames) { + throw new Error('Expected num_frames input'); + } + numFrames.value = 141; + + const updated = updateNode(node, currentMiniMaxH3DenoiseTemplate, { connectedInputNames: new Set() }); + + expect(updated.data.version).toBe('1.3.0'); + // Without the migration the stale number survives the merge and the field renders as nothing. + expect(updated.data.inputs.num_frames?.value).toBe('141'); + }); + + it('falls back to the default when a stored frame count is no longer offered', () => { + const node = buildInvocationNode({ x: 0, y: 0 }, oldMiniMaxH3DenoiseTemplate); + const numFrames = node.data.inputs.num_frames; + if (!numFrames) { + throw new Error('Expected num_frames input'); + } + numFrames.value = 22; + + const updated = updateNode(node, currentMiniMaxH3DenoiseTemplate, { connectedInputNames: new Set() }); + + expect(updated.data.inputs.num_frames?.value).toBe('124'); + }); + + it('leaves an already-migrated choice value alone', () => { + const node = buildInvocationNode({ x: 0, y: 0 }, currentMiniMaxH3DenoiseTemplate); + const numFrames = node.data.inputs.num_frames; + if (!numFrames) { + throw new Error('Expected num_frames input'); + } + numFrames.value = '345'; + + const updated = updateNode( + node, + { ...currentMiniMaxH3DenoiseTemplate, version: '1.4.0' }, + { connectedInputNames: new Set() } + ); + + expect(updated.data.inputs.num_frames?.value).toBe('345'); + }); +}); diff --git a/invokeai/frontend/webv1/src/features/nodes/util/node/nodeUpdate.ts b/invokeai/frontend/webv1/src/features/nodes/util/node/nodeUpdate.ts new file mode 100644 index 00000000000..3519b4f64c6 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/util/node/nodeUpdate.ts @@ -0,0 +1,166 @@ +import { deepClone } from 'common/util/deepClone'; +import { compare, satisfies } from 'compare-versions'; +import { defaultsDeep, keys, pick } from 'es-toolkit/compat'; +import { NodeUpdateError } from 'features/nodes/types/error'; +import { nodeAcceptsExtraInputs } from 'features/nodes/types/extraInputs'; +import { isEnumFieldInputTemplate } from 'features/nodes/types/field'; +import type { InvocationNode, InvocationNodeData, InvocationTemplate } from 'features/nodes/types/invocation'; +import { zParsedSemver } from 'features/nodes/types/semver'; + +import { buildInvocationNode } from './buildInvocationNode'; + +type ConnectedInputEdge = { type?: string; target: string; targetHandle?: string | null }; + +type UpdateNodeOptions = { + connectedInputNames: Set; +}; + +export const getConnectedInputNames = (nodeId: string, edges: ConnectedInputEdge[]): Set => + new Set( + edges.flatMap((edge) => + (edge.type === 'default' || edge.type === 'loop_linkage') && edge.target === nodeId && edge.targetHandle + ? [edge.targetHandle] + : [] + ) + ); + +export const getUpdatedFieldName = (node: InvocationNode, fieldName: string): string => { + if (node.data.type === 'image_collection' && fieldName === 'collection' && node.data.inputs.images) { + return 'images'; + } + return fieldName; +}; + +export const getNeedsUpdate = (data: InvocationNodeData, template: InvocationTemplate): boolean => { + if (data.type !== template.type) { + return true; + } + return data.version !== template.version; +}; + +/** + * Checks if a node may be updated by comparing its major version with the template's major version. + * @param node The node to check. + * @param template The invocation template to check against. + */ +const getMayUpdateNode = (node: InvocationNode, template: InvocationTemplate): boolean => { + const needsUpdate = getNeedsUpdate(node.data, template); + if (!needsUpdate || node.data.type !== template.type) { + return false; + } + const templateMajor = zParsedSemver.parse(template.version).major; + + return satisfies(node.data.version, `^${templateMajor}`); +}; + +export const migrateImageCollectionInputValues = ( + node: InvocationNode, + options: UpdateNodeOptions & { sourceVersion?: string } +) => { + if (node.data.type !== 'image_collection') { + return; + } + if (options.sourceVersion && compare(options.sourceVersion, '1.0.2', '>=')) { + return; + } + + const collection = node.data.inputs.collection; + const images = node.data.inputs.images; + if (!collection || !images || !Array.isArray(collection.value)) { + return; + } + if (Array.isArray(images.value) && images.value.length > 0) { + return; + } + + if (options.connectedInputNames.has('collection')) { + return; + } + + images.value = collection.value; + collection.value = []; +}; + +/** + * `minimax_h3_denoise.num_frames` became a grid-aligned choice list in 1.3.0, having been a free + * integer. The recursive merge below preserves the stored number, which no longer validates + * against the enum instance schema - the field then renders as nothing at all, leaving the user + * no way to fix it from the editor. Convert a stored count that is still on the offered grid, and + * fall back to the template's default for one that is not. + */ +export const migrateMiniMaxH3NumFramesValue = ( + node: InvocationNode, + template: InvocationTemplate, + sourceVersion?: string +) => { + if (node.data.type !== 'minimax_h3_denoise') { + return; + } + if (sourceVersion && compare(sourceVersion, '1.3.0', '>=')) { + return; + } + + const field = node.data.inputs.num_frames; + const fieldTemplate = template.inputs.num_frames; + if (!field || !fieldTemplate || !isEnumFieldInputTemplate(fieldTemplate)) { + return; + } + + // Annotated `unknown` so the typeof check below narrows the local, not `field.value` itself - + // the assignment that follows has to widen back to a string. + const storedCount: unknown = field.value; + if (typeof storedCount !== 'number') { + return; + } + + const asChoice = String(storedCount); + field.value = fieldTemplate.options.includes(asChoice) ? asChoice : fieldTemplate.default; +}; + +/** + * Updates a node to the latest version of its template: + * - Create a new node data object with the latest version of the template. + * - Recursively merge new node data object into the node to be updated. + * + * The input node is not mutated; a new object is returned. + * + * @param node The node to be updated. + * @param template The invocation template to update to. + * @throws {NodeUpdateError} If the node is not an invocation node. + */ +export const updateNode = ( + node: InvocationNode, + template: InvocationTemplate, + options: UpdateNodeOptions +): InvocationNode => { + const mayUpdate = getMayUpdateNode(node, template); + + if (!mayUpdate || node.data.type !== template.type) { + throw new NodeUpdateError(`Unable to update node ${node.id}`); + } + + // Start with a "fresh" node - just as if the user created a new node of this type + const defaults = buildInvocationNode(node.position, template); + + // The updateability of a node, via semver comparison, relies on the this kind of recursive merge + // being valid. We rely on the template's major version to be majorly incremented if this kind of + // merge would result in an invalid node. + const clone = deepClone(node); + const sourceVersion = clone.data.version; + clone.data.version = template.version; + defaultsDeep(clone, defaults); // mutates! + migrateImageCollectionInputValues(clone, { ...options, sourceVersion }); + migrateMiniMaxH3NumFramesValue(clone, template, sourceVersion); + + // Remove any fields that are not in the template. Nodes that accept extras (pydantic + // `extra='allow'`, e.g. `core_metadata`) may carry undeclared inputs that are not in the template + // but must be preserved across the update, so keep those keys too. + const allowedKeys = keys(defaults.data.inputs); + if (nodeAcceptsExtraInputs(clone.data.type)) { + const extraKeys = keys(clone.data.inputs).filter((key) => !(key in defaults.data.inputs)); + clone.data.inputs = pick(clone.data.inputs, [...allowedKeys, ...extraKeys]); + } else { + clone.data.inputs = pick(clone.data.inputs, allowedKeys); + } + return clone; +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/node/resolveBatchValue.ts b/invokeai/frontend/webv1/src/features/nodes/util/node/resolveBatchValue.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/node/resolveBatchValue.ts rename to invokeai/frontend/webv1/src/features/nodes/util/node/resolveBatchValue.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputInstance.ts b/invokeai/frontend/webv1/src/features/nodes/util/schema/buildFieldInputInstance.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputInstance.ts rename to invokeai/frontend/webv1/src/features/nodes/util/schema/buildFieldInputInstance.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts b/invokeai/frontend/webv1/src/features/nodes/util/schema/buildFieldInputTemplate.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts rename to invokeai/frontend/webv1/src/features/nodes/util/schema/buildFieldInputTemplate.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldOutputTemplate.ts b/invokeai/frontend/webv1/src/features/nodes/util/schema/buildFieldOutputTemplate.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/schema/buildFieldOutputTemplate.ts rename to invokeai/frontend/webv1/src/features/nodes/util/schema/buildFieldOutputTemplate.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/parseFieldType.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/schema/parseFieldType.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/schema/parseFieldType.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/schema/parseFieldType.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/parseFieldType.ts b/invokeai/frontend/webv1/src/features/nodes/util/schema/parseFieldType.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/schema/parseFieldType.ts rename to invokeai/frontend/webv1/src/features/nodes/util/schema/parseFieldType.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/schema/parseSchema.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/schema/parseSchema.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts b/invokeai/frontend/webv1/src/features/nodes/util/schema/parseSchema.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts rename to invokeai/frontend/webv1/src/features/nodes/util/schema/parseSchema.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchemaCoreMetadata.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/schema/parseSchemaCoreMetadata.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/schema/parseSchemaCoreMetadata.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/schema/parseSchemaCoreMetadata.test.ts diff --git a/invokeai/frontend/webv1/src/features/nodes/util/workflow/buildWorkflow.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflow/buildWorkflow.test.ts new file mode 100644 index 00000000000..5300db8657f --- /dev/null +++ b/invokeai/frontend/webv1/src/features/nodes/util/workflow/buildWorkflow.test.ts @@ -0,0 +1,74 @@ +import { getInitialWorkflow } from 'features/nodes/store/nodesSlice'; +import { buildNode, call_saved_workflow } from 'features/nodes/store/util/testUtils'; +import { describe, expect, it } from 'vitest'; + +describe('buildWorkflowFast', () => { + it('persists the selected workflow id for call_saved_workflow nodes', async () => { + Object.assign(globalThis, { + window: { + location: { + origin: 'http://localhost', + }, + }, + }); + + const { buildWorkflowFast } = await import('features/nodes/util/workflow/buildWorkflow'); + const node = buildNode(call_saved_workflow); + const workflowIdInput = node.data.inputs.workflow_id; + if (!workflowIdInput) { + throw new Error('Expected workflow_id input'); + } + workflowIdInput.value = 'workflow-123'; + + const workflow = buildWorkflowFast({ + _version: 1, + formFieldInitialValues: {}, + ...getInitialWorkflow(), + nodes: [node], + edges: [], + }); + + expect(workflow.nodes).toHaveLength(1); + expect(workflow.nodes[0]?.type).toBe('invocation'); + if (workflow.nodes[0]?.type !== 'invocation') { + throw new Error('Expected invocation node'); + } + expect(workflow.nodes[0].data.type).toBe('call_saved_workflow'); + const serializedWorkflowIdInput = workflow.nodes[0].data.inputs.workflow_id; + if (!serializedWorkflowIdInput) { + throw new Error('Expected serialized workflow_id input'); + } + expect(serializedWorkflowIdInput.value).toBe('workflow-123'); + }); + + it('preserves loop_linkage edges when saving a loaded workflow', async () => { + Object.assign(globalThis, { + window: { + location: { + origin: 'http://localhost', + }, + }, + }); + + const { buildWorkflowFast } = await import('features/nodes/util/workflow/buildWorkflow'); + const node = buildNode(call_saved_workflow); + const loopEdge = { + id: 'loop-edge', + source: node.id, + sourceHandle: 'loop_linkage', + target: node.id, + targetHandle: 'loop_linkage', + type: 'loop_linkage', + } as unknown as ReturnType['edges'][number]; + + const workflow = buildWorkflowFast({ + _version: 1, + formFieldInitialValues: {}, + ...getInitialWorkflow(), + nodes: [node], + edges: [loopEdge], + }); + + expect(workflow.edges).toContainEqual(loopEdge); + }); +}); diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflow/buildWorkflow.ts similarity index 96% rename from invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflow/buildWorkflow.ts index 6d35fdf3f47..c1cdb1179fa 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/workflow/buildWorkflow.ts @@ -52,7 +52,7 @@ export const buildWorkflowFast = (nodesState: NodesState): WorkflowV3 => { } for (const edge of edges) { - if (edge.type === 'default' && edge.sourceHandle && edge.targetHandle) { + if ((edge.type === 'default' || edge.type === 'loop_linkage') && edge.sourceHandle && edge.targetHandle) { const { id, type, source, target, sourceHandle, targetHandle, hidden } = edge; newWorkflow.edges.push({ id, type, source, target, sourceHandle, targetHandle, hidden }); } else if (edge.type === 'collapsed') { diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflow/graphToWorkflow.test.ts similarity index 84% rename from invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflow/graphToWorkflow.test.ts index 18c30d486b2..211a6d2ca81 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.test.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/workflow/graphToWorkflow.test.ts @@ -182,6 +182,52 @@ describe('issue #9151: graphToWorkflow + zod validation roundtrip', () => { expect(findInput(migrated, 'core_metadata:sbmUlPbCpY', 'z_image_seed_variance_strength')?.value).toBe(0.1); expect(findInput(migrated, 'core_metadata:sbmUlPbCpY', 'z_image_seed_variance_randomize_percent')?.value).toBe(50); }); + + it('loads webv2 workflows containing loop_linkage edges', () => { + const workflow = graphToWorkflow( + { + ...userFirstGraph, + edges: [ + { + destination: { field: 'loop_linkage', node_id: 'lora_selector:lXZkTpWiQQ' }, + source: { field: 'loop_linkage', node_id: 'core_metadata:sbmUlPbCpY' }, + }, + ], + } as NonNullableGraph, + false + ); + const legacyWorkflow = JSON.parse(JSON.stringify(workflow)) as Record; + legacyWorkflow.meta = { ...(legacyWorkflow.meta as Record), version: '3.0.0' }; + legacyWorkflow.edges = (legacyWorkflow.edges as Array>).map((edge) => ({ + ...edge, + type: 'loop_linkage', + })); + + const migrated = parseAndMigrateWorkflow(legacyWorkflow); + + expect(migrated.edges[0]?.type).toBe('loop_linkage'); + }); + // The graph carries the edge type (`Literal["default", "loop_linkage"]`), and + // this is the path used to load a workflow from an image that embedded only a + // graph. Downgrading the linkage to a data edge produces a workflow that looks + // intact but no longer loops, and that the backend rejects on the next run. + it('preserves a loop_linkage edge taken from the graph', () => { + const workflow = graphToWorkflow( + { + ...userFirstGraph, + edges: [ + { + destination: { field: 'loop_linkage', node_id: 'lora_selector:lXZkTpWiQQ' }, + source: { field: 'loop_linkage', node_id: 'core_metadata:sbmUlPbCpY' }, + type: 'loop_linkage', + }, + ], + } as NonNullableGraph, + false + ); + + expect(workflow.edges[0]?.type).toBe('loop_linkage'); + }); }); const imageCollectionTemplate = { diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflow/graphToWorkflow.ts similarity index 90% rename from invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflow/graphToWorkflow.ts index 2245c0b7092..7f355f852ee 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/workflow/graphToWorkflow.ts @@ -7,7 +7,11 @@ import { nodeAcceptsExtraInputs } from 'features/nodes/types/extraInputs'; import type { FieldInputInstance, FieldInputTemplate } from 'features/nodes/types/field'; import type { WorkflowV3 } from 'features/nodes/types/workflow'; import { getDefaultForm, isWorkflowInvocationNode } from 'features/nodes/types/workflow'; -import { getConnectedInputNames, migrateImageCollectionInputValues } from 'features/nodes/util/node/nodeUpdate'; +import { + getConnectedInputNames, + migrateImageCollectionInputValues, + migrateMiniMaxH3NumFramesValue, +} from 'features/nodes/util/node/nodeUpdate'; import { buildFieldInputInstance } from 'features/nodes/util/schema/buildFieldInputInstance'; import type { NonNullableGraph } from 'services/api/types'; import { v4 as uuidv4 } from 'uuid'; @@ -118,7 +122,7 @@ export const graphToWorkflow = (graph: NonNullableGraph, autoLayout = true): Wor forEach(graph.edges, (edge) => { workflow.edges.push({ id: uuidv4(), // we don't have edge IDs in the graph - type: 'default', + type: edge.type === 'loop_linkage' ? 'loop_linkage' : 'default', source: edge.source.node_id, sourceHandle: edge.source.field, target: edge.destination.node_id, @@ -139,6 +143,14 @@ export const graphToWorkflow = (graph: NonNullableGraph, autoLayout = true): Wor migrateImageCollectionInputValues(node, { connectedInputNames: getConnectedInputNames(node.id, workflow.edges), }); + + // A graph recalled from an older session carries num_frames as a raw integer, and the node is + // built at the current template version, so nothing else would convert it. No source version + // is available here; the migration is a no-op unless the stored value really is a number. + const nodeTemplate = templates[node.data.type]; + if (nodeTemplate) { + migrateMiniMaxH3NumFramesValue(node, nodeTemplate); + } }); if (autoLayout) { diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/loraMetadataRoundtrip.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflow/loraMetadataRoundtrip.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/workflow/loraMetadataRoundtrip.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflow/loraMetadataRoundtrip.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflow/migrations.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflow/migrations.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflow/validateWorkflow.test.ts similarity index 99% rename from invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflow/validateWorkflow.test.ts index 7d5abf34e5e..c0f4edfa21f 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.test.ts +++ b/invokeai/frontend/webv1/src/features/nodes/util/workflow/validateWorkflow.test.ts @@ -544,6 +544,7 @@ describe('validateWorkflow', () => { opened_at: null, user_id: 'user-1', is_public: false, + revision: 1, thumbnail_url: null, workflow: { id: 'saved-workflow-1', diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflow/validateWorkflow.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflow/validateWorkflow.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/workflowImageExport.edgeCases.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflowImageExport.edgeCases.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/workflowImageExport.edgeCases.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflowImageExport.edgeCases.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/workflowImageExport.test.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflowImageExport.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/workflowImageExport.test.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflowImageExport.test.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/workflowImageExport.ts b/invokeai/frontend/webv1/src/features/nodes/util/workflowImageExport.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/workflowImageExport.ts rename to invokeai/frontend/webv1/src/features/nodes/util/workflowImageExport.ts diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamAnimaModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamAnimaModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamAnimaModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamAnimaModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCLIPEmbedModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamCLIPEmbedModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCLIPEmbedModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamCLIPEmbedModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCLIPGEmbedModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamCLIPGEmbedModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCLIPGEmbedModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamCLIPGEmbedModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCLIPLEmbedModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamCLIPLEmbedModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCLIPLEmbedModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamCLIPLEmbedModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamClipSkip.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamClipSkip.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamClipSkip.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamClipSkip.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamFlux2DevModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamFlux2DevModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamFlux2DevModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamFlux2DevModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamFlux2KleinModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamFlux2KleinModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamFlux2KleinModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamFlux2KleinModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamHiDiffusionToggle.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamHiDiffusionToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamHiDiffusionToggle.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamHiDiffusionToggle.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamKrea2ModelSelects.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamKrea2ModelSelects.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamKrea2ModelSelects.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamKrea2ModelSelects.tsx diff --git a/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamMiniMaxH3ModelSelects.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamMiniMaxH3ModelSelects.tsx new file mode 100644 index 00000000000..961c56ee63c --- /dev/null +++ b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamMiniMaxH3ModelSelects.tsx @@ -0,0 +1,131 @@ +import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useModelCombobox } from 'common/hooks/useModelCombobox'; +import { + minimaxH3TextEncoderModelSelected, + minimaxH3TransformerModelSelected, + selectMiniMaxH3TextEncoderModel, + selectMiniMaxH3TransformerModel, +} from 'features/controlLayers/store/paramsSlice'; +import { zModelIdentifierField } from 'features/nodes/types/common'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMiniMaxH3CheckpointModels, useMiniMaxH3TextEncoderModels } from 'services/api/hooks/modelsByType'; +import type { MainModelConfig, Qwen3VLEncoderModelConfig } from 'services/api/types'; + +/** + * MiniMax H3 Transformer (single file) Select + * + * Picks an optional single-file transformer checkpoint (e.g. the pruned int8 repack) that + * replaces the diffusers folder's 62 GB bf16 transformer. The text encoder and both VAEs + * still come from the folder install selected as the main model. + */ +const ParamMiniMaxH3TransformerSelect = memo(() => { + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + const value = useAppSelector(selectMiniMaxH3TransformerModel); + const [modelConfigs, { isLoading }] = useMiniMaxH3CheckpointModels(); + + const _onChange = useCallback( + (model: MainModelConfig | null) => { + if (model) { + dispatch(minimaxH3TransformerModelSelected(zModelIdentifierField.parse(model))); + } else { + dispatch(minimaxH3TransformerModelSelected(null)); + } + }, + [dispatch] + ); + + const { + options, + value: comboValue, + onChange, + noOptionsMessage, + } = useModelCombobox({ + modelConfigs, + onChange: _onChange, + selectedModel: value, + isLoading, + }); + + return ( + + {t('modelManager.minimaxH3TransformerModel')} + + + ); +}); + +ParamMiniMaxH3TransformerSelect.displayName = 'ParamMiniMaxH3TransformerSelect'; + +/** + * MiniMax H3 Text Encoder (single file) Select + * + * Picks an optional single-file truncated Qwen3-VL-32B encoder (e.g. the int8 repack) that + * replaces the diffusers folder's 62 GB bf16 text encoder. The tokenizer and processor still + * come from the folder install selected as the main model. + */ +const ParamMiniMaxH3TextEncoderSelect = memo(() => { + const dispatch = useAppDispatch(); + const { t } = useTranslation(); + const value = useAppSelector(selectMiniMaxH3TextEncoderModel); + const [modelConfigs, { isLoading }] = useMiniMaxH3TextEncoderModels(); + + const _onChange = useCallback( + (model: Qwen3VLEncoderModelConfig | null) => { + if (model) { + dispatch(minimaxH3TextEncoderModelSelected(zModelIdentifierField.parse(model))); + } else { + dispatch(minimaxH3TextEncoderModelSelected(null)); + } + }, + [dispatch] + ); + + const { + options, + value: comboValue, + onChange, + noOptionsMessage, + } = useModelCombobox({ + modelConfigs, + onChange: _onChange, + selectedModel: value, + isLoading, + }); + + return ( + + {t('modelManager.minimaxH3TextEncoderModel')} + + + ); +}); + +ParamMiniMaxH3TextEncoderSelect.displayName = 'ParamMiniMaxH3TextEncoderSelect'; + +const ParamMiniMaxH3ModelSelects = () => { + return ( + <> + + + + ); +}; + +export default memo(ParamMiniMaxH3ModelSelects); diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamOptimizedDenoisingToggle.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamOptimizedDenoisingToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamOptimizedDenoisingToggle.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamOptimizedDenoisingToggle.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamQwenImageComponentSourceSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamQwenImageComponentSourceSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamQwenImageComponentSourceSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamQwenImageComponentSourceSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamQwenImageQuantization.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamQwenImageQuantization.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamQwenImageQuantization.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamQwenImageQuantization.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamT5EncoderModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamT5EncoderModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamT5EncoderModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamT5EncoderModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamWanModelSelects.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamWanModelSelects.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamWanModelSelects.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamWanModelSelects.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamZImageQwen3VaeModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamZImageQwen3VaeModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/ParamZImageQwen3VaeModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/ParamZImageQwen3VaeModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/PidSettings.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Advanced/PidSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Advanced/PidSettings.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Advanced/PidSettings.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxAspectRatioSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxAspectRatioSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxHeight.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxHeight.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxHeight.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxHeight.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxLockAspectRatioButton.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxLockAspectRatioButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxLockAspectRatioButton.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxLockAspectRatioButton.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxPreview.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxPreview.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxPreview.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxPreview.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxScaleMethod.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxScaleMethod.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxScaleMethod.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxScaleMethod.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxScaledHeight.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxScaledHeight.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxScaledHeight.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxScaledHeight.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxScaledWidth.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxScaledWidth.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxScaledWidth.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxScaledWidth.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSetOptimalSizeButton.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxSetOptimalSizeButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSetOptimalSizeButton.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxSetOptimalSizeButton.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSettings.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSettings.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxSettings.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSwapDimensionsButton.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxSwapDimensionsButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSwapDimensionsButton.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxSwapDimensionsButton.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxWidth.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxWidth.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/BboxWidth.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/BboxWidth.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/use-is-bbox-size-locked.ts b/invokeai/frontend/webv1/src/features/parameters/components/Bbox/use-is-bbox-size-locked.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Bbox/use-is-bbox-size-locked.ts rename to invokeai/frontend/webv1/src/features/parameters/components/Bbox/use-is-bbox-size-locked.ts diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceEdgeSize.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceEdgeSize.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceEdgeSize.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceEdgeSize.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMinDenoise.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMinDenoise.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMinDenoise.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMinDenoise.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMode.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMode.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMode.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMode.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillColorOptions.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillColorOptions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillColorOptions.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillColorOptions.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillOptions.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillOptions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillOptions.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillOptions.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillPatchmatchDownscaleSize.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillPatchmatchDownscaleSize.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillPatchmatchDownscaleSize.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillPatchmatchDownscaleSize.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillTilesize.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillTilesize.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillTilesize.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillTilesize.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/NegativePromptToggleButton.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/NegativePromptToggleButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/NegativePromptToggleButton.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/NegativePromptToggleButton.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamAnimaScheduler.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamAnimaScheduler.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamAnimaScheduler.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamAnimaScheduler.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamCFGScale.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamCFGScale.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamErnieImagePromptEnhancer.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamErnieImagePromptEnhancer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamErnieImagePromptEnhancer.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamErnieImagePromptEnhancer.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamErnieImageScheduler.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamErnieImageScheduler.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamErnieImageScheduler.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamErnieImageScheduler.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamFluxDypeExponent.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamFluxDypeExponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamFluxDypeExponent.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamFluxDypeExponent.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamFluxDypePreset.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamFluxDypePreset.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamFluxDypePreset.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamFluxDypePreset.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamFluxDypeScale.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamFluxDypeScale.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamFluxDypeScale.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamFluxDypeScale.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamFluxScheduler.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamFluxScheduler.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamFluxScheduler.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamFluxScheduler.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamGuidance.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamGuidance.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamGuidance.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamGuidance.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4ColorPalette.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4ColorPalette.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4ColorPalette.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4ColorPalette.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4GuidanceScale.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4GuidanceScale.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4GuidanceScale.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4GuidanceScale.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4Mu.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4Mu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4Mu.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4Mu.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4SamplerPreset.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4SamplerPreset.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4SamplerPreset.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4SamplerPreset.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4Steps.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4Steps.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamIdeogram4Steps.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamIdeogram4Steps.tsx diff --git a/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamMiniMaxH3Duration.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamMiniMaxH3Duration.tsx new file mode 100644 index 00000000000..b379375a45d --- /dev/null +++ b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamMiniMaxH3Duration.tsx @@ -0,0 +1,66 @@ +import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { + minimaxH3DurationSecondsChanged, + selectMiniMaxH3DurationSeconds, + selectMiniMaxH3OutputMode, +} from 'features/controlLayers/store/paramsSlice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +// MiniMax H3 runs at a fixed 24 fps and frame counts snap to the 17n+5 grid. The slider works +// in whole seconds; the graph builder snaps to the nearest legal frame count, and the 14 s +// stop maps to the model's true ceiling (345 frames = 14.375 s). +const CONSTRAINTS = { + initial: 5, + sliderMin: 5, + sliderMax: 14, + fineStep: 1, + coarseStep: 1, +}; + +const MARKS = [5, 10, 14]; + +/** + * MiniMax H3 video duration in seconds. Only shown in the 'video' output mode - the 'image' + * mode always generates the 5-frame minimum block. + */ +const ParamMiniMaxH3Duration = () => { + const { t } = useTranslation(); + const duration = useAppSelector(selectMiniMaxH3DurationSeconds); + const outputMode = useAppSelector(selectMiniMaxH3OutputMode); + const dispatch = useAppDispatch(); + + const onChange = useCallback((v: number) => dispatch(minimaxH3DurationSecondsChanged(v)), [dispatch]); + + if (outputMode !== 'video') { + return null; + } + + return ( + + {t('parameters.minimaxH3DurationSeconds')} + + + + ); +}; + +export default memo(ParamMiniMaxH3Duration); diff --git a/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamMiniMaxH3OutputMode.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamMiniMaxH3OutputMode.tsx new file mode 100644 index 00000000000..df4b82c9629 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamMiniMaxH3OutputMode.tsx @@ -0,0 +1,34 @@ +import { Button, ButtonGroup, FormControl, FormLabel } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { minimaxH3OutputModeChanged, selectMiniMaxH3OutputMode } from 'features/controlLayers/store/paramsSlice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +/** + * MiniMax H3 output mode toggle: 'video' (joint audio-video, Generate tab only) or 'image' + * (a 5-frame minimum clip decoded to one gallery image - the txt2img mode, canvas-capable). + */ +const ParamMiniMaxH3OutputMode = () => { + const { t } = useTranslation(); + const outputMode = useAppSelector(selectMiniMaxH3OutputMode); + const dispatch = useAppDispatch(); + + const onClickVideo = useCallback(() => dispatch(minimaxH3OutputModeChanged('video')), [dispatch]); + const onClickImage = useCallback(() => dispatch(minimaxH3OutputModeChanged('image')), [dispatch]); + + return ( + + {t('parameters.minimaxH3OutputMode')} + + + + + + ); +}; + +export default memo(ParamMiniMaxH3OutputMode); diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamNegativePrompt.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamNegativePrompt.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamPositivePrompt.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamPositivePrompt.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamQwenImageShift.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamQwenImageShift.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamQwenImageShift.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamQwenImageShift.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamScheduler.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamScheduler.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamSteps.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamSteps.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamWanGuidanceScaleLowNoise.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamWanGuidanceScaleLowNoise.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamWanGuidanceScaleLowNoise.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamWanGuidanceScaleLowNoise.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamZImageScheduler.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamZImageScheduler.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamZImageScheduler.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamZImageScheduler.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamZImageShift.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/ParamZImageShift.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/ParamZImageShift.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/ParamZImageShift.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/PositivePromptHistory.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Core/PositivePromptHistory.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Core/PositivePromptHistory.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Core/PositivePromptHistory.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/Dimensions.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/Dimensions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/Dimensions.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/Dimensions.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsHeight.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsHeight.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsHeight.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsHeight.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsLockAspectRatioButton.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsLockAspectRatioButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsLockAspectRatioButton.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsLockAspectRatioButton.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsPreview.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsPreview.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsPreview.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsPreview.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsSetOptimalSizeButton.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsSetOptimalSizeButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsSetOptimalSizeButton.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsSetOptimalSizeButton.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsSwapButton.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsSwapButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsSwapButton.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsSwapButton.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsWidth.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsWidth.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsWidth.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/DimensionsWidth.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/ExternalModelImageSizeSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/ExternalModelImageSizeSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/ExternalModelImageSizeSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/ExternalModelImageSizeSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/ExternalModelResolutionSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Dimensions/ExternalModelResolutionSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Dimensions/ExternalModelResolutionSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Dimensions/ExternalModelResolutionSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/External/GeminiProviderOptions.tsx b/invokeai/frontend/webv1/src/features/parameters/components/External/GeminiProviderOptions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/External/GeminiProviderOptions.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/External/GeminiProviderOptions.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/External/OpenAIProviderOptions.tsx b/invokeai/frontend/webv1/src/features/parameters/components/External/OpenAIProviderOptions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/External/OpenAIProviderOptions.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/External/OpenAIProviderOptions.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/External/SeedreamProviderOptions.tsx b/invokeai/frontend/webv1/src/features/parameters/components/External/SeedreamProviderOptions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/External/SeedreamProviderOptions.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/External/SeedreamProviderOptions.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/External/externalProviderOptionsI18n.test.ts b/invokeai/frontend/webv1/src/features/parameters/components/External/externalProviderOptionsI18n.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/External/externalProviderOptionsI18n.test.ts rename to invokeai/frontend/webv1/src/features/parameters/components/External/externalProviderOptionsI18n.test.ts diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2EnhancersSettings.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2EnhancersSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2EnhancersSettings.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2EnhancersSettings.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceEnabled.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceEnabled.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceEnabled.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceEnabled.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceMultiplier.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceMultiplier.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceMultiplier.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceMultiplier.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.test.ts b/invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.test.ts rename to invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.test.ts diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2RebalanceWeights.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceEnabled.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceEnabled.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceEnabled.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceEnabled.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceRandomizePercent.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceRandomizePercent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceRandomizePercent.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceRandomizePercent.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceStrength.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceStrength.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceStrength.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Krea2Enhancers/ParamKrea2SeedVarianceStrength.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/NavigateToModelManagerButton.tsx b/invokeai/frontend/webv1/src/features/parameters/components/MainModel/NavigateToModelManagerButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/MainModel/NavigateToModelManagerButton.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/MainModel/NavigateToModelManagerButton.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/UseDefaultSettingsButton.tsx b/invokeai/frontend/webv1/src/features/parameters/components/MainModel/UseDefaultSettingsButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/MainModel/UseDefaultSettingsButton.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/MainModel/UseDefaultSettingsButton.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts b/invokeai/frontend/webv1/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts rename to invokeai/frontend/webv1/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.ts b/invokeai/frontend/webv1/src/features/parameters/components/MainModel/mainModelPickerUtils.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.ts rename to invokeai/frontend/webv1/src/features/parameters/components/MainModel/mainModelPickerUtils.ts diff --git a/invokeai/frontend/web/src/features/parameters/components/ModelPicker.tsx b/invokeai/frontend/webv1/src/features/parameters/components/ModelPicker.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/ModelPicker.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/ModelPicker.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/PostProcessing/ParamPostProcessingModel.tsx b/invokeai/frontend/webv1/src/features/parameters/components/PostProcessing/ParamPostProcessingModel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/PostProcessing/ParamPostProcessingModel.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/PostProcessing/ParamPostProcessingModel.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/PostProcessing/PostProcessingPopover.tsx b/invokeai/frontend/webv1/src/features/parameters/components/PostProcessing/PostProcessingPopover.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/PostProcessing/PostProcessingPopover.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/PostProcessing/PostProcessingPopover.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/PromptLabel.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Prompts/PromptLabel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Prompts/PromptLabel.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Prompts/PromptLabel.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/PromptOverlayButtonWrapper.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Prompts/PromptOverlayButtonWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Prompts/PromptOverlayButtonWrapper.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Prompts/PromptOverlayButtonWrapper.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/PromptResizeHandle.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Prompts/PromptResizeHandle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Prompts/PromptResizeHandle.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Prompts/PromptResizeHandle.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.stories.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Prompts/Prompts.stories.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.stories.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Prompts/Prompts.stories.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Prompts/Prompts.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Prompts/Prompts.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/UpscalePrompts.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Prompts/UpscalePrompts.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Prompts/UpscalePrompts.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Prompts/UpscalePrompts.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/ViewModePrompt.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Prompts/ViewModePrompt.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Prompts/ViewModePrompt.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Prompts/ViewModePrompt.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessXAxis.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Seamless/ParamSeamlessXAxis.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessXAxis.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Seamless/ParamSeamlessXAxis.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessYAxis.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Seamless/ParamSeamlessYAxis.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Seamless/ParamSeamlessYAxis.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Seamless/ParamSeamlessYAxis.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeed.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Seed/ParamSeed.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeed.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Seed/ParamSeed.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedRandomize.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Seed/ParamSeedRandomize.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedRandomize.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Seed/ParamSeedRandomize.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedShuffle.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Seed/ParamSeedShuffle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedShuffle.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Seed/ParamSeedShuffle.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceEnabled.tsx b/invokeai/frontend/webv1/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceEnabled.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceEnabled.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceEnabled.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceRandomizePercent.tsx b/invokeai/frontend/webv1/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceRandomizePercent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceRandomizePercent.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceRandomizePercent.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceSettings.tsx b/invokeai/frontend/webv1/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceSettings.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceSettings.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceSettings.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceStrength.tsx b/invokeai/frontend/webv1/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceStrength.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceStrength.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/SeedVariance/ParamZImageSeedVarianceStrength.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamCreativity.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamCreativity.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Upscale/ParamCreativity.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamCreativity.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamStructure.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamStructure.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Upscale/ParamStructure.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamStructure.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamTileControlNetModel.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamTileControlNetModel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Upscale/ParamTileControlNetModel.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamTileControlNetModel.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamTileOverlap.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamTileOverlap.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Upscale/ParamTileOverlap.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamTileOverlap.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamTileSize.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamTileSize.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Upscale/ParamTileSize.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamTileSize.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamUpscaleCFGScale.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamUpscaleCFGScale.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Upscale/ParamUpscaleCFGScale.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamUpscaleCFGScale.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamUpscaleScheduler.tsx b/invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamUpscaleScheduler.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/Upscale/ParamUpscaleScheduler.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/Upscale/ParamUpscaleScheduler.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamColorCompensation.tsx b/invokeai/frontend/webv1/src/features/parameters/components/VAEModel/ParamColorCompensation.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamColorCompensation.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/VAEModel/ParamColorCompensation.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamFLUXVAEModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/VAEModel/ParamFLUXVAEModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamFLUXVAEModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/VAEModel/ParamFLUXVAEModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx b/invokeai/frontend/webv1/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx b/invokeai/frontend/webv1/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx similarity index 100% rename from invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx rename to invokeai/frontend/webv1/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx diff --git a/invokeai/frontend/web/src/features/parameters/store/actions.ts b/invokeai/frontend/webv1/src/features/parameters/store/actions.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/store/actions.ts rename to invokeai/frontend/webv1/src/features/parameters/store/actions.ts diff --git a/invokeai/frontend/web/src/features/parameters/store/upscaleSlice.ts b/invokeai/frontend/webv1/src/features/parameters/store/upscaleSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/store/upscaleSlice.ts rename to invokeai/frontend/webv1/src/features/parameters/store/upscaleSlice.ts diff --git a/invokeai/frontend/web/src/features/parameters/types/constants.ts b/invokeai/frontend/webv1/src/features/parameters/types/constants.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/types/constants.ts rename to invokeai/frontend/webv1/src/features/parameters/types/constants.ts diff --git a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts b/invokeai/frontend/webv1/src/features/parameters/types/parameterSchemas.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts rename to invokeai/frontend/webv1/src/features/parameters/types/parameterSchemas.ts diff --git a/invokeai/frontend/web/src/features/parameters/util/externalPanelSchema.ts b/invokeai/frontend/webv1/src/features/parameters/util/externalPanelSchema.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/util/externalPanelSchema.ts rename to invokeai/frontend/webv1/src/features/parameters/util/externalPanelSchema.ts diff --git a/invokeai/frontend/web/src/features/parameters/util/flux2Klein.ts b/invokeai/frontend/webv1/src/features/parameters/util/flux2Klein.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/util/flux2Klein.ts rename to invokeai/frontend/webv1/src/features/parameters/util/flux2Klein.ts diff --git a/invokeai/frontend/web/src/features/parameters/util/optimalDimension.test.ts b/invokeai/frontend/webv1/src/features/parameters/util/optimalDimension.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/util/optimalDimension.test.ts rename to invokeai/frontend/webv1/src/features/parameters/util/optimalDimension.test.ts diff --git a/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts b/invokeai/frontend/webv1/src/features/parameters/util/optimalDimension.ts similarity index 91% rename from invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts rename to invokeai/frontend/webv1/src/features/parameters/util/optimalDimension.ts index 1777033e6b2..6cb461c0c7c 100644 --- a/invokeai/frontend/web/src/features/parameters/util/optimalDimension.ts +++ b/invokeai/frontend/webv1/src/features/parameters/util/optimalDimension.ts @@ -33,6 +33,12 @@ export const getOptimalDimension = (base?: BaseModelType | null, pidScale = 1): case 'sd-1': case 'sd-2': return 512; + case 'minimax-h3': + // Native canvas has a 768px short edge (soft cap 768x1344). + return 768; + case 'ltx-2': + // Declared canvas is 1280x704: a 704px short edge. + return 704; case 'sdxl': case 'flux': case 'flux2': @@ -107,6 +113,15 @@ export const getGridSize = (base?: BaseModelType | null, pidScale = 1): number = case 'cogview4': gridSize = 32; break; + case 'minimax-h3': + // The H3 denoise node hard-validates width/height as multiples of 32 + // (16x VAE spatial compression x patch size 2). + gridSize = 32; + break; + case 'ltx-2': + // 32x VAE spatial compression at patch size 1. + gridSize = 32; + break; case 'flux': case 'flux2': case 'sd-3': diff --git a/invokeai/frontend/web/src/features/parameters/util/pid.ts b/invokeai/frontend/webv1/src/features/parameters/util/pid.ts similarity index 100% rename from invokeai/frontend/web/src/features/parameters/util/pid.ts rename to invokeai/frontend/webv1/src/features/parameters/util/pid.ts diff --git a/invokeai/frontend/web/src/features/prompt/AddPromptTriggerButton.tsx b/invokeai/frontend/webv1/src/features/prompt/AddPromptTriggerButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/prompt/AddPromptTriggerButton.tsx rename to invokeai/frontend/webv1/src/features/prompt/AddPromptTriggerButton.tsx diff --git a/invokeai/frontend/web/src/features/prompt/ExpandPromptButton.tsx b/invokeai/frontend/webv1/src/features/prompt/ExpandPromptButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/prompt/ExpandPromptButton.tsx rename to invokeai/frontend/webv1/src/features/prompt/ExpandPromptButton.tsx diff --git a/invokeai/frontend/web/src/features/prompt/ImageToPromptButton.tsx b/invokeai/frontend/webv1/src/features/prompt/ImageToPromptButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/prompt/ImageToPromptButton.tsx rename to invokeai/frontend/webv1/src/features/prompt/ImageToPromptButton.tsx diff --git a/invokeai/frontend/web/src/features/prompt/LLMTaskProgressDisplay.tsx b/invokeai/frontend/webv1/src/features/prompt/LLMTaskProgressDisplay.tsx similarity index 100% rename from invokeai/frontend/web/src/features/prompt/LLMTaskProgressDisplay.tsx rename to invokeai/frontend/webv1/src/features/prompt/LLMTaskProgressDisplay.tsx diff --git a/invokeai/frontend/web/src/features/prompt/PromptPopover.tsx b/invokeai/frontend/webv1/src/features/prompt/PromptPopover.tsx similarity index 100% rename from invokeai/frontend/web/src/features/prompt/PromptPopover.tsx rename to invokeai/frontend/webv1/src/features/prompt/PromptPopover.tsx diff --git a/invokeai/frontend/web/src/features/prompt/PromptTriggerSelect.stories.tsx b/invokeai/frontend/webv1/src/features/prompt/PromptTriggerSelect.stories.tsx similarity index 100% rename from invokeai/frontend/web/src/features/prompt/PromptTriggerSelect.stories.tsx rename to invokeai/frontend/webv1/src/features/prompt/PromptTriggerSelect.stories.tsx diff --git a/invokeai/frontend/web/src/features/prompt/PromptTriggerSelect.tsx b/invokeai/frontend/webv1/src/features/prompt/PromptTriggerSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/prompt/PromptTriggerSelect.tsx rename to invokeai/frontend/webv1/src/features/prompt/PromptTriggerSelect.tsx diff --git a/invokeai/frontend/web/src/features/prompt/promptUndo.test.ts b/invokeai/frontend/webv1/src/features/prompt/promptUndo.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/prompt/promptUndo.test.ts rename to invokeai/frontend/webv1/src/features/prompt/promptUndo.test.ts diff --git a/invokeai/frontend/web/src/features/prompt/promptUndo.ts b/invokeai/frontend/webv1/src/features/prompt/promptUndo.ts similarity index 100% rename from invokeai/frontend/web/src/features/prompt/promptUndo.ts rename to invokeai/frontend/webv1/src/features/prompt/promptUndo.ts diff --git a/invokeai/frontend/web/src/features/prompt/store/expandPromptSlice.ts b/invokeai/frontend/webv1/src/features/prompt/store/expandPromptSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/prompt/store/expandPromptSlice.ts rename to invokeai/frontend/webv1/src/features/prompt/store/expandPromptSlice.ts diff --git a/invokeai/frontend/web/src/features/prompt/types.ts b/invokeai/frontend/webv1/src/features/prompt/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/prompt/types.ts rename to invokeai/frontend/webv1/src/features/prompt/types.ts diff --git a/invokeai/frontend/web/src/features/prompt/usePrompt.ts b/invokeai/frontend/webv1/src/features/prompt/usePrompt.ts similarity index 100% rename from invokeai/frontend/web/src/features/prompt/usePrompt.ts rename to invokeai/frontend/webv1/src/features/prompt/usePrompt.ts diff --git a/invokeai/frontend/web/src/features/prompt/usePromptAttentionHotkeys.ts b/invokeai/frontend/webv1/src/features/prompt/usePromptAttentionHotkeys.ts similarity index 100% rename from invokeai/frontend/web/src/features/prompt/usePromptAttentionHotkeys.ts rename to invokeai/frontend/webv1/src/features/prompt/usePromptAttentionHotkeys.ts diff --git a/invokeai/frontend/web/src/features/queue/components/CancelAllExceptCurrentIconButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/CancelAllExceptCurrentIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/CancelAllExceptCurrentIconButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/CancelAllExceptCurrentIconButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog.tsx b/invokeai/frontend/webv1/src/features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog.tsx rename to invokeai/frontend/webv1/src/features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/CancelCurrentQueueItemIconButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/CancelCurrentQueueItemIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/CancelCurrentQueueItemIconButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/CancelCurrentQueueItemIconButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/ClearInvocationCacheButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/ClearInvocationCacheButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/ClearInvocationCacheButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/ClearInvocationCacheButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/ClearModelCacheButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/ClearModelCacheButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/ClearModelCacheButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/ClearModelCacheButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/ClearQueueButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/ClearQueueButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/ClearQueueButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx b/invokeai/frontend/webv1/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx rename to invokeai/frontend/webv1/src/features/queue/components/ClearQueueConfirmationAlertDialog.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/DeleteAllExceptCurrentIconButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/DeleteAllExceptCurrentIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/DeleteAllExceptCurrentIconButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/DeleteAllExceptCurrentIconButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog.tsx b/invokeai/frontend/webv1/src/features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog.tsx rename to invokeai/frontend/webv1/src/features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/DeleteCurrentQueueItemIconButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/DeleteCurrentQueueItemIconButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/DeleteCurrentQueueItemIconButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/DeleteCurrentQueueItemIconButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/InvocationCacheStatus.tsx b/invokeai/frontend/webv1/src/features/queue/components/InvocationCacheStatus.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/InvocationCacheStatus.tsx rename to invokeai/frontend/webv1/src/features/queue/components/InvocationCacheStatus.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/InvokeButtonIcon.tsx b/invokeai/frontend/webv1/src/features/queue/components/InvokeButtonIcon.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/InvokeButtonIcon.tsx rename to invokeai/frontend/webv1/src/features/queue/components/InvokeButtonIcon.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx b/invokeai/frontend/webv1/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx rename to invokeai/frontend/webv1/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/InvokeQueueBackButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/InvokeQueueBackButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/PauseProcessorButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/PauseProcessorButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/PauseProcessorButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/PauseProcessorButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/PruneQueueButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/PruneQueueButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/PruneQueueButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/PruneQueueButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueActionsMenuButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueActionsMenuButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueControls.stories.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueControls.stories.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueControls.stories.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueControls.stories.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueControls.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueControls.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueControls.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueCountBadge.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueCountBadge.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueCountBadge.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueCountBadge.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueIterationsNumberInput.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueIterationsNumberInput.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueIterationsNumberInput.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueIterationsNumberInput.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemComponent.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueItemComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemComponent.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueItemComponent.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemDetail.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueItemDetail.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemDetail.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueItemDetail.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/QueueList.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueList.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueListComponent.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueListComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/QueueListComponent.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueListComponent.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueListHeader.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueListHeader.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/QueueListHeader.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueListHeader.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueListHeaderColumn.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueListHeaderColumn.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/QueueListHeaderColumn.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/QueueListHeaderColumn.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/constants.ts b/invokeai/frontend/webv1/src/features/queue/components/QueueList/constants.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/constants.ts rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/constants.ts diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/getQueueItemActionVisibility.test.ts b/invokeai/frontend/webv1/src/features/queue/components/QueueList/getQueueItemActionVisibility.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/getQueueItemActionVisibility.test.ts rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/getQueueItemActionVisibility.test.ts diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/getQueueItemActionVisibility.ts b/invokeai/frontend/webv1/src/features/queue/components/QueueList/getQueueItemActionVisibility.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/getQueueItemActionVisibility.ts rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/getQueueItemActionVisibility.ts diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/types.ts b/invokeai/frontend/webv1/src/features/queue/components/QueueList/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/types.ts rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/types.ts diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/useDestinationText.ts b/invokeai/frontend/webv1/src/features/queue/components/QueueList/useDestinationText.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/useDestinationText.ts rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/useDestinationText.ts diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/useOriginText.ts b/invokeai/frontend/webv1/src/features/queue/components/QueueList/useOriginText.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/useOriginText.ts rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/useOriginText.ts diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/useQueueItemIds.ts b/invokeai/frontend/webv1/src/features/queue/components/QueueList/useQueueItemIds.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/useQueueItemIds.ts rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/useQueueItemIds.ts diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/useScrollableQueueList.ts b/invokeai/frontend/webv1/src/features/queue/components/QueueList/useScrollableQueueList.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueList/useScrollableQueueList.ts rename to invokeai/frontend/webv1/src/features/queue/components/QueueList/useScrollableQueueList.ts diff --git a/invokeai/frontend/web/src/features/queue/components/QueueStatus.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueStatus.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueStatus.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueStatus.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueTabContent.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueTabContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueTabContent.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueTabContent.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/QueueTabQueueControls.tsx b/invokeai/frontend/webv1/src/features/queue/components/QueueTabQueueControls.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/QueueTabQueueControls.tsx rename to invokeai/frontend/webv1/src/features/queue/components/QueueTabQueueControls.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/ResumeProcessorButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/ResumeProcessorButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/ResumeProcessorButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/ResumeProcessorButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/ToggleInvocationCacheButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/ToggleInvocationCacheButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/ToggleInvocationCacheButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/ToggleInvocationCacheButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/common/QueueButton.tsx b/invokeai/frontend/webv1/src/features/queue/components/common/QueueButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/common/QueueButton.tsx rename to invokeai/frontend/webv1/src/features/queue/components/common/QueueButton.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/common/QueueStatusBadge.test.ts b/invokeai/frontend/webv1/src/features/queue/components/common/QueueStatusBadge.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/common/QueueStatusBadge.test.ts rename to invokeai/frontend/webv1/src/features/queue/components/common/QueueStatusBadge.test.ts diff --git a/invokeai/frontend/web/src/features/queue/components/common/QueueStatusBadge.tsx b/invokeai/frontend/webv1/src/features/queue/components/common/QueueStatusBadge.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/common/QueueStatusBadge.tsx rename to invokeai/frontend/webv1/src/features/queue/components/common/QueueStatusBadge.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/common/StatusStatGroup.tsx b/invokeai/frontend/webv1/src/features/queue/components/common/StatusStatGroup.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/common/StatusStatGroup.tsx rename to invokeai/frontend/webv1/src/features/queue/components/common/StatusStatGroup.tsx diff --git a/invokeai/frontend/web/src/features/queue/components/common/StatusStatItem.tsx b/invokeai/frontend/webv1/src/features/queue/components/common/StatusStatItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/queue/components/common/StatusStatItem.tsx rename to invokeai/frontend/webv1/src/features/queue/components/common/StatusStatItem.tsx diff --git a/invokeai/frontend/web/src/features/queue/hooks/invocationCacheControls.test.ts b/invokeai/frontend/webv1/src/features/queue/hooks/invocationCacheControls.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/invocationCacheControls.test.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/invocationCacheControls.test.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/invocationCacheControls.ts b/invokeai/frontend/webv1/src/features/queue/hooks/invocationCacheControls.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/invocationCacheControls.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/invocationCacheControls.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useBatchIsCanceled.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useBatchIsCanceled.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useBatchIsCanceled.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useBatchIsCanceled.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelAllExceptCurrentQueueItem.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useCancelAllExceptCurrentQueueItem.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useCancelAllExceptCurrentQueueItem.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useCancelAllExceptCurrentQueueItem.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useCancelBatch.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useCancelBatch.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useCancelCurrentQueueItem.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useCancelCurrentQueueItem.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useCancelQueueItem.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useCancelQueueItem.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItemsByDestination.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useCancelQueueItemsByDestination.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItemsByDestination.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useCancelQueueItemsByDestination.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useClearInvocationCache.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useClearInvocationCache.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useClearQueue.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useClearQueue.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCurrentQueueItemDestination.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useCurrentQueueItemDestination.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useCurrentQueueItemDestination.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useCurrentQueueItemDestination.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCurrentQueueItemId.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useCurrentQueueItemId.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useCurrentQueueItemId.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useCurrentQueueItemId.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useDeleteAllExceptCurrentQueueItem.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useDeleteAllExceptCurrentQueueItem.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useDeleteAllExceptCurrentQueueItem.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useDeleteAllExceptCurrentQueueItem.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useDeleteCurrentQueueItem.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useDeleteCurrentQueueItem.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useDeleteCurrentQueueItem.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useDeleteCurrentQueueItem.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useDeleteQueueItem.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useDeleteQueueItem.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useDeleteQueueItem.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useDeleteQueueItem.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useDisableInvocationCache.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useDisableInvocationCache.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useEnableInvocationCache.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useEnableInvocationCache.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueCanvas.ts similarity index 97% rename from invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueCanvas.ts index e8c823d1c5f..cd24e57ec28 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts +++ b/invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueCanvas.ts @@ -20,6 +20,7 @@ import { buildExternalGraph } from 'features/nodes/util/graph/generation/buildEx import { buildFLUXGraph } from 'features/nodes/util/graph/generation/buildFLUXGraph'; import { buildIdeogram4Graph } from 'features/nodes/util/graph/generation/buildIdeogram4Graph'; import { buildKrea2Graph } from 'features/nodes/util/graph/generation/buildKrea2Graph'; +import { buildMiniMaxH3Graph } from 'features/nodes/util/graph/generation/buildMiniMaxH3Graph'; import { buildQwenImageGraph } from 'features/nodes/util/graph/generation/buildQwenImageGraph'; import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph'; import { buildSD3Graph } from 'features/nodes/util/graph/generation/buildSD3Graph'; @@ -85,6 +86,8 @@ const enqueueCanvas = async (store: AppStore, canvasManager: CanvasManager, prep return await buildAnimaGraph(graphBuilderArg); case 'wan': return await buildWanGraph(graphBuilderArg); + case 'minimax-h3': + return await buildMiniMaxH3Graph(graphBuilderArg); default: assert(false, `No graph builders for base ${base}`); } diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueGenerate.ts similarity index 97% rename from invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueGenerate.ts index 65309420664..6f2c770dc63 100644 --- a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts +++ b/invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueGenerate.ts @@ -18,6 +18,7 @@ import { buildExternalGraph } from 'features/nodes/util/graph/generation/buildEx import { buildFLUXGraph } from 'features/nodes/util/graph/generation/buildFLUXGraph'; import { buildIdeogram4Graph } from 'features/nodes/util/graph/generation/buildIdeogram4Graph'; import { buildKrea2Graph } from 'features/nodes/util/graph/generation/buildKrea2Graph'; +import { buildMiniMaxH3Graph } from 'features/nodes/util/graph/generation/buildMiniMaxH3Graph'; import { buildQwenImageGraph } from 'features/nodes/util/graph/generation/buildQwenImageGraph'; import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph'; import { buildSD3Graph } from 'features/nodes/util/graph/generation/buildSD3Graph'; @@ -78,6 +79,8 @@ const enqueueGenerate = async (store: AppStore, prepend: boolean) => { return await buildAnimaGraph(graphBuilderArg); case 'wan': return await buildWanGraph(graphBuilderArg); + case 'minimax-h3': + return await buildMiniMaxH3Graph(graphBuilderArg); default: assert(false, `No graph builders for base ${base}`); } diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueUpscaling.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueUpscaling.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useEnqueueUpscaling.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueUpscaling.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueWorkflows.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueWorkflows.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useEnqueueWorkflows.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useEnqueueWorkflows.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useInvoke.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useInvoke.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useInvoke.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useInvoke.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useIsQueueMutationInProgress.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useIsQueueMutationInProgress.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useIsQueueMutationInProgress.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useIsQueueMutationInProgress.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts b/invokeai/frontend/webv1/src/features/queue/hooks/usePauseProcessor.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/usePauseProcessor.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts b/invokeai/frontend/webv1/src/features/queue/hooks/usePruneQueue.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/usePruneQueue.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useRangeBasedQueueItemFetching.test.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useRangeBasedQueueItemFetching.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useRangeBasedQueueItemFetching.test.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useRangeBasedQueueItemFetching.test.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useRangeBasedQueueItemFetching.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useRangeBasedQueueItemFetching.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useRangeBasedQueueItemFetching.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useRangeBasedQueueItemFetching.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useResumeProcessor.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useResumeProcessor.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useRetryQueueItem.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useRetryQueueItem.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useRetryQueueItem.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useRetryQueueItem.ts diff --git a/invokeai/frontend/web/src/features/queue/hooks/useUserHasActiveQueueItems.ts b/invokeai/frontend/webv1/src/features/queue/hooks/useUserHasActiveQueueItems.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/hooks/useUserHasActiveQueueItems.ts rename to invokeai/frontend/webv1/src/features/queue/hooks/useUserHasActiveQueueItems.ts diff --git a/invokeai/frontend/web/src/features/queue/store/queueSlice.ts b/invokeai/frontend/webv1/src/features/queue/store/queueSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/store/queueSlice.ts rename to invokeai/frontend/webv1/src/features/queue/store/queueSlice.ts diff --git a/invokeai/frontend/web/src/features/queue/store/readiness.test.ts b/invokeai/frontend/webv1/src/features/queue/store/readiness.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/store/readiness.test.ts rename to invokeai/frontend/webv1/src/features/queue/store/readiness.test.ts diff --git a/invokeai/frontend/web/src/features/queue/store/readiness.ts b/invokeai/frontend/webv1/src/features/queue/store/readiness.ts similarity index 97% rename from invokeai/frontend/web/src/features/queue/store/readiness.ts rename to invokeai/frontend/webv1/src/features/queue/store/readiness.ts index eaf8cde3c9e..dd8fad2ebc3 100644 --- a/invokeai/frontend/web/src/features/queue/store/readiness.ts +++ b/invokeai/frontend/webv1/src/features/queue/store/readiness.ts @@ -599,6 +599,18 @@ export const getReasonsWhyCannotEnqueueGenerateTab = (arg: { } } + if (model?.base === 'minimax-h3' && model.format === 'diffusers' && model.components_only) { + // A slim ("components-only") install ships only the shared components (tokenizer, processor, + // VAEs) - the transformer and text encoder must come from single-file installs selected in + // Advanced settings, or generation fails at model-load time. + if (!params.minimaxH3TransformerModel) { + reasons.push({ content: i18n.t('parameters.invoke.noMiniMaxH3TransformerModelSelected') }); + } + if (!params.minimaxH3TextEncoderModel) { + reasons.push({ content: i18n.t('parameters.invoke.noMiniMaxH3TextEncoderModelSelected') }); + } + } + if (model) { for (const lora of loras.filter(({ isEnabled }) => isEnabled === true)) { if (model.base !== lora.model.base) { @@ -1413,6 +1425,24 @@ export const getReasonsWhyCannotEnqueueCanvasTab = (arg: { } } + if (model?.base === 'minimax-h3' && params.minimaxH3OutputMode === 'video') { + // Video output only exists on the Generate tab - the canvas compositing pipeline is + // image-based. The image output mode works on canvas like any other txt2img model. + reasons.push({ content: i18n.t('parameters.invoke.minimaxH3VideoOnGenerateTab') }); + } + + if (model?.base === 'minimax-h3' && model.format === 'diffusers' && model.components_only) { + // A slim ("components-only") install ships only the shared components (tokenizer, processor, + // VAEs) - the transformer and text encoder must come from single-file installs selected in + // Advanced settings, or generation fails at model-load time. + if (!params.minimaxH3TransformerModel) { + reasons.push({ content: i18n.t('parameters.invoke.noMiniMaxH3TransformerModelSelected') }); + } + if (!params.minimaxH3TextEncoderModel) { + reasons.push({ content: i18n.t('parameters.invoke.noMiniMaxH3TextEncoderModelSelected') }); + } + } + if (model) { for (const lora of loras.filter(({ isEnabled }) => isEnabled === true)) { if (model.base !== lora.model.base) { diff --git a/invokeai/frontend/web/src/features/queue/store/userScopedQueueCounts.ts b/invokeai/frontend/webv1/src/features/queue/store/userScopedQueueCounts.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/store/userScopedQueueCounts.ts rename to invokeai/frontend/webv1/src/features/queue/store/userScopedQueueCounts.ts diff --git a/invokeai/frontend/web/src/features/queue/util/getSecondsFromTimestamps.ts b/invokeai/frontend/webv1/src/features/queue/util/getSecondsFromTimestamps.ts similarity index 100% rename from invokeai/frontend/web/src/features/queue/util/getSecondsFromTimestamps.ts rename to invokeai/frontend/webv1/src/features/queue/util/getSecondsFromTimestamps.ts diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale.tsx b/invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale.tsx similarity index 100% rename from invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale.tsx rename to invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerCFGScale.tsx diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx b/invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx rename to invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore.tsx b/invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore.tsx similarity index 100% rename from invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore.tsx rename to invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerNegativeAestheticScore.tsx diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerPositiveAestheticScore.tsx b/invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerPositiveAestheticScore.tsx similarity index 100% rename from invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerPositiveAestheticScore.tsx rename to invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerPositiveAestheticScore.tsx diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerScheduler.tsx b/invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerScheduler.tsx similarity index 100% rename from invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerScheduler.tsx rename to invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerScheduler.tsx diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerStart.tsx b/invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerStart.tsx similarity index 100% rename from invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerStart.tsx rename to invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerStart.tsx diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerSteps.tsx b/invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerSteps.tsx similarity index 100% rename from invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerSteps.tsx rename to invokeai/frontend/webv1/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerSteps.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.stories.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.stories.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.stories.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.stories.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx similarity index 96% rename from invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx index 374822d4058..689c8d8da04 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx +++ b/invokeai/frontend/webv1/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx @@ -12,6 +12,7 @@ import { selectIsFlux2Dev, selectIsIdeogram4, selectIsKrea2, + selectIsMiniMaxH3, selectIsQwenImage, selectIsSD3, selectIsWan, @@ -35,6 +36,7 @@ import { ParamHiDiffusionWindowAttnToggle, } from 'features/parameters/components/Advanced/ParamHiDiffusionToggle'; import ParamKrea2ModelSelects from 'features/parameters/components/Advanced/ParamKrea2ModelSelects'; +import ParamMiniMaxH3ModelSelects from 'features/parameters/components/Advanced/ParamMiniMaxH3ModelSelects'; import ParamQwenImageComponentSourceSelect from 'features/parameters/components/Advanced/ParamQwenImageComponentSourceSelect'; import ParamQwenImageQuantization from 'features/parameters/components/Advanced/ParamQwenImageQuantization'; import ParamT5EncoderModelSelect from 'features/parameters/components/Advanced/ParamT5EncoderModelSelect'; @@ -79,6 +81,7 @@ export const AdvancedSettingsAccordion = memo(() => { const isErnieImage = useAppSelector(selectIsErnieImage); const isKrea2 = useAppSelector(selectIsKrea2); const isWan = useAppSelector(selectIsWan); + const isMiniMaxH3 = useAppSelector(selectIsMiniMaxH3); const selectBadges = useMemo( () => @@ -231,6 +234,11 @@ export const AdvancedSettingsAccordion = memo(() => { )} + {isMiniMaxH3 && ( + + + + )} {isIdeogram4 && ( <> diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/UpscaleTabAdvancedSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/AdvancedSettingsAccordion/UpscaleTabAdvancedSettingsAccordion.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/UpscaleTabAdvancedSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/AdvancedSettingsAccordion/UpscaleTabAdvancedSettingsAccordion.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion.stories.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion.stories.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion.stories.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion.stories.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/CompositingSettingsAccordion/CompositingSettingsAccordion.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/ExternalSettingsAccordion/ExternalSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/ExternalSettingsAccordion/ExternalSettingsAccordion.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/ExternalSettingsAccordion/ExternalSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/ExternalSettingsAccordion/ExternalSettingsAccordion.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.stories.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.stories.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.stories.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.stories.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx similarity index 94% rename from invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx index 6693b97ee06..2fa325ff9e0 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx +++ b/invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx @@ -15,6 +15,7 @@ import { selectIsFlux2Dev, selectIsIdeogram4, selectIsKrea2, + selectIsMiniMaxH3, selectIsQwenImage, selectIsWan, selectIsZImage, @@ -33,6 +34,8 @@ import ParamFluxDypeScale from 'features/parameters/components/Core/ParamFluxDyp import ParamFluxScheduler from 'features/parameters/components/Core/ParamFluxScheduler'; import ParamGuidance from 'features/parameters/components/Core/ParamGuidance'; import ParamIdeogram4SamplerPreset from 'features/parameters/components/Core/ParamIdeogram4SamplerPreset'; +import ParamMiniMaxH3Duration from 'features/parameters/components/Core/ParamMiniMaxH3Duration'; +import ParamMiniMaxH3OutputMode from 'features/parameters/components/Core/ParamMiniMaxH3OutputMode'; import ParamQwenImageShift from 'features/parameters/components/Core/ParamQwenImageShift'; import ParamScheduler from 'features/parameters/components/Core/ParamScheduler'; import ParamSteps from 'features/parameters/components/Core/ParamSteps'; @@ -71,6 +74,7 @@ export const GenerationSettingsAccordion = memo(() => { const isKrea2 = useAppSelector(selectIsKrea2); const isAnima = useAppSelector(selectIsAnima); const isWan = useAppSelector(selectIsWan); + const isMiniMaxH3 = useAppSelector(selectIsMiniMaxH3); const fluxDypePreset = useAppSelector(selectFluxDypePreset); const modelSupportsGuidance = useAppSelector(selectModelSupportsGuidance); const modelSupportsSteps = useAppSelector(selectModelSupportsSteps); @@ -127,8 +131,10 @@ export const GenerationSettingsAccordion = memo(() => { )} {!isExternal && isFlux2Dev && } - {!isExternal && !isFLUX && !isFlux2 && !isIdeogram4 && } + {!isExternal && !isFLUX && !isFlux2 && !isIdeogram4 && !isMiniMaxH3 && } {!isExternal && isWan && } + {!isExternal && isMiniMaxH3 && } + {!isExternal && isMiniMaxH3 && } {!isExternal && isZImage && } {!isExternal && isQwenImage && } {!isExternal && isFLUX && } diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx similarity index 90% rename from invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx index 7b9e7d72057..459cda7de4d 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx +++ b/invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx @@ -22,10 +22,8 @@ export const MainModelPicker = memo(() => { const dispatch = useAppDispatch(); const activeTab = useAppSelector(selectActiveTab); const [allModelConfigs] = useMainModels(); - // Low-noise Wan single-file experts (GGUF or safetensors checkpoint) belong in - // the Transformer (Low Noise) slot of the Wan advanced section, not as a primary - // main. Shared with the other two places a primary main can be chosen so the - // three cannot disagree about what is offerable. + // Shared with the other primary-main entry points so transformer-only models are + // offered only in their dedicated advanced slots. const modelConfigs = useMemo(() => selectPrimaryMainModelOptions(allModelConfigs), [allModelConfigs]); const selectedModelConfig = useSelectedModelConfig(); const onChange = useCallback( diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/UpscaleTabGenerationSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/UpscaleTabGenerationSettingsAccordion.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/UpscaleTabGenerationSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/UpscaleTabGenerationSettingsAccordion.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.test.ts b/invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.test.ts rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.test.ts diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.ts b/invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.ts similarity index 96% rename from invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.ts rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.ts index 304bf8f79ab..9ccbe304493 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.ts +++ b/invokeai/frontend/webv1/src/features/settingsAccordions/components/GenerationSettingsAccordion/generationSettingsVisibility.ts @@ -13,6 +13,7 @@ const BASES_WITHOUT_STANDARD_SCHEDULER = new Set([ 'krea-2', 'wan', 'ideogram-4', + 'minimax-h3', ]); export const shouldShowStandardScheduler = (base: BaseModelType | null | undefined): boolean => diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/ImageSettingsAccordion/CanvasTabImageSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/ImageSettingsAccordion/CanvasTabImageSettingsAccordion.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/ImageSettingsAccordion/CanvasTabImageSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/ImageSettingsAccordion/CanvasTabImageSettingsAccordion.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/ImageSettingsAccordion/GenerateTabImageSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/ImageSettingsAccordion/GenerateTabImageSettingsAccordion.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/ImageSettingsAccordion/GenerateTabImageSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/ImageSettingsAccordion/GenerateTabImageSettingsAccordion.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.stories.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.stories.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.stories.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.stories.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleInitialImage.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleInitialImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleInitialImage.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleInitialImage.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleScaleSlider.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleScaleSlider.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleScaleSlider.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleScaleSlider.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleWarning.tsx b/invokeai/frontend/webv1/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleWarning.tsx similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleWarning.tsx rename to invokeai/frontend/webv1/src/features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleWarning.tsx diff --git a/invokeai/frontend/web/src/features/settingsAccordions/hooks/useExpanderToggle.ts b/invokeai/frontend/webv1/src/features/settingsAccordions/hooks/useExpanderToggle.ts similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/hooks/useExpanderToggle.ts rename to invokeai/frontend/webv1/src/features/settingsAccordions/hooks/useExpanderToggle.ts diff --git a/invokeai/frontend/web/src/features/settingsAccordions/hooks/useStandaloneAccordionToggle.ts b/invokeai/frontend/webv1/src/features/settingsAccordions/hooks/useStandaloneAccordionToggle.ts similarity index 100% rename from invokeai/frontend/web/src/features/settingsAccordions/hooks/useStandaloneAccordionToggle.ts rename to invokeai/frontend/webv1/src/features/settingsAccordions/hooks/useStandaloneAccordionToggle.ts diff --git a/invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/ActiveStylePreset.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/ActiveStylePreset.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/DeleteStylePresetDialog.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/DeleteStylePresetDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/DeleteStylePresetDialog.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/DeleteStylePresetDialog.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetCreateButton.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetCreateButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetCreateButton.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetCreateButton.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetExportButton.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetExportButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetExportButton.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetExportButton.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetForm.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetForm/StylePresetForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetForm.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetForm/StylePresetForm.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetModal.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetForm/StylePresetModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetModal.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetForm/StylePresetModal.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetPromptField.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetForm/StylePresetPromptField.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetPromptField.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetForm/StylePresetPromptField.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetImage.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetImage.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImportButton.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetImportButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetImportButton.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetImportButton.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetList.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetList.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetList.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetListItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetListItem.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetMenu.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenuTrigger.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetMenuTrigger.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenuTrigger.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetMenuTrigger.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetPromptPreviewToggle.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetPromptPreviewToggle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetPromptPreviewToggle.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetPromptPreviewToggle.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetSearch.tsx b/invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetSearch.tsx similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/components/StylePresetSearch.tsx rename to invokeai/frontend/webv1/src/features/stylePresets/components/StylePresetSearch.tsx diff --git a/invokeai/frontend/web/src/features/stylePresets/hooks/usePresetModifiedPrompts.ts b/invokeai/frontend/webv1/src/features/stylePresets/hooks/usePresetModifiedPrompts.ts similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/hooks/usePresetModifiedPrompts.ts rename to invokeai/frontend/webv1/src/features/stylePresets/hooks/usePresetModifiedPrompts.ts diff --git a/invokeai/frontend/web/src/features/stylePresets/store/stylePresetModal.ts b/invokeai/frontend/webv1/src/features/stylePresets/store/stylePresetModal.ts similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/store/stylePresetModal.ts rename to invokeai/frontend/webv1/src/features/stylePresets/store/stylePresetModal.ts diff --git a/invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts b/invokeai/frontend/webv1/src/features/stylePresets/store/stylePresetSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts rename to invokeai/frontend/webv1/src/features/stylePresets/store/stylePresetSlice.ts diff --git a/invokeai/frontend/web/src/features/stylePresets/util/getViewModeChunks.test.ts b/invokeai/frontend/webv1/src/features/stylePresets/util/getViewModeChunks.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/util/getViewModeChunks.test.ts rename to invokeai/frontend/webv1/src/features/stylePresets/util/getViewModeChunks.test.ts diff --git a/invokeai/frontend/web/src/features/stylePresets/util/getViewModeChunks.ts b/invokeai/frontend/webv1/src/features/stylePresets/util/getViewModeChunks.ts similarity index 100% rename from invokeai/frontend/web/src/features/stylePresets/util/getViewModeChunks.ts rename to invokeai/frontend/webv1/src/features/stylePresets/util/getViewModeChunks.ts diff --git a/invokeai/frontend/web/src/features/system/components/AboutModal/AboutModal.tsx b/invokeai/frontend/webv1/src/features/system/components/AboutModal/AboutModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/AboutModal/AboutModal.tsx rename to invokeai/frontend/webv1/src/features/system/components/AboutModal/AboutModal.tsx diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeyListItem.tsx b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/HotkeyListItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeyListItem.tsx rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/HotkeyListItem.tsx diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeysListWrapper.tsx b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/HotkeysListWrapper.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeysListWrapper.tsx rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/HotkeysListWrapper.tsx diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeysModal.tsx b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/HotkeysModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeysModal.tsx rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/HotkeysModal.tsx diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/hotkeyStrings.test.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/hotkeyStrings.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/hotkeyStrings.test.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/hotkeyStrings.test.ts diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/hotkeyStrings.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/hotkeyStrings.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/hotkeyStrings.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/hotkeyStrings.ts diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/registeredHotkeyOptions.test.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/registeredHotkeyOptions.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/registeredHotkeyOptions.test.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/registeredHotkeyOptions.test.ts diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/registeredHotkeyOptions.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/registeredHotkeyOptions.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/registeredHotkeyOptions.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/registeredHotkeyOptions.ts diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/sharedHotkeyRouting.test.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/sharedHotkeyRouting.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/sharedHotkeyRouting.test.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/sharedHotkeyRouting.test.ts diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/sharedHotkeyRouting.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/sharedHotkeyRouting.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/sharedHotkeyRouting.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/sharedHotkeyRouting.ts diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.test.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/useHotkeyData.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.test.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/useHotkeyData.test.ts diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/useHotkeyData.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/useHotkeyData.ts diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useKeyboardLayoutMap.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/useKeyboardLayoutMap.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/useKeyboardLayoutMap.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/useKeyboardLayoutMap.ts diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useSharedInfoHotkeyTarget.ts b/invokeai/frontend/webv1/src/features/system/components/HotkeysModal/useSharedInfoHotkeyTarget.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/HotkeysModal/useSharedInfoHotkeyTarget.ts rename to invokeai/frontend/webv1/src/features/system/components/HotkeysModal/useSharedInfoHotkeyTarget.ts diff --git a/invokeai/frontend/web/src/features/system/components/InvokeAILogoComponent.tsx b/invokeai/frontend/webv1/src/features/system/components/InvokeAILogoComponent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/InvokeAILogoComponent.tsx rename to invokeai/frontend/webv1/src/features/system/components/InvokeAILogoComponent.tsx diff --git a/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx b/invokeai/frontend/webv1/src/features/system/components/ProgressBar.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/ProgressBar.tsx rename to invokeai/frontend/webv1/src/features/system/components/ProgressBar.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/ExternalProviderStatusList.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/ExternalProviderStatusList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/ExternalProviderStatusList.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/ExternalProviderStatusList.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/RefreshAfterResetModal.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/RefreshAfterResetModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/RefreshAfterResetModal.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/RefreshAfterResetModal.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogLevel.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsDeveloperLogLevel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogLevel.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsDeveloperLogLevel.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogNamespaces.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsDeveloperLogNamespaces.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogNamespaces.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsDeveloperLogNamespaces.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsGenerationDevices.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsGenerationDevices.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsGenerationDevices.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsGenerationDevices.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsImageStorageMaintenance.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsImageStorageMaintenance.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsImageStorageMaintenance.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsImageStorageMaintenance.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsImageSubfolderStrategySelect.test.ts b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsImageSubfolderStrategySelect.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsImageSubfolderStrategySelect.test.ts rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsImageSubfolderStrategySelect.test.ts diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsImageSubfolderStrategySelect.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsImageSubfolderStrategySelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsImageSubfolderStrategySelect.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsImageSubfolderStrategySelect.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsMenu.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsMenu.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/SettingsModal.tsx diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.test.ts b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/externalProviderStatusUtils.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.test.ts rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/externalProviderStatusUtils.test.ts diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.ts b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/externalProviderStatusUtils.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.ts rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/externalProviderStatusUtils.ts diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/imageStorageMaintenanceUtils.test.ts b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/imageStorageMaintenanceUtils.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/imageStorageMaintenanceUtils.test.ts rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/imageStorageMaintenanceUtils.test.ts diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/imageStorageMaintenanceUtils.ts b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/imageStorageMaintenanceUtils.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/imageStorageMaintenanceUtils.ts rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/imageStorageMaintenanceUtils.ts diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts b/invokeai/frontend/webv1/src/features/system/components/SettingsModal/useClearIntermediates.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts rename to invokeai/frontend/webv1/src/features/system/components/SettingsModal/useClearIntermediates.ts diff --git a/invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx b/invokeai/frontend/webv1/src/features/system/components/StatusIndicator.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx rename to invokeai/frontend/webv1/src/features/system/components/StatusIndicator.tsx diff --git a/invokeai/frontend/web/src/features/system/components/StickyScrollable.tsx b/invokeai/frontend/webv1/src/features/system/components/StickyScrollable.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/StickyScrollable.tsx rename to invokeai/frontend/webv1/src/features/system/components/StickyScrollable.tsx diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/VideoCard.tsx b/invokeai/frontend/webv1/src/features/system/components/VideosModal/VideoCard.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/VideosModal/VideoCard.tsx rename to invokeai/frontend/webv1/src/features/system/components/VideosModal/VideoCard.tsx diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/VideoCardList.tsx b/invokeai/frontend/webv1/src/features/system/components/VideosModal/VideoCardList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/VideosModal/VideoCardList.tsx rename to invokeai/frontend/webv1/src/features/system/components/VideosModal/VideoCardList.tsx diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/VideosModal.tsx b/invokeai/frontend/webv1/src/features/system/components/VideosModal/VideosModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/VideosModal/VideosModal.tsx rename to invokeai/frontend/webv1/src/features/system/components/VideosModal/VideosModal.tsx diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/VideosModalButton.tsx b/invokeai/frontend/webv1/src/features/system/components/VideosModal/VideosModalButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/system/components/VideosModal/VideosModalButton.tsx rename to invokeai/frontend/webv1/src/features/system/components/VideosModal/VideosModalButton.tsx diff --git a/invokeai/frontend/web/src/features/system/components/VideosModal/data.ts b/invokeai/frontend/webv1/src/features/system/components/VideosModal/data.ts similarity index 95% rename from invokeai/frontend/web/src/features/system/components/VideosModal/data.ts rename to invokeai/frontend/webv1/src/features/system/components/VideosModal/data.ts index a9c478fc41a..ead68497a71 100644 --- a/invokeai/frontend/web/src/features/system/components/VideosModal/data.ts +++ b/invokeai/frontend/webv1/src/features/system/components/VideosModal/data.ts @@ -1,7 +1,7 @@ /** * To add a support video, you'll need to add the video to the list below. * - * The `tKey` is a sub-key in the translation file `invokeai/frontend/web/public/locales/en.json`. + * The `tKey` is a sub-key in the translation file `invokeai/frontend/webv1/public/locales/en.json`. * Add the title and description under `supportVideos.videos`, following the existing format. */ diff --git a/invokeai/frontend/web/src/features/system/store/constants.ts b/invokeai/frontend/webv1/src/features/system/store/constants.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/store/constants.ts rename to invokeai/frontend/webv1/src/features/system/store/constants.ts diff --git a/invokeai/frontend/web/src/features/system/store/hotkeysSlice.test.ts b/invokeai/frontend/webv1/src/features/system/store/hotkeysSlice.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/store/hotkeysSlice.test.ts rename to invokeai/frontend/webv1/src/features/system/store/hotkeysSlice.test.ts diff --git a/invokeai/frontend/web/src/features/system/store/hotkeysSlice.ts b/invokeai/frontend/webv1/src/features/system/store/hotkeysSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/store/hotkeysSlice.ts rename to invokeai/frontend/webv1/src/features/system/store/hotkeysSlice.ts diff --git a/invokeai/frontend/web/src/features/system/store/hotkeysTypes.ts b/invokeai/frontend/webv1/src/features/system/store/hotkeysTypes.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/store/hotkeysTypes.ts rename to invokeai/frontend/webv1/src/features/system/store/hotkeysTypes.ts diff --git a/invokeai/frontend/web/src/features/system/store/systemSelectors.ts b/invokeai/frontend/webv1/src/features/system/store/systemSelectors.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/store/systemSelectors.ts rename to invokeai/frontend/webv1/src/features/system/store/systemSelectors.ts diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/webv1/src/features/system/store/systemSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/store/systemSlice.ts rename to invokeai/frontend/webv1/src/features/system/store/systemSlice.ts diff --git a/invokeai/frontend/web/src/features/system/store/types.ts b/invokeai/frontend/webv1/src/features/system/store/types.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/store/types.ts rename to invokeai/frontend/webv1/src/features/system/store/types.ts diff --git a/invokeai/frontend/web/src/features/system/store/zodSchemas.ts b/invokeai/frontend/webv1/src/features/system/store/zodSchemas.ts similarity index 100% rename from invokeai/frontend/web/src/features/system/store/zodSchemas.ts rename to invokeai/frontend/webv1/src/features/system/store/zodSchemas.ts diff --git a/invokeai/frontend/web/src/features/systemPrompts/components/DeleteSystemPromptDialog.tsx b/invokeai/frontend/webv1/src/features/systemPrompts/components/DeleteSystemPromptDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/systemPrompts/components/DeleteSystemPromptDialog.tsx rename to invokeai/frontend/webv1/src/features/systemPrompts/components/DeleteSystemPromptDialog.tsx diff --git a/invokeai/frontend/web/src/features/systemPrompts/components/SystemPromptForm.tsx b/invokeai/frontend/webv1/src/features/systemPrompts/components/SystemPromptForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/systemPrompts/components/SystemPromptForm.tsx rename to invokeai/frontend/webv1/src/features/systemPrompts/components/SystemPromptForm.tsx diff --git a/invokeai/frontend/web/src/features/systemPrompts/components/SystemPromptListItem.tsx b/invokeai/frontend/webv1/src/features/systemPrompts/components/SystemPromptListItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/systemPrompts/components/SystemPromptListItem.tsx rename to invokeai/frontend/webv1/src/features/systemPrompts/components/SystemPromptListItem.tsx diff --git a/invokeai/frontend/web/src/features/systemPrompts/components/SystemPromptsModal.tsx b/invokeai/frontend/webv1/src/features/systemPrompts/components/SystemPromptsModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/systemPrompts/components/SystemPromptsModal.tsx rename to invokeai/frontend/webv1/src/features/systemPrompts/components/SystemPromptsModal.tsx diff --git a/invokeai/frontend/web/src/features/systemPrompts/hooks/useCanEditSystemPrompt.ts b/invokeai/frontend/webv1/src/features/systemPrompts/hooks/useCanEditSystemPrompt.ts similarity index 100% rename from invokeai/frontend/web/src/features/systemPrompts/hooks/useCanEditSystemPrompt.ts rename to invokeai/frontend/webv1/src/features/systemPrompts/hooks/useCanEditSystemPrompt.ts diff --git a/invokeai/frontend/web/src/features/systemPrompts/store/systemPromptModal.ts b/invokeai/frontend/webv1/src/features/systemPrompts/store/systemPromptModal.ts similarity index 100% rename from invokeai/frontend/web/src/features/systemPrompts/store/systemPromptModal.ts rename to invokeai/frontend/webv1/src/features/systemPrompts/store/systemPromptModal.ts diff --git a/invokeai/frontend/web/src/features/toast/ErrorToastDescription.tsx b/invokeai/frontend/webv1/src/features/toast/ErrorToastDescription.tsx similarity index 100% rename from invokeai/frontend/web/src/features/toast/ErrorToastDescription.tsx rename to invokeai/frontend/webv1/src/features/toast/ErrorToastDescription.tsx diff --git a/invokeai/frontend/web/src/features/toast/toast.ts b/invokeai/frontend/webv1/src/features/toast/toast.ts similarity index 100% rename from invokeai/frontend/web/src/features/toast/toast.ts rename to invokeai/frontend/webv1/src/features/toast/toast.ts diff --git a/invokeai/frontend/webv1/src/features/ui/README.md b/invokeai/frontend/webv1/src/features/ui/README.md new file mode 100644 index 00000000000..b9282dc9b82 --- /dev/null +++ b/invokeai/frontend/webv1/src/features/ui/README.md @@ -0,0 +1,26 @@ +# UI/Layout + +We use https://github.com/mathuo/dockview for layout. This library supports resizable and dockable panels. Users can drag and drop panels to rearrange them. + +The intention when adopting this library was to allow users to create their own custom layouts and save them. However, this feature is not yet implemented and each tab only has a predefined layout. + +This works well, but it _is_ fairly complex. You can see that we've needed to write a fairly involved API to manage the layouts: invokeai/frontend/webv1/src/features/ui/layouts/navigation-api.ts + +And the layouts themselves are awkward to define, especially when compared to plain JSX: invokeai/frontend/webv1/src/features/ui/layouts/generate-tab-auto-layout.tsx + +This complexity may or may not be worth it. + +## Previous approach + +Previously we used https://github.com/bvaughn/react-resizable-panels and simple JSX components. + +This library is great except it doesn't support absolute size constraints, only relative/percentage constraints. We had a brittle abstraction layer on top of it to try to enforce minimum pixel sizes for panels but it was janky and had FP precision issues causing drifting sizes. + +It also doesn't support dockable panels. + +## Future possibilities + +1. Continue with dockview and implement custom layout saving/loading. We experimented with this and it was _really_ nice. We defined a component for each panel type and use react context to manage state. But we thought that it would be confusing for most users, so we flagged it for a future iteration and instead shipped with predefined layouts. +2. Switch to a simpler layout library or roll our own. + +In hindsight, we should have skipped dockview and found something else that was simpler until we were ready to invest in custom layouts. diff --git a/invokeai/frontend/web/src/features/ui/components/AppContent.tsx b/invokeai/frontend/webv1/src/features/ui/components/AppContent.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/AppContent.tsx rename to invokeai/frontend/webv1/src/features/ui/components/AppContent.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingLeftPanelButtons.tsx b/invokeai/frontend/webv1/src/features/ui/components/FloatingLeftPanelButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/FloatingLeftPanelButtons.tsx rename to invokeai/frontend/webv1/src/features/ui/components/FloatingLeftPanelButtons.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingRightPanelButtons.tsx b/invokeai/frontend/webv1/src/features/ui/components/FloatingRightPanelButtons.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/FloatingRightPanelButtons.tsx rename to invokeai/frontend/webv1/src/features/ui/components/FloatingRightPanelButtons.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/Notifications.tsx b/invokeai/frontend/webv1/src/features/ui/components/Notifications.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/Notifications.tsx rename to invokeai/frontend/webv1/src/features/ui/components/Notifications.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelCanvas.tsx b/invokeai/frontend/webv1/src/features/ui/components/ParametersPanels/ParametersPanelCanvas.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelCanvas.tsx rename to invokeai/frontend/webv1/src/features/ui/components/ParametersPanels/ParametersPanelCanvas.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelGenerate.tsx b/invokeai/frontend/webv1/src/features/ui/components/ParametersPanels/ParametersPanelGenerate.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelGenerate.tsx rename to invokeai/frontend/webv1/src/features/ui/components/ParametersPanels/ParametersPanelGenerate.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelUpscale.tsx b/invokeai/frontend/webv1/src/features/ui/components/ParametersPanels/ParametersPanelUpscale.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelUpscale.tsx rename to invokeai/frontend/webv1/src/features/ui/components/ParametersPanels/ParametersPanelUpscale.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/TabButton.tsx b/invokeai/frontend/webv1/src/features/ui/components/TabButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/TabButton.tsx rename to invokeai/frontend/webv1/src/features/ui/components/TabButton.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/VerticalNavBar.tsx b/invokeai/frontend/webv1/src/features/ui/components/VerticalNavBar.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/VerticalNavBar.tsx rename to invokeai/frontend/webv1/src/features/ui/components/VerticalNavBar.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/WhatsNew.tsx b/invokeai/frontend/webv1/src/features/ui/components/WhatsNew.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/WhatsNew.tsx rename to invokeai/frontend/webv1/src/features/ui/components/WhatsNew.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/CustomNodesManagerTab.tsx b/invokeai/frontend/webv1/src/features/ui/components/tabs/CustomNodesManagerTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/CustomNodesManagerTab.tsx rename to invokeai/frontend/webv1/src/features/ui/components/tabs/CustomNodesManagerTab.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx b/invokeai/frontend/webv1/src/features/ui/components/tabs/ModelManagerTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx rename to invokeai/frontend/webv1/src/features/ui/components/tabs/ModelManagerTab.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/QueueTab.tsx b/invokeai/frontend/webv1/src/features/ui/components/tabs/QueueTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/QueueTab.tsx rename to invokeai/frontend/webv1/src/features/ui/components/tabs/QueueTab.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ResizeHandle.tsx b/invokeai/frontend/webv1/src/features/ui/components/tabs/ResizeHandle.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ResizeHandle.tsx rename to invokeai/frontend/webv1/src/features/ui/components/tabs/ResizeHandle.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/AutoLayoutPanelContainer.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/AutoLayoutPanelContainer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/AutoLayoutPanelContainer.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/AutoLayoutPanelContainer.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/CanvasLaunchpadPanel.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/CanvasLaunchpadPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/CanvasLaunchpadPanel.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/CanvasLaunchpadPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/CanvasTabLeftPanel.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/CanvasTabLeftPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/CanvasTabLeftPanel.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/CanvasTabLeftPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/CanvasWorkspacePanel.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/CanvasWorkspacePanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/CanvasWorkspacePanel.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/CanvasWorkspacePanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/DockviewCanvasHeaderActions.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/DockviewCanvasHeaderActions.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/DockviewCanvasHeaderActions.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/DockviewCanvasHeaderActions.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/DockviewTab.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/DockviewTab.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/DockviewTab.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/DockviewTab.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/DockviewTabCanvasViewer.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/DockviewTabCanvasViewer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/DockviewTabCanvasViewer.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/DockviewTabCanvasViewer.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/DockviewTabCanvasWorkspace.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/DockviewTabCanvasWorkspace.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/DockviewTabCanvasWorkspace.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/DockviewTabCanvasWorkspace.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/DockviewTabLaunchpad.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/DockviewTabLaunchpad.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/DockviewTabLaunchpad.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/DockviewTabLaunchpad.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/DockviewTabProgress.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/DockviewTabProgress.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/DockviewTabProgress.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/DockviewTabProgress.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/GenerateLaunchpadPanel.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/GenerateLaunchpadPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/GenerateLaunchpadPanel.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/GenerateLaunchpadPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/GenerateTabLeftPanel.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/GenerateTabLeftPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/GenerateTabLeftPanel.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/GenerateTabLeftPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/InitialStateMainModelPicker.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/InitialStateMainModelPicker.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/LaunchpadAddStyleReference.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadAddStyleReference.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/LaunchpadAddStyleReference.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadAddStyleReference.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/LaunchpadButton.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/LaunchpadButton.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadButton.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/LaunchpadContainer.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadContainer.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/LaunchpadContainer.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadContainer.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/LaunchpadEditImageButton.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadEditImageButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/LaunchpadEditImageButton.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadEditImageButton.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/LaunchpadGenerateFromTextButton.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadGenerateFromTextButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/LaunchpadGenerateFromTextButton.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadGenerateFromTextButton.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/LaunchpadUseALayoutImageButton.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadUseALayoutImageButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/LaunchpadUseALayoutImageButton.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/LaunchpadUseALayoutImageButton.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/PanelHotkeysLogical.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/PanelHotkeysLogical.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/PanelHotkeysLogical.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/PanelHotkeysLogical.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/StagingArea.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/StagingArea.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/StagingArea.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/StagingArea.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/UpscalingLaunchpadPanel.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/UpscalingLaunchpadPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/UpscalingLaunchpadPanel.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/UpscalingLaunchpadPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/UpscalingTabLeftPanel.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/UpscalingTabLeftPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/UpscalingTabLeftPanel.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/UpscalingTabLeftPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/WorkflowsLaunchpadPanel.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/WorkflowsLaunchpadPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/WorkflowsLaunchpadPanel.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/WorkflowsLaunchpadPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/auto-layout-context.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/auto-layout-context.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/auto-layout-context.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/auto-layout-context.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/canvas-tab-auto-layout.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/canvas-tab-auto-layout.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/canvas-tab-auto-layout.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/canvas-tab-auto-layout.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/canvasToolModifierHints.test.ts b/invokeai/frontend/webv1/src/features/ui/layouts/canvasToolModifierHints.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/canvasToolModifierHints.test.ts rename to invokeai/frontend/webv1/src/features/ui/layouts/canvasToolModifierHints.test.ts diff --git a/invokeai/frontend/web/src/features/ui/layouts/canvasToolModifierHints.ts b/invokeai/frontend/webv1/src/features/ui/layouts/canvasToolModifierHints.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/canvasToolModifierHints.ts rename to invokeai/frontend/webv1/src/features/ui/layouts/canvasToolModifierHints.ts diff --git a/invokeai/frontend/web/src/features/ui/layouts/customnodes-tab-auto-layout.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/customnodes-tab-auto-layout.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/customnodes-tab-auto-layout.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/customnodes-tab-auto-layout.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/generate-tab-auto-layout.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/generate-tab-auto-layout.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/generate-tab-auto-layout.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/generate-tab-auto-layout.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/models-tab-auto-layout.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/models-tab-auto-layout.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/models-tab-auto-layout.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/models-tab-auto-layout.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/navigation-api.test.ts b/invokeai/frontend/webv1/src/features/ui/layouts/navigation-api.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/navigation-api.test.ts rename to invokeai/frontend/webv1/src/features/ui/layouts/navigation-api.test.ts diff --git a/invokeai/frontend/web/src/features/ui/layouts/navigation-api.ts b/invokeai/frontend/webv1/src/features/ui/layouts/navigation-api.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/navigation-api.ts rename to invokeai/frontend/webv1/src/features/ui/layouts/navigation-api.ts diff --git a/invokeai/frontend/web/src/features/ui/layouts/queue-tab-auto-layout.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/queue-tab-auto-layout.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/queue-tab-auto-layout.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/queue-tab-auto-layout.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/shared.ts b/invokeai/frontend/webv1/src/features/ui/layouts/shared.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/shared.ts rename to invokeai/frontend/webv1/src/features/ui/layouts/shared.ts diff --git a/invokeai/frontend/web/src/features/ui/layouts/upscaling-tab-auto-layout.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/upscaling-tab-auto-layout.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/upscaling-tab-auto-layout.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/upscaling-tab-auto-layout.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/use-collapsible-gridview-panel.ts b/invokeai/frontend/webv1/src/features/ui/layouts/use-collapsible-gridview-panel.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/use-collapsible-gridview-panel.ts rename to invokeai/frontend/webv1/src/features/ui/layouts/use-collapsible-gridview-panel.ts diff --git a/invokeai/frontend/web/src/features/ui/layouts/use-gallery-panel.ts b/invokeai/frontend/webv1/src/features/ui/layouts/use-gallery-panel.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/use-gallery-panel.ts rename to invokeai/frontend/webv1/src/features/ui/layouts/use-gallery-panel.ts diff --git a/invokeai/frontend/web/src/features/ui/layouts/use-navigation-api.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/use-navigation-api.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/use-navigation-api.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/use-navigation-api.tsx diff --git a/invokeai/frontend/web/src/features/ui/layouts/workflows-tab-auto-layout.tsx b/invokeai/frontend/webv1/src/features/ui/layouts/workflows-tab-auto-layout.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/layouts/workflows-tab-auto-layout.tsx rename to invokeai/frontend/webv1/src/features/ui/layouts/workflows-tab-auto-layout.tsx diff --git a/invokeai/frontend/web/src/features/ui/store/uiSelectors.ts b/invokeai/frontend/webv1/src/features/ui/store/uiSelectors.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/store/uiSelectors.ts rename to invokeai/frontend/webv1/src/features/ui/store/uiSelectors.ts diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/webv1/src/features/ui/store/uiSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/store/uiSlice.ts rename to invokeai/frontend/webv1/src/features/ui/store/uiSlice.ts diff --git a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts b/invokeai/frontend/webv1/src/features/ui/store/uiTypes.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/store/uiTypes.ts rename to invokeai/frontend/webv1/src/features/ui/store/uiTypes.ts diff --git a/invokeai/frontend/web/src/features/ui/styles/dockview-theme-invoke.css b/invokeai/frontend/webv1/src/features/ui/styles/dockview-theme-invoke.css similarity index 100% rename from invokeai/frontend/web/src/features/ui/styles/dockview-theme-invoke.css rename to invokeai/frontend/webv1/src/features/ui/styles/dockview-theme-invoke.css diff --git a/invokeai/frontend/web/src/features/ui/styles/theme.ts b/invokeai/frontend/webv1/src/features/ui/styles/theme.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/styles/theme.ts rename to invokeai/frontend/webv1/src/features/ui/styles/theme.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/DeleteLibraryWorkflowConfirmationAlertDialog.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/DeleteLibraryWorkflowConfirmationAlertDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/DeleteLibraryWorkflowConfirmationAlertDialog.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/DeleteLibraryWorkflowConfirmationAlertDialog.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowButton.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/NewWorkflowButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowButton.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/NewWorkflowButton.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/SaveWorkflowAsDialog.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/SaveWorkflowAsDialog.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/SaveWorkflowAsDialog.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/SaveWorkflowAsDialog.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/UploadWorkflowButton.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/UploadWorkflowButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/UploadWorkflowButton.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/UploadWorkflowButton.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/DownloadWorkflowMenuItem.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/DownloadWorkflowMenuItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/DownloadWorkflowMenuItem.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/DownloadWorkflowMenuItem.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/LoadWorkflowFromGraphMenuItem.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/LoadWorkflowFromGraphMenuItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/LoadWorkflowFromGraphMenuItem.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/LoadWorkflowFromGraphMenuItem.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowMenuItem.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowMenuItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowMenuItem.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowMenuItem.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/UploadWorkflowMenuItem.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/UploadWorkflowMenuItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/UploadWorkflowMenuItem.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/UploadWorkflowMenuItem.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useCreateNewWorkflow.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useCreateNewWorkflow.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useCreateNewWorkflow.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useCreateNewWorkflow.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDownloadCurrentlyLoadedWorkflow.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useDownloadCurrentlyLoadedWorkflow.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useDownloadCurrentlyLoadedWorkflow.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useDownloadCurrentlyLoadedWorkflow.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDownloadWorkflowById.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useDownloadWorkflowById.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useDownloadWorkflowById.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useDownloadWorkflowById.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetFormInitialValues.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useGetFormInitialValues.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetFormInitialValues.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useGetFormInitialValues.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useIsCurrentWorkflowOwner.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useIsCurrentWorkflowOwner.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useIsCurrentWorkflowOwner.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useIsCurrentWorkflowOwner.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromImage.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromImage.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromImage.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromImage.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromLibrary.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromLibrary.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromLibrary.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromLibrary.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromObject.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromObject.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromObject.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromObject.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromVideo.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromVideo.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromVideo.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useLoadWorkflowFromVideo.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useSaveLibraryWorkflow.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useSaveLibraryWorkflow.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useSaveLibraryWorkflow.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useSaveLibraryWorkflow.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useSaveOrSaveAsWorkflow.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useSaveOrSaveAsWorkflow.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useSaveOrSaveAsWorkflow.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useSaveOrSaveAsWorkflow.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useValidateAndLoadWorkflow.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useValidateAndLoadWorkflow.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/hooks/useValidateAndLoadWorkflow.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/hooks/useValidateAndLoadWorkflow.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/store/actions.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/store/actions.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/store/actions.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/store/actions.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/util/workflowCallCompatibility.test.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/util/workflowCallCompatibility.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/util/workflowCallCompatibility.test.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/util/workflowCallCompatibility.test.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/util/workflowCallCompatibility.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/util/workflowCallCompatibility.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/util/workflowCallCompatibility.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/util/workflowCallCompatibility.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/util/workflowLibraryListItemState.test.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/util/workflowLibraryListItemState.test.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/util/workflowLibraryListItemState.test.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/util/workflowLibraryListItemState.test.ts diff --git a/invokeai/frontend/web/src/features/workflowLibrary/util/workflowLibraryListItemState.ts b/invokeai/frontend/webv1/src/features/workflowLibrary/util/workflowLibraryListItemState.ts similarity index 100% rename from invokeai/frontend/web/src/features/workflowLibrary/util/workflowLibraryListItemState.ts rename to invokeai/frontend/webv1/src/features/workflowLibrary/util/workflowLibraryListItemState.ts diff --git a/invokeai/frontend/web/src/i18.d.ts b/invokeai/frontend/webv1/src/i18.d.ts similarity index 100% rename from invokeai/frontend/web/src/i18.d.ts rename to invokeai/frontend/webv1/src/i18.d.ts diff --git a/invokeai/frontend/web/src/i18n.ts b/invokeai/frontend/webv1/src/i18n.ts similarity index 100% rename from invokeai/frontend/web/src/i18n.ts rename to invokeai/frontend/webv1/src/i18n.ts diff --git a/invokeai/frontend/webv1/src/main.tsx b/invokeai/frontend/webv1/src/main.tsx new file mode 100644 index 00000000000..b4fd0ed7cef --- /dev/null +++ b/invokeai/frontend/webv1/src/main.tsx @@ -0,0 +1,23 @@ +import ReactDOM from 'react-dom/client'; + +import { getDeploymentBaseUrl } from './common/util/baseUrl'; +import { retireDefaultServiceWorker } from './common/util/retireDefaultServiceWorker'; + +const boot = async () => { + if (await retireDefaultServiceWorker(getDeploymentBaseUrl())) { + return; + } + const { default: InvokeAIUI } = await import('./app/components/InvokeAIUI'); + ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(); +}; + +void boot().catch(() => { + const root = document.getElementById('root'); + if (root) { + root.textContent = 'Unable to start the legacy frontend. Reload to retry.'; + const retry = document.createElement('button'); + retry.textContent = 'Reload'; + retry.addEventListener('click', () => window.location.reload()); + root.appendChild(retry); + } +}); diff --git a/invokeai/frontend/webv1/src/services/api/README.md b/invokeai/frontend/webv1/src/services/api/README.md new file mode 100644 index 00000000000..817eb2d85bc --- /dev/null +++ b/invokeai/frontend/webv1/src/services/api/README.md @@ -0,0 +1,11 @@ +# API + +The API client is a fairly standard Redux Toolkit Query (RTK-Query) setup. + +It defines a simple base query with special handling for OpenAPI schema queries and endpoints: invokeai/frontend/webv1/src/services/api/index.ts + +## Types + +The shared `invokeai/frontend/api` package owns the generated `openapi.json`, `schema.ts`, and tooling. This package's `src/services/api/schema.ts` keeps the existing `services/api/schema` import path as a type-only re-export. + +From the repository root in its activated Python environment, run `make frontend-api-install`, `make frontend-openapi`, and `make frontend-typegen`. See `invokeai/frontend/api/README.md` for ownership and generation details. Contract CI runs in that shared package; frontend CI runs webv2. diff --git a/invokeai/frontend/web/src/services/api/endpoints/appInfo.ts b/invokeai/frontend/webv1/src/services/api/endpoints/appInfo.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/appInfo.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/appInfo.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/auth.test.ts b/invokeai/frontend/webv1/src/services/api/endpoints/auth.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/auth.test.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/auth.test.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/auth.ts b/invokeai/frontend/webv1/src/services/api/endpoints/auth.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/auth.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/auth.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/boards.ts b/invokeai/frontend/webv1/src/services/api/endpoints/boards.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/boards.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/boards.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/clientState.ts b/invokeai/frontend/webv1/src/services/api/endpoints/clientState.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/clientState.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/clientState.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/customNodes.ts b/invokeai/frontend/webv1/src/services/api/endpoints/customNodes.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/customNodes.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/customNodes.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/gallery.ts b/invokeai/frontend/webv1/src/services/api/endpoints/gallery.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/gallery.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/gallery.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/imageMoves.ts b/invokeai/frontend/webv1/src/services/api/endpoints/imageMoves.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/imageMoves.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/imageMoves.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.test.ts b/invokeai/frontend/webv1/src/services/api/endpoints/images.test.ts similarity index 99% rename from invokeai/frontend/web/src/services/api/endpoints/images.test.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/images.test.ts index 3bb3efd3f21..3e9b557275c 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/images.test.ts +++ b/invokeai/frontend/webv1/src/services/api/endpoints/images.test.ts @@ -32,11 +32,11 @@ describe('IMAGE_BATCH_CHUNK_SIZE', () => { it('matches the bound the server actually publishes', () => { // The client-side chunk size exists only to satisfy the server-side cap, and the two are // declared in different languages. openapi.json is the committed contract between them - // (typegen-checks keeps it in sync with the routers), so drift on either side fails here + // (openapi-checks keeps it in sync with the routers), so drift on either side fails here // rather than at runtime as a batch of 422s. type ArraySchema = { maxItems?: number; anyOf?: ArraySchema[] }; const openapi = JSON.parse( - readFileSync(fileURLToPath(new URL('../../../../openapi.json', import.meta.url)), 'utf8') + readFileSync(fileURLToPath(new URL('../../../../../api/openapi.json', import.meta.url)), 'utf8') ) as { components: { schemas: Record }; }; diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/webv1/src/services/api/endpoints/images.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/images.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/images.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/modelRelationships.ts b/invokeai/frontend/webv1/src/services/api/endpoints/modelRelationships.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/modelRelationships.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/modelRelationships.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/models.ts b/invokeai/frontend/webv1/src/services/api/endpoints/models.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/models.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/models.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/queue.ts b/invokeai/frontend/webv1/src/services/api/endpoints/queue.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/queue.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/queue.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/stylePresets.ts b/invokeai/frontend/webv1/src/services/api/endpoints/stylePresets.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/stylePresets.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/stylePresets.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/systemPrompts.ts b/invokeai/frontend/webv1/src/services/api/endpoints/systemPrompts.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/systemPrompts.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/systemPrompts.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/utilities.ts b/invokeai/frontend/webv1/src/services/api/endpoints/utilities.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/utilities.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/utilities.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/videos.ts b/invokeai/frontend/webv1/src/services/api/endpoints/videos.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/videos.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/videos.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/virtual_boards.ts b/invokeai/frontend/webv1/src/services/api/endpoints/virtual_boards.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/virtual_boards.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/virtual_boards.ts diff --git a/invokeai/frontend/web/src/services/api/endpoints/workflows.ts b/invokeai/frontend/webv1/src/services/api/endpoints/workflows.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/endpoints/workflows.ts rename to invokeai/frontend/webv1/src/services/api/endpoints/workflows.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/accessChecks.ts b/invokeai/frontend/webv1/src/services/api/hooks/accessChecks.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/accessChecks.ts rename to invokeai/frontend/webv1/src/services/api/hooks/accessChecks.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/modelsByType.test.ts b/invokeai/frontend/webv1/src/services/api/hooks/modelsByType.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/modelsByType.test.ts rename to invokeai/frontend/webv1/src/services/api/hooks/modelsByType.test.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts b/invokeai/frontend/webv1/src/services/api/hooks/modelsByType.ts similarity index 94% rename from invokeai/frontend/web/src/services/api/hooks/modelsByType.ts rename to invokeai/frontend/webv1/src/services/api/hooks/modelsByType.ts index f84fa32d385..5ea03073def 100644 --- a/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts +++ b/invokeai/frontend/webv1/src/services/api/hooks/modelsByType.ts @@ -27,13 +27,15 @@ import { isFluxReduxModelConfig, isGemma2EncoderModelConfig, isIPAdapterModelConfig, + isKrea2Qwen3VLEncoderModelConfig, isLLaVAModelConfig, isLoRAModelConfig, isMainOrExternalModelConfig, + isMiniMaxH3CheckpointMainModelConfig, + isMiniMaxH3TextEncoderModelConfig, isMistralEncoderModelConfig, isPiDDecoderModelConfig, isQwen3EncoderModelConfig, - isQwen3VLEncoderModelConfig, isQwenImageDiffusersMainModelConfig, isQwenImageVAEModelConfig, isQwenVLEncoderModelConfig, @@ -128,11 +130,13 @@ export const useQwenImageVAEModels = () => buildModelsHook(isQwenImageVAEModelCo export const useQwenVLEncoderModels = () => buildModelsHook(isQwenVLEncoderModelConfig)(); export const useQwen3EncoderModels = () => buildModelsHook(isQwen3EncoderModelConfig)(); export const useZImageQwen3EncoderModels = () => buildModelsHook(isZImageQwen3EncoderModelConfig)(); -export const useQwen3VLEncoderModels = () => buildModelsHook(isQwen3VLEncoderModelConfig)(); +export const useQwen3VLEncoderModels = () => buildModelsHook(isKrea2Qwen3VLEncoderModelConfig)(); export const useWanDiffusersModels = () => buildModelsHook(isWanDiffusersMainModelConfig)(); export const useWanSingleFileLowNoiseModels = () => buildModelsHook(isWanLowNoisePartnerOption)(); export const useWanVAEModels = () => buildModelsHook(isWanVAEModelConfig)(); export const useWanT5EncoderModels = () => buildModelsHook(isWanT5EncoderModelConfig)(); +export const useMiniMaxH3CheckpointModels = () => buildModelsHook(isMiniMaxH3CheckpointMainModelConfig)(); +export const useMiniMaxH3TextEncoderModels = () => buildModelsHook(isMiniMaxH3TextEncoderModelConfig)(); export const usePiDDecoderModels = buildModelsHook(isPiDDecoderModelConfig); export const useGemma2EncoderModels = () => buildModelsHook(isGemma2EncoderModelConfig)(); export const useGlobalReferenceImageModels = buildModelsHook( @@ -189,7 +193,9 @@ export const selectFlux2DevDiffusersModels = buildModelsSelector(isFlux2DevDiffu export const selectFlux1VAEModels = buildModelsSelector(isFlux1VAEModelConfig); export const selectAnimaVAEModels = buildModelsSelector(isAnimaVAEModelConfig); export const selectAnimaCompatibleVAEModels = buildModelsSelector(isAnimaCompatibleVAEModelConfig); -export const selectQwen3VLEncoderModels = buildModelsSelector(isQwen3VLEncoderModelConfig); +// Krea-2-scoped: its consumers (modelSelected Krea-2 component updates) must never auto-select +// the MiniMax H3 truncated encoders, which share the model type. +export const selectQwen3VLEncoderModels = buildModelsSelector(isKrea2Qwen3VLEncoderModelConfig); export const selectWanDiffusersModels = buildModelsSelector(isWanDiffusersMainModelConfig); export const selectWanVAEModels = buildModelsSelector(isWanVAEModelConfig); export const selectWanT5EncoderModels = buildModelsSelector(isWanT5EncoderModelConfig); diff --git a/invokeai/frontend/web/src/services/api/hooks/useAutoAddBoard.ts b/invokeai/frontend/webv1/src/services/api/hooks/useAutoAddBoard.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/useAutoAddBoard.ts rename to invokeai/frontend/webv1/src/services/api/hooks/useAutoAddBoard.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/useBoardAccess.ts b/invokeai/frontend/webv1/src/services/api/hooks/useBoardAccess.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/useBoardAccess.ts rename to invokeai/frontend/webv1/src/services/api/hooks/useBoardAccess.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/useBoardName.ts b/invokeai/frontend/webv1/src/services/api/hooks/useBoardName.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/useBoardName.ts rename to invokeai/frontend/webv1/src/services/api/hooks/useBoardName.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts b/invokeai/frontend/webv1/src/services/api/hooks/useDebouncedImageWorkflow.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts rename to invokeai/frontend/webv1/src/services/api/hooks/useDebouncedImageWorkflow.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/useDebouncedMetadata.ts b/invokeai/frontend/webv1/src/services/api/hooks/useDebouncedMetadata.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/useDebouncedMetadata.ts rename to invokeai/frontend/webv1/src/services/api/hooks/useDebouncedMetadata.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/useIsRefinerAvailable.ts b/invokeai/frontend/webv1/src/services/api/hooks/useIsRefinerAvailable.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/useIsRefinerAvailable.ts rename to invokeai/frontend/webv1/src/services/api/hooks/useIsRefinerAvailable.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/useSelectedBoard.ts b/invokeai/frontend/webv1/src/services/api/hooks/useSelectedBoard.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/useSelectedBoard.ts rename to invokeai/frontend/webv1/src/services/api/hooks/useSelectedBoard.ts diff --git a/invokeai/frontend/web/src/services/api/hooks/useSelectedModelConfig.ts b/invokeai/frontend/webv1/src/services/api/hooks/useSelectedModelConfig.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/hooks/useSelectedModelConfig.ts rename to invokeai/frontend/webv1/src/services/api/hooks/useSelectedModelConfig.ts diff --git a/invokeai/frontend/web/src/services/api/index.ts b/invokeai/frontend/webv1/src/services/api/index.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/index.ts rename to invokeai/frontend/webv1/src/services/api/index.ts diff --git a/invokeai/frontend/web/src/services/api/run-graph.test.ts b/invokeai/frontend/webv1/src/services/api/run-graph.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/run-graph.test.ts rename to invokeai/frontend/webv1/src/services/api/run-graph.test.ts diff --git a/invokeai/frontend/web/src/services/api/run-graph.ts b/invokeai/frontend/webv1/src/services/api/run-graph.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/run-graph.ts rename to invokeai/frontend/webv1/src/services/api/run-graph.ts diff --git a/invokeai/frontend/webv1/src/services/api/schema.ts b/invokeai/frontend/webv1/src/services/api/schema.ts new file mode 100644 index 00000000000..e8cfae791d0 --- /dev/null +++ b/invokeai/frontend/webv1/src/services/api/schema.ts @@ -0,0 +1 @@ +export type * from '../../../../api/schema'; diff --git a/invokeai/frontend/web/src/services/api/types.test.ts b/invokeai/frontend/webv1/src/services/api/types.test.ts similarity index 90% rename from invokeai/frontend/web/src/services/api/types.test.ts rename to invokeai/frontend/webv1/src/services/api/types.test.ts index d99abf0a726..1679c92a9c3 100644 --- a/invokeai/frontend/web/src/services/api/types.test.ts +++ b/invokeai/frontend/webv1/src/services/api/types.test.ts @@ -125,9 +125,9 @@ const wanMain = (over: Record) => describe('Wan low-noise partner picker', () => { it('offers an untagged single-file A14B', () => { // The case the whole branch exists to support. `expert` comes from a filename - // heuristic, is absent from `ModelRecordChanges`, and installed records are never - // re-probed — so requiring `expert === 'low'` here strands untagged pairs - // permanently, with no correction short of delete-and-reinstall. + // heuristic and is absent from `ModelRecordChanges`. Reidentify can re-probe the + // record, but the unchanged untagged filename yields `none` again — so requiring + // `expert === 'low'` here strands the pair until the file itself is renamed. expect(isWanLowNoisePartnerOption(wanMain({ key: 'untagged', expert: 'none' }))).toBe(true); }); @@ -194,3 +194,26 @@ describe('Wan low-noise partner picker', () => { expect(selectPrimaryMainModelOptions([high, low]).map((c) => c.key)).toEqual(['high']); }); }); + +describe('primary main model options', () => { + it('keeps MiniMax H3 single-file transformers in their dedicated advanced slot', () => { + const diffusers = { + key: 'minimax-diffusers', + type: 'main', + base: 'minimax-h3', + format: 'diffusers', + name: 'minimax-diffusers', + } as unknown as MainModelConfig; + const checkpoint = { + key: 'minimax-checkpoint', + type: 'main', + base: 'minimax-h3', + format: 'checkpoint', + name: 'minimax-checkpoint', + } as unknown as MainModelConfig; + + expect(selectPrimaryMainModelOptions([diffusers, checkpoint]).map((config) => config.key)).toEqual([ + 'minimax-diffusers', + ]); + }); +}); diff --git a/invokeai/frontend/webv1/src/services/api/types.ts b/invokeai/frontend/webv1/src/services/api/types.ts new file mode 100644 index 00000000000..24cd320aa71 --- /dev/null +++ b/invokeai/frontend/webv1/src/services/api/types.ts @@ -0,0 +1,991 @@ +import type { Dimensions } from 'features/controlLayers/store/types'; +import type { components, paths } from 'services/api/schema'; +import type { Equals } from 'tsafe'; +import { assert } from 'tsafe'; +import type { JsonObject, SetRequired } from 'type-fest'; +import z from 'zod'; + +export type S = components['schemas']; + +export type ListImagesArgs = NonNullable; +export type ListImagesResponse = paths['/api/v1/images/']['get']['responses']['200']['content']['application/json']; + +export type GetImageNamesResult = + paths['/api/v1/images/names']['get']['responses']['200']['content']['application/json']; +export type GetImageNamesArgs = NonNullable; + +export type ListBoardsArgs = NonNullable; + +export type CreateBoardArg = paths['/api/v1/boards/']['post']['parameters']['query']; + +export type UpdateBoardArg = paths['/api/v1/boards/{board_id}']['patch']['parameters']['path'] & { + changes: paths['/api/v1/boards/{board_id}']['patch']['requestBody']['content']['application/json']; +}; + +export type GraphAndWorkflowResponse = + paths['/api/v1/images/i/{image_name}/workflow']['get']['responses']['200']['content']['application/json']; + +export type EnqueueBatchArg = + paths['/api/v1/queue/{queue_id}/enqueue_batch']['post']['requestBody']['content']['application/json']; + +export type GetQueueItemIdsResult = + paths['/api/v1/queue/{queue_id}/item_ids']['get']['responses']['200']['content']['application/json']; +export type GetQueueItemIdsArgs = NonNullable; + +export type GetQueueItemSummariesByItemIdsResult = + paths['/api/v1/queue/{queue_id}/item_summaries_by_ids']['post']['responses']['200']['content']['application/json']; +export type GetQueueItemSummariesByItemIdsArgs = + paths['/api/v1/queue/{queue_id}/item_summaries_by_ids']['post']['requestBody']['content']['application/json']; + +export type InputFieldJSONSchemaExtra = S['InputFieldJSONSchemaExtra']; +export type OutputFieldJSONSchemaExtra = S['OutputFieldJSONSchemaExtra']; +export type InvocationJSONSchemaExtra = S['UIConfigBase']; + +// App Info +export type AppVersion = S['AppVersion']; +export type ExternalProviderStatus = { + provider_id: string; + configured: boolean; + message?: string | null; +}; +export type ExternalProviderConfig = { + provider_id: string; + api_key_configured: boolean; + base_url?: string | null; + message?: string | null; +}; +export type ExternalProviderConfigUpdate = { + api_key?: string; + base_url?: string | null; +}; +export type UpdateModelBody = paths['/api/v2/models/i/{key}']['patch']['requestBody']['content']['application/json']; + +const zResourceOrigin = z.enum(['internal', 'external']); +type ResourceOrigin = z.infer; +assert>(); +const zImageCategory = z.enum(['general', 'mask', 'control', 'user', 'other']); +export type ImageCategory = z.infer; +assert>(); + +// Images +const _zImageDTO = z.object({ + image_name: z.string(), + image_url: z.string(), + thumbnail_url: z.string(), + image_origin: zResourceOrigin, + image_category: zImageCategory, + width: z.number().int().gt(0), + height: z.number().int().gt(0), + created_at: z.string(), + updated_at: z.string(), + deleted_at: z.string().nullish(), + is_intermediate: z.boolean(), + session_id: z.string().nullish(), + node_id: z.string().nullish(), + starred: z.boolean(), + has_workflow: z.boolean(), + project_id: z.string().nullish(), + file_size_bytes: z.number().nullish(), + board_id: z.string().nullish(), + image_subfolder: z.string().optional(), +}); +export type ImageDTO = z.infer; +assert>(); + +export type BoardDTO = S['BoardDTO']; +export type OffsetPaginatedResults_ImageDTO_ = S['OffsetPaginatedResults_ImageDTO_']; + +// Model Configs +type InternalAnyModelConfig = S['AnyModelConfig']; +export type MainModelConfig = Extract; +type FLUXModelConfig = Extract; +type FLUX2ModelConfig = Extract; +export type AnyFLUXModelConfig = FLUXModelConfig | FLUX2ModelConfig; +export type ControlLoRAModelConfig = Extract; +export type LoRAModelConfig = Extract; +type WanLoRAModelConfig = Extract; +export type VAEModelConfig = Extract; +export type ControlNetModelConfig = Extract; +type AnimaControlNetModelConfig = Extract; +export type IPAdapterModelConfig = Extract; +export type T2IAdapterModelConfig = Extract; +export type CLIPLEmbedModelConfig = Extract; +export type CLIPGEmbedModelConfig = Extract; +export type CLIPEmbedModelConfig = Extract; +export type LlavaOnevisionModelConfig = Extract; +export type TextLLMModelConfig = Extract; +export type T5EncoderModelConfig = Extract; +export type T5EncoderBnbQuantizedLlmInt8bModelConfig = Extract< + InternalAnyModelConfig, + { type: 't5_encoder'; format: 'bnb_quantized_int8b' } +>; +export type Qwen3EncoderModelConfig = Extract; +export type MistralEncoderModelConfig = Extract; +export type QwenVLEncoderModelConfig = Extract; +export type Qwen3VLEncoderModelConfig = Extract; +export type WanT5EncoderModelConfig = Extract; +type Gemma2EncoderModelConfig = Extract; +type Gemma4EncoderModelConfig = Extract; +type PiDDecoderModelConfig = Extract; +type LTX2DurationHeadModelConfig = Extract; +export type SpandrelImageToImageModelConfig = Extract; +export type CheckpointModelConfig = Extract; +export type CLIPVisionModelConfig = Extract; +export type SigLIPModelConfig = Extract; +export type FLUXReduxModelConfig = Extract; +type ApiModelConfig = Extract; +type UnknownModelConfig = Extract; +export type FLUXKontextModelConfig = MainModelConfig; +export type ChatGPT4oModelConfig = ApiModelConfig; +export type Gemini2_5ModelConfig = ApiModelConfig; +type SubmodelDefinition = S['SubmodelDefinition']; + +export type ExternalImageSize = { + width: number; + height: number; +}; + +type ExternalResolutionPreset = { + label: string; + aspect_ratio: string; + image_size: string; + width: number; + height: number; +}; + +export type ExternalModelCapabilities = { + modes: ('txt2img' | 'img2img' | 'inpaint')[]; + supports_reference_images?: boolean; + supports_negative_prompt?: boolean; + supports_seed?: boolean; + supports_guidance?: boolean; + supports_steps?: boolean; + max_images_per_request?: number | null; + max_image_size?: ExternalImageSize | null; + allowed_aspect_ratios?: string[] | null; + aspect_ratio_sizes?: Record | null; + resolution_presets?: ExternalResolutionPreset[] | null; + max_reference_images?: number | null; + mask_format?: 'alpha' | 'binary' | 'none'; + input_image_required_for?: ('txt2img' | 'img2img' | 'inpaint')[] | null; +}; + +export type ExternalApiModelDefaultSettings = { + width?: number | null; + height?: number | null; + steps?: number | null; + guidance?: number | null; + num_images?: number | null; +}; + +export type ExternalPanelControlName = + | 'negative_prompt' + | 'reference_images' + | 'dimensions' + | 'seed' + | 'steps' + | 'guidance'; + +export type ExternalModelPanelControl = { + name: ExternalPanelControlName; + slider_min?: number | null; + slider_max?: number | null; + number_input_min?: number | null; + number_input_max?: number | null; + fine_step?: number | null; + coarse_step?: number | null; + marks?: number[] | null; +}; + +export type ExternalModelPanelSchema = { + prompts: ExternalModelPanelControl[]; + image: ExternalModelPanelControl[]; + generation: ExternalModelPanelControl[]; +}; + +export type ExternalApiModelConfig = { + key: string; + hash: string; + path: string; + file_size: number; + name: string; + description: string | null; + source: string; + source_type: string; + source_api_response: JsonObject | null; + cover_image: string | null; + base: 'external'; + type: 'external_image_generator'; + format: 'external_api'; + provider_id: string; + provider_model_id: string; + capabilities: ExternalModelCapabilities; + default_settings?: ExternalApiModelDefaultSettings | null; + panel_schema?: ExternalModelPanelSchema | null; + tags?: string[] | null; + is_default?: boolean; +}; +export type AnyModelConfig = InternalAnyModelConfig; +export type AnyModelConfigWithExternal = AnyModelConfig | ExternalApiModelConfig; +export type MainOrExternalModelConfig = MainModelConfig | ExternalApiModelConfig; + +/** + * Checks if a list of submodels contains any that match a given variant or type + * @param submodels The list of submodels to check + * @param checkStr The string to check against for variant or type + * @returns A boolean + */ +const checkSubmodel = (submodels: Record, checkStr: string): boolean => { + for (const submodel in submodels) { + if ( + submodel && + submodels[submodel] && + (submodels[submodel].model_type === checkStr || submodels[submodel].variant === checkStr) + ) { + return true; + } + } + return false; +}; + +/** + * Checks if a main model config has submodels that match a given variant or type + * @param identifiers A list of strings to check against for variant or type in submodels + * @param config The model config + * @returns A boolean + */ +const checkSubmodels = (identifiers: string[], config: AnyModelConfig): boolean => { + return identifiers.every( + (identifier) => + config.type === 'main' && + 'submodels' in config && + config.submodels && + (identifier in config.submodels || checkSubmodel(config.submodels, identifier)) + ); +}; + +export const isLoRAModelConfig = (config: AnyModelConfig): config is LoRAModelConfig => { + return config.type === 'lora'; +}; + +export const isControlLoRAModelConfig = (config: AnyModelConfig): config is ControlLoRAModelConfig => { + return config.type === 'control_lora'; +}; + +export const isVAEModelConfigOrSubmodel = ( + config: AnyModelConfig, + excludeSubmodels?: boolean +): config is VAEModelConfig => { + return config.type === 'vae' || (!excludeSubmodels && config.type === 'main' && checkSubmodels(['vae'], config)); +}; + +export const isVAEModelConfig = (config: AnyModelConfig): config is VAEModelConfig => { + return config.type === 'vae'; +}; + +export const isNonFluxVAEModelConfig = ( + config: AnyModelConfig, + excludeSubmodels?: boolean +): config is VAEModelConfig => { + return ( + (config.type === 'vae' || (!excludeSubmodels && config.type === 'main' && checkSubmodels(['vae'], config))) && + config.base !== 'flux' && + config.base !== 'flux2' + ); +}; + +export const isFlux1VAEModelConfig = (config: AnyModelConfig, excludeSubmodels?: boolean): config is VAEModelConfig => { + return ( + (config.type === 'vae' || (!excludeSubmodels && config.type === 'main' && checkSubmodels(['vae'], config))) && + config.base === 'flux' + ); +}; + +export const isFlux2VAEModelConfig = (config: AnyModelConfig, excludeSubmodels?: boolean): config is VAEModelConfig => { + return ( + (config.type === 'vae' || (!excludeSubmodels && config.type === 'main' && checkSubmodels(['vae'], config))) && + config.base === 'flux2' + ); +}; + +export const isWanVAEModelConfig = (config: AnyModelConfig, excludeSubmodels?: boolean): config is VAEModelConfig => { + return ( + (config.type === 'vae' || (!excludeSubmodels && config.type === 'main' && checkSubmodels(['vae'], config))) && + config.base === 'wan' + ); +}; + +export const isAnimaVAEModelConfig = (config: AnyModelConfig, excludeSubmodels?: boolean): config is VAEModelConfig => { + return ( + (config.type === 'vae' || (!excludeSubmodels && config.type === 'main' && checkSubmodels(['vae'], config))) && + config.base === 'anima' + ); +}; + +/** + * Wan VAEs whose latent geometry matches Anima's, i.e. `AutoencoderKLWan` with 16 latent channels + * (Wan 2.1 / A14B). The 48-channel TI2V-5B VAE (`Wan2.2-VAE`) is the same class but a different + * latent space, so it must stay out. + * + * Submodels are excluded deliberately: a main model's bundled `vae` carries no `latent_channels` of + * its own, so its geometry cannot be verified from the config. + */ +const isAnimaCompatibleWanVAEModelConfig = (config: AnyModelConfig): config is VAEModelConfig => { + return isWanVAEModelConfig(config, true) && 'latent_channels' in config && config.latent_channels === 16; +}; + +/** + * VAEs the Anima model loader accepts, gated on the backend's latent geometry rather than on base + * alone. `AnimaModelLoaderInvocation.vae_model` carries no `ui_model_base`, and both `anima_l2i` and + * `anima_i2l` branch explicitly on `FluxAutoEncoder` (4D decode, no Wan denormalisation) beside + * `AutoencoderKLWan` - so an Anima-base VAE, a FLUX VAE and a 16-channel Wan VAE are all supported, + * not merely tolerated. + * + * Kept separate from `isAnimaVAEModelConfig`, which stays base-driven because Krea-2 draws its + * own VAE pool from it and must not be offered FLUX or Wan VAEs. + */ +export const isAnimaCompatibleVAEModelConfig = ( + config: AnyModelConfig, + excludeSubmodels?: boolean +): config is VAEModelConfig => { + return ( + isAnimaVAEModelConfig(config, excludeSubmodels) || + isFlux1VAEModelConfig(config, excludeSubmodels) || + isAnimaCompatibleWanVAEModelConfig(config) + ); +}; + +export const isQwenImageVAEModelConfig = ( + config: AnyModelConfig, + excludeSubmodels?: boolean +): config is VAEModelConfig => { + return ( + (config.type === 'vae' || (!excludeSubmodels && config.type === 'main' && checkSubmodels(['vae'], config))) && + config.base === 'qwen-image' + ); +}; + +export const isControlNetModelConfig = (config: AnyModelConfig): config is ControlNetModelConfig => { + return config.type === 'controlnet'; +}; + +export const isAnimaControlNetModelConfig = (config: AnyModelConfig): config is AnimaControlNetModelConfig => { + return config.type === 'controlnet' && config.base === 'anima'; +}; + +export const isAnimaInpaintControlNetModelConfig = (config: AnyModelConfig): config is AnimaControlNetModelConfig => { + // 4-channel (RGB + mask) variants are inpaint adapters. Models with a null cond_in_channels were installed before + // the field was recorded - only the inpaint variant predates it, so treat them as inpaint adapters too. (A 3ch + // model installed from a pre-release dev build also reads as null and must be reinstalled to register as a + // control layer model.) + return isAnimaControlNetModelConfig(config) && config.cond_in_channels !== 3; +}; + +export const isControlLayerModelConfig = ( + config: AnyModelConfig +): config is ControlNetModelConfig | T2IAdapterModelConfig | ControlLoRAModelConfig => { + if (isAnimaControlNetModelConfig(config)) { + // Only the 3-channel (general control) Anima ControlNet-LLLite variants are usable as control layers; 4-channel + // and legacy (null cond_in_channels) variants are inpaint adapters. + return config.cond_in_channels === 3; + } + return config.type === 'controlnet' || config.type === 't2i_adapter' || config.type === 'control_lora'; +}; + +export const isIPAdapterModelConfig = (config: AnyModelConfig): config is IPAdapterModelConfig => { + return config.type === 'ip_adapter'; +}; + +export const isCLIPVisionModelConfig = (config: AnyModelConfig): config is CLIPVisionModelConfig => { + return config.type === 'clip_vision'; +}; + +export const isLLaVAModelConfig = (config: AnyModelConfig): config is LlavaOnevisionModelConfig => { + return config.type === 'llava_onevision'; +}; + +export const isTextLLMModelConfig = (config: AnyModelConfig): config is TextLLMModelConfig => { + return config.type === 'text_llm'; +}; + +export const isT2IAdapterModelConfig = (config: AnyModelConfig): config is T2IAdapterModelConfig => { + return config.type === 't2i_adapter'; +}; + +export const isT5EncoderModelConfigOrSubmodel = ( + config: AnyModelConfig, + excludeSubmodels?: boolean +): config is T5EncoderModelConfig | T5EncoderBnbQuantizedLlmInt8bModelConfig => { + return ( + config.type === 't5_encoder' || + (!excludeSubmodels && config.type === 'main' && checkSubmodels(['t5_encoder'], config)) + ); +}; + +export const isT5EncoderModelConfig = ( + config: AnyModelConfig +): config is T5EncoderModelConfig | T5EncoderBnbQuantizedLlmInt8bModelConfig => { + return config.type === 't5_encoder'; +}; + +export const isQwen3EncoderModelConfig = (config: AnyModelConfig): config is Qwen3EncoderModelConfig => { + return config.type === 'qwen3_encoder' && config.variant !== 'qwen3_06b'; +}; + +export const isAnimaQwen3EncoderModelConfig = (config: AnyModelConfig): config is Qwen3EncoderModelConfig => { + return config.type === 'qwen3_encoder' && config.variant === 'qwen3_06b'; +}; + +/** Z-Image consumes 2560-wide embeddings, so only the 4B encoder fits - Klein 9B's 8B one (4096) does not. */ +export const isZImageQwen3EncoderModelConfig = (config: AnyModelConfig): config is Qwen3EncoderModelConfig => { + return config.type === 'qwen3_encoder' && config.variant === 'qwen3_4b'; +}; + +export const isMistralEncoderModelConfig = (config: AnyModelConfig): config is MistralEncoderModelConfig => { + return config.type === 'mistral_encoder'; +}; + +export const isQwenVLEncoderModelConfig = (config: AnyModelConfig): config is QwenVLEncoderModelConfig => { + return config.type === 'qwen_vl_encoder'; +}; + +export const isQwen3VLEncoderModelConfig = (config: AnyModelConfig): config is Qwen3VLEncoderModelConfig => { + return config.type === 'qwen3_vl_encoder'; +}; + +/** Qwen3-VL encoders usable by Krea-2. Excludes MiniMax H3's truncated 32B conditioning + * encoders, which share the model type but are useless to Krea-2 (50-layer truncation, no + * final norm) and have their own guard below. */ +export const isKrea2Qwen3VLEncoderModelConfig = (config: AnyModelConfig): config is Qwen3VLEncoderModelConfig => { + return config.type === 'qwen3_vl_encoder' && config.base !== 'minimax-h3'; +}; + +/** MiniMax H3 single-file truncated Qwen3-VL-32B encoders (e.g. the int8 repack). These serve + * only the Text Encoder (single file) override slot of the MiniMax H3 advanced section. */ +export const isMiniMaxH3TextEncoderModelConfig = (config: AnyModelConfig): config is Qwen3VLEncoderModelConfig => { + return config.type === 'qwen3_vl_encoder' && config.base === 'minimax-h3' && config.format === 'checkpoint'; +}; + +export const isWanT5EncoderModelConfig = (config: AnyModelConfig): config is WanT5EncoderModelConfig => { + return config.type === 'wan_t5_encoder'; +}; + +export const isGemma2EncoderModelConfig = (config: AnyModelConfig): config is Gemma2EncoderModelConfig => { + return config.type === 'gemma2_encoder'; +}; + +export const isGemma4EncoderModelConfig = (config: AnyModelConfig): config is Gemma4EncoderModelConfig => { + return config.type === 'gemma4_encoder'; +}; + +export const isPiDDecoderModelConfig = (config: AnyModelConfig): config is PiDDecoderModelConfig => { + return config.type === 'pid_decoder'; +}; + +export const isLTX2DurationHeadModelConfig = (config: AnyModelConfig): config is LTX2DurationHeadModelConfig => { + return config.type === 'ltx2_duration_head'; +}; + +export const isCLIPEmbedModelConfigOrSubmodel = ( + config: AnyModelConfig, + excludeSubmodels?: boolean +): config is CLIPEmbedModelConfig => { + return ( + config.type === 'clip_embed' || + (!excludeSubmodels && config.type === 'main' && checkSubmodels(['clip_embed'], config)) + ); +}; + +export const isCLIPEmbedModelConfig = (config: AnyModelConfig): config is CLIPEmbedModelConfig => { + return config.type === 'clip_embed'; +}; + +export const isCLIPLEmbedModelConfigOrSubmodel = ( + config: AnyModelConfig, + excludeSubmodels?: boolean +): config is CLIPLEmbedModelConfig => { + return ( + (config.type === 'clip_embed' && config.variant === 'large') || + (!excludeSubmodels && config.type === 'main' && checkSubmodels(['clip_embed', 'large'], config)) + ); +}; + +export const isCLIPGEmbedModelConfigOrSubmodel = ( + config: AnyModelConfig, + excludeSubmodels?: boolean +): config is CLIPGEmbedModelConfig => { + return ( + (config.type === 'clip_embed' && config.variant === 'gigantic') || + (!excludeSubmodels && config.type === 'main' && checkSubmodels(['clip_embed', 'gigantic'], config)) + ); +}; + +export const isSpandrelImageToImageModelConfig = ( + config: AnyModelConfig +): config is SpandrelImageToImageModelConfig => { + return config.type === 'spandrel_image_to_image'; +}; + +export const isSigLipModelConfig = (config: AnyModelConfig): config is SigLIPModelConfig => { + return config.type === 'siglip'; +}; + +export const isFluxReduxModelConfig = (config: AnyModelConfig): config is FLUXReduxModelConfig => { + return config.type === 'flux_redux'; +}; + +export const isExternalApiModelConfig = ( + config: AnyModelConfigWithExternal | null | undefined +): config is ExternalApiModelConfig => { + return !!config && (config as { format?: string }).format === 'external_api'; +}; + +export const isUnknownModelConfig = (config: AnyModelConfig): config is UnknownModelConfig => { + return config.type === 'unknown'; +}; + +export const isFluxKontextModelConfig = (config: AnyModelConfig): config is FLUXKontextModelConfig => { + return config.type === 'main' && config.base === 'flux' && config.name.toLowerCase().includes('kontext'); +}; + +export const isNonRefinerMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'main' && config.base !== 'sdxl-refiner'; +}; + +export const isMainOrExternalModelConfig = ( + config: AnyModelConfigWithExternal +): config is MainOrExternalModelConfig => { + if (isExternalApiModelConfig(config)) { + return true; + } + return isNonRefinerMainModelConfig(config); +}; + +export const isRefinerMainModelModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'main' && config.base === 'sdxl-refiner'; +}; + +const isFluxDevMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'main' && config.base === 'flux' && config.variant === 'dev'; +}; + +const isFlux2Klein9BMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'main' && config.base === 'flux2' && config.name.toLowerCase().includes('9b'); +}; + +const isFlux2DevMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'main' && config.base === 'flux2' && config.variant === 'dev'; +}; + +export const isFlux2DevDiffusersMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return isFlux2DevMainModelConfig(config) && config.format === 'diffusers'; +}; + +const isIdeogram4MainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'main' && config.base === 'ideogram-4'; +}; + +export const isNonCommercialMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return ( + isFluxDevMainModelConfig(config) || + isFlux2Klein9BMainModelConfig(config) || + isFlux2DevMainModelConfig(config) || + isIdeogram4MainModelConfig(config) + ); +}; + +export const isFluxFillMainModelModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'main' && config.base === 'flux' && config.variant === 'dev_fill'; +}; + +/** + * The submodels an SDNQ pipeline install must expose before it can act as a component source. + * Mirrors `_REQUIRED_PIPELINE_SUBMODELS` / `is_self_contained_sdnq_pipeline()` in + * `invokeai/app/invocations/model.py` — the frontend and the backend must agree on what "complete" + * means, or the graph builders offer a source the invocation validation then rejects. + */ +const SDNQ_PIPELINE_REQUIRED_SUBMODELS = ['transformer', 'vae', 'text_encoder', 'tokenizer'] as const; + +/** + * True if an SDNQ pipeline config ships every component its loaders read from a fixed subfolder. + * + * A truthy `submodels` map is not enough: a partial pipeline can expose only the transformer, and a + * malformed model_index.json can expose the components while omitting the transformer. Either would + * otherwise be offered in the source pickers, auto-selected by the graph builders, and only rejected + * by the backend after graph construction. + */ +export const isSelfContainedSDNQPipeline = (config: AnyModelConfig): boolean => { + return hasSubmodels(config, SDNQ_PIPELINE_REQUIRED_SUBMODELS); +}; + +/** + * FLUX.1 drives two text encoders, so a pipeline install can only replace the standalone components + * if it also ships the T5 pair on top of the CLIP one. Mirrors + * `_REQUIRED_FLUX1_PIPELINE_SUBMODELS` / `is_self_contained_sdnq_flux1_pipeline()` in + * `invokeai/app/invocations/model.py`; if the two disagree, the UI either blocks a model the node + * would have accepted or builds a graph the node then rejects. + */ +const SDNQ_FLUX1_PIPELINE_REQUIRED_SUBMODELS = [ + ...SDNQ_PIPELINE_REQUIRED_SUBMODELS, + 'text_encoder_2', + 'tokenizer_2', +] as const; + +const hasSubmodels = (config: AnyModelConfig, required: readonly string[]): boolean => { + const submodels = (config as { submodels?: unknown }).submodels; + if (typeof submodels !== 'object' || submodels === null) { + return false; + } + return required.every((submodel) => Boolean((submodels as Record)[submodel])); +}; + +/** + * True for a FLUX.1 SDNQ pipeline that ships every component the FLUX graph needs, so the + * standalone T5 / CLIP / VAE selections are not required. + */ +export const isSelfContainedSDNQFlux1Pipeline = (config: AnyModelConfig): boolean => { + if ((config as { format?: unknown }).format !== 'sdnq_quantized') { + return false; + } + return hasSubmodels(config, SDNQ_FLUX1_PIPELINE_REQUIRED_SUBMODELS); +}; + +export const isZImageDiffusersMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + if (config.type !== 'main' || config.base !== 'z-image') { + return false; + } + // Read `format` and `submodels` as plain strings/unknown so TS doesn't narrow away the + // `sdnq_quantized` branch. The OpenAPI schema is regenerated separately and currently + // doesn't list the `sdnq_quantized` Z-Image format variant. + const format = (config as { format?: unknown }).format as string | undefined; + if (format === 'diffusers') { + return true; + } + // SDNQ-quantized ZImagePipeline folders carry the same submodels layout (transformer, vae, + // text_encoder, ...) as a plain diffusers ZImagePipeline. Single-file SDNQ Z-Image + // checkpoints have no submodels and must not match here, and neither may a partial pipeline. + if (format !== 'sdnq_quantized') { + return false; + } + return isSelfContainedSDNQPipeline(config); +}; + +export const isFlux2DiffusersMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + if (config.type !== 'main' || config.base !== 'flux2') { + return false; + } + // Same reasoning as isZImageDiffusersMainModelConfig: an SDNQ FLUX.2 pipeline folder ships + // the same submodels (transformer/text_encoder/tokenizer/vae) and qualifies as a source model. + const format = (config as { format?: unknown }).format as string | undefined; + if (format === 'diffusers') { + return true; + } + if (format !== 'sdnq_quantized') { + return false; + } + return isSelfContainedSDNQPipeline(config); +}; + +export const isQwenImageDiffusersMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'main' && config.base === 'qwen-image' && config.format === 'diffusers'; +}; + +export const isWanDiffusersMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'main' && config.base === 'wan' && config.format === 'diffusers'; +}; + +/** The single-file Wan main formats. Both are transformer-only: one file holds one + * A14B expert, and the VAE + UMT5-XXL encoder have to come from somewhere else. + * Anything gating on that property must use this, not a bare `=== 'gguf_quantized'` + * — the two formats are interchangeable here and drifting apart has bitten us. */ +const WAN_SINGLE_FILE_FORMATS = ['gguf_quantized', 'checkpoint'] as const; + +export const isWanSingleFileMainModelConfig = (config: AnyModelConfigWithExternal): config is MainModelConfig => { + // Takes AnyModelConfig, not a structural `{base?; type?; format?}`. An all-optional + // parameter type is a *weak type*, which TypeScript satisfies with any object sharing + // one property name — so `ModelIdentifierField` (base + type, no format) would compile + // and silently return false, disabling every gate below it. The bare + // `format === 'gguf_quantized'` this replaced was at least a compile error there. + return ( + config.type === 'main' && + config.base === 'wan' && + (WAN_SINGLE_FILE_FORMATS as readonly string[]).includes(config.format) + ); +}; + +/** MiniMax H3 single-file transformer checkpoints (e.g. the pruned int8 repack). These serve + * only the Transformer (single file) override slot of the MiniMax H3 advanced section; the + * primary main dropdown shows only diffusers-format H3 installs. */ +export const isMiniMaxH3CheckpointMainModelConfig = (config: AnyModelConfigWithExternal): config is MainModelConfig => { + return config.type === 'main' && config.base === 'minimax-h3' && config.format === 'checkpoint'; +}; + +/** TI2V-5B is the single-transformer Wan variant: it has no expert pair, so no expert + * tag on it means anything. Both predicates below have to agree about that, or a file + * can fall through the gap between them. */ +const isWanTi2v5bConfig = (config: AnyModelConfigWithExternal): boolean => + 'variant' in config && config.variant === 'ti2v_5b'; + +/** Wan single-file main models *tagged* as the low-noise expert. This is the narrow, + * tag-based test, and its only job is deciding what to hide from the primary main + * dropdown — see `selectPrimaryMainModelOptions`, its one caller. Deliberately not + * exported: the Transformer (Low Noise) picker needs the wider test below, and reaching + * for this one there is the mistake that left untagged pairs unwireable. + * + * TI2V-5B is excluded for the same reason it is excluded from the partner picker. The + * two exclusions have to match: hiding a 5B from the primary list steers it toward a + * partner slot that will not offer it either, which is how a model ends up reachable + * from nowhere. Such a record is not hypothetical — the pre-branch GGUF probe applied + * the tag without consulting the variant, so a 5B named `...-low_noise.gguf` installed + * before this branch still carries `expert='low'` today. */ +const isWanSingleFileLowNoiseMainModelConfig = (config: AnyModelConfigWithExternal): config is MainModelConfig => { + return ( + isWanSingleFileMainModelConfig(config) && + !isWanTi2v5bConfig(config) && + 'expert' in config && + config.expert === 'low' + ); +}; + +/** What the Transformer (Low Noise) picker may offer. + * + * Deliberately wider than the tag test above. Since #9505 the *wiring* decides which + * expert a file is used as and the `expert` tag is only advisory, so requiring + * `expert === 'low'` here strands every pair that probes to `none`/`none`: both halves + * show up in the primary picker (which hides only models tagged `low`) and neither + * shows up here, leaving the pair impossible to assemble outside the workflow editor. + * That is the exact case this branch exists to support. It is also not something the + * user can tag their way out of: `expert` is absent from `ModelRecordChanges`, so no + * edit sets it. Re-probing via Reidentify recomputes it, but only from the filename, + * which for an untagged file returns `none` again. + * + * Two exclusions. Files tagged `high` belong in the primary slot — the loader would + * only swap them back. TI2V-5B is single-transformer, so it has no partner at all and + * offering one could only produce the variant mismatch the loader rejects. */ +export const isWanLowNoisePartnerOption = (config: AnyModelConfigWithExternal): config is MainModelConfig => { + if (!isWanSingleFileMainModelConfig(config)) { + return false; + } + if ('expert' in config && config.expert === 'high') { + return false; + } + return !isWanTi2v5bConfig(config); +}; + +/** Narrows a main-model list to what may be offered as the *primary* main. Every list + * the user can pick a primary main from must go through this — there are three + * (MainModelPicker, InitialStateMainModelPicker, and the auto-select in the + * modelsLoaded listener), and filtering in only some of them means the excluded models + * are still reachable. + * + * It hides MiniMax H3 single-file transformer overrides, plus Wan A14B low-noise + * experts only when the user has a partner to + * pick instead. The steer is worth making — a low-noise expert belongs in the + * Transformer (Low Noise) slot, and running it alone gives visibly worse output — but + * since #9505 the loader accepts an unpaired low expert with a warning rather than + * refusing it. Hiding unconditionally would leave someone whose only Wan file is a low + * expert staring at a list that doesn't contain their model, with nothing to do about + * it. Partner-aware, the list degrades instead of dead-ending. + * + * A partner is another single-file Wan main of the same variant that isn't itself + * tagged low — i.e. the high-noise or untagged half of the same pair. */ +export const selectPrimaryMainModelOptions = (configs: T[]): T[] => { + // Annotated `: boolean` rather than left as an inferred type predicate. Two predicates + // narrowing to the same type would make the negated one resolve `candidate` to `never` + // below, and the `variant` read would stop compiling. + const isLowExpert = (config: T): boolean => isWanSingleFileLowNoiseMainModelConfig(config); + const variantOf = (config: T): string | null => + 'variant' in config && typeof config.variant === 'string' ? config.variant : null; + + const hasPartner = (low: T): boolean => + configs.some( + (candidate) => + candidate.key !== low.key && + isWanSingleFileMainModelConfig(candidate) && + !isLowExpert(candidate) && + variantOf(candidate) === variantOf(low) + ); + + return configs.filter( + (config) => !isMiniMaxH3CheckpointMainModelConfig(config) && (!isLowExpert(config) || !hasPartner(config)) + ); +}; + +export const isWanLoRAModelConfig = (config: AnyModelConfig): config is WanLoRAModelConfig => { + return config.type === 'lora' && config.base === 'wan'; +}; + +export const isTIModelConfig = (config: AnyModelConfig): config is MainModelConfig => { + return config.type === 'embedding'; +}; + +type ExternalModelInstallSource = { + type: 'external'; + provider_id: string; + provider_model_id: string; +}; +type ModelInstallSource = S['ModelInstallJob']['source'] | ExternalModelInstallSource; +export type ModelInstallJob = Omit & { + source: ModelInstallSource; +}; +export type ModelInstallStatus = S['InstallStatus']; + +// Graphs +export type Graph = S['Graph']; +export type NonNullableGraph = SetRequired; +export type Batch = S['Batch']; +export const zWorkflowRecordOrderBy = z.enum(['name', 'created_at', 'updated_at', 'opened_at', 'is_public']); +export type WorkflowRecordOrderBy = z.infer; +assert>(); + +export const zSQLiteDirection = z.enum(['ASC', 'DESC']); +export type SQLiteDirection = z.infer; +assert>(); +export type WorkflowRecordListItemWithThumbnailDTO = S['WorkflowRecordListItemWithThumbnailDTO']; + +type KeysOfUnion = T extends T ? keyof T : never; + +export type AnyInvocation = Exclude< + NonNullable[string], + S['CoreMetadataInvocation'] | S['MetadataInvocation'] | S['MetadataItemInvocation'] | S['MergeMetadataInvocation'] +>; +export type AnyInvocationIncMetadata = NonNullable[string]; + +export type InvocationType = AnyInvocation['type']; +type InvocationOutputMap = S['InvocationOutputMap']; +export type AnyInvocationOutput = InvocationOutputMap[InvocationType]; + +export type Invocation = Extract; +// export type InvocationOutput = InvocationOutputMap[T]; + +type NonInputFields = 'id' | 'type' | 'is_intermediate' | 'use_cache' | 'board' | 'metadata'; +export type AnyInvocationInputField = Exclude>, NonInputFields>; +export type InputFields = Extract; + +type ExcludeIndexSignature = { + [K in keyof T as string extends K ? never : K]: T[K]; +}; + +export type CoreMetadataFields = Exclude< + keyof ExcludeIndexSignature, + NonInputFields +>; + +type NonOutputFields = 'type'; +export type AnyInvocationOutputField = Exclude>, NonOutputFields>; +export type OutputFields = Extract< + keyof InvocationOutputMap[T['type']], + AnyInvocationOutputField +>; + +// Node Outputs +export type ImageOutput = S['ImageOutput']; + +export type BoardRecordOrderBy = S['BoardRecordOrderBy']; +export type StarterModel = S['StarterModel']; + +export type GetHFTokenStatusResponse = + paths['/api/v2/models/hf_login']['get']['responses']['200']['content']['application/json']; +export type SetHFTokenResponse = NonNullable< + paths['/api/v2/models/hf_login']['post']['responses']['200']['content']['application/json'] +>; +export type ResetHFTokenResponse = NonNullable< + paths['/api/v2/models/hf_login']['delete']['responses']['200']['content']['application/json'] +>; +export type SetHFTokenArg = NonNullable< + paths['/api/v2/models/hf_login']['post']['requestBody']['content']['application/json'] +>; + +export type UploadImageArg = { + /** + * The file object to upload + */ + file: File; + /** + * THe category of image to upload + */ + image_category: ImageCategory; + /** + * Whether the uploaded image is an intermediate image (intermediate images are not shown int he gallery) + */ + is_intermediate: boolean; + /** + * The session with which to associate the uploaded image + */ + session_id?: string; + /** + * The board id to add the image to + */ + board_id?: string; + /** + * Whether or not to crop the image to its bounding box before saving + */ + crop_visible?: boolean; + /** + * Metadata to embed in the image when saving it + */ + metadata?: JsonObject; + /** + * Whether this upload should be "silent" (no toast on upload, no changing of gallery view) + */ + silent?: boolean; + /** + * Whether this is the first upload of a batch (used when displaying user feedback with toasts - ignored if the upload is silent) + */ + isFirstUploadOfBatch?: boolean; + /** + * If provided, the uploaded image will resized to the given dimensions. + */ + resize_to?: Dimensions; +}; + +export type ImageUploadEntryResponse = S['ImageUploadEntry']; +export type ImageUploadEntryRequest = paths['/api/v1/images/']['post']['requestBody']['content']['application/json']; + +// Videos +export type VideoDTO = S['VideoDTO']; +/** @knipignore Used by Phase 4+ video gallery mutations. */ +export type VideoRecordChanges = S['VideoRecordChanges']; +export type OffsetPaginatedResults_VideoDTO_ = S['OffsetPaginatedResults_VideoDTO_']; +export type ListVideosArgs = NonNullable; +export type ListVideosResponse = paths['/api/v1/videos/']['get']['responses']['200']['content']['application/json']; +export type GetVideoNamesArgs = NonNullable; +export type GetVideoNamesResult = + paths['/api/v1/videos/names']['get']['responses']['200']['content']['application/json']; + +export type UploadVideoArg = { + /** The MP4 (or other accepted video) file to upload. */ + file: File; + /** The category of video to upload. Reuses the image category enum. */ + video_category: ImageCategory; + /** Whether the uploaded video is an intermediate (intermediates are not shown in the gallery). */ + is_intermediate: boolean; + /** The session with which to associate the uploaded video, if any. */ + session_id?: string; + /** The board to add the video to, if any. */ + board_id?: string; + /** Metadata JSON to attach to the video record. */ + metadata?: JsonObject; + /** Suppress the upload toast / gallery navigation side effects. */ + silent?: boolean; + /** Whether this is the first upload of a batch (used by toast logic). */ + isFirstUploadOfBatch?: boolean; +}; + +// Polymorphic gallery items (images + videos). Consumed by the gallery wiring in Phase 4. +/** @knipignore Consumed by gallery wiring in Phase 4. */ +export type GalleryItem = S['GalleryItem']; +/** @knipignore Consumed by gallery wiring in Phase 4. */ +export type GalleryItemKind = S['GalleryItemKind']; +/** @knipignore Consumed by gallery wiring in Phase 4. */ +export type GalleryItemRef = S['GalleryItemRef']; +/** @knipignore Consumed by gallery wiring in Phase 4. */ +export type GalleryItemNamesResult = S['GalleryItemNamesResult']; +/** @knipignore Consumed by gallery wiring in Phase 4. */ +export type OffsetPaginatedResults_GalleryItem_ = S['OffsetPaginatedResults_GalleryItem_']; +export type ListGalleryItemsArgs = NonNullable; +export type ListGalleryItemsResponse = + paths['/api/v1/gallery/items/']['get']['responses']['200']['content']['application/json']; +export type ListGalleryItemNamesArgs = NonNullable; +export type ListGalleryItemNamesResult = + paths['/api/v1/gallery/item_names']['get']['responses']['200']['content']['application/json']; diff --git a/invokeai/frontend/web/src/services/api/util.ts b/invokeai/frontend/webv1/src/services/api/util.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/util.ts rename to invokeai/frontend/webv1/src/services/api/util.ts diff --git a/invokeai/frontend/web/src/services/api/util/imageErrors.test.ts b/invokeai/frontend/webv1/src/services/api/util/imageErrors.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/util/imageErrors.test.ts rename to invokeai/frontend/webv1/src/services/api/util/imageErrors.test.ts diff --git a/invokeai/frontend/web/src/services/api/util/imageErrors.ts b/invokeai/frontend/webv1/src/services/api/util/imageErrors.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/util/imageErrors.ts rename to invokeai/frontend/webv1/src/services/api/util/imageErrors.ts diff --git a/invokeai/frontend/web/src/services/api/util/optimisticUpdates.ts b/invokeai/frontend/webv1/src/services/api/util/optimisticUpdates.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/util/optimisticUpdates.ts rename to invokeai/frontend/webv1/src/services/api/util/optimisticUpdates.ts diff --git a/invokeai/frontend/web/src/services/api/util/tagInvalidation.ts b/invokeai/frontend/webv1/src/services/api/util/tagInvalidation.ts similarity index 100% rename from invokeai/frontend/web/src/services/api/util/tagInvalidation.ts rename to invokeai/frontend/webv1/src/services/api/util/tagInvalidation.ts diff --git a/invokeai/frontend/web/src/services/events/eventScope.test.ts b/invokeai/frontend/webv1/src/services/events/eventScope.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/eventScope.test.ts rename to invokeai/frontend/webv1/src/services/events/eventScope.test.ts diff --git a/invokeai/frontend/web/src/services/events/eventScope.ts b/invokeai/frontend/webv1/src/services/events/eventScope.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/eventScope.ts rename to invokeai/frontend/webv1/src/services/events/eventScope.ts diff --git a/invokeai/frontend/web/src/services/events/invocationTracking.test.ts b/invokeai/frontend/webv1/src/services/events/invocationTracking.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/invocationTracking.test.ts rename to invokeai/frontend/webv1/src/services/events/invocationTracking.test.ts diff --git a/invokeai/frontend/web/src/services/events/invocationTracking.ts b/invokeai/frontend/webv1/src/services/events/invocationTracking.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/invocationTracking.ts rename to invokeai/frontend/webv1/src/services/events/invocationTracking.ts diff --git a/invokeai/frontend/web/src/services/events/nodeExecutionState.test.ts b/invokeai/frontend/webv1/src/services/events/nodeExecutionState.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/nodeExecutionState.test.ts rename to invokeai/frontend/webv1/src/services/events/nodeExecutionState.test.ts diff --git a/invokeai/frontend/web/src/services/events/nodeExecutionState.ts b/invokeai/frontend/webv1/src/services/events/nodeExecutionState.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/nodeExecutionState.ts rename to invokeai/frontend/webv1/src/services/events/nodeExecutionState.ts diff --git a/invokeai/frontend/web/src/services/events/onInvocationComplete.test.ts b/invokeai/frontend/webv1/src/services/events/onInvocationComplete.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/onInvocationComplete.test.ts rename to invokeai/frontend/webv1/src/services/events/onInvocationComplete.test.ts diff --git a/invokeai/frontend/web/src/services/events/onInvocationComplete.tsx b/invokeai/frontend/webv1/src/services/events/onInvocationComplete.tsx similarity index 100% rename from invokeai/frontend/web/src/services/events/onInvocationComplete.tsx rename to invokeai/frontend/webv1/src/services/events/onInvocationComplete.tsx diff --git a/invokeai/frontend/web/src/services/events/onModelInstallError.tsx b/invokeai/frontend/webv1/src/services/events/onModelInstallError.tsx similarity index 100% rename from invokeai/frontend/web/src/services/events/onModelInstallError.tsx rename to invokeai/frontend/webv1/src/services/events/onModelInstallError.tsx diff --git a/invokeai/frontend/web/src/services/events/onQueueItemStatusChanged.tsx b/invokeai/frontend/webv1/src/services/events/onQueueItemStatusChanged.tsx similarity index 100% rename from invokeai/frontend/web/src/services/events/onQueueItemStatusChanged.tsx rename to invokeai/frontend/webv1/src/services/events/onQueueItemStatusChanged.tsx diff --git a/invokeai/frontend/web/src/services/events/queueCacheTags.ts b/invokeai/frontend/webv1/src/services/events/queueCacheTags.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/queueCacheTags.ts rename to invokeai/frontend/webv1/src/services/events/queueCacheTags.ts diff --git a/invokeai/frontend/web/src/services/events/queueStatusEvents.test.ts b/invokeai/frontend/webv1/src/services/events/queueStatusEvents.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/queueStatusEvents.test.ts rename to invokeai/frontend/webv1/src/services/events/queueStatusEvents.test.ts diff --git a/invokeai/frontend/web/src/services/events/queueStatusEvents.ts b/invokeai/frontend/webv1/src/services/events/queueStatusEvents.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/queueStatusEvents.ts rename to invokeai/frontend/webv1/src/services/events/queueStatusEvents.ts diff --git a/invokeai/frontend/web/src/services/events/setEventListeners.test.ts b/invokeai/frontend/webv1/src/services/events/setEventListeners.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/setEventListeners.test.ts rename to invokeai/frontend/webv1/src/services/events/setEventListeners.test.ts diff --git a/invokeai/frontend/web/src/services/events/setEventListeners.tsx b/invokeai/frontend/webv1/src/services/events/setEventListeners.tsx similarity index 100% rename from invokeai/frontend/web/src/services/events/setEventListeners.tsx rename to invokeai/frontend/webv1/src/services/events/setEventListeners.tsx diff --git a/invokeai/frontend/web/src/services/events/stores.ts b/invokeai/frontend/webv1/src/services/events/stores.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/stores.ts rename to invokeai/frontend/webv1/src/services/events/stores.ts diff --git a/invokeai/frontend/web/src/services/events/types.ts b/invokeai/frontend/webv1/src/services/events/types.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/types.ts rename to invokeai/frontend/webv1/src/services/events/types.ts diff --git a/invokeai/frontend/web/src/services/events/useSocketIO.test.tsx b/invokeai/frontend/webv1/src/services/events/useSocketIO.test.tsx similarity index 100% rename from invokeai/frontend/web/src/services/events/useSocketIO.test.tsx rename to invokeai/frontend/webv1/src/services/events/useSocketIO.test.tsx diff --git a/invokeai/frontend/web/src/services/events/useSocketIO.ts b/invokeai/frontend/webv1/src/services/events/useSocketIO.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/useSocketIO.ts rename to invokeai/frontend/webv1/src/services/events/useSocketIO.ts diff --git a/invokeai/frontend/web/src/services/events/workflowExecutionCoordinator.test.ts b/invokeai/frontend/webv1/src/services/events/workflowExecutionCoordinator.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/workflowExecutionCoordinator.test.ts rename to invokeai/frontend/webv1/src/services/events/workflowExecutionCoordinator.test.ts diff --git a/invokeai/frontend/web/src/services/events/workflowExecutionCoordinator.ts b/invokeai/frontend/webv1/src/services/events/workflowExecutionCoordinator.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/workflowExecutionCoordinator.ts rename to invokeai/frontend/webv1/src/services/events/workflowExecutionCoordinator.ts diff --git a/invokeai/frontend/web/src/services/events/workflowExecutionState.test.ts b/invokeai/frontend/webv1/src/services/events/workflowExecutionState.test.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/workflowExecutionState.test.ts rename to invokeai/frontend/webv1/src/services/events/workflowExecutionState.test.ts diff --git a/invokeai/frontend/web/src/services/events/workflowExecutionState.ts b/invokeai/frontend/webv1/src/services/events/workflowExecutionState.ts similarity index 100% rename from invokeai/frontend/web/src/services/events/workflowExecutionState.ts rename to invokeai/frontend/webv1/src/services/events/workflowExecutionState.ts diff --git a/invokeai/frontend/web/src/vite-env.d.ts b/invokeai/frontend/webv1/src/vite-env.d.ts similarity index 100% rename from invokeai/frontend/web/src/vite-env.d.ts rename to invokeai/frontend/webv1/src/vite-env.d.ts diff --git a/invokeai/frontend/web/static/docs/invoke-favicon-docs.svg b/invokeai/frontend/webv1/static/docs/invoke-favicon-docs.svg similarity index 100% rename from invokeai/frontend/web/static/docs/invoke-favicon-docs.svg rename to invokeai/frontend/webv1/static/docs/invoke-favicon-docs.svg diff --git a/invokeai/frontend/web/tsconfig.json b/invokeai/frontend/webv1/tsconfig.json similarity index 100% rename from invokeai/frontend/web/tsconfig.json rename to invokeai/frontend/webv1/tsconfig.json diff --git a/invokeai/frontend/web/tsconfig.node.json b/invokeai/frontend/webv1/tsconfig.node.json similarity index 100% rename from invokeai/frontend/web/tsconfig.node.json rename to invokeai/frontend/webv1/tsconfig.node.json diff --git a/invokeai/frontend/web/vite-plugin-eslint.d.ts b/invokeai/frontend/webv1/vite-plugin-eslint.d.ts similarity index 100% rename from invokeai/frontend/web/vite-plugin-eslint.d.ts rename to invokeai/frontend/webv1/vite-plugin-eslint.d.ts diff --git a/invokeai/frontend/web/vite-plugin-logger-context.ts b/invokeai/frontend/webv1/vite-plugin-logger-context.ts similarity index 100% rename from invokeai/frontend/web/vite-plugin-logger-context.ts rename to invokeai/frontend/webv1/vite-plugin-logger-context.ts diff --git a/invokeai/frontend/web/vite.config.mts b/invokeai/frontend/webv1/vite.config.mts similarity index 100% rename from invokeai/frontend/web/vite.config.mts rename to invokeai/frontend/webv1/vite.config.mts diff --git a/invokeai/frontend/webv2/.gitattributes b/invokeai/frontend/webv2/.gitattributes new file mode 100644 index 00000000000..b0e848ad0e3 --- /dev/null +++ b/invokeai/frontend/webv2/.gitattributes @@ -0,0 +1 @@ +**/__golden__/*.png binary diff --git a/invokeai/frontend/webv2/.gitignore b/invokeai/frontend/webv2/.gitignore new file mode 100644 index 00000000000..c4816fcb83a --- /dev/null +++ b/invokeai/frontend/webv2/.gitignore @@ -0,0 +1,14 @@ +dist/ +node_modules/ +stats.html +artifacts/ + +# vitest browser-mode failure artifacts +.vitest/ +__screenshots__/ + +# planning scratch, never committed +docs/superpowers/ +*.tsbuildinfo +vite.config.mjs +vite.config.d.mts diff --git a/invokeai/frontend/webv2/.oxfmtrc.json b/invokeai/frontend/webv2/.oxfmtrc.json new file mode 100644 index 00000000000..23258f74ffd --- /dev/null +++ b/invokeai/frontend/webv2/.oxfmtrc.json @@ -0,0 +1,33 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "arrowParens": "always", + "bracketSameLine": false, + "bracketSpacing": true, + "endOfLine": "lf", + "ignorePatterns": [ + "dist/**", + "node_modules/**", + "stats.html", + "src/features/generation/core/__snapshots__/generateGraphNodeTypes.json", + "src/features/video/core/__snapshots__/videoGraphNodeTypes.json", + "src/features/generation/core/__fixtures__/architectureCapabilities.json" + ], + "printWidth": 120, + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "sortImports": { + "groups": [ + "type-import", + ["value-builtin", "value-external"], + ["type-internal", "value-internal"], + ["type-parent", "type-sibling", "type-index"], + ["value-parent", "value-sibling", "value-index"], + "unknown" + ] + }, + "sortPackageJson": { + "sortScripts": true + } +} diff --git a/invokeai/frontend/webv2/.oxlintrc.json b/invokeai/frontend/webv2/.oxlintrc.json new file mode 100644 index 00000000000..9cde55d75f9 --- /dev/null +++ b/invokeai/frontend/webv2/.oxlintrc.json @@ -0,0 +1,200 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "react", "import", "unicorn", "oxc", "react-perf", "jsx-a11y", "eslint"], + "categories": { + "correctness": "error" + }, + "rules": { + "curly": "error", + "eqeqeq": "error", + + "import/no-cycle": "error", + "import/no-duplicates": "error", + "import/no-relative-parent-imports": "error", + "import/no-absolute-path": "error", + "import/no-amd": "error", + "import/no-commonjs": "error", + "import/no-mutable-exports": "error", + + "no-console": "warn", + "no-eval": "error", + "no-extend-native": "error", + "no-implied-eval": "error", + "no-label-var": "error", + "no-promise-executor-return": "error", + "no-return-assign": "error", + "no-sequences": "error", + "no-template-curly-in-string": "error", + "no-throw-literal": "error", + "no-unmodified-loop-condition": "error", + "no-var": "error", + + "prefer-template": "error", + "radix": "error", + "no-restricted-exports": "error", + // One resize affordance: the shared handle, not Chakra's splitter. + "no-restricted-imports": [ + "error", + { + "paths": [ + { + "name": "@chakra-ui/react", + "importNames": ["Splitter", "useSplitter", "useSplitterContext"], + "message": "Use ResizeHandle from @platform/ui/ResizeHandle." + } + ] + } + ], + + "react/jsx-curly-brace-presence": [ + "error", + { + "props": "never", + "children": "never" + } + ], + "react/no-children-prop": "error", + "react/no-danger": "error", + "react/no-unknown-property": "error", + "react/jsx-no-useless-fragment": "error", + "react/exhaustive-deps": "error", + + // Omit optimization-only todo and suppression diagnostics. Identity-trigger/ref-only dependencies use + // react/exhaustive-deps rather than compiler dependency rules. + "react/capitalized-calls": "error", + "react/error-boundaries": "error", + "react/globals": "error", + "react/hooks": "error", + "react/immutability": "error", + "react/incompatible-library": "error", + "react/invariant": "error", + "react/no-deriving-state-in-effects": "error", + "react/preserve-manual-memoization": "error", + "react/purity": "error", + "react/refs": "error", + "react/set-state-in-effect": "error", + "react/set-state-in-render": "error", + "react/static-components": "error", + "react/syntax": "error", + "react/unsupported-syntax": "error", + "react/use-memo": "error", + "react/void-use-memo": "error", + + "react-hooks/exhaustive-deps": "error", + "react-hooks/rules-of-hooks": "error", + + // Chakra/Ark polymorphic composites own keyboard semantics; native-tag substitutions can break them. Axe + // checks the resulting tree. + "jsx-a11y/prefer-tag-over-role": "off", + + "react-perf/jsx-no-new-object-as-prop": [ + "error", + { + "nativeAllowList": "all" + } + ], + "react-perf/jsx-no-new-function-as-prop": [ + "error", + { + "nativeAllowList": "all" + } + ], + "react-perf/jsx-no-new-array-as-prop": [ + "error", + { + "nativeAllowList": "all" + } + ], + "react-perf/jsx-no-jsx-as-prop": [ + "error", + { + "nativeAllowList": "all" + } + ], + + "require-await": "error", + + "typescript/consistent-type-imports": [ + "error", + { + "fixStyle": "separate-type-imports", + "prefer": "type-imports" + } + ], + "typescript/no-empty-interface": [ + "error", + { + "allowSingleExtends": true + } + ], + "typescript/consistent-type-assertions": [ + "error", + { + "assertionStyle": "as" + } + ], + "typescript/ban-ts-comment": "error", + "typescript/no-import-type-side-effects": "error", + + "unicorn/no-abusive-eslint-disable": "error", + "unicorn/error-message": "error", + "unicorn/throw-new-error": "error", + "unicorn/prefer-type-error": "error", + + "vitest/require-mock-type-parameters": "off" + }, + "overrides": [ + { + // Allow scoped autofocus for task-entry dialogs, authentication, and inline rename. + "files": [ + "src/features/gallery/ui/GalleryBoardMenu.tsx", + "src/features/identity/ui/LoginScreen.tsx", + "src/features/identity/ui/SetupScreen.tsx", + "src/features/identity/ui/UserFormDialog.tsx", + "src/features/workflow/ui/editor/AddNodeDialog.tsx", + "src/features/workflow/ui/editor/InvocationFlowNode.tsx", + "src/platform/ui/RenameDialog.tsx", + "src/workbench/palette/CommandPaletteDialog.tsx", + "src/workbench/widgets/layers/LayerListItem.tsx" + ], + "rules": { + "jsx-a11y/no-autofocus": "off" + } + }, + { + // Chakra primitives supply focusable DOM; palette options retain focus on the aria-activedescendant + // combobox. + "files": [ + "src/features/models/ui/components/ModelSelect.tsx", + "src/features/models/ui/shared/ModelFilterMenu.tsx", + "src/features/workflow/ui/editor/AddNodeDialog.tsx", + "src/workbench/palette/CommandPaletteDialog.tsx", + "src/workbench/palette/CommandPaletteRows.tsx" + ], + "rules": { + "jsx-a11y/interactive-supports-focus": "off" + } + } + ], + "env": { + "browser": true, + "builtin": true, + "es2022": true, + "node": true + }, + "globals": { + "GlobalCompositeOperation": "readonly", + "RequestInit": "readonly" + }, + "ignorePatterns": [ + "dist/**", + "node_modules/**", + "static/**", + ".husky/**", + "patches/**", + "stats.html", + "index.html", + ".yarn/**", + "**/*.scss" + ] +} diff --git a/invokeai/frontend/webv2/AGENTS.md b/invokeai/frontend/webv2/AGENTS.md new file mode 100644 index 00000000000..0991b56f375 --- /dev/null +++ b/invokeai/frontend/webv2/AGENTS.md @@ -0,0 +1,47 @@ +# Webv2 + +Read [ARCHITECTURE.md](ARCHITECTURE.md) for ownership/interface changes and affected owner-local READMEs for lifecycle/persistence. Stack: React 19, Vite, Chakra UI 3, TanStack Query/Router, @dnd-kit; legacy web differs. Webv2 is served by default; `--webv2` is a compatibility alias and `--web-legacy` selects `../webv1/`. + +Shared generated API contracts and their CI gates belong to [../api/AGENTS.md](../api/AGENTS.md). + +## Ownership and state + +- Follow `app -> workbench/features -> platform` and `src/architecture/` policy. App composes dependencies; Platform owns domain-neutral infrastructure; Features own domain behavior; Workbench owns the project aggregate, shell, and Canvas. +- Import Features through registered top-level public entries, never another owner's internals to avoid barrels. Pure cores exclude React/UI/transport dependencies, including type-only imports. +- Production Canvas callers use `canvas-engine/api.ts`. Contracts belong with their invariants' owner; consult the ownership manifest before adding Workbench modules. +- Map/serialize wire DTOs and persisted schemas explicitly. Do not turn compatibility re-exports into implementations or add architecture exceptions to pass checks. +- Query owns backend read models; non-React runtimes own orchestration/lifecycles. Use stable, narrowly selected external-store snapshots, not broad component-state mirrors. +- Preserve account/project isolation, stale-result fencing, revision conflicts, reconnects, and start/dispose ownership. No process-wide mutable per-account/project caches. +- For persistence, read `src/workbench/projects/README.md`: preserve backend authority, bounded recovery, cross-tab ownership, durable queue receipts, and explicit conflicts. + +## React and product quality + +- No direct `useEffect`: derive during render, act in event handlers, fetch through Query, subscribe via external-store adapters. +- `@platform/react/useMountEffect` is only for mount/unmount registration with stable dependencies and complete cleanup. No changing-dependency refs, wrapper aliases, or layout-effect evasions; layout effects require pre-paint DOM measurement. +- Migrate effects when changing their behavior, without unrelated sweeps. Identity changes use store/runtime lifecycles or deliberate keyed remounts without accidental state loss. +- Reuse Platform controls, Chakra/theme tokens, icons, and interactions; no second styling system or generic Next.js/SWR patterns. +- Include disabled states; preserve keyboard operation, visible/restored focus, accessible names, and pointer alternatives. +- Icon-only controls carry the platform `Tooltip`, including menu and popover triggers (share `useTooltipTriggerIds` between the root and the Tooltip); never a native `title`. +- Keep dense layouts usable across window sizes/zoom. Check overflow, long/localized text, selected/hover/active states, and themes; follow localization conventions. +- Keep interactions immediate and motion restrained, interruptible, and reduced-motion compatible. Specify transition properties; animation must neither delay editing nor be the sole state cue. +- Provide visual evidence of browser-inspected flows; browser tests and axe do not replace visual/interaction review. + +## Performance and validation + +- Inspect rendering/subscriptions, galleries/lists, pointer handlers, Canvas resources, lazy loading, and request scheduling. Use existing virtualization/lazy boundaries; memoize only identified costs. +- Preserve disposal, bounded memory, account transitions, and responsive input. Never update goldens/baselines to hide regressions. +- Run commands here or via `pnpm -C invokeai/frontend/webv2`. Install with `pnpm install --frozen-lockfile`. + +| Change/check | Command | +| ------------------------------------- | ------------------------------------------------------------------ | +| Formatting, lint, types, architecture | `pnpm lint` | +| Focused behavior | `pnpm test ` or `pnpm test:browser ` | +| Unit, Chromium, mock-backend fixtures | `pnpm test:all` | +| Architecture and browser performance | `pnpm test:performance:architecture` | +| Project-file integration | `pnpm test:project-files` | +| Accessibility in a fresh build | `pnpm test:accessibility` | +| Milestone/release completion | `pnpm check:release` | + +Install missing Chromium: `pnpm exec playwright install chromium` (`--with-deps` in Linux CI). Colocate unit `*.test.ts`/`*.test.tsx` and browser `*.browser.test.ts`/`*.browser.test.tsx` tests. Use real IndexedDB, Web Locks, focus, pointer, and rendering where mocks cannot establish correctness. + +Run focused checks during development and `check:release` before code-milestone readiness: lint, all tests, architecture performance/build, project-file journeys, and accessibility. Docs-only changes need formatting/link review, not application tests. Keep architecture inventories/performance reports in ignored `artifacts/`, never committed. diff --git a/invokeai/frontend/webv2/ARCHITECTURE.md b/invokeai/frontend/webv2/ARCHITECTURE.md new file mode 100644 index 00000000000..34d1e96da33 --- /dev/null +++ b/invokeai/frontend/webv2/ARCHITECTURE.md @@ -0,0 +1,118 @@ +# Webv2 module architecture + +Webv2 uses four top-level module groups: + +- `app/` owns providers, routing, boot, and composition roots. +- `platform/` owns application infrastructure with no workbench or feature dependency. +- `workbench/` owns the atomic project aggregate, layout, shell, widget runtime, and extension runtime. +- `features//` owns domain behavior behind a small set of public top-level entry modules (`features//index.ts` first among them). + +Dependency direction is `app -> workbench/features -> platform`. Feature callers import only a feature's public entry modules — the top-level modules each feature registers in `src/architecture/featureInterfaces.ts` (`index` is implicitly public; an unregistered feature or module is private, fail-closed); everything under `core/`, `data/`, `ui/`, and runtime implementation is private. A feature may group substantial behavior under `core/`, `data/`, and `ui/`; pure cores do not import transport, Data, React, or UI modules, including through type-only imports. Production canvas callers use `canvas-engine/api.ts`, not the engine implementation root. + +## App and Platform ownership + +Ownership is based on invariants, not the first caller: + +| Module | Owned implementation | Must not own | +| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| `app/` | `main`, provider assembly, routes and guards, route runtimes, adapter selection, cross-module dependency construction, theme/language DOM controllers | Domain behavior, transport implementation, reusable state or UI | +| `platform/transport` | HTTP primitives and URL resolution, injected auth hook, Socket.IO mechanics, connection snapshots | Auth token policy, Queue/model/node event interpretation | +| `platform/state` | React-free listener/store/selector cores and separate React hook adapters | Domain stores or aggregate state | +| `platform/query` | Query client construction and defaults | Feature query keys, DTOs, invalidation policy | +| `platform/i18n` | i18next bootstrap and supported-language metadata | Persisted preference ownership | +| `platform/logging` | Event contracts, recording filters, safe normalization, bounded in-memory history, subscriptions, console output, export envelope | Preference persistence, diagnostic presentation, event policy | +| `platform/ui` | Chakra system/theme, reusable controls, toaster, brand mark | Workbench widgets or feature-specific views | +| `platform/runtime` | Domain-neutral build/runtime metadata | Route or feature policy | +| `workbench/backend` | Queue/node/model progress interpretation and transient domain stores pending their feature migrations | HTTP, sockets, connection mechanics, generic stores | +| `workbench/components` | Workbench hydration and Queue progress presentation | Reusable control or theme implementation | + +The root of `src/` contains declaration files only. Runtime boot files live in App, the old root entry paths do not exist, and `index.html` names `src/app/main.tsx` directly. `@theme` resolves to Platform UI theme implementation; all other Platform callers use `@platform/*`. No compatibility path exists for moved App, HTTP, socket, external-store, selector, theme, i18n, or reusable-UI modules. + +Platform transport receives authentication through `app/main.tsx`, before React mounts: Auth owns token storage and unauthorized-session behavior, while Platform only requests a token and reports a 401. The Platform socket publishes raw events and connection transitions; Queue and other domains interpret those events. Lifecycle-specific production composition remains explicit at its natural root: the authenticated socket in `app/router.tsx`, editor runtimes in `app/WorkbenchApp.tsx`, feature UI providers in `app/workbenchPorts.tsx`, and lazy editor bridges beside their lazy entry modules. App constructs the production Queue runtime from Queue, Workbench, Gallery, and Workflow adapters. + +Platform State also owns reusable projections of broad external stores into referentially stable read-only stores. App may construct those projections for a feature port, but the listener, caching, and equality mechanics remain domain-neutral Platform code. + +Platform Logging is the one recorder for application and project events. `createLogger` binds a source (namespace, area, optional project, widget and operation identity); a runtime passes the account scope it captured so writes after that lifetime are discarded, while module-level loggers stay unbound. Entries are normalized once at ingestion into immutable JSON-safe snapshots: errors keep name, message, stack, cause and status/code, credential-like keys are redacted, URL queries and fragments are stripped, and depth, collection, string and 8 KiB entry ceilings are marked explicitly. History is account-wide and memory-only in three bounded buffers (warn and above, lower severities, timings) so verbose traffic cannot evict problems; closed projects keep their entries until eviction or account cleanup. Publication is coalesced per microtask; clears and account transitions publish synchronously and restore default capture settings. Workbench owns the preference-to-config mapping and the diagnostics panel; App applies saved preferences before React mounts, installs uncaught-error and router catch reporting, and keeps configuration in step with the active account. Each handled terminal failure has one reporting owner: the Workbench store records reported errors, widget failures, autosave outcomes and queue item transitions; transport and coordinator events are debug breadcrumbs. + +`pnpm architecture:check` is the executable source of truth for these dependency directions. It uses the TypeScript parser to classify static imports, type imports, dynamic imports, re-exports, and inline imported types after resolving aliases and relative paths. Temporary violations must be recorded in `src/architecture/migrationExceptions.ts` with an owner and a removal ticket; architecture completion requires that registry to be empty. + +The queue pilot is the first migrated feature. Its private `QueueBackendPort` is the adapter seam; public callers use domain reads, commands, and production runtime factories from `@features/queue`. Private snake_case HTTP DTOs map explicitly to camelCase Queue read models before crossing that interface. TanStack Query owns the global backend queue read model, and non-React runtimes own reconciliation and realtime orchestration. Persisted per-project queue history remains in the Workbench aggregate. + +Queue exposes board-independent active backend sessions, including preparation and result settlement. The Gallery progress section includes every unfinished batch slot, including queued work; Preview follows executing and settling sessions. Both use gallery-destination projections of the active project's queue; eligibility is presentation policy, separate from discovery. Workbench owns transient overview/pinned follow selection for its mounted lifetime, clears pins on project changes or when following stops, and never persists pins or preview frames. Live sessions do not participate in board counts, saved-image selection, or navigation. Saved outputs retain their actual backend board destinations; automatic result selection does not change the browsed board or its paging window. + +Intermediates owns the manager for temporary media behind `@features/intermediates`: transport mapping, account-fenced query keys, the selection core, the confirmation dialog and the operation panel. It is a Settings section, contributed as metadata through `settingsContribution` so the dialog's catalog never loads the manager or its transport until the section opens. The eligibility policy (safe, referenced, active, recent) is server-owned; the manager requests a server preview, which fixes the instant recency is judged at and describes what a cleanup would delete and keep, and confirms it once. "All matching" selections travel as a `matching` scope carrying the summary's filters and the excluded rows, which the server resolves; the client never enumerates pages it has not loaded. Operations are not persisted by the server: a manager that opens with nothing followed lists the account's operations and follows the newest unfinished one, so a reload picks a running cleanup back up, and a settled operation that left failures behind offers to delete its scope again. The client's one input to the policy is a browser hold lease: `holdLease` (a public entry, started by Workbench for the provider's lifetime) keeps the media open editors still need (live content from every open project plus the undo state each live Canvas engine retains) classified as active. Workbench owns the name set: `projects/projectAssets` scans each immutable project subtree once, and each Canvas engine registers its retained undo state with the mounted Workbench, which re-reads it when the history changes; an engine released by one Workbench is replaced, not reused, by the next, and one another Workbench still uses is refused. The lease is one per tab, holding up to the server's per-kind limit of sorted unique names, which the server replaces atomically; it skips unchanged sets without re-serialising them, forgets a send the server did not acknowledge so the next change, heartbeat or return to the foreground sends again while the previous lease still stands, refreshes every five minutes and when a tab becomes visible after missing a refresh, and releases with `keepalive` on dispose and `pagehide`; the server keeps at most eight leases per account and expires a lease after 15 minutes, which bounds anything a crash leaves behind. Realtime updates attach for the section's mounted lifetime through the Platform socket hub and invalidate the account's summaries when an operation settles, by either the socket or the polled record, or when the socket reconnects. Workbench supplies the entry points (the Data & workspace link, the project actions menu, the Users page callback) and hands the manager a one-shot focus intent (a project to preselect or an account to filter by) rather than route state. + +Fonts owns its authenticated catalog, managed-library UI, and browser font-registration lifetime behind `@features/fonts`. App composes the session runtime; Canvas receives a font-resolution port and owns text geometry, rendering, and output readiness. Persisted text references and project-file remapping remain in Workbench. Launchpad font previews and Canvas share the same account lifetime; font binaries and browser registration names are not project state. The browser registry admits at most 64 faces and 128 MiB of font input data, including faces currently being parsed. Active references are protected from eviction; if they exhaust the budget, further loads fail until references are released. This bounds retained input, not browser-specific decoded font memory. Canvas reconciles active references only when the document forests or text draft source change. + +Font runtime composition and project-file option state travel in the existing shared route chunk. Catalog transport, the Fonts page, and project-file option dialogs load on demand. Missing-font recovery loads only for documents containing custom references. The locale asset plugin serves font-library strings as a separate namespace in development and production, loaded when the library or recovery UI opens; source translations remain in the canonical locale files. Text tool controls load when selected; their shared Canvas/Layer form and interaction dependencies remain grouped to avoid extra widget-activation requests. Catalog query keys have a lightweight public entry so invalidating an imported font does not pull the transport into startup. + +Projects own their workflows. The Workbench aggregate keeps an ordered workflow collection with an active selection and one session edit history per workflow (`src/workbench/projectWorkflows.ts`); the project document is schema 3 and `migrateProjectDocument` in `projects/projectDocument.ts` is the single migration boundary for older documents. Workflow reads the active workflow through the App-composed port and edits, publishes and presents documents; it never replaces authored project work. The library is written only by the Workflow-owned publication controller, which captures the account, project, workflow and content before it starts, refuses to overlap itself per workflow, reconciles a lost answer against the destination record before resending, and records the resulting library id and revision on the originating workflow alone. Queue submissions carry the originating project workflow so seed advances, node progress and run previews return to it whatever is active later. + +Workbench persistence follows the same lifetime rule. `createWorkbenchPersistenceRuntime` owns hydration ordering, debounced autosave, revision/generation checks, conflict application, reconnect replay, status publication, and disposal without importing React. `createSyncedWorkbenchPersistence` constructs the local/remote project synchronization state for one mounted Workbench; no process-wide maps, sets, flags, or caches hold that state. `WorkbenchProvider` only constructs these lifetimes, starts/disposes the runtime through the mount lifecycle adapter, and selects aggregate hydration state. + +## Contract ownership + +Contracts live with the module that owns their invariants. The retired `workbench/types.ts` path is an architecture violation; Workbench aggregate, widget, Canvas, persistence, settings, and feature contracts each have an owner-local canonical path. Persisted schemas and backend DTOs remain private versioned/wire values and cross seams only through explicit serializers, hydrators, or mappers. + +`platform/core/outputImages.ts` is the explicit shared adapter for backend invocation-output image names. It is platform-owned because Queue and Nodes both need the projection and cannot import each other's private implementations. The adapter recognizes only the backend's direct image, image-collection, and workflow-return output contracts; it must not become a recursive walker for arbitrary metadata or input fields. + +Feature callers import a feature's public entry modules; imports of another feature's `core/`, `data/`, `ui/`, or runtime implementation are rejected outside a ticketed migration exception. Wildcard exports may aggregate files inside one owner, but may not cross an ownership seam. The contract caller matrix and development-invalidation budgets are generated by `pnpm architecture:check` and `pnpm test:performance:build` respectively. + +The byte budgets in `performance/architecture-baseline.json` and `performance/browser-baseline.json` record measurements only. Each byte ceiling is the lower of `max(1%, 4 KB)` over the **higher of the base reference and committed measurement**, and `max(10%, 32 KB)` over the committed measurement (`deriveLimit` in `scripts/performance-budgets.mjs`). Deliberately re-recording a higher baseline therefore accepts reviewed growth, such as a dependency upgrade, in the same PR. Growth beyond that allowance still fails. Previously recorded higher measurements remain accepted until a downward re-record protects the savings; the committed hard ceiling continues to bound cumulative growth in the base branch. Re-record with `pnpm test:performance:build:update-baseline` and `pnpm test:performance:browser:update-baseline`, and include the measured cost or savings in review. + +CI restores `main`'s own measurements from the Actions cache (`WEBV2_PERF_REFERENCE_DIR`) for pull requests into `main` and merge groups targeting `main`. Logs and reports name the restored reference. Local runs, pushes to `main`, and stacked PRs without a reference compare against committed measurements directly. Source-owner sets stay pinned exactly in every mode: a module entering a route's initial graph fails at zero bytes. Request counts carry no allowance and remain capped at the lower of the reference and committed counts. A byte rebaseline does not relax request counts, source ownership, inactive-widget loading, or other structural checks. + +Compatibility exports under `workbench/` are temporary. New behavior belongs behind the target module interface and compatibility files must not acquire new implementation logic. + +## Workbench ownership destinations + +`src/architecture/workbenchOwnershipManifest.json` is the executable current-to-target ownership manifest. `pnpm architecture:check` proves that every production Workbench module has exactly one target owner and writes the detailed import/export inventory to `artifacts/architecture/workbench-ownership-inventory.json`. The generated inventory is regenerated as modules change; tests and type-test fixtures are intentionally excluded. + +| Current Workbench path | Target owner | Split or locality decision | +| ---------------------------------------------------------------------------------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| aggregate root modules, `projects`, `persistence`, `settings` | Workbench | Atomic project/account state and its persistence policy remain local to the aggregate. Persistence mechanics move behind the aggregate's adapter. | +| `shell`, `components/WorkbenchSplashScreen`, `launchpad` shell, `widget-frame`, non-domain `widgets` | Workbench | Route-independent shell, layout, widget hosts, and registration are Workbench implementation. | +| `extensions`, `hotkeys`, `diagnostics` | Workbench | These enforce the Workbench extension runtime; they are not independent vertical domains. | +| `canvas-engine`, `canvas-operations`, Canvas/layers widgets, root Canvas modules | Workbench | Canvas is a deep internal Workbench module behind `canvas-engine/api.ts`, not another top-level Feature. | +| `image-actions`, `images` | Workbench | Cross-surface image orchestration and transient display stay internal until a second independent implementation justifies a seam. | +| `auth`, `users`, Launchpad Users page | `feature:identity` | Session/token policy and user administration form one identity vocabulary. | +| `models`, Launchpad model manager/page, `backend/modelLoadStore` | `feature:models` | Model catalog, install/load lifecycle, and model administration co-locate. | +| `customNodes`, Launchpad node manager/page, `backend/nodeExecutionStore` | `feature:nodes` | Node catalog/template and execution lifecycle co-locate. | +| `backend` except model/node stores, queue root modules, queue widget, Queue progress component | `feature:queue` | Queue owns server-event interpretation, transient progress, submission, reconciliation, and Queue UI. Persisted project queue history stays in Workbench. | +| `gallery`, Gallery widget | `feature:gallery` | Gallery data, selection policy, and Gallery UI form one vertical module. | +| `generation`, Generate widget | `feature:generation` | Generation settings, graph compilation, and Generate UI form one vertical module. | +| `workflows`, `graph-preview`, Workflow widget | `feature:workflow` | Workflow documents, compilation, preview, editor, and library form one vertical module. | +| `upscale`, Upscale widget | `feature:upscale` | Upscale settings, validation, compilation, and UI form one vertical module. | +| `hooks/useMountEffect`, root `useModifierHeld` | Platform | These are domain-neutral React lifecycle/input adapters and must have no Workbench import. | + +The mixed-folder rules are longest-prefix rules in the manifest, so the Backend, Components, Launchpad, and Widgets splits are explicit rather than inferred from imports. Root modules are listed individually. Compatibility re-exports keep the target owner of the implementation they forward and are deletion work, never a fifth owner. + +The target dependency graph intentionally records the current migration cycle: Workbench and the eight destination Features are one transitional strongly connected component. That is evidence for the cycle-breaking sequence, not permission for the destination architecture to retain the cycle. The migration must replace implementation imports with public module interfaces or composition-root adapters until the only direction is `app -> workbench/features -> platform`. + +Feature interfaces remain ordinary module interfaces unless two adapters add leverage. Queue's backend port has production transport/realtime and test adapters; Identity's token/session port has browser-session and test adapters; Models and Nodes use production event/transport adapters; their sinks (model-load activity, node execution) are injected into Queue's runtime at the App composition root, with test doubles in Queue's runtime tests. Gallery, Generation, Workflow, and Upscale keep their compilation and UI seams internal until their feature tickets demonstrate another independent adapter. Workbench image actions likewise remain internal rather than becoming a shallow Image Feature. Gallery's picker and media slots read a narrow `GalleryHost` (gallery scope, notifications, an optional Gallery-widget reveal): the workbench derives it from the Gallery UI adapter, and the Launchpad's model manager, which has no workbench, supplies one itself. Media slots accept dragged gallery items only inside `GalleryDragScope`, which the workbench shell provides with its drag context; widget dialog hosts and the Launchpad sit outside it. + +| Feature | Responsibility and representative callers | Allowed dependencies and candidate public interface | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Identity | Session/token policy and user administration; App guards/login, Queue capabilities, Workbench account/Launchpad surfaces | Platform plus Models' public capability reads; session reads/commands, token port, user queries and manager contribution | +| Models | Catalog, configuration, install/load lifecycle; Generation, Queue, Workflow, Upscale, Identity, model manager | Platform plus Identity's public authorization reads and Gallery's public picker; descriptors/capabilities, catalog/load commands and manager contribution | +| Nodes | Custom-node catalog/templates and execution lifecycle; Workflow editor and node manager | Platform; descriptors, catalog commands, execution snapshots and manager contribution | +| Queue | Backend queue state, realtime interpretation, submission/reconciliation and Queue UI; App runtime, Workbench aggregate/shell, Generation/Gallery/Workflow | Platform and public envelopes/capabilities from Identity, Models, Generation, Workflow, Upscale; backend port, queue reads/commands and widget contribution | +| Gallery | Image collection, selection, board policy and Gallery UI; App adapter, Generation, Workflow, Upscale, Models, Workbench image actions | Platform and Queue's public progress reads; gallery reads/commands and widget contribution | +| Generation | Generation settings, validation, graph compilation and Generate UI; Workbench invocation, Queue, Gallery, Workflow, Upscale | Platform and public Models/Gallery/Queue interfaces; settings/validation, compiled submission envelope and widget contribution | +| Workflow | Workflow document, validation, compilation, preview/editor/library and explicit publication; App adapter, Workbench invocation, Nodes | Platform and public Nodes/Models/Gallery/Queue/Generation interfaces; document reads/commands, compiled submission envelope and widget contribution | +| Upscale | Upscale settings, validation, compilation and UI; Workbench invocation and Queue | Platform and public Models/Gallery/Queue interfaces; value/validation reads, submission command and widget contribution | +| Intermediates | Per-project intermediates read models, cleanup previews/operations and the Settings section that manages them; Workbench settings, project menus, account administration | Platform plus Identity's public session/capability reads; query keys, read models, the focus intent, the browser hold lease and the `settingsContribution` metadata whose loader owns the section body | + +## Settings contributions + +Settings are declared by their owners and presented by Workbench. Platform UI owns domain-neutral field metadata and controls; it does not own setting values or persistence. A widget manifest exposes a `settings` contribution containing localized field metadata, an ordered `quick` subset, and a deferred field binding loader. The full dialog includes every declared field. Bindings retain the owner's normalization and command path, including Gallery's size/density conversion and project/instance fencing. + +Workbench composes widget and application contributions into a metadata-only catalog. Search, dialog navigation, quick popovers, and command-palette destinations share that catalog. Features publish contribution metadata through registered public entries; loading metadata must not load their UI or start data queries. Application-owned additions can join a widget section without changing the owner of their values. Deferred resources cache code only; field subscriptions exist only while their controls are mounted. + +Section placement does not determine storage lifetime. Account/install preferences, project settings, widget-instance values, and server configuration retain their existing persistence and authorization. Each surface lists only settings it can edit: the Launchpad Preferences page leaves out project and widget settings, and the workbench dialog leaves out widget settings for widget types the open project lacks. The shell resolves explicit project/instance targets and disposes target-bound editing when projects change. Owner bindings fence writes against stale account/project lifetimes. + +The settings host owns the dialog's presence lifecycle and keeps its root mounted through closing. The body is deferred until first opening, remains present during exit, and unmounts afterward. Quick-popover handoff carries both the instance target and the original gear for focus restoration. + +Before section navigation becomes available, the deferred dialog prepares binding/editor code without mounting editors or starting their queries. Section and scroll position survive closing within the account lifetime. The shell renders a shared modified-setting indicator; bindings determine whether a value differs from the owner’s canonical default. Quick settings use the shared arrowed popover with an inner scroll area, and dialog selects keep their bounded scroll areas inside the modal focus scope. + +Production chunking keeps the catalog and manifest metadata separate from editor boot UI. The build gate checks that Launchpad’s palette and settings dialog do not statically include the editor application or widget renderer. Shared settings controls remain in the common UI chunk; owner field bindings and custom editors remain deferred. diff --git a/invokeai/frontend/webv2/OXC_RULE_COMPATIBILITY.md b/invokeai/frontend/webv2/OXC_RULE_COMPATIBILITY.md new file mode 100644 index 00000000000..2af36ab0b91 --- /dev/null +++ b/invokeai/frontend/webv2/OXC_RULE_COMPATIBILITY.md @@ -0,0 +1,23 @@ +# OXC Rule Compatibility + +`webv2` uses `oxlint` and `oxfmt` instead of ESLint and Prettier. + +The config mirrors the existing Invoke web lint intent where OXC supports equivalent rules: + +- React and hooks checks: `react/jsx-no-bind`, `react/jsx-curly-brace-presence`, `react-hooks/*`. +- TypeScript checks: `typescript/consistent-type-imports`, `typescript/no-empty-interface`, plus TypeScript compile checks through `tsc --noEmit`. +- Import checks: `import/no-duplicates`, `import/no-cycle`. +- General correctness/style checks: `curly`, `no-var`, `prefer-template`, `radix`, `eqeqeq`, `no-eval`, `no-extend-native`, `no-implied-eval`, `no-label-var`, `no-return-assign`, `no-sequences`, `no-template-curly-in-string`, `no-throw-literal`, `no-unmodified-loop-condition`, `no-console`, `no-promise-executor-return`, and `require-await`. +- Formatting: `oxfmt` uses the same print width, tab width, semicolon, single quote, and trailing comma preferences as the existing web formatter. It uses `lf` line endings because `oxfmt` does not support Prettier's `auto` value. + +Known unsupported or intentionally deferred equivalents: + +- `brace-style`, `one-var`, and `react/jsx-no-bind`: oxlint 1.69.0 does not expose these ESLint/plugin rule names. +- `simple-import-sort/*`: oxlint does not currently provide the same configurable import sorting behavior. +- `unused-imports/no-unused-imports`: oxlint reports unused bindings, but it is not the same plugin rule. +- `@typescript-eslint/ban-ts-comment`: no exact oxlint equivalent is configured yet. +- `@typescript-eslint/no-import-type-side-effects`: no exact oxlint equivalent is configured yet. +- `@typescript-eslint/consistent-type-assertions`: no exact oxlint equivalent is configured yet. +- `path/no-relative-imports`: no exact oxlint equivalent is configured yet. +- `no-restricted-syntax`, `no-restricted-properties`, `no-restricted-imports`: no exact oxlint config equivalent is configured yet. +- `i18next/no-literal-string` and Storybook-specific overrides: not configured for the initial `webv2` shell. diff --git a/invokeai/frontend/webv2/__init__.py b/invokeai/frontend/webv2/__init__.py new file mode 100644 index 00000000000..333fba38183 --- /dev/null +++ b/invokeai/frontend/webv2/__init__.py @@ -0,0 +1 @@ +"""Default InvokeAI frontend package.""" diff --git a/invokeai/frontend/webv2/index.html b/invokeai/frontend/webv2/index.html new file mode 100644 index 00000000000..87c2ce44e31 --- /dev/null +++ b/invokeai/frontend/webv2/index.html @@ -0,0 +1,59 @@ + + + + + + + Invoke V7 Workbench + + + + + +
+ + + diff --git a/invokeai/frontend/webv2/package.json b/invokeai/frontend/webv2/package.json new file mode 100644 index 00000000000..d1749ef2cc1 --- /dev/null +++ b/invokeai/frontend/webv2/package.json @@ -0,0 +1,97 @@ +{ + "name": "@invoke-ai/invoke-ai-webv2", + "version": "0.0.1", + "private": true, + "type": "module", + "imports": { + "#architecture/source-analysis": "./src/architecture/tsSourceAnalysis.ts" + }, + "scripts": { + "architecture:check": "vitest run --config vitest.config.mts src/architecture/dependencyPolicy.test.ts src/architecture/workbenchOwnership.test.ts src/architecture/architectureWorkflow.test.ts src/architecture/canvasSeamOwnership.test.ts src/architecture/tsSourceAnalysis.test.ts src/architecture/secureContextApis.test.ts src/architecture/axeAudits.test.ts && node --experimental-strip-types scripts/write-workbench-ownership-inventory.mjs && node --experimental-strip-types scripts/write-contract-caller-matrix.mjs && node --experimental-strip-types scripts/write-canvas-import-matrix.mjs", + "build": "pnpm run lint && vite build", + "check:architecture": "pnpm run lint && pnpm run test:all && pnpm run test:performance:architecture && pnpm run test:project-files:browser", + "check:release": "pnpm run check:architecture && pnpm run test:accessibility:browser", + "dev": "vite dev", + "dev:host": "vite dev --host", + "fix": "pnpm run lint:oxc:fix && pnpm run format:write", + "format:check": "oxfmt --check .", + "format:write": "oxfmt --write .", + "i18n:report": "node scripts/i18n-report.mjs", + "i18n:report:verbose": "node scripts/i18n-report.mjs --verbose", + "lint": "pnpm run format:check && pnpm run lint:oxc && pnpm run lint:tsc && pnpm run architecture:check", + "lint:oxc": "oxlint --react-plugin --import-plugin --jsx-a11y-plugin --deny-warnings .", + "lint:oxc:fix": "oxlint --react-plugin --import-plugin --jsx-a11y-plugin --fix .", + "lint:tsc": "tsc --noEmit", + "preview": "vite preview", + "test": "vitest run --config vitest.config.mts", + "test:accessibility": "pnpm run test:accessibility:build && pnpm run test:accessibility:browser", + "test:accessibility:browser": "node scripts/run-accessibility-journeys.mjs", + "test:accessibility:build": "vite build", + "test:all": "pnpm run test && pnpm run test:browser && pnpm run test:fixtures", + "test:browser": "vitest run --config vitest.browser.config.mts", + "test:browser:update-goldens": "CANVAS_GOLDEN_UPDATE=1 vitest run --config vitest.browser.config.mts src/workbench/canvas-engine/render/golden", + "test:fixtures": "node --test scripts/mock-backend-fixtures.test.mjs scripts/preview-server.test.mjs scripts/run-project-file-journey.test.mjs scripts/locale-assets-plugin.test.mjs", + "test:performance:architecture": "pnpm run test:performance:build && pnpm run test:performance:browser", + "test:performance:browser": "node scripts/measure-architecture-performance.mjs", + "test:performance:browser:update-baseline": "node scripts/measure-architecture-performance.mjs --update-baseline", + "test:performance:build": "pnpm run test:performance:contracts && vite build && node --experimental-strip-types scripts/check-architecture-performance.mjs", + "test:performance:build:update-baseline": "pnpm run test:performance:contracts && vite build && node --experimental-strip-types scripts/check-architecture-performance.mjs --update-baseline", + "test:performance:contracts": "node --test scripts/performance-budgets.test.mjs", + "test:project-files": "vite build && pnpm run test:project-files:browser", + "test:project-files:browser": "node scripts/run-project-file-journey.mjs" + }, + "dependencies": { + "@chakra-ui/react": "^3.37.0", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@emotion/react": "^11.14.0", + "@fontsource/inter": "^5.3.0", + "@tanstack/react-query": "^5.102.8", + "@tanstack/react-router": "^1.170.36", + "@tanstack/react-virtual": "^3.14.13", + "@xyflow/react": "^12.11.6", + "ag-psd": "^31.0.2", + "fflate": "^0.8.3", + "fuzzysort": "^4.0.2", + "html-to-image": "^1.11.13", + "i18next": "^26.4.2", + "i18next-http-backend": "^4.0.2", + "idb": "^8.0.3", + "lucide-react": "^1.46.0", + "perfect-freehand": "^1.2.3", + "plotly.js-gl2d-dist-min": "^4.1.1", + "react": "^19.3.0", + "react-dom": "^19.3.0", + "react-hook-tanstack-virtual": "^0.0.4", + "react-i18next": "^17.0.14", + "socket.io-client": "^4.8.3", + "tinykeys": "^4.0.0", + "use-sync-external-store": "^1.7.0", + "yaml": "^2.9.1", + "zod": "^4.6.5" + }, + "devDependencies": { + "@rolldown/plugin-babel": "^0.2.4", + "@types/node": "^26.5.1", + "@types/plotly.js": "^3.0.13", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@types/use-sync-external-store": "^1.7.0", + "@vitejs/plugin-react": "^6.1.1", + "@vitest/browser-playwright": "5.0.0", + "axe-core": "^4.13.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxfmt": "^0.68.0", + "oxlint": "^1.83.0", + "playwright": "1.63.0", + "typescript": "^7.0.2", + "vite": "^8.3.0", + "vitest": "5.0.0" + }, + "engines": { + "pnpm": "10" + }, + "packageManager": "pnpm@10.12.4" +} diff --git a/invokeai/frontend/webv2/patches/react-hook-tanstack-virtual.patch b/invokeai/frontend/webv2/patches/react-hook-tanstack-virtual.patch new file mode 100644 index 00000000000..ca01485a9fc --- /dev/null +++ b/invokeai/frontend/webv2/patches/react-hook-tanstack-virtual.patch @@ -0,0 +1,66 @@ +diff --git a/dist/useVirtualizerBase-C3OWQGkj.js b/dist/useVirtualizerBase-C3OWQGkj.js +index 6e8df8ec5304bea75001914bccd115c745a96a44..2cdf2ca421952ab4be1a36cf755fac792833cd20 100644 +--- a/dist/useVirtualizerBase-C3OWQGkj.js ++++ b/dist/useVirtualizerBase-C3OWQGkj.js +@@ -1,2 +1,2 @@ +-import{Virtualizer as e}from"@tanstack/virtual-core";import*as t from"react";import{useCallback as n,useInsertionEffect as r,useMemo as i,useRef as a,useState as o,useSyncExternalStore as s}from"react";import{flushSync as c}from"react-dom";const l=(e,...t)=>e,u=(e,t)=>{if(e.size!==t.size)return!1;for(let[n,r]of e){let e=t.get(n);if(r!==e||!Object.is(r,e))return!1}return!0},d=(e,t)=>{if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0},f=(e,t)=>{if(e===t||Object.is(e,t))return!0;if(typeof e!=`object`||!e||typeof t!=`object`||!t)return!1;if(e instanceof Map&&t instanceof Map)return u(e,t);if(e instanceof Set&&t instanceof Set)return d(e,t);let n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;for(let r of n){if(!Object.hasOwn(t,r))return!1;let{[r]:n}=e,{[r]:i}=t;if(n!==i||!Object.is(n,i))return!1}return!0},p=typeof document<`u`?t.useLayoutEffect:()=>{},m=e=>{let[t,n]=o(e);return f(t,e)||n(e),t};let h=1,g=!1;const _=new Map,v=({callback:e,args:t})=>e.apply(void 0,t),y=e=>{if(g){x(y,e);return}let t=_.get(e);if(t){g=!0;try{v(t)}finally{S(e),g=!1}}},b=`scheduler`in globalThis?{setImmediate:(e,...t)=>{let n=h++;return _.set(n,{callback:e,args:t}),scheduler.postTask(()=>y(n),{priority:`user-blocking`}),n},clearImmediate:e=>{_.delete(e)}}:(()=>{let e=new MessageChannel;e.port1.onmessage=function(e){let t=e.data;y(t)};let t=e.port2;return{setImmediate:(e,...n)=>{let r=h++;return _.set(r,{callback:e,args:n}),t.postMessage(r),r},clearImmediate:e=>{_.delete(e)}}})(),x=b.setImmediate,S=b.clearImmediate;let C=1;const w=new Map,T=({callback:e,args:t})=>e.apply(void 0,t),E=e=>{let t=w.get(e);if(t)try{T(t)}finally{O(e)}},D=(e,...t)=>{let n=C++;return w.set(n,{callback:e,args:t}),requestAnimationFrame(()=>x(()=>E(n))),n},O=e=>{w.delete(e)};function k(e){let[t,r]=o(e);return[t,n(e=>(r(e),()=>{r(null)}),[])]}const A=({getVirtualItems:e,getVirtualIndexes:t,getTotalSize:n,...r})=>({...r,virtualItems:e(),virtualIndexes:t(),totalSize:n()}),j=({options:e,virtualIndexes:t,virtualItems:n,...r},{options:i,virtualIndexes:a,virtualItems:o,...s})=>f(e,i)&&f(t,a)&&f(n,o)&&f(r,s),M=({useFlushSync:t=!0,directDomUpdates:u=!1,directDomUpdatesMode:d=`transform`,...h},g=l,_=f)=>{"use no memo";let v=m(h),{horizontal:y=!1,scrollMargin:b=0}=v,x=y?`width`:`height`,S=d===`transform`,[C,w]=o(null),[T,E]=k(null),[M]=o(()=>new WeakMap),[N]=o(()=>new WeakMap),[P]=o(()=>new e(v)),F=i(()=>Object.assign(P,{containerRef:E}),[P,E]);p(()=>{if(!u||!T||C==null)return;let e=``,t=D(()=>{e=T.style.getPropertyValue(x),t=requestAnimationFrame(()=>{T.style.setProperty(x,`${C}px`)})});return()=>{w(null),cancelAnimationFrame(t),O(t),requestAnimationFrame(()=>{T?.style.setProperty(x,e)})}},[x,T,u,C]),p(()=>{if(!(!u||!T))return()=>{requestAnimationFrame(()=>{let e=F.getVirtualItems();for(let t of e){let e=F.elementsCache.get(t.key);if(!e)continue;M.delete(e);let n=N.get(e);n!=null&&(N.delete(e),S?e.style.setProperty(`transform`,n):e.style.setProperty(x,n))}})}},[x,T,u,F,M,N,S]);let I=a({hasValue:!1,value:null}),[L]=o(()=>new Set),R=n(e=>(L.add(e),()=>{L.delete(e)}),[L]),z=i(()=>{let e=!1,t,n;return()=>{let r=A(F);if(!e){e=!0,t=r;let i=g(r,F);if(I.current.hasValue){let e=I.current.value;if(_(e,i))return n=e,e}return n=i,i}let i=t,a=n;if(j(i,r))return a;let o=g(r,F);return _!==void 0&&_(a,o)?(t=r,a):(t=r,n=o,o)}},[g,_,F]),B=n(e=>{if(!u||!T)return;w(e.getTotalSize());let t=e.getVirtualItems();for(let n of t){let t=n.start-b,r=e.elementsCache.get(n.key);r&&M.get(r)!==t&&(N.has(r)||(S?N.set(r,r.style.getPropertyValue(`transform`)):N.set(r,r.style.getPropertyValue(x))),M.set(r,t),S?r.style.setProperty(`transform`,y?`translate3d(${t}px, 0, 0)`:`translate3d(0, ${t}px, 0)`):r.style.setProperty(x,`${t}px`))}},[x,T,u,y,M,N,b,S]),V=n((e,n)=>{if(B(e),t&&n)c(()=>{for(let e of L)e()});else for(let e of L)e();v.onChange?.call(void 0,e,n)},[B,L,v.onChange,t]),H=i(()=>({...v,onChange:V}),[V,v]);p(()=>{F.setOptions(H)},[F,H]),p(()=>F._didMount(),[F]),p(()=>F._willUpdate()),p(()=>{B(F)});let U=s(R,z);return r(()=>{I.current.hasValue=!0,I.current.value=U},[U]),U};export{M as t}; ++import{Virtualizer as e}from"@tanstack/virtual-core";import*as t from"react";import{useCallback as n,useInsertionEffect as r,useMemo as i,useRef as a,useState as o,useSyncExternalStore as s}from"react";import{flushSync as c}from"react-dom";const l=(e,...t)=>e,u=(e,t)=>{if(e.size!==t.size)return!1;for(let[n,r]of e){let e=t.get(n);if(r!==e||!Object.is(r,e))return!1}return!0},d=(e,t)=>{if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0},f=(e,t)=>{if(e===t||Object.is(e,t))return!0;if(typeof e!=`object`||!e||typeof t!=`object`||!t)return!1;if(e instanceof Map&&t instanceof Map)return u(e,t);if(e instanceof Set&&t instanceof Set)return d(e,t);let n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;for(let r of n){if(!Object.hasOwn(t,r))return!1;let{[r]:n}=e,{[r]:i}=t;if(n!==i||!Object.is(n,i))return!1}return!0},p=typeof document<`u`?t.useLayoutEffect:()=>{},m=e=>{let[t,n]=o(e);return f(t,e)||n(e),t};let h=1,g=!1;const _=new Map,v=({callback:e,args:t})=>e.apply(void 0,t),y=e=>{if(g){x(y,e);return}let t=_.get(e);if(t){g=!0;try{v(t)}finally{S(e),g=!1}}},b=`scheduler`in globalThis?{setImmediate:(e,...t)=>{let n=h++;return _.set(n,{callback:e,args:t}),scheduler.postTask(()=>y(n),{priority:`user-blocking`}),n},clearImmediate:e=>{_.delete(e)}}:(()=>{let e=new MessageChannel;e.port1.onmessage=function(e){let t=e.data;y(t)};let t=e.port2;return{setImmediate:(e,...n)=>{let r=h++;return _.set(r,{callback:e,args:n}),t.postMessage(r),r},clearImmediate:e=>{_.delete(e)}}})(),x=b.setImmediate,S=b.clearImmediate;let C=1;const w=new Map,T=({callback:e,args:t})=>e.apply(void 0,t),E=e=>{let t=w.get(e);if(t)try{T(t)}finally{O(e)}},D=(e,...t)=>{let n=C++;return w.set(n,{callback:e,args:t}),requestAnimationFrame(()=>x(()=>E(n))),n},O=e=>{w.delete(e)};function k(e){let[t,r]=o(e);return[t,n(e=>(r(e),()=>{r(null)}),[])]}const A=({getVirtualItems:e,getVirtualIndexes:t,getTotalSize:n,...r})=>({...r,virtualItems:e(),virtualIndexes:t(),totalSize:n()}),j=({options:e,virtualIndexes:t,virtualItems:n,...r},{options:i,virtualIndexes:a,virtualItems:o,...s})=>f(e,i)&&f(t,a)&&f(n,o)&&f(r,s),M=({useFlushSync:t=!0,directDomUpdates:u=!1,directDomUpdatesMode:d=`transform`,...h},g=l,_=f)=>{"use no memo";let v=m(h),{horizontal:y=!1,scrollMargin:b=0}=v,x=y?`width`:`height`,S=d===`transform`,[C,w]=o(null),[T,E]=k(null),[M]=o(()=>new WeakMap),[N]=o(()=>new WeakMap),[P]=o(()=>new e(v)),F=i(()=>Object.assign(P,{containerRef:E}),[P,E]);p(()=>{if(!u||!T||C==null)return;let e=``,t=D(()=>{e=T.style.getPropertyValue(x),t=requestAnimationFrame(()=>{T.style.setProperty(x,`${C}px`)})});return()=>{w(null),cancelAnimationFrame(t),O(t),requestAnimationFrame(()=>{T?.style.setProperty(x,e)})}},[x,T,u,C]),p(()=>{if(!(!u||!T))return()=>{requestAnimationFrame(()=>{let e=F.getVirtualItems();for(let t of e){let e=F.elementsCache.get(t.key);if(!e)continue;M.delete(e);let n=N.get(e);n!=null&&(N.delete(e),S?e.style.setProperty(`transform`,n):e.style.setProperty(x,n))}})}},[x,T,u,F,M,N,S]);let I=a({hasValue:!1,value:null}),[L]=o(()=>new Set),R=n(e=>(L.add(e),()=>{L.delete(e)}),[L]),z=i(()=>{let e=!1,t,n;return()=>{let r=A(F);if(!e){e=!0,t=r;let i=g(r,F);if(I.current.hasValue){let e=I.current.value;if(_(e,i))return n=e,e}return n=i,i}let i=t,a=n;if(j(i,r))return a;let o=g(r,F);return _!==void 0&&_(a,o)?(t=r,a):(t=r,n=o,o)}},[g,_,F]),B=n(e=>{if(!u||!T)return;w(e.getTotalSize());let t=e.getVirtualItems();for(let n of t){let t=n.start-b,r=e.elementsCache.get(n.key);r&&M.get(r)!==t&&(N.has(r)||(S?N.set(r,r.style.getPropertyValue(`transform`)):N.set(r,r.style.getPropertyValue(x))),M.set(r,t),S?r.style.setProperty(`transform`,y?`translate3d(${t}px, 0, 0)`:`translate3d(0, ${t}px, 0)`):r.style.setProperty(x,`${t}px`))}},[x,T,u,y,M,N,b,S]),V=n((e,n)=>{if(B(e),t&&n)c(()=>{for(let e of L)e()});else for(let e of L)e();v.onChange?.call(void 0,e,n)},[B,L,v.onChange,t]),H=i(()=>({...v,onChange:V}),[V,v]);p(()=>{F.setOptions(H),F.maybeNotify()},[F,H]),p(()=>F._didMount(),[F]),p(()=>{const e=F._willUpdate(),t=F.scrollElement;if(t&&`scrollTop`in t&&!F.isScrolling){const e=F.options.horizontal?t.scrollLeft*(F.options.isRtl?-1:1):t.scrollTop;F.scrollOffset!==null&&Math.abs(F.scrollOffset-e)>1&&(F.scrollOffset=e,F.scrollAdjustments=0,F.maybeNotify())}return e}),p(()=>{B(F)});let U=s(R,z);return r(()=>{I.current.hasValue=!0,I.current.value=U},[U]),U};export{M as t}; + //# sourceMappingURL=useVirtualizerBase-C3OWQGkj.js.map +\ No newline at end of file +diff --git a/dist/useVirtualizerBase-K4A1fZan.cjs b/dist/useVirtualizerBase-K4A1fZan.cjs +index 69494e5c083f991a4b7417cd2b6a216be3bf00e6..df02f4d2032b807b6b2c2fc3881051254a55fb71 100644 +--- a/dist/useVirtualizerBase-K4A1fZan.cjs ++++ b/dist/useVirtualizerBase-K4A1fZan.cjs +@@ -1,1 +1,1 @@ +-var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("@tanstack/virtual-core"),l=require("react");l=s(l,1);let u=require("react-dom");const d=(e,...t)=>e,f=(e,t)=>{if(e.size!==t.size)return!1;for(let[n,r]of e){let e=t.get(n);if(r!==e||!Object.is(r,e))return!1}return!0},p=(e,t)=>{if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0},m=(e,t)=>{if(e===t||Object.is(e,t))return!0;if(typeof e!=`object`||!e||typeof t!=`object`||!t)return!1;if(e instanceof Map&&t instanceof Map)return f(e,t);if(e instanceof Set&&t instanceof Set)return p(e,t);let n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;for(let r of n){if(!Object.hasOwn(t,r))return!1;let{[r]:n}=e,{[r]:i}=t;if(n!==i||!Object.is(n,i))return!1}return!0},h=typeof document<`u`?l.useLayoutEffect:()=>{},g=e=>{let[t,n]=(0,l.useState)(e);return m(t,e)||n(e),t};let _=1,v=!1;const y=new Map,b=({callback:e,args:t})=>e.apply(void 0,t),x=e=>{if(v){C(x,e);return}let t=y.get(e);if(t){v=!0;try{b(t)}finally{w(e),v=!1}}},S=`scheduler`in globalThis?{setImmediate:(e,...t)=>{let n=_++;return y.set(n,{callback:e,args:t}),scheduler.postTask(()=>x(n),{priority:`user-blocking`}),n},clearImmediate:e=>{y.delete(e)}}:(()=>{let e=new MessageChannel;e.port1.onmessage=function(e){let t=e.data;x(t)};let t=e.port2;return{setImmediate:(e,...n)=>{let r=_++;return y.set(r,{callback:e,args:n}),t.postMessage(r),r},clearImmediate:e=>{y.delete(e)}}})(),C=S.setImmediate,w=S.clearImmediate;let T=1;const E=new Map,D=({callback:e,args:t})=>e.apply(void 0,t),O=e=>{let t=E.get(e);if(t)try{D(t)}finally{A(e)}},k=(e,...t)=>{let n=T++;return E.set(n,{callback:e,args:t}),requestAnimationFrame(()=>C(()=>O(n))),n},A=e=>{E.delete(e)};function j(e){let[t,n]=(0,l.useState)(e);return[t,(0,l.useCallback)(e=>(n(e),()=>{n(null)}),[])]}const M=({getVirtualItems:e,getVirtualIndexes:t,getTotalSize:n,...r})=>({...r,virtualItems:e(),virtualIndexes:t(),totalSize:n()}),N=({options:e,virtualIndexes:t,virtualItems:n,...r},{options:i,virtualIndexes:a,virtualItems:o,...s})=>m(e,i)&&m(t,a)&&m(n,o)&&m(r,s),P=({useFlushSync:e=!0,directDomUpdates:t=!1,directDomUpdatesMode:n=`transform`,...r},i=d,a=m)=>{"use no memo";let o=g(r),{horizontal:s=!1,scrollMargin:f=0}=o,p=s?`width`:`height`,_=n===`transform`,[v,y]=(0,l.useState)(null),[b,x]=j(null),[S]=(0,l.useState)(()=>new WeakMap),[C]=(0,l.useState)(()=>new WeakMap),[w]=(0,l.useState)(()=>new c.Virtualizer(o)),T=(0,l.useMemo)(()=>Object.assign(w,{containerRef:x}),[w,x]);h(()=>{if(!t||!b||v==null)return;let e=``,n=k(()=>{e=b.style.getPropertyValue(p),n=requestAnimationFrame(()=>{b.style.setProperty(p,`${v}px`)})});return()=>{y(null),cancelAnimationFrame(n),A(n),requestAnimationFrame(()=>{b?.style.setProperty(p,e)})}},[p,b,t,v]),h(()=>{if(!(!t||!b))return()=>{requestAnimationFrame(()=>{let e=T.getVirtualItems();for(let t of e){let e=T.elementsCache.get(t.key);if(!e)continue;S.delete(e);let n=C.get(e);n!=null&&(C.delete(e),_?e.style.setProperty(`transform`,n):e.style.setProperty(p,n))}})}},[p,b,t,T,S,C,_]);let E=(0,l.useRef)({hasValue:!1,value:null}),[D]=(0,l.useState)(()=>new Set),O=(0,l.useCallback)(e=>(D.add(e),()=>{D.delete(e)}),[D]),P=(0,l.useMemo)(()=>{let e=!1,t,n;return()=>{let r=M(T);if(!e){e=!0,t=r;let o=i(r,T);if(E.current.hasValue){let e=E.current.value;if(a(e,o))return n=e,e}return n=o,o}let o=t,s=n;if(N(o,r))return s;let c=i(r,T);return a!==void 0&&a(s,c)?(t=r,s):(t=r,n=c,c)}},[i,a,T]),F=(0,l.useCallback)(e=>{if(!t||!b)return;y(e.getTotalSize());let n=e.getVirtualItems();for(let t of n){let n=t.start-f,r=e.elementsCache.get(t.key);r&&S.get(r)!==n&&(C.has(r)||(_?C.set(r,r.style.getPropertyValue(`transform`)):C.set(r,r.style.getPropertyValue(p))),S.set(r,n),_?r.style.setProperty(`transform`,s?`translate3d(${n}px, 0, 0)`:`translate3d(0, ${n}px, 0)`):r.style.setProperty(p,`${n}px`))}},[p,b,t,s,S,C,f,_]),I=(0,l.useCallback)((t,n)=>{if(F(t),e&&n)(0,u.flushSync)(()=>{for(let e of D)e()});else for(let e of D)e();o.onChange?.call(void 0,t,n)},[F,D,o.onChange,e]),L=(0,l.useMemo)(()=>({...o,onChange:I}),[I,o]);h(()=>{T.setOptions(L)},[T,L]),h(()=>T._didMount(),[T]),h(()=>T._willUpdate()),h(()=>{F(T)});let R=(0,l.useSyncExternalStore)(O,P);return(0,l.useInsertionEffect)(()=>{E.current.hasValue=!0,E.current.value=R},[R]),R};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return P}}); +\ No newline at end of file ++var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;li[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("@tanstack/virtual-core"),l=require("react");l=s(l,1);let u=require("react-dom");const d=(e,...t)=>e,f=(e,t)=>{if(e.size!==t.size)return!1;for(let[n,r]of e){let e=t.get(n);if(r!==e||!Object.is(r,e))return!1}return!0},p=(e,t)=>{if(e.size!==t.size)return!1;for(let n of e)if(!t.has(n))return!1;return!0},m=(e,t)=>{if(e===t||Object.is(e,t))return!0;if(typeof e!=`object`||!e||typeof t!=`object`||!t)return!1;if(e instanceof Map&&t instanceof Map)return f(e,t);if(e instanceof Set&&t instanceof Set)return p(e,t);let n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;for(let r of n){if(!Object.hasOwn(t,r))return!1;let{[r]:n}=e,{[r]:i}=t;if(n!==i||!Object.is(n,i))return!1}return!0},h=typeof document<`u`?l.useLayoutEffect:()=>{},g=e=>{let[t,n]=(0,l.useState)(e);return m(t,e)||n(e),t};let _=1,v=!1;const y=new Map,b=({callback:e,args:t})=>e.apply(void 0,t),x=e=>{if(v){C(x,e);return}let t=y.get(e);if(t){v=!0;try{b(t)}finally{w(e),v=!1}}},S=`scheduler`in globalThis?{setImmediate:(e,...t)=>{let n=_++;return y.set(n,{callback:e,args:t}),scheduler.postTask(()=>x(n),{priority:`user-blocking`}),n},clearImmediate:e=>{y.delete(e)}}:(()=>{let e=new MessageChannel;e.port1.onmessage=function(e){let t=e.data;x(t)};let t=e.port2;return{setImmediate:(e,...n)=>{let r=_++;return y.set(r,{callback:e,args:n}),t.postMessage(r),r},clearImmediate:e=>{y.delete(e)}}})(),C=S.setImmediate,w=S.clearImmediate;let T=1;const E=new Map,D=({callback:e,args:t})=>e.apply(void 0,t),O=e=>{let t=E.get(e);if(t)try{D(t)}finally{A(e)}},k=(e,...t)=>{let n=T++;return E.set(n,{callback:e,args:t}),requestAnimationFrame(()=>C(()=>O(n))),n},A=e=>{E.delete(e)};function j(e){let[t,n]=(0,l.useState)(e);return[t,(0,l.useCallback)(e=>(n(e),()=>{n(null)}),[])]}const M=({getVirtualItems:e,getVirtualIndexes:t,getTotalSize:n,...r})=>({...r,virtualItems:e(),virtualIndexes:t(),totalSize:n()}),N=({options:e,virtualIndexes:t,virtualItems:n,...r},{options:i,virtualIndexes:a,virtualItems:o,...s})=>m(e,i)&&m(t,a)&&m(n,o)&&m(r,s),P=({useFlushSync:e=!0,directDomUpdates:t=!1,directDomUpdatesMode:n=`transform`,...r},i=d,a=m)=>{"use no memo";let o=g(r),{horizontal:s=!1,scrollMargin:f=0}=o,p=s?`width`:`height`,_=n===`transform`,[v,y]=(0,l.useState)(null),[b,x]=j(null),[S]=(0,l.useState)(()=>new WeakMap),[C]=(0,l.useState)(()=>new WeakMap),[w]=(0,l.useState)(()=>new c.Virtualizer(o)),T=(0,l.useMemo)(()=>Object.assign(w,{containerRef:x}),[w,x]);h(()=>{if(!t||!b||v==null)return;let e=``,n=k(()=>{e=b.style.getPropertyValue(p),n=requestAnimationFrame(()=>{b.style.setProperty(p,`${v}px`)})});return()=>{y(null),cancelAnimationFrame(n),A(n),requestAnimationFrame(()=>{b?.style.setProperty(p,e)})}},[p,b,t,v]),h(()=>{if(!(!t||!b))return()=>{requestAnimationFrame(()=>{let e=T.getVirtualItems();for(let t of e){let e=T.elementsCache.get(t.key);if(!e)continue;S.delete(e);let n=C.get(e);n!=null&&(C.delete(e),_?e.style.setProperty(`transform`,n):e.style.setProperty(p,n))}})}},[p,b,t,T,S,C,_]);let E=(0,l.useRef)({hasValue:!1,value:null}),[D]=(0,l.useState)(()=>new Set),O=(0,l.useCallback)(e=>(D.add(e),()=>{D.delete(e)}),[D]),P=(0,l.useMemo)(()=>{let e=!1,t,n;return()=>{let r=M(T);if(!e){e=!0,t=r;let o=i(r,T);if(E.current.hasValue){let e=E.current.value;if(a(e,o))return n=e,e}return n=o,o}let o=t,s=n;if(N(o,r))return s;let c=i(r,T);return a!==void 0&&a(s,c)?(t=r,s):(t=r,n=c,c)}},[i,a,T]),F=(0,l.useCallback)(e=>{if(!t||!b)return;y(e.getTotalSize());let n=e.getVirtualItems();for(let t of n){let n=t.start-f,r=e.elementsCache.get(t.key);r&&S.get(r)!==n&&(C.has(r)||(_?C.set(r,r.style.getPropertyValue(`transform`)):C.set(r,r.style.getPropertyValue(p))),S.set(r,n),_?r.style.setProperty(`transform`,s?`translate3d(${n}px, 0, 0)`:`translate3d(0, ${n}px, 0)`):r.style.setProperty(p,`${n}px`))}},[p,b,t,s,S,C,f,_]),I=(0,l.useCallback)((t,n)=>{if(F(t),e&&n)(0,u.flushSync)(()=>{for(let e of D)e()});else for(let e of D)e();o.onChange?.call(void 0,t,n)},[F,D,o.onChange,e]),L=(0,l.useMemo)(()=>({...o,onChange:I}),[I,o]);h(()=>{T.setOptions(L),T.maybeNotify()},[T,L]),h(()=>T._didMount(),[T]),h(()=>{const e=T._willUpdate(),t=T.scrollElement;if(t&&`scrollTop`in t&&!T.isScrolling){const e=T.options.horizontal?t.scrollLeft*(T.options.isRtl?-1:1):t.scrollTop;T.scrollOffset!==null&&Math.abs(T.scrollOffset-e)>1&&(T.scrollOffset=e,T.scrollAdjustments=0,T.maybeNotify())}return e}),h(()=>{F(T)});let R=(0,l.useSyncExternalStore)(O,P);return(0,l.useInsertionEffect)(()=>{E.current.hasValue=!0,E.current.value=R},[R]),R};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return P}}); +\ No newline at end of file +diff --git a/src/lib/useVirtualizerBase.ts b/src/lib/useVirtualizerBase.ts +index 59f46c47fe9bf8c09b482e110a623fbff5e65da3..1cb263dbc0c5f39c30f380886b4a7c19d6cee1b5 100644 +--- a/src/lib/useVirtualizerBase.ts ++++ b/src/lib/useVirtualizerBase.ts +@@ -364,14 +364,42 @@ export const useVirtualizerBase = < + + useLayoutEffect(() => { + instance.setOptions(resolvedOptions) ++ // InvokeAI patch: notify after setOptions because range changes otherwise leave the external- ++ // store snapshot stale. ++ ;(instance as unknown as { maybeNotify: () => void }).maybeNotify() + }, [instance, resolvedOptions]) + + useLayoutEffect(() => { + return instance._didMount() + }, [instance]) + ++ // InvokeAI patch: reconcile actual offset after commits when not scrolling; content shrink/remount ++ // can clamp position without a scroll event. + useLayoutEffect(() => { +- return instance._willUpdate() ++ const result = instance._willUpdate() ++ const scrollElement = instance.scrollElement ++ if (scrollElement && "scrollTop" in scrollElement && !instance.isScrolling) { ++ const { horizontal: isHorizontal, isRtl } = instance.options ++ const actualOffset = ++ isHorizontal ? ++ scrollElement.scrollLeft * (isRtl ? -1 : 1) ++ : scrollElement.scrollTop ++ if ( ++ instance.scrollOffset !== null ++ && Math.abs(instance.scrollOffset - actualOffset) > 1 ++ ) { ++ const mutable = instance as unknown as { ++ maybeNotify: () => void ++ scrollAdjustments: number ++ } ++ instance.scrollOffset = actualOffset ++ mutable.scrollAdjustments = 0 ++ mutable.maybeNotify() ++ // Preserve _intendedScrollOffset until its real event arrives; clearing it while ++ // updating scrollOffset suppresses programmatic scroll notifications. ++ } ++ } ++ return result + }) + + useLayoutEffect(() => { diff --git a/invokeai/frontend/webv2/performance/architecture-baseline.json b/invokeai/frontend/webv2/performance/architecture-baseline.json new file mode 100644 index 00000000000..5dc3c67c054 --- /dev/null +++ b/invokeai/frontend/webv2/performance/architecture-baseline.json @@ -0,0 +1,1118 @@ +{ + "build": { + "launchpad": { + "baseline": { + "brotliBytes": 893586, + "cssRawBytes": 2159, + "fontRawBytes": 219480, + "gzipBytes": 892942, + "imageRawBytes": 156376, + "initialRawBytes": 2118609, + "largestAssetRawBytes": 749035, + "otherAssetRawBytes": 0, + "ownedRawBytes": 93082, + "requestCount": 36, + "scriptRequestCount": 20, + "sourceOwners": [ + "package:@ark-ui/react", + "package:@chakra-ui/react", + "package:@emotion/cache", + "package:@emotion/hash", + "package:@emotion/is-prop-valid", + "package:@emotion/memoize", + "package:@emotion/react", + "package:@emotion/serialize", + "package:@emotion/sheet", + "package:@emotion/unitless", + "package:@emotion/use-insertion-effect-with-fallbacks", + "package:@emotion/utils", + "package:@floating-ui/core", + "package:@floating-ui/dom", + "package:@floating-ui/utils", + "package:@fontsource/inter", + "package:@internationalized/number", + "package:@pandacss/is-valid-prop", + "package:@socket.io/component-emitter", + "package:@tanstack/history", + "package:@tanstack/query-core", + "package:@tanstack/react-query", + "package:@tanstack/react-router", + "package:@tanstack/react-store", + "package:@tanstack/react-virtual", + "package:@tanstack/router-core", + "package:@tanstack/store", + "package:@tanstack/virtual-core", + "package:@zag-js/accordion", + "package:@zag-js/anatomy", + "package:@zag-js/aria-hidden", + "package:@zag-js/auto-resize", + "package:@zag-js/avatar", + "package:@zag-js/carousel", + "package:@zag-js/checkbox", + "package:@zag-js/clipboard", + "package:@zag-js/collapsible", + "package:@zag-js/collection", + "package:@zag-js/color-picker", + "package:@zag-js/color-utils", + "package:@zag-js/combobox", + "package:@zag-js/core", + "package:@zag-js/date-input", + "package:@zag-js/date-picker", + "package:@zag-js/dialog", + "package:@zag-js/dismissable", + "package:@zag-js/dom-query", + "package:@zag-js/editable", + "package:@zag-js/file-upload", + "package:@zag-js/floating-panel", + "package:@zag-js/focus-trap", + "package:@zag-js/focus-visible", + "package:@zag-js/hover-card", + "package:@zag-js/interact-outside", + "package:@zag-js/listbox", + "package:@zag-js/live-region", + "package:@zag-js/marquee", + "package:@zag-js/menu", + "package:@zag-js/number-input", + "package:@zag-js/pagination", + "package:@zag-js/pin-input", + "package:@zag-js/popover", + "package:@zag-js/popper", + "package:@zag-js/presence", + "package:@zag-js/progress", + "package:@zag-js/qr-code", + "package:@zag-js/radio-group", + "package:@zag-js/rating-group", + "package:@zag-js/react", + "package:@zag-js/rect-utils", + "package:@zag-js/remove-scroll", + "package:@zag-js/scroll-area", + "package:@zag-js/select", + "package:@zag-js/slider", + "package:@zag-js/splitter", + "package:@zag-js/switch", + "package:@zag-js/tabs", + "package:@zag-js/tags-input", + "package:@zag-js/toast", + "package:@zag-js/tooltip", + "package:@zag-js/tree-view", + "package:@zag-js/types", + "package:@zag-js/utils", + "package:classcat", + "package:engine.io-client", + "package:engine.io-parser", + "package:fuzzysort", + "package:hoist-non-react-statics", + "package:i18next", + "package:i18next-http-backend", + "package:idb", + "package:lucide-react", + "package:react", + "package:react-dom", + "package:react-hook-tanstack-virtual", + "package:react-i18next", + "package:react-is", + "package:scheduler", + "package:socket.io-client", + "package:socket.io-parser", + "package:stylis", + "package:tinykeys", + "package:use-sync-external-store", + "package:zod", + "package:zustand", + "source:index.html", + "source:src/app/App.tsx", + "source:src/app/AppProviders.tsx", + "source:src/app/FeatureHintsProvider.tsx", + "source:src/app/I18nController.tsx", + "source:src/app/SocketHubRuntime.tsx", + "source:src/app/ThemeController.tsx", + "source:src/app/accountLifecycle.ts", + "source:src/app/loggingRuntime.ts", + "source:src/app/main.tsx", + "source:src/app/router.tsx", + "source:src/assets/SplashImage.webp", + "source:src/features/fonts/data/keys.ts", + "source:src/features/fonts/launchpad.tsx", + "source:src/features/fonts/react.tsx", + "source:src/features/fonts/runtime.ts", + "source:src/features/identity/capabilities.ts", + "source:src/features/identity/core/schemas.ts", + "source:src/features/identity/core/sessionPolicy.ts", + "source:src/features/identity/core/tokenStorage.ts", + "source:src/features/identity/data/api.ts", + "source:src/features/identity/index.ts", + "source:src/features/identity/session.ts", + "source:src/features/identity/transportAdapter.ts", + "source:src/features/identity/ui/AccountMenu.tsx", + "source:src/features/identity/ui/AccountMenuSection.tsx", + "source:src/features/identity/ui/AuthScreen.tsx", + "source:src/features/identity/ui/AuthUnavailableScreen.tsx", + "source:src/features/identity/ui/LoginScreen.tsx", + "source:src/features/identity/ui/PasswordInput.tsx", + "source:src/features/identity/ui/ProfileDialog.tsx", + "source:src/features/identity/ui/SetupScreen.tsx", + "source:src/features/identity/ui/UserFormDialog.tsx", + "source:src/features/identity/ui/UserManagement.tsx", + "source:src/features/identity/ui/UsersPage.tsx", + "source:src/features/identity/ui/useIdentityNotify.ts", + "source:src/features/intermediates/data/focus.ts", + "source:src/features/intermediates/index.ts", + "source:src/features/models/core/baseIdentity.ts", + "source:src/features/models/data/api.ts", + "source:src/features/models/data/apiKeys.ts", + "source:src/features/models/data/installsStore.ts", + "source:src/features/models/data/modelLoadStore.ts", + "source:src/features/models/data/modelsStore.ts", + "source:src/features/models/data/startersStore.ts", + "source:src/features/models/data/transport.ts", + "source:src/features/models/index.ts", + "source:src/features/models/ui/ModelInstallRuntime.tsx", + "source:src/features/models/ui/ModelsPage.tsx", + "source:src/features/models/ui/ModelsUiContext.tsx", + "source:src/features/models/ui/add-models/useInstallActions.ts", + "source:src/features/models/ui/useInstallOutcomeToasts.ts", + "source:src/features/models/ui/useModelsNotify.ts", + "source:src/features/nodes/core/catalog.ts", + "source:src/features/nodes/data/api.ts", + "source:src/features/nodes/data/nodeExecutionStore.ts", + "source:src/features/nodes/data/nodesStore.ts", + "source:src/features/nodes/data/transport.ts", + "source:src/features/nodes/index.ts", + "source:src/features/nodes/ui/NodesPage.tsx", + "source:src/platform/browser/downloadBlob.ts", + "source:src/platform/browser/knownBrowserIssues.ts", + "source:src/platform/browser/randomUuid.ts", + "source:src/platform/browser/sha256.ts", + "source:src/platform/core/concurrency.ts", + "source:src/platform/core/outputImages.ts", + "source:src/platform/core/seed.ts", + "source:src/platform/i18n/client.ts", + "source:src/platform/i18n/languages.ts", + "source:src/platform/logging/contracts.ts", + "source:src/platform/logging/logger.ts", + "source:src/platform/logging/normalize.ts", + "source:src/platform/performance/semanticReady.ts", + "source:src/platform/pwa/registerServiceWorker.ts", + "source:src/platform/query/client.ts", + "source:src/platform/react/useMountEffect.ts", + "source:src/platform/react/usePreservedScrollOffset.ts", + "source:src/platform/react/useZodForm.ts", + "source:src/platform/search/dateTokens.ts", + "source:src/platform/state/accountLifecycle.ts", + "source:src/platform/state/externalStore.ts", + "source:src/platform/state/externalStoreCore.ts", + "source:src/platform/state/selectorCore.ts", + "source:src/platform/state/selectors.ts", + "source:src/platform/state/singleFlight.ts", + "source:src/platform/time/serverTimestamp.ts", + "source:src/platform/transport/connectionStore.ts", + "source:src/platform/transport/deploymentBase.ts", + "source:src/platform/transport/http.ts", + "source:src/platform/transport/socketHub.ts", + "source:src/platform/ui/Button.tsx", + "source:src/platform/ui/ColorPicker.tsx", + "source:src/platform/ui/Combobox.tsx", + "source:src/platform/ui/ConfirmDialog.tsx", + "source:src/platform/ui/DropTargetOverlay.tsx", + "source:src/platform/ui/DropZone.tsx", + "source:src/platform/ui/EmptyState.tsx", + "source:src/platform/ui/Field.tsx", + "source:src/platform/ui/Group.tsx", + "source:src/platform/ui/InputShell.tsx", + "source:src/platform/ui/InvokeMark.tsx", + "source:src/platform/ui/JsonPreview.tsx", + "source:src/platform/ui/Menu.tsx", + "source:src/platform/ui/MiddleTruncate.tsx", + "source:src/platform/ui/PageShell.tsx", + "source:src/platform/ui/Panel.tsx", + "source:src/platform/ui/PanelHeader.tsx", + "source:src/platform/ui/Popover.tsx", + "source:src/platform/ui/RenameDialog.tsx", + "source:src/platform/ui/ResizableTextarea.tsx", + "source:src/platform/ui/RetryBoundary.tsx", + "source:src/platform/ui/Row.tsx", + "source:src/platform/ui/Scrollable.tsx", + "source:src/platform/ui/SegmentTabs.tsx", + "source:src/platform/ui/SegmentedControl.tsx", + "source:src/platform/ui/Select.tsx", + "source:src/platform/ui/Slider.tsx", + "source:src/platform/ui/StatusWidgetChip.tsx", + "source:src/platform/ui/Tabs.tsx", + "source:src/platform/ui/ToggleDot.tsx", + "source:src/platform/ui/Toolbar.tsx", + "source:src/platform/ui/Tooltip.tsx", + "source:src/platform/ui/VendoredIcon.tsx", + "source:src/platform/ui/color.ts", + "source:src/platform/ui/colorPickerStore.ts", + "source:src/platform/ui/colorPickerSync.ts", + "source:src/platform/ui/hints/FeatureHint.tsx", + "source:src/platform/ui/hints/hintRegistry.ts", + "source:src/platform/ui/hints/hintsContext.tsx", + "source:src/platform/ui/hints/index.ts", + "source:src/platform/ui/index.ts", + "source:src/platform/ui/list/ListDivider.tsx", + "source:src/platform/ui/list/ListItem.tsx", + "source:src/platform/ui/list/ListSectionHeader.tsx", + "source:src/platform/ui/list/ListStack.tsx", + "source:src/platform/ui/list/listLayout.ts", + "source:src/platform/ui/settings/contracts.ts", + "source:src/platform/ui/theme/applyTheme.ts", + "source:src/platform/ui/theme/recipes.ts", + "source:src/platform/ui/theme/system.ts", + "source:src/platform/ui/theme/themes.ts", + "source:src/platform/ui/toaster.tsx", + "source:src/platform/ui/useScrollAreaPhantomHeal.ts", + "source:src/platform/ui/widgetOverlayRegistry.ts", + "source:src/platform/ui/widgetOverlays.ts", + "source:src/workbench/canvasSchemaVersion.ts", + "source:src/workbench/components/WorkbenchSplashScreen.tsx", + "source:src/workbench/deferredResource.ts", + "source:src/workbench/diagnostics/loggingPreferences.ts", + "source:src/workbench/hotkeys/catalog.ts", + "source:src/workbench/hotkeys/keys.ts", + "source:src/workbench/hotkeys/modalLayer.ts", + "source:src/workbench/hotkeys/resolve.ts", + "source:src/workbench/launchpad/HelpMenu.tsx", + "source:src/workbench/launchpad/KnownBrowserIssuesAlert.tsx", + "source:src/workbench/launchpad/Launchpad.tsx", + "source:src/workbench/launchpad/LaunchpadNav.tsx", + "source:src/workbench/launchpad/LaunchpadTopBar.tsx", + "source:src/workbench/launchpad/OpenProjectsControl.tsx", + "source:src/workbench/launchpad/formatRelativeTime.ts", + "source:src/workbench/launchpad/home/IntentTiles.tsx", + "source:src/workbench/launchpad/home/LivePanel.tsx", + "source:src/workbench/launchpad/home/RecentProjectsRow.tsx", + "source:src/workbench/launchpad/home/ResumeCard.tsx", + "source:src/workbench/launchpad/intents.ts", + "source:src/workbench/launchpad/pages/HomePage.tsx", + "source:src/workbench/launchpad/pages/ProjectsPage.tsx", + "source:src/workbench/launchpad/projects/NewProjectButton.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenu.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenuHost.tsx", + "source:src/workbench/launchpad/projects/ProjectCard.tsx", + "source:src/workbench/launchpad/projects/ProjectCompatibilityBadge.tsx", + "source:src/workbench/launchpad/projects/ProjectCover.tsx", + "source:src/workbench/launchpad/projects/ProjectRow.tsx", + "source:src/workbench/launchpad/projects/ProjectsBrowser.tsx", + "source:src/workbench/launchpad/projects/ProjectsToolbar.tsx", + "source:src/workbench/launchpad/projects/projectLibraryView.ts", + "source:src/workbench/launchpad/projects/projectPins.ts", + "source:src/workbench/launchpad/projects/projectsMetrics.ts", + "source:src/workbench/launchpad/projects/useProjectCardActions.ts", + "source:src/workbench/launchpad/startEntries.ts", + "source:src/workbench/mediaReferences.ts", + "source:src/workbench/palette/LaunchpadCommandPalette.tsx", + "source:src/workbench/palette/PaletteButton.tsx", + "source:src/workbench/palette/paletteStore.ts", + "source:src/workbench/palette/settingsEntryDeps.ts", + "source:src/workbench/projects/api.ts", + "source:src/workbench/projects/components/ProjectFileOptionsProvider.tsx", + "source:src/workbench/projects/covers.ts", + "source:src/workbench/projects/ids.ts", + "source:src/workbench/projects/invk/format.ts", + "source:src/workbench/projects/library.ts", + "source:src/workbench/projects/openProjects.ts", + "source:src/workbench/projects/projectAssets.ts", + "source:src/workbench/projects/projectFile.ts", + "source:src/workbench/projects/projectFileErrors.ts", + "source:src/workbench/projects/projectFileToasts.ts", + "source:src/workbench/projects/projectFlush.ts", + "source:src/workbench/projects/session.ts", + "source:src/workbench/projects/syncStore.ts", + "source:src/workbench/projects/useProjectFileActions.ts", + "source:src/workbench/settings/SettingsDialogHost.tsx", + "source:src/workbench/settings/dialogResource.ts", + "source:src/workbench/settings/launchpad.tsx", + "source:src/workbench/settings/settingsDialogStore.ts", + "source:src/workbench/settings/settingsSearchShortcut.ts", + "source:src/workbench/settings/store.ts" + ] + }, + "owner": "app", + "remediationTicket": "set-performance-budgets", + "source": "index.html" + }, + "editor": { + "baseline": { + "brotliBytes": 1243382, + "cssRawBytes": 2159, + "fontRawBytes": 219480, + "gzipBytes": 1237668, + "imageRawBytes": 156376, + "initialRawBytes": 3203516, + "largestAssetRawBytes": 749035, + "otherAssetRawBytes": 0, + "ownedRawBytes": 144128, + "requestCount": 74, + "scriptRequestCount": 58, + "sourceOwners": [ + "package:@ark-ui/react", + "package:@chakra-ui/react", + "package:@dnd-kit/accessibility", + "package:@dnd-kit/core", + "package:@dnd-kit/modifiers", + "package:@dnd-kit/sortable", + "package:@dnd-kit/utilities", + "package:@emotion/cache", + "package:@emotion/hash", + "package:@emotion/is-prop-valid", + "package:@emotion/memoize", + "package:@emotion/react", + "package:@emotion/serialize", + "package:@emotion/sheet", + "package:@emotion/unitless", + "package:@emotion/use-insertion-effect-with-fallbacks", + "package:@emotion/utils", + "package:@floating-ui/core", + "package:@floating-ui/dom", + "package:@floating-ui/utils", + "package:@fontsource/inter", + "package:@internationalized/number", + "package:@pandacss/is-valid-prop", + "package:@socket.io/component-emitter", + "package:@tanstack/history", + "package:@tanstack/query-core", + "package:@tanstack/react-query", + "package:@tanstack/react-router", + "package:@tanstack/react-store", + "package:@tanstack/react-virtual", + "package:@tanstack/router-core", + "package:@tanstack/store", + "package:@tanstack/virtual-core", + "package:@zag-js/accordion", + "package:@zag-js/anatomy", + "package:@zag-js/aria-hidden", + "package:@zag-js/auto-resize", + "package:@zag-js/avatar", + "package:@zag-js/carousel", + "package:@zag-js/checkbox", + "package:@zag-js/clipboard", + "package:@zag-js/collapsible", + "package:@zag-js/collection", + "package:@zag-js/color-picker", + "package:@zag-js/color-utils", + "package:@zag-js/combobox", + "package:@zag-js/core", + "package:@zag-js/date-input", + "package:@zag-js/date-picker", + "package:@zag-js/dialog", + "package:@zag-js/dismissable", + "package:@zag-js/dom-query", + "package:@zag-js/editable", + "package:@zag-js/file-upload", + "package:@zag-js/floating-panel", + "package:@zag-js/focus-trap", + "package:@zag-js/focus-visible", + "package:@zag-js/hover-card", + "package:@zag-js/interact-outside", + "package:@zag-js/listbox", + "package:@zag-js/live-region", + "package:@zag-js/marquee", + "package:@zag-js/menu", + "package:@zag-js/number-input", + "package:@zag-js/pagination", + "package:@zag-js/pin-input", + "package:@zag-js/popover", + "package:@zag-js/popper", + "package:@zag-js/presence", + "package:@zag-js/progress", + "package:@zag-js/qr-code", + "package:@zag-js/radio-group", + "package:@zag-js/rating-group", + "package:@zag-js/react", + "package:@zag-js/rect-utils", + "package:@zag-js/remove-scroll", + "package:@zag-js/scroll-area", + "package:@zag-js/select", + "package:@zag-js/slider", + "package:@zag-js/splitter", + "package:@zag-js/switch", + "package:@zag-js/tabs", + "package:@zag-js/tags-input", + "package:@zag-js/toast", + "package:@zag-js/tooltip", + "package:@zag-js/tree-view", + "package:@zag-js/types", + "package:@zag-js/utils", + "package:classcat", + "package:engine.io-client", + "package:engine.io-parser", + "package:fuzzysort", + "package:hoist-non-react-statics", + "package:i18next", + "package:i18next-http-backend", + "package:idb", + "package:lucide-react", + "package:perfect-freehand", + "package:react", + "package:react-dom", + "package:react-hook-tanstack-virtual", + "package:react-i18next", + "package:react-is", + "package:scheduler", + "package:socket.io-client", + "package:socket.io-parser", + "package:stylis", + "package:tinykeys", + "package:use-sync-external-store", + "package:zod", + "package:zustand", + "source:index.html", + "source:src/app/App.tsx", + "source:src/app/AppProviders.tsx", + "source:src/app/BootWidgetHintController.tsx", + "source:src/app/FeatureHintsProvider.tsx", + "source:src/app/GalleryRealtimeRuntime.tsx", + "source:src/app/GalleryUiAdapter.tsx", + "source:src/app/GenerateWidgetSyncRuntime.tsx", + "source:src/app/GenerationUiAdapter.tsx", + "source:src/app/I18nController.tsx", + "source:src/app/ModelsUiAdapter.tsx", + "source:src/app/QueueRuntimeAdapter.tsx", + "source:src/app/QueueUiAdapter.tsx", + "source:src/app/RecallParametersRuntime.tsx", + "source:src/app/SocketHubRuntime.tsx", + "source:src/app/ThemeController.tsx", + "source:src/app/UpscaleUiAdapter.tsx", + "source:src/app/VideoUiAdapter.tsx", + "source:src/app/WorkbenchApp.tsx", + "source:src/app/WorkflowUiAdapter.tsx", + "source:src/app/accountLifecycle.ts", + "source:src/app/loggingRuntime.ts", + "source:src/app/main.tsx", + "source:src/app/router.tsx", + "source:src/app/workbenchPorts.tsx", + "source:src/assets/SplashImage.webp", + "source:src/features/fonts/data/keys.ts", + "source:src/features/fonts/launchpad.tsx", + "source:src/features/fonts/react.tsx", + "source:src/features/fonts/runtime.ts", + "source:src/features/gallery/core/boardLabels.ts", + "source:src/features/gallery/core/image.ts", + "source:src/features/gallery/core/imagePaths.ts", + "source:src/features/gallery/core/items.ts", + "source:src/features/gallery/core/recentImages.ts", + "source:src/features/gallery/core/selection.ts", + "source:src/features/gallery/core/semanticImageQuery.ts", + "source:src/features/gallery/core/settings.ts", + "source:src/features/gallery/core/videoPaths.ts", + "source:src/features/gallery/data/backend.ts", + "source:src/features/gallery/data/imageUrls.ts", + "source:src/features/gallery/data/queries.ts", + "source:src/features/gallery/data/queryCache.ts", + "source:src/features/gallery/data/realtimeRuntime.ts", + "source:src/features/gallery/data/videoUrls.ts", + "source:src/features/gallery/picker.ts", + "source:src/features/gallery/queries.ts", + "source:src/features/gallery/react.ts", + "source:src/features/gallery/settingsContribution.ts", + "source:src/features/gallery/ui/GalleryBoardCover.tsx", + "source:src/features/gallery/ui/GalleryBoardRowShell.tsx", + "source:src/features/gallery/ui/GalleryDragCursor.tsx", + "source:src/features/gallery/ui/GalleryItemSearch.tsx", + "source:src/features/gallery/ui/GallerySearchField.tsx", + "source:src/features/gallery/ui/GallerySearchHelp.tsx", + "source:src/features/gallery/ui/GalleryTileFrame.tsx", + "source:src/features/gallery/ui/GalleryUiContext.tsx", + "source:src/features/gallery/ui/GalleryUploadButton.tsx", + "source:src/features/gallery/ui/GalleryViewTabs.tsx", + "source:src/features/gallery/ui/GalleryWidgetContext.tsx", + "source:src/features/gallery/ui/galleryBoardGroups.ts", + "source:src/features/gallery/ui/galleryBoardLabels.ts", + "source:src/features/gallery/ui/galleryDnd.ts", + "source:src/features/gallery/ui/galleryGridLayout.ts", + "source:src/features/gallery/ui/galleryStateView.ts", + "source:src/features/gallery/ui/picker/GalleryPickerPopover.tsx", + "source:src/features/gallery/ui/useGalleryData.ts", + "source:src/features/gallery/ui/useGalleryUploadAction.ts", + "source:src/features/gallery/ui/useGalleryUploadInput.ts", + "source:src/features/gallery/utility.ts", + "source:src/features/generation/core/architectureCapabilities.ts", + "source:src/features/generation/core/baseGenerationPolicies.ts", + "source:src/features/generation/core/batch.ts", + "source:src/features/generation/core/canvas/addControlLayers.ts", + "source:src/features/generation/core/canvas/addRegionalGuidance.ts", + "source:src/features/generation/core/canvas/controlValidation.ts", + "source:src/features/generation/core/catalogSearch.ts", + "source:src/features/generation/core/componentCompatibility.ts", + "source:src/features/generation/core/conditioningRebalance.ts", + "source:src/features/generation/core/dynamicPrompts.ts", + "source:src/features/generation/core/graph.ts", + "source:src/features/generation/core/graphBuilder.ts", + "source:src/features/generation/core/krea2Conditioning.ts", + "source:src/features/generation/core/pid.ts", + "source:src/features/generation/core/pidGraph.ts", + "source:src/features/generation/core/projectPromptDraft.ts", + "source:src/features/generation/core/prompt/ast.ts", + "source:src/features/generation/core/prompt/attention.ts", + "source:src/features/generation/core/promptHistory.ts", + "source:src/features/generation/core/promptTemplates.ts", + "source:src/features/generation/core/referenceImage.ts", + "source:src/features/generation/core/resolveGenerateWidgetValues.ts", + "source:src/features/generation/core/settings.ts", + "source:src/features/generation/core/supportedBases.ts", + "source:src/features/generation/data/architectureCapabilitiesApi.ts", + "source:src/features/generation/data/architectureCapabilitiesStore.ts", + "source:src/features/generation/data/dynamicPromptsQueries.ts", + "source:src/features/generation/data/llmTaskProgress.ts", + "source:src/features/generation/data/promptTemplates.ts", + "source:src/features/generation/data/promptUtilities.ts", + "source:src/features/generation/data/systemPrompts.ts", + "source:src/features/generation/graph.ts", + "source:src/features/generation/prompts.ts", + "source:src/features/generation/queries.ts", + "source:src/features/generation/react.ts", + "source:src/features/generation/runtime.ts", + "source:src/features/generation/settings.ts", + "source:src/features/generation/ui/GenerationUiContext.tsx", + "source:src/features/generation/ui/LLMTaskProgressRuntime.tsx", + "source:src/features/generation/ui/generateDraftRegistry.ts", + "source:src/features/generation/ui/generateFormViewModel.ts", + "source:src/features/generation/ui/promptFields/promptAttentionHotkeys.ts", + "source:src/features/generation/ui/promptFields/promptFocus.ts", + "source:src/features/generation/ui/promptFields/promptHistoryNavigation.ts", + "source:src/features/generation/ui/promptTemplateDraftStore.ts", + "source:src/features/generation/ui/useDebouncedDraftValue.ts", + "source:src/features/generation/ui/useDynamicPrompts.ts", + "source:src/features/identity/capabilities.ts", + "source:src/features/identity/core/schemas.ts", + "source:src/features/identity/core/sessionPolicy.ts", + "source:src/features/identity/core/tokenStorage.ts", + "source:src/features/identity/data/api.ts", + "source:src/features/identity/index.ts", + "source:src/features/identity/session.ts", + "source:src/features/identity/transportAdapter.ts", + "source:src/features/identity/ui/AccountMenu.tsx", + "source:src/features/identity/ui/AccountMenuSection.tsx", + "source:src/features/identity/ui/AuthScreen.tsx", + "source:src/features/identity/ui/AuthUnavailableScreen.tsx", + "source:src/features/identity/ui/LoginScreen.tsx", + "source:src/features/identity/ui/PasswordInput.tsx", + "source:src/features/identity/ui/ProfileDialog.tsx", + "source:src/features/identity/ui/SetupScreen.tsx", + "source:src/features/identity/ui/UserFormDialog.tsx", + "source:src/features/identity/ui/UserManagement.tsx", + "source:src/features/identity/ui/UsersPage.tsx", + "source:src/features/identity/ui/useIdentityNotify.ts", + "source:src/features/intermediates/data/focus.ts", + "source:src/features/intermediates/holdLease.ts", + "source:src/features/intermediates/index.ts", + "source:src/features/intermediates/settingsContribution.ts", + "source:src/features/models/core/baseIdentity.ts", + "source:src/features/models/data/api.ts", + "source:src/features/models/data/apiKeys.ts", + "source:src/features/models/data/installsStore.ts", + "source:src/features/models/data/modelLoadStore.ts", + "source:src/features/models/data/modelsStore.ts", + "source:src/features/models/data/startersStore.ts", + "source:src/features/models/data/transport.ts", + "source:src/features/models/index.ts", + "source:src/features/models/ui/ModelInstallRuntime.tsx", + "source:src/features/models/ui/ModelsPage.tsx", + "source:src/features/models/ui/ModelsUiContext.tsx", + "source:src/features/models/ui/add-models/useInstallActions.ts", + "source:src/features/models/ui/useInstallOutcomeToasts.ts", + "source:src/features/models/ui/useModelsNotify.ts", + "source:src/features/nodes/core/catalog.ts", + "source:src/features/nodes/data/api.ts", + "source:src/features/nodes/data/nodeExecutionStore.ts", + "source:src/features/nodes/data/nodesStore.ts", + "source:src/features/nodes/data/transport.ts", + "source:src/features/nodes/index.ts", + "source:src/features/nodes/ui/NodesPage.tsx", + "source:src/features/queue/contracts.ts", + "source:src/features/queue/core/activeSessions.ts", + "source:src/features/queue/core/deviceLabels.ts", + "source:src/features/queue/core/generationMeta.ts", + "source:src/features/queue/core/graphInputMedia.ts", + "source:src/features/queue/core/historySnapshot.ts", + "source:src/features/queue/core/historySummary.ts", + "source:src/features/queue/core/progressImage.ts", + "source:src/features/queue/core/progressRail.ts", + "source:src/features/queue/core/promptBatch.ts", + "source:src/features/queue/core/submissionRules.ts", + "source:src/features/queue/core/types.ts", + "source:src/features/queue/data/activeProgressTargetStore.ts", + "source:src/features/queue/data/events.ts", + "source:src/features/queue/data/generationDevicesStore.ts", + "source:src/features/queue/data/httpRealtimeQueueBackend.ts", + "source:src/features/queue/data/itemProgressStore.ts", + "source:src/features/queue/data/mappers.ts", + "source:src/features/queue/data/progressImageStore.ts", + "source:src/features/queue/data/progressStore.ts", + "source:src/features/queue/data/queries.ts", + "source:src/features/queue/data/realtimeRuntime.ts", + "source:src/features/queue/data/serverApi.ts", + "source:src/features/queue/data/submissionApi.ts", + "source:src/features/queue/devices.ts", + "source:src/features/queue/index.ts", + "source:src/features/queue/menu.ts", + "source:src/features/queue/publicApi.ts", + "source:src/features/queue/react.ts", + "source:src/features/queue/reveal.ts", + "source:src/features/queue/runtime.ts", + "source:src/features/queue/runtime/coordinator.ts", + "source:src/features/queue/runtime/receiptAcknowledgements.ts", + "source:src/features/queue/ui/QueueUiContext.tsx", + "source:src/features/queue/ui/currentBatchItems.ts", + "source:src/features/queue/ui/queueConfirmationStore.ts", + "source:src/features/queue/ui/queueDataStore.ts", + "source:src/features/queue/ui/queueMenuActions.tsx", + "source:src/features/queue/ui/queueOwnership.ts", + "source:src/features/queue/ui/queueProgressCleanup.ts", + "source:src/features/queue/ui/queueScope.ts", + "source:src/features/queue/ui/queueUiStore.ts", + "source:src/features/queue/ui/queueViewModel.ts", + "source:src/features/queue/ui/useDeviceLabel.ts", + "source:src/features/queue/widget.ts", + "source:src/features/upscale/core/graph.ts", + "source:src/features/upscale/core/settings.ts", + "source:src/features/upscale/index.ts", + "source:src/features/upscale/ui/UpscaleUiContext.tsx", + "source:src/features/video/core/dimensions.ts", + "source:src/features/video/core/graph.ts", + "source:src/features/video/core/settings.ts", + "source:src/features/video/core/videoPolicies.ts", + "source:src/features/video/core/widgetValues.ts", + "source:src/features/video/index.ts", + "source:src/features/video/ui/VideoUiContext.tsx", + "source:src/features/workflow/core/batch.ts", + "source:src/features/workflow/core/buildGraph.ts", + "source:src/features/workflow/core/callSavedWorkflow.ts", + "source:src/features/workflow/core/connectors.ts", + "source:src/features/workflow/core/document.ts", + "source:src/features/workflow/core/fields.ts", + "source:src/features/workflow/core/forLoops.ts", + "source:src/features/workflow/core/graphIndex.ts", + "source:src/features/workflow/core/types.ts", + "source:src/features/workflow/core/validation.ts", + "source:src/features/workflow/core/workflowJson.ts", + "source:src/features/workflow/data/templates.ts", + "source:src/features/workflow/react.ts", + "source:src/features/workflow/ui/WorkflowUiContext.tsx", + "source:src/features/workflow/ui/workflowUiStore.ts", + "source:src/features/workflow/utility.ts", + "source:src/features/workflow/widget.ts", + "source:src/platform/browser/downloadBlob.ts", + "source:src/platform/browser/knownBrowserIssues.ts", + "source:src/platform/browser/randomUuid.ts", + "source:src/platform/browser/sha256.ts", + "source:src/platform/browser/webLocks.ts", + "source:src/platform/core/concurrency.ts", + "source:src/platform/core/json.ts", + "source:src/platform/core/outputImages.ts", + "source:src/platform/core/seed.ts", + "source:src/platform/i18n/client.ts", + "source:src/platform/i18n/languages.ts", + "source:src/platform/logging/contracts.ts", + "source:src/platform/logging/logger.ts", + "source:src/platform/logging/normalize.ts", + "source:src/platform/performance/semanticReady.ts", + "source:src/platform/pwa/registerServiceWorker.ts", + "source:src/platform/query/client.ts", + "source:src/platform/react/composeProviders.tsx", + "source:src/platform/react/draftRegistry.ts", + "source:src/platform/react/useMountEffect.ts", + "source:src/platform/react/usePreservedScrollOffset.ts", + "source:src/platform/react/useZodForm.ts", + "source:src/platform/search/dateTokens.ts", + "source:src/platform/state/accountLifecycle.ts", + "source:src/platform/state/compareAndSwapRollback.ts", + "source:src/platform/state/externalStore.ts", + "source:src/platform/state/externalStoreCore.ts", + "source:src/platform/state/projectedExternalStore.ts", + "source:src/platform/state/selectorCore.ts", + "source:src/platform/state/selectors.ts", + "source:src/platform/state/singleFlight.ts", + "source:src/platform/time/serverTimestamp.ts", + "source:src/platform/transport/connectionStore.ts", + "source:src/platform/transport/deploymentBase.ts", + "source:src/platform/transport/http.ts", + "source:src/platform/transport/socketHub.ts", + "source:src/platform/ui/Button.tsx", + "source:src/platform/ui/ColorPicker.tsx", + "source:src/platform/ui/Combobox.tsx", + "source:src/platform/ui/ConfirmDialog.tsx", + "source:src/platform/ui/DropTargetOverlay.tsx", + "source:src/platform/ui/DropZone.tsx", + "source:src/platform/ui/EmptyState.tsx", + "source:src/platform/ui/Field.tsx", + "source:src/platform/ui/Group.tsx", + "source:src/platform/ui/InputShell.tsx", + "source:src/platform/ui/InvokeMark.tsx", + "source:src/platform/ui/JsonPreview.tsx", + "source:src/platform/ui/Menu.tsx", + "source:src/platform/ui/MiddleTruncate.tsx", + "source:src/platform/ui/PageShell.tsx", + "source:src/platform/ui/Panel.tsx", + "source:src/platform/ui/PanelHeader.tsx", + "source:src/platform/ui/Popover.tsx", + "source:src/platform/ui/RenameDialog.tsx", + "source:src/platform/ui/ResizableTextarea.tsx", + "source:src/platform/ui/ResizeHandle.tsx", + "source:src/platform/ui/RetryBoundary.tsx", + "source:src/platform/ui/Row.tsx", + "source:src/platform/ui/Scrollable.tsx", + "source:src/platform/ui/SeedInput.tsx", + "source:src/platform/ui/SegmentTabs.tsx", + "source:src/platform/ui/SegmentedControl.tsx", + "source:src/platform/ui/Select.tsx", + "source:src/platform/ui/Slider.tsx", + "source:src/platform/ui/StatusWidgetChip.tsx", + "source:src/platform/ui/Tabs.tsx", + "source:src/platform/ui/ToggleDot.tsx", + "source:src/platform/ui/Toolbar.tsx", + "source:src/platform/ui/Tooltip.tsx", + "source:src/platform/ui/VendoredIcon.tsx", + "source:src/platform/ui/color.ts", + "source:src/platform/ui/colorPickerStore.ts", + "source:src/platform/ui/colorPickerSync.ts", + "source:src/platform/ui/hints/FeatureHint.tsx", + "source:src/platform/ui/hints/hintRegistry.ts", + "source:src/platform/ui/hints/hintsContext.tsx", + "source:src/platform/ui/hints/index.ts", + "source:src/platform/ui/index.ts", + "source:src/platform/ui/list/ListDivider.tsx", + "source:src/platform/ui/list/ListItem.tsx", + "source:src/platform/ui/list/ListSectionHeader.tsx", + "source:src/platform/ui/list/ListStack.tsx", + "source:src/platform/ui/list/listLayout.ts", + "source:src/platform/ui/settings/contracts.ts", + "source:src/platform/ui/theme/applyTheme.ts", + "source:src/platform/ui/theme/recipes.ts", + "source:src/platform/ui/theme/system.ts", + "source:src/platform/ui/theme/themes.ts", + "source:src/platform/ui/toaster.tsx", + "source:src/platform/ui/useScrollAreaPhantomHeal.ts", + "source:src/platform/ui/widgetOverlayRegistry.ts", + "source:src/platform/ui/widgetOverlays.ts", + "source:src/workbench/WorkbenchContext.tsx", + "source:src/workbench/WorkbenchRuntime.tsx", + "source:src/workbench/WorkbenchSessionController.tsx", + "source:src/workbench/WorkbenchWidgetRegistryContext.tsx", + "source:src/workbench/activeInvocationSubmission.ts", + "source:src/workbench/autoRoutePolicy.ts", + "source:src/workbench/bootWidgetPreload.ts", + "source:src/workbench/canvas-engine/api.ts", + "source:src/workbench/canvas-engine/capabilities.ts", + "source:src/workbench/canvas-engine/contracts.ts", + "source:src/workbench/canvas-engine/document-model/documentModel.ts", + "source:src/workbench/canvas-engine/document-model/postconditions.ts", + "source:src/workbench/canvas-engine/document-model/screenComposition.ts", + "source:src/workbench/canvas-engine/document-model/semanticLeaf.ts", + "source:src/workbench/canvas-engine/document-model/semanticNode.ts", + "source:src/workbench/canvas-engine/document/documentIndex.ts", + "source:src/workbench/canvas-engine/document/documentTree.ts", + "source:src/workbench/canvas-engine/document/insertionAnchors.ts", + "source:src/workbench/canvas-engine/document/layerEligibility.ts", + "source:src/workbench/canvas-engine/document/layerStacks.ts", + "source:src/workbench/canvas-engine/document/mergeVisible.ts", + "source:src/workbench/canvas-engine/document/selectionRepair.ts", + "source:src/workbench/canvas-engine/document/sources.ts", + "source:src/workbench/canvas-engine/editing/controlPixelEdit.ts", + "source:src/workbench/canvas-engine/engineStores.ts", + "source:src/workbench/canvas-engine/layerExportGuards.ts", + "source:src/workbench/canvas-engine/math/mat2d.ts", + "source:src/workbench/canvas-engine/math/rect.ts", + "source:src/workbench/canvas-engine/math/snapping.ts", + "source:src/workbench/canvas-engine/mutationContracts.ts", + "source:src/workbench/canvas-engine/render/adjustments.ts", + "source:src/workbench/canvas-engine/render/compositor.ts", + "source:src/workbench/canvas-engine/render/controlTransparency.ts", + "source:src/workbench/canvas-engine/render/groupCompositeScopes.ts", + "source:src/workbench/canvas-engine/render/maskFill.ts", + "source:src/workbench/canvas-engine/render/rasterComposite.ts", + "source:src/workbench/canvas-engine/render/rasterizers/textRasterizer.ts", + "source:src/workbench/canvas-engine/transform/transformMath.ts", + "source:src/workbench/canvasDimsSync.ts", + "source:src/workbench/canvasInvocationPreparation.ts", + "source:src/workbench/canvasLayerContent.ts", + "source:src/workbench/canvasMigration.ts", + "source:src/workbench/canvasProjectMutations.ts", + "source:src/workbench/canvasSchemaVersion.ts", + "source:src/workbench/canvasStagingView.ts", + "source:src/workbench/components/QueueProgressIndicator.tsx", + "source:src/workbench/components/WorkbenchSplashScreen.tsx", + "source:src/workbench/components/WorkbenchUnavailableScreen.tsx", + "source:src/workbench/controlAdapters.ts", + "source:src/workbench/controlLayerChecks.ts", + "source:src/workbench/deferredResource.ts", + "source:src/workbench/diagnostics/loggingPreferences.ts", + "source:src/workbench/extensions/contributionSource.ts", + "source:src/workbench/extensions/extensionRegistry.ts", + "source:src/workbench/floatingWindows.ts", + "source:src/workbench/focusRegions.tsx", + "source:src/workbench/graphPreviewInvocation.ts", + "source:src/workbench/graphSurfaces.ts", + "source:src/workbench/graphWidgets.ts", + "source:src/workbench/hotkeys/WorkbenchHotkeyRuntime.tsx", + "source:src/workbench/hotkeys/catalog.ts", + "source:src/workbench/hotkeys/extensionHotkeys.ts", + "source:src/workbench/hotkeys/firstPartyCommands.ts", + "source:src/workbench/hotkeys/index.ts", + "source:src/workbench/hotkeys/keyGlyphs.tsx", + "source:src/workbench/hotkeys/keys.ts", + "source:src/workbench/hotkeys/modalLayer.ts", + "source:src/workbench/hotkeys/resolve.ts", + "source:src/workbench/hotkeys/targetWidget.ts", + "source:src/workbench/iconResolver.tsx", + "source:src/workbench/image-actions/recallParametersBridge.ts", + "source:src/workbench/image-actions/useFindGalleryItem.ts", + "source:src/workbench/invocation.ts", + "source:src/workbench/invocationSubmit.ts", + "source:src/workbench/launchpad/HelpMenu.tsx", + "source:src/workbench/launchpad/KnownBrowserIssuesAlert.tsx", + "source:src/workbench/launchpad/Launchpad.tsx", + "source:src/workbench/launchpad/LaunchpadNav.tsx", + "source:src/workbench/launchpad/LaunchpadTopBar.tsx", + "source:src/workbench/launchpad/OpenProjectsControl.tsx", + "source:src/workbench/launchpad/formatRelativeTime.ts", + "source:src/workbench/launchpad/home/IntentTiles.tsx", + "source:src/workbench/launchpad/home/LivePanel.tsx", + "source:src/workbench/launchpad/home/RecentProjectsRow.tsx", + "source:src/workbench/launchpad/home/ResumeCard.tsx", + "source:src/workbench/launchpad/intents.ts", + "source:src/workbench/launchpad/pages/HomePage.tsx", + "source:src/workbench/launchpad/pages/ProjectsPage.tsx", + "source:src/workbench/launchpad/projects/NewProjectButton.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenu.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenuHost.tsx", + "source:src/workbench/launchpad/projects/ProjectCard.tsx", + "source:src/workbench/launchpad/projects/ProjectCompatibilityBadge.tsx", + "source:src/workbench/launchpad/projects/ProjectCover.tsx", + "source:src/workbench/launchpad/projects/ProjectRow.tsx", + "source:src/workbench/launchpad/projects/ProjectsBrowser.tsx", + "source:src/workbench/launchpad/projects/ProjectsToolbar.tsx", + "source:src/workbench/launchpad/projects/projectLibraryView.ts", + "source:src/workbench/launchpad/projects/projectPins.ts", + "source:src/workbench/launchpad/projects/projectsMetrics.ts", + "source:src/workbench/launchpad/projects/useProjectCardActions.ts", + "source:src/workbench/launchpad/startEntries.ts", + "source:src/workbench/layerPanelState.ts", + "source:src/workbench/layoutContracts.ts", + "source:src/workbench/layoutPresetActivation.ts", + "source:src/workbench/layoutPresetCollection.ts", + "source:src/workbench/layoutPresetRouting.ts", + "source:src/workbench/layoutPresetSnapshots.ts", + "source:src/workbench/layoutPresets.ts", + "source:src/workbench/layoutWidgetSet.ts", + "source:src/workbench/mediaReferences.ts", + "source:src/workbench/palette/LaunchpadCommandPalette.tsx", + "source:src/workbench/palette/PaletteButton.tsx", + "source:src/workbench/palette/WorkbenchCommandPalette.tsx", + "source:src/workbench/palette/paletteStore.ts", + "source:src/workbench/palette/settingsEntryDeps.ts", + "source:src/workbench/performanceMarks.ts", + "source:src/workbench/persistenceRuntime.ts", + "source:src/workbench/projectCanvasGate.ts", + "source:src/workbench/projectEvents.ts", + "source:src/workbench/projectWorkflows.ts", + "source:src/workbench/projects/accountOwnedWorkbenchDatabase.ts", + "source:src/workbench/projects/api.ts", + "source:src/workbench/projects/components/OpenProjectDialog.tsx", + "source:src/workbench/projects/components/ProjectFileOptionsProvider.tsx", + "source:src/workbench/projects/components/index.ts", + "source:src/workbench/projects/covers.ts", + "source:src/workbench/projects/draftStore.ts", + "source:src/workbench/projects/durableSyncedPersistence.ts", + "source:src/workbench/projects/editorSession.ts", + "source:src/workbench/projects/ids.ts", + "source:src/workbench/projects/indexedDbDraftStore.ts", + "source:src/workbench/projects/invk/format.ts", + "source:src/workbench/projects/library.ts", + "source:src/workbench/projects/openProjectBroker.ts", + "source:src/workbench/projects/openProjects.ts", + "source:src/workbench/projects/projectAssets.ts", + "source:src/workbench/projects/projectDocument.ts", + "source:src/workbench/projects/projectFile.ts", + "source:src/workbench/projects/projectFileErrors.ts", + "source:src/workbench/projects/projectFileToasts.ts", + "source:src/workbench/projects/projectFlush.ts", + "source:src/workbench/projects/projectHydration.ts", + "source:src/workbench/projects/projectLifecycleLocks.ts", + "source:src/workbench/projects/projectLoadRefusal.ts", + "source:src/workbench/projects/projectTabsA11y.ts", + "source:src/workbench/projects/session.ts", + "source:src/workbench/projects/syncStore.ts", + "source:src/workbench/projects/syncedPersistence.ts", + "source:src/workbench/projects/useProjectActions.ts", + "source:src/workbench/projects/useProjectFileActions.ts", + "source:src/workbench/projects/workbenchDatabase.ts", + "source:src/workbench/queue-integration/QueueProgressRail.tsx", + "source:src/workbench/queue-integration/QueueRecoveryNotice.tsx", + "source:src/workbench/queue-integration/activeQueueRuns.ts", + "source:src/workbench/queue-integration/queueRecallCache.ts", + "source:src/workbench/queue-integration/queueRecovery.ts", + "source:src/workbench/queue-integration/queueRunJournal.ts", + "source:src/workbench/queue-integration/queueRunLocks.ts", + "source:src/workbench/queue-integration/queueRunPersistence.ts", + "source:src/workbench/queue-integration/queueRunRestoration.ts", + "source:src/workbench/queue-integration/useActiveQueueProgress.ts", + "source:src/workbench/queue-integration/useQueueRecoveryProjectIds.ts", + "source:src/workbench/settings/SettingsDialogHost.tsx", + "source:src/workbench/settings/WidgetSettingsButton.tsx", + "source:src/workbench/settings/applicationContributions.ts", + "source:src/workbench/settings/catalog.ts", + "source:src/workbench/settings/clearWorkspaceData.ts", + "source:src/workbench/settings/dialogResource.ts", + "source:src/workbench/settings/launchpad.tsx", + "source:src/workbench/settings/settingsDialogStore.ts", + "source:src/workbench/settings/settingsSearchShortcut.ts", + "source:src/workbench/settings/store.ts", + "source:src/workbench/shell/BottomPanel.tsx", + "source:src/workbench/shell/CenterArea.tsx", + "source:src/workbench/shell/DocumentTitleProgress.tsx", + "source:src/workbench/shell/Panels.tsx", + "source:src/workbench/shell/PasteMediaRuntime.tsx", + "source:src/workbench/shell/ProjectConflictBanner.tsx", + "source:src/workbench/shell/QueueRecoveryBanner.tsx", + "source:src/workbench/shell/StatusBar.tsx", + "source:src/workbench/shell/WorkbenchShell.tsx", + "source:src/workbench/shell/documentTitle.ts", + "source:src/workbench/shell/holdToDragSensor.ts", + "source:src/workbench/shell/index.ts", + "source:src/workbench/shell/notifications/WorkbenchNotificationToaster.tsx", + "source:src/workbench/shell/notifications/index.ts", + "source:src/workbench/shell/notifications/toastPolicy.ts", + "source:src/workbench/shell/topbar/AppMenu.tsx", + "source:src/workbench/shell/topbar/InvocationCluster.tsx", + "source:src/workbench/shell/topbar/InvokeButton.tsx", + "source:src/workbench/shell/topbar/IterationsField.tsx", + "source:src/workbench/shell/topbar/LayoutPresetAdminDialogs.tsx", + "source:src/workbench/shell/topbar/LayoutPresetDialog.tsx", + "source:src/workbench/shell/topbar/LayoutPresetManagerDialog.tsx", + "source:src/workbench/shell/topbar/LayoutPresetStrip.tsx", + "source:src/workbench/shell/topbar/ProjectSwitcher.tsx", + "source:src/workbench/shell/topbar/QueueCluster.tsx", + "source:src/workbench/shell/topbar/RoutingControl.tsx", + "source:src/workbench/shell/topbar/RoutingDestinationSegments.tsx", + "source:src/workbench/shell/topbar/TopBar.tsx", + "source:src/workbench/shell/topbar/TopbarProgressRail.tsx", + "source:src/workbench/shell/topbar/TopbarShortcutKeys.tsx", + "source:src/workbench/shell/topbar/index.ts", + "source:src/workbench/shell/topbar/invokeButtonModel.ts", + "source:src/workbench/shell/topbar/layoutPresetDialogModel.ts", + "source:src/workbench/shell/topbar/layoutPresetIcons.ts", + "source:src/workbench/shell/topbar/layoutPresetManagerStore.ts", + "source:src/workbench/shell/topbar/projectSwitcherStore.ts", + "source:src/workbench/shell/topbar/topbarBreakpoints.ts", + "source:src/workbench/shell/topbar/useInvocationState.ts", + "source:src/workbench/shell/topbar/useLayoutDrift.ts", + "source:src/workbench/shell/topbar/useTopbarShortcut.ts", + "source:src/workbench/shell/useMountedInstanceIds.ts", + "source:src/workbench/useNotify.ts", + "source:src/workbench/useOpenWorkbenchWidget.ts", + "source:src/workbench/widget-frame/FloatingWidgetLayer.tsx", + "source:src/workbench/widget-frame/WidgetActionsMenu.tsx", + "source:src/workbench/widget-frame/WidgetBar.tsx", + "source:src/workbench/widget-frame/WidgetEnableMenu.tsx", + "source:src/workbench/widget-frame/WidgetFailureBoundary.tsx", + "source:src/workbench/widget-frame/WidgetFrames.tsx", + "source:src/workbench/widget-frame/WidgetHosts.tsx", + "source:src/workbench/widget-frame/WidgetIdentityIcon.tsx", + "source:src/workbench/widget-frame/WidgetInstanceContextMenu.tsx", + "source:src/workbench/widget-frame/WidgetLoadingFallback.tsx", + "source:src/workbench/widget-frame/WidgetRegionDropOverlay.tsx", + "source:src/workbench/widget-frame/WidgetRenderer.tsx", + "source:src/workbench/widget-frame/WidgetSourceLockBadge.tsx", + "source:src/workbench/widget-frame/WidgetStrip.tsx", + "source:src/workbench/widget-frame/createWidgetRuntime.ts", + "source:src/workbench/widget-frame/index.ts", + "source:src/workbench/widget-frame/useWidgetIntentPreload.ts", + "source:src/workbench/widget-frame/useWidgetSortable.ts", + "source:src/workbench/widget-frame/widgetRenderInstance.ts", + "source:src/workbench/widgetDnd.ts", + "source:src/workbench/widgetImplementationResource.ts", + "source:src/workbench/widgetLabels.ts", + "source:src/workbench/widgetPlacementCommands.ts", + "source:src/workbench/widgetPlacementMeta.ts", + "source:src/workbench/widgetRegionViewModel.ts", + "source:src/workbench/widgetRegistry.ts", + "source:src/workbench/widgetState.ts", + "source:src/workbench/widgets/autosave-status/manifest.ts", + "source:src/workbench/widgets/canvas/bboxGrid.ts", + "source:src/workbench/widgets/canvas/canvasSettings.ts", + "source:src/workbench/widgets/canvas/invoke/canvasCompositing.ts", + "source:src/workbench/widgets/canvas/invoke/canvasStrength.ts", + "source:src/workbench/widgets/canvas/manifest.ts", + "source:src/workbench/widgets/canvas/settingsContribution.ts", + "source:src/workbench/widgets/diagnostics/manifest.ts", + "source:src/workbench/widgets/gallery/manifest.ts", + "source:src/workbench/widgets/generate/manifest.ts", + "source:src/workbench/widgets/image-map/manifest.ts", + "source:src/workbench/widgets/image-map/settingsContribution.ts", + "source:src/workbench/widgets/layers/manifest.ts", + "source:src/workbench/widgets/layers/panes/editorPaneLayout.ts", + "source:src/workbench/widgets/manifests.ts", + "source:src/workbench/widgets/notifications/manifest.ts", + "source:src/workbench/widgets/preview/livePreviewFollow.tsx", + "source:src/workbench/widgets/preview/manifest.ts", + "source:src/workbench/widgets/preview/previewSettings.ts", + "source:src/workbench/widgets/preview/settingsContribution.ts", + "source:src/workbench/widgets/preview/spanPlaybackRequest.ts", + "source:src/workbench/widgets/project/manifest.ts", + "source:src/workbench/widgets/queue-status/manifest.ts", + "source:src/workbench/widgets/queue/manifest.ts", + "source:src/workbench/widgets/server-status/manifest.ts", + "source:src/workbench/widgets/upscale/manifest.ts", + "source:src/workbench/widgets/video/manifest.ts", + "source:src/workbench/widgets/workflow/manifest.ts", + "source:src/workbench/workbenchState.ts", + "source:src/workbench/workbenchStore.ts" + ] + }, + "owner": "workbench", + "remediationTicket": "deepen-widget-registry-loading", + "source": "src/app/WorkbenchApp.tsx" + } + }, + "capturedAt": "2026-09-29", + "developmentInvalidation": { + "platformUiBarrel": { + "specifier": "@platform/ui", + "owner": "platform-ui", + "remediationTicket": "return-contracts-to-owners", + "maxDirectImporters": 160 + }, + "retiredWorkbenchTypeHub": { + "specifier": "@workbench/types", + "owner": "workbench", + "remediationTicket": "return-contracts-to-owners", + "maxDirectImporters": 0 + }, + "canvasContracts": { + "specifier": "@workbench/canvas-engine/contracts", + "owner": "canvas", + "remediationTicket": "seal-canvas-engine-seam", + "maxDirectImporters": 120 + }, + "queuePublicInterface": { + "specifier": "@features/queue", + "owner": "queue", + "remediationTicket": "finish-queue-ownership", + "maxDirectImporters": 24 + }, + "isolatedGalleryView": { + "specifier": "./GalleryWidgetView", + "owner": "gallery", + "remediationTicket": "define-gallery-feature", + "maxDirectImporters": 1 + } + }, + "schemaVersion": 2, + "structural": { + "launchpadForbiddenInitialSources": [ + "_ag-psd", + "src/app/WorkbenchApp.tsx", + "src/features/gallery/ui/launchpad/RecentOutputs.tsx", + "src/features/intermediates/ui/IntermediatesManager.tsx", + "src/features/models/ui/ModelManagerView.tsx", + "src/features/models/ui/launchpad/ModelsNotice.tsx", + "src/features/nodes/ui/NodeManagerView.tsx", + "src/features/queue/ui/launchpad/QueueStatusBand.tsx", + "src/workbench/launchpad/home/livePanels.ts" + ], + "editorForbiddenInitialChunkNames": ["ag-psd"], + "inactiveWidgetGateOwner": "deepen-widget-registry-loading" + } +} diff --git a/invokeai/frontend/webv2/performance/architecture-fixtures.json b/invokeai/frontend/webv2/performance/architecture-fixtures.json new file mode 100644 index 00000000000..ab8b9c795ed --- /dev/null +++ b/invokeai/frontend/webv2/performance/architecture-fixtures.json @@ -0,0 +1,162 @@ +{ + "schemaVersion": 2, + "scoredSamples": 5, + "traceSamples": 1, + "warmups": 1, + "stateProfiles": { + "empty": { + "images": 0, + "layers": 0, + "models": 0, + "nodes": 0, + "projects": 0, + "queueItems": 0, + "workflowNodes": 0 + }, + "representative": { + "images": 1000, + "layers": 64, + "models": 100, + "nodes": 100, + "projects": 40, + "queueItems": 500, + "workflowNodes": 100 + } + }, + "routes": [ + { + "id": "launchpad", + "stateProfile": "empty", + "owner": "app", + "path": "/#/", + "readyMark": "invokeai:ready:launchpad", + "remediationTicket": "set-performance-budgets" + }, + { + "id": "launchpad", + "stateProfile": "representative", + "owner": "app", + "path": "/#/", + "readyMark": "invokeai:ready:launchpad", + "remediationTicket": "set-performance-budgets" + }, + { + "id": "editor-minimal", + "stateProfile": "empty", + "owner": "workbench", + "path": "/#/app?new=true", + "readyMark": "invokeai:ready:widget:center:preview", + "expectedScriptPattern": "WorkbenchApp-", + "measureProjectSwitch": true, + "requiredWidgetRequests": ["GalleryImageActionsBridge", "widget:generate", "widget:preview", "widget:gallery"], + "forbiddenWidgetRequests": ["widget:canvas", "widget:layers", "widget:project", "widget:upscale"], + "remediationTicket": "deepen-widget-registry-loading" + }, + { + "id": "editor-minimal", + "stateProfile": "representative", + "owner": "workbench", + "path": "/#/app?project=fixture-project-001", + "readyMark": "invokeai:ready:widget:center:preview", + "expectedScriptPattern": "WorkbenchApp-", + "measureProjectSwitch": true, + "requiredWidgetRequests": ["GalleryImageActionsBridge", "widget:generate", "widget:preview", "widget:gallery"], + "forbiddenWidgetRequests": ["widget:canvas", "widget:layers", "widget:project", "widget:upscale"], + "remediationTicket": "deepen-widget-registry-loading" + }, + { + "id": "editor-canvas", + "stateProfile": "empty", + "owner": "canvas", + "path": "/#/app?new=true", + "readyMark": "invokeai:ready:widget:center:canvas", + "expectedScriptPattern": "WorkbenchApp-", + "layoutPreset": "Edit", + "layoutPresetId": "edit", + "layoutReturnPreset": "Compose", + "layoutReturnPresetId": "compose", + "layoutReturnReadyMark": "invokeai:ready:widget:center:preview", + "preActivationRequiredWidgetRequests": ["GalleryImageActionsBridge"], + "requiredWidgetRequests": ["GalleryImageActionsBridge", "widget:canvas", "widget:layers"], + "forbiddenWidgetRequests": ["widget:project", "widget:upscale"], + "remediationTicket": "deepen-widget-registry-loading" + }, + { + "id": "editor-canvas", + "stateProfile": "representative", + "owner": "canvas", + "path": "/#/app?project=fixture-project-001", + "readyMark": "invokeai:ready:widget:center:canvas", + "expectedScriptPattern": "WorkbenchApp-", + "layoutPreset": "Edit", + "layoutPresetId": "edit", + "layoutReturnPreset": "Compose", + "layoutReturnPresetId": "compose", + "layoutReturnReadyMark": "invokeai:ready:widget:center:preview", + "preActivationRequiredWidgetRequests": ["GalleryImageActionsBridge"], + "requiredWidgetRequests": ["GalleryImageActionsBridge", "widget:canvas", "widget:layers"], + "forbiddenWidgetRequests": ["widget:project", "widget:upscale"], + "remediationTicket": "deepen-widget-registry-loading" + }, + { + "id": "editor-workflow", + "stateProfile": "empty", + "owner": "workflow", + "path": "/#/app?new=true", + "readyMark": "invokeai:ready:widget:center:workflow", + "expectedScriptPattern": "WorkbenchApp-", + "layoutPreset": "Automate", + "layoutPresetId": "automate", + "layoutReturnPreset": "Compose", + "layoutReturnPresetId": "compose", + "layoutReturnReadyMark": "invokeai:ready:widget:center:preview", + "preActivationRequiredWidgetRequests": ["GalleryImageActionsBridge"], + "requiredWidgetRequests": ["GalleryImageActionsBridge", "widget:workflow", "widget:queue"], + "forbiddenWidgetRequests": ["widget:canvas", "widget:layers", "widget:project", "widget:upscale"], + "remediationTicket": "deepen-widget-registry-loading" + }, + { + "id": "editor-workflow", + "stateProfile": "representative", + "owner": "workflow", + "path": "/#/app?project=fixture-project-001", + "readyMark": "invokeai:ready:widget:center:workflow", + "expectedScriptPattern": "WorkbenchApp-", + "layoutPreset": "Automate", + "layoutPresetId": "automate", + "layoutReturnPreset": "Compose", + "layoutReturnPresetId": "compose", + "layoutReturnReadyMark": "invokeai:ready:widget:center:preview", + "preActivationRequiredWidgetRequests": ["GalleryImageActionsBridge"], + "requiredWidgetRequests": ["GalleryImageActionsBridge", "widget:workflow", "widget:queue"], + "forbiddenWidgetRequests": ["widget:canvas", "widget:layers", "widget:project", "widget:upscale"], + "remediationTicket": "deepen-widget-registry-loading" + }, + { + "id": "editor-gallery", + "stateProfile": "empty", + "owner": "gallery", + "path": "/#/app?new=true", + "readyMark": "invokeai:ready:widget:center:gallery", + "expectedScriptPattern": "WorkbenchApp-", + "centerView": "Gallery", + "preActivationRequiredWidgetRequests": ["GalleryImageActionsBridge"], + "requiredWidgetRequests": ["GalleryImageActionsBridge", "widget:gallery", "widget:preview"], + "forbiddenWidgetRequests": ["widget:canvas", "widget:layers", "widget:project", "widget:upscale"], + "remediationTicket": "deepen-widget-registry-loading" + }, + { + "id": "editor-gallery", + "stateProfile": "representative", + "owner": "gallery", + "path": "/#/app?project=fixture-project-001", + "readyMark": "invokeai:ready:widget:center:gallery", + "expectedScriptPattern": "WorkbenchApp-", + "centerView": "Gallery", + "preActivationRequiredWidgetRequests": ["GalleryImageActionsBridge"], + "requiredWidgetRequests": ["GalleryImageActionsBridge", "widget:gallery", "widget:preview"], + "forbiddenWidgetRequests": ["widget:canvas", "widget:layers", "widget:project", "widget:upscale"], + "remediationTicket": "deepen-widget-registry-loading" + } + ] +} diff --git a/invokeai/frontend/webv2/performance/browser-baseline.json b/invokeai/frontend/webv2/performance/browser-baseline.json new file mode 100644 index 00000000000..20420c48665 --- /dev/null +++ b/invokeai/frontend/webv2/performance/browser-baseline.json @@ -0,0 +1,4177 @@ +{ + "browserExecutable": "/home/toast/.cache/ms-playwright/chromium-1243/chrome-linux64/chrome", + "capturedAt": "2026-10-01", + "routes": [ + { + "activatedResourceBaseline": { + "cssRawBytes": 0, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 0, + "otherRawBytes": 0, + "requestCount": 0, + "scriptRawBytes": 0, + "scriptRequestCount": 0, + "totalRawBytes": 0 + }, + "domContentLoadedMedianMs": 128, + "id": "launchpad", + "layoutAckMedianMs": 0, + "layoutReturnSwitchMedianMs": 0, + "layoutSwitchMedianMs": 0, + "loadMedianMs": 128.09999999776483, + "longestTaskMaxMs": 81, + "owner": "app", + "projectSwitchMedianMs": 0, + "readyMark": "invokeai:ready:launchpad", + "remediationTicket": "set-performance-budgets", + "resourceBaseline": { + "cssRawBytes": 2159, + "fontRawBytes": 23664, + "imageRawBytes": 0, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 31, + "scriptRawBytes": 1806891, + "scriptRequestCount": 27, + "totalRawBytes": 2017245 + }, + "routeReadyMedianMs": 266.29999999701977, + "scriptSourceOwnerSet": "launchpad-static", + "stateProfile": "empty" + }, + { + "activatedResourceBaseline": { + "cssRawBytes": 0, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 0, + "otherRawBytes": 0, + "requestCount": 0, + "scriptRawBytes": 0, + "scriptRequestCount": 0, + "totalRawBytes": 0 + }, + "domContentLoadedMedianMs": 125, + "id": "launchpad", + "layoutAckMedianMs": 0, + "layoutReturnSwitchMedianMs": 0, + "layoutSwitchMedianMs": 0, + "loadMedianMs": 125.09999999776483, + "longestTaskMaxMs": 80, + "owner": "app", + "projectSwitchMedianMs": 0, + "readyMark": "invokeai:ready:launchpad", + "remediationTicket": "set-performance-budgets", + "resourceBaseline": { + "cssRawBytes": 2159, + "fontRawBytes": 23664, + "imageRawBytes": 0, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 31, + "scriptRawBytes": 1806891, + "scriptRequestCount": 27, + "totalRawBytes": 2017245 + }, + "routeReadyMedianMs": 275.6000000014901, + "scriptSourceOwnerSet": "launchpad-static", + "stateProfile": "representative" + }, + { + "activatedResourceBaseline": { + "cssRawBytes": 0, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 0, + "otherRawBytes": 0, + "requestCount": 0, + "scriptRawBytes": 0, + "scriptRequestCount": 0, + "totalRawBytes": 0 + }, + "domContentLoadedMedianMs": 129.30000000074506, + "id": "editor-minimal", + "layoutAckMedianMs": 0, + "layoutReturnSwitchMedianMs": 0, + "layoutSwitchMedianMs": 0, + "loadMedianMs": 129.5, + "longestTaskMaxMs": 160, + "owner": "workbench", + "projectSwitchMedianMs": 36.80000000074506, + "readyMark": "invokeai:ready:widget:center:preview", + "remediationTicket": "deepen-widget-registry-loading", + "resourceBaseline": { + "cssRawBytes": 2159, + "fontRawBytes": 23664, + "imageRawBytes": 156376, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 89, + "scriptRawBytes": 3900320, + "scriptRequestCount": 84, + "totalRawBytes": 4267050 + }, + "routeReadyMedianMs": 424.0999999977648, + "scriptSourceOwnerSet": "editor-minimal-static", + "stateProfile": "empty" + }, + { + "activatedResourceBaseline": { + "cssRawBytes": 0, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 0, + "otherRawBytes": 0, + "requestCount": 0, + "scriptRawBytes": 0, + "scriptRequestCount": 0, + "totalRawBytes": 0 + }, + "domContentLoadedMedianMs": 125.60000000149012, + "id": "editor-minimal", + "layoutAckMedianMs": 0, + "layoutReturnSwitchMedianMs": 0, + "layoutSwitchMedianMs": 0, + "loadMedianMs": 125.80000000074506, + "longestTaskMaxMs": 162, + "owner": "workbench", + "projectSwitchMedianMs": 39.80000000074506, + "readyMark": "invokeai:ready:widget:center:preview", + "remediationTicket": "deepen-widget-registry-loading", + "resourceBaseline": { + "cssRawBytes": 2159, + "fontRawBytes": 23664, + "imageRawBytes": 156376, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 89, + "scriptRawBytes": 3900320, + "scriptRequestCount": 84, + "totalRawBytes": 4267050 + }, + "routeReadyMedianMs": 429.80000000074506, + "scriptSourceOwnerSet": "editor-minimal-static", + "stateProfile": "representative" + }, + { + "activatedResourceBaseline": { + "cssRawBytes": 0, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 310063, + "otherRawBytes": 0, + "requestCount": 9, + "scriptRawBytes": 507630, + "scriptRequestCount": 9, + "totalRawBytes": 507630 + }, + "domContentLoadedMedianMs": 128.30000000074506, + "id": "editor-canvas", + "layoutAckMedianMs": 2.800000000745058, + "layoutReturnSwitchMedianMs": 44.400000002235174, + "layoutSwitchMedianMs": 86.89999999850988, + "loadMedianMs": 128.5, + "longestTaskMaxMs": 160, + "owner": "canvas", + "projectSwitchMedianMs": 0, + "readyMark": "invokeai:ready:widget:center:canvas", + "remediationTicket": "deepen-widget-registry-loading", + "resourceBaseline": { + "cssRawBytes": 2159, + "fontRawBytes": 23664, + "imageRawBytes": 156376, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 98, + "scriptRawBytes": 4407950, + "scriptRequestCount": 93, + "totalRawBytes": 4774680 + }, + "routeReadyMedianMs": 1163.800000000745, + "scriptSourceOwnerSet": "editor-canvas-static", + "stateProfile": "empty" + }, + { + "activatedResourceBaseline": { + "cssRawBytes": 0, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 310063, + "otherRawBytes": 0, + "requestCount": 9, + "scriptRawBytes": 507630, + "scriptRequestCount": 9, + "totalRawBytes": 507630 + }, + "domContentLoadedMedianMs": 129.79999999701977, + "id": "editor-canvas", + "layoutAckMedianMs": 2.300000000745058, + "layoutReturnSwitchMedianMs": 52.5, + "layoutSwitchMedianMs": 88, + "loadMedianMs": 129.89999999850988, + "longestTaskMaxMs": 199, + "owner": "canvas", + "projectSwitchMedianMs": 0, + "readyMark": "invokeai:ready:widget:center:canvas", + "remediationTicket": "deepen-widget-registry-loading", + "resourceBaseline": { + "cssRawBytes": 2159, + "fontRawBytes": 23664, + "imageRawBytes": 156376, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 98, + "scriptRawBytes": 4407950, + "scriptRequestCount": 93, + "totalRawBytes": 4774680 + }, + "routeReadyMedianMs": 1132.9000000022352, + "scriptSourceOwnerSet": "editor-canvas-static", + "stateProfile": "representative" + }, + { + "activatedResourceBaseline": { + "cssRawBytes": 15413, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 181637, + "otherRawBytes": 0, + "requestCount": 7, + "scriptRawBytes": 415221, + "scriptRequestCount": 6, + "totalRawBytes": 430634 + }, + "domContentLoadedMedianMs": 126.40000000223517, + "id": "editor-workflow", + "layoutAckMedianMs": 2.5, + "layoutReturnSwitchMedianMs": 34.100000001490116, + "layoutSwitchMedianMs": 92.19999999925494, + "loadMedianMs": 126.5, + "longestTaskMaxMs": 167, + "owner": "workflow", + "projectSwitchMedianMs": 0, + "readyMark": "invokeai:ready:widget:center:workflow", + "remediationTicket": "deepen-widget-registry-loading", + "resourceBaseline": { + "cssRawBytes": 17572, + "fontRawBytes": 23664, + "imageRawBytes": 156376, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 96, + "scriptRawBytes": 4315541, + "scriptRequestCount": 90, + "totalRawBytes": 4697684 + }, + "routeReadyMedianMs": 1172.300000000745, + "scriptSourceOwnerSet": "editor-workflow-static", + "stateProfile": "empty" + }, + { + "activatedResourceBaseline": { + "cssRawBytes": 15413, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 181637, + "otherRawBytes": 0, + "requestCount": 7, + "scriptRawBytes": 415221, + "scriptRequestCount": 6, + "totalRawBytes": 430634 + }, + "domContentLoadedMedianMs": 126.80000000074506, + "id": "editor-workflow", + "layoutAckMedianMs": 2.699999999254942, + "layoutReturnSwitchMedianMs": 43.80000000074506, + "layoutSwitchMedianMs": 162.80000000074506, + "loadMedianMs": 126.89999999850988, + "longestTaskMaxMs": 435, + "owner": "workflow", + "projectSwitchMedianMs": 0, + "readyMark": "invokeai:ready:widget:center:workflow", + "remediationTicket": "deepen-widget-registry-loading", + "resourceBaseline": { + "cssRawBytes": 17572, + "fontRawBytes": 23664, + "imageRawBytes": 156376, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 96, + "scriptRawBytes": 4315541, + "scriptRequestCount": 90, + "totalRawBytes": 4697684 + }, + "routeReadyMedianMs": 1196.7000000029802, + "scriptSourceOwnerSet": "editor-workflow-static", + "stateProfile": "representative" + }, + { + "activatedResourceBaseline": { + "cssRawBytes": 0, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 0, + "otherRawBytes": 0, + "requestCount": 0, + "scriptRawBytes": 0, + "scriptRequestCount": 0, + "totalRawBytes": 0 + }, + "domContentLoadedMedianMs": 132.69999999925494, + "id": "editor-gallery", + "layoutAckMedianMs": 0, + "layoutReturnSwitchMedianMs": 0, + "layoutSwitchMedianMs": 35.80000000074506, + "loadMedianMs": 132.80000000074506, + "longestTaskMaxMs": 174, + "owner": "gallery", + "projectSwitchMedianMs": 0, + "readyMark": "invokeai:ready:widget:center:gallery", + "remediationTicket": "deepen-widget-registry-loading", + "resourceBaseline": { + "cssRawBytes": 2159, + "fontRawBytes": 23664, + "imageRawBytes": 156376, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 89, + "scriptRawBytes": 3900320, + "scriptRequestCount": 84, + "totalRawBytes": 4267050 + }, + "routeReadyMedianMs": 1390.300000000745, + "scriptSourceOwnerSet": "editor-minimal-static", + "stateProfile": "empty" + }, + { + "activatedResourceBaseline": { + "cssRawBytes": 0, + "fontRawBytes": 0, + "imageRawBytes": 0, + "largestAssetRawBytes": 0, + "otherRawBytes": 0, + "requestCount": 0, + "scriptRawBytes": 0, + "scriptRequestCount": 0, + "totalRawBytes": 0 + }, + "domContentLoadedMedianMs": 128.80000000074506, + "id": "editor-gallery", + "layoutAckMedianMs": 0, + "layoutReturnSwitchMedianMs": 0, + "layoutSwitchMedianMs": 46.69999999925494, + "loadMedianMs": 129, + "longestTaskMaxMs": 159, + "owner": "gallery", + "projectSwitchMedianMs": 0, + "readyMark": "invokeai:ready:widget:center:gallery", + "remediationTicket": "deepen-widget-registry-loading", + "resourceBaseline": { + "cssRawBytes": 2159, + "fontRawBytes": 23664, + "imageRawBytes": 156376, + "largestAssetRawBytes": 749035, + "otherRawBytes": 184531, + "requestCount": 89, + "scriptRawBytes": 3900320, + "scriptRequestCount": 84, + "totalRawBytes": 4267050 + }, + "routeReadyMedianMs": 1363.699999999255, + "scriptSourceOwnerSet": "editor-minimal-static", + "stateProfile": "representative" + } + ], + "sampling": { + "scoredSamples": 5, + "traceSamples": 1, + "warmups": 1 + }, + "schemaVersion": 2, + "scriptSourceOwnerSets": { + "launchpad-static": [ + "package:@ark-ui/react", + "package:@chakra-ui/react", + "package:@emotion/cache", + "package:@emotion/hash", + "package:@emotion/is-prop-valid", + "package:@emotion/memoize", + "package:@emotion/react", + "package:@emotion/serialize", + "package:@emotion/sheet", + "package:@emotion/unitless", + "package:@emotion/use-insertion-effect-with-fallbacks", + "package:@emotion/utils", + "package:@floating-ui/core", + "package:@floating-ui/dom", + "package:@floating-ui/utils", + "package:@fontsource/inter", + "package:@internationalized/number", + "package:@pandacss/is-valid-prop", + "package:@socket.io/component-emitter", + "package:@tanstack/history", + "package:@tanstack/query-core", + "package:@tanstack/react-query", + "package:@tanstack/react-router", + "package:@tanstack/react-store", + "package:@tanstack/react-virtual", + "package:@tanstack/router-core", + "package:@tanstack/store", + "package:@tanstack/virtual-core", + "package:@zag-js/accordion", + "package:@zag-js/anatomy", + "package:@zag-js/aria-hidden", + "package:@zag-js/auto-resize", + "package:@zag-js/avatar", + "package:@zag-js/carousel", + "package:@zag-js/checkbox", + "package:@zag-js/clipboard", + "package:@zag-js/collapsible", + "package:@zag-js/collection", + "package:@zag-js/color-picker", + "package:@zag-js/color-utils", + "package:@zag-js/combobox", + "package:@zag-js/core", + "package:@zag-js/date-input", + "package:@zag-js/date-picker", + "package:@zag-js/dialog", + "package:@zag-js/dismissable", + "package:@zag-js/dom-query", + "package:@zag-js/editable", + "package:@zag-js/file-upload", + "package:@zag-js/floating-panel", + "package:@zag-js/focus-trap", + "package:@zag-js/focus-visible", + "package:@zag-js/hover-card", + "package:@zag-js/interact-outside", + "package:@zag-js/listbox", + "package:@zag-js/live-region", + "package:@zag-js/marquee", + "package:@zag-js/menu", + "package:@zag-js/number-input", + "package:@zag-js/pagination", + "package:@zag-js/pin-input", + "package:@zag-js/popover", + "package:@zag-js/popper", + "package:@zag-js/presence", + "package:@zag-js/progress", + "package:@zag-js/qr-code", + "package:@zag-js/radio-group", + "package:@zag-js/rating-group", + "package:@zag-js/react", + "package:@zag-js/rect-utils", + "package:@zag-js/remove-scroll", + "package:@zag-js/scroll-area", + "package:@zag-js/select", + "package:@zag-js/slider", + "package:@zag-js/splitter", + "package:@zag-js/switch", + "package:@zag-js/tabs", + "package:@zag-js/tags-input", + "package:@zag-js/toast", + "package:@zag-js/tooltip", + "package:@zag-js/tree-view", + "package:@zag-js/types", + "package:@zag-js/utils", + "package:classcat", + "package:engine.io-client", + "package:engine.io-parser", + "package:fuzzysort", + "package:hoist-non-react-statics", + "package:i18next", + "package:i18next-http-backend", + "package:idb", + "package:lucide-react", + "package:react", + "package:react-dom", + "package:react-hook-tanstack-virtual", + "package:react-i18next", + "package:react-is", + "package:scheduler", + "package:socket.io-client", + "package:socket.io-parser", + "package:stylis", + "package:tinykeys", + "package:use-sync-external-store", + "package:zod", + "package:zustand", + "source:index.html", + "source:src/app/App.tsx", + "source:src/app/AppProviders.tsx", + "source:src/app/FeatureHintsProvider.tsx", + "source:src/app/I18nController.tsx", + "source:src/app/SocketHubRuntime.tsx", + "source:src/app/ThemeController.tsx", + "source:src/app/accountLifecycle.ts", + "source:src/app/loggingRuntime.ts", + "source:src/app/main.tsx", + "source:src/app/router.tsx", + "source:src/assets/SplashImage.webp", + "source:src/features/fonts/data/keys.ts", + "source:src/features/fonts/launchpad.tsx", + "source:src/features/fonts/react.tsx", + "source:src/features/fonts/runtime.ts", + "source:src/features/gallery/core/imagePaths.ts", + "source:src/features/gallery/core/items.ts", + "source:src/features/gallery/core/recentImages.ts", + "source:src/features/gallery/core/selection.ts", + "source:src/features/gallery/core/semanticImageQuery.ts", + "source:src/features/gallery/core/settings.ts", + "source:src/features/gallery/core/videoPaths.ts", + "source:src/features/gallery/data/backend.ts", + "source:src/features/gallery/data/imageUrls.ts", + "source:src/features/gallery/data/videoUrls.ts", + "source:src/features/gallery/launchpad.ts", + "source:src/features/gallery/ui/GalleryUiContext.tsx", + "source:src/features/gallery/ui/galleryStateView.ts", + "source:src/features/gallery/ui/launchpad/RecentOutputs.tsx", + "source:src/features/identity/capabilities.ts", + "source:src/features/identity/core/schemas.ts", + "source:src/features/identity/core/sessionPolicy.ts", + "source:src/features/identity/core/tokenStorage.ts", + "source:src/features/identity/data/api.ts", + "source:src/features/identity/index.ts", + "source:src/features/identity/session.ts", + "source:src/features/identity/transportAdapter.ts", + "source:src/features/identity/ui/AccountMenu.tsx", + "source:src/features/identity/ui/AccountMenuSection.tsx", + "source:src/features/identity/ui/AuthScreen.tsx", + "source:src/features/identity/ui/AuthUnavailableScreen.tsx", + "source:src/features/identity/ui/LoginScreen.tsx", + "source:src/features/identity/ui/PasswordInput.tsx", + "source:src/features/identity/ui/ProfileDialog.tsx", + "source:src/features/identity/ui/SetupScreen.tsx", + "source:src/features/identity/ui/UserFormDialog.tsx", + "source:src/features/identity/ui/UserManagement.tsx", + "source:src/features/identity/ui/UsersPage.tsx", + "source:src/features/identity/ui/useIdentityNotify.ts", + "source:src/features/intermediates/data/focus.ts", + "source:src/features/intermediates/index.ts", + "source:src/features/models/core/baseIdentity.ts", + "source:src/features/models/core/library.ts", + "source:src/features/models/core/taxonomy.ts", + "source:src/features/models/data/api.ts", + "source:src/features/models/data/apiKeys.ts", + "source:src/features/models/data/installsStore.ts", + "source:src/features/models/data/modelLoadStore.ts", + "source:src/features/models/data/modelsStore.ts", + "source:src/features/models/data/startersStore.ts", + "source:src/features/models/data/transport.ts", + "source:src/features/models/index.ts", + "source:src/features/models/launchpad.ts", + "source:src/features/models/ui/ModelInstallRuntime.tsx", + "source:src/features/models/ui/ModelsPage.tsx", + "source:src/features/models/ui/ModelsUiContext.tsx", + "source:src/features/models/ui/add-models/useInstallActions.ts", + "source:src/features/models/ui/launchpad/ModelsNotice.tsx", + "source:src/features/models/ui/uiStore.ts", + "source:src/features/models/ui/useInstallOutcomeToasts.ts", + "source:src/features/models/ui/useModelsNotify.ts", + "source:src/features/nodes/core/catalog.ts", + "source:src/features/nodes/data/api.ts", + "source:src/features/nodes/data/nodeExecutionStore.ts", + "source:src/features/nodes/data/nodesStore.ts", + "source:src/features/nodes/data/transport.ts", + "source:src/features/nodes/index.ts", + "source:src/features/nodes/ui/NodesPage.tsx", + "source:src/features/queue/contracts.ts", + "source:src/features/queue/core/generationMeta.ts", + "source:src/features/queue/core/historySnapshot.ts", + "source:src/features/queue/core/historySummary.ts", + "source:src/features/queue/core/progressRail.ts", + "source:src/features/queue/core/promptBatch.ts", + "source:src/features/queue/core/submissionRules.ts", + "source:src/features/queue/data/events.ts", + "source:src/features/queue/data/httpRealtimeQueueBackend.ts", + "source:src/features/queue/data/mappers.ts", + "source:src/features/queue/data/queries.ts", + "source:src/features/queue/data/serverApi.ts", + "source:src/features/queue/data/submissionApi.ts", + "source:src/features/queue/launchpad.ts", + "source:src/features/queue/ui/launchpad/QueueStatusBand.tsx", + "source:src/platform/browser/downloadBlob.ts", + "source:src/platform/browser/knownBrowserIssues.ts", + "source:src/platform/browser/randomUuid.ts", + "source:src/platform/browser/sha256.ts", + "source:src/platform/core/concurrency.ts", + "source:src/platform/core/outputImages.ts", + "source:src/platform/core/seed.ts", + "source:src/platform/i18n/client.ts", + "source:src/platform/i18n/languages.ts", + "source:src/platform/logging/contracts.ts", + "source:src/platform/logging/logger.ts", + "source:src/platform/logging/normalize.ts", + "source:src/platform/performance/semanticReady.ts", + "source:src/platform/pwa/registerServiceWorker.ts", + "source:src/platform/query/client.ts", + "source:src/platform/react/useMountEffect.ts", + "source:src/platform/react/usePreservedScrollOffset.ts", + "source:src/platform/react/useZodForm.ts", + "source:src/platform/search/dateTokens.ts", + "source:src/platform/state/accountLifecycle.ts", + "source:src/platform/state/externalStore.ts", + "source:src/platform/state/externalStoreCore.ts", + "source:src/platform/state/selectorCore.ts", + "source:src/platform/state/selectors.ts", + "source:src/platform/state/singleFlight.ts", + "source:src/platform/time/serverTimestamp.ts", + "source:src/platform/transport/connectionStore.ts", + "source:src/platform/transport/deploymentBase.ts", + "source:src/platform/transport/http.ts", + "source:src/platform/transport/socketHub.ts", + "source:src/platform/ui/Button.tsx", + "source:src/platform/ui/ColorPicker.tsx", + "source:src/platform/ui/Combobox.tsx", + "source:src/platform/ui/ConfirmDialog.tsx", + "source:src/platform/ui/DropTargetOverlay.tsx", + "source:src/platform/ui/DropZone.tsx", + "source:src/platform/ui/EmptyState.tsx", + "source:src/platform/ui/Field.tsx", + "source:src/platform/ui/Group.tsx", + "source:src/platform/ui/InputShell.tsx", + "source:src/platform/ui/InvokeMark.tsx", + "source:src/platform/ui/JsonPreview.tsx", + "source:src/platform/ui/Menu.tsx", + "source:src/platform/ui/MiddleTruncate.tsx", + "source:src/platform/ui/PageShell.tsx", + "source:src/platform/ui/Panel.tsx", + "source:src/platform/ui/PanelHeader.tsx", + "source:src/platform/ui/Popover.tsx", + "source:src/platform/ui/RenameDialog.tsx", + "source:src/platform/ui/ResizableTextarea.tsx", + "source:src/platform/ui/RetryBoundary.tsx", + "source:src/platform/ui/Row.tsx", + "source:src/platform/ui/Scrollable.tsx", + "source:src/platform/ui/SegmentTabs.tsx", + "source:src/platform/ui/SegmentedControl.tsx", + "source:src/platform/ui/Select.tsx", + "source:src/platform/ui/Slider.tsx", + "source:src/platform/ui/StatusWidgetChip.tsx", + "source:src/platform/ui/Tabs.tsx", + "source:src/platform/ui/ToggleDot.tsx", + "source:src/platform/ui/Toolbar.tsx", + "source:src/platform/ui/Tooltip.tsx", + "source:src/platform/ui/VendoredIcon.tsx", + "source:src/platform/ui/color.ts", + "source:src/platform/ui/colorPickerStore.ts", + "source:src/platform/ui/colorPickerSync.ts", + "source:src/platform/ui/hints/FeatureHint.tsx", + "source:src/platform/ui/hints/hintRegistry.ts", + "source:src/platform/ui/hints/hintsContext.tsx", + "source:src/platform/ui/hints/index.ts", + "source:src/platform/ui/index.ts", + "source:src/platform/ui/list/ListDivider.tsx", + "source:src/platform/ui/list/ListItem.tsx", + "source:src/platform/ui/list/ListSectionHeader.tsx", + "source:src/platform/ui/list/ListStack.tsx", + "source:src/platform/ui/list/listLayout.ts", + "source:src/platform/ui/settings/contracts.ts", + "source:src/platform/ui/theme/applyTheme.ts", + "source:src/platform/ui/theme/recipes.ts", + "source:src/platform/ui/theme/system.ts", + "source:src/platform/ui/theme/themes.ts", + "source:src/platform/ui/toaster.tsx", + "source:src/platform/ui/useScrollAreaPhantomHeal.ts", + "source:src/platform/ui/widgetOverlayRegistry.ts", + "source:src/platform/ui/widgetOverlays.ts", + "source:src/workbench/canvasSchemaVersion.ts", + "source:src/workbench/components/WorkbenchSplashScreen.tsx", + "source:src/workbench/deferredResource.ts", + "source:src/workbench/diagnostics/loggingPreferences.ts", + "source:src/workbench/hotkeys/catalog.ts", + "source:src/workbench/hotkeys/keys.ts", + "source:src/workbench/hotkeys/modalLayer.ts", + "source:src/workbench/hotkeys/resolve.ts", + "source:src/workbench/launchpad/HelpMenu.tsx", + "source:src/workbench/launchpad/KnownBrowserIssuesAlert.tsx", + "source:src/workbench/launchpad/Launchpad.tsx", + "source:src/workbench/launchpad/LaunchpadNav.tsx", + "source:src/workbench/launchpad/LaunchpadTopBar.tsx", + "source:src/workbench/launchpad/OpenProjectsControl.tsx", + "source:src/workbench/launchpad/formatRelativeTime.ts", + "source:src/workbench/launchpad/home/IntentTiles.tsx", + "source:src/workbench/launchpad/home/LivePanel.tsx", + "source:src/workbench/launchpad/home/RecentProjectsRow.tsx", + "source:src/workbench/launchpad/home/ResumeCard.tsx", + "source:src/workbench/launchpad/home/livePanels.ts", + "source:src/workbench/launchpad/intents.ts", + "source:src/workbench/launchpad/pages/HomePage.tsx", + "source:src/workbench/launchpad/pages/ProjectsPage.tsx", + "source:src/workbench/launchpad/projects/NewProjectButton.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenu.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenuHost.tsx", + "source:src/workbench/launchpad/projects/ProjectCard.tsx", + "source:src/workbench/launchpad/projects/ProjectCompatibilityBadge.tsx", + "source:src/workbench/launchpad/projects/ProjectCover.tsx", + "source:src/workbench/launchpad/projects/ProjectRow.tsx", + "source:src/workbench/launchpad/projects/ProjectsBrowser.tsx", + "source:src/workbench/launchpad/projects/ProjectsToolbar.tsx", + "source:src/workbench/launchpad/projects/projectLibraryView.ts", + "source:src/workbench/launchpad/projects/projectPins.ts", + "source:src/workbench/launchpad/projects/projectsMetrics.ts", + "source:src/workbench/launchpad/projects/useProjectCardActions.ts", + "source:src/workbench/launchpad/startEntries.ts", + "source:src/workbench/mediaReferences.ts", + "source:src/workbench/palette/LaunchpadCommandPalette.tsx", + "source:src/workbench/palette/PaletteButton.tsx", + "source:src/workbench/palette/paletteStore.ts", + "source:src/workbench/palette/settingsEntryDeps.ts", + "source:src/workbench/projects/api.ts", + "source:src/workbench/projects/components/ProjectFileOptionsProvider.tsx", + "source:src/workbench/projects/covers.ts", + "source:src/workbench/projects/ids.ts", + "source:src/workbench/projects/invk/format.ts", + "source:src/workbench/projects/library.ts", + "source:src/workbench/projects/openProjects.ts", + "source:src/workbench/projects/projectAssets.ts", + "source:src/workbench/projects/projectFile.ts", + "source:src/workbench/projects/projectFileErrors.ts", + "source:src/workbench/projects/projectFileToasts.ts", + "source:src/workbench/projects/projectFlush.ts", + "source:src/workbench/projects/session.ts", + "source:src/workbench/projects/syncStore.ts", + "source:src/workbench/projects/useProjectFileActions.ts", + "source:src/workbench/settings/SettingsDialogHost.tsx", + "source:src/workbench/settings/dialogResource.ts", + "source:src/workbench/settings/launchpad.tsx", + "source:src/workbench/settings/settingsDialogStore.ts", + "source:src/workbench/settings/settingsSearchShortcut.ts", + "source:src/workbench/settings/store.ts" + ], + "editor-minimal-static": [ + "package:@ark-ui/react", + "package:@chakra-ui/react", + "package:@dnd-kit/accessibility", + "package:@dnd-kit/core", + "package:@dnd-kit/modifiers", + "package:@dnd-kit/sortable", + "package:@dnd-kit/utilities", + "package:@emotion/cache", + "package:@emotion/hash", + "package:@emotion/is-prop-valid", + "package:@emotion/memoize", + "package:@emotion/react", + "package:@emotion/serialize", + "package:@emotion/sheet", + "package:@emotion/unitless", + "package:@emotion/use-insertion-effect-with-fallbacks", + "package:@emotion/utils", + "package:@floating-ui/core", + "package:@floating-ui/dom", + "package:@floating-ui/utils", + "package:@fontsource/inter", + "package:@internationalized/number", + "package:@pandacss/is-valid-prop", + "package:@socket.io/component-emitter", + "package:@tanstack/history", + "package:@tanstack/query-core", + "package:@tanstack/react-query", + "package:@tanstack/react-router", + "package:@tanstack/react-store", + "package:@tanstack/react-virtual", + "package:@tanstack/router-core", + "package:@tanstack/store", + "package:@tanstack/virtual-core", + "package:@zag-js/accordion", + "package:@zag-js/anatomy", + "package:@zag-js/aria-hidden", + "package:@zag-js/auto-resize", + "package:@zag-js/avatar", + "package:@zag-js/carousel", + "package:@zag-js/checkbox", + "package:@zag-js/clipboard", + "package:@zag-js/collapsible", + "package:@zag-js/collection", + "package:@zag-js/color-picker", + "package:@zag-js/color-utils", + "package:@zag-js/combobox", + "package:@zag-js/core", + "package:@zag-js/date-input", + "package:@zag-js/date-picker", + "package:@zag-js/dialog", + "package:@zag-js/dismissable", + "package:@zag-js/dom-query", + "package:@zag-js/editable", + "package:@zag-js/file-upload", + "package:@zag-js/floating-panel", + "package:@zag-js/focus-trap", + "package:@zag-js/focus-visible", + "package:@zag-js/hover-card", + "package:@zag-js/interact-outside", + "package:@zag-js/listbox", + "package:@zag-js/live-region", + "package:@zag-js/marquee", + "package:@zag-js/menu", + "package:@zag-js/number-input", + "package:@zag-js/pagination", + "package:@zag-js/pin-input", + "package:@zag-js/popover", + "package:@zag-js/popper", + "package:@zag-js/presence", + "package:@zag-js/progress", + "package:@zag-js/qr-code", + "package:@zag-js/radio-group", + "package:@zag-js/rating-group", + "package:@zag-js/react", + "package:@zag-js/rect-utils", + "package:@zag-js/remove-scroll", + "package:@zag-js/scroll-area", + "package:@zag-js/select", + "package:@zag-js/slider", + "package:@zag-js/splitter", + "package:@zag-js/switch", + "package:@zag-js/tabs", + "package:@zag-js/tags-input", + "package:@zag-js/toast", + "package:@zag-js/tooltip", + "package:@zag-js/tree-view", + "package:@zag-js/types", + "package:@zag-js/utils", + "package:classcat", + "package:engine.io-client", + "package:engine.io-parser", + "package:fuzzysort", + "package:hoist-non-react-statics", + "package:i18next", + "package:i18next-http-backend", + "package:idb", + "package:lucide-react", + "package:perfect-freehand", + "package:react", + "package:react-dom", + "package:react-hook-tanstack-virtual", + "package:react-i18next", + "package:react-is", + "package:scheduler", + "package:socket.io-client", + "package:socket.io-parser", + "package:stylis", + "package:tinykeys", + "package:use-sync-external-store", + "package:zod", + "package:zustand", + "source:index.html", + "source:src/app/App.tsx", + "source:src/app/AppProviders.tsx", + "source:src/app/BootWidgetHintController.tsx", + "source:src/app/FeatureHintsProvider.tsx", + "source:src/app/GalleryImageActionsBridge.tsx", + "source:src/app/GalleryRealtimeRuntime.tsx", + "source:src/app/GalleryUiAdapter.tsx", + "source:src/app/GenerateWidgetSyncRuntime.tsx", + "source:src/app/GenerationUiAdapter.tsx", + "source:src/app/I18nController.tsx", + "source:src/app/ModelsUiAdapter.tsx", + "source:src/app/QueueRuntimeAdapter.tsx", + "source:src/app/QueueUiAdapter.tsx", + "source:src/app/RecallParametersRuntime.tsx", + "source:src/app/SocketHubRuntime.tsx", + "source:src/app/ThemeController.tsx", + "source:src/app/UpscaleUiAdapter.tsx", + "source:src/app/VideoUiAdapter.tsx", + "source:src/app/WorkbenchApp.tsx", + "source:src/app/WorkflowUiAdapter.tsx", + "source:src/app/accountLifecycle.ts", + "source:src/app/loggingRuntime.ts", + "source:src/app/main.tsx", + "source:src/app/router.tsx", + "source:src/app/workbenchPorts.tsx", + "source:src/assets/SplashImage.webp", + "source:src/features/fonts/data/keys.ts", + "source:src/features/fonts/launchpad.tsx", + "source:src/features/fonts/react.tsx", + "source:src/features/fonts/runtime.ts", + "source:src/features/gallery/core/boardLabels.ts", + "source:src/features/gallery/core/image.ts", + "source:src/features/gallery/core/imagePaths.ts", + "source:src/features/gallery/core/items.ts", + "source:src/features/gallery/core/recentImages.ts", + "source:src/features/gallery/core/selection.ts", + "source:src/features/gallery/core/semanticImageQuery.ts", + "source:src/features/gallery/core/settings.ts", + "source:src/features/gallery/core/videoPaths.ts", + "source:src/features/gallery/data/backend.ts", + "source:src/features/gallery/data/imageUrls.ts", + "source:src/features/gallery/data/queries.ts", + "source:src/features/gallery/data/queryCache.ts", + "source:src/features/gallery/data/realtimeRuntime.ts", + "source:src/features/gallery/data/videoUrls.ts", + "source:src/features/gallery/index.ts", + "source:src/features/gallery/mediaSlot.ts", + "source:src/features/gallery/picker.ts", + "source:src/features/gallery/queries.ts", + "source:src/features/gallery/react.ts", + "source:src/features/gallery/settingsContribution.ts", + "source:src/features/gallery/ui/FindInGalleryButton.tsx", + "source:src/features/gallery/ui/GalleryBoardCover.tsx", + "source:src/features/gallery/ui/GalleryBoardDragMonitor.tsx", + "source:src/features/gallery/ui/GalleryBoardFilters.tsx", + "source:src/features/gallery/ui/GalleryBoardMenu.tsx", + "source:src/features/gallery/ui/GalleryBoardRow.tsx", + "source:src/features/gallery/ui/GalleryBoardRowShell.tsx", + "source:src/features/gallery/ui/GalleryBoardSection.tsx", + "source:src/features/gallery/ui/GalleryBoardsPanel.tsx", + "source:src/features/gallery/ui/GalleryDragCursor.tsx", + "source:src/features/gallery/ui/GalleryImageGrid.tsx", + "source:src/features/gallery/ui/GalleryItemSearch.tsx", + "source:src/features/gallery/ui/GalleryItemSortMenu.tsx", + "source:src/features/gallery/ui/GalleryLayout.tsx", + "source:src/features/gallery/ui/GalleryProgressSection.tsx", + "source:src/features/gallery/ui/GallerySearchField.tsx", + "source:src/features/gallery/ui/GallerySearchHelp.tsx", + "source:src/features/gallery/ui/GallerySelectionBar.tsx", + "source:src/features/gallery/ui/GalleryStackedLayout.tsx", + "source:src/features/gallery/ui/GalleryStarredFilterToggle.tsx", + "source:src/features/gallery/ui/GalleryThumbnail.tsx", + "source:src/features/gallery/ui/GalleryTileFrame.tsx", + "source:src/features/gallery/ui/GalleryUiContext.tsx", + "source:src/features/gallery/ui/GalleryUploadButton.tsx", + "source:src/features/gallery/ui/GalleryViewTabs.tsx", + "source:src/features/gallery/ui/GalleryWideLayout.tsx", + "source:src/features/gallery/ui/GalleryWidgetChrome.tsx", + "source:src/features/gallery/ui/GalleryWidgetContext.tsx", + "source:src/features/gallery/ui/GalleryWidgetFooter.tsx", + "source:src/features/gallery/ui/GalleryWidgetView.tsx", + "source:src/features/gallery/ui/galleryBoardGroups.ts", + "source:src/features/gallery/ui/galleryBoardLabels.ts", + "source:src/features/gallery/ui/galleryDensity.ts", + "source:src/features/gallery/ui/galleryDnd.ts", + "source:src/features/gallery/ui/galleryGridLayout.ts", + "source:src/features/gallery/ui/galleryMenuIds.ts", + "source:src/features/gallery/ui/galleryStateView.ts", + "source:src/features/gallery/ui/picker/GalleryMediaSlot.tsx", + "source:src/features/gallery/ui/picker/GalleryPickerPopover.tsx", + "source:src/features/gallery/ui/useGalleryActions.ts", + "source:src/features/gallery/ui/useGalleryData.ts", + "source:src/features/gallery/ui/useGalleryGridHotkeys.ts", + "source:src/features/gallery/ui/useGalleryGridSelection.ts", + "source:src/features/gallery/ui/useGalleryStarredStrip.ts", + "source:src/features/gallery/ui/useGalleryUploadAction.ts", + "source:src/features/gallery/ui/useGalleryUploadInput.ts", + "source:src/features/gallery/utility.ts", + "source:src/features/gallery/widget.ts", + "source:src/features/generation/core/architectureCapabilities.ts", + "source:src/features/generation/core/baseGenerationPolicies.ts", + "source:src/features/generation/core/batch.ts", + "source:src/features/generation/core/canvas/addControlLayers.ts", + "source:src/features/generation/core/canvas/addRegionalGuidance.ts", + "source:src/features/generation/core/canvas/controlValidation.ts", + "source:src/features/generation/core/catalogSearch.ts", + "source:src/features/generation/core/componentCompatibility.ts", + "source:src/features/generation/core/conditioningRebalance.ts", + "source:src/features/generation/core/dynamicPrompts.ts", + "source:src/features/generation/core/graph.ts", + "source:src/features/generation/core/graphBuilder.ts", + "source:src/features/generation/core/krea2Conditioning.ts", + "source:src/features/generation/core/pid.ts", + "source:src/features/generation/core/pidGraph.ts", + "source:src/features/generation/core/projectPromptDraft.ts", + "source:src/features/generation/core/prompt/ast.ts", + "source:src/features/generation/core/prompt/attention.ts", + "source:src/features/generation/core/prompt/dynamicPromptSyntax.ts", + "source:src/features/generation/core/prompt/highlight.ts", + "source:src/features/generation/core/promptHistory.ts", + "source:src/features/generation/core/promptTemplateOwnership.ts", + "source:src/features/generation/core/promptTemplates.ts", + "source:src/features/generation/core/referenceImage.ts", + "source:src/features/generation/core/resolveGenerateWidgetValues.ts", + "source:src/features/generation/core/settings.ts", + "source:src/features/generation/core/supportedBases.ts", + "source:src/features/generation/core/systemPrompts.ts", + "source:src/features/generation/core/wildcardCatalog.ts", + "source:src/features/generation/core/wildcardTransfer.ts", + "source:src/features/generation/data/architectureCapabilitiesApi.ts", + "source:src/features/generation/data/architectureCapabilitiesStore.ts", + "source:src/features/generation/data/dynamicPromptsQueries.ts", + "source:src/features/generation/data/llmTaskProgress.ts", + "source:src/features/generation/data/promptTemplates.ts", + "source:src/features/generation/data/promptUtilities.ts", + "source:src/features/generation/data/referenceImageUrls.ts", + "source:src/features/generation/data/systemPrompts.ts", + "source:src/features/generation/data/wildcards.ts", + "source:src/features/generation/graph.ts", + "source:src/features/generation/prompts.ts", + "source:src/features/generation/queries.ts", + "source:src/features/generation/react.ts", + "source:src/features/generation/runtime.ts", + "source:src/features/generation/settings.ts", + "source:src/features/generation/ui/GenerateAdvancedFields.tsx", + "source:src/features/generation/ui/GenerateCanvasSections.tsx", + "source:src/features/generation/ui/GenerateComponentsSection.tsx", + "source:src/features/generation/ui/GenerateConceptsSection.tsx", + "source:src/features/generation/ui/GenerateConditioningRebalanceField.tsx", + "source:src/features/generation/ui/GenerateDimensionFields.tsx", + "source:src/features/generation/ui/GenerateGuidanceSection.tsx", + "source:src/features/generation/ui/GenerateHeaderActions.tsx", + "source:src/features/generation/ui/GenerateModelCard.tsx", + "source:src/features/generation/ui/GeneratePresetsPopover.tsx", + "source:src/features/generation/ui/GenerateRenderSection.tsx", + "source:src/features/generation/ui/GenerateSettingsForm.tsx", + "source:src/features/generation/ui/GenerateWidgetView.tsx", + "source:src/features/generation/ui/GenerationUiContext.tsx", + "source:src/features/generation/ui/LLMTaskProgressRuntime.tsx", + "source:src/features/generation/ui/generateDebounce.ts", + "source:src/features/generation/ui/generateDraftRegistry.ts", + "source:src/features/generation/ui/generateFormViewModel.ts", + "source:src/features/generation/ui/modelSelectionNotice.ts", + "source:src/features/generation/ui/promptFields/DynamicPromptsButton.tsx", + "source:src/features/generation/ui/promptFields/DynamicPromptsPanel.tsx", + "source:src/features/generation/ui/promptFields/GeneratePromptFields.tsx", + "source:src/features/generation/ui/promptFields/LLMTaskProgressDisplay.tsx", + "source:src/features/generation/ui/promptFields/NegativePromptField.tsx", + "source:src/features/generation/ui/promptFields/PositivePromptActions.tsx", + "source:src/features/generation/ui/promptFields/PositivePromptField.tsx", + "source:src/features/generation/ui/promptFields/PromptHighlight.tsx", + "source:src/features/generation/ui/promptFields/PromptLineNumbers.tsx", + "source:src/features/generation/ui/promptFields/PromptPanelHeader.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplateEditor.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplateImage.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplatesButton.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplatesPanel.tsx", + "source:src/features/generation/ui/promptFields/PromptTextarea.tsx", + "source:src/features/generation/ui/promptFields/PromptTriggerAutocomplete.tsx", + "source:src/features/generation/ui/promptFields/SystemPromptsField.tsx", + "source:src/features/generation/ui/promptFields/WildcardImportDialog.tsx", + "source:src/features/generation/ui/promptFields/WildcardTransferActions.tsx", + "source:src/features/generation/ui/promptFields/WildcardsPanel.tsx", + "source:src/features/generation/ui/promptFields/index.ts", + "source:src/features/generation/ui/promptFields/promptAttentionHotkeys.ts", + "source:src/features/generation/ui/promptFields/promptCaret.ts", + "source:src/features/generation/ui/promptFields/promptFocus.ts", + "source:src/features/generation/ui/promptFields/promptHistoryNavigation.ts", + "source:src/features/generation/ui/promptFields/promptTriggerOptions.ts", + "source:src/features/generation/ui/promptFields/useDismissOnViewportChange.ts", + "source:src/features/generation/ui/promptFields/usePromptImageDrop.ts", + "source:src/features/generation/ui/promptFields/usePromptTriggerAutocomplete.tsx", + "source:src/features/generation/ui/promptFields/usePromptTriggerPicker.ts", + "source:src/features/generation/ui/promptFields/useSystemPrompts.ts", + "source:src/features/generation/ui/promptTemplateDraftStore.ts", + "source:src/features/generation/ui/reference-images/GenerateReferenceImagesSection.tsx", + "source:src/features/generation/ui/reference-images/ReferenceImageCard.tsx", + "source:src/features/generation/ui/reference-images/ReferenceImageControls.tsx", + "source:src/features/generation/ui/reference-images/ReferenceImageCropDialog.tsx", + "source:src/features/generation/ui/reference-images/referenceImageConfig.ts", + "source:src/features/generation/ui/shared/AspectRatioPreview.tsx", + "source:src/features/generation/ui/shared/AspectRatioSelect.tsx", + "source:src/features/generation/ui/shared/ConditioningRebalanceBars.tsx", + "source:src/features/generation/ui/shared/ConditioningRebalanceSparkline.tsx", + "source:src/features/generation/ui/shared/GenerateCollapsibleSection.tsx", + "source:src/features/generation/ui/shared/GenerateFieldContextMenu.tsx", + "source:src/features/generation/ui/shared/GenerateToggleSwitch.tsx", + "source:src/features/generation/ui/shared/SeedField.tsx", + "source:src/features/generation/ui/shared/modelDefaultSettings.ts", + "source:src/features/generation/ui/useDebouncedDraftValue.ts", + "source:src/features/generation/ui/useDynamicPrompts.ts", + "source:src/features/generation/ui/usePromptTemplates.ts", + "source:src/features/generation/ui/useWildcards.ts", + "source:src/features/generation/ui/wildcardFiles.ts", + "source:src/features/generation/widget.ts", + "source:src/features/identity/capabilities.ts", + "source:src/features/identity/core/schemas.ts", + "source:src/features/identity/core/sessionPolicy.ts", + "source:src/features/identity/core/tokenStorage.ts", + "source:src/features/identity/data/api.ts", + "source:src/features/identity/index.ts", + "source:src/features/identity/session.ts", + "source:src/features/identity/transportAdapter.ts", + "source:src/features/identity/ui/AccountMenu.tsx", + "source:src/features/identity/ui/AccountMenuSection.tsx", + "source:src/features/identity/ui/AuthScreen.tsx", + "source:src/features/identity/ui/AuthUnavailableScreen.tsx", + "source:src/features/identity/ui/LoginScreen.tsx", + "source:src/features/identity/ui/PasswordInput.tsx", + "source:src/features/identity/ui/ProfileDialog.tsx", + "source:src/features/identity/ui/SetupScreen.tsx", + "source:src/features/identity/ui/UserFormDialog.tsx", + "source:src/features/identity/ui/UserManagement.tsx", + "source:src/features/identity/ui/UsersPage.tsx", + "source:src/features/identity/ui/useIdentityNotify.ts", + "source:src/features/intermediates/data/focus.ts", + "source:src/features/intermediates/holdLease.ts", + "source:src/features/intermediates/index.ts", + "source:src/features/intermediates/settingsContribution.ts", + "source:src/features/models/core/baseIdentity.ts", + "source:src/features/models/core/library.ts", + "source:src/features/models/core/taxonomy.ts", + "source:src/features/models/data/api.ts", + "source:src/features/models/data/apiKeys.ts", + "source:src/features/models/data/installsStore.ts", + "source:src/features/models/data/modelLoadStore.ts", + "source:src/features/models/data/modelsStore.ts", + "source:src/features/models/data/startersStore.ts", + "source:src/features/models/data/transport.ts", + "source:src/features/models/index.ts", + "source:src/features/models/react.ts", + "source:src/features/models/ui/ModelInstallRuntime.tsx", + "source:src/features/models/ui/ModelsPage.tsx", + "source:src/features/models/ui/ModelsUiContext.tsx", + "source:src/features/models/ui/add-models/starterModelInstallSources.ts", + "source:src/features/models/ui/add-models/useInstallActions.ts", + "source:src/features/models/ui/components/ModelSelect.tsx", + "source:src/features/models/ui/uiStore.ts", + "source:src/features/models/ui/useInstallOutcomeToasts.ts", + "source:src/features/models/ui/useModelsNotify.ts", + "source:src/features/nodes/core/catalog.ts", + "source:src/features/nodes/data/api.ts", + "source:src/features/nodes/data/nodeExecutionStore.ts", + "source:src/features/nodes/data/nodesStore.ts", + "source:src/features/nodes/data/transport.ts", + "source:src/features/nodes/index.ts", + "source:src/features/nodes/ui/NodesPage.tsx", + "source:src/features/queue/contracts.ts", + "source:src/features/queue/core/activeSessions.ts", + "source:src/features/queue/core/deviceLabels.ts", + "source:src/features/queue/core/generationMeta.ts", + "source:src/features/queue/core/graphInputMedia.ts", + "source:src/features/queue/core/historySnapshot.ts", + "source:src/features/queue/core/historySummary.ts", + "source:src/features/queue/core/progressImage.ts", + "source:src/features/queue/core/progressRail.ts", + "source:src/features/queue/core/promptBatch.ts", + "source:src/features/queue/core/submissionRules.ts", + "source:src/features/queue/core/types.ts", + "source:src/features/queue/data/activeProgressTargetStore.ts", + "source:src/features/queue/data/events.ts", + "source:src/features/queue/data/generationDevicesStore.ts", + "source:src/features/queue/data/httpRealtimeQueueBackend.ts", + "source:src/features/queue/data/itemProgressStore.ts", + "source:src/features/queue/data/mappers.ts", + "source:src/features/queue/data/progressImageStore.ts", + "source:src/features/queue/data/progressStore.ts", + "source:src/features/queue/data/queries.ts", + "source:src/features/queue/data/realtimeRuntime.ts", + "source:src/features/queue/data/serverApi.ts", + "source:src/features/queue/data/submissionApi.ts", + "source:src/features/queue/devices.ts", + "source:src/features/queue/index.ts", + "source:src/features/queue/menu.ts", + "source:src/features/queue/publicApi.ts", + "source:src/features/queue/react.ts", + "source:src/features/queue/reveal.ts", + "source:src/features/queue/runtime.ts", + "source:src/features/queue/runtime/coordinator.ts", + "source:src/features/queue/runtime/receiptAcknowledgements.ts", + "source:src/features/queue/runtime/utilityGraph.ts", + "source:src/features/queue/ui/QueueDataRuntime.tsx", + "source:src/features/queue/ui/QueueUiContext.tsx", + "source:src/features/queue/ui/currentBatchItems.ts", + "source:src/features/queue/ui/modelCacheStore.ts", + "source:src/features/queue/ui/queueConfirmationStore.ts", + "source:src/features/queue/ui/queueDataStore.ts", + "source:src/features/queue/ui/queueMenuActions.tsx", + "source:src/features/queue/ui/queueOwnership.ts", + "source:src/features/queue/ui/queueProgressCleanup.ts", + "source:src/features/queue/ui/queueScope.ts", + "source:src/features/queue/ui/queueUiStore.ts", + "source:src/features/queue/ui/queueViewModel.ts", + "source:src/features/queue/ui/useDeviceLabel.ts", + "source:src/features/queue/utility.ts", + "source:src/features/queue/widget.ts", + "source:src/features/upscale/core/graph.ts", + "source:src/features/upscale/core/settings.ts", + "source:src/features/upscale/index.ts", + "source:src/features/upscale/ui/UpscaleUiContext.tsx", + "source:src/features/video/core/dimensions.ts", + "source:src/features/video/core/graph.ts", + "source:src/features/video/core/settings.ts", + "source:src/features/video/core/videoPolicies.ts", + "source:src/features/video/core/widgetValues.ts", + "source:src/features/video/index.ts", + "source:src/features/video/ui/VideoUiContext.tsx", + "source:src/features/workflow/core/batch.ts", + "source:src/features/workflow/core/buildGraph.ts", + "source:src/features/workflow/core/callSavedWorkflow.ts", + "source:src/features/workflow/core/connectors.ts", + "source:src/features/workflow/core/document.ts", + "source:src/features/workflow/core/fields.ts", + "source:src/features/workflow/core/forLoops.ts", + "source:src/features/workflow/core/graphIndex.ts", + "source:src/features/workflow/core/libraryTags.ts", + "source:src/features/workflow/core/modelRequirements.ts", + "source:src/features/workflow/core/types.ts", + "source:src/features/workflow/core/validation.ts", + "source:src/features/workflow/core/workflowJson.ts", + "source:src/features/workflow/data/api.ts", + "source:src/features/workflow/data/libraryBrowseStore.ts", + "source:src/features/workflow/data/libraryCache.ts", + "source:src/features/workflow/data/publication.ts", + "source:src/features/workflow/data/savedWorkflowQueries.ts", + "source:src/features/workflow/data/templates.ts", + "source:src/features/workflow/queries.ts", + "source:src/features/workflow/react.ts", + "source:src/features/workflow/ui/MenuActionItem.tsx", + "source:src/features/workflow/ui/PendingLibraryWorkflowLoader.tsx", + "source:src/features/workflow/ui/WorkflowUiContext.tsx", + "source:src/features/workflow/ui/WorkflowWidgetChrome.tsx", + "source:src/features/workflow/ui/editor/AddNodeDialog.tsx", + "source:src/features/workflow/ui/editor/CallSavedWorkflowSyncRuntime.tsx", + "source:src/features/workflow/ui/editor/flowInstanceStore.ts", + "source:src/features/workflow/ui/editor/workflowViewportStore.ts", + "source:src/features/workflow/ui/library/LibraryCopyChoiceDialog.tsx", + "source:src/features/workflow/ui/library/ProjectWorkflowsView.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryCard.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryDetailPanel.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryDialog.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryGrid.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryTagChips.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryThumbnail.tsx", + "source:src/features/workflow/ui/library/WorkflowPublicationHost.tsx", + "source:src/features/workflow/ui/library/WorkflowRequirementsList.tsx", + "source:src/features/workflow/ui/library/libraryPreviewSource.ts", + "source:src/features/workflow/ui/library/projectWorkflowEntries.ts", + "source:src/features/workflow/ui/library/relativeTime.ts", + "source:src/features/workflow/ui/library/useOpenLibraryWorkflow.ts", + "source:src/features/workflow/ui/library/useWorkflowPublication.ts", + "source:src/features/workflow/ui/pendingLibraryWorkflowLoadRuntime.ts", + "source:src/features/workflow/ui/useProjectGraphCommands.ts", + "source:src/features/workflow/ui/workflowTransfer.ts", + "source:src/features/workflow/ui/workflowUiStore.ts", + "source:src/features/workflow/utility.ts", + "source:src/features/workflow/widget.ts", + "source:src/platform/browser/downloadBlob.ts", + "source:src/platform/browser/knownBrowserIssues.ts", + "source:src/platform/browser/randomUuid.ts", + "source:src/platform/browser/sha256.ts", + "source:src/platform/browser/webLocks.ts", + "source:src/platform/core/concurrency.ts", + "source:src/platform/core/json.ts", + "source:src/platform/core/outputImages.ts", + "source:src/platform/core/seed.ts", + "source:src/platform/i18n/client.ts", + "source:src/platform/i18n/languages.ts", + "source:src/platform/logging/contracts.ts", + "source:src/platform/logging/logger.ts", + "source:src/platform/logging/normalize.ts", + "source:src/platform/performance/semanticReady.ts", + "source:src/platform/pwa/registerServiceWorker.ts", + "source:src/platform/query/client.ts", + "source:src/platform/react/composeProviders.tsx", + "source:src/platform/react/draftRegistry.ts", + "source:src/platform/react/focusIfUnclaimed.ts", + "source:src/platform/react/useMountEffect.ts", + "source:src/platform/react/usePreservedScrollOffset.ts", + "source:src/platform/react/useZodForm.ts", + "source:src/platform/search/dateTokens.ts", + "source:src/platform/state/accountLifecycle.ts", + "source:src/platform/state/compareAndSwapRollback.ts", + "source:src/platform/state/externalStore.ts", + "source:src/platform/state/externalStoreCore.ts", + "source:src/platform/state/projectedExternalStore.ts", + "source:src/platform/state/selectorCore.ts", + "source:src/platform/state/selectors.ts", + "source:src/platform/state/singleFlight.ts", + "source:src/platform/time/serverTimestamp.ts", + "source:src/platform/transport/connectionStore.ts", + "source:src/platform/transport/deploymentBase.ts", + "source:src/platform/transport/http.ts", + "source:src/platform/transport/socketHub.ts", + "source:src/platform/ui/Button.tsx", + "source:src/platform/ui/ColorPicker.tsx", + "source:src/platform/ui/Combobox.tsx", + "source:src/platform/ui/ConfirmDialog.tsx", + "source:src/platform/ui/DropTargetOverlay.tsx", + "source:src/platform/ui/DropZone.tsx", + "source:src/platform/ui/EmptyState.tsx", + "source:src/platform/ui/Field.tsx", + "source:src/platform/ui/Group.tsx", + "source:src/platform/ui/InputShell.tsx", + "source:src/platform/ui/InvokeMark.tsx", + "source:src/platform/ui/JsonPreview.tsx", + "source:src/platform/ui/Menu.tsx", + "source:src/platform/ui/MiddleTruncate.tsx", + "source:src/platform/ui/ModelDefaultButton.tsx", + "source:src/platform/ui/PageShell.tsx", + "source:src/platform/ui/Panel.tsx", + "source:src/platform/ui/PanelHeader.tsx", + "source:src/platform/ui/Picker.tsx", + "source:src/platform/ui/Popover.tsx", + "source:src/platform/ui/RenameDialog.tsx", + "source:src/platform/ui/ResizableTextarea.tsx", + "source:src/platform/ui/ResizeHandle.tsx", + "source:src/platform/ui/RetryBoundary.tsx", + "source:src/platform/ui/Row.tsx", + "source:src/platform/ui/Scrollable.tsx", + "source:src/platform/ui/ScrubberField.tsx", + "source:src/platform/ui/SeedInput.tsx", + "source:src/platform/ui/SegmentTabs.tsx", + "source:src/platform/ui/SegmentedControl.tsx", + "source:src/platform/ui/Select.tsx", + "source:src/platform/ui/Slider.tsx", + "source:src/platform/ui/SliderNumberField.tsx", + "source:src/platform/ui/StatusWidgetChip.tsx", + "source:src/platform/ui/Tabs.tsx", + "source:src/platform/ui/ToggleDot.tsx", + "source:src/platform/ui/Toolbar.tsx", + "source:src/platform/ui/Tooltip.tsx", + "source:src/platform/ui/VendoredIcon.tsx", + "source:src/platform/ui/color.ts", + "source:src/platform/ui/colorPickerStore.ts", + "source:src/platform/ui/colorPickerSync.ts", + "source:src/platform/ui/hints/FeatureHint.tsx", + "source:src/platform/ui/hints/hintRegistry.ts", + "source:src/platform/ui/hints/hintsContext.tsx", + "source:src/platform/ui/hints/index.ts", + "source:src/platform/ui/index.ts", + "source:src/platform/ui/list/ListDivider.tsx", + "source:src/platform/ui/list/ListItem.tsx", + "source:src/platform/ui/list/ListSectionHeader.tsx", + "source:src/platform/ui/list/ListStack.tsx", + "source:src/platform/ui/list/listLayout.ts", + "source:src/platform/ui/settings/contracts.ts", + "source:src/platform/ui/streaming-image/StreamingImageFrame.tsx", + "source:src/platform/ui/streaming-image/streamingImageSource.ts", + "source:src/platform/ui/streaming-image/useStreamingImageSource.ts", + "source:src/platform/ui/theme/applyTheme.ts", + "source:src/platform/ui/theme/recipes.ts", + "source:src/platform/ui/theme/system.ts", + "source:src/platform/ui/theme/themes.ts", + "source:src/platform/ui/toaster.tsx", + "source:src/platform/ui/useScrollAreaPhantomHeal.ts", + "source:src/platform/ui/widgetOverlayRegistry.ts", + "source:src/platform/ui/widgetOverlays.ts", + "source:src/workbench/WorkbenchContext.tsx", + "source:src/workbench/WorkbenchRuntime.tsx", + "source:src/workbench/WorkbenchSessionController.tsx", + "source:src/workbench/WorkbenchWidgetRegistryContext.tsx", + "source:src/workbench/activeInvocationSubmission.ts", + "source:src/workbench/autoRoutePolicy.ts", + "source:src/workbench/bootWidgetPreload.ts", + "source:src/workbench/canvas-engine/api.ts", + "source:src/workbench/canvas-engine/capabilities.ts", + "source:src/workbench/canvas-engine/color.ts", + "source:src/workbench/canvas-engine/contracts.ts", + "source:src/workbench/canvas-engine/controllers/booleanMergeController.ts", + "source:src/workbench/canvas-engine/controllers/controlPixelController.ts", + "source:src/workbench/canvas-engine/controllers/copyLayerController.ts", + "source:src/workbench/canvas-engine/controllers/cropLayerController.ts", + "source:src/workbench/canvas-engine/controllers/editSteps.ts", + "source:src/workbench/canvas-engine/controllers/editingController.ts", + "source:src/workbench/canvas-engine/controllers/extractMaskedAreaController.ts", + "source:src/workbench/canvas-engine/controllers/filterResultController.ts", + "source:src/workbench/canvas-engine/controllers/floatingSelectionController.ts", + "source:src/workbench/canvas-engine/controllers/generatedResultController.ts", + "source:src/workbench/canvas-engine/controllers/historyController.ts", + "source:src/workbench/canvas-engine/controllers/interactionController.ts", + "source:src/workbench/canvas-engine/controllers/layerController.ts", + "source:src/workbench/canvas-engine/controllers/layerMutationController.ts", + "source:src/workbench/canvas-engine/controllers/maskLayerController.ts", + "source:src/workbench/canvas-engine/controllers/maskResultController.ts", + "source:src/workbench/canvas-engine/controllers/mergeLayerController.ts", + "source:src/workbench/canvas-engine/controllers/mutationContext.ts", + "source:src/workbench/canvas-engine/controllers/newRasterLayerController.ts", + "source:src/workbench/canvas-engine/controllers/persistenceController.ts", + "source:src/workbench/canvas-engine/controllers/previewStateController.ts", + "source:src/workbench/canvas-engine/controllers/psdExportController.ts", + "source:src/workbench/canvas-engine/controllers/rasterController.ts", + "source:src/workbench/canvas-engine/controllers/rasterExportController.ts", + "source:src/workbench/canvas-engine/controllers/rasterMemoryBudgetController.ts", + "source:src/workbench/canvas-engine/controllers/rasterizeLayerController.ts", + "source:src/workbench/canvas-engine/controllers/renderController.ts", + "source:src/workbench/canvas-engine/controllers/selectionImageController.ts", + "source:src/workbench/canvas-engine/controllers/selectionPixelController.ts", + "source:src/workbench/canvas-engine/controllers/stagedResultController.ts", + "source:src/workbench/canvas-engine/controllers/structuralLayerController.ts", + "source:src/workbench/canvas-engine/controllers/textEditingController.ts", + "source:src/workbench/canvas-engine/controllers/thumbnailController.ts", + "source:src/workbench/canvas-engine/controllers/transformEditingController.ts", + "source:src/workbench/canvas-engine/diagnostics.ts", + "source:src/workbench/canvas-engine/document-model/documentModel.ts", + "source:src/workbench/canvas-engine/document-model/postconditions.ts", + "source:src/workbench/canvas-engine/document-model/screenComposition.ts", + "source:src/workbench/canvas-engine/document-model/semanticLeaf.ts", + "source:src/workbench/canvas-engine/document-model/semanticNode.ts", + "source:src/workbench/canvas-engine/document/bitmapStore.ts", + "source:src/workbench/canvas-engine/document/documentIndex.ts", + "source:src/workbench/canvas-engine/document/documentMirror.ts", + "source:src/workbench/canvas-engine/document/documentTree.ts", + "source:src/workbench/canvas-engine/document/insertionAnchors.ts", + "source:src/workbench/canvas-engine/document/layerChangeDecision.ts", + "source:src/workbench/canvas-engine/document/layerEligibility.ts", + "source:src/workbench/canvas-engine/document/layerFactories.ts", + "source:src/workbench/canvas-engine/document/layerStacks.ts", + "source:src/workbench/canvas-engine/document/mergeDown.ts", + "source:src/workbench/canvas-engine/document/mergeVisible.ts", + "source:src/workbench/canvas-engine/document/selectionRepair.ts", + "source:src/workbench/canvas-engine/document/sources.ts", + "source:src/workbench/canvas-engine/editGate.ts", + "source:src/workbench/canvas-engine/editing/controlPixelEdit.ts", + "source:src/workbench/canvas-engine/engine.ts", + "source:src/workbench/canvas-engine/engineStores.ts", + "source:src/workbench/canvas-engine/export/psdExport.ts", + "source:src/workbench/canvas-engine/exportRasterComposite.ts", + "source:src/workbench/canvas-engine/filterError.ts", + "source:src/workbench/canvas-engine/fontReferences.ts", + "source:src/workbench/canvas-engine/freehand.ts", + "source:src/workbench/canvas-engine/history/documentPatch.ts", + "source:src/workbench/canvas-engine/history/history.ts", + "source:src/workbench/canvas-engine/history/imagePatch.ts", + "source:src/workbench/canvas-engine/history/layerSnapshot.ts", + "source:src/workbench/canvas-engine/input/pointerPipeline.ts", + "source:src/workbench/canvas-engine/input/wheel.ts", + "source:src/workbench/canvas-engine/layerExportGuards.ts", + "source:src/workbench/canvas-engine/layerRasterizer.ts", + "source:src/workbench/canvas-engine/math/mat2d.ts", + "source:src/workbench/canvas-engine/math/rect.ts", + "source:src/workbench/canvas-engine/math/snapping.ts", + "source:src/workbench/canvas-engine/mutationContracts.ts", + "source:src/workbench/canvas-engine/pressureBands.ts", + "source:src/workbench/canvas-engine/previewPublisher.ts", + "source:src/workbench/canvas-engine/rasterSnapshotCapture.ts", + "source:src/workbench/canvas-engine/render/adjustedSurfaceCache.ts", + "source:src/workbench/canvas-engine/render/adjustments.ts", + "source:src/workbench/canvas-engine/render/alphaBounds.ts", + "source:src/workbench/canvas-engine/render/colorSample.ts", + "source:src/workbench/canvas-engine/render/compositeFrame.ts", + "source:src/workbench/canvas-engine/render/compositor.ts", + "source:src/workbench/canvas-engine/render/controlTransparency.ts", + "source:src/workbench/canvas-engine/render/decodedBitmapPool.ts", + "source:src/workbench/canvas-engine/render/derivedSurfaceCache.ts", + "source:src/workbench/canvas-engine/render/floatingSelectionFrame.ts", + "source:src/workbench/canvas-engine/render/fontLoader.ts", + "source:src/workbench/canvas-engine/render/frameDemand.ts", + "source:src/workbench/canvas-engine/render/groupCompositeScopes.ts", + "source:src/workbench/canvas-engine/render/groupSurfaceCache.ts", + "source:src/workbench/canvas-engine/render/layerCache.ts", + "source:src/workbench/canvas-engine/render/layerDisplayEffect.ts", + "source:src/workbench/canvas-engine/render/maskFill.ts", + "source:src/workbench/canvas-engine/render/overlayFrame.ts", + "source:src/workbench/canvas-engine/render/overlayRenderer.ts", + "source:src/workbench/canvas-engine/render/paintCacheTrim.ts", + "source:src/workbench/canvas-engine/render/raster.ts", + "source:src/workbench/canvas-engine/render/rasterComposite.ts", + "source:src/workbench/canvas-engine/render/rasterizers/gradientRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/imageRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/index.ts", + "source:src/workbench/canvas-engine/render/rasterizers/paintRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/shapeRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/textRasterizer.ts", + "source:src/workbench/canvas-engine/render/scheduler.ts", + "source:src/workbench/canvas-engine/render/thumbnail.ts", + "source:src/workbench/canvas-engine/samCoordinates.ts", + "source:src/workbench/canvas-engine/selectObjectBridge.ts", + "source:src/workbench/canvas-engine/selection/floatingSelection.ts", + "source:src/workbench/canvas-engine/selection/marchingAnts.ts", + "source:src/workbench/canvas-engine/selection/maskOutline.ts", + "source:src/workbench/canvas-engine/selection/selectionHistory.ts", + "source:src/workbench/canvas-engine/selection/selectionOpMode.ts", + "source:src/workbench/canvas-engine/selection/selectionOps.ts", + "source:src/workbench/canvas-engine/selection/selectionPaths.ts", + "source:src/workbench/canvas-engine/selection/selectionState.ts", + "source:src/workbench/canvas-engine/stagedPreviewBlobCache.ts", + "source:src/workbench/canvas-engine/strokeCommit.ts", + "source:src/workbench/canvas-engine/tools/bboxHitTest.ts", + "source:src/workbench/canvas-engine/tools/bboxTool.ts", + "source:src/workbench/canvas-engine/tools/brushTool.ts", + "source:src/workbench/canvas-engine/tools/colorPickerTool.ts", + "source:src/workbench/canvas-engine/tools/eraserTool.ts", + "source:src/workbench/canvas-engine/tools/gradientTool.ts", + "source:src/workbench/canvas-engine/tools/gridSnap.ts", + "source:src/workbench/canvas-engine/tools/lassoTool.ts", + "source:src/workbench/canvas-engine/tools/marqueeTool.ts", + "source:src/workbench/canvas-engine/tools/moveHitTest.ts", + "source:src/workbench/canvas-engine/tools/moveTool.ts", + "source:src/workbench/canvas-engine/tools/paintConstants.ts", + "source:src/workbench/canvas-engine/tools/paintTool.ts", + "source:src/workbench/canvas-engine/tools/polylineSession.ts", + "source:src/workbench/canvas-engine/tools/samHitTest.ts", + "source:src/workbench/canvas-engine/tools/samTool.ts", + "source:src/workbench/canvas-engine/tools/shapeTool.ts", + "source:src/workbench/canvas-engine/tools/strokeSession.ts", + "source:src/workbench/canvas-engine/tools/textTool.ts", + "source:src/workbench/canvas-engine/tools/transformTool.ts", + "source:src/workbench/canvas-engine/tools/viewTool.ts", + "source:src/workbench/canvas-engine/transform/transformMath.ts", + "source:src/workbench/canvas-engine/types.ts", + "source:src/workbench/canvas-engine/viewport.ts", + "source:src/workbench/canvas-operations/FilterOperationCoordinator.ts", + "source:src/workbench/canvas-operations/SelectObjectCoordinator.ts", + "source:src/workbench/canvas-operations/api.ts", + "source:src/workbench/canvas-operations/applicationPort.ts", + "source:src/workbench/canvas-operations/backend/canvasImages.ts", + "source:src/workbench/canvas-operations/canvasImportNotice.ts", + "source:src/workbench/canvas-operations/compositeForGeneration.ts", + "source:src/workbench/canvas-operations/controlRecommendations.ts", + "source:src/workbench/canvas-operations/createCanvasEngine.ts", + "source:src/workbench/canvas-operations/createCanvasFromImages.ts", + "source:src/workbench/canvas-operations/createFromBbox.ts", + "source:src/workbench/canvas-operations/engineRegistry.ts", + "source:src/workbench/canvas-operations/exportCanvasComposite.ts", + "source:src/workbench/canvas-operations/filterGraphs.ts", + "source:src/workbench/canvas-operations/filterOperationSession.ts", + "source:src/workbench/canvas-operations/filterPreferences.ts", + "source:src/workbench/canvas-operations/generationComposite.ts", + "source:src/workbench/canvas-operations/generationCompositePlan.ts", + "source:src/workbench/canvas-operations/importGalleryImages.ts", + "source:src/workbench/canvas-operations/layerFilterRunner.ts", + "source:src/workbench/canvas-operations/layerImageResult.ts", + "source:src/workbench/canvas-operations/operationAccess.ts", + "source:src/workbench/canvas-operations/operationController.ts", + "source:src/workbench/canvas-operations/operationStores.ts", + "source:src/workbench/canvas-operations/samGraph.ts", + "source:src/workbench/canvas-operations/saveCanvasToGallery.ts", + "source:src/workbench/canvas-operations/selectObjectSession.ts", + "source:src/workbench/canvasDimsSync.ts", + "source:src/workbench/canvasInvocationPreparation.ts", + "source:src/workbench/canvasLayerContent.ts", + "source:src/workbench/canvasMigration.ts", + "source:src/workbench/canvasProjectMutations.ts", + "source:src/workbench/canvasSchemaVersion.ts", + "source:src/workbench/canvasStagingView.ts", + "source:src/workbench/components/QueueProgressIndicator.tsx", + "source:src/workbench/components/WorkbenchSplashScreen.tsx", + "source:src/workbench/components/WorkbenchUnavailableScreen.tsx", + "source:src/workbench/controlAdapters.ts", + "source:src/workbench/controlLayerChecks.ts", + "source:src/workbench/deferredResource.ts", + "source:src/workbench/diagnostics/loggingPreferences.ts", + "source:src/workbench/extensions/contributionSource.ts", + "source:src/workbench/extensions/extensionRegistry.ts", + "source:src/workbench/floatingWindows.ts", + "source:src/workbench/focusRegions.tsx", + "source:src/workbench/graphPreviewInvocation.ts", + "source:src/workbench/graphSurfaces.ts", + "source:src/workbench/graphWidgets.ts", + "source:src/workbench/hotkeys/WorkbenchHotkeyRuntime.tsx", + "source:src/workbench/hotkeys/catalog.ts", + "source:src/workbench/hotkeys/extensionHotkeys.ts", + "source:src/workbench/hotkeys/firstPartyCommands.ts", + "source:src/workbench/hotkeys/index.ts", + "source:src/workbench/hotkeys/keyGlyphs.tsx", + "source:src/workbench/hotkeys/keys.ts", + "source:src/workbench/hotkeys/modalLayer.ts", + "source:src/workbench/hotkeys/resolve.ts", + "source:src/workbench/hotkeys/targetWidget.ts", + "source:src/workbench/iconResolver.tsx", + "source:src/workbench/image-actions/ImageContextMenu.tsx", + "source:src/workbench/image-actions/RecallActionButtons.tsx", + "source:src/workbench/image-actions/appendReferenceImage.ts", + "source:src/workbench/image-actions/canvasImportError.ts", + "source:src/workbench/image-actions/executeImageRecall.ts", + "source:src/workbench/image-actions/executeVideoRecall.ts", + "source:src/workbench/image-actions/galleryOptimisticRollback.ts", + "source:src/workbench/image-actions/imageRecall.ts", + "source:src/workbench/image-actions/index.ts", + "source:src/workbench/image-actions/recallParametersBridge.ts", + "source:src/workbench/image-actions/useDeletionConfirmation.tsx", + "source:src/workbench/image-actions/useFindGalleryItem.ts", + "source:src/workbench/image-actions/useImageActions.ts", + "source:src/workbench/image-actions/videoRecall.ts", + "source:src/workbench/image-map/api.ts", + "source:src/workbench/image-map/imageMapStore.ts", + "source:src/workbench/image-map/indexProgress.ts", + "source:src/workbench/invocation.ts", + "source:src/workbench/invocationSubmit.ts", + "source:src/workbench/launchpad/HelpMenu.tsx", + "source:src/workbench/launchpad/KnownBrowserIssuesAlert.tsx", + "source:src/workbench/launchpad/Launchpad.tsx", + "source:src/workbench/launchpad/LaunchpadNav.tsx", + "source:src/workbench/launchpad/LaunchpadTopBar.tsx", + "source:src/workbench/launchpad/OpenProjectsControl.tsx", + "source:src/workbench/launchpad/formatRelativeTime.ts", + "source:src/workbench/launchpad/home/IntentTiles.tsx", + "source:src/workbench/launchpad/home/LivePanel.tsx", + "source:src/workbench/launchpad/home/RecentProjectsRow.tsx", + "source:src/workbench/launchpad/home/ResumeCard.tsx", + "source:src/workbench/launchpad/intents.ts", + "source:src/workbench/launchpad/pages/HomePage.tsx", + "source:src/workbench/launchpad/pages/ProjectsPage.tsx", + "source:src/workbench/launchpad/projects/NewProjectButton.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenu.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenuHost.tsx", + "source:src/workbench/launchpad/projects/ProjectCard.tsx", + "source:src/workbench/launchpad/projects/ProjectCompatibilityBadge.tsx", + "source:src/workbench/launchpad/projects/ProjectCover.tsx", + "source:src/workbench/launchpad/projects/ProjectRow.tsx", + "source:src/workbench/launchpad/projects/ProjectsBrowser.tsx", + "source:src/workbench/launchpad/projects/ProjectsToolbar.tsx", + "source:src/workbench/launchpad/projects/projectLibraryView.ts", + "source:src/workbench/launchpad/projects/projectPins.ts", + "source:src/workbench/launchpad/projects/projectsMetrics.ts", + "source:src/workbench/launchpad/projects/useProjectCardActions.ts", + "source:src/workbench/launchpad/startEntries.ts", + "source:src/workbench/layerPanelState.ts", + "source:src/workbench/layoutContracts.ts", + "source:src/workbench/layoutPresetActivation.ts", + "source:src/workbench/layoutPresetCollection.ts", + "source:src/workbench/layoutPresetRouting.ts", + "source:src/workbench/layoutPresetSnapshots.ts", + "source:src/workbench/layoutPresets.ts", + "source:src/workbench/layoutWidgetSet.ts", + "source:src/workbench/mediaReferences.ts", + "source:src/workbench/palette/LaunchpadCommandPalette.tsx", + "source:src/workbench/palette/PaletteButton.tsx", + "source:src/workbench/palette/WorkbenchCommandPalette.tsx", + "source:src/workbench/palette/paletteStore.ts", + "source:src/workbench/palette/settingsEntryDeps.ts", + "source:src/workbench/panZoom.ts", + "source:src/workbench/performanceMarks.ts", + "source:src/workbench/persistenceRuntime.ts", + "source:src/workbench/projectCanvasGate.ts", + "source:src/workbench/projectEvents.ts", + "source:src/workbench/projectWorkflows.ts", + "source:src/workbench/projects/accountOwnedWorkbenchDatabase.ts", + "source:src/workbench/projects/api.ts", + "source:src/workbench/projects/components/OpenProjectDialog.tsx", + "source:src/workbench/projects/components/ProjectFileOptionsProvider.tsx", + "source:src/workbench/projects/components/index.ts", + "source:src/workbench/projects/covers.ts", + "source:src/workbench/projects/draftStore.ts", + "source:src/workbench/projects/durableSyncedPersistence.ts", + "source:src/workbench/projects/editorSession.ts", + "source:src/workbench/projects/ids.ts", + "source:src/workbench/projects/indexedDbDraftStore.ts", + "source:src/workbench/projects/invk/format.ts", + "source:src/workbench/projects/library.ts", + "source:src/workbench/projects/openProjectBroker.ts", + "source:src/workbench/projects/openProjects.ts", + "source:src/workbench/projects/projectAssets.ts", + "source:src/workbench/projects/projectDocument.ts", + "source:src/workbench/projects/projectFile.ts", + "source:src/workbench/projects/projectFileErrors.ts", + "source:src/workbench/projects/projectFileToasts.ts", + "source:src/workbench/projects/projectFlush.ts", + "source:src/workbench/projects/projectHydration.ts", + "source:src/workbench/projects/projectLifecycleLocks.ts", + "source:src/workbench/projects/projectLoadRefusal.ts", + "source:src/workbench/projects/projectTabsA11y.ts", + "source:src/workbench/projects/session.ts", + "source:src/workbench/projects/syncStore.ts", + "source:src/workbench/projects/syncedPersistence.ts", + "source:src/workbench/projects/useProjectActions.ts", + "source:src/workbench/projects/useProjectFileActions.ts", + "source:src/workbench/projects/workbenchDatabase.ts", + "source:src/workbench/queue-integration/QueueProgressRail.tsx", + "source:src/workbench/queue-integration/QueueRecoveryNotice.tsx", + "source:src/workbench/queue-integration/activeQueueRuns.ts", + "source:src/workbench/queue-integration/queueRecallCache.ts", + "source:src/workbench/queue-integration/queueRecovery.ts", + "source:src/workbench/queue-integration/queueRunJournal.ts", + "source:src/workbench/queue-integration/queueRunLocks.ts", + "source:src/workbench/queue-integration/queueRunPersistence.ts", + "source:src/workbench/queue-integration/queueRunRestoration.ts", + "source:src/workbench/queue-integration/useActiveQueueProgress.ts", + "source:src/workbench/queue-integration/useQueueRecoveryProjectIds.ts", + "source:src/workbench/settings/SettingsDialogHost.tsx", + "source:src/workbench/settings/WidgetSettingsButton.tsx", + "source:src/workbench/settings/applicationContributions.ts", + "source:src/workbench/settings/catalog.ts", + "source:src/workbench/settings/clearWorkspaceData.ts", + "source:src/workbench/settings/dialogResource.ts", + "source:src/workbench/settings/launchpad.tsx", + "source:src/workbench/settings/settingsDialogStore.ts", + "source:src/workbench/settings/settingsSearchShortcut.ts", + "source:src/workbench/settings/store.ts", + "source:src/workbench/shell/BottomPanel.tsx", + "source:src/workbench/shell/CenterArea.tsx", + "source:src/workbench/shell/DocumentTitleProgress.tsx", + "source:src/workbench/shell/Panels.tsx", + "source:src/workbench/shell/PasteMediaRuntime.tsx", + "source:src/workbench/shell/ProjectConflictBanner.tsx", + "source:src/workbench/shell/QueueRecoveryBanner.tsx", + "source:src/workbench/shell/StatusBar.tsx", + "source:src/workbench/shell/WorkbenchShell.tsx", + "source:src/workbench/shell/documentTitle.ts", + "source:src/workbench/shell/holdToDragSensor.ts", + "source:src/workbench/shell/index.ts", + "source:src/workbench/shell/notifications/WorkbenchNotificationToaster.tsx", + "source:src/workbench/shell/notifications/index.ts", + "source:src/workbench/shell/notifications/toastPolicy.ts", + "source:src/workbench/shell/topbar/AppMenu.tsx", + "source:src/workbench/shell/topbar/InvocationCluster.tsx", + "source:src/workbench/shell/topbar/InvokeButton.tsx", + "source:src/workbench/shell/topbar/IterationsField.tsx", + "source:src/workbench/shell/topbar/LayoutPresetAdminDialogs.tsx", + "source:src/workbench/shell/topbar/LayoutPresetDialog.tsx", + "source:src/workbench/shell/topbar/LayoutPresetManagerDialog.tsx", + "source:src/workbench/shell/topbar/LayoutPresetStrip.tsx", + "source:src/workbench/shell/topbar/ProjectSwitcher.tsx", + "source:src/workbench/shell/topbar/QueueCluster.tsx", + "source:src/workbench/shell/topbar/RoutingControl.tsx", + "source:src/workbench/shell/topbar/RoutingDestinationSegments.tsx", + "source:src/workbench/shell/topbar/TopBar.tsx", + "source:src/workbench/shell/topbar/TopbarProgressRail.tsx", + "source:src/workbench/shell/topbar/TopbarShortcutKeys.tsx", + "source:src/workbench/shell/topbar/index.ts", + "source:src/workbench/shell/topbar/invokeButtonModel.ts", + "source:src/workbench/shell/topbar/layoutPresetDialogModel.ts", + "source:src/workbench/shell/topbar/layoutPresetIcons.ts", + "source:src/workbench/shell/topbar/layoutPresetManagerStore.ts", + "source:src/workbench/shell/topbar/projectSwitcherStore.ts", + "source:src/workbench/shell/topbar/topbarBreakpoints.ts", + "source:src/workbench/shell/topbar/useInvocationState.ts", + "source:src/workbench/shell/topbar/useLayoutDrift.ts", + "source:src/workbench/shell/topbar/useTopbarShortcut.ts", + "source:src/workbench/shell/useMountedInstanceIds.ts", + "source:src/workbench/useNotify.ts", + "source:src/workbench/useOpenWorkbenchWidget.ts", + "source:src/workbench/widget-frame/FloatingWidgetLayer.tsx", + "source:src/workbench/widget-frame/WidgetActionsMenu.tsx", + "source:src/workbench/widget-frame/WidgetBar.tsx", + "source:src/workbench/widget-frame/WidgetEnableMenu.tsx", + "source:src/workbench/widget-frame/WidgetFailureBoundary.tsx", + "source:src/workbench/widget-frame/WidgetFrames.tsx", + "source:src/workbench/widget-frame/WidgetHosts.tsx", + "source:src/workbench/widget-frame/WidgetIdentityIcon.tsx", + "source:src/workbench/widget-frame/WidgetInstanceContextMenu.tsx", + "source:src/workbench/widget-frame/WidgetLoadingFallback.tsx", + "source:src/workbench/widget-frame/WidgetRegionDropOverlay.tsx", + "source:src/workbench/widget-frame/WidgetRenderer.tsx", + "source:src/workbench/widget-frame/WidgetSourceLockBadge.tsx", + "source:src/workbench/widget-frame/WidgetStrip.tsx", + "source:src/workbench/widget-frame/createWidgetRuntime.ts", + "source:src/workbench/widget-frame/index.ts", + "source:src/workbench/widget-frame/useWidgetIntentPreload.ts", + "source:src/workbench/widget-frame/useWidgetSortable.ts", + "source:src/workbench/widget-frame/widgetRenderInstance.ts", + "source:src/workbench/widgetDnd.ts", + "source:src/workbench/widgetImplementationResource.ts", + "source:src/workbench/widgetLabels.ts", + "source:src/workbench/widgetPlacementCommands.ts", + "source:src/workbench/widgetPlacementMeta.ts", + "source:src/workbench/widgetRegionViewModel.ts", + "source:src/workbench/widgetRegistry.ts", + "source:src/workbench/widgetState.ts", + "source:src/workbench/widgets/autosave-status/AutosaveStatusWidgetView.tsx", + "source:src/workbench/widgets/autosave-status/implementation.ts", + "source:src/workbench/widgets/autosave-status/manifest.ts", + "source:src/workbench/widgets/canvas/bboxGrid.ts", + "source:src/workbench/widgets/canvas/canvasSettings.ts", + "source:src/workbench/widgets/canvas/invoke/canvasCompositing.ts", + "source:src/workbench/widgets/canvas/invoke/canvasStrength.ts", + "source:src/workbench/widgets/canvas/manifest.ts", + "source:src/workbench/widgets/canvas/settingsContribution.ts", + "source:src/workbench/widgets/diagnostics/manifest.ts", + "source:src/workbench/widgets/gallery/manifest.ts", + "source:src/workbench/widgets/generate/manifest.ts", + "source:src/workbench/widgets/image-map/ImageMapDataRuntime.tsx", + "source:src/workbench/widgets/image-map/manifest.ts", + "source:src/workbench/widgets/image-map/settingsContribution.ts", + "source:src/workbench/widgets/layers/controlModelOptions.ts", + "source:src/workbench/widgets/layers/layerOps.ts", + "source:src/workbench/widgets/layers/manifest.ts", + "source:src/workbench/widgets/layers/panes/editorPaneLayout.ts", + "source:src/workbench/widgets/layers/selectedModel.ts", + "source:src/workbench/widgets/manifests.ts", + "source:src/workbench/widgets/notifications/NotificationsHeaderActions.tsx", + "source:src/workbench/widgets/notifications/NotificationsWidgetView.tsx", + "source:src/workbench/widgets/notifications/implementation.ts", + "source:src/workbench/widgets/notifications/manifest.ts", + "source:src/workbench/widgets/preview/PreviewActionStrip.tsx", + "source:src/workbench/widgets/preview/PreviewCompare.tsx", + "source:src/workbench/widgets/preview/PreviewCompareDropZone.tsx", + "source:src/workbench/widgets/preview/PreviewDetailsPopover.tsx", + "source:src/workbench/widgets/preview/PreviewFilmstrip.tsx", + "source:src/workbench/widgets/preview/PreviewFrame.tsx", + "source:src/workbench/widgets/preview/PreviewHeaderActions.tsx", + "source:src/workbench/widgets/preview/PreviewMetadataPanel.tsx", + "source:src/workbench/widgets/preview/PreviewStage.tsx", + "source:src/workbench/widgets/preview/PreviewSwipeNeighbor.tsx", + "source:src/workbench/widgets/preview/PreviewWidgetChrome.tsx", + "source:src/workbench/widgets/preview/PreviewWidgetView.tsx", + "source:src/workbench/widgets/preview/PreviewZoomMenu.tsx", + "source:src/workbench/widgets/preview/implementation.ts", + "source:src/workbench/widgets/preview/livePreviewFollow.tsx", + "source:src/workbench/widgets/preview/loupeGestures.ts", + "source:src/workbench/widgets/preview/manifest.ts", + "source:src/workbench/widgets/preview/previewCompareDnd.ts", + "source:src/workbench/widgets/preview/previewDensity.ts", + "source:src/workbench/widgets/preview/previewHeaderStore.ts", + "source:src/workbench/widgets/preview/previewMetadata.ts", + "source:src/workbench/widgets/preview/previewSettings.ts", + "source:src/workbench/widgets/preview/previewSwipe.ts", + "source:src/workbench/widgets/preview/settingsContribution.ts", + "source:src/workbench/widgets/preview/spanPlaybackRequest.ts", + "source:src/workbench/widgets/preview/useCompareLoupe.ts", + "source:src/workbench/widgets/preview/usePreviewLoupe.ts", + "source:src/workbench/widgets/preview/usePreviewNavigation.ts", + "source:src/workbench/widgets/preview/usePreviewSwipe.ts", + "source:src/workbench/widgets/project/manifest.ts", + "source:src/workbench/widgets/queue-status/QueueStatusWidgetView.tsx", + "source:src/workbench/widgets/queue-status/implementation.ts", + "source:src/workbench/widgets/queue-status/manifest.ts", + "source:src/workbench/widgets/queue-status/queueStatusModel.ts", + "source:src/workbench/widgets/queue/manifest.ts", + "source:src/workbench/widgets/server-status/ServerStatusWidgetView.tsx", + "source:src/workbench/widgets/server-status/implementation.ts", + "source:src/workbench/widgets/server-status/manifest.ts", + "source:src/workbench/widgets/upscale/manifest.ts", + "source:src/workbench/widgets/video/manifest.ts", + "source:src/workbench/widgets/workflow/manifest.ts", + "source:src/workbench/workbenchState.ts", + "source:src/workbench/workbenchStore.ts" + ], + "editor-canvas-static": [ + "package:@ark-ui/react", + "package:@chakra-ui/react", + "package:@dnd-kit/accessibility", + "package:@dnd-kit/core", + "package:@dnd-kit/modifiers", + "package:@dnd-kit/sortable", + "package:@dnd-kit/utilities", + "package:@emotion/cache", + "package:@emotion/hash", + "package:@emotion/is-prop-valid", + "package:@emotion/memoize", + "package:@emotion/react", + "package:@emotion/serialize", + "package:@emotion/sheet", + "package:@emotion/unitless", + "package:@emotion/use-insertion-effect-with-fallbacks", + "package:@emotion/utils", + "package:@floating-ui/core", + "package:@floating-ui/dom", + "package:@floating-ui/utils", + "package:@fontsource/inter", + "package:@internationalized/number", + "package:@pandacss/is-valid-prop", + "package:@socket.io/component-emitter", + "package:@tanstack/history", + "package:@tanstack/query-core", + "package:@tanstack/react-query", + "package:@tanstack/react-router", + "package:@tanstack/react-store", + "package:@tanstack/react-virtual", + "package:@tanstack/router-core", + "package:@tanstack/store", + "package:@tanstack/virtual-core", + "package:@zag-js/accordion", + "package:@zag-js/anatomy", + "package:@zag-js/aria-hidden", + "package:@zag-js/auto-resize", + "package:@zag-js/avatar", + "package:@zag-js/carousel", + "package:@zag-js/checkbox", + "package:@zag-js/clipboard", + "package:@zag-js/collapsible", + "package:@zag-js/collection", + "package:@zag-js/color-picker", + "package:@zag-js/color-utils", + "package:@zag-js/combobox", + "package:@zag-js/core", + "package:@zag-js/date-input", + "package:@zag-js/date-picker", + "package:@zag-js/dialog", + "package:@zag-js/dismissable", + "package:@zag-js/dom-query", + "package:@zag-js/editable", + "package:@zag-js/file-upload", + "package:@zag-js/floating-panel", + "package:@zag-js/focus-trap", + "package:@zag-js/focus-visible", + "package:@zag-js/hover-card", + "package:@zag-js/interact-outside", + "package:@zag-js/listbox", + "package:@zag-js/live-region", + "package:@zag-js/marquee", + "package:@zag-js/menu", + "package:@zag-js/number-input", + "package:@zag-js/pagination", + "package:@zag-js/pin-input", + "package:@zag-js/popover", + "package:@zag-js/popper", + "package:@zag-js/presence", + "package:@zag-js/progress", + "package:@zag-js/qr-code", + "package:@zag-js/radio-group", + "package:@zag-js/rating-group", + "package:@zag-js/react", + "package:@zag-js/rect-utils", + "package:@zag-js/remove-scroll", + "package:@zag-js/scroll-area", + "package:@zag-js/select", + "package:@zag-js/slider", + "package:@zag-js/splitter", + "package:@zag-js/switch", + "package:@zag-js/tabs", + "package:@zag-js/tags-input", + "package:@zag-js/toast", + "package:@zag-js/tooltip", + "package:@zag-js/tree-view", + "package:@zag-js/types", + "package:@zag-js/utils", + "package:classcat", + "package:engine.io-client", + "package:engine.io-parser", + "package:fuzzysort", + "package:hoist-non-react-statics", + "package:i18next", + "package:i18next-http-backend", + "package:idb", + "package:lucide-react", + "package:perfect-freehand", + "package:react", + "package:react-dom", + "package:react-hook-tanstack-virtual", + "package:react-i18next", + "package:react-is", + "package:scheduler", + "package:socket.io-client", + "package:socket.io-parser", + "package:stylis", + "package:tinykeys", + "package:use-sync-external-store", + "package:zod", + "package:zustand", + "source:index.html", + "source:src/app/App.tsx", + "source:src/app/AppProviders.tsx", + "source:src/app/BootWidgetHintController.tsx", + "source:src/app/FeatureHintsProvider.tsx", + "source:src/app/GalleryImageActionsBridge.tsx", + "source:src/app/GalleryRealtimeRuntime.tsx", + "source:src/app/GalleryUiAdapter.tsx", + "source:src/app/GenerateWidgetSyncRuntime.tsx", + "source:src/app/GenerationUiAdapter.tsx", + "source:src/app/I18nController.tsx", + "source:src/app/ModelsUiAdapter.tsx", + "source:src/app/QueueRuntimeAdapter.tsx", + "source:src/app/QueueUiAdapter.tsx", + "source:src/app/RecallParametersRuntime.tsx", + "source:src/app/SocketHubRuntime.tsx", + "source:src/app/ThemeController.tsx", + "source:src/app/UpscaleUiAdapter.tsx", + "source:src/app/VideoUiAdapter.tsx", + "source:src/app/WorkbenchApp.tsx", + "source:src/app/WorkflowUiAdapter.tsx", + "source:src/app/accountLifecycle.ts", + "source:src/app/loggingRuntime.ts", + "source:src/app/main.tsx", + "source:src/app/router.tsx", + "source:src/app/workbenchPorts.tsx", + "source:src/assets/SplashImage.webp", + "source:src/features/fonts/data/keys.ts", + "source:src/features/fonts/launchpad.tsx", + "source:src/features/fonts/react.tsx", + "source:src/features/fonts/runtime.ts", + "source:src/features/gallery/core/boardLabels.ts", + "source:src/features/gallery/core/image.ts", + "source:src/features/gallery/core/imagePaths.ts", + "source:src/features/gallery/core/items.ts", + "source:src/features/gallery/core/recentImages.ts", + "source:src/features/gallery/core/selection.ts", + "source:src/features/gallery/core/semanticImageQuery.ts", + "source:src/features/gallery/core/settings.ts", + "source:src/features/gallery/core/videoPaths.ts", + "source:src/features/gallery/data/backend.ts", + "source:src/features/gallery/data/imageUrls.ts", + "source:src/features/gallery/data/queries.ts", + "source:src/features/gallery/data/queryCache.ts", + "source:src/features/gallery/data/realtimeRuntime.ts", + "source:src/features/gallery/data/videoUrls.ts", + "source:src/features/gallery/index.ts", + "source:src/features/gallery/mediaSlot.ts", + "source:src/features/gallery/picker.ts", + "source:src/features/gallery/queries.ts", + "source:src/features/gallery/react.ts", + "source:src/features/gallery/settingsContribution.ts", + "source:src/features/gallery/ui/FindInGalleryButton.tsx", + "source:src/features/gallery/ui/GalleryBoardCover.tsx", + "source:src/features/gallery/ui/GalleryBoardDragMonitor.tsx", + "source:src/features/gallery/ui/GalleryBoardFilters.tsx", + "source:src/features/gallery/ui/GalleryBoardMenu.tsx", + "source:src/features/gallery/ui/GalleryBoardRow.tsx", + "source:src/features/gallery/ui/GalleryBoardRowShell.tsx", + "source:src/features/gallery/ui/GalleryBoardSection.tsx", + "source:src/features/gallery/ui/GalleryBoardsPanel.tsx", + "source:src/features/gallery/ui/GalleryDragCursor.tsx", + "source:src/features/gallery/ui/GalleryImageGrid.tsx", + "source:src/features/gallery/ui/GalleryItemSearch.tsx", + "source:src/features/gallery/ui/GalleryItemSortMenu.tsx", + "source:src/features/gallery/ui/GalleryLayout.tsx", + "source:src/features/gallery/ui/GalleryProgressSection.tsx", + "source:src/features/gallery/ui/GallerySearchField.tsx", + "source:src/features/gallery/ui/GallerySearchHelp.tsx", + "source:src/features/gallery/ui/GallerySelectionBar.tsx", + "source:src/features/gallery/ui/GalleryStackedLayout.tsx", + "source:src/features/gallery/ui/GalleryStarredFilterToggle.tsx", + "source:src/features/gallery/ui/GalleryThumbnail.tsx", + "source:src/features/gallery/ui/GalleryTileFrame.tsx", + "source:src/features/gallery/ui/GalleryUiContext.tsx", + "source:src/features/gallery/ui/GalleryUploadButton.tsx", + "source:src/features/gallery/ui/GalleryViewTabs.tsx", + "source:src/features/gallery/ui/GalleryWideLayout.tsx", + "source:src/features/gallery/ui/GalleryWidgetChrome.tsx", + "source:src/features/gallery/ui/GalleryWidgetContext.tsx", + "source:src/features/gallery/ui/GalleryWidgetFooter.tsx", + "source:src/features/gallery/ui/GalleryWidgetView.tsx", + "source:src/features/gallery/ui/galleryBoardGroups.ts", + "source:src/features/gallery/ui/galleryBoardLabels.ts", + "source:src/features/gallery/ui/galleryDensity.ts", + "source:src/features/gallery/ui/galleryDnd.ts", + "source:src/features/gallery/ui/galleryGridLayout.ts", + "source:src/features/gallery/ui/galleryMenuIds.ts", + "source:src/features/gallery/ui/galleryStateView.ts", + "source:src/features/gallery/ui/picker/GalleryMediaSlot.tsx", + "source:src/features/gallery/ui/picker/GalleryPickerPopover.tsx", + "source:src/features/gallery/ui/useGalleryActions.ts", + "source:src/features/gallery/ui/useGalleryData.ts", + "source:src/features/gallery/ui/useGalleryGridHotkeys.ts", + "source:src/features/gallery/ui/useGalleryGridSelection.ts", + "source:src/features/gallery/ui/useGalleryStarredStrip.ts", + "source:src/features/gallery/ui/useGalleryUploadAction.ts", + "source:src/features/gallery/ui/useGalleryUploadInput.ts", + "source:src/features/gallery/utility.ts", + "source:src/features/gallery/widget.ts", + "source:src/features/generation/canvasGraph.ts", + "source:src/features/generation/canvasProcessingSize.ts", + "source:src/features/generation/components.ts", + "source:src/features/generation/core/architectureCapabilities.ts", + "source:src/features/generation/core/baseGenerationPolicies.ts", + "source:src/features/generation/core/batch.ts", + "source:src/features/generation/core/canvas/addControlLayers.ts", + "source:src/features/generation/core/canvas/addRegionalGuidance.ts", + "source:src/features/generation/core/canvas/canvasMode.ts", + "source:src/features/generation/core/canvas/canvasProcessingSize.ts", + "source:src/features/generation/core/canvas/compileCanvasGraph.ts", + "source:src/features/generation/core/canvas/controlValidation.ts", + "source:src/features/generation/core/catalogSearch.ts", + "source:src/features/generation/core/componentCompatibility.ts", + "source:src/features/generation/core/conditioningRebalance.ts", + "source:src/features/generation/core/dynamicPrompts.ts", + "source:src/features/generation/core/graph.ts", + "source:src/features/generation/core/graphBuilder.ts", + "source:src/features/generation/core/krea2Conditioning.ts", + "source:src/features/generation/core/pid.ts", + "source:src/features/generation/core/pidGraph.ts", + "source:src/features/generation/core/projectPromptDraft.ts", + "source:src/features/generation/core/prompt/ast.ts", + "source:src/features/generation/core/prompt/attention.ts", + "source:src/features/generation/core/prompt/dynamicPromptSyntax.ts", + "source:src/features/generation/core/prompt/highlight.ts", + "source:src/features/generation/core/promptHistory.ts", + "source:src/features/generation/core/promptTemplateOwnership.ts", + "source:src/features/generation/core/promptTemplates.ts", + "source:src/features/generation/core/referenceImage.ts", + "source:src/features/generation/core/resolveGenerateWidgetValues.ts", + "source:src/features/generation/core/settings.ts", + "source:src/features/generation/core/supportedBases.ts", + "source:src/features/generation/core/systemPrompts.ts", + "source:src/features/generation/core/wildcardCatalog.ts", + "source:src/features/generation/core/wildcardTransfer.ts", + "source:src/features/generation/data/architectureCapabilitiesApi.ts", + "source:src/features/generation/data/architectureCapabilitiesStore.ts", + "source:src/features/generation/data/dynamicPromptsQueries.ts", + "source:src/features/generation/data/llmTaskProgress.ts", + "source:src/features/generation/data/promptTemplates.ts", + "source:src/features/generation/data/promptUtilities.ts", + "source:src/features/generation/data/referenceImageUrls.ts", + "source:src/features/generation/data/systemPrompts.ts", + "source:src/features/generation/data/wildcards.ts", + "source:src/features/generation/graph.ts", + "source:src/features/generation/prompts.ts", + "source:src/features/generation/queries.ts", + "source:src/features/generation/react.ts", + "source:src/features/generation/runtime.ts", + "source:src/features/generation/settings.ts", + "source:src/features/generation/ui/GenerateAdvancedFields.tsx", + "source:src/features/generation/ui/GenerateCanvasSections.tsx", + "source:src/features/generation/ui/GenerateComponentsSection.tsx", + "source:src/features/generation/ui/GenerateConceptsSection.tsx", + "source:src/features/generation/ui/GenerateConditioningRebalanceField.tsx", + "source:src/features/generation/ui/GenerateDimensionFields.tsx", + "source:src/features/generation/ui/GenerateGuidanceSection.tsx", + "source:src/features/generation/ui/GenerateHeaderActions.tsx", + "source:src/features/generation/ui/GenerateModelCard.tsx", + "source:src/features/generation/ui/GeneratePresetsPopover.tsx", + "source:src/features/generation/ui/GenerateRenderSection.tsx", + "source:src/features/generation/ui/GenerateSettingsForm.tsx", + "source:src/features/generation/ui/GenerateWidgetView.tsx", + "source:src/features/generation/ui/GenerationUiContext.tsx", + "source:src/features/generation/ui/LLMTaskProgressRuntime.tsx", + "source:src/features/generation/ui/generateDebounce.ts", + "source:src/features/generation/ui/generateDraftRegistry.ts", + "source:src/features/generation/ui/generateFormViewModel.ts", + "source:src/features/generation/ui/modelSelectionNotice.ts", + "source:src/features/generation/ui/promptFields/DynamicPromptsButton.tsx", + "source:src/features/generation/ui/promptFields/DynamicPromptsPanel.tsx", + "source:src/features/generation/ui/promptFields/GeneratePromptFields.tsx", + "source:src/features/generation/ui/promptFields/LLMTaskProgressDisplay.tsx", + "source:src/features/generation/ui/promptFields/NegativePromptField.tsx", + "source:src/features/generation/ui/promptFields/PositivePromptActions.tsx", + "source:src/features/generation/ui/promptFields/PositivePromptField.tsx", + "source:src/features/generation/ui/promptFields/PromptHighlight.tsx", + "source:src/features/generation/ui/promptFields/PromptLineNumbers.tsx", + "source:src/features/generation/ui/promptFields/PromptPanelHeader.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplateEditor.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplateImage.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplatesButton.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplatesPanel.tsx", + "source:src/features/generation/ui/promptFields/PromptTextarea.tsx", + "source:src/features/generation/ui/promptFields/PromptTriggerAutocomplete.tsx", + "source:src/features/generation/ui/promptFields/SystemPromptsField.tsx", + "source:src/features/generation/ui/promptFields/WildcardImportDialog.tsx", + "source:src/features/generation/ui/promptFields/WildcardTransferActions.tsx", + "source:src/features/generation/ui/promptFields/WildcardsPanel.tsx", + "source:src/features/generation/ui/promptFields/index.ts", + "source:src/features/generation/ui/promptFields/promptAttentionHotkeys.ts", + "source:src/features/generation/ui/promptFields/promptCaret.ts", + "source:src/features/generation/ui/promptFields/promptFocus.ts", + "source:src/features/generation/ui/promptFields/promptHistoryNavigation.ts", + "source:src/features/generation/ui/promptFields/promptTriggerOptions.ts", + "source:src/features/generation/ui/promptFields/useDismissOnViewportChange.ts", + "source:src/features/generation/ui/promptFields/usePromptImageDrop.ts", + "source:src/features/generation/ui/promptFields/usePromptTriggerAutocomplete.tsx", + "source:src/features/generation/ui/promptFields/usePromptTriggerPicker.ts", + "source:src/features/generation/ui/promptFields/useSystemPrompts.ts", + "source:src/features/generation/ui/promptTemplateDraftStore.ts", + "source:src/features/generation/ui/reference-images/GenerateReferenceImagesSection.tsx", + "source:src/features/generation/ui/reference-images/ReferenceImageCard.tsx", + "source:src/features/generation/ui/reference-images/ReferenceImageControls.tsx", + "source:src/features/generation/ui/reference-images/ReferenceImageCropDialog.tsx", + "source:src/features/generation/ui/reference-images/referenceImageConfig.ts", + "source:src/features/generation/ui/shared/AspectRatioPreview.tsx", + "source:src/features/generation/ui/shared/AspectRatioSelect.tsx", + "source:src/features/generation/ui/shared/ConditioningRebalanceBars.tsx", + "source:src/features/generation/ui/shared/ConditioningRebalanceSparkline.tsx", + "source:src/features/generation/ui/shared/GenerateCollapsibleSection.tsx", + "source:src/features/generation/ui/shared/GenerateFieldContextMenu.tsx", + "source:src/features/generation/ui/shared/GenerateToggleSwitch.tsx", + "source:src/features/generation/ui/shared/SeedField.tsx", + "source:src/features/generation/ui/shared/modelDefaultSettings.ts", + "source:src/features/generation/ui/useDebouncedDraftValue.ts", + "source:src/features/generation/ui/useDynamicPrompts.ts", + "source:src/features/generation/ui/usePromptTemplates.ts", + "source:src/features/generation/ui/useWildcards.ts", + "source:src/features/generation/ui/wildcardFiles.ts", + "source:src/features/generation/widget.ts", + "source:src/features/identity/capabilities.ts", + "source:src/features/identity/core/schemas.ts", + "source:src/features/identity/core/sessionPolicy.ts", + "source:src/features/identity/core/tokenStorage.ts", + "source:src/features/identity/data/api.ts", + "source:src/features/identity/index.ts", + "source:src/features/identity/session.ts", + "source:src/features/identity/transportAdapter.ts", + "source:src/features/identity/ui/AccountMenu.tsx", + "source:src/features/identity/ui/AccountMenuSection.tsx", + "source:src/features/identity/ui/AuthScreen.tsx", + "source:src/features/identity/ui/AuthUnavailableScreen.tsx", + "source:src/features/identity/ui/LoginScreen.tsx", + "source:src/features/identity/ui/PasswordInput.tsx", + "source:src/features/identity/ui/ProfileDialog.tsx", + "source:src/features/identity/ui/SetupScreen.tsx", + "source:src/features/identity/ui/UserFormDialog.tsx", + "source:src/features/identity/ui/UserManagement.tsx", + "source:src/features/identity/ui/UsersPage.tsx", + "source:src/features/identity/ui/useIdentityNotify.ts", + "source:src/features/intermediates/data/focus.ts", + "source:src/features/intermediates/holdLease.ts", + "source:src/features/intermediates/index.ts", + "source:src/features/intermediates/settingsContribution.ts", + "source:src/features/models/core/baseIdentity.ts", + "source:src/features/models/core/library.ts", + "source:src/features/models/core/taxonomy.ts", + "source:src/features/models/data/api.ts", + "source:src/features/models/data/apiKeys.ts", + "source:src/features/models/data/installsStore.ts", + "source:src/features/models/data/modelLoadStore.ts", + "source:src/features/models/data/modelsStore.ts", + "source:src/features/models/data/startersStore.ts", + "source:src/features/models/data/transport.ts", + "source:src/features/models/index.ts", + "source:src/features/models/react.ts", + "source:src/features/models/ui/ModelInstallRuntime.tsx", + "source:src/features/models/ui/ModelsPage.tsx", + "source:src/features/models/ui/ModelsUiContext.tsx", + "source:src/features/models/ui/add-models/starterModelInstallSources.ts", + "source:src/features/models/ui/add-models/useInstallActions.ts", + "source:src/features/models/ui/components/ModelSelect.tsx", + "source:src/features/models/ui/uiStore.ts", + "source:src/features/models/ui/useInstallOutcomeToasts.ts", + "source:src/features/models/ui/useModelsNotify.ts", + "source:src/features/nodes/core/catalog.ts", + "source:src/features/nodes/data/api.ts", + "source:src/features/nodes/data/nodeExecutionStore.ts", + "source:src/features/nodes/data/nodesStore.ts", + "source:src/features/nodes/data/transport.ts", + "source:src/features/nodes/index.ts", + "source:src/features/nodes/ui/NodesPage.tsx", + "source:src/features/queue/contracts.ts", + "source:src/features/queue/core/activeSessions.ts", + "source:src/features/queue/core/deviceLabels.ts", + "source:src/features/queue/core/generationMeta.ts", + "source:src/features/queue/core/graphInputMedia.ts", + "source:src/features/queue/core/historySnapshot.ts", + "source:src/features/queue/core/historySummary.ts", + "source:src/features/queue/core/progressImage.ts", + "source:src/features/queue/core/progressRail.ts", + "source:src/features/queue/core/promptBatch.ts", + "source:src/features/queue/core/submissionRules.ts", + "source:src/features/queue/core/types.ts", + "source:src/features/queue/data/activeProgressTargetStore.ts", + "source:src/features/queue/data/events.ts", + "source:src/features/queue/data/generationDevicesStore.ts", + "source:src/features/queue/data/httpRealtimeQueueBackend.ts", + "source:src/features/queue/data/itemProgressStore.ts", + "source:src/features/queue/data/mappers.ts", + "source:src/features/queue/data/progressImageStore.ts", + "source:src/features/queue/data/progressStore.ts", + "source:src/features/queue/data/queries.ts", + "source:src/features/queue/data/realtimeRuntime.ts", + "source:src/features/queue/data/serverApi.ts", + "source:src/features/queue/data/submissionApi.ts", + "source:src/features/queue/devices.ts", + "source:src/features/queue/index.ts", + "source:src/features/queue/menu.ts", + "source:src/features/queue/publicApi.ts", + "source:src/features/queue/queries.ts", + "source:src/features/queue/react.ts", + "source:src/features/queue/reveal.ts", + "source:src/features/queue/runtime.ts", + "source:src/features/queue/runtime/coordinator.ts", + "source:src/features/queue/runtime/receiptAcknowledgements.ts", + "source:src/features/queue/runtime/utilityGraph.ts", + "source:src/features/queue/ui/QueueDataRuntime.tsx", + "source:src/features/queue/ui/QueueUiContext.tsx", + "source:src/features/queue/ui/currentBatchItems.ts", + "source:src/features/queue/ui/modelCacheStore.ts", + "source:src/features/queue/ui/queueConfirmationStore.ts", + "source:src/features/queue/ui/queueDataStore.ts", + "source:src/features/queue/ui/queueMenuActions.tsx", + "source:src/features/queue/ui/queueOwnership.ts", + "source:src/features/queue/ui/queueProgressCleanup.ts", + "source:src/features/queue/ui/queueScope.ts", + "source:src/features/queue/ui/queueUiStore.ts", + "source:src/features/queue/ui/queueViewModel.ts", + "source:src/features/queue/ui/useDeviceLabel.ts", + "source:src/features/queue/utility.ts", + "source:src/features/queue/widget.ts", + "source:src/features/upscale/core/graph.ts", + "source:src/features/upscale/core/settings.ts", + "source:src/features/upscale/index.ts", + "source:src/features/upscale/ui/UpscaleUiContext.tsx", + "source:src/features/video/core/dimensions.ts", + "source:src/features/video/core/graph.ts", + "source:src/features/video/core/settings.ts", + "source:src/features/video/core/videoPolicies.ts", + "source:src/features/video/core/widgetValues.ts", + "source:src/features/video/index.ts", + "source:src/features/video/ui/VideoUiContext.tsx", + "source:src/features/workflow/core/batch.ts", + "source:src/features/workflow/core/buildGraph.ts", + "source:src/features/workflow/core/callSavedWorkflow.ts", + "source:src/features/workflow/core/connectors.ts", + "source:src/features/workflow/core/document.ts", + "source:src/features/workflow/core/fields.ts", + "source:src/features/workflow/core/forLoops.ts", + "source:src/features/workflow/core/graphIndex.ts", + "source:src/features/workflow/core/layerWorkflow.ts", + "source:src/features/workflow/core/libraryTags.ts", + "source:src/features/workflow/core/modelRequirements.ts", + "source:src/features/workflow/core/types.ts", + "source:src/features/workflow/core/validation.ts", + "source:src/features/workflow/core/workflowJson.ts", + "source:src/features/workflow/data/api.ts", + "source:src/features/workflow/data/libraryBrowseStore.ts", + "source:src/features/workflow/data/libraryCache.ts", + "source:src/features/workflow/data/publication.ts", + "source:src/features/workflow/data/savedWorkflowQueries.ts", + "source:src/features/workflow/data/templates.ts", + "source:src/features/workflow/queries.ts", + "source:src/features/workflow/react.ts", + "source:src/features/workflow/ui/MenuActionItem.tsx", + "source:src/features/workflow/ui/PendingLibraryWorkflowLoader.tsx", + "source:src/features/workflow/ui/WorkflowUiContext.tsx", + "source:src/features/workflow/ui/WorkflowWidgetChrome.tsx", + "source:src/features/workflow/ui/editor/AddNodeDialog.tsx", + "source:src/features/workflow/ui/editor/CallSavedWorkflowSyncRuntime.tsx", + "source:src/features/workflow/ui/editor/flowInstanceStore.ts", + "source:src/features/workflow/ui/editor/workflowViewportStore.ts", + "source:src/features/workflow/ui/library/LibraryCopyChoiceDialog.tsx", + "source:src/features/workflow/ui/library/ProjectWorkflowsView.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryCard.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryDetailPanel.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryDialog.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryGrid.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryTagChips.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryThumbnail.tsx", + "source:src/features/workflow/ui/library/WorkflowPublicationHost.tsx", + "source:src/features/workflow/ui/library/WorkflowRequirementsList.tsx", + "source:src/features/workflow/ui/library/libraryPreviewSource.ts", + "source:src/features/workflow/ui/library/projectWorkflowEntries.ts", + "source:src/features/workflow/ui/library/relativeTime.ts", + "source:src/features/workflow/ui/library/useOpenLibraryWorkflow.ts", + "source:src/features/workflow/ui/library/useWorkflowPublication.ts", + "source:src/features/workflow/ui/pendingLibraryWorkflowLoadRuntime.ts", + "source:src/features/workflow/ui/useProjectGraphCommands.ts", + "source:src/features/workflow/ui/workflowTransfer.ts", + "source:src/features/workflow/ui/workflowUiStore.ts", + "source:src/features/workflow/utility.ts", + "source:src/features/workflow/widget.ts", + "source:src/platform/browser/downloadBlob.ts", + "source:src/platform/browser/knownBrowserIssues.ts", + "source:src/platform/browser/randomUuid.ts", + "source:src/platform/browser/sha256.ts", + "source:src/platform/browser/webLocks.ts", + "source:src/platform/core/concurrency.ts", + "source:src/platform/core/json.ts", + "source:src/platform/core/outputImages.ts", + "source:src/platform/core/seed.ts", + "source:src/platform/i18n/client.ts", + "source:src/platform/i18n/languages.ts", + "source:src/platform/logging/contracts.ts", + "source:src/platform/logging/logger.ts", + "source:src/platform/logging/normalize.ts", + "source:src/platform/performance/semanticReady.ts", + "source:src/platform/pwa/registerServiceWorker.ts", + "source:src/platform/query/client.ts", + "source:src/platform/react/composeProviders.tsx", + "source:src/platform/react/draftRegistry.ts", + "source:src/platform/react/focusIfUnclaimed.ts", + "source:src/platform/react/useModifierHeld.ts", + "source:src/platform/react/useMountEffect.ts", + "source:src/platform/react/usePreservedScrollOffset.ts", + "source:src/platform/react/useZodForm.ts", + "source:src/platform/search/dateTokens.ts", + "source:src/platform/state/accountLifecycle.ts", + "source:src/platform/state/compareAndSwapRollback.ts", + "source:src/platform/state/externalStore.ts", + "source:src/platform/state/externalStoreCore.ts", + "source:src/platform/state/projectedExternalStore.ts", + "source:src/platform/state/selectorCore.ts", + "source:src/platform/state/selectors.ts", + "source:src/platform/state/singleFlight.ts", + "source:src/platform/time/serverTimestamp.ts", + "source:src/platform/transport/connectionStore.ts", + "source:src/platform/transport/deploymentBase.ts", + "source:src/platform/transport/http.ts", + "source:src/platform/transport/socketHub.ts", + "source:src/platform/ui/Button.tsx", + "source:src/platform/ui/ColorPicker.tsx", + "source:src/platform/ui/Combobox.tsx", + "source:src/platform/ui/ConfirmDialog.tsx", + "source:src/platform/ui/DropTargetOverlay.tsx", + "source:src/platform/ui/DropZone.tsx", + "source:src/platform/ui/EmptyState.tsx", + "source:src/platform/ui/Field.tsx", + "source:src/platform/ui/Group.tsx", + "source:src/platform/ui/HsvBoxPicker.tsx", + "source:src/platform/ui/HsvWheelPicker.tsx", + "source:src/platform/ui/InputShell.tsx", + "source:src/platform/ui/InvokeMark.tsx", + "source:src/platform/ui/JsonPreview.tsx", + "source:src/platform/ui/Menu.tsx", + "source:src/platform/ui/MiddleTruncate.tsx", + "source:src/platform/ui/ModelDefaultButton.tsx", + "source:src/platform/ui/PageShell.tsx", + "source:src/platform/ui/Panel.tsx", + "source:src/platform/ui/PanelHeader.tsx", + "source:src/platform/ui/Picker.tsx", + "source:src/platform/ui/Popover.tsx", + "source:src/platform/ui/RenameDialog.tsx", + "source:src/platform/ui/ResizableTextarea.tsx", + "source:src/platform/ui/ResizeHandle.tsx", + "source:src/platform/ui/RetryBoundary.tsx", + "source:src/platform/ui/Row.tsx", + "source:src/platform/ui/Scrollable.tsx", + "source:src/platform/ui/ScrubberField.tsx", + "source:src/platform/ui/SeedInput.tsx", + "source:src/platform/ui/SegmentTabs.tsx", + "source:src/platform/ui/SegmentedControl.tsx", + "source:src/platform/ui/Select.tsx", + "source:src/platform/ui/Slider.tsx", + "source:src/platform/ui/SliderNumberField.tsx", + "source:src/platform/ui/StatusWidgetChip.tsx", + "source:src/platform/ui/Tabs.tsx", + "source:src/platform/ui/ToggleDot.tsx", + "source:src/platform/ui/Toolbar.tsx", + "source:src/platform/ui/Tooltip.tsx", + "source:src/platform/ui/VendoredIcon.tsx", + "source:src/platform/ui/color.ts", + "source:src/platform/ui/colorPickerStore.ts", + "source:src/platform/ui/colorPickerSync.ts", + "source:src/platform/ui/hints/FeatureHint.tsx", + "source:src/platform/ui/hints/hintRegistry.ts", + "source:src/platform/ui/hints/hintsContext.tsx", + "source:src/platform/ui/hints/index.ts", + "source:src/platform/ui/hsv.ts", + "source:src/platform/ui/index.ts", + "source:src/platform/ui/list/ListDivider.tsx", + "source:src/platform/ui/list/ListItem.tsx", + "source:src/platform/ui/list/ListSectionHeader.tsx", + "source:src/platform/ui/list/ListStack.tsx", + "source:src/platform/ui/list/listLayout.ts", + "source:src/platform/ui/settings/contracts.ts", + "source:src/platform/ui/streaming-image/StreamingImageFrame.tsx", + "source:src/platform/ui/streaming-image/streamingImageSource.ts", + "source:src/platform/ui/streaming-image/useStreamingImageSource.ts", + "source:src/platform/ui/theme/applyTheme.ts", + "source:src/platform/ui/theme/recipes.ts", + "source:src/platform/ui/theme/system.ts", + "source:src/platform/ui/theme/themes.ts", + "source:src/platform/ui/toaster.tsx", + "source:src/platform/ui/useScrollAreaPhantomHeal.ts", + "source:src/platform/ui/wheelScrollsHorizontally.ts", + "source:src/platform/ui/widgetOverlayRegistry.ts", + "source:src/platform/ui/widgetOverlays.ts", + "source:src/workbench/WorkbenchContext.tsx", + "source:src/workbench/WorkbenchRuntime.tsx", + "source:src/workbench/WorkbenchSessionController.tsx", + "source:src/workbench/WorkbenchWidgetRegistryContext.tsx", + "source:src/workbench/activeInvocationSubmission.ts", + "source:src/workbench/autoRoutePolicy.ts", + "source:src/workbench/bootWidgetPreload.ts", + "source:src/workbench/canvas-engine/api.ts", + "source:src/workbench/canvas-engine/capabilities.ts", + "source:src/workbench/canvas-engine/color.ts", + "source:src/workbench/canvas-engine/contracts.ts", + "source:src/workbench/canvas-engine/controllers/booleanMergeController.ts", + "source:src/workbench/canvas-engine/controllers/controlPixelController.ts", + "source:src/workbench/canvas-engine/controllers/copyLayerController.ts", + "source:src/workbench/canvas-engine/controllers/cropLayerController.ts", + "source:src/workbench/canvas-engine/controllers/editSteps.ts", + "source:src/workbench/canvas-engine/controllers/editingController.ts", + "source:src/workbench/canvas-engine/controllers/extractMaskedAreaController.ts", + "source:src/workbench/canvas-engine/controllers/filterResultController.ts", + "source:src/workbench/canvas-engine/controllers/floatingSelectionController.ts", + "source:src/workbench/canvas-engine/controllers/generatedResultController.ts", + "source:src/workbench/canvas-engine/controllers/historyController.ts", + "source:src/workbench/canvas-engine/controllers/interactionController.ts", + "source:src/workbench/canvas-engine/controllers/layerController.ts", + "source:src/workbench/canvas-engine/controllers/layerMutationController.ts", + "source:src/workbench/canvas-engine/controllers/maskLayerController.ts", + "source:src/workbench/canvas-engine/controllers/maskResultController.ts", + "source:src/workbench/canvas-engine/controllers/mergeLayerController.ts", + "source:src/workbench/canvas-engine/controllers/mutationContext.ts", + "source:src/workbench/canvas-engine/controllers/newRasterLayerController.ts", + "source:src/workbench/canvas-engine/controllers/persistenceController.ts", + "source:src/workbench/canvas-engine/controllers/previewStateController.ts", + "source:src/workbench/canvas-engine/controllers/psdExportController.ts", + "source:src/workbench/canvas-engine/controllers/rasterController.ts", + "source:src/workbench/canvas-engine/controllers/rasterExportController.ts", + "source:src/workbench/canvas-engine/controllers/rasterMemoryBudgetController.ts", + "source:src/workbench/canvas-engine/controllers/rasterizeLayerController.ts", + "source:src/workbench/canvas-engine/controllers/renderController.ts", + "source:src/workbench/canvas-engine/controllers/selectionImageController.ts", + "source:src/workbench/canvas-engine/controllers/selectionPixelController.ts", + "source:src/workbench/canvas-engine/controllers/stagedResultController.ts", + "source:src/workbench/canvas-engine/controllers/structuralLayerController.ts", + "source:src/workbench/canvas-engine/controllers/textEditingController.ts", + "source:src/workbench/canvas-engine/controllers/thumbnailController.ts", + "source:src/workbench/canvas-engine/controllers/transformEditingController.ts", + "source:src/workbench/canvas-engine/diagnostics.ts", + "source:src/workbench/canvas-engine/document-model/documentModel.ts", + "source:src/workbench/canvas-engine/document-model/postconditions.ts", + "source:src/workbench/canvas-engine/document-model/screenComposition.ts", + "source:src/workbench/canvas-engine/document-model/semanticLeaf.ts", + "source:src/workbench/canvas-engine/document-model/semanticNode.ts", + "source:src/workbench/canvas-engine/document/bitmapStore.ts", + "source:src/workbench/canvas-engine/document/documentIndex.ts", + "source:src/workbench/canvas-engine/document/documentMirror.ts", + "source:src/workbench/canvas-engine/document/documentTree.ts", + "source:src/workbench/canvas-engine/document/insertionAnchors.ts", + "source:src/workbench/canvas-engine/document/layerChangeDecision.ts", + "source:src/workbench/canvas-engine/document/layerEligibility.ts", + "source:src/workbench/canvas-engine/document/layerFactories.ts", + "source:src/workbench/canvas-engine/document/layerStacks.ts", + "source:src/workbench/canvas-engine/document/mergeDown.ts", + "source:src/workbench/canvas-engine/document/mergeVisible.ts", + "source:src/workbench/canvas-engine/document/selectionRepair.ts", + "source:src/workbench/canvas-engine/document/sources.ts", + "source:src/workbench/canvas-engine/editGate.ts", + "source:src/workbench/canvas-engine/editing/controlPixelEdit.ts", + "source:src/workbench/canvas-engine/engine.ts", + "source:src/workbench/canvas-engine/engineStores.ts", + "source:src/workbench/canvas-engine/export/psdExport.ts", + "source:src/workbench/canvas-engine/exportRasterComposite.ts", + "source:src/workbench/canvas-engine/filterError.ts", + "source:src/workbench/canvas-engine/fontReferences.ts", + "source:src/workbench/canvas-engine/freehand.ts", + "source:src/workbench/canvas-engine/history/documentPatch.ts", + "source:src/workbench/canvas-engine/history/history.ts", + "source:src/workbench/canvas-engine/history/imagePatch.ts", + "source:src/workbench/canvas-engine/history/layerSnapshot.ts", + "source:src/workbench/canvas-engine/input/pointerPipeline.ts", + "source:src/workbench/canvas-engine/input/wheel.ts", + "source:src/workbench/canvas-engine/layerExportGuards.ts", + "source:src/workbench/canvas-engine/layerRasterizer.ts", + "source:src/workbench/canvas-engine/math/mat2d.ts", + "source:src/workbench/canvas-engine/math/rect.ts", + "source:src/workbench/canvas-engine/math/snapping.ts", + "source:src/workbench/canvas-engine/mutationContracts.ts", + "source:src/workbench/canvas-engine/pressureBands.ts", + "source:src/workbench/canvas-engine/previewPublisher.ts", + "source:src/workbench/canvas-engine/rasterSnapshotCapture.ts", + "source:src/workbench/canvas-engine/render/adjustedSurfaceCache.ts", + "source:src/workbench/canvas-engine/render/adjustments.ts", + "source:src/workbench/canvas-engine/render/alphaBounds.ts", + "source:src/workbench/canvas-engine/render/colorSample.ts", + "source:src/workbench/canvas-engine/render/compositeFrame.ts", + "source:src/workbench/canvas-engine/render/compositor.ts", + "source:src/workbench/canvas-engine/render/controlTransparency.ts", + "source:src/workbench/canvas-engine/render/decodedBitmapPool.ts", + "source:src/workbench/canvas-engine/render/derivedSurfaceCache.ts", + "source:src/workbench/canvas-engine/render/floatingSelectionFrame.ts", + "source:src/workbench/canvas-engine/render/fontLoader.ts", + "source:src/workbench/canvas-engine/render/frameDemand.ts", + "source:src/workbench/canvas-engine/render/groupCompositeScopes.ts", + "source:src/workbench/canvas-engine/render/groupSurfaceCache.ts", + "source:src/workbench/canvas-engine/render/layerCache.ts", + "source:src/workbench/canvas-engine/render/layerDisplayEffect.ts", + "source:src/workbench/canvas-engine/render/maskFill.ts", + "source:src/workbench/canvas-engine/render/overlayFrame.ts", + "source:src/workbench/canvas-engine/render/overlayRenderer.ts", + "source:src/workbench/canvas-engine/render/paintCacheTrim.ts", + "source:src/workbench/canvas-engine/render/raster.ts", + "source:src/workbench/canvas-engine/render/rasterComposite.ts", + "source:src/workbench/canvas-engine/render/rasterizers/gradientRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/imageRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/index.ts", + "source:src/workbench/canvas-engine/render/rasterizers/paintRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/shapeRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/textRasterizer.ts", + "source:src/workbench/canvas-engine/render/scheduler.ts", + "source:src/workbench/canvas-engine/render/thumbnail.ts", + "source:src/workbench/canvas-engine/samCoordinates.ts", + "source:src/workbench/canvas-engine/selectObjectBridge.ts", + "source:src/workbench/canvas-engine/selection/floatingSelection.ts", + "source:src/workbench/canvas-engine/selection/marchingAnts.ts", + "source:src/workbench/canvas-engine/selection/maskOutline.ts", + "source:src/workbench/canvas-engine/selection/selectionHistory.ts", + "source:src/workbench/canvas-engine/selection/selectionOpMode.ts", + "source:src/workbench/canvas-engine/selection/selectionOps.ts", + "source:src/workbench/canvas-engine/selection/selectionPaths.ts", + "source:src/workbench/canvas-engine/selection/selectionState.ts", + "source:src/workbench/canvas-engine/stagedPreviewBlobCache.ts", + "source:src/workbench/canvas-engine/strokeCommit.ts", + "source:src/workbench/canvas-engine/tools/bboxHitTest.ts", + "source:src/workbench/canvas-engine/tools/bboxTool.ts", + "source:src/workbench/canvas-engine/tools/brushTool.ts", + "source:src/workbench/canvas-engine/tools/colorPickerTool.ts", + "source:src/workbench/canvas-engine/tools/eraserTool.ts", + "source:src/workbench/canvas-engine/tools/gradientTool.ts", + "source:src/workbench/canvas-engine/tools/gridSnap.ts", + "source:src/workbench/canvas-engine/tools/lassoTool.ts", + "source:src/workbench/canvas-engine/tools/marqueeTool.ts", + "source:src/workbench/canvas-engine/tools/moveHitTest.ts", + "source:src/workbench/canvas-engine/tools/moveTool.ts", + "source:src/workbench/canvas-engine/tools/paintConstants.ts", + "source:src/workbench/canvas-engine/tools/paintTool.ts", + "source:src/workbench/canvas-engine/tools/polylineSession.ts", + "source:src/workbench/canvas-engine/tools/samHitTest.ts", + "source:src/workbench/canvas-engine/tools/samTool.ts", + "source:src/workbench/canvas-engine/tools/shapeTool.ts", + "source:src/workbench/canvas-engine/tools/strokeSession.ts", + "source:src/workbench/canvas-engine/tools/textTool.ts", + "source:src/workbench/canvas-engine/tools/transformTool.ts", + "source:src/workbench/canvas-engine/tools/viewTool.ts", + "source:src/workbench/canvas-engine/transform/transformMath.ts", + "source:src/workbench/canvas-engine/types.ts", + "source:src/workbench/canvas-engine/viewport.ts", + "source:src/workbench/canvas-operations/FilterOperationCoordinator.ts", + "source:src/workbench/canvas-operations/SelectObjectCoordinator.ts", + "source:src/workbench/canvas-operations/api.ts", + "source:src/workbench/canvas-operations/applicationPort.ts", + "source:src/workbench/canvas-operations/backend/canvasImages.ts", + "source:src/workbench/canvas-operations/canvasImportNotice.ts", + "source:src/workbench/canvas-operations/compositeForGeneration.ts", + "source:src/workbench/canvas-operations/controlRecommendations.ts", + "source:src/workbench/canvas-operations/createCanvasEngine.ts", + "source:src/workbench/canvas-operations/createCanvasFromImages.ts", + "source:src/workbench/canvas-operations/createFromBbox.ts", + "source:src/workbench/canvas-operations/createFromBboxNotice.ts", + "source:src/workbench/canvas-operations/engineRegistry.ts", + "source:src/workbench/canvas-operations/exportCanvasComposite.ts", + "source:src/workbench/canvas-operations/filterGraphs.ts", + "source:src/workbench/canvas-operations/filterOperationSession.ts", + "source:src/workbench/canvas-operations/filterPreferences.ts", + "source:src/workbench/canvas-operations/generationComposite.ts", + "source:src/workbench/canvas-operations/generationCompositePlan.ts", + "source:src/workbench/canvas-operations/importGalleryImages.ts", + "source:src/workbench/canvas-operations/layerFilterRunner.ts", + "source:src/workbench/canvas-operations/layerImageResult.ts", + "source:src/workbench/canvas-operations/operationAccess.ts", + "source:src/workbench/canvas-operations/operationController.ts", + "source:src/workbench/canvas-operations/operationStores.ts", + "source:src/workbench/canvas-operations/react.ts", + "source:src/workbench/canvas-operations/samGraph.ts", + "source:src/workbench/canvas-operations/saveCanvasToGallery.ts", + "source:src/workbench/canvas-operations/selectObjectSession.ts", + "source:src/workbench/canvas-operations/useCanvasEngine.ts", + "source:src/workbench/canvasDimsSync.ts", + "source:src/workbench/canvasInvocationPreparation.ts", + "source:src/workbench/canvasLayerContent.ts", + "source:src/workbench/canvasMigration.ts", + "source:src/workbench/canvasProjectMutationPort.ts", + "source:src/workbench/canvasProjectMutations.ts", + "source:src/workbench/canvasSchemaVersion.ts", + "source:src/workbench/canvasStagingView.ts", + "source:src/workbench/components/QueueProgressIndicator.tsx", + "source:src/workbench/components/WorkbenchSplashScreen.tsx", + "source:src/workbench/components/WorkbenchUnavailableScreen.tsx", + "source:src/workbench/controlAdapters.ts", + "source:src/workbench/controlLayerChecks.ts", + "source:src/workbench/deferredResource.ts", + "source:src/workbench/diagnostics/loggingPreferences.ts", + "source:src/workbench/extensions/contributionSource.ts", + "source:src/workbench/extensions/extensionRegistry.ts", + "source:src/workbench/floatingWindows.ts", + "source:src/workbench/focusRegions.tsx", + "source:src/workbench/graphPreviewInvocation.ts", + "source:src/workbench/graphSurfaces.ts", + "source:src/workbench/graphWidgets.ts", + "source:src/workbench/hotkeys/WorkbenchHotkeyRuntime.tsx", + "source:src/workbench/hotkeys/catalog.ts", + "source:src/workbench/hotkeys/extensionHotkeys.ts", + "source:src/workbench/hotkeys/firstPartyCommands.ts", + "source:src/workbench/hotkeys/index.ts", + "source:src/workbench/hotkeys/keyGlyphs.tsx", + "source:src/workbench/hotkeys/keys.ts", + "source:src/workbench/hotkeys/modalLayer.ts", + "source:src/workbench/hotkeys/resolve.ts", + "source:src/workbench/hotkeys/targetWidget.ts", + "source:src/workbench/iconResolver.tsx", + "source:src/workbench/image-actions/ImageContextMenu.tsx", + "source:src/workbench/image-actions/RecallActionButtons.tsx", + "source:src/workbench/image-actions/appendReferenceImage.ts", + "source:src/workbench/image-actions/canvasImportError.ts", + "source:src/workbench/image-actions/executeImageRecall.ts", + "source:src/workbench/image-actions/executeVideoRecall.ts", + "source:src/workbench/image-actions/galleryOptimisticRollback.ts", + "source:src/workbench/image-actions/imageRecall.ts", + "source:src/workbench/image-actions/index.ts", + "source:src/workbench/image-actions/recallParametersBridge.ts", + "source:src/workbench/image-actions/useDeletionConfirmation.tsx", + "source:src/workbench/image-actions/useFindGalleryItem.ts", + "source:src/workbench/image-actions/useImageActions.ts", + "source:src/workbench/image-actions/videoRecall.ts", + "source:src/workbench/image-map/api.ts", + "source:src/workbench/image-map/imageMapStore.ts", + "source:src/workbench/image-map/indexProgress.ts", + "source:src/workbench/invocation.ts", + "source:src/workbench/invocationSubmit.ts", + "source:src/workbench/launchpad/HelpMenu.tsx", + "source:src/workbench/launchpad/KnownBrowserIssuesAlert.tsx", + "source:src/workbench/launchpad/Launchpad.tsx", + "source:src/workbench/launchpad/LaunchpadNav.tsx", + "source:src/workbench/launchpad/LaunchpadTopBar.tsx", + "source:src/workbench/launchpad/OpenProjectsControl.tsx", + "source:src/workbench/launchpad/formatRelativeTime.ts", + "source:src/workbench/launchpad/home/IntentTiles.tsx", + "source:src/workbench/launchpad/home/LivePanel.tsx", + "source:src/workbench/launchpad/home/RecentProjectsRow.tsx", + "source:src/workbench/launchpad/home/ResumeCard.tsx", + "source:src/workbench/launchpad/intents.ts", + "source:src/workbench/launchpad/pages/HomePage.tsx", + "source:src/workbench/launchpad/pages/ProjectsPage.tsx", + "source:src/workbench/launchpad/projects/NewProjectButton.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenu.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenuHost.tsx", + "source:src/workbench/launchpad/projects/ProjectCard.tsx", + "source:src/workbench/launchpad/projects/ProjectCompatibilityBadge.tsx", + "source:src/workbench/launchpad/projects/ProjectCover.tsx", + "source:src/workbench/launchpad/projects/ProjectRow.tsx", + "source:src/workbench/launchpad/projects/ProjectsBrowser.tsx", + "source:src/workbench/launchpad/projects/ProjectsToolbar.tsx", + "source:src/workbench/launchpad/projects/projectLibraryView.ts", + "source:src/workbench/launchpad/projects/projectPins.ts", + "source:src/workbench/launchpad/projects/projectsMetrics.ts", + "source:src/workbench/launchpad/projects/useProjectCardActions.ts", + "source:src/workbench/launchpad/startEntries.ts", + "source:src/workbench/layerPanelState.ts", + "source:src/workbench/layoutContracts.ts", + "source:src/workbench/layoutPresetActivation.ts", + "source:src/workbench/layoutPresetCollection.ts", + "source:src/workbench/layoutPresetRouting.ts", + "source:src/workbench/layoutPresetSnapshots.ts", + "source:src/workbench/layoutPresets.ts", + "source:src/workbench/layoutWidgetSet.ts", + "source:src/workbench/mediaReferences.ts", + "source:src/workbench/palette/LaunchpadCommandPalette.tsx", + "source:src/workbench/palette/PaletteButton.tsx", + "source:src/workbench/palette/WorkbenchCommandPalette.tsx", + "source:src/workbench/palette/paletteStore.ts", + "source:src/workbench/palette/settingsEntryDeps.ts", + "source:src/workbench/panZoom.ts", + "source:src/workbench/performanceMarks.ts", + "source:src/workbench/persistenceRuntime.ts", + "source:src/workbench/projectCanvasGate.ts", + "source:src/workbench/projectEvents.ts", + "source:src/workbench/projectWorkflows.ts", + "source:src/workbench/projects/accountOwnedWorkbenchDatabase.ts", + "source:src/workbench/projects/api.ts", + "source:src/workbench/projects/components/OpenProjectDialog.tsx", + "source:src/workbench/projects/components/ProjectFileOptionsProvider.tsx", + "source:src/workbench/projects/components/index.ts", + "source:src/workbench/projects/covers.ts", + "source:src/workbench/projects/draftStore.ts", + "source:src/workbench/projects/durableSyncedPersistence.ts", + "source:src/workbench/projects/editorSession.ts", + "source:src/workbench/projects/ids.ts", + "source:src/workbench/projects/indexedDbDraftStore.ts", + "source:src/workbench/projects/invk/format.ts", + "source:src/workbench/projects/library.ts", + "source:src/workbench/projects/openProjectBroker.ts", + "source:src/workbench/projects/openProjects.ts", + "source:src/workbench/projects/projectAssets.ts", + "source:src/workbench/projects/projectDocument.ts", + "source:src/workbench/projects/projectFile.ts", + "source:src/workbench/projects/projectFileErrors.ts", + "source:src/workbench/projects/projectFileToasts.ts", + "source:src/workbench/projects/projectFlush.ts", + "source:src/workbench/projects/projectHydration.ts", + "source:src/workbench/projects/projectLifecycleLocks.ts", + "source:src/workbench/projects/projectLoadRefusal.ts", + "source:src/workbench/projects/projectTabsA11y.ts", + "source:src/workbench/projects/session.ts", + "source:src/workbench/projects/syncStore.ts", + "source:src/workbench/projects/syncedPersistence.ts", + "source:src/workbench/projects/useProjectActions.ts", + "source:src/workbench/projects/useProjectFileActions.ts", + "source:src/workbench/projects/workbenchDatabase.ts", + "source:src/workbench/queue-integration/QueueProgressRail.tsx", + "source:src/workbench/queue-integration/QueueRecoveryNotice.tsx", + "source:src/workbench/queue-integration/activeQueueRuns.ts", + "source:src/workbench/queue-integration/queueRecall.ts", + "source:src/workbench/queue-integration/queueRecallCache.ts", + "source:src/workbench/queue-integration/queueRecovery.ts", + "source:src/workbench/queue-integration/queueRunJournal.ts", + "source:src/workbench/queue-integration/queueRunLocks.ts", + "source:src/workbench/queue-integration/queueRunPersistence.ts", + "source:src/workbench/queue-integration/queueRunRestoration.ts", + "source:src/workbench/queue-integration/useActiveQueueProgress.ts", + "source:src/workbench/queue-integration/useLocalRecallSnapshot.ts", + "source:src/workbench/queue-integration/useQueueItemRecall.ts", + "source:src/workbench/queue-integration/useQueueRecoveryProjectIds.ts", + "source:src/workbench/settings/SettingsDialogHost.tsx", + "source:src/workbench/settings/WidgetSettingsButton.tsx", + "source:src/workbench/settings/applicationContributions.ts", + "source:src/workbench/settings/catalog.ts", + "source:src/workbench/settings/clearWorkspaceData.ts", + "source:src/workbench/settings/dialogResource.ts", + "source:src/workbench/settings/launchpad.tsx", + "source:src/workbench/settings/settingsDialogStore.ts", + "source:src/workbench/settings/settingsSearchShortcut.ts", + "source:src/workbench/settings/store.ts", + "source:src/workbench/shell/BottomPanel.tsx", + "source:src/workbench/shell/CenterArea.tsx", + "source:src/workbench/shell/DocumentTitleProgress.tsx", + "source:src/workbench/shell/Panels.tsx", + "source:src/workbench/shell/PasteMediaRuntime.tsx", + "source:src/workbench/shell/ProjectConflictBanner.tsx", + "source:src/workbench/shell/QueueRecoveryBanner.tsx", + "source:src/workbench/shell/StatusBar.tsx", + "source:src/workbench/shell/WorkbenchShell.tsx", + "source:src/workbench/shell/documentTitle.ts", + "source:src/workbench/shell/holdToDragSensor.ts", + "source:src/workbench/shell/index.ts", + "source:src/workbench/shell/notifications/WorkbenchNotificationToaster.tsx", + "source:src/workbench/shell/notifications/index.ts", + "source:src/workbench/shell/notifications/toastPolicy.ts", + "source:src/workbench/shell/topbar/AppMenu.tsx", + "source:src/workbench/shell/topbar/InvocationCluster.tsx", + "source:src/workbench/shell/topbar/InvokeButton.tsx", + "source:src/workbench/shell/topbar/IterationsField.tsx", + "source:src/workbench/shell/topbar/LayoutPresetAdminDialogs.tsx", + "source:src/workbench/shell/topbar/LayoutPresetDialog.tsx", + "source:src/workbench/shell/topbar/LayoutPresetManagerDialog.tsx", + "source:src/workbench/shell/topbar/LayoutPresetStrip.tsx", + "source:src/workbench/shell/topbar/ProjectSwitcher.tsx", + "source:src/workbench/shell/topbar/QueueCluster.tsx", + "source:src/workbench/shell/topbar/RoutingControl.tsx", + "source:src/workbench/shell/topbar/RoutingDestinationSegments.tsx", + "source:src/workbench/shell/topbar/TopBar.tsx", + "source:src/workbench/shell/topbar/TopbarProgressRail.tsx", + "source:src/workbench/shell/topbar/TopbarShortcutKeys.tsx", + "source:src/workbench/shell/topbar/index.ts", + "source:src/workbench/shell/topbar/invokeButtonModel.ts", + "source:src/workbench/shell/topbar/layoutPresetDialogModel.ts", + "source:src/workbench/shell/topbar/layoutPresetIcons.ts", + "source:src/workbench/shell/topbar/layoutPresetManagerStore.ts", + "source:src/workbench/shell/topbar/projectSwitcherStore.ts", + "source:src/workbench/shell/topbar/topbarBreakpoints.ts", + "source:src/workbench/shell/topbar/useInvocationState.ts", + "source:src/workbench/shell/topbar/useLayoutDrift.ts", + "source:src/workbench/shell/topbar/useTopbarShortcut.ts", + "source:src/workbench/shell/useMountedInstanceIds.ts", + "source:src/workbench/useCanvasProjectMutationDispatch.ts", + "source:src/workbench/useNotify.ts", + "source:src/workbench/useOpenWorkbenchWidget.ts", + "source:src/workbench/widget-frame/FloatingWidgetLayer.tsx", + "source:src/workbench/widget-frame/WidgetActionsMenu.tsx", + "source:src/workbench/widget-frame/WidgetBar.tsx", + "source:src/workbench/widget-frame/WidgetEnableMenu.tsx", + "source:src/workbench/widget-frame/WidgetFailureBoundary.tsx", + "source:src/workbench/widget-frame/WidgetFrames.tsx", + "source:src/workbench/widget-frame/WidgetHosts.tsx", + "source:src/workbench/widget-frame/WidgetIdentityIcon.tsx", + "source:src/workbench/widget-frame/WidgetInstanceContextMenu.tsx", + "source:src/workbench/widget-frame/WidgetLoadingFallback.tsx", + "source:src/workbench/widget-frame/WidgetRegionDropOverlay.tsx", + "source:src/workbench/widget-frame/WidgetRenderer.tsx", + "source:src/workbench/widget-frame/WidgetSourceLockBadge.tsx", + "source:src/workbench/widget-frame/WidgetStrip.tsx", + "source:src/workbench/widget-frame/createWidgetRuntime.ts", + "source:src/workbench/widget-frame/index.ts", + "source:src/workbench/widget-frame/useWidgetIntentPreload.ts", + "source:src/workbench/widget-frame/useWidgetSortable.ts", + "source:src/workbench/widget-frame/widgetRenderInstance.ts", + "source:src/workbench/widgetDnd.ts", + "source:src/workbench/widgetImplementationResource.ts", + "source:src/workbench/widgetLabels.ts", + "source:src/workbench/widgetPlacementCommands.ts", + "source:src/workbench/widgetPlacementMeta.ts", + "source:src/workbench/widgetRegionViewModel.ts", + "source:src/workbench/widgetRegistry.ts", + "source:src/workbench/widgetState.ts", + "source:src/workbench/widgets/autosave-status/AutosaveStatusWidgetView.tsx", + "source:src/workbench/widgets/autosave-status/implementation.ts", + "source:src/workbench/widgets/autosave-status/manifest.ts", + "source:src/workbench/widgets/canvas/CanvasBottomOverlay.tsx", + "source:src/workbench/widgets/canvas/CanvasCreateFromBboxSubmenu.tsx", + "source:src/workbench/widgets/canvas/CanvasEditRefusalNotices.tsx", + "source:src/workbench/widgets/canvas/CanvasFloatingBar.tsx", + "source:src/workbench/widgets/canvas/CanvasGlobalContextMenu.tsx", + "source:src/workbench/widgets/canvas/CanvasHeaderActions.tsx", + "source:src/workbench/widgets/canvas/CanvasImageDropOverlay.tsx", + "source:src/workbench/widgets/canvas/CanvasImageDropZone.tsx", + "source:src/workbench/widgets/canvas/CanvasSaveToGallerySubmenu.tsx", + "source:src/workbench/widgets/canvas/CanvasSurface.tsx", + "source:src/workbench/widgets/canvas/CanvasSurfaceContextLayout.tsx", + "source:src/workbench/widgets/canvas/CanvasWidgetView.tsx", + "source:src/workbench/widgets/canvas/DenoisingStrengthWave.tsx", + "source:src/workbench/widgets/canvas/GenerateCanvasCompositingSection.tsx", + "source:src/workbench/widgets/canvas/GenerateCanvasScalingSection.tsx", + "source:src/workbench/widgets/canvas/GenerateCanvasSections.tsx", + "source:src/workbench/widgets/canvas/GenerateDenoisingStrength.tsx", + "source:src/workbench/widgets/canvas/StagingBar.tsx", + "source:src/workbench/widgets/canvas/StagingItemContextMenu.tsx", + "source:src/workbench/widgets/canvas/TextEditPortal.tsx", + "source:src/workbench/widgets/canvas/ToolStrip.tsx", + "source:src/workbench/widgets/canvas/ToolStripFlyout.tsx", + "source:src/workbench/widgets/canvas/bboxGrid.ts", + "source:src/workbench/widgets/canvas/canvasClipboard.ts", + "source:src/workbench/widgets/canvas/canvasContextMenu.ts", + "source:src/workbench/widgets/canvas/canvasGallerySaveState.ts", + "source:src/workbench/widgets/canvas/canvasHeaderCommands.ts", + "source:src/workbench/widgets/canvas/canvasHotkeyCommands.ts", + "source:src/workbench/widgets/canvas/canvasImageDnd.ts", + "source:src/workbench/widgets/canvas/canvasInteractionLock.ts", + "source:src/workbench/widgets/canvas/canvasSettings.ts", + "source:src/workbench/widgets/canvas/checkerColors.ts", + "source:src/workbench/widgets/canvas/color-system/CanvasColorFeed.tsx", + "source:src/workbench/widgets/canvas/color-system/colorPair.ts", + "source:src/workbench/widgets/canvas/color-system/maskTintTarget.ts", + "source:src/workbench/widgets/canvas/color-system/useActiveColors.ts", + "source:src/workbench/widgets/canvas/color-system/useMaskTintEditor.ts", + "source:src/workbench/widgets/canvas/engineStoreHooks.ts", + "source:src/workbench/widgets/canvas/executeCanvasImageDropImport.ts", + "source:src/workbench/widgets/canvas/fitBbox.ts", + "source:src/workbench/widgets/canvas/implementation.ts", + "source:src/workbench/widgets/canvas/invoke/canvasCompositing.ts", + "source:src/workbench/widgets/canvas/invoke/canvasScaling.ts", + "source:src/workbench/widgets/canvas/invoke/canvasStrength.ts", + "source:src/workbench/widgets/canvas/invoke/prepareCanvasInvocation.ts", + "source:src/workbench/widgets/canvas/manifest.ts", + "source:src/workbench/widgets/canvas/settingsContribution.ts", + "source:src/workbench/widgets/canvas/stagingPreview.ts", + "source:src/workbench/widgets/canvas/surfaceFocus.ts", + "source:src/workbench/widgets/canvas/textFontStyle.tsx", + "source:src/workbench/widgets/canvas/tool-options/BrushOptions.tsx", + "source:src/workbench/widgets/canvas/tool-options/CanvasOptionsBar.tsx", + "source:src/workbench/widgets/canvas/tool-options/FilterOptions.tsx", + "source:src/workbench/widgets/canvas/tool-options/GradientOptions.tsx", + "source:src/workbench/widgets/canvas/tool-options/OperationStatusSlot.tsx", + "source:src/workbench/widgets/canvas/tool-options/SamOptions.tsx", + "source:src/workbench/widgets/canvas/tool-options/SelectionOptionsRow.tsx", + "source:src/workbench/widgets/canvas/tool-options/ShapeOptions.tsx", + "source:src/workbench/widgets/canvas/tool-options/geometryForm.tsx", + "source:src/workbench/widgets/canvas/tool-options/gradientStops.ts", + "source:src/workbench/widgets/canvas/tool-options/hintForms.tsx", + "source:src/workbench/widgets/canvas/tool-options/paintForm.tsx", + "source:src/workbench/widgets/canvas/tool-options/selectionForm.tsx", + "source:src/workbench/widgets/canvas/tool-options/textForm.tsx", + "source:src/workbench/widgets/canvas/tool-options/useBboxEditor.ts", + "source:src/workbench/widgets/canvas/tool-presentation/FormControls.tsx", + "source:src/workbench/widgets/canvas/tool-presentation/PropertyPrimitives.tsx", + "source:src/workbench/widgets/canvas/tool-presentation/propertyGroupStore.ts", + "source:src/workbench/widgets/canvas/tool-presentation/toolAdapters.ts", + "source:src/workbench/widgets/canvas/toolFamilyStore.ts", + "source:src/workbench/widgets/canvas/useCanvasGallerySave.ts", + "source:src/workbench/widgets/canvas/useColorSampler.ts", + "source:src/workbench/widgets/canvas/useCreateFromBbox.ts", + "source:src/workbench/widgets/canvas/useStructuralCommit.ts", + "source:src/workbench/widgets/canvas/zoomOptions.ts", + "source:src/workbench/widgets/diagnostics/manifest.ts", + "source:src/workbench/widgets/gallery/manifest.ts", + "source:src/workbench/widgets/generate/manifest.ts", + "source:src/workbench/widgets/image-map/ImageMapDataRuntime.tsx", + "source:src/workbench/widgets/image-map/manifest.ts", + "source:src/workbench/widgets/image-map/settingsContribution.ts", + "source:src/workbench/widgets/layers/AdjustmentSettings.tsx", + "source:src/workbench/widgets/layers/ControlLayerSettings.tsx", + "source:src/workbench/widgets/layers/ControlLayerWarningIcon.tsx", + "source:src/workbench/widgets/layers/InpaintMaskSettings.tsx", + "source:src/workbench/widgets/layers/LayerActiveDot.tsx", + "source:src/workbench/widgets/layers/LayerBlendRow.tsx", + "source:src/workbench/widgets/layers/LayerChildMenu.tsx", + "source:src/workbench/widgets/layers/LayerChildRow.tsx", + "source:src/workbench/widgets/layers/LayerContextMenu.tsx", + "source:src/workbench/widgets/layers/LayerFilterControls.tsx", + "source:src/workbench/widgets/layers/LayerFilterOperationButton.tsx", + "source:src/workbench/widgets/layers/LayerGroupContextMenu.tsx", + "source:src/workbench/widgets/layers/LayerRegionSettings.tsx", + "source:src/workbench/widgets/layers/LayerRow.tsx", + "source:src/workbench/widgets/layers/LayerRowSurface.tsx", + "source:src/workbench/widgets/layers/LayerStackHeader.tsx", + "source:src/workbench/widgets/layers/LayerStackMenu.tsx", + "source:src/workbench/widgets/layers/LayerSurfaceHost.tsx", + "source:src/workbench/widgets/layers/LayerThumbnail.tsx", + "source:src/workbench/widgets/layers/LayersHeaderActions.tsx", + "source:src/workbench/widgets/layers/LayersPanelFooter.tsx", + "source:src/workbench/widgets/layers/LayersTree.tsx", + "source:src/workbench/widgets/layers/LayersWidgetView.tsx", + "source:src/workbench/widgets/layers/MaskModifierSettings.tsx", + "source:src/workbench/widgets/layers/RasterLayerFilterSection.tsx", + "source:src/workbench/widgets/layers/ReferenceImageSettings.tsx", + "source:src/workbench/widgets/layers/RegionalGuidanceSettings.tsx", + "source:src/workbench/widgets/layers/RunLayerWorkflowDialog.tsx", + "source:src/workbench/widgets/layers/addLayerMenu.ts", + "source:src/workbench/widgets/layers/colorLabels.ts", + "source:src/workbench/widgets/layers/controlModelOptions.ts", + "source:src/workbench/widgets/layers/curveEditorMath.ts", + "source:src/workbench/widgets/layers/implementation.ts", + "source:src/workbench/widgets/layers/layerActionSession.ts", + "source:src/workbench/widgets/layers/layerChildRows.ts", + "source:src/workbench/widgets/layers/layerChildSelection.ts", + "source:src/workbench/widgets/layers/layerContextActions.ts", + "source:src/workbench/widgets/layers/layerContextMenuLayout.ts", + "source:src/workbench/widgets/layers/layerExportActions.ts", + "source:src/workbench/widgets/layers/layerGroupCommands.ts", + "source:src/workbench/widgets/layers/layerMenuState.ts", + "source:src/workbench/widgets/layers/layerOps.ts", + "source:src/workbench/widgets/layers/layerPanelDiagnostics.ts", + "source:src/workbench/widgets/layers/layerPanelRows.ts", + "source:src/workbench/widgets/layers/layerPropertiesOperation.ts", + "source:src/workbench/widgets/layers/layerPropertiesRequestStore.ts", + "source:src/workbench/widgets/layers/layerRowCommands.ts", + "source:src/workbench/widgets/layers/layerRowSummary.ts", + "source:src/workbench/widgets/layers/layerStackActions.ts", + "source:src/workbench/widgets/layers/layerTreeAutoScroll.ts", + "source:src/workbench/widgets/layers/layerTreeDrop.ts", + "source:src/workbench/widgets/layers/layerTreeRows.ts", + "source:src/workbench/widgets/layers/layerTypeIcon.ts", + "source:src/workbench/widgets/layers/manifest.ts", + "source:src/workbench/widgets/layers/panes/ColorPane.tsx", + "source:src/workbench/widgets/layers/panes/GroupSelectedNotice.tsx", + "source:src/workbench/widgets/layers/panes/HistoryPane.tsx", + "source:src/workbench/widgets/layers/panes/LayerEditorPanes.tsx", + "source:src/workbench/widgets/layers/panes/LayerSection.tsx", + "source:src/workbench/widgets/layers/panes/OverviewPane.tsx", + "source:src/workbench/widgets/layers/panes/PropertiesPane.tsx", + "source:src/workbench/widgets/layers/panes/PropertiesSection.tsx", + "source:src/workbench/widgets/layers/panes/SwatchesPane.tsx", + "source:src/workbench/widgets/layers/panes/TransformPane.tsx", + "source:src/workbench/widgets/layers/panes/editorPaneLayout.ts", + "source:src/workbench/widgets/layers/psdExportNotice.ts", + "source:src/workbench/widgets/layers/runLayerWorkflow.ts", + "source:src/workbench/widgets/layers/selectedModel.ts", + "source:src/workbench/widgets/layers/useAddLayer.ts", + "source:src/workbench/widgets/layers/useCanvasHistoryHotkeys.ts", + "source:src/workbench/widgets/layers/useLayerSelectionCommands.ts", + "source:src/workbench/widgets/layers/useLayerStackActions.ts", + "source:src/workbench/widgets/layers/useSelectedMainModel.ts", + "source:src/workbench/widgets/layers/useSelectedModelBase.ts", + "source:src/workbench/widgets/manifests.ts", + "source:src/workbench/widgets/notifications/NotificationsHeaderActions.tsx", + "source:src/workbench/widgets/notifications/NotificationsWidgetView.tsx", + "source:src/workbench/widgets/notifications/implementation.ts", + "source:src/workbench/widgets/notifications/manifest.ts", + "source:src/workbench/widgets/preview/PreviewActionStrip.tsx", + "source:src/workbench/widgets/preview/PreviewCompare.tsx", + "source:src/workbench/widgets/preview/PreviewCompareDropZone.tsx", + "source:src/workbench/widgets/preview/PreviewDetailsPopover.tsx", + "source:src/workbench/widgets/preview/PreviewFilmstrip.tsx", + "source:src/workbench/widgets/preview/PreviewFrame.tsx", + "source:src/workbench/widgets/preview/PreviewHeaderActions.tsx", + "source:src/workbench/widgets/preview/PreviewMetadataPanel.tsx", + "source:src/workbench/widgets/preview/PreviewStage.tsx", + "source:src/workbench/widgets/preview/PreviewSwipeNeighbor.tsx", + "source:src/workbench/widgets/preview/PreviewWidgetChrome.tsx", + "source:src/workbench/widgets/preview/PreviewWidgetView.tsx", + "source:src/workbench/widgets/preview/PreviewZoomMenu.tsx", + "source:src/workbench/widgets/preview/implementation.ts", + "source:src/workbench/widgets/preview/livePreviewFollow.tsx", + "source:src/workbench/widgets/preview/loupeGestures.ts", + "source:src/workbench/widgets/preview/manifest.ts", + "source:src/workbench/widgets/preview/previewCompareDnd.ts", + "source:src/workbench/widgets/preview/previewDensity.ts", + "source:src/workbench/widgets/preview/previewHeaderStore.ts", + "source:src/workbench/widgets/preview/previewMetadata.ts", + "source:src/workbench/widgets/preview/previewSettings.ts", + "source:src/workbench/widgets/preview/previewSwipe.ts", + "source:src/workbench/widgets/preview/settingsContribution.ts", + "source:src/workbench/widgets/preview/spanPlaybackRequest.ts", + "source:src/workbench/widgets/preview/useCompareLoupe.ts", + "source:src/workbench/widgets/preview/usePreviewLoupe.ts", + "source:src/workbench/widgets/preview/usePreviewNavigation.ts", + "source:src/workbench/widgets/preview/usePreviewSwipe.ts", + "source:src/workbench/widgets/project/manifest.ts", + "source:src/workbench/widgets/queue-status/QueueStatusWidgetView.tsx", + "source:src/workbench/widgets/queue-status/implementation.ts", + "source:src/workbench/widgets/queue-status/manifest.ts", + "source:src/workbench/widgets/queue-status/queueStatusModel.ts", + "source:src/workbench/widgets/queue/manifest.ts", + "source:src/workbench/widgets/server-status/ServerStatusWidgetView.tsx", + "source:src/workbench/widgets/server-status/implementation.ts", + "source:src/workbench/widgets/server-status/manifest.ts", + "source:src/workbench/widgets/upscale/manifest.ts", + "source:src/workbench/widgets/video/manifest.ts", + "source:src/workbench/widgets/workflow/manifest.ts", + "source:src/workbench/workbenchState.ts", + "source:src/workbench/workbenchStore.ts" + ], + "editor-workflow-static": [ + "package:@ark-ui/react", + "package:@chakra-ui/react", + "package:@dnd-kit/accessibility", + "package:@dnd-kit/core", + "package:@dnd-kit/modifiers", + "package:@dnd-kit/sortable", + "package:@dnd-kit/utilities", + "package:@emotion/cache", + "package:@emotion/hash", + "package:@emotion/is-prop-valid", + "package:@emotion/memoize", + "package:@emotion/react", + "package:@emotion/serialize", + "package:@emotion/sheet", + "package:@emotion/unitless", + "package:@emotion/use-insertion-effect-with-fallbacks", + "package:@emotion/utils", + "package:@floating-ui/core", + "package:@floating-ui/dom", + "package:@floating-ui/utils", + "package:@fontsource/inter", + "package:@internationalized/number", + "package:@pandacss/is-valid-prop", + "package:@socket.io/component-emitter", + "package:@tanstack/history", + "package:@tanstack/query-core", + "package:@tanstack/react-query", + "package:@tanstack/react-router", + "package:@tanstack/react-store", + "package:@tanstack/react-virtual", + "package:@tanstack/router-core", + "package:@tanstack/store", + "package:@tanstack/virtual-core", + "package:@xyflow/react", + "package:@xyflow/system", + "package:@zag-js/accordion", + "package:@zag-js/anatomy", + "package:@zag-js/aria-hidden", + "package:@zag-js/auto-resize", + "package:@zag-js/avatar", + "package:@zag-js/carousel", + "package:@zag-js/checkbox", + "package:@zag-js/clipboard", + "package:@zag-js/collapsible", + "package:@zag-js/collection", + "package:@zag-js/color-picker", + "package:@zag-js/color-utils", + "package:@zag-js/combobox", + "package:@zag-js/core", + "package:@zag-js/date-input", + "package:@zag-js/date-picker", + "package:@zag-js/dialog", + "package:@zag-js/dismissable", + "package:@zag-js/dom-query", + "package:@zag-js/editable", + "package:@zag-js/file-upload", + "package:@zag-js/floating-panel", + "package:@zag-js/focus-trap", + "package:@zag-js/focus-visible", + "package:@zag-js/hover-card", + "package:@zag-js/interact-outside", + "package:@zag-js/listbox", + "package:@zag-js/live-region", + "package:@zag-js/marquee", + "package:@zag-js/menu", + "package:@zag-js/number-input", + "package:@zag-js/pagination", + "package:@zag-js/pin-input", + "package:@zag-js/popover", + "package:@zag-js/popper", + "package:@zag-js/presence", + "package:@zag-js/progress", + "package:@zag-js/qr-code", + "package:@zag-js/radio-group", + "package:@zag-js/rating-group", + "package:@zag-js/react", + "package:@zag-js/rect-utils", + "package:@zag-js/remove-scroll", + "package:@zag-js/scroll-area", + "package:@zag-js/select", + "package:@zag-js/slider", + "package:@zag-js/splitter", + "package:@zag-js/switch", + "package:@zag-js/tabs", + "package:@zag-js/tags-input", + "package:@zag-js/toast", + "package:@zag-js/tooltip", + "package:@zag-js/tree-view", + "package:@zag-js/types", + "package:@zag-js/utils", + "package:classcat", + "package:d3-color", + "package:d3-dispatch", + "package:d3-drag", + "package:d3-ease", + "package:d3-interpolate", + "package:d3-selection", + "package:d3-timer", + "package:d3-transition", + "package:d3-zoom", + "package:engine.io-client", + "package:engine.io-parser", + "package:fuzzysort", + "package:hoist-non-react-statics", + "package:i18next", + "package:i18next-http-backend", + "package:idb", + "package:lucide-react", + "package:perfect-freehand", + "package:react", + "package:react-dom", + "package:react-hook-tanstack-virtual", + "package:react-i18next", + "package:react-is", + "package:scheduler", + "package:socket.io-client", + "package:socket.io-parser", + "package:stylis", + "package:tinykeys", + "package:use-sync-external-store", + "package:zod", + "package:zustand", + "source:index.html", + "source:src/app/App.tsx", + "source:src/app/AppProviders.tsx", + "source:src/app/BootWidgetHintController.tsx", + "source:src/app/FeatureHintsProvider.tsx", + "source:src/app/GalleryImageActionsBridge.tsx", + "source:src/app/GalleryRealtimeRuntime.tsx", + "source:src/app/GalleryUiAdapter.tsx", + "source:src/app/GenerateWidgetSyncRuntime.tsx", + "source:src/app/GenerationUiAdapter.tsx", + "source:src/app/I18nController.tsx", + "source:src/app/ModelsUiAdapter.tsx", + "source:src/app/QueueRuntimeAdapter.tsx", + "source:src/app/QueueUiAdapter.tsx", + "source:src/app/RecallParametersRuntime.tsx", + "source:src/app/SocketHubRuntime.tsx", + "source:src/app/ThemeController.tsx", + "source:src/app/UpscaleUiAdapter.tsx", + "source:src/app/VideoUiAdapter.tsx", + "source:src/app/WorkbenchApp.tsx", + "source:src/app/WorkflowUiAdapter.tsx", + "source:src/app/accountLifecycle.ts", + "source:src/app/loggingRuntime.ts", + "source:src/app/main.tsx", + "source:src/app/router.tsx", + "source:src/app/workbenchPorts.tsx", + "source:src/assets/SplashImage.webp", + "source:src/features/fonts/data/keys.ts", + "source:src/features/fonts/launchpad.tsx", + "source:src/features/fonts/react.tsx", + "source:src/features/fonts/runtime.ts", + "source:src/features/gallery/core/boardLabels.ts", + "source:src/features/gallery/core/image.ts", + "source:src/features/gallery/core/imagePaths.ts", + "source:src/features/gallery/core/items.ts", + "source:src/features/gallery/core/recentImages.ts", + "source:src/features/gallery/core/selection.ts", + "source:src/features/gallery/core/semanticImageQuery.ts", + "source:src/features/gallery/core/settings.ts", + "source:src/features/gallery/core/videoPaths.ts", + "source:src/features/gallery/data/backend.ts", + "source:src/features/gallery/data/imageUrls.ts", + "source:src/features/gallery/data/queries.ts", + "source:src/features/gallery/data/queryCache.ts", + "source:src/features/gallery/data/realtimeRuntime.ts", + "source:src/features/gallery/data/videoUrls.ts", + "source:src/features/gallery/index.ts", + "source:src/features/gallery/mediaSlot.ts", + "source:src/features/gallery/picker.ts", + "source:src/features/gallery/queries.ts", + "source:src/features/gallery/react.ts", + "source:src/features/gallery/settingsContribution.ts", + "source:src/features/gallery/ui/FindInGalleryButton.tsx", + "source:src/features/gallery/ui/GalleryBoardCover.tsx", + "source:src/features/gallery/ui/GalleryBoardDragMonitor.tsx", + "source:src/features/gallery/ui/GalleryBoardFilters.tsx", + "source:src/features/gallery/ui/GalleryBoardMenu.tsx", + "source:src/features/gallery/ui/GalleryBoardRow.tsx", + "source:src/features/gallery/ui/GalleryBoardRowShell.tsx", + "source:src/features/gallery/ui/GalleryBoardSection.tsx", + "source:src/features/gallery/ui/GalleryBoardsPanel.tsx", + "source:src/features/gallery/ui/GalleryDragCursor.tsx", + "source:src/features/gallery/ui/GalleryImageGrid.tsx", + "source:src/features/gallery/ui/GalleryItemSearch.tsx", + "source:src/features/gallery/ui/GalleryItemSortMenu.tsx", + "source:src/features/gallery/ui/GalleryLayout.tsx", + "source:src/features/gallery/ui/GalleryProgressSection.tsx", + "source:src/features/gallery/ui/GallerySearchField.tsx", + "source:src/features/gallery/ui/GallerySearchHelp.tsx", + "source:src/features/gallery/ui/GallerySelectionBar.tsx", + "source:src/features/gallery/ui/GalleryStackedLayout.tsx", + "source:src/features/gallery/ui/GalleryStarredFilterToggle.tsx", + "source:src/features/gallery/ui/GalleryThumbnail.tsx", + "source:src/features/gallery/ui/GalleryTileFrame.tsx", + "source:src/features/gallery/ui/GalleryUiContext.tsx", + "source:src/features/gallery/ui/GalleryUploadButton.tsx", + "source:src/features/gallery/ui/GalleryViewTabs.tsx", + "source:src/features/gallery/ui/GalleryWideLayout.tsx", + "source:src/features/gallery/ui/GalleryWidgetChrome.tsx", + "source:src/features/gallery/ui/GalleryWidgetContext.tsx", + "source:src/features/gallery/ui/GalleryWidgetFooter.tsx", + "source:src/features/gallery/ui/GalleryWidgetView.tsx", + "source:src/features/gallery/ui/galleryBoardGroups.ts", + "source:src/features/gallery/ui/galleryBoardLabels.ts", + "source:src/features/gallery/ui/galleryDensity.ts", + "source:src/features/gallery/ui/galleryDnd.ts", + "source:src/features/gallery/ui/galleryGridLayout.ts", + "source:src/features/gallery/ui/galleryMenuIds.ts", + "source:src/features/gallery/ui/galleryStateView.ts", + "source:src/features/gallery/ui/picker/GalleryMediaSlot.tsx", + "source:src/features/gallery/ui/picker/GalleryPickerPopover.tsx", + "source:src/features/gallery/ui/useGalleryActions.ts", + "source:src/features/gallery/ui/useGalleryData.ts", + "source:src/features/gallery/ui/useGalleryGridHotkeys.ts", + "source:src/features/gallery/ui/useGalleryGridSelection.ts", + "source:src/features/gallery/ui/useGalleryStarredStrip.ts", + "source:src/features/gallery/ui/useGalleryUploadAction.ts", + "source:src/features/gallery/ui/useGalleryUploadInput.ts", + "source:src/features/gallery/utility.ts", + "source:src/features/gallery/widget.ts", + "source:src/features/generation/core/architectureCapabilities.ts", + "source:src/features/generation/core/baseGenerationPolicies.ts", + "source:src/features/generation/core/batch.ts", + "source:src/features/generation/core/canvas/addControlLayers.ts", + "source:src/features/generation/core/canvas/addRegionalGuidance.ts", + "source:src/features/generation/core/canvas/controlValidation.ts", + "source:src/features/generation/core/catalogSearch.ts", + "source:src/features/generation/core/componentCompatibility.ts", + "source:src/features/generation/core/conditioningRebalance.ts", + "source:src/features/generation/core/dynamicPrompts.ts", + "source:src/features/generation/core/graph.ts", + "source:src/features/generation/core/graphBuilder.ts", + "source:src/features/generation/core/krea2Conditioning.ts", + "source:src/features/generation/core/pid.ts", + "source:src/features/generation/core/pidGraph.ts", + "source:src/features/generation/core/projectPromptDraft.ts", + "source:src/features/generation/core/prompt/ast.ts", + "source:src/features/generation/core/prompt/attention.ts", + "source:src/features/generation/core/prompt/dynamicPromptSyntax.ts", + "source:src/features/generation/core/prompt/highlight.ts", + "source:src/features/generation/core/promptHistory.ts", + "source:src/features/generation/core/promptTemplateOwnership.ts", + "source:src/features/generation/core/promptTemplates.ts", + "source:src/features/generation/core/referenceImage.ts", + "source:src/features/generation/core/resolveGenerateWidgetValues.ts", + "source:src/features/generation/core/settings.ts", + "source:src/features/generation/core/supportedBases.ts", + "source:src/features/generation/core/systemPrompts.ts", + "source:src/features/generation/core/wildcardCatalog.ts", + "source:src/features/generation/core/wildcardTransfer.ts", + "source:src/features/generation/data/architectureCapabilitiesApi.ts", + "source:src/features/generation/data/architectureCapabilitiesStore.ts", + "source:src/features/generation/data/dynamicPromptsQueries.ts", + "source:src/features/generation/data/llmTaskProgress.ts", + "source:src/features/generation/data/promptTemplates.ts", + "source:src/features/generation/data/promptUtilities.ts", + "source:src/features/generation/data/referenceImageUrls.ts", + "source:src/features/generation/data/systemPrompts.ts", + "source:src/features/generation/data/wildcards.ts", + "source:src/features/generation/graph.ts", + "source:src/features/generation/prompts.ts", + "source:src/features/generation/queries.ts", + "source:src/features/generation/react.ts", + "source:src/features/generation/runtime.ts", + "source:src/features/generation/settings.ts", + "source:src/features/generation/ui/GenerateAdvancedFields.tsx", + "source:src/features/generation/ui/GenerateCanvasSections.tsx", + "source:src/features/generation/ui/GenerateComponentsSection.tsx", + "source:src/features/generation/ui/GenerateConceptsSection.tsx", + "source:src/features/generation/ui/GenerateConditioningRebalanceField.tsx", + "source:src/features/generation/ui/GenerateDimensionFields.tsx", + "source:src/features/generation/ui/GenerateGuidanceSection.tsx", + "source:src/features/generation/ui/GenerateHeaderActions.tsx", + "source:src/features/generation/ui/GenerateModelCard.tsx", + "source:src/features/generation/ui/GeneratePresetsPopover.tsx", + "source:src/features/generation/ui/GenerateRenderSection.tsx", + "source:src/features/generation/ui/GenerateSettingsForm.tsx", + "source:src/features/generation/ui/GenerateWidgetView.tsx", + "source:src/features/generation/ui/GenerationUiContext.tsx", + "source:src/features/generation/ui/LLMTaskProgressRuntime.tsx", + "source:src/features/generation/ui/generateDebounce.ts", + "source:src/features/generation/ui/generateDraftRegistry.ts", + "source:src/features/generation/ui/generateFormViewModel.ts", + "source:src/features/generation/ui/modelSelectionNotice.ts", + "source:src/features/generation/ui/promptFields/DynamicPromptsButton.tsx", + "source:src/features/generation/ui/promptFields/DynamicPromptsPanel.tsx", + "source:src/features/generation/ui/promptFields/GeneratePromptFields.tsx", + "source:src/features/generation/ui/promptFields/LLMTaskProgressDisplay.tsx", + "source:src/features/generation/ui/promptFields/NegativePromptField.tsx", + "source:src/features/generation/ui/promptFields/PositivePromptActions.tsx", + "source:src/features/generation/ui/promptFields/PositivePromptField.tsx", + "source:src/features/generation/ui/promptFields/PromptHighlight.tsx", + "source:src/features/generation/ui/promptFields/PromptLineNumbers.tsx", + "source:src/features/generation/ui/promptFields/PromptPanelHeader.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplateEditor.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplateImage.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplatesButton.tsx", + "source:src/features/generation/ui/promptFields/PromptTemplatesPanel.tsx", + "source:src/features/generation/ui/promptFields/PromptTextarea.tsx", + "source:src/features/generation/ui/promptFields/PromptTriggerAutocomplete.tsx", + "source:src/features/generation/ui/promptFields/SystemPromptsField.tsx", + "source:src/features/generation/ui/promptFields/WildcardImportDialog.tsx", + "source:src/features/generation/ui/promptFields/WildcardTransferActions.tsx", + "source:src/features/generation/ui/promptFields/WildcardsPanel.tsx", + "source:src/features/generation/ui/promptFields/index.ts", + "source:src/features/generation/ui/promptFields/promptAttentionHotkeys.ts", + "source:src/features/generation/ui/promptFields/promptCaret.ts", + "source:src/features/generation/ui/promptFields/promptFocus.ts", + "source:src/features/generation/ui/promptFields/promptHistoryNavigation.ts", + "source:src/features/generation/ui/promptFields/promptTriggerOptions.ts", + "source:src/features/generation/ui/promptFields/useDismissOnViewportChange.ts", + "source:src/features/generation/ui/promptFields/usePromptImageDrop.ts", + "source:src/features/generation/ui/promptFields/usePromptTriggerAutocomplete.tsx", + "source:src/features/generation/ui/promptFields/usePromptTriggerPicker.ts", + "source:src/features/generation/ui/promptFields/useSystemPrompts.ts", + "source:src/features/generation/ui/promptTemplateDraftStore.ts", + "source:src/features/generation/ui/reference-images/GenerateReferenceImagesSection.tsx", + "source:src/features/generation/ui/reference-images/ReferenceImageCard.tsx", + "source:src/features/generation/ui/reference-images/ReferenceImageControls.tsx", + "source:src/features/generation/ui/reference-images/ReferenceImageCropDialog.tsx", + "source:src/features/generation/ui/reference-images/referenceImageConfig.ts", + "source:src/features/generation/ui/shared/AspectRatioPreview.tsx", + "source:src/features/generation/ui/shared/AspectRatioSelect.tsx", + "source:src/features/generation/ui/shared/ConditioningRebalanceBars.tsx", + "source:src/features/generation/ui/shared/ConditioningRebalanceSparkline.tsx", + "source:src/features/generation/ui/shared/GenerateCollapsibleSection.tsx", + "source:src/features/generation/ui/shared/GenerateFieldContextMenu.tsx", + "source:src/features/generation/ui/shared/GenerateToggleSwitch.tsx", + "source:src/features/generation/ui/shared/SeedField.tsx", + "source:src/features/generation/ui/shared/modelDefaultSettings.ts", + "source:src/features/generation/ui/useDebouncedDraftValue.ts", + "source:src/features/generation/ui/useDynamicPrompts.ts", + "source:src/features/generation/ui/usePromptTemplates.ts", + "source:src/features/generation/ui/useWildcards.ts", + "source:src/features/generation/ui/wildcardFiles.ts", + "source:src/features/generation/widget.ts", + "source:src/features/identity/capabilities.ts", + "source:src/features/identity/core/schemas.ts", + "source:src/features/identity/core/sessionPolicy.ts", + "source:src/features/identity/core/tokenStorage.ts", + "source:src/features/identity/data/api.ts", + "source:src/features/identity/index.ts", + "source:src/features/identity/session.ts", + "source:src/features/identity/transportAdapter.ts", + "source:src/features/identity/ui/AccountMenu.tsx", + "source:src/features/identity/ui/AccountMenuSection.tsx", + "source:src/features/identity/ui/AuthScreen.tsx", + "source:src/features/identity/ui/AuthUnavailableScreen.tsx", + "source:src/features/identity/ui/LoginScreen.tsx", + "source:src/features/identity/ui/PasswordInput.tsx", + "source:src/features/identity/ui/ProfileDialog.tsx", + "source:src/features/identity/ui/SetupScreen.tsx", + "source:src/features/identity/ui/UserFormDialog.tsx", + "source:src/features/identity/ui/UserManagement.tsx", + "source:src/features/identity/ui/UsersPage.tsx", + "source:src/features/identity/ui/useIdentityNotify.ts", + "source:src/features/intermediates/data/focus.ts", + "source:src/features/intermediates/holdLease.ts", + "source:src/features/intermediates/index.ts", + "source:src/features/intermediates/settingsContribution.ts", + "source:src/features/models/core/baseIdentity.ts", + "source:src/features/models/core/library.ts", + "source:src/features/models/core/taxonomy.ts", + "source:src/features/models/data/api.ts", + "source:src/features/models/data/apiKeys.ts", + "source:src/features/models/data/installsStore.ts", + "source:src/features/models/data/modelLoadStore.ts", + "source:src/features/models/data/modelsStore.ts", + "source:src/features/models/data/startersStore.ts", + "source:src/features/models/data/transport.ts", + "source:src/features/models/index.ts", + "source:src/features/models/react.ts", + "source:src/features/models/ui/ModelInstallRuntime.tsx", + "source:src/features/models/ui/ModelsPage.tsx", + "source:src/features/models/ui/ModelsUiContext.tsx", + "source:src/features/models/ui/add-models/starterModelInstallSources.ts", + "source:src/features/models/ui/add-models/useInstallActions.ts", + "source:src/features/models/ui/components/ModelSelect.tsx", + "source:src/features/models/ui/uiStore.ts", + "source:src/features/models/ui/useInstallOutcomeToasts.ts", + "source:src/features/models/ui/useModelsNotify.ts", + "source:src/features/nodes/core/catalog.ts", + "source:src/features/nodes/data/api.ts", + "source:src/features/nodes/data/nodeExecutionStore.ts", + "source:src/features/nodes/data/nodesStore.ts", + "source:src/features/nodes/data/transport.ts", + "source:src/features/nodes/index.ts", + "source:src/features/nodes/ui/NodesPage.tsx", + "source:src/features/queue/contracts.ts", + "source:src/features/queue/core/activeSessions.ts", + "source:src/features/queue/core/deviceLabels.ts", + "source:src/features/queue/core/generationMeta.ts", + "source:src/features/queue/core/graphInputMedia.ts", + "source:src/features/queue/core/historySnapshot.ts", + "source:src/features/queue/core/historySummary.ts", + "source:src/features/queue/core/progressImage.ts", + "source:src/features/queue/core/progressRail.ts", + "source:src/features/queue/core/promptBatch.ts", + "source:src/features/queue/core/submissionRules.ts", + "source:src/features/queue/core/types.ts", + "source:src/features/queue/data/activeProgressTargetStore.ts", + "source:src/features/queue/data/events.ts", + "source:src/features/queue/data/generationDevicesStore.ts", + "source:src/features/queue/data/httpRealtimeQueueBackend.ts", + "source:src/features/queue/data/itemProgressStore.ts", + "source:src/features/queue/data/mappers.ts", + "source:src/features/queue/data/progressImageStore.ts", + "source:src/features/queue/data/progressStore.ts", + "source:src/features/queue/data/queries.ts", + "source:src/features/queue/data/realtimeRuntime.ts", + "source:src/features/queue/data/serverApi.ts", + "source:src/features/queue/data/submissionApi.ts", + "source:src/features/queue/devices.ts", + "source:src/features/queue/index.ts", + "source:src/features/queue/menu.ts", + "source:src/features/queue/publicApi.ts", + "source:src/features/queue/react.ts", + "source:src/features/queue/reveal.ts", + "source:src/features/queue/runtime.ts", + "source:src/features/queue/runtime/coordinator.ts", + "source:src/features/queue/runtime/receiptAcknowledgements.ts", + "source:src/features/queue/runtime/utilityGraph.ts", + "source:src/features/queue/ui/CancelQueueItemButton.tsx", + "source:src/features/queue/ui/ModelCacheFooter.tsx", + "source:src/features/queue/ui/NowNextSection.tsx", + "source:src/features/queue/ui/QueueDataRuntime.tsx", + "source:src/features/queue/ui/QueueFilterTabs.tsx", + "source:src/features/queue/ui/QueueHeaderActions.tsx", + "source:src/features/queue/ui/QueueHeaderLabel.tsx", + "source:src/features/queue/ui/QueueHeaderMenu.tsx", + "source:src/features/queue/ui/QueueItemDetails.tsx", + "source:src/features/queue/ui/QueueItemRow.tsx", + "source:src/features/queue/ui/QueueItemThumbnail.tsx", + "source:src/features/queue/ui/QueueStats.tsx", + "source:src/features/queue/ui/QueueStatusDot.tsx", + "source:src/features/queue/ui/QueueStepProgress.tsx", + "source:src/features/queue/ui/QueueUiContext.tsx", + "source:src/features/queue/ui/QueueWidgetView.tsx", + "source:src/features/queue/ui/RecentSection.tsx", + "source:src/features/queue/ui/currentBatchItems.ts", + "source:src/features/queue/ui/formatDuration.ts", + "source:src/features/queue/ui/index.ts", + "source:src/features/queue/ui/modelCacheStore.ts", + "source:src/features/queue/ui/queueConfirmationStore.ts", + "source:src/features/queue/ui/queueDataStore.ts", + "source:src/features/queue/ui/queueFilters.ts", + "source:src/features/queue/ui/queueMenuActions.tsx", + "source:src/features/queue/ui/queueOwnership.ts", + "source:src/features/queue/ui/queueProgressCleanup.ts", + "source:src/features/queue/ui/queueScope.ts", + "source:src/features/queue/ui/queueUiStore.ts", + "source:src/features/queue/ui/queueViewModel.ts", + "source:src/features/queue/ui/statusMeta.ts", + "source:src/features/queue/ui/useDeviceLabel.ts", + "source:src/features/queue/utility.ts", + "source:src/features/queue/widget.ts", + "source:src/features/upscale/core/graph.ts", + "source:src/features/upscale/core/settings.ts", + "source:src/features/upscale/index.ts", + "source:src/features/upscale/ui/UpscaleUiContext.tsx", + "source:src/features/video/core/dimensions.ts", + "source:src/features/video/core/graph.ts", + "source:src/features/video/core/settings.ts", + "source:src/features/video/core/videoPolicies.ts", + "source:src/features/video/core/widgetValues.ts", + "source:src/features/video/index.ts", + "source:src/features/video/ui/VideoUiContext.tsx", + "source:src/features/workflow/core/batch.ts", + "source:src/features/workflow/core/buildGraph.ts", + "source:src/features/workflow/core/callSavedWorkflow.ts", + "source:src/features/workflow/core/connectors.ts", + "source:src/features/workflow/core/document.ts", + "source:src/features/workflow/core/fields.ts", + "source:src/features/workflow/core/forLoops.ts", + "source:src/features/workflow/core/graphIndex.ts", + "source:src/features/workflow/core/graphLayout.ts", + "source:src/features/workflow/core/graphToDocument.ts", + "source:src/features/workflow/core/libraryTags.ts", + "source:src/features/workflow/core/modelRequirements.ts", + "source:src/features/workflow/core/outputFields.ts", + "source:src/features/workflow/core/types.ts", + "source:src/features/workflow/core/validation.ts", + "source:src/features/workflow/core/workflowJson.ts", + "source:src/features/workflow/data/api.ts", + "source:src/features/workflow/data/libraryBrowseStore.ts", + "source:src/features/workflow/data/libraryCache.ts", + "source:src/features/workflow/data/publication.ts", + "source:src/features/workflow/data/savedWorkflowFieldUtils.ts", + "source:src/features/workflow/data/savedWorkflowQueries.ts", + "source:src/features/workflow/data/templates.ts", + "source:src/features/workflow/queries.ts", + "source:src/features/workflow/react.ts", + "source:src/features/workflow/ui/MenuActionItem.tsx", + "source:src/features/workflow/ui/PendingLibraryWorkflowLoader.tsx", + "source:src/features/workflow/ui/WorkflowUiContext.tsx", + "source:src/features/workflow/ui/WorkflowWidgetChrome.tsx", + "source:src/features/workflow/ui/WorkflowWidgetView.tsx", + "source:src/features/workflow/ui/editor/AddNodeDialog.tsx", + "source:src/features/workflow/ui/editor/CallSavedWorkflowSyncRuntime.tsx", + "source:src/features/workflow/ui/editor/ConnectorFlowNode.tsx", + "source:src/features/workflow/ui/editor/CurrentImageFlowNode.tsx", + "source:src/features/workflow/ui/editor/EditorToolbar.tsx", + "source:src/features/workflow/ui/editor/InvocationFlowNode.tsx", + "source:src/features/workflow/ui/editor/LoopBodyBoundaryOverlay.tsx", + "source:src/features/workflow/ui/editor/NodeContextMenu.tsx", + "source:src/features/workflow/ui/editor/NotesFlowNode.tsx", + "source:src/features/workflow/ui/editor/WorkflowEdge.tsx", + "source:src/features/workflow/ui/editor/WorkflowEditorView.tsx", + "source:src/features/workflow/ui/editor/WorkflowSelectionRequestRuntime.tsx", + "source:src/features/workflow/ui/editor/clipboard.ts", + "source:src/features/workflow/ui/editor/flowAdapters.ts", + "source:src/features/workflow/ui/editor/flowInstanceStore.ts", + "source:src/features/workflow/ui/editor/handleTooltip.ts", + "source:src/features/workflow/ui/editor/performanceConstants.ts", + "source:src/features/workflow/ui/editor/selectionStore.ts", + "source:src/features/workflow/ui/editor/useEraser.tsx", + "source:src/features/workflow/ui/editor/useLasso.tsx", + "source:src/features/workflow/ui/editor/workflowViewportStore.ts", + "source:src/features/workflow/ui/fields/FieldDescriptionPopover.tsx", + "source:src/features/workflow/ui/fields/NumericInput.tsx", + "source:src/features/workflow/ui/fields/WorkflowFieldInput.tsx", + "source:src/features/workflow/ui/fields/mediaFieldDnd.ts", + "source:src/features/workflow/ui/graph-preview/FlowMiniMap.tsx", + "source:src/features/workflow/ui/graph-preview/GraphPreviewDialog.tsx", + "source:src/features/workflow/ui/graph-preview/GraphPreviewFlow.tsx", + "source:src/features/workflow/ui/graph-preview/GraphPreviewOpenAsMenu.tsx", + "source:src/features/workflow/ui/graph-preview/GraphPreviewSidePanel.tsx", + "source:src/features/workflow/ui/graph-preview/GraphPreviewSnapshot.tsx", + "source:src/features/workflow/ui/graph-preview/flowTheme.ts", + "source:src/features/workflow/ui/graph-preview/index.ts", + "source:src/features/workflow/ui/graph-preview/nodeSummaries.ts", + "source:src/features/workflow/ui/implementation.ts", + "source:src/features/workflow/ui/library/LibraryCopyChoiceDialog.tsx", + "source:src/features/workflow/ui/library/ProjectWorkflowsView.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryCard.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryDetailPanel.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryDialog.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryGrid.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryTagChips.tsx", + "source:src/features/workflow/ui/library/WorkflowLibraryThumbnail.tsx", + "source:src/features/workflow/ui/library/WorkflowPublicationHost.tsx", + "source:src/features/workflow/ui/library/WorkflowRequirementsList.tsx", + "source:src/features/workflow/ui/library/libraryPreviewSource.ts", + "source:src/features/workflow/ui/library/projectWorkflowEntries.ts", + "source:src/features/workflow/ui/library/relativeTime.ts", + "source:src/features/workflow/ui/library/useOpenLibraryWorkflow.ts", + "source:src/features/workflow/ui/library/useWorkflowPublication.ts", + "source:src/features/workflow/ui/linear/FormBuilderTab.tsx", + "source:src/features/workflow/ui/linear/LinearFormView.tsx", + "source:src/features/workflow/ui/linear/NodeFieldControl.tsx", + "source:src/features/workflow/ui/linear/NodeInspector.tsx", + "source:src/features/workflow/ui/linear/WorkflowDetailsTab.tsx", + "source:src/features/workflow/ui/linear/WorkflowJsonTab.tsx", + "source:src/features/workflow/ui/linear/WorkflowLinearPanel.tsx", + "source:src/features/workflow/ui/linear/formBuilderDnd.ts", + "source:src/features/workflow/ui/linear/formElementMeta.ts", + "source:src/features/workflow/ui/loopLinkage.ts", + "source:src/features/workflow/ui/nodeChrome.tsx", + "source:src/features/workflow/ui/pendingLibraryWorkflowLoadRuntime.ts", + "source:src/features/workflow/ui/useProjectGraphCommands.ts", + "source:src/features/workflow/ui/workflowTransfer.ts", + "source:src/features/workflow/ui/workflowUiStore.ts", + "source:src/features/workflow/utility.ts", + "source:src/features/workflow/widget.ts", + "source:src/platform/browser/downloadBlob.ts", + "source:src/platform/browser/knownBrowserIssues.ts", + "source:src/platform/browser/randomUuid.ts", + "source:src/platform/browser/sha256.ts", + "source:src/platform/browser/webLocks.ts", + "source:src/platform/core/concurrency.ts", + "source:src/platform/core/json.ts", + "source:src/platform/core/outputImages.ts", + "source:src/platform/core/seed.ts", + "source:src/platform/i18n/client.ts", + "source:src/platform/i18n/languages.ts", + "source:src/platform/logging/contracts.ts", + "source:src/platform/logging/logger.ts", + "source:src/platform/logging/normalize.ts", + "source:src/platform/performance/semanticReady.ts", + "source:src/platform/pwa/registerServiceWorker.ts", + "source:src/platform/query/client.ts", + "source:src/platform/react/composeProviders.tsx", + "source:src/platform/react/draftRegistry.ts", + "source:src/platform/react/focusIfUnclaimed.ts", + "source:src/platform/react/useModifierHeld.ts", + "source:src/platform/react/useMountEffect.ts", + "source:src/platform/react/usePreservedScrollOffset.ts", + "source:src/platform/react/useZodForm.ts", + "source:src/platform/search/dateTokens.ts", + "source:src/platform/state/accountLifecycle.ts", + "source:src/platform/state/compareAndSwapRollback.ts", + "source:src/platform/state/externalStore.ts", + "source:src/platform/state/externalStoreCore.ts", + "source:src/platform/state/projectedExternalStore.ts", + "source:src/platform/state/selectorCore.ts", + "source:src/platform/state/selectors.ts", + "source:src/platform/state/singleFlight.ts", + "source:src/platform/time/serverTimestamp.ts", + "source:src/platform/transport/connectionStore.ts", + "source:src/platform/transport/deploymentBase.ts", + "source:src/platform/transport/http.ts", + "source:src/platform/transport/socketHub.ts", + "source:src/platform/ui/Button.tsx", + "source:src/platform/ui/ColorPicker.tsx", + "source:src/platform/ui/Combobox.tsx", + "source:src/platform/ui/ConfirmDialog.tsx", + "source:src/platform/ui/DropTargetOverlay.tsx", + "source:src/platform/ui/DropZone.tsx", + "source:src/platform/ui/EmptyState.tsx", + "source:src/platform/ui/Field.tsx", + "source:src/platform/ui/Group.tsx", + "source:src/platform/ui/InputShell.tsx", + "source:src/platform/ui/InvokeMark.tsx", + "source:src/platform/ui/JsonPreview.tsx", + "source:src/platform/ui/Menu.tsx", + "source:src/platform/ui/MiddleTruncate.tsx", + "source:src/platform/ui/ModelDefaultButton.tsx", + "source:src/platform/ui/PageShell.tsx", + "source:src/platform/ui/Panel.tsx", + "source:src/platform/ui/PanelHeader.tsx", + "source:src/platform/ui/Picker.tsx", + "source:src/platform/ui/Popover.tsx", + "source:src/platform/ui/RenameDialog.tsx", + "source:src/platform/ui/ResizableTextarea.tsx", + "source:src/platform/ui/ResizeHandle.tsx", + "source:src/platform/ui/RetryBoundary.tsx", + "source:src/platform/ui/Row.tsx", + "source:src/platform/ui/Scrollable.tsx", + "source:src/platform/ui/ScrubberField.tsx", + "source:src/platform/ui/SeedInput.tsx", + "source:src/platform/ui/SegmentTabs.tsx", + "source:src/platform/ui/SegmentedControl.tsx", + "source:src/platform/ui/Select.tsx", + "source:src/platform/ui/Slider.tsx", + "source:src/platform/ui/SliderNumberField.tsx", + "source:src/platform/ui/StatusWidgetChip.tsx", + "source:src/platform/ui/Tabs.tsx", + "source:src/platform/ui/ToggleDot.tsx", + "source:src/platform/ui/Toolbar.tsx", + "source:src/platform/ui/Tooltip.tsx", + "source:src/platform/ui/VendoredIcon.tsx", + "source:src/platform/ui/color.ts", + "source:src/platform/ui/colorPickerStore.ts", + "source:src/platform/ui/colorPickerSync.ts", + "source:src/platform/ui/hints/FeatureHint.tsx", + "source:src/platform/ui/hints/hintRegistry.ts", + "source:src/platform/ui/hints/hintsContext.tsx", + "source:src/platform/ui/hints/index.ts", + "source:src/platform/ui/index.ts", + "source:src/platform/ui/list/ListDivider.tsx", + "source:src/platform/ui/list/ListItem.tsx", + "source:src/platform/ui/list/ListSectionHeader.tsx", + "source:src/platform/ui/list/ListStack.tsx", + "source:src/platform/ui/list/listLayout.ts", + "source:src/platform/ui/settings/contracts.ts", + "source:src/platform/ui/streaming-image/StreamingImageFrame.tsx", + "source:src/platform/ui/streaming-image/streamingImageSource.ts", + "source:src/platform/ui/streaming-image/useStreamingImageSource.ts", + "source:src/platform/ui/theme/applyTheme.ts", + "source:src/platform/ui/theme/recipes.ts", + "source:src/platform/ui/theme/system.ts", + "source:src/platform/ui/theme/themes.ts", + "source:src/platform/ui/toaster.tsx", + "source:src/platform/ui/useScrollAreaPhantomHeal.ts", + "source:src/platform/ui/widgetOverlayRegistry.ts", + "source:src/platform/ui/widgetOverlays.ts", + "source:src/workbench/WorkbenchContext.tsx", + "source:src/workbench/WorkbenchRuntime.tsx", + "source:src/workbench/WorkbenchSessionController.tsx", + "source:src/workbench/WorkbenchWidgetRegistryContext.tsx", + "source:src/workbench/activeInvocationSubmission.ts", + "source:src/workbench/autoRoutePolicy.ts", + "source:src/workbench/bootWidgetPreload.ts", + "source:src/workbench/canvas-engine/api.ts", + "source:src/workbench/canvas-engine/capabilities.ts", + "source:src/workbench/canvas-engine/color.ts", + "source:src/workbench/canvas-engine/contracts.ts", + "source:src/workbench/canvas-engine/controllers/booleanMergeController.ts", + "source:src/workbench/canvas-engine/controllers/controlPixelController.ts", + "source:src/workbench/canvas-engine/controllers/copyLayerController.ts", + "source:src/workbench/canvas-engine/controllers/cropLayerController.ts", + "source:src/workbench/canvas-engine/controllers/editSteps.ts", + "source:src/workbench/canvas-engine/controllers/editingController.ts", + "source:src/workbench/canvas-engine/controllers/extractMaskedAreaController.ts", + "source:src/workbench/canvas-engine/controllers/filterResultController.ts", + "source:src/workbench/canvas-engine/controllers/floatingSelectionController.ts", + "source:src/workbench/canvas-engine/controllers/generatedResultController.ts", + "source:src/workbench/canvas-engine/controllers/historyController.ts", + "source:src/workbench/canvas-engine/controllers/interactionController.ts", + "source:src/workbench/canvas-engine/controllers/layerController.ts", + "source:src/workbench/canvas-engine/controllers/layerMutationController.ts", + "source:src/workbench/canvas-engine/controllers/maskLayerController.ts", + "source:src/workbench/canvas-engine/controllers/maskResultController.ts", + "source:src/workbench/canvas-engine/controllers/mergeLayerController.ts", + "source:src/workbench/canvas-engine/controllers/mutationContext.ts", + "source:src/workbench/canvas-engine/controllers/newRasterLayerController.ts", + "source:src/workbench/canvas-engine/controllers/persistenceController.ts", + "source:src/workbench/canvas-engine/controllers/previewStateController.ts", + "source:src/workbench/canvas-engine/controllers/psdExportController.ts", + "source:src/workbench/canvas-engine/controllers/rasterController.ts", + "source:src/workbench/canvas-engine/controllers/rasterExportController.ts", + "source:src/workbench/canvas-engine/controllers/rasterMemoryBudgetController.ts", + "source:src/workbench/canvas-engine/controllers/rasterizeLayerController.ts", + "source:src/workbench/canvas-engine/controllers/renderController.ts", + "source:src/workbench/canvas-engine/controllers/selectionImageController.ts", + "source:src/workbench/canvas-engine/controllers/selectionPixelController.ts", + "source:src/workbench/canvas-engine/controllers/stagedResultController.ts", + "source:src/workbench/canvas-engine/controllers/structuralLayerController.ts", + "source:src/workbench/canvas-engine/controllers/textEditingController.ts", + "source:src/workbench/canvas-engine/controllers/thumbnailController.ts", + "source:src/workbench/canvas-engine/controllers/transformEditingController.ts", + "source:src/workbench/canvas-engine/diagnostics.ts", + "source:src/workbench/canvas-engine/document-model/documentModel.ts", + "source:src/workbench/canvas-engine/document-model/postconditions.ts", + "source:src/workbench/canvas-engine/document-model/screenComposition.ts", + "source:src/workbench/canvas-engine/document-model/semanticLeaf.ts", + "source:src/workbench/canvas-engine/document-model/semanticNode.ts", + "source:src/workbench/canvas-engine/document/bitmapStore.ts", + "source:src/workbench/canvas-engine/document/documentIndex.ts", + "source:src/workbench/canvas-engine/document/documentMirror.ts", + "source:src/workbench/canvas-engine/document/documentTree.ts", + "source:src/workbench/canvas-engine/document/insertionAnchors.ts", + "source:src/workbench/canvas-engine/document/layerChangeDecision.ts", + "source:src/workbench/canvas-engine/document/layerEligibility.ts", + "source:src/workbench/canvas-engine/document/layerFactories.ts", + "source:src/workbench/canvas-engine/document/layerStacks.ts", + "source:src/workbench/canvas-engine/document/mergeDown.ts", + "source:src/workbench/canvas-engine/document/mergeVisible.ts", + "source:src/workbench/canvas-engine/document/selectionRepair.ts", + "source:src/workbench/canvas-engine/document/sources.ts", + "source:src/workbench/canvas-engine/editGate.ts", + "source:src/workbench/canvas-engine/editing/controlPixelEdit.ts", + "source:src/workbench/canvas-engine/engine.ts", + "source:src/workbench/canvas-engine/engineStores.ts", + "source:src/workbench/canvas-engine/export/psdExport.ts", + "source:src/workbench/canvas-engine/exportRasterComposite.ts", + "source:src/workbench/canvas-engine/filterError.ts", + "source:src/workbench/canvas-engine/fontReferences.ts", + "source:src/workbench/canvas-engine/freehand.ts", + "source:src/workbench/canvas-engine/history/documentPatch.ts", + "source:src/workbench/canvas-engine/history/history.ts", + "source:src/workbench/canvas-engine/history/imagePatch.ts", + "source:src/workbench/canvas-engine/history/layerSnapshot.ts", + "source:src/workbench/canvas-engine/input/pointerPipeline.ts", + "source:src/workbench/canvas-engine/input/wheel.ts", + "source:src/workbench/canvas-engine/layerExportGuards.ts", + "source:src/workbench/canvas-engine/layerRasterizer.ts", + "source:src/workbench/canvas-engine/math/mat2d.ts", + "source:src/workbench/canvas-engine/math/rect.ts", + "source:src/workbench/canvas-engine/math/snapping.ts", + "source:src/workbench/canvas-engine/mutationContracts.ts", + "source:src/workbench/canvas-engine/pressureBands.ts", + "source:src/workbench/canvas-engine/previewPublisher.ts", + "source:src/workbench/canvas-engine/rasterSnapshotCapture.ts", + "source:src/workbench/canvas-engine/render/adjustedSurfaceCache.ts", + "source:src/workbench/canvas-engine/render/adjustments.ts", + "source:src/workbench/canvas-engine/render/alphaBounds.ts", + "source:src/workbench/canvas-engine/render/colorSample.ts", + "source:src/workbench/canvas-engine/render/compositeFrame.ts", + "source:src/workbench/canvas-engine/render/compositor.ts", + "source:src/workbench/canvas-engine/render/controlTransparency.ts", + "source:src/workbench/canvas-engine/render/decodedBitmapPool.ts", + "source:src/workbench/canvas-engine/render/derivedSurfaceCache.ts", + "source:src/workbench/canvas-engine/render/floatingSelectionFrame.ts", + "source:src/workbench/canvas-engine/render/fontLoader.ts", + "source:src/workbench/canvas-engine/render/frameDemand.ts", + "source:src/workbench/canvas-engine/render/groupCompositeScopes.ts", + "source:src/workbench/canvas-engine/render/groupSurfaceCache.ts", + "source:src/workbench/canvas-engine/render/layerCache.ts", + "source:src/workbench/canvas-engine/render/layerDisplayEffect.ts", + "source:src/workbench/canvas-engine/render/maskFill.ts", + "source:src/workbench/canvas-engine/render/overlayFrame.ts", + "source:src/workbench/canvas-engine/render/overlayRenderer.ts", + "source:src/workbench/canvas-engine/render/paintCacheTrim.ts", + "source:src/workbench/canvas-engine/render/raster.ts", + "source:src/workbench/canvas-engine/render/rasterComposite.ts", + "source:src/workbench/canvas-engine/render/rasterizers/gradientRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/imageRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/index.ts", + "source:src/workbench/canvas-engine/render/rasterizers/paintRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/shapeRasterizer.ts", + "source:src/workbench/canvas-engine/render/rasterizers/textRasterizer.ts", + "source:src/workbench/canvas-engine/render/scheduler.ts", + "source:src/workbench/canvas-engine/render/thumbnail.ts", + "source:src/workbench/canvas-engine/samCoordinates.ts", + "source:src/workbench/canvas-engine/selectObjectBridge.ts", + "source:src/workbench/canvas-engine/selection/floatingSelection.ts", + "source:src/workbench/canvas-engine/selection/marchingAnts.ts", + "source:src/workbench/canvas-engine/selection/maskOutline.ts", + "source:src/workbench/canvas-engine/selection/selectionHistory.ts", + "source:src/workbench/canvas-engine/selection/selectionOpMode.ts", + "source:src/workbench/canvas-engine/selection/selectionOps.ts", + "source:src/workbench/canvas-engine/selection/selectionPaths.ts", + "source:src/workbench/canvas-engine/selection/selectionState.ts", + "source:src/workbench/canvas-engine/stagedPreviewBlobCache.ts", + "source:src/workbench/canvas-engine/strokeCommit.ts", + "source:src/workbench/canvas-engine/tools/bboxHitTest.ts", + "source:src/workbench/canvas-engine/tools/bboxTool.ts", + "source:src/workbench/canvas-engine/tools/brushTool.ts", + "source:src/workbench/canvas-engine/tools/colorPickerTool.ts", + "source:src/workbench/canvas-engine/tools/eraserTool.ts", + "source:src/workbench/canvas-engine/tools/gradientTool.ts", + "source:src/workbench/canvas-engine/tools/gridSnap.ts", + "source:src/workbench/canvas-engine/tools/lassoTool.ts", + "source:src/workbench/canvas-engine/tools/marqueeTool.ts", + "source:src/workbench/canvas-engine/tools/moveHitTest.ts", + "source:src/workbench/canvas-engine/tools/moveTool.ts", + "source:src/workbench/canvas-engine/tools/paintConstants.ts", + "source:src/workbench/canvas-engine/tools/paintTool.ts", + "source:src/workbench/canvas-engine/tools/polylineSession.ts", + "source:src/workbench/canvas-engine/tools/samHitTest.ts", + "source:src/workbench/canvas-engine/tools/samTool.ts", + "source:src/workbench/canvas-engine/tools/shapeTool.ts", + "source:src/workbench/canvas-engine/tools/strokeSession.ts", + "source:src/workbench/canvas-engine/tools/textTool.ts", + "source:src/workbench/canvas-engine/tools/transformTool.ts", + "source:src/workbench/canvas-engine/tools/viewTool.ts", + "source:src/workbench/canvas-engine/transform/transformMath.ts", + "source:src/workbench/canvas-engine/types.ts", + "source:src/workbench/canvas-engine/viewport.ts", + "source:src/workbench/canvas-operations/FilterOperationCoordinator.ts", + "source:src/workbench/canvas-operations/SelectObjectCoordinator.ts", + "source:src/workbench/canvas-operations/api.ts", + "source:src/workbench/canvas-operations/applicationPort.ts", + "source:src/workbench/canvas-operations/backend/canvasImages.ts", + "source:src/workbench/canvas-operations/canvasImportNotice.ts", + "source:src/workbench/canvas-operations/compositeForGeneration.ts", + "source:src/workbench/canvas-operations/controlRecommendations.ts", + "source:src/workbench/canvas-operations/createCanvasEngine.ts", + "source:src/workbench/canvas-operations/createCanvasFromImages.ts", + "source:src/workbench/canvas-operations/createFromBbox.ts", + "source:src/workbench/canvas-operations/engineRegistry.ts", + "source:src/workbench/canvas-operations/exportCanvasComposite.ts", + "source:src/workbench/canvas-operations/filterGraphs.ts", + "source:src/workbench/canvas-operations/filterOperationSession.ts", + "source:src/workbench/canvas-operations/filterPreferences.ts", + "source:src/workbench/canvas-operations/generationComposite.ts", + "source:src/workbench/canvas-operations/generationCompositePlan.ts", + "source:src/workbench/canvas-operations/importGalleryImages.ts", + "source:src/workbench/canvas-operations/layerFilterRunner.ts", + "source:src/workbench/canvas-operations/layerImageResult.ts", + "source:src/workbench/canvas-operations/operationAccess.ts", + "source:src/workbench/canvas-operations/operationController.ts", + "source:src/workbench/canvas-operations/operationStores.ts", + "source:src/workbench/canvas-operations/samGraph.ts", + "source:src/workbench/canvas-operations/saveCanvasToGallery.ts", + "source:src/workbench/canvas-operations/selectObjectSession.ts", + "source:src/workbench/canvasDimsSync.ts", + "source:src/workbench/canvasInvocationPreparation.ts", + "source:src/workbench/canvasLayerContent.ts", + "source:src/workbench/canvasMigration.ts", + "source:src/workbench/canvasProjectMutations.ts", + "source:src/workbench/canvasSchemaVersion.ts", + "source:src/workbench/canvasStagingView.ts", + "source:src/workbench/components/QueueProgressIndicator.tsx", + "source:src/workbench/components/WorkbenchSplashScreen.tsx", + "source:src/workbench/components/WorkbenchUnavailableScreen.tsx", + "source:src/workbench/controlAdapters.ts", + "source:src/workbench/controlLayerChecks.ts", + "source:src/workbench/deferredResource.ts", + "source:src/workbench/diagnostics/loggingPreferences.ts", + "source:src/workbench/extensions/contributionSource.ts", + "source:src/workbench/extensions/extensionRegistry.ts", + "source:src/workbench/floatingWindows.ts", + "source:src/workbench/focusRegions.tsx", + "source:src/workbench/graphPreviewInvocation.ts", + "source:src/workbench/graphSurfaces.ts", + "source:src/workbench/graphWidgets.ts", + "source:src/workbench/hotkeys/WorkbenchHotkeyRuntime.tsx", + "source:src/workbench/hotkeys/catalog.ts", + "source:src/workbench/hotkeys/extensionHotkeys.ts", + "source:src/workbench/hotkeys/firstPartyCommands.ts", + "source:src/workbench/hotkeys/index.ts", + "source:src/workbench/hotkeys/keyGlyphs.tsx", + "source:src/workbench/hotkeys/keys.ts", + "source:src/workbench/hotkeys/modalLayer.ts", + "source:src/workbench/hotkeys/resolve.ts", + "source:src/workbench/hotkeys/targetWidget.ts", + "source:src/workbench/iconResolver.tsx", + "source:src/workbench/image-actions/ImageContextMenu.tsx", + "source:src/workbench/image-actions/RecallActionButtons.tsx", + "source:src/workbench/image-actions/appendReferenceImage.ts", + "source:src/workbench/image-actions/canvasImportError.ts", + "source:src/workbench/image-actions/executeImageRecall.ts", + "source:src/workbench/image-actions/executeVideoRecall.ts", + "source:src/workbench/image-actions/galleryOptimisticRollback.ts", + "source:src/workbench/image-actions/imageRecall.ts", + "source:src/workbench/image-actions/index.ts", + "source:src/workbench/image-actions/recallParametersBridge.ts", + "source:src/workbench/image-actions/useDeletionConfirmation.tsx", + "source:src/workbench/image-actions/useFindGalleryItem.ts", + "source:src/workbench/image-actions/useImageActions.ts", + "source:src/workbench/image-actions/videoRecall.ts", + "source:src/workbench/image-map/api.ts", + "source:src/workbench/image-map/imageMapStore.ts", + "source:src/workbench/image-map/indexProgress.ts", + "source:src/workbench/invocation.ts", + "source:src/workbench/invocationSubmit.ts", + "source:src/workbench/launchpad/HelpMenu.tsx", + "source:src/workbench/launchpad/KnownBrowserIssuesAlert.tsx", + "source:src/workbench/launchpad/Launchpad.tsx", + "source:src/workbench/launchpad/LaunchpadNav.tsx", + "source:src/workbench/launchpad/LaunchpadTopBar.tsx", + "source:src/workbench/launchpad/OpenProjectsControl.tsx", + "source:src/workbench/launchpad/formatRelativeTime.ts", + "source:src/workbench/launchpad/home/IntentTiles.tsx", + "source:src/workbench/launchpad/home/LivePanel.tsx", + "source:src/workbench/launchpad/home/RecentProjectsRow.tsx", + "source:src/workbench/launchpad/home/ResumeCard.tsx", + "source:src/workbench/launchpad/intents.ts", + "source:src/workbench/launchpad/pages/HomePage.tsx", + "source:src/workbench/launchpad/pages/ProjectsPage.tsx", + "source:src/workbench/launchpad/projects/NewProjectButton.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenu.tsx", + "source:src/workbench/launchpad/projects/ProjectActionsMenuHost.tsx", + "source:src/workbench/launchpad/projects/ProjectCard.tsx", + "source:src/workbench/launchpad/projects/ProjectCompatibilityBadge.tsx", + "source:src/workbench/launchpad/projects/ProjectCover.tsx", + "source:src/workbench/launchpad/projects/ProjectRow.tsx", + "source:src/workbench/launchpad/projects/ProjectsBrowser.tsx", + "source:src/workbench/launchpad/projects/ProjectsToolbar.tsx", + "source:src/workbench/launchpad/projects/projectLibraryView.ts", + "source:src/workbench/launchpad/projects/projectPins.ts", + "source:src/workbench/launchpad/projects/projectsMetrics.ts", + "source:src/workbench/launchpad/projects/useProjectCardActions.ts", + "source:src/workbench/launchpad/startEntries.ts", + "source:src/workbench/layerPanelState.ts", + "source:src/workbench/layoutContracts.ts", + "source:src/workbench/layoutPresetActivation.ts", + "source:src/workbench/layoutPresetCollection.ts", + "source:src/workbench/layoutPresetRouting.ts", + "source:src/workbench/layoutPresetSnapshots.ts", + "source:src/workbench/layoutPresets.ts", + "source:src/workbench/layoutWidgetSet.ts", + "source:src/workbench/mediaReferences.ts", + "source:src/workbench/palette/LaunchpadCommandPalette.tsx", + "source:src/workbench/palette/PaletteButton.tsx", + "source:src/workbench/palette/WorkbenchCommandPalette.tsx", + "source:src/workbench/palette/paletteStore.ts", + "source:src/workbench/palette/settingsEntryDeps.ts", + "source:src/workbench/panZoom.ts", + "source:src/workbench/performanceMarks.ts", + "source:src/workbench/persistenceRuntime.ts", + "source:src/workbench/projectCanvasGate.ts", + "source:src/workbench/projectEvents.ts", + "source:src/workbench/projectWorkflows.ts", + "source:src/workbench/projects/accountOwnedWorkbenchDatabase.ts", + "source:src/workbench/projects/api.ts", + "source:src/workbench/projects/components/OpenProjectDialog.tsx", + "source:src/workbench/projects/components/ProjectFileOptionsProvider.tsx", + "source:src/workbench/projects/components/index.ts", + "source:src/workbench/projects/covers.ts", + "source:src/workbench/projects/draftStore.ts", + "source:src/workbench/projects/durableSyncedPersistence.ts", + "source:src/workbench/projects/editorSession.ts", + "source:src/workbench/projects/ids.ts", + "source:src/workbench/projects/indexedDbDraftStore.ts", + "source:src/workbench/projects/invk/format.ts", + "source:src/workbench/projects/library.ts", + "source:src/workbench/projects/openProjectBroker.ts", + "source:src/workbench/projects/openProjects.ts", + "source:src/workbench/projects/projectAssets.ts", + "source:src/workbench/projects/projectDocument.ts", + "source:src/workbench/projects/projectFile.ts", + "source:src/workbench/projects/projectFileErrors.ts", + "source:src/workbench/projects/projectFileToasts.ts", + "source:src/workbench/projects/projectFlush.ts", + "source:src/workbench/projects/projectHydration.ts", + "source:src/workbench/projects/projectLifecycleLocks.ts", + "source:src/workbench/projects/projectLoadRefusal.ts", + "source:src/workbench/projects/projectTabsA11y.ts", + "source:src/workbench/projects/session.ts", + "source:src/workbench/projects/syncStore.ts", + "source:src/workbench/projects/syncedPersistence.ts", + "source:src/workbench/projects/useProjectActions.ts", + "source:src/workbench/projects/useProjectFileActions.ts", + "source:src/workbench/projects/workbenchDatabase.ts", + "source:src/workbench/queue-integration/QueueProgressRail.tsx", + "source:src/workbench/queue-integration/QueueRecoveryNotice.tsx", + "source:src/workbench/queue-integration/activeQueueRuns.ts", + "source:src/workbench/queue-integration/queueRecallCache.ts", + "source:src/workbench/queue-integration/queueRecovery.ts", + "source:src/workbench/queue-integration/queueRunJournal.ts", + "source:src/workbench/queue-integration/queueRunLocks.ts", + "source:src/workbench/queue-integration/queueRunPersistence.ts", + "source:src/workbench/queue-integration/queueRunRestoration.ts", + "source:src/workbench/queue-integration/useActiveQueueProgress.ts", + "source:src/workbench/queue-integration/useQueueRecoveryProjectIds.ts", + "source:src/workbench/settings/SettingsDialogHost.tsx", + "source:src/workbench/settings/WidgetSettingsButton.tsx", + "source:src/workbench/settings/applicationContributions.ts", + "source:src/workbench/settings/catalog.ts", + "source:src/workbench/settings/clearWorkspaceData.ts", + "source:src/workbench/settings/dialogResource.ts", + "source:src/workbench/settings/launchpad.tsx", + "source:src/workbench/settings/settingsDialogStore.ts", + "source:src/workbench/settings/settingsSearchShortcut.ts", + "source:src/workbench/settings/store.ts", + "source:src/workbench/shell/BottomPanel.tsx", + "source:src/workbench/shell/CenterArea.tsx", + "source:src/workbench/shell/DocumentTitleProgress.tsx", + "source:src/workbench/shell/Panels.tsx", + "source:src/workbench/shell/PasteMediaRuntime.tsx", + "source:src/workbench/shell/ProjectConflictBanner.tsx", + "source:src/workbench/shell/QueueRecoveryBanner.tsx", + "source:src/workbench/shell/StatusBar.tsx", + "source:src/workbench/shell/WorkbenchShell.tsx", + "source:src/workbench/shell/documentTitle.ts", + "source:src/workbench/shell/holdToDragSensor.ts", + "source:src/workbench/shell/index.ts", + "source:src/workbench/shell/notifications/WorkbenchNotificationToaster.tsx", + "source:src/workbench/shell/notifications/index.ts", + "source:src/workbench/shell/notifications/toastPolicy.ts", + "source:src/workbench/shell/topbar/AppMenu.tsx", + "source:src/workbench/shell/topbar/InvocationCluster.tsx", + "source:src/workbench/shell/topbar/InvokeButton.tsx", + "source:src/workbench/shell/topbar/IterationsField.tsx", + "source:src/workbench/shell/topbar/LayoutPresetAdminDialogs.tsx", + "source:src/workbench/shell/topbar/LayoutPresetDialog.tsx", + "source:src/workbench/shell/topbar/LayoutPresetManagerDialog.tsx", + "source:src/workbench/shell/topbar/LayoutPresetStrip.tsx", + "source:src/workbench/shell/topbar/ProjectSwitcher.tsx", + "source:src/workbench/shell/topbar/QueueCluster.tsx", + "source:src/workbench/shell/topbar/RoutingControl.tsx", + "source:src/workbench/shell/topbar/RoutingDestinationSegments.tsx", + "source:src/workbench/shell/topbar/TopBar.tsx", + "source:src/workbench/shell/topbar/TopbarProgressRail.tsx", + "source:src/workbench/shell/topbar/TopbarShortcutKeys.tsx", + "source:src/workbench/shell/topbar/index.ts", + "source:src/workbench/shell/topbar/invokeButtonModel.ts", + "source:src/workbench/shell/topbar/layoutPresetDialogModel.ts", + "source:src/workbench/shell/topbar/layoutPresetIcons.ts", + "source:src/workbench/shell/topbar/layoutPresetManagerStore.ts", + "source:src/workbench/shell/topbar/projectSwitcherStore.ts", + "source:src/workbench/shell/topbar/topbarBreakpoints.ts", + "source:src/workbench/shell/topbar/useInvocationState.ts", + "source:src/workbench/shell/topbar/useLayoutDrift.ts", + "source:src/workbench/shell/topbar/useTopbarShortcut.ts", + "source:src/workbench/shell/useMountedInstanceIds.ts", + "source:src/workbench/useNotify.ts", + "source:src/workbench/useOpenWorkbenchWidget.ts", + "source:src/workbench/widget-frame/FloatingWidgetLayer.tsx", + "source:src/workbench/widget-frame/WidgetActionsMenu.tsx", + "source:src/workbench/widget-frame/WidgetBar.tsx", + "source:src/workbench/widget-frame/WidgetEnableMenu.tsx", + "source:src/workbench/widget-frame/WidgetFailureBoundary.tsx", + "source:src/workbench/widget-frame/WidgetFrames.tsx", + "source:src/workbench/widget-frame/WidgetHosts.tsx", + "source:src/workbench/widget-frame/WidgetIdentityIcon.tsx", + "source:src/workbench/widget-frame/WidgetInstanceContextMenu.tsx", + "source:src/workbench/widget-frame/WidgetLoadingFallback.tsx", + "source:src/workbench/widget-frame/WidgetRegionDropOverlay.tsx", + "source:src/workbench/widget-frame/WidgetRenderer.tsx", + "source:src/workbench/widget-frame/WidgetSourceLockBadge.tsx", + "source:src/workbench/widget-frame/WidgetStrip.tsx", + "source:src/workbench/widget-frame/createWidgetRuntime.ts", + "source:src/workbench/widget-frame/index.ts", + "source:src/workbench/widget-frame/useWidgetIntentPreload.ts", + "source:src/workbench/widget-frame/useWidgetSortable.ts", + "source:src/workbench/widget-frame/widgetRenderInstance.ts", + "source:src/workbench/widgetDnd.ts", + "source:src/workbench/widgetImplementationResource.ts", + "source:src/workbench/widgetLabels.ts", + "source:src/workbench/widgetPlacementCommands.ts", + "source:src/workbench/widgetPlacementMeta.ts", + "source:src/workbench/widgetRegionViewModel.ts", + "source:src/workbench/widgetRegistry.ts", + "source:src/workbench/widgetState.ts", + "source:src/workbench/widgets/autosave-status/AutosaveStatusWidgetView.tsx", + "source:src/workbench/widgets/autosave-status/implementation.ts", + "source:src/workbench/widgets/autosave-status/manifest.ts", + "source:src/workbench/widgets/canvas/bboxGrid.ts", + "source:src/workbench/widgets/canvas/canvasSettings.ts", + "source:src/workbench/widgets/canvas/invoke/canvasCompositing.ts", + "source:src/workbench/widgets/canvas/invoke/canvasStrength.ts", + "source:src/workbench/widgets/canvas/manifest.ts", + "source:src/workbench/widgets/canvas/settingsContribution.ts", + "source:src/workbench/widgets/diagnostics/manifest.ts", + "source:src/workbench/widgets/gallery/manifest.ts", + "source:src/workbench/widgets/generate/manifest.ts", + "source:src/workbench/widgets/image-map/ImageMapDataRuntime.tsx", + "source:src/workbench/widgets/image-map/manifest.ts", + "source:src/workbench/widgets/image-map/settingsContribution.ts", + "source:src/workbench/widgets/layers/controlModelOptions.ts", + "source:src/workbench/widgets/layers/layerOps.ts", + "source:src/workbench/widgets/layers/manifest.ts", + "source:src/workbench/widgets/layers/panes/editorPaneLayout.ts", + "source:src/workbench/widgets/layers/selectedModel.ts", + "source:src/workbench/widgets/manifests.ts", + "source:src/workbench/widgets/notifications/NotificationsHeaderActions.tsx", + "source:src/workbench/widgets/notifications/NotificationsWidgetView.tsx", + "source:src/workbench/widgets/notifications/implementation.ts", + "source:src/workbench/widgets/notifications/manifest.ts", + "source:src/workbench/widgets/preview/PreviewActionStrip.tsx", + "source:src/workbench/widgets/preview/PreviewCompare.tsx", + "source:src/workbench/widgets/preview/PreviewCompareDropZone.tsx", + "source:src/workbench/widgets/preview/PreviewDetailsPopover.tsx", + "source:src/workbench/widgets/preview/PreviewFilmstrip.tsx", + "source:src/workbench/widgets/preview/PreviewFrame.tsx", + "source:src/workbench/widgets/preview/PreviewHeaderActions.tsx", + "source:src/workbench/widgets/preview/PreviewMetadataPanel.tsx", + "source:src/workbench/widgets/preview/PreviewStage.tsx", + "source:src/workbench/widgets/preview/PreviewSwipeNeighbor.tsx", + "source:src/workbench/widgets/preview/PreviewWidgetChrome.tsx", + "source:src/workbench/widgets/preview/PreviewWidgetView.tsx", + "source:src/workbench/widgets/preview/PreviewZoomMenu.tsx", + "source:src/workbench/widgets/preview/implementation.ts", + "source:src/workbench/widgets/preview/livePreviewFollow.tsx", + "source:src/workbench/widgets/preview/loupeGestures.ts", + "source:src/workbench/widgets/preview/manifest.ts", + "source:src/workbench/widgets/preview/previewCompareDnd.ts", + "source:src/workbench/widgets/preview/previewDensity.ts", + "source:src/workbench/widgets/preview/previewHeaderStore.ts", + "source:src/workbench/widgets/preview/previewMetadata.ts", + "source:src/workbench/widgets/preview/previewSettings.ts", + "source:src/workbench/widgets/preview/previewSwipe.ts", + "source:src/workbench/widgets/preview/settingsContribution.ts", + "source:src/workbench/widgets/preview/spanPlaybackRequest.ts", + "source:src/workbench/widgets/preview/useCompareLoupe.ts", + "source:src/workbench/widgets/preview/usePreviewLoupe.ts", + "source:src/workbench/widgets/preview/usePreviewNavigation.ts", + "source:src/workbench/widgets/preview/usePreviewSwipe.ts", + "source:src/workbench/widgets/project/manifest.ts", + "source:src/workbench/widgets/queue-status/QueueStatusWidgetView.tsx", + "source:src/workbench/widgets/queue-status/implementation.ts", + "source:src/workbench/widgets/queue-status/manifest.ts", + "source:src/workbench/widgets/queue-status/queueStatusModel.ts", + "source:src/workbench/widgets/queue/manifest.ts", + "source:src/workbench/widgets/server-status/ServerStatusWidgetView.tsx", + "source:src/workbench/widgets/server-status/implementation.ts", + "source:src/workbench/widgets/server-status/manifest.ts", + "source:src/workbench/widgets/upscale/manifest.ts", + "source:src/workbench/widgets/video/manifest.ts", + "source:src/workbench/widgets/workflow/manifest.ts", + "source:src/workbench/workbenchState.ts", + "source:src/workbench/workbenchStore.ts" + ] + }, + "timingPolicy": { + "enforce": false, + "longTaskTargetMs": 50, + "runner": { + "id": "unconfigured", + "minimumStableRuns": 20, + "observedStableRuns": 0, + "stable": false + }, + "tolerancePercent": 0.1 + } +} diff --git a/invokeai/frontend/webv2/pnpm-lock.yaml b/invokeai/frontend/webv2/pnpm-lock.yaml new file mode 100644 index 00000000000..b9b47687164 --- /dev/null +++ b/invokeai/frontend/webv2/pnpm-lock.yaml @@ -0,0 +1,4121 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +patchedDependencies: + react-hook-tanstack-virtual: + hash: 2d69c3f291348e61b3d4e3f96732527872259fee1c4df92376fff4ebc8432072 + path: patches/react-hook-tanstack-virtual.patch + +importers: + + .: + dependencies: + '@chakra-ui/react': + specifier: ^3.37.0 + version: 3.37.0(@emotion/react@11.14.0(@types/react@19.3.0)(react@19.3.0))(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@dnd-kit/core': + specifier: ^6.3.1 + version: 6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@dnd-kit/modifiers': + specifier: ^9.0.0 + version: 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0) + '@dnd-kit/sortable': + specifier: ^10.0.0 + version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0) + '@dnd-kit/utilities': + specifier: ^3.2.2 + version: 3.2.2(react@19.3.0) + '@emotion/react': + specifier: ^11.14.0 + version: 11.14.0(@types/react@19.3.0)(react@19.3.0) + '@fontsource/inter': + specifier: ^5.3.0 + version: 5.3.0 + '@tanstack/react-query': + specifier: ^5.102.8 + version: 5.102.8(react@19.3.0) + '@tanstack/react-router': + specifier: ^1.170.36 + version: 1.170.36(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/react-virtual': + specifier: ^3.14.13 + version: 3.14.13(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@xyflow/react': + specifier: ^12.11.6 + version: 12.11.6(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + ag-psd: + specifier: ^31.0.2 + version: 31.0.2 + fflate: + specifier: ^0.8.3 + version: 0.8.3 + fuzzysort: + specifier: ^4.0.2 + version: 4.0.2 + html-to-image: + specifier: ^1.11.13 + version: 1.11.13 + i18next: + specifier: ^26.4.2 + version: 26.4.2(typescript@7.0.2) + i18next-http-backend: + specifier: ^4.0.2 + version: 4.0.2 + idb: + specifier: ^8.0.3 + version: 8.0.3 + lucide-react: + specifier: ^1.46.0 + version: 1.46.0(react@19.3.0) + perfect-freehand: + specifier: ^1.2.3 + version: 1.2.3 + plotly.js-gl2d-dist-min: + specifier: ^4.1.1 + version: 4.1.1 + react: + specifier: ^19.3.0 + version: 19.3.0 + react-dom: + specifier: ^19.3.0 + version: 19.3.0(react@19.3.0) + react-hook-tanstack-virtual: + specifier: ^0.0.4 + version: 0.0.4(patch_hash=2d69c3f291348e61b3d4e3f96732527872259fee1c4df92376fff4ebc8432072)(@tanstack/react-virtual@3.14.13(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(@tanstack/virtual-core@3.17.11)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react-i18next: + specifier: ^17.0.14 + version: 17.0.14(i18next@26.4.2(typescript@7.0.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@7.0.2) + socket.io-client: + specifier: ^4.8.3 + version: 4.8.3 + tinykeys: + specifier: ^4.0.0 + version: 4.0.0 + use-sync-external-store: + specifier: ^1.7.0 + version: 1.7.0(react@19.3.0) + yaml: + specifier: ^2.9.1 + version: 2.9.1 + zod: + specifier: ^4.6.5 + version: 4.6.5 + devDependencies: + '@rolldown/plugin-babel': + specifier: ^0.2.4 + version: 0.2.4(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.2.8)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + '@types/node': + specifier: ^26.5.1 + version: 26.5.1 + '@types/plotly.js': + specifier: ^3.0.13 + version: 3.0.13 + '@types/react': + specifier: ^19.3.0 + version: 19.3.0 + '@types/react-dom': + specifier: ^19.3.0 + version: 19.3.0(@types/react@19.3.0) + '@types/use-sync-external-store': + specifier: ^1.7.0 + version: 1.7.0 + '@vitejs/plugin-react': + specifier: ^6.1.1 + version: 6.1.1(@rolldown/plugin-babel@0.2.4(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.2.8)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)))(babel-plugin-react-compiler@1.0.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + '@vitest/browser-playwright': + specifier: 5.0.0 + version: 5.0.0(playwright@1.63.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1))(vitest@5.0.0) + axe-core: + specifier: ^4.13.0 + version: 4.13.0 + babel-plugin-react-compiler: + specifier: ^1.0.0 + version: 1.0.0 + oxfmt: + specifier: ^0.68.0 + version: 0.68.0 + oxlint: + specifier: ^1.83.0 + version: 1.83.0 + playwright: + specifier: 1.63.0 + version: 1.63.0 + typescript: + specifier: ^7.0.2 + version: 7.0.2 + vite: + specifier: ^8.3.0 + version: 8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1) + vitest: + specifier: 5.0.0 + version: 5.0.0(@types/node@26.5.1)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + +packages: + + '@ark-ui/react@5.39.0': + resolution: {integrity: sha512-qKMyliIHgkp4TPrdr9xmkXeP9lmuEvhRJNPNah7Wnp54lspsZJY4b08rNM+NWtlnhxO1P8iChKRKcohSQgXL/g==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@blazediff/core@1.10.0': + resolution: {integrity: sha512-AOQff0zgR7cGsZL+4E7hVkmujoPUpm0J9xzWGWZj5wCjd3gmxESXAPfKyuzs93VdpQNFhHlBhfOjrcZ+XTERtQ==} + + '@chakra-ui/react@3.37.0': + resolution: {integrity: sha512-TaFju5LTT7GEZ8brcyZOhM7fNN54tJ/rMaMFvUAD7UE8tsUJb/j3zCUYPjchkoVXD1vinXtN9S31cp2QcOTUPg==} + peerDependencies: + '@emotion/react': '>=11' + react: '>=18' + react-dom: '>=18' + + '@dnd-kit/accessibility@3.1.1': + resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} + peerDependencies: + react: '>=16.8.0' + + '@dnd-kit/core@6.3.1': + resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@dnd-kit/modifiers@9.0.0': + resolution: {integrity: sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==} + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/sortable@10.0.0': + resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==} + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/utilities@3.2.2': + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@fontsource/inter@5.3.0': + resolution: {integrity: sha512-RofMylZmjlJEfELXeNHFWBRcSs75rGU/6bV2S2jfnvv/3rPXPGe0LgUJTklcHZ9lM4OZmAVFhcJPnACfb91A3g==} + + '@internationalized/date@3.12.3': + resolution: {integrity: sha512-fuLX+3ZKLsxI73y8b01EG/WjHb6gE6weCqlfawPO27kBWGMh9G1yH6Csv1uU7/cac9H2GHmOMt6CjmuQ1aia4Q==} + + '@internationalized/number@3.6.7': + resolution: {integrity: sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@oxc-project/types@0.149.0': + resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==} + + '@oxfmt/binding-android-arm-eabi@0.68.0': + resolution: {integrity: sha512-dhfYPbzv/h9JgHjNkl2R6sOjUfxDyLGOZVb3g8/ScaTNwwJcYgmHh8kcYFDUhinuy1QAoANCWUvw1jlk+z6gAg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.68.0': + resolution: {integrity: sha512-v3Njdi6qY0O/5eGfg01ww2w6gTn2mUvZ72Bnx1/UN53A9wruh3Nk6otc3WkgJLkXD4Qgz1SOcVQieH1oD03V9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.68.0': + resolution: {integrity: sha512-ei4MCMzHFREmZwPJ7KuWUB4kBuHdsgDrnXGJVcEAopU7fj7S42I8BChdFILWdHvhFqR08FLJtOfbZIr2CDw0cA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.68.0': + resolution: {integrity: sha512-UrKgzZxYhwB9DSvTX+vdgl9M32wLUNKJcAKIoiyx/Kzn/zveqi7W6kYVcRroFMhS3Kwz0KhTk3WBeSuQn4YCTg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.68.0': + resolution: {integrity: sha512-6jrEKgpJbilM1QaRv7hEtKXr4p4AK4jvvyOtajwyhu0kOz3e0O7OLnSTk6tBotRqCcUC4ehZRJ1Zx+Y99wieLw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': + resolution: {integrity: sha512-YOIVnKOBaLeGullskS179N12hjSAdFYnzLjOaKiLhAKNWgnShq9w4xRdtmUm6BlnP65l2/EA9Aw/KlftNxDM7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.68.0': + resolution: {integrity: sha512-xW5XoEHVNqydPBv2KXvk9lmEzyAlOQHVEazKoXUuAacqekjya+OdiaFjjEBl0oJD02raG8g3TRl9OVCh9PDIHA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.68.0': + resolution: {integrity: sha512-QCvYwVVQieu6oyJglAgV9vH/YMDxZyR4cwVSYtoq9oOXd5N+D3TDUBjNwxFrLn5AdcJZOcvn/7IB17vJGp+2Og==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-arm64-musl@0.68.0': + resolution: {integrity: sha512-4TVz5iFQ8ndrHnhX50UXiz9BIWAtUSOHJ6Nus4qWFfJBXq/Ed/krXbF/ehJu42BXM5tIvBs99jNIM22s9agY5A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-ppc64-gnu@0.68.0': + resolution: {integrity: sha512-qLe3ao0RP84bnPxBvRI+GnlK/jybo538NWu0Xrm+zYeTmJtpzqLhnnd5BH21NafqKnplbGZjtN1cnrOlWF73lw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-gnu@0.68.0': + resolution: {integrity: sha512-Yvyl7a6gbb0vM6r925KW2dO+/CmXySO5TVbcX7o/uZJ+d108HFOG0TxIyHApmn5USuj5mhDPxzhiVwOlGP7uSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-musl@0.68.0': + resolution: {integrity: sha512-mJlFuFVCxzrYM5sFStN433D/s/mb6Wq4aCQAM02vs/OudHywnaSAd2rb1vlYUJtqdYIciJtiasuxvfbYkv5fLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-s390x-gnu@0.68.0': + resolution: {integrity: sha512-RlfSg++qs1hbKltRR6lYvV9EoI3MdlfSQD9w1hdHVYjHqjIn1tkH4FWOpMSmjKGN20zr+nI+W9o4ARogCDudGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxfmt/binding-linux-x64-gnu@0.68.0': + resolution: {integrity: sha512-nyzRB9U+dlYUKu3pMo3afHzZBUv/oTHZMG36ZfJViNVfOIzp70Q4GS8FFRGgYJ/p0zcyDCgpBvYISOdJOMh+jQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-linux-x64-musl@0.68.0': + resolution: {integrity: sha512-iCx3sbZRIvGrL1RafphEiUKBaW1lc0/tAjKOIB/Wjw2+STRBEdu5+fH1Gc1faEWEmc2k5Ks4iUUV54Zd5C9a1A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-openharmony-arm64@0.68.0': + resolution: {integrity: sha512-x2X5AZez7OgyLLFpwIgItoXBUqudDM7yiaTsxv8R8vKQ6e81l0jVw0NFeUCXzcl1sAJq8h+tC8N4mY8EiMeL4w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.68.0': + resolution: {integrity: sha512-AHVPjXkenLPQUh6kB8zSC8pX2ct9r4T1Edk9r/RNJyov6wPsS5uAfYtipwG4chn6+3bPFG5rI/3DxEeH8vib1w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.68.0': + resolution: {integrity: sha512-n09SjEk5VH7z8Hl4WVP7hho+cCwGViENkQFiM45vbW85dJd7kEhWaHRUobaPzEmrWyu6uumd4EuNfNyDKLtzDA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.68.0': + resolution: {integrity: sha512-gPe+dJLXaPuWPWqlpklDAJp0k+K9KhQPYiQLHfb+i2rmFuUGfJ/5Qlj6tr1mO6of5g0DiLjG/XCFHIaPhotqqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.83.0': + resolution: {integrity: sha512-0yGY24EwsLk5YDe6F+VkmZyRHSwJDALa3nIrPpq7FXmp2lV2d0TzvBCGeZk+wgiULRGr5blhyr4QMp5KCXJUqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.83.0': + resolution: {integrity: sha512-hHfJ0vc17A4iUjH5p9BsTUPYbYRNxGpvD2lbu1aBRk54bzNIx9o5TtYF39QPZcV95DagZd+4DEAw2RH3G2ZsMg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.83.0': + resolution: {integrity: sha512-hsOjYjszLb/3zym/TkzUMPAoQlTJcuzSyEPOAyA+skXJIX9M0o+4JfOtqopX/Vf4hSLrJ98j0nvFo23gzk8auQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.83.0': + resolution: {integrity: sha512-mjh5oH2EA+wl5yRJYT9K9G61O2zFlpuv+yf2JwZOi0+dq2FnTUtm1h8i+5Ik0fXPWIu/k84I1psZR9aQsLAnyA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.83.0': + resolution: {integrity: sha512-fNHr64/YaO8YssuoDVC8+F4Uk5enR86q5uxfHkQrjAPs1dbAILOrD2uaud+J7MO8Fx774g44ERLD0IGIvZE48w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.83.0': + resolution: {integrity: sha512-Qpwy3zzAwMj+8/lyYItHmkSMwbkprFNWTK7jPYDOxSyxEhaSLOWYUTCMkjF334J8/WD0nznCCsoBbIH6hpsuIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.83.0': + resolution: {integrity: sha512-s+BirYLFq7JL2k9sP0XI3ZXJ9dYvJ8sX3jLCLoag7tt+zrSHpZxP0jqznfL+Gdgwu7ay0dYgGYJXrQvq3iWloA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.83.0': + resolution: {integrity: sha512-7lihXt3vKr+GIyapNbHrnFHm/biiW30le6Zv/DExbAFPF6YwCQXVFlONPFehxs0CpGO4CBfYPM9rdDT+XMoIlg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-arm64-musl@1.83.0': + resolution: {integrity: sha512-q63JalLYVkZiZvls1z3PPUnpmQluOMXp0khqQMznCeAPLGydfNY8JhvuA4WlK57JfrvikU8wB5lPVveqpIXvew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxlint/binding-linux-ppc64-gnu@1.83.0': + resolution: {integrity: sha512-krQmDF+dRbxvdqVPV88ZuOoPPu8X5BuqDA8Hd+qcS4YMRQCb+nexA57DazgGsc/rGdKBe3QmV0mnv0bdpW/p5g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxlint/binding-linux-riscv64-gnu@1.83.0': + resolution: {integrity: sha512-MmOl8Y6txEAXZU1RG8Rr264jQ6D7VPmqFsU/45x/FeWsGe32hklTqGrLE6UxHzp5Rjt0wP+20tY8YXKgSFB3mw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-riscv64-musl@1.83.0': + resolution: {integrity: sha512-u1rMymh0W3JZkq370kzQsYPULGWqhE09pZRqnZvUSoYaI9pVO5yVX+iYIslmWuEgwuzH9YAaOsScJiobWCHoOw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxlint/binding-linux-s390x-gnu@1.83.0': + resolution: {integrity: sha512-y0zK3HNwGysu7rqtE+BQG/d0bx5gh/KwlOtghN8oWeK1KcWzeaLqtZrbm8owqdma1lFyrce/hTO5ismuNu+INQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxlint/binding-linux-x64-gnu@1.83.0': + resolution: {integrity: sha512-rS5gM0NgD7ngmuJmbIehsidtrOwKkLFwCQbKEeb9KuyQrrWNq5Zkn0uV6AYdXOMJ0grrWEiLwBuvMxt8w5vsNw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-linux-x64-musl@1.83.0': + resolution: {integrity: sha512-W2IH4EtpcPaWcvNGCA95YoDg4vxqE/ZiPCi3arrxEEpsK7+JQN9WYwrlYFx9pcdP6KPXqRqkv3zdQPHcx7b6YQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxlint/binding-openharmony-arm64@1.83.0': + resolution: {integrity: sha512-6LyKkUyoajssTPLlZmDbZIbu4IZ5B4bGuRUnBgCGpEvHP3FQMaYITncHA/unPUo7q+Z+pIu2HhdkQ+8d1SG7iA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.83.0': + resolution: {integrity: sha512-Uz/fObEtF0jmNJQJ8CGRBKfefYstS0/wjD3s6IGzP8nUwsJykHQJBiN3npHwKiGRGn/vvBEgNr4B3cCzmmatvg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.83.0': + resolution: {integrity: sha512-u7XcvPW6Bk58tY5iWs2ESb0vJjoE/kuSpHxopbwp/p3ZtWVQXZ6wor5w3ssVTHOqd/v8b+QdhSFWQ4grEUNWpA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.83.0': + resolution: {integrity: sha512-LZRubd7ph13QmAg4fFecTYVZkiYbROR2Htaxh/ufWRkDhPOm2wrwaEYR89e0YpPFD3dqBrPoxS7myBw5hmYA7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@pandacss/is-valid-prop@1.12.1': + resolution: {integrity: sha512-JFmahiSKrpBE48NHl8NIsNO5e5y1SrOrOGvyle9h9rkNrYw+RJrm5CLeGimmyhg2FIymsmZE76sDnw5Zuu2LVA==} + engines: {node: '>=20'} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rolldown/binding-android-arm-eabi@1.2.8': + resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.8': + resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.8': + resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.8': + resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.8': + resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.8': + resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.8': + resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.2.8': + resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.2.8': + resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.2.8': + resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.2.8': + resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.2.8': + resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.2.8': + resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.8': + resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.8': + resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/plugin-babel@0.2.4': + resolution: {integrity: sha512-ygmm2j/mN+aCvstHo0x3IOu4uc6LqXT9SJ3q4kpfG/+Yhfz0B+E47FITcC0FljZADwUFARP6E2kiE+Lc26D8wg==} + engines: {node: '>=22.12.0 || ^24.0.0'} + peerDependencies: + '@babel/core': ^7.29.0 || ^8.0.0-rc.1 + '@babel/plugin-transform-runtime': ^7.29.0 || ^8.0.0-rc.1 + '@babel/runtime': ^7.27.0 || ^8.0.0-rc.1 + rolldown: ^1.0.0-rc.5 + vite: ^8.0.0 + peerDependenciesMeta: + '@babel/plugin-transform-runtime': + optional: true + '@babel/runtime': + optional: true + vite: + optional: true + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + + '@tanstack/history@1.162.4': + resolution: {integrity: sha512-utTS5L2OkeYUzXGohL1Z8sefu1GLNOJcxe8Hd6iIdc/Xo1K1nDB2JEp4iSFhvYh33xKC9V91TxrS8qfrpoKobQ==} + engines: {node: '>=20.19'} + + '@tanstack/query-core@5.102.8': + resolution: {integrity: sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==} + + '@tanstack/react-query@5.102.8': + resolution: {integrity: sha512-TYBea4OuXWD7MhaSHq069TWbFe7rcwWN6kzT7JF0OKi1K6c1gTv2IzD6A6ExJsCMozdkqBWeuIUZmu4KQg0O5A==} + peerDependencies: + react: ^18 || ^19 + + '@tanstack/react-router@1.170.36': + resolution: {integrity: sha512-AbgcI/xO8AJY1r6y5tF7TZyHH0VKm0XcfY3QVIVCrHq8k04Hkj1N4nfLL+1xCGC3VnMI3AYGAVWkatnqE2dxAQ==} + engines: {node: '>=20.19'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-store@0.11.1': + resolution: {integrity: sha512-HaIGKI3YLmjBYIvy5DFDY23oNaYZIsTZfngey07Uh5iLVJgM3bIGCnZeOFOqzjFld9JHWcaHJnasD/bKoGKwJQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/react-virtual@3.14.13': + resolution: {integrity: sha512-JbDTAwtzZ99aOeCrAfW5EsE5KSq5RWh6Af2dtFwyLIIk48Ja7vm6n4axu/43T3vfjFnEGJalxJ1wyYjSQD6bSg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.171.30': + resolution: {integrity: sha512-wtS1yCAb/k5CJBauvrCyqf2n4/kRc3aiz81tFBJ1QXH4Utsu81xFTMq15AdmNWgWmt2UstUSMfWi4Ua7ph2F+w==} + engines: {node: '>=20.19'} + + '@tanstack/store@0.11.1': + resolution: {integrity: sha512-mzTOBhypOuDJAy/D8n2MfUZ1HFkXnmSETviRyhqEC8LUE7/IZQExOTxMANj3KjTofYTkFNpBY67qaVrT41YccA==} + + '@tanstack/virtual-core@3.17.11': + resolution: {integrity: sha512-+ILjvtHup6Y2hzQ6YzwMgX1Q+oQpxEGOXCEsCNaPoIP0VxMbizIBTmYTDtkerkIQS8/CbP1BRuyt8V/8BCsy1g==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/node@26.5.1': + resolution: {integrity: sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/plotly.js@3.0.13': + resolution: {integrity: sha512-mWI1NZRDHDoiFiPEvUjR0P12fv21kmT8XSLrEjfNwIDahELaXXkSz38n6o+5SYsq0YEGxkK3ANqqM0cRl8uZdg==} + + '@types/react-dom@19.3.0': + resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==} + peerDependencies: + '@types/react': ^19.3.0 + + '@types/react@19.3.0': + resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} + + '@types/use-sync-external-store@1.7.0': + resolution: {integrity: sha512-v8435kzWm3FuxyUa4r8o4sG6b0D+r+uOrzF5MXHxZKW4Haoo6txu/lq56Vio45e2i1nkmFPA+R7myWcmV7Z7Bg==} + + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + '@vitejs/plugin-react@6.1.1': + resolution: {integrity: sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + oxc-transform-react: ^0.145.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + oxc-transform-react: + optional: true + + '@vitest/browser-playwright@5.0.0': + resolution: {integrity: sha512-N+gED9y4/8pypaHjz/x0ah3CjoBr+N0hWWT+Gq4VXtMzyB+rUIdHni0ulzpD1j4H77iWy5Jg8PRMlVn6kjxo6g==} + peerDependencies: + playwright: '*' + vitest: 5.0.0 + + '@vitest/browser@5.0.0': + resolution: {integrity: sha512-JC9FG5xIRxPHXJPcdCaluIJcEoeM0IwGQ3xneuJk09LXKHRNs40BqWDWymQikbb20yOpYvzpKzmYgPRuSzKmvg==} + peerDependencies: + vitest: 5.0.0 + + '@vitest/mocker@5.0.0': + resolution: {integrity: sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@5.0.0': + resolution: {integrity: sha512-PVRNuB3wpReb4SQEs4zTKM4KWFhQ5pw3spE8naoDJNB5T5aWRzGKHwXcLUllr0WeOTXpB6bSr3CJLo5+7XQSSQ==} + + '@vitest/spy@5.0.0': + resolution: {integrity: sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==} + + '@vitest/ui@5.0.0': + resolution: {integrity: sha512-h2FIFwggCY2GxUd2UdQoYNVQkOIqEQLPhNREcl3FUiRsdzQep7NWwYbSmhGEA9nFLPDq5pXzRMcBZQU8Py83sg==} + peerDependencies: + vitest: 5.0.0 + + '@vitest/utils@5.0.0': + resolution: {integrity: sha512-dO++xL3vDfvhTAVimfkuQUA3k+JClIF1i1vAkPqpcGAthRmeWnXmHB7YPViPvgCwviX8u7Y5W1u2N//AaQr3fw==} + + '@xyflow/react@12.11.6': + resolution: {integrity: sha512-9XsEJNHjatKYndszKTF/bsU7FOP9dJ6V/EQwzy3oMdtqgBuUq7BjKSwkEo+C7s4qHstHQfwwoHA3E8QfpPxZZQ==} + peerDependencies: + '@types/react': '>=17' + '@types/react-dom': '>=17' + react: '>=17' + react-dom: '>=17' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@xyflow/system@0.0.82': + resolution: {integrity: sha512-4DKnL3CGtCGLRSmgDqaajRVgeksMXq/Yw4wPfdMfm7JvdIiWHGzVYOfFUztiATwdBXZqUU6HhehwUdbV9G23PQ==} + + '@zag-js/accordion@1.43.3': + resolution: {integrity: sha512-J6rGbMPXhYDa2dLgp66DPpwC3OcNAJH71kuGakz7SfCvQWiR9lIyB26G/QdSjJjf0rVARM8rN90e5PxZjYdWjA==} + + '@zag-js/anatomy@1.43.3': + resolution: {integrity: sha512-KzosWgu0TTtdBNHPUZg2W8E5JsGsExQ8oGkLDmV6Sym9twaATU2YVMCWsfitq3Isss4099wNMsHdjEPK2UyCjw==} + + '@zag-js/angle-slider@1.43.3': + resolution: {integrity: sha512-/WbbxE/0YhFsoKO/5t/c4LlWRg0VFXD1t3H0u40ic7jnsyNyH9RWoIjwbVSvNC98Ij3s7qmUBY63GwTPGuS/rA==} + + '@zag-js/aria-hidden@1.43.3': + resolution: {integrity: sha512-f8mqntgsDQFwPDlClF30oXJolY26sg1lnYe6avQB79kwgUQKvw10aYeiUou2INYN7mACNs4nNERfAf1/p54jHw==} + + '@zag-js/async-list@1.43.3': + resolution: {integrity: sha512-gTtQE0ZbPohOaInzXa3bsqBKDfHcyCtyq5GXXoGGC/JEn1+xbx9fssG4aydG4GBjReutOmZ5vMa5il+PB6huyA==} + + '@zag-js/auto-resize@1.43.3': + resolution: {integrity: sha512-Qb47USNXmbg5p2s8QjUwnTBWI5waVz8YHDCQO75sMoXi1CCxcdW3lSjsOI12FIBD0tiEhjkNGZrhTKnNu1gDkQ==} + + '@zag-js/avatar@1.43.3': + resolution: {integrity: sha512-cAQFzmZRBoV8M2oGUMHYiBlF2hiwwSJOzmRLjz/yCElK2EHs63O9BzhRk05EZeFqJLvQXoz6bpZ/ug5YX48cRg==} + + '@zag-js/carousel@1.43.3': + resolution: {integrity: sha512-hXnG0XL/mpBPCJ3a+H88CZMSCzVsKnOLY7i1Fza+hKxUiGovxojCAitoASeg6ETd7bOzAbz0pvfGgwJPCdnGTQ==} + + '@zag-js/cascade-select@1.43.3': + resolution: {integrity: sha512-zSHgbaPuD369sVwlc1cEn0XB4UgK+7RpHRRD/Lw0h7FJLbXvhz6afByK+3/3NpAuQ1YBg6wdiRfikxcVdkyPOQ==} + + '@zag-js/checkbox@1.43.3': + resolution: {integrity: sha512-1vikdGcAokmLSMAnHQo5lYG/DMMNej2FlaAxiuyna2n0XJy/VFhv/3+/FZNB9CAJBkeqwLm9kqo94c7iEL7cxA==} + + '@zag-js/clipboard@1.43.3': + resolution: {integrity: sha512-PHcYmVdmQltIWdtQ+QAP6EHTdjaeN4+TVocdQskBGPEW/0Vf05C/kOuznbl4mdIlhN6GGkNKJCL8D07b+etW+A==} + + '@zag-js/collapsible@1.43.3': + resolution: {integrity: sha512-ABOmW6o2SxKfuG5qNWeLXYgVDQDe30dyeRCbEGBhWFeYFO0IMvjlOLiLdk6MSXmFjMynUTIuYxaEv8gNhoOkCA==} + + '@zag-js/collection@1.43.3': + resolution: {integrity: sha512-POufSu17mxTic09DP3CD2sGiRCaNODv/gAYyotrM/ynWSeb2+TjSxlKxuiyQS0amiY+BwJ3+m5qJ4odmU9TLMQ==} + + '@zag-js/color-picker@1.43.3': + resolution: {integrity: sha512-EevMgWLagXzxa1nkcyk1HKfb+tPU0CXGNhzCdnN1bZSOeQ5eH/4Y0xDIDez0IPXp+L7wnMwIGy5ATTKCGSmVWA==} + + '@zag-js/color-utils@1.43.3': + resolution: {integrity: sha512-a7R2TfWAlxwObDt1ohdBJzL+HfrYQ/6gLwP2Vj4o7nSaUFhrj5uvT56gg1Uhzp4ALnDcg6cLLEJlwWUaYLpq6g==} + + '@zag-js/combobox@1.43.3': + resolution: {integrity: sha512-qdm81w9PnR7/f/YNxie5iT50+7tXkKgiRJZEN59pYqwRA2AbwtE87jk7CQAuBVD1ko/wfcSBfIocoeltVKy2Kw==} + + '@zag-js/core@1.43.3': + resolution: {integrity: sha512-afREBcZWvPLHz/2AxJeuAP2o6+o3V7NZO/MutLhdclKaVSBlPzCr6qqP9k8hq1zOxk1jogKT5VVGielzWDY2Dw==} + + '@zag-js/date-input@1.43.3': + resolution: {integrity: sha512-jw1hEJg1yjoRUp2U//b57w/w0bolrTTTrzO89WeKU1P/UoW1G5j5zAw1TMnL6fp2IsRP/7QgjGR4HSkAgrOhkw==} + peerDependencies: + '@internationalized/date': '>=3.0.0' + + '@zag-js/date-picker@1.43.3': + resolution: {integrity: sha512-2nQZsMkb1EddF6qTvjinsrZdZbXcor+VluniK85bg9JQsA4WvtkmIHpy/pMQQ6zS8i9fbMBYbYH7bQVbfk2JtA==} + peerDependencies: + '@internationalized/date': '>=3.0.0' + + '@zag-js/date-utils@1.43.3': + resolution: {integrity: sha512-EUQ2OUJs7crXy3OOgt8DhI2pm/jH+5IF2hzxfI7YRiVuvBtL6YbzHAWrT+CpcMcJZwwMiM2WJ9+lph7V7SoyZQ==} + peerDependencies: + '@internationalized/date': '>=3.0.0' + + '@zag-js/dialog@1.43.3': + resolution: {integrity: sha512-DGIUQ686slMHiphl5di/0hmsg+sWL5vlTxZtVZ+5zvNYTWgEVpz8bUXXnGrIsVBXkWgK8CVL34pdMCs3jAmQXA==} + + '@zag-js/dismissable@1.43.3': + resolution: {integrity: sha512-3znmDAC6qv7I9h/ZInVE+sFxE2wcBeQhT0bEiUjXAlgFGgaVmgA6A0ElGI7CDrot8qhgC88BZQisMWfYRRWgeg==} + + '@zag-js/dom-query@1.43.3': + resolution: {integrity: sha512-kpTTYMemMmHxhp4gyOllK7/Lf86AySTO+AlGds9iQM21eUwi1jvc9q3VTxP+qD/KwHePGftfPwkwxifC3iSGbw==} + + '@zag-js/drawer@1.43.3': + resolution: {integrity: sha512-/oWCjJZLxB4cNfrjjobVkrLdPgAO5uCLnpaDZ9p4ZmIY/ENO2QesboqdAK4WfypiMeYEoVpA3DFbGIVt4qzazA==} + + '@zag-js/editable@1.43.3': + resolution: {integrity: sha512-G+cFSuRQoqvTAWV1cWAxnAf1W9lJdqlzjy1BJi3rK2jMThdZUUV97dq19W0u/Qvd5V9sTnhUHjkOtq0z1fActw==} + + '@zag-js/file-upload@1.43.3': + resolution: {integrity: sha512-kpXjL4XD0kZD+OukpjoENytE9qbs/rpshOwPR+g1IJHvl45IUFXzdXZn3m8SQD4Fa5ZTp4ZiExGBjUNceTaJcg==} + + '@zag-js/file-utils@1.43.3': + resolution: {integrity: sha512-iawUjq0klSsjhuEI43u/cPlNbA+b/7gvJi5TT+KHh9DxAr3u2CTnZUCurBLeuAPtrSXvlR4MQ6MmH93NlNnj7A==} + + '@zag-js/floating-panel@1.43.3': + resolution: {integrity: sha512-DnB71JgNSk+AZNHl1PF2DFp2HkzuN7D/tsFIaV4sOK+5wgiNH4dWth7RlYkIrX9m2vpqunaQ+4/C2aZpVErYsQ==} + + '@zag-js/focus-trap@1.43.3': + resolution: {integrity: sha512-/U4xWaHvHL83yDylTjLE1txCd0FR3As/1qgFWpzb1qOGWiCp/9NrLHVfegjDPgaoKLHRC4kEHzN+0NYNGHBXgQ==} + + '@zag-js/focus-visible@1.43.3': + resolution: {integrity: sha512-32l21nQUI328nfNTe2adXyXj+8t1ltDaEm7IQGmhtAw/DuIKvjpEkhJZHL1zUSlp47QGNfuYGPeD+ZXaZWFWMA==} + + '@zag-js/highlight-word@1.43.3': + resolution: {integrity: sha512-oN87IS5aA58jj1jMnhepLdQQ85bcjK6dWK+XK3QHAFdJZLSpCK++5HjGYng/G9xsnu+FWh8RMais9c5Ci738NQ==} + + '@zag-js/hotkeys@1.43.3': + resolution: {integrity: sha512-uC3bc6WakCe0T2Hx73q+ywGiogibErBQep5UtRZGfTBhyKGyKa158NFmhl64oYx4RnlNyZliQ5cF/d74+HNtdA==} + + '@zag-js/hover-card@1.43.3': + resolution: {integrity: sha512-1gyT11fRK9r2XKwgxH6uS+Y0HYvr95QU4TjgnkzHJ2d6Q+wf89Fxb2zG7HCer0VV/rwz0ZmWZRDgKkZHCNxS7Q==} + + '@zag-js/i18n-utils@1.43.3': + resolution: {integrity: sha512-bVCZxrd7xWTMhWks8zk/xRQNMPZz3CVoXjG9207b0GVTTQOwoiVg+MEIHDqirEaDJtzUKR6VbV0dRpC8LsceFg==} + + '@zag-js/image-cropper@1.43.3': + resolution: {integrity: sha512-MBoWA7V8TMx1pxSZYK2pCbiQC5NRhsnJot8JmAjqz52EcgQM5I2Qw3mISfZ558Idg/w1ilxD0MyAw8MBHOuiDA==} + + '@zag-js/interact-outside@1.43.3': + resolution: {integrity: sha512-qgyAyWELSzFrHUtB6D7IzemI48NjX7E5oIDCXsz/DBPh+ybsK+pKMGsZkduTCK+uEqJcA/w+DAWaDUOxhjqvWA==} + + '@zag-js/json-tree-utils@1.43.3': + resolution: {integrity: sha512-Fal5YYy3+E+eHIreqZtrS8s6o+8+tiq02z2FsmQNRz/txOKb0rgMiP81C//dGLH0eVJK8GUBvj5moeJ1ueRN7A==} + + '@zag-js/listbox@1.43.3': + resolution: {integrity: sha512-1u1O002M5hXuJSQmiaWgG2isdaJwqyLe/5F6Uv6ApOcl7i0GjO4fIF0aGCDrHfGki98A9TWn4ioaU0ExL3SeUw==} + + '@zag-js/live-region@1.43.3': + resolution: {integrity: sha512-lB2ryGpRjTgbfJ5D86MQnvZRCJupKbpwS53jfCkEP0EBC9Tk7Crq9p0pobzXgH5U9fnjn4BPsqBSyuSOxPOJnw==} + + '@zag-js/marquee@1.43.3': + resolution: {integrity: sha512-NdWm4vwd9BfgcUPGwlQNvd+uaC9LcrG9sL7Jo0nZjmuxiABSO/lfyd/B4h8Md3u0lj3fVUGxO/tisrfwGo/9kA==} + + '@zag-js/menu@1.43.3': + resolution: {integrity: sha512-RGCqRv/GeEehBYOB3JV0NUfyfaWW5KBN79RHUUYpxF9DMsPM8igHOzuoOoz+FaYoepjC34oMtmWicF1Dan6apA==} + + '@zag-js/navigation-menu@1.43.3': + resolution: {integrity: sha512-7pVwpWwe/CtfvPclwd9k6Ja89RSdnYYC/rAC5Oaj5BUCVwdjNxh0VuyLS/awozNQclPFA6ww22KQSaLf4lAnPQ==} + + '@zag-js/number-input@1.43.3': + resolution: {integrity: sha512-XBuky+uuFXnshBHbIFfpUCly5nKZi+hW0R6pvgmqirqVyc4K/lwg4nz88/Z6U/q4uRQarKRr22fOMLthc7q7Yw==} + + '@zag-js/pagination@1.43.3': + resolution: {integrity: sha512-8shW+/WwkLNKh9nhVXQCi2mQ50un52obzgheIm/BwlUlHu+qY/dM2al3Kx9L4yK7YYTDaXFxKyvP1iaYUQd7ZA==} + + '@zag-js/password-input@1.43.3': + resolution: {integrity: sha512-r7RPikBcwN8zTpuah54Bo3jclkz+ze+wV6g5SC7Ox6yOWdbC6A6ORyvXc3yqw2YG4tvwOtrD4p9w21zCVzNaqQ==} + + '@zag-js/pin-input@1.43.3': + resolution: {integrity: sha512-eUIyyJeA07yJzdGPh7vkQdnd/32OtU9xMppQDGIKHSJFA1ZIeqxdmy33Safj41XFurLBTStQAKLCSJmBGgY2Gg==} + + '@zag-js/popover@1.43.3': + resolution: {integrity: sha512-kLQIj+9XaaEvSbviG+r8hI1OJxC4P4JTETNcnHV4w4aE5nR2r/LDP3JoJSEQsAvTR/MMYpZQDL5pdVQj0M/Oqg==} + + '@zag-js/popper@1.43.3': + resolution: {integrity: sha512-99PPQerLylh+ouG16d0Go0UgpLR3rCQvWMmJoO5Ti8+2Ww3aqBNLTqpF0rJuLPe1Ps4BfEt5Lr4dYhL1ixgbRQ==} + + '@zag-js/presence@1.43.3': + resolution: {integrity: sha512-b+HxlhTOxTn/I0q1lmt8Afyk+pZpXV/FqHUkbUzy3m+2t9QgMONi5NDuRc6gNRTXIeG6lLVDHSDHT/FxxMLDWw==} + + '@zag-js/progress@1.43.3': + resolution: {integrity: sha512-hGZGtncpNWsKoU5lgXWzKAPMaG5w/B5kCNnlYnCZ30TnK9Zg5GwCkvYC45pKiY+Z9i8DxLNPkxIVWHG2oyYbcg==} + + '@zag-js/qr-code@1.43.3': + resolution: {integrity: sha512-moSv0oTbO/XSAPnpPJGp5n4mpRlCsJYOaJW/I6zK1Ls6/YHWt7K9HUjZE06AwHwU+050ftivgWmXBlVgozc5GA==} + + '@zag-js/radio-group@1.43.3': + resolution: {integrity: sha512-1QM1rNnK6mQ36aYEshFVlboBDMQTth2wxHuh5V+ALIXf4Dl1Eexy0dyLVFkYV7R3E90e6b8d+xZZ3tziTvnRDg==} + + '@zag-js/rating-group@1.43.3': + resolution: {integrity: sha512-BiKcyo3z7q4UBMsGuOs8w6u827pQ50eOuRCh1oqVpKqBoyRL0z9aFQYg9YeYnxXn8eyoEBMiVv01MTogJ2JBQA==} + + '@zag-js/react@1.43.3': + resolution: {integrity: sha512-PVfq738OhicFsGOZNqrsBmdOHuh54XnIhkyJ9HSWDpzQcGHq4fuFWIvfmqW/5o/Xkj+JwFQLoIDVX/KHaUjcXA==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@zag-js/rect-utils@1.43.3': + resolution: {integrity: sha512-xZA5IKUtQeRKdzM+9jGj8R18MXmORDO0smXAAoyFj1Swj/uB17eVwvsWoQLXbb/AaYXEzSIJXNUt21n3GAX24Q==} + + '@zag-js/remove-scroll@1.43.3': + resolution: {integrity: sha512-1bX6IQM7q26uL0476wFqtaebeIeDheyJd8fuje2zXTCg1XDRHJmRsM/tzcmcdi4W5s+hG0cUCfuVzUTXvODM5Q==} + + '@zag-js/scroll-area@1.43.3': + resolution: {integrity: sha512-qJpVs70vGM+Ogl8rQCG0iuZ7oliKlk6ZwzJMPQXnoRGeG6NzwIR0S3O+lJGmgR+xBul1gXkaAJzlyxGQr82bmA==} + + '@zag-js/scroll-snap@1.43.3': + resolution: {integrity: sha512-izT8eXvwqZMEKNM3lLcAWz9MpUwvZ0UCjBnfQX2JLHLcjYxLOytMd6rfGM2hE1erD0CBYlBUi5bIqstwQFLmCA==} + + '@zag-js/select@1.43.3': + resolution: {integrity: sha512-vd5uKPOoRXXrHlL8VPNk5Mih6XRhIzXAon7emEupCe6UAAShqYnxumrLBbr5LifiePP93CGJx3rGn8RZky/OVg==} + + '@zag-js/signature-pad@1.43.3': + resolution: {integrity: sha512-5AbaVRXIwJQGheffPsvFiShQyjb4ufgvt6eyJX5ovpF9PiPuvaP6mnVYdJwK/YelWltW41b7NABtSghUYl5gIw==} + + '@zag-js/slider@1.43.3': + resolution: {integrity: sha512-SwnwdabVPouHraF4fcICz9szGMARxUd8CPA0+x+w48D8hEn0GzFXDRI0ntXByEstwbpyzvy4ptRvh0WRvwCJbw==} + + '@zag-js/splitter@1.43.3': + resolution: {integrity: sha512-iQDlQvAD+ldG0QEj6pwQazIiEjR5bvJhZFeqoSRy11BHOls5mT978QNOcCTZ0gQ1ilwsWoAMnESY8d33U1v1Vw==} + + '@zag-js/steps@1.43.3': + resolution: {integrity: sha512-Au4LFM0jqn/v66HSU1ZIpb8Dl9LK3yDQ1pPhE2k2tuJz0ksq9s61m47Vf1FjQhlfBFgXkh20xASAgsVVbDXZdg==} + + '@zag-js/store@1.43.3': + resolution: {integrity: sha512-NL3Uxk5Rszjd+X+cPXw0N7Kn5TNMyrS6cTMazlBOcN2+J68mzXhNFvM/0H38m0X+3ybK3sE5Ta6NPlhOQADRqw==} + + '@zag-js/switch@1.43.3': + resolution: {integrity: sha512-MZxRjKE9RAb0qq6bBGj9PKX4m+wWKrNJ7MxdZuopcyWxRe7c6B389bprbgAUmDmm1WB8EMQwluEqyvXqC2j9tA==} + + '@zag-js/tabs@1.43.3': + resolution: {integrity: sha512-uLT5YiQAcDkSi9T1plYQyXAXaqGi1H5iw/PA0yXsWDOoNFU9jlPM5s8SuXbBJzXiufSFekdhzpNhMUhcapnBdA==} + + '@zag-js/tags-input@1.43.3': + resolution: {integrity: sha512-JJx8WbN75PzKDBCWuIwZ6Anw2bk+CDPkB0gkCobuvAVhlYh0nN8HAWYPzMSzGZ+P2AhWN8KYEF9l8Pyx17XRXA==} + + '@zag-js/timer@1.43.3': + resolution: {integrity: sha512-HioBlMnCf+DO5T/tJPm8EnHF+Tp/jjrAYqCA5R2FgLGBC0kBWI2eFqpc74wXlH3jjTzeFaZJgaq2HnkQNvAG/w==} + + '@zag-js/toast@1.43.3': + resolution: {integrity: sha512-XBDewuymsIzZy23xrcx1v6F/HeDlk3NAfZ2C02P+DHiNKdj+a53PpqUGZg2e2FX7ZGibag+aywWG4pnvKaSQWg==} + + '@zag-js/toc@1.43.3': + resolution: {integrity: sha512-ciIs9byITgsziuGdXKTVnAP3oYd4Nti2hsI0a5VqFMBslWiU/jO6pqRAPTj5V+e2z+Z7gM1RzzMNs8di5HuUFQ==} + + '@zag-js/toggle-group@1.43.3': + resolution: {integrity: sha512-ED8LA8cXEaC/MZtFNBY4+yCoP1qFYPZH0rBq0NszaxdYSw0hBTo7WfSbSlthF2IAl4z52ZrneTjQjB4LDEUNBw==} + + '@zag-js/toggle@1.43.3': + resolution: {integrity: sha512-zZR4D6BRfdAzoEyPYqmkCw8GPh5s87E+dr3U+mHa3kabQBiVJ0Z3fvY2pNb3DBBCLJne+1+OF9NWiDTI8cRqGA==} + + '@zag-js/tooltip@1.43.3': + resolution: {integrity: sha512-sBVhcejAQN5oDFE3PoGrvvjqOt5+9rHQjtjnnuhPZqeghsiX/IaL7AY8KelwDoNkMf2npN5i7loQvAZMZLVFaw==} + + '@zag-js/tour@1.43.3': + resolution: {integrity: sha512-Wxp2HJBnHI0Nm94PvItCEdKyBnwp/PJ1Y5u45X/VTebgVXggPug88ZD51Mm6twIDPpCTkOstOcSy8zz+FlTYlQ==} + + '@zag-js/tree-view@1.43.3': + resolution: {integrity: sha512-5fj6RWe4FSRbE3QewuqbghS+ojd2vaWfTrn0jUmH3KHzay2h/w1464oW0IYOONzwOmUUHj4wLDZZKwVyzPvmJA==} + + '@zag-js/types@1.43.3': + resolution: {integrity: sha512-QokzUgkJ7a/TRE8SAXqlm1XfiNDbyM8y+mx0PpmzHa8bvmWXLEe/Zx2TRz8aYoGim75NluYzDy3x+WWileF7uA==} + + '@zag-js/utils@1.43.3': + resolution: {integrity: sha512-9P9fvFFxuiDcLqX0BYgGNkf0/a/id32nHvQpwgv/Xv3b9n5yeyw2U8nKefpWr+1VfFsrpf2XMXiA2CDSWZgt2g==} + + ag-psd@31.0.2: + resolution: {integrity: sha512-5s5PvqbomPIIJ9YjL6robz55rT4RYPiQkww4brisyZEb5jzlHB1EKh47IJg7gIFj0RUrD0cMgdntM907qaZjXA==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + axe-core@4.13.0: + resolution: {integrity: sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==} + engines: {node: '>=4'} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + babel-plugin-react-compiler@1.0.0: + resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.11.23: + resolution: {integrity: sha512-le521dGVfxM7yRX0EikCoSz+rOK+hHzdDt/E7mG1jOJB/6WAAUuwVroLwaB7ApaUsz5Q0kFlDXLSA9MheUIfRQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + browserslist@4.28.9: + resolution: {integrity: sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + electron-to-chromium@1.5.428: + resolution: {integrity: sha512-1JxbaFJj1bRKurj1uY3l4xxpU9kOUAUjcIgApj0qu1Pao5GhoIWI8iL0BeMYJ2njig1hBx0A7eKD9VGjH9wlHw==} + + engine.io-client@6.6.6: + resolution: {integrity: sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + fuzzysort@4.0.2: + resolution: {integrity: sha512-3c7yD6rK1EXIsqpcu1ZVzXJugODWNMzvUij6yTiziaAHrpNS8kzFOI9mJLE8pmG4K9JOaVgzQRgklfOEk8f4Tg==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + html-parse-stringify@4.0.1: + resolution: {integrity: sha512-0zHsZJrK7S3K2aucXWL6ycoYJ/iNtIcFHC/nYQgFklPtrv5LpJctIiSCroWZWeuoXvuyFdzp6KzjJQ+OT5MfFw==} + + html-to-image@1.11.13: + resolution: {integrity: sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==} + + i18next-http-backend@4.0.2: + resolution: {integrity: sha512-oay62dIB2kL7+WHzoUXBjWfL3+mwijD3pQkQkIEaRLhy6kjXxUhrRenV0gInwlCASAaJaDy94+XvYizK+cAGdA==} + engines: {node: '>=18'} + + i18next@26.4.2: + resolution: {integrity: sha512-RX+R0VLg13IbvRuJSxnqykUFS9vQZTl8wYpWPCIUDWVrSGjsQywB5Y+pjzrkboxGAuYfJZVH1InFTdgBdxq6ug==} + peerDependencies: + typescript: ^5 || ^6 || ^7 + peerDependenciesMeta: + typescript: + optional: true + + idb@8.0.3: + resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + isbot@5.2.2: + resolution: {integrity: sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w==} + engines: {node: '>=18'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@1.46.0: + resolution: {integrity: sha512-Bv+FZXgZPrxc/NCl1e7JJVQFLdiCxYgxNVhqoV7X0p6I8ADJo8DxBnK1auH0fZz4AmqOJ3jgneL4f1i8LJQRAA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@1.3.1: + resolution: {integrity: sha512-rm91zr2Ou+XueDTohjQQjdQEcYM6zVi8KVUCG8Ec3vHwUEKrhSdCNyfuIywkA6hcCAteIn0ZOtAHA6eGpiX+Pg==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-releases@2.0.55: + resolution: {integrity: sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==} + engines: {node: '>=18'} + + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + + oxfmt@0.68.0: + resolution: {integrity: sha512-Z0XMofcXCGUXbcpBHnWyUiX93BGiw1B+lcHNbQDWEtOhX06ewoFfu4zXkyiLhRrNnMq0twqXRHUcJetf+GsiQQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + oxlint@1.83.0: + resolution: {integrity: sha512-cyDzSzaw3uzP0TeCeq3lLRPPoaUxkbB4ZOXj+kn+5r+BX9V+4bNVGk9lxer+WrgcpebH4JxLlJ3KQjveVztOLQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=7.0.2001' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + perfect-freehand@1.2.3: + resolution: {integrity: sha512-bHZSfqDHGNlPpgH2yxXgPHlQSPpEbo+qg7li0M78J9vNAi2yjwLeA4x79BEQhX44lEWpCLSFCeRZwpw0niiXPA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + playwright-core@1.63.0: + resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.63.0: + resolution: {integrity: sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==} + engines: {node: '>=20'} + hasBin: true + + plotly.js-gl2d-dist-min@4.1.1: + resolution: {integrity: sha512-9CSoFzeOcmdXCCMBzuvUechnMPxB9I7rXKmJW+f8OukkOADG3VzgSRE9dzxNNdsDNzkERV6RcJivdxuCbmYrXA==} + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + proxy-compare@3.0.1: + resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} + + proxy-memoize@3.0.1: + resolution: {integrity: sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g==} + + react-dom@19.3.0: + resolution: {integrity: sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==} + peerDependencies: + react: ^19.3.0 + + react-hook-tanstack-virtual@0.0.4: + resolution: {integrity: sha512-N6OnfdklUygY6K9Ik7SGZlAlc3uVOVm8kp3OP7d4m+0ELGgABm4W/f8kJPcda8CkgQUkFvDO8t8lOzbEsdJG3w==} + peerDependencies: + '@tanstack/react-virtual': ^3.14.2 + '@tanstack/virtual-core': ^3.17.0 + react: ^19.2.0 + react-dom: ^19.2.0 + + react-i18next@17.0.14: + resolution: {integrity: sha512-ZpMBfJL3BiXPuYHj5QMY1GwvKJNhE1vCxOJQ8BoMBdHP5XONTfi/Qss1nuRlnFdIl72PKo2jPmw+fxuLjRFgaQ==} + peerDependencies: + i18next: '>= 26.2.0' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + typescript: ^5 || ^6 || ^7 + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + typescript: + optional: true + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react@19.3.0: + resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + rolldown@1.2.8: + resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + scheduler@0.28.0: + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + seroval-plugins@1.6.7: + resolution: {integrity: sha512-4Nk35ttD3DTDJW4hgw5StsVAPeU6qnDFnULAouw6tQ7oLTV/ICXrWpsXo2EE52eSP2joUMazbVf52mFEcADqRw==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.6.7: + resolution: {integrity: sha512-AeDcLh0yO2SFm9W71essgnSzLV9DI8ZH0x0knXn2DMnUZj728mpLbxjlbB6IqKCmqh8JA3cEqRyGoNkt584JcQ==} + engines: {node: '>=10'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + socket.io-client@4.8.3: + resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.7: + resolution: {integrity: sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==} + engines: {node: '>=10.0.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tinybench@6.1.4: + resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} + engines: {node: '>=20.0.0'} + + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinykeys@4.0.0: + resolution: {integrity: sha512-z5bQRQHL1PSx3lGOZEAMM2oKp0EBtn5T5f7HJnaKPOzk6vEH0wUPvdHLeQ7F4tmkek8AgoTQISUFmn/5SNF2xA==} + engines: {node: '>=22'} + + tinypool@2.1.2: + resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} + engines: {node: ^20.0.0 || >=22.0.0} + + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} + hasBin: true + + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + + update-browserslist-db@1.3.3: + resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uqr@0.1.3: + resolution: {integrity: sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==} + + use-sync-external-store@1.7.0: + resolution: {integrity: sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vite@8.3.0: + resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@5.0.0: + resolution: {integrity: sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.0 + '@vitest/browser-preview': 5.0.0 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.0 + '@vitest/coverage-v8': 5.0.0 + '@vitest/ui': 5.0.0 + happy-dom: '*' + jsdom: '*' + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + +snapshots: + + '@ark-ui/react@5.39.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@internationalized/date': 3.12.3 + '@zag-js/accordion': 1.43.3 + '@zag-js/anatomy': 1.43.3 + '@zag-js/angle-slider': 1.43.3 + '@zag-js/async-list': 1.43.3 + '@zag-js/auto-resize': 1.43.3 + '@zag-js/avatar': 1.43.3 + '@zag-js/carousel': 1.43.3 + '@zag-js/cascade-select': 1.43.3 + '@zag-js/checkbox': 1.43.3 + '@zag-js/clipboard': 1.43.3 + '@zag-js/collapsible': 1.43.3 + '@zag-js/collection': 1.43.3 + '@zag-js/color-picker': 1.43.3 + '@zag-js/color-utils': 1.43.3 + '@zag-js/combobox': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/date-input': 1.43.3(@internationalized/date@3.12.3) + '@zag-js/date-picker': 1.43.3(@internationalized/date@3.12.3) + '@zag-js/date-utils': 1.43.3(@internationalized/date@3.12.3) + '@zag-js/dialog': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/drawer': 1.43.3 + '@zag-js/editable': 1.43.3 + '@zag-js/file-upload': 1.43.3 + '@zag-js/file-utils': 1.43.3 + '@zag-js/floating-panel': 1.43.3 + '@zag-js/focus-trap': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/highlight-word': 1.43.3 + '@zag-js/hotkeys': 1.43.3 + '@zag-js/hover-card': 1.43.3 + '@zag-js/i18n-utils': 1.43.3 + '@zag-js/image-cropper': 1.43.3 + '@zag-js/json-tree-utils': 1.43.3 + '@zag-js/listbox': 1.43.3 + '@zag-js/marquee': 1.43.3 + '@zag-js/menu': 1.43.3 + '@zag-js/navigation-menu': 1.43.3 + '@zag-js/number-input': 1.43.3 + '@zag-js/pagination': 1.43.3 + '@zag-js/password-input': 1.43.3 + '@zag-js/pin-input': 1.43.3 + '@zag-js/popover': 1.43.3 + '@zag-js/presence': 1.43.3 + '@zag-js/progress': 1.43.3 + '@zag-js/qr-code': 1.43.3 + '@zag-js/radio-group': 1.43.3 + '@zag-js/rating-group': 1.43.3 + '@zag-js/react': 1.43.3(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@zag-js/scroll-area': 1.43.3 + '@zag-js/select': 1.43.3 + '@zag-js/signature-pad': 1.43.3 + '@zag-js/slider': 1.43.3 + '@zag-js/splitter': 1.43.3 + '@zag-js/steps': 1.43.3 + '@zag-js/switch': 1.43.3 + '@zag-js/tabs': 1.43.3 + '@zag-js/tags-input': 1.43.3 + '@zag-js/timer': 1.43.3 + '@zag-js/toast': 1.43.3 + '@zag-js/toc': 1.43.3 + '@zag-js/toggle': 1.43.3 + '@zag-js/toggle-group': 1.43.3 + '@zag-js/tooltip': 1.43.3 + '@zag-js/tour': 1.43.3 + '@zag-js/tree-view': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.9 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@blazediff/core@1.10.0': {} + + '@chakra-ui/react@3.37.0(@emotion/react@11.14.0(@types/react@19.3.0)(react@19.3.0))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@ark-ui/react': 5.39.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@emotion/is-prop-valid': 1.4.0 + '@emotion/react': 11.14.0(@types/react@19.3.0)(react@19.3.0) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.3.0) + '@emotion/utils': 1.4.2 + '@pandacss/is-valid-prop': 1.12.1 + csstype: 3.2.3 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@dnd-kit/accessibility@3.1.1(react@19.3.0)': + dependencies: + react: 19.3.0 + tslib: 2.8.1 + + '@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@dnd-kit/accessibility': 3.1.1(react@19.3.0) + '@dnd-kit/utilities': 3.2.2(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + tslib: 2.8.1 + + '@dnd-kit/modifiers@9.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@dnd-kit/utilities': 3.2.2(react@19.3.0) + react: 19.3.0 + tslib: 2.8.1 + + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@dnd-kit/utilities': 3.2.2(react@19.3.0) + react: 19.3.0 + tslib: 2.8.1 + + '@dnd-kit/utilities@3.2.2(react@19.3.0)': + dependencies: + react: 19.3.0 + tslib: 2.8.1 + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.29.7 + '@babel/runtime': 7.29.7 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.3.0)(react@19.3.0)': + dependencies: + '@babel/runtime': 7.29.7 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.3.0) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 19.3.0 + optionalDependencies: + '@types/react': 19.3.0 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.2.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.3.0)': + dependencies: + react: 19.3.0 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/utils@0.2.12': {} + + '@fontsource/inter@5.3.0': {} + + '@internationalized/date@3.12.3': + dependencies: + '@swc/helpers': 0.5.23 + + '@internationalized/number@3.6.7': + dependencies: + '@swc/helpers': 0.5.23 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@oxc-project/types@0.149.0': {} + + '@oxfmt/binding-android-arm-eabi@0.68.0': + optional: true + + '@oxfmt/binding-android-arm64@0.68.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.68.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.68.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.68.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.68.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.68.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.68.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.68.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.68.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.68.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.68.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.68.0': + optional: true + + '@oxlint/binding-android-arm-eabi@1.83.0': + optional: true + + '@oxlint/binding-android-arm64@1.83.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.83.0': + optional: true + + '@oxlint/binding-darwin-x64@1.83.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.83.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.83.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.83.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.83.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.83.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.83.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.83.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.83.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.83.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.83.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.83.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.83.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.83.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.83.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.83.0': + optional: true + + '@pandacss/is-valid-prop@1.12.1': {} + + '@polka/url@1.0.0-next.29': {} + + '@rolldown/binding-android-arm-eabi@1.2.8': + optional: true + + '@rolldown/binding-android-arm64@1.2.8': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.8': + optional: true + + '@rolldown/binding-darwin-x64@1.2.8': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.8': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.8': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.8': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.8': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.8': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.8': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.8': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.8': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.8': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.8': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.8': + optional: true + + '@rolldown/plugin-babel@0.2.4(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.2.8)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1))': + dependencies: + '@babel/core': 7.29.7 + picomatch: 4.0.7 + rolldown: 1.2.8 + optionalDependencies: + '@babel/runtime': 7.29.7 + vite: 8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1) + + '@rolldown/pluginutils@1.0.1': {} + + '@socket.io/component-emitter@3.1.2': {} + + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + + '@tanstack/history@1.162.4': {} + + '@tanstack/query-core@5.102.8': {} + + '@tanstack/react-query@5.102.8(react@19.3.0)': + dependencies: + '@tanstack/query-core': 5.102.8 + react: 19.3.0 + + '@tanstack/react-router@1.170.36(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/history': 1.162.4 + '@tanstack/react-store': 0.11.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-core': 1.171.30 + isbot: 5.2.2 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@tanstack/react-store@0.11.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/store': 0.11.1 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + use-sync-external-store: 1.7.0(react@19.3.0) + + '@tanstack/react-virtual@3.14.13(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/virtual-core': 3.17.11 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@tanstack/router-core@1.171.30': + dependencies: + '@tanstack/history': 1.162.4 + cookie-es: 3.1.1 + seroval: 1.6.7 + seroval-plugins: 1.6.7(seroval@1.6.7) + + '@tanstack/store@0.11.1': {} + + '@tanstack/virtual-core@3.17.11': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/d3-color@3.1.3': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/node@26.5.1': + dependencies: + undici-types: 8.9.0 + + '@types/parse-json@4.0.2': {} + + '@types/plotly.js@3.0.13': {} + + '@types/react-dom@19.3.0(@types/react@19.3.0)': + dependencies: + '@types/react': 19.3.0 + + '@types/react@19.3.0': + dependencies: + csstype: 3.2.3 + + '@types/use-sync-external-store@1.7.0': {} + + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + '@vitejs/plugin-react@6.1.1(@rolldown/plugin-babel@0.2.4(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.2.8)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)))(babel-plugin-react-compiler@1.0.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1) + optionalDependencies: + '@rolldown/plugin-babel': 0.2.4(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.2.8)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + babel-plugin-react-compiler: 1.0.0 + + '@vitest/browser-playwright@5.0.0(playwright@1.63.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1))(vitest@5.0.0)': + dependencies: + '@vitest/browser': 5.0.0(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1))(vitest@5.0.0) + '@vitest/mocker': 5.0.0(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + playwright: 1.63.0 + tinyrainbow: 3.1.1 + vitest: 5.0.0(@types/node@26.5.1)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@5.0.0(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1))(vitest@5.0.0)': + dependencies: + '@blazediff/core': 1.10.0 + '@vitest/mocker': 5.0.0(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + '@vitest/ui': 5.0.0(vitest@5.0.0) + '@vitest/utils': 5.0.0 + magic-string: 1.3.1 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.1 + vitest: 5.0.0(@types/node@26.5.1)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + ws: 8.21.3 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/mocker@5.0.0(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1))': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.0 + estree-walker: 3.0.3 + magic-string: 1.3.1 + optionalDependencies: + vite: 8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1) + + '@vitest/pretty-format@5.0.0': + dependencies: + tinyrainbow: 3.1.1 + + '@vitest/spy@5.0.0': {} + + '@vitest/ui@5.0.0(vitest@5.0.0)': + dependencies: + '@vitest/utils': 5.0.0 + fflate: 0.8.3 + flatted: 3.4.4 + pathe: 2.0.3 + sirv: 3.0.2 + tinyrainbow: 3.1.1 + vitest: 5.0.0(@types/node@26.5.1)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + + '@vitest/utils@5.0.0': + dependencies: + '@vitest/pretty-format': 5.0.0 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + '@xyflow/react@12.11.6(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@xyflow/system': 0.0.82 + classcat: 5.0.5 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + zustand: 4.5.7(@types/react@19.3.0)(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + transitivePeerDependencies: + - immer + + '@xyflow/system@0.0.82': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + + '@zag-js/accordion@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/anatomy@1.43.3': {} + + '@zag-js/angle-slider@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/rect-utils': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/aria-hidden@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + + '@zag-js/async-list@1.43.3': + dependencies: + '@zag-js/core': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/auto-resize@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + + '@zag-js/avatar@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/carousel@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/scroll-snap': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/cascade-select@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/collection': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/rect-utils': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/checkbox@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/clipboard@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/collapsible@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/collection@1.43.3': + dependencies: + '@zag-js/utils': 1.43.3 + + '@zag-js/color-picker@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/color-utils': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/color-utils@1.43.3': + dependencies: + '@zag-js/utils': 1.43.3 + + '@zag-js/combobox@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/collection': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/live-region': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/core@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/date-input@1.43.3(@internationalized/date@3.12.3)': + dependencies: + '@internationalized/date': 3.12.3 + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/date-utils': 1.43.3(@internationalized/date@3.12.3) + '@zag-js/dom-query': 1.43.3 + '@zag-js/live-region': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/date-picker@1.43.3(@internationalized/date@3.12.3)': + dependencies: + '@internationalized/date': 3.12.3 + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/date-utils': 1.43.3(@internationalized/date@3.12.3) + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/live-region': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/date-utils@1.43.3(@internationalized/date@3.12.3)': + dependencies: + '@internationalized/date': 3.12.3 + + '@zag-js/dialog@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/aria-hidden': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-trap': 1.43.3 + '@zag-js/remove-scroll': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/dismissable@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + '@zag-js/interact-outside': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/dom-query@1.43.3': + dependencies: + '@zag-js/types': 1.43.3 + + '@zag-js/drawer@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/aria-hidden': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-trap': 1.43.3 + '@zag-js/remove-scroll': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/editable@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/interact-outside': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/file-upload@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/file-utils': 1.43.3 + '@zag-js/i18n-utils': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/file-utils@1.43.3': + dependencies: + '@zag-js/i18n-utils': 1.43.3 + + '@zag-js/floating-panel@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/rect-utils': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/focus-trap@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + + '@zag-js/focus-visible@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + + '@zag-js/highlight-word@1.43.3': {} + + '@zag-js/hotkeys@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + + '@zag-js/hover-card@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/i18n-utils@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + + '@zag-js/image-cropper@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/interact-outside@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/json-tree-utils@1.43.3': {} + + '@zag-js/listbox@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/collection': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/live-region@1.43.3': {} + + '@zag-js/marquee@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/menu@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/rect-utils': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/navigation-menu@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/number-input@1.43.3': + dependencies: + '@internationalized/number': 3.6.7 + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/pagination@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/password-input@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/pin-input@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/popover@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/aria-hidden': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-trap': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/remove-scroll': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/popper@1.43.3': + dependencies: + '@floating-ui/dom': 1.8.0 + '@zag-js/dom-query': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/presence@1.43.3': + dependencies: + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + + '@zag-js/progress@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/qr-code@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + proxy-memoize: 3.0.1 + uqr: 0.1.3 + + '@zag-js/radio-group@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/rating-group@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/react@1.43.3(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@zag-js/core': 1.43.3 + '@zag-js/store': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@zag-js/rect-utils@1.43.3': {} + + '@zag-js/remove-scroll@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + + '@zag-js/scroll-area@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/scroll-snap@1.43.3': + dependencies: + '@zag-js/dom-query': 1.43.3 + + '@zag-js/select@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/collection': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/signature-pad@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + perfect-freehand: 1.2.3 + + '@zag-js/slider@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/splitter@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/steps@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/store@1.43.3': + dependencies: + proxy-compare: 3.0.1 + + '@zag-js/switch@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/tabs@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/tags-input@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/auto-resize': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/interact-outside': 1.43.3 + '@zag-js/live-region': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/timer@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/toast@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/toc@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/toggle-group@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/toggle@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/tooltip@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-visible': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/tour@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dismissable': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/focus-trap': 1.43.3 + '@zag-js/interact-outside': 1.43.3 + '@zag-js/popper': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/tree-view@1.43.3': + dependencies: + '@zag-js/anatomy': 1.43.3 + '@zag-js/collection': 1.43.3 + '@zag-js/core': 1.43.3 + '@zag-js/dom-query': 1.43.3 + '@zag-js/types': 1.43.3 + '@zag-js/utils': 1.43.3 + + '@zag-js/types@1.43.3': + dependencies: + csstype: 3.2.3 + + '@zag-js/utils@1.43.3': {} + + ag-psd@31.0.2: + dependencies: + base64-js: 1.5.1 + pako: 2.1.0 + + assertion-error@2.0.1: {} + + axe-core@4.13.0: {} + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.29.7 + cosmiconfig: 7.1.0 + resolve: 1.22.12 + + babel-plugin-react-compiler@1.0.0: + dependencies: + '@babel/types': 7.29.8 + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.11.23: {} + + browserslist@4.28.9: + dependencies: + baseline-browser-mapping: 2.11.23 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.428 + node-releases: 2.0.55 + update-browserslist-db: 1.3.3(browserslist@4.28.9) + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001810: {} + + chai@6.2.2: {} + + classcat@5.0.5: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie-es@3.1.1: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + + csstype@3.2.3: {} + + d3-color@3.1.0: {} + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-ease@3.0.1: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-selection@3.0.0: {} + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + detect-libc@2.1.2: {} + + electron-to-chromium@1.5.428: {} + + engine.io-client@6.6.6: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-parser: 5.2.3 + ws: 8.21.3 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-errors@1.3.0: {} + + es-module-lexer@2.3.2: {} + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + optional: true + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + expect-type@1.4.0: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fflate@0.8.3: {} + + find-root@1.1.0: {} + + flatted@3.4.4: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + fuzzysort@4.0.2: {} + + gensync@1.0.0-beta.2: {} + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + html-parse-stringify@4.0.1: {} + + html-to-image@1.11.13: {} + + i18next-http-backend@4.0.2: {} + + i18next@26.4.2(typescript@7.0.2): + optionalDependencies: + typescript: 7.0.2 + + idb@8.0.3: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + is-arrayish@0.2.1: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + isbot@5.2.2: {} + + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json5@2.2.3: {} + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lines-and-columns@1.2.4: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@1.46.0(react@19.3.0): + dependencies: + react: 19.3.0 + + magic-string@1.3.1: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.19: {} + + node-releases@2.0.55: {} + + obug@2.2.1: {} + + oxfmt@0.68.0: + dependencies: + tinypool: 2.1.2 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.68.0 + '@oxfmt/binding-android-arm64': 0.68.0 + '@oxfmt/binding-darwin-arm64': 0.68.0 + '@oxfmt/binding-darwin-x64': 0.68.0 + '@oxfmt/binding-freebsd-x64': 0.68.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.68.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.68.0 + '@oxfmt/binding-linux-arm64-gnu': 0.68.0 + '@oxfmt/binding-linux-arm64-musl': 0.68.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.68.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.68.0 + '@oxfmt/binding-linux-riscv64-musl': 0.68.0 + '@oxfmt/binding-linux-s390x-gnu': 0.68.0 + '@oxfmt/binding-linux-x64-gnu': 0.68.0 + '@oxfmt/binding-linux-x64-musl': 0.68.0 + '@oxfmt/binding-openharmony-arm64': 0.68.0 + '@oxfmt/binding-win32-arm64-msvc': 0.68.0 + '@oxfmt/binding-win32-ia32-msvc': 0.68.0 + '@oxfmt/binding-win32-x64-msvc': 0.68.0 + + oxlint@1.83.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.83.0 + '@oxlint/binding-android-arm64': 1.83.0 + '@oxlint/binding-darwin-arm64': 1.83.0 + '@oxlint/binding-darwin-x64': 1.83.0 + '@oxlint/binding-freebsd-x64': 1.83.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.83.0 + '@oxlint/binding-linux-arm-musleabihf': 1.83.0 + '@oxlint/binding-linux-arm64-gnu': 1.83.0 + '@oxlint/binding-linux-arm64-musl': 1.83.0 + '@oxlint/binding-linux-ppc64-gnu': 1.83.0 + '@oxlint/binding-linux-riscv64-gnu': 1.83.0 + '@oxlint/binding-linux-riscv64-musl': 1.83.0 + '@oxlint/binding-linux-s390x-gnu': 1.83.0 + '@oxlint/binding-linux-x64-gnu': 1.83.0 + '@oxlint/binding-linux-x64-musl': 1.83.0 + '@oxlint/binding-openharmony-arm64': 1.83.0 + '@oxlint/binding-win32-arm64-msvc': 1.83.0 + '@oxlint/binding-win32-ia32-msvc': 1.83.0 + '@oxlint/binding-win32-x64-msvc': 1.83.0 + + pako@2.1.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + pathe@2.0.3: {} + + perfect-freehand@1.2.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + playwright-core@1.63.0: {} + + playwright@1.63.0: + dependencies: + playwright-core: 1.63.0 + + plotly.js-gl2d-dist-min@4.1.1: {} + + pngjs@7.0.0: {} + + postcss@8.5.28: + dependencies: + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + proxy-compare@3.0.1: {} + + proxy-memoize@3.0.1: + dependencies: + proxy-compare: 3.0.1 + + react-dom@19.3.0(react@19.3.0): + dependencies: + react: 19.3.0 + scheduler: 0.28.0 + + react-hook-tanstack-virtual@0.0.4(patch_hash=2d69c3f291348e61b3d4e3f96732527872259fee1c4df92376fff4ebc8432072)(@tanstack/react-virtual@3.14.13(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(@tanstack/virtual-core@3.17.11)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + dependencies: + '@tanstack/react-virtual': 3.14.13(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/virtual-core': 3.17.11 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + react-i18next@17.0.14(i18next@26.4.2(typescript@7.0.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@7.0.2): + dependencies: + '@babel/runtime': 7.29.7 + html-parse-stringify: 4.0.1 + i18next: 26.4.2(typescript@7.0.2) + react: 19.3.0 + use-sync-external-store: 1.7.0(react@19.3.0) + optionalDependencies: + react-dom: 19.3.0(react@19.3.0) + typescript: 7.0.2 + + react-is@16.13.1: {} + + react@19.3.0: {} + + resolve-from@4.0.0: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rolldown@1.2.8: + dependencies: + '@oxc-project/types': 0.149.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.8 + '@rolldown/binding-android-arm64': 1.2.8 + '@rolldown/binding-darwin-arm64': 1.2.8 + '@rolldown/binding-darwin-x64': 1.2.8 + '@rolldown/binding-freebsd-x64': 1.2.8 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.8 + '@rolldown/binding-linux-arm64-gnu': 1.2.8 + '@rolldown/binding-linux-arm64-musl': 1.2.8 + '@rolldown/binding-linux-ppc64-gnu': 1.2.8 + '@rolldown/binding-linux-s390x-gnu': 1.2.8 + '@rolldown/binding-linux-x64-gnu': 1.2.8 + '@rolldown/binding-linux-x64-musl': 1.2.8 + '@rolldown/binding-openharmony-arm64': 1.2.8 + '@rolldown/binding-win32-arm64-msvc': 1.2.8 + '@rolldown/binding-win32-x64-msvc': 1.2.8 + + scheduler@0.28.0: {} + + semver@6.3.1: {} + + seroval-plugins@1.6.7(seroval@1.6.7): + dependencies: + seroval: 1.6.7 + + seroval@1.6.7: {} + + siginfo@2.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + socket.io-client@4.8.3: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + engine.io-client: 6.6.6 + socket.io-parser: 4.2.7 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.7: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + source-map-js@1.2.1: {} + + source-map@0.5.7: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + stylis@4.2.0: {} + + supports-preserve-symlinks-flag@1.0.0: {} + + tinybench@6.1.4: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + tinykeys@4.0.0: {} + + tinypool@2.1.2: {} + + tinyrainbow@3.1.1: {} + + totalist@3.0.1: {} + + tslib@2.8.1: {} + + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + undici-types@8.9.0: {} + + update-browserslist-db@1.3.3(browserslist@4.28.9): + dependencies: + browserslist: 4.28.9 + escalade: 3.2.0 + picocolors: 1.1.1 + + uqr@0.1.3: {} + + use-sync-external-store@1.7.0(react@19.3.0): + dependencies: + react: 19.3.0 + + vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.8 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.5.1 + esbuild: 0.27.7 + fsevents: 2.3.3 + yaml: 2.9.1 + + vitest@5.0.0(@types/node@26.5.1)(@vitest/browser-playwright@5.0.0)(@vitest/ui@5.0.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)): + dependencies: + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.0(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1)) + chai: 6.2.2 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.3.1 + obug: 2.2.1 + picomatch: 4.0.7 + std-env: 4.2.0 + tinybench: 6.1.4 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + vite: 8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 26.5.1 + '@vitest/browser-playwright': 5.0.0(playwright@1.63.0)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.27.7)(yaml@2.9.1))(vitest@5.0.0) + '@vitest/ui': 5.0.0(vitest@5.0.0) + transitivePeerDependencies: + - msw + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + ws@8.21.3: {} + + xmlhttprequest-ssl@2.1.2: {} + + yallist@3.1.1: {} + + yaml@1.10.3: {} + + yaml@2.9.1: {} + + zod@4.6.5: {} + + zustand@4.5.7(@types/react@19.3.0)(react@19.3.0): + dependencies: + use-sync-external-store: 1.7.0(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + react: 19.3.0 diff --git a/invokeai/frontend/webv2/pnpm-workspace.yaml b/invokeai/frontend/webv2/pnpm-workspace.yaml new file mode 100644 index 00000000000..cb8c26c003d --- /dev/null +++ b/invokeai/frontend/webv2/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +patchedDependencies: + react-hook-tanstack-virtual: patches/react-hook-tanstack-virtual.patch diff --git a/invokeai/frontend/webv2/public/invoke-icon.svg b/invokeai/frontend/webv2/public/invoke-icon.svg new file mode 100755 index 00000000000..3a3d4d553b9 --- /dev/null +++ b/invokeai/frontend/webv2/public/invoke-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/invokeai/frontend/webv2/public/locales/ar.json b/invokeai/frontend/webv2/public/locales/ar.json new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/invokeai/frontend/webv2/public/locales/ar.json @@ -0,0 +1 @@ +{} diff --git a/invokeai/frontend/webv2/public/locales/az.json b/invokeai/frontend/webv2/public/locales/az.json new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/invokeai/frontend/webv2/public/locales/az.json @@ -0,0 +1 @@ +{} diff --git a/invokeai/frontend/webv2/public/locales/bg.json b/invokeai/frontend/webv2/public/locales/bg.json new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/invokeai/frontend/webv2/public/locales/bg.json @@ -0,0 +1 @@ +{} diff --git a/invokeai/frontend/webv2/public/locales/de.json b/invokeai/frontend/webv2/public/locales/de.json new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/invokeai/frontend/webv2/public/locales/de.json @@ -0,0 +1 @@ +{} diff --git a/invokeai/frontend/webv2/public/locales/en-GB.json b/invokeai/frontend/webv2/public/locales/en-GB.json new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/invokeai/frontend/webv2/public/locales/en-GB.json @@ -0,0 +1 @@ +{} diff --git a/invokeai/frontend/webv2/public/locales/en.json b/invokeai/frontend/webv2/public/locales/en.json new file mode 100644 index 00000000000..025608e4354 --- /dev/null +++ b/invokeai/frontend/webv2/public/locales/en.json @@ -0,0 +1,4597 @@ +{ + "alphaNotice": { + "body": "This is an alpha build of the new Invoke interface. Expect rough edges, missing features, and behavior that changes between releases.", + "dismiss": "Got it", + "reportLink": "file an issue on GitHub", + "reportPrefix": "When something breaks or looks wrong, please", + "title": "Alpha build" + }, + "commandPalette": { + "title": "Command palette", + "buttonLabel": "Command palette", + "buttonTooltip": "Command palette ({{hotkey}})", + "inputLabel": "Search commands and settings", + "layoutPresetCommand": "{{name}} layout", + "resultsLabel": "Command palette results", + "placeholders": { + "root": "Search commands, settings, and more…", + "scope": "Search {{label}}…", + "stage": "Pick a value…" + }, + "actions": { + "backToAll": "Back to all results", + "closeScope": "Close {{label}} — back to all results", + "openInSettings": "Open in Settings", + "openModelManager": "Open in Model Manager", + "revealInGallery": "Reveal in Gallery" + }, + "footer": { + "back": "Back", + "close": "Close", + "navigate": "Navigate", + "pick": "Pick", + "run": "Run", + "scope": "Scope" + }, + "states": { + "couldNotSearch": "Couldn't search {{label}}", + "invalidDate": "Invalid date: “{{value}}”", + "noItemsYet": "No {{label}} yet", + "noResults": "No results for “{{query}}”", + "partialFailure": "{{count}} results. {{failed}} searches failed.", + "resultCount": "{{count}} results.", + "retrySearch": "Retry {{label}} search", + "searching": "Searching…", + "searchingSection": "{{label}} — Searching…", + "semanticTextUnavailable": "The image index can't search by text right now. Its embedding model may lack a text encoder; searching the gallery by image still works." + }, + "date": { + "aWeekAgo": "A week ago", + "date": "Date", + "day": "{{date}}", + "formatHint": "Or type a date — YYYY-MM-DD, 7d, 2w, 1m", + "from": "From {{date}}", + "pastWeek": "Past week", + "range": "{{from}} – {{to}}", + "through": "Through {{date}}", + "today": "Today", + "yesterday": "Yesterday" + }, + "syntax": { + "commands": "Type > for commands", + "commandsAndDates": "Type > for commands · from:/date: to filter by date" + }, + "search": { + "byDate": "Search {{label}} by date", + "forQuery": "Search {{label}} for “{{query}}”", + "in": "Search in", + "provider": "Search {{label}}…" + }, + "groups": { + "app": "App", + "boards": "Boards", + "canvas": "Canvas", + "commands": "Commands", + "date": "Date", + "gallery": "Gallery", + "generation": "Generation", + "images": "Images", + "layout": "Layout", + "models": "Models", + "navigation": "Navigation", + "promptHistory": "Prompt history", + "queue": "Queue", + "queueItems": "Queue items", + "recent": "Recent", + "settings": "Settings", + "viewer": "Viewer", + "workflows": "Workflows" + }, + "launchpad": { + "goToFonts": "Go to Fonts", + "goToModels": "Go to Models", + "goToNodes": "Go to Nodes", + "goToProjects": "Go to Projects", + "goToUsers": "Go to Users", + "openEditor": "Open Editor" + }, + "appEntries": { + "keyboardShortcuts": "Keyboard Shortcuts", + "openSettings": "Open Settings" + }, + "providers": { + "boardSubtitle": "Board · {{count}} image", + "boardSubtitle_other": "Board · {{count}} images", + "defaultWorkflow": "Default workflow", + "images": "Images", + "models": "Models", + "noTitle": "Untitled generation", + "promptHistory": "Prompt history", + "queueItems": "Queue items", + "semanticImages": "Images by meaning", + "uncategorized": "Uncategorized", + "workflow": "Workflow", + "workflows": "Workflows" + }, + "queueStatuses": { + "canceled": "Canceled", + "completed": "Completed", + "failed": "Failed", + "in_progress": "In progress", + "pending": "Pending", + "waiting": "Waiting" + }, + "settings": { + "current": "Current", + "off": "Off", + "on": "On", + "sections": { + "appearance": "Appearance", + "behavior": "Behavior", + "developer": "Developer", + "hotkeys": "Hotkeys", + "project": "Project", + "queue": "Queue", + "workflow": "Workflow", + "workspace": "Workspace" + }, + "settingsSection": "Settings: {{section}}", + "toggles": { + "confirmImageDeletion": "Confirm Image Deletion", + "developerLogEnabled": "Developer Logging", + "enableInformationalPopovers": "Informational Popovers", + "enableModelDescriptions": "Model Descriptions", + "reduceMotion": "Reduce Motion", + "showFocusRegionHighlight": "Show Focus Region Highlight", + "workflowShowMinimap": "Show Workflow Minimap", + "workflowSnapToGrid": "Snap Workflow Nodes to Grid", + "workflowValidateConnections": "Validate Workflow Connections" + }, + "values": { + "activeProject": "Active Project", + "allProjects": "All Projects", + "curved": "Curved", + "language": "Language", + "queueJobsScope": "Queue Jobs Scope", + "square": "Square", + "theme": "Theme", + "workflowEdgeStyle": "Workflow Edge Style" + } + }, + "workflowLoad": { + "couldNotLoad": "Could not load the workflow.", + "failed": "Failed to load workflow", + "loaded": "Loaded “{{name}}” from library", + "warning": "Workflow load warning" + } + }, + "common": { + "actions": "Actions", + "active": "Active", + "add": "Add", + "all": "All", + "apply": "Apply", + "archived": "Archived", + "asc": "Asc", + "assets": "Uploads", + "cancel": "Cancel", + "cancelFailed": "Cancel failed", + "canceled": "Canceled", + "clear": "Clear", + "clearSearch": "Clear search", + "dontShowMeThese": "Don't show me these", + "learnMore": "Learn more", + "noSchedulersFound": "No schedulers found", + "openSelector": "Open selector", + "searchSchedulers": "Search schedulers…", + "close": "Close", + "colorPicker": { + "format": "Color format", + "sampleFromCanvas": "Pick a color from the canvas", + "sampleFromScreen": "Pick a color from the screen", + "opacity": "Opacity (%)", + "hue": "Hue", + "saturationValue": "Saturation and value", + "saturationValueText": "Saturation {{saturation}}%, value {{value}}%" + }, + "scrubber": { + "editValue": "Edit {{label}}" + }, + "list": { + "selectItem": "Select {{name}}" + }, + "configure": "Configure", + "copy": "Copy", + "crop": "Crop", + "clipboardBlocked": "Clipboard access was blocked by the browser.", + "couldNotCopy": "Could not copy", + "countOfTotal": "{{count}} of {{total}}", + "created": "Created", + "delete": "Delete", + "desc": "Desc", + "details": "Details", + "discard": "Discard", + "discardAll": "Discard All", + "done": "Done", + "duplicate": "Duplicate", + "edit": "Edit", + "error": "Error", + "export": "Export", + "failed": "Failed", + "generating": "Generating", + "graph": "Graph", + "import": "Import", + "id": "ID", + "item": "Item", + "json": "JSON", + "lastSaved": "Last saved", + "loadingApplication": "Loading Application", + "media": "Media", + "negative": "Negative", + "name": "Name", + "nothingHereYet": "Nothing here yet.", + "none": "None", + "notYet": "Not yet", + "open": "Open", + "pageNumber": "Page {{page}}", + "pages": "Pages", + "plusMore": " +{{count}} more", + "previousPage": "Previous page", + "prompt": "Prompt", + "project": "Project", + "recent": "Recent", + "rename": "Rename", + "required": "Required", + "refresh": "Refresh", + "reload": "Reload", + "reset": "Reset", + "retry": "Retry", + "saved": "Saved", + "save": "Save", + "seed": "Seed", + "settings": "Settings", + "settingsWithShortcut": "Settings ({{hotkey}})", + "nextPage": "Next page", + "somethingWentWrong": "Something went wrong.", + "swap": "Swap", + "status": { + "canceled": "Canceled", + "completed": "Completed", + "failed": "Failed", + "pending": "Pending" + }, + "total": "Total", + "unknownError": "Unknown error.", + "unknownTime": "Unknown time", + "view": "View", + "viewJson": "View JSON", + "widgetViewUnavailable": "Widget view unavailable.", + "loading": "Loading…", + "seedMode": { + "label": "Seed mode", + "random": "Random", + "randomDescription": "Choose a fresh starting seed for each submission.", + "fixed": "Fixed", + "fixedDescription": "Reuse the entered seed.", + "increment": "Increment", + "incrementDescription": "Use successive seeds, increasing by 1.", + "decrement": "Decrement", + "decrementDescription": "Use successive seeds, decreasing by 1." + }, + "newSeed": "New seed", + "shuffle": "Shuffle", + "seedNextBatch": "Next batch: {{seed}}", + "seedNextBatchRange": "Next batch: {{first}} → {{last}}", + "decreaseValue": "Decrease value", + "increaseValue": "Increase value" + }, + "app": { + "name": "Invoke AI", + "nameWithVersion": "{{name}} {{version}}" + }, + "auth": { + "account": "Account", + "accountSettings": "Account settings", + "accountUpdated": "Account updated", + "administrator": "Administrator", + "changePassword": "Change password", + "confirmNewPassword": "Confirm new password", + "confirmPassword": "Confirm password", + "couldNotCreateAdmin": "Could not create the administrator account.", + "couldNotUpdateAccount": "Could not update your account.", + "createAdminAccount": "Create administrator account", + "currentPassword": "Current password", + "displayNameHelp": "Optional — shown instead of your email.", + "hidePassword": "Hide password", + "keepSignedIn": "Keep me signed in for a week", + "leaveBlankKeepPassword": "Leave blank to keep your password.", + "password": "Password", + "passwordGeneratedDescription": "Reveal it with the eye icon and store it somewhere safe.", + "passwordStrength": { + "moderate": "Moderate", + "strong": "Strong", + "weak": "Weak" + }, + "profileDisplayNameHelp": "Shown instead of your email across the workspace.", + "sessionExpired": "Your session expired. Sign in again to continue.", + "setupFooter": "You can add more users later from the user menu.", + "setupSubtitle": "Create the administrator account to get started.", + "setupTitle": "Set up Invoke", + "showPassword": "Show password", + "signIn": "Sign in", + "signInFailed": "Sign-in failed. Check your email and password.", + "signInSubtitle": "Sign in to your workspace to continue.", + "signedInAs": "Signed in as {{email}}", + "signOut": "Sign out", + "welcomeTitle": "Welcome to Invoke", + "yourPassword": "Your password" + }, + "splash": { + "artworkBy": "Artwork by {{artist}}", + "copyright": "© 2022-{{year}} InvokeAI Contributors | All rights reserved", + "loadingApplication": "Loading Application", + "loadingWorkspace": "Loading workspace", + "openingProject": "Opening project", + "tagline": "Image Generation for Creatives" + }, + "notifications": { + "empty": "Successful operations, errors, and system messages appear here.", + "labelWithCount": "Notifications: {{label}}", + "newCount_one": "{{count}} new", + "newCount_other": "{{count}} new", + "totalCount_one": "{{count}} total", + "totalCount_other": "{{count}} total", + "kind": { + "error": "Error", + "info": "Info", + "success": "Success" + }, + "markRead": "Mark Read" + }, + "topbar": { + "appMenu": { + "documentation": "Documentation", + "manage": "Manage", + "open": "Open menu" + }, + "invoke": { + "addToQueue": "Add to queue", + "addingImagesTo": "Adding images to {{destination}}", + "expandingPrompts": "Expanding prompts…", + "invoke": "Invoke", + "noNodes": "No nodes in graph", + "preparing": "Preparing canvas generation…", + "unavailable": "Invoke unavailable: {{reason}}", + "unrunnable": "route is not runnable", + "unableToQueue": "Unable to queue" + }, + "iterations": { + "label": "Iterations", + "tooltip_one": "{{count}} iteration per invoke", + "tooltip_other": "{{count}} iterations per invoke" + }, + "presets": { + "builtIn": "Built-in", + "custom": "Custom", + "customLayouts": "Custom layouts", + "defaultDestination": "Default destination", + "defaultSource": "Default source", + "delete": "Delete preset", + "deleteBody": "Delete “{{name}}”? This only removes the saved preset.", + "deleteNamed": "Delete {{name}}", + "deleteQuestion": "Delete layout preset?", + "deleteWithEllipsis": "Delete preset…", + "edit": "Edit layout preset", + "edited": "Edited", + "editNamed": "Edit {{name}}", + "editWithEllipsis": "Edit preset…", + "icon": "Icon", + "iconPicker": "Layout preset icon", + "layoutPreset": "Layout preset", + "manage": "Manage presets", + "more": "More layout presets", + "moreAvailable": "More layout presets, {{count}} available", + "name": "Preset name", + "none": "No custom presets yet. Arrange the dock, then choose Save as new preset.", + "noInvocationSource": "This layout does not contain an invocation source.", + "reorderInstructions": "To reorder a layout preset, press Space. Use the Left and Right arrow keys to move it, then press Space to drop or Escape to cancel.", + "reorderNamed": "Reorder {{name}}", + "restore": "Restore shipped preset defaults", + "restoreNamed": "Restore {{name}} defaults", + "revert": "Revert to saved layout", + "save": "Save preset", + "saveAs": "Save as new preset", + "saveAsTooltip": "Save this layout as a new preset", + "saveChanges": "Save changes", + "switch": "Switch to this layout", + "unsaved": "Unsaved changes" + }, + "projectSwitcher": { + "trigger": "Switch project. Current project: {{name}}" + }, + "queue": { + "actions": "Queue actions", + "backendBatch": "Backend batch {{id}}", + "backendItem": "backend item {{id}}", + "batchImage": "Batch image {{current}}/{{total}}", + "currentImageProgress": "Current image progress", + "idle": "Queue idle", + "imageProgress": "Image {{current}} of {{total}}", + "imageProgressPercent": "Image {{current}} of {{total}}, {{percent}} percent", + "loadingImage": "Loading image {{current}}/{{total}}", + "loadingModels": "Loading models", + "loadingModelsCount_one": "Loading {{count}} model", + "loadingModelsCount_other": "Loading {{count}} models", + "more": "{{count}} more…", + "noActiveBatches": "No active or pending generation batches.", + "queuedImages_one": "{{count}} image queued.", + "queuedImages_other": "{{count}} images queued.", + "queueItem": "Queue item {{id}}", + "remaining_one": "{{count}} image remaining. Open queue", + "remaining_other": "{{count}} images remaining. Open queue", + "route": "{{source}} to {{destination}} · {{graph}}", + "shiftCancelsAll": "Shift-click cancels all", + "waitingForProgress": "Backend accepted batch; waiting for progress event.", + "waitingToStart": "Waiting to start" + }, + "routing": { + "change": "Change routing", + "destination": "Destination", + "lockDestination": "Lock output destination", + "lockRouting": "Lock routing", + "lockSource": "Lock source", + "source": "Source", + "unlockDestination": "Unlock output destination", + "unlockRouting": "Unlock routing", + "unlockSource": "Unlock source" + } + }, + "projects": { + "allSavedAlreadyOpen": "All saved projects are already open.", + "activeRunsMustFinish": "Wait for active queue runs to finish first.", + "closeProjectLabel": "Close {{name}}", + "canvasPixelsNotSaved": "The project stays open because its canvas changes are not saved yet. {{reason}}", + "closeBlocked": "Project not closed", + "couldNotOpen": "Could not open project", + "couldNotOpenDescription": "\"{{name}}\" could not be loaded from the server.", + "createNewProject": "Create new project", + "deleteFailed": "Delete failed", + "deleteProject": "Delete project", + "deleteProjectBoardNote": "Its board goes with it; the images and videos it held return to Uncategorized.", + "deleteProjectCardBody": "Delete \"{{name}}\"? The project is removed from the server permanently.", + "deleteProjectQuestion": "Delete project?", + "deleteProjectTabBody": "Delete \"{{name}}\"? The project and its saved copy on the server are removed permanently. To keep it in your library, close the tab instead.", + "deleteProjectWithEllipsis": "Delete project…", + "duplicateFailed": "Duplicate failed", + "duplicating": "Duplicating project…", + "editedRelative": "Edited {{time}}", + "exportFailed": "Export failed", + "exported": "Exported \"{{name}}\"", + "exporting": "Exporting \"{{name}}\"…", + "failedToLoad": "Failed to load your projects.", + "requiresNewerInvoke": "Requires a newer Invoke", + "legacyFormat": "Older project format", + "file": { + "bundlingProgress": "Bundling {{completed}} of {{total}} assets…", + "damaged": "This project file is damaged and cannot be opened.", + "damagedProject": "This project cannot be read and could not be copied.", + "legacyCanvasProject": "This is a canvas project from an earlier version of Invoke, which cannot be opened here.", + "missingBoardItems_one": "1 board item could not be included.", + "missingBoardItems_other": "{{count}} board items could not be included.", + "missingReferences_one": "1 project reference could not be included.", + "missingReferences_other": "{{count}} project references could not be included.", + "notAProject": "This file is not an Invoke project.", + "notSynced": "This project has changes that have not reached the server yet. Try again once it reconnects.", + "updateClient": "This project now requires a newer version of Invoke. Your local changes remain saved in this browser.", + "supersededElsewhere": "This project was changed on another device. Your local edits continue in the recovered copy — use that one instead.", + "packing": "Packing the archive…", + "restoringProgress": "Restoring {{completed}} of {{total}} assets…", + "tooLarge": "This project file is too large to open.", + "tooLargeToWrite": "This project is too large to package into a project file.", + "unstarredItems_one": "1 item could not be re-starred.", + "unstarredItems_other": "{{count}} items could not be re-starred.", + "unsupportedVersion": "This project file was written by a newer version of Invoke." + }, + "gridView": "Grid view", + "groups": { + "all": "All projects", + "month": "Earlier this month", + "older": "Older", + "open": "Open now", + "pinned": "Pinned", + "today": "Today", + "week": "Earlier this week" + }, + "imported": "Imported \"{{name}}\"", + "importFailed": "Import failed", + "importing": "Importing project…", + "importWithEllipsis": "Import…", + "listView": "List view", + "load": { + "invalid": "\"{{name}}\" contains canvas data this version of Invoke cannot read, and it was not opened.", + "multiple": "{{count}} saved projects were not opened because this version of Invoke cannot read them: {{names}}.", + "legacyVersion": "\"{{name}}\" was saved by an older version of Invoke and cannot be opened here.", + "malformed": "\"{{name}}\" is damaged and was not opened. Its saved copy is untouched and can be exported.", + "unsupportedVersion": "\"{{name}}\" was saved by a newer version of Invoke and cannot be opened here." + }, + "newProject": "New project", + "newProjectStart": "Choose how to start", + "noSavedProjects": "No saved projects yet.", + "noSavedProjectsDescription": "Create your first project to start generating.", + "noSearchMatches": "No projects match “{{search}}”.", + "noSearchMatchesDescription": "Try a different name, or clear the search.", + "openProject": "Open project", + "openProjectLabel": "Open {{name}}", + "openProjects": "Open projects", + "pin": "Pin", + "projectDetails": "Project details", + "projectDuplicated": "Project duplicated", + "projectDuplicatedDescription": "\"{{name}}\" was created.", + "renameFailed": "Rename failed", + "renameProject": "Rename project", + "renameProjectNameLabel": "Project name", + "renameWithEllipsis": "Rename…", + "searchLabel": "Search projects", + "searchPlaceholder": "Search projects…", + "sort": { + "created": "Date created", + "edited": "Last edited", + "label": "Sort by", + "name": "Name" + }, + "unpin": "Unpin", + "viewLabel": "Project layout", + "fonts": { + "exportTitle": "Export {{name}}", + "includeFiles": "Include font files", + "exportDescription": "Without font files, recipients may need to install or replace fonts used in this project.", + "rightsReminder": "Only include fonts you have permission to redistribute. Recipients import them into their private libraries.", + "exportAction": "Export project", + "importTitle": "Font storage limit reached", + "quotaDescription": "This server has insufficient font storage. Import with font references, then install or replace missing fonts before generating or exporting images.", + "importReferences": "Import without font files", + "installing": "Installing fonts {{completed}} / {{total}}" + }, + "deleteIntermediates": "Delete intermediates…" + }, + "users": { + "accountCount_one": "{{count}} account in this workspace.", + "accountCount_other": "{{count}} accounts in this workspace.", + "activeLabel": "{{name}} active", + "addUser": "Add user", + "admin": "Admin", + "adminOnly": "User management is available to administrators only.", + "administrator": "Administrator", + "administratorHelp": "Can manage users and all workspace settings.", + "backendRejectedChange": "The backend rejected the change.", + "backendRejectedRequest": "The backend rejected the request.", + "cannotChangeSelfRole": "You cannot change your own role.", + "cannotDeactivateSelf": "You cannot deactivate your own account.", + "cannotDeleteSelf": "You cannot delete your own account.", + "couldNotActivate": "Could not activate the user", + "couldNotCreate": "Could not create the user.", + "couldNotDeactivate": "Could not deactivate the user", + "couldNotDelete": "Could not delete the user", + "couldNotGeneratePassword": "Could not generate a password", + "couldNotLoad": "Could not load users.", + "couldNotUpdate": "Could not update the user.", + "createUser": "Create user", + "created": "User created", + "deleted": "User deleted", + "deleteBody": "Delete {{name}}? Their account is removed permanently.", + "deleteTitle": "Delete user?", + "deleteUser": "Delete user", + "deleteUserNamed": "Delete {{name}}", + "description": "Manage workspace accounts and roles.", + "displayName": "Display name", + "displayNameHelp": "Optional — shown instead of the email.", + "editUser": "Edit user", + "editUserNamed": "Edit {{name}}", + "email": "Email", + "generate": "Generate", + "lastSignIn": "Last sign-in", + "leaveBlankPassword": "Leave blank to keep the current password.", + "manageUsers": "Manage users", + "management": "User management", + "never": "Never", + "newPassword": "New password", + "password": "Password", + "passwordGenerated": "Password generated", + "passwordGeneratedDescription": "Reveal it with the eye icon and share it with the user securely.", + "role": "Role", + "saveChanges": "Save changes", + "thisUser": "this user", + "title": "Users", + "updated": "User updated", + "user": "User", + "you": "You", + "manageIntermediatesNamed": "Manage intermediates of {{name}}" + }, + "fonts": { + "title": "Fonts", + "description": "Browse fonts available to this Invoke workspace and add your own files.", + "filterLabel": "Font library scope", + "filterMenu": "Filter fonts", + "library": "Font library", + "filters": { + "all": "All", + "my": "My fonts", + "shared": "Shared" + }, + "searchLabel": "Search fonts", + "searchPlaceholder": "Search fonts…", + "upload": "Upload fonts", + "uploadShared": "Upload shared", + "loadMore": "Load more fonts", + "loadingMore": "Loading more fonts…", + "uploadScopeLabel": "Upload destination", + "rescan": "Rescan directory", + "scope": { + "private": "My font", + "shared": "Shared" + }, + "source": { + "directory": "Server directory", + "uploaded": "Uploaded" + }, + "previewText": "The quick brown fox jumps over the lazy dog.", + "loadingPreview": "Loading preview…", + "retryPreview": "Retry preview", + "axisCount_one": "{{count}} axis", + "axisCount_other": "{{count}} axes", + "namedInstances_one": "{{count}} named instance", + "namedInstances_other": "{{count}} named instances", + "fileSize": "{{bytes}}", + "emptyTitle": "No fonts available", + "emptyDescription": "Upload a TTF, OTF, WOFF, or WOFF2 file, or add one to the server fonts directory.", + "noSearchMatches": "No fonts match this search", + "noSearchMatchesDescription": "Try a different name or clear the search.", + "couldNotLoad": "Could not load fonts", + "rescanFailed": "Could not rescan the fonts directory", + "uploading": "Uploading…", + "uploaded": "Uploaded", + "duplicate": "Already available", + "uploadFailed": "Upload failed", + "deleteNamed": "Delete {{name}}", + "deleteTitle": "Delete font?", + "deleteBody": "Delete {{name}}? Existing projects keep their reference and may need a replacement.", + "deleteConfirm": "Delete font", + "deleteFailed": "Could not delete the font", + "missing": { + "warning_one": "Missing font — review", + "warning_other": "Missing fonts — review", + "title": "Missing fonts", + "description": "Some text fonts are unavailable. Previews use a fallback. Export, baking, and generation that use this text remain blocked until its fonts are restored or replaced.", + "continue": "Continue with previews", + "layers_one": "Used by {{count}} text layer", + "layers_other": "Used by {{count}} text layers", + "search": "Search replacement fonts", + "replacement": "Replacement font", + "select": "Select a replacement", + "upload": "Upload font", + "replaceAll": "Replace all uses", + "axesNotice": "Replacement preserves supported variation axes within their limits and resets unsupported axes. You can undo this change.", + "catalogError": "Unable to load the font library. Retry to reconnect.", + "failed": "The font could not be replaced. Finish the current Canvas operation and retry." + }, + "addFonts": "Add Fonts", + "details": "Details", + "selectFont": "Select a font", + "selectFontDescription": "Choose a font from your library to preview it and view its details.", + "uploadDescription": "Upload TTF, OTF, WOFF or WOFF2 files. My fonts are private to your account; shared fonts are available to everyone on this server.", + "axesTitle": "Variation axes", + "axisDefault": "Default {{value}}", + "instancesTitle": "Named instances" + }, + "graphPreview": { + "back": "Back", + "cfgScale": "CFG scale", + "compiledFrom": "Compiled from {{source}}.", + "copied": "Copied", + "copyFailed": "Failed to copy JSON", + "copyJson": "Copy JSON", + "destination": "Destination", + "downloadJson": "Download JSON", + "downloadJsonHint": "For bug reports and sharing", + "edges": "Edges", + "edgesInNone": "in · none", + "edgesIn_one": "in · {{count}} input from {{sources}}", + "edgesIn_other": "in · {{count}} inputs from {{sources}}", + "edgesOut": "out · {{field}} → {{target}}", + "editInEditor": "Edit in workflow editor", + "editInEditorFailed": "No editable nodes in this graph.", + "editInEditorHint": "Replaces the current workflow", + "forkIntoProject": "Fork into new project", + "forkIntoProjectFailed": "No nodes to fork into a project.", + "forkIntoProjectHint": "Copies this graph into a fresh project", + "graph": "Graph", + "graphJsonLabel": "{{title}} graph JSON", + "inputCount_one": "{{count}} input", + "inputCount_other": "{{count}} inputs", + "invalidTitle": "This graph can't compile yet.", + "invokeRoute": "Invoke {{route}}", + "list": "List", + "liveHint": "Updates as you change settings.", + "loras": "LoRAs", + "model": "Model", + "noCompiledGraph": "No compiled graph is available for \"{{graphId}}\" yet.", + "nodeSummary": { + "denoise": "{{steps}} steps · cfg {{cfg}}", + "noise": "{{width}} × {{height}}" + }, + "nodes": "Nodes", + "nodesSkipped_one": "Skipped {{count}} node type without a template: {{types}}", + "nodesSkipped_other": "Skipped {{count}} node types without templates: {{types}}", + "openAs": "Open as", + "openedFromPreview": "Opened from graph preview", + "provenance": { + "cfgScale": "Generate → CFG scale", + "clipSkip": "Generate → CLIP skip", + "loras": "Generate → LoRAs", + "model": "Generate → Model", + "negativePrompt": "Generate → Negative prompt", + "positivePrompt": "Generate → Prompt", + "scheduler": "Generate → Scheduler", + "seed": "Generate → Seed", + "seedRandom": "Generate → Seed (random)", + "size": "Generate → Size", + "steps": "Generate → Steps" + }, + "resolvedInputs": "Resolved inputs", + "saveToLibrary": "Save to workflow library", + "saveToLibraryFailed": "No saveable nodes in this graph.", + "saveToLibraryHint": "Reusable, leaves this project alone", + "savedToLibrary": "Saved to workflow library", + "scheduler": "Scheduler", + "seed": "Seed", + "seedRandomValue": "random", + "seedRandomized": "Seed is randomized. This graph runs differently each time.", + "seedRegenerated": "regenerated each run", + "selectNode": "Select a node for details.", + "setBy": "Set by", + "showNode": "show node", + "size": "Size", + "steps": "Steps", + "thisGraph": "This graph", + "title": "Graph preview" + }, + "hotkeys": { + "addHotkey": "Add hotkey", + "bindings": "Hotkey bindings", + "cancelEdit": "Cancel hotkey edit", + "categories": { + "app": "App", + "canvas": "Canvas", + "gallery": "Gallery", + "viewer": "Viewer", + "workflows": "Workflows" + }, + "conflictsWith": "Conflicts with {{title}}", + "custom": "Custom", + "deleteHotkey": "Delete hotkey", + "description": "Account-bound keybinds. Project-level overrides can layer on later without changing hotkey ids.", + "disable": "Disable", + "disabled": "Disabled", + "duplicateBinding": "Duplicate binding in this hotkey", + "noMatches": "No hotkeys match your search", + "pending": "Pending", + "pressKeys": "Press keys", + "resetAll": "Reset all", + "resetHotkey": "Reset hotkey", + "saveEdit": "Save hotkey edit", + "searchPlaceholder": "Search hotkeys", + "title": "Hotkeys" + }, + "hints": { + "aspectRatio": { + "heading": "Aspect Ratio", + "paragraphs": [ + "The aspect ratio of the dimensions of the image generated.", + "An image size (in number of pixels) equivalent to 512x512 is recommended for SD1.5 models and a size equivalent to 1024x1024 is recommended for SDXL models." + ] + }, + "cfgRescale": { + "heading": "CFG Rescale Multiplier", + "paragraphs": [ + "Rescale multiplier for CFG guidance, used for models trained using zero-terminal SNR (ztsnr).", + "Suggested value of 0.7 for these models." + ] + }, + "cfgScale": { + "heading": "CFG Scale", + "paragraphs": [ + "Controls how much the prompt influences the generation process.", + "High CFG Scale values can result in over-saturation and distorted generation results." + ] + }, + "clipSkip": { + "heading": "CLIP Skip", + "paragraphs": [ + "How many layers of the CLIP model to skip.", + "Certain models are better suited to be used with CLIP Skip." + ] + }, + "colorCompensation": { + "heading": "Color Compensation", + "paragraphs": ["Adjust the input image to reduce color shifts during inpainting or img2img (SDXL Only)."] + }, + "concepts": { + "heading": "LoRA", + "paragraphs": ["Lightweight models that are used in conjunction with base models."] + }, + "conditioningRebalance": { + "heading": "Conditioning rebalance", + "paragraphs": [ + "Krea 2 reads your prompt through twelve tapped layers of its text encoder and stacks them together. Each bar sets how much one of those layers counts.", + "Shallow taps, on the left, track the prompt's literal wording. Deep taps, on the right, track its meaning and composition. Raising the deep taps pushes the model harder toward what you asked for; raising the shallow taps holds it closer to how you phrased it.", + "Gain scales the whole stack afterwards, which counteracts the quality dilution from distillation. Leave both at the defaults unless prompt adherence is the problem you are solving." + ] + }, + "creativity": { + "heading": "Creativity", + "paragraphs": [ + "Creativity controls the amount of freedom granted to the model when adding details. Low creativity stays close to the original image, while high creativity allows for more change. When using a prompt, high creativity increases the influence of the prompt." + ] + }, + "guidance": { + "heading": "Guidance", + "paragraphs": [ + "Controls how much the prompt influences the generation process.", + "High guidance values can result in over-saturation and high or low guidance may result in distorted generation results. Guidance only applies to FLUX DEV models." + ] + }, + "height": { + "heading": "Height", + "paragraphs": ["Height of the generated image. Must be a multiple of 8."] + }, + "imageInfluence": { + "heading": "Denoising Strength", + "paragraphs": [ + "Controls how much the generated image varies from the raster layers.", + "Lower strength stays closer to the combined visible raster layers. Higher strength relies more on the global prompt.", + "When there are no raster layers with visible content, this setting is ignored." + ] + }, + "hidiffusion": { + "heading": "HiDiffusion", + "paragraphs": [ + "Enables HiDiffusion denoising for higher-resolution generation.", + "Try at 1536px or higher for the most visible effect." + ] + }, + "hidiffusionRauNet": { + "heading": "HiDiffusion: RAU-Net", + "paragraphs": [ + "Enables RAU-Net blocks used by HiDiffusion.", + "Can improve structure and detail at higher resolutions." + ] + }, + "hidiffusionT1Ratio": { + "heading": "HiDiffusion: T1 Ratio", + "paragraphs": [ + "Controls the early switch point for HiDiffusion (T1).", + "Lower values switch earlier; higher values preserve global structure longer." + ] + }, + "hidiffusionT2Ratio": { + "heading": "HiDiffusion: T2 Ratio", + "paragraphs": [ + "Controls the late switch point for HiDiffusion (T2).", + "Higher values keep window attention active longer." + ] + }, + "hidiffusionWindowAttn": { + "heading": "HiDiffusion: Window Attention", + "paragraphs": [ + "Enables windowed attention used by HiDiffusion.", + "Can boost local detail, but may affect global coherence." + ] + }, + "layerStackControl": { + "heading": "Control Layers", + "paragraphs": [ + "Layers that guide generation with structural hints such as edges, depth, or pose maps.", + "Each control layer pairs its image with a control model and a weight that sets how strongly it steers the result." + ] + }, + "layerStackInpaintMask": { + "heading": "Inpaint Masks", + "paragraphs": [ + "Masks that mark regions of the image for regeneration.", + "Painted areas are regenerated on the next invoke; noise and denoise-limit modifiers tune how strongly the area may change." + ] + }, + "layerStackRaster": { + "heading": "Raster Layers", + "paragraphs": [ + "The pixel layers that compose your image and feed generation.", + "Layers apply top to bottom, and each can carry a stack of non-destructive adjustments such as levels, curves, or hue." + ] + }, + "layerStackRegionalGuidance": { + "heading": "Regional Guidance", + "paragraphs": [ + "Regions that apply prompts and reference images to part of the image.", + "Painted regions scope their prompts and reference images to just that area of the canvas." + ] + }, + "model": { + "heading": "Model", + "paragraphs": [ + "Model used for generation. Different models are trained to specialize in producing different aesthetic results and content." + ] + }, + "negativePrompt": { + "heading": "Negative Prompt", + "paragraphs": [ + "The generation process avoids the concepts in the negative prompt. Use this to exclude qualities or objects from the output.", + "Supports Compel syntax and embeddings." + ] + }, + "pidMode": { + "heading": "PiD Decode (Super-Resolution Decoder)", + "paragraphs": [ + "PiD replaces the standard VAE decode with NVIDIA's Pixel Diffusion Decoder, a diffusion-based 4x super-resolution decoder. It requires a PiD decoder model and a Gemma-2 caption encoder.", + "Fit: generate at your chosen resolution, PiD decodes it 4x, then downscales back to that size - extra detail at the same output size, and it composites onto the canvas (works for Image to Image too).", + "Native (4x): your dimensions are the 4x target. Generation runs at a quarter of them (e.g. 512 -> 2048) and PiD's full 4x output is used directly. The PiD decoders are trained for 2K output (512px sources), with 2K-to-4K variants for 4K.", + "Because PiD's diffusion decode reconstructs detail, you can usually lower the generation Steps to save time. 'Scale Before Processing' must be set to None while PiD is enabled." + ] + }, + "positivePrompt": { + "heading": "Positive Prompt", + "paragraphs": [ + "Guides the generation process. You may use any words or phrases.", + "Compel and Dynamic Prompts syntaxes and embeddings." + ] + }, + "referenceImage": { + "heading": "Global Reference Image", + "paragraphs": ["Applies a reference image to influence the entire generation."] + }, + "referenceImageWeight": { + "heading": "Reference Image Weight", + "paragraphs": [ + "How strongly this reference image steers the generation.", + "Lower weight treats it as a loose suggestion; higher weight holds the output closer to the reference." + ] + }, + "scheduler": { + "heading": "Scheduler", + "paragraphs": [ + "Scheduler used during the generation process.", + "Each scheduler defines how to iteratively add noise to an image or how to update a sample based on a model's output." + ] + }, + "seamlessTiling": { + "heading": "Seamless Tiling", + "paragraphs": [ + "Generates an image that tiles without visible seams.", + "Enable each axis independently to tile horizontally, vertically, or both." + ] + }, + "seed": { + "heading": "Seed", + "paragraphs": [ + "Controls the starting noise used for generation.", + "Disable the \"Random\" option to produce identical results with the same generation settings." + ] + }, + "steps": { + "heading": "Steps", + "paragraphs": [ + "Number of steps that will be performed in each generation.", + "Higher step counts will typically create better images but will require more generation time." + ] + }, + "structure": { + "heading": "Structure", + "paragraphs": [ + "Structure controls how closely the output image will keep to the layout of the original. Low structure allows major changes, while high structure strictly maintains the original composition and layout." + ] + }, + "clipEmbed": { + "heading": "CLIP Embed", + "paragraphs": [ + "FLUX.1 pairs its T5 encoder with a CLIP text encoder. A bare FLUX checkpoint does not carry one, so it must be chosen explicitly." + ] + }, + "t5Encoder": { + "heading": "T5 Encoder", + "paragraphs": [ + "FLUX.1 reads your prompt with a T5 text encoder. Most FLUX transformers ship as a bare checkpoint and cannot supply one, so it has to be selected here." + ] + }, + "tileControlNet": { + "heading": "ControlNet", + "paragraphs": [ + "ControlNets provide guidance to the generation process, helping create images with controlled composition, structure, or style, depending on the model selected." + ] + }, + "tileOverlap": { + "heading": "Tile Overlap", + "paragraphs": [ + "Controls the overlap between adjacent tiles during upscaling. Higher overlap values help reduce visible seams between tiles but use more memory.", + "The default value of 128 works well for most cases, but you can adjust based on your specific needs and memory constraints." + ] + }, + "tileSize": { + "heading": "Tile Size", + "paragraphs": [ + "Controls the size of tiles used during the upscaling process. Larger tiles use more memory but may produce better results.", + "SD1.5 models default to 768, while SDXL models default to 1024. Reduce tile size if you encounter memory issues." + ] + }, + "upscaleModel": { + "heading": "Upscale Model", + "paragraphs": [ + "The upscale model scales the image to the output size before details are added. Any supported upscale model may be used, but some are specialized for different kinds of images, like photos or line drawings." + ] + }, + "upscaleScale": { + "heading": "Scale", + "paragraphs": [ + "Scale controls the output image size, and is based on a multiple of the input image resolution. For example a 2x upscale on a 1024x1024 image would produce a 2048 x 2048 output." + ] + }, + "vae": { + "heading": "VAE", + "paragraphs": ["Model used for translating AI output into the final image."] + }, + "vaePrecision": { + "heading": "VAE Precision", + "paragraphs": [ + "The precision used during VAE encoding and decoding.", + "Fp16/Half precision is more efficient, at the expense of minor image variations." + ] + }, + "width": { + "heading": "Width", + "paragraphs": ["Width of the generated image. Must be a multiple of 8."] + } + }, + "models": { + "accessToken": "Access Token", + "accessTokenForDownload": "Access token for this download", + "accessTokenHelp": "Attached to the next Pull only. For saved HuggingFace, Civitai, and provider keys, use the API Keys tab.", + "accessTokenPlaceholder": "Only needed for protected sources", + "accessTokenRequiredMessage": "This source needs an access token. Add one to continue.", + "actions": "Model actions", + "addModels": "Add Models", + "addModelsResults": "Add models results", + "addToken": "Add token", + "addTriggerPhrase": "Add a trigger phrase...", + "allBases": "All bases", + "allInstalled": "all installed", + "allModels": "All models", + "apiKey": "API key", + "apiKeyConfigured": "API key configured", + "apiKeyFor": "{{title}} API key", + "apiKeys": "API Keys", + "apiKeysDescription": "Keys are used when installing protected models and when generating with external API models.", + "backendDisconnectedProgressStale": "Backend disconnected. Progress may be stale.", + "base": "Base", + "baseArchitecture": "Base Architecture", + "baseUrlFor": "{{title}} base URL", + "bulkDeleteBody_one": "Delete {{count}} selected model? Database records are removed, and files inside the InvokeAI models directory are deleted.", + "bulkDeleteBody_other": "Delete {{count}} selected models? Database records are removed, and files inside the InvokeAI models directory are deleted.", + "bulkDeleteConfirm_one": "Delete {{count}} Model", + "bulkDeleteConfirm_other": "Delete {{count}} Models", + "bulkDeleteFailed": "Bulk delete failed", + "bulkDeletePartialDescription": "{{deleted}} deleted; {{failed}} failed: {{error}}", + "bulkReidentifyBody_one": "Re-probe {{count}} selected model to re-detect its base, type, and format? Custom variants and default settings on it are reset.", + "bulkReidentifyBody_other": "Re-probe {{count}} selected models to re-detect their base, type, and format? Custom variants and default settings on them are reset.", + "bulkReidentifyConfirm_one": "Re-identify {{count}} Model", + "bulkReidentifyConfirm_other": "Re-identify {{count}} Models", + "bulkReidentifyFailed": "Bulk re-identify failed", + "bulkReidentifyPartialDescription": "{{succeeded}} re-identified; {{failed}} failed: {{error}}", + "bulkReidentifyTitle": "Re-identify selected models", + "bundleInstallQueued": "Bundle install queued", + "bundleInstallQueuedDescription_one": "{{name}}: {{count}} install queued.", + "bundleInstallQueuedDescription_other": "{{name}}: {{count}} installs queued.", + "bytePlusApiKeyPlaceholder": "API key from BytePlus console", + "bytesCopied": "{{bytes}} copied", + "cacheEmptied": "Model cache emptied", + "currentPath": "Current path", + "cancelAll": "Cancel all", + "cancelFailed": "Cancel failed", + "cancelInstall": "Cancel install", + "civitaiKey": "Civitai key", + "civitaiKeyDescription": "Attached automatically when installing civitai.com models that require login. Stored in this browser only.", + "civitaiKeyPlaceholder": "32-character key from civitai.com/user/account", + "cleanupOrphaned": "Clean up orphaned models…", + "clearFinished": "Clear finished", + "clearSelectedModel": "Clear selected model", + "clearSelection": "Clear selection", + "compactRows": "Compact rows", + "configPath": "Config Path", + "configPathHelp": "Path to the .yaml config file for legacy checkpoints.", + "conversionFailed": "Conversion failed", + "convert": "Convert", + "convertBody": "Convert \"{{name}}\" to the diffusers format in place? The original checkpoint file is replaced by a diffusers folder.", + "convertToDiffusers": "Convert to diffusers", + "cpuSettingSaved": "CPU setting saved", + "convertedToDiffusers": "Converted to diffusers", + "couldNotLoad": "Could not load models", + "couldNotLoadInstallQueue": "Could not load the install queue", + "couldNotLoadStarterModels": "Could not load starter models", + "starterModelsList": "Starter models", + "countToInstall": "{{count}} to install", + "customizeDefaultField": "Customize {{field}} for this model", + "customizedForThisModel": "Customized for this model", + "defaultFieldInherited": { + "cfgRescale": "App default: 0 (off)", + "cfgScale": "App default: 7", + "fp8Storage": "Weights stored at full precision", + "guidance": "Uses the generator default", + "height": "App default: 1024", + "preprocessor": "Chosen automatically per image", + "scheduler": "App default: euler_a", + "steps": "App default: 30", + "vae": "Uses the compatible default VAE", + "vaePrecision": "App default: fp16", + "weight": "App default: 0.75", + "weightMax": "Slider default: 2", + "weightMin": "Slider default: -1", + "width": "App default: 1024" + }, + "defaultFields": { + "cfgRescale": "CFG Rescale", + "cfgScale": "CFG Scale", + "fp8Storage": "FP8 storage", + "guidance": "Guidance", + "height": "Height", + "preprocessor": "Preprocessor", + "scheduler": "Scheduler", + "steps": "Steps", + "vae": "VAE", + "vaePrecision": "VAE Precision", + "weight": "Weight", + "weightMax": "Weight Max", + "weightMin": "Weight Min", + "width": "Width" + }, + "defaultSettings": "Default Settings", + "defaultVae": "Default VAE", + "defaultSettingsHelp": "Applied automatically when this model is selected. Off = use the app default.", + "defaultSettingsSaved": "Default settings saved", + "deleteBody": "Delete \"{{name}}\"? The database record is removed, and the model files are deleted if they live inside the InvokeAI models directory.", + "deleteFailed": "Delete failed", + "deleteModel": "Delete model", + "deleteSelectedTitle": "Delete selected models", + "deleteSelectedWithBytes": "Delete Selected ({{bytes}})", + "deleted": "Models deleted", + "deletedDescription_one": "{{count}} model deleted.", + "deletedDescription_other": "{{count}} models deleted.", + "description": "Description", + "details": "Model Details", + "dismissResults": "Dismiss results", + "dismissScanResults": "Dismiss scan results", + "downloadProgress": "Download progress", + "editing": "Editing", + "emptyCache": "Empty model cache", + "eta": { + "hours": "~{{count}} h left", + "minutes": "~{{count}} min left", + "seconds": "~{{count}}s left" + }, + "externalProviderKeyDescription": "Stored server-side and used for every request to this provider.", + "externalProviderKeysUnavailable": "Could not load external provider keys", + "exportSettings": "Export settings…", + "externalProvidersUnavailable": "External providers are unavailable: {{error}}", + "failedToEmptyCache": "Failed to empty model cache", + "failedToLinkModels": "Failed to link models.", + "failedToLoadExternalProviders": "Failed to load external providers.", + "failedToLoadModels": "Failed to load models.", + "failedToLoadRelatedModels": "Failed to load related models.", + "failedToRemoveModelImage": "Failed to remove model image.", + "failedToSaveCpuSetting": "Failed to save CPU setting.", + "failedToSaveDefaults": "Failed to save default settings.", + "failedToScanOrphaned": "Failed to scan for orphaned models.", + "failedToUnlink": "Failed to unlink models.", + "failedToUpdateTriggerPhrases": "Failed to update trigger phrases.", + "failedToUploadModelImage": "Failed to upload model image.", + "fileCount_one": "{{count}} file", + "fileCount_other": "{{count}} files", + "fileNotFoundOnDisk": "The file for this model was not found on disk.", + "fileSize": "File Size", + "filesInRepo_one": "{{count}} file in {{repo}}", + "filesInRepo_other": "{{count}} files in {{repo}}", + "filterAndSort": "Filter and sort models", + "filterAndSortStarterModels": "Filter and sort starter models", + "filterFiles": "Filter files", + "filterResults": "Filter results", + "format": "Format", + "formatHelp": "Changing the format re-classifies the model; invalid combinations are rejected.", + "fullRows": "Full rows", + "gatedRepoMessage": "This repo is gated. Add a Hugging Face token to continue.", + "hash": "Hash", + "huggingFaceKeyDescription": "For gated repos like FLUX. Stored on the InvokeAI server and verified with HuggingFace.", + "huggingFaceRejectedToken": "HuggingFace rejected this token. Check that it has read access.", + "huggingFaceToken": "HuggingFace token", + "install": "Install", + "installNamed": "Install {{name}}", + "installAllCount": "Install all ({{count}})", + "installAllQueued": "Installs queued", + "installAllQueuedDescription_one": "{{count}} install queued.", + "installAllQueuedDescription_other": "{{count}} installs queued.", + "installBundle": "Install bundle", + "installFailed": "Install failed", + "installFp8Storage": "FP8 storage", + "installFp8StorageTooltip": "Store the weights in FP8 to roughly halve VRAM use. Applied only where the model's loader supports it — a checkpoint that already stores FP8 weights gets it automatically, and models it cannot help (GGUF, NF4 and SDNQ among them) are left alone.", + "installInPlace": "Install in place", + "installJobSummary_one": "{{count}} job · no active installs", + "installJobSummary_other": "{{count}} jobs · no active installs", + "installJobs": "Install jobs", + "installQueue": "Install Queue", + "installed": "Installed", + "installing": "Installing", + "installsDependencies_one": " (installs {{count}} dependency)", + "installsDependencies_other": " (installs {{count}} dependencies)", + "imageEncoderModelId": "Image Encoder", + "importSettings": "Import settings…", + "invalidDefaultSettings": "Invalid default settings.", + "invalidPathFormat": "Must be an absolute path.", + "invalidTriggerPhrase": "Invalid trigger phrase.", + "keyStatus": { + "configured": "Configured", + "invalid": "Invalid", + "saved": "Saved", + "unknown": "Not set", + "valid": "Valid" + }, + "launchpad": { + "installing_one": "{{count}} model installing…", + "installing_other": "{{count}} models installing…", + "noModelsDescription": "Invoke needs at least one model before it can generate. Pick a starter bundle to see what's inside and install it from the model manager.", + "noModelsTitle": "No models installed", + "starterBundles": "Starter bundles" + }, + "library": "Model library", + "libraryMaintenance": "Model library maintenance", + "linkedModel": "Linked model", + "linkedToCurrentSelection": "Linked to the current selection", + "loadingModels": "Loading models…", + "manageApiKeys": "Manage API keys", + "manageModels": "Manage models", + "manager": "Model Manager", + "maximizeQueue": "Expand", + "maximizeQueueTooltip": "Fill the pane with the install queue", + "missingFile": "Missing file", + "missingFilesCount": "Missing files ({{count}})", + "modelAndDependenciesQueued_one": "{{name}} and {{count}} dependency", + "modelAndDependenciesQueued_other": "{{name}} and {{count}} dependencies", + "modelCount_one": "{{count}} model", + "modelCount_other": "{{count}} models", + "modelCoverAlt": "{{name}} cover", + "modelDeleted": "Model deleted", + "modelImage": "Model image", + "modelImageUpdated": "Model image updated", + "modelInstallCancelled": "Model install cancelled", + "modelInstallFailed": "Model install failed", + "modelInstallFailedToStart": "Model install failed to start", + "modelInstallQueued": "Model install queued", + "modelInstalled": "Model installed", + "modelManager": "Model manager", + "modelNoLongerInLibrary": "This model is no longer in the library.", + "modelReidentified": "Model re-identified", + "modelType": "Model Type", + "modelUpdated": "Model updated", + "moreQueueActions": "More queue actions", + "noInstallableModelFiles": "This HuggingFace repo has no installable model files.", + "noInstallsDescription": "Model installs appear here while they download and import.", + "noExternalProviders": "No external model providers are available on this server.", + "noInstallsYet": "No installs yet", + "noModelFilesFound": "No model files found in {{path}}.", + "noModelFilesFoundTitle": "No model files found", + "newPath": "New path", + "newPathPlaceholder": "/absolute/path/to/model.safetensors", + "noOrphaned": "No orphaned model folders found — your library is clean.", + "noRelatedModels": "No related models linked yet.", + "noStarterModelsPull": "No starter models match - press Pull to install the source you entered.", + "noStarterModelsSearch": "No starter models match your search.", + "noTriggerPhrasesYet": "No trigger phrases yet.", + "noneInstalled": "No models installed", + "noneInstalledDescription": "Use Add Models to install your first model.", + "noneMatchFilters": "No models match your filters", + "noneMatchFiltersDescription": "Try clearing the search or filters.", + "openModel": "Open model", + "orphanedCleanup": "Orphaned model cleanup", + "orphanedCleanupFailed": "Orphaned model cleanup failed", + "orphanedCleanupPartialDescription": "{{deleted}} deleted, {{failed}} failed: {{error}}", + "orphanedDeletedDescription_one": "{{count}} orphaned model folder deleted.", + "orphanedDeletedDescription_other": "{{count}} orphaned model folders deleted.", + "orphanedDescription": "Folders in the models directory with no database record — usually leftovers from failed installs or external deletes.", + "orphanedTitle": "Orphaned Models", + "overrideBaseUrl": "Override base URL", + "path": "Path", + "pathUpdateFailed": "Failed to update path", + "pathUpdated": "Path updated", + "pauseAll": "Pause all", + "pauseDownload": "Pause download", + "pauseFailed": "Pause failed", + "predictionType": "Prediction Type", + "press": "Press", + "progressNotStarted": "Not started", + "progressStopped": "Stopped", + "pruneFailed": "Prune failed", + "pull": "Pull", + "emptyCacheConfirmBody": "Unloads all models from RAM and VRAM. The next generation reloads them from disk.", + "emptyCacheConfirmTitle": "Empty model cache?", + "providerId": "Provider", + "providerModelId": "Provider Model ID", + "queueAttentionCount_one": "{{count}} needs attention", + "queueAttentionCount_other": "{{count}} need attention", + "queueActionFailed": "Queue action failed", + "queueActionPartialDescription": "{{failed}} of {{total}} failed: {{error}}", + "queueColumns": { + "actions": "Actions", + "name": "Name", + "progress": "Progress", + "state": "State", + "status": "Status" + }, + "queueDownloadingCount_one": "{{count}} downloading", + "queueDownloadingCount_other": "{{count}} downloading", + "queueInstalledCount_one": "{{count}} installed", + "queueInstalledCount_other": "{{count}} installed", + "queueInstallingCount_one": "{{count}} installing", + "queueInstallingCount_other": "{{count}} installing", + "queueMoreQueued_one": "+{{count}} queued", + "queueMoreQueued_other": "+{{count}} queued", + "queuePausedCount_one": "{{count}} paused", + "queuePausedCount_other": "{{count}} paused", + "queuePosition": "#{{position}} in queue", + "queueQueuedCount_one": "{{count}} queued", + "queueQueuedCount_other": "{{count}} queued", + "refreshFailed": "Failed to refresh models", + "refreshList": "Refresh model list", + "reidentified": "Models re-identified", + "reidentifiedDescription_one": "{{count}} model re-probed and updated.", + "reidentifiedDescription_other": "{{count}} models re-probed and updated.", + "reidentify": "Re-identify model", + "reidentifyFailed": "Re-identify failed", + "reidentifySelected": "Re-identify", + "removeFromList": "Remove from list", + "repoVariant": "Repo Variant", + "relatedModels": "Related Models", + "relatedModelsHelp": "Link the models that pair well with this one - they are suggested together elsewhere in the app.", + "removeTriggerPhrase": "Remove trigger phrase {{phrase}}", + "resetBaseFilters": "Reset base filters", + "restartFileFailed": "Restart file failed", + "restartThisFile": "Restart this file", + "restoreQueue": "Restore", + "restoreQueueTooltip": "Dock the install queue to the bottom of the pane", + "resumeAll": "Resume all", + "resumeDownload": "Resume download", + "resumeFailed": "Resume failed", + "resumeRequired": "Resume required", + "retryFailed": "Retry failed", + "retryFailedDownload": "Retry failed download", + "retryInstall": "Retry install", + "runOnCpu": "Run on CPU", + "runOnCpuHelp": "Run this encoder on CPU only to reduce VRAM usage. This may be slower.", + "runVaeOnCpu": "Run on CPU", + "runVaeOnCpuHelp": "Run this VAE on CPU only to reduce VRAM usage. This may be slower.", + "saveDefaults": "Save Defaults", + "scan": "Scan", + "scanFailed": "Scan failed", + "statusQueued": "Queued", + "statusUnauthorized": "Unauthorized", + "stopScan": "Stop", + "scanFolderTooltip": "Scan this folder for model files", + "scanSummary_one": "{{count}} file in {{path}} · {{notInstalled}} not installed", + "scanSummary_other": "{{count}} files in {{path}} · {{notInstalled}} not installed", + "scopeAvailable": "Available {{scope}}", + "scopeModels": "models", + "scopeNoCompatibleInstalled": "No compatible {{scope}} installed.", + "scopeNoMatchBases": "No {{scope}} match the selected bases.", + "scopeNoMatchSearch": "No {{scope}} match your search.", + "scopeSearch": "Search {{scope}}…", + "scopeSelect": "Select {{scope}}…", + "searchCompatibleToLink": "Search compatible models to link...", + "searchModels": "Search models", + "searchModelsPlaceholder": "Search models…", + "searchOrAdd": "Search or add a model", + "searchOrAddPlaceholder": "Search models, or paste a URL, file path, or Hugging Face repo", + "selectAll": "Select all", + "selectAllCount": "Select all ({{count}})", + "selectModel": "Select a model", + "selectModelDescription": "Pick a model from the library to view details, edit metadata, set per-model defaults, and manage trigger phrases.", + "selectedCount_one": "{{count}} selected", + "selectedCount_other": "{{count}} selected", + "settingsExported": "Settings exported", + "settingsImportFailed": "Failed to import settings", + "settingsImportIncompatible": "None of the imported settings apply to this model", + "settingsImportInvalidFile": "Not a valid model settings file", + "settingsImported": "Settings imported", + "settingsImportedPartial": "Imported with skipped fields: {{fields}}", + "someCouldNotBeDeleted": "Some models could not be deleted", + "someCouldNotBeReidentified": "Some models could not be re-identified", + "sort": { + "base": "Base", + "default": "Default", + "format": "Format", + "name": "Name", + "path": "Path", + "size": "Size", + "type": "Type" + }, + "sortBy": "Sort By", + "source": "Source", + "sourceKind": { + "filePath": "file path", + "folderPath": "folder path", + "hfRepo": "Hugging Face repo", + "url": "URL" + }, + "sourceUrl": "Source URL", + "sourceUrlHelp": "Model page or download URL.", + "statusCancelled": "Cancelled", + "statusDownloading": "Downloading", + "statusPaused": "Paused", + "statusWaiting": "Waiting", + "title": "Models", + "toFindModelsInFolder": "to find models in this folder.", + "toInstallFrom": "to install from {{source}}.", + "transferRate": "{{rate}}/s", + "triggerPhraseDuplicate": "That trigger phrase is already on this model.", + "triggerPhrases": "Trigger Phrases", + "triggerPhrasesHelp": "Phrases that activate this model, surfaced in the prompt editor.", + "type": "Type", + "unlink": "Unlink", + "unlinkNamed": "Unlink {{name}}", + "updatePath": "Update path", + "updatePathDescription": "If the model file moved on disk, point Invoke at its new location. The file is not moved or copied.", + "useSupportedImage": "Use a PNG, JPEG, or WebP image.", + "variant": "Variant", + "variantHelp": "Architecture variant, e.g. inpaint or dev.", + "viewModel": "View model", + "viewQueue": "View queue" + }, + "nodes": { + "addNodes": "Add Nodes", + "alreadyInstalledError": "'{{name}}' is already installed. Uninstall it first to reinstall.", + "copyPath": "Copy path", + "couldNotReloadCustomNodes": "Could not reload custom nodes.", + "couldNotLoadPacks": "Could not load custom node packs", + "customNodesReloaded": "Custom nodes reloaded", + "dependenciesRequired": "Dependencies required", + "dependenciesRequiredDescription": "{{name}} requires dependencies from {{dependencyFile}}. Install them, then restart InvokeAI.", + "details": "Node Pack Details", + "fieldInfo": "Field: {{name}} · Type: {{type}}", + "fieldName": "Field: {{name}}", + "fieldType": "Type: {{type}}", + "required": "Required", + "optional": "Optional", + "inputModes": { + "connection": "Connection only", + "direct": "Direct value only", + "any": "Direct value or connection" + }, + "pinnedToLinearUi": "Pinned to Linear UI.", + "renameFieldHint": "Double-click the name to rename it.", + "batch": "batch", + "any": "Any", + "anyInput": "Any input", + "anyOutput": "Any output", + "connectorTitle": "Connector: {{input}} → {{output}}", + "filterAndSort": "Filter and sort node packs", + "gitUrl": "Git URL", + "gitUrlHelp": "Paste a public Git repository URL. InvokeAI clones it into your custom nodes directory.", + "input": "Input", + "install": "Install", + "installActivity": "Custom node install activity", + "installComplete": "Node pack installed", + "installCompleteWithWorkflows_one": "Installed {{name}} and imported {{count}} workflow.", + "installCompleteWithWorkflows_other": "Installed {{name}} and imported {{count}} workflows.", + "installFailed": "Install failed.", + "installFailedTitle": "Install failed", + "installManually": "Install manually", + "installing": "Installing", + "installedPacks": "Installed custom node packs", + "invalidSourceName": "The URL must end in a valid pack name (letters, numbers, dots, dashes).", + "manager": "Nodes Manager", + "message": "Message", + "nodeCategory": "Category: {{category}}", + "nodeCount_one": "{{count}} node", + "nodeCount_other": "{{count}} nodes", + "nodePack": "node pack", + "nodePacks": "Node Packs", + "nodeType": "Type: {{type}}", + "nodeVersion": "Version: {{version}}", + "nodeClassification": "Classification: {{classification}}", + "seedModeTooltip": "Applies between queued runs of the workflow; a loop inside one run reuses that run's seed.", + "providedByConnection": "Provided by connection.", + "savedWorkflowChoose": "Choose a saved workflow", + "savedWorkflowMissing": "Saved workflow unavailable", + "savedWorkflowUnsupported": "Unsupported", + "savedWorkflowDefaultBadge": "Default", + "savedWorkflowShared": "Shared", + "savedWorkflowSearch": "Search saved workflows…", + "savedWorkflowClear": "Clear saved workflow", + "savedWorkflowListLoading": "Loading saved workflows…", + "savedWorkflowDetailLoading": "Loading saved workflow…", + "savedWorkflowNoExposedFields": "This workflow has no exposed fields.", + "savedWorkflowUpdating": "Updating saved workflows…", + "noMatchingWorkflows": "No matching saved workflows", + "iterationOutputs": "Iteration outputs", + "finalOutputs": "Final outputs", + "forLoopValidationFailed": "For loop validation failed", + "forLoopMissingIterationOutput": "The For node must have at least one iteration output connection", + "forLoopReturnCount": "The For node must have exactly one matching ForReturn", + "forLoopUnterminatedBody": "Every For body path must terminate at its matching ForReturn", + "forLoopNestedUnsupported": "This nested For loop arrangement is not supported", + "forLoopIterateUnsupported": "Iterate nodes are not supported in this For loop body", + "forLoopIteratorInputUnsupported": "For loop body inputs cannot depend on an external Iterate node", + "forLoopFinalOutputInBody": "Final-scoped For outputs cannot feed the loop body", + "forLoopBodyEscape": "For loop body paths cannot leave the loop before ForReturn", + "forLoopInputCount": "For inputs have too many connections", + "forReturnInputCount": "ForReturn inputs have too many connections", + "forLoopLinkageMissing": "Every For and ForReturn must have loop linkage", + "forLoopLinkageInvalid": "Loop linkage must connect a For to its ForReturn", + "forLoopLinkageDuplicate": "A For and ForReturn can each belong to only one loop", + "forReturnOwnership": "Each ForReturn must belong to exactly one For", + "executionCompleted": "Completed", + "executionFailed": "Failed", + "childWorkflowError": "Child workflow error: {{error}}", + "forLoopBodyBoundary": "For loop body", + "imageCollectionCount_one": "{{count}} image", + "imageCollectionCount_other": "{{count}} images", + "addItem": "Add item", + "removeItem": "Remove {{field}} item {{index}}", + "collectionItemLabel": "{{field}} item {{index}}", + "stylePresetSearch": "Search style presets…", + "stylePresetMissing": "Style preset unavailable", + "stylePresetClear": "Clear style preset", + "noMatchingStylePresets": "No matching style presets", + "systemPromptSearch": "Search system prompts…", + "systemPromptMissing": "System prompt unavailable", + "systemPromptClear": "Clear system prompt", + "noMatchingSystemPrompts": "No matching system prompts", + "recordListLoading": "Loading…", + "recordListFailed": "The list could not be loaded.", + "updateNode": "Update node", + "updateAllNodes_one": "Update {{count}} outdated node", + "updateAllNodes_other": "Update {{count}} outdated nodes", + "updateNodeTo": "Update to {{version}}", + "nodeUpdateAvailable": "Version {{from}} → {{to}}: update available", + "nodeNewerThanBackend": "Version {{from}} is newer than this Invoke installation supports ({{to}}); update Invoke", + "nodeVersionIncompatible": "Version {{from}} cannot be updated to {{to}}; delete and re-add the node", + "unableToUpdateNodes_one": "{{count}} node could not be updated to its current version.", + "unableToUpdateNodes_other": "{{count}} nodes could not be updated to their current versions.", + "updateDroppedEdges_one": "Removed {{count}} connection to an input its updated node no longer has.", + "updateDroppedEdges_other": "Removed {{count}} connections to inputs their updated nodes no longer have.", + "updateDroppedFormFields_one": "Removed {{count}} form field for an input its updated node no longer has.", + "updateDroppedFormFields_other": "Removed {{count}} form fields for inputs their updated nodes no longer have.", + "contextPaste": "Paste", + "contextCollapse": "Collapse", + "contextExpand": "Expand", + "contextAddConnector": "Add connector", + "arithmeticSequence": "Arithmetic sequence", + "linearDistribution": "Linear distribution", + "uniformRandomDistribution": "Uniform random distribution", + "parseString": "Parse string", + "splitOn": "Split on", + "dynamicPromptsRandom": "Dynamic prompts (random)", + "dynamicPromptsCombinatorial": "Dynamic prompts (combinatorial)", + "generatorImagesFromBoard": "Images from board", + "generatorImagesCategory": "Category", + "generatorCategoryImages": "Images", + "generatorCategoryAssets": "Uploads", + "generatorImages_one": "{{count}} image", + "generatorImages_other": "{{count}} images", + "generatorNRandomValues_one": "{{count}} random value", + "generatorNRandomValues_other": "{{count}} random values", + "generatorNoValues": "empty", + "generatorLoading": "Loading…", + "generatorLoadFromFile": "Load from file", + "generatorFileTooLarge": "The file is larger than 128 KB.", + "generatorPreviewFailed": "The preview could not be loaded.", + "generatorPreviewTruncated": "{{count}} values in all", + "generatorTooMany": "More than {{max}} values; the queue cannot take that many.", + "generatorStart": "Start", + "generatorStep": "Step", + "generatorEnd": "End", + "generatorCount": "Count", + "generatorMin": "Min", + "generatorMax": "Max", + "generatorSeed": "Seed", + "generatorMaxPrompts": "Max prompts", + "generatorInput": "Input", + "generatorBoard": "Board", + "generatorSelectBoard": "Select a board…", + "generatorBoardMissing": "Board unavailable", + "noBatchGroup": "no group", + "collectionItemCount_one": "{{count}} item", + "collectionItemCount_other": "{{count}} items", + "forLoopBodyBoundaryStatus": { + "missing_linkage": "missing loop linkage", + "invalid_linkage": "invalid loop linkage", + "duplicate_linkage": "duplicate loop linkage", + "missing_return": "missing ForReturn", + "multiple_returns": "multiple ForReturns", + "orphan_return": "orphan ForReturn" + }, + "nodeDetailsAria": "Details for {{title}}", + "nodePackLabel": "Node pack: {{name}}", + "nodesDirectory": "Nodes Directory", + "nodesInPack": "Nodes in this pack", + "noExposedFields": "No exposed fields.", + "noNodesRegistered": "No nodes registered", + "noNodesRegisteredHint": "This pack registered no nodes — reload nodes or restart InvokeAI, and check its dependencies.", + "noPacks": "No custom node packs", + "noPacksDescription": "Install from a Git URL or place packs in your custom nodes directory, then reload.", + "noPacksMatch": "No packs match", + "noPreviews": "No node previews available", + "noPreviewsDescription": "The backend has no node definitions for this pack yet. Reload nodes or restart InvokeAI to load them.", + "noRecentActivity": "No recent activity", + "noRecentActivityDescription": "Install or uninstall a node pack and the outcome shows up here.", + "output": "Output", + "recentActivitySummary_one": "{{count}} recent activity", + "recentActivitySummary_other": "{{count}} recent activities", + "pathCopied": "Path copied", + "problemPacksOnly": "Packs with no nodes", + "refreshFailed": "Refresh failed", + "reloadFailed": "Reload failed", + "reloadNotice": "Custom nodes reload", + "reloading": "Reloading", + "scanFolderDescription": "Add node packs directly to the custom nodes directory, then reload nodes. InvokeAI scans this folder on startup and reload.", + "searchPacks": "Search node packs", + "searchPacksPlaceholder": "Search packs…", + "selectPack": "Select a node pack", + "sort": { + "name": "Name", + "nodeCount": "Node count", + "path": "Path" + }, + "sortBy": "Sort By", + "selectPackDescription": "Choose a pack from the library to see its path, manage it, and preview every node it adds.", + "status": "Status", + "trustWarning": "Custom nodes execute Python code. Only install packs from authors you trust.", + "tryDifferentSearch": "Try a different search.", + "uninstall": "Uninstall", + "uninstallBody": "Remove this pack from the custom nodes directory? A restart is required for removal to fully apply.", + "uninstallBodyWithWorkflows_one": "Remove this pack from the custom nodes directory? {{count}} workflow imported by this pack will also be deleted. A restart is required for removal to fully apply.", + "uninstallBodyWithWorkflows_other": "Remove this pack from the custom nodes directory? {{count}} workflows imported by this pack will also be deleted. A restart is required for removal to fully apply.", + "uninstallFailed": "Uninstall failed", + "uninstallPack": "Uninstall Node Pack", + "uninstallTitle": "Uninstall {{name}}?", + "uninstalledDescription": "Restart InvokeAI for node removal to take full effect.", + "uninstalledTitle": "Node pack uninstalled", + "couldNotUninstall": "Could not uninstall the node pack.", + "uninstalled": "Uninstalled" + }, + "launchpad": { + "browserIssues": { + "canvasReadbackIntegrity": { + "braveWorkaround": "Click the Brave Shields icon in the address bar and turn Shields off for this site, then reload Invoke.", + "description": "Your browser is modifying or blocking canvas pixel data. This can make later strokes noisy or pixelated.", + "firefoxWorkaround": "Click the canvas permission icon in the address bar and allow this site to read canvas data. If no icon appears, click the shield icon and turn off Enhanced Tracking Protection for this site. Then reload Invoke.", + "genericWorkaround": "If you use another privacy browser or canvas-protection extension, allow unmodified canvas access for this site, then reload Invoke.", + "heliumWorkaround": "In Helium, open the address below, disable “[Helium Noise] Canvas pixel noising”, then relaunch Helium.", + "title": "Canvas protection may corrupt painted strokes" + }, + "copyFailed": "Could not copy browser setting", + "copySetting": "Copy browser setting", + "settingCopied": "Browser setting copied" + }, + "connection": { + "connecting": "Connecting…", + "disconnected": "Disconnected" + }, + "groups": { + "manage": "Manage", + "workspace": "Workspace" + }, + "help": { + "community": "Community", + "discord": "Discord", + "documentation": "Documentation", + "github": "GitHub", + "guides": "Guides", + "label": "Help and resources", + "version": "Version {{version}}", + "youtube": "YouTube" + }, + "home": { + "intents": { + "canvas": "Edit on canvas", + "canvasDescription": "Paint, compose, and refine", + "generate": "Generate from text", + "generateDescription": "Start from a prompt", + "heading": "Start something", + "upscale": "Upscale an image", + "upscaleDescription": "Add resolution and detail", + "workflow": "Build a workflow", + "workflowDescription": "Wire nodes together", + "video": "Generate a video", + "videoDescription": "Text, image, or extend an existing clip" + }, + "queue": { + "summary": "{{inProgress}} running · {{pending}} queued" + }, + "recentOutputs": "Recent outputs", + "recentProjects": "Recent projects", + "resume": "Pick up where you left off", + "resumeAction": "Resume", + "viewAllProjects": "View all projects" + }, + "openProjects": { + "current": "Current", + "label": "Open projects", + "openEditor": "Open editor" + }, + "projectsGreeting": "Welcome to Invoke", + "projectsGreetingWithName": "Welcome back, {{name}}", + "sections": { + "home": "Home", + "models": "Models", + "nodes": "Nodes", + "projects": "Projects", + "users": "Users", + "fonts": "Fonts", + "preferences": "Preferences" + }, + "sectionsLabel": "Launchpad sections" + }, + "settings": { + "informationalPopoversDisabled": "Informational popovers disabled", + "informationalPopoversDisabledDesc": "Re-enable them under Settings, in the Behavior section.", + "title": "Settings", + "descriptionGlobal": "Global preferences for this single-user install. Project settings apply only to the active project.", + "descriptionUser": "Preferences for the signed-in user. Project settings apply only to the active project.", + "enableHighlightFocusedRegions": "Highlight focused region", + "language": "Language", + "languageDescription": "Interface language. New workbench copy falls back to English until translators add coverage.", + "tabs": { + "about": "About", + "appearance": "Appearance", + "behavior": "Behavior", + "developer": "Developer", + "hotkeys": "Hotkeys", + "project": "Project", + "queue": "Queue", + "workflow": "Workflow", + "workspace": "Workspace", + "imageMap": "Image Map" + }, + "about": { + "title": "About Invoke", + "description": "Version and system information for this Invoke server.", + "github": "GitHub", + "discord": "Discord", + "loading": "Loading system information…", + "systemInformation": "System information" + }, + "imageMapVocabulary": { + "title": "Cluster Label Vocabulary", + "description": "Supplementary terms the image map can use when labeling clusters, alongside its built-in vocabulary. This is a server-wide list.", + "addLabel": "Add terms", + "adminOnly": "Only an administrator can change the vocabulary.", + "addPlaceholder": "Add terms (comma or newline separated)", + "addHelp": "Terms are lowercased and applied the next time labels are computed. Paste a comma- or newline-separated list to add several at once.", + "alreadyInList": "Every entered term is already in the list.", + "termTooLong": "Terms must be {{max}} characters or fewer.", + "tooManyTerms": "The list is limited to {{max}} terms.", + "removeTerm": "Remove {{term}}", + "noTermsYet": "No supplementary terms yet. The built-in vocabulary is still used.", + "count": "{{count}} of {{max}} supplementary terms", + "rebuilding": "Updating cluster labels with the new vocabulary…", + "rebuildingQueued": "Waiting for image indexing to finish ({{progress}}) before updating cluster labels…", + "buildFailed": "The vocabulary could not be prepared: {{message}}", + "indexOff": "The image index is not running. Terms are saved and will apply when it is next enabled.", + "saveFailed": "Could not save the vocabulary.", + "loadFailed": "Could not load the vocabulary." + }, + "catalog": { + "themeId": { + "label": "Theme", + "description": "Choose the workbench colors." + }, + "highContrast": { + "label": "High contrast", + "description": "Strengthen text and border contrast on every theme." + }, + "reduceMotion": { + "label": "Reduce motion", + "description": "Disable transitions and animations across the workbench." + }, + "showFocusRegionHighlight": { + "label": "Highlight focused regions", + "description": "Highlight the region receiving keyboard shortcuts." + }, + "autoSwitchInvocationRoute": { + "label": "Auto-switch Invoke route", + "description": "Match the Invoke source and destination to the surface you are editing. Locked routes are never changed." + }, + "enableInformationalPopovers": { + "label": "Enable informational popovers", + "description": "Show educational popovers on controls that have extra guidance." + }, + "enableModelDescriptions": { + "label": "Show model descriptions", + "description": "Include model descriptions in model dropdowns where available." + }, + "notifyOnEnqueue": { + "label": "Notify when queued", + "description": "Show a toast for each successful enqueue. Enqueues are always recorded in the notification center." + }, + "preferNumericAttentionStyle": { + "label": "Prefer numeric attention style", + "description": "Use numeric prompt attention syntax when inserting attention weights." + }, + "showPromptSyntaxHighlighting": { + "label": "Highlight prompt syntax", + "description": "Color syntax in prompt fields without changing their text or validation." + }, + "hotkeys": { + "label": "Keyboard shortcuts", + "description": "Find and customize keyboard shortcuts." + }, + "useCpuNoise": { + "label": "Use CPU noise", + "description": "Use CPU noise generation for deterministic legacy-compatible outputs." + }, + "confirmImageDeletion": { + "label": "Confirm image deletion", + "description": "Ask for confirmation before permanently deleting images." + }, + "antialiasProgressImages": { + "label": "Antialias progress images", + "description": "Smooth progress previews instead of rendering them pixelated." + }, + "workflowSnapToGrid": { + "label": "Always snap to grid", + "description": "Snap nodes to the grid while dragging. When off, hold Ctrl to snap temporarily." + }, + "workflowEdgesBehindNodes": { + "label": "Keep highlighted connections behind nodes", + "description": "Connections to a selected node normally rise above other nodes. Keep them beneath so they never cover node controls." + }, + "workflowGroupNodesByCategory": { + "label": "Group node search by category", + "description": "Show add-node results under their categories. When off, results are one list ordered by how closely they match." + }, + "workflowShowMinimap": { + "label": "Show minimap", + "description": "Show an overview in the corner of the workflow editor." + }, + "workflowValidateConnections": { + "label": "Validate connections", + "description": "Reject incompatible field connections. Disabling validation allows connections that may fail during a run." + }, + "imageMapVocabulary": { + "label": "Vocabulary", + "description": "Manage the vocabulary used to organize the Image Map." + }, + "developerLogEnabled": { + "label": "Record diagnostic logs", + "description": "Record selected diagnostic events in the Diagnostics widget. Warnings and errors from every namespace are recorded by default." + }, + "developerPerformanceTimingsEnabled": { + "label": "Collect performance timings", + "description": "Record workflow editing and project serialization measurements." + }, + "developerLogNamespaces": { + "label": "Log namespaces", + "description": "Choose which diagnostic sources to record. New namespaces are not added to a saved selection; use Reset logging defaults." + }, + "generationDevices": { + "label": "Generation devices", + "description": "Server-wide accelerator selection. Changes require a server restart." + }, + "workspaceActions": { + "label": "Data & workspace", + "description": "Reset the layout, manage intermediates, or permanently delete saved projects and settings." + }, + "about": { + "label": "About InvokeAI", + "description": "Version, build, and server information." + }, + "language": { + "label": "Language" + }, + "workflowEdgeStyle": { + "label": "Connection style", + "description": "How connections between nodes are drawn." + }, + "options": { + "curved": "Curved", + "square": "Square", + "activeProject": "Active project", + "all": "All projects", + "trace": "Trace", + "debug": "Debug", + "info": "Info", + "warn": "Warn", + "error": "Error", + "fatal": "Fatal" + }, + "queueJobsScope": { + "label": "Show jobs from", + "description": "Choose which jobs the Queue widget includes in counts and lists." + }, + "developerLogLevel": { + "label": "Log level" + }, + "sections": { + "appearance": "Appearance", + "behavior": "Behavior", + "hotkeys": "Keyboard shortcuts", + "project": "Project", + "gallery": "Gallery", + "preview": "Preview", + "workflow": "Workflow", + "queue": "Queue", + "imageMap": "Image Map", + "developer": "Developer", + "server": "Server", + "workspace": "Data & workspace", + "about": "About", + "intermediates": "Intermediates" + }, + "developerConsoleOutputEnabled": { + "label": "Mirror logs to the browser console", + "description": "Write recorded events to the developer console as well. The same level and namespace filters apply." + }, + "developerLoggingReset": { + "label": "Reset logging defaults", + "description": "Restore the default level, namespaces, console and timing settings, including namespaces added in newer versions." + }, + "resetLoggingDefaults": "Reset to defaults", + "loggingDefaultsActive": "Default logging settings are active.", + "manageIntermediates": "Manage intermediates…" + } + }, + "shell": { + "addCurrentLayout": "Add current layout...", + "backendUnavailable": { + "connectionFailed": "Could not reach the project server", + "description": "The editor is paused so it cannot replace your saved session with incomplete data. Retry when the server is available, or export a browser draft below.", + "draftsTitle": "Recoverable browser drafts", + "draftsUnavailable": "Browser recovery storage is unavailable. Drafts may still exist, but cannot be listed or exported in this browser session.", + "draftUnavailable": "This browser draft is no longer available. Reload and try again.", + "exportDraft": "Export draft", + "exportFailed": "The browser draft could not be exported.", + "loadMore": "Load more drafts", + "loadingDrafts": "Looking for drafts…", + "noDrafts": "No browser drafts are available.", + "retry": "Retry connection", + "title": "Projects are temporarily unavailable" + }, + "builtInPresets": "Built-in presets", + "customLayout": "Custom layout {{number}}", + "customPresets": "Custom presets", + "projectConflict": { + "actionFailed": "The project recovery action failed.", + "backupDescription": "Server saves continue, but browser crash recovery is unavailable until local storage recovers.", + "backupTitle": "Local crash recovery is unavailable", + "confirmTitle": "Resolve project conflict?", + "copyOpenedDuringResolution": "The project copy opened before recovery finished. Close it and retry.", + "deletedDescription": "This project was deleted elsewhere. Your local work remains available until you choose what to do.", + "deletedTitle": "Project deleted on the server", + "deleteDraft": "Delete draft", + "deleteDraftConfirmBody": "Permanently delete this browser draft? This cannot be undone.", + "recoverableDraftDescription": "A browser draft for project {{projectId}} could not be opened automatically. Export it before deleting it or retrying recovery.", + "recoverableDraftPosition": "Draft {{number}} of {{count}}.", + "recoverableDraftTitle": "Recoverable project draft available", + "nextDraft": "Next draft", + "previousDraft": "Previous draft", + "discard": "Discard local work", + "discardConfirmBody": "Permanently discard the local draft? This cannot be undone.", + "draftUnavailable": "The local draft is unavailable.", + "export": "Export local draft", + "revisionDescription": "The server and this tab contain different edits. You can keep both versions or replace this tab with the server version.", + "revisionTitle": "Project changed elsewhere", + "saveAsNew": "Save as new project", + "saveAsNewConfirmBody": "Create a separate project from the local version? Editing is paused until the copy is safely created.", + "schemaDescription": "This project needs a newer version of InvokeAI. Export it before discarding the local draft.", + "schemaTitle": "Project schema is not supported", + "serverVersionUnavailable": "The server version could not be loaded.", + "useServer": "Use server version", + "useServerConfirmBody": "Replace the local draft with the current server version? Local changes will be discarded." + }, + "queueRecovery": { + "actionFailed": "The queue recovery action failed.", + "description": "Project {{projectId}} has locally saved queue run recovery data.", + "discard": "Discard recovery", + "discardConfirmBody": "Permanently discard this project's local queue recovery data? This does not cancel work that may still be running on the server.", + "discardConfirmTitle": "Discard queue recovery?", + "export": "Export recovery", + "loading": "Looking for queue recovery data…", + "lockContended": "This project's queue recovery is active in another tab. Close that tab or wait for its runs to finish.", + "lockUnavailable": "Queue recovery cannot be discarded because cross-tab coordination is unavailable.", + "next": "Next", + "open": "Open project", + "openFailed": "The project could not be opened. Its local queue recovery data is still available.", + "position": "Recovery {{number}} of {{count}}.", + "previous": "Previous", + "storageUnavailable": "Browser queue recovery storage may contain data, but it cannot be inspected in this browser session.", + "storageUnavailableTitle": "Queue recovery storage is unavailable", + "title": "Queue run recovery available" + }, + "deleteLayoutPreset": "Delete preset", + "deleteLayoutPresetBody": "Delete \"{{name}}\"? This only removes the saved preset.", + "deleteLayoutPresetQuestion": "Delete layout preset?", + "layoutPresetName": "Preset name", + "renameLayoutPreset": "Rename layout preset", + "pasteMedia": { + "title_one": "Pasted file", + "title_other": "Pasted files", + "description_one": "Choose where the pasted file should go.", + "description_other": "Choose where the {{count}} pasted files should go.", + "gallery": "Upload to Gallery", + "canvasHeading": "Add to Canvas as", + "reference": "Use as Reference Image", + "videosGalleryOnly": "Videos are uploaded to the gallery only.", + "imagePreview": "Pasted image {{index}}", + "videoPreview": "Pasted video {{index}}", + "morePreviews": "+{{count}} more" + } + }, + "widgets": { + "actionsLabel": "{{label}} actions", + "centerRegion": "Center view", + "centerViewClose": "Close {{label}}", + "centerViewEmpty": "No view", + "centerViewLabel": "Center view: {{label}}", + "centerViews": "Center Views", + "centerViewsAdd": "Add To Center", + "failure": { + "compactLabel": "{{label}} failed — select to retry", + "copiedError": "Error details copied", + "copyError": "Copy Error", + "copyErrorFailed": "Could not copy the error details", + "retry": "Retry", + "title": "{{label}} failed" + }, + "floating": { + "dock": "Dock to panel", + "floatWindow": "Float Window", + "maximize": "Maximize", + "move": "Move {{label}} window", + "railSlot": "{{label}} (floating — click to dock)", + "resize": "Resize window", + "restore": "Restore", + "shade": "Collapse", + "unshade": "Expand" + }, + "loadingLabel": "Loading {{label}}", + "moveToLeftSide": "Move to left side", + "moveToRightSide": "Move to right side", + "removeWidget": "Remove {{label}}", + "panelLabel": "{{region}} widget panel", + "resizePanel": "Resize {{region}} widget panel", + "settingsLabel": "{{label}} settings", + "visibilityLabel": "{{region}} widget visibility", + "rail": { + "create": "Create", + "inspect": "Inspect", + "createWidgets": "Create widgets", + "inspectWidgets": "Inspect widgets" + }, + "properties": { + "noCanvas": "Open a canvas to edit its properties.", + "groups": { + "settings": "Settings", + "gestures": "Gestures", + "input": "Input", + "stroke": "Stroke", + "dynamics": "Dynamics", + "shape": "Shape", + "gradient": "Gradient", + "stops": "Stops", + "font": "Font", + "paragraph": "Paragraph", + "frame": "Frame", + "selection": "Selection" + }, + "foreground": "Foreground", + "gestures": { + "click": "Click", + "drag": "Drag", + "scroll": "Scroll", + "shiftClick": "Shift+Click", + "clickPoint": "Click a point", + "holdAlt": "Hold Alt", + "holdSpace": "Hold Space" + }, + "hints": { + "view": { + "drag": "Pan the canvas.", + "scroll": "Zoom in and out.", + "hold": "Pan from any tool; release to return." + }, + "colorPicker": { + "click": "Sample the color under the cursor into the active color target.", + "hold": "Sample from any tool; release to keep painting with the picked color." + }, + "sam": { + "click": "Place a point with the current label.", + "shiftClick": "Place a point with the opposite label.", + "clickPoint": "Remove that point.", + "drag": "Draw or adjust the search box." + } + }, + "title": { + "group": "Group: {{name}}", + "layer": "Layer: {{name}}", + "operation": "Operation: {{name}}" + }, + "target": { + "defaults": "Defaults", + "editing": "Editing: {{name}}", + "newText": "New text" + }, + "rows": { + "color": "Color", + "fill": "Fill", + "kind": "Kind", + "width": "Width", + "angle": "Angle", + "stop": "Stop", + "offset": "Offset", + "family": "Family", + "size": "Size", + "weight": "Weight", + "lineHeight": "Line height", + "align": "Align", + "aspect": "Aspect", + "shape": "Shape", + "mode": "Mode" + }, + "sections": { + "operation": "Operation", + "tool": "Tool", + "layer": "Layer" + } + }, + "transform": { + "edit": "Transform layer", + "groupSelected": "Select a layer inside this group to transform.", + "hint": "Each field commits one undoable change to the selected layer.", + "noSelection": "No layer selected", + "position": "Position", + "rotation": "Rotation", + "scale": "Scale", + "session": "Transform in progress", + "title": "Transform" + }, + "labels": { + "autosaveStatus": "Autosave", + "canvas": "Canvas", + "diagnostics": "Diagnostics", + "gallery": "Gallery", + "generate": "Generate", + "imageMap": "Image Map", + "layers": "Layers", + "properties": "Properties", + "transform": "Transform", + "notifications": "Notifications", + "preview": "Preview", + "project": "Project", + "queue": "Queue", + "queueStatus": "Queue status", + "serverStatus": "Server Status", + "upscale": "Upscale", + "workflow": "Workflow", + "video": "Video", + "color": "Color", + "swatches": "Swatches", + "history": "History", + "overview": "Overview" + }, + "generate": { + "activeCount_one": "{{count}} active", + "activeCount_other": "{{count}} active", + "activeSteps": "Active steps", + "addConcept": "Add concept", + "addConceptsHelp": "Add LoRAs/concepts here to blend them into this generation.", + "addPromptTrigger": "Add prompt trigger", + "addReferenceImage": "Add reference image", + "advanced": "Advanced", + "aspectRatio": "Aspect ratio", + "capabilitiesLoadFailed": "Could not load model capabilities from the backend.", + "capabilitiesUnavailableForRecall": "Model capabilities are not loaded, so settings that depend on the model cannot be applied yet. Prompts and seed can. If loading failed, retry from the Generate panel.", + "capabilitiesUnavailableForSetup": "Model capabilities are not loaded, so this project cannot be set up for Generate yet. If loading failed, retry from the Generate panel.", + "cfgRescale": "CFG rescale", + "clearPromptTemplate": "Stop using this template", + "clearReferenceImages": "Remove all", + "clipSkip": "CLIP skip", + "collapseReferenceImage": "Collapse reference image", + "colorCompensation": "Color compensation", + "compatibleEmbeddings": "Compatible embeddings", + "completionSuggestions": "Completions", + "components": "Components", + "compositing": "Compositing", + "compositingOptions": { + "infillColorValue": "Infill color", + "infillMethod": "Infill method", + "infillMethods": { + "patchmatch": "PatchMatch", + "lama": "LaMa", + "cv2": "OpenCV", + "color": "Solid color", + "tile": "Tile" + }, + "coherenceMode": "Coherence pass mode", + "coherenceModes": { + "Gaussian Blur": "Gaussian Blur", + "Box Blur": "Box Blur", + "Staged": "Staged" + }, + "coherenceEdgeSize": "Coherence edge size", + "coherenceMinDenoise": "Coherence min denoise", + "maskBlur": "Mask blur" + }, + "conceptWeight": "{{name}} weight", + "concepts": "Concepts", + "copyValue": "Copy value", + "couldNotExpandPrompt": "Could not expand the prompt.", + "couldNotGeneratePromptFromImage": "Could not generate a prompt from the selected image.", + "cropReferenceImage": "Crop reference image", + "cropReferenceImageHelp": "Drag the crop box to move it, or drag its handles to resize. Apply to create a cropped reference image.", + "deletePresetNamed": "Delete preset {{name}}", + "deletePromptHistoryItem": "Delete prompt history item", + "denoisingStrength": "Denoising strength", + "strength": "Strength", + "disableConcept": "Disable {{name}}", + "disableReferenceImage": "Disable reference image", + "dropImageToPrompt": "Describe this image", + "dropReferenceImages": "Drop Reference Images", + "dynamicButton": "Dynamic", + "dynamicPrompts": { + "title": "Dynamic Prompts", + "showPrompts": "Show dynamic prompts", + "noDynamicSyntax": "Wrap alternatives in braces to generate one image per combination, e.g. a {red|green} ball", + "summary": "{{prompts}} × {{iterations}} → {{generations}}", + "expanding": "Expanding…", + "allCombinations": "All combinations", + "randomSample": "Random sample", + "shuffle": "Shuffle the sample", + "maxPrompts": "Max prompts", + "numberOfPrompts": "Number of prompts", + "promptsPreview": "Prompts preview", + "usePrompt": "Use this prompt", + "andMore": "and {{count}} more", + "problemGeneratingPrompts": "Could not expand this prompt", + "preview": "Preview", + "wildcards": "Wildcards", + "newWildcard": "New wildcard", + "editWildcard": "Edit wildcard", + "wildcardName": "Name", + "wildcardNamePlaceholder": "colors", + "wildcardNameInvalid": "Letters, numbers, underscores and hyphens only, starting and ending with a letter or number. Nest with '/', e.g. animals/dogs.", + "wildcardNameTaken": "You already have a wildcard with this name", + "wildcardNameTooLong": "Name must be at most 128 characters", + "wildcardValues": "Values", + "wildcardValuesPlaceholder": "One value per line", + "wildcardHasNoValues": "No values yet", + "noWildcardsYet": "No wildcards yet. Create one to reuse a list of values as __name__.", + "searchWildcards": "Search wildcards", + "noMatchingWildcards": "No wildcards match your search", + "insertWildcard": "Insert into the prompt", + "couldNotSaveWildcard": "Could not save this wildcard", + "couldNotDeleteWildcard": "Could not delete this wildcard", + "deleteWildcardTitle": "Delete wildcard", + "deleteWildcardBody": "Delete __{{name}}__ and its values? This cannot be undone.", + "resizeWildcardValues": "Resize wildcard values", + "mode": "Mode", + "newSeedPerImage": "New seed for every image", + "seedHeldForEveryImage": "A fixed seed is used for every image.", + "import": "Import", + "export": "Export", + "exportAsYaml": "As YAML", + "exportAsJson": "As JSON", + "importWildcards": "Import wildcards", + "importSummary": "{{fresh}} new, {{conflicts}} already in your library", + "importAlreadyExist": "Already in your library", + "importSkip": "Skip", + "importReplace": "Replace", + "importKeepBoth": "Keep both", + "importValueCount_one": "{{count}} value", + "importValueCount_other": "{{count}} values", + "importCannotImport": "Cannot be imported", + "importUnnamed": "(no name)", + "importRejectedInvalid": "Unusable name", + "importRejectedTooLong": "Name too long", + "importRejectedDuplicate": "Repeated in this import", + "importRejectedNoValues": "No values", + "importRejectedTooManyValues": "Too many values", + "importRejectedValueTooLong": "A value is too long", + "importedCount_one": "Imported {{count}} wildcard", + "importedCount_other": "Imported {{count}} wildcards", + "wildcardTooManyValues": "Too many values (limit 10,000)", + "wildcardValueTooLong": "One value is too long (limit 2,000 characters)", + "importFiles": "From files…", + "importFolder": "From a folder…", + "importNoWildcardFiles": "That folder has no wildcard files in it", + "couldNotImport": "Could not import these wildcards", + "couldNotImportAfter": "Stopped after importing {{done}} of {{total}} wildcards", + "couldNotExport": "Could not export your wildcards", + "couldNotReadFile": "Could not read {{name}}" + }, + "enableConcept": "Enable {{name}}", + "enableNegativePrompt": "Enable negative prompt", + "enableReferenceImage": "Enable reference image", + "enabled": "Enabled", + "enterPromptToExpand": "Enter a prompt to expand.", + "expand": "Expand", + "expandPrompt": "Expand prompt", + "expandFromFirstFrame": "Describe from the first frame", + "expandFirstFrameUnreadable": "This model can't read images, so the first frame won't be used.", + "expandSuggestedModelMissing": "{{model}}, the prompt enhancer recommended for this model, isn't installed. It is among the starter models.", + "expandReferenceImage": "Expand reference image", + "generatePrompt": "Generate Prompt", + "guidance": "Guidance", + "height": "Height", + "hiDiffusion": "HiDiffusion", + "hiDiffusionRauNet": "HiDiffusion: RAU-Net", + "hiDiffusionT1Ratio": "HiDiffusion: T1 Ratio", + "hiDiffusionT2Ratio": "HiDiffusion: T2 Ratio", + "hiDiffusionWindowAttn": "HiDiffusion: Window Attention", + "ideogram4ColorHelp": "Comma-separated color terms folded into the caption.", + "ideogram4ColorPalette": "Color palette", + "ideogram4GuidanceScale": "Guidance", + "ideogram4Mu": "Mu", + "ideogram4MuHelp": "Timestep shift. Higher values spend more of the schedule at high noise.", + "ideogram4PresetDerived": "Set by the sampler preset unless overridden.", + "ideogram4SamplerPreset": "Sampler preset", + "ideogram4Steps": "Steps", + "imageInfluence": "Image influence", + "imageToPrompt": "Image to prompt", + "incompatible": "Incompatible", + "incompatibleSettingsCleared": "Incompatible settings cleared", + "incompatibleSettingsClearedDescription_one": "{{labels}} was not compatible with {{name}}.", + "incompatibleSettingsClearedDescription_other": "{{labels}} were not compatible with {{name}}.", + "installTextLlmToExpandPrompts": "Install a Text LLM model to expand prompts.", + "installVisionModelToGeneratePrompts": "Install a LLaVA OneVision model to generate prompts from images.", + "krea2Rebalance": "Conditioning rebalance", + "krea2RebalanceAxisDeep": "deep", + "krea2RebalanceAxisShallow": "shallow", + "krea2RebalanceGain": "Gain", + "krea2RebalanceHelp": "Scales conditioning toward the prompt before denoising.", + "krea2RebalancePreset": "Rebalance preset", + "krea2RebalancePresetActions": "Preset actions", + "krea2RebalancePresetCustom": "Custom", + "krea2RebalancePresetDelete": "Delete preset", + "krea2RebalancePresetDeleteBody": "Delete the saved preset \"{{name}}\"? This cannot be undone.", + "krea2RebalancePresetName": "Preset name", + "krea2RebalancePresetRename": "Rename preset", + "krea2RebalancePresetSave": "Save current as preset", + "krea2RebalancePresetSaveAction": "Save", + "krea2RebalanceReset": "Reset to default curve", + "krea2RebalanceTapLabel": "Tap {{tap}}, encoder layer {{layer}}", + "krea2RebalanceTapReadout": "tap {{tap}} · {{value}}", + "krea2RebalanceWeights": "Per-tap weights", + "krea2RebalanceWeightsHelp": "{{count}} comma-separated numbers, one per tap.", + "krea2RebalanceWeightsInvalid": "Enter exactly {{count}} comma-separated numbers.", + "krea2SeedVariance": "Seed variance", + "krea2SeedVarianceHelp": "Perturbs conditioning so nearby seeds diverge more.", + "krea2SeedVarianceRandomize": "Randomized channels", + "krea2SeedVarianceStrength": "Variance strength", + "llmTaskGenerating": "Generating", + "llmTaskLoadingModel": "Loading model", + "llmTaskProgress": "LLM task progress", + "loadingCapabilities": "Loading model capabilities…", + "loadingModels": "Loading backend models…", + "lockAspectRatio": "Lock aspect ratio", + "mainModel": "Main model", + "megapixelsValue": "{{value}} MP", + "mode": "Mode", + "modeBoth": "Both", + "modeComposition": "Composition", + "modeStyle": "Style", + "model": "Model", + "modelDefault": "Model default", + "moveReferenceImageDown": "Move reference image down", + "moveReferenceImageUp": "Move reference image up", + "moreRatios": "More aspect ratios", + "negativePrompt": "Negative prompt", + "negativePromptAdd": "Negative prompt…", + "negativePromptHide": "Turn off negative prompt", + "noMatchingPrompts": "No matching prompts.", + "noMatchingTriggers": "No matching triggers.", + "noPromptHistoryYet": "No prompt history yet.", + "noPromptTriggersAvailable": "No prompt triggers available.", + "noSupportedModels": "No compatible generation model was found on the backend.", + "offCount_one": "{{count}} off", + "offCount_other": "{{count}} off", + "openModelManager": "Open Model Manager", + "override": "Override", + "positivePrompt": "Positive prompt", + "presetDeleteBody": "Delete the preset \"{{name}}\"? This cannot be undone.", + "presetDeleteTitle": "Delete preset?", + "presetModelMissing": "This preset's model is not installed.", + "presetName": "Preset name", + "presets": "Presets", + "presetsEmpty": "No presets yet. Save the current settings to reuse them later.", + "presetsNoMatches": "No presets match your search.", + "presetUnreadable": "This preset could not be read.", + "promptHistory": "Prompt history", + "promptHistoryEntries": "Prompt history entries", + "promptHistoryKeyboardHelp": "Alt+Up/Down switches between prompts while focused.", + "recentSeeds": "Recent seeds", + "seedSummary": "{{mode}} · {{seed}}", + "renamePresetNamed": "Rename preset {{name}}", + "renamePresetTitle": "Rename preset", + "resetToModelDefault": "Reset to model default", + "resetToConceptDefault": "Reset to concept default", + "resetToDefault": "Reset to default", + "retry": "Retry", + "savePresetAction": "Save", + "savePresetTitle": "Save preset", + "scaleBeforeProcessing": "Scale before processing", + "selectSupportedModelFirst": "Select a supported Generate model first.", + "scalingOptions": { + "height": "Height", + "method": "Scale method", + "methods": { + "auto": "Auto", + "manual": "Manual", + "none": "Off" + }, + "processingAtFrame": "Generates at the generation size; nothing to resize.", + "processingSize": "Generates at {{width}}x{{height}}, then resizes back to the generation size.", + "width": "Width" + }, + "searchPresets": "Search presets", + "secondsPerRun": "~{{value}}s/run", + "sizePreviewHandle": "Drag to resize the generation size", + "switchModelBody": "Switching to {{name}} turns off or removes: {{labels}}.", + "switchModelConfirm": "Switch model", + "switchModelTitle": "Switch model?", + "systemPrompts": { + "title": "System prompt", + "manage": "Manage system prompts", + "new": "New", + "edit": "Edit system prompt", + "name": "Name", + "namePlaceholder": "Concise and literal", + "content": "Instructions", + "contentPlaceholder": "Describe how the model should rewrite the prompt.", + "maxTokens": "Max output length", + "maxTokensHelp": "Tokens the model may generate with this prompt. Leave empty for the default ({{tokens}}).", + "maxTokensInvalid": "Enter a whole number between {{min}} and {{max}}, or leave it empty.", + "none": "No system prompts", + "shared": "Shared", + "duplicate": "Duplicate", + "sharedEditWarning": "Shared with everyone. Changes apply to all users.", + "deleteTitle": "Delete system prompt", + "deleteBody": "“{{name}}” will be deleted. This cannot be undone.", + "couldNotSave": "Could not save this system prompt.", + "couldNotDelete": "Could not delete this system prompt." + }, + "promptTemplates": { + "title": "Prompt templates", + "newTemplate": "New template", + "editTemplate": "Edit template", + "search": "Search templates", + "yourTemplates": "Your templates", + "sharedTemplates": "Shared templates", + "defaultTemplates": "Built-in templates", + "noTemplatesYet": "No templates yet. Create one to reuse a prompt with {prompt} where your own text goes.", + "noMatches": "No matching templates", + "applied": "Template: {{name}}", + "appliedMissing": "Template: {{name}} (deleted)", + "appliedMissingHelp": "“{{name}}” was deleted. It still shapes your prompt until you stop using it.", + "clearApplied": "Stop using {{name}}", + "clear": "Clear", + "name": "Name", + "namePlaceholder": "Cinematic", + "nameHelp": "Templates you create are saved to your account.", + "nameTooLong": "Name must be at most 128 characters", + "positivePrompt": "Template prompt", + "positivePromptPlaceholder": "{prompt}. cinematic still, 35mm, shallow depth of field", + "negativePrompt": "Template negative prompt", + "negativePromptPlaceholder": "{prompt}, blurry, lowres", + "resizePositivePrompt": "Resize template prompt", + "resizeNegativePrompt": "Resize template negative prompt", + "insertPlaceholderHelp": "Insert {prompt}, which your own prompt replaces. Without it the template is added to the end.", + "placeholderAlreadyUsed": "Only the first {prompt} is replaced", + "image": "Preview image", + "addImage": "Add an image", + "replaceImage": "Replace image", + "removeImage": "Remove image", + "couldNotLoadImage": "Could not load that image", + "import": "Import", + "importHelp": "Import templates from a CSV or JSON file with name, prompt and negative_prompt.", + "export": "Export", + "exportHelp": "Download every user template as a CSV file.", + "couldNotSave": "Could not save this template", + "couldNotDelete": "Could not delete this template", + "couldNotImport": "Could not import templates", + "couldNotExport": "Could not export templates", + "deleteTitle": "Delete template", + "deleteBody": "Delete “{{name}}”? This cannot be undone.", + "viewMerged": "Show the merged prompt", + "editAuthored": "Back to editing your prompt", + "loading": "Loading templates…" + }, + "promptTriggerOptions": "Prompt trigger options", + "readyWithIterations_one": "Ready · {{count}} iteration", + "readyWithIterations_other": "Ready · {{count}} iterations", + "referenceImage": "Reference Image", + "referenceImageModel": "Reference image model", + "referenceImages": "Reference images", + "referenceImagesUnsupported": "Reference images are not supported by the selected model.", + "removeConcept": "Remove concept", + "removeConceptNamed": "Remove {{name}}", + "removeReferenceImage": "Remove reference image", + "render": "Render", + "resetAllToModelDefaults": "Reset all to model defaults", + "resizeNegativePrompt": "Resize negative prompt", + "resizePositivePrompt": "Resize positive prompt", + "scheduler": "Scheduler", + "seamlessTiling": "Seamless tiling", + "searchCompatibleConcepts": "Search compatible concepts...", + "searchPromptHistory": "Search prompt history", + "searchPromptTriggers": "Search prompt triggers", + "selectImageFirst": "Select an image in Gallery or Preview first.", + "selectMainModelBeforeConcepts": "Select a main model before adding concepts.", + "selectModel": "Select a model…", + "selectModelFirst": "Select a model first", + "selectTextLlm": "Select Text LLM", + "selectVisionModel": "Select vision model", + "setOptimalSize": "Set optimal size", + "setOptimalSizeDescription": "Set size to the model's optimal pixel count", + "size": "Size", + "sizeRecommended": "Recommended ✓", + "steps": "Steps", + "styleVariant": "Style variant", + "styleVariantPrecise": "Precise", + "styleVariantPreciseDesc": "Applies a precise visual style, eliminating subject influence.", + "styleVariantStandard": "Standard", + "styleVariantStandardDesc": "Applies visual style (colors, textures) without considering its layout. Previously called Style Only.", + "styleVariantStrong": "Strong", + "styleVariantStrongDesc": "Applies a strong visual style, with a slightly reduced composition influence.", + "swapWidthAndHeight": "Swap width and height", + "templatesButton": "Templates", + "unlockAspectRatio": "Unlock aspect ratio", + "upload": "Upload", + "useModelDefaultScheduler": "Use model default scheduler", + "useModelDefaultVae": "Use model default VAE", + "useModelDefaultVaePrecision": "Use model default VAE precision", + "usePrompt": "Use prompt", + "useSeed": "Use seed {{seed}}", + "useSize": "Use size", + "usingBundledVae": "Using the VAE bundled with the model.", + "vae": "VAE", + "vaePrecision": "VAE precision", + "wanGuidanceLowNoise": "Low-noise guidance", + "wanGuidanceLowNoiseHelp": "Guidance for the low-noise expert. Reuses the main guidance when unset.", + "weight": "Weight", + "width": "Width", + "xAxis": "X axis", + "yAxis": "Y axis", + "overridesCount_one": "{{count}} override", + "overridesCount_other": "{{count}} overrides", + "pid": "PiD Decode", + "pidOff": "Off", + "pidFit": "Fit to size", + "pidNative": "Native 4x", + "pidSteps": "PiD steps", + "pidHelp": "Replaces the VAE decode with a 4x super-resolution decode.", + "pidFitHelp": "Generates at the requested size, then decodes 4x and scales back down.", + "pidNativeHelp": "The requested size is the 4x target; generation runs at a quarter of it." + }, + "upscale": { + "addLora": "LoRAs", + "advanced": "Advanced", + "bundledVae": "Bundled VAE", + "cfgScale": "CFG scale", + "clipSkip": "CLIP skip", + "controlNetChanged": "Tile ControlNet changed to {{name}}.", + "controlNetCleared": "The incompatible Tile ControlNet was cleared.", + "creativity": "Creativity", + "creativityHelp": "Higher values invent more detail and increase prompt influence.", + "detailGuidance": "Detail guidance", + "enableNegativePrompt": "Enable negative prompt", + "generation": "Generation", + "inputSize": "Input", + "largeOutput": "Large output", + "largeOutputDescription": "This output may take substantially longer and require more VRAM. Check tile settings before queueing.", + "lorasRemoved": "Removed {{count}} incompatible LoRA model(s).", + "loraWeight": "{{name}} weight", + "mainModel": "Main model", + "mainModelRequired": "Select a supported main model.", + "negativePrompt": "Negative prompt", + "negativePromptAdd": "Negative prompt…", + "negativePromptHide": "Turn off negative prompt", + "notReady": "Upscale is not ready", + "outputMegapixels": "Output area", + "outputSize": "Output", + "positivePrompt": "Positive prompt", + "presetDescriptions": { + "artistic": "Allow substantial reinterpretation for a stylized result.", + "balanced": "Add detail while keeping the original image recognizable.", + "conservative": "Preserve composition and minimize invented detail.", + "creative": "Permit visible new detail while retaining the main structure." + }, + "presetValues": "Creativity {{creativity}} · Structure {{structure}}", + "presets": { + "artistic": "Artistic", + "balanced": "Balanced", + "conservative": "Conservative", + "creative": "Creative" + }, + "presetsLabel": "Treatment preset", + "prompts": "Prompts", + "ready": "Ready", + "readyDescription": "The upscale graph is ready to queue.", + "removeLora": "Remove {{name}}", + "scale": "Scale", + "scaleHelp": "Output dimensions are the source dimensions multiplied by this value.", + "scheduler": "Scheduler", + "seed": "Seed", + "selectLora": "Select a compatible LoRA…", + "selectMainModel": "Select a supported model…", + "selectSpandrelModel": "Select a Spandrel model…", + "selectTileControlNet": "Select a Tile or Union ControlNet…", + "settingsAdjusted": "Adjusted incompatible settings", + "shared": "Shared", + "sharedPromptDescription": "This is the project prompt shared with Generate. For upscaling, describe medium, texture, and style rather than new scene content.", + "sourceAndTreatment": "Source & treatment", + "spandrelModel": "Spandrel model", + "spandrelModelHelp": "The first-pass upscaler. Choose a model suited to photography, illustration, or linework.", + "spandrelModelRequired": "Select a Spandrel image-to-image model.", + "steps": "Steps", + "structure": "Structure", + "structureHelp": "Higher values preserve the source composition more strictly.", + "clipEmbed": "CLIP Embed", + "clipEmbedRequired": "This model needs a CLIP Embed model selected.", + "selectClipEmbed": "Select a CLIP Embed model", + "selectVae": "Select a VAE", + "selectT5Encoder": "Select a T5 encoder", + "t5Encoder": "T5 Encoder", + "t5EncoderRequired": "This model needs a T5 encoder selected.", + "vaeRequired": "This model needs a VAE selected.", + "tileControlNet": "Tile ControlNet", + "tileControlNetHelp": "Guides detail generation with the source image and must match the main model architecture.", + "tileControlNetRequired": "Select a compatible Tile or Union ControlNet.", + "tileOverlap": "Tile overlap", + "tileOverlapHelp": "More overlap reduces seams but increases processing and memory use.", + "tileSize": "Tile size", + "tileSizeHelp": "Larger tiles may improve continuity but require more VRAM.", + "upscale": "Upscale", + "vae": "VAE", + "vaeCleared": "The incompatible VAE was cleared.", + "vaePrecision": "VAE precision" + }, + "graph": { + "setSource": "Set Source", + "previewFailed": "Could not open the graph preview", + "viewGraph": "View Graph" + }, + "project": { + "copyId": "Copy project id", + "activeRuns": "Active runs", + "activeRunCounts": "{{running}} running · {{queued}} queued", + "events": "Events", + "graphNodes": "Graph nodes", + "idCopied": "Project id copied", + "nameHelp": "Saved with the project.", + "nameLabel": "Project name", + "sync": "Sync", + "syncOffline": "Offline — awaiting sync", + "syncSynced": "Synced (revision {{revision}})", + "syncConflict": "Needs conflict resolution", + "syncUpdateClient": "Update Invoke to sync", + "syncWaiting": "Waiting to sync", + "workflows": "Workflows" + }, + "preview": { + "commands": { + "deletePreviewImage": "Delete preview image", + "nextComparisonMode": "Next comparison mode", + "swapComparisonImages": "Swap comparison images", + "toggleFilmstrip": "Toggle filmstrip", + "zoomToActual": "Zoom preview to 100%", + "zoomToFit": "Zoom preview to fit" + }, + "compare": "Compare", + "copyCurrentFrame": "Copy Current Frame", + "copyCurrentFrameDrawFailed": "Could not read the current video frame", + "copyCurrentFrameEncodeFailed": "Could not encode the current video frame", + "copyCurrentFrameNotReady": "The video does not have a current frame yet", + "copyCurrentFrameStale": "The selected video changed before the frame was copied", + "copyCurrentFrameSuccess": "Current video frame copied", + "copyCurrentFrameUnsupported": "This browser cannot copy video frames", + "copyCurrentFrameWriteFailed": "Could not write the current video frame to the clipboard", + "details": "Details", + "dragVideo": "Drag video", + "dropToCompare": "Drop to compare", + "editOnCanvas": "Edit on Canvas", + "emptyDescription": "Generate to Gallery, then select a thumbnail in the Gallery widget to inspect it here.", + "framesPerSecond": "{{count}} fps", + "graph": "Graph", + "graphJsonLabel": "Video graph JSON", + "hideFilmstrip": "Hide filmstrip", + "exitCompare": "Exit Compare", + "hideInProgressDiffusion": "Hide in-progress diffusion", + "imageCount_one": "{{count}} image", + "imageCount_other": "{{count}} images", + "itemCount_one": "{{count}} item", + "itemCount_other": "{{count}} items", + "loadingBoard": "Loading board", + "loadingMetadata": "Loading metadata", + "metadata": "Metadata", + "metadataJsonLabel": "Video metadata JSON", + "noGallerySelection": "No gallery selection", + "showFilmstrip": "Show filmstrip", + "showInProgressDiffusion": "Show in-progress diffusion", + "sideBySide": "Side by Side", + "slider": "Slider", + "hover": "Hover", + "imageTags": "Image tags:", + "hoverComparisonAriaLabel": "Reveal comparison image on hover, focus, or touch", + "sourceRun": "Source run {{id}}", + "videoDuration": "Duration {{duration}}", + "videoDetails": "Video Details", + "videoFailed": "Video could not be loaded", + "videoLabel": "Video {{name}}", + "videoRetry": "Retry", + "viewing": "Viewing", + "workflow": "Workflow", + "workflowJsonLabel": "Video workflow JSON", + "zoom": "Zoom", + "zoomLevel": "Zoom {{percent}}%", + "zoomPercent": "{{percent}}%", + "zoomFit": "Fit to view", + "selectForCompare": "Select for Compare", + "imageActions": "Image actions", + "starImage": "Star image", + "unstarImage": "Unstar image", + "starVideo": "Star video", + "unstarVideo": "Unstar video" + }, + "queue": { + "allScopedItemsRequested": "All scoped queue items were requested.", + "actionConfirmationTitle": "{{action}}?", + "backendItem": "Backend queue item {{id}}.", + "batch": "Batch", + "cancelAllExceptCurrent": "Cancel all except current item", + "cancelAllExceptCurrentRequested": "All pending scoped items except the current item were requested.", + "cancelAllItems": "Cancel All Items", + "cancelConfirmationBody": "This requests cancellation for queue work in the current scope. In-progress items may stop after the backend acknowledges cancellation.", + "cancelCurrent": "Cancel Current", + "cancelCurrentItem": "Cancel Current Item", + "cancelItem": "Cancel queue item #{{id}}", + "cancellationRequested": "Cancellation requested", + "clearAllItems": "Clear All Items", + "clearFailedConfirmationBody": "This permanently removes failed queue items in the current queue scope.", + "clearFailedItems": "Clear Failed Items", + "clearFailedTitle": "Clear failed queue items?", + "clearQueue": "Clear Queue", + "clearQueueConfirmationBody": "This permanently clears queue items in the current queue scope.", + "clearQueueTitle": "Clear queue?", + "couldNotCancelCurrentItem": "Could not cancel the current item.", + "couldNotCancelItem": "Could not cancel this item.", + "couldNotCancelItems": "Could not cancel scoped queue items.", + "couldNotChangeProcessor": "Could not change the queue processor.", + "couldNotClearFailedItems": "Could not clear failed queue items.", + "couldNotClearQueue": "Could not clear the queue.", + "currentBatch": "Current Batch", + "failedItemsCleared": "Failed queue items cleared", + "failedToCancelCurrentItem": "Failed to cancel current item", + "failedToCancelItems": "Failed to cancel queue items", + "failedToClearItems": "Failed to clear queue items", + "failedToClearQueue": "Failed to clear queue", + "failedToPauseProcessor": "Failed to pause processor", + "failedToResumeProcessor": "Failed to resume processor", + "filterRecentByStatus": "Filter recent queue items by status", + "generating_one": "{{count}} generating", + "generating_other": "{{count}} generating", + "itemJsonLabel": "Queue item {{id}} JSON", + "itemProgress": "Queue item progress", + "itemTitle": "Queue item #{{id}}", + "loadSettingsIntoGenerate": "Load these settings into Generate", + "loading": "Loading queue…", + "modelCache": { + "clear": "Clear cache", + "clearFailed": "Clear cache failed", + "cleared": "Model cache cleared", + "clearedDescription_one": "Cleared {{count}} cached model and freed {{bytes}}.", + "clearedDescription_other": "Cleared {{count}} cached models and freed {{bytes}}.", + "couldNotClear": "Could not clear the model cache.", + "hits": "Hits", + "label": "Model Cache", + "loaded": "Loaded", + "misses": "Misses", + "noModelsCleared": "No cached models cleared", + "noModelsClearedDescription": "No unlocked cached models were available to clear. Active models stay loaded until they are no longer in use.", + "usage": "Model cache usage", + "usedOfTotal": "{{used}} of {{total}} used" + }, + "noCurrentItem": "No current item", + "noCurrentItemDescription": "The scoped queue has no in-progress item to cancel.", + "noPrompt": "No prompt", + "openQueue": "Open Queue", + "pauseProcessor": "Pause Processor", + "processorPaused": "Processor paused", + "processorResumed": "Processor resumed", + "queueCancellationRequested": "Queue cancellation requested", + "queueCleared": "Queue cleared", + "queued_one": "{{count}} queued", + "queued_other": "{{count}} queued", + "recallAll": "Recall All", + "recallUnavailable": "Nothing recallable for this item — select a supported Generate model first.", + "recallUnavailableCapabilities": "Nothing recallable yet — model capabilities have not loaded. If loading failed, retry from the Generate panel.", + "recallUnavailableForItem": "Nothing recallable for this item.", + "resumeProcessor": "Resume Processor", + "sendToCanvas": "Send to canvas", + "sendToCanvasComingSoon": "Sending queue results to the canvas is coming soon.", + "settingsRecalled": "Settings recalled", + "settingsRecalledDescription": "Loaded these settings into Generate.", + "settingsRecalledIntoVideoDescription": "Loaded these settings into Video.", + "took": "Took", + "waiting_one": "{{count}} waiting", + "waiting_other": "{{count}} waiting", + "device": { + "label": "GPU", + "shortLabel": "GPU {{index}}", + "tooltip": "Ran on {{name}}" + } + }, + "autosaveStatus": { + "chipLabel": "Autosave: {{status}}", + "label": "Autosave", + "lastSaved": "Last saved: {{time}}", + "pending": "Unsaved changes", + "status": "Status: {{status}}" + }, + "canvas": { + "acceptToLayer": "Accept to Layer", + "candidateCount": "{{current}} of {{total}}", + "clipboard": { + "copyFailed": "Could not copy the selection to the clipboard", + "pasteFailed": "Could not paste an image from the clipboard" + }, + "commands": { + "copySelection": "Copy selection", + "cutSelection": "Cut selection", + "decreaseBrushSize": "Decrease brush/eraser size", + "deleteLayer": "Delete layer", + "deleteSelected": "Delete selected canvas item", + "deselect": "Deselect", + "duplicateLayer": "Duplicate layer", + "fitBbox": "Fit generation frame", + "increaseBrushSize": "Increase brush/eraser size", + "invertSelection": "Invert selection", + "layerBackward": "Send layer backward", + "layerForward": "Bring layer forward", + "layerToBack": "Send layer to back", + "layerToFront": "Bring layer to front", + "mergeDown": "Merge layer down", + "nextEntity": "Next canvas entity", + "nudgeDown": "Nudge layer down", + "nudgeDownLarge": "Nudge layer down 10px", + "nudgeLeft": "Nudge layer left", + "nudgeLeftLarge": "Nudge layer left 10px", + "nudgeRight": "Nudge layer right", + "nudgeRightLarge": "Nudge layer right 10px", + "nudgeUp": "Nudge layer up", + "nudgeUpLarge": "Nudge layer up 10px", + "pasteImage": "Paste image", + "previousEntity": "Previous canvas entity", + "redo": "Redo canvas edit", + "reorderLayer": "Reorder layer", + "resetSelected": "Clear selected mask", + "selectAll": "Select all", + "selectBboxTool": "Select generation frame tool", + "selectBrushTool": "Select brush tool", + "selectEraserTool": "Select eraser tool", + "selectGradientTool": "Select gradient tool", + "selectLassoTool": "Select lasso tool", + "selectMarqueeTool": "Select marquee tool", + "selectMoveTool": "Select move tool", + "selectShapeTool": "Select shape tool", + "selectTextTool": "Select text tool", + "selectTransformTool": "Select transform tool", + "selectViewTool": "Select view tool", + "toggleNonRasterLayers": "Hide control, guidance and mask layers", + "undo": "Undo canvas edit", + "groupLayers": "Group selected layers", + "ungroupLayers": "Ungroup selected layers", + "swapColors": "Swap foreground and background colors", + "resetColors": "Reset colors to black and white" + }, + "contextMenu": { + "createError": "Couldn't create from BBox", + "createFromBbox": "Create From BBox", + "createdFromBbox": "Added {{destination}} from BBox", + "empty": "This region has no visible raster content.", + "newControlLayer": "New Control Layer", + "newGlobalReferenceImage": "New Global Reference Image", + "newRasterLayer": "New Raster Layer", + "newRegionalReferenceImage": "New Regional Reference Image", + "notReady": "The canvas is still preparing image data.", + "referenceImageAdded": "Added reference image", + "referenceImageFull": "The selected model can't accept more reference images.", + "referenceImageUnsupported": "The selected model doesn't support reference images.", + "saveBboxToGallery": "Save BBox to Gallery", + "saveCanvasToGallery": "Save Canvas to Gallery", + "saveError": "Couldn't save to gallery", + "saveToGallery": "Save to Gallery", + "saved": "Saved to gallery", + "savedDescription": "{{name}} was added to your gallery.", + "stale": "The canvas changed while saving. Try again." + }, + "controls": { + "fitBboxToLayers": "Fit frame to layers", + "fitBboxToMasks": "Fit frame to masks", + "fitToView": "Fit to view", + "moreSaveOptions": "More save options", + "newCanvas": "New canvas", + "newCanvasConfirm": "Clear all layers and start a fresh canvas? This cannot be undone.", + "newSession": "New session", + "zoomLevel": "Zoom level" + }, + "hideStagedResultPreview": "Hide staged result preview", + "hideStagingThumbnails": "Hide staging thumbnails", + "import": { + "blocked": "Finish the current canvas operation before adding images.", + "capabilitiesUnavailable": "The resize target depends on model capabilities, which haven't loaded yet. Try again in a moment.", + "control": "Control Layer", + "dropControl": "Add as Control Layer", + "dropInpaintMask": "Add as Inpaint Mask", + "dropRaster": "Add as Raster Layer", + "dropRegionalReference": "Add as Regional Reference Image", + "dropResizedControl": "Add as Resized Control Layer", + "empty": "No gallery images were available to add.", + "failed": "Couldn't add images to canvas", + "inpaintMask": "Inpaint Mask", + "newCanvasFromImage_one": "New Canvas from Image", + "newCanvasFromImage_other": "New Canvas from Images", + "newCanvasSuccess_one": "Created a new canvas from the image", + "newCanvasSuccess_other": "Created a new canvas from {{count}} images", + "newFromImage_one": "New from Image", + "newFromImage_other": "New from Images", + "newLayerFromImage_one": "New Layer from Image", + "newLayerFromImage_other": "New Layers from Images", + "partial_one": "Added {{successCount}} image; {{failedCount}} could not be prepared.", + "partial_other": "Added {{successCount}} images; {{failedCount}} could not be prepared.", + "raster": "Raster Layer", + "regionalGuidance": "Regional Guidance", + "regionalReference": "Regional Reference Image", + "resizedControl": "Resized Control Layer", + "staleDocument": "The canvas changed while the images were being prepared. Try again.", + "staleProject": "The destination project was closed before the images were ready.", + "success_one": "Added {{count}} image as canvas layers", + "success_other": "Added {{count}} images as canvas layers" + }, + "nextStagedCandidate": "Next staged candidate", + "previousStagedCandidate": "Previous staged candidate", + "selectStagedCandidate": "Select staged candidate {{number}}", + "settings": { + "bboxOverlay": "Show bbox overlay", + "checkerboard": "Checkerboard background", + "clipToBbox": "Clip strokes to bbox", + "clearCaches": "Clear caches", + "clearHistory": "Clear history", + "grid": "Show grid", + "invertBrushScroll": "Invert scroll for brush size", + "label": "Canvas settings", + "logDebugInfo": "Log debug info", + "outputOnlyMaskedRegions": "Output only generated regions", + "ruleOfThirds": "Rule of thirds", + "sections": { + "behavior": "Behavior", + "debug": "Debug", + "display": "Display", + "grid": "Grid" + }, + "showBbox": "Show generation frame", + "showProgressOnCanvas": "Show progress on canvas", + "snapToGrid": "Snap to grid" + }, + "showStagedResultPreview": "Show staged result preview", + "showStagingThumbnails": "Show staging thumbnails", + "staging": { + "autoSwitch": "Auto-switch", + "autoSwitchLatest": "Newest", + "autoSwitchOff": "Off", + "autoSwitchProgress": "In progress", + "generating": "Generating…", + "moreAcceptOptions": "More accept options", + "recall": "Recall settings", + "saveAsDisabledLayer": "Keep as Hidden Layer", + "saveError": "Couldn't save to gallery", + "saveToGallery": "Save to gallery", + "saved": "Saved to gallery", + "savedDescription": "{{name}} added to your gallery." + }, + "structural": { + "busy": "Another canvas operation is in progress, so this edit was not kept.", + "failed": "Canvas edit failed", + "gestureActive": "Finish the current canvas gesture, then try again.", + "notReady": "The canvas is not ready to record this edit.", + "overBudget": "This edit is too large to undo, so it was not made.", + "refusedInvalidTarget": "That layer cannot be the target of this edit.", + "refusedLocked": "A locked layer is in the way. Unlock it first.", + "refusedMissing": "A layer this edit needs is no longer in the canvas.", + "refusedUnsupported": "This edit is not available here.", + "refusedWrongType": "This edit does not apply to this kind of layer.", + "rejected": "The canvas did not accept this edit. Nothing changed.", + "reverted": "The edit could not be verified and was reverted.", + "stale": "The canvas changed before this edit landed. Try again.", + "unmirrored": "The edit was reverted, but the canvas view may be out of date.", + "unverified": "The edit could not be verified or reverted. Check the canvas before continuing.", + "refusedNotSiblings": "Only items that share the same group can be grouped together.", + "refusedDepth": "Groups can nest at most 10 levels deep.", + "refusedCycle": "A group can't be moved into itself.", + "refusedNodeLimit": "The canvas can't hold more than 10,000 layers and groups." + }, + "surface": "Canvas surface", + "toolOptions": { + "brushColor": "Brush color", + "brushSize": "Brush size", + "hardness": "Hardness", + "strokePreview": "Stroke preview", + "deselect": "Deselect", + "eraseSelection": "Erase", + "eraserSize": "Eraser size", + "fillSelection": "Fill", + "frameHeight": "H", + "frameWidth": "W", + "gradientAngle": "Angle", + "gradientEdit": "Edit gradient", + "gradientLinear": "Linear", + "gradientRadial": "Radial", + "gradientAddStop": "Add stop", + "gradientRemoveStop": "Remove stop", + "gradientStopAt": "Gradient stop at {{percent}}%", + "gradientStopColor": "Stop color", + "gradientStops": "Stops", + "invertSelection": "Invert", + "lassoFreehand": "Freehand", + "lassoHint": "Draw a shape to select. Shift adds, Alt subtracts.", + "lassoPolygon": "Polygon", + "lassoPolygonHint": "Click to place points. Double-click, Enter, or click the first point to close.", + "lassoShape": "Lasso mode", + "liftSelectionToLayer": "New layer", + "marqueeHint": "Drag to select a region. Shift adds, Alt subtracts; press them mid-drag to constrain or draw from center.", + "marqueeKind": "Marquee shape", + "movePosition": "Move layer", + "opacity": "Opacity", + "positionX": "X", + "positionY": "Y", + "pressureAffectsOpacity": "Pen pressure affects opacity", + "pressureAffectsWidth": "Pen pressure affects width", + "rotation": "Rotation", + "scaleHeight": "H %", + "scaleWidth": "W %", + "selectAll": "Select all", + "selectionActions": "Actions", + "selectionAdd": "Add", + "selectionIntersect": "Intersect", + "selectionMode": "Selection mode", + "selectionNeedsPaintLayer": "Select an unlocked, visible paint layer first", + "selectionNeedsSelection": "Make a selection first", + "selectionReplace": "Replace", + "selectionSubtract": "Subtract", + "setFrame": "Set generation frame", + "shapeEdit": "Edit shape", + "shapeEllipse": "Ellipse", + "shapeFill": "Fill", + "shapeFreehand": "Freehand", + "shapeFreehandHint": "Drag to draw a freeform shape.", + "shapeHint": "Drag to draw a shape.", + "shapePolygon": "Polygon", + "shapePolygonHint": "Click to place points. Double-click, Enter, or click the first point to close.", + "shapeRect": "Rectangle", + "shapeStar": "Star", + "shapeTarget": "Place on", + "shapeTargetNew": "New layer", + "shapeTargetSelected": "Selected layer", + "shapeTriangle": "Triangle", + "shapeStroke": "Stroke", + "shapeStrokeWidth": "Stroke width", + "size": "Size", + "textAlignCenter": "Align center", + "textAlignLeft": "Align left", + "textAlignRight": "Align right", + "textColor": "Text color", + "textEdit": "Edit text", + "textFont": "Font", + "textFontBuiltinGroup": "Built-in", + "textFontCustomGroup": "Custom", + "textFontDefaultPreset": "Default", + "textFontHideAdvancedAxes": "Hide hidden axes", + "textLineHeight": "Line height", + "textFontPreset": "Preset", + "textFontResetAxes": "Reset axes", + "textFontShowAdvancedAxes": "Show hidden axes", + "textFontStyle": "Style", + "textFontStyleItalic": "Italic", + "textFontStyleNormal": "Regular", + "textFontStyleOblique": "Oblique", + "textSize": "Font size", + "textWeight": "Weight", + "gradientPairPreset": "Foreground to background preset" + }, + "tools": { + "bbox": "Generation frame", + "brush": "Brush", + "colorPicker": "Color picker", + "eraser": "Eraser", + "gradient": "Gradient", + "label": "Tools", + "lasso": "Lasso select", + "marquee": "Marquee select", + "move": "Move", + "sam": "Select object", + "shape": "Shape", + "text": "Text", + "transform": "Transform", + "view": "View" + } + }, + "workflow": { + "about": "About", + "addNode": "Add node", + "author": "Author", + "class": "Class", + "connectorNode": "Connector node", + "contact": "Contact", + "copyJson": "Copy workflow JSON", + "copyJsonFailed": "Failed to copy workflow JSON", + "copyJsonSuccess": "Workflow JSON copied", + "currentImageNode": "Current Image node", + "declaredOutputs": "Declared outputs", + "description": "Description", + "details": "Details", + "detailsWithEllipsis": "Workflow details…", + "renameWithEllipsis": "Rename workflow…", + "exportJson": "Export workflow JSON", + "exportAsPng": "Download workflow as PNG", + "exportImageFailed": "Could not export workflow image.", + "form": "Form", + "importFailed": "Failed to import workflow", + "importInvalid": "The file is not a valid workflow JSON.", + "importJsonWithEllipsis": "Import workflow JSON…", + "importWarning": "Workflow import warning", + "importedLabel": "Imported \"{{name}}\"", + "inspectorTabs": { + "data": "Data", + "details": "Details", + "outputs": "Outputs", + "template": "Template" + }, + "latestOutput": "Latest output", + "library": "Workflow library", + "libraryWithEllipsis": "Workflow library…", + "newWorkflow": "New workflow", + "noRunRecorded": "This node has not run in this session.", + "noTemplateKnown": "No template is known for \"{{type}}\" on this backend.", + "nodeData": "Node data", + "nodeInspector": "Node Inspector", + "nodeNotes": "Node notes", + "nodeNotesPlaceholder": "Notes about this node…", + "nodeTemplate": "Node template", + "notes": "Notes", + "notesNode": "Notes node", + "openProjectWorkflows": "Open this project's workflows. Current workflow: {{name}}", + "outputImage": "Output image", + "pack": "Pack", + "panelContent": "Workflow panel content", + "panelMode": "Workflow panel mode", + "persistenceConflict": "Project has a sync conflict — resolve it to keep saving", + "persistenceError": "Project save failed — retrying", + "persistencePending": "Project changes waiting to save (recovery draft kept)", + "persistencePendingNoRecovery": "Project changes waiting to save — no browser recovery available", + "persistenceSaved": "Project saved to the server", + "persistenceSaving": "Saving project…", + "projectWorkflows": "This project's workflows", + "resizeNodeInspector": "Resize node inspector", + "runStatus": { + "completed": "Completed", + "failed": "Failed", + "running": "Running…" + }, + "saveToLibrary": "Save to library", + "saveToLibraryWithEllipsis": "Save to library…", + "selectNodeToInspect": "Select a node in the Workflow editor to inspect it.", + "selectedNodeInspector": "Selected node inspector", + "status": "Status", + "submitRemovedBody": "The workflow was removed from the project before it could be queued.", + "submitRemovedTitle": "Workflow not queued", + "tags": "Tags", + "title": "Title", + "type": "Type", + "untitled": "Untitled Workflow", + "updateTemplateWithEllipsis": "Update library template…", + "version": "Version", + "workflowJson": "Workflow JSON" + }, + "layers": { + "actions": { + "addControlLayer": "Control layer", + "addInpaintMask": "Inpaint mask", + "addRasterLayer": "Raster layer", + "addRegionalGuidance": "Regional guidance", + "addRegionalReferenceImage": "Regional reference image", + "actionFailed": "Layer action failed", + "blendMode": "Blend mode", + "busy": "The canvas is busy. Try again when the current action finishes.", + "copyFailed": "The layer could not be copied.", + "copyLayerToClipboard": "Copy layer to clipboard", + "copyToControl": "Copy to control layer", + "copyToInpaintMask": "Copy to inpaint mask", + "copyToRasterLayer": "Copy to raster layer", + "copyToRegionalGuidance": "Copy to regional guidance", + "convertToControl": "Convert to control layer", + "convertToInpaintMask": "Convert to inpaint mask", + "convertToRaster": "Convert to raster layer", + "convertToRegionalGuidance": "Convert to regional guidance", + "cropBusy": "Finish the current canvas action before cropping this layer.", + "cropFailed": "The layer could not be cropped.", + "cropLayerToBbox": "Crop layer to bbox", + "cropUnsupported": "This layer cannot be cropped.", + "cropped": "Layer cropped to bbox.", + "cutaway": "Cutaway with layer below", + "cutout": "Cutout with layer below", + "delete": "Delete layer", + "deleteSelected": "Delete selected layers", + "disableAutoNegative": "Disable auto-negative", + "disableTransparencyEffect": "Disable transparent preview", + "disabled": "Enable the layer before using this action.", + "duplicate": "Duplicate layer", + "duplicateSelected": "Duplicate selected layers", + "empty": "This layer has no content.", + "enableAutoNegative": "Enable auto-negative", + "enableTransparencyEffect": "Enable transparent preview", + "exclude": "Exclude layer below", + "extractMaskedArea": "Extract masked area", + "fitToBbox": "Fit to bbox", + "disableLayer": "Disable layer", + "enableLayer": "Enable layer", + "disableSelected": "Disable selected", + "enableSelected": "Enable selected", + "disableGroup": "Disable group", + "enableGroup": "Enable group", + "hideLayer": "Hide layer", + "showLayer": "Show layer", + "hideSelectedLayers": "Hide selected", + "showSelectedLayers": "Show selected", + "intersect": "Intersect with layer below", + "mergeSelected": "Merge selected layers", + "lock": "Lock layer", + "lockSelected": "Lock selected", + "locked": "Unlock the layer before using this action.", + "mergeDown": "Merge down", + "missing": "The layer no longer exists.", + "moveBackward": "Move backward", + "moveForward": "Move forward", + "moveToBack": "Move to back", + "moveToFront": "Move to front", + "notReady": "The layer content is still loading. Try again shortly.", + "opacity": "Opacity", + "operationFailed": "The canvas could not apply this change, so nothing was changed.", + "overBudget": "There is not enough canvas memory to make this change undoable, so nothing was changed.", + "stale": "The layer changed before this action finished, so nothing was changed. Try again.", + "rasterize": "Rasterize layer", + "rename": "Rename layer", + "reorder": "Reorder layer", + "runWorkflow": "Run workflow", + "saveLayerToAssets": "Save layer to uploads", + "select": "Select {{name}}", + "selectObject": "Select object", + "toggleAutoNegative": "Toggle auto-negative", + "toggleHidden": "Show/hide layer", + "toggleLock": "Toggle lock", + "toggleTransparencyEffect": "Toggle transparent preview", + "toggleVisibility": "Enable/disable", + "transform": "Transform", + "unsupported": "This action is not supported for this layer.", + "unlock": "Unlock layer", + "unlockSelected": "Unlock selected", + "group": "Group layers", + "groupSelected": "Group selected layers", + "ungroup": "Ungroup", + "newGroup": "New group", + "expandGroup": "Expand group", + "collapseGroup": "Collapse group", + "indent": "Move into the group above", + "outdent": "Move out of the group", + "groupLocked": "Locked by a group", + "groupDisabled": "Disabled by a group", + "groupHidden": "Hidden by a group", + "duplicateCount_one": "Duplicate layer", + "duplicateCount_other": "Duplicate {{count}} layers", + "groupCount_one": "Group layer", + "groupCount_other": "Group {{count}} layers", + "deleteCount_one": "Delete layer", + "deleteCount_other": "Delete {{count}} layers", + "toggleActive": "Enable/disable layer", + "hideModifiers": "Hide modifiers", + "showModifiers": "Show modifiers", + "addDenoiseLimit": "Add denoise limit", + "addNoise": "Add noise", + "addRegenerateRegion": "Add regenerate region" + }, + "control": { + "apply": "Apply", + "applyFilter": "Apply filter", + "beginStep": "Begin step", + "cancel": "Cancel", + "capabilitiesLoadFailed": "Model capabilities could not be loaded, so this layer cannot be checked yet.", + "capabilitiesLoading": "This layer is checked once model capabilities have loaded.", + "capabilitiesRetryHint": "Open the layer's settings to retry.", + "endStep": "End step", + "filter": "Filter", + "filterParams": { + "amount": "Amount", + "autoScale": "Autoscale", + "blur_type": "Blur type", + "channel": "Channel", + "coarse": "Coarse", + "distance_threshold": "Distance threshold", + "draw_body": "Draw body", + "draw_face": "Draw face", + "draw_hands": "Draw hands", + "high_threshold": "High threshold", + "low_threshold": "Low threshold", + "max_faces": "Max faces", + "min_confidence": "Min confidence", + "model": "Image-to-image model", + "model_size": "Model size", + "quantize_edges": "Quantize edges", + "scale_factor": "Scale factor", + "scale": "Scale", + "scale_values": "Multiply values", + "score_threshold": "Score threshold", + "scribble": "Scribble", + "tile_size": "Tile size", + "noise_color": "Color noise", + "noise_type": "Noise type", + "radius": "Radius", + "size": "Size", + "value": "Value" + }, + "filterOptions": { + "blur_type": { + "box": "Box", + "gaussian": "Gaussian" + }, + "channel": { + "alpha": "Alpha (RGBA)", + "black": "Black (CMYK)", + "blue": "Blue (RGBA)", + "cb": "Cb (YCbCr)", + "cr": "Cr (YCbCr)", + "cyan": "Cyan (CMYK)", + "green": "Green (RGBA)", + "hue": "Hue (HSV)", + "lab_a": "A (LAB)", + "lab_b": "B (LAB)", + "luminosity": "Luminosity (LAB)", + "magenta": "Magenta (CMYK)", + "red": "Red (RGBA)", + "saturation": "Saturation (HSV)", + "value": "Value (HSV)", + "y": "Y (YCbCr)", + "yellow": "Yellow (CMYK)" + }, + "model_size": { + "base": "Base", + "large": "Large", + "small": "Small", + "small_v2": "Small v2" + }, + "noise_type": { + "gaussian": "Gaussian", + "salt_and_pepper": "Salt and pepper" + } + }, + "filters": { + "adjust_image": "Adjust image", + "canny_edge_detection": "Canny", + "color_map": "Color map", + "content_shuffle": "Content shuffle", + "depth_anything_depth_estimation": "Depth Anything", + "dw_openpose_detection": "DW Openpose", + "hed_edge_detection": "HED", + "img_blur": "Blur", + "img_noise": "Noise", + "lineart_anime_edge_detection": "Lineart Anime", + "lineart_edge_detection": "Lineart", + "mediapipe_face_detection": "Mediapipe Face", + "mlsd_detection": "MLSD", + "normal_map": "Normal map", + "pidi_edge_detection": "PiDiNet", + "scribble_edge_detection": "Scribble", + "spandrel_filter": "Spandrel" + }, + "invalidLayer": "Control layer \"{{name}}\": {{reason}}", + "kind": "Adapter type", + "kinds": { + "control_lora": "Control LoRA", + "controlnet": "ControlNet", + "t2i_adapter": "T2I Adapter", + "z_image_control": "Z-Image ControlNet" + }, + "mode": "Control mode", + "model": "Model", + "modes": { + "balanced": "Balanced", + "more_control": "More control", + "more_prompt": "More prompt", + "unbalanced": "Unbalanced" + }, + "needsAttention": "Control layer needs attention", + "preview": "Preview filter", + "selectModel": "Select a model", + "stepRange": "Step range", + "transparencyEffect": "Transparent preview", + "validation": { + "capabilities_unavailable": "waiting for model capabilities to load.", + "control_lora_limit": "Only one Control LoRA can be used at a time.", + "flux_fill_control_lora": "Control LoRA is not compatible with FLUX Fill.", + "incompatible_adapter": "The selected model does not match this adapter type.", + "incompatible_base": "The control model is incompatible with the selected base model.", + "invalid_adapter_values": "Control weight and step range must be valid before generating.", + "missing_model": "Select a control model before generating.", + "unsupported_adapter": "This control adapter is not supported by the selected base model.", + "z_image_control_limit": "Only one Z-Image control can be used at a time." + }, + "weight": "Weight" + }, + "rasterFilter": { + "aborted": "The filter was cancelled before it was applied.", + "applyAs": "Apply as", + "applyFailed": "The filter could not be applied.", + "autoProcess": "Process automatically", + "categories": { + "edge": "Edge detection", + "depth": "Depth & normal", + "pose": "Pose & face", + "color": "Color & tone", + "noise": "Blur & noise", + "upscale": "Upscale" + }, + "busy": "Finish the current canvas interaction, then try again.", + "cancel": "Cancel", + "commitFailure": "The filtered pixels could not be committed: {{message}}", + "copy": "Copy", + "disabled": "Show the layer before filtering it.", + "durabilityFailure": "The filtered image could not be preserved: {{message}}", + "empty": "This layer has no pixels to filter.", + "graphFailure": "The filter failed: {{message}}", + "locked": "Unlock the layer before applying the filter.", + "missing": "The layer is no longer in the canvas.", + "notReady": "The canvas is not ready for the filter yet. Try again in a moment.", + "overBudget": "The filtered layer is too large to undo, so it was not applied.", + "preview": "Preview", + "processFailed": "The filter could not run.", + "replace": "Replace", + "running": "Running filter…", + "sourceOverBudget": "Not enough memory is free to read this layer for the filter.", + "statusCommitting": "Applying filter…", + "statusError": "Filter needs attention.", + "stale": "The layer changed while filtering. Preview it again.", + "title": "Filter", + "unsupported": "This layer content cannot be filtered." + }, + "selectObject": { + "autoProcess": "Auto Process", + "bbox": "BBox", + "bboxActive": "Bounding box active", + "bboxInactive": "No bounding box", + "errorDecode": "The object preview could not be decoded.", + "errorEmpty": "The layer has no visible content to select.", + "errorInvalid": "Add an object prompt, point, or bounding box before processing.", + "errorLocked": "Select Object cannot finish while canvas editing is locked.", + "errorNoOutput": "Select Object finished without producing an image.", + "errorNotReady": "The layer source is not ready. Try again when it finishes loading.", + "errorOutputDimension": "The Select Object result dimensions do not match the layer source.", + "errorQueue": "Select Object processing failed on the backend.", + "errorReconcile": "The completed Select Object result could not be retrieved.", + "errorUnknown": "Select Object could not finish.", + "errorUpload": "The layer source could not be uploaded for Select Object.", + "exclude": "Exclude", + "excludeCount": "Exclude {{count}}", + "include": "Include", + "includeCount": "Include {{count}}", + "invert": "Invert", + "isolatedPreview": "Isolated Preview", + "model": "Model", + "modelHuge": "SAM Huge", + "modelSam2Large": "SAM 2 Large", + "mode": "Selection mode", + "prompt": "Object prompt", + "promptGuidance": "Describe the object you want to select.", + "promptMode": "Prompt", + "pointType": "Point type", + "process": "Process", + "refine": "Refine", + "reset": "Reset", + "saveAs": "Save As", + "saveAs_control": "Control layer", + "saveAs_inpaint_mask": "Inpaint mask", + "saveAs_raster": "Raster layer", + "saveAs_regional_guidance": "Regional guidance", + "saveAs_selection": "Selection", + "settings": "Select Object settings", + "sourceLayerLabel": "{{name}} · {{type}} · {{width}} × {{height}}", + "statusCommitting": "Applying object result…", + "statusError": "Select Object needs attention.", + "statusPreparingSource": "Preparing layer source…", + "statusProcessingSam": "Finding object…", + "statusReady": "Ready", + "statusRenderingPreview": "Rendering object preview…", + "statusScheduled": "Waiting to process object…", + "statusUploading": "Uploading layer source…", + "technicalDetails": "Technical details", + "title": "Select Object", + "visual": "Visual", + "visualGuidance": "Click to add · drag to move · Shift flips type" + }, + "runWorkflow": { + "aborted": "Workflow run was canceled.", + "busy": "Finish the current canvas interaction, then try again.", + "cancel": "Cancel", + "copyRaster": "Copy as raster layer", + "copySuccess": "Added the workflow result as a raster layer.", + "destination": "Destination", + "disabled": "Enable the layer before running the workflow.", + "durabilityFailure": "The workflow output could not be preserved: {{message}}", + "empty": "This layer has no content to run through the workflow.", + "failed": "Workflow run failed: {{message}}", + "gallery": "Gallery", + "gallerySuccess": "Saved the workflow result to Gallery.", + "graphFailure": "The active workflow failed: {{message}}", + "hydrationFailure": "The workflow output could not be loaded: {{message}}", + "input": "Workflow image input", + "locked": "Unlock the layer before running the workflow.", + "missing": "The source layer no longer exists.", + "noBindings": "The active workflow has no compatible image input and output.", + "notReady": "The active workflow is not ready with this layer input.", + "overBudget": "The workflow result is too large to apply undoably, so the layer was not changed.", + "output": "Workflow image output", + "replace": "Replace layer", + "replaceSuccess": "Replaced the layer with the workflow result.", + "run": "Run", + "running": "Running workflow…", + "staging": "Canvas staging", + "stagingSuccess": "Added the workflow result to canvas staging.", + "stale": "The layer or project changed while the workflow was running. Run it again.", + "title": "Run workflow from layer", + "unsupported": "This layer cannot be used as a workflow image input." + }, + "addLayer": "Add layer", + "adjustments": { + "brightness": "Brightness", + "channel": "Channel", + "channels": { + "rgb": "RGB", + "r": "Red", + "g": "Green", + "b": "Blue" + }, + "contrast": "Contrast", + "curves": "Curves", + "exposure": "Exposure", + "curvesHint": "Drag points to bend the curve. Double-click empty space to add a point; double-click a point to remove it.", + "levels": "Levels", + "inputLevels": "Input levels", + "inputBlack": "Input black point", + "inputWhite": "Input white point", + "gamma": "Gamma", + "outputLevels": "Output levels", + "outputBlack": "Output black point", + "outputWhite": "Output white point", + "hue": "Hue", + "invert": "Invert", + "invertHint": "Inverts the layer’s colors. This adjustment has no options.", + "saturation": "Saturation", + "transparencyLock": "Lock transparency" + }, + "blendModes": { + "color": "Color", + "color-burn": "Color burn", + "color-dodge": "Color dodge", + "darken": "Darken", + "difference": "Difference", + "exclusion": "Exclusion", + "hard-light": "Hard light", + "hue": "Hue", + "lighten": "Lighten", + "luminosity": "Luminosity", + "multiply": "Multiply", + "normal": "Normal", + "overlay": "Overlay", + "saturation": "Saturation", + "screen": "Screen", + "soft-light": "Soft light" + }, + "empty": "No layers yet. Add a layer or paint on the canvas to get started.", + "groupActions": { + "collapse": "Collapse group", + "expand": "Expand group", + "exportNothing": "No raster layer content to export", + "exportNotReady": "Layers are still loading — try exporting again in a moment", + "exportOverBudget": "Not enough raster memory is available to export this PSD", + "exportStale": "The canvas changed during export — try again", + "exportAborted": "PSD export was canceled", + "exportFailed": "PSD export failed", + "exportPsd": "Export to PSD", + "exportTooLarge": "Canvas is too large to export to PSD", + "hideAll": "Hide all", + "mergeNotReady": "Layers are still loading — try merging again in a moment", + "mergeOverBudget": "Not enough canvas memory to merge these layers", + "mergeVisible": "Merge visible", + "new": "New layer", + "showAll": "Show all", + "toggleVisibility": "Enable/disable group", + "toggleHidden": "Show/hide group" + }, + "groups": { + "control": "Control Layers", + "inpaint_mask": "Inpaint Masks", + "raster": "Raster Layers", + "regional_guidance": "Regional Guidance" + }, + "maskFill": { + "color": "Fill color", + "denoiseLimit": "Denoise limit", + "fill": "Fill", + "invert": "Invert mask", + "noiseLevel": "Noise level", + "style": "Fill style", + "styles": { + "crosshatch": "Crosshatch", + "diagonal": "Diagonal", + "grid": "Grid", + "horizontal": "Horizontal", + "solid": "Solid", + "vertical": "Vertical" + }, + "editInColorPane": "Edit tint in the Color pane" + }, + "menuGroups": { + "layers": "Layers", + "regional": "Regional" + }, + "menu": { + "arrange": "Arrange", + "booleanOperations": "Boolean operations", + "convertTo": "Convert to", + "copyTo": "Copy to", + "addAdjustment": "Add adjustment", + "colorLabel": "Color label" + }, + "labels": { + "none": "None", + "red": "Red", + "orange": "Orange", + "yellow": "Yellow", + "green": "Green", + "blue": "Blue", + "violet": "Violet", + "gray": "Gray" + }, + "options": "Layer options", + "regionalGuidance": { + "addReferenceImage": "Add reference image", + "autoNegative": "Auto-Negative", + "clearReferenceImage": "Clear reference image", + "method": "Method", + "methods": { + "composition": "Composition", + "full": "Full", + "style": "Style", + "style_precise": "Style (precise)", + "style_strong": "Style (strong)" + }, + "model": "Model", + "negativePrompt": "Negative prompt", + "negativePromptAdd": "Negative prompt…", + "negativePromptHide": "Turn off negative prompt", + "negativePromptPlaceholder": "Region negative prompt", + "positivePrompt": "Positive prompt", + "positivePromptPlaceholder": "Region positive prompt", + "referenceImage": "Reference image", + "referenceImageHelp": "Choose an image from the gallery, drop one here, or upload a file.", + "referenceImages": "Reference images", + "removeReferenceImage": "Remove reference image", + "selectModel": "Select a model", + "unsupportedModel": "The selected model does not support regional guidance. This layer is skipped during generation.", + "weight": "Weight" + }, + "types": { + "control": "Control", + "image": "Image", + "inpaint_mask": "Inpaint", + "paint": "Paint", + "gradient": "Gradient", + "shape": "Shape", + "text": "Text", + "regional_guidance": "Region", + "group": "Group" + }, + "groupSummary_one": "{{count}} layer", + "groupSummary_other": "{{count}} layers", + "groupEmpty": "Empty group", + "groupSelectedHint": "Select a layer inside this group to paint.", + "tree": "Layer tree", + "summary": { + "referenceImages_one": "{{count}} reference image", + "referenceImages_other": "{{count}} reference images", + "denoiseLimit": "Denoise ≤ {{value}}%", + "noise": "Noise {{value}}%" + }, + "footer": { + "filter": "Filter layers", + "degraded": "Large document: thumbnails and drag reordering are off." + }, + "dropRefused": "Can't drop here: {{reason}}", + "panes": { + "collapse": "Collapse editor panes", + "expand": "Expand editor panes", + "resize": "Resize editor panes", + "tabs": "Layer editor panes" + }, + "colorPane": { + "addToPalette": "Add current color to palette", + "background": "Background color", + "channels": { + "blue": "Blue", + "brightness": "Brightness", + "green": "Green", + "hex": "Hex color", + "hue": "Hue", + "lightness": "Lightness", + "red": "Red", + "saturation": "Saturation" + }, + "collapse": "Collapse color pane", + "expand": "Expand color pane", + "foreground": "Foreground color", + "format": "Color value format", + "palette": "Project palette", + "pickerModeBox": "Switch to box picker", + "pickerModeWheel": "Switch to wheel picker", + "recents": "Recent colors", + "removeSwatchHint": "{{color}} — right-click or press Delete to remove", + "resize": "Resize color pane", + "swatches": "Swatches", + "tabs": "Color panes", + "maskTint": "Mask tint" + }, + "historyPane": { + "count_one": "{{count}} step", + "count_other": "{{count}} steps", + "empty": "No edits yet.", + "steps": "Edit history" + }, + "overviewPane": { + "pan": "Pan the canvas view" + }, + "treeTabs": "Layer panel views", + "modifiers": { + "disable": "Disable", + "enable": "Enable", + "parentDisabled": "Layer is disabled", + "toggleActive": "Enable/disable", + "denoise": "Denoise limit", + "denoiseHelp": "Caps how strongly generation may repaint the masked area.", + "noise": "Noise", + "regenerateRegion": "Regenerate region", + "removeRegion": "Remove regenerate region", + "regionHint": "Shows this layer's content as a live inpaint mask: everything on the layer, now and as you keep editing it, marks its area to regenerate while enabled.", + "noiseHelp": "Adds noise inside the masked area before generation.", + "removeDenoise": "Remove denoise limit", + "removeNoise": "Remove noise", + "brightnessContrast": "Brightness/Contrast", + "duplicateAdjustment": "Duplicate adjustment", + "renameAdjustment": "Rename adjustment", + "renameRegion": "Rename region", + "moveDown": "Move down", + "moveUp": "Move up", + "removeAdjustment": "Remove adjustment", + "reorderAdjustment": "Reorder adjustment", + "moveReferenceImage": "Move reference image", + "moveToLayer": "Move to “{{name}}”" + } + }, + "gallery": { + "alwaysShowDimensions": "Always show dimensions", + "archiveBoard": "Archive Board", + "archivedBoards": "Archived boards", + "autoAddBadge": "Auto", + "autoAddBadgeTooltip": "New generations, canvas saves and pasted uploads land here, as do workflow nodes whose board is Auto.", + "autoAddToBoard": "Auto-add to this board", + "stopAutoAdd": "Stop auto-adding here", + "assetCount_one": "{{count}} upload", + "assetCount_other": "{{count}} uploads", + "boardActionsForBoard": "Board actions for {{name}}", + "boardArchived": "Archived board \"{{name}}\"", + "boardArchivePreparing_one": "Preparing an image archive of \"{{name}}\". {{count}} video will be omitted.", + "boardArchivePreparing_other": "Preparing an image archive of \"{{name}}\". {{count}} videos will be omitted.", + "boardCountsBreakdown": "{{media}} media, {{assets}} uploads", + "boardCreated": "Created board \"{{name}}\"", + "boardGroups": { + "boards": "Boards", + "byDate": "By Date" + }, + "boardItemCounts": "{{images}} · {{videos}} · {{assets}}", + "boardName": "Board name", + "boardRenamed": "Renamed board to \"{{name}}\"", + "boardUnarchived": "Unarchived board \"{{name}}\"", + "boardVisibility": "Board visibility", + "destination": "Destination", + "followSelectedBoard": "Send new results to the selected board", + "commands": { + "clearSelection": "Clear gallery selection", + "deleteSelection": "Delete gallery selection", + "navigationDown": "Gallery navigation down", + "navigationLeft": "Gallery navigation left", + "navigationRight": "Gallery navigation right", + "navigationUp": "Gallery navigation up", + "selectAllOnPage": "Select all gallery images on page", + "toggleStarImage": "Toggle gallery image star", + "toggleStarredOnly": "Toggle gallery starred-only filter" + }, + "clearClusterSearch": "Clear cluster filter", + "clearImageSearch": "Clear image similarity search", + "clearSemanticSearch": "Clear semantic search", + "exitSemanticSearch": "Exit semantic search", + "createBoard": "Create board", + "createBoardNamed": "Create board \"{{name}}\"", + "collapseStarredItems": "Collapse starred items", + "dateBoards": "Date boards", + "dateFilterDay": "{{date}}", + "dateFilterFrom": "From {{date}}", + "dateFilterInvalid": "Unrecognized date “{{value}}” — use YYYY-MM-DD, today, yesterday, 7d, 2w, 1m", + "dateFilterRange": "{{from}} – {{to}}", + "dateFilterThrough": "Through {{date}}", + "deleteBoard": "Delete Board", + "deleteBoardAndMedia": "Delete Board and Media", + "deleteBoardDescription": "Deleted boards cannot be restored. Choose whether the board's media moves to Uncategorized or is permanently deleted with it.", + "deleteBoardMediaOutcome": "Deleted {{images}} and {{videos}}; {{failedImages}} and {{failedVideos}} could not be deleted.", + "deleteBoardMoveOutcome": "Moved {{images}} and {{videos}} to Uncategorized.", + "deleteBoardOnly": "Delete Board Only", + "deleteBoardPartialTitle": "Deleted board \"{{name}}\" with partial media cleanup", + "deleteBoardQuestion": "Delete board \"{{name}}\"?", + "deleteBoardSuccessTitle": "Deleted board \"{{name}}\"", + "deleteConfirmLabel": "Delete", + "deleteImagesConfirmBody_one": "This permanently deletes the image from every board and from disk. This cannot be undone. You can disable this confirmation in Settings.", + "deleteImagesConfirmBody_other": "This permanently deletes these images from every board and from disk. This cannot be undone. You can disable this confirmation in Settings.", + "deleteImagesConfirmTitle_one": "Delete image?", + "deleteImagesConfirmTitle_other": "Delete {{count}} images?", + "deleteItemsConfirmBody_one": "This permanently deletes the item from every board and from disk. This cannot be undone. You can disable this confirmation in Settings.", + "deleteItemsConfirmBody_other": "This permanently deletes these items from every board and from disk. This cannot be undone. You can disable this confirmation in Settings.", + "deleteItemsConfirmTitle_one": "Delete item?", + "deleteItemsConfirmTitle_other": "Delete {{count}} items?", + "deleteSelection": "Delete selection", + "downloadBoard": "Download Board", + "downloadBoardWithOmission_one": "Download Board ({{count}} video omitted)", + "downloadBoardWithOmission_other": "Download Board ({{count}} videos omitted)", + "downloadReady": "Download ready", + "downloadSelection": "Download selection", + "dropMediaToUploadToBoard": "Drop images, videos, or audio to upload to {{name}}", + "emptyBoardUploadHint": "Drop media here or click to upload", + "exportProjectFromBoard": "Export project (.invk)", + "expandStarredItems": "Expand starred items", + "filterAndSortBoards": "Filter & sort boards", + "findInGallery": "Find in Gallery", + "findNamedInGallery": "Find {{name}} in Gallery", + "generationProgress": "Generation progress", + "generationProgressPercent": "Generation {{percentage}}%", + "imageCount_one": "{{count}} image", + "imageCount_other": "{{count}} images", + "imageSize": "Image size", + "imageSort": "Image Sort", + "infinite": "Infinite", + "itemActions": { + "loadWorkflow": { + "editorUnavailable": "The Workflow editor is not available in this layout.", + "failed": "Could not load the image's workflow", + "loadedLabel": "Loaded “{{name}}” from image", + "missing": "This image has no workflow embedded." + }, + "delete": { + "failure_one": "Could not delete {{count}} item.", + "failure_other": "Could not delete {{count}} items.", + "partial": "{{succeeded}} of {{count}} items were deleted; {{failed}} failed.", + "success_one": "Deleted {{count}} item.", + "success_other": "Deleted {{count}} items." + }, + "download": { + "partial": "{{succeeded}} of {{count}} items downloaded; {{failed}} failed or were skipped.", + "success": "Download ready" + }, + "move": { + "failure_one": "Could not move {{count}} item to {{board}}.", + "failure_other": "Could not move {{count}} items to {{board}}.", + "partial": "{{succeeded}} of {{count}} items moved to {{board}}; {{failed}} failed.", + "success_one": "Moved {{count}} item to {{board}}.", + "success_other": "Moved {{count}} items to {{board}}." + }, + "star": { + "failure_one": "Could not star {{count}} item.", + "failure_other": "Could not star {{count}} items.", + "partial": "{{succeeded}} of {{count}} items starred; {{failed}} failed.", + "success_one": "Starred {{count}} item.", + "success_other": "Starred {{count}} items." + }, + "unstar": { + "failure_one": "Could not unstar {{count}} item.", + "failure_other": "Could not unstar {{count}} items.", + "partial": "{{succeeded}} of {{count}} items unstarred; {{failed}} failed.", + "success_one": "Unstarred {{count}} item.", + "success_other": "Unstarred {{count}} items." + } + }, + "itemsAriaLabel": "Gallery items", + "loadingBackendGallery": "Loading backend gallery...", + "loadingBoard": "Loading board...", + "moveSelectionToBoard": "Move selection to board", + "newest": "Newest", + "noBoardsMatchSearch": "No boards match this search.", + "noImagesMatch": "No items match this board, tab, or search.", + "noStarredItemsMatch": "No starred items match this board, tab, or search.", + "oldest": "Oldest", + "otherProjectBoards": "Other projects' boards", + "pagination": "Pagination", + "picker": { + "addedItem": "{{name}} (added)", + "boardCount_one": "{{count}} board", + "boardCount_other": "{{count}} boards", + "boardsLabel": "Boards", + "changeBoardNamed": "Change board ({{name}})", + "chooseImage": "Choose image", + "chooseMedia": "Choose image or video", + "chooseVideo": "Choose video", + "dropHint": "or drop one from the Gallery", + "dropImage": "Drop image", + "dropMedia": "Drop image or video", + "dropVideo": "Drop video", + "empty": "Nothing in {{name}} yet", + "findBoard": "Find a board…", + "itemCount_one": "{{count}} item", + "itemCount_other": "{{count}} items", + "itemsLabel": "Gallery items", + "matchCount_one": "{{count}} match", + "matchCount_other": "{{count}} matches", + "openGallery": "Open Gallery", + "remainingCount_one": "{{count}} more can be added", + "remainingCount_other": "{{count}} more can be added", + "remainingNone": "Nothing more can be added", + "removeImage": "Remove image", + "removeMedia": "Remove", + "removeVideo": "Remove video", + "replaceHint": "Click to replace, or drop from the Gallery", + "replaceClickHint": "Click to replace", + "replaceImage": "Replace image", + "replaceMedia": "Replace", + "replaceVideo": "Replace video", + "searchIn": "Search {{name}}…", + "unsupportedImage": "Images can't be used here", + "unsupportedVideo": "Videos can't be used here", + "upload": "Upload", + "windowLimit": "Showing the first {{count}} items. Narrow the search to reach the rest.", + "working": "Working…" + }, + "preparingDownload": "Preparing download", + "renameBoard": "Rename board", + "resizeBoardPanel": "Resize board panel", + "searchHelpDate": "A single day.", + "searchHelpFrom": "On or after this day.", + "searchHelpIntro": "Combine plain text with date tokens. Values accept YYYY-MM-DD, today, yesterday, or relative values such as 7d, 2w, and 1m.", + "searchHelpRelative": "Relative: days (d), weeks (w), or months (m) ago.", + "searchHelpTitle": "Search syntax", + "searchHelpTo": "On or before this day.", + "searchImagesAriaLabel": "Search gallery items", + "searchImagesPlaceholder": "Search items", + "searchOrCreateBoards": "Search or create boards", + "searchSemantically": "Search semantically", + "selectedBoardFallback": "selected board", + "semanticCluster": "Cluster: {{name}}", + "semanticSimilarTo": "Similar to {{name}}", + "semanticTextSearch": "Semantic search: {{name}}", + "semanticSearchArchivedBoard": "Archived boards aren't indexed, so semantic search finds nothing here.", + "semanticSearchAriaLabel": "Semantic search", + "semanticSearchInstallModel": "Install the “{{model}}” model to build the image index and enable semantic search.", + "semanticSearchModelMissing": "Semantic search needs the embedding model “{{model}}”, which is not installed.", + "semanticSearchModelMissingTitle": "Embedding model not installed", + "semanticSearchPlaceholder": "Describe what to find…", + "semanticWebImage": "web image", + "selectImageForPreview": "Select {{name}} for preview", + "selectionActions": "Selection actions", + "selectionCount_one": "{{count}} selected", + "selectionCount_other": "{{count}} selected", + "selectVideoForPreview": "Select video {{name}}, duration {{duration}}, for preview", + "settings": "Gallery settings", + "settingsGridSize": "Grid Size", + "settingsThumbnails": "Thumbnails", + "showPendingItems": "Show in-progress section", + "sortBoardsAscending": "Sort boards ascending", + "sortBoardsBy": "Sort by", + "sortBoardsDescending": "Sort boards descending", + "sortBy": "Sort by", + "starImage": "Star {{name}}", + "showAllStarred": "Show all", + "showAllStarredItems": "Show all starred items", + "starredItems": "Starred", + "starredOnly": "Starred only", + "starredOnlyTooltip": "Show only starred items", + "starredOnlyUnavailable": "The starred filter does not apply to similarity results", + "starSelection": "Star selection", + "statusChip_one": "Gallery: {{count}} item", + "statusChip_other": "Gallery: {{count}} items", + "thumbnailFitAspect": "Aspect", + "thumbnailFitSquare": "Square", + "toggleBoardsNamed": "Toggle boards, currently {{name}}", + "unarchiveBoard": "Unarchive Board", + "uncategorized": "Uncategorized", + "unknownBoard": "Unknown board", + "unstarImage": "Unstar {{name}}", + "unstarSelection": "Unstar selection", + "uploadDateBoardUnavailable": "Uploads are unavailable for date boards.", + "uploadFailed": "No files uploaded. {{failed}} failed.", + "uploadMediaToBoard": "Upload media to {{name}}", + "uploadPartialTitle": "Uploaded {{succeeded}} of {{total}} files", + "uploadSplit": "Images appear in Uploads; videos appear in Media.", + "uploadSuccessTitle_one": "Uploaded {{count}} file", + "uploadSuccessTitle_other": "Uploaded {{count}} files", + "uploadSummary": "{{images}} and {{videos}} uploaded to {{board}}. {{failed}} failed.", + "uploadsUnavailableForDateBoards": "Uploads are unavailable for date boards", + "uploadUnsupported": "No supported media files to upload (PNG, JPEG, or WebP images; video files; or audio files).", + "videoCount_one": "{{count}} video", + "videoCount_other": "{{count}} videos", + "windowAnchored": "Showing this board from item {{index}}.", + "backToBoardTop": "Back to top", + "windowLimit": "Showing the first {{count}} items. Narrow the search or switch to Pages to reach the rest.", + "windowLimitFrom": "Showing {{count}} items from item {{index}}. Narrow the search or switch to Pages to reach the rest.", + "boardOrder": "Board order", + "inProgress": "In progress", + "progressSession": "{{name}} · {{index}} of {{total}}", + "progressQueued": "Queued", + "progressPreparing": "Preparing", + "progressSettling": "Finishing", + "progressShared": "Active gallery runs across all boards" + }, + "queueStatus": { + "idle": "Queue idle", + "paused": "{{count}} queued — paused", + "queued": "{{count}} queued" + }, + "serverStatus": { + "connected": "Connected to Server", + "connecting": "Connecting to Server", + "disconnected": "Disconnected from Server", + "label": "Server Status", + "labelWithError": "{{label}}: {{error}}" + }, + "video": { + "accelerator": "{{label}} (fast)", + "acceleratorHelp": "Runs the {{label}} distillation LoRA at {{steps}} steps. Slower but sometimes higher-quality results with it off.", + "acceleratorMissing": "Accelerator LoRA not installed", + "acceleratorMissingDescription": "Install the {{label}} LoRA from Model Manager to enable the fast path.", + "aspectRatio": "Aspect Ratio", + "cfg": "CFG", + "cfgLowNoise": "CFG (Low Noise)", + "cfgLowNoiseHelp": "Guidance for the low-noise half of the A14B expert schedule.", + "chooseReference": "Choose from Gallery", + "derivedSize": "Output size: {{width}} × {{height}}", + "derivedSizeUnavailable": "Output size unavailable for the current settings.", + "dimensions": "Dimensions", + "dropFirstFrame": "Drop First Frame", + "dropInitialVideo": "Drop Initial Video", + "dropLastFrame": "Drop Last Frame", + "fixedFps": "This model generates at a fixed {{fps}} FPS.", + "fps": "FPS", + "extendContext": "Context Frames", + "extendContextHelp": "Frames of the initial video carried in. Adds {{frames}} new frames ({{seconds}}s).", + "frames": "Frames", + "maxFrames": "Max frames", + "framesDuration": "{{seconds}} s at the current frame rate", + "mainModel": "Model", + "modelRequired": "Select a Wan 2.2 or MiniMax H3 model.", + "render": "Render", + "seed": "Seed", + "selectModel": "Select a video model…", + "settingsAdjusted": "Settings adjusted", + "settingsAdjustedDescription": "Adjusted for the selected model: {{labels}}.", + "steps": "Steps", + "swapAspectRatio": "Swap orientation", + "targetResolution": "Target Resolution", + "twoStageHelp": "Two passes: generates at {{baseWidth}}×{{baseHeight}}, then upscales and refines to {{width}}×{{height}}. The refine pass runs at 4× the tokens, so it dominates the render time.", + "dimensionSource": { + "aspect-ratio": "from the aspect-ratio preset", + "first-frame": "from the first frame", + "last-frame": "from the last frame", + "source-video": "from the initial video", + "conditioning-clip": "from the conditioning clip" + }, + "dimensionSourceValue": { + "first-frame": "From the first frame", + "last-frame": "From the last frame", + "source-video": "From the initial video" + }, + "firstFrame": "First Frame", + "firstFrameBlocked": "Clear the initial video to start from a frame instead.", + "firstFrameHelp": "The video starts on this image.", + "fpsExtendLocked": "The extension inherits the source clip's frame rate.", + "initialFrames": "Start & End Images", + "initialVideo": "Initial Video", + "initialVideoBlocked": "Clear the first frame to extend a video instead.", + "initialVideoCapBlocked": "All 3 video reference slots are in use, so there is no room for the continuity reference this clip needs. Remove a video reference first.", + "lastFrame": "Last Frame", + "lastFrameExtendHelp": "Optional destination: the extension ends on this image.", + "lastFrameHelp": "Optional: the video ends on this image. With a first frame set, it interpolates between the two.", + "pauseSelection": "Pause selection in Preview", + "playSelection": "Play selection in Preview", + "sampleLength": "Sample Length (Frames)", + "sampleLengthWithSeconds": "Sample Length · {{seconds}}s", + "trim": "Trim", + "trimEnd": "End Frame", + "trimEndShort": "End", + "trimHelp": "The kept range of the source clip. The extension continues from the end frame.", + "trimStart": "Start Frame", + "trimStartShort": "Start", + "uploadImageReference": "Upload image", + "clearStaleMedia": "Clear", + "staleFirstFrame": "A first frame is set, but this model does not support starting from one.", + "staleLastFrame": "A last frame is set, but this model does not support ending on one.", + "staleSourceVideo": "An initial video is set, but this model does not support extending videos.", + "addLora": "Search compatible concepts…", + "components": "Model Components", + "componentRequired": "Required for this model.", + "componentSlots": { + "h3ComponentSourceHelp": "Required: a Diffusers MiniMax H3 install (full or components-only) provides the tokenizer, processor, and VAEs the single-file transformer does not carry.", + "h3HybridBaseHelp": "Optional: an FL2VA transformer to run with this Ref2VA model’s AdaLN projections. This improves the video quality at the cost of some loss of reference fidelity. ", + "h3TextEncoderCheckpointHelp": "Required when the Model Components install is components-only (no text-encoder weights); a full install provides its own.", + "h3TextEncoderOptionalHelp": "Optional single-file Qwen3-VL encoder used in place of the main model’s text encoder.", + "h3TextEncoderRequiredHelp": "Required: this install has no text-encoder weights, so the text encoder must come from a single-file Qwen3-VL checkpoint.", + "ltx2ComponentSourceHelp": "The LTX-2 components install supplying the VAEs, vocoder and text connectors.", + "ltx2DurationHeadHelp": "Optional. Predicts how long the shot your prompt describes should run, so Auto duration can set the length.", + "ltx2TextEncoderHelp": "LTX-2’s Gemma-4 text encoder. Required: no LTX-2 model carries one.", + "wanComponentSourceHelp": "Select a Diffusers Wan model to provide VAE and text-encoder components.", + "wanLowNoiseHelp": "Optional second A14B expert. Without it the high-noise expert runs the whole schedule.", + "wanOptionalWithSourceHelp": "Required unless a Diffusers component source is available." + }, + "negativeCfgHelp": "Used only when CFG is greater than 1.", + "concepts": "Concepts", + "loraWeight": "Weight for {{name}}", + "noLoras": "No concepts added. Compatible LoRAs adjust style and motion.", + "removeLora": "Remove {{name}}", + "selectComponent": "Select a component model…", + "hybridStartBlock": "Ref2VA AdaLN from block", + "hybridStartBlockHelp": "Blocks from here through 49 keep this Ref2VA model’s AdaLN projections; earlier blocks use the FL2VA base. Lower values follow references more closely at more of Ref2VA’s quality cost; 25 is the recommended balance.", + "acceleratorBroken": "Accelerator turned off", + "acceleratorBrokenDescription": "One of its LoRAs was removed or disabled, so steps and CFG were restored to the model's defaults.", + "acceleratorSwitched": "{{label}} now running on a different LoRA", + "acceleratorSwitchedDescription": "The fast path follows {{name}}, so steps were set to {{steps}}.", + "resetAllToModelDefaults": "Reset all settings to the model's defaults", + "expertWiring": { + "highAsLow": "\"{{lowName}}\" is tagged as the high-noise expert but is wired as the low-noise one. The wiring is authoritative — swap if unintended.", + "lowAsMain": "\"{{mainName}}\" is tagged as the low-noise expert but is wired as the main (high-noise) transformer. The wiring is authoritative — swap if unintended.", + "singleLow": "\"{{mainName}}\" is tagged as the low-noise expert. When running a single expert, the high-noise one is usually the better choice.", + "swap": "Swap experts", + "swapped": "The filename tags suggest these two experts are swapped. The wiring is authoritative — swap if unintended." + }, + "addReference": "Add reference", + "dropReference": "Drop Reference", + "moveReferenceDown": "Move reference down", + "moveReferenceUp": "Move reference up", + "referenceConditioningAudio": "Audio only", + "referenceConditioningVideo": "Video only", + "referenceConditioningVideoAudio": "Video + audio", + "referenceDetailMatch": "Match generation size", + "referenceDetailMax": "Max detail (2048px, slower)", + "referenceExtendCapFull": "Initial video not set", + "referenceExtendCapFullDescription": "All 3 video reference slots are in use, so there is no room for the continuity reference this clip needs. Remove a video reference, then drop the clip again.", + "placement": { + "conditioningClipDisplaced": "A conditioning clip is used on its own, so the panel's other conditioning media was cleared.", + "conditioningClipNoPicture": "This is an uploaded audio file, so it has no picture to generate a soundtrack for. Use its soundtrack instead.", + "conditioningClipNotSet": "Conditioning clip not set", + "conditioningClipSet": "Conditioning clip set", + "conditioningClipUnsupported": "The selected video model can't generate from a conditioning clip. Choose one that can, such as LTX-2.", + "initialVideoFull": "All video reference slots are in use, so there is no room for the continuity reference this clip needs. Remove a video reference and try again.", + "initialVideoSet": "Initial video set", + "initialVideoUnused": "The selected video model can't extend a clip, so it won't use this initial video until you choose one that can.", + "referenceAdded": "Reference video added", + "referenceFull": "All video reference slots are in use. Remove a video reference and try again.", + "referenceNotAdded": "Reference video not added", + "referenceUnsupported": "The selected video model doesn't take reference videos. Choose one that does, such as MiniMax H3 Ref2VA." + }, + "externalRecall": { + "nothingApplied": "Nothing recalled", + "nothingAppliedDescription": "None of the recalled settings apply to the Video panel's model." + }, + "referenceExtendHelp": "The generated video is appended to this clip at its End Frame, and samples the last ~5 seconds before the cutpoint to achieve video continuity.", + "referenceImageCapRace": "All {{max}} image reference slots filled while this one was loading, so it was not added.", + "referenceImageCost": "{{width}}×{{height}} · {{rows}} rows per step", + "referenceVideoCapRace": "All {{max}} video or audio reference slots filled while this one was loading, so it was not added.", + "referenceFromInitialVideo": "Initial video", + "references": "References", + "referencesHelp": "Add up to 3 videos and 9 images to condition the generation. A video reference can contribute its image track, its soundtrack, or both. In the text prompt, refer to the conditionings as ,