diff --git a/.ci/update_windows/update.py b/.ci/update_windows/update.py
index cc79a914e..6a04e5e16 100755
--- a/.ci/update_windows/update.py
+++ b/.ci/update_windows/update.py
@@ -75,6 +75,25 @@ else:
print("pulling latest changes")
pull(repo)
+if "--stable" in sys.argv:
+ def latest_tag(repo):
+ versions = []
+ for k in repo.references:
+ try:
+ prefix = "refs/tags/v"
+ if k.startswith(prefix):
+ version = list(map(int, k[len(prefix):].split(".")))
+ versions.append((version[0] * 10000000000 + version[1] * 100000 + version[2], k))
+ except:
+ pass
+ versions.sort()
+ if len(versions) > 0:
+ return versions[-1][1]
+ return None
+ latest_tag = latest_tag(repo)
+ if latest_tag is not None:
+ repo.checkout(latest_tag)
+
print("Done!")
self_update = True
@@ -115,3 +134,13 @@ if not os.path.exists(req_path) or not files_equal(repo_req_path, req_path):
shutil.copy(repo_req_path, req_path)
except:
pass
+
+
+stable_update_script = os.path.join(repo_path, ".ci/update_windows/update_comfyui_stable.bat")
+stable_update_script_to = os.path.join(cur_path, "update_comfyui_stable.bat")
+
+try:
+ if not file_size(stable_update_script_to) > 10:
+ shutil.copy(stable_update_script, stable_update_script_to)
+except:
+ pass
diff --git a/.ci/update_windows/update_comfyui_stable.bat b/.ci/update_windows/update_comfyui_stable.bat
new file mode 100755
index 000000000..e18010da3
--- /dev/null
+++ b/.ci/update_windows/update_comfyui_stable.bat
@@ -0,0 +1,8 @@
+@echo off
+..\python_embeded\python.exe .\update.py ..\ComfyUI\ --stable
+if exist update_new.py (
+ move /y update_new.py update.py
+ echo Running updater again since it got updated.
+ ..\python_embeded\python.exe .\update.py ..\ComfyUI\ --skip_self_update --stable
+)
+if "%~1"=="" pause
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..4391de678
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+/web/assets/** linguist-generated
+/web/** linguist-vendored
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 2c519ede5..09fea712e 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,8 @@
blank_issues_enabled: true
contact_links:
+ - name: ComfyUI Frontend Issues
+ url: https://github.com/Comfy-Org/ComfyUI_frontend/issues
+ about: Issues related to the ComfyUI frontend (display issues, user interaction bugs), please go to the frontend repo to file the issue
- name: ComfyUI Matrix Space
url: https://app.element.io/#/room/%23comfyui_space%3Amatrix.org
about: The ComfyUI Matrix Space is available for support and general discussion related to ComfyUI (Matrix is like Discord but open source).
diff --git a/.github/workflows/pullrequest-ci-run.yml b/.github/workflows/pullrequest-ci-run.yml
new file mode 100644
index 000000000..691480bcf
--- /dev/null
+++ b/.github/workflows/pullrequest-ci-run.yml
@@ -0,0 +1,53 @@
+# This is the GitHub Workflow that drives full-GPU-enabled tests of pull requests to ComfyUI, when the 'Run-CI-Test' label is added
+# Results are reported as checkmarks on the commits, as well as onto https://ci.comfy.org/
+name: Pull Request CI Workflow Runs
+on:
+ pull_request_target:
+ types: [labeled]
+
+jobs:
+ pr-test-stable:
+ if: ${{ github.event.label.name == 'Run-CI-Test' }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [macos, linux, windows]
+ python_version: ["3.9", "3.10", "3.11", "3.12"]
+ cuda_version: ["12.1"]
+ torch_version: ["stable"]
+ include:
+ - os: macos
+ runner_label: [self-hosted, macOS]
+ flags: "--use-pytorch-cross-attention"
+ - os: linux
+ runner_label: [self-hosted, Linux]
+ flags: ""
+ - os: windows
+ runner_label: [self-hosted, win]
+ flags: ""
+ runs-on: ${{ matrix.runner_label }}
+ steps:
+ - name: Test Workflows
+ uses: comfy-org/comfy-action@main
+ with:
+ os: ${{ matrix.os }}
+ python_version: ${{ matrix.python_version }}
+ torch_version: ${{ matrix.torch_version }}
+ google_credentials: ${{ secrets.GCS_SERVICE_ACCOUNT_JSON }}
+ comfyui_flags: ${{ matrix.flags }}
+ use_prior_commit: 'true'
+ comment:
+ if: ${{ github.event.label.name == 'Run-CI-Test' }}
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - uses: actions/github-script@v6
+ with:
+ script: |
+ github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: '(Automated Bot Message) CI Tests are running, you can view the results at https://ci.comfy.org/?branch=${{ github.event.pull_request.number }}%2Fmerge'
+ })
diff --git a/.github/workflows/test-ci.yml b/.github/workflows/test-ci.yml
new file mode 100644
index 000000000..be7622907
--- /dev/null
+++ b/.github/workflows/test-ci.yml
@@ -0,0 +1,95 @@
+# This is the GitHub Workflow that drives automatic full-GPU-enabled tests of all new commits to the master branch of ComfyUI
+# Results are reported as checkmarks on the commits, as well as onto https://ci.comfy.org/
+name: Full Comfy CI Workflow Runs
+on:
+ push:
+ branches:
+ - master
+ paths-ignore:
+ - 'app/**'
+ - 'input/**'
+ - 'output/**'
+ - 'notebooks/**'
+ - 'script_examples/**'
+ - '.github/**'
+ - 'web/**'
+ workflow_dispatch:
+
+jobs:
+ test-stable:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [macos, linux, windows]
+ python_version: ["3.9", "3.10", "3.11", "3.12"]
+ cuda_version: ["12.1"]
+ torch_version: ["stable"]
+ include:
+ - os: macos
+ runner_label: [self-hosted, macOS]
+ flags: "--use-pytorch-cross-attention"
+ - os: linux
+ runner_label: [self-hosted, Linux]
+ flags: ""
+ - os: windows
+ runner_label: [self-hosted, win]
+ flags: ""
+ runs-on: ${{ matrix.runner_label }}
+ steps:
+ - name: Test Workflows
+ uses: comfy-org/comfy-action@main
+ with:
+ os: ${{ matrix.os }}
+ python_version: ${{ matrix.python_version }}
+ torch_version: ${{ matrix.torch_version }}
+ google_credentials: ${{ secrets.GCS_SERVICE_ACCOUNT_JSON }}
+ comfyui_flags: ${{ matrix.flags }}
+
+ test-win-nightly:
+ strategy:
+ fail-fast: true
+ matrix:
+ os: [windows]
+ python_version: ["3.9", "3.10", "3.11", "3.12"]
+ cuda_version: ["12.1"]
+ torch_version: ["nightly"]
+ include:
+ - os: windows
+ runner_label: [self-hosted, win]
+ flags: ""
+ runs-on: ${{ matrix.runner_label }}
+ steps:
+ - name: Test Workflows
+ uses: comfy-org/comfy-action@main
+ with:
+ os: ${{ matrix.os }}
+ python_version: ${{ matrix.python_version }}
+ torch_version: ${{ matrix.torch_version }}
+ google_credentials: ${{ secrets.GCS_SERVICE_ACCOUNT_JSON }}
+ comfyui_flags: ${{ matrix.flags }}
+
+ test-unix-nightly:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [macos, linux]
+ python_version: ["3.11"]
+ cuda_version: ["12.1"]
+ torch_version: ["nightly"]
+ include:
+ - os: macos
+ runner_label: [self-hosted, macOS]
+ flags: "--use-pytorch-cross-attention"
+ - os: linux
+ runner_label: [self-hosted, Linux]
+ flags: ""
+ runs-on: ${{ matrix.runner_label }}
+ steps:
+ - name: Test Workflows
+ uses: comfy-org/comfy-action@main
+ with:
+ os: ${{ matrix.os }}
+ python_version: ${{ matrix.python_version }}
+ torch_version: ${{ matrix.torch_version }}
+ google_credentials: ${{ secrets.GCS_SERVICE_ACCOUNT_JSON }}
+ comfyui_flags: ${{ matrix.flags }}
diff --git a/.github/workflows/test-browser.yml b/.github/workflows/test-launch.yml
similarity index 50%
rename from .github/workflows/test-browser.yml
rename to .github/workflows/test-launch.yml
index ce0bc37a3..42f1dbe99 100644
--- a/.github/workflows/test-browser.yml
+++ b/.github/workflows/test-launch.yml
@@ -1,10 +1,4 @@
-# This is a temporary action during frontend TS migration.
-# This file should be removed after TS migration is completed.
-# The browser test is here to ensure TS repo is working the same way as the
-# current JS code.
-# If you are adding UI feature, please sync your changes to the TS repo:
-# huchenlei/ComfyUI_frontend and update test expectation files accordingly.
-name: Playwright Browser Tests CI
+name: Test server launches without errors
on:
push:
@@ -21,15 +15,6 @@ jobs:
with:
repository: "comfyanonymous/ComfyUI"
path: "ComfyUI"
- - name: Checkout ComfyUI_frontend
- uses: actions/checkout@v4
- with:
- repository: "huchenlei/ComfyUI_frontend"
- path: "ComfyUI_frontend"
- ref: "fcc54d803e5b6a9b08a462a1d94899318c96dcbb"
- - uses: actions/setup-node@v3
- with:
- node-version: lts/*
- uses: actions/setup-python@v4
with:
python-version: '3.8'
@@ -45,16 +30,6 @@ jobs:
python main.py --cpu 2>&1 | tee console_output.log &
wait-for-it --service 127.0.0.1:8188 -t 600
working-directory: ComfyUI
- - name: Install ComfyUI_frontend dependencies
- run: |
- npm ci
- working-directory: ComfyUI_frontend
- - name: Install Playwright Browsers
- run: npx playwright install --with-deps
- working-directory: ComfyUI_frontend
- - name: Run Playwright tests
- run: npx playwright test
- working-directory: ComfyUI_frontend
- name: Check for unhandled exceptions in server log
run: |
if grep -qE "Exception|Error" console_output.log; then
@@ -62,12 +37,6 @@ jobs:
exit 1
fi
working-directory: ComfyUI
- - uses: actions/upload-artifact@v4
- if: always()
- with:
- name: playwright-report
- path: ComfyUI_frontend/playwright-report/
- retention-days: 30
- uses: actions/upload-artifact@v4
if: always()
with:
diff --git a/.github/workflows/test-ui.yaml b/.github/workflows/test-ui.yaml
deleted file mode 100644
index d947e9d5f..000000000
--- a/.github/workflows/test-ui.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-name: Tests CI
-
-on: [push, pull_request]
-
-jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-node@v3
- with:
- node-version: 18
- - uses: actions/setup-python@v4
- with:
- python-version: '3.10'
- - name: Install requirements
- run: |
- python -m pip install --upgrade pip
- pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
- pip install -r requirements.txt
- - name: Run Tests
- run: |
- npm ci
- npm run test:generate
- npm test -- --verbose
- working-directory: ./tests-ui
- - name: Run Unit Tests
- run: |
- pip install -r tests-unit/requirements.txt
- python -m pytest tests-unit
diff --git a/.gitignore b/.gitignore
index 5092c98f4..b212b0297 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,4 +18,5 @@ venv/
/tests-ui/data/object_info.json
/user/
*.log
-web_custom_versions/
\ No newline at end of file
+web_custom_versions/
+.DS_Store
diff --git a/README.md b/README.md
index d5ded7297..252ed0e2c 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,35 @@
-ComfyUI
-=======
-The most powerful and modular stable diffusion GUI and backend.
------------
+
+
+# ComfyUI
+**The most powerful and modular stable diffusion GUI and backend.**
+
+
+[![Website][website-shield]][website-url]
+[![Dynamic JSON Badge][discord-shield]][discord-url]
+[![Matrix][matrix-shield]][matrix-url]
+
+[![][github-release-shield]][github-release-link]
+[![][github-release-date-shield]][github-release-link]
+[![][github-downloads-shield]][github-downloads-link]
+[![][github-downloads-latest-shield]][github-downloads-link]
+
+[matrix-shield]: https://img.shields.io/badge/Matrix-000000?style=flat&logo=matrix&logoColor=white
+[matrix-url]: https://app.element.io/#/room/%23comfyui_space%3Amatrix.org
+[website-shield]: https://img.shields.io/badge/ComfyOrg-4285F4?style=flat
+[website-url]: https://www.comfy.org/
+
+[discord-shield]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscord.com%2Fapi%2Finvites%2Fcomfyorg%3Fwith_counts%3Dtrue&query=%24.approximate_member_count&logo=discord&logoColor=white&label=Discord&color=green&suffix=%20total
+[discord-url]: https://www.comfy.org/discord
+
+[github-release-shield]: https://img.shields.io/github/v/release/comfyanonymous/ComfyUI?style=flat&sort=semver
+[github-release-link]: https://github.com/comfyanonymous/ComfyUI/releases
+[github-release-date-shield]: https://img.shields.io/github/release-date/comfyanonymous/ComfyUI?style=flat
+[github-downloads-shield]: https://img.shields.io/github/downloads/comfyanonymous/ComfyUI/total?style=flat
+[github-downloads-latest-shield]: https://img.shields.io/github/downloads/comfyanonymous/ComfyUI/latest/total?style=flat&label=downloads%40latest
+[github-downloads-link]: https://github.com/comfyanonymous/ComfyUI/releases
+

+
This ui will let you design and execute advanced stable diffusion pipelines using a graph/nodes/flowchart based interface. For some workflow examples and see what ComfyUI can do you can check out:
### [ComfyUI Examples](https://comfyanonymous.github.io/ComfyUI_examples/)
@@ -48,6 +75,7 @@ Workflow examples can be found on the [Examples page](https://comfyanonymous.git
|------------------------------------|--------------------------------------------------------------------------------------------------------------------|
| Ctrl + Enter | Queue up current graph for generation |
| Ctrl + Shift + Enter | Queue up current graph as first for generation |
+| Ctrl + Alt + Enter | Cancel current generation |
| Ctrl + Z/Ctrl + Y | Undo/Redo |
| Ctrl + S | Save workflow |
| Ctrl + O | Load workflow |
@@ -70,6 +98,8 @@ Workflow examples can be found on the [Examples page](https://comfyanonymous.git
| H | Toggle visibility of history |
| R | Refresh graph |
| Double-Click LMB | Open node quick search palette |
+| Shift + Drag | Move multiple wires at once |
+| Ctrl + Alt + LMB | Disconnect all wires from clicked slot |
Ctrl can also be replaced with Cmd instead for macOS users
@@ -216,6 +246,47 @@ Use `--tls-keyfile key.pem --tls-certfile cert.pem` to enable TLS/SSL, the app w
See also: [https://www.comfy.org/](https://www.comfy.org/)
+## Frontend Development
+
+As of August 15, 2024, we have transitioned to a new frontend, which is now hosted in a separate repository: [ComfyUI Frontend](https://github.com/Comfy-Org/ComfyUI_frontend). This repository now hosts the compiled JS (from TS/Vue) under the `web/` directory.
+
+### Reporting Issues and Requesting Features
+
+For any bugs, issues, or feature requests related to the frontend, please use the [ComfyUI Frontend repository](https://github.com/Comfy-Org/ComfyUI_frontend). This will help us manage and address frontend-specific concerns more efficiently.
+
+### Using the Latest Frontend
+
+The new frontend is now the default for ComfyUI. However, please note:
+
+1. The frontend in the main ComfyUI repository is updated weekly.
+2. Daily releases are available in the separate frontend repository.
+
+To use the most up-to-date frontend version:
+
+1. For the latest daily release, launch ComfyUI with this command line argument:
+
+ ```
+ --front-end-version Comfy-Org/ComfyUI_frontend@latest
+ ```
+
+2. For a specific version, replace `latest` with the desired version number:
+
+ ```
+ --front-end-version Comfy-Org/ComfyUI_frontend@1.2.2
+ ```
+
+This approach allows you to easily switch between the stable weekly release and the cutting-edge daily updates, or even specific versions for testing purposes.
+
+### Accessing the Legacy Frontend
+
+If you need to use the legacy frontend for any reason, you can access it using the following command line argument:
+
+```
+--front-end-version Comfy-Org/ComfyUI_legacy_frontend@latest
+```
+
+This will use a snapshot of the legacy frontend preserved in the [ComfyUI Legacy Frontend repository](https://github.com/Comfy-Org/ComfyUI_legacy_frontend).
+
# QA
### Which GPU should I buy for this?
diff --git a/comfy/cli_args.py b/comfy/cli_args.py
index 2397de3d6..77009c912 100644
--- a/comfy/cli_args.py
+++ b/comfy/cli_args.py
@@ -92,6 +92,10 @@ class LatentPreviewMethod(enum.Enum):
parser.add_argument("--preview-method", type=LatentPreviewMethod, default=LatentPreviewMethod.NoPreviews, help="Default preview method for sampler nodes.", action=EnumAction)
+cache_group = parser.add_mutually_exclusive_group()
+cache_group.add_argument("--cache-classic", action="store_true", help="Use the old style (aggressive) caching.")
+cache_group.add_argument("--cache-lru", type=int, default=0, help="Use LRU caching with a maximum of N node results cached. May use more RAM/VRAM.")
+
attn_group = parser.add_mutually_exclusive_group()
attn_group.add_argument("--use-split-cross-attention", action="store_true", help="Use the split cross attention optimization. Ignored when xformers is used.")
attn_group.add_argument("--use-quad-cross-attention", action="store_true", help="Use the sub-quadratic cross attention optimization . Ignored when xformers is used.")
@@ -112,6 +116,9 @@ vram_group.add_argument("--lowvram", action="store_true", help="Split the unet i
vram_group.add_argument("--novram", action="store_true", help="When lowvram isn't enough.")
vram_group.add_argument("--cpu", action="store_true", help="To use the CPU for everything (slow).")
+parser.add_argument("--reserve-vram", type=float, default=None, help="Set the amount of vram in GB you want to reserve for use by your OS/other software. By default some amount is reverved depending on your OS.")
+
+
parser.add_argument("--default-hashing-function", type=str, choices=['md5', 'sha1', 'sha256', 'sha512'], default='sha256', help="Allows you to choose the hash function to use for duplicate filename / contents comparison. Default is sha256.")
parser.add_argument("--disable-smart-memory", action="store_true", help="Force ComfyUI to agressively offload to regular ram instead of keeping models in vram when it can.")
diff --git a/comfy/controlnet.py b/comfy/controlnet.py
index 12e5f16c8..dcfe492ce 100644
--- a/comfy/controlnet.py
+++ b/comfy/controlnet.py
@@ -1,4 +1,24 @@
+"""
+ This file is part of ComfyUI.
+ Copyright (C) 2024 Comfy
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
+
import torch
+from enum import Enum
import math
import os
import logging
@@ -13,6 +33,8 @@ import comfy.cldm.cldm
import comfy.t2i_adapter.adapter
import comfy.ldm.cascade.controlnet
import comfy.cldm.mmdit
+import comfy.ldm.hydit.controlnet
+import comfy.ldm.flux.controlnet_xlabs
def broadcast_image_to(tensor, target_batch_size, batched_number):
@@ -33,6 +55,10 @@ def broadcast_image_to(tensor, target_batch_size, batched_number):
else:
return torch.cat([tensor] * batched_number, dim=0)
+class StrengthType(Enum):
+ CONSTANT = 1
+ LINEAR_UP = 2
+
class ControlBase:
def __init__(self, device=None):
self.cond_hint_original = None
@@ -51,6 +77,8 @@ class ControlBase:
device = comfy.model_management.get_torch_device()
self.device = device
self.previous_controlnet = None
+ self.extra_conds = []
+ self.strength_type = StrengthType.CONSTANT
def set_cond_hint(self, cond_hint, strength=1.0, timestep_percent_range=(0.0, 1.0), vae=None):
self.cond_hint_original = cond_hint
@@ -93,6 +121,8 @@ class ControlBase:
c.latent_format = self.latent_format
c.extra_args = self.extra_args.copy()
c.vae = self.vae
+ c.extra_conds = self.extra_conds.copy()
+ c.strength_type = self.strength_type
def inference_memory_requirements(self, dtype):
if self.previous_controlnet is not None:
@@ -113,7 +143,10 @@ class ControlBase:
if x not in applied_to: #memory saving strategy, allow shared tensors and only apply strength to shared tensors once
applied_to.add(x)
- x *= self.strength
+ if self.strength_type == StrengthType.CONSTANT:
+ x *= self.strength
+ elif self.strength_type == StrengthType.LINEAR_UP:
+ x *= (self.strength ** float(len(control_output) - i))
if x.dtype != output_dtype:
x = x.to(output_dtype)
@@ -142,7 +175,7 @@ class ControlBase:
class ControlNet(ControlBase):
- def __init__(self, control_model=None, global_average_pooling=False, compression_ratio=8, latent_format=None, device=None, load_device=None, manual_cast_dtype=None):
+ def __init__(self, control_model=None, global_average_pooling=False, compression_ratio=8, latent_format=None, device=None, load_device=None, manual_cast_dtype=None, extra_conds=["y"], strength_type=StrengthType.CONSTANT):
super().__init__(device)
self.control_model = control_model
self.load_device = load_device
@@ -154,6 +187,8 @@ class ControlNet(ControlBase):
self.model_sampling_current = None
self.manual_cast_dtype = manual_cast_dtype
self.latent_format = latent_format
+ self.extra_conds += extra_conds
+ self.strength_type = strength_type
def get_control(self, x_noisy, t, cond, batched_number):
control_prev = None
@@ -191,13 +226,16 @@ class ControlNet(ControlBase):
self.cond_hint = broadcast_image_to(self.cond_hint, x_noisy.shape[0], batched_number)
context = cond.get('crossattn_controlnet', cond['c_crossattn'])
- y = cond.get('y', None)
- if y is not None:
- y = y.to(dtype)
+ extra = self.extra_args.copy()
+ for c in self.extra_conds:
+ temp = cond.get(c, None)
+ if temp is not None:
+ extra[c] = temp.to(dtype)
+
timestep = self.model_sampling_current.timestep(t)
x_noisy = self.model_sampling_current.calculate_input(t, x_noisy)
- control = self.control_model(x=x_noisy.to(dtype), hint=self.cond_hint, timesteps=timestep.float(), context=context.to(dtype), y=y, **self.extra_args)
+ control = self.control_model(x=x_noisy.to(dtype), hint=self.cond_hint, timesteps=timestep.to(dtype), context=context.to(dtype), **extra)
return self.control_merge(control, control_prev, output_dtype)
def copy(self):
@@ -286,6 +324,7 @@ class ControlLora(ControlNet):
ControlBase.__init__(self, device)
self.control_weights = control_weights
self.global_average_pooling = global_average_pooling
+ self.extra_conds += ["y"]
def pre_run(self, model, percent_to_timestep_function):
super().pre_run(model, percent_to_timestep_function)
@@ -338,12 +377,8 @@ class ControlLora(ControlNet):
def inference_memory_requirements(self, dtype):
return comfy.utils.calculate_parameters(self.control_weights) * comfy.model_management.dtype_size(dtype) + ControlBase.inference_memory_requirements(self, dtype)
-def load_controlnet_mmdit(sd):
- new_sd = comfy.model_detection.convert_diffusers_mmdit(sd, "")
- model_config = comfy.model_detection.model_config_from_unet(new_sd, "", True)
- num_blocks = comfy.model_detection.count_blocks(new_sd, 'joint_blocks.{}.')
- for k in sd:
- new_sd[k] = sd[k]
+def controlnet_config(sd):
+ model_config = comfy.model_detection.model_config_from_unet(sd, "", True)
supported_inference_dtypes = model_config.supported_inference_dtypes
@@ -356,14 +391,27 @@ def load_controlnet_mmdit(sd):
else:
operations = comfy.ops.disable_weight_init
- control_model = comfy.cldm.mmdit.ControlNet(num_blocks=num_blocks, operations=operations, device=load_device, dtype=unet_dtype, **controlnet_config)
- missing, unexpected = control_model.load_state_dict(new_sd, strict=False)
+ return model_config, operations, load_device, unet_dtype, manual_cast_dtype
+
+def controlnet_load_state_dict(control_model, sd):
+ missing, unexpected = control_model.load_state_dict(sd, strict=False)
if len(missing) > 0:
logging.warning("missing controlnet keys: {}".format(missing))
if len(unexpected) > 0:
logging.debug("unexpected controlnet keys: {}".format(unexpected))
+ return control_model
+
+def load_controlnet_mmdit(sd):
+ new_sd = comfy.model_detection.convert_diffusers_mmdit(sd, "")
+ model_config, operations, load_device, unet_dtype, manual_cast_dtype = controlnet_config(new_sd)
+ num_blocks = comfy.model_detection.count_blocks(new_sd, 'joint_blocks.{}.')
+ for k in sd:
+ new_sd[k] = sd[k]
+
+ control_model = comfy.cldm.mmdit.ControlNet(num_blocks=num_blocks, operations=operations, device=load_device, dtype=unet_dtype, **model_config.unet_config)
+ control_model = controlnet_load_state_dict(control_model, new_sd)
latent_format = comfy.latent_formats.SD3()
latent_format.shift_factor = 0 #SD3 controlnet weirdness
@@ -371,8 +419,31 @@ def load_controlnet_mmdit(sd):
return control
+def load_controlnet_hunyuandit(controlnet_data):
+ model_config, operations, load_device, unet_dtype, manual_cast_dtype = controlnet_config(controlnet_data)
+
+ control_model = comfy.ldm.hydit.controlnet.HunYuanControlNet(operations=operations, device=load_device, dtype=unet_dtype)
+ control_model = controlnet_load_state_dict(control_model, controlnet_data)
+
+ latent_format = comfy.latent_formats.SDXL()
+ extra_conds = ['text_embedding_mask', 'encoder_hidden_states_t5', 'text_embedding_mask_t5', 'image_meta_size', 'style', 'cos_cis_img', 'sin_cis_img']
+ control = ControlNet(control_model, compression_ratio=1, latent_format=latent_format, load_device=load_device, manual_cast_dtype=manual_cast_dtype, extra_conds=extra_conds, strength_type=StrengthType.CONSTANT)
+ return control
+
+def load_controlnet_flux_xlabs(sd):
+ model_config, operations, load_device, unet_dtype, manual_cast_dtype = controlnet_config(sd)
+ control_model = comfy.ldm.flux.controlnet_xlabs.ControlNetFlux(operations=operations, device=load_device, dtype=unet_dtype, **model_config.unet_config)
+ control_model = controlnet_load_state_dict(control_model, sd)
+ extra_conds = ['y', 'guidance']
+ control = ControlNet(control_model, load_device=load_device, manual_cast_dtype=manual_cast_dtype, extra_conds=extra_conds)
+ return control
+
+
def load_controlnet(ckpt_path, model=None):
controlnet_data = comfy.utils.load_torch_file(ckpt_path, safe_load=True)
+ if 'after_proj_list.18.bias' in controlnet_data.keys(): #Hunyuan DiT
+ return load_controlnet_hunyuandit(controlnet_data)
+
if "lora_controlnet" in controlnet_data:
return ControlLora(controlnet_data)
@@ -430,7 +501,10 @@ def load_controlnet(ckpt_path, model=None):
logging.warning("leftover keys: {}".format(leftover_keys))
controlnet_data = new_sd
elif "controlnet_blocks.0.weight" in controlnet_data: #SD3 diffusers format
- return load_controlnet_mmdit(controlnet_data)
+ if "double_blocks.0.img_attn.norm.key_norm.scale" in controlnet_data:
+ return load_controlnet_flux_xlabs(controlnet_data)
+ else:
+ return load_controlnet_mmdit(controlnet_data)
pth_key = 'control_model.zero_convs.0.0.weight'
pth = False
diff --git a/comfy/diffusers_load.py b/comfy/diffusers_load.py
index 98b888a19..56e63a756 100644
--- a/comfy/diffusers_load.py
+++ b/comfy/diffusers_load.py
@@ -22,7 +22,7 @@ def load_diffusers(model_path, output_vae=True, output_clip=True, embedding_dire
if text_encoder2_path is not None:
text_encoder_paths.append(text_encoder2_path)
- unet = comfy.sd.load_unet(unet_path)
+ unet = comfy.sd.load_diffusion_model(unet_path)
clip = None
if output_clip:
diff --git a/comfy/float.py b/comfy/float.py
new file mode 100644
index 000000000..1dbdafd59
--- /dev/null
+++ b/comfy/float.py
@@ -0,0 +1,59 @@
+import torch
+
+#Not 100% sure about this
+def manual_stochastic_round_to_float8(x, dtype):
+ if dtype == torch.float8_e4m3fn:
+ EXPONENT_BITS, MANTISSA_BITS, EXPONENT_BIAS = 4, 3, 7
+ elif dtype == torch.float8_e5m2:
+ EXPONENT_BITS, MANTISSA_BITS, EXPONENT_BIAS = 5, 2, 15
+ else:
+ raise ValueError("Unsupported dtype")
+
+ sign = torch.sign(x)
+ abs_x = x.abs()
+
+ # Combine exponent calculation and clamping
+ exponent = torch.clamp(
+ torch.floor(torch.log2(abs_x)).to(torch.int32) + EXPONENT_BIAS,
+ 0, 2**EXPONENT_BITS - 1
+ )
+
+ # Combine mantissa calculation and rounding
+ # min_normal = 2.0 ** (-EXPONENT_BIAS + 1)
+ # zero_mask = (abs_x == 0)
+ # subnormal_mask = (exponent == 0) & (abs_x != 0)
+ normal_mask = ~(exponent == 0)
+
+ mantissa_scaled = torch.where(
+ normal_mask,
+ (abs_x / (2.0 ** (exponent - EXPONENT_BIAS)) - 1.0) * (2**MANTISSA_BITS),
+ (abs_x / (2.0 ** (-EXPONENT_BIAS + 1 - MANTISSA_BITS)))
+ )
+ mantissa_floor = mantissa_scaled.floor()
+ mantissa = torch.where(
+ torch.rand_like(mantissa_scaled) < (mantissa_scaled - mantissa_floor),
+ (mantissa_floor + 1) / (2**MANTISSA_BITS),
+ mantissa_floor / (2**MANTISSA_BITS)
+ )
+ result = torch.where(
+ normal_mask,
+ sign * (2.0 ** (exponent - EXPONENT_BIAS)) * (1.0 + mantissa),
+ sign * (2.0 ** (-EXPONENT_BIAS + 1)) * mantissa
+ )
+
+ result = torch.where(abs_x == 0, 0, result)
+ return result.to(dtype=dtype)
+
+
+
+def stochastic_rounding(value, dtype):
+ if dtype == torch.float32:
+ return value.to(dtype=torch.float32)
+ if dtype == torch.float16:
+ return value.to(dtype=torch.float16)
+ if dtype == torch.bfloat16:
+ return value.to(dtype=torch.bfloat16)
+ if dtype == torch.float8_e4m3fn or dtype == torch.float8_e5m2:
+ return manual_stochastic_round_to_float8(value, dtype)
+
+ return value.to(dtype=dtype)
diff --git a/comfy/k_diffusion/sampling.py b/comfy/k_diffusion/sampling.py
index 763d8cc78..385a25164 100644
--- a/comfy/k_diffusion/sampling.py
+++ b/comfy/k_diffusion/sampling.py
@@ -9,6 +9,7 @@ from tqdm.auto import trange, tqdm
from . import utils
from . import deis
import comfy.model_patcher
+import comfy.model_sampling
def append_zero(x):
return torch.cat([x, x.new_zeros([1])])
@@ -509,6 +510,9 @@ def sample_dpm_adaptive(model, x, sigma_min, sigma_max, extra_args=None, callbac
@torch.no_grad()
def sample_dpmpp_2s_ancestral(model, x, sigmas, extra_args=None, callback=None, disable=None, eta=1., s_noise=1., noise_sampler=None):
+ if isinstance(model.inner_model.inner_model.model_sampling, comfy.model_sampling.CONST):
+ return sample_dpmpp_2s_ancestral_RF(model, x, sigmas, extra_args, callback, disable, eta, s_noise, noise_sampler)
+
"""Ancestral sampling with DPM-Solver++(2S) second-order steps."""
extra_args = {} if extra_args is None else extra_args
noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler
@@ -541,6 +545,55 @@ def sample_dpmpp_2s_ancestral(model, x, sigmas, extra_args=None, callback=None,
return x
+@torch.no_grad()
+def sample_dpmpp_2s_ancestral_RF(model, x, sigmas, extra_args=None, callback=None, disable=None, eta=1., s_noise=1., noise_sampler=None):
+ """Ancestral sampling with DPM-Solver++(2S) second-order steps."""
+ extra_args = {} if extra_args is None else extra_args
+ noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler
+ s_in = x.new_ones([x.shape[0]])
+ sigma_fn = lambda lbda: (lbda.exp() + 1) ** -1
+ lambda_fn = lambda sigma: ((1-sigma)/sigma).log()
+
+ # logged_x = x.unsqueeze(0)
+
+ for i in trange(len(sigmas) - 1, disable=disable):
+ denoised = model(x, sigmas[i] * s_in, **extra_args)
+ downstep_ratio = 1 + (sigmas[i+1]/sigmas[i] - 1) * eta
+ sigma_down = sigmas[i+1] * downstep_ratio
+ alpha_ip1 = 1 - sigmas[i+1]
+ alpha_down = 1 - sigma_down
+ renoise_coeff = (sigmas[i+1]**2 - sigma_down**2*alpha_ip1**2/alpha_down**2)**0.5
+ # sigma_down, sigma_up = get_ancestral_step(sigmas[i], sigmas[i + 1], eta=eta)
+ if callback is not None:
+ callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
+ if sigmas[i + 1] == 0:
+ # Euler method
+ d = to_d(x, sigmas[i], denoised)
+ dt = sigma_down - sigmas[i]
+ x = x + d * dt
+ else:
+ # DPM-Solver++(2S)
+ if sigmas[i] == 1.0:
+ sigma_s = 0.9999
+ else:
+ t_i, t_down = lambda_fn(sigmas[i]), lambda_fn(sigma_down)
+ r = 1 / 2
+ h = t_down - t_i
+ s = t_i + r * h
+ sigma_s = sigma_fn(s)
+ # sigma_s = sigmas[i+1]
+ sigma_s_i_ratio = sigma_s / sigmas[i]
+ u = sigma_s_i_ratio * x + (1 - sigma_s_i_ratio) * denoised
+ D_i = model(u, sigma_s * s_in, **extra_args)
+ sigma_down_i_ratio = sigma_down / sigmas[i]
+ x = sigma_down_i_ratio * x + (1 - sigma_down_i_ratio) * D_i
+ # print("sigma_i", sigmas[i], "sigma_ip1", sigmas[i+1],"sigma_down", sigma_down, "sigma_down_i_ratio", sigma_down_i_ratio, "sigma_s_i_ratio", sigma_s_i_ratio, "renoise_coeff", renoise_coeff)
+ # Noise addition
+ if sigmas[i + 1] > 0 and eta > 0:
+ x = (alpha_ip1/alpha_down) * x + noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * renoise_coeff
+ # logged_x = torch.cat((logged_x, x.unsqueeze(0)), dim=0)
+ return x
+
@torch.no_grad()
def sample_dpmpp_sde(model, x, sigmas, extra_args=None, callback=None, disable=None, eta=1., s_noise=1., noise_sampler=None, r=1 / 2):
"""DPM-Solver++ (stochastic)."""
diff --git a/comfy/latent_formats.py b/comfy/latent_formats.py
index ecb03b010..ee19faeae 100644
--- a/comfy/latent_formats.py
+++ b/comfy/latent_formats.py
@@ -141,6 +141,7 @@ class StableAudio1(LatentFormat):
latent_channels = 64
class Flux(SD3):
+ latent_channels = 16
def __init__(self):
self.scale_factor = 0.3611
self.shift_factor = 0.1159
@@ -162,6 +163,7 @@ class Flux(SD3):
[-0.0005, -0.0530, -0.0020],
[-0.1273, -0.0932, -0.0680]
]
+ self.taesd_decoder_name = "taef1_decoder"
def process_in(self, latent):
return (latent - self.shift_factor) * self.scale_factor
diff --git a/comfy/ldm/flux/controlnet_xlabs.py b/comfy/ldm/flux/controlnet_xlabs.py
new file mode 100644
index 000000000..5d700f16c
--- /dev/null
+++ b/comfy/ldm/flux/controlnet_xlabs.py
@@ -0,0 +1,104 @@
+#Original code can be found on: https://github.com/XLabs-AI/x-flux/blob/main/src/flux/controlnet.py
+
+import torch
+from torch import Tensor, nn
+from einops import rearrange, repeat
+
+from .layers import (DoubleStreamBlock, EmbedND, LastLayer,
+ MLPEmbedder, SingleStreamBlock,
+ timestep_embedding)
+
+from .model import Flux
+import comfy.ldm.common_dit
+
+
+class ControlNetFlux(Flux):
+ def __init__(self, image_model=None, dtype=None, device=None, operations=None, **kwargs):
+ super().__init__(final_layer=False, dtype=dtype, device=device, operations=operations, **kwargs)
+
+ # add ControlNet blocks
+ self.controlnet_blocks = nn.ModuleList([])
+ for _ in range(self.params.depth):
+ controlnet_block = operations.Linear(self.hidden_size, self.hidden_size, dtype=dtype, device=device)
+ # controlnet_block = zero_module(controlnet_block)
+ self.controlnet_blocks.append(controlnet_block)
+ self.pos_embed_input = operations.Linear(self.in_channels, self.hidden_size, bias=True, dtype=dtype, device=device)
+ self.gradient_checkpointing = False
+ self.input_hint_block = nn.Sequential(
+ operations.Conv2d(3, 16, 3, padding=1, dtype=dtype, device=device),
+ nn.SiLU(),
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
+ nn.SiLU(),
+ operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
+ nn.SiLU(),
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
+ nn.SiLU(),
+ operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
+ nn.SiLU(),
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
+ nn.SiLU(),
+ operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
+ nn.SiLU(),
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device)
+ )
+
+ def forward_orig(
+ self,
+ img: Tensor,
+ img_ids: Tensor,
+ controlnet_cond: Tensor,
+ txt: Tensor,
+ txt_ids: Tensor,
+ timesteps: Tensor,
+ y: Tensor,
+ guidance: Tensor = None,
+ ) -> Tensor:
+ if img.ndim != 3 or txt.ndim != 3:
+ raise ValueError("Input img and txt tensors must have 3 dimensions.")
+
+ # running on sequences img
+ img = self.img_in(img)
+ controlnet_cond = self.input_hint_block(controlnet_cond)
+ controlnet_cond = rearrange(controlnet_cond, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2)
+ controlnet_cond = self.pos_embed_input(controlnet_cond)
+ img = img + controlnet_cond
+ vec = self.time_in(timestep_embedding(timesteps, 256))
+ if self.params.guidance_embed:
+ vec = vec + self.guidance_in(timestep_embedding(guidance, 256))
+ vec = vec + self.vector_in(y)
+ txt = self.txt_in(txt)
+
+ ids = torch.cat((txt_ids, img_ids), dim=1)
+ pe = self.pe_embedder(ids)
+
+ block_res_samples = ()
+
+ for block in self.double_blocks:
+ img, txt = block(img=img, txt=txt, vec=vec, pe=pe)
+ block_res_samples = block_res_samples + (img,)
+
+ controlnet_block_res_samples = ()
+ for block_res_sample, controlnet_block in zip(block_res_samples, self.controlnet_blocks):
+ block_res_sample = controlnet_block(block_res_sample)
+ controlnet_block_res_samples = controlnet_block_res_samples + (block_res_sample,)
+
+ return {"input": (controlnet_block_res_samples * 10)[:19]}
+
+ def forward(self, x, timesteps, context, y, guidance=None, hint=None, **kwargs):
+ hint = hint * 2.0 - 1.0
+
+ bs, c, h, w = x.shape
+ patch_size = 2
+ x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size))
+
+ img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size)
+
+ h_len = ((h + (patch_size // 2)) // patch_size)
+ w_len = ((w + (patch_size // 2)) // patch_size)
+ img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype)
+ img_ids[..., 1] = img_ids[..., 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype)[:, None]
+ img_ids[..., 2] = img_ids[..., 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype)[None, :]
+ img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
+
+ txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
+ return self.forward_orig(img, img_ids, hint, context, txt_ids, timesteps, y, guidance)
diff --git a/comfy/ldm/flux/layers.py b/comfy/ldm/flux/layers.py
index 99f498106..da0cf61b1 100644
--- a/comfy/ldm/flux/layers.py
+++ b/comfy/ldm/flux/layers.py
@@ -2,12 +2,12 @@ import math
from dataclasses import dataclass
import torch
-from einops import rearrange
from torch import Tensor, nn
from .math import attention, rope
import comfy.ops
+
class EmbedND(nn.Module):
def __init__(self, dim: int, theta: int, axes_dim: list):
super().__init__()
@@ -36,9 +36,7 @@ def timestep_embedding(t: Tensor, dim, max_period=10000, time_factor: float = 10
"""
t = time_factor * t
half = dim // 2
- freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half).to(
- t.device
- )
+ freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32, device=t.device) / half)
args = t[:, None].float() * freqs[None]
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
@@ -48,7 +46,6 @@ def timestep_embedding(t: Tensor, dim, max_period=10000, time_factor: float = 10
embedding = embedding.to(t)
return embedding
-
class MLPEmbedder(nn.Module):
def __init__(self, in_dim: int, hidden_dim: int, dtype=None, device=None, operations=None):
super().__init__()
@@ -94,14 +91,6 @@ class SelfAttention(nn.Module):
self.norm = QKNorm(head_dim, dtype=dtype, device=device, operations=operations)
self.proj = operations.Linear(dim, dim, dtype=dtype, device=device)
- def forward(self, x: Tensor, pe: Tensor) -> Tensor:
- qkv = self.qkv(x)
- q, k, v = rearrange(qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
- q, k = self.norm(q, k, v)
- x = attention(q, k, v, pe=pe)
- x = self.proj(x)
- return x
-
@dataclass
class ModulationOut:
@@ -163,22 +152,21 @@ class DoubleStreamBlock(nn.Module):
img_modulated = self.img_norm1(img)
img_modulated = (1 + img_mod1.scale) * img_modulated + img_mod1.shift
img_qkv = self.img_attn.qkv(img_modulated)
- img_q, img_k, img_v = rearrange(img_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
+ img_q, img_k, img_v = img_qkv.view(img_qkv.shape[0], img_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
img_q, img_k = self.img_attn.norm(img_q, img_k, img_v)
# prepare txt for attention
txt_modulated = self.txt_norm1(txt)
txt_modulated = (1 + txt_mod1.scale) * txt_modulated + txt_mod1.shift
txt_qkv = self.txt_attn.qkv(txt_modulated)
- txt_q, txt_k, txt_v = rearrange(txt_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
+ txt_q, txt_k, txt_v = txt_qkv.view(txt_qkv.shape[0], txt_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
txt_q, txt_k = self.txt_attn.norm(txt_q, txt_k, txt_v)
# run actual attention
- q = torch.cat((txt_q, img_q), dim=2)
- k = torch.cat((txt_k, img_k), dim=2)
- v = torch.cat((txt_v, img_v), dim=2)
+ attn = attention(torch.cat((txt_q, img_q), dim=2),
+ torch.cat((txt_k, img_k), dim=2),
+ torch.cat((txt_v, img_v), dim=2), pe=pe)
- attn = attention(q, k, v, pe=pe)
txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1] :]
# calculate the img bloks
@@ -186,8 +174,12 @@ class DoubleStreamBlock(nn.Module):
img = img + img_mod2.gate * self.img_mlp((1 + img_mod2.scale) * self.img_norm2(img) + img_mod2.shift)
# calculate the txt bloks
- txt = txt + txt_mod1.gate * self.txt_attn.proj(txt_attn)
- txt = txt + txt_mod2.gate * self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift)
+ txt += txt_mod1.gate * self.txt_attn.proj(txt_attn)
+ txt += txt_mod2.gate * self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift)
+
+ if txt.dtype == torch.float16:
+ txt = txt.clip(-65504, 65504)
+
return img, txt
@@ -232,14 +224,17 @@ class SingleStreamBlock(nn.Module):
x_mod = (1 + mod.scale) * self.pre_norm(x) + mod.shift
qkv, mlp = torch.split(self.linear1(x_mod), [3 * self.hidden_size, self.mlp_hidden_dim], dim=-1)
- q, k, v = rearrange(qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
+ q, k, v = qkv.view(qkv.shape[0], qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
q, k = self.norm(q, k, v)
# compute attention
attn = attention(q, k, v, pe=pe)
# compute activation in mlp stream, cat again and run second linear layer
output = self.linear2(torch.cat((attn, self.mlp_act(mlp)), 2))
- return x + mod.gate * output
+ x += mod.gate * output
+ if x.dtype == torch.float16:
+ x = x.clip(-65504, 65504)
+ return x
class LastLayer(nn.Module):
diff --git a/comfy/ldm/flux/model.py b/comfy/ldm/flux/model.py
index db6cf3d22..63970cad2 100644
--- a/comfy/ldm/flux/model.py
+++ b/comfy/ldm/flux/model.py
@@ -38,7 +38,7 @@ class Flux(nn.Module):
Transformer model for flow matching on sequences.
"""
- def __init__(self, image_model=None, dtype=None, device=None, operations=None, **kwargs):
+ def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs):
super().__init__()
self.dtype = dtype
params = FluxParams(**kwargs)
@@ -83,7 +83,8 @@ class Flux(nn.Module):
]
)
- self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels, dtype=dtype, device=device, operations=operations)
+ if final_layer:
+ self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels, dtype=dtype, device=device, operations=operations)
def forward_orig(
self,
@@ -94,6 +95,7 @@ class Flux(nn.Module):
timesteps: Tensor,
y: Tensor,
guidance: Tensor = None,
+ control=None,
) -> Tensor:
if img.ndim != 3 or txt.ndim != 3:
raise ValueError("Input img and txt tensors must have 3 dimensions.")
@@ -112,18 +114,34 @@ class Flux(nn.Module):
ids = torch.cat((txt_ids, img_ids), dim=1)
pe = self.pe_embedder(ids)
- for block in self.double_blocks:
+ for i, block in enumerate(self.double_blocks):
img, txt = block(img=img, txt=txt, vec=vec, pe=pe)
+ if control is not None: # Controlnet
+ control_i = control.get("input")
+ if i < len(control_i):
+ add = control_i[i]
+ if add is not None:
+ img += add
+
img = torch.cat((txt, img), 1)
- for block in self.single_blocks:
+
+ for i, block in enumerate(self.single_blocks):
img = block(img, vec=vec, pe=pe)
+
+ if control is not None: # Controlnet
+ control_o = control.get("output")
+ if i < len(control_o):
+ add = control_o[i]
+ if add is not None:
+ img[:, txt.shape[1] :, ...] += add
+
img = img[:, txt.shape[1] :, ...]
img = self.final_layer(img, vec) # (N, T, patch_size ** 2 * out_channels)
return img
- def forward(self, x, timestep, context, y, guidance, **kwargs):
+ def forward(self, x, timestep, context, y, guidance, control=None, **kwargs):
bs, c, h, w = x.shape
patch_size = 2
x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size))
@@ -138,5 +156,5 @@ class Flux(nn.Module):
img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
- out = self.forward_orig(img, img_ids, context, txt_ids, timestep, y, guidance)
+ out = self.forward_orig(img, img_ids, context, txt_ids, timestep, y, guidance, control)
return rearrange(out, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=2, pw=2)[:,:,:h,:w]
diff --git a/comfy/ldm/hydit/attn_layers.py b/comfy/ldm/hydit/attn_layers.py
index 920b84286..e2801f714 100644
--- a/comfy/ldm/hydit/attn_layers.py
+++ b/comfy/ldm/hydit/attn_layers.py
@@ -47,7 +47,7 @@ def reshape_for_broadcast(freqs_cis: Union[torch.Tensor, Tuple[torch.Tensor]], x
def rotate_half(x):
- x_real, x_imag = x.float().reshape(*x.shape[:-1], -1, 2).unbind(-1) # [B, S, H, D//2]
+ x_real, x_imag = x.reshape(*x.shape[:-1], -1, 2).unbind(-1) # [B, S, H, D//2]
return torch.stack([-x_imag, x_real], dim=-1).flatten(3)
@@ -78,10 +78,9 @@ def apply_rotary_emb(
xk_out = None
if isinstance(freqs_cis, tuple):
cos, sin = reshape_for_broadcast(freqs_cis, xq, head_first) # [S, D]
- cos, sin = cos.to(xq.device), sin.to(xq.device)
- xq_out = (xq.float() * cos + rotate_half(xq.float()) * sin).type_as(xq)
+ xq_out = (xq * cos + rotate_half(xq) * sin)
if xk is not None:
- xk_out = (xk.float() * cos + rotate_half(xk.float()) * sin).type_as(xk)
+ xk_out = (xk * cos + rotate_half(xk) * sin)
else:
xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2)) # [B, S, H, D//2]
freqs_cis = reshape_for_broadcast(freqs_cis, xq_, head_first).to(xq.device) # [S, D//2] --> [1, S, 1, D//2]
diff --git a/comfy/ldm/hydit/controlnet.py b/comfy/ldm/hydit/controlnet.py
new file mode 100644
index 000000000..cd71fca31
--- /dev/null
+++ b/comfy/ldm/hydit/controlnet.py
@@ -0,0 +1,321 @@
+from typing import Any, Optional
+
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+
+from torch.utils import checkpoint
+
+from comfy.ldm.modules.diffusionmodules.mmdit import (
+ Mlp,
+ TimestepEmbedder,
+ PatchEmbed,
+ RMSNorm,
+)
+from comfy.ldm.modules.diffusionmodules.util import timestep_embedding
+from .poolers import AttentionPool
+
+import comfy.latent_formats
+from .models import HunYuanDiTBlock, calc_rope
+
+from .posemb_layers import get_2d_rotary_pos_embed, get_fill_resize_and_crop
+
+
+class HunYuanControlNet(nn.Module):
+ """
+ HunYuanDiT: Diffusion model with a Transformer backbone.
+
+ Inherit ModelMixin and ConfigMixin to be compatible with the sampler StableDiffusionPipeline of diffusers.
+
+ Inherit PeftAdapterMixin to be compatible with the PEFT training pipeline.
+
+ Parameters
+ ----------
+ args: argparse.Namespace
+ The arguments parsed by argparse.
+ input_size: tuple
+ The size of the input image.
+ patch_size: int
+ The size of the patch.
+ in_channels: int
+ The number of input channels.
+ hidden_size: int
+ The hidden size of the transformer backbone.
+ depth: int
+ The number of transformer blocks.
+ num_heads: int
+ The number of attention heads.
+ mlp_ratio: float
+ The ratio of the hidden size of the MLP in the transformer block.
+ log_fn: callable
+ The logging function.
+ """
+
+ def __init__(
+ self,
+ input_size: tuple = 128,
+ patch_size: int = 2,
+ in_channels: int = 4,
+ hidden_size: int = 1408,
+ depth: int = 40,
+ num_heads: int = 16,
+ mlp_ratio: float = 4.3637,
+ text_states_dim=1024,
+ text_states_dim_t5=2048,
+ text_len=77,
+ text_len_t5=256,
+ qk_norm=True, # See http://arxiv.org/abs/2302.05442 for details.
+ size_cond=False,
+ use_style_cond=False,
+ learn_sigma=True,
+ norm="layer",
+ log_fn: callable = print,
+ attn_precision=None,
+ dtype=None,
+ device=None,
+ operations=None,
+ **kwargs,
+ ):
+ super().__init__()
+ self.log_fn = log_fn
+ self.depth = depth
+ self.learn_sigma = learn_sigma
+ self.in_channels = in_channels
+ self.out_channels = in_channels * 2 if learn_sigma else in_channels
+ self.patch_size = patch_size
+ self.num_heads = num_heads
+ self.hidden_size = hidden_size
+ self.text_states_dim = text_states_dim
+ self.text_states_dim_t5 = text_states_dim_t5
+ self.text_len = text_len
+ self.text_len_t5 = text_len_t5
+ self.size_cond = size_cond
+ self.use_style_cond = use_style_cond
+ self.norm = norm
+ self.dtype = dtype
+ self.latent_format = comfy.latent_formats.SDXL
+
+ self.mlp_t5 = nn.Sequential(
+ nn.Linear(
+ self.text_states_dim_t5,
+ self.text_states_dim_t5 * 4,
+ bias=True,
+ dtype=dtype,
+ device=device,
+ ),
+ nn.SiLU(),
+ nn.Linear(
+ self.text_states_dim_t5 * 4,
+ self.text_states_dim,
+ bias=True,
+ dtype=dtype,
+ device=device,
+ ),
+ )
+ # learnable replace
+ self.text_embedding_padding = nn.Parameter(
+ torch.randn(
+ self.text_len + self.text_len_t5,
+ self.text_states_dim,
+ dtype=dtype,
+ device=device,
+ )
+ )
+
+ # Attention pooling
+ pooler_out_dim = 1024
+ self.pooler = AttentionPool(
+ self.text_len_t5,
+ self.text_states_dim_t5,
+ num_heads=8,
+ output_dim=pooler_out_dim,
+ dtype=dtype,
+ device=device,
+ operations=operations,
+ )
+
+ # Dimension of the extra input vectors
+ self.extra_in_dim = pooler_out_dim
+
+ if self.size_cond:
+ # Image size and crop size conditions
+ self.extra_in_dim += 6 * 256
+
+ if self.use_style_cond:
+ # Here we use a default learned embedder layer for future extension.
+ self.style_embedder = nn.Embedding(
+ 1, hidden_size, dtype=dtype, device=device
+ )
+ self.extra_in_dim += hidden_size
+
+ # Text embedding for `add`
+ self.x_embedder = PatchEmbed(
+ input_size,
+ patch_size,
+ in_channels,
+ hidden_size,
+ dtype=dtype,
+ device=device,
+ operations=operations,
+ )
+ self.t_embedder = TimestepEmbedder(
+ hidden_size, dtype=dtype, device=device, operations=operations
+ )
+ self.extra_embedder = nn.Sequential(
+ operations.Linear(
+ self.extra_in_dim, hidden_size * 4, dtype=dtype, device=device
+ ),
+ nn.SiLU(),
+ operations.Linear(
+ hidden_size * 4, hidden_size, bias=True, dtype=dtype, device=device
+ ),
+ )
+
+ # Image embedding
+ num_patches = self.x_embedder.num_patches
+
+ # HUnYuanDiT Blocks
+ self.blocks = nn.ModuleList(
+ [
+ HunYuanDiTBlock(
+ hidden_size=hidden_size,
+ c_emb_size=hidden_size,
+ num_heads=num_heads,
+ mlp_ratio=mlp_ratio,
+ text_states_dim=self.text_states_dim,
+ qk_norm=qk_norm,
+ norm_type=self.norm,
+ skip=False,
+ attn_precision=attn_precision,
+ dtype=dtype,
+ device=device,
+ operations=operations,
+ )
+ for _ in range(19)
+ ]
+ )
+
+ # Input zero linear for the first block
+ self.before_proj = operations.Linear(self.hidden_size, self.hidden_size, dtype=dtype, device=device)
+
+
+ # Output zero linear for the every block
+ self.after_proj_list = nn.ModuleList(
+ [
+
+ operations.Linear(
+ self.hidden_size, self.hidden_size, dtype=dtype, device=device
+ )
+ for _ in range(len(self.blocks))
+ ]
+ )
+
+ def forward(
+ self,
+ x,
+ hint,
+ timesteps,
+ context,#encoder_hidden_states=None,
+ text_embedding_mask=None,
+ encoder_hidden_states_t5=None,
+ text_embedding_mask_t5=None,
+ image_meta_size=None,
+ style=None,
+ return_dict=False,
+ **kwarg,
+ ):
+ """
+ Forward pass of the encoder.
+
+ Parameters
+ ----------
+ x: torch.Tensor
+ (B, D, H, W)
+ t: torch.Tensor
+ (B)
+ encoder_hidden_states: torch.Tensor
+ CLIP text embedding, (B, L_clip, D)
+ text_embedding_mask: torch.Tensor
+ CLIP text embedding mask, (B, L_clip)
+ encoder_hidden_states_t5: torch.Tensor
+ T5 text embedding, (B, L_t5, D)
+ text_embedding_mask_t5: torch.Tensor
+ T5 text embedding mask, (B, L_t5)
+ image_meta_size: torch.Tensor
+ (B, 6)
+ style: torch.Tensor
+ (B)
+ cos_cis_img: torch.Tensor
+ sin_cis_img: torch.Tensor
+ return_dict: bool
+ Whether to return a dictionary.
+ """
+ condition = hint
+ if condition.shape[0] == 1:
+ condition = torch.repeat_interleave(condition, x.shape[0], dim=0)
+
+ text_states = context # 2,77,1024
+ text_states_t5 = encoder_hidden_states_t5 # 2,256,2048
+ text_states_mask = text_embedding_mask.bool() # 2,77
+ text_states_t5_mask = text_embedding_mask_t5.bool() # 2,256
+ b_t5, l_t5, c_t5 = text_states_t5.shape
+ text_states_t5 = self.mlp_t5(text_states_t5.view(-1, c_t5)).view(b_t5, l_t5, -1)
+
+ padding = comfy.ops.cast_to_input(self.text_embedding_padding, text_states)
+
+ text_states[:, -self.text_len :] = torch.where(
+ text_states_mask[:, -self.text_len :].unsqueeze(2),
+ text_states[:, -self.text_len :],
+ padding[: self.text_len],
+ )
+ text_states_t5[:, -self.text_len_t5 :] = torch.where(
+ text_states_t5_mask[:, -self.text_len_t5 :].unsqueeze(2),
+ text_states_t5[:, -self.text_len_t5 :],
+ padding[self.text_len :],
+ )
+
+ text_states = torch.cat([text_states, text_states_t5], dim=1) # 2,205,1024
+
+ # _, _, oh, ow = x.shape
+ # th, tw = oh // self.patch_size, ow // self.patch_size
+
+ # Get image RoPE embedding according to `reso`lution.
+ freqs_cis_img = calc_rope(
+ x, self.patch_size, self.hidden_size // self.num_heads
+ ) # (cos_cis_img, sin_cis_img)
+
+ # ========================= Build time and image embedding =========================
+ t = self.t_embedder(timesteps, dtype=self.dtype)
+ x = self.x_embedder(x)
+
+ # ========================= Concatenate all extra vectors =========================
+ # Build text tokens with pooling
+ extra_vec = self.pooler(encoder_hidden_states_t5)
+
+ # Build image meta size tokens if applicable
+ # if image_meta_size is not None:
+ # image_meta_size = timestep_embedding(image_meta_size.view(-1), 256) # [B * 6, 256]
+ # if image_meta_size.dtype != self.dtype:
+ # image_meta_size = image_meta_size.half()
+ # image_meta_size = image_meta_size.view(-1, 6 * 256)
+ # extra_vec = torch.cat([extra_vec, image_meta_size], dim=1) # [B, D + 6 * 256]
+
+ # Build style tokens
+ if style is not None:
+ style_embedding = self.style_embedder(style)
+ extra_vec = torch.cat([extra_vec, style_embedding], dim=1)
+
+ # Concatenate all extra vectors
+ c = t + self.extra_embedder(extra_vec) # [B, D]
+
+ # ========================= Deal with Condition =========================
+ condition = self.x_embedder(condition)
+
+ # ========================= Forward pass through HunYuanDiT blocks =========================
+ controls = []
+ x = x + self.before_proj(condition) # add condition
+ for layer, block in enumerate(self.blocks):
+ x = block(x, c, text_states, freqs_cis_img)
+ controls.append(self.after_proj_list[layer](x)) # zero linear for output
+
+ return {"output": controls}
diff --git a/comfy/ldm/hydit/models.py b/comfy/ldm/hydit/models.py
index c70dbf92a..f3afaad34 100644
--- a/comfy/ldm/hydit/models.py
+++ b/comfy/ldm/hydit/models.py
@@ -21,6 +21,7 @@ def calc_rope(x, patch_size, head_size):
sub_args = [start, stop, (th, tw)]
# head_size = HUNYUAN_DIT_CONFIG['DiT-g/2']['hidden_size'] // HUNYUAN_DIT_CONFIG['DiT-g/2']['num_heads']
rope = get_2d_rotary_pos_embed(head_size, *sub_args)
+ rope = (rope[0].to(x), rope[1].to(x))
return rope
@@ -91,6 +92,8 @@ class HunYuanDiTBlock(nn.Module):
# Long Skip Connection
if self.skip_linear is not None:
cat = torch.cat([x, skip], dim=-1)
+ if cat.dtype != x.dtype:
+ cat = cat.to(x.dtype)
cat = self.skip_norm(cat)
x = self.skip_linear(cat)
@@ -362,6 +365,8 @@ class HunYuanDiT(nn.Module):
c = t + self.extra_embedder(extra_vec) # [B, D]
controls = None
+ if control:
+ controls = control.get("output", None)
# ========================= Forward pass through HunYuanDiT blocks =========================
skips = []
for layer, block in enumerate(self.blocks):
diff --git a/comfy/ldm/modules/attention.py b/comfy/ldm/modules/attention.py
index 65a8bcf42..85ea406e0 100644
--- a/comfy/ldm/modules/attention.py
+++ b/comfy/ldm/modules/attention.py
@@ -358,7 +358,7 @@ def attention_xformers(q, k, v, heads, mask=None, attn_precision=None, skip_resh
disabled_xformers = True
if disabled_xformers:
- return attention_pytorch(q, k, v, heads, mask)
+ return attention_pytorch(q, k, v, heads, mask, skip_reshape=skip_reshape)
if skip_reshape:
q, k, v = map(
diff --git a/comfy/lora.py b/comfy/lora.py
index 04e8861c9..3b8b6c162 100644
--- a/comfy/lora.py
+++ b/comfy/lora.py
@@ -1,3 +1,21 @@
+"""
+ This file is part of ComfyUI.
+ Copyright (C) 2024 Comfy
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
import comfy.utils
import logging
@@ -218,11 +236,17 @@ def model_lora_keys_clip(model, key_map={}):
lora_key = "lora_prior_te_text_model_encoder_layers_{}_{}".format(b, LORA_CLIP_MAP[c]) #cascade lora: TODO put lora key prefix in the model config
key_map[lora_key] = k
- for k in sdk: #OneTrainer SD3 lora
- if k.startswith("t5xxl.transformer.") and k.endswith(".weight"):
- l_key = k[len("t5xxl.transformer."):-len(".weight")]
- lora_key = "lora_te3_{}".format(l_key.replace(".", "_"))
- key_map[lora_key] = k
+ for k in sdk:
+ if k.endswith(".weight"):
+ if k.startswith("t5xxl.transformer."):#OneTrainer SD3 lora
+ l_key = k[len("t5xxl.transformer."):-len(".weight")]
+ lora_key = "lora_te3_{}".format(l_key.replace(".", "_"))
+ key_map[lora_key] = k
+ elif k.startswith("hydit_clip.transformer.bert."): #HunyuanDiT Lora
+ l_key = k[len("hydit_clip.transformer.bert."):-len(".weight")]
+ lora_key = "lora_te1_{}".format(l_key.replace(".", "_"))
+ key_map[lora_key] = k
+
k = "clip_g.transformer.text_projection.weight"
if k in sdk:
@@ -245,6 +269,7 @@ def model_lora_keys_unet(model, key_map={}):
key_lora = k[len("diffusion_model."):-len(".weight")].replace(".", "_")
key_map["lora_unet_{}".format(key_lora)] = k
key_map["lora_prior_unet_{}".format(key_lora)] = k #cascade lora: TODO put lora key prefix in the model config
+ key_map["{}".format(k[:-len(".weight")])] = k #generic lora format without any weird key names
diffusers_keys = comfy.utils.unet_to_diffusers(model.model_config.unet_config)
for k in diffusers_keys:
diff --git a/comfy/model_base.py b/comfy/model_base.py
index cb6949649..830bcc68c 100644
--- a/comfy/model_base.py
+++ b/comfy/model_base.py
@@ -1,3 +1,21 @@
+"""
+ This file is part of ComfyUI.
+ Copyright (C) 2024 Comfy
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
import torch
import logging
from comfy.ldm.modules.diffusionmodules.openaimodel import UNetModel, Timestep
@@ -77,10 +95,13 @@ class BaseModel(torch.nn.Module):
self.device = device
if not unet_config.get("disable_unet_model_creation", False):
- if self.manual_cast_dtype is not None:
- operations = comfy.ops.manual_cast
+ if model_config.custom_operations is None:
+ if self.manual_cast_dtype is not None:
+ operations = comfy.ops.manual_cast
+ else:
+ operations = comfy.ops.disable_weight_init
else:
- operations = comfy.ops.disable_weight_init
+ operations = model_config.custom_operations
self.diffusion_model = unet_model(**unet_config, device=device, operations=operations)
if comfy.model_management.force_channels_last():
self.diffusion_model.to(memory_format=torch.channels_last)
diff --git a/comfy/model_detection.py b/comfy/model_detection.py
index c47119686..c05975cc9 100644
--- a/comfy/model_detection.py
+++ b/comfy/model_detection.py
@@ -137,8 +137,8 @@ def detect_unet_config(state_dict, key_prefix):
dit_config["hidden_size"] = 3072
dit_config["mlp_ratio"] = 4.0
dit_config["num_heads"] = 24
- dit_config["depth"] = 19
- dit_config["depth_single_blocks"] = 38
+ dit_config["depth"] = count_blocks(state_dict_keys, '{}double_blocks.'.format(key_prefix) + '{}.')
+ dit_config["depth_single_blocks"] = count_blocks(state_dict_keys, '{}single_blocks.'.format(key_prefix) + '{}.')
dit_config["axes_dim"] = [16, 56, 56]
dit_config["theta"] = 10000
dit_config["qkv_bias"] = True
@@ -495,7 +495,12 @@ def model_config_from_diffusers_unet(state_dict):
def convert_diffusers_mmdit(state_dict, output_prefix=""):
out_sd = {}
- if 'transformer_blocks.0.attn.add_q_proj.weight' in state_dict: #SD3
+ if 'transformer_blocks.0.attn.norm_added_k.weight' in state_dict: #Flux
+ depth = count_blocks(state_dict, 'transformer_blocks.{}.')
+ depth_single_blocks = count_blocks(state_dict, 'single_transformer_blocks.{}.')
+ hidden_size = state_dict["x_embedder.bias"].shape[0]
+ sd_map = comfy.utils.flux_to_diffusers({"depth": depth, "depth_single_blocks": depth_single_blocks, "hidden_size": hidden_size}, output_prefix=output_prefix)
+ elif 'transformer_blocks.0.attn.add_q_proj.weight' in state_dict: #SD3
num_blocks = count_blocks(state_dict, 'transformer_blocks.{}.')
depth = state_dict["pos_embed.proj.weight"].shape[0] // 64
sd_map = comfy.utils.mmdit_to_diffusers({"depth": depth, "num_blocks": num_blocks}, output_prefix=output_prefix)
@@ -521,7 +526,12 @@ def convert_diffusers_mmdit(state_dict, output_prefix=""):
old_weight = out_sd.get(t[0], None)
if old_weight is None:
old_weight = torch.empty_like(weight)
- old_weight = old_weight.repeat([3] + [1] * (len(old_weight.shape) - 1))
+ if old_weight.shape[offset[0]] < offset[1] + offset[2]:
+ exp = list(weight.shape)
+ exp[offset[0]] = offset[1] + offset[2]
+ new = torch.empty(exp, device=weight.device, dtype=weight.dtype)
+ new[:old_weight.shape[0]] = old_weight
+ old_weight = new
w = old_weight.narrow(offset[0], offset[1], offset[2])
else:
diff --git a/comfy/model_management.py b/comfy/model_management.py
index d68899a69..e7887fdb6 100644
--- a/comfy/model_management.py
+++ b/comfy/model_management.py
@@ -1,3 +1,21 @@
+"""
+ This file is part of ComfyUI.
+ Copyright (C) 2024 Comfy
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
import psutil
import logging
from enum import Enum
@@ -274,9 +292,12 @@ class LoadedModel:
def model_memory(self):
return self.model.model_size()
+ def model_offloaded_memory(self):
+ return self.model.model_size() - self.model.loaded_size()
+
def model_memory_required(self, device):
if device == self.model.current_loaded_device():
- return 0
+ return self.model_offloaded_memory()
else:
return self.model_memory()
@@ -288,15 +309,18 @@ class LoadedModel:
load_weights = not self.weights_loaded
- try:
- if lowvram_model_memory > 0 and load_weights:
- self.real_model = self.model.patch_model_lowvram(device_to=patch_model_to, lowvram_model_memory=lowvram_model_memory, force_patch_weights=force_patch_weights)
- else:
- self.real_model = self.model.patch_model(device_to=patch_model_to, patch_weights=load_weights)
- except Exception as e:
- self.model.unpatch_model(self.model.offload_device)
- self.model_unload()
- raise e
+ if self.model.loaded_size() > 0:
+ use_more_vram = lowvram_model_memory
+ if use_more_vram == 0:
+ use_more_vram = 1e32
+ self.model_use_more_vram(use_more_vram)
+ else:
+ try:
+ self.real_model = self.model.patch_model(device_to=patch_model_to, lowvram_model_memory=lowvram_model_memory, load_weights=load_weights, force_patch_weights=force_patch_weights)
+ except Exception as e:
+ self.model.unpatch_model(self.model.offload_device)
+ self.model_unload()
+ raise e
if is_intel_xpu() and not args.disable_ipex_optimize:
self.real_model = ipex.optimize(self.real_model.eval(), graph_mode=True, concat_linear=True)
@@ -305,22 +329,56 @@ class LoadedModel:
return self.real_model
def should_reload_model(self, force_patch_weights=False):
- if force_patch_weights and self.model.lowvram_patch_counter > 0:
+ if force_patch_weights and self.model.lowvram_patch_counter() > 0:
return True
return False
- def model_unload(self, unpatch_weights=True):
+ def model_unload(self, memory_to_free=None, unpatch_weights=True):
+ if memory_to_free is not None:
+ if memory_to_free < self.model.loaded_size():
+ freed = self.model.partially_unload(self.model.offload_device, memory_to_free)
+ if freed >= memory_to_free:
+ return False
self.model.unpatch_model(self.model.offload_device, unpatch_weights=unpatch_weights)
self.model.model_patches_to(self.model.offload_device)
self.weights_loaded = self.weights_loaded and not unpatch_weights
self.real_model = None
+ return True
+
+ def model_use_more_vram(self, extra_memory):
+ return self.model.partially_load(self.device, extra_memory)
def __eq__(self, other):
return self.model is other.model
+def use_more_memory(extra_memory, loaded_models, device):
+ for m in loaded_models:
+ if m.device == device:
+ extra_memory -= m.model_use_more_vram(extra_memory)
+ if extra_memory <= 0:
+ break
+
+def offloaded_memory(loaded_models, device):
+ offloaded_mem = 0
+ for m in loaded_models:
+ if m.device == device:
+ offloaded_mem += m.model_offloaded_memory()
+ return offloaded_mem
+
def minimum_inference_memory():
return (1024 * 1024 * 1024) * 1.2
+EXTRA_RESERVED_VRAM = 200 * 1024 * 1024
+if any(platform.win32_ver()):
+ EXTRA_RESERVED_VRAM = 400 * 1024 * 1024 #Windows is higher because of the shared vram issue
+
+if args.reserve_vram is not None:
+ EXTRA_RESERVED_VRAM = args.reserve_vram * 1024 * 1024 * 1024
+ logging.debug("Reserving {}MB vram for other applications.".format(EXTRA_RESERVED_VRAM / (1024 * 1024)))
+
+def extra_reserved_memory():
+ return EXTRA_RESERVED_VRAM
+
def unload_model_clones(model, unload_weights_only=True, force_unload=True):
to_unload = []
for i in range(len(current_loaded_models)):
@@ -364,11 +422,15 @@ def free_memory(memory_required, device, keep_loaded=[]):
for x in sorted(can_unload):
i = x[-1]
+ memory_to_free = None
if not DISABLE_SMART_MEMORY:
- if get_free_memory(device) > memory_required:
+ free_mem = get_free_memory(device)
+ if free_mem > memory_required:
break
- current_loaded_models[i].model_unload()
- unloaded_model.append(i)
+ memory_to_free = memory_required - free_mem
+ logging.debug(f"Unloading {current_loaded_models[i].model.model.__class__.__name__}")
+ if current_loaded_models[i].model_unload(memory_to_free):
+ unloaded_model.append(i)
for i in sorted(unloaded_model, reverse=True):
unloaded_models.append(current_loaded_models.pop(i))
@@ -382,15 +444,15 @@ def free_memory(memory_required, device, keep_loaded=[]):
soft_empty_cache()
return unloaded_models
-def load_models_gpu(models, memory_required=0, force_patch_weights=False, minimum_memory_required=None):
+def load_models_gpu(models, memory_required=0, force_patch_weights=False, minimum_memory_required=None, force_full_load=False):
global vram_state
inference_memory = minimum_inference_memory()
- extra_mem = max(inference_memory, memory_required)
+ extra_mem = max(inference_memory, memory_required + extra_reserved_memory())
if minimum_memory_required is None:
minimum_memory_required = extra_mem
else:
- minimum_memory_required = max(inference_memory, minimum_memory_required)
+ minimum_memory_required = max(inference_memory, minimum_memory_required + extra_reserved_memory())
models = set(models)
@@ -423,12 +485,14 @@ def load_models_gpu(models, memory_required=0, force_patch_weights=False, minimu
devs = set(map(lambda a: a.device, models_already_loaded))
for d in devs:
if d != torch.device("cpu"):
- free_memory(extra_mem, d, models_already_loaded)
+ free_memory(extra_mem + offloaded_memory(models_already_loaded, d), d, models_already_loaded)
free_mem = get_free_memory(d)
if free_mem < minimum_memory_required:
logging.info("Unloading models for lowram load.") #TODO: partial model unloading when this case happens, also handle the opposite case where models can be unlowvramed.
models_to_load = free_memory(minimum_memory_required, d)
logging.info("{} models unloaded.".format(len(models_to_load)))
+ else:
+ use_more_memory(free_mem - minimum_memory_required, models_already_loaded, d)
if len(models_to_load) == 0:
return
@@ -436,18 +500,21 @@ def load_models_gpu(models, memory_required=0, force_patch_weights=False, minimu
total_memory_required = {}
for loaded_model in models_to_load:
- if unload_model_clones(loaded_model.model, unload_weights_only=True, force_unload=False) == True:#unload clones where the weights are different
- total_memory_required[loaded_model.device] = total_memory_required.get(loaded_model.device, 0) + loaded_model.model_memory_required(loaded_model.device)
+ unload_model_clones(loaded_model.model, unload_weights_only=True, force_unload=False) #unload clones where the weights are different
+ total_memory_required[loaded_model.device] = total_memory_required.get(loaded_model.device, 0) + loaded_model.model_memory_required(loaded_model.device)
- for device in total_memory_required:
- if device != torch.device("cpu"):
- free_memory(total_memory_required[device] * 1.3 + extra_mem, device, models_already_loaded)
+ for loaded_model in models_already_loaded:
+ total_memory_required[loaded_model.device] = total_memory_required.get(loaded_model.device, 0) + loaded_model.model_memory_required(loaded_model.device)
for loaded_model in models_to_load:
weights_unloaded = unload_model_clones(loaded_model.model, unload_weights_only=False, force_unload=False) #unload the rest of the clones where the weights can stay loaded
if weights_unloaded is not None:
loaded_model.weights_loaded = not weights_unloaded
+ for device in total_memory_required:
+ if device != torch.device("cpu"):
+ free_memory(total_memory_required[device] * 1.1 + extra_mem, device, models_already_loaded)
+
for loaded_model in models_to_load:
model = loaded_model.model
torch_dev = model.load_device
@@ -456,7 +523,7 @@ def load_models_gpu(models, memory_required=0, force_patch_weights=False, minimu
else:
vram_set_state = vram_state
lowvram_model_memory = 0
- if lowvram_available and (vram_set_state == VRAMState.LOW_VRAM or vram_set_state == VRAMState.NORMAL_VRAM):
+ if lowvram_available and (vram_set_state == VRAMState.LOW_VRAM or vram_set_state == VRAMState.NORMAL_VRAM) and not force_full_load:
model_size = loaded_model.model_memory_required(torch_dev)
current_free_mem = get_free_memory(torch_dev)
lowvram_model_memory = max(64 * (1024 * 1024), (current_free_mem - minimum_memory_required), min(current_free_mem * 0.4, current_free_mem - minimum_inference_memory()))
@@ -468,6 +535,14 @@ def load_models_gpu(models, memory_required=0, force_patch_weights=False, minimu
cur_loaded_model = loaded_model.model_load(lowvram_model_memory, force_patch_weights=force_patch_weights)
current_loaded_models.insert(0, loaded_model)
+
+
+ devs = set(map(lambda a: a.device, models_already_loaded))
+ for d in devs:
+ if d != torch.device("cpu"):
+ free_mem = get_free_memory(d)
+ if free_mem > minimum_memory_required:
+ use_more_memory(free_mem - minimum_memory_required, models_already_loaded, d)
return
@@ -579,12 +654,22 @@ def unet_dtype(device=None, model_params=0, supported_dtypes=[torch.float16, tor
if model_params * 2 > free_model_memory:
return fp8_dtype
- if should_use_fp16(device=device, model_params=model_params, manual_cast=True):
- if torch.float16 in supported_dtypes:
- return torch.float16
- if should_use_bf16(device, model_params=model_params, manual_cast=True):
- if torch.bfloat16 in supported_dtypes:
- return torch.bfloat16
+ for dt in supported_dtypes:
+ if dt == torch.float16 and should_use_fp16(device=device, model_params=model_params):
+ if torch.float16 in supported_dtypes:
+ return torch.float16
+ if dt == torch.bfloat16 and should_use_bf16(device, model_params=model_params):
+ if torch.bfloat16 in supported_dtypes:
+ return torch.bfloat16
+
+ for dt in supported_dtypes:
+ if dt == torch.float16 and should_use_fp16(device=device, model_params=model_params, manual_cast=True):
+ if torch.float16 in supported_dtypes:
+ return torch.float16
+ if dt == torch.bfloat16 and should_use_bf16(device, model_params=model_params, manual_cast=True):
+ if torch.bfloat16 in supported_dtypes:
+ return torch.bfloat16
+
return torch.float32
# None means no manual cast
@@ -600,13 +685,13 @@ def unet_manual_cast(weight_dtype, inference_device, supported_dtypes=[torch.flo
if bf16_supported and weight_dtype == torch.bfloat16:
return None
- if fp16_supported and torch.float16 in supported_dtypes:
- return torch.float16
+ for dt in supported_dtypes:
+ if dt == torch.float16 and fp16_supported:
+ return torch.float16
+ if dt == torch.bfloat16 and bf16_supported:
+ return torch.bfloat16
- elif bf16_supported and torch.bfloat16 in supported_dtypes:
- return torch.bfloat16
- else:
- return torch.float32
+ return torch.float32
def text_encoder_offload_device():
if args.gpu_only:
@@ -625,6 +710,20 @@ def text_encoder_device():
else:
return torch.device("cpu")
+def text_encoder_initial_device(load_device, offload_device, model_size=0):
+ if load_device == offload_device or model_size <= 1024 * 1024 * 1024:
+ return offload_device
+
+ if is_device_mps(load_device):
+ return offload_device
+
+ mem_l = get_free_memory(load_device)
+ mem_o = get_free_memory(offload_device)
+ if mem_l > (mem_o * 0.5) and model_size * 1.2 < mem_l:
+ return load_device
+ else:
+ return offload_device
+
def text_encoder_dtype(device=None):
if args.fp8_e4m3fn_text_enc:
return torch.float8_e4m3fn
diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py
index 430b59879..ae33687c1 100644
--- a/comfy/model_patcher.py
+++ b/comfy/model_patcher.py
@@ -1,16 +1,37 @@
+"""
+ This file is part of ComfyUI.
+ Copyright (C) 2024 Comfy
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
import torch
import copy
import inspect
import logging
import uuid
+import collections
+import math
import comfy.utils
+import comfy.float
import comfy.model_management
from comfy.types import UnetWrapperFunction
-def weight_decompose(dora_scale, weight, lora_diff, alpha, strength):
- dora_scale = comfy.model_management.cast_to_device(dora_scale, weight.device, torch.float32)
+def weight_decompose(dora_scale, weight, lora_diff, alpha, strength, intermediate_dtype):
+ dora_scale = comfy.model_management.cast_to_device(dora_scale, weight.device, intermediate_dtype)
lora_diff *= alpha
weight_calc = weight + lora_diff.type(weight.dtype)
weight_norm = (
@@ -63,12 +84,27 @@ def set_model_options_pre_cfg_function(model_options, pre_cfg_function, disable_
model_options["disable_cfg1_optimization"] = True
return model_options
+def wipe_lowvram_weight(m):
+ if hasattr(m, "prev_comfy_cast_weights"):
+ m.comfy_cast_weights = m.prev_comfy_cast_weights
+ del m.prev_comfy_cast_weights
+ m.weight_function = None
+ m.bias_function = None
+
+class LowVramPatch:
+ def __init__(self, key, model_patcher):
+ self.key = key
+ self.model_patcher = model_patcher
+ def __call__(self, weight):
+ return self.model_patcher.calculate_weight(self.model_patcher.patches[self.key], weight, self.key, intermediate_dtype=weight.dtype)
+
+
class ModelPatcher:
def __init__(self, model, load_device, offload_device, size=0, weight_inplace_update=False):
self.size = size
self.model = model
if not hasattr(self.model, 'device'):
- logging.info("Model doesn't have a device attribute.")
+ logging.debug("Model doesn't have a device attribute.")
self.model.device = offload_device
elif self.model.device is None:
self.model.device = offload_device
@@ -82,16 +118,29 @@ class ModelPatcher:
self.load_device = load_device
self.offload_device = offload_device
self.weight_inplace_update = weight_inplace_update
- self.model_lowvram = False
- self.lowvram_patch_counter = 0
self.patches_uuid = uuid.uuid4()
+ if not hasattr(self.model, 'model_loaded_weight_memory'):
+ self.model.model_loaded_weight_memory = 0
+
+ if not hasattr(self.model, 'lowvram_patch_counter'):
+ self.model.lowvram_patch_counter = 0
+
+ if not hasattr(self.model, 'model_lowvram'):
+ self.model.model_lowvram = False
+
def model_size(self):
if self.size > 0:
return self.size
self.size = comfy.model_management.module_size(self.model)
return self.size
+ def loaded_size(self):
+ return self.model.model_loaded_weight_memory
+
+ def lowvram_patch_counter(self):
+ return self.model.lowvram_patch_counter
+
def clone(self):
n = ModelPatcher(self.model, self.load_device, self.offload_device, self.size, weight_inplace_update=self.weight_inplace_update)
n.patches = {}
@@ -265,67 +314,43 @@ class ModelPatcher:
sd.pop(k)
return sd
- def patch_weight_to_device(self, key, device_to=None):
+ def patch_weight_to_device(self, key, device_to=None, inplace_update=False):
if key not in self.patches:
return
weight = comfy.utils.get_attr(self.model, key)
- inplace_update = self.weight_inplace_update
+ inplace_update = self.weight_inplace_update or inplace_update
if key not in self.backup:
- self.backup[key] = weight.to(device=self.offload_device, copy=inplace_update)
+ self.backup[key] = collections.namedtuple('Dimension', ['weight', 'inplace_update'])(weight.to(device=self.offload_device, copy=inplace_update), inplace_update)
if device_to is not None:
temp_weight = comfy.model_management.cast_to_device(weight, device_to, torch.float32, copy=True)
else:
temp_weight = weight.to(torch.float32, copy=True)
- out_weight = self.calculate_weight(self.patches[key], temp_weight, key).to(weight.dtype)
+ out_weight = self.calculate_weight(self.patches[key], temp_weight, key)
+ out_weight = comfy.float.stochastic_rounding(out_weight, weight.dtype)
if inplace_update:
comfy.utils.copy_to_param(self.model, key, out_weight)
else:
comfy.utils.set_attr_param(self.model, key, out_weight)
- def patch_model(self, device_to=None, patch_weights=True):
- for k in self.object_patches:
- old = comfy.utils.set_attr(self.model, k, self.object_patches[k])
- if k not in self.object_patches_backup:
- self.object_patches_backup[k] = old
-
- if patch_weights:
- model_sd = self.model_state_dict()
- for key in self.patches:
- if key not in model_sd:
- logging.warning("could not patch. key doesn't exist in model: {}".format(key))
- continue
-
- self.patch_weight_to_device(key, device_to)
-
- if device_to is not None:
- self.model.to(device_to)
- self.model.device = device_to
-
- return self.model
-
- def patch_model_lowvram(self, device_to=None, lowvram_model_memory=0, force_patch_weights=False):
- self.patch_model(device_to, patch_weights=False)
-
- logging.info("loading in lowvram mode {}".format(lowvram_model_memory/(1024 * 1024)))
- class LowVramPatch:
- def __init__(self, key, model_patcher):
- self.key = key
- self.model_patcher = model_patcher
- def __call__(self, weight):
- return self.model_patcher.calculate_weight(self.model_patcher.patches[self.key], weight, self.key)
-
+ def load(self, device_to=None, lowvram_model_memory=0, force_patch_weights=False, full_load=False):
mem_counter = 0
patch_counter = 0
+ lowvram_counter = 0
+ load_completely = []
for n, m in self.model.named_modules():
lowvram_weight = False
- if hasattr(m, "comfy_cast_weights"):
+
+ if not full_load and hasattr(m, "comfy_cast_weights"):
module_mem = comfy.model_management.module_size(m)
if mem_counter + module_mem >= lowvram_model_memory:
lowvram_weight = True
+ lowvram_counter += 1
+ if hasattr(m, "prev_comfy_cast_weights"): #Already lowvramed
+ continue
weight_key = "{}.weight".format(n)
bias_key = "{}.bias".format(n)
@@ -347,19 +372,63 @@ class ModelPatcher:
m.prev_comfy_cast_weights = m.comfy_cast_weights
m.comfy_cast_weights = True
else:
- if hasattr(m, "weight"):
- self.patch_weight_to_device(weight_key, device_to)
- self.patch_weight_to_device(bias_key, device_to)
- m.to(device_to)
- mem_counter += comfy.model_management.module_size(m)
- logging.debug("lowvram: loaded module regularly {} {}".format(n, m))
+ if hasattr(m, "comfy_cast_weights"):
+ if m.comfy_cast_weights:
+ wipe_lowvram_weight(m)
- self.model_lowvram = True
- self.lowvram_patch_counter = patch_counter
+ if hasattr(m, "weight"):
+ mem_used = comfy.model_management.module_size(m)
+ mem_counter += mem_used
+ load_completely.append((mem_used, n, m))
+
+ load_completely.sort(reverse=True)
+ for x in load_completely:
+ n = x[1]
+ m = x[2]
+ weight_key = "{}.weight".format(n)
+ bias_key = "{}.bias".format(n)
+ if hasattr(m, "comfy_patched_weights"):
+ if m.comfy_patched_weights == True:
+ continue
+
+ self.patch_weight_to_device(weight_key, device_to=device_to)
+ self.patch_weight_to_device(bias_key, device_to=device_to)
+ logging.debug("lowvram: loaded module regularly {} {}".format(n, m))
+ m.comfy_patched_weights = True
+
+ for x in load_completely:
+ x[2].to(device_to)
+
+ if lowvram_counter > 0:
+ logging.info("loaded partially {} {} {}".format(lowvram_model_memory / (1024 * 1024), mem_counter / (1024 * 1024), patch_counter))
+ self.model.model_lowvram = True
+ else:
+ logging.info("loaded completely {} {} {}".format(lowvram_model_memory / (1024 * 1024), mem_counter / (1024 * 1024), full_load))
+ self.model.model_lowvram = False
+ if full_load:
+ self.model.to(device_to)
+ mem_counter = self.model_size()
+
+ self.model.lowvram_patch_counter += patch_counter
self.model.device = device_to
+ self.model.model_loaded_weight_memory = mem_counter
+
+ def patch_model(self, device_to=None, lowvram_model_memory=0, load_weights=True, force_patch_weights=False):
+ for k in self.object_patches:
+ old = comfy.utils.set_attr(self.model, k, self.object_patches[k])
+ if k not in self.object_patches_backup:
+ self.object_patches_backup[k] = old
+
+ if lowvram_model_memory == 0:
+ full_load = True
+ else:
+ full_load = False
+
+ if load_weights:
+ self.load(device_to, lowvram_model_memory=lowvram_model_memory, force_patch_weights=force_patch_weights, full_load=full_load)
return self.model
- def calculate_weight(self, patches, weight, key):
+ def calculate_weight(self, patches, weight, key, intermediate_dtype=torch.float32):
for p in patches:
strength = p[0]
v = p[1]
@@ -378,7 +447,7 @@ class ModelPatcher:
weight *= strength_model
if isinstance(v, list):
- v = (self.calculate_weight(v[1:], v[0].clone(), key), )
+ v = (self.calculate_weight(v[1:], v[0].clone(), key, intermediate_dtype=intermediate_dtype), )
if len(v) == 1:
patch_type = "diff"
@@ -394,8 +463,8 @@ class ModelPatcher:
else:
weight += function(strength * comfy.model_management.cast_to_device(w1, weight.device, weight.dtype))
elif patch_type == "lora": #lora/locon
- mat1 = comfy.model_management.cast_to_device(v[0], weight.device, torch.float32)
- mat2 = comfy.model_management.cast_to_device(v[1], weight.device, torch.float32)
+ mat1 = comfy.model_management.cast_to_device(v[0], weight.device, intermediate_dtype)
+ mat2 = comfy.model_management.cast_to_device(v[1], weight.device, intermediate_dtype)
dora_scale = v[4]
if v[2] is not None:
alpha = v[2] / mat2.shape[0]
@@ -404,13 +473,13 @@ class ModelPatcher:
if v[3] is not None:
#locon mid weights, hopefully the math is fine because I didn't properly test it
- mat3 = comfy.model_management.cast_to_device(v[3], weight.device, torch.float32)
+ mat3 = comfy.model_management.cast_to_device(v[3], weight.device, intermediate_dtype)
final_shape = [mat2.shape[1], mat2.shape[0], mat3.shape[2], mat3.shape[3]]
mat2 = torch.mm(mat2.transpose(0, 1).flatten(start_dim=1), mat3.transpose(0, 1).flatten(start_dim=1)).reshape(final_shape).transpose(0, 1)
try:
lora_diff = torch.mm(mat1.flatten(start_dim=1), mat2.flatten(start_dim=1)).reshape(weight.shape)
if dora_scale is not None:
- weight = function(weight_decompose(dora_scale, weight, lora_diff, alpha, strength))
+ weight = function(weight_decompose(dora_scale, weight, lora_diff, alpha, strength, intermediate_dtype))
else:
weight += function(((strength * alpha) * lora_diff).type(weight.dtype))
except Exception as e:
@@ -428,23 +497,23 @@ class ModelPatcher:
if w1 is None:
dim = w1_b.shape[0]
- w1 = torch.mm(comfy.model_management.cast_to_device(w1_a, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w1_b, weight.device, torch.float32))
+ w1 = torch.mm(comfy.model_management.cast_to_device(w1_a, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w1_b, weight.device, intermediate_dtype))
else:
- w1 = comfy.model_management.cast_to_device(w1, weight.device, torch.float32)
+ w1 = comfy.model_management.cast_to_device(w1, weight.device, intermediate_dtype)
if w2 is None:
dim = w2_b.shape[0]
if t2 is None:
- w2 = torch.mm(comfy.model_management.cast_to_device(w2_a, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w2_b, weight.device, torch.float32))
+ w2 = torch.mm(comfy.model_management.cast_to_device(w2_a, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w2_b, weight.device, intermediate_dtype))
else:
w2 = torch.einsum('i j k l, j r, i p -> p r k l',
- comfy.model_management.cast_to_device(t2, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w2_b, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w2_a, weight.device, torch.float32))
+ comfy.model_management.cast_to_device(t2, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w2_b, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w2_a, weight.device, intermediate_dtype))
else:
- w2 = comfy.model_management.cast_to_device(w2, weight.device, torch.float32)
+ w2 = comfy.model_management.cast_to_device(w2, weight.device, intermediate_dtype)
if len(w2.shape) == 4:
w1 = w1.unsqueeze(2).unsqueeze(2)
@@ -456,7 +525,7 @@ class ModelPatcher:
try:
lora_diff = torch.kron(w1, w2).reshape(weight.shape)
if dora_scale is not None:
- weight = function(weight_decompose(dora_scale, weight, lora_diff, alpha, strength))
+ weight = function(weight_decompose(dora_scale, weight, lora_diff, alpha, strength, intermediate_dtype))
else:
weight += function(((strength * alpha) * lora_diff).type(weight.dtype))
except Exception as e:
@@ -476,24 +545,24 @@ class ModelPatcher:
t1 = v[5]
t2 = v[6]
m1 = torch.einsum('i j k l, j r, i p -> p r k l',
- comfy.model_management.cast_to_device(t1, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w1b, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w1a, weight.device, torch.float32))
+ comfy.model_management.cast_to_device(t1, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w1b, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w1a, weight.device, intermediate_dtype))
m2 = torch.einsum('i j k l, j r, i p -> p r k l',
- comfy.model_management.cast_to_device(t2, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w2b, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w2a, weight.device, torch.float32))
+ comfy.model_management.cast_to_device(t2, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w2b, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w2a, weight.device, intermediate_dtype))
else:
- m1 = torch.mm(comfy.model_management.cast_to_device(w1a, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w1b, weight.device, torch.float32))
- m2 = torch.mm(comfy.model_management.cast_to_device(w2a, weight.device, torch.float32),
- comfy.model_management.cast_to_device(w2b, weight.device, torch.float32))
+ m1 = torch.mm(comfy.model_management.cast_to_device(w1a, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w1b, weight.device, intermediate_dtype))
+ m2 = torch.mm(comfy.model_management.cast_to_device(w2a, weight.device, intermediate_dtype),
+ comfy.model_management.cast_to_device(w2b, weight.device, intermediate_dtype))
try:
lora_diff = (m1 * m2).reshape(weight.shape)
if dora_scale is not None:
- weight = function(weight_decompose(dora_scale, weight, lora_diff, alpha, strength))
+ weight = function(weight_decompose(dora_scale, weight, lora_diff, alpha, strength, intermediate_dtype))
else:
weight += function(((strength * alpha) * lora_diff).type(weight.dtype))
except Exception as e:
@@ -506,15 +575,15 @@ class ModelPatcher:
dora_scale = v[5]
- a1 = comfy.model_management.cast_to_device(v[0].flatten(start_dim=1), weight.device, torch.float32)
- a2 = comfy.model_management.cast_to_device(v[1].flatten(start_dim=1), weight.device, torch.float32)
- b1 = comfy.model_management.cast_to_device(v[2].flatten(start_dim=1), weight.device, torch.float32)
- b2 = comfy.model_management.cast_to_device(v[3].flatten(start_dim=1), weight.device, torch.float32)
+ a1 = comfy.model_management.cast_to_device(v[0].flatten(start_dim=1), weight.device, intermediate_dtype)
+ a2 = comfy.model_management.cast_to_device(v[1].flatten(start_dim=1), weight.device, intermediate_dtype)
+ b1 = comfy.model_management.cast_to_device(v[2].flatten(start_dim=1), weight.device, intermediate_dtype)
+ b2 = comfy.model_management.cast_to_device(v[3].flatten(start_dim=1), weight.device, intermediate_dtype)
try:
lora_diff = (torch.mm(b2, b1) + torch.mm(torch.mm(weight.flatten(start_dim=1), a2), a1)).reshape(weight.shape)
if dora_scale is not None:
- weight = function(weight_decompose(dora_scale, weight, lora_diff, alpha, strength))
+ weight = function(weight_decompose(dora_scale, weight, lora_diff, alpha, strength, intermediate_dtype))
else:
weight += function(((strength * alpha) * lora_diff).type(weight.dtype))
except Exception as e:
@@ -529,31 +598,32 @@ class ModelPatcher:
def unpatch_model(self, device_to=None, unpatch_weights=True):
if unpatch_weights:
- if self.model_lowvram:
+ if self.model.model_lowvram:
for m in self.model.modules():
- if hasattr(m, "prev_comfy_cast_weights"):
- m.comfy_cast_weights = m.prev_comfy_cast_weights
- del m.prev_comfy_cast_weights
- m.weight_function = None
- m.bias_function = None
+ wipe_lowvram_weight(m)
- self.model_lowvram = False
- self.lowvram_patch_counter = 0
+ self.model.model_lowvram = False
+ self.model.lowvram_patch_counter = 0
keys = list(self.backup.keys())
- if self.weight_inplace_update:
- for k in keys:
- comfy.utils.copy_to_param(self.model, k, self.backup[k])
- else:
- for k in keys:
- comfy.utils.set_attr_param(self.model, k, self.backup[k])
+ for k in keys:
+ bk = self.backup[k]
+ if bk.inplace_update:
+ comfy.utils.copy_to_param(self.model, k, bk.weight)
+ else:
+ comfy.utils.set_attr_param(self.model, k, bk.weight)
self.backup.clear()
if device_to is not None:
self.model.to(device_to)
self.model.device = device_to
+ self.model.model_loaded_weight_memory = 0
+
+ for m in self.model.modules():
+ if hasattr(m, "comfy_patched_weights"):
+ del m.comfy_patched_weights
keys = list(self.object_patches_backup.keys())
for k in keys:
@@ -561,5 +631,67 @@ class ModelPatcher:
self.object_patches_backup.clear()
+ def partially_unload(self, device_to, memory_to_free=0):
+ memory_freed = 0
+ patch_counter = 0
+ unload_list = []
+
+ for n, m in self.model.named_modules():
+ shift_lowvram = False
+ if hasattr(m, "comfy_cast_weights"):
+ module_mem = comfy.model_management.module_size(m)
+ unload_list.append((module_mem, n, m))
+
+ unload_list.sort()
+ for unload in unload_list:
+ if memory_to_free < memory_freed:
+ break
+ module_mem = unload[0]
+ n = unload[1]
+ m = unload[2]
+ weight_key = "{}.weight".format(n)
+ bias_key = "{}.bias".format(n)
+
+ if hasattr(m, "comfy_patched_weights") and m.comfy_patched_weights == True:
+ for key in [weight_key, bias_key]:
+ bk = self.backup.get(key, None)
+ if bk is not None:
+ if bk.inplace_update:
+ comfy.utils.copy_to_param(self.model, key, bk.weight)
+ else:
+ comfy.utils.set_attr_param(self.model, key, bk.weight)
+ self.backup.pop(key)
+
+ m.to(device_to)
+ if weight_key in self.patches:
+ m.weight_function = LowVramPatch(weight_key, self)
+ patch_counter += 1
+ if bias_key in self.patches:
+ m.bias_function = LowVramPatch(bias_key, self)
+ patch_counter += 1
+
+ m.prev_comfy_cast_weights = m.comfy_cast_weights
+ m.comfy_cast_weights = True
+ m.comfy_patched_weights = False
+ memory_freed += module_mem
+ logging.debug("freed {}".format(n))
+
+ self.model.model_lowvram = True
+ self.model.lowvram_patch_counter += patch_counter
+ self.model.model_loaded_weight_memory -= memory_freed
+ return memory_freed
+
+ def partially_load(self, device_to, extra_memory=0):
+ self.unpatch_model(unpatch_weights=False)
+ self.patch_model(load_weights=False)
+ full_load = False
+ if self.model.model_lowvram == False:
+ return 0
+ if self.model.model_loaded_weight_memory + extra_memory > self.model_size():
+ full_load = True
+ current_used = self.model.model_loaded_weight_memory
+ self.load(device_to, lowvram_model_memory=current_used + extra_memory, full_load=full_load)
+ return self.model.model_loaded_weight_memory - current_used
+
def current_loaded_device(self):
return self.model.device
diff --git a/comfy/ops.py b/comfy/ops.py
index 47e8d7a9d..ce132911c 100644
--- a/comfy/ops.py
+++ b/comfy/ops.py
@@ -21,7 +21,9 @@ import comfy.model_management
def cast_to(weight, dtype=None, device=None, non_blocking=False):
- return weight.to(device=device, dtype=dtype, non_blocking=non_blocking)
+ r = torch.empty_like(weight, dtype=dtype, device=device)
+ r.copy_(weight, non_blocking=non_blocking)
+ return r
def cast_to_input(weight, input, non_blocking=False):
return cast_to(weight, input.dtype, input.device, non_blocking=non_blocking)
@@ -34,7 +36,7 @@ def cast_bias_weight(s, input=None, dtype=None, device=None):
device = input.device
bias = None
- non_blocking = comfy.model_management.device_should_use_non_blocking(device)
+ non_blocking = comfy.model_management.device_supports_non_blocking(device)
if s.bias is not None:
bias = cast_to(s.bias, dtype, device, non_blocking=non_blocking)
if s.bias_function is not None:
diff --git a/comfy/sd.py b/comfy/sd.py
index 3dab67028..30bf037ab 100644
--- a/comfy/sd.py
+++ b/comfy/sd.py
@@ -64,7 +64,7 @@ def load_lora_for_models(model, clip, lora, strength_model, strength_clip):
class CLIP:
- def __init__(self, target=None, embedding_directory=None, no_init=False, tokenizer_data={}):
+ def __init__(self, target=None, embedding_directory=None, no_init=False, tokenizer_data={}, parameters=0, model_options={}):
if no_init:
return
params = target.params.copy()
@@ -73,20 +73,29 @@ class CLIP:
load_device = model_management.text_encoder_device()
offload_device = model_management.text_encoder_offload_device()
- params['device'] = offload_device
- dtype = model_management.text_encoder_dtype(load_device)
+ dtype = model_options.get("dtype", None)
+ if dtype is None:
+ dtype = model_management.text_encoder_dtype(load_device)
+
params['dtype'] = dtype
+ params['device'] = model_management.text_encoder_initial_device(load_device, offload_device, parameters * model_management.dtype_size(dtype))
+ params['model_options'] = model_options
self.cond_stage_model = clip(**(params))
for dt in self.cond_stage_model.dtypes:
if not model_management.supports_cast(load_device, dt):
load_device = offload_device
+ if params['device'] != offload_device:
+ self.cond_stage_model.to(offload_device)
+ logging.warning("Had to shift TE back.")
self.tokenizer = tokenizer(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data)
self.patcher = comfy.model_patcher.ModelPatcher(self.cond_stage_model, load_device=load_device, offload_device=offload_device)
+ if params['device'] == load_device:
+ model_management.load_models_gpu([self.patcher], force_full_load=True)
self.layer_idx = None
- logging.debug("CLIP model load device: {}, offload device: {}".format(load_device, offload_device))
+ logging.debug("CLIP model load device: {}, offload device: {}, current: {}".format(load_device, offload_device, params['device']))
def clone(self):
n = CLIP(no_init=True)
@@ -392,11 +401,14 @@ class CLIPType(Enum):
HUNYUAN_DIT = 5
FLUX = 6
-def load_clip(ckpt_paths, embedding_directory=None, clip_type=CLIPType.STABLE_DIFFUSION):
+def load_clip(ckpt_paths, embedding_directory=None, clip_type=CLIPType.STABLE_DIFFUSION, model_options={}):
clip_data = []
for p in ckpt_paths:
clip_data.append(comfy.utils.load_torch_file(p, safe_load=True))
+ return load_text_encoder_state_dicts(clip_data, embedding_directory=embedding_directory, clip_type=clip_type, model_options=model_options)
+def load_text_encoder_state_dicts(state_dicts=[], embedding_directory=None, clip_type=CLIPType.STABLE_DIFFUSION, model_options={}):
+ clip_data = state_dicts
class EmptyClass:
pass
@@ -458,7 +470,11 @@ def load_clip(ckpt_paths, embedding_directory=None, clip_type=CLIPType.STABLE_DI
clip_target.clip = comfy.text_encoders.sd3_clip.SD3ClipModel
clip_target.tokenizer = comfy.text_encoders.sd3_clip.SD3Tokenizer
- clip = CLIP(clip_target, embedding_directory=embedding_directory)
+ parameters = 0
+ for c in clip_data:
+ parameters += comfy.utils.calculate_parameters(c)
+
+ clip = CLIP(clip_target, embedding_directory=embedding_directory, parameters=parameters, model_options=model_options)
for c in clip_data:
m, u = clip.load_sd(c)
if len(m) > 0:
@@ -500,15 +516,19 @@ def load_checkpoint(config_path=None, ckpt_path=None, output_vae=True, output_cl
return (model, clip, vae)
-def load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, output_clipvision=False, embedding_directory=None, output_model=True):
+def load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, output_clipvision=False, embedding_directory=None, output_model=True, model_options={}, te_model_options={}):
sd = comfy.utils.load_torch_file(ckpt_path)
- sd_keys = sd.keys()
+ out = load_state_dict_guess_config(sd, output_vae, output_clip, output_clipvision, embedding_directory, output_model, model_options, te_model_options=te_model_options)
+ if out is None:
+ raise RuntimeError("ERROR: Could not detect model type of: {}".format(ckpt_path))
+ return out
+
+def load_state_dict_guess_config(sd, output_vae=True, output_clip=True, output_clipvision=False, embedding_directory=None, output_model=True, model_options={}, te_model_options={}):
clip = None
clipvision = None
vae = None
model = None
model_patcher = None
- clip_target = None
diffusion_model_prefix = model_detection.unet_prefix_from_state_dict(sd)
parameters = comfy.utils.calculate_parameters(sd, diffusion_model_prefix)
@@ -517,13 +537,18 @@ def load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, o
model_config = model_detection.model_config_from_unet(sd, diffusion_model_prefix)
if model_config is None:
- raise RuntimeError("ERROR: Could not detect model type of: {}".format(ckpt_path))
+ return None
unet_weight_dtype = list(model_config.supported_inference_dtypes)
if weight_dtype is not None:
unet_weight_dtype.append(weight_dtype)
- unet_dtype = model_management.unet_dtype(model_params=parameters, supported_dtypes=unet_weight_dtype)
+ model_config.custom_operations = model_options.get("custom_operations", None)
+ unet_dtype = model_options.get("weight_dtype", None)
+
+ if unet_dtype is None:
+ unet_dtype = model_management.unet_dtype(model_params=parameters, supported_dtypes=unet_weight_dtype)
+
manual_cast_dtype = model_management.unet_manual_cast(unet_dtype, load_device, model_config.supported_inference_dtypes)
model_config.set_inference_dtype(unet_dtype, manual_cast_dtype)
@@ -548,8 +573,7 @@ def load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, o
if inital_load_device != torch.device("cpu"):
logging.info("loaded straight to GPU")
- model_management.load_model_gpu(model_patcher)
-
+ model_management.load_models_gpu([model_patcher], force_full_load=True)
if output_vae:
vae = model_cache.get_item(ckpt_path, 'vae')
@@ -584,7 +608,8 @@ def load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, o
if clip_target is not None:
clip_sd = model_config.process_clip_state_dict(sd)
if len(clip_sd) > 0:
- clip = CLIP(clip_target, embedding_directory=embedding_directory, tokenizer_data=clip_sd)
+ parameters = comfy.utils.calculate_parameters(clip_sd)
+ clip = CLIP(clip_target, embedding_directory=embedding_directory, tokenizer_data=clip_sd, parameters=parameters, model_options=te_model_options)
m, u = clip.load_sd(clip_sd, full_model=True)
if len(m) > 0:
m_filter = list(filter(lambda a: ".logit_scale" not in a and ".transformer.text_projection.weight" not in a, m))
@@ -601,6 +626,7 @@ def load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, o
left_over = sd.keys()
if len(left_over) > 0:
logging.debug("left over keys: {}".format(left_over))
+
if model:
logging.debug(f"cache model of : {ckpt_path}")
model_cache.cache_model(ckpt_path, model_patcher)
@@ -614,12 +640,13 @@ def load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, o
logging.debug(f"cache clipvision of : {ckpt_path}")
model_cache.cache_clip_vision(ckpt_path, clipvision)
- model_cache.refresh_cache(clipvision)
+ model_cache.refresh_cache(ckpt_path)
return (model_patcher, clip, vae, clipvision)
-def load_unet_state_dict(sd, dtype=None): #load unet in diffusers or regular format
+def load_diffusion_model_state_dict(sd, model_options={}): #load unet in diffusers or regular format
+ dtype = model_options.get("dtype", None)
#Allow loading unets from checkpoint files
diffusion_model_prefix = model_detection.unet_prefix_from_state_dict(sd)
@@ -661,6 +688,7 @@ def load_unet_state_dict(sd, dtype=None): #load unet in diffusers or regular for
manual_cast_dtype = model_management.unet_manual_cast(unet_dtype, load_device, model_config.supported_inference_dtypes)
model_config.set_inference_dtype(unet_dtype, manual_cast_dtype)
+ model_config.custom_operations = model_options.get("custom_operations", None)
model = model_config.get_model(new_sd, "")
model = model.to(offload_device)
model.load_model_weights(new_sd, "")
@@ -669,24 +697,36 @@ def load_unet_state_dict(sd, dtype=None): #load unet in diffusers or regular for
logging.info("left over keys in unet: {}".format(left_over))
return comfy.model_patcher.ModelPatcher(model, load_device=load_device, offload_device=offload_device)
-def load_unet(unet_path, dtype=None):
+
+def load_diffusion_model(unet_path, model_options={}):
sd = comfy.utils.load_torch_file(unet_path)
- model = load_unet_state_dict(sd, dtype=dtype)
+ model = load_diffusion_model_state_dict(sd, model_options=model_options)
if model is None:
logging.error("ERROR UNSUPPORTED UNET {}".format(unet_path))
raise RuntimeError("ERROR: Could not detect model type of: {}".format(unet_path))
return model
+def load_unet(unet_path, dtype=None):
+ print("WARNING: the load_unet function has been deprecated and will be removed please switch to: load_diffusion_model")
+ return load_diffusion_model(unet_path, model_options={"dtype": dtype})
+
+def load_unet_state_dict(sd, dtype=None):
+ print("WARNING: the load_unet_state_dict function has been deprecated and will be removed please switch to: load_diffusion_model_state_dict")
+ return load_diffusion_model_state_dict(sd, model_options={"dtype": dtype})
+
def save_checkpoint(output_path, model, clip=None, vae=None, clip_vision=None, metadata=None, extra_keys={}):
clip_sd = None
load_models = [model]
if clip is not None:
load_models.append(clip.load_model())
clip_sd = clip.get_sd()
+ vae_sd = None
+ if vae is not None:
+ vae_sd = vae.get_sd()
model_management.load_models_gpu(load_models, force_patch_weights=True)
clip_vision_sd = clip_vision.get_sd() if clip_vision is not None else None
- sd = model.model.state_dict_for_saving(clip_sd, vae.get_sd(), clip_vision_sd)
+ sd = model.model.state_dict_for_saving(clip_sd, vae_sd, clip_vision_sd)
for k in extra_keys:
sd[k] = extra_keys[k]
diff --git a/comfy/sd1_clip.py b/comfy/sd1_clip.py
index d32121d1b..dc8413b7b 100644
--- a/comfy/sd1_clip.py
+++ b/comfy/sd1_clip.py
@@ -84,7 +84,7 @@ class SDClipModel(torch.nn.Module, ClipTokenWeightEncoder):
def __init__(self, version="openai/clip-vit-large-patch14", device="cpu", max_length=77,
freeze=True, layer="last", layer_idx=None, textmodel_json_config=None, dtype=None, model_class=comfy.clip_model.CLIPTextModel,
special_tokens={"start": 49406, "end": 49407, "pad": 49407}, layer_norm_hidden_state=True, enable_attention_masks=False, zero_out_masked=False,
- return_projected_pooled=True, return_attention_masks=False): # clip-vit-base-patch32
+ return_projected_pooled=True, return_attention_masks=False, model_options={}): # clip-vit-base-patch32
super().__init__()
assert layer in self.LAYERS
@@ -94,7 +94,11 @@ class SDClipModel(torch.nn.Module, ClipTokenWeightEncoder):
with open(textmodel_json_config) as f:
config = json.load(f)
- self.operations = comfy.ops.manual_cast
+ operations = model_options.get("custom_operations", None)
+ if operations is None:
+ operations = comfy.ops.manual_cast
+
+ self.operations = operations
self.transformer = model_class(config, dtype, device, self.operations)
self.num_layers = self.transformer.num_layers
@@ -313,6 +317,17 @@ def expand_directory_list(directories):
dirs.add(root)
return list(dirs)
+def bundled_embed(embed, prefix, suffix): #bundled embedding in lora format
+ i = 0
+ out_list = []
+ for k in embed:
+ if k.startswith(prefix) and k.endswith(suffix):
+ out_list.append(embed[k])
+ if len(out_list) == 0:
+ return None
+
+ return torch.cat(out_list, dim=0)
+
def load_embed(embedding_name, embedding_directory, embedding_size, embed_key=None):
if isinstance(embedding_directory, str):
embedding_directory = [embedding_directory]
@@ -379,8 +394,12 @@ def load_embed(embedding_name, embedding_directory, embedding_size, embed_key=No
elif embed_key is not None and embed_key in embed:
embed_out = embed[embed_key]
else:
- values = embed.values()
- embed_out = next(iter(values))
+ embed_out = bundled_embed(embed, 'bundle_emb.', '.string_to_param.*')
+ if embed_out is None:
+ embed_out = bundled_embed(embed, 'bundle_emb.', '.{}'.format(embed_key))
+ if embed_out is None:
+ values = embed.values()
+ embed_out = next(iter(values))
return embed_out
class SDTokenizer:
@@ -538,7 +557,7 @@ class SD1Tokenizer:
return {}
class SD1ClipModel(torch.nn.Module):
- def __init__(self, device="cpu", dtype=None, clip_name="l", clip_model=SDClipModel, name=None, **kwargs):
+ def __init__(self, device="cpu", dtype=None, model_options={}, clip_name="l", clip_model=SDClipModel, name=None, **kwargs):
super().__init__()
if name is not None:
@@ -548,7 +567,7 @@ class SD1ClipModel(torch.nn.Module):
self.clip_name = clip_name
self.clip = "clip_{}".format(self.clip_name)
- setattr(self, self.clip, clip_model(device=device, dtype=dtype, **kwargs))
+ setattr(self, self.clip, clip_model(device=device, dtype=dtype, model_options=model_options, **kwargs))
self.dtypes = set()
if dtype is not None:
diff --git a/comfy/sdxl_clip.py b/comfy/sdxl_clip.py
index 6e6b87d62..860900ccd 100644
--- a/comfy/sdxl_clip.py
+++ b/comfy/sdxl_clip.py
@@ -3,14 +3,14 @@ import torch
import os
class SDXLClipG(sd1_clip.SDClipModel):
- def __init__(self, device="cpu", max_length=77, freeze=True, layer="penultimate", layer_idx=None, dtype=None):
+ def __init__(self, device="cpu", max_length=77, freeze=True, layer="penultimate", layer_idx=None, dtype=None, model_options={}):
if layer == "penultimate":
layer="hidden"
layer_idx=-2
textmodel_json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_config_bigg.json")
super().__init__(device=device, freeze=freeze, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype,
- special_tokens={"start": 49406, "end": 49407, "pad": 0}, layer_norm_hidden_state=False)
+ special_tokens={"start": 49406, "end": 49407, "pad": 0}, layer_norm_hidden_state=False, model_options=model_options)
def load_sd(self, sd):
return super().load_sd(sd)
@@ -38,10 +38,10 @@ class SDXLTokenizer:
return {}
class SDXLClipModel(torch.nn.Module):
- def __init__(self, device="cpu", dtype=None):
+ def __init__(self, device="cpu", dtype=None, model_options={}):
super().__init__()
- self.clip_l = sd1_clip.SDClipModel(layer="hidden", layer_idx=-2, device=device, dtype=dtype, layer_norm_hidden_state=False)
- self.clip_g = SDXLClipG(device=device, dtype=dtype)
+ self.clip_l = sd1_clip.SDClipModel(layer="hidden", layer_idx=-2, device=device, dtype=dtype, layer_norm_hidden_state=False, model_options=model_options)
+ self.clip_g = SDXLClipG(device=device, dtype=dtype, model_options=model_options)
self.dtypes = set([dtype])
def set_clip_options(self, options):
@@ -66,8 +66,8 @@ class SDXLClipModel(torch.nn.Module):
return self.clip_l.load_sd(sd)
class SDXLRefinerClipModel(sd1_clip.SD1ClipModel):
- def __init__(self, device="cpu", dtype=None):
- super().__init__(device=device, dtype=dtype, clip_name="g", clip_model=SDXLClipG)
+ def __init__(self, device="cpu", dtype=None, model_options={}):
+ super().__init__(device=device, dtype=dtype, clip_name="g", clip_model=SDXLClipG, model_options=model_options)
class StableCascadeClipGTokenizer(sd1_clip.SDTokenizer):
@@ -79,14 +79,14 @@ class StableCascadeTokenizer(sd1_clip.SD1Tokenizer):
super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, clip_name="g", tokenizer=StableCascadeClipGTokenizer)
class StableCascadeClipG(sd1_clip.SDClipModel):
- def __init__(self, device="cpu", max_length=77, freeze=True, layer="hidden", layer_idx=-1, dtype=None):
+ def __init__(self, device="cpu", max_length=77, freeze=True, layer="hidden", layer_idx=-1, dtype=None, model_options={}):
textmodel_json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_config_bigg.json")
super().__init__(device=device, freeze=freeze, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype,
- special_tokens={"start": 49406, "end": 49407, "pad": 49407}, layer_norm_hidden_state=False, enable_attention_masks=True)
+ special_tokens={"start": 49406, "end": 49407, "pad": 49407}, layer_norm_hidden_state=False, enable_attention_masks=True, model_options=model_options)
def load_sd(self, sd):
return super().load_sd(sd)
class StableCascadeClipModel(sd1_clip.SD1ClipModel):
- def __init__(self, device="cpu", dtype=None):
- super().__init__(device=device, dtype=dtype, clip_name="g", clip_model=StableCascadeClipG)
+ def __init__(self, device="cpu", dtype=None, model_options={}):
+ super().__init__(device=device, dtype=dtype, clip_name="g", clip_model=StableCascadeClipG, model_options=model_options)
diff --git a/comfy/supported_models.py b/comfy/supported_models.py
index 6cecb9a02..98aa8056b 100644
--- a/comfy/supported_models.py
+++ b/comfy/supported_models.py
@@ -181,7 +181,7 @@ class SDXL(supported_models_base.BASE):
latent_format = latent_formats.SDXL
- memory_usage_factor = 0.7
+ memory_usage_factor = 0.8
def model_type(self, state_dict, prefix=""):
if 'edm_mean' in state_dict and 'edm_std' in state_dict: #Playground V2.5
@@ -642,7 +642,7 @@ class Flux(supported_models_base.BASE):
memory_usage_factor = 2.8
- supported_inference_dtypes = [torch.bfloat16, torch.float32]
+ supported_inference_dtypes = [torch.bfloat16, torch.float16, torch.float32]
vae_key_prefix = ["vae."]
text_encoder_key_prefix = ["text_encoders."]
diff --git a/comfy/supported_models_base.py b/comfy/supported_models_base.py
index bc0a7e311..7a2152f91 100644
--- a/comfy/supported_models_base.py
+++ b/comfy/supported_models_base.py
@@ -1,3 +1,21 @@
+"""
+ This file is part of ComfyUI.
+ Copyright (C) 2024 Comfy
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
import torch
from . import model_base
from . import utils
@@ -30,6 +48,7 @@ class BASE:
memory_usage_factor = 2.0
manual_cast_dtype = None
+ custom_operations = None
@classmethod
def matches(s, unet_config, state_dict=None):
diff --git a/comfy/text_encoders/aura_t5.py b/comfy/text_encoders/aura_t5.py
index 8500c7b70..e9ad45a7f 100644
--- a/comfy/text_encoders/aura_t5.py
+++ b/comfy/text_encoders/aura_t5.py
@@ -4,9 +4,9 @@ import comfy.text_encoders.t5
import os
class PT5XlModel(sd1_clip.SDClipModel):
- def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None):
+ def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None, model_options={}):
textmodel_json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "t5_pile_config_xl.json")
- super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"end": 2, "pad": 1}, model_class=comfy.text_encoders.t5.T5, enable_attention_masks=True, zero_out_masked=True)
+ super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"end": 2, "pad": 1}, model_class=comfy.text_encoders.t5.T5, enable_attention_masks=True, zero_out_masked=True, model_options=model_options)
class PT5XlTokenizer(sd1_clip.SDTokenizer):
def __init__(self, embedding_directory=None, tokenizer_data={}):
@@ -18,5 +18,5 @@ class AuraT5Tokenizer(sd1_clip.SD1Tokenizer):
super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, clip_name="pile_t5xl", tokenizer=PT5XlTokenizer)
class AuraT5Model(sd1_clip.SD1ClipModel):
- def __init__(self, device="cpu", dtype=None, **kwargs):
- super().__init__(device=device, dtype=dtype, name="pile_t5xl", clip_model=PT5XlModel, **kwargs)
+ def __init__(self, device="cpu", dtype=None, model_options={}, **kwargs):
+ super().__init__(device=device, dtype=dtype, model_options=model_options, name="pile_t5xl", clip_model=PT5XlModel, **kwargs)
diff --git a/comfy/text_encoders/flux.py b/comfy/text_encoders/flux.py
index ee26f560d..91d1f249d 100644
--- a/comfy/text_encoders/flux.py
+++ b/comfy/text_encoders/flux.py
@@ -6,9 +6,9 @@ import torch
import os
class T5XXLModel(sd1_clip.SDClipModel):
- def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None):
+ def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None, model_options={}):
textmodel_json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "t5_config_xxl.json")
- super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"end": 1, "pad": 0}, model_class=comfy.text_encoders.t5.T5)
+ super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"end": 1, "pad": 0}, model_class=comfy.text_encoders.t5.T5, model_options=model_options)
class T5XXLTokenizer(sd1_clip.SDTokenizer):
def __init__(self, embedding_directory=None, tokenizer_data={}):
@@ -35,11 +35,11 @@ class FluxTokenizer:
class FluxClipModel(torch.nn.Module):
- def __init__(self, dtype_t5=None, device="cpu", dtype=None):
+ def __init__(self, dtype_t5=None, device="cpu", dtype=None, model_options={}):
super().__init__()
dtype_t5 = comfy.model_management.pick_weight_dtype(dtype_t5, dtype, device)
- self.clip_l = sd1_clip.SDClipModel(device=device, dtype=dtype, return_projected_pooled=False)
- self.t5xxl = T5XXLModel(device=device, dtype=dtype_t5)
+ self.clip_l = sd1_clip.SDClipModel(device=device, dtype=dtype, return_projected_pooled=False, model_options=model_options)
+ self.t5xxl = T5XXLModel(device=device, dtype=dtype_t5, model_options=model_options)
self.dtypes = set([dtype, dtype_t5])
def set_clip_options(self, options):
@@ -66,6 +66,6 @@ class FluxClipModel(torch.nn.Module):
def flux_clip(dtype_t5=None):
class FluxClipModel_(FluxClipModel):
- def __init__(self, device="cpu", dtype=None):
- super().__init__(dtype_t5=dtype_t5, device=device, dtype=dtype)
+ def __init__(self, device="cpu", dtype=None, model_options={}):
+ super().__init__(dtype_t5=dtype_t5, device=device, dtype=dtype, model_options=model_options)
return FluxClipModel_
diff --git a/comfy/text_encoders/hydit.py b/comfy/text_encoders/hydit.py
index 9dfa288b1..7cb790f45 100644
--- a/comfy/text_encoders/hydit.py
+++ b/comfy/text_encoders/hydit.py
@@ -7,9 +7,9 @@ import os
import torch
class HyditBertModel(sd1_clip.SDClipModel):
- def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None):
+ def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None, model_options={}):
textmodel_json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "hydit_clip.json")
- super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"start": 101, "end": 102, "pad": 0}, model_class=BertModel, enable_attention_masks=True, return_attention_masks=True)
+ super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"start": 101, "end": 102, "pad": 0}, model_class=BertModel, enable_attention_masks=True, return_attention_masks=True, model_options=model_options)
class HyditBertTokenizer(sd1_clip.SDTokenizer):
def __init__(self, embedding_directory=None, tokenizer_data={}):
@@ -18,9 +18,9 @@ class HyditBertTokenizer(sd1_clip.SDTokenizer):
class MT5XLModel(sd1_clip.SDClipModel):
- def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None):
+ def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None, model_options={}):
textmodel_json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "mt5_config_xl.json")
- super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"end": 1, "pad": 0}, model_class=comfy.text_encoders.t5.T5, enable_attention_masks=True, return_attention_masks=True)
+ super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"end": 1, "pad": 0}, model_class=comfy.text_encoders.t5.T5, enable_attention_masks=True, return_attention_masks=True, model_options=model_options)
class MT5XLTokenizer(sd1_clip.SDTokenizer):
def __init__(self, embedding_directory=None, tokenizer_data={}):
@@ -50,10 +50,10 @@ class HyditTokenizer:
return {"mt5xl.spiece_model": self.mt5xl.state_dict()["spiece_model"]}
class HyditModel(torch.nn.Module):
- def __init__(self, device="cpu", dtype=None):
+ def __init__(self, device="cpu", dtype=None, model_options={}):
super().__init__()
- self.hydit_clip = HyditBertModel(dtype=dtype)
- self.mt5xl = MT5XLModel(dtype=dtype)
+ self.hydit_clip = HyditBertModel(dtype=dtype, model_options=model_options)
+ self.mt5xl = MT5XLModel(dtype=dtype, model_options=model_options)
self.dtypes = set()
if dtype is not None:
diff --git a/comfy/text_encoders/sa_t5.py b/comfy/text_encoders/sa_t5.py
index 189f8c181..7778ce47a 100644
--- a/comfy/text_encoders/sa_t5.py
+++ b/comfy/text_encoders/sa_t5.py
@@ -4,9 +4,9 @@ import comfy.text_encoders.t5
import os
class T5BaseModel(sd1_clip.SDClipModel):
- def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None):
+ def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None, model_options={}):
textmodel_json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "t5_config_base.json")
- super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"end": 1, "pad": 0}, model_class=comfy.text_encoders.t5.T5, enable_attention_masks=True, zero_out_masked=True)
+ super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, model_options=model_options, special_tokens={"end": 1, "pad": 0}, model_class=comfy.text_encoders.t5.T5, enable_attention_masks=True, zero_out_masked=True)
class T5BaseTokenizer(sd1_clip.SDTokenizer):
def __init__(self, embedding_directory=None, tokenizer_data={}):
@@ -18,5 +18,5 @@ class SAT5Tokenizer(sd1_clip.SD1Tokenizer):
super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, clip_name="t5base", tokenizer=T5BaseTokenizer)
class SAT5Model(sd1_clip.SD1ClipModel):
- def __init__(self, device="cpu", dtype=None, **kwargs):
- super().__init__(device=device, dtype=dtype, name="t5base", clip_model=T5BaseModel, **kwargs)
+ def __init__(self, device="cpu", dtype=None, model_options={}, **kwargs):
+ super().__init__(device=device, dtype=dtype, model_options=model_options, name="t5base", clip_model=T5BaseModel, **kwargs)
diff --git a/comfy/text_encoders/sd2_clip.py b/comfy/text_encoders/sd2_clip.py
index 8ea8e1cd0..0c98cd853 100644
--- a/comfy/text_encoders/sd2_clip.py
+++ b/comfy/text_encoders/sd2_clip.py
@@ -2,13 +2,13 @@ from comfy import sd1_clip
import os
class SD2ClipHModel(sd1_clip.SDClipModel):
- def __init__(self, arch="ViT-H-14", device="cpu", max_length=77, freeze=True, layer="penultimate", layer_idx=None, dtype=None):
+ def __init__(self, arch="ViT-H-14", device="cpu", max_length=77, freeze=True, layer="penultimate", layer_idx=None, dtype=None, model_options={}):
if layer == "penultimate":
layer="hidden"
layer_idx=-2
textmodel_json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "sd2_clip_config.json")
- super().__init__(device=device, freeze=freeze, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"start": 49406, "end": 49407, "pad": 0})
+ super().__init__(device=device, freeze=freeze, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"start": 49406, "end": 49407, "pad": 0}, model_options=model_options)
class SD2ClipHTokenizer(sd1_clip.SDTokenizer):
def __init__(self, tokenizer_path=None, embedding_directory=None, tokenizer_data={}):
@@ -19,5 +19,5 @@ class SD2Tokenizer(sd1_clip.SD1Tokenizer):
super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, clip_name="h", tokenizer=SD2ClipHTokenizer)
class SD2ClipModel(sd1_clip.SD1ClipModel):
- def __init__(self, device="cpu", dtype=None, **kwargs):
- super().__init__(device=device, dtype=dtype, clip_name="h", clip_model=SD2ClipHModel, **kwargs)
+ def __init__(self, device="cpu", dtype=None, model_options={}, **kwargs):
+ super().__init__(device=device, dtype=dtype, model_options=model_options, clip_name="h", clip_model=SD2ClipHModel, **kwargs)
diff --git a/comfy/text_encoders/sd3_clip.py b/comfy/text_encoders/sd3_clip.py
index 549c068e9..83e8fa1f3 100644
--- a/comfy/text_encoders/sd3_clip.py
+++ b/comfy/text_encoders/sd3_clip.py
@@ -8,9 +8,9 @@ import comfy.model_management
import logging
class T5XXLModel(sd1_clip.SDClipModel):
- def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None):
+ def __init__(self, device="cpu", layer="last", layer_idx=None, dtype=None, model_options={}):
textmodel_json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "t5_config_xxl.json")
- super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"end": 1, "pad": 0}, model_class=comfy.text_encoders.t5.T5)
+ super().__init__(device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config=textmodel_json_config, dtype=dtype, special_tokens={"end": 1, "pad": 0}, model_class=comfy.text_encoders.t5.T5, model_options=model_options)
class T5XXLTokenizer(sd1_clip.SDTokenizer):
def __init__(self, embedding_directory=None, tokenizer_data={}):
@@ -38,24 +38,24 @@ class SD3Tokenizer:
return {}
class SD3ClipModel(torch.nn.Module):
- def __init__(self, clip_l=True, clip_g=True, t5=True, dtype_t5=None, device="cpu", dtype=None):
+ def __init__(self, clip_l=True, clip_g=True, t5=True, dtype_t5=None, device="cpu", dtype=None, model_options={}):
super().__init__()
self.dtypes = set()
if clip_l:
- self.clip_l = sd1_clip.SDClipModel(layer="hidden", layer_idx=-2, device=device, dtype=dtype, layer_norm_hidden_state=False, return_projected_pooled=False)
+ self.clip_l = sd1_clip.SDClipModel(layer="hidden", layer_idx=-2, device=device, dtype=dtype, layer_norm_hidden_state=False, return_projected_pooled=False, model_options=model_options)
self.dtypes.add(dtype)
else:
self.clip_l = None
if clip_g:
- self.clip_g = sdxl_clip.SDXLClipG(device=device, dtype=dtype)
+ self.clip_g = sdxl_clip.SDXLClipG(device=device, dtype=dtype, model_options=model_options)
self.dtypes.add(dtype)
else:
self.clip_g = None
if t5:
dtype_t5 = comfy.model_management.pick_weight_dtype(dtype_t5, dtype, device)
- self.t5xxl = T5XXLModel(device=device, dtype=dtype_t5)
+ self.t5xxl = T5XXLModel(device=device, dtype=dtype_t5, model_options=model_options)
self.dtypes.add(dtype_t5)
else:
self.t5xxl = None
@@ -132,6 +132,6 @@ class SD3ClipModel(torch.nn.Module):
def sd3_clip(clip_l=True, clip_g=True, t5=True, dtype_t5=None):
class SD3ClipModel_(SD3ClipModel):
- def __init__(self, device="cpu", dtype=None):
- super().__init__(clip_l=clip_l, clip_g=clip_g, t5=t5, dtype_t5=dtype_t5, device=device, dtype=dtype)
+ def __init__(self, device="cpu", dtype=None, model_options={}):
+ super().__init__(clip_l=clip_l, clip_g=clip_g, t5=t5, dtype_t5=dtype_t5, device=device, dtype=dtype, model_options=model_options)
return SD3ClipModel_
diff --git a/comfy/utils.py b/comfy/utils.py
index 068ccee00..dd2235d8b 100644
--- a/comfy/utils.py
+++ b/comfy/utils.py
@@ -1,3 +1,22 @@
+"""
+ This file is part of ComfyUI.
+ Copyright (C) 2024 Comfy
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
+
import torch
import math
import struct
@@ -440,8 +459,33 @@ def flux_to_diffusers(mmdit_config, output_prefix=""):
key_map["{}to_k.{}".format(k, end)] = (qkv, (0, hidden_size, hidden_size))
key_map["{}to_v.{}".format(k, end)] = (qkv, (0, hidden_size * 2, hidden_size))
- block_map = {"attn.to_out.0.weight": "img_attn.proj.weight",
- "attn.to_out.0.bias": "img_attn.proj.bias",
+ k = "{}.attn.".format(prefix_from)
+ qkv = "{}.txt_attn.qkv.{}".format(prefix_to, end)
+ key_map["{}add_q_proj.{}".format(k, end)] = (qkv, (0, 0, hidden_size))
+ key_map["{}add_k_proj.{}".format(k, end)] = (qkv, (0, hidden_size, hidden_size))
+ key_map["{}add_v_proj.{}".format(k, end)] = (qkv, (0, hidden_size * 2, hidden_size))
+
+ block_map = {
+ "attn.to_out.0.weight": "img_attn.proj.weight",
+ "attn.to_out.0.bias": "img_attn.proj.bias",
+ "norm1.linear.weight": "img_mod.lin.weight",
+ "norm1.linear.bias": "img_mod.lin.bias",
+ "norm1_context.linear.weight": "txt_mod.lin.weight",
+ "norm1_context.linear.bias": "txt_mod.lin.bias",
+ "attn.to_add_out.weight": "txt_attn.proj.weight",
+ "attn.to_add_out.bias": "txt_attn.proj.bias",
+ "ff.net.0.proj.weight": "img_mlp.0.weight",
+ "ff.net.0.proj.bias": "img_mlp.0.bias",
+ "ff.net.2.weight": "img_mlp.2.weight",
+ "ff.net.2.bias": "img_mlp.2.bias",
+ "ff_context.net.0.proj.weight": "txt_mlp.0.weight",
+ "ff_context.net.0.proj.bias": "txt_mlp.0.bias",
+ "ff_context.net.2.weight": "txt_mlp.2.weight",
+ "ff_context.net.2.bias": "txt_mlp.2.bias",
+ "attn.norm_q.weight": "img_attn.norm.query_norm.scale",
+ "attn.norm_k.weight": "img_attn.norm.key_norm.scale",
+ "attn.norm_added_q.weight": "txt_attn.norm.query_norm.scale",
+ "attn.norm_added_k.weight": "txt_attn.norm.key_norm.scale",
}
for k in block_map:
@@ -457,15 +501,41 @@ def flux_to_diffusers(mmdit_config, output_prefix=""):
key_map["{}to_q.{}".format(k, end)] = (qkv, (0, 0, hidden_size))
key_map["{}to_k.{}".format(k, end)] = (qkv, (0, hidden_size, hidden_size))
key_map["{}to_v.{}".format(k, end)] = (qkv, (0, hidden_size * 2, hidden_size))
- key_map["{}proj_mlp.{}".format(k, end)] = (qkv, (0, hidden_size * 3, hidden_size))
+ key_map["{}.proj_mlp.{}".format(prefix_from, end)] = (qkv, (0, hidden_size * 3, hidden_size * 4))
- block_map = {#TODO
+ block_map = {
+ "norm.linear.weight": "modulation.lin.weight",
+ "norm.linear.bias": "modulation.lin.bias",
+ "proj_out.weight": "linear2.weight",
+ "proj_out.bias": "linear2.bias",
+ "attn.norm_q.weight": "norm.query_norm.scale",
+ "attn.norm_k.weight": "norm.key_norm.scale",
}
for k in block_map:
key_map["{}.{}".format(prefix_from, k)] = "{}.{}".format(prefix_to, block_map[k])
- MAP_BASIC = { #TODO
+ MAP_BASIC = {
+ ("final_layer.linear.bias", "proj_out.bias"),
+ ("final_layer.linear.weight", "proj_out.weight"),
+ ("img_in.bias", "x_embedder.bias"),
+ ("img_in.weight", "x_embedder.weight"),
+ ("time_in.in_layer.bias", "time_text_embed.timestep_embedder.linear_1.bias"),
+ ("time_in.in_layer.weight", "time_text_embed.timestep_embedder.linear_1.weight"),
+ ("time_in.out_layer.bias", "time_text_embed.timestep_embedder.linear_2.bias"),
+ ("time_in.out_layer.weight", "time_text_embed.timestep_embedder.linear_2.weight"),
+ ("txt_in.bias", "context_embedder.bias"),
+ ("txt_in.weight", "context_embedder.weight"),
+ ("vector_in.in_layer.bias", "time_text_embed.text_embedder.linear_1.bias"),
+ ("vector_in.in_layer.weight", "time_text_embed.text_embedder.linear_1.weight"),
+ ("vector_in.out_layer.bias", "time_text_embed.text_embedder.linear_2.bias"),
+ ("vector_in.out_layer.weight", "time_text_embed.text_embedder.linear_2.weight"),
+ ("guidance_in.in_layer.bias", "time_text_embed.guidance_embedder.linear_1.bias"),
+ ("guidance_in.in_layer.weight", "time_text_embed.guidance_embedder.linear_1.weight"),
+ ("guidance_in.out_layer.bias", "time_text_embed.guidance_embedder.linear_2.bias"),
+ ("guidance_in.out_layer.weight", "time_text_embed.guidance_embedder.linear_2.weight"),
+ ("final_layer.adaLN_modulation.1.bias", "norm_out.linear.bias", swap_scale_shift),
+ ("final_layer.adaLN_modulation.1.weight", "norm_out.linear.weight", swap_scale_shift),
}
for k in MAP_BASIC:
diff --git a/comfy_execution/caching.py b/comfy_execution/caching.py
new file mode 100644
index 000000000..6664a3428
--- /dev/null
+++ b/comfy_execution/caching.py
@@ -0,0 +1,299 @@
+import itertools
+from typing import Sequence, Mapping
+from comfy_execution.graph import DynamicPrompt
+
+import nodes
+
+from comfy_execution.graph_utils import is_link
+
+class CacheKeySet:
+ def __init__(self, dynprompt, node_ids, is_changed_cache):
+ self.keys = {}
+ self.subcache_keys = {}
+
+ def add_keys(self, node_ids):
+ raise NotImplementedError()
+
+ def all_node_ids(self):
+ return set(self.keys.keys())
+
+ def get_used_keys(self):
+ return self.keys.values()
+
+ def get_used_subcache_keys(self):
+ return self.subcache_keys.values()
+
+ def get_data_key(self, node_id):
+ return self.keys.get(node_id, None)
+
+ def get_subcache_key(self, node_id):
+ return self.subcache_keys.get(node_id, None)
+
+class Unhashable:
+ def __init__(self):
+ self.value = float("NaN")
+
+def to_hashable(obj):
+ # So that we don't infinitely recurse since frozenset and tuples
+ # are Sequences.
+ if isinstance(obj, (int, float, str, bool, type(None))):
+ return obj
+ elif isinstance(obj, Mapping):
+ return frozenset([(to_hashable(k), to_hashable(v)) for k, v in sorted(obj.items())])
+ elif isinstance(obj, Sequence):
+ return frozenset(zip(itertools.count(), [to_hashable(i) for i in obj]))
+ else:
+ # TODO - Support other objects like tensors?
+ return Unhashable()
+
+class CacheKeySetID(CacheKeySet):
+ def __init__(self, dynprompt, node_ids, is_changed_cache):
+ super().__init__(dynprompt, node_ids, is_changed_cache)
+ self.dynprompt = dynprompt
+ self.add_keys(node_ids)
+
+ def add_keys(self, node_ids):
+ for node_id in node_ids:
+ if node_id in self.keys:
+ continue
+ node = self.dynprompt.get_node(node_id)
+ self.keys[node_id] = (node_id, node["class_type"])
+ self.subcache_keys[node_id] = (node_id, node["class_type"])
+
+class CacheKeySetInputSignature(CacheKeySet):
+ def __init__(self, dynprompt, node_ids, is_changed_cache):
+ super().__init__(dynprompt, node_ids, is_changed_cache)
+ self.dynprompt = dynprompt
+ self.is_changed_cache = is_changed_cache
+ self.add_keys(node_ids)
+
+ def include_node_id_in_input(self) -> bool:
+ return False
+
+ def add_keys(self, node_ids):
+ for node_id in node_ids:
+ if node_id in self.keys:
+ continue
+ node = self.dynprompt.get_node(node_id)
+ self.keys[node_id] = self.get_node_signature(self.dynprompt, node_id)
+ self.subcache_keys[node_id] = (node_id, node["class_type"])
+
+ def get_node_signature(self, dynprompt, node_id):
+ signature = []
+ ancestors, order_mapping = self.get_ordered_ancestry(dynprompt, node_id)
+ signature.append(self.get_immediate_node_signature(dynprompt, node_id, order_mapping))
+ for ancestor_id in ancestors:
+ signature.append(self.get_immediate_node_signature(dynprompt, ancestor_id, order_mapping))
+ return to_hashable(signature)
+
+ def get_immediate_node_signature(self, dynprompt, node_id, ancestor_order_mapping):
+ node = dynprompt.get_node(node_id)
+ class_type = node["class_type"]
+ class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
+ signature = [class_type, self.is_changed_cache.get(node_id)]
+ if self.include_node_id_in_input() or (hasattr(class_def, "NOT_IDEMPOTENT") and class_def.NOT_IDEMPOTENT):
+ signature.append(node_id)
+ inputs = node["inputs"]
+ for key in sorted(inputs.keys()):
+ if is_link(inputs[key]):
+ (ancestor_id, ancestor_socket) = inputs[key]
+ ancestor_index = ancestor_order_mapping[ancestor_id]
+ signature.append((key,("ANCESTOR", ancestor_index, ancestor_socket)))
+ else:
+ signature.append((key, inputs[key]))
+ return signature
+
+ # This function returns a list of all ancestors of the given node. The order of the list is
+ # deterministic based on which specific inputs the ancestor is connected by.
+ def get_ordered_ancestry(self, dynprompt, node_id):
+ ancestors = []
+ order_mapping = {}
+ self.get_ordered_ancestry_internal(dynprompt, node_id, ancestors, order_mapping)
+ return ancestors, order_mapping
+
+ def get_ordered_ancestry_internal(self, dynprompt, node_id, ancestors, order_mapping):
+ inputs = dynprompt.get_node(node_id)["inputs"]
+ input_keys = sorted(inputs.keys())
+ for key in input_keys:
+ if is_link(inputs[key]):
+ ancestor_id = inputs[key][0]
+ if ancestor_id not in order_mapping:
+ ancestors.append(ancestor_id)
+ order_mapping[ancestor_id] = len(ancestors) - 1
+ self.get_ordered_ancestry_internal(dynprompt, ancestor_id, ancestors, order_mapping)
+
+class BasicCache:
+ def __init__(self, key_class):
+ self.key_class = key_class
+ self.initialized = False
+ self.dynprompt: DynamicPrompt
+ self.cache_key_set: CacheKeySet
+ self.cache = {}
+ self.subcaches = {}
+
+ def set_prompt(self, dynprompt, node_ids, is_changed_cache):
+ self.dynprompt = dynprompt
+ self.cache_key_set = self.key_class(dynprompt, node_ids, is_changed_cache)
+ self.is_changed_cache = is_changed_cache
+ self.initialized = True
+
+ def all_node_ids(self):
+ assert self.initialized
+ node_ids = self.cache_key_set.all_node_ids()
+ for subcache in self.subcaches.values():
+ node_ids = node_ids.union(subcache.all_node_ids())
+ return node_ids
+
+ def _clean_cache(self):
+ preserve_keys = set(self.cache_key_set.get_used_keys())
+ to_remove = []
+ for key in self.cache:
+ if key not in preserve_keys:
+ to_remove.append(key)
+ for key in to_remove:
+ del self.cache[key]
+
+ def _clean_subcaches(self):
+ preserve_subcaches = set(self.cache_key_set.get_used_subcache_keys())
+
+ to_remove = []
+ for key in self.subcaches:
+ if key not in preserve_subcaches:
+ to_remove.append(key)
+ for key in to_remove:
+ del self.subcaches[key]
+
+ def clean_unused(self):
+ assert self.initialized
+ self._clean_cache()
+ self._clean_subcaches()
+
+ def _set_immediate(self, node_id, value):
+ assert self.initialized
+ cache_key = self.cache_key_set.get_data_key(node_id)
+ self.cache[cache_key] = value
+
+ def _get_immediate(self, node_id):
+ if not self.initialized:
+ return None
+ cache_key = self.cache_key_set.get_data_key(node_id)
+ if cache_key in self.cache:
+ return self.cache[cache_key]
+ else:
+ return None
+
+ def _ensure_subcache(self, node_id, children_ids):
+ subcache_key = self.cache_key_set.get_subcache_key(node_id)
+ subcache = self.subcaches.get(subcache_key, None)
+ if subcache is None:
+ subcache = BasicCache(self.key_class)
+ self.subcaches[subcache_key] = subcache
+ subcache.set_prompt(self.dynprompt, children_ids, self.is_changed_cache)
+ return subcache
+
+ def _get_subcache(self, node_id):
+ assert self.initialized
+ subcache_key = self.cache_key_set.get_subcache_key(node_id)
+ if subcache_key in self.subcaches:
+ return self.subcaches[subcache_key]
+ else:
+ return None
+
+ def recursive_debug_dump(self):
+ result = []
+ for key in self.cache:
+ result.append({"key": key, "value": self.cache[key]})
+ for key in self.subcaches:
+ result.append({"subcache_key": key, "subcache": self.subcaches[key].recursive_debug_dump()})
+ return result
+
+class HierarchicalCache(BasicCache):
+ def __init__(self, key_class):
+ super().__init__(key_class)
+
+ def _get_cache_for(self, node_id):
+ assert self.dynprompt is not None
+ parent_id = self.dynprompt.get_parent_node_id(node_id)
+ if parent_id is None:
+ return self
+
+ hierarchy = []
+ while parent_id is not None:
+ hierarchy.append(parent_id)
+ parent_id = self.dynprompt.get_parent_node_id(parent_id)
+
+ cache = self
+ for parent_id in reversed(hierarchy):
+ cache = cache._get_subcache(parent_id)
+ if cache is None:
+ return None
+ return cache
+
+ def get(self, node_id):
+ cache = self._get_cache_for(node_id)
+ if cache is None:
+ return None
+ return cache._get_immediate(node_id)
+
+ def set(self, node_id, value):
+ cache = self._get_cache_for(node_id)
+ assert cache is not None
+ cache._set_immediate(node_id, value)
+
+ def ensure_subcache_for(self, node_id, children_ids):
+ cache = self._get_cache_for(node_id)
+ assert cache is not None
+ return cache._ensure_subcache(node_id, children_ids)
+
+class LRUCache(BasicCache):
+ def __init__(self, key_class, max_size=100):
+ super().__init__(key_class)
+ self.max_size = max_size
+ self.min_generation = 0
+ self.generation = 0
+ self.used_generation = {}
+ self.children = {}
+
+ def set_prompt(self, dynprompt, node_ids, is_changed_cache):
+ super().set_prompt(dynprompt, node_ids, is_changed_cache)
+ self.generation += 1
+ for node_id in node_ids:
+ self._mark_used(node_id)
+
+ def clean_unused(self):
+ while len(self.cache) > self.max_size and self.min_generation < self.generation:
+ self.min_generation += 1
+ to_remove = [key for key in self.cache if self.used_generation[key] < self.min_generation]
+ for key in to_remove:
+ del self.cache[key]
+ del self.used_generation[key]
+ if key in self.children:
+ del self.children[key]
+ self._clean_subcaches()
+
+ def get(self, node_id):
+ self._mark_used(node_id)
+ return self._get_immediate(node_id)
+
+ def _mark_used(self, node_id):
+ cache_key = self.cache_key_set.get_data_key(node_id)
+ if cache_key is not None:
+ self.used_generation[cache_key] = self.generation
+
+ def set(self, node_id, value):
+ self._mark_used(node_id)
+ return self._set_immediate(node_id, value)
+
+ def ensure_subcache_for(self, node_id, children_ids):
+ # Just uses subcaches for tracking 'live' nodes
+ super()._ensure_subcache(node_id, children_ids)
+
+ self.cache_key_set.add_keys(children_ids)
+ self._mark_used(node_id)
+ cache_key = self.cache_key_set.get_data_key(node_id)
+ self.children[cache_key] = []
+ for child_id in children_ids:
+ self._mark_used(child_id)
+ self.children[cache_key].append(self.cache_key_set.get_data_key(child_id))
+ return self
+
diff --git a/comfy_execution/graph.py b/comfy_execution/graph.py
new file mode 100644
index 000000000..b53e10f3f
--- /dev/null
+++ b/comfy_execution/graph.py
@@ -0,0 +1,259 @@
+import nodes
+
+from comfy_execution.graph_utils import is_link
+
+class DependencyCycleError(Exception):
+ pass
+
+class NodeInputError(Exception):
+ pass
+
+class NodeNotFoundError(Exception):
+ pass
+
+class DynamicPrompt:
+ def __init__(self, original_prompt):
+ # The original prompt provided by the user
+ self.original_prompt = original_prompt
+ # Any extra pieces of the graph created during execution
+ self.ephemeral_prompt = {}
+ self.ephemeral_parents = {}
+ self.ephemeral_display = {}
+
+ def get_node(self, node_id):
+ if node_id in self.ephemeral_prompt:
+ return self.ephemeral_prompt[node_id]
+ if node_id in self.original_prompt:
+ return self.original_prompt[node_id]
+ raise NodeNotFoundError(f"Node {node_id} not found")
+
+ def has_node(self, node_id):
+ return node_id in self.original_prompt or node_id in self.ephemeral_prompt
+
+ def add_ephemeral_node(self, node_id, node_info, parent_id, display_id):
+ self.ephemeral_prompt[node_id] = node_info
+ self.ephemeral_parents[node_id] = parent_id
+ self.ephemeral_display[node_id] = display_id
+
+ def get_real_node_id(self, node_id):
+ while node_id in self.ephemeral_parents:
+ node_id = self.ephemeral_parents[node_id]
+ return node_id
+
+ def get_parent_node_id(self, node_id):
+ return self.ephemeral_parents.get(node_id, None)
+
+ def get_display_node_id(self, node_id):
+ while node_id in self.ephemeral_display:
+ node_id = self.ephemeral_display[node_id]
+ return node_id
+
+ def all_node_ids(self):
+ return set(self.original_prompt.keys()).union(set(self.ephemeral_prompt.keys()))
+
+ def get_original_prompt(self):
+ return self.original_prompt
+
+def get_input_info(class_def, input_name):
+ valid_inputs = class_def.INPUT_TYPES()
+ input_info = None
+ input_category = None
+ if "required" in valid_inputs and input_name in valid_inputs["required"]:
+ input_category = "required"
+ input_info = valid_inputs["required"][input_name]
+ elif "optional" in valid_inputs and input_name in valid_inputs["optional"]:
+ input_category = "optional"
+ input_info = valid_inputs["optional"][input_name]
+ elif "hidden" in valid_inputs and input_name in valid_inputs["hidden"]:
+ input_category = "hidden"
+ input_info = valid_inputs["hidden"][input_name]
+ if input_info is None:
+ return None, None, None
+ input_type = input_info[0]
+ if len(input_info) > 1:
+ extra_info = input_info[1]
+ else:
+ extra_info = {}
+ return input_type, input_category, extra_info
+
+class TopologicalSort:
+ def __init__(self, dynprompt):
+ self.dynprompt = dynprompt
+ self.pendingNodes = {}
+ self.blockCount = {} # Number of nodes this node is directly blocked by
+ self.blocking = {} # Which nodes are blocked by this node
+
+ def get_input_info(self, unique_id, input_name):
+ class_type = self.dynprompt.get_node(unique_id)["class_type"]
+ class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
+ return get_input_info(class_def, input_name)
+
+ def make_input_strong_link(self, to_node_id, to_input):
+ inputs = self.dynprompt.get_node(to_node_id)["inputs"]
+ if to_input not in inputs:
+ raise NodeInputError(f"Node {to_node_id} says it needs input {to_input}, but there is no input to that node at all")
+ value = inputs[to_input]
+ if not is_link(value):
+ raise NodeInputError(f"Node {to_node_id} says it needs input {to_input}, but that value is a constant")
+ from_node_id, from_socket = value
+ self.add_strong_link(from_node_id, from_socket, to_node_id)
+
+ def add_strong_link(self, from_node_id, from_socket, to_node_id):
+ self.add_node(from_node_id)
+ if to_node_id not in self.blocking[from_node_id]:
+ self.blocking[from_node_id][to_node_id] = {}
+ self.blockCount[to_node_id] += 1
+ self.blocking[from_node_id][to_node_id][from_socket] = True
+
+ def add_node(self, unique_id, include_lazy=False, subgraph_nodes=None):
+ if unique_id in self.pendingNodes:
+ return
+ self.pendingNodes[unique_id] = True
+ self.blockCount[unique_id] = 0
+ self.blocking[unique_id] = {}
+
+ inputs = self.dynprompt.get_node(unique_id)["inputs"]
+ for input_name in inputs:
+ value = inputs[input_name]
+ if is_link(value):
+ from_node_id, from_socket = value
+ if subgraph_nodes is not None and from_node_id not in subgraph_nodes:
+ continue
+ input_type, input_category, input_info = self.get_input_info(unique_id, input_name)
+ is_lazy = input_info is not None and "lazy" in input_info and input_info["lazy"]
+ if include_lazy or not is_lazy:
+ self.add_strong_link(from_node_id, from_socket, unique_id)
+
+ def get_ready_nodes(self):
+ return [node_id for node_id in self.pendingNodes if self.blockCount[node_id] == 0]
+
+ def pop_node(self, unique_id):
+ del self.pendingNodes[unique_id]
+ for blocked_node_id in self.blocking[unique_id]:
+ self.blockCount[blocked_node_id] -= 1
+ del self.blocking[unique_id]
+
+ def is_empty(self):
+ return len(self.pendingNodes) == 0
+
+class ExecutionList(TopologicalSort):
+ """
+ ExecutionList implements a topological dissolve of the graph. After a node is staged for execution,
+ it can still be returned to the graph after having further dependencies added.
+ """
+ def __init__(self, dynprompt, output_cache):
+ super().__init__(dynprompt)
+ self.output_cache = output_cache
+ self.staged_node_id = None
+
+ def add_strong_link(self, from_node_id, from_socket, to_node_id):
+ if self.output_cache.get(from_node_id) is not None:
+ # Nothing to do
+ return
+ super().add_strong_link(from_node_id, from_socket, to_node_id)
+
+ def stage_node_execution(self):
+ assert self.staged_node_id is None
+ if self.is_empty():
+ return None, None, None
+ available = self.get_ready_nodes()
+ if len(available) == 0:
+ cycled_nodes = self.get_nodes_in_cycle()
+ # Because cycles composed entirely of static nodes are caught during initial validation,
+ # we will 'blame' the first node in the cycle that is not a static node.
+ blamed_node = cycled_nodes[0]
+ for node_id in cycled_nodes:
+ display_node_id = self.dynprompt.get_display_node_id(node_id)
+ if display_node_id != node_id:
+ blamed_node = display_node_id
+ break
+ ex = DependencyCycleError("Dependency cycle detected")
+ error_details = {
+ "node_id": blamed_node,
+ "exception_message": str(ex),
+ "exception_type": "graph.DependencyCycleError",
+ "traceback": [],
+ "current_inputs": []
+ }
+ return None, error_details, ex
+
+ self.staged_node_id = self.ux_friendly_pick_node(available)
+ return self.staged_node_id, None, None
+
+ def ux_friendly_pick_node(self, node_list):
+ # If an output node is available, do that first.
+ # Technically this has no effect on the overall length of execution, but it feels better as a user
+ # for a PreviewImage to display a result as soon as it can
+ # Some other heuristics could probably be used here to improve the UX further.
+ def is_output(node_id):
+ class_type = self.dynprompt.get_node(node_id)["class_type"]
+ class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
+ if hasattr(class_def, 'OUTPUT_NODE') and class_def.OUTPUT_NODE == True:
+ return True
+ return False
+
+ for node_id in node_list:
+ if is_output(node_id):
+ return node_id
+
+ #This should handle the VAEDecode -> preview case
+ for node_id in node_list:
+ for blocked_node_id in self.blocking[node_id]:
+ if is_output(blocked_node_id):
+ return node_id
+
+ #This should handle the VAELoader -> VAEDecode -> preview case
+ for node_id in node_list:
+ for blocked_node_id in self.blocking[node_id]:
+ for blocked_node_id1 in self.blocking[blocked_node_id]:
+ if is_output(blocked_node_id1):
+ return node_id
+
+ #TODO: this function should be improved
+ return node_list[0]
+
+ def unstage_node_execution(self):
+ assert self.staged_node_id is not None
+ self.staged_node_id = None
+
+ def complete_node_execution(self):
+ node_id = self.staged_node_id
+ self.pop_node(node_id)
+ self.staged_node_id = None
+
+ def get_nodes_in_cycle(self):
+ # We'll dissolve the graph in reverse topological order to leave only the nodes in the cycle.
+ # We're skipping some of the performance optimizations from the original TopologicalSort to keep
+ # the code simple (and because having a cycle in the first place is a catastrophic error)
+ blocked_by = { node_id: {} for node_id in self.pendingNodes }
+ for from_node_id in self.blocking:
+ for to_node_id in self.blocking[from_node_id]:
+ if True in self.blocking[from_node_id][to_node_id].values():
+ blocked_by[to_node_id][from_node_id] = True
+ to_remove = [node_id for node_id in blocked_by if len(blocked_by[node_id]) == 0]
+ while len(to_remove) > 0:
+ for node_id in to_remove:
+ for to_node_id in blocked_by:
+ if node_id in blocked_by[to_node_id]:
+ del blocked_by[to_node_id][node_id]
+ del blocked_by[node_id]
+ to_remove = [node_id for node_id in blocked_by if len(blocked_by[node_id]) == 0]
+ return list(blocked_by.keys())
+
+class ExecutionBlocker:
+ """
+ Return this from a node and any users will be blocked with the given error message.
+ If the message is None, execution will be blocked silently instead.
+ Generally, you should avoid using this functionality unless absolutely necessary. Whenever it's
+ possible, a lazy input will be more efficient and have a better user experience.
+ This functionality is useful in two cases:
+ 1. You want to conditionally prevent an output node from executing. (Particularly a built-in node
+ like SaveImage. For your own output nodes, I would recommend just adding a BOOL input and using
+ lazy evaluation to let it conditionally disable itself.)
+ 2. You have a node with multiple possible outputs, some of which are invalid and should not be used.
+ (I would recommend not making nodes like this in the future -- instead, make multiple nodes with
+ different outputs. Unfortunately, there are several popular existing nodes using this pattern.)
+ """
+ def __init__(self, message):
+ self.message = message
+
diff --git a/comfy_execution/graph_utils.py b/comfy_execution/graph_utils.py
new file mode 100644
index 000000000..8595e942d
--- /dev/null
+++ b/comfy_execution/graph_utils.py
@@ -0,0 +1,139 @@
+def is_link(obj):
+ if not isinstance(obj, list):
+ return False
+ if len(obj) != 2:
+ return False
+ if not isinstance(obj[0], str):
+ return False
+ if not isinstance(obj[1], int) and not isinstance(obj[1], float):
+ return False
+ return True
+
+# The GraphBuilder is just a utility class that outputs graphs in the form expected by the ComfyUI back-end
+class GraphBuilder:
+ _default_prefix_root = ""
+ _default_prefix_call_index = 0
+ _default_prefix_graph_index = 0
+
+ def __init__(self, prefix = None):
+ if prefix is None:
+ self.prefix = GraphBuilder.alloc_prefix()
+ else:
+ self.prefix = prefix
+ self.nodes = {}
+ self.id_gen = 1
+
+ @classmethod
+ def set_default_prefix(cls, prefix_root, call_index, graph_index = 0):
+ cls._default_prefix_root = prefix_root
+ cls._default_prefix_call_index = call_index
+ cls._default_prefix_graph_index = graph_index
+
+ @classmethod
+ def alloc_prefix(cls, root=None, call_index=None, graph_index=None):
+ if root is None:
+ root = GraphBuilder._default_prefix_root
+ if call_index is None:
+ call_index = GraphBuilder._default_prefix_call_index
+ if graph_index is None:
+ graph_index = GraphBuilder._default_prefix_graph_index
+ result = f"{root}.{call_index}.{graph_index}."
+ GraphBuilder._default_prefix_graph_index += 1
+ return result
+
+ def node(self, class_type, id=None, **kwargs):
+ if id is None:
+ id = str(self.id_gen)
+ self.id_gen += 1
+ id = self.prefix + id
+ if id in self.nodes:
+ return self.nodes[id]
+
+ node = Node(id, class_type, kwargs)
+ self.nodes[id] = node
+ return node
+
+ def lookup_node(self, id):
+ id = self.prefix + id
+ return self.nodes.get(id)
+
+ def finalize(self):
+ output = {}
+ for node_id, node in self.nodes.items():
+ output[node_id] = node.serialize()
+ return output
+
+ def replace_node_output(self, node_id, index, new_value):
+ node_id = self.prefix + node_id
+ to_remove = []
+ for node in self.nodes.values():
+ for key, value in node.inputs.items():
+ if is_link(value) and value[0] == node_id and value[1] == index:
+ if new_value is None:
+ to_remove.append((node, key))
+ else:
+ node.inputs[key] = new_value
+ for node, key in to_remove:
+ del node.inputs[key]
+
+ def remove_node(self, id):
+ id = self.prefix + id
+ del self.nodes[id]
+
+class Node:
+ def __init__(self, id, class_type, inputs):
+ self.id = id
+ self.class_type = class_type
+ self.inputs = inputs
+ self.override_display_id = None
+
+ def out(self, index):
+ return [self.id, index]
+
+ def set_input(self, key, value):
+ if value is None:
+ if key in self.inputs:
+ del self.inputs[key]
+ else:
+ self.inputs[key] = value
+
+ def get_input(self, key):
+ return self.inputs.get(key)
+
+ def set_override_display_id(self, override_display_id):
+ self.override_display_id = override_display_id
+
+ def serialize(self):
+ serialized = {
+ "class_type": self.class_type,
+ "inputs": self.inputs
+ }
+ if self.override_display_id is not None:
+ serialized["override_display_id"] = self.override_display_id
+ return serialized
+
+def add_graph_prefix(graph, outputs, prefix):
+ # Change the node IDs and any internal links
+ new_graph = {}
+ for node_id, node_info in graph.items():
+ # Make sure the added nodes have unique IDs
+ new_node_id = prefix + node_id
+ new_node = { "class_type": node_info["class_type"], "inputs": {} }
+ for input_name, input_value in node_info.get("inputs", {}).items():
+ if is_link(input_value):
+ new_node["inputs"][input_name] = [prefix + input_value[0], input_value[1]]
+ else:
+ new_node["inputs"][input_name] = input_value
+ new_graph[new_node_id] = new_node
+
+ # Change the node IDs in the outputs
+ new_outputs = []
+ for n in range(len(outputs)):
+ output = outputs[n]
+ if is_link(output):
+ new_outputs.append([prefix + output[0], output[1]])
+ else:
+ new_outputs.append(output)
+
+ return new_graph, tuple(new_outputs)
+
diff --git a/comfy_extras/nodes_hunyuan.py b/comfy_extras/nodes_hunyuan.py
index a3ac8cb06..b03eaf6a2 100644
--- a/comfy_extras/nodes_hunyuan.py
+++ b/comfy_extras/nodes_hunyuan.py
@@ -19,6 +19,7 @@ class CLIPTextEncodeHunyuanDiT:
cond = output.pop("cond")
return ([[cond, output]], )
+
NODE_CLASS_MAPPINGS = {
"CLIPTextEncodeHunyuanDiT": CLIPTextEncodeHunyuanDiT,
}
diff --git a/comfy_extras/nodes_model_merging.py b/comfy_extras/nodes_model_merging.py
index 136f9a984..ccf601158 100644
--- a/comfy_extras/nodes_model_merging.py
+++ b/comfy_extras/nodes_model_merging.py
@@ -333,6 +333,25 @@ class VAESave:
comfy.utils.save_torch_file(vae.get_sd(), output_checkpoint, metadata=metadata)
return {}
+class ModelSave:
+ def __init__(self):
+ self.output_dir = folder_paths.get_output_directory()
+
+ @classmethod
+ def INPUT_TYPES(s):
+ return {"required": { "model": ("MODEL",),
+ "filename_prefix": ("STRING", {"default": "diffusion_models/ComfyUI"}),},
+ "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},}
+ RETURN_TYPES = ()
+ FUNCTION = "save"
+ OUTPUT_NODE = True
+
+ CATEGORY = "advanced/model_merging"
+
+ def save(self, model, filename_prefix, prompt=None, extra_pnginfo=None):
+ save_checkpoint(model, filename_prefix=filename_prefix, output_dir=self.output_dir, prompt=prompt, extra_pnginfo=extra_pnginfo)
+ return {}
+
NODE_CLASS_MAPPINGS = {
"ModelMergeSimple": ModelMergeSimple,
"ModelMergeBlocks": ModelMergeBlocks,
@@ -344,4 +363,9 @@ NODE_CLASS_MAPPINGS = {
"CLIPMergeAdd": CLIPAdd,
"CLIPSave": CLIPSave,
"VAESave": VAESave,
+ "ModelSave": ModelSave,
+}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "CheckpointSave": "Save Checkpoint",
}
diff --git a/comfy_extras/nodes_sd3.py b/comfy_extras/nodes_sd3.py
index ae9b85981..046096cba 100644
--- a/comfy_extras/nodes_sd3.py
+++ b/comfy_extras/nodes_sd3.py
@@ -100,3 +100,8 @@ NODE_CLASS_MAPPINGS = {
"CLIPTextEncodeSD3": CLIPTextEncodeSD3,
"ControlNetApplySD3": ControlNetApplySD3,
}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ # Sampling
+ "ControlNetApplySD3": "ControlNetApply SD3 and HunyuanDiT",
+}
diff --git a/custom_nodes/example_node.py.example b/custom_nodes/example_node.py.example
index 72ca3688c..9c68ab769 100644
--- a/custom_nodes/example_node.py.example
+++ b/custom_nodes/example_node.py.example
@@ -54,7 +54,8 @@ class Example:
"min": 0, #Minimum value
"max": 4096, #Maximum value
"step": 64, #Slider's step
- "display": "number" # Cosmetic only: display as "number" or "slider"
+ "display": "number", # Cosmetic only: display as "number" or "slider"
+ "lazy": True # Will only be evaluated if check_lazy_status requires it
}),
"float_field": ("FLOAT", {
"default": 1.0,
@@ -62,11 +63,14 @@ class Example:
"max": 10.0,
"step": 0.01,
"round": 0.001, #The value representing the precision to round to, will be set to the step value by default. Can be set to False to disable rounding.
- "display": "number"}),
+ "display": "number",
+ "lazy": True
+ }),
"print_to_screen": (["enable", "disable"],),
"string_field": ("STRING", {
"multiline": False, #True if you want the field to look like the one on the ClipTextEncode node
- "default": "Hello World!"
+ "default": "Hello World!",
+ "lazy": True
}),
},
}
@@ -80,6 +84,23 @@ class Example:
CATEGORY = "Example"
+ def check_lazy_status(self, image, string_field, int_field, float_field, print_to_screen):
+ """
+ Return a list of input names that need to be evaluated.
+
+ This function will be called if there are any lazy inputs which have not yet been
+ evaluated. As long as you return at least one field which has not yet been evaluated
+ (and more exist), this function will be called again once the value of the requested
+ field is available.
+
+ Any evaluated inputs will be passed as arguments to this function. Any unevaluated
+ inputs will have the value None.
+ """
+ if print_to_screen == "enable":
+ return ["int_field", "float_field", "string_field"]
+ else:
+ return []
+
def test(self, image, string_field, int_field, float_field, print_to_screen):
if print_to_screen == "enable":
print(f"""Your input contains:
diff --git a/execution.py b/execution.py
index d207e1b9e..9278af35f 100644
--- a/execution.py
+++ b/execution.py
@@ -5,6 +5,7 @@ import threading
import heapq
import time
import traceback
+from enum import Enum
import inspect
from typing import List, Literal, NamedTuple, Optional
@@ -12,102 +13,218 @@ import torch
import nodes
import comfy.model_management
+from comfy_execution.graph import get_input_info, ExecutionList, DynamicPrompt, ExecutionBlocker
+from comfy_execution.graph_utils import is_link, GraphBuilder
+from comfy_execution.caching import HierarchicalCache, LRUCache, CacheKeySetInputSignature, CacheKeySetID
+from comfy.cli_args import args
-def get_input_data(inputs, class_def, unique_id, outputs={}, prompt={}, extra_data={}):
+class ExecutionResult(Enum):
+ SUCCESS = 0
+ FAILURE = 1
+ PENDING = 2
+
+class DuplicateNodeError(Exception):
+ pass
+
+class IsChangedCache:
+ def __init__(self, dynprompt, outputs_cache):
+ self.dynprompt = dynprompt
+ self.outputs_cache = outputs_cache
+ self.is_changed = {}
+
+ def get(self, node_id):
+ if node_id in self.is_changed:
+ return self.is_changed[node_id]
+
+ node = self.dynprompt.get_node(node_id)
+ class_type = node["class_type"]
+ class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
+ if not hasattr(class_def, "IS_CHANGED"):
+ self.is_changed[node_id] = False
+ return self.is_changed[node_id]
+
+ if "is_changed" in node:
+ self.is_changed[node_id] = node["is_changed"]
+ return self.is_changed[node_id]
+
+ input_data_all, _ = get_input_data(node["inputs"], class_def, node_id, self.outputs_cache)
+ try:
+ is_changed = _map_node_over_list(class_def, input_data_all, "IS_CHANGED")
+ node["is_changed"] = [None if isinstance(x, ExecutionBlocker) else x for x in is_changed]
+ except Exception as e:
+ logging.warning("WARNING: {}".format(e))
+ node["is_changed"] = float("NaN")
+ finally:
+ self.is_changed[node_id] = node["is_changed"]
+ return self.is_changed[node_id]
+
+class CacheSet:
+ def __init__(self, lru_size=None):
+ if lru_size is None or lru_size == 0:
+ self.init_classic_cache()
+ else:
+ self.init_lru_cache(lru_size)
+ self.all = [self.outputs, self.ui, self.objects]
+
+ # Useful for those with ample RAM/VRAM -- allows experimenting without
+ # blowing away the cache every time
+ def init_lru_cache(self, cache_size):
+ self.outputs = LRUCache(CacheKeySetInputSignature, max_size=cache_size)
+ self.ui = LRUCache(CacheKeySetInputSignature, max_size=cache_size)
+ self.objects = HierarchicalCache(CacheKeySetID)
+
+ # Performs like the old cache -- dump data ASAP
+ def init_classic_cache(self):
+ self.outputs = HierarchicalCache(CacheKeySetInputSignature)
+ self.ui = HierarchicalCache(CacheKeySetInputSignature)
+ self.objects = HierarchicalCache(CacheKeySetID)
+
+ def recursive_debug_dump(self):
+ result = {
+ "outputs": self.outputs.recursive_debug_dump(),
+ "ui": self.ui.recursive_debug_dump(),
+ }
+ return result
+
+def get_input_data(inputs, class_def, unique_id, outputs=None, dynprompt=None, extra_data={}):
valid_inputs = class_def.INPUT_TYPES()
input_data_all = {}
+ missing_keys = {}
for x in inputs:
input_data = inputs[x]
- if isinstance(input_data, list):
+ input_type, input_category, input_info = get_input_info(class_def, x)
+ def mark_missing():
+ missing_keys[x] = True
+ input_data_all[x] = (None,)
+ if is_link(input_data) and (not input_info or not input_info.get("rawLink", False)):
input_unique_id = input_data[0]
output_index = input_data[1]
- if input_unique_id not in outputs:
- input_data_all[x] = (None,)
+ if outputs is None:
+ mark_missing()
+ continue # This might be a lazily-evaluated input
+ cached_output = outputs.get(input_unique_id)
+ if cached_output is None:
+ mark_missing()
continue
- obj = outputs[input_unique_id][output_index]
+ if output_index >= len(cached_output):
+ mark_missing()
+ continue
+ obj = cached_output[output_index]
input_data_all[x] = obj
- else:
- if ("required" in valid_inputs and x in valid_inputs["required"]) or ("optional" in valid_inputs and x in valid_inputs["optional"]):
- input_data_all[x] = [input_data]
+ elif input_category is not None:
+ input_data_all[x] = [input_data]
if "hidden" in valid_inputs:
h = valid_inputs["hidden"]
for x in h:
if h[x] == "PROMPT":
- input_data_all[x] = [prompt]
+ input_data_all[x] = [dynprompt.get_original_prompt() if dynprompt is not None else {}]
+ if h[x] == "DYNPROMPT":
+ input_data_all[x] = [dynprompt]
if h[x] == "EXTRA_PNGINFO":
input_data_all[x] = [extra_data.get('extra_pnginfo', None)]
if h[x] == "UNIQUE_ID":
input_data_all[x] = [unique_id]
- return input_data_all
+ return input_data_all, missing_keys
-def map_node_over_list(obj, input_data_all, func, allow_interrupt=False):
+map_node_over_list = None #Don't hook this please
+
+def _map_node_over_list(obj, input_data_all, func, allow_interrupt=False, execution_block_cb=None, pre_execute_cb=None):
# check if node wants the lists
- input_is_list = False
- if hasattr(obj, "INPUT_IS_LIST"):
- input_is_list = obj.INPUT_IS_LIST
+ input_is_list = getattr(obj, "INPUT_IS_LIST", False)
if len(input_data_all) == 0:
max_len_input = 0
else:
- max_len_input = max([len(x) for x in input_data_all.values()])
+ max_len_input = max(len(x) for x in input_data_all.values())
# get a slice of inputs, repeat last input when list isn't long enough
def slice_dict(d, i):
- d_new = dict()
- for k,v in d.items():
- d_new[k] = v[i if len(v) > i else -1]
- return d_new
+ return {k: v[i if len(v) > i else -1] for k, v in d.items()}
results = []
+ def process_inputs(inputs, index=None):
+ if allow_interrupt:
+ nodes.before_node_execution()
+ execution_block = None
+ for k, v in inputs.items():
+ if isinstance(v, ExecutionBlocker):
+ execution_block = execution_block_cb(v) if execution_block_cb else v
+ break
+ if execution_block is None:
+ if pre_execute_cb is not None and index is not None:
+ pre_execute_cb(index)
+ results.append(getattr(obj, func)(**inputs))
+ else:
+ results.append(execution_block)
+
if input_is_list:
- if allow_interrupt:
- nodes.before_node_execution()
- results.append(getattr(obj, func)(**input_data_all))
+ process_inputs(input_data_all, 0)
elif max_len_input == 0:
- if allow_interrupt:
- nodes.before_node_execution()
- results.append(getattr(obj, func)())
- else:
+ process_inputs({})
+ else:
for i in range(max_len_input):
- if allow_interrupt:
- nodes.before_node_execution()
- results.append(getattr(obj, func)(**slice_dict(input_data_all, i)))
+ input_dict = slice_dict(input_data_all, i)
+ process_inputs(input_dict, i)
return results
-def get_output_data(obj, input_data_all):
+def merge_result_data(results, obj):
+ # check which outputs need concatenating
+ output = []
+ output_is_list = [False] * len(results[0])
+ if hasattr(obj, "OUTPUT_IS_LIST"):
+ output_is_list = obj.OUTPUT_IS_LIST
+
+ # merge node execution results
+ for i, is_list in zip(range(len(results[0])), output_is_list):
+ if is_list:
+ output.append([x for o in results for x in o[i]])
+ else:
+ output.append([o[i] for o in results])
+ return output
+
+def get_output_data(obj, input_data_all, execution_block_cb=None, pre_execute_cb=None):
results = []
uis = []
- return_values = map_node_over_list(obj, input_data_all, obj.FUNCTION, allow_interrupt=True)
-
- for r in return_values:
+ subgraph_results = []
+ return_values = _map_node_over_list(obj, input_data_all, obj.FUNCTION, allow_interrupt=True, execution_block_cb=execution_block_cb, pre_execute_cb=pre_execute_cb)
+ has_subgraph = False
+ for i in range(len(return_values)):
+ r = return_values[i]
if isinstance(r, dict):
if 'ui' in r:
uis.append(r['ui'])
- if 'result' in r:
- results.append(r['result'])
+ if 'expand' in r:
+ # Perform an expansion, but do not append results
+ has_subgraph = True
+ new_graph = r['expand']
+ result = r.get("result", None)
+ if isinstance(result, ExecutionBlocker):
+ result = tuple([result] * len(obj.RETURN_TYPES))
+ subgraph_results.append((new_graph, result))
+ elif 'result' in r:
+ result = r.get("result", None)
+ if isinstance(result, ExecutionBlocker):
+ result = tuple([result] * len(obj.RETURN_TYPES))
+ results.append(result)
+ subgraph_results.append((None, result))
else:
+ if isinstance(r, ExecutionBlocker):
+ r = tuple([r] * len(obj.RETURN_TYPES))
results.append(r)
+ subgraph_results.append((None, r))
- output = []
- if len(results) > 0:
- # check which outputs need concatenating
- output_is_list = [False] * len(results[0])
- if hasattr(obj, "OUTPUT_IS_LIST"):
- output_is_list = obj.OUTPUT_IS_LIST
-
- # merge node execution results
- for i, is_list in zip(range(len(results[0])), output_is_list):
- if is_list:
- output.append([x for o in results for x in o[i]])
- else:
- output.append([o[i] for o in results])
-
+ if has_subgraph:
+ output = subgraph_results
+ elif len(results) > 0:
+ output = merge_result_data(results, obj)
+ else:
+ output = []
ui = dict()
if len(uis) > 0:
ui = {k: [y for x in uis for y in x[k]] for k in uis[0].keys()}
- return output, ui
+ return output, ui, has_subgraph
def format_value(x):
if x is None:
@@ -117,53 +234,145 @@ def format_value(x):
else:
return str(x)
-def recursive_execute(server, prompt, outputs, current_item, extra_data, executed, prompt_id, outputs_ui, object_storage):
+def execute(server, dynprompt, caches, current_item, extra_data, executed, prompt_id, execution_list, pending_subgraph_results):
unique_id = current_item
- inputs = prompt[unique_id]['inputs']
- class_type = prompt[unique_id]['class_type']
+ real_node_id = dynprompt.get_real_node_id(unique_id)
+ display_node_id = dynprompt.get_display_node_id(unique_id)
+ parent_node_id = dynprompt.get_parent_node_id(unique_id)
+ inputs = dynprompt.get_node(unique_id)['inputs']
+ class_type = dynprompt.get_node(unique_id)['class_type']
class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
- if unique_id in outputs:
- return (True, None, None)
-
- for x in inputs:
- input_data = inputs[x]
-
- if isinstance(input_data, list):
- input_unique_id = input_data[0]
- output_index = input_data[1]
- if input_unique_id not in outputs:
- result = recursive_execute(server, prompt, outputs, input_unique_id, extra_data, executed, prompt_id, outputs_ui, object_storage)
- if result[0] is not True:
- # Another node failed further upstream
- return result
+ if caches.outputs.get(unique_id) is not None:
+ if server.client_id is not None:
+ cached_output = caches.ui.get(unique_id) or {}
+ server.send_sync("executed", { "node": unique_id, "display_node": display_node_id, "output": cached_output.get("output",None), "prompt_id": prompt_id }, server.client_id)
+ return (ExecutionResult.SUCCESS, None, None)
input_data_all = None
try:
- input_data_all = get_input_data(inputs, class_def, unique_id, outputs, prompt, extra_data)
- if server.client_id is not None:
- server.last_node_id = unique_id
- server.send_sync("executing", { "node": unique_id, "prompt_id": prompt_id }, server.client_id)
+ if unique_id in pending_subgraph_results:
+ cached_results = pending_subgraph_results[unique_id]
+ resolved_outputs = []
+ for is_subgraph, result in cached_results:
+ if not is_subgraph:
+ resolved_outputs.append(result)
+ else:
+ resolved_output = []
+ for r in result:
+ if is_link(r):
+ source_node, source_output = r[0], r[1]
+ node_output = caches.outputs.get(source_node)[source_output]
+ for o in node_output:
+ resolved_output.append(o)
- obj = object_storage.get((unique_id, class_type), None)
- if obj is None:
- obj = class_def()
- object_storage[(unique_id, class_type)] = obj
-
- output_data, output_ui = get_output_data(obj, input_data_all)
- outputs[unique_id] = output_data
- if len(output_ui) > 0:
- outputs_ui[unique_id] = output_ui
+ else:
+ resolved_output.append(r)
+ resolved_outputs.append(tuple(resolved_output))
+ output_data = merge_result_data(resolved_outputs, class_def)
+ output_ui = []
+ has_subgraph = False
+ else:
+ input_data_all, missing_keys = get_input_data(inputs, class_def, unique_id, caches.outputs, dynprompt, extra_data)
if server.client_id is not None:
- server.send_sync("executed", { "node": unique_id, "output": output_ui, "prompt_id": prompt_id }, server.client_id)
+ server.last_node_id = display_node_id
+ server.send_sync("executing", { "node": unique_id, "display_node": display_node_id, "prompt_id": prompt_id }, server.client_id)
+
+ obj = caches.objects.get(unique_id)
+ if obj is None:
+ obj = class_def()
+ caches.objects.set(unique_id, obj)
+
+ if hasattr(obj, "check_lazy_status"):
+ required_inputs = _map_node_over_list(obj, input_data_all, "check_lazy_status", allow_interrupt=True)
+ required_inputs = set(sum([r for r in required_inputs if isinstance(r,list)], []))
+ required_inputs = [x for x in required_inputs if isinstance(x,str) and (
+ x not in input_data_all or x in missing_keys
+ )]
+ if len(required_inputs) > 0:
+ for i in required_inputs:
+ execution_list.make_input_strong_link(unique_id, i)
+ return (ExecutionResult.PENDING, None, None)
+
+ def execution_block_cb(block):
+ if block.message is not None:
+ mes = {
+ "prompt_id": prompt_id,
+ "node_id": unique_id,
+ "node_type": class_type,
+ "executed": list(executed),
+
+ "exception_message": f"Execution Blocked: {block.message}",
+ "exception_type": "ExecutionBlocked",
+ "traceback": [],
+ "current_inputs": [],
+ "current_outputs": [],
+ }
+ server.send_sync("execution_error", mes, server.client_id)
+ return ExecutionBlocker(None)
+ else:
+ return block
+ def pre_execute_cb(call_index):
+ GraphBuilder.set_default_prefix(unique_id, call_index, 0)
+ output_data, output_ui, has_subgraph = get_output_data(obj, input_data_all, execution_block_cb=execution_block_cb, pre_execute_cb=pre_execute_cb)
+ if len(output_ui) > 0:
+ caches.ui.set(unique_id, {
+ "meta": {
+ "node_id": unique_id,
+ "display_node": display_node_id,
+ "parent_node": parent_node_id,
+ "real_node_id": real_node_id,
+ },
+ "output": output_ui
+ })
+ if server.client_id is not None:
+ server.send_sync("executed", { "node": unique_id, "display_node": display_node_id, "output": output_ui, "prompt_id": prompt_id }, server.client_id)
+ if has_subgraph:
+ cached_outputs = []
+ new_node_ids = []
+ new_output_ids = []
+ new_output_links = []
+ for i in range(len(output_data)):
+ new_graph, node_outputs = output_data[i]
+ if new_graph is None:
+ cached_outputs.append((False, node_outputs))
+ else:
+ # Check for conflicts
+ for node_id in new_graph.keys():
+ if dynprompt.has_node(node_id):
+ raise DuplicateNodeError(f"Attempt to add duplicate node {node_id}. Ensure node ids are unique and deterministic or use graph_utils.GraphBuilder.")
+ for node_id, node_info in new_graph.items():
+ new_node_ids.append(node_id)
+ display_id = node_info.get("override_display_id", unique_id)
+ dynprompt.add_ephemeral_node(node_id, node_info, unique_id, display_id)
+ # Figure out if the newly created node is an output node
+ class_type = node_info["class_type"]
+ class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
+ if hasattr(class_def, 'OUTPUT_NODE') and class_def.OUTPUT_NODE == True:
+ new_output_ids.append(node_id)
+ for i in range(len(node_outputs)):
+ if is_link(node_outputs[i]):
+ from_node_id, from_socket = node_outputs[i][0], node_outputs[i][1]
+ new_output_links.append((from_node_id, from_socket))
+ cached_outputs.append((True, node_outputs))
+ new_node_ids = set(new_node_ids)
+ for cache in caches.all:
+ cache.ensure_subcache_for(unique_id, new_node_ids).clean_unused()
+ for node_id in new_output_ids:
+ execution_list.add_node(node_id)
+ for link in new_output_links:
+ execution_list.add_strong_link(link[0], link[1], unique_id)
+ pending_subgraph_results[unique_id] = cached_outputs
+ return (ExecutionResult.PENDING, None, None)
+ caches.outputs.set(unique_id, output_data)
except comfy.model_management.InterruptProcessingException as iex:
logging.info("Processing interrupted")
# skip formatting inputs/outputs
error_details = {
- "node_id": unique_id,
+ "node_id": real_node_id,
}
- return (False, error_details, iex)
+ return (ExecutionResult.FAILURE, error_details, iex)
except Exception as ex:
typ, _, tb = sys.exc_info()
exception_type = full_type_name(typ)
@@ -173,121 +382,36 @@ def recursive_execute(server, prompt, outputs, current_item, extra_data, execute
for name, inputs in input_data_all.items():
input_data_formatted[name] = [format_value(x) for x in inputs]
- output_data_formatted = {}
- for node_id, node_outputs in outputs.items():
- output_data_formatted[node_id] = [[format_value(x) for x in l] for l in node_outputs]
-
- logging.error(f"!!! Exception during processing!!! {ex}")
+ logging.error(f"!!! Exception during processing !!! {ex}")
logging.error(traceback.format_exc())
error_details = {
- "node_id": unique_id,
+ "node_id": real_node_id,
"exception_message": str(ex),
"exception_type": exception_type,
"traceback": traceback.format_tb(tb),
- "current_inputs": input_data_formatted,
- "current_outputs": output_data_formatted
+ "current_inputs": input_data_formatted
}
-
if isinstance(ex, comfy.model_management.OOM_EXCEPTION):
logging.error("Got an OOM, unloading all loaded models.")
comfy.model_management.unload_all_models()
- return (False, error_details, ex)
+ return (ExecutionResult.FAILURE, error_details, ex)
executed.add(unique_id)
- return (True, None, None)
-
-def recursive_will_execute(prompt, outputs, current_item, memo={}):
- unique_id = current_item
-
- if unique_id in memo:
- return memo[unique_id]
-
- inputs = prompt[unique_id]['inputs']
- will_execute = []
- if unique_id in outputs:
- return []
-
- for x in inputs:
- input_data = inputs[x]
- if isinstance(input_data, list):
- input_unique_id = input_data[0]
- output_index = input_data[1]
- if input_unique_id not in outputs:
- will_execute += recursive_will_execute(prompt, outputs, input_unique_id, memo)
-
- memo[unique_id] = will_execute + [unique_id]
- return memo[unique_id]
-
-def recursive_output_delete_if_changed(prompt, old_prompt, outputs, current_item):
- unique_id = current_item
- inputs = prompt[unique_id]['inputs']
- class_type = prompt[unique_id]['class_type']
- class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
-
- is_changed_old = ''
- is_changed = ''
- to_delete = False
- if hasattr(class_def, 'IS_CHANGED'):
- if unique_id in old_prompt and 'is_changed' in old_prompt[unique_id]:
- is_changed_old = old_prompt[unique_id]['is_changed']
- if 'is_changed' not in prompt[unique_id]:
- input_data_all = get_input_data(inputs, class_def, unique_id, outputs)
- if input_data_all is not None:
- try:
- #is_changed = class_def.IS_CHANGED(**input_data_all)
- is_changed = map_node_over_list(class_def, input_data_all, "IS_CHANGED")
- prompt[unique_id]['is_changed'] = is_changed
- except:
- to_delete = True
- else:
- is_changed = prompt[unique_id]['is_changed']
-
- if unique_id not in outputs:
- return True
-
- if not to_delete:
- if is_changed != is_changed_old:
- to_delete = True
- elif unique_id not in old_prompt:
- to_delete = True
- elif class_type != old_prompt[unique_id]['class_type']:
- to_delete = True
- elif inputs == old_prompt[unique_id]['inputs']:
- for x in inputs:
- input_data = inputs[x]
-
- if isinstance(input_data, list):
- input_unique_id = input_data[0]
- output_index = input_data[1]
- if input_unique_id in outputs:
- to_delete = recursive_output_delete_if_changed(prompt, old_prompt, outputs, input_unique_id)
- else:
- to_delete = True
- if to_delete:
- break
- else:
- to_delete = True
-
- if to_delete:
- d = outputs.pop(unique_id)
- del d
- return to_delete
+ return (ExecutionResult.SUCCESS, None, None)
class PromptExecutor:
- def __init__(self, server):
+ def __init__(self, server, lru_size=None):
+ self.lru_size = lru_size
self.server = server
self.reset()
def reset(self):
- self.outputs = {}
- self.object_storage = {}
- self.outputs_ui = {}
+ self.caches = CacheSet(self.lru_size)
self.status_messages = []
self.success = True
- self.old_prompt = {}
def add_message(self, event, data: dict, broadcast: bool):
data = {
@@ -318,27 +442,14 @@ class PromptExecutor:
"node_id": node_id,
"node_type": class_type,
"executed": list(executed),
-
"exception_message": error["exception_message"],
"exception_type": error["exception_type"],
"traceback": error["traceback"],
"current_inputs": error["current_inputs"],
- "current_outputs": error["current_outputs"],
+ "current_outputs": list(current_outputs),
}
self.add_message("execution_error", mes, broadcast=False)
- # Next, remove the subsequent outputs since they will not be executed
- to_delete = []
- for o in self.outputs:
- if (o not in current_outputs) and (o not in executed):
- to_delete += [o]
- if o in self.old_prompt:
- d = self.old_prompt.pop(o)
- del d
- for o in to_delete:
- d = self.outputs.pop(o)
- del d
-
def execute(self, prompt, prompt_id, extra_data={}, execute_outputs=[]):
nodes.interrupt_processing(False)
@@ -351,65 +462,58 @@ class PromptExecutor:
self.add_message("execution_start", { "prompt_id": prompt_id}, broadcast=False)
with torch.inference_mode():
- #delete cached outputs if nodes don't exist for them
- to_delete = []
- for o in self.outputs:
- if o not in prompt:
- to_delete += [o]
- for o in to_delete:
- d = self.outputs.pop(o)
- del d
- to_delete = []
- for o in self.object_storage:
- if o[0] not in prompt:
- to_delete += [o]
- else:
- p = prompt[o[0]]
- if o[1] != p['class_type']:
- to_delete += [o]
- for o in to_delete:
- d = self.object_storage.pop(o)
- del d
+ dynamic_prompt = DynamicPrompt(prompt)
+ is_changed_cache = IsChangedCache(dynamic_prompt, self.caches.outputs)
+ for cache in self.caches.all:
+ cache.set_prompt(dynamic_prompt, prompt.keys(), is_changed_cache)
+ cache.clean_unused()
- for x in prompt:
- recursive_output_delete_if_changed(prompt, self.old_prompt, self.outputs, x)
-
- current_outputs = set(self.outputs.keys())
- for x in list(self.outputs_ui.keys()):
- if x not in current_outputs:
- d = self.outputs_ui.pop(x)
- del d
+ cached_nodes = []
+ for node_id in prompt:
+ if self.caches.outputs.get(node_id) is not None:
+ cached_nodes.append(node_id)
comfy.model_management.cleanup_models(keep_clone_weights_loaded=True)
self.add_message("execution_cached",
- { "nodes": list(current_outputs) , "prompt_id": prompt_id},
+ { "nodes": cached_nodes, "prompt_id": prompt_id},
broadcast=False)
+ pending_subgraph_results = {}
executed = set()
- output_node_id = None
- to_execute = []
-
+ execution_list = ExecutionList(dynamic_prompt, self.caches.outputs)
+ current_outputs = self.caches.outputs.all_node_ids()
for node_id in list(execute_outputs):
- to_execute += [(0, node_id)]
+ execution_list.add_node(node_id)
- while len(to_execute) > 0:
- #always execute the output that depends on the least amount of unexecuted nodes first
- memo = {}
- to_execute = sorted(list(map(lambda a: (len(recursive_will_execute(prompt, self.outputs, a[-1], memo)), a[-1]), to_execute)))
- output_node_id = to_execute.pop(0)[-1]
-
- # This call shouldn't raise anything if there's an error deep in
- # the actual SD code, instead it will report the node where the
- # error was raised
- self.success, error, ex = recursive_execute(self.server, prompt, self.outputs, output_node_id, extra_data, executed, prompt_id, self.outputs_ui, self.object_storage)
- if self.success is not True:
- self.handle_execution_error(prompt_id, prompt, current_outputs, executed, error, ex)
+ while not execution_list.is_empty():
+ node_id, error, ex = execution_list.stage_node_execution()
+ if error is not None:
+ self.handle_execution_error(prompt_id, dynamic_prompt.original_prompt, current_outputs, executed, error, ex)
break
+
+ result, error, ex = execute(self.server, dynamic_prompt, self.caches, node_id, extra_data, executed, prompt_id, execution_list, pending_subgraph_results)
+ if result == ExecutionResult.FAILURE:
+ self.handle_execution_error(prompt_id, dynamic_prompt.original_prompt, current_outputs, executed, error, ex)
+ break
+ elif result == ExecutionResult.PENDING:
+ execution_list.unstage_node_execution()
+ else: # result == ExecutionResult.SUCCESS:
+ execution_list.complete_node_execution()
else:
# Only execute when the while-loop ends without break
self.add_message("execution_success", { "prompt_id": prompt_id }, broadcast=False)
- for x in executed:
- self.old_prompt[x] = copy.deepcopy(prompt[x])
+ ui_outputs = {}
+ meta_outputs = {}
+ all_node_ids = self.caches.ui.all_node_ids()
+ for node_id in all_node_ids:
+ ui_info = self.caches.ui.get(node_id)
+ if ui_info is not None:
+ ui_outputs[node_id] = ui_info["output"]
+ meta_outputs[node_id] = ui_info["meta"]
+ self.history_result = {
+ "outputs": ui_outputs,
+ "meta": meta_outputs,
+ }
self.server.last_node_id = None
if comfy.model_management.DISABLE_SMART_MEMORY:
comfy.model_management.unload_all_models()
@@ -426,31 +530,37 @@ def validate_inputs(prompt, item, validated):
obj_class = nodes.NODE_CLASS_MAPPINGS[class_type]
class_inputs = obj_class.INPUT_TYPES()
- required_inputs = class_inputs['required']
+ valid_inputs = set(class_inputs.get('required',{})).union(set(class_inputs.get('optional',{})))
errors = []
valid = True
validate_function_inputs = []
+ validate_has_kwargs = False
if hasattr(obj_class, "VALIDATE_INPUTS"):
- validate_function_inputs = inspect.getfullargspec(obj_class.VALIDATE_INPUTS).args
+ argspec = inspect.getfullargspec(obj_class.VALIDATE_INPUTS)
+ validate_function_inputs = argspec.args
+ validate_has_kwargs = argspec.varkw is not None
+ received_types = {}
- for x in required_inputs:
+ for x in valid_inputs:
+ type_input, input_category, extra_info = get_input_info(obj_class, x)
+ assert extra_info is not None
if x not in inputs:
- error = {
- "type": "required_input_missing",
- "message": "Required input is missing",
- "details": f"{x}",
- "extra_info": {
- "input_name": x
+ if input_category == "required":
+ error = {
+ "type": "required_input_missing",
+ "message": "Required input is missing",
+ "details": f"{x}",
+ "extra_info": {
+ "input_name": x
+ }
}
- }
- errors.append(error)
+ errors.append(error)
continue
val = inputs[x]
- info = required_inputs[x]
- type_input = info[0]
+ info = (type_input, extra_info)
if isinstance(val, list):
if len(val) != 2:
error = {
@@ -469,8 +579,9 @@ def validate_inputs(prompt, item, validated):
o_id = val[0]
o_class_type = prompt[o_id]['class_type']
r = nodes.NODE_CLASS_MAPPINGS[o_class_type].RETURN_TYPES
- if r[val[1]] != type_input:
- received_type = r[val[1]]
+ received_type = r[val[1]]
+ received_types[x] = received_type
+ if 'input_types' not in validate_function_inputs and received_type != type_input:
details = f"{x}, {received_type} != {type_input}"
error = {
"type": "return_type_mismatch",
@@ -521,6 +632,9 @@ def validate_inputs(prompt, item, validated):
if type_input == "STRING":
val = str(val)
inputs[x] = val
+ if type_input == "BOOLEAN":
+ val = bool(val)
+ inputs[x] = val
except Exception as ex:
error = {
"type": "invalid_input_type",
@@ -536,11 +650,11 @@ def validate_inputs(prompt, item, validated):
errors.append(error)
continue
- if len(info) > 1:
- if "min" in info[1] and val < info[1]["min"]:
+ if x not in validate_function_inputs and not validate_has_kwargs:
+ if "min" in extra_info and val < extra_info["min"]:
error = {
"type": "value_smaller_than_min",
- "message": "Value {} smaller than min of {}".format(val, info[1]["min"]),
+ "message": "Value {} smaller than min of {}".format(val, extra_info["min"]),
"details": f"{x}",
"extra_info": {
"input_name": x,
@@ -550,10 +664,10 @@ def validate_inputs(prompt, item, validated):
}
errors.append(error)
continue
- if "max" in info[1] and val > info[1]["max"]:
+ if "max" in extra_info and val > extra_info["max"]:
error = {
"type": "value_bigger_than_max",
- "message": "Value {} bigger than max of {}".format(val, info[1]["max"]),
+ "message": "Value {} bigger than max of {}".format(val, extra_info["max"]),
"details": f"{x}",
"extra_info": {
"input_name": x,
@@ -564,7 +678,6 @@ def validate_inputs(prompt, item, validated):
errors.append(error)
continue
- if x not in validate_function_inputs:
if isinstance(type_input, list):
if val not in type_input:
input_config = info
@@ -591,18 +704,20 @@ def validate_inputs(prompt, item, validated):
errors.append(error)
continue
- if len(validate_function_inputs) > 0:
- input_data_all = get_input_data(inputs, obj_class, unique_id)
+ if len(validate_function_inputs) > 0 or validate_has_kwargs:
+ input_data_all, _ = get_input_data(inputs, obj_class, unique_id)
input_filtered = {}
for x in input_data_all:
- if x in validate_function_inputs:
+ if x in validate_function_inputs or validate_has_kwargs:
input_filtered[x] = input_data_all[x]
+ if 'input_types' in validate_function_inputs:
+ input_filtered['input_types'] = [received_types]
#ret = obj_class.VALIDATE_INPUTS(**input_filtered)
- ret = map_node_over_list(obj_class, input_filtered, "VALIDATE_INPUTS")
+ ret = _map_node_over_list(obj_class, input_filtered, "VALIDATE_INPUTS")
for x in input_filtered:
for i, r in enumerate(ret):
- if r is not True:
+ if r is not True and not isinstance(r, ExecutionBlocker):
details = f"{x}"
if r is not False:
details += f" - {str(r)}"
@@ -613,8 +728,6 @@ def validate_inputs(prompt, item, validated):
"details": details,
"extra_info": {
"input_name": x,
- "input_config": info,
- "received_value": val,
}
}
errors.append(error)
@@ -780,7 +893,7 @@ class PromptQueue:
completed: bool
messages: List[str]
- def task_done(self, item_id, outputs,
+ def task_done(self, item_id, history_result,
status: Optional['PromptQueue.ExecutionStatus']):
with self.mutex:
prompt = self.currently_running.pop(item_id)
@@ -793,9 +906,10 @@ class PromptQueue:
self.history[prompt[1]] = {
"prompt": prompt,
- "outputs": copy.deepcopy(outputs),
+ "outputs": {},
'status': status_dict,
}
+ self.history[prompt[1]].update(history_result)
self.server.queue_updated()
def get_current_queue(self):
diff --git a/folder_paths.py b/folder_paths.py
index 71faa2df4..74a7d527c 100644
--- a/folder_paths.py
+++ b/folder_paths.py
@@ -1,13 +1,13 @@
+from __future__ import annotations
+
import os
import time
import logging
-from typing import Set, List, Dict, Tuple
+from collections.abc import Collection
-supported_pt_extensions: Set[str] = set(['.ckpt', '.pt', '.bin', '.pth', '.safetensors', '.pkl', '.sft'])
+supported_pt_extensions: set[str] = {'.ckpt', '.pt', '.bin', '.pth', '.safetensors', '.pkl', '.sft'}
-SupportedFileExtensionsType = Set[str]
-ScanPathType = List[str]
-folder_names_and_paths: Dict[str, Tuple[ScanPathType, SupportedFileExtensionsType]] = {}
+folder_names_and_paths: dict[str, tuple[list[str], set[str]]] = {}
base_path = os.path.dirname(os.path.realpath(__file__))
models_dir = os.path.join(base_path, "models")
@@ -17,7 +17,7 @@ folder_names_and_paths["configs"] = ([os.path.join(models_dir, "configs")], [".y
folder_names_and_paths["loras"] = ([os.path.join(models_dir, "loras")], supported_pt_extensions)
folder_names_and_paths["vae"] = ([os.path.join(models_dir, "vae")], supported_pt_extensions)
folder_names_and_paths["clip"] = ([os.path.join(models_dir, "clip")], supported_pt_extensions)
-folder_names_and_paths["unet"] = ([os.path.join(models_dir, "unet")], supported_pt_extensions)
+folder_names_and_paths["diffusion_models"] = ([os.path.join(models_dir, "unet"), os.path.join(models_dir, "diffusion_models")], supported_pt_extensions)
folder_names_and_paths["clip_vision"] = ([os.path.join(models_dir, "clip_vision")], supported_pt_extensions)
folder_names_and_paths["style_models"] = ([os.path.join(models_dir, "style_models")], supported_pt_extensions)
folder_names_and_paths["embeddings"] = ([os.path.join(models_dir, "embeddings")], supported_pt_extensions)
@@ -42,7 +42,11 @@ temp_directory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "temp
input_directory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "input")
user_directory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "user")
-filename_list_cache = {}
+filename_list_cache: dict[str, tuple[list[str], dict[str, float], float]] = {}
+
+def map_legacy(folder_name: str) -> str:
+ legacy = {"unet": "diffusion_models"}
+ return legacy.get(folder_name, folder_name)
if not os.path.exists(input_directory):
try:
@@ -50,33 +54,33 @@ if not os.path.exists(input_directory):
except:
logging.error("Failed to create input directory")
-def set_output_directory(output_dir):
+def set_output_directory(output_dir: str) -> None:
global output_directory
output_directory = output_dir
-def set_temp_directory(temp_dir):
+def set_temp_directory(temp_dir: str) -> None:
global temp_directory
temp_directory = temp_dir
-def set_input_directory(input_dir):
+def set_input_directory(input_dir: str) -> None:
global input_directory
input_directory = input_dir
-def get_output_directory():
+def get_output_directory() -> str:
global output_directory
return output_directory
-def get_temp_directory():
+def get_temp_directory() -> str:
global temp_directory
return temp_directory
-def get_input_directory():
+def get_input_directory() -> str:
global input_directory
return input_directory
#NOTE: used in http server so don't put folders that should not be accessed remotely
-def get_directory_by_type(type_name):
+def get_directory_by_type(type_name: str) -> str | None:
if type_name == "output":
return get_output_directory()
if type_name == "temp":
@@ -88,7 +92,7 @@ def get_directory_by_type(type_name):
# determine base_dir rely on annotation if name is 'filename.ext [annotation]' format
# otherwise use default_path as base_dir
-def annotated_filepath(name):
+def annotated_filepath(name: str) -> tuple[str, str | None]:
if name.endswith("[output]"):
base_dir = get_output_directory()
name = name[:-9]
@@ -104,7 +108,7 @@ def annotated_filepath(name):
return name, base_dir
-def get_annotated_filepath(name, default_dir=None):
+def get_annotated_filepath(name: str, default_dir: str | None=None) -> str:
name, base_dir = annotated_filepath(name)
if base_dir is None:
@@ -116,7 +120,7 @@ def get_annotated_filepath(name, default_dir=None):
return os.path.join(base_dir, name)
-def exists_annotated_filepath(name):
+def exists_annotated_filepath(name) -> bool:
name, base_dir = annotated_filepath(name)
if base_dir is None:
@@ -126,17 +130,19 @@ def exists_annotated_filepath(name):
return os.path.exists(filepath)
-def add_model_folder_path(folder_name, full_folder_path):
+def add_model_folder_path(folder_name: str, full_folder_path: str) -> None:
global folder_names_and_paths
+ folder_name = map_legacy(folder_name)
if folder_name in folder_names_and_paths:
folder_names_and_paths[folder_name][0].append(full_folder_path)
else:
folder_names_and_paths[folder_name] = ([full_folder_path], set())
-def get_folder_paths(folder_name):
+def get_folder_paths(folder_name: str) -> list[str]:
+ folder_name = map_legacy(folder_name)
return folder_names_and_paths[folder_name][0][:]
-def recursive_search(directory, excluded_dir_names=None):
+def recursive_search(directory: str, excluded_dir_names: list[str] | None=None) -> tuple[list[str], dict[str, float]]:
if not os.path.isdir(directory):
return [], {}
@@ -153,6 +159,10 @@ def recursive_search(directory, excluded_dir_names=None):
logging.warning(f"Warning: Unable to access {directory}. Skipping this path.")
logging.debug("recursive file list on directory {}".format(directory))
+ dirpath: str
+ subdirs: list[str]
+ filenames: list[str]
+
for dirpath, subdirs, filenames in os.walk(directory, followlinks=True, topdown=True):
subdirs[:] = [d for d in subdirs if d not in excluded_dir_names]
for file_name in filenames:
@@ -160,7 +170,7 @@ def recursive_search(directory, excluded_dir_names=None):
result.append(relative_path)
for d in subdirs:
- path = os.path.join(dirpath, d)
+ path: str = os.path.join(dirpath, d)
try:
dirs[path] = os.path.getmtime(path)
except FileNotFoundError:
@@ -169,13 +179,14 @@ def recursive_search(directory, excluded_dir_names=None):
logging.debug("found {} files".format(len(result)))
return result, dirs
-def filter_files_extensions(files, extensions):
+def filter_files_extensions(files: Collection[str], extensions: Collection[str]) -> list[str]:
return sorted(list(filter(lambda a: os.path.splitext(a)[-1].lower() in extensions or len(extensions) == 0, files)))
-def get_full_path(folder_name, filename):
+def get_full_path(folder_name: str, filename: str) -> str | None:
global folder_names_and_paths
+ folder_name = map_legacy(folder_name)
if folder_name not in folder_names_and_paths:
return None
folders = folder_names_and_paths[folder_name]
@@ -189,7 +200,8 @@ def get_full_path(folder_name, filename):
return None
-def get_filename_list_(folder_name):
+def get_filename_list_(folder_name: str) -> tuple[list[str], dict[str, float], float]:
+ folder_name = map_legacy(folder_name)
global folder_names_and_paths
output_list = set()
folders = folder_names_and_paths[folder_name]
@@ -199,11 +211,12 @@ def get_filename_list_(folder_name):
output_list.update(filter_files_extensions(files, folders[1]))
output_folders = {**output_folders, **folders_all}
- return (sorted(list(output_list)), output_folders, time.perf_counter())
+ return sorted(list(output_list)), output_folders, time.perf_counter()
-def cached_filename_list_(folder_name):
+def cached_filename_list_(folder_name: str) -> tuple[list[str], dict[str, float], float] | None:
global filename_list_cache
global folder_names_and_paths
+ folder_name = map_legacy(folder_name)
if folder_name not in filename_list_cache:
return None
out = filename_list_cache[folder_name]
@@ -222,7 +235,8 @@ def cached_filename_list_(folder_name):
return out
-def get_filename_list(folder_name):
+def get_filename_list(folder_name: str) -> list[str]:
+ folder_name = map_legacy(folder_name)
out = cached_filename_list_(folder_name)
if out is None:
out = get_filename_list_(folder_name)
@@ -230,17 +244,17 @@ def get_filename_list(folder_name):
filename_list_cache[folder_name] = out
return list(out[0])
-def get_save_image_path(filename_prefix, output_dir, image_width=0, image_height=0):
- def map_filename(filename):
+def get_save_image_path(filename_prefix: str, output_dir: str, image_width=0, image_height=0) -> tuple[str, str, int, str, str]:
+ def map_filename(filename: str) -> tuple[int, str]:
prefix_len = len(os.path.basename(filename_prefix))
prefix = filename[:prefix_len + 1]
try:
digits = int(filename[prefix_len + 1:].split('_')[0])
except:
digits = 0
- return (digits, prefix)
+ return digits, prefix
- def compute_vars(input, image_width, image_height):
+ def compute_vars(input: str, image_width: int, image_height: int) -> str:
input = input.replace("%width%", str(image_width))
input = input.replace("%height%", str(image_height))
return input
diff --git a/main.py b/main.py
index 479643b84..9bd985149 100644
--- a/main.py
+++ b/main.py
@@ -101,7 +101,7 @@ def cuda_malloc_warning():
logging.warning("\nWARNING: this card most likely does not support cuda-malloc, if you get \"CUDA error\" please run ComfyUI with: --disable-cuda-malloc\n")
def prompt_worker(q, server):
- e = execution.PromptExecutor(server)
+ e = execution.PromptExecutor(server, lru_size=args.cache_lru)
last_gc_collect = 0
need_gc = False
gc_collect_interval = 10.0
@@ -121,7 +121,7 @@ def prompt_worker(q, server):
e.execute(item[2], prompt_id, item[3], item[4])
need_gc = True
q.task_done(item_id,
- e.outputs_ui,
+ e.history_result,
status=execution.PromptQueue.ExecutionStatus(
status_str='success' if e.success else 'error',
completed=e.success,
@@ -242,6 +242,7 @@ if __name__ == "__main__":
folder_paths.add_model_folder_path("checkpoints", os.path.join(folder_paths.get_output_directory(), "checkpoints"))
folder_paths.add_model_folder_path("clip", os.path.join(folder_paths.get_output_directory(), "clip"))
folder_paths.add_model_folder_path("vae", os.path.join(folder_paths.get_output_directory(), "vae"))
+ folder_paths.add_model_folder_path("diffusion_models", os.path.join(folder_paths.get_output_directory(), "diffusion_models"))
if args.input_directory:
input_dir = os.path.abspath(args.input_directory)
@@ -261,6 +262,7 @@ if __name__ == "__main__":
call_on_start = startup_server
try:
+ loop.run_until_complete(server.setup())
loop.run_until_complete(run(server, address=args.listen, port=args.port, verbose=not args.dont_print_server, call_on_start=call_on_start))
except KeyboardInterrupt:
logging.info("\nStopped server")
diff --git a/model_filemanager/__init__.py b/model_filemanager/__init__.py
new file mode 100644
index 000000000..e318351c0
--- /dev/null
+++ b/model_filemanager/__init__.py
@@ -0,0 +1,2 @@
+# model_manager/__init__.py
+from .download_models import download_model, DownloadModelStatus, DownloadStatusType, create_model_path, check_file_exists, track_download_progress, validate_model_subdirectory, validate_filename
diff --git a/model_filemanager/download_models.py b/model_filemanager/download_models.py
new file mode 100644
index 000000000..712d59328
--- /dev/null
+++ b/model_filemanager/download_models.py
@@ -0,0 +1,240 @@
+from __future__ import annotations
+import aiohttp
+import os
+import traceback
+import logging
+from folder_paths import models_dir
+import re
+from typing import Callable, Any, Optional, Awaitable, Dict
+from enum import Enum
+import time
+from dataclasses import dataclass
+
+
+class DownloadStatusType(Enum):
+ PENDING = "pending"
+ IN_PROGRESS = "in_progress"
+ COMPLETED = "completed"
+ ERROR = "error"
+
+@dataclass
+class DownloadModelStatus():
+ status: str
+ progress_percentage: float
+ message: str
+ already_existed: bool = False
+
+ def __init__(self, status: DownloadStatusType, progress_percentage: float, message: str, already_existed: bool):
+ self.status = status.value # Store the string value of the Enum
+ self.progress_percentage = progress_percentage
+ self.message = message
+ self.already_existed = already_existed
+
+ def to_dict(self) -> Dict[str, Any]:
+ return {
+ "status": self.status,
+ "progress_percentage": self.progress_percentage,
+ "message": self.message,
+ "already_existed": self.already_existed
+ }
+
+async def download_model(model_download_request: Callable[[str], Awaitable[aiohttp.ClientResponse]],
+ model_name: str,
+ model_url: str,
+ model_sub_directory: str,
+ progress_callback: Callable[[str, DownloadModelStatus], Awaitable[Any]],
+ progress_interval: float = 1.0) -> DownloadModelStatus:
+ """
+ Download a model file from a given URL into the models directory.
+
+ Args:
+ model_download_request (Callable[[str], Awaitable[aiohttp.ClientResponse]]):
+ A function that makes an HTTP request. This makes it easier to mock in unit tests.
+ model_name (str):
+ The name of the model file to be downloaded. This will be the filename on disk.
+ model_url (str):
+ The URL from which to download the model.
+ model_sub_directory (str):
+ The subdirectory within the main models directory where the model
+ should be saved (e.g., 'checkpoints', 'loras', etc.).
+ progress_callback (Callable[[str, DownloadModelStatus], Awaitable[Any]]):
+ An asynchronous function to call with progress updates.
+
+ Returns:
+ DownloadModelStatus: The result of the download operation.
+ """
+ if not validate_model_subdirectory(model_sub_directory):
+ return DownloadModelStatus(
+ DownloadStatusType.ERROR,
+ 0,
+ "Invalid model subdirectory",
+ False
+ )
+
+ if not validate_filename(model_name):
+ return DownloadModelStatus(
+ DownloadStatusType.ERROR,
+ 0,
+ "Invalid model name",
+ False
+ )
+
+ file_path, relative_path = create_model_path(model_name, model_sub_directory, models_dir)
+ existing_file = await check_file_exists(file_path, model_name, progress_callback, relative_path)
+ if existing_file:
+ return existing_file
+
+ try:
+ status = DownloadModelStatus(DownloadStatusType.PENDING, 0, f"Starting download of {model_name}", False)
+ await progress_callback(relative_path, status)
+
+ response = await model_download_request(model_url)
+ if response.status != 200:
+ error_message = f"Failed to download {model_name}. Status code: {response.status}"
+ logging.error(error_message)
+ status = DownloadModelStatus(DownloadStatusType.ERROR, 0, error_message, False)
+ await progress_callback(relative_path, status)
+ return DownloadModelStatus(DownloadStatusType.ERROR, 0, error_message, False)
+
+ return await track_download_progress(response, file_path, model_name, progress_callback, relative_path, progress_interval)
+
+ except Exception as e:
+ logging.error(f"Error in downloading model: {e}")
+ return await handle_download_error(e, model_name, progress_callback, relative_path)
+
+
+def create_model_path(model_name: str, model_directory: str, models_base_dir: str) -> tuple[str, str]:
+ full_model_dir = os.path.join(models_base_dir, model_directory)
+ os.makedirs(full_model_dir, exist_ok=True)
+ file_path = os.path.join(full_model_dir, model_name)
+
+ # Ensure the resulting path is still within the base directory
+ abs_file_path = os.path.abspath(file_path)
+ abs_base_dir = os.path.abspath(str(models_base_dir))
+ if os.path.commonprefix([abs_file_path, abs_base_dir]) != abs_base_dir:
+ raise Exception(f"Invalid model directory: {model_directory}/{model_name}")
+
+
+ relative_path = '/'.join([model_directory, model_name])
+ return file_path, relative_path
+
+async def check_file_exists(file_path: str,
+ model_name: str,
+ progress_callback: Callable[[str, DownloadModelStatus], Awaitable[Any]],
+ relative_path: str) -> Optional[DownloadModelStatus]:
+ if os.path.exists(file_path):
+ status = DownloadModelStatus(DownloadStatusType.COMPLETED, 100, f"{model_name} already exists", True)
+ await progress_callback(relative_path, status)
+ return status
+ return None
+
+
+async def track_download_progress(response: aiohttp.ClientResponse,
+ file_path: str,
+ model_name: str,
+ progress_callback: Callable[[str, DownloadModelStatus], Awaitable[Any]],
+ relative_path: str,
+ interval: float = 1.0) -> DownloadModelStatus:
+ try:
+ total_size = int(response.headers.get('Content-Length', 0))
+ downloaded = 0
+ last_update_time = time.time()
+
+ async def update_progress():
+ nonlocal last_update_time
+ progress = (downloaded / total_size) * 100 if total_size > 0 else 0
+ status = DownloadModelStatus(DownloadStatusType.IN_PROGRESS, progress, f"Downloading {model_name}", False)
+ await progress_callback(relative_path, status)
+ last_update_time = time.time()
+
+ with open(file_path, 'wb') as f:
+ chunk_iterator = response.content.iter_chunked(8192)
+ while True:
+ try:
+ chunk = await chunk_iterator.__anext__()
+ except StopAsyncIteration:
+ break
+ f.write(chunk)
+ downloaded += len(chunk)
+
+ if time.time() - last_update_time >= interval:
+ await update_progress()
+
+ await update_progress()
+
+ logging.info(f"Successfully downloaded {model_name}. Total downloaded: {downloaded}")
+ status = DownloadModelStatus(DownloadStatusType.COMPLETED, 100, f"Successfully downloaded {model_name}", False)
+ await progress_callback(relative_path, status)
+
+ return status
+ except Exception as e:
+ logging.error(f"Error in track_download_progress: {e}")
+ logging.error(traceback.format_exc())
+ return await handle_download_error(e, model_name, progress_callback, relative_path)
+
+async def handle_download_error(e: Exception,
+ model_name: str,
+ progress_callback: Callable[[str, DownloadModelStatus], Any],
+ relative_path: str) -> DownloadModelStatus:
+ error_message = f"Error downloading {model_name}: {str(e)}"
+ status = DownloadModelStatus(DownloadStatusType.ERROR, 0, error_message, False)
+ await progress_callback(relative_path, status)
+ return status
+
+def validate_model_subdirectory(model_subdirectory: str) -> bool:
+ """
+ Validate that the model subdirectory is safe to install into.
+ Must not contain relative paths, nested paths or special characters
+ other than underscores and hyphens.
+
+ Args:
+ model_subdirectory (str): The subdirectory for the specific model type.
+
+ Returns:
+ bool: True if the subdirectory is safe, False otherwise.
+ """
+ if len(model_subdirectory) > 50:
+ return False
+
+ if '..' in model_subdirectory or '/' in model_subdirectory:
+ return False
+
+ if not re.match(r'^[a-zA-Z0-9_-]+$', model_subdirectory):
+ return False
+
+ return True
+
+def validate_filename(filename: str)-> bool:
+ """
+ Validate a filename to ensure it's safe and doesn't contain any path traversal attempts.
+
+ Args:
+ filename (str): The filename to validate
+
+ Returns:
+ bool: True if the filename is valid, False otherwise
+ """
+ if not filename.lower().endswith(('.sft', '.safetensors')):
+ return False
+
+ # Check if the filename is empty, None, or just whitespace
+ if not filename or not filename.strip():
+ return False
+
+ # Check for any directory traversal attempts or invalid characters
+ if any(char in filename for char in ['..', '/', '\\', '\n', '\r', '\t', '\0']):
+ return False
+
+ # Check if the filename starts with a dot (hidden file)
+ if filename.startswith('.'):
+ return False
+
+ # Use a whitelist of allowed characters
+ if not re.match(r'^[a-zA-Z0-9_\-. ]+$', filename):
+ return False
+
+ # Ensure the filename isn't too long
+ if len(filename) > 255:
+ return False
+
+ return True
diff --git a/models/diffusion_models/put_diffusion_model_files_here b/models/diffusion_models/put_diffusion_model_files_here
new file mode 100644
index 000000000..e69de29bb
diff --git a/nodes.py b/nodes.py
index e296597c5..513cd0c7f 100644
--- a/nodes.py
+++ b/nodes.py
@@ -47,11 +47,18 @@ MAX_RESOLUTION=16384
class CLIPTextEncode:
@classmethod
def INPUT_TYPES(s):
- return {"required": {"text": ("STRING", {"multiline": True, "dynamicPrompts": True}), "clip": ("CLIP", )}}
+ return {
+ "required": {
+ "text": ("STRING", {"multiline": True, "dynamicPrompts": True, "tooltip": "The text to be encoded."}),
+ "clip": ("CLIP", {"tooltip": "The CLIP model used for encoding the text."})
+ }
+ }
RETURN_TYPES = ("CONDITIONING",)
+ OUTPUT_TOOLTIPS = ("A conditioning containing the embedded text used to guide the diffusion model.",)
FUNCTION = "encode"
CATEGORY = "conditioning"
+ DESCRIPTION = "Encodes a text prompt using a CLIP model into an embedding that can be used to guide the diffusion model towards generating specific images."
def encode(self, clip, text):
tokens = clip.tokenize(text)
@@ -260,11 +267,18 @@ class ConditioningSetTimestepRange:
class VAEDecode:
@classmethod
def INPUT_TYPES(s):
- return {"required": { "samples": ("LATENT", ), "vae": ("VAE", )}}
+ return {
+ "required": {
+ "samples": ("LATENT", {"tooltip": "The latent to be decoded."}),
+ "vae": ("VAE", {"tooltip": "The VAE model used for decoding the latent."})
+ }
+ }
RETURN_TYPES = ("IMAGE",)
+ OUTPUT_TOOLTIPS = ("The decoded image.",)
FUNCTION = "decode"
CATEGORY = "latent"
+ DESCRIPTION = "Decodes latent images back into pixel space images."
def decode(self, vae, samples):
return (vae.decode(samples["samples"]), )
@@ -506,12 +520,19 @@ class CheckpointLoader:
class CheckpointLoaderSimple:
@classmethod
def INPUT_TYPES(s):
- return {"required": { "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ),
- }}
+ return {
+ "required": {
+ "ckpt_name": (folder_paths.get_filename_list("checkpoints"), {"tooltip": "The name of the checkpoint (model) to load."}),
+ }
+ }
RETURN_TYPES = ("MODEL", "CLIP", "VAE")
+ OUTPUT_TOOLTIPS = ("The model used for denoising latents.",
+ "The CLIP model used for encoding text prompts.",
+ "The VAE model used for encoding and decoding images to and from latent space.")
FUNCTION = "load_checkpoint"
CATEGORY = "loaders"
+ DESCRIPTION = "Loads a diffusion model checkpoint, diffusion models are used to denoise latents."
def load_checkpoint(self, ckpt_name):
ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
@@ -582,16 +603,22 @@ class LoraLoader:
@classmethod
def INPUT_TYPES(s):
- return {"required": { "model": ("MODEL",),
- "clip": ("CLIP", ),
- "lora_name": (folder_paths.get_filename_list("loras"), ),
- "strength_model": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01}),
- "strength_clip": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01}),
- }}
+ return {
+ "required": {
+ "model": ("MODEL", {"tooltip": "The diffusion model the LoRA will be applied to."}),
+ "clip": ("CLIP", {"tooltip": "The CLIP model the LoRA will be applied to."}),
+ "lora_name": (folder_paths.get_filename_list("loras"), {"tooltip": "The name of the LoRA."}),
+ "strength_model": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01, "tooltip": "How strongly to modify the diffusion model. This value can be negative."}),
+ "strength_clip": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01, "tooltip": "How strongly to modify the CLIP model. This value can be negative."}),
+ }
+ }
+
RETURN_TYPES = ("MODEL", "CLIP")
+ OUTPUT_TOOLTIPS = ("The modified diffusion model.", "The modified CLIP model.")
FUNCTION = "load_lora"
CATEGORY = "loaders"
+ DESCRIPTION = "LoRAs are used to modify diffusion and CLIP models, altering the way in which latents are denoised such as applying styles. Multiple LoRA nodes can be linked together."
def load_lora(self, model, clip, lora_name, strength_model, strength_clip):
if strength_model == 0 and strength_clip == 0:
@@ -638,6 +665,8 @@ class VAELoader:
sd1_taesd_dec = False
sd3_taesd_enc = False
sd3_taesd_dec = False
+ f1_taesd_enc = False
+ f1_taesd_dec = False
for v in approx_vaes:
if v.startswith("taesd_decoder."):
@@ -652,12 +681,18 @@ class VAELoader:
sd3_taesd_dec = True
elif v.startswith("taesd3_encoder."):
sd3_taesd_enc = True
+ elif v.startswith("taef1_encoder."):
+ f1_taesd_dec = True
+ elif v.startswith("taef1_decoder."):
+ f1_taesd_enc = True
if sd1_taesd_dec and sd1_taesd_enc:
vaes.append("taesd")
if sdxl_taesd_dec and sdxl_taesd_enc:
vaes.append("taesdxl")
if sd3_taesd_dec and sd3_taesd_enc:
vaes.append("taesd3")
+ if f1_taesd_dec and f1_taesd_enc:
+ vaes.append("taef1")
return vaes
@staticmethod
@@ -685,6 +720,9 @@ class VAELoader:
elif name == "taesd3":
sd["vae_scale"] = torch.tensor(1.5305)
sd["vae_shift"] = torch.tensor(0.0609)
+ elif name == "taef1":
+ sd["vae_scale"] = torch.tensor(0.3611)
+ sd["vae_shift"] = torch.tensor(0.1159)
return sd
@classmethod
@@ -697,7 +735,7 @@ class VAELoader:
#TODO: scale factor?
def load_vae(self, vae_name):
- if vae_name in ["taesd", "taesdxl", "taesd3"]:
+ if vae_name in ["taesd", "taesdxl", "taesd3", "taef1"]:
sd = self.load_taesd(vae_name)
else:
vae_path = folder_paths.get_full_path("vae", vae_name)
@@ -817,7 +855,7 @@ class ControlNetApplyAdvanced:
class UNETLoader:
@classmethod
def INPUT_TYPES(s):
- return {"required": { "unet_name": (folder_paths.get_filename_list("unet"), ),
+ return {"required": { "unet_name": (folder_paths.get_filename_list("diffusion_models"), ),
"weight_dtype": (["default", "fp8_e4m3fn", "fp8_e5m2"],)
}}
RETURN_TYPES = ("MODEL",)
@@ -826,14 +864,14 @@ class UNETLoader:
CATEGORY = "advanced/loaders"
def load_unet(self, unet_name, weight_dtype):
- dtype = None
+ model_options = {}
if weight_dtype == "fp8_e4m3fn":
- dtype = torch.float8_e4m3fn
+ model_options["dtype"] = torch.float8_e4m3fn
elif weight_dtype == "fp8_e5m2":
- dtype = torch.float8_e5m2
+ model_options["dtype"] = torch.float8_e5m2
- unet_path = folder_paths.get_full_path("unet", unet_name)
- model = comfy.sd.load_unet(unet_path, dtype=dtype)
+ unet_path = folder_paths.get_full_path("diffusion_models", unet_name)
+ model = comfy.sd.load_diffusion_model(unet_path, model_options=model_options)
return (model,)
class CLIPLoader:
@@ -1033,13 +1071,19 @@ class EmptyLatentImage:
@classmethod
def INPUT_TYPES(s):
- return {"required": { "width": ("INT", {"default": 512, "min": 16, "max": MAX_RESOLUTION, "step": 8}),
- "height": ("INT", {"default": 512, "min": 16, "max": MAX_RESOLUTION, "step": 8}),
- "batch_size": ("INT", {"default": 1, "min": 1, "max": 4096})}}
+ return {
+ "required": {
+ "width": ("INT", {"default": 512, "min": 16, "max": MAX_RESOLUTION, "step": 8, "tooltip": "The width of the latent images in pixels."}),
+ "height": ("INT", {"default": 512, "min": 16, "max": MAX_RESOLUTION, "step": 8, "tooltip": "The height of the latent images in pixels."}),
+ "batch_size": ("INT", {"default": 1, "min": 1, "max": 4096, "tooltip": "The number of latent images in the batch."})
+ }
+ }
RETURN_TYPES = ("LATENT",)
+ OUTPUT_TOOLTIPS = ("The empty latent image batch.",)
FUNCTION = "generate"
CATEGORY = "latent"
+ DESCRIPTION = "Create a new batch of empty latent images to be denoised via sampling."
def generate(self, width, height, batch_size=1):
latent = torch.zeros([batch_size, 4, height // 8, width // 8], device=self.device)
@@ -1359,24 +1403,27 @@ def common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive,
class KSampler:
@classmethod
def INPUT_TYPES(s):
- return {"required":
- {"model": ("MODEL",),
- "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
- "steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
- "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
- "sampler_name": (comfy.samplers.KSampler.SAMPLERS, ),
- "scheduler": (comfy.samplers.KSampler.SCHEDULERS, ),
- "positive": ("CONDITIONING", ),
- "negative": ("CONDITIONING", ),
- "latent_image": ("LATENT", ),
- "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
- }
- }
+ return {
+ "required": {
+ "model": ("MODEL", {"tooltip": "The model used for denoising the input latent."}),
+ "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "tooltip": "The random seed used for creating the noise."}),
+ "steps": ("INT", {"default": 20, "min": 1, "max": 10000, "tooltip": "The number of steps used in the denoising process."}),
+ "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01, "tooltip": "The Classifier-Free Guidance scale balances creativity and adherence to the prompt. Higher values result in images more closely matching the prompt however too high values will negatively impact quality."}),
+ "sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"tooltip": "The algorithm used when sampling, this can affect the quality, speed, and style of the generated output."}),
+ "scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"tooltip": "The scheduler controls how noise is gradually removed to form the image."}),
+ "positive": ("CONDITIONING", {"tooltip": "The conditioning describing the attributes you want to include in the image."}),
+ "negative": ("CONDITIONING", {"tooltip": "The conditioning describing the attributes you want to exclude from the image."}),
+ "latent_image": ("LATENT", {"tooltip": "The latent image to denoise."}),
+ "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "The amount of denoising applied, lower values will maintain the structure of the initial image allowing for image to image sampling."}),
+ }
+ }
RETURN_TYPES = ("LATENT",)
+ OUTPUT_TOOLTIPS = ("The denoised latent.",)
FUNCTION = "sample"
CATEGORY = "sampling"
+ DESCRIPTION = "Uses the provided model, positive and negative conditioning to denoise the latent image."
def sample(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise=1.0):
return common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise=denoise)
@@ -1424,11 +1471,15 @@ class SaveImage:
@classmethod
def INPUT_TYPES(s):
- return {"required":
- {"images": ("IMAGE", ),
- "filename_prefix": ("STRING", {"default": "ComfyUI"})},
- "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
- }
+ return {
+ "required": {
+ "images": ("IMAGE", {"tooltip": "The images to save."}),
+ "filename_prefix": ("STRING", {"default": "ComfyUI", "tooltip": "The prefix for the file to save. This may include formatting information such as %date:yyyy-MM-dd% or %Empty Latent Image.width% to include values from nodes."})
+ },
+ "hidden": {
+ "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"
+ },
+ }
RETURN_TYPES = ()
FUNCTION = "save_images"
@@ -1436,6 +1487,7 @@ class SaveImage:
OUTPUT_NODE = True
CATEGORY = "image"
+ DESCRIPTION = "Saves the input images to your ComfyUI output directory."
def save_images(self, images, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None):
filename_prefix += self.prefix_append
diff --git a/pytest.ini b/pytest.ini
index 8b7a747e7..a224d8cbb 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,6 +1,7 @@
[pytest]
markers =
inference: mark as inference test (deselect with '-m "not inference"')
+ execution: mark as execution test (deselect with '-m "not execution"')
testpaths =
tests
tests-unit
diff --git a/server.py b/server.py
index 1c07f9781..a2bb376a4 100644
--- a/server.py
+++ b/server.py
@@ -12,7 +12,6 @@ import json
import glob
import struct
import ssl
-import hashlib
from PIL import Image, ImageOps
from PIL.PngImagePlugin import PngInfo
from io import BytesIO
@@ -28,7 +27,8 @@ import comfy.model_management
import node_helpers
from app.frontend_management import FrontendManager
from app.user_manager import UserManager
-
+from model_filemanager import download_model, DownloadModelStatus
+from typing import Optional
class BinaryEventTypes:
PREVIEW_IMAGE = 1
@@ -76,6 +76,7 @@ class PromptServer():
self.prompt_queue = None
self.loop = loop
self.messages = asyncio.Queue()
+ self.client_session:Optional[aiohttp.ClientSession] = None
self.number = 0
middlewares = [cache_control]
@@ -422,6 +423,7 @@ class PromptServer():
obj_class = nodes.NODE_CLASS_MAPPINGS[node_class]
info = {}
info['input'] = obj_class.INPUT_TYPES()
+ info['input_order'] = {key: list(value.keys()) for (key, value) in obj_class.INPUT_TYPES().items()}
info['output'] = obj_class.RETURN_TYPES
info['output_is_list'] = obj_class.OUTPUT_IS_LIST if hasattr(obj_class, 'OUTPUT_IS_LIST') else [False] * len(obj_class.RETURN_TYPES)
info['output_name'] = obj_class.RETURN_NAMES if hasattr(obj_class, 'RETURN_NAMES') else info['output']
@@ -437,6 +439,9 @@ class PromptServer():
if hasattr(obj_class, 'CATEGORY'):
info['category'] = obj_class.CATEGORY
+
+ if hasattr(obj_class, 'OUTPUT_TOOLTIPS'):
+ info['output_tooltips'] = obj_class.OUTPUT_TOOLTIPS
return info
@routes.get("/object_info")
@@ -559,6 +564,36 @@ class PromptServer():
self.prompt_queue.delete_history_item(id_to_delete)
return web.Response(status=200)
+
+ # Internal route. Should not be depended upon and is subject to change at any time.
+ # TODO(robinhuang): Move to internal route table class once we refactor PromptServer to pass around Websocket.
+ @routes.post("/internal/models/download")
+ async def download_handler(request):
+ async def report_progress(filename: str, status: DownloadModelStatus):
+ await self.send_json("download_progress", status.to_dict())
+
+ data = await request.json()
+ url = data.get('url')
+ model_directory = data.get('model_directory')
+ model_filename = data.get('model_filename')
+ progress_interval = data.get('progress_interval', 1.0) # In seconds, how often to report download progress.
+
+ if not url or not model_directory or not model_filename:
+ return web.json_response({"status": "error", "message": "Missing URL or folder path or filename"}, status=400)
+
+ session = self.client_session
+ if session is None:
+ logging.error("Client session is not initialized")
+ return web.Response(status=500)
+
+ task = asyncio.create_task(download_model(lambda url: session.get(url), model_filename, url, model_directory, report_progress, progress_interval))
+ await task
+
+ return web.json_response(task.result().to_dict())
+
+ async def setup(self):
+ timeout = aiohttp.ClientTimeout(total=None) # no timeout
+ self.client_session = aiohttp.ClientSession(timeout=timeout)
def add_routes(self):
self.user_manager.add_routes(self.routes)
@@ -680,6 +715,9 @@ class PromptServer():
site = web.TCPSite(runner, address, port, ssl_context=ssl_ctx)
await site.start()
+ self.address = address
+ self.port = port
+
if verbose:
logging.info("Starting server\n")
logging.info("To see the GUI go to: {}://{}:{}".format(scheme, address, port))
diff --git a/tests-ui/.gitignore b/tests-ui/.gitignore
deleted file mode 100644
index b512c09d4..000000000
--- a/tests-ui/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-node_modules
\ No newline at end of file
diff --git a/tests-ui/afterSetup.js b/tests-ui/afterSetup.js
deleted file mode 100644
index 983f3af64..000000000
--- a/tests-ui/afterSetup.js
+++ /dev/null
@@ -1,9 +0,0 @@
-const { start } = require("./utils");
-const lg = require("./utils/litegraph");
-
-// Load things once per test file before to ensure its all warmed up for the tests
-beforeAll(async () => {
- lg.setup(global);
- await start({ resetEnv: true });
- lg.teardown(global);
-});
diff --git a/tests-ui/babel.config.json b/tests-ui/babel.config.json
deleted file mode 100644
index f27d6c397..000000000
--- a/tests-ui/babel.config.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "presets": ["@babel/preset-env"],
- "plugins": ["babel-plugin-transform-import-meta"]
-}
diff --git a/tests-ui/globalSetup.js b/tests-ui/globalSetup.js
deleted file mode 100644
index b9d97f58a..000000000
--- a/tests-ui/globalSetup.js
+++ /dev/null
@@ -1,14 +0,0 @@
-module.exports = async function () {
- global.ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
- };
-
- const { nop } = require("./utils/nopProxy");
- global.enableWebGLCanvas = nop;
-
- HTMLCanvasElement.prototype.getContext = nop;
-
- localStorage["Comfy.Settings.Comfy.Logging.Enabled"] = "false";
-};
diff --git a/tests-ui/jest.config.js b/tests-ui/jest.config.js
deleted file mode 100644
index 86fff5057..000000000
--- a/tests-ui/jest.config.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/** @type {import('jest').Config} */
-const config = {
- testEnvironment: "jsdom",
- setupFiles: ["./globalSetup.js"],
- setupFilesAfterEnv: ["./afterSetup.js"],
- clearMocks: true,
- resetModules: true,
- testTimeout: 10000
-};
-
-module.exports = config;
diff --git a/tests-ui/package-lock.json b/tests-ui/package-lock.json
deleted file mode 100644
index 0f409ca24..000000000
--- a/tests-ui/package-lock.json
+++ /dev/null
@@ -1,5586 +0,0 @@
-{
- "name": "comfui-tests",
- "version": "1.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "comfui-tests",
- "version": "1.0.0",
- "license": "GPL-3.0",
- "devDependencies": {
- "@babel/preset-env": "^7.22.20",
- "@types/jest": "^29.5.5",
- "babel-plugin-transform-import-meta": "^2.2.1",
- "jest": "^29.7.0",
- "jest-environment-jsdom": "^29.7.0"
- }
- },
- "node_modules/@ampproject/remapping": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
- "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
- "dev": true,
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.0",
- "@jridgewell/trace-mapping": "^0.3.9"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@babel/code-frame": {
- "version": "7.22.13",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
- "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
- "dev": true,
- "dependencies": {
- "@babel/highlight": "^7.22.13",
- "chalk": "^2.4.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/code-frame/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/code-frame/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/code-frame/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/@babel/code-frame/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true
- },
- "node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/@babel/code-frame/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/code-frame/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/compat-data": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz",
- "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/core": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz",
- "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==",
- "dev": true,
- "dependencies": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.22.13",
- "@babel/generator": "^7.23.0",
- "@babel/helper-compilation-targets": "^7.22.15",
- "@babel/helper-module-transforms": "^7.23.0",
- "@babel/helpers": "^7.23.0",
- "@babel/parser": "^7.23.0",
- "@babel/template": "^7.22.15",
- "@babel/traverse": "^7.23.0",
- "@babel/types": "^7.23.0",
- "convert-source-map": "^2.0.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.3",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/babel"
- }
- },
- "node_modules/@babel/generator": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
- "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.23.0",
- "@jridgewell/gen-mapping": "^0.3.2",
- "@jridgewell/trace-mapping": "^0.3.17",
- "jsesc": "^2.5.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-annotate-as-pure": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
- "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz",
- "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.15"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz",
- "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==",
- "dev": true,
- "dependencies": {
- "@babel/compat-data": "^7.22.9",
- "@babel/helper-validator-option": "^7.22.15",
- "browserslist": "^4.21.9",
- "lru-cache": "^5.1.1",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz",
- "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-environment-visitor": "^7.22.5",
- "@babel/helper-function-name": "^7.22.5",
- "@babel/helper-member-expression-to-functions": "^7.22.15",
- "@babel/helper-optimise-call-expression": "^7.22.5",
- "@babel/helper-replace-supers": "^7.22.9",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
- "@babel/helper-split-export-declaration": "^7.22.6",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-create-regexp-features-plugin": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz",
- "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==",
- "dev": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "regexpu-core": "^5.3.1",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-define-polyfill-provider": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz",
- "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-compilation-targets": "^7.22.6",
- "@babel/helper-plugin-utils": "^7.22.5",
- "debug": "^4.1.1",
- "lodash.debounce": "^4.0.8",
- "resolve": "^1.14.2"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/@babel/helper-environment-visitor": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
- "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-function-name": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
- "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.22.15",
- "@babel/types": "^7.23.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-hoist-variables": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
- "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-member-expression-to-functions": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz",
- "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.23.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-imports": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
- "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.15"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-transforms": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz",
- "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-module-imports": "^7.22.15",
- "@babel/helper-simple-access": "^7.22.5",
- "@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/helper-validator-identifier": "^7.22.20"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-optimise-call-expression": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz",
- "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-plugin-utils": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
- "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-remap-async-to-generator": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz",
- "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-wrap-function": "^7.22.20"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-replace-supers": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz",
- "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-member-expression-to-functions": "^7.22.15",
- "@babel/helper-optimise-call-expression": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-simple-access": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
- "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz",
- "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-split-export-declaration": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
- "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-string-parser": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
- "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-identifier": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
- "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-option": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz",
- "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-wrap-function": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz",
- "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-function-name": "^7.22.5",
- "@babel/template": "^7.22.15",
- "@babel/types": "^7.22.19"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helpers": {
- "version": "7.23.1",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz",
- "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.22.15",
- "@babel/traverse": "^7.23.0",
- "@babel/types": "^7.23.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/highlight": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
- "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.22.20",
- "chalk": "^2.4.2",
- "js-tokens": "^4.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/highlight/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/highlight/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/highlight/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/@babel/highlight/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true
- },
- "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/@babel/highlight/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/highlight/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/parser": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
- "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
- "dev": true,
- "bin": {
- "parser": "bin/babel-parser.js"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz",
- "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz",
- "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
- "@babel/plugin-transform-optional-chaining": "^7.22.15"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.13.0"
- }
- },
- "node_modules/@babel/plugin-proposal-private-property-in-object": {
- "version": "7.21.0-placeholder-for-preset-env.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
- "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-async-generators": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
- "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-bigint": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
- "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-class-properties": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
- "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.12.13"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-class-static-block": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
- "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.14.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-dynamic-import": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
- "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-export-namespace-from": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
- "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.3"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-import-assertions": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz",
- "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-import-attributes": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz",
- "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-import-meta": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
- "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.10.4"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-json-strings": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
- "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-jsx": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz",
- "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
- "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.10.4"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-numeric-separator": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
- "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.10.4"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-object-rest-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
- "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-optional-catch-binding": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
- "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-private-property-in-object": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
- "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.14.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-top-level-await": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
- "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.14.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-typescript": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz",
- "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
- "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.18.6",
- "@babel/helper-plugin-utils": "^7.18.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-arrow-functions": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz",
- "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-async-generator-functions": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz",
- "integrity": "sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==",
- "dev": true,
- "dependencies": {
- "@babel/helper-environment-visitor": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-remap-async-to-generator": "^7.22.9",
- "@babel/plugin-syntax-async-generators": "^7.8.4"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-async-to-generator": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz",
- "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-module-imports": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-remap-async-to-generator": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-block-scoped-functions": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz",
- "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-block-scoping": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz",
- "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-class-properties": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz",
- "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-class-static-block": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz",
- "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==",
- "dev": true,
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.22.11",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-class-static-block": "^7.14.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.12.0"
- }
- },
- "node_modules/@babel/plugin-transform-classes": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz",
- "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-compilation-targets": "^7.22.15",
- "@babel/helper-environment-visitor": "^7.22.5",
- "@babel/helper-function-name": "^7.22.5",
- "@babel/helper-optimise-call-expression": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-replace-supers": "^7.22.9",
- "@babel/helper-split-export-declaration": "^7.22.6",
- "globals": "^11.1.0"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-computed-properties": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz",
- "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/template": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-destructuring": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz",
- "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-dotall-regex": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz",
- "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-duplicate-keys": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz",
- "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-dynamic-import": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz",
- "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-exponentiation-operator": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz",
- "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==",
- "dev": true,
- "dependencies": {
- "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-export-namespace-from": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz",
- "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-for-of": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz",
- "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-function-name": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz",
- "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-compilation-targets": "^7.22.5",
- "@babel/helper-function-name": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-json-strings": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz",
- "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-json-strings": "^7.8.3"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-literals": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz",
- "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-logical-assignment-operators": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz",
- "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-member-expression-literals": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz",
- "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-amd": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz",
- "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-module-transforms": "^7.23.0",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-commonjs": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz",
- "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-module-transforms": "^7.23.0",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-simple-access": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-systemjs": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz",
- "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-hoist-variables": "^7.22.5",
- "@babel/helper-module-transforms": "^7.23.0",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-validator-identifier": "^7.22.20"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-umd": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz",
- "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-module-transforms": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz",
- "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-new-target": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz",
- "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz",
- "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-numeric-separator": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz",
- "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-numeric-separator": "^7.10.4"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-object-rest-spread": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz",
- "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==",
- "dev": true,
- "dependencies": {
- "@babel/compat-data": "^7.22.9",
- "@babel/helper-compilation-targets": "^7.22.15",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-transform-parameters": "^7.22.15"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-object-super": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz",
- "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-replace-supers": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-optional-catch-binding": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz",
- "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-optional-chaining": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz",
- "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-parameters": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz",
- "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-private-methods": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz",
- "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-private-property-in-object": {
- "version": "7.22.11",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz",
- "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.22.5",
- "@babel/helper-create-class-features-plugin": "^7.22.11",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-property-literals": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz",
- "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-regenerator": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz",
- "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "regenerator-transform": "^0.15.2"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-reserved-words": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz",
- "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-shorthand-properties": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz",
- "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-spread": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz",
- "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-sticky-regex": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz",
- "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-template-literals": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz",
- "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-typeof-symbol": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz",
- "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-escapes": {
- "version": "7.22.10",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz",
- "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-property-regex": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz",
- "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==",
- "dev": true,
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-regex": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz",
- "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-sets-regex": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz",
- "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.22.5",
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/preset-env": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.20.tgz",
- "integrity": "sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==",
- "dev": true,
- "dependencies": {
- "@babel/compat-data": "^7.22.20",
- "@babel/helper-compilation-targets": "^7.22.15",
- "@babel/helper-plugin-utils": "^7.22.5",
- "@babel/helper-validator-option": "^7.22.15",
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15",
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15",
- "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
- "@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/plugin-syntax-class-properties": "^7.12.13",
- "@babel/plugin-syntax-class-static-block": "^7.14.5",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
- "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
- "@babel/plugin-syntax-import-assertions": "^7.22.5",
- "@babel/plugin-syntax-import-attributes": "^7.22.5",
- "@babel/plugin-syntax-import-meta": "^7.10.4",
- "@babel/plugin-syntax-json-strings": "^7.8.3",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.10.4",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
- "@babel/plugin-syntax-top-level-await": "^7.14.5",
- "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
- "@babel/plugin-transform-arrow-functions": "^7.22.5",
- "@babel/plugin-transform-async-generator-functions": "^7.22.15",
- "@babel/plugin-transform-async-to-generator": "^7.22.5",
- "@babel/plugin-transform-block-scoped-functions": "^7.22.5",
- "@babel/plugin-transform-block-scoping": "^7.22.15",
- "@babel/plugin-transform-class-properties": "^7.22.5",
- "@babel/plugin-transform-class-static-block": "^7.22.11",
- "@babel/plugin-transform-classes": "^7.22.15",
- "@babel/plugin-transform-computed-properties": "^7.22.5",
- "@babel/plugin-transform-destructuring": "^7.22.15",
- "@babel/plugin-transform-dotall-regex": "^7.22.5",
- "@babel/plugin-transform-duplicate-keys": "^7.22.5",
- "@babel/plugin-transform-dynamic-import": "^7.22.11",
- "@babel/plugin-transform-exponentiation-operator": "^7.22.5",
- "@babel/plugin-transform-export-namespace-from": "^7.22.11",
- "@babel/plugin-transform-for-of": "^7.22.15",
- "@babel/plugin-transform-function-name": "^7.22.5",
- "@babel/plugin-transform-json-strings": "^7.22.11",
- "@babel/plugin-transform-literals": "^7.22.5",
- "@babel/plugin-transform-logical-assignment-operators": "^7.22.11",
- "@babel/plugin-transform-member-expression-literals": "^7.22.5",
- "@babel/plugin-transform-modules-amd": "^7.22.5",
- "@babel/plugin-transform-modules-commonjs": "^7.22.15",
- "@babel/plugin-transform-modules-systemjs": "^7.22.11",
- "@babel/plugin-transform-modules-umd": "^7.22.5",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5",
- "@babel/plugin-transform-new-target": "^7.22.5",
- "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11",
- "@babel/plugin-transform-numeric-separator": "^7.22.11",
- "@babel/plugin-transform-object-rest-spread": "^7.22.15",
- "@babel/plugin-transform-object-super": "^7.22.5",
- "@babel/plugin-transform-optional-catch-binding": "^7.22.11",
- "@babel/plugin-transform-optional-chaining": "^7.22.15",
- "@babel/plugin-transform-parameters": "^7.22.15",
- "@babel/plugin-transform-private-methods": "^7.22.5",
- "@babel/plugin-transform-private-property-in-object": "^7.22.11",
- "@babel/plugin-transform-property-literals": "^7.22.5",
- "@babel/plugin-transform-regenerator": "^7.22.10",
- "@babel/plugin-transform-reserved-words": "^7.22.5",
- "@babel/plugin-transform-shorthand-properties": "^7.22.5",
- "@babel/plugin-transform-spread": "^7.22.5",
- "@babel/plugin-transform-sticky-regex": "^7.22.5",
- "@babel/plugin-transform-template-literals": "^7.22.5",
- "@babel/plugin-transform-typeof-symbol": "^7.22.5",
- "@babel/plugin-transform-unicode-escapes": "^7.22.10",
- "@babel/plugin-transform-unicode-property-regex": "^7.22.5",
- "@babel/plugin-transform-unicode-regex": "^7.22.5",
- "@babel/plugin-transform-unicode-sets-regex": "^7.22.5",
- "@babel/preset-modules": "0.1.6-no-external-plugins",
- "@babel/types": "^7.22.19",
- "babel-plugin-polyfill-corejs2": "^0.4.5",
- "babel-plugin-polyfill-corejs3": "^0.8.3",
- "babel-plugin-polyfill-regenerator": "^0.5.2",
- "core-js-compat": "^3.31.0",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/preset-modules": {
- "version": "0.1.6-no-external-plugins",
- "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
- "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@babel/types": "^7.4.4",
- "esutils": "^2.0.2"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/@babel/regjsgen": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz",
- "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==",
- "dev": true
- },
- "node_modules/@babel/runtime": {
- "version": "7.23.1",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz",
- "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==",
- "dev": true,
- "dependencies": {
- "regenerator-runtime": "^0.14.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/template": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
- "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.22.13",
- "@babel/parser": "^7.22.15",
- "@babel/types": "^7.22.15"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/traverse": {
- "version": "7.23.2",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
- "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.22.13",
- "@babel/generator": "^7.23.0",
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-function-name": "^7.23.0",
- "@babel/helper-hoist-variables": "^7.22.5",
- "@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/parser": "^7.23.0",
- "@babel/types": "^7.23.0",
- "debug": "^4.1.0",
- "globals": "^11.1.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/types": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
- "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-string-parser": "^7.22.5",
- "@babel/helper-validator-identifier": "^7.22.20",
- "to-fast-properties": "^2.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@bcoe/v8-coverage": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
- "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
- "dev": true
- },
- "node_modules/@istanbuljs/load-nyc-config": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
- "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
- "dev": true,
- "dependencies": {
- "camelcase": "^5.3.1",
- "find-up": "^4.1.0",
- "get-package-type": "^0.1.0",
- "js-yaml": "^3.13.1",
- "resolve-from": "^5.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@jest/console": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
- "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "jest-message-util": "^29.7.0",
- "jest-util": "^29.7.0",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/core": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
- "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
- "dev": true,
- "dependencies": {
- "@jest/console": "^29.7.0",
- "@jest/reporters": "^29.7.0",
- "@jest/test-result": "^29.7.0",
- "@jest/transform": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.9",
- "jest-changed-files": "^29.7.0",
- "jest-config": "^29.7.0",
- "jest-haste-map": "^29.7.0",
- "jest-message-util": "^29.7.0",
- "jest-regex-util": "^29.6.3",
- "jest-resolve": "^29.7.0",
- "jest-resolve-dependencies": "^29.7.0",
- "jest-runner": "^29.7.0",
- "jest-runtime": "^29.7.0",
- "jest-snapshot": "^29.7.0",
- "jest-util": "^29.7.0",
- "jest-validate": "^29.7.0",
- "jest-watcher": "^29.7.0",
- "micromatch": "^4.0.4",
- "pretty-format": "^29.7.0",
- "slash": "^3.0.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "peerDependencies": {
- "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
- },
- "peerDependenciesMeta": {
- "node-notifier": {
- "optional": true
- }
- }
- },
- "node_modules/@jest/environment": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
- "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
- "dev": true,
- "dependencies": {
- "@jest/fake-timers": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "jest-mock": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/expect": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
- "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
- "dev": true,
- "dependencies": {
- "expect": "^29.7.0",
- "jest-snapshot": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/expect-utils": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
- "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
- "dev": true,
- "dependencies": {
- "jest-get-type": "^29.6.3"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/fake-timers": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
- "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "@sinonjs/fake-timers": "^10.0.2",
- "@types/node": "*",
- "jest-message-util": "^29.7.0",
- "jest-mock": "^29.7.0",
- "jest-util": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/globals": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
- "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^29.7.0",
- "@jest/expect": "^29.7.0",
- "@jest/types": "^29.6.3",
- "jest-mock": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/reporters": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
- "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
- "dev": true,
- "dependencies": {
- "@bcoe/v8-coverage": "^0.2.3",
- "@jest/console": "^29.7.0",
- "@jest/test-result": "^29.7.0",
- "@jest/transform": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@jridgewell/trace-mapping": "^0.3.18",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "collect-v8-coverage": "^1.0.0",
- "exit": "^0.1.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-instrument": "^6.0.0",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.1.3",
- "jest-message-util": "^29.7.0",
- "jest-util": "^29.7.0",
- "jest-worker": "^29.7.0",
- "slash": "^3.0.0",
- "string-length": "^4.0.1",
- "strip-ansi": "^6.0.0",
- "v8-to-istanbul": "^9.0.1"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "peerDependencies": {
- "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
- },
- "peerDependenciesMeta": {
- "node-notifier": {
- "optional": true
- }
- }
- },
- "node_modules/@jest/schemas": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
- "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
- "dev": true,
- "dependencies": {
- "@sinclair/typebox": "^0.27.8"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/source-map": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
- "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
- "dev": true,
- "dependencies": {
- "@jridgewell/trace-mapping": "^0.3.18",
- "callsites": "^3.0.0",
- "graceful-fs": "^4.2.9"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/test-result": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
- "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
- "dev": true,
- "dependencies": {
- "@jest/console": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "collect-v8-coverage": "^1.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/test-sequencer": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
- "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
- "dev": true,
- "dependencies": {
- "@jest/test-result": "^29.7.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.7.0",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/transform": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
- "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.11.6",
- "@jest/types": "^29.6.3",
- "@jridgewell/trace-mapping": "^0.3.18",
- "babel-plugin-istanbul": "^6.1.1",
- "chalk": "^4.0.0",
- "convert-source-map": "^2.0.0",
- "fast-json-stable-stringify": "^2.1.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.7.0",
- "jest-regex-util": "^29.6.3",
- "jest-util": "^29.7.0",
- "micromatch": "^4.0.4",
- "pirates": "^4.0.4",
- "slash": "^3.0.0",
- "write-file-atomic": "^4.0.2"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/types": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
- "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^29.6.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
- "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
- "dev": true,
- "dependencies": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
- "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
- "dev": true,
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/set-array": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
- "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
- "dev": true,
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.4.15",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
- "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
- "dev": true
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.19",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
- "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
- "dev": true,
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@sinclair/typebox": {
- "version": "0.27.8",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
- "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
- "dev": true
- },
- "node_modules/@sinonjs/commons": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
- "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
- "dev": true,
- "dependencies": {
- "type-detect": "4.0.8"
- }
- },
- "node_modules/@sinonjs/fake-timers": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
- "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
- "dev": true,
- "dependencies": {
- "@sinonjs/commons": "^3.0.0"
- }
- },
- "node_modules/@tootallnate/once": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
- "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
- "dev": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@types/babel__core": {
- "version": "7.20.2",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz",
- "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.20.7",
- "@babel/types": "^7.20.7",
- "@types/babel__generator": "*",
- "@types/babel__template": "*",
- "@types/babel__traverse": "*"
- }
- },
- "node_modules/@types/babel__generator": {
- "version": "7.6.5",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz",
- "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__template": {
- "version": "7.4.2",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz",
- "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__traverse": {
- "version": "7.20.2",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz",
- "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.20.7"
- }
- },
- "node_modules/@types/graceful-fs": {
- "version": "4.1.7",
- "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz",
- "integrity": "sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==",
- "dev": true,
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/istanbul-lib-coverage": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
- "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
- "dev": true
- },
- "node_modules/@types/istanbul-lib-report": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
- "integrity": "sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==",
- "dev": true,
- "dependencies": {
- "@types/istanbul-lib-coverage": "*"
- }
- },
- "node_modules/@types/istanbul-reports": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
- "integrity": "sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==",
- "dev": true,
- "dependencies": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "node_modules/@types/jest": {
- "version": "29.5.5",
- "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz",
- "integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==",
- "dev": true,
- "dependencies": {
- "expect": "^29.0.0",
- "pretty-format": "^29.0.0"
- }
- },
- "node_modules/@types/jsdom": {
- "version": "20.0.1",
- "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
- "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==",
- "dev": true,
- "dependencies": {
- "@types/node": "*",
- "@types/tough-cookie": "*",
- "parse5": "^7.0.0"
- }
- },
- "node_modules/@types/node": {
- "version": "20.8.3",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.3.tgz",
- "integrity": "sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==",
- "dev": true
- },
- "node_modules/@types/stack-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
- "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
- "dev": true
- },
- "node_modules/@types/tough-cookie": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.3.tgz",
- "integrity": "sha512-THo502dA5PzG/sfQH+42Lw3fvmYkceefOspdCwpHRul8ik2Jv1K8I5OZz1AT3/rs46kwgMCe9bSBmDLYkkOMGg==",
- "dev": true
- },
- "node_modules/@types/yargs": {
- "version": "17.0.28",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.28.tgz",
- "integrity": "sha512-N3e3fkS86hNhtk6BEnc0rj3zcehaxx8QWhCROJkqpl5Zaoi7nAic3jH8q94jVD3zu5LGk+PUB6KAiDmimYOEQw==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@types/yargs-parser": {
- "version": "21.0.1",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz",
- "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==",
- "dev": true
- },
- "node_modules/abab": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
- "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
- "dev": true
- },
- "node_modules/acorn": {
- "version": "8.10.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
- "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
- "dev": true,
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-globals": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
- "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
- "dev": true,
- "dependencies": {
- "acorn": "^8.1.0",
- "acorn-walk": "^8.0.2"
- }
- },
- "node_modules/acorn-walk": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
- "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
- "dev": true,
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "dev": true,
- "dependencies": {
- "debug": "4"
- },
- "engines": {
- "node": ">= 6.0.0"
- }
- },
- "node_modules/ansi-escapes": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
- "dev": true,
- "dependencies": {
- "type-fest": "^0.21.3"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "dev": true,
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
- "dependencies": {
- "sprintf-js": "~1.0.2"
- }
- },
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
- "dev": true
- },
- "node_modules/babel-jest": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
- "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
- "dev": true,
- "dependencies": {
- "@jest/transform": "^29.7.0",
- "@types/babel__core": "^7.1.14",
- "babel-plugin-istanbul": "^6.1.1",
- "babel-preset-jest": "^29.6.3",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.8.0"
- }
- },
- "node_modules/babel-plugin-istanbul": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
- "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@istanbuljs/load-nyc-config": "^1.0.0",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-instrument": "^5.0.4",
- "test-exclude": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
- "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.12.3",
- "@babel/parser": "^7.14.7",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.2.0",
- "semver": "^6.3.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/babel-plugin-jest-hoist": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
- "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.3.3",
- "@babel/types": "^7.3.3",
- "@types/babel__core": "^7.1.14",
- "@types/babel__traverse": "^7.0.6"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/babel-plugin-polyfill-corejs2": {
- "version": "0.4.5",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz",
- "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==",
- "dev": true,
- "dependencies": {
- "@babel/compat-data": "^7.22.6",
- "@babel/helper-define-polyfill-provider": "^0.4.2",
- "semver": "^6.3.1"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/babel-plugin-polyfill-corejs3": {
- "version": "0.8.4",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.4.tgz",
- "integrity": "sha512-9l//BZZsPR+5XjyJMPtZSK4jv0BsTO1zDac2GC6ygx9WLGlcsnRd1Co0B2zT5fF5Ic6BZy+9m3HNZ3QcOeDKfg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.4.2",
- "core-js-compat": "^3.32.2"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/babel-plugin-polyfill-regenerator": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz",
- "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.4.2"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/babel-plugin-transform-import-meta": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.2.1.tgz",
- "integrity": "sha512-AxNh27Pcg8Kt112RGa3Vod2QS2YXKKJ6+nSvRtv7qQTJAdx0MZa4UHZ4lnxHUWA2MNbLuZQv5FVab4P1CoLOWw==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.4.4",
- "tslib": "^2.4.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.10.0"
- }
- },
- "node_modules/babel-preset-current-node-syntax": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
- "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
- "dev": true,
- "dependencies": {
- "@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/plugin-syntax-bigint": "^7.8.3",
- "@babel/plugin-syntax-class-properties": "^7.8.3",
- "@babel/plugin-syntax-import-meta": "^7.8.3",
- "@babel/plugin-syntax-json-strings": "^7.8.3",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.8.3",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-top-level-await": "^7.8.3"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/babel-preset-jest": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
- "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
- "dev": true,
- "dependencies": {
- "babel-plugin-jest-hoist": "^29.6.3",
- "babel-preset-current-node-syntax": "^1.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
- },
- "node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "dependencies": {
- "fill-range": "^7.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/browserslist": {
- "version": "4.22.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
- "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "caniuse-lite": "^1.0.30001541",
- "electron-to-chromium": "^1.4.535",
- "node-releases": "^2.0.13",
- "update-browserslist-db": "^1.0.13"
- },
- "bin": {
- "browserslist": "cli.js"
- },
- "engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
- }
- },
- "node_modules/bser": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
- "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
- "dev": true,
- "dependencies": {
- "node-int64": "^0.4.0"
- }
- },
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true
- },
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001546",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz",
- "integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ]
- },
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/char-regex": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
- "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/ci-info": {
- "version": "3.9.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
- "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/sibiraj-s"
- }
- ],
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cjs-module-lexer": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz",
- "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==",
- "dev": true
- },
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "dev": true,
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/co": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
- "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
- "dev": true,
- "engines": {
- "iojs": ">= 1.0.0",
- "node": ">= 0.12.0"
- }
- },
- "node_modules/collect-v8-coverage": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
- "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==",
- "dev": true
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dev": true,
- "dependencies": {
- "delayed-stream": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true
- },
- "node_modules/convert-source-map": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "dev": true
- },
- "node_modules/core-js-compat": {
- "version": "3.33.0",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.0.tgz",
- "integrity": "sha512-0w4LcLXsVEuNkIqwjjf9rjCoPhK8uqA4tMRh4Ge26vfLtUutshn+aRJU21I9LCJlh2QQHfisNToLjw1XEJLTWw==",
- "dev": true,
- "dependencies": {
- "browserslist": "^4.22.1"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/core-js"
- }
- },
- "node_modules/create-jest": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
- "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "chalk": "^4.0.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.9",
- "jest-config": "^29.7.0",
- "jest-util": "^29.7.0",
- "prompts": "^2.0.1"
- },
- "bin": {
- "create-jest": "bin/create-jest.js"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/cssom": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
- "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
- "dev": true
- },
- "node_modules/cssstyle": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
- "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
- "dev": true,
- "dependencies": {
- "cssom": "~0.3.6"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cssstyle/node_modules/cssom": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
- "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
- "dev": true
- },
- "node_modules/data-urls": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
- "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
- "dev": true,
- "dependencies": {
- "abab": "^2.0.6",
- "whatwg-mimetype": "^3.0.0",
- "whatwg-url": "^11.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/decimal.js": {
- "version": "10.4.3",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
- "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
- "dev": true
- },
- "node_modules/dedent": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz",
- "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==",
- "dev": true,
- "peerDependencies": {
- "babel-plugin-macros": "^3.1.0"
- },
- "peerDependenciesMeta": {
- "babel-plugin-macros": {
- "optional": true
- }
- }
- },
- "node_modules/deepmerge": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "dev": true,
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/detect-newline": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
- "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/diff-sequences": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
- "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
- "dev": true,
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/domexception": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
- "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
- "dev": true,
- "dependencies": {
- "webidl-conversions": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/electron-to-chromium": {
- "version": "1.4.544",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.544.tgz",
- "integrity": "sha512-54z7squS1FyFRSUqq/knOFSptjjogLZXbKcYk3B0qkE1KZzvqASwRZnY2KzZQJqIYLVD38XZeoiMRflYSwyO4w==",
- "dev": true
- },
- "node_modules/emittery": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
- "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
- "dev": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/emittery?sponsor=1"
- }
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "dev": true,
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "dev": true,
- "dependencies": {
- "is-arrayish": "^0.2.1"
- }
- },
- "node_modules/escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/escodegen": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
- "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
- "dev": true,
- "dependencies": {
- "esprima": "^4.0.1",
- "estraverse": "^5.2.0",
- "esutils": "^2.0.2"
- },
- "bin": {
- "escodegen": "bin/escodegen.js",
- "esgenerate": "bin/esgenerate.js"
- },
- "engines": {
- "node": ">=6.0"
- },
- "optionalDependencies": {
- "source-map": "~0.6.1"
- }
- },
- "node_modules/esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "dev": true,
- "bin": {
- "esparse": "bin/esparse.js",
- "esvalidate": "bin/esvalidate.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "dev": true,
- "dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
- }
- },
- "node_modules/exit": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
- "dev": true,
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/expect": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
- "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
- "dev": true,
- "dependencies": {
- "@jest/expect-utils": "^29.7.0",
- "jest-get-type": "^29.6.3",
- "jest-matcher-utils": "^29.7.0",
- "jest-message-util": "^29.7.0",
- "jest-util": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true
- },
- "node_modules/fb-watchman": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
- "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
- "dev": true,
- "dependencies": {
- "bser": "2.1.1"
- }
- },
- "node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "dependencies": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dev": true,
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true,
- "engines": {
- "node": "6.* || 8.* || >= 10.*"
- }
- },
- "node_modules/get-package-type": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
- "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
- "dev": true,
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "dev": true
- },
- "node_modules/has": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz",
- "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==",
- "dev": true,
- "engines": {
- "node": ">= 0.4.0"
- }
- },
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/html-encoding-sniffer": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
- "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
- "dev": true,
- "dependencies": {
- "whatwg-encoding": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true
- },
- "node_modules/http-proxy-agent": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
- "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
- "dev": true,
- "dependencies": {
- "@tootallnate/once": "2",
- "agent-base": "6",
- "debug": "4"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/https-proxy-agent": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "dev": true,
- "dependencies": {
- "agent-base": "6",
- "debug": "4"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/human-signals": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
- "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
- "dev": true,
- "engines": {
- "node": ">=10.17.0"
- }
- },
- "node_modules/iconv-lite": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "dev": true,
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/import-local": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
- "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
- "dev": true,
- "dependencies": {
- "pkg-dir": "^4.2.0",
- "resolve-cwd": "^3.0.0"
- },
- "bin": {
- "import-local-fixture": "fixtures/cli.js"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true,
- "engines": {
- "node": ">=0.8.19"
- }
- },
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "dev": true,
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
- },
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true
- },
- "node_modules/is-core-module": {
- "version": "2.13.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
- "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
- "dev": true,
- "dependencies": {
- "has": "^1.0.3"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-generator-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
- "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/is-potential-custom-element-name": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
- "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
- "dev": true
- },
- "node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
- },
- "node_modules/istanbul-lib-coverage": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
- "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-instrument": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz",
- "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.12.3",
- "@babel/parser": "^7.14.7",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.2.0",
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-instrument/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-instrument/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-instrument/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
- "node_modules/istanbul-lib-report": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
- "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
- "dev": true,
- "dependencies": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^4.0.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-source-maps": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
- "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
- "dev": true,
- "dependencies": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-reports": {
- "version": "3.1.6",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
- "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
- "dev": true,
- "dependencies": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/jest": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
- "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
- "dev": true,
- "dependencies": {
- "@jest/core": "^29.7.0",
- "@jest/types": "^29.6.3",
- "import-local": "^3.0.2",
- "jest-cli": "^29.7.0"
- },
- "bin": {
- "jest": "bin/jest.js"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "peerDependencies": {
- "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
- },
- "peerDependenciesMeta": {
- "node-notifier": {
- "optional": true
- }
- }
- },
- "node_modules/jest-changed-files": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
- "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
- "dev": true,
- "dependencies": {
- "execa": "^5.0.0",
- "jest-util": "^29.7.0",
- "p-limit": "^3.1.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-circus": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
- "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^29.7.0",
- "@jest/expect": "^29.7.0",
- "@jest/test-result": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "co": "^4.6.0",
- "dedent": "^1.0.0",
- "is-generator-fn": "^2.0.0",
- "jest-each": "^29.7.0",
- "jest-matcher-utils": "^29.7.0",
- "jest-message-util": "^29.7.0",
- "jest-runtime": "^29.7.0",
- "jest-snapshot": "^29.7.0",
- "jest-util": "^29.7.0",
- "p-limit": "^3.1.0",
- "pretty-format": "^29.7.0",
- "pure-rand": "^6.0.0",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-cli": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
- "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
- "dev": true,
- "dependencies": {
- "@jest/core": "^29.7.0",
- "@jest/test-result": "^29.7.0",
- "@jest/types": "^29.6.3",
- "chalk": "^4.0.0",
- "create-jest": "^29.7.0",
- "exit": "^0.1.2",
- "import-local": "^3.0.2",
- "jest-config": "^29.7.0",
- "jest-util": "^29.7.0",
- "jest-validate": "^29.7.0",
- "yargs": "^17.3.1"
- },
- "bin": {
- "jest": "bin/jest.js"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "peerDependencies": {
- "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
- },
- "peerDependenciesMeta": {
- "node-notifier": {
- "optional": true
- }
- }
- },
- "node_modules/jest-config": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
- "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.11.6",
- "@jest/test-sequencer": "^29.7.0",
- "@jest/types": "^29.6.3",
- "babel-jest": "^29.7.0",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "deepmerge": "^4.2.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "jest-circus": "^29.7.0",
- "jest-environment-node": "^29.7.0",
- "jest-get-type": "^29.6.3",
- "jest-regex-util": "^29.6.3",
- "jest-resolve": "^29.7.0",
- "jest-runner": "^29.7.0",
- "jest-util": "^29.7.0",
- "jest-validate": "^29.7.0",
- "micromatch": "^4.0.4",
- "parse-json": "^5.2.0",
- "pretty-format": "^29.7.0",
- "slash": "^3.0.0",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "peerDependencies": {
- "@types/node": "*",
- "ts-node": ">=9.0.0"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "ts-node": {
- "optional": true
- }
- }
- },
- "node_modules/jest-diff": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
- "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
- "dev": true,
- "dependencies": {
- "chalk": "^4.0.0",
- "diff-sequences": "^29.6.3",
- "jest-get-type": "^29.6.3",
- "pretty-format": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-docblock": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
- "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
- "dev": true,
- "dependencies": {
- "detect-newline": "^3.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-each": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
- "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "chalk": "^4.0.0",
- "jest-get-type": "^29.6.3",
- "jest-util": "^29.7.0",
- "pretty-format": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-environment-jsdom": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz",
- "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^29.7.0",
- "@jest/fake-timers": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/jsdom": "^20.0.0",
- "@types/node": "*",
- "jest-mock": "^29.7.0",
- "jest-util": "^29.7.0",
- "jsdom": "^20.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "peerDependencies": {
- "canvas": "^2.5.0"
- },
- "peerDependenciesMeta": {
- "canvas": {
- "optional": true
- }
- }
- },
- "node_modules/jest-environment-node": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
- "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^29.7.0",
- "@jest/fake-timers": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "jest-mock": "^29.7.0",
- "jest-util": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-get-type": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
- "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
- "dev": true,
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-haste-map": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
- "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "@types/graceful-fs": "^4.1.3",
- "@types/node": "*",
- "anymatch": "^3.0.3",
- "fb-watchman": "^2.0.0",
- "graceful-fs": "^4.2.9",
- "jest-regex-util": "^29.6.3",
- "jest-util": "^29.7.0",
- "jest-worker": "^29.7.0",
- "micromatch": "^4.0.4",
- "walker": "^1.0.8"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "optionalDependencies": {
- "fsevents": "^2.3.2"
- }
- },
- "node_modules/jest-leak-detector": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
- "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
- "dev": true,
- "dependencies": {
- "jest-get-type": "^29.6.3",
- "pretty-format": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-matcher-utils": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
- "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
- "dev": true,
- "dependencies": {
- "chalk": "^4.0.0",
- "jest-diff": "^29.7.0",
- "jest-get-type": "^29.6.3",
- "pretty-format": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-message-util": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
- "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^29.6.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^29.7.0",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-mock": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
- "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "jest-util": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-pnp-resolver": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
- "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
- "dev": true,
- "engines": {
- "node": ">=6"
- },
- "peerDependencies": {
- "jest-resolve": "*"
- },
- "peerDependenciesMeta": {
- "jest-resolve": {
- "optional": true
- }
- }
- },
- "node_modules/jest-regex-util": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
- "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
- "dev": true,
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-resolve": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
- "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
- "dev": true,
- "dependencies": {
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.7.0",
- "jest-pnp-resolver": "^1.2.2",
- "jest-util": "^29.7.0",
- "jest-validate": "^29.7.0",
- "resolve": "^1.20.0",
- "resolve.exports": "^2.0.0",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-resolve-dependencies": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
- "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
- "dev": true,
- "dependencies": {
- "jest-regex-util": "^29.6.3",
- "jest-snapshot": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-runner": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
- "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
- "dev": true,
- "dependencies": {
- "@jest/console": "^29.7.0",
- "@jest/environment": "^29.7.0",
- "@jest/test-result": "^29.7.0",
- "@jest/transform": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "emittery": "^0.13.1",
- "graceful-fs": "^4.2.9",
- "jest-docblock": "^29.7.0",
- "jest-environment-node": "^29.7.0",
- "jest-haste-map": "^29.7.0",
- "jest-leak-detector": "^29.7.0",
- "jest-message-util": "^29.7.0",
- "jest-resolve": "^29.7.0",
- "jest-runtime": "^29.7.0",
- "jest-util": "^29.7.0",
- "jest-watcher": "^29.7.0",
- "jest-worker": "^29.7.0",
- "p-limit": "^3.1.0",
- "source-map-support": "0.5.13"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-runtime": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
- "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^29.7.0",
- "@jest/fake-timers": "^29.7.0",
- "@jest/globals": "^29.7.0",
- "@jest/source-map": "^29.6.3",
- "@jest/test-result": "^29.7.0",
- "@jest/transform": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "cjs-module-lexer": "^1.0.0",
- "collect-v8-coverage": "^1.0.0",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^29.7.0",
- "jest-message-util": "^29.7.0",
- "jest-mock": "^29.7.0",
- "jest-regex-util": "^29.6.3",
- "jest-resolve": "^29.7.0",
- "jest-snapshot": "^29.7.0",
- "jest-util": "^29.7.0",
- "slash": "^3.0.0",
- "strip-bom": "^4.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-snapshot": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
- "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.11.6",
- "@babel/generator": "^7.7.2",
- "@babel/plugin-syntax-jsx": "^7.7.2",
- "@babel/plugin-syntax-typescript": "^7.7.2",
- "@babel/types": "^7.3.3",
- "@jest/expect-utils": "^29.7.0",
- "@jest/transform": "^29.7.0",
- "@jest/types": "^29.6.3",
- "babel-preset-current-node-syntax": "^1.0.0",
- "chalk": "^4.0.0",
- "expect": "^29.7.0",
- "graceful-fs": "^4.2.9",
- "jest-diff": "^29.7.0",
- "jest-get-type": "^29.6.3",
- "jest-matcher-utils": "^29.7.0",
- "jest-message-util": "^29.7.0",
- "jest-util": "^29.7.0",
- "natural-compare": "^1.4.0",
- "pretty-format": "^29.7.0",
- "semver": "^7.5.3"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-snapshot/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/jest-snapshot/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/jest-snapshot/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
- "node_modules/jest-util": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
- "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-validate": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
- "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "camelcase": "^6.2.0",
- "chalk": "^4.0.0",
- "jest-get-type": "^29.6.3",
- "leven": "^3.1.0",
- "pretty-format": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-validate/node_modules/camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/jest-watcher": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
- "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
- "dev": true,
- "dependencies": {
- "@jest/test-result": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "emittery": "^0.13.1",
- "jest-util": "^29.7.0",
- "string-length": "^4.0.1"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-worker": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
- "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
- "dev": true,
- "dependencies": {
- "@types/node": "*",
- "jest-util": "^29.7.0",
- "merge-stream": "^2.0.0",
- "supports-color": "^8.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-worker/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
- "node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
- },
- "node_modules/js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
- "dev": true,
- "dependencies": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/jsdom": {
- "version": "20.0.3",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz",
- "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
- "dev": true,
- "dependencies": {
- "abab": "^2.0.6",
- "acorn": "^8.8.1",
- "acorn-globals": "^7.0.0",
- "cssom": "^0.5.0",
- "cssstyle": "^2.3.0",
- "data-urls": "^3.0.2",
- "decimal.js": "^10.4.2",
- "domexception": "^4.0.0",
- "escodegen": "^2.0.0",
- "form-data": "^4.0.0",
- "html-encoding-sniffer": "^3.0.0",
- "http-proxy-agent": "^5.0.0",
- "https-proxy-agent": "^5.0.1",
- "is-potential-custom-element-name": "^1.0.1",
- "nwsapi": "^2.2.2",
- "parse5": "^7.1.1",
- "saxes": "^6.0.0",
- "symbol-tree": "^3.2.4",
- "tough-cookie": "^4.1.2",
- "w3c-xmlserializer": "^4.0.0",
- "webidl-conversions": "^7.0.0",
- "whatwg-encoding": "^2.0.0",
- "whatwg-mimetype": "^3.0.0",
- "whatwg-url": "^11.0.0",
- "ws": "^8.11.0",
- "xml-name-validator": "^4.0.0"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "canvas": "^2.5.0"
- },
- "peerDependenciesMeta": {
- "canvas": {
- "optional": true
- }
- }
- },
- "node_modules/jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/json-parse-even-better-errors": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
- "dev": true
- },
- "node_modules/json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
- "dev": true,
- "bin": {
- "json5": "lib/cli.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/kleur": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/leven": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
- "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "dev": true
- },
- "node_modules/locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "dependencies": {
- "p-locate": "^4.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/lodash.debounce": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
- "dev": true
- },
- "node_modules/lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "dev": true,
- "dependencies": {
- "yallist": "^3.0.2"
- }
- },
- "node_modules/make-dir": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
- "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
- "dev": true,
- "dependencies": {
- "semver": "^7.5.3"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/make-dir/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/make-dir/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/make-dir/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
- "node_modules/makeerror": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
- "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
- "dev": true,
- "dependencies": {
- "tmpl": "1.0.5"
- }
- },
- "node_modules/merge-stream": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
- "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
- "dev": true
- },
- "node_modules/micromatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
- "dev": true,
- "dependencies": {
- "braces": "^3.0.2",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "dev": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dev": true,
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "node_modules/natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true
- },
- "node_modules/node-int64": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
- "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
- "dev": true
- },
- "node_modules/node-releases": {
- "version": "2.0.13",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
- "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
- "dev": true
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
- "dependencies": {
- "path-key": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/nwsapi": {
- "version": "2.2.7",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
- "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",
- "dev": true
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/onetime": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "dev": true,
- "dependencies": {
- "mimic-fn": "^2.1.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "dependencies": {
- "p-limit": "^2.2.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/p-locate/node_modules/p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "dependencies": {
- "p-try": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/parse-json": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
- "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/parse5": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
- "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
- "dev": true,
- "dependencies": {
- "entities": "^4.4.0"
- },
- "funding": {
- "url": "https://github.com/inikulin/parse5?sponsor=1"
- }
- },
- "node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true
- },
- "node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/pirates": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
- "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
- "dev": true,
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/pkg-dir": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
- "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "dev": true,
- "dependencies": {
- "find-up": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/pretty-format": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
- "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^29.6.3",
- "ansi-styles": "^5.0.0",
- "react-is": "^18.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/pretty-format/node_modules/ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/prompts": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
- "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
- "dev": true,
- "dependencies": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.5"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/psl": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
- "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
- "dev": true
- },
- "node_modules/punycode": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
- "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/pure-rand": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz",
- "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==",
- "dev": true,
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/dubzzz"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/fast-check"
- }
- ]
- },
- "node_modules/querystringify": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
- "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
- "dev": true
- },
- "node_modules/react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
- },
- "node_modules/regenerate": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
- "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
- "dev": true
- },
- "node_modules/regenerate-unicode-properties": {
- "version": "10.1.1",
- "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz",
- "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==",
- "dev": true,
- "dependencies": {
- "regenerate": "^1.4.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/regenerator-runtime": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
- "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==",
- "dev": true
- },
- "node_modules/regenerator-transform": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
- "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
- "dev": true,
- "dependencies": {
- "@babel/runtime": "^7.8.4"
- }
- },
- "node_modules/regexpu-core": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz",
- "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==",
- "dev": true,
- "dependencies": {
- "@babel/regjsgen": "^0.8.0",
- "regenerate": "^1.4.2",
- "regenerate-unicode-properties": "^10.1.0",
- "regjsparser": "^0.9.1",
- "unicode-match-property-ecmascript": "^2.0.0",
- "unicode-match-property-value-ecmascript": "^2.1.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/regjsparser": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
- "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
- "dev": true,
- "dependencies": {
- "jsesc": "~0.5.0"
- },
- "bin": {
- "regjsparser": "bin/parser"
- }
- },
- "node_modules/regjsparser/node_modules/jsesc": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
- "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- }
- },
- "node_modules/require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/requires-port": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
- "dev": true
- },
- "node_modules/resolve": {
- "version": "1.22.6",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz",
- "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==",
- "dev": true,
- "dependencies": {
- "is-core-module": "^2.13.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/resolve-cwd": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
- "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
- "dev": true,
- "dependencies": {
- "resolve-from": "^5.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/resolve.exports": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
- "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true
- },
- "node_modules/saxes": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
- "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
- "dev": true,
- "dependencies": {
- "xmlchars": "^2.2.0"
- },
- "engines": {
- "node": ">=v12.22.7"
- }
- },
- "node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true
- },
- "node_modules/sisteransi": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
- "dev": true
- },
- "node_modules/slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-support": {
- "version": "0.5.13",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
- "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
- "dev": true,
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "node_modules/sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
- "dev": true
- },
- "node_modules/stack-utils": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
- "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
- "dev": true,
- "dependencies": {
- "escape-string-regexp": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/string-length": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
- "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
- "dev": true,
- "dependencies": {
- "char-regex": "^1.0.2",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-bom": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-final-newline": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/symbol-tree": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
- "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
- "dev": true
- },
- "node_modules/test-exclude": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
- "dev": true,
- "dependencies": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^7.1.4",
- "minimatch": "^3.0.4"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/tmpl": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
- "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
- "dev": true
- },
- "node_modules/to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/tough-cookie": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
- "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
- "dev": true,
- "dependencies": {
- "psl": "^1.1.33",
- "punycode": "^2.1.1",
- "universalify": "^0.2.0",
- "url-parse": "^1.5.3"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/tr46": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
- "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
- "dev": true,
- "dependencies": {
- "punycode": "^2.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/tslib": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
- "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
- "dev": true
- },
- "node_modules/type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/type-fest": {
- "version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/unicode-canonical-property-names-ecmascript": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
- "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-match-property-ecmascript": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
- "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
- "dev": true,
- "dependencies": {
- "unicode-canonical-property-names-ecmascript": "^2.0.0",
- "unicode-property-aliases-ecmascript": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-match-property-value-ecmascript": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz",
- "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-property-aliases-ecmascript": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
- "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/universalify": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
- "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
- "dev": true,
- "engines": {
- "node": ">= 4.0.0"
- }
- },
- "node_modules/update-browserslist-db": {
- "version": "1.0.13",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
- "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
- },
- "bin": {
- "update-browserslist-db": "cli.js"
- },
- "peerDependencies": {
- "browserslist": ">= 4.21.0"
- }
- },
- "node_modules/url-parse": {
- "version": "1.5.10",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
- "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
- "dev": true,
- "dependencies": {
- "querystringify": "^2.1.1",
- "requires-port": "^1.0.0"
- }
- },
- "node_modules/v8-to-istanbul": {
- "version": "9.1.3",
- "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz",
- "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==",
- "dev": true,
- "dependencies": {
- "@jridgewell/trace-mapping": "^0.3.12",
- "@types/istanbul-lib-coverage": "^2.0.1",
- "convert-source-map": "^2.0.0"
- },
- "engines": {
- "node": ">=10.12.0"
- }
- },
- "node_modules/w3c-xmlserializer": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
- "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
- "dev": true,
- "dependencies": {
- "xml-name-validator": "^4.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/walker": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
- "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
- "dev": true,
- "dependencies": {
- "makeerror": "1.0.12"
- }
- },
- "node_modules/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/whatwg-encoding": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
- "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
- "dev": true,
- "dependencies": {
- "iconv-lite": "0.6.3"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/whatwg-mimetype": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
- "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/whatwg-url": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
- "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
- "dev": true,
- "dependencies": {
- "tr46": "^3.0.0",
- "webidl-conversions": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true
- },
- "node_modules/write-file-atomic": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
- "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
- "dev": true,
- "dependencies": {
- "imurmurhash": "^0.1.4",
- "signal-exit": "^3.0.7"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
- }
- },
- "node_modules/ws": {
- "version": "8.14.2",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
- "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
- "dev": true,
- "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
- }
- }
- },
- "node_modules/xml-name-validator": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
- "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/xmlchars": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
- "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
- "dev": true
- },
- "node_modules/y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true
- },
- "node_modules/yargs": {
- "version": "17.7.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
- "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
- "dev": true,
- "dependencies": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- }
- }
-}
diff --git a/tests-ui/package.json b/tests-ui/package.json
deleted file mode 100644
index ae7e49084..000000000
--- a/tests-ui/package.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "name": "comfui-tests",
- "version": "1.0.0",
- "description": "UI tests",
- "main": "index.js",
- "scripts": {
- "test": "jest",
- "test:generate": "node setup.js"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/comfyanonymous/ComfyUI.git"
- },
- "keywords": [
- "comfyui",
- "test"
- ],
- "author": "comfyanonymous",
- "license": "GPL-3.0",
- "bugs": {
- "url": "https://github.com/comfyanonymous/ComfyUI/issues"
- },
- "homepage": "https://github.com/comfyanonymous/ComfyUI#readme",
- "devDependencies": {
- "@babel/preset-env": "^7.22.20",
- "@types/jest": "^29.5.5",
- "babel-plugin-transform-import-meta": "^2.2.1",
- "jest": "^29.7.0",
- "jest-environment-jsdom": "^29.7.0"
- }
-}
diff --git a/tests-ui/setup.js b/tests-ui/setup.js
deleted file mode 100644
index 8bbd9dcdf..000000000
--- a/tests-ui/setup.js
+++ /dev/null
@@ -1,88 +0,0 @@
-const { spawn } = require("child_process");
-const { resolve } = require("path");
-const { existsSync, mkdirSync, writeFileSync } = require("fs");
-const http = require("http");
-
-async function setup() {
- // Wait up to 30s for it to start
- let success = false;
- let child;
- for (let i = 0; i < 30; i++) {
- try {
- await new Promise((res, rej) => {
- http
- .get("http://127.0.0.1:8188/object_info", (resp) => {
- let data = "";
- resp.on("data", (chunk) => {
- data += chunk;
- });
- resp.on("end", () => {
- // Modify the response data to add some checkpoints
- const objectInfo = JSON.parse(data);
- objectInfo.CheckpointLoaderSimple.input.required.ckpt_name[0] = ["model1.safetensors", "model2.ckpt"];
- objectInfo.VAELoader.input.required.vae_name[0] = ["vae1.safetensors", "vae2.ckpt"];
-
- data = JSON.stringify(objectInfo, undefined, "\t");
-
- const outDir = resolve("./data");
- if (!existsSync(outDir)) {
- mkdirSync(outDir);
- }
-
- const outPath = resolve(outDir, "object_info.json");
- console.log(`Writing ${Object.keys(objectInfo).length} nodes to ${outPath}`);
- writeFileSync(outPath, data, {
- encoding: "utf8",
- });
- res();
- });
- })
- .on("error", rej);
- });
- success = true;
- break;
- } catch (error) {
- console.log(i + "/30", error);
- if (i === 0) {
- // Start the server on first iteration if it fails to connect
- console.log("Starting ComfyUI server...");
-
- let python = resolve("../../python_embeded/python.exe");
- let args;
- let cwd;
- if (existsSync(python)) {
- args = ["-s", "ComfyUI/main.py"];
- cwd = "../..";
- } else {
- python = "python";
- args = ["main.py"];
- cwd = "..";
- }
- args.push("--cpu");
- console.log(python, ...args);
- child = spawn(python, args, { cwd });
- child.on("error", (err) => {
- console.log(`Server error (${err})`);
- i = 30;
- });
- child.on("exit", (code) => {
- if (!success) {
- console.log(`Server exited (${code})`);
- i = 30;
- }
- });
- }
- await new Promise((r) => {
- setTimeout(r, 1000);
- });
- }
- }
-
- child?.kill();
-
- if (!success) {
- throw new Error("Waiting for server failed...");
- }
-}
-
- setup();
\ No newline at end of file
diff --git a/tests-ui/tests/extensions.test.js b/tests-ui/tests/extensions.test.js
deleted file mode 100644
index 159e5113a..000000000
--- a/tests-ui/tests/extensions.test.js
+++ /dev/null
@@ -1,196 +0,0 @@
-// @ts-check
-///
-const { start } = require("../utils");
-const lg = require("../utils/litegraph");
-
-describe("extensions", () => {
- beforeEach(() => {
- lg.setup(global);
- });
-
- afterEach(() => {
- lg.teardown(global);
- });
-
- it("calls each extension hook", async () => {
- const mockExtension = {
- name: "TestExtension",
- init: jest.fn(),
- setup: jest.fn(),
- addCustomNodeDefs: jest.fn(),
- getCustomWidgets: jest.fn(),
- beforeRegisterNodeDef: jest.fn(),
- registerCustomNodes: jest.fn(),
- loadedGraphNode: jest.fn(),
- nodeCreated: jest.fn(),
- beforeConfigureGraph: jest.fn(),
- afterConfigureGraph: jest.fn(),
- };
-
- const { app, ez, graph } = await start({
- async preSetup(app) {
- app.registerExtension(mockExtension);
- },
- });
-
- // Basic initialisation hooks should be called once, with app
- expect(mockExtension.init).toHaveBeenCalledTimes(1);
- expect(mockExtension.init).toHaveBeenCalledWith(app);
-
- // Adding custom node defs should be passed the full list of nodes
- expect(mockExtension.addCustomNodeDefs).toHaveBeenCalledTimes(1);
- expect(mockExtension.addCustomNodeDefs.mock.calls[0][1]).toStrictEqual(app);
- const defs = mockExtension.addCustomNodeDefs.mock.calls[0][0];
- expect(defs).toHaveProperty("KSampler");
- expect(defs).toHaveProperty("LoadImage");
-
- // Get custom widgets is called once and should return new widget types
- expect(mockExtension.getCustomWidgets).toHaveBeenCalledTimes(1);
- expect(mockExtension.getCustomWidgets).toHaveBeenCalledWith(app);
-
- // Before register node def will be called once per node type
- const nodeNames = Object.keys(defs);
- const nodeCount = nodeNames.length;
- expect(mockExtension.beforeRegisterNodeDef).toHaveBeenCalledTimes(nodeCount);
- for (let i = 0; i < 10; i++) {
- // It should be send the JS class and the original JSON definition
- const nodeClass = mockExtension.beforeRegisterNodeDef.mock.calls[i][0];
- const nodeDef = mockExtension.beforeRegisterNodeDef.mock.calls[i][1];
-
- expect(nodeClass.name).toBe("ComfyNode");
- expect(nodeClass.comfyClass).toBe(nodeNames[i]);
- expect(nodeDef.name).toBe(nodeNames[i]);
- expect(nodeDef).toHaveProperty("input");
- expect(nodeDef).toHaveProperty("output");
- }
-
- // Register custom nodes is called once after registerNode defs to allow adding other frontend nodes
- expect(mockExtension.registerCustomNodes).toHaveBeenCalledTimes(1);
-
- // Before configure graph will be called here as the default graph is being loaded
- expect(mockExtension.beforeConfigureGraph).toHaveBeenCalledTimes(1);
- // it gets sent the graph data that is going to be loaded
- const graphData = mockExtension.beforeConfigureGraph.mock.calls[0][0];
-
- // A node created is fired for each node constructor that is called
- expect(mockExtension.nodeCreated).toHaveBeenCalledTimes(graphData.nodes.length);
- for (let i = 0; i < graphData.nodes.length; i++) {
- expect(mockExtension.nodeCreated.mock.calls[i][0].type).toBe(graphData.nodes[i].type);
- }
-
- // Each node then calls loadedGraphNode to allow them to be updated
- expect(mockExtension.loadedGraphNode).toHaveBeenCalledTimes(graphData.nodes.length);
- for (let i = 0; i < graphData.nodes.length; i++) {
- expect(mockExtension.loadedGraphNode.mock.calls[i][0].type).toBe(graphData.nodes[i].type);
- }
-
- // After configure is then called once all the setup is done
- expect(mockExtension.afterConfigureGraph).toHaveBeenCalledTimes(1);
-
- expect(mockExtension.setup).toHaveBeenCalledTimes(1);
- expect(mockExtension.setup).toHaveBeenCalledWith(app);
-
- // Ensure hooks are called in the correct order
- const callOrder = [
- "init",
- "addCustomNodeDefs",
- "getCustomWidgets",
- "beforeRegisterNodeDef",
- "registerCustomNodes",
- "beforeConfigureGraph",
- "nodeCreated",
- "loadedGraphNode",
- "afterConfigureGraph",
- "setup",
- ];
- for (let i = 1; i < callOrder.length; i++) {
- const fn1 = mockExtension[callOrder[i - 1]];
- const fn2 = mockExtension[callOrder[i]];
- expect(fn1.mock.invocationCallOrder[0]).toBeLessThan(fn2.mock.invocationCallOrder[0]);
- }
-
- graph.clear();
-
- // Ensure adding a new node calls the correct callback
- ez.LoadImage();
- expect(mockExtension.loadedGraphNode).toHaveBeenCalledTimes(graphData.nodes.length);
- expect(mockExtension.nodeCreated).toHaveBeenCalledTimes(graphData.nodes.length + 1);
- expect(mockExtension.nodeCreated.mock.lastCall[0].type).toBe("LoadImage");
-
- // Reload the graph to ensure correct hooks are fired
- await graph.reload();
-
- // These hooks should not be fired again
- expect(mockExtension.init).toHaveBeenCalledTimes(1);
- expect(mockExtension.addCustomNodeDefs).toHaveBeenCalledTimes(1);
- expect(mockExtension.getCustomWidgets).toHaveBeenCalledTimes(1);
- expect(mockExtension.registerCustomNodes).toHaveBeenCalledTimes(1);
- expect(mockExtension.beforeRegisterNodeDef).toHaveBeenCalledTimes(nodeCount);
- expect(mockExtension.setup).toHaveBeenCalledTimes(1);
-
- // These should be called again
- expect(mockExtension.beforeConfigureGraph).toHaveBeenCalledTimes(2);
- expect(mockExtension.nodeCreated).toHaveBeenCalledTimes(graphData.nodes.length + 2);
- expect(mockExtension.loadedGraphNode).toHaveBeenCalledTimes(graphData.nodes.length + 1);
- expect(mockExtension.afterConfigureGraph).toHaveBeenCalledTimes(2);
- }, 15000);
-
- it("allows custom nodeDefs and widgets to be registered", async () => {
- const widgetMock = jest.fn((node, inputName, inputData, app) => {
- expect(node.constructor.comfyClass).toBe("TestNode");
- expect(inputName).toBe("test_input");
- expect(inputData[0]).toBe("CUSTOMWIDGET");
- expect(inputData[1]?.hello).toBe("world");
- expect(app).toStrictEqual(app);
-
- return {
- widget: node.addWidget("button", inputName, "hello", () => {}),
- };
- });
-
- // Register our extension that adds a custom node + widget type
- const mockExtension = {
- name: "TestExtension",
- addCustomNodeDefs: (nodeDefs) => {
- nodeDefs["TestNode"] = {
- output: [],
- output_name: [],
- output_is_list: [],
- name: "TestNode",
- display_name: "TestNode",
- category: "Test",
- input: {
- required: {
- test_input: ["CUSTOMWIDGET", { hello: "world" }],
- },
- },
- };
- },
- getCustomWidgets: jest.fn(() => {
- return {
- CUSTOMWIDGET: widgetMock,
- };
- }),
- };
-
- const { graph, ez } = await start({
- async preSetup(app) {
- app.registerExtension(mockExtension);
- },
- });
-
- expect(mockExtension.getCustomWidgets).toBeCalledTimes(1);
-
- graph.clear();
- expect(widgetMock).toBeCalledTimes(0);
- const node = ez.TestNode();
- expect(widgetMock).toBeCalledTimes(1);
-
- // Ensure our custom widget is created
- expect(node.inputs.length).toBe(0);
- expect(node.widgets.length).toBe(1);
- const w = node.widgets[0].widget;
- expect(w.name).toBe("test_input");
- expect(w.type).toBe("button");
- });
-});
diff --git a/tests-ui/tests/groupNode.test.js b/tests-ui/tests/groupNode.test.js
deleted file mode 100644
index 53a5828d1..000000000
--- a/tests-ui/tests/groupNode.test.js
+++ /dev/null
@@ -1,1005 +0,0 @@
-// @ts-check
-///
-
-const { start, createDefaultWorkflow, getNodeDef, checkBeforeAndAfterReload } = require("../utils");
-const lg = require("../utils/litegraph");
-
-describe("group node", () => {
- beforeEach(() => {
- lg.setup(global);
- });
-
- afterEach(() => {
- lg.teardown(global);
- });
-
- /**
- *
- * @param {*} app
- * @param {*} graph
- * @param {*} name
- * @param {*} nodes
- * @returns { Promise> }
- */
- async function convertToGroup(app, graph, name, nodes) {
- // Select the nodes we are converting
- for (const n of nodes) {
- n.select(true);
- }
-
- expect(Object.keys(app.canvas.selected_nodes).sort((a, b) => +a - +b)).toEqual(
- nodes.map((n) => n.id + "").sort((a, b) => +a - +b)
- );
-
- global.prompt = jest.fn().mockImplementation(() => name);
- const groupNode = await nodes[0].menu["Convert to Group Node"].call(false);
-
- // Check group name was requested
- expect(window.prompt).toHaveBeenCalled();
-
- // Ensure old nodes are removed
- for (const n of nodes) {
- expect(n.isRemoved).toBeTruthy();
- }
-
- expect(groupNode.type).toEqual("workflow/" + name);
-
- return graph.find(groupNode);
- }
-
- /**
- * @param { Record | number[] } idMap
- * @param { Record> } valueMap
- */
- function getOutput(idMap = {}, valueMap = {}) {
- if (idMap instanceof Array) {
- idMap = idMap.reduce((p, n) => {
- p[n] = n + "";
- return p;
- }, {});
- }
- const expected = {
- 1: { inputs: { ckpt_name: "model1.safetensors", ...valueMap?.[1] }, class_type: "CheckpointLoaderSimple" },
- 2: { inputs: { text: "positive", clip: ["1", 1], ...valueMap?.[2] }, class_type: "CLIPTextEncode" },
- 3: { inputs: { text: "negative", clip: ["1", 1], ...valueMap?.[3] }, class_type: "CLIPTextEncode" },
- 4: { inputs: { width: 512, height: 512, batch_size: 1, ...valueMap?.[4] }, class_type: "EmptyLatentImage" },
- 5: {
- inputs: {
- seed: 0,
- steps: 20,
- cfg: 8,
- sampler_name: "euler",
- scheduler: "normal",
- denoise: 1,
- model: ["1", 0],
- positive: ["2", 0],
- negative: ["3", 0],
- latent_image: ["4", 0],
- ...valueMap?.[5],
- },
- class_type: "KSampler",
- },
- 6: { inputs: { samples: ["5", 0], vae: ["1", 2], ...valueMap?.[6] }, class_type: "VAEDecode" },
- 7: { inputs: { filename_prefix: "ComfyUI", images: ["6", 0], ...valueMap?.[7] }, class_type: "SaveImage" },
- };
-
- // Map old IDs to new at the top level
- const mapped = {};
- for (const oldId in idMap) {
- mapped[idMap[oldId]] = expected[oldId];
- delete expected[oldId];
- }
- Object.assign(mapped, expected);
-
- // Map old IDs to new inside links
- for (const k in mapped) {
- for (const input in mapped[k].inputs) {
- const v = mapped[k].inputs[input];
- if (v instanceof Array) {
- if (v[0] in idMap) {
- v[0] = idMap[v[0]] + "";
- }
- }
- }
- }
-
- return mapped;
- }
-
- test("can be created from selected nodes", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
- const group = await convertToGroup(app, graph, "test", [nodes.pos, nodes.neg, nodes.empty]);
-
- // Ensure links are now to the group node
- expect(group.inputs).toHaveLength(2);
- expect(group.outputs).toHaveLength(3);
-
- expect(group.inputs.map((i) => i.input.name)).toEqual(["clip", "CLIPTextEncode clip"]);
- expect(group.outputs.map((i) => i.output.name)).toEqual(["LATENT", "CONDITIONING", "CLIPTextEncode CONDITIONING"]);
-
- // ckpt clip to both clip inputs on the group
- expect(nodes.ckpt.outputs.CLIP.connections.map((t) => [t.targetNode.id, t.targetInput.index])).toEqual([
- [group.id, 0],
- [group.id, 1],
- ]);
-
- // group conditioning to sampler
- expect(group.outputs["CONDITIONING"].connections.map((t) => [t.targetNode.id, t.targetInput.index])).toEqual([
- [nodes.sampler.id, 1],
- ]);
- // group conditioning 2 to sampler
- expect(
- group.outputs["CLIPTextEncode CONDITIONING"].connections.map((t) => [t.targetNode.id, t.targetInput.index])
- ).toEqual([[nodes.sampler.id, 2]]);
- // group latent to sampler
- expect(group.outputs["LATENT"].connections.map((t) => [t.targetNode.id, t.targetInput.index])).toEqual([
- [nodes.sampler.id, 3],
- ]);
- });
-
- test("maintains all output links on conversion", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
- const save2 = ez.SaveImage(...nodes.decode.outputs);
- const save3 = ez.SaveImage(...nodes.decode.outputs);
- // Ensure an output with multiple links maintains them on convert to group
- const group = await convertToGroup(app, graph, "test", [nodes.sampler, nodes.decode]);
- expect(group.outputs[0].connections.length).toBe(3);
- expect(group.outputs[0].connections[0].targetNode.id).toBe(nodes.save.id);
- expect(group.outputs[0].connections[1].targetNode.id).toBe(save2.id);
- expect(group.outputs[0].connections[2].targetNode.id).toBe(save3.id);
-
- // and they're still linked when converting back to nodes
- const newNodes = group.menu["Convert to nodes"].call();
- const decode = graph.find(newNodes.find((n) => n.type === "VAEDecode"));
- expect(decode.outputs[0].connections.length).toBe(3);
- expect(decode.outputs[0].connections[0].targetNode.id).toBe(nodes.save.id);
- expect(decode.outputs[0].connections[1].targetNode.id).toBe(save2.id);
- expect(decode.outputs[0].connections[2].targetNode.id).toBe(save3.id);
- });
- test("can be be converted back to nodes", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
- const toConvert = [nodes.pos, nodes.neg, nodes.empty, nodes.sampler];
- const group = await convertToGroup(app, graph, "test", toConvert);
-
- // Edit some values to ensure they are set back onto the converted nodes
- expect(group.widgets["text"].value).toBe("positive");
- group.widgets["text"].value = "pos";
- expect(group.widgets["CLIPTextEncode text"].value).toBe("negative");
- group.widgets["CLIPTextEncode text"].value = "neg";
- expect(group.widgets["width"].value).toBe(512);
- group.widgets["width"].value = 1024;
- expect(group.widgets["sampler_name"].value).toBe("euler");
- group.widgets["sampler_name"].value = "ddim";
- expect(group.widgets["control_after_generate"].value).toBe("randomize");
- group.widgets["control_after_generate"].value = "fixed";
-
- /** @type { Array } */
- group.menu["Convert to nodes"].call();
-
- // ensure widget values are set
- const pos = graph.find(nodes.pos.id);
- expect(pos.node.type).toBe("CLIPTextEncode");
- expect(pos.widgets["text"].value).toBe("pos");
- const neg = graph.find(nodes.neg.id);
- expect(neg.node.type).toBe("CLIPTextEncode");
- expect(neg.widgets["text"].value).toBe("neg");
- const empty = graph.find(nodes.empty.id);
- expect(empty.node.type).toBe("EmptyLatentImage");
- expect(empty.widgets["width"].value).toBe(1024);
- const sampler = graph.find(nodes.sampler.id);
- expect(sampler.node.type).toBe("KSampler");
- expect(sampler.widgets["sampler_name"].value).toBe("ddim");
- expect(sampler.widgets["control_after_generate"].value).toBe("fixed");
-
- // validate links
- expect(nodes.ckpt.outputs.CLIP.connections.map((t) => [t.targetNode.id, t.targetInput.index])).toEqual([
- [pos.id, 0],
- [neg.id, 0],
- ]);
-
- expect(pos.outputs["CONDITIONING"].connections.map((t) => [t.targetNode.id, t.targetInput.index])).toEqual([
- [nodes.sampler.id, 1],
- ]);
-
- expect(neg.outputs["CONDITIONING"].connections.map((t) => [t.targetNode.id, t.targetInput.index])).toEqual([
- [nodes.sampler.id, 2],
- ]);
-
- expect(empty.outputs["LATENT"].connections.map((t) => [t.targetNode.id, t.targetInput.index])).toEqual([
- [nodes.sampler.id, 3],
- ]);
- });
- test("it can embed reroutes as inputs", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
-
- // Add and connect a reroute to the clip text encodes
- const reroute = ez.Reroute();
- nodes.ckpt.outputs.CLIP.connectTo(reroute.inputs[0]);
- reroute.outputs[0].connectTo(nodes.pos.inputs[0]);
- reroute.outputs[0].connectTo(nodes.neg.inputs[0]);
-
- // Convert to group and ensure we only have 1 input of the correct type
- const group = await convertToGroup(app, graph, "test", [nodes.pos, nodes.neg, nodes.empty, reroute]);
- expect(group.inputs).toHaveLength(1);
- expect(group.inputs[0].input.type).toEqual("CLIP");
-
- expect((await graph.toPrompt()).output).toEqual(getOutput());
- });
- test("it can embed reroutes as outputs", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
-
- // Add a reroute with no output so we output IMAGE even though its used internally
- const reroute = ez.Reroute();
- nodes.decode.outputs.IMAGE.connectTo(reroute.inputs[0]);
-
- // Convert to group and ensure there is an IMAGE output
- const group = await convertToGroup(app, graph, "test", [nodes.decode, nodes.save, reroute]);
- expect(group.outputs).toHaveLength(1);
- expect(group.outputs[0].output.type).toEqual("IMAGE");
- expect((await graph.toPrompt()).output).toEqual(getOutput([nodes.decode.id, nodes.save.id]));
- });
- test("it can embed reroutes as pipes", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
-
- // Use reroutes as a pipe
- const rerouteModel = ez.Reroute();
- const rerouteClip = ez.Reroute();
- const rerouteVae = ez.Reroute();
- nodes.ckpt.outputs.MODEL.connectTo(rerouteModel.inputs[0]);
- nodes.ckpt.outputs.CLIP.connectTo(rerouteClip.inputs[0]);
- nodes.ckpt.outputs.VAE.connectTo(rerouteVae.inputs[0]);
-
- const group = await convertToGroup(app, graph, "test", [rerouteModel, rerouteClip, rerouteVae]);
-
- expect(group.outputs).toHaveLength(3);
- expect(group.outputs.map((o) => o.output.type)).toEqual(["MODEL", "CLIP", "VAE"]);
-
- expect(group.outputs).toHaveLength(3);
- expect(group.outputs.map((o) => o.output.type)).toEqual(["MODEL", "CLIP", "VAE"]);
-
- group.outputs[0].connectTo(nodes.sampler.inputs.model);
- group.outputs[1].connectTo(nodes.pos.inputs.clip);
- group.outputs[1].connectTo(nodes.neg.inputs.clip);
- });
- test("can handle reroutes used internally", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
-
- let reroutes = [];
- let prevNode = nodes.ckpt;
- for (let i = 0; i < 5; i++) {
- const reroute = ez.Reroute();
- prevNode.outputs[0].connectTo(reroute.inputs[0]);
- prevNode = reroute;
- reroutes.push(reroute);
- }
- prevNode.outputs[0].connectTo(nodes.sampler.inputs.model);
-
- const group = await convertToGroup(app, graph, "test", [...reroutes, ...Object.values(nodes)]);
- expect((await graph.toPrompt()).output).toEqual(getOutput());
-
- group.menu["Convert to nodes"].call();
- expect((await graph.toPrompt()).output).toEqual(getOutput());
- });
- test("creates with widget values from inner nodes", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
-
- nodes.ckpt.widgets.ckpt_name.value = "model2.ckpt";
- nodes.pos.widgets.text.value = "hello";
- nodes.neg.widgets.text.value = "world";
- nodes.empty.widgets.width.value = 256;
- nodes.empty.widgets.height.value = 1024;
- nodes.sampler.widgets.seed.value = 1;
- nodes.sampler.widgets.control_after_generate.value = "increment";
- nodes.sampler.widgets.steps.value = 8;
- nodes.sampler.widgets.cfg.value = 4.5;
- nodes.sampler.widgets.sampler_name.value = "uni_pc";
- nodes.sampler.widgets.scheduler.value = "karras";
- nodes.sampler.widgets.denoise.value = 0.9;
-
- const group = await convertToGroup(app, graph, "test", [
- nodes.ckpt,
- nodes.pos,
- nodes.neg,
- nodes.empty,
- nodes.sampler,
- ]);
-
- expect(group.widgets["ckpt_name"].value).toEqual("model2.ckpt");
- expect(group.widgets["text"].value).toEqual("hello");
- expect(group.widgets["CLIPTextEncode text"].value).toEqual("world");
- expect(group.widgets["width"].value).toEqual(256);
- expect(group.widgets["height"].value).toEqual(1024);
- expect(group.widgets["seed"].value).toEqual(1);
- expect(group.widgets["control_after_generate"].value).toEqual("increment");
- expect(group.widgets["steps"].value).toEqual(8);
- expect(group.widgets["cfg"].value).toEqual(4.5);
- expect(group.widgets["sampler_name"].value).toEqual("uni_pc");
- expect(group.widgets["scheduler"].value).toEqual("karras");
- expect(group.widgets["denoise"].value).toEqual(0.9);
-
- expect((await graph.toPrompt()).output).toEqual(
- getOutput([nodes.ckpt.id, nodes.pos.id, nodes.neg.id, nodes.empty.id, nodes.sampler.id], {
- [nodes.ckpt.id]: { ckpt_name: "model2.ckpt" },
- [nodes.pos.id]: { text: "hello" },
- [nodes.neg.id]: { text: "world" },
- [nodes.empty.id]: { width: 256, height: 1024 },
- [nodes.sampler.id]: {
- seed: 1,
- steps: 8,
- cfg: 4.5,
- sampler_name: "uni_pc",
- scheduler: "karras",
- denoise: 0.9,
- },
- })
- );
- });
- test("group inputs can be reroutes", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
- const group = await convertToGroup(app, graph, "test", [nodes.pos, nodes.neg]);
-
- const reroute = ez.Reroute();
- nodes.ckpt.outputs.CLIP.connectTo(reroute.inputs[0]);
-
- reroute.outputs[0].connectTo(group.inputs[0]);
- reroute.outputs[0].connectTo(group.inputs[1]);
-
- expect((await graph.toPrompt()).output).toEqual(getOutput([nodes.pos.id, nodes.neg.id]));
- });
- test("group outputs can be reroutes", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
- const group = await convertToGroup(app, graph, "test", [nodes.pos, nodes.neg]);
-
- const reroute1 = ez.Reroute();
- const reroute2 = ez.Reroute();
- group.outputs[0].connectTo(reroute1.inputs[0]);
- group.outputs[1].connectTo(reroute2.inputs[0]);
-
- reroute1.outputs[0].connectTo(nodes.sampler.inputs.positive);
- reroute2.outputs[0].connectTo(nodes.sampler.inputs.negative);
-
- expect((await graph.toPrompt()).output).toEqual(getOutput([nodes.pos.id, nodes.neg.id]));
- });
- test("groups can connect to each other", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
- const group1 = await convertToGroup(app, graph, "test", [nodes.pos, nodes.neg]);
- const group2 = await convertToGroup(app, graph, "test2", [nodes.empty, nodes.sampler]);
-
- group1.outputs[0].connectTo(group2.inputs["positive"]);
- group1.outputs[1].connectTo(group2.inputs["negative"]);
-
- expect((await graph.toPrompt()).output).toEqual(
- getOutput([nodes.pos.id, nodes.neg.id, nodes.empty.id, nodes.sampler.id])
- );
- });
- test("groups can connect to each other via internal reroutes", async () => {
- const { ez, graph, app } = await start();
-
- const latent = ez.EmptyLatentImage();
- const vae = ez.VAELoader();
- const latentReroute = ez.Reroute();
- const vaeReroute = ez.Reroute();
-
- latent.outputs[0].connectTo(latentReroute.inputs[0]);
- vae.outputs[0].connectTo(vaeReroute.inputs[0]);
-
- const group1 = await convertToGroup(app, graph, "test", [latentReroute, vaeReroute]);
- group1.menu.Clone.call();
- expect(app.graph._nodes).toHaveLength(4);
- const group2 = graph.find(app.graph._nodes[3]);
- expect(group2.node.type).toEqual("workflow/test");
- expect(group2.id).not.toEqual(group1.id);
-
- group1.outputs.VAE.connectTo(group2.inputs.VAE);
- group1.outputs.LATENT.connectTo(group2.inputs.LATENT);
-
- const decode = ez.VAEDecode(group2.outputs.LATENT, group2.outputs.VAE);
- const preview = ez.PreviewImage(decode.outputs[0]);
-
- const output = {
- [latent.id]: { inputs: { width: 512, height: 512, batch_size: 1 }, class_type: "EmptyLatentImage" },
- [vae.id]: { inputs: { vae_name: "vae1.safetensors" }, class_type: "VAELoader" },
- [decode.id]: { inputs: { samples: [latent.id + "", 0], vae: [vae.id + "", 0] }, class_type: "VAEDecode" },
- [preview.id]: { inputs: { images: [decode.id + "", 0] }, class_type: "PreviewImage" },
- };
- expect((await graph.toPrompt()).output).toEqual(output);
-
- // Ensure missing connections dont cause errors
- group2.inputs.VAE.disconnect();
- delete output[decode.id].inputs.vae;
- expect((await graph.toPrompt()).output).toEqual(output);
- });
- test("displays generated image on group node", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
- let group = await convertToGroup(app, graph, "test", [
- nodes.pos,
- nodes.neg,
- nodes.empty,
- nodes.sampler,
- nodes.decode,
- nodes.save,
- ]);
-
- const { api } = require("../../web/scripts/api");
-
- api.dispatchEvent(new CustomEvent("execution_start", {}));
- api.dispatchEvent(new CustomEvent("executing", { detail: `${nodes.save.id}` }));
- // Event should be forwarded to group node id
- expect(+app.runningNodeId).toEqual(group.id);
- expect(group.node["imgs"]).toBeFalsy();
- api.dispatchEvent(
- new CustomEvent("executed", {
- detail: {
- node: `${nodes.save.id}`,
- output: {
- images: [
- {
- filename: "test.png",
- type: "output",
- },
- ],
- },
- },
- })
- );
-
- // Trigger paint
- group.node.onDrawBackground?.(app.canvas.ctx, app.canvas.canvas);
-
- expect(group.node["images"]).toEqual([
- {
- filename: "test.png",
- type: "output",
- },
- ]);
-
- // Reload
- const workflow = JSON.stringify((await graph.toPrompt()).workflow);
- await app.loadGraphData(JSON.parse(workflow));
- group = graph.find(group);
-
- // Trigger inner nodes to get created
- group.node["getInnerNodes"]();
-
- // Check it works for internal node ids
- api.dispatchEvent(new CustomEvent("execution_start", {}));
- api.dispatchEvent(new CustomEvent("executing", { detail: `${group.id}:5` }));
- // Event should be forwarded to group node id
- expect(+app.runningNodeId).toEqual(group.id);
- expect(group.node["imgs"]).toBeFalsy();
- api.dispatchEvent(
- new CustomEvent("executed", {
- detail: {
- node: `${group.id}:5`,
- output: {
- images: [
- {
- filename: "test2.png",
- type: "output",
- },
- ],
- },
- },
- })
- );
-
- // Trigger paint
- group.node.onDrawBackground?.(app.canvas.ctx, app.canvas.canvas);
-
- expect(group.node["images"]).toEqual([
- {
- filename: "test2.png",
- type: "output",
- },
- ]);
- });
- test("allows widgets to be converted to inputs", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
- const group = await convertToGroup(app, graph, "test", [nodes.pos, nodes.neg]);
- group.widgets[0].convertToInput();
-
- const primitive = ez.PrimitiveNode();
- primitive.outputs[0].connectTo(group.inputs["text"]);
- primitive.widgets[0].value = "hello";
-
- expect((await graph.toPrompt()).output).toEqual(
- getOutput([nodes.pos.id, nodes.neg.id], {
- [nodes.pos.id]: { text: "hello" },
- })
- );
- });
- test("can be copied", async () => {
- const { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
-
- const group1 = await convertToGroup(app, graph, "test", [
- nodes.pos,
- nodes.neg,
- nodes.empty,
- nodes.sampler,
- nodes.decode,
- nodes.save,
- ]);
-
- group1.widgets["text"].value = "hello";
- group1.widgets["width"].value = 256;
- group1.widgets["seed"].value = 1;
-
- // Clone the node
- group1.menu.Clone.call();
- expect(app.graph._nodes).toHaveLength(3);
- const group2 = graph.find(app.graph._nodes[2]);
- expect(group2.node.type).toEqual("workflow/test");
- expect(group2.id).not.toEqual(group1.id);
-
- // Reconnect ckpt
- nodes.ckpt.outputs.MODEL.connectTo(group2.inputs["model"]);
- nodes.ckpt.outputs.CLIP.connectTo(group2.inputs["clip"]);
- nodes.ckpt.outputs.CLIP.connectTo(group2.inputs["CLIPTextEncode clip"]);
- nodes.ckpt.outputs.VAE.connectTo(group2.inputs["vae"]);
-
- group2.widgets["text"].value = "world";
- group2.widgets["width"].value = 1024;
- group2.widgets["seed"].value = 100;
-
- let i = 0;
- expect((await graph.toPrompt()).output).toEqual({
- ...getOutput([nodes.empty.id, nodes.pos.id, nodes.neg.id, nodes.sampler.id, nodes.decode.id, nodes.save.id], {
- [nodes.empty.id]: { width: 256 },
- [nodes.pos.id]: { text: "hello" },
- [nodes.sampler.id]: { seed: 1 },
- }),
- ...getOutput(
- {
- [nodes.empty.id]: `${group2.id}:${i++}`,
- [nodes.pos.id]: `${group2.id}:${i++}`,
- [nodes.neg.id]: `${group2.id}:${i++}`,
- [nodes.sampler.id]: `${group2.id}:${i++}`,
- [nodes.decode.id]: `${group2.id}:${i++}`,
- [nodes.save.id]: `${group2.id}:${i++}`,
- },
- {
- [nodes.empty.id]: { width: 1024 },
- [nodes.pos.id]: { text: "world" },
- [nodes.sampler.id]: { seed: 100 },
- }
- ),
- });
-
- graph.arrange();
- });
- test("is embedded in workflow", async () => {
- let { ez, graph, app } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
- let group = await convertToGroup(app, graph, "test", [nodes.pos, nodes.neg]);
- const workflow = JSON.stringify((await graph.toPrompt()).workflow);
-
- // Clear the environment
- ({ ez, graph, app } = await start({
- resetEnv: true,
- }));
- // Ensure the node isnt registered
- expect(() => ez["workflow/test"]).toThrow();
-
- // Reload the workflow
- await app.loadGraphData(JSON.parse(workflow));
-
- // Ensure the node is found
- group = graph.find(group);
-
- // Generate prompt and ensure it is as expected
- expect((await graph.toPrompt()).output).toEqual(
- getOutput({
- [nodes.pos.id]: `${group.id}:0`,
- [nodes.neg.id]: `${group.id}:1`,
- })
- );
- });
- test("shows missing node error on missing internal node when loading graph data", async () => {
- const { graph } = await start();
-
- const dialogShow = jest.spyOn(graph.app.ui.dialog, "show");
- await graph.app.loadGraphData({
- last_node_id: 3,
- last_link_id: 1,
- nodes: [
- {
- id: 3,
- type: "workflow/testerror",
- },
- ],
- links: [],
- groups: [],
- config: {},
- extra: {
- groupNodes: {
- testerror: {
- nodes: [
- {
- type: "NotKSampler",
- },
- {
- type: "NotVAEDecode",
- },
- ],
- },
- },
- },
- });
-
- expect(dialogShow).toBeCalledTimes(1);
- const call = dialogShow.mock.calls[0][0].innerHTML;
- expect(call).toContain("the following node types were not found");
- expect(call).toContain("NotKSampler");
- expect(call).toContain("NotVAEDecode");
- expect(call).toContain("workflow/testerror");
- });
- test("maintains widget inputs on conversion back to nodes", async () => {
- const { ez, graph, app } = await start();
- let pos = ez.CLIPTextEncode({ text: "positive" });
- pos.node.title = "Positive";
- let neg = ez.CLIPTextEncode({ text: "negative" });
- neg.node.title = "Negative";
- pos.widgets.text.convertToInput();
- neg.widgets.text.convertToInput();
-
- let primitive = ez.PrimitiveNode();
- primitive.outputs[0].connectTo(pos.inputs.text);
- primitive.outputs[0].connectTo(neg.inputs.text);
-
- const group = await convertToGroup(app, graph, "test", [pos, neg, primitive]);
- // This will use a primitive widget named 'value'
- expect(group.widgets.length).toBe(1);
- expect(group.widgets["value"].value).toBe("positive");
-
- const newNodes = group.menu["Convert to nodes"].call();
- pos = graph.find(newNodes.find((n) => n.title === "Positive"));
- neg = graph.find(newNodes.find((n) => n.title === "Negative"));
- primitive = graph.find(newNodes.find((n) => n.type === "PrimitiveNode"));
-
- expect(pos.inputs).toHaveLength(2);
- expect(neg.inputs).toHaveLength(2);
- expect(primitive.outputs[0].connections).toHaveLength(2);
-
- expect((await graph.toPrompt()).output).toEqual({
- 1: { inputs: { text: "positive" }, class_type: "CLIPTextEncode" },
- 2: { inputs: { text: "positive" }, class_type: "CLIPTextEncode" },
- });
- });
- test("correctly handles widget inputs", async () => {
- const { ez, graph, app } = await start();
- const upscaleMethods = (await getNodeDef("ImageScaleBy")).input.required["upscale_method"][0];
-
- const image = ez.LoadImage();
- const scale1 = ez.ImageScaleBy(image.outputs[0]);
- const scale2 = ez.ImageScaleBy(image.outputs[0]);
- const preview1 = ez.PreviewImage(scale1.outputs[0]);
- const preview2 = ez.PreviewImage(scale2.outputs[0]);
- scale1.widgets.upscale_method.value = upscaleMethods[1];
- scale1.widgets.upscale_method.convertToInput();
-
- const group = await convertToGroup(app, graph, "test", [scale1, scale2]);
- expect(group.inputs.length).toBe(3);
- expect(group.inputs[0].input.type).toBe("IMAGE");
- expect(group.inputs[1].input.type).toBe("IMAGE");
- expect(group.inputs[2].input.type).toBe("COMBO");
-
- // Ensure links are maintained
- expect(group.inputs[0].connection?.originNode?.id).toBe(image.id);
- expect(group.inputs[1].connection?.originNode?.id).toBe(image.id);
- expect(group.inputs[2].connection).toBeFalsy();
-
- // Ensure primitive gets correct type
- const primitive = ez.PrimitiveNode();
- primitive.outputs[0].connectTo(group.inputs[2]);
- expect(primitive.widgets.value.widget.options.values).toBe(upscaleMethods);
- expect(primitive.widgets.value.value).toBe(upscaleMethods[1]); // Ensure value is copied
- primitive.widgets.value.value = upscaleMethods[1];
-
- await checkBeforeAndAfterReload(graph, async (r) => {
- const scale1id = r ? `${group.id}:0` : scale1.id;
- const scale2id = r ? `${group.id}:1` : scale2.id;
- // Ensure widget value is applied to prompt
- expect((await graph.toPrompt()).output).toStrictEqual({
- [image.id]: { inputs: { image: "example.png", upload: "image" }, class_type: "LoadImage" },
- [scale1id]: {
- inputs: { upscale_method: upscaleMethods[1], scale_by: 1, image: [`${image.id}`, 0] },
- class_type: "ImageScaleBy",
- },
- [scale2id]: {
- inputs: { upscale_method: "nearest-exact", scale_by: 1, image: [`${image.id}`, 0] },
- class_type: "ImageScaleBy",
- },
- [preview1.id]: { inputs: { images: [`${scale1id}`, 0] }, class_type: "PreviewImage" },
- [preview2.id]: { inputs: { images: [`${scale2id}`, 0] }, class_type: "PreviewImage" },
- });
- });
- });
- test("adds widgets in node execution order", async () => {
- const { ez, graph, app } = await start();
- const scale = ez.LatentUpscale();
- const save = ez.SaveImage();
- const empty = ez.EmptyLatentImage();
- const decode = ez.VAEDecode();
-
- scale.outputs.LATENT.connectTo(decode.inputs.samples);
- decode.outputs.IMAGE.connectTo(save.inputs.images);
- empty.outputs.LATENT.connectTo(scale.inputs.samples);
-
- const group = await convertToGroup(app, graph, "test", [scale, save, empty, decode]);
- const widgets = group.widgets.map((w) => w.widget.name);
- expect(widgets).toStrictEqual([
- "width",
- "height",
- "batch_size",
- "upscale_method",
- "LatentUpscale width",
- "LatentUpscale height",
- "crop",
- "filename_prefix",
- ]);
- });
- test("adds output for external links when converting to group", async () => {
- const { ez, graph, app } = await start();
- const img = ez.EmptyLatentImage();
- let decode = ez.VAEDecode(...img.outputs);
- const preview1 = ez.PreviewImage(...decode.outputs);
- const preview2 = ez.PreviewImage(...decode.outputs);
-
- const group = await convertToGroup(app, graph, "test", [img, decode, preview1]);
-
- // Ensure we have an output connected to the 2nd preview node
- expect(group.outputs.length).toBe(1);
- expect(group.outputs[0].connections.length).toBe(1);
- expect(group.outputs[0].connections[0].targetNode.id).toBe(preview2.id);
-
- // Convert back and ensure bothe previews are still connected
- group.menu["Convert to nodes"].call();
- decode = graph.find(decode);
- expect(decode.outputs[0].connections.length).toBe(2);
- expect(decode.outputs[0].connections[0].targetNode.id).toBe(preview1.id);
- expect(decode.outputs[0].connections[1].targetNode.id).toBe(preview2.id);
- });
- test("adds output for external links when converting to group when nodes are not in execution order", async () => {
- const { ez, graph, app } = await start();
- const sampler = ez.KSampler();
- const ckpt = ez.CheckpointLoaderSimple();
- const empty = ez.EmptyLatentImage();
- const pos = ez.CLIPTextEncode(ckpt.outputs.CLIP, { text: "positive" });
- const neg = ez.CLIPTextEncode(ckpt.outputs.CLIP, { text: "negative" });
- const decode1 = ez.VAEDecode(sampler.outputs.LATENT, ckpt.outputs.VAE);
- const save = ez.SaveImage(decode1.outputs.IMAGE);
- ckpt.outputs.MODEL.connectTo(sampler.inputs.model);
- pos.outputs.CONDITIONING.connectTo(sampler.inputs.positive);
- neg.outputs.CONDITIONING.connectTo(sampler.inputs.negative);
- empty.outputs.LATENT.connectTo(sampler.inputs.latent_image);
-
- const encode = ez.VAEEncode(decode1.outputs.IMAGE);
- const vae = ez.VAELoader();
- const decode2 = ez.VAEDecode(encode.outputs.LATENT, vae.outputs.VAE);
- const preview = ez.PreviewImage(decode2.outputs.IMAGE);
- vae.outputs.VAE.connectTo(encode.inputs.vae);
-
- const group = await convertToGroup(app, graph, "test", [vae, decode1, encode, sampler]);
-
- expect(group.outputs.length).toBe(3);
- expect(group.outputs[0].output.name).toBe("VAE");
- expect(group.outputs[0].output.type).toBe("VAE");
- expect(group.outputs[1].output.name).toBe("IMAGE");
- expect(group.outputs[1].output.type).toBe("IMAGE");
- expect(group.outputs[2].output.name).toBe("LATENT");
- expect(group.outputs[2].output.type).toBe("LATENT");
-
- expect(group.outputs[0].connections.length).toBe(1);
- expect(group.outputs[0].connections[0].targetNode.id).toBe(decode2.id);
- expect(group.outputs[0].connections[0].targetInput.index).toBe(1);
-
- expect(group.outputs[1].connections.length).toBe(1);
- expect(group.outputs[1].connections[0].targetNode.id).toBe(save.id);
- expect(group.outputs[1].connections[0].targetInput.index).toBe(0);
-
- expect(group.outputs[2].connections.length).toBe(1);
- expect(group.outputs[2].connections[0].targetNode.id).toBe(decode2.id);
- expect(group.outputs[2].connections[0].targetInput.index).toBe(0);
-
- expect((await graph.toPrompt()).output).toEqual({
- ...getOutput({ 1: ckpt.id, 2: pos.id, 3: neg.id, 4: empty.id, 5: sampler.id, 6: decode1.id, 7: save.id }),
- [vae.id]: { inputs: { vae_name: "vae1.safetensors" }, class_type: vae.node.type },
- [encode.id]: { inputs: { pixels: ["6", 0], vae: [vae.id + "", 0] }, class_type: encode.node.type },
- [decode2.id]: { inputs: { samples: [encode.id + "", 0], vae: [vae.id + "", 0] }, class_type: decode2.node.type },
- [preview.id]: { inputs: { images: [decode2.id + "", 0] }, class_type: preview.node.type },
- });
- });
- test("works with IMAGEUPLOAD widget", async () => {
- const { ez, graph, app } = await start();
- const img = ez.LoadImage();
- const preview1 = ez.PreviewImage(img.outputs[0]);
-
- const group = await convertToGroup(app, graph, "test", [img, preview1]);
- const widget = group.widgets["upload"];
- expect(widget).toBeTruthy();
- expect(widget.widget.type).toBe("button");
- });
- test("internal primitive populates widgets for all linked inputs", async () => {
- const { ez, graph, app } = await start();
- const img = ez.LoadImage();
- const scale1 = ez.ImageScale(img.outputs[0]);
- const scale2 = ez.ImageScale(img.outputs[0]);
- ez.PreviewImage(scale1.outputs[0]);
- ez.PreviewImage(scale2.outputs[0]);
-
- scale1.widgets.width.convertToInput();
- scale2.widgets.height.convertToInput();
-
- const primitive = ez.PrimitiveNode();
- primitive.outputs[0].connectTo(scale1.inputs.width);
- primitive.outputs[0].connectTo(scale2.inputs.height);
-
- const group = await convertToGroup(app, graph, "test", [img, primitive, scale1, scale2]);
- group.widgets.value.value = 100;
- expect((await graph.toPrompt()).output).toEqual({
- 1: {
- inputs: { image: img.widgets.image.value, upload: "image" },
- class_type: "LoadImage",
- },
- 2: {
- inputs: { upscale_method: "nearest-exact", width: 100, height: 512, crop: "disabled", image: ["1", 0] },
- class_type: "ImageScale",
- },
- 3: {
- inputs: { upscale_method: "nearest-exact", width: 512, height: 100, crop: "disabled", image: ["1", 0] },
- class_type: "ImageScale",
- },
- 4: { inputs: { images: ["2", 0] }, class_type: "PreviewImage" },
- 5: { inputs: { images: ["3", 0] }, class_type: "PreviewImage" },
- });
- });
- test("primitive control widgets values are copied on convert", async () => {
- const { ez, graph, app } = await start();
- const sampler = ez.KSampler();
- sampler.widgets.seed.convertToInput();
- sampler.widgets.sampler_name.convertToInput();
-
- let p1 = ez.PrimitiveNode();
- let p2 = ez.PrimitiveNode();
- p1.outputs[0].connectTo(sampler.inputs.seed);
- p2.outputs[0].connectTo(sampler.inputs.sampler_name);
-
- p1.widgets.control_after_generate.value = "increment";
- p2.widgets.control_after_generate.value = "decrement";
- p2.widgets.control_filter_list.value = "/.*/";
-
- p2.node.title = "p2";
-
- const group = await convertToGroup(app, graph, "test", [sampler, p1, p2]);
- expect(group.widgets.control_after_generate.value).toBe("increment");
- expect(group.widgets["p2 control_after_generate"].value).toBe("decrement");
- expect(group.widgets["p2 control_filter_list"].value).toBe("/.*/");
-
- group.widgets.control_after_generate.value = "fixed";
- group.widgets["p2 control_after_generate"].value = "randomize";
- group.widgets["p2 control_filter_list"].value = "/.+/";
-
- group.menu["Convert to nodes"].call();
- p1 = graph.find(p1);
- p2 = graph.find(p2);
-
- expect(p1.widgets.control_after_generate.value).toBe("fixed");
- expect(p2.widgets.control_after_generate.value).toBe("randomize");
- expect(p2.widgets.control_filter_list.value).toBe("/.+/");
- });
- test("internal reroutes work with converted inputs and merge options", async () => {
- const { ez, graph, app } = await start();
- const vae = ez.VAELoader();
- const latent = ez.EmptyLatentImage();
- const decode = ez.VAEDecode(latent.outputs.LATENT, vae.outputs.VAE);
- const scale = ez.ImageScale(decode.outputs.IMAGE);
- ez.PreviewImage(scale.outputs.IMAGE);
-
- const r1 = ez.Reroute();
- const r2 = ez.Reroute();
-
- latent.widgets.width.value = 64;
- latent.widgets.height.value = 128;
-
- latent.widgets.width.convertToInput();
- latent.widgets.height.convertToInput();
- latent.widgets.batch_size.convertToInput();
-
- scale.widgets.width.convertToInput();
- scale.widgets.height.convertToInput();
-
- r1.inputs[0].input.label = "hbw";
- r1.outputs[0].connectTo(latent.inputs.height);
- r1.outputs[0].connectTo(latent.inputs.batch_size);
- r1.outputs[0].connectTo(scale.inputs.width);
-
- r2.inputs[0].input.label = "wh";
- r2.outputs[0].connectTo(latent.inputs.width);
- r2.outputs[0].connectTo(scale.inputs.height);
-
- const group = await convertToGroup(app, graph, "test", [r1, r2, latent, decode, scale]);
-
- expect(group.inputs[0].input.type).toBe("VAE");
- expect(group.inputs[1].input.type).toBe("INT");
- expect(group.inputs[2].input.type).toBe("INT");
-
- const p1 = ez.PrimitiveNode();
- const p2 = ez.PrimitiveNode();
- p1.outputs[0].connectTo(group.inputs[1]);
- p2.outputs[0].connectTo(group.inputs[2]);
-
- expect(p1.widgets.value.widget.options?.min).toBe(16); // width/height min
- expect(p1.widgets.value.widget.options?.max).toBe(4096); // batch max
- expect(p1.widgets.value.widget.options?.step).toBe(80); // width/height step * 10
-
- expect(p2.widgets.value.widget.options?.min).toBe(16); // width/height min
- expect(p2.widgets.value.widget.options?.max).toBe(16384); // width/height max
- expect(p2.widgets.value.widget.options?.step).toBe(80); // width/height step * 10
-
- expect(p1.widgets.value.value).toBe(128);
- expect(p2.widgets.value.value).toBe(64);
-
- p1.widgets.value.value = 16;
- p2.widgets.value.value = 32;
-
- await checkBeforeAndAfterReload(graph, async (r) => {
- const id = (v) => (r ? `${group.id}:` : "") + v;
- expect((await graph.toPrompt()).output).toStrictEqual({
- 1: { inputs: { vae_name: "vae1.safetensors" }, class_type: "VAELoader" },
- [id(2)]: { inputs: { width: 32, height: 16, batch_size: 16 }, class_type: "EmptyLatentImage" },
- [id(3)]: { inputs: { samples: [id(2), 0], vae: ["1", 0] }, class_type: "VAEDecode" },
- [id(4)]: {
- inputs: { upscale_method: "nearest-exact", width: 16, height: 32, crop: "disabled", image: [id(3), 0] },
- class_type: "ImageScale",
- },
- 5: { inputs: { images: [id(4), 0] }, class_type: "PreviewImage" },
- });
- });
- });
- test("converted inputs with linked widgets map values correctly on creation", async () => {
- const { ez, graph, app } = await start();
- const k1 = ez.KSampler();
- const k2 = ez.KSampler();
- k1.widgets.seed.convertToInput();
- k2.widgets.seed.convertToInput();
-
- const rr = ez.Reroute();
- rr.outputs[0].connectTo(k1.inputs.seed);
- rr.outputs[0].connectTo(k2.inputs.seed);
-
- const group = await convertToGroup(app, graph, "test", [k1, k2, rr]);
- expect(group.widgets.steps.value).toBe(20);
- expect(group.widgets.cfg.value).toBe(8);
- expect(group.widgets.scheduler.value).toBe("normal");
- expect(group.widgets["KSampler steps"].value).toBe(20);
- expect(group.widgets["KSampler cfg"].value).toBe(8);
- expect(group.widgets["KSampler scheduler"].value).toBe("normal");
- });
- test("allow multiple of the same node type to be added", async () => {
- const { ez, graph, app } = await start();
- const nodes = [...Array(10)].map(() => ez.ImageScaleBy());
- const group = await convertToGroup(app, graph, "test", nodes);
- expect(group.inputs.length).toBe(10);
- expect(group.outputs.length).toBe(10);
- expect(group.widgets.length).toBe(20);
- expect(group.widgets.map((w) => w.widget.name)).toStrictEqual(
- [...Array(10)]
- .map((_, i) => `${i > 0 ? "ImageScaleBy " : ""}${i > 1 ? i + " " : ""}`)
- .flatMap((p) => [`${p}upscale_method`, `${p}scale_by`])
- );
- });
-});
diff --git a/tests-ui/tests/users.test.js b/tests-ui/tests/users.test.js
deleted file mode 100644
index 5e0730730..000000000
--- a/tests-ui/tests/users.test.js
+++ /dev/null
@@ -1,295 +0,0 @@
-// @ts-check
-///
-const { start } = require("../utils");
-const lg = require("../utils/litegraph");
-
-describe("users", () => {
- beforeEach(() => {
- lg.setup(global);
- });
-
- afterEach(() => {
- lg.teardown(global);
- });
-
- function expectNoUserScreen() {
- // Ensure login isnt visible
- const selection = document.querySelectorAll("#comfy-user-selection")?.[0];
- expect(selection["style"].display).toBe("none");
- const menu = document.querySelectorAll(".comfy-menu")?.[0];
- expect(window.getComputedStyle(menu)?.display).not.toBe("none");
- }
-
- describe("multi-user", () => {
- function mockAddStylesheet() {
- const utils = require("../../web/scripts/utils");
- utils.addStylesheet = jest.fn().mockReturnValue(Promise.resolve());
- }
-
- async function waitForUserScreenShow() {
- mockAddStylesheet();
-
- // Wait for "show" to be called
- const { UserSelectionScreen } = require("../../web/scripts/ui/userSelection");
- let resolve, reject;
- const fn = UserSelectionScreen.prototype.show;
- const p = new Promise((res, rej) => {
- resolve = res;
- reject = rej;
- });
- jest.spyOn(UserSelectionScreen.prototype, "show").mockImplementation(async (...args) => {
- const res = fn(...args);
- await new Promise(process.nextTick); // wait for promises to resolve
- resolve();
- return res;
- });
- // @ts-ignore
- setTimeout(() => reject("timeout waiting for UserSelectionScreen to be shown."), 500);
- await p;
- await new Promise(process.nextTick); // wait for promises to resolve
- }
-
- async function testUserScreen(onShown, users) {
- if (!users) {
- users = {};
- }
- const starting = start({
- resetEnv: true,
- userConfig: { storage: "server", users },
- });
-
- // Ensure no current user
- expect(localStorage["Comfy.userId"]).toBeFalsy();
- expect(localStorage["Comfy.userName"]).toBeFalsy();
-
- await waitForUserScreenShow();
-
- const selection = document.querySelectorAll("#comfy-user-selection")?.[0];
- expect(selection).toBeTruthy();
-
- // Ensure login is visible
- expect(window.getComputedStyle(selection)?.display).not.toBe("none");
- // Ensure menu is hidden
- const menu = document.querySelectorAll(".comfy-menu")?.[0];
- expect(window.getComputedStyle(menu)?.display).toBe("none");
-
- const isCreate = await onShown(selection);
-
- // Submit form
- selection.querySelectorAll("form")[0].submit();
- await new Promise(process.nextTick); // wait for promises to resolve
-
- // Wait for start
- const s = await starting;
-
- // Ensure login is removed
- expect(document.querySelectorAll("#comfy-user-selection")).toHaveLength(0);
- expect(window.getComputedStyle(menu)?.display).not.toBe("none");
-
- // Ensure settings + templates are saved
- const { api } = require("../../web/scripts/api");
- expect(api.createUser).toHaveBeenCalledTimes(+isCreate);
- expect(api.storeSettings).toHaveBeenCalledTimes(+isCreate);
- expect(api.storeUserData).toHaveBeenCalledTimes(+isCreate);
- if (isCreate) {
- expect(api.storeUserData).toHaveBeenCalledWith("comfy.templates.json", null, { stringify: false });
- expect(s.app.isNewUserSession).toBeTruthy();
- } else {
- expect(s.app.isNewUserSession).toBeFalsy();
- }
-
- return { users, selection, ...s };
- }
-
- it("allows user creation if no users", async () => {
- const { users } = await testUserScreen((selection) => {
- // Ensure we have no users flag added
- expect(selection.classList.contains("no-users")).toBeTruthy();
-
- // Enter a username
- const input = selection.getElementsByTagName("input")[0];
- input.focus();
- input.value = "Test User";
-
- return true;
- });
-
- expect(users).toStrictEqual({
- "Test User!": "Test User",
- });
-
- expect(localStorage["Comfy.userId"]).toBe("Test User!");
- expect(localStorage["Comfy.userName"]).toBe("Test User");
- });
- it("allows user creation if no current user but other users", async () => {
- const users = {
- "Test User 2!": "Test User 2",
- };
-
- await testUserScreen((selection) => {
- expect(selection.classList.contains("no-users")).toBeFalsy();
-
- // Enter a username
- const input = selection.getElementsByTagName("input")[0];
- input.focus();
- input.value = "Test User 3";
- return true;
- }, users);
-
- expect(users).toStrictEqual({
- "Test User 2!": "Test User 2",
- "Test User 3!": "Test User 3",
- });
-
- expect(localStorage["Comfy.userId"]).toBe("Test User 3!");
- expect(localStorage["Comfy.userName"]).toBe("Test User 3");
- });
- it("allows user selection if no current user but other users", async () => {
- const users = {
- "A!": "A",
- "B!": "B",
- "C!": "C",
- };
-
- await testUserScreen((selection) => {
- expect(selection.classList.contains("no-users")).toBeFalsy();
-
- // Check user list
- const select = selection.getElementsByTagName("select")[0];
- const options = select.getElementsByTagName("option");
- expect(
- [...options]
- .filter((o) => !o.disabled)
- .reduce((p, n) => {
- p[n.getAttribute("value")] = n.textContent;
- return p;
- }, {})
- ).toStrictEqual(users);
-
- // Select an option
- select.focus();
- select.value = options[2].value;
-
- return false;
- }, users);
-
- expect(users).toStrictEqual(users);
-
- expect(localStorage["Comfy.userId"]).toBe("B!");
- expect(localStorage["Comfy.userName"]).toBe("B");
- });
- it("doesnt show user screen if current user", async () => {
- const starting = start({
- resetEnv: true,
- userConfig: {
- storage: "server",
- users: {
- "User!": "User",
- },
- },
- localStorage: {
- "Comfy.userId": "User!",
- "Comfy.userName": "User",
- },
- });
- await new Promise(process.nextTick); // wait for promises to resolve
-
- expectNoUserScreen();
-
- await starting;
- });
- it("allows user switching", async () => {
- const { app } = await start({
- resetEnv: true,
- userConfig: {
- storage: "server",
- users: {
- "User!": "User",
- },
- },
- localStorage: {
- "Comfy.userId": "User!",
- "Comfy.userName": "User",
- },
- });
-
- // cant actually test switching user easily but can check the setting is present
- expect(app.ui.settings.settingsLookup["Comfy.SwitchUser"]).toBeTruthy();
- });
- });
- describe("single-user", () => {
- it("doesnt show user creation if no default user", async () => {
- const { app } = await start({
- resetEnv: true,
- userConfig: { migrated: false, storage: "server" },
- });
- expectNoUserScreen();
-
- // It should store the settings
- const { api } = require("../../web/scripts/api");
- expect(api.storeSettings).toHaveBeenCalledTimes(1);
- expect(api.storeUserData).toHaveBeenCalledTimes(1);
- expect(api.storeUserData).toHaveBeenCalledWith("comfy.templates.json", null, { stringify: false });
- expect(app.isNewUserSession).toBeTruthy();
- });
- it("doesnt show user creation if default user", async () => {
- const { app } = await start({
- resetEnv: true,
- userConfig: { migrated: true, storage: "server" },
- });
- expectNoUserScreen();
-
- // It should store the settings
- const { api } = require("../../web/scripts/api");
- expect(api.storeSettings).toHaveBeenCalledTimes(0);
- expect(api.storeUserData).toHaveBeenCalledTimes(0);
- expect(app.isNewUserSession).toBeFalsy();
- });
- it("doesnt allow user switching", async () => {
- const { app } = await start({
- resetEnv: true,
- userConfig: { migrated: true, storage: "server" },
- });
- expectNoUserScreen();
-
- expect(app.ui.settings.settingsLookup["Comfy.SwitchUser"]).toBeFalsy();
- });
- });
- describe("browser-user", () => {
- it("doesnt show user creation if no default user", async () => {
- const { app } = await start({
- resetEnv: true,
- userConfig: { migrated: false, storage: "browser" },
- });
- expectNoUserScreen();
-
- // It should store the settings
- const { api } = require("../../web/scripts/api");
- expect(api.storeSettings).toHaveBeenCalledTimes(0);
- expect(api.storeUserData).toHaveBeenCalledTimes(0);
- expect(app.isNewUserSession).toBeFalsy();
- });
- it("doesnt show user creation if default user", async () => {
- const { app } = await start({
- resetEnv: true,
- userConfig: { migrated: true, storage: "server" },
- });
- expectNoUserScreen();
-
- // It should store the settings
- const { api } = require("../../web/scripts/api");
- expect(api.storeSettings).toHaveBeenCalledTimes(0);
- expect(api.storeUserData).toHaveBeenCalledTimes(0);
- expect(app.isNewUserSession).toBeFalsy();
- });
- it("doesnt allow user switching", async () => {
- const { app } = await start({
- resetEnv: true,
- userConfig: { migrated: true, storage: "browser" },
- });
- expectNoUserScreen();
-
- expect(app.ui.settings.settingsLookup["Comfy.SwitchUser"]).toBeFalsy();
- });
- });
-});
diff --git a/tests-ui/tests/widgetInputs.test.js b/tests-ui/tests/widgetInputs.test.js
deleted file mode 100644
index 67e3fa341..000000000
--- a/tests-ui/tests/widgetInputs.test.js
+++ /dev/null
@@ -1,557 +0,0 @@
-// @ts-check
-///
-
-const {
- start,
- makeNodeDef,
- checkBeforeAndAfterReload,
- assertNotNullOrUndefined,
- createDefaultWorkflow,
-} = require("../utils");
-const lg = require("../utils/litegraph");
-
-/**
- * @typedef { import("../utils/ezgraph") } Ez
- * @typedef { ReturnType["ez"] } EzNodeFactory
- */
-
-/**
- * @param { EzNodeFactory } ez
- * @param { InstanceType } graph
- * @param { InstanceType } input
- * @param { string } widgetType
- * @param { number } controlWidgetCount
- * @returns
- */
-async function connectPrimitiveAndReload(ez, graph, input, widgetType, controlWidgetCount = 0) {
- // Connect to primitive and ensure its still connected after
- let primitive = ez.PrimitiveNode();
- primitive.outputs[0].connectTo(input);
-
- await checkBeforeAndAfterReload(graph, async () => {
- primitive = graph.find(primitive);
- let { connections } = primitive.outputs[0];
- expect(connections).toHaveLength(1);
- expect(connections[0].targetNode.id).toBe(input.node.node.id);
-
- // Ensure widget is correct type
- const valueWidget = primitive.widgets.value;
- expect(valueWidget.widget.type).toBe(widgetType);
-
- // Check if control_after_generate should be added
- if (controlWidgetCount) {
- const controlWidget = primitive.widgets.control_after_generate;
- expect(controlWidget.widget.type).toBe("combo");
- if (widgetType === "combo") {
- const filterWidget = primitive.widgets.control_filter_list;
- expect(filterWidget.widget.type).toBe("string");
- }
- }
-
- // Ensure we dont have other widgets
- expect(primitive.node.widgets).toHaveLength(1 + controlWidgetCount);
- });
-
- return primitive;
-}
-
-describe("widget inputs", () => {
- beforeEach(() => {
- lg.setup(global);
- });
-
- afterEach(() => {
- lg.teardown(global);
- });
-
- [
- { name: "int", type: "INT", widget: "number", control: 1 },
- { name: "float", type: "FLOAT", widget: "number", control: 1 },
- { name: "text", type: "STRING" },
- {
- name: "customtext",
- type: "STRING",
- opt: { multiline: true },
- },
- { name: "toggle", type: "BOOLEAN" },
- { name: "combo", type: ["a", "b", "c"], control: 2 },
- ].forEach((c) => {
- test(`widget conversion + primitive works on ${c.name}`, async () => {
- const { ez, graph } = await start({
- mockNodeDefs: makeNodeDef("TestNode", { [c.name]: [c.type, c.opt ?? {}] }),
- });
-
- // Create test node and convert to input
- const n = ez.TestNode();
- const w = n.widgets[c.name];
- w.convertToInput();
- expect(w.isConvertedToInput).toBeTruthy();
- const input = w.getConvertedInput();
- expect(input).toBeTruthy();
-
- // @ts-ignore : input is valid here
- await connectPrimitiveAndReload(ez, graph, input, c.widget ?? c.name, c.control);
- });
- });
-
- test("converted widget works after reload", async () => {
- const { ez, graph } = await start();
- let n = ez.CheckpointLoaderSimple();
-
- const inputCount = n.inputs.length;
-
- // Convert ckpt name to an input
- n.widgets.ckpt_name.convertToInput();
- expect(n.widgets.ckpt_name.isConvertedToInput).toBeTruthy();
- expect(n.inputs.ckpt_name).toBeTruthy();
- expect(n.inputs.length).toEqual(inputCount + 1);
-
- // Convert back to widget and ensure input is removed
- n.widgets.ckpt_name.convertToWidget();
- expect(n.widgets.ckpt_name.isConvertedToInput).toBeFalsy();
- expect(n.inputs.ckpt_name).toBeFalsy();
- expect(n.inputs.length).toEqual(inputCount);
-
- // Convert again and reload the graph to ensure it maintains state
- n.widgets.ckpt_name.convertToInput();
- expect(n.inputs.length).toEqual(inputCount + 1);
-
- const primitive = await connectPrimitiveAndReload(ez, graph, n.inputs.ckpt_name, "combo", 2);
-
- // Disconnect & reconnect
- primitive.outputs[0].connections[0].disconnect();
- let { connections } = primitive.outputs[0];
- expect(connections).toHaveLength(0);
-
- primitive.outputs[0].connectTo(n.inputs.ckpt_name);
- ({ connections } = primitive.outputs[0]);
- expect(connections).toHaveLength(1);
- expect(connections[0].targetNode.id).toBe(n.node.id);
-
- // Convert back to widget and ensure input is removed
- n.widgets.ckpt_name.convertToWidget();
- expect(n.widgets.ckpt_name.isConvertedToInput).toBeFalsy();
- expect(n.inputs.ckpt_name).toBeFalsy();
- expect(n.inputs.length).toEqual(inputCount);
- });
-
- test("converted widget works on clone", async () => {
- const { graph, ez } = await start();
- let n = ez.CheckpointLoaderSimple();
-
- // Convert the widget to an input
- n.widgets.ckpt_name.convertToInput();
- expect(n.widgets.ckpt_name.isConvertedToInput).toBeTruthy();
-
- // Clone the node
- n.menu["Clone"].call();
- expect(graph.nodes).toHaveLength(2);
- const clone = graph.nodes[1];
- expect(clone.id).not.toEqual(n.id);
-
- // Ensure the clone has an input
- expect(clone.widgets.ckpt_name.isConvertedToInput).toBeTruthy();
- expect(clone.inputs.ckpt_name).toBeTruthy();
-
- // Ensure primitive connects to both nodes
- let primitive = ez.PrimitiveNode();
- primitive.outputs[0].connectTo(n.inputs.ckpt_name);
- primitive.outputs[0].connectTo(clone.inputs.ckpt_name);
- expect(primitive.outputs[0].connections).toHaveLength(2);
-
- // Convert back to widget and ensure input is removed
- clone.widgets.ckpt_name.convertToWidget();
- expect(clone.widgets.ckpt_name.isConvertedToInput).toBeFalsy();
- expect(clone.inputs.ckpt_name).toBeFalsy();
- });
-
- test("shows missing node error on custom node with converted input", async () => {
- const { graph } = await start();
-
- const dialogShow = jest.spyOn(graph.app.ui.dialog, "show");
-
- await graph.app.loadGraphData({
- last_node_id: 3,
- last_link_id: 4,
- nodes: [
- {
- id: 1,
- type: "TestNode",
- pos: [41.87329101561909, 389.7381480823742],
- size: { 0: 220, 1: 374 },
- flags: {},
- order: 1,
- mode: 0,
- inputs: [{ name: "test", type: "FLOAT", link: 4, widget: { name: "test" }, slot_index: 0 }],
- outputs: [],
- properties: { "Node name for S&R": "TestNode" },
- widgets_values: [1],
- },
- {
- id: 3,
- type: "PrimitiveNode",
- pos: [-312, 433],
- size: { 0: 210, 1: 82 },
- flags: {},
- order: 0,
- mode: 0,
- outputs: [{ links: [4], widget: { name: "test" } }],
- title: "test",
- properties: {},
- },
- ],
- links: [[4, 3, 0, 1, 6, "FLOAT"]],
- groups: [],
- config: {},
- extra: {},
- version: 0.4,
- });
-
- expect(dialogShow).toBeCalledTimes(1);
- expect(dialogShow.mock.calls[0][0].innerHTML).toContain("the following node types were not found");
- expect(dialogShow.mock.calls[0][0].innerHTML).toContain("TestNode");
- });
-
- test("defaultInput widgets can be converted back to inputs", async () => {
- const { graph, ez } = await start({
- mockNodeDefs: makeNodeDef("TestNode", { example: ["INT", { defaultInput: true }] }),
- });
-
- // Create test node and ensure it starts as an input
- let n = ez.TestNode();
- let w = n.widgets.example;
- expect(w.isConvertedToInput).toBeTruthy();
- let input = w.getConvertedInput();
- expect(input).toBeTruthy();
-
- // Ensure it can be converted to
- w.convertToWidget();
- expect(w.isConvertedToInput).toBeFalsy();
- expect(n.inputs.length).toEqual(0);
- // and from
- w.convertToInput();
- expect(w.isConvertedToInput).toBeTruthy();
- input = w.getConvertedInput();
-
- // Reload and ensure it still only has 1 converted widget
- if (!assertNotNullOrUndefined(input)) return;
-
- await connectPrimitiveAndReload(ez, graph, input, "number", 1);
- n = graph.find(n);
- expect(n.widgets).toHaveLength(1);
- w = n.widgets.example;
- expect(w.isConvertedToInput).toBeTruthy();
-
- // Convert back to widget and ensure it is still a widget after reload
- w.convertToWidget();
- await graph.reload();
- n = graph.find(n);
- expect(n.widgets).toHaveLength(1);
- expect(n.widgets[0].isConvertedToInput).toBeFalsy();
- expect(n.inputs.length).toEqual(0);
- });
-
- test("forceInput widgets can not be converted back to inputs", async () => {
- const { graph, ez } = await start({
- mockNodeDefs: makeNodeDef("TestNode", { example: ["INT", { forceInput: true }] }),
- });
-
- // Create test node and ensure it starts as an input
- let n = ez.TestNode();
- let w = n.widgets.example;
- expect(w.isConvertedToInput).toBeTruthy();
- const input = w.getConvertedInput();
- expect(input).toBeTruthy();
-
- // Convert to widget should error
- expect(() => w.convertToWidget()).toThrow();
-
- // Reload and ensure it still only has 1 converted widget
- if (assertNotNullOrUndefined(input)) {
- await connectPrimitiveAndReload(ez, graph, input, "number", 1);
- n = graph.find(n);
- expect(n.widgets).toHaveLength(1);
- expect(n.widgets.example.isConvertedToInput).toBeTruthy();
- }
- });
-
- test("primitive can connect to matching combos on converted widgets", async () => {
- const { ez } = await start({
- mockNodeDefs: {
- ...makeNodeDef("TestNode1", { example: [["A", "B", "C"], { forceInput: true }] }),
- ...makeNodeDef("TestNode2", { example: [["A", "B", "C"], { forceInput: true }] }),
- },
- });
-
- const n1 = ez.TestNode1();
- const n2 = ez.TestNode2();
- const p = ez.PrimitiveNode();
- p.outputs[0].connectTo(n1.inputs[0]);
- p.outputs[0].connectTo(n2.inputs[0]);
- expect(p.outputs[0].connections).toHaveLength(2);
- const valueWidget = p.widgets.value;
- expect(valueWidget.widget.type).toBe("combo");
- expect(valueWidget.widget.options.values).toEqual(["A", "B", "C"]);
- });
-
- test("primitive can not connect to non matching combos on converted widgets", async () => {
- const { ez } = await start({
- mockNodeDefs: {
- ...makeNodeDef("TestNode1", { example: [["A", "B", "C"], { forceInput: true }] }),
- ...makeNodeDef("TestNode2", { example: [["A", "B"], { forceInput: true }] }),
- },
- });
-
- const n1 = ez.TestNode1();
- const n2 = ez.TestNode2();
- const p = ez.PrimitiveNode();
- p.outputs[0].connectTo(n1.inputs[0]);
- expect(() => p.outputs[0].connectTo(n2.inputs[0])).toThrow();
- expect(p.outputs[0].connections).toHaveLength(1);
- });
-
- test("combo output can not connect to non matching combos list input", async () => {
- const { ez } = await start({
- mockNodeDefs: {
- ...makeNodeDef("TestNode1", {}, [["A", "B"]]),
- ...makeNodeDef("TestNode2", { example: [["A", "B"], { forceInput: true }] }),
- ...makeNodeDef("TestNode3", { example: [["A", "B", "C"], { forceInput: true }] }),
- },
- });
-
- const n1 = ez.TestNode1();
- const n2 = ez.TestNode2();
- const n3 = ez.TestNode3();
-
- n1.outputs[0].connectTo(n2.inputs[0]);
- expect(() => n1.outputs[0].connectTo(n3.inputs[0])).toThrow();
- });
-
- test("combo primitive can filter list when control_after_generate called", async () => {
- const { ez } = await start({
- mockNodeDefs: {
- ...makeNodeDef("TestNode1", { example: [["A", "B", "C", "D", "AA", "BB", "CC", "DD", "AAA", "BBB"], {}] }),
- },
- });
-
- const n1 = ez.TestNode1();
- n1.widgets.example.convertToInput();
- const p = ez.PrimitiveNode();
- p.outputs[0].connectTo(n1.inputs[0]);
-
- const value = p.widgets.value;
- const control = p.widgets.control_after_generate.widget;
- const filter = p.widgets.control_filter_list;
-
- expect(p.widgets.length).toBe(3);
- control.value = "increment";
- expect(value.value).toBe("A");
-
- // Manually trigger after queue when set to increment
- control["afterQueued"]();
- expect(value.value).toBe("B");
-
- // Filter to items containing D
- filter.value = "D";
- control["afterQueued"]();
- expect(value.value).toBe("D");
- control["afterQueued"]();
- expect(value.value).toBe("DD");
-
- // Check decrement
- value.value = "BBB";
- control.value = "decrement";
- filter.value = "B";
- control["afterQueued"]();
- expect(value.value).toBe("BB");
- control["afterQueued"]();
- expect(value.value).toBe("B");
-
- // Check regex works
- value.value = "BBB";
- filter.value = "/[AB]|^C$/";
- control["afterQueued"]();
- expect(value.value).toBe("AAA");
- control["afterQueued"]();
- expect(value.value).toBe("BB");
- control["afterQueued"]();
- expect(value.value).toBe("AA");
- control["afterQueued"]();
- expect(value.value).toBe("C");
- control["afterQueued"]();
- expect(value.value).toBe("B");
- control["afterQueued"]();
- expect(value.value).toBe("A");
-
- // Check random
- control.value = "randomize";
- filter.value = "/D/";
- for (let i = 0; i < 100; i++) {
- control["afterQueued"]();
- expect(value.value === "D" || value.value === "DD").toBeTruthy();
- }
-
- // Ensure it doesnt apply when fixed
- control.value = "fixed";
- value.value = "B";
- filter.value = "C";
- control["afterQueued"]();
- expect(value.value).toBe("B");
- });
-
- describe("reroutes", () => {
- async function checkOutput(graph, values) {
- expect((await graph.toPrompt()).output).toStrictEqual({
- 1: { inputs: { ckpt_name: "model1.safetensors" }, class_type: "CheckpointLoaderSimple" },
- 2: { inputs: { text: "positive", clip: ["1", 1] }, class_type: "CLIPTextEncode" },
- 3: { inputs: { text: "negative", clip: ["1", 1] }, class_type: "CLIPTextEncode" },
- 4: {
- inputs: { width: values.width ?? 512, height: values.height ?? 512, batch_size: values?.batch_size ?? 1 },
- class_type: "EmptyLatentImage",
- },
- 5: {
- inputs: {
- seed: 0,
- steps: 20,
- cfg: 8,
- sampler_name: "euler",
- scheduler: values?.scheduler ?? "normal",
- denoise: 1,
- model: ["1", 0],
- positive: ["2", 0],
- negative: ["3", 0],
- latent_image: ["4", 0],
- },
- class_type: "KSampler",
- },
- 6: { inputs: { samples: ["5", 0], vae: ["1", 2] }, class_type: "VAEDecode" },
- 7: {
- inputs: { filename_prefix: values.filename_prefix ?? "ComfyUI", images: ["6", 0] },
- class_type: "SaveImage",
- },
- });
- }
-
- async function waitForWidget(node) {
- // widgets are created slightly after the graph is ready
- // hard to find an exact hook to get these so just wait for them to be ready
- for (let i = 0; i < 10; i++) {
- await new Promise((r) => setTimeout(r, 10));
- if (node.widgets?.value) {
- return;
- }
- }
- }
-
- it("can connect primitive via a reroute path to a widget input", async () => {
- const { ez, graph } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
-
- nodes.empty.widgets.width.convertToInput();
- nodes.sampler.widgets.scheduler.convertToInput();
- nodes.save.widgets.filename_prefix.convertToInput();
-
- let widthReroute = ez.Reroute();
- let schedulerReroute = ez.Reroute();
- let fileReroute = ez.Reroute();
-
- let widthNext = widthReroute;
- let schedulerNext = schedulerReroute;
- let fileNext = fileReroute;
-
- for (let i = 0; i < 5; i++) {
- let next = ez.Reroute();
- widthNext.outputs[0].connectTo(next.inputs[0]);
- widthNext = next;
-
- next = ez.Reroute();
- schedulerNext.outputs[0].connectTo(next.inputs[0]);
- schedulerNext = next;
-
- next = ez.Reroute();
- fileNext.outputs[0].connectTo(next.inputs[0]);
- fileNext = next;
- }
-
- widthNext.outputs[0].connectTo(nodes.empty.inputs.width);
- schedulerNext.outputs[0].connectTo(nodes.sampler.inputs.scheduler);
- fileNext.outputs[0].connectTo(nodes.save.inputs.filename_prefix);
-
- let widthPrimitive = ez.PrimitiveNode();
- let schedulerPrimitive = ez.PrimitiveNode();
- let filePrimitive = ez.PrimitiveNode();
-
- widthPrimitive.outputs[0].connectTo(widthReroute.inputs[0]);
- schedulerPrimitive.outputs[0].connectTo(schedulerReroute.inputs[0]);
- filePrimitive.outputs[0].connectTo(fileReroute.inputs[0]);
- expect(widthPrimitive.widgets.value.value).toBe(512);
- widthPrimitive.widgets.value.value = 1024;
- expect(schedulerPrimitive.widgets.value.value).toBe("normal");
- schedulerPrimitive.widgets.value.value = "simple";
- expect(filePrimitive.widgets.value.value).toBe("ComfyUI");
- filePrimitive.widgets.value.value = "ComfyTest";
-
- await checkBeforeAndAfterReload(graph, async () => {
- widthPrimitive = graph.find(widthPrimitive);
- schedulerPrimitive = graph.find(schedulerPrimitive);
- filePrimitive = graph.find(filePrimitive);
- await waitForWidget(filePrimitive);
- expect(widthPrimitive.widgets.length).toBe(2);
- expect(schedulerPrimitive.widgets.length).toBe(3);
- expect(filePrimitive.widgets.length).toBe(1);
-
- await checkOutput(graph, {
- width: 1024,
- scheduler: "simple",
- filename_prefix: "ComfyTest",
- });
- });
- });
- it("can connect primitive via a reroute path to multiple widget inputs", async () => {
- const { ez, graph } = await start();
- const nodes = createDefaultWorkflow(ez, graph);
-
- nodes.empty.widgets.width.convertToInput();
- nodes.empty.widgets.height.convertToInput();
- nodes.empty.widgets.batch_size.convertToInput();
-
- let reroute = ez.Reroute();
- let prevReroute = reroute;
- for (let i = 0; i < 5; i++) {
- const next = ez.Reroute();
- prevReroute.outputs[0].connectTo(next.inputs[0]);
- prevReroute = next;
- }
-
- const r1 = ez.Reroute(prevReroute.outputs[0]);
- const r2 = ez.Reroute(prevReroute.outputs[0]);
- const r3 = ez.Reroute(r2.outputs[0]);
- const r4 = ez.Reroute(r2.outputs[0]);
-
- r1.outputs[0].connectTo(nodes.empty.inputs.width);
- r3.outputs[0].connectTo(nodes.empty.inputs.height);
- r4.outputs[0].connectTo(nodes.empty.inputs.batch_size);
-
- let primitive = ez.PrimitiveNode();
- primitive.outputs[0].connectTo(reroute.inputs[0]);
- expect(primitive.widgets.value.value).toBe(1);
- primitive.widgets.value.value = 64;
-
- await checkBeforeAndAfterReload(graph, async (r) => {
- primitive = graph.find(primitive);
- await waitForWidget(primitive);
-
- // Ensure widget configs are merged
- expect(primitive.widgets.value.widget.options?.min).toBe(16); // width/height min
- expect(primitive.widgets.value.widget.options?.max).toBe(4096); // batch max
- expect(primitive.widgets.value.widget.options?.step).toBe(80); // width/height step * 10
-
- await checkOutput(graph, {
- width: 64,
- height: 64,
- batch_size: 64,
- });
- });
- });
- });
-});
diff --git a/tests-ui/utils/ezgraph.js b/tests-ui/utils/ezgraph.js
deleted file mode 100644
index 97be7aa72..000000000
--- a/tests-ui/utils/ezgraph.js
+++ /dev/null
@@ -1,452 +0,0 @@
-// @ts-check
-///
-
-/**
- * @typedef { import("../../web/scripts/app")["app"] } app
- * @typedef { import("../../web/types/litegraph") } LG
- * @typedef { import("../../web/types/litegraph").IWidget } IWidget
- * @typedef { import("../../web/types/litegraph").ContextMenuItem } ContextMenuItem
- * @typedef { import("../../web/types/litegraph").INodeInputSlot } INodeInputSlot
- * @typedef { import("../../web/types/litegraph").INodeOutputSlot } INodeOutputSlot
- * @typedef { InstanceType & { widgets?: Array } } LGNode
- * @typedef { (...args: EzOutput[] | [...EzOutput[], Record]) => EzNode } EzNodeFactory
- */
-
-export class EzConnection {
- /** @type { app } */
- app;
- /** @type { InstanceType } */
- link;
-
- get originNode() {
- return new EzNode(this.app, this.app.graph.getNodeById(this.link.origin_id));
- }
-
- get originOutput() {
- return this.originNode.outputs[this.link.origin_slot];
- }
-
- get targetNode() {
- return new EzNode(this.app, this.app.graph.getNodeById(this.link.target_id));
- }
-
- get targetInput() {
- return this.targetNode.inputs[this.link.target_slot];
- }
-
- /**
- * @param { app } app
- * @param { InstanceType } link
- */
- constructor(app, link) {
- this.app = app;
- this.link = link;
- }
-
- disconnect() {
- this.targetInput.disconnect();
- }
-}
-
-export class EzSlot {
- /** @type { EzNode } */
- node;
- /** @type { number } */
- index;
-
- /**
- * @param { EzNode } node
- * @param { number } index
- */
- constructor(node, index) {
- this.node = node;
- this.index = index;
- }
-}
-
-export class EzInput extends EzSlot {
- /** @type { INodeInputSlot } */
- input;
-
- /**
- * @param { EzNode } node
- * @param { number } index
- * @param { INodeInputSlot } input
- */
- constructor(node, index, input) {
- super(node, index);
- this.input = input;
- }
-
- get connection() {
- const link = this.node.node.inputs?.[this.index]?.link;
- if (link == null) {
- return null;
- }
- return new EzConnection(this.node.app, this.node.app.graph.links[link]);
- }
-
- disconnect() {
- this.node.node.disconnectInput(this.index);
- }
-}
-
-export class EzOutput extends EzSlot {
- /** @type { INodeOutputSlot } */
- output;
-
- /**
- * @param { EzNode } node
- * @param { number } index
- * @param { INodeOutputSlot } output
- */
- constructor(node, index, output) {
- super(node, index);
- this.output = output;
- }
-
- get connections() {
- return (this.node.node.outputs?.[this.index]?.links ?? []).map(
- (l) => new EzConnection(this.node.app, this.node.app.graph.links[l])
- );
- }
-
- /**
- * @param { EzInput } input
- */
- connectTo(input) {
- if (!input) throw new Error("Invalid input");
-
- /**
- * @type { LG["LLink"] | null }
- */
- const link = this.node.node.connect(this.index, input.node.node, input.index);
- if (!link) {
- const inp = input.input;
- const inName = inp.name || inp.label || inp.type;
- throw new Error(
- `Connecting from ${input.node.node.type}#${input.node.id}[${inName}#${input.index}] -> ${this.node.node.type}#${this.node.id}[${
- this.output.name ?? this.output.type
- }#${this.index}] failed.`
- );
- }
- return link;
- }
-}
-
-export class EzNodeMenuItem {
- /** @type { EzNode } */
- node;
- /** @type { number } */
- index;
- /** @type { ContextMenuItem } */
- item;
-
- /**
- * @param { EzNode } node
- * @param { number } index
- * @param { ContextMenuItem } item
- */
- constructor(node, index, item) {
- this.node = node;
- this.index = index;
- this.item = item;
- }
-
- call(selectNode = true) {
- if (!this.item?.callback) throw new Error(`Menu Item ${this.item?.content ?? "[null]"} has no callback.`);
- if (selectNode) {
- this.node.select();
- }
- return this.item.callback.call(this.node.node, undefined, undefined, undefined, undefined, this.node.node);
- }
-}
-
-export class EzWidget {
- /** @type { EzNode } */
- node;
- /** @type { number } */
- index;
- /** @type { IWidget } */
- widget;
-
- /**
- * @param { EzNode } node
- * @param { number } index
- * @param { IWidget } widget
- */
- constructor(node, index, widget) {
- this.node = node;
- this.index = index;
- this.widget = widget;
- }
-
- get value() {
- return this.widget.value;
- }
-
- set value(v) {
- this.widget.value = v;
- this.widget.callback?.call?.(this.widget, v)
- }
-
- get isConvertedToInput() {
- // @ts-ignore : this type is valid for converted widgets
- return this.widget.type === "converted-widget";
- }
-
- getConvertedInput() {
- if (!this.isConvertedToInput) throw new Error(`Widget ${this.widget.name} is not converted to input.`);
-
- return this.node.inputs.find((inp) => inp.input["widget"]?.name === this.widget.name);
- }
-
- convertToWidget() {
- if (!this.isConvertedToInput)
- throw new Error(`Widget ${this.widget.name} cannot be converted as it is already a widget.`);
- var menu = this.node.menu["Convert Input to Widget"].item.submenu.options;
- var index = menu.findIndex(a => a.content == `Convert ${this.widget.name} to widget`);
- menu[index].callback.call();
- }
-
- convertToInput() {
- if (this.isConvertedToInput)
- throw new Error(`Widget ${this.widget.name} cannot be converted as it is already an input.`);
- var menu = this.node.menu["Convert Widget to Input"].item.submenu.options;
- var index = menu.findIndex(a => a.content == `Convert ${this.widget.name} to input`);
- menu[index].callback.call();
- }
-}
-
-export class EzNode {
- /** @type { app } */
- app;
- /** @type { LGNode } */
- node;
-
- /**
- * @param { app } app
- * @param { LGNode } node
- */
- constructor(app, node) {
- this.app = app;
- this.node = node;
- }
-
- get id() {
- return this.node.id;
- }
-
- get inputs() {
- return this.#makeLookupArray("inputs", "name", EzInput);
- }
-
- get outputs() {
- return this.#makeLookupArray("outputs", "name", EzOutput);
- }
-
- get widgets() {
- return this.#makeLookupArray("widgets", "name", EzWidget);
- }
-
- get menu() {
- return this.#makeLookupArray(() => this.app.canvas.getNodeMenuOptions(this.node), "content", EzNodeMenuItem);
- }
-
- get isRemoved() {
- return !this.app.graph.getNodeById(this.id);
- }
-
- select(addToSelection = false) {
- this.app.canvas.selectNode(this.node, addToSelection);
- }
-
- // /**
- // * @template { "inputs" | "outputs" } T
- // * @param { T } type
- // * @returns { Record & (type extends "inputs" ? EzInput [] : EzOutput[]) }
- // */
- // #getSlotItems(type) {
- // // @ts-ignore : these items are correct
- // return (this.node[type] ?? []).reduce((p, s, i) => {
- // if (s.name in p) {
- // throw new Error(`Unable to store input ${s.name} on array as name conflicts.`);
- // }
- // // @ts-ignore
- // p.push((p[s.name] = new (type === "inputs" ? EzInput : EzOutput)(this, i, s)));
- // return p;
- // }, Object.assign([], { $: this }));
- // }
-
- /**
- * @template { { new(node: EzNode, index: number, obj: any): any } } T
- * @param { "inputs" | "outputs" | "widgets" | (() => Array) } nodeProperty
- * @param { string } nameProperty
- * @param { T } ctor
- * @returns { Record> & Array> }
- */
- #makeLookupArray(nodeProperty, nameProperty, ctor) {
- const items = typeof nodeProperty === "function" ? nodeProperty() : this.node[nodeProperty];
- // @ts-ignore
- return (items ?? []).reduce((p, s, i) => {
- if (!s) return p;
-
- const name = s[nameProperty];
- const item = new ctor(this, i, s);
- // @ts-ignore
- p.push(item);
- if (name) {
- // @ts-ignore
- if (name in p) {
- throw new Error(`Unable to store ${nodeProperty} ${name} on array as name conflicts.`);
- }
- }
- // @ts-ignore
- p[name] = item;
- return p;
- }, Object.assign([], { $: this }));
- }
-}
-
-export class EzGraph {
- /** @type { app } */
- app;
-
- /**
- * @param { app } app
- */
- constructor(app) {
- this.app = app;
- }
-
- get nodes() {
- return this.app.graph._nodes.map((n) => new EzNode(this.app, n));
- }
-
- clear() {
- this.app.graph.clear();
- }
-
- arrange() {
- this.app.graph.arrange();
- }
-
- stringify() {
- return JSON.stringify(this.app.graph.serialize(), undefined);
- }
-
- /**
- * @param { number | LGNode | EzNode } obj
- * @returns { EzNode }
- */
- find(obj) {
- let match;
- let id;
- if (typeof obj === "number") {
- id = obj;
- } else {
- id = obj.id;
- }
-
- match = this.app.graph.getNodeById(id);
-
- if (!match) {
- throw new Error(`Unable to find node with ID ${id}.`);
- }
-
- return new EzNode(this.app, match);
- }
-
- /**
- * @returns { Promise }
- */
- reload() {
- const graph = JSON.parse(JSON.stringify(this.app.graph.serialize()));
- return new Promise((r) => {
- this.app.graph.clear();
- setTimeout(async () => {
- await this.app.loadGraphData(graph);
- r();
- }, 10);
- });
- }
-
- /**
- * @returns { Promise<{
- * workflow: {},
- * output: Record
- * }>}> }
- */
- toPrompt() {
- // @ts-ignore
- return this.app.graphToPrompt();
- }
-}
-
-export const Ez = {
- /**
- * Quickly build and interact with a ComfyUI graph
- * @example
- * const { ez, graph } = Ez.graph(app);
- * graph.clear();
- * const [model, clip, vae] = ez.CheckpointLoaderSimple().outputs;
- * const [pos] = ez.CLIPTextEncode(clip, { text: "positive" }).outputs;
- * const [neg] = ez.CLIPTextEncode(clip, { text: "negative" }).outputs;
- * const [latent] = ez.KSampler(model, pos, neg, ...ez.EmptyLatentImage().outputs).outputs;
- * const [image] = ez.VAEDecode(latent, vae).outputs;
- * const saveNode = ez.SaveImage(image);
- * console.log(saveNode);
- * graph.arrange();
- * @param { app } app
- * @param { LG["LiteGraph"] } LiteGraph
- * @param { LG["LGraphCanvas"] } LGraphCanvas
- * @param { boolean } clearGraph
- * @returns { { graph: EzGraph, ez: Record } }
- */
- graph(app, LiteGraph = window["LiteGraph"], LGraphCanvas = window["LGraphCanvas"], clearGraph = true) {
- // Always set the active canvas so things work
- LGraphCanvas.active_canvas = app.canvas;
-
- if (clearGraph) {
- app.graph.clear();
- }
-
- // @ts-ignore : this proxy handles utility methods & node creation
- const factory = new Proxy(
- {},
- {
- get(_, p) {
- if (typeof p !== "string") throw new Error("Invalid node");
- const node = LiteGraph.createNode(p);
- if (!node) throw new Error(`Unknown node "${p}"`);
- app.graph.add(node);
-
- /**
- * @param {Parameters} args
- */
- return function (...args) {
- const ezNode = new EzNode(app, node);
- const inputs = ezNode.inputs;
-
- let slot = 0;
- for (const arg of args) {
- if (arg instanceof EzOutput) {
- arg.connectTo(inputs[slot++]);
- } else {
- for (const k in arg) {
- ezNode.widgets[k].value = arg[k];
- }
- }
- }
-
- return ezNode;
- };
- },
- }
- );
-
- return { graph: new EzGraph(app), ez: factory };
- },
-};
diff --git a/tests-ui/utils/index.js b/tests-ui/utils/index.js
deleted file mode 100644
index 74b6cf93d..000000000
--- a/tests-ui/utils/index.js
+++ /dev/null
@@ -1,129 +0,0 @@
-const { mockApi } = require("./setup");
-const { Ez } = require("./ezgraph");
-const lg = require("./litegraph");
-const fs = require("fs");
-const path = require("path");
-
-const html = fs.readFileSync(path.resolve(__dirname, "../../web/index.html"))
-
-/**
- *
- * @param { Parameters[0] & {
- * resetEnv?: boolean,
- * preSetup?(app): Promise,
- * localStorage?: Record
- * } } config
- * @returns
- */
-export async function start(config = {}) {
- if(config.resetEnv) {
- jest.resetModules();
- jest.resetAllMocks();
- lg.setup(global);
- localStorage.clear();
- sessionStorage.clear();
- }
-
- Object.assign(localStorage, config.localStorage ?? {});
- document.body.innerHTML = html;
-
- mockApi(config);
- const { app } = require("../../web/scripts/app");
- config.preSetup?.(app);
- await app.setup();
-
- return { ...Ez.graph(app, global["LiteGraph"], global["LGraphCanvas"]), app };
-}
-
-/**
- * @param { ReturnType["graph"] } graph
- * @param { (hasReloaded: boolean) => (Promise | void) } cb
- */
-export async function checkBeforeAndAfterReload(graph, cb) {
- await cb(false);
- await graph.reload();
- await cb(true);
-}
-
-/**
- * @param { string } name
- * @param { Record } input
- * @param { (string | string[])[] | Record } output
- * @returns { Record }
- */
-export function makeNodeDef(name, input, output = {}) {
- const nodeDef = {
- name,
- category: "test",
- output: [],
- output_name: [],
- output_is_list: [],
- input: {
- required: {},
- },
- };
- for (const k in input) {
- nodeDef.input.required[k] = typeof input[k] === "string" ? [input[k], {}] : [...input[k]];
- }
- if (output instanceof Array) {
- output = output.reduce((p, c) => {
- p[c] = c;
- return p;
- }, {});
- }
- for (const k in output) {
- nodeDef.output.push(output[k]);
- nodeDef.output_name.push(k);
- nodeDef.output_is_list.push(false);
- }
-
- return { [name]: nodeDef };
-}
-
-/**
-/**
- * @template { any } T
- * @param { T } x
- * @returns { x is Exclude }
- */
-export function assertNotNullOrUndefined(x) {
- expect(x).not.toEqual(null);
- expect(x).not.toEqual(undefined);
- return true;
-}
-
-/**
- *
- * @param { ReturnType["ez"] } ez
- * @param { ReturnType["graph"] } graph
- */
-export function createDefaultWorkflow(ez, graph) {
- graph.clear();
- const ckpt = ez.CheckpointLoaderSimple();
-
- const pos = ez.CLIPTextEncode(ckpt.outputs.CLIP, { text: "positive" });
- const neg = ez.CLIPTextEncode(ckpt.outputs.CLIP, { text: "negative" });
-
- const empty = ez.EmptyLatentImage();
- const sampler = ez.KSampler(
- ckpt.outputs.MODEL,
- pos.outputs.CONDITIONING,
- neg.outputs.CONDITIONING,
- empty.outputs.LATENT
- );
-
- const decode = ez.VAEDecode(sampler.outputs.LATENT, ckpt.outputs.VAE);
- const save = ez.SaveImage(decode.outputs.IMAGE);
- graph.arrange();
-
- return { ckpt, pos, neg, empty, sampler, decode, save };
-}
-
-export async function getNodeDefs() {
- const { api } = require("../../web/scripts/api");
- return api.getNodeDefs();
-}
-
-export async function getNodeDef(nodeId) {
- return (await getNodeDefs())[nodeId];
-}
\ No newline at end of file
diff --git a/tests-ui/utils/litegraph.js b/tests-ui/utils/litegraph.js
deleted file mode 100644
index 777f8c3ba..000000000
--- a/tests-ui/utils/litegraph.js
+++ /dev/null
@@ -1,36 +0,0 @@
-const fs = require("fs");
-const path = require("path");
-const { nop } = require("../utils/nopProxy");
-
-function forEachKey(cb) {
- for (const k of [
- "LiteGraph",
- "LGraph",
- "LLink",
- "LGraphNode",
- "LGraphGroup",
- "DragAndScale",
- "LGraphCanvas",
- "ContextMenu",
- ]) {
- cb(k);
- }
-}
-
-export function setup(ctx) {
- const lg = fs.readFileSync(path.resolve("../web/lib/litegraph.core.js"), "utf-8");
- const globalTemp = {};
- (function (console) {
- eval(lg);
- }).call(globalTemp, nop);
-
- forEachKey((k) => (ctx[k] = globalTemp[k]));
- require(path.resolve("../web/lib/litegraph.extensions.js"));
-}
-
-export function teardown(ctx) {
- forEachKey((k) => delete ctx[k]);
-
- // Clear document after each run
- document.getElementsByTagName("html")[0].innerHTML = "";
-}
diff --git a/tests-ui/utils/nopProxy.js b/tests-ui/utils/nopProxy.js
deleted file mode 100644
index 2502d9d03..000000000
--- a/tests-ui/utils/nopProxy.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export const nop = new Proxy(function () {}, {
- get: () => nop,
- set: () => true,
- apply: () => nop,
- construct: () => nop,
-});
diff --git a/tests-ui/utils/setup.js b/tests-ui/utils/setup.js
deleted file mode 100644
index 3a6d69b72..000000000
--- a/tests-ui/utils/setup.js
+++ /dev/null
@@ -1,82 +0,0 @@
-require("../../web/scripts/api");
-
-const fs = require("fs");
-const path = require("path");
-function* walkSync(dir) {
- const files = fs.readdirSync(dir, { withFileTypes: true });
- for (const file of files) {
- if (file.isDirectory()) {
- yield* walkSync(path.join(dir, file.name));
- } else {
- yield path.join(dir, file.name);
- }
- }
-}
-
-/**
- * @typedef { import("../../web/types/comfy").ComfyObjectInfo } ComfyObjectInfo
- */
-
-/**
- * @param {{
- * mockExtensions?: string[],
- * mockNodeDefs?: Record,
-* settings?: Record
-* userConfig?: {storage: "server" | "browser", users?: Record, migrated?: boolean },
-* userData?: Record
- * }} config
- */
-export function mockApi(config = {}) {
- let { mockExtensions, mockNodeDefs, userConfig, settings, userData } = {
- userConfig,
- settings: {},
- userData: {},
- ...config,
- };
- if (!mockExtensions) {
- mockExtensions = Array.from(walkSync(path.resolve("../web/extensions/core")))
- .filter((x) => x.endsWith(".js"))
- .map((x) => path.relative(path.resolve("../web"), x));
- }
- if (!mockNodeDefs) {
- mockNodeDefs = JSON.parse(fs.readFileSync(path.resolve("./data/object_info.json")));
- }
-
- const events = new EventTarget();
- const mockApi = {
- addEventListener: events.addEventListener.bind(events),
- removeEventListener: events.removeEventListener.bind(events),
- dispatchEvent: events.dispatchEvent.bind(events),
- getSystemStats: jest.fn(),
- getExtensions: jest.fn(() => mockExtensions),
- getNodeDefs: jest.fn(() => mockNodeDefs),
- init: jest.fn(),
- apiURL: jest.fn((x) => "../../web/" + x),
- createUser: jest.fn((username) => {
- if(username in userConfig.users) {
- return { status: 400, json: () => "Duplicate" }
- }
- userConfig.users[username + "!"] = username;
- return { status: 200, json: () => username + "!" }
- }),
- getUserConfig: jest.fn(() => userConfig ?? { storage: "browser", migrated: false }),
- getSettings: jest.fn(() => settings),
- storeSettings: jest.fn((v) => Object.assign(settings, v)),
- getUserData: jest.fn((f) => {
- if (f in userData) {
- return { status: 200, json: () => userData[f] };
- } else {
- return { status: 404 };
- }
- }),
- storeUserData: jest.fn((file, data) => {
- userData[file] = data;
- }),
- listUserData: jest.fn(() => [])
- };
- jest.mock("../../web/scripts/api", () => ({
- get api() {
- return mockApi;
- },
- }));
-}
diff --git a/tests-unit/prompt_server_test/__init__.py b/tests-unit/prompt_server_test/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests-unit/prompt_server_test/download_models_test.py b/tests-unit/prompt_server_test/download_models_test.py
new file mode 100644
index 000000000..66150a468
--- /dev/null
+++ b/tests-unit/prompt_server_test/download_models_test.py
@@ -0,0 +1,321 @@
+import pytest
+import aiohttp
+from aiohttp import ClientResponse
+import itertools
+import os
+from unittest.mock import AsyncMock, patch, MagicMock
+from model_filemanager import download_model, validate_model_subdirectory, track_download_progress, create_model_path, check_file_exists, DownloadStatusType, DownloadModelStatus, validate_filename
+
+class AsyncIteratorMock:
+ """
+ A mock class that simulates an asynchronous iterator.
+ This is used to mimic the behavior of aiohttp's content iterator.
+ """
+ def __init__(self, seq):
+ # Convert the input sequence into an iterator
+ self.iter = iter(seq)
+
+ def __aiter__(self):
+ # This method is called when 'async for' is used
+ return self
+
+ async def __anext__(self):
+ # This method is called for each iteration in an 'async for' loop
+ try:
+ return next(self.iter)
+ except StopIteration:
+ # This is the asynchronous equivalent of StopIteration
+ raise StopAsyncIteration
+
+class ContentMock:
+ """
+ A mock class that simulates the content attribute of an aiohttp ClientResponse.
+ This class provides the iter_chunked method which returns an async iterator of chunks.
+ """
+ def __init__(self, chunks):
+ # Store the chunks that will be returned by the iterator
+ self.chunks = chunks
+
+ def iter_chunked(self, chunk_size):
+ # This method mimics aiohttp's content.iter_chunked()
+ # For simplicity in testing, we ignore chunk_size and just return our predefined chunks
+ return AsyncIteratorMock(self.chunks)
+
+@pytest.mark.asyncio
+async def test_download_model_success():
+ mock_response = AsyncMock(spec=aiohttp.ClientResponse)
+ mock_response.status = 200
+ mock_response.headers = {'Content-Length': '1000'}
+ # Create a mock for content that returns an async iterator directly
+ chunks = [b'a' * 500, b'b' * 300, b'c' * 200]
+ mock_response.content = ContentMock(chunks)
+
+ mock_make_request = AsyncMock(return_value=mock_response)
+ mock_progress_callback = AsyncMock()
+
+ # Mock file operations
+ mock_open = MagicMock()
+ mock_file = MagicMock()
+ mock_open.return_value.__enter__.return_value = mock_file
+ time_values = itertools.count(0, 0.1)
+
+ with patch('model_filemanager.create_model_path', return_value=('models/checkpoints/model.sft', 'checkpoints/model.sft')), \
+ patch('model_filemanager.check_file_exists', return_value=None), \
+ patch('builtins.open', mock_open), \
+ patch('time.time', side_effect=time_values): # Simulate time passing
+
+ result = await download_model(
+ mock_make_request,
+ 'model.sft',
+ 'http://example.com/model.sft',
+ 'checkpoints',
+ mock_progress_callback
+ )
+
+ # Assert the result
+ assert isinstance(result, DownloadModelStatus)
+ assert result.message == 'Successfully downloaded model.sft'
+ assert result.status == 'completed'
+ assert result.already_existed is False
+
+ # Check progress callback calls
+ assert mock_progress_callback.call_count >= 3 # At least start, one progress update, and completion
+
+ # Check initial call
+ mock_progress_callback.assert_any_call(
+ 'checkpoints/model.sft',
+ DownloadModelStatus(DownloadStatusType.PENDING, 0, "Starting download of model.sft", False)
+ )
+
+ # Check final call
+ mock_progress_callback.assert_any_call(
+ 'checkpoints/model.sft',
+ DownloadModelStatus(DownloadStatusType.COMPLETED, 100, "Successfully downloaded model.sft", False)
+ )
+
+ # Verify file writing
+ mock_file.write.assert_any_call(b'a' * 500)
+ mock_file.write.assert_any_call(b'b' * 300)
+ mock_file.write.assert_any_call(b'c' * 200)
+
+ # Verify request was made
+ mock_make_request.assert_called_once_with('http://example.com/model.sft')
+
+@pytest.mark.asyncio
+async def test_download_model_url_request_failure():
+ # Mock dependencies
+ mock_response = AsyncMock(spec=ClientResponse)
+ mock_response.status = 404 # Simulate a "Not Found" error
+ mock_get = AsyncMock(return_value=mock_response)
+ mock_progress_callback = AsyncMock()
+
+ # Mock the create_model_path function
+ with patch('model_filemanager.create_model_path', return_value=('/mock/path/model.safetensors', 'mock/path/model.safetensors')):
+ # Mock the check_file_exists function to return None (file doesn't exist)
+ with patch('model_filemanager.check_file_exists', return_value=None):
+ # Call the function
+ result = await download_model(
+ mock_get,
+ 'model.safetensors',
+ 'http://example.com/model.safetensors',
+ 'mock_directory',
+ mock_progress_callback
+ )
+
+ # Assert the expected behavior
+ assert isinstance(result, DownloadModelStatus)
+ assert result.status == 'error'
+ assert result.message == 'Failed to download model.safetensors. Status code: 404'
+ assert result.already_existed is False
+
+ # Check that progress_callback was called with the correct arguments
+ mock_progress_callback.assert_any_call(
+ 'mock_directory/model.safetensors',
+ DownloadModelStatus(
+ status=DownloadStatusType.PENDING,
+ progress_percentage=0,
+ message='Starting download of model.safetensors',
+ already_existed=False
+ )
+ )
+ mock_progress_callback.assert_called_with(
+ 'mock_directory/model.safetensors',
+ DownloadModelStatus(
+ status=DownloadStatusType.ERROR,
+ progress_percentage=0,
+ message='Failed to download model.safetensors. Status code: 404',
+ already_existed=False
+ )
+ )
+
+ # Verify that the get method was called with the correct URL
+ mock_get.assert_called_once_with('http://example.com/model.safetensors')
+
+@pytest.mark.asyncio
+async def test_download_model_invalid_model_subdirectory():
+
+ mock_make_request = AsyncMock()
+ mock_progress_callback = AsyncMock()
+
+
+ result = await download_model(
+ mock_make_request,
+ 'model.sft',
+ 'http://example.com/model.sft',
+ '../bad_path',
+ mock_progress_callback
+ )
+
+ # Assert the result
+ assert isinstance(result, DownloadModelStatus)
+ assert result.message == 'Invalid model subdirectory'
+ assert result.status == 'error'
+ assert result.already_existed is False
+
+
+# For create_model_path function
+def test_create_model_path(tmp_path, monkeypatch):
+ mock_models_dir = tmp_path / "models"
+ monkeypatch.setattr('folder_paths.models_dir', str(mock_models_dir))
+
+ model_name = "test_model.sft"
+ model_directory = "test_dir"
+
+ file_path, relative_path = create_model_path(model_name, model_directory, mock_models_dir)
+
+ assert file_path == str(mock_models_dir / model_directory / model_name)
+ assert relative_path == f"{model_directory}/{model_name}"
+ assert os.path.exists(os.path.dirname(file_path))
+
+
+@pytest.mark.asyncio
+async def test_check_file_exists_when_file_exists(tmp_path):
+ file_path = tmp_path / "existing_model.sft"
+ file_path.touch() # Create an empty file
+
+ mock_callback = AsyncMock()
+
+ result = await check_file_exists(str(file_path), "existing_model.sft", mock_callback, "test/existing_model.sft")
+
+ assert result is not None
+ assert result.status == "completed"
+ assert result.message == "existing_model.sft already exists"
+ assert result.already_existed is True
+
+ mock_callback.assert_called_once_with(
+ "test/existing_model.sft",
+ DownloadModelStatus(DownloadStatusType.COMPLETED, 100, "existing_model.sft already exists", already_existed=True)
+ )
+
+@pytest.mark.asyncio
+async def test_check_file_exists_when_file_does_not_exist(tmp_path):
+ file_path = tmp_path / "non_existing_model.sft"
+
+ mock_callback = AsyncMock()
+
+ result = await check_file_exists(str(file_path), "non_existing_model.sft", mock_callback, "test/non_existing_model.sft")
+
+ assert result is None
+ mock_callback.assert_not_called()
+
+@pytest.mark.asyncio
+async def test_track_download_progress_no_content_length():
+ mock_response = AsyncMock(spec=aiohttp.ClientResponse)
+ mock_response.headers = {} # No Content-Length header
+ mock_response.content.iter_chunked.return_value = AsyncIteratorMock([b'a' * 500, b'b' * 500])
+
+ mock_callback = AsyncMock()
+ mock_open = MagicMock(return_value=MagicMock())
+
+ with patch('builtins.open', mock_open):
+ result = await track_download_progress(
+ mock_response, '/mock/path/model.sft', 'model.sft',
+ mock_callback, 'models/model.sft', interval=0.1
+ )
+
+ assert result.status == "completed"
+ # Check that progress was reported even without knowing the total size
+ mock_callback.assert_any_call(
+ 'models/model.sft',
+ DownloadModelStatus(DownloadStatusType.IN_PROGRESS, 0, "Downloading model.sft", already_existed=False)
+ )
+
+@pytest.mark.asyncio
+async def test_track_download_progress_interval():
+ mock_response = AsyncMock(spec=aiohttp.ClientResponse)
+ mock_response.headers = {'Content-Length': '1000'}
+ mock_response.content.iter_chunked.return_value = AsyncIteratorMock([b'a' * 100] * 10)
+
+ mock_callback = AsyncMock()
+ mock_open = MagicMock(return_value=MagicMock())
+
+ # Create a mock time function that returns incremental float values
+ mock_time = MagicMock()
+ mock_time.side_effect = [i * 0.5 for i in range(30)] # This should be enough for 10 chunks
+
+ with patch('builtins.open', mock_open), \
+ patch('time.time', mock_time):
+ await track_download_progress(
+ mock_response, '/mock/path/model.sft', 'model.sft',
+ mock_callback, 'models/model.sft', interval=1.0
+ )
+
+ # Print out the actual call count and the arguments of each call for debugging
+ print(f"mock_callback was called {mock_callback.call_count} times")
+ for i, call in enumerate(mock_callback.call_args_list):
+ args, kwargs = call
+ print(f"Call {i + 1}: {args[1].status}, Progress: {args[1].progress_percentage:.2f}%")
+
+ # Assert that progress was updated at least 3 times (start, at least one interval, and end)
+ assert mock_callback.call_count >= 3, f"Expected at least 3 calls, but got {mock_callback.call_count}"
+
+ # Verify the first and last calls
+ first_call = mock_callback.call_args_list[0]
+ assert first_call[0][1].status == "in_progress"
+ # Allow for some initial progress, but it should be less than 50%
+ assert 0 <= first_call[0][1].progress_percentage < 50, f"First call progress was {first_call[0][1].progress_percentage}%"
+
+ last_call = mock_callback.call_args_list[-1]
+ assert last_call[0][1].status == "completed"
+ assert last_call[0][1].progress_percentage == 100
+
+def test_valid_subdirectory():
+ assert validate_model_subdirectory("valid-model123") is True
+
+def test_subdirectory_too_long():
+ assert validate_model_subdirectory("a" * 51) is False
+
+def test_subdirectory_with_double_dots():
+ assert validate_model_subdirectory("model/../unsafe") is False
+
+def test_subdirectory_with_slash():
+ assert validate_model_subdirectory("model/unsafe") is False
+
+def test_subdirectory_with_special_characters():
+ assert validate_model_subdirectory("model@unsafe") is False
+
+def test_subdirectory_with_underscore_and_dash():
+ assert validate_model_subdirectory("valid_model-name") is True
+
+def test_empty_subdirectory():
+ assert validate_model_subdirectory("") is False
+
+@pytest.mark.parametrize("filename, expected", [
+ ("valid_model.safetensors", True),
+ ("valid_model.sft", True),
+ ("valid model.safetensors", True), # Test with space
+ ("UPPERCASE_MODEL.SAFETENSORS", True),
+ ("model_with.multiple.dots.pt", False),
+ ("", False), # Empty string
+ ("../../../etc/passwd", False), # Path traversal attempt
+ ("/etc/passwd", False), # Absolute path
+ ("\\windows\\system32\\config\\sam", False), # Windows path
+ (".hidden_file.pt", False), # Hidden file
+ ("invalid.ckpt", False), # Invalid character
+ ("invalid?.ckpt", False), # Another invalid character
+ ("very" * 100 + ".safetensors", False), # Too long filename
+ ("\nmodel_with_newline.pt", False), # Newline character
+ ("model_with_emoji😊.pt", False), # Emoji in filename
+])
+def test_validate_filename(filename, expected):
+ assert validate_filename(filename) == expected
diff --git a/tests-unit/requirements.txt b/tests-unit/requirements.txt
index 0587502f8..d70d00f4b 100644
--- a/tests-unit/requirements.txt
+++ b/tests-unit/requirements.txt
@@ -1 +1,3 @@
pytest>=7.8.0
+pytest-aiohttp
+pytest-asyncio
diff --git a/tests/inference/extra_model_paths.yaml b/tests/inference/extra_model_paths.yaml
new file mode 100644
index 000000000..75b2e1ae4
--- /dev/null
+++ b/tests/inference/extra_model_paths.yaml
@@ -0,0 +1,4 @@
+# Config for testing nodes
+testing:
+ custom_nodes: tests/inference/testing_nodes
+
diff --git a/tests/inference/test_execution.py b/tests/inference/test_execution.py
new file mode 100644
index 000000000..9df1d7df8
--- /dev/null
+++ b/tests/inference/test_execution.py
@@ -0,0 +1,461 @@
+from io import BytesIO
+import numpy
+from PIL import Image
+import pytest
+from pytest import fixture
+import time
+import torch
+from typing import Union, Dict
+import json
+import subprocess
+import websocket #NOTE: websocket-client (https://github.com/websocket-client/websocket-client)
+import uuid
+import urllib.request
+import urllib.parse
+import urllib.error
+from comfy_execution.graph_utils import GraphBuilder, Node
+
+class RunResult:
+ def __init__(self, prompt_id: str):
+ self.outputs: Dict[str,Dict] = {}
+ self.runs: Dict[str,bool] = {}
+ self.prompt_id: str = prompt_id
+
+ def get_output(self, node: Node):
+ return self.outputs.get(node.id, None)
+
+ def did_run(self, node: Node):
+ return self.runs.get(node.id, False)
+
+ def get_images(self, node: Node):
+ output = self.get_output(node)
+ if output is None:
+ return []
+ return output.get('image_objects', [])
+
+ def get_prompt_id(self):
+ return self.prompt_id
+
+class ComfyClient:
+ def __init__(self):
+ self.test_name = ""
+
+ def connect(self,
+ listen:str = '127.0.0.1',
+ port:Union[str,int] = 8188,
+ client_id: str = str(uuid.uuid4())
+ ):
+ self.client_id = client_id
+ self.server_address = f"{listen}:{port}"
+ ws = websocket.WebSocket()
+ ws.connect("ws://{}/ws?clientId={}".format(self.server_address, self.client_id))
+ self.ws = ws
+
+ def queue_prompt(self, prompt):
+ p = {"prompt": prompt, "client_id": self.client_id}
+ data = json.dumps(p).encode('utf-8')
+ req = urllib.request.Request("http://{}/prompt".format(self.server_address), data=data)
+ return json.loads(urllib.request.urlopen(req).read())
+
+ def get_image(self, filename, subfolder, folder_type):
+ data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
+ url_values = urllib.parse.urlencode(data)
+ with urllib.request.urlopen("http://{}/view?{}".format(self.server_address, url_values)) as response:
+ return response.read()
+
+ def get_history(self, prompt_id):
+ with urllib.request.urlopen("http://{}/history/{}".format(self.server_address, prompt_id)) as response:
+ return json.loads(response.read())
+
+ def set_test_name(self, name):
+ self.test_name = name
+
+ def run(self, graph):
+ prompt = graph.finalize()
+ for node in graph.nodes.values():
+ if node.class_type == 'SaveImage':
+ node.inputs['filename_prefix'] = self.test_name
+
+ prompt_id = self.queue_prompt(prompt)['prompt_id']
+ result = RunResult(prompt_id)
+ while True:
+ out = self.ws.recv()
+ if isinstance(out, str):
+ message = json.loads(out)
+ if message['type'] == 'executing':
+ data = message['data']
+ if data['prompt_id'] != prompt_id:
+ continue
+ if data['node'] is None:
+ break
+ result.runs[data['node']] = True
+ elif message['type'] == 'execution_error':
+ raise Exception(message['data'])
+ elif message['type'] == 'execution_cached':
+ pass # Probably want to store this off for testing
+
+ history = self.get_history(prompt_id)[prompt_id]
+ for o in history['outputs']:
+ for node_id in history['outputs']:
+ node_output = history['outputs'][node_id]
+ result.outputs[node_id] = node_output
+ if 'images' in node_output:
+ images_output = []
+ for image in node_output['images']:
+ image_data = self.get_image(image['filename'], image['subfolder'], image['type'])
+ image_obj = Image.open(BytesIO(image_data))
+ images_output.append(image_obj)
+ node_output['image_objects'] = images_output
+
+ return result
+
+#
+# Loop through these variables
+#
+@pytest.mark.execution
+class TestExecution:
+ #
+ # Initialize server and client
+ #
+ @fixture(scope="class", autouse=True, params=[
+ # (use_lru, lru_size)
+ (False, 0),
+ (True, 0),
+ (True, 100),
+ ])
+ def _server(self, args_pytest, request):
+ # Start server
+ pargs = [
+ 'python','main.py',
+ '--output-directory', args_pytest["output_dir"],
+ '--listen', args_pytest["listen"],
+ '--port', str(args_pytest["port"]),
+ '--extra-model-paths-config', 'tests/inference/extra_model_paths.yaml',
+ ]
+ use_lru, lru_size = request.param
+ if use_lru:
+ pargs += ['--cache-lru', str(lru_size)]
+ print("Running server with args:", pargs)
+ p = subprocess.Popen(pargs)
+ yield
+ p.kill()
+ torch.cuda.empty_cache()
+
+ def start_client(self, listen:str, port:int):
+ # Start client
+ comfy_client = ComfyClient()
+ # Connect to server (with retries)
+ n_tries = 5
+ for i in range(n_tries):
+ time.sleep(4)
+ try:
+ comfy_client.connect(listen=listen, port=port)
+ except ConnectionRefusedError as e:
+ print(e)
+ print(f"({i+1}/{n_tries}) Retrying...")
+ else:
+ break
+ return comfy_client
+
+ @fixture(scope="class", autouse=True)
+ def shared_client(self, args_pytest, _server):
+ client = self.start_client(args_pytest["listen"], args_pytest["port"])
+ yield client
+ del client
+ torch.cuda.empty_cache()
+
+ @fixture
+ def client(self, shared_client, request):
+ shared_client.set_test_name(f"execution[{request.node.name}]")
+ yield shared_client
+
+ @fixture
+ def builder(self, request):
+ yield GraphBuilder(prefix=request.node.name)
+
+ def test_lazy_input(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ input2 = g.node("StubImage", content="WHITE", height=512, width=512, batch_size=1)
+ mask = g.node("StubMask", value=0.0, height=512, width=512, batch_size=1)
+
+ lazy_mix = g.node("TestLazyMixImages", image1=input1.out(0), image2=input2.out(0), mask=mask.out(0))
+ output = g.node("SaveImage", images=lazy_mix.out(0))
+ result = client.run(g)
+
+ result_image = result.get_images(output)[0]
+ assert numpy.array(result_image).any() == 0, "Image should be black"
+ assert result.did_run(input1)
+ assert not result.did_run(input2)
+ assert result.did_run(mask)
+ assert result.did_run(lazy_mix)
+
+ def test_full_cache(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ input2 = g.node("StubImage", content="NOISE", height=512, width=512, batch_size=1)
+ mask = g.node("StubMask", value=0.5, height=512, width=512, batch_size=1)
+
+ lazy_mix = g.node("TestLazyMixImages", image1=input1.out(0), image2=input2.out(0), mask=mask.out(0))
+ g.node("SaveImage", images=lazy_mix.out(0))
+
+ client.run(g)
+ result2 = client.run(g)
+ for node_id, node in g.nodes.items():
+ assert not result2.did_run(node), f"Node {node_id} ran, but should have been cached"
+
+ def test_partial_cache(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ input2 = g.node("StubImage", content="NOISE", height=512, width=512, batch_size=1)
+ mask = g.node("StubMask", value=0.5, height=512, width=512, batch_size=1)
+
+ lazy_mix = g.node("TestLazyMixImages", image1=input1.out(0), image2=input2.out(0), mask=mask.out(0))
+ g.node("SaveImage", images=lazy_mix.out(0))
+
+ client.run(g)
+ mask.inputs['value'] = 0.4
+ result2 = client.run(g)
+ assert not result2.did_run(input1), "Input1 should have been cached"
+ assert not result2.did_run(input2), "Input2 should have been cached"
+
+ def test_error(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ # Different size of the two images
+ input2 = g.node("StubImage", content="NOISE", height=256, width=256, batch_size=1)
+ mask = g.node("StubMask", value=0.5, height=512, width=512, batch_size=1)
+
+ lazy_mix = g.node("TestLazyMixImages", image1=input1.out(0), image2=input2.out(0), mask=mask.out(0))
+ g.node("SaveImage", images=lazy_mix.out(0))
+
+ try:
+ client.run(g)
+ assert False, "Should have raised an error"
+ except Exception as e:
+ assert 'prompt_id' in e.args[0], f"Did not get back a proper error message: {e}"
+
+ @pytest.mark.parametrize("test_value, expect_error", [
+ (5, True),
+ ("foo", True),
+ (5.0, False),
+ ])
+ def test_validation_error_literal(self, test_value, expect_error, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ validation1 = g.node("TestCustomValidation1", input1=test_value, input2=3.0)
+ g.node("SaveImage", images=validation1.out(0))
+
+ if expect_error:
+ with pytest.raises(urllib.error.HTTPError):
+ client.run(g)
+ else:
+ client.run(g)
+
+ @pytest.mark.parametrize("test_type, test_value", [
+ ("StubInt", 5),
+ ("StubFloat", 5.0)
+ ])
+ def test_validation_error_edge1(self, test_type, test_value, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ stub = g.node(test_type, value=test_value)
+ validation1 = g.node("TestCustomValidation1", input1=stub.out(0), input2=3.0)
+ g.node("SaveImage", images=validation1.out(0))
+
+ with pytest.raises(urllib.error.HTTPError):
+ client.run(g)
+
+ @pytest.mark.parametrize("test_type, test_value, expect_error", [
+ ("StubInt", 5, True),
+ ("StubFloat", 5.0, False)
+ ])
+ def test_validation_error_edge2(self, test_type, test_value, expect_error, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ stub = g.node(test_type, value=test_value)
+ validation2 = g.node("TestCustomValidation2", input1=stub.out(0), input2=3.0)
+ g.node("SaveImage", images=validation2.out(0))
+
+ if expect_error:
+ with pytest.raises(urllib.error.HTTPError):
+ client.run(g)
+ else:
+ client.run(g)
+
+ @pytest.mark.parametrize("test_type, test_value, expect_error", [
+ ("StubInt", 5, True),
+ ("StubFloat", 5.0, False)
+ ])
+ def test_validation_error_edge3(self, test_type, test_value, expect_error, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ stub = g.node(test_type, value=test_value)
+ validation3 = g.node("TestCustomValidation3", input1=stub.out(0), input2=3.0)
+ g.node("SaveImage", images=validation3.out(0))
+
+ if expect_error:
+ with pytest.raises(urllib.error.HTTPError):
+ client.run(g)
+ else:
+ client.run(g)
+
+ @pytest.mark.parametrize("test_type, test_value, expect_error", [
+ ("StubInt", 5, True),
+ ("StubFloat", 5.0, False)
+ ])
+ def test_validation_error_edge4(self, test_type, test_value, expect_error, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ stub = g.node(test_type, value=test_value)
+ validation4 = g.node("TestCustomValidation4", input1=stub.out(0), input2=3.0)
+ g.node("SaveImage", images=validation4.out(0))
+
+ if expect_error:
+ with pytest.raises(urllib.error.HTTPError):
+ client.run(g)
+ else:
+ client.run(g)
+
+ @pytest.mark.parametrize("test_value1, test_value2, expect_error", [
+ (0.0, 0.5, False),
+ (0.0, 5.0, False),
+ (0.0, 7.0, True)
+ ])
+ def test_validation_error_kwargs(self, test_value1, test_value2, expect_error, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ validation5 = g.node("TestCustomValidation5", input1=test_value1, input2=test_value2)
+ g.node("SaveImage", images=validation5.out(0))
+
+ if expect_error:
+ with pytest.raises(urllib.error.HTTPError):
+ client.run(g)
+ else:
+ client.run(g)
+
+ def test_cycle_error(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ input2 = g.node("StubImage", content="WHITE", height=512, width=512, batch_size=1)
+ mask = g.node("StubMask", value=0.5, height=512, width=512, batch_size=1)
+
+ lazy_mix1 = g.node("TestLazyMixImages", image1=input1.out(0), mask=mask.out(0))
+ lazy_mix2 = g.node("TestLazyMixImages", image1=lazy_mix1.out(0), image2=input2.out(0), mask=mask.out(0))
+ g.node("SaveImage", images=lazy_mix2.out(0))
+
+ # When the cycle exists on initial submission, it should raise a validation error
+ with pytest.raises(urllib.error.HTTPError):
+ client.run(g)
+
+ def test_dynamic_cycle_error(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ input2 = g.node("StubImage", content="WHITE", height=512, width=512, batch_size=1)
+ generator = g.node("TestDynamicDependencyCycle", input1=input1.out(0), input2=input2.out(0))
+ g.node("SaveImage", images=generator.out(0))
+
+ # When the cycle is in a graph that is generated dynamically, it should raise a runtime error
+ try:
+ client.run(g)
+ assert False, "Should have raised an error"
+ except Exception as e:
+ assert 'prompt_id' in e.args[0], f"Did not get back a proper error message: {e}"
+ assert e.args[0]['node_id'] == generator.id, "Error should have been on the generator node"
+
+ def test_custom_is_changed(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ # Creating the nodes in this specific order previously caused a bug
+ save = g.node("SaveImage")
+ is_changed = g.node("TestCustomIsChanged", should_change=False)
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+
+ save.set_input('images', is_changed.out(0))
+ is_changed.set_input('image', input1.out(0))
+
+ result1 = client.run(g)
+ result2 = client.run(g)
+ is_changed.set_input('should_change', True)
+ result3 = client.run(g)
+ result4 = client.run(g)
+ assert result1.did_run(is_changed), "is_changed should have been run"
+ assert not result2.did_run(is_changed), "is_changed should have been cached"
+ assert result3.did_run(is_changed), "is_changed should have been re-run"
+ assert result4.did_run(is_changed), "is_changed should not have been cached"
+
+ def test_undeclared_inputs(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ input2 = g.node("StubImage", content="WHITE", height=512, width=512, batch_size=1)
+ input3 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ input4 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ average = g.node("TestVariadicAverage", input1=input1.out(0), input2=input2.out(0), input3=input3.out(0), input4=input4.out(0))
+ output = g.node("SaveImage", images=average.out(0))
+
+ result = client.run(g)
+ result_image = result.get_images(output)[0]
+ expected = 255 // 4
+ assert numpy.array(result_image).min() == expected and numpy.array(result_image).max() == expected, "Image should be grey"
+
+ def test_for_loop(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ iterations = 4
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ input2 = g.node("StubImage", content="WHITE", height=512, width=512, batch_size=1)
+ is_changed = g.node("TestCustomIsChanged", should_change=True, image=input2.out(0))
+ for_open = g.node("TestForLoopOpen", remaining=iterations, initial_value1=is_changed.out(0))
+ average = g.node("TestVariadicAverage", input1=input1.out(0), input2=for_open.out(2))
+ for_close = g.node("TestForLoopClose", flow_control=for_open.out(0), initial_value1=average.out(0))
+ output = g.node("SaveImage", images=for_close.out(0))
+
+ for iterations in range(1, 5):
+ for_open.set_input('remaining', iterations)
+ result = client.run(g)
+ result_image = result.get_images(output)[0]
+ expected = 255 // (2 ** iterations)
+ assert numpy.array(result_image).min() == expected and numpy.array(result_image).max() == expected, "Image should be grey"
+ assert result.did_run(is_changed)
+
+ def test_mixed_expansion_returns(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ val_list = g.node("TestMakeListNode", value1=0.1, value2=0.2, value3=0.3)
+ mixed = g.node("TestMixedExpansionReturns", input1=val_list.out(0))
+ output_dynamic = g.node("SaveImage", images=mixed.out(0))
+ output_literal = g.node("SaveImage", images=mixed.out(1))
+
+ result = client.run(g)
+ images_dynamic = result.get_images(output_dynamic)
+ assert len(images_dynamic) == 3, "Should have 2 images"
+ assert numpy.array(images_dynamic[0]).min() == 25 and numpy.array(images_dynamic[0]).max() == 25, "First image should be 0.1"
+ assert numpy.array(images_dynamic[1]).min() == 51 and numpy.array(images_dynamic[1]).max() == 51, "Second image should be 0.2"
+ assert numpy.array(images_dynamic[2]).min() == 76 and numpy.array(images_dynamic[2]).max() == 76, "Third image should be 0.3"
+
+ images_literal = result.get_images(output_literal)
+ assert len(images_literal) == 3, "Should have 2 images"
+ for i in range(3):
+ assert numpy.array(images_literal[i]).min() == 255 and numpy.array(images_literal[i]).max() == 255, "All images should be white"
+
+ def test_mixed_lazy_results(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ val_list = g.node("TestMakeListNode", value1=0.0, value2=0.5, value3=1.0)
+ mask = g.node("StubMask", value=val_list.out(0), height=512, width=512, batch_size=1)
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ input2 = g.node("StubImage", content="WHITE", height=512, width=512, batch_size=1)
+ mix = g.node("TestLazyMixImages", image1=input1.out(0), image2=input2.out(0), mask=mask.out(0))
+ rebatch = g.node("RebatchImages", images=mix.out(0), batch_size=3)
+ output = g.node("SaveImage", images=rebatch.out(0))
+
+ result = client.run(g)
+ images = result.get_images(output)
+ assert len(images) == 3, "Should have 3 image"
+ assert numpy.array(images[0]).min() == 0 and numpy.array(images[0]).max() == 0, "First image should be 0.0"
+ assert numpy.array(images[1]).min() == 127 and numpy.array(images[1]).max() == 127, "Second image should be 0.5"
+ assert numpy.array(images[2]).min() == 255 and numpy.array(images[2]).max() == 255, "Third image should be 1.0"
+
+ def test_output_reuse(self, client: ComfyClient, builder: GraphBuilder):
+ g = builder
+ input1 = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+
+ output1 = g.node("PreviewImage", images=input1.out(0))
+ output2 = g.node("PreviewImage", images=input1.out(0))
+
+ result = client.run(g)
+ images1 = result.get_images(output1)
+ images2 = result.get_images(output2)
+ assert len(images1) == 1, "Should have 1 image"
+ assert len(images2) == 1, "Should have 1 image"
+
diff --git a/tests/inference/testing_nodes/testing-pack/__init__.py b/tests/inference/testing_nodes/testing-pack/__init__.py
new file mode 100644
index 000000000..dcc71659a
--- /dev/null
+++ b/tests/inference/testing_nodes/testing-pack/__init__.py
@@ -0,0 +1,23 @@
+from .specific_tests import TEST_NODE_CLASS_MAPPINGS, TEST_NODE_DISPLAY_NAME_MAPPINGS
+from .flow_control import FLOW_CONTROL_NODE_CLASS_MAPPINGS, FLOW_CONTROL_NODE_DISPLAY_NAME_MAPPINGS
+from .util import UTILITY_NODE_CLASS_MAPPINGS, UTILITY_NODE_DISPLAY_NAME_MAPPINGS
+from .conditions import CONDITION_NODE_CLASS_MAPPINGS, CONDITION_NODE_DISPLAY_NAME_MAPPINGS
+from .stubs import TEST_STUB_NODE_CLASS_MAPPINGS, TEST_STUB_NODE_DISPLAY_NAME_MAPPINGS
+
+# NODE_CLASS_MAPPINGS = GENERAL_NODE_CLASS_MAPPINGS.update(COMPONENT_NODE_CLASS_MAPPINGS)
+# NODE_DISPLAY_NAME_MAPPINGS = GENERAL_NODE_DISPLAY_NAME_MAPPINGS.update(COMPONENT_NODE_DISPLAY_NAME_MAPPINGS)
+
+NODE_CLASS_MAPPINGS = {}
+NODE_CLASS_MAPPINGS.update(TEST_NODE_CLASS_MAPPINGS)
+NODE_CLASS_MAPPINGS.update(FLOW_CONTROL_NODE_CLASS_MAPPINGS)
+NODE_CLASS_MAPPINGS.update(UTILITY_NODE_CLASS_MAPPINGS)
+NODE_CLASS_MAPPINGS.update(CONDITION_NODE_CLASS_MAPPINGS)
+NODE_CLASS_MAPPINGS.update(TEST_STUB_NODE_CLASS_MAPPINGS)
+
+NODE_DISPLAY_NAME_MAPPINGS = {}
+NODE_DISPLAY_NAME_MAPPINGS.update(TEST_NODE_DISPLAY_NAME_MAPPINGS)
+NODE_DISPLAY_NAME_MAPPINGS.update(FLOW_CONTROL_NODE_DISPLAY_NAME_MAPPINGS)
+NODE_DISPLAY_NAME_MAPPINGS.update(UTILITY_NODE_DISPLAY_NAME_MAPPINGS)
+NODE_DISPLAY_NAME_MAPPINGS.update(CONDITION_NODE_DISPLAY_NAME_MAPPINGS)
+NODE_DISPLAY_NAME_MAPPINGS.update(TEST_STUB_NODE_DISPLAY_NAME_MAPPINGS)
+
diff --git a/tests/inference/testing_nodes/testing-pack/conditions.py b/tests/inference/testing_nodes/testing-pack/conditions.py
new file mode 100644
index 000000000..0c200ee28
--- /dev/null
+++ b/tests/inference/testing_nodes/testing-pack/conditions.py
@@ -0,0 +1,194 @@
+import re
+import torch
+
+class TestIntConditions:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "a": ("INT", {"default": 0, "min": -0xffffffffffffffff, "max": 0xffffffffffffffff, "step": 1}),
+ "b": ("INT", {"default": 0, "min": -0xffffffffffffffff, "max": 0xffffffffffffffff, "step": 1}),
+ "operation": (["==", "!=", "<", ">", "<=", ">="],),
+ },
+ }
+
+ RETURN_TYPES = ("BOOLEAN",)
+ FUNCTION = "int_condition"
+
+ CATEGORY = "Testing/Logic"
+
+ def int_condition(self, a, b, operation):
+ if operation == "==":
+ return (a == b,)
+ elif operation == "!=":
+ return (a != b,)
+ elif operation == "<":
+ return (a < b,)
+ elif operation == ">":
+ return (a > b,)
+ elif operation == "<=":
+ return (a <= b,)
+ elif operation == ">=":
+ return (a >= b,)
+
+
+class TestFloatConditions:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "a": ("FLOAT", {"default": 0, "min": -999999999999.0, "max": 999999999999.0, "step": 1}),
+ "b": ("FLOAT", {"default": 0, "min": -999999999999.0, "max": 999999999999.0, "step": 1}),
+ "operation": (["==", "!=", "<", ">", "<=", ">="],),
+ },
+ }
+
+ RETURN_TYPES = ("BOOLEAN",)
+ FUNCTION = "float_condition"
+
+ CATEGORY = "Testing/Logic"
+
+ def float_condition(self, a, b, operation):
+ if operation == "==":
+ return (a == b,)
+ elif operation == "!=":
+ return (a != b,)
+ elif operation == "<":
+ return (a < b,)
+ elif operation == ">":
+ return (a > b,)
+ elif operation == "<=":
+ return (a <= b,)
+ elif operation == ">=":
+ return (a >= b,)
+
+class TestStringConditions:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "a": ("STRING", {"multiline": False}),
+ "b": ("STRING", {"multiline": False}),
+ "operation": (["a == b", "a != b", "a IN b", "a MATCH REGEX(b)", "a BEGINSWITH b", "a ENDSWITH b"],),
+ "case_sensitive": ("BOOLEAN", {"default": True}),
+ },
+ }
+
+ RETURN_TYPES = ("BOOLEAN",)
+ FUNCTION = "string_condition"
+
+ CATEGORY = "Testing/Logic"
+
+ def string_condition(self, a, b, operation, case_sensitive):
+ if not case_sensitive:
+ a = a.lower()
+ b = b.lower()
+
+ if operation == "a == b":
+ return (a == b,)
+ elif operation == "a != b":
+ return (a != b,)
+ elif operation == "a IN b":
+ return (a in b,)
+ elif operation == "a MATCH REGEX(b)":
+ try:
+ return (re.match(b, a) is not None,)
+ except:
+ return (False,)
+ elif operation == "a BEGINSWITH b":
+ return (a.startswith(b),)
+ elif operation == "a ENDSWITH b":
+ return (a.endswith(b),)
+
+class TestToBoolNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "value": ("*",),
+ },
+ "optional": {
+ "invert": ("BOOLEAN", {"default": False}),
+ },
+ }
+
+ RETURN_TYPES = ("BOOLEAN",)
+ FUNCTION = "to_bool"
+
+ CATEGORY = "Testing/Logic"
+
+ def to_bool(self, value, invert = False):
+ if isinstance(value, torch.Tensor):
+ if value.max().item() == 0 and value.min().item() == 0:
+ result = False
+ else:
+ result = True
+ else:
+ try:
+ result = bool(value)
+ except:
+ # Can't convert it? Well then it's something or other. I dunno, I'm not a Python programmer.
+ result = True
+
+ if invert:
+ result = not result
+
+ return (result,)
+
+class TestBoolOperationNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "a": ("BOOLEAN",),
+ "b": ("BOOLEAN",),
+ "op": (["a AND b", "a OR b", "a XOR b", "NOT a"],),
+ },
+ }
+
+ RETURN_TYPES = ("BOOLEAN",)
+ FUNCTION = "bool_operation"
+
+ CATEGORY = "Testing/Logic"
+
+ def bool_operation(self, a, b, op):
+ if op == "a AND b":
+ return (a and b,)
+ elif op == "a OR b":
+ return (a or b,)
+ elif op == "a XOR b":
+ return (a ^ b,)
+ elif op == "NOT a":
+ return (not a,)
+
+
+CONDITION_NODE_CLASS_MAPPINGS = {
+ "TestIntConditions": TestIntConditions,
+ "TestFloatConditions": TestFloatConditions,
+ "TestStringConditions": TestStringConditions,
+ "TestToBoolNode": TestToBoolNode,
+ "TestBoolOperationNode": TestBoolOperationNode,
+}
+
+CONDITION_NODE_DISPLAY_NAME_MAPPINGS = {
+ "TestIntConditions": "Int Condition",
+ "TestFloatConditions": "Float Condition",
+ "TestStringConditions": "String Condition",
+ "TestToBoolNode": "To Bool",
+ "TestBoolOperationNode": "Bool Operation",
+}
diff --git a/tests/inference/testing_nodes/testing-pack/flow_control.py b/tests/inference/testing_nodes/testing-pack/flow_control.py
new file mode 100644
index 000000000..ba943be60
--- /dev/null
+++ b/tests/inference/testing_nodes/testing-pack/flow_control.py
@@ -0,0 +1,173 @@
+from comfy_execution.graph_utils import GraphBuilder, is_link
+from comfy_execution.graph import ExecutionBlocker
+from .tools import VariantSupport
+
+NUM_FLOW_SOCKETS = 5
+@VariantSupport()
+class TestWhileLoopOpen:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ inputs = {
+ "required": {
+ "condition": ("BOOLEAN", {"default": True}),
+ },
+ "optional": {
+ },
+ }
+ for i in range(NUM_FLOW_SOCKETS):
+ inputs["optional"][f"initial_value{i}"] = ("*",)
+ return inputs
+
+ RETURN_TYPES = tuple(["FLOW_CONTROL"] + ["*"] * NUM_FLOW_SOCKETS)
+ RETURN_NAMES = tuple(["FLOW_CONTROL"] + [f"value{i}" for i in range(NUM_FLOW_SOCKETS)])
+ FUNCTION = "while_loop_open"
+
+ CATEGORY = "Testing/Flow"
+
+ def while_loop_open(self, condition, **kwargs):
+ values = []
+ for i in range(NUM_FLOW_SOCKETS):
+ values.append(kwargs.get(f"initial_value{i}", None))
+ return tuple(["stub"] + values)
+
+@VariantSupport()
+class TestWhileLoopClose:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ inputs = {
+ "required": {
+ "flow_control": ("FLOW_CONTROL", {"rawLink": True}),
+ "condition": ("BOOLEAN", {"forceInput": True}),
+ },
+ "optional": {
+ },
+ "hidden": {
+ "dynprompt": "DYNPROMPT",
+ "unique_id": "UNIQUE_ID",
+ }
+ }
+ for i in range(NUM_FLOW_SOCKETS):
+ inputs["optional"][f"initial_value{i}"] = ("*",)
+ return inputs
+
+ RETURN_TYPES = tuple(["*"] * NUM_FLOW_SOCKETS)
+ RETURN_NAMES = tuple([f"value{i}" for i in range(NUM_FLOW_SOCKETS)])
+ FUNCTION = "while_loop_close"
+
+ CATEGORY = "Testing/Flow"
+
+ def explore_dependencies(self, node_id, dynprompt, upstream):
+ node_info = dynprompt.get_node(node_id)
+ if "inputs" not in node_info:
+ return
+ for k, v in node_info["inputs"].items():
+ if is_link(v):
+ parent_id = v[0]
+ if parent_id not in upstream:
+ upstream[parent_id] = []
+ self.explore_dependencies(parent_id, dynprompt, upstream)
+ upstream[parent_id].append(node_id)
+
+ def collect_contained(self, node_id, upstream, contained):
+ if node_id not in upstream:
+ return
+ for child_id in upstream[node_id]:
+ if child_id not in contained:
+ contained[child_id] = True
+ self.collect_contained(child_id, upstream, contained)
+
+
+ def while_loop_close(self, flow_control, condition, dynprompt=None, unique_id=None, **kwargs):
+ assert dynprompt is not None
+ if not condition:
+ # We're done with the loop
+ values = []
+ for i in range(NUM_FLOW_SOCKETS):
+ values.append(kwargs.get(f"initial_value{i}", None))
+ return tuple(values)
+
+ # We want to loop
+ upstream = {}
+ # Get the list of all nodes between the open and close nodes
+ self.explore_dependencies(unique_id, dynprompt, upstream)
+
+ contained = {}
+ open_node = flow_control[0]
+ self.collect_contained(open_node, upstream, contained)
+ contained[unique_id] = True
+ contained[open_node] = True
+
+ # We'll use the default prefix, but to avoid having node names grow exponentially in size,
+ # we'll use "Recurse" for the name of the recursively-generated copy of this node.
+ graph = GraphBuilder()
+ for node_id in contained:
+ original_node = dynprompt.get_node(node_id)
+ node = graph.node(original_node["class_type"], "Recurse" if node_id == unique_id else node_id)
+ node.set_override_display_id(node_id)
+ for node_id in contained:
+ original_node = dynprompt.get_node(node_id)
+ node = graph.lookup_node("Recurse" if node_id == unique_id else node_id)
+ assert node is not None
+ for k, v in original_node["inputs"].items():
+ if is_link(v) and v[0] in contained:
+ parent = graph.lookup_node(v[0])
+ assert parent is not None
+ node.set_input(k, parent.out(v[1]))
+ else:
+ node.set_input(k, v)
+ new_open = graph.lookup_node(open_node)
+ assert new_open is not None
+ for i in range(NUM_FLOW_SOCKETS):
+ key = f"initial_value{i}"
+ new_open.set_input(key, kwargs.get(key, None))
+ my_clone = graph.lookup_node("Recurse")
+ assert my_clone is not None
+ result = map(lambda x: my_clone.out(x), range(NUM_FLOW_SOCKETS))
+ return {
+ "result": tuple(result),
+ "expand": graph.finalize(),
+ }
+
+@VariantSupport()
+class TestExecutionBlockerNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ inputs = {
+ "required": {
+ "input": ("*",),
+ "block": ("BOOLEAN",),
+ "verbose": ("BOOLEAN", {"default": False}),
+ },
+ }
+ return inputs
+
+ RETURN_TYPES = ("*",)
+ RETURN_NAMES = ("output",)
+ FUNCTION = "execution_blocker"
+
+ CATEGORY = "Testing/Flow"
+
+ def execution_blocker(self, input, block, verbose):
+ if block:
+ return (ExecutionBlocker("Blocked Execution" if verbose else None),)
+ return (input,)
+
+FLOW_CONTROL_NODE_CLASS_MAPPINGS = {
+ "TestWhileLoopOpen": TestWhileLoopOpen,
+ "TestWhileLoopClose": TestWhileLoopClose,
+ "TestExecutionBlocker": TestExecutionBlockerNode,
+}
+FLOW_CONTROL_NODE_DISPLAY_NAME_MAPPINGS = {
+ "TestWhileLoopOpen": "While Loop Open",
+ "TestWhileLoopClose": "While Loop Close",
+ "TestExecutionBlocker": "Execution Blocker",
+}
diff --git a/tests/inference/testing_nodes/testing-pack/specific_tests.py b/tests/inference/testing_nodes/testing-pack/specific_tests.py
new file mode 100644
index 000000000..b961d1b63
--- /dev/null
+++ b/tests/inference/testing_nodes/testing-pack/specific_tests.py
@@ -0,0 +1,335 @@
+import torch
+from .tools import VariantSupport
+from comfy_execution.graph_utils import GraphBuilder
+
+class TestLazyMixImages:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "image1": ("IMAGE",{"lazy": True}),
+ "image2": ("IMAGE",{"lazy": True}),
+ "mask": ("MASK",),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "mix"
+
+ CATEGORY = "Testing/Nodes"
+
+ def check_lazy_status(self, mask, image1, image2):
+ mask_min = mask.min()
+ mask_max = mask.max()
+ needed = []
+ if image1 is None and (mask_min != 1.0 or mask_max != 1.0):
+ needed.append("image1")
+ if image2 is None and (mask_min != 0.0 or mask_max != 0.0):
+ needed.append("image2")
+ return needed
+
+ # Not trying to handle different batch sizes here just to keep the demo simple
+ def mix(self, mask, image1, image2):
+ mask_min = mask.min()
+ mask_max = mask.max()
+ if mask_min == 0.0 and mask_max == 0.0:
+ return (image1,)
+ elif mask_min == 1.0 and mask_max == 1.0:
+ return (image2,)
+
+ if len(mask.shape) == 2:
+ mask = mask.unsqueeze(0)
+ if len(mask.shape) == 3:
+ mask = mask.unsqueeze(3)
+ if mask.shape[3] < image1.shape[3]:
+ mask = mask.repeat(1, 1, 1, image1.shape[3])
+
+ result = image1 * (1. - mask) + image2 * mask,
+ return (result[0],)
+
+class TestVariadicAverage:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input1": ("IMAGE",),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "variadic_average"
+
+ CATEGORY = "Testing/Nodes"
+
+ def variadic_average(self, input1, **kwargs):
+ inputs = [input1]
+ while 'input' + str(len(inputs) + 1) in kwargs:
+ inputs.append(kwargs['input' + str(len(inputs) + 1)])
+ return (torch.stack(inputs).mean(dim=0),)
+
+
+class TestCustomIsChanged:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "image": ("IMAGE",),
+ },
+ "optional": {
+ "should_change": ("BOOL", {"default": False}),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "custom_is_changed"
+
+ CATEGORY = "Testing/Nodes"
+
+ def custom_is_changed(self, image, should_change=False):
+ return (image,)
+
+ @classmethod
+ def IS_CHANGED(cls, should_change=False, *args, **kwargs):
+ if should_change:
+ return float("NaN")
+ else:
+ return False
+
+class TestCustomValidation1:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input1": ("IMAGE,FLOAT",),
+ "input2": ("IMAGE,FLOAT",),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "custom_validation1"
+
+ CATEGORY = "Testing/Nodes"
+
+ def custom_validation1(self, input1, input2):
+ if isinstance(input1, float) and isinstance(input2, float):
+ result = torch.ones([1, 512, 512, 3]) * input1 * input2
+ else:
+ result = input1 * input2
+ return (result,)
+
+ @classmethod
+ def VALIDATE_INPUTS(cls, input1=None, input2=None):
+ if input1 is not None:
+ if not isinstance(input1, (torch.Tensor, float)):
+ return f"Invalid type of input1: {type(input1)}"
+ if input2 is not None:
+ if not isinstance(input2, (torch.Tensor, float)):
+ return f"Invalid type of input2: {type(input2)}"
+
+ return True
+
+class TestCustomValidation2:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input1": ("IMAGE,FLOAT",),
+ "input2": ("IMAGE,FLOAT",),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "custom_validation2"
+
+ CATEGORY = "Testing/Nodes"
+
+ def custom_validation2(self, input1, input2):
+ if isinstance(input1, float) and isinstance(input2, float):
+ result = torch.ones([1, 512, 512, 3]) * input1 * input2
+ else:
+ result = input1 * input2
+ return (result,)
+
+ @classmethod
+ def VALIDATE_INPUTS(cls, input_types, input1=None, input2=None):
+ if input1 is not None:
+ if not isinstance(input1, (torch.Tensor, float)):
+ return f"Invalid type of input1: {type(input1)}"
+ if input2 is not None:
+ if not isinstance(input2, (torch.Tensor, float)):
+ return f"Invalid type of input2: {type(input2)}"
+
+ if 'input1' in input_types:
+ if input_types['input1'] not in ["IMAGE", "FLOAT"]:
+ return f"Invalid type of input1: {input_types['input1']}"
+ if 'input2' in input_types:
+ if input_types['input2'] not in ["IMAGE", "FLOAT"]:
+ return f"Invalid type of input2: {input_types['input2']}"
+
+ return True
+
+@VariantSupport()
+class TestCustomValidation3:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input1": ("IMAGE,FLOAT",),
+ "input2": ("IMAGE,FLOAT",),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "custom_validation3"
+
+ CATEGORY = "Testing/Nodes"
+
+ def custom_validation3(self, input1, input2):
+ if isinstance(input1, float) and isinstance(input2, float):
+ result = torch.ones([1, 512, 512, 3]) * input1 * input2
+ else:
+ result = input1 * input2
+ return (result,)
+
+class TestCustomValidation4:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input1": ("FLOAT",),
+ "input2": ("FLOAT",),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "custom_validation4"
+
+ CATEGORY = "Testing/Nodes"
+
+ def custom_validation4(self, input1, input2):
+ result = torch.ones([1, 512, 512, 3]) * input1 * input2
+ return (result,)
+
+ @classmethod
+ def VALIDATE_INPUTS(cls, input1, input2):
+ if input1 is not None:
+ if not isinstance(input1, float):
+ return f"Invalid type of input1: {type(input1)}"
+ if input2 is not None:
+ if not isinstance(input2, float):
+ return f"Invalid type of input2: {type(input2)}"
+
+ return True
+
+class TestCustomValidation5:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input1": ("FLOAT", {"min": 0.0, "max": 1.0}),
+ "input2": ("FLOAT", {"min": 0.0, "max": 1.0}),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "custom_validation5"
+
+ CATEGORY = "Testing/Nodes"
+
+ def custom_validation5(self, input1, input2):
+ value = input1 * input2
+ return (torch.ones([1, 512, 512, 3]) * value,)
+
+ @classmethod
+ def VALIDATE_INPUTS(cls, **kwargs):
+ if kwargs['input2'] == 7.0:
+ return "7s are not allowed. I've never liked 7s."
+ return True
+
+class TestDynamicDependencyCycle:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input1": ("IMAGE",),
+ "input2": ("IMAGE",),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "dynamic_dependency_cycle"
+
+ CATEGORY = "Testing/Nodes"
+
+ def dynamic_dependency_cycle(self, input1, input2):
+ g = GraphBuilder()
+ mask = g.node("StubMask", value=0.5, height=512, width=512, batch_size=1)
+ mix1 = g.node("TestLazyMixImages", image1=input1, mask=mask.out(0))
+ mix2 = g.node("TestLazyMixImages", image1=mix1.out(0), image2=input2, mask=mask.out(0))
+
+ # Create the cyle
+ mix1.set_input("image2", mix2.out(0))
+
+ return {
+ "result": (mix2.out(0),),
+ "expand": g.finalize(),
+ }
+
+class TestMixedExpansionReturns:
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "input1": ("FLOAT",),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE","IMAGE")
+ FUNCTION = "mixed_expansion_returns"
+
+ CATEGORY = "Testing/Nodes"
+
+ def mixed_expansion_returns(self, input1):
+ white_image = torch.ones([1, 512, 512, 3])
+ if input1 <= 0.1:
+ return (torch.ones([1, 512, 512, 3]) * 0.1, white_image)
+ elif input1 <= 0.2:
+ return {
+ "result": (torch.ones([1, 512, 512, 3]) * 0.2, white_image),
+ }
+ else:
+ g = GraphBuilder()
+ mask = g.node("StubMask", value=0.3, height=512, width=512, batch_size=1)
+ black = g.node("StubImage", content="BLACK", height=512, width=512, batch_size=1)
+ white = g.node("StubImage", content="WHITE", height=512, width=512, batch_size=1)
+ mix = g.node("TestLazyMixImages", image1=black.out(0), image2=white.out(0), mask=mask.out(0))
+ return {
+ "result": (mix.out(0), white_image),
+ "expand": g.finalize(),
+ }
+
+TEST_NODE_CLASS_MAPPINGS = {
+ "TestLazyMixImages": TestLazyMixImages,
+ "TestVariadicAverage": TestVariadicAverage,
+ "TestCustomIsChanged": TestCustomIsChanged,
+ "TestCustomValidation1": TestCustomValidation1,
+ "TestCustomValidation2": TestCustomValidation2,
+ "TestCustomValidation3": TestCustomValidation3,
+ "TestCustomValidation4": TestCustomValidation4,
+ "TestCustomValidation5": TestCustomValidation5,
+ "TestDynamicDependencyCycle": TestDynamicDependencyCycle,
+ "TestMixedExpansionReturns": TestMixedExpansionReturns,
+}
+
+TEST_NODE_DISPLAY_NAME_MAPPINGS = {
+ "TestLazyMixImages": "Lazy Mix Images",
+ "TestVariadicAverage": "Variadic Average",
+ "TestCustomIsChanged": "Custom IsChanged",
+ "TestCustomValidation1": "Custom Validation 1",
+ "TestCustomValidation2": "Custom Validation 2",
+ "TestCustomValidation3": "Custom Validation 3",
+ "TestCustomValidation4": "Custom Validation 4",
+ "TestCustomValidation5": "Custom Validation 5",
+ "TestDynamicDependencyCycle": "Dynamic Dependency Cycle",
+ "TestMixedExpansionReturns": "Mixed Expansion Returns",
+}
diff --git a/tests/inference/testing_nodes/testing-pack/stubs.py b/tests/inference/testing_nodes/testing-pack/stubs.py
new file mode 100644
index 000000000..9be6eac9d
--- /dev/null
+++ b/tests/inference/testing_nodes/testing-pack/stubs.py
@@ -0,0 +1,105 @@
+import torch
+
+class StubImage:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "content": (['WHITE', 'BLACK', 'NOISE'],),
+ "height": ("INT", {"default": 512, "min": 1, "max": 1024 ** 3, "step": 1}),
+ "width": ("INT", {"default": 512, "min": 1, "max": 4096 ** 3, "step": 1}),
+ "batch_size": ("INT", {"default": 1, "min": 1, "max": 1024 ** 3, "step": 1}),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ FUNCTION = "stub_image"
+
+ CATEGORY = "Testing/Stub Nodes"
+
+ def stub_image(self, content, height, width, batch_size):
+ if content == "WHITE":
+ return (torch.ones(batch_size, height, width, 3),)
+ elif content == "BLACK":
+ return (torch.zeros(batch_size, height, width, 3),)
+ elif content == "NOISE":
+ return (torch.rand(batch_size, height, width, 3),)
+
+class StubMask:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "value": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
+ "height": ("INT", {"default": 512, "min": 1, "max": 1024 ** 3, "step": 1}),
+ "width": ("INT", {"default": 512, "min": 1, "max": 4096 ** 3, "step": 1}),
+ "batch_size": ("INT", {"default": 1, "min": 1, "max": 1024 ** 3, "step": 1}),
+ },
+ }
+
+ RETURN_TYPES = ("MASK",)
+ FUNCTION = "stub_mask"
+
+ CATEGORY = "Testing/Stub Nodes"
+
+ def stub_mask(self, value, height, width, batch_size):
+ return (torch.ones(batch_size, height, width) * value,)
+
+class StubInt:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "value": ("INT", {"default": 0, "min": -0xffffffff, "max": 0xffffffff, "step": 1}),
+ },
+ }
+
+ RETURN_TYPES = ("INT",)
+ FUNCTION = "stub_int"
+
+ CATEGORY = "Testing/Stub Nodes"
+
+ def stub_int(self, value):
+ return (value,)
+
+class StubFloat:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "value": ("FLOAT", {"default": 0.0, "min": -1.0e38, "max": 1.0e38, "step": 0.01}),
+ },
+ }
+
+ RETURN_TYPES = ("FLOAT",)
+ FUNCTION = "stub_float"
+
+ CATEGORY = "Testing/Stub Nodes"
+
+ def stub_float(self, value):
+ return (value,)
+
+TEST_STUB_NODE_CLASS_MAPPINGS = {
+ "StubImage": StubImage,
+ "StubMask": StubMask,
+ "StubInt": StubInt,
+ "StubFloat": StubFloat,
+}
+TEST_STUB_NODE_DISPLAY_NAME_MAPPINGS = {
+ "StubImage": "Stub Image",
+ "StubMask": "Stub Mask",
+ "StubInt": "Stub Int",
+ "StubFloat": "Stub Float",
+}
diff --git a/tests/inference/testing_nodes/testing-pack/tools.py b/tests/inference/testing_nodes/testing-pack/tools.py
new file mode 100644
index 000000000..34b28c0eb
--- /dev/null
+++ b/tests/inference/testing_nodes/testing-pack/tools.py
@@ -0,0 +1,53 @@
+
+def MakeSmartType(t):
+ if isinstance(t, str):
+ return SmartType(t)
+ return t
+
+class SmartType(str):
+ def __ne__(self, other):
+ if self == "*" or other == "*":
+ return False
+ selfset = set(self.split(','))
+ otherset = set(other.split(','))
+ return not selfset.issubset(otherset)
+
+def VariantSupport():
+ def decorator(cls):
+ if hasattr(cls, "INPUT_TYPES"):
+ old_input_types = getattr(cls, "INPUT_TYPES")
+ def new_input_types(*args, **kwargs):
+ types = old_input_types(*args, **kwargs)
+ for category in ["required", "optional"]:
+ if category not in types:
+ continue
+ for key, value in types[category].items():
+ if isinstance(value, tuple):
+ types[category][key] = (MakeSmartType(value[0]),) + value[1:]
+ return types
+ setattr(cls, "INPUT_TYPES", new_input_types)
+ if hasattr(cls, "RETURN_TYPES"):
+ old_return_types = cls.RETURN_TYPES
+ setattr(cls, "RETURN_TYPES", tuple(MakeSmartType(x) for x in old_return_types))
+ if hasattr(cls, "VALIDATE_INPUTS"):
+ # Reflection is used to determine what the function signature is, so we can't just change the function signature
+ raise NotImplementedError("VariantSupport does not support VALIDATE_INPUTS yet")
+ else:
+ def validate_inputs(input_types):
+ inputs = cls.INPUT_TYPES()
+ for key, value in input_types.items():
+ if isinstance(value, SmartType):
+ continue
+ if "required" in inputs and key in inputs["required"]:
+ expected_type = inputs["required"][key][0]
+ elif "optional" in inputs and key in inputs["optional"]:
+ expected_type = inputs["optional"][key][0]
+ else:
+ expected_type = None
+ if expected_type is not None and MakeSmartType(value) != expected_type:
+ return f"Invalid type of {key}: {value} (expected {expected_type})"
+ return True
+ setattr(cls, "VALIDATE_INPUTS", validate_inputs)
+ return cls
+ return decorator
+
diff --git a/tests/inference/testing_nodes/testing-pack/util.py b/tests/inference/testing_nodes/testing-pack/util.py
new file mode 100644
index 000000000..ca116c16e
--- /dev/null
+++ b/tests/inference/testing_nodes/testing-pack/util.py
@@ -0,0 +1,364 @@
+from comfy_execution.graph_utils import GraphBuilder
+from .tools import VariantSupport
+
+@VariantSupport()
+class TestAccumulateNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "to_add": ("*",),
+ },
+ "optional": {
+ "accumulation": ("ACCUMULATION",),
+ },
+ }
+
+ RETURN_TYPES = ("ACCUMULATION",)
+ FUNCTION = "accumulate"
+
+ CATEGORY = "Testing/Lists"
+
+ def accumulate(self, to_add, accumulation = None):
+ if accumulation is None:
+ value = [to_add]
+ else:
+ value = accumulation["accum"] + [to_add]
+ return ({"accum": value},)
+
+@VariantSupport()
+class TestAccumulationHeadNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "accumulation": ("ACCUMULATION",),
+ },
+ }
+
+ RETURN_TYPES = ("ACCUMULATION", "*",)
+ FUNCTION = "accumulation_head"
+
+ CATEGORY = "Testing/Lists"
+
+ def accumulation_head(self, accumulation):
+ accum = accumulation["accum"]
+ if len(accum) == 0:
+ return (accumulation, None)
+ else:
+ return ({"accum": accum[1:]}, accum[0])
+
+class TestAccumulationTailNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "accumulation": ("ACCUMULATION",),
+ },
+ }
+
+ RETURN_TYPES = ("ACCUMULATION", "*",)
+ FUNCTION = "accumulation_tail"
+
+ CATEGORY = "Testing/Lists"
+
+ def accumulation_tail(self, accumulation):
+ accum = accumulation["accum"]
+ if len(accum) == 0:
+ return (None, accumulation)
+ else:
+ return ({"accum": accum[:-1]}, accum[-1])
+
+@VariantSupport()
+class TestAccumulationToListNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "accumulation": ("ACCUMULATION",),
+ },
+ }
+
+ RETURN_TYPES = ("*",)
+ OUTPUT_IS_LIST = (True,)
+
+ FUNCTION = "accumulation_to_list"
+
+ CATEGORY = "Testing/Lists"
+
+ def accumulation_to_list(self, accumulation):
+ return (accumulation["accum"],)
+
+@VariantSupport()
+class TestListToAccumulationNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "list": ("*",),
+ },
+ }
+
+ RETURN_TYPES = ("ACCUMULATION",)
+ INPUT_IS_LIST = (True,)
+
+ FUNCTION = "list_to_accumulation"
+
+ CATEGORY = "Testing/Lists"
+
+ def list_to_accumulation(self, list):
+ return ({"accum": list},)
+
+@VariantSupport()
+class TestAccumulationGetLengthNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "accumulation": ("ACCUMULATION",),
+ },
+ }
+
+ RETURN_TYPES = ("INT",)
+
+ FUNCTION = "accumlength"
+
+ CATEGORY = "Testing/Lists"
+
+ def accumlength(self, accumulation):
+ return (len(accumulation['accum']),)
+
+@VariantSupport()
+class TestAccumulationGetItemNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "accumulation": ("ACCUMULATION",),
+ "index": ("INT", {"default":0, "step":1})
+ },
+ }
+
+ RETURN_TYPES = ("*",)
+
+ FUNCTION = "get_item"
+
+ CATEGORY = "Testing/Lists"
+
+ def get_item(self, accumulation, index):
+ return (accumulation['accum'][index],)
+
+@VariantSupport()
+class TestAccumulationSetItemNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "accumulation": ("ACCUMULATION",),
+ "index": ("INT", {"default":0, "step":1}),
+ "value": ("*",),
+ },
+ }
+
+ RETURN_TYPES = ("ACCUMULATION",)
+
+ FUNCTION = "set_item"
+
+ CATEGORY = "Testing/Lists"
+
+ def set_item(self, accumulation, index, value):
+ new_accum = accumulation['accum'][:]
+ new_accum[index] = value
+ return ({"accum": new_accum},)
+
+class TestIntMathOperation:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "a": ("INT", {"default": 0, "min": -0xffffffffffffffff, "max": 0xffffffffffffffff, "step": 1}),
+ "b": ("INT", {"default": 0, "min": -0xffffffffffffffff, "max": 0xffffffffffffffff, "step": 1}),
+ "operation": (["add", "subtract", "multiply", "divide", "modulo", "power"],),
+ },
+ }
+
+ RETURN_TYPES = ("INT",)
+ FUNCTION = "int_math_operation"
+
+ CATEGORY = "Testing/Logic"
+
+ def int_math_operation(self, a, b, operation):
+ if operation == "add":
+ return (a + b,)
+ elif operation == "subtract":
+ return (a - b,)
+ elif operation == "multiply":
+ return (a * b,)
+ elif operation == "divide":
+ return (a // b,)
+ elif operation == "modulo":
+ return (a % b,)
+ elif operation == "power":
+ return (a ** b,)
+
+
+from .flow_control import NUM_FLOW_SOCKETS
+@VariantSupport()
+class TestForLoopOpen:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "remaining": ("INT", {"default": 1, "min": 0, "max": 100000, "step": 1}),
+ },
+ "optional": {
+ f"initial_value{i}": ("*",) for i in range(1, NUM_FLOW_SOCKETS)
+ },
+ "hidden": {
+ "initial_value0": ("*",)
+ }
+ }
+
+ RETURN_TYPES = tuple(["FLOW_CONTROL", "INT",] + ["*"] * (NUM_FLOW_SOCKETS-1))
+ RETURN_NAMES = tuple(["flow_control", "remaining"] + [f"value{i}" for i in range(1, NUM_FLOW_SOCKETS)])
+ FUNCTION = "for_loop_open"
+
+ CATEGORY = "Testing/Flow"
+
+ def for_loop_open(self, remaining, **kwargs):
+ graph = GraphBuilder()
+ if "initial_value0" in kwargs:
+ remaining = kwargs["initial_value0"]
+ while_open = graph.node("TestWhileLoopOpen", condition=remaining, initial_value0=remaining, **{(f"initial_value{i}"): kwargs.get(f"initial_value{i}", None) for i in range(1, NUM_FLOW_SOCKETS)})
+ outputs = [kwargs.get(f"initial_value{i}", None) for i in range(1, NUM_FLOW_SOCKETS)]
+ return {
+ "result": tuple(["stub", remaining] + outputs),
+ "expand": graph.finalize(),
+ }
+
+@VariantSupport()
+class TestForLoopClose:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "flow_control": ("FLOW_CONTROL", {"rawLink": True}),
+ },
+ "optional": {
+ f"initial_value{i}": ("*",{"rawLink": True}) for i in range(1, NUM_FLOW_SOCKETS)
+ },
+ }
+
+ RETURN_TYPES = tuple(["*"] * (NUM_FLOW_SOCKETS-1))
+ RETURN_NAMES = tuple([f"value{i}" for i in range(1, NUM_FLOW_SOCKETS)])
+ FUNCTION = "for_loop_close"
+
+ CATEGORY = "Testing/Flow"
+
+ def for_loop_close(self, flow_control, **kwargs):
+ graph = GraphBuilder()
+ while_open = flow_control[0]
+ sub = graph.node("TestIntMathOperation", operation="subtract", a=[while_open,1], b=1)
+ cond = graph.node("TestToBoolNode", value=sub.out(0))
+ input_values = {f"initial_value{i}": kwargs.get(f"initial_value{i}", None) for i in range(1, NUM_FLOW_SOCKETS)}
+ while_close = graph.node("TestWhileLoopClose",
+ flow_control=flow_control,
+ condition=cond.out(0),
+ initial_value0=sub.out(0),
+ **input_values)
+ return {
+ "result": tuple([while_close.out(i) for i in range(1, NUM_FLOW_SOCKETS)]),
+ "expand": graph.finalize(),
+ }
+
+NUM_LIST_SOCKETS = 10
+@VariantSupport()
+class TestMakeListNode:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "value1": ("*",),
+ },
+ "optional": {
+ f"value{i}": ("*",) for i in range(1, NUM_LIST_SOCKETS)
+ },
+ }
+
+ RETURN_TYPES = ("*",)
+ FUNCTION = "make_list"
+ OUTPUT_IS_LIST = (True,)
+
+ CATEGORY = "Testing/Lists"
+
+ def make_list(self, **kwargs):
+ result = []
+ for i in range(NUM_LIST_SOCKETS):
+ if f"value{i}" in kwargs:
+ result.append(kwargs[f"value{i}"])
+ return (result,)
+
+UTILITY_NODE_CLASS_MAPPINGS = {
+ "TestAccumulateNode": TestAccumulateNode,
+ "TestAccumulationHeadNode": TestAccumulationHeadNode,
+ "TestAccumulationTailNode": TestAccumulationTailNode,
+ "TestAccumulationToListNode": TestAccumulationToListNode,
+ "TestListToAccumulationNode": TestListToAccumulationNode,
+ "TestAccumulationGetLengthNode": TestAccumulationGetLengthNode,
+ "TestAccumulationGetItemNode": TestAccumulationGetItemNode,
+ "TestAccumulationSetItemNode": TestAccumulationSetItemNode,
+ "TestForLoopOpen": TestForLoopOpen,
+ "TestForLoopClose": TestForLoopClose,
+ "TestIntMathOperation": TestIntMathOperation,
+ "TestMakeListNode": TestMakeListNode,
+}
+UTILITY_NODE_DISPLAY_NAME_MAPPINGS = {
+ "TestAccumulateNode": "Accumulate",
+ "TestAccumulationHeadNode": "Accumulation Head",
+ "TestAccumulationTailNode": "Accumulation Tail",
+ "TestAccumulationToListNode": "Accumulation to List",
+ "TestListToAccumulationNode": "List to Accumulation",
+ "TestAccumulationGetLengthNode": "Accumulation Get Length",
+ "TestAccumulationGetItemNode": "Accumulation Get Item",
+ "TestAccumulationSetItemNode": "Accumulation Set Item",
+ "TestForLoopOpen": "For Loop Open",
+ "TestForLoopClose": "For Loop Close",
+ "TestIntMathOperation": "Int Math Operation",
+ "TestMakeListNode": "Make List",
+}
diff --git a/web/assets/index--0nRVkuV.js b/web/assets/index--0nRVkuV.js
new file mode 100644
index 000000000..1b34b2165
--- /dev/null
+++ b/web/assets/index--0nRVkuV.js
@@ -0,0 +1,6179 @@
+var __defProp = Object.defineProperty;
+var __defProps = Object.defineProperties;
+var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
+var __getOwnPropSymbols = Object.getOwnPropertySymbols;
+var __hasOwnProp = Object.prototype.hasOwnProperty;
+var __propIsEnum = Object.prototype.propertyIsEnumerable;
+var __typeError = (msg) => {
+ throw TypeError(msg);
+};
+var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
+var __spreadValues = (a, b) => {
+ for (var prop in b || (b = {}))
+ if (__hasOwnProp.call(b, prop))
+ __defNormalProp(a, prop, b[prop]);
+ if (__getOwnPropSymbols)
+ for (var prop of __getOwnPropSymbols(b)) {
+ if (__propIsEnum.call(b, prop))
+ __defNormalProp(a, prop, b[prop]);
+ }
+ return a;
+};
+var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
+var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
+var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
+var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
+var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
+var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
+var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
+var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
+var __async = (__this, __arguments, generator) => {
+ return new Promise((resolve, reject) => {
+ var fulfilled = (value) => {
+ try {
+ step(generator.next(value));
+ } catch (e) {
+ reject(e);
+ }
+ };
+ var rejected = (value) => {
+ try {
+ step(generator.throw(value));
+ } catch (e) {
+ reject(e);
+ }
+ };
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
+ step((generator = generator.apply(__this, __arguments)).next());
+ });
+};
+var _PrimitiveNode_instances, onFirstConnection_fn, createWidget_fn, mergeWidgetConfig_fn, isValidConnection_fn, removeWidgets_fn, _convertedToProcess;
+import { C as ComfyDialog, $ as $el, a as ComfyApp, b as app, L as LGraphCanvas, c as LiteGraph, d as applyTextReplacements, e as ComfyWidgets, f as addValueControlWidgets, D as DraggableList, g as api, h as LGraphGroup, i as LGraphNode } from "./index-D8Zp4vRl.js";
+const _ClipspaceDialog = class _ClipspaceDialog extends ComfyDialog {
+ static registerButton(name, contextPredicate, callback) {
+ const item = $el("button", {
+ type: "button",
+ textContent: name,
+ contextPredicate,
+ onclick: callback
+ });
+ _ClipspaceDialog.items.push(item);
+ }
+ static invalidatePreview() {
+ if (ComfyApp.clipspace && ComfyApp.clipspace.imgs && ComfyApp.clipspace.imgs.length > 0) {
+ const img_preview = document.getElementById(
+ "clipspace_preview"
+ );
+ if (img_preview) {
+ img_preview.src = ComfyApp.clipspace.imgs[ComfyApp.clipspace["selectedIndex"]].src;
+ img_preview.style.maxHeight = "100%";
+ img_preview.style.maxWidth = "100%";
+ }
+ }
+ }
+ static invalidate() {
+ if (_ClipspaceDialog.instance) {
+ const self = _ClipspaceDialog.instance;
+ const children = $el("div.comfy-modal-content", [
+ self.createImgSettings(),
+ ...self.createButtons()
+ ]);
+ if (self.element) {
+ self.element.removeChild(self.element.firstChild);
+ self.element.appendChild(children);
+ } else {
+ self.element = $el("div.comfy-modal", { parent: document.body }, [
+ children
+ ]);
+ }
+ if (self.element.children[0].children.length <= 1) {
+ self.element.children[0].appendChild(
+ $el("p", {}, [
+ "Unable to find the features to edit content of a format stored in the current Clipspace."
+ ])
+ );
+ }
+ _ClipspaceDialog.invalidatePreview();
+ }
+ }
+ constructor() {
+ super();
+ }
+ createButtons() {
+ const buttons = [];
+ for (let idx in _ClipspaceDialog.items) {
+ const item = _ClipspaceDialog.items[idx];
+ if (!item.contextPredicate || item.contextPredicate())
+ buttons.push(_ClipspaceDialog.items[idx]);
+ }
+ buttons.push(
+ $el("button", {
+ type: "button",
+ textContent: "Close",
+ onclick: /* @__PURE__ */ __name(() => {
+ this.close();
+ }, "onclick")
+ })
+ );
+ return buttons;
+ }
+ createImgSettings() {
+ if (ComfyApp.clipspace.imgs) {
+ const combo_items = [];
+ const imgs = ComfyApp.clipspace.imgs;
+ for (let i = 0; i < imgs.length; i++) {
+ combo_items.push($el("option", { value: i }, [`${i}`]));
+ }
+ const combo1 = $el(
+ "select",
+ {
+ id: "clipspace_img_selector",
+ onchange: /* @__PURE__ */ __name((event) => {
+ ComfyApp.clipspace["selectedIndex"] = event.target.selectedIndex;
+ _ClipspaceDialog.invalidatePreview();
+ }, "onchange")
+ },
+ combo_items
+ );
+ const row1 = $el("tr", {}, [
+ $el("td", {}, [$el("font", { color: "white" }, ["Select Image"])]),
+ $el("td", {}, [combo1])
+ ]);
+ const combo2 = $el(
+ "select",
+ {
+ id: "clipspace_img_paste_mode",
+ onchange: /* @__PURE__ */ __name((event) => {
+ ComfyApp.clipspace["img_paste_mode"] = event.target.value;
+ }, "onchange")
+ },
+ [
+ $el("option", { value: "selected" }, "selected"),
+ $el("option", { value: "all" }, "all")
+ ]
+ );
+ combo2.value = ComfyApp.clipspace["img_paste_mode"];
+ const row2 = $el("tr", {}, [
+ $el("td", {}, [$el("font", { color: "white" }, ["Paste Mode"])]),
+ $el("td", {}, [combo2])
+ ]);
+ const td = $el(
+ "td",
+ { align: "center", width: "100px", height: "100px", colSpan: "2" },
+ [$el("img", { id: "clipspace_preview", ondragstart: /* @__PURE__ */ __name(() => false, "ondragstart") }, [])]
+ );
+ const row3 = $el("tr", {}, [td]);
+ return $el("table", {}, [row1, row2, row3]);
+ } else {
+ return [];
+ }
+ }
+ createImgPreview() {
+ if (ComfyApp.clipspace.imgs) {
+ return $el("img", { id: "clipspace_preview", ondragstart: /* @__PURE__ */ __name(() => false, "ondragstart") });
+ } else return [];
+ }
+ show() {
+ const img_preview = document.getElementById("clipspace_preview");
+ _ClipspaceDialog.invalidate();
+ this.element.style.display = "block";
+ }
+};
+__name(_ClipspaceDialog, "ClipspaceDialog");
+__publicField(_ClipspaceDialog, "items", []);
+__publicField(_ClipspaceDialog, "instance", null);
+let ClipspaceDialog = _ClipspaceDialog;
+app.registerExtension({
+ name: "Comfy.Clipspace",
+ init(app2) {
+ app2.openClipspace = function() {
+ if (!ClipspaceDialog.instance) {
+ ClipspaceDialog.instance = new ClipspaceDialog();
+ ComfyApp.clipspace_invalidate_handler = ClipspaceDialog.invalidate;
+ }
+ if (ComfyApp.clipspace) {
+ ClipspaceDialog.instance.show();
+ } else app2.ui.dialog.show("Clipspace is Empty!");
+ };
+ }
+});
+window.comfyAPI = window.comfyAPI || {};
+window.comfyAPI.clipspace = window.comfyAPI.clipspace || {};
+window.comfyAPI.clipspace.ClipspaceDialog = ClipspaceDialog;
+const colorPalettes = {
+ dark: {
+ id: "dark",
+ name: "Dark (Default)",
+ colors: {
+ node_slot: {
+ CLIP: "#FFD500",
+ // bright yellow
+ CLIP_VISION: "#A8DADC",
+ // light blue-gray
+ CLIP_VISION_OUTPUT: "#ad7452",
+ // rusty brown-orange
+ CONDITIONING: "#FFA931",
+ // vibrant orange-yellow
+ CONTROL_NET: "#6EE7B7",
+ // soft mint green
+ IMAGE: "#64B5F6",
+ // bright sky blue
+ LATENT: "#FF9CF9",
+ // light pink-purple
+ MASK: "#81C784",
+ // muted green
+ MODEL: "#B39DDB",
+ // light lavender-purple
+ STYLE_MODEL: "#C2FFAE",
+ // light green-yellow
+ VAE: "#FF6E6E",
+ // bright red
+ NOISE: "#B0B0B0",
+ // gray
+ GUIDER: "#66FFFF",
+ // cyan
+ SAMPLER: "#ECB4B4",
+ // very soft red
+ SIGMAS: "#CDFFCD",
+ // soft lime green
+ TAESD: "#DCC274"
+ // cheesecake
+ },
+ litegraph_base: {
+ BACKGROUND_IMAGE: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII=",
+ CLEAR_BACKGROUND_COLOR: "#222",
+ NODE_TITLE_COLOR: "#999",
+ NODE_SELECTED_TITLE_COLOR: "#FFF",
+ NODE_TEXT_SIZE: 14,
+ NODE_TEXT_COLOR: "#AAA",
+ NODE_SUBTEXT_SIZE: 12,
+ NODE_DEFAULT_COLOR: "#333",
+ NODE_DEFAULT_BGCOLOR: "#353535",
+ NODE_DEFAULT_BOXCOLOR: "#666",
+ NODE_DEFAULT_SHAPE: "box",
+ NODE_BOX_OUTLINE_COLOR: "#FFF",
+ DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)",
+ DEFAULT_GROUP_FONT: 24,
+ WIDGET_BGCOLOR: "#222",
+ WIDGET_OUTLINE_COLOR: "#666",
+ WIDGET_TEXT_COLOR: "#DDD",
+ WIDGET_SECONDARY_TEXT_COLOR: "#999",
+ LINK_COLOR: "#9A9",
+ EVENT_LINK_COLOR: "#A86",
+ CONNECTING_LINK_COLOR: "#AFA"
+ },
+ comfy_base: {
+ "fg-color": "#fff",
+ "bg-color": "#202020",
+ "comfy-menu-bg": "#353535",
+ "comfy-input-bg": "#222",
+ "input-text": "#ddd",
+ "descrip-text": "#999",
+ "drag-text": "#ccc",
+ "error-text": "#ff4444",
+ "border-color": "#4e4e4e",
+ "tr-even-bg-color": "#222",
+ "tr-odd-bg-color": "#353535",
+ "content-bg": "#4e4e4e",
+ "content-fg": "#fff",
+ "content-hover-bg": "#222",
+ "content-hover-fg": "#fff"
+ }
+ }
+ },
+ light: {
+ id: "light",
+ name: "Light",
+ colors: {
+ node_slot: {
+ CLIP: "#FFA726",
+ // orange
+ CLIP_VISION: "#5C6BC0",
+ // indigo
+ CLIP_VISION_OUTPUT: "#8D6E63",
+ // brown
+ CONDITIONING: "#EF5350",
+ // red
+ CONTROL_NET: "#66BB6A",
+ // green
+ IMAGE: "#42A5F5",
+ // blue
+ LATENT: "#AB47BC",
+ // purple
+ MASK: "#9CCC65",
+ // light green
+ MODEL: "#7E57C2",
+ // deep purple
+ STYLE_MODEL: "#D4E157",
+ // lime
+ VAE: "#FF7043"
+ // deep orange
+ },
+ litegraph_base: {
+ BACKGROUND_IMAGE: "data:image/gif;base64,R0lGODlhZABkALMAAAAAAP///+vr6+rq6ujo6Ofn5+bm5uXl5d3d3f///wAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAkALAAAAABkAGQAAAT/UMhJq7046827HkcoHkYxjgZhnGG6si5LqnIM0/fL4qwwIMAg0CAsEovBIxKhRDaNy2GUOX0KfVFrssrNdpdaqTeKBX+dZ+jYvEaTf+y4W66mC8PUdrE879f9d2mBeoNLfH+IhYBbhIx2jkiHiomQlGKPl4uZe3CaeZifnnijgkESBqipqqusra6vsLGys62SlZO4t7qbuby7CLa+wqGWxL3Gv3jByMOkjc2lw8vOoNSi0czAncXW3Njdx9Pf48/Z4Kbbx+fQ5evZ4u3k1fKR6cn03vHlp7T9/v8A/8Gbp4+gwXoFryXMB2qgwoMMHyKEqA5fxX322FG8tzBcRnMW/zlulPbRncmQGidKjMjyYsOSKEF2FBlJQMCbOHP6c9iSZs+UnGYCdbnSo1CZI5F64kn0p1KnTH02nSoV3dGTV7FFHVqVq1dtWcMmVQZTbNGu72zqXMuW7danVL+6e4t1bEy6MeueBYLXrNO5Ze36jQtWsOG97wIj1vt3St/DjTEORss4nNq2mDP3e7w4r1bFkSET5hy6s2TRlD2/mSxXtSHQhCunXo26NevCpmvD/UU6tuullzULH76q92zdZG/Ltv1a+W+osI/nRmyc+fRi1Xdbh+68+0vv10dH3+77KD/i6IdnX669/frn5Zsjh4/2PXju8+8bzc9/6fj27LFnX11/+IUnXWl7BJfegm79FyB9JOl3oHgSklefgxAC+FmFGpqHIYcCfkhgfCohSKKJVo044YUMttggiBkmp6KFXw1oII24oYhjiDByaKOOHcp3Y5BD/njikSkO+eBREQAAOw==",
+ CLEAR_BACKGROUND_COLOR: "lightgray",
+ NODE_TITLE_COLOR: "#222",
+ NODE_SELECTED_TITLE_COLOR: "#000",
+ NODE_TEXT_SIZE: 14,
+ NODE_TEXT_COLOR: "#444",
+ NODE_SUBTEXT_SIZE: 12,
+ NODE_DEFAULT_COLOR: "#F7F7F7",
+ NODE_DEFAULT_BGCOLOR: "#F5F5F5",
+ NODE_DEFAULT_BOXCOLOR: "#CCC",
+ NODE_DEFAULT_SHAPE: "box",
+ NODE_BOX_OUTLINE_COLOR: "#000",
+ DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.1)",
+ DEFAULT_GROUP_FONT: 24,
+ WIDGET_BGCOLOR: "#D4D4D4",
+ WIDGET_OUTLINE_COLOR: "#999",
+ WIDGET_TEXT_COLOR: "#222",
+ WIDGET_SECONDARY_TEXT_COLOR: "#555",
+ LINK_COLOR: "#4CAF50",
+ EVENT_LINK_COLOR: "#FF9800",
+ CONNECTING_LINK_COLOR: "#2196F3"
+ },
+ comfy_base: {
+ "fg-color": "#222",
+ "bg-color": "#DDD",
+ "comfy-menu-bg": "#F5F5F5",
+ "comfy-input-bg": "#C9C9C9",
+ "input-text": "#222",
+ "descrip-text": "#444",
+ "drag-text": "#555",
+ "error-text": "#F44336",
+ "border-color": "#888",
+ "tr-even-bg-color": "#f9f9f9",
+ "tr-odd-bg-color": "#fff",
+ "content-bg": "#e0e0e0",
+ "content-fg": "#222",
+ "content-hover-bg": "#adadad",
+ "content-hover-fg": "#222"
+ }
+ }
+ },
+ solarized: {
+ id: "solarized",
+ name: "Solarized",
+ colors: {
+ node_slot: {
+ CLIP: "#2AB7CA",
+ // light blue
+ CLIP_VISION: "#6c71c4",
+ // blue violet
+ CLIP_VISION_OUTPUT: "#859900",
+ // olive green
+ CONDITIONING: "#d33682",
+ // magenta
+ CONTROL_NET: "#d1ffd7",
+ // light mint green
+ IMAGE: "#5940bb",
+ // deep blue violet
+ LATENT: "#268bd2",
+ // blue
+ MASK: "#CCC9E7",
+ // light purple-gray
+ MODEL: "#dc322f",
+ // red
+ STYLE_MODEL: "#1a998a",
+ // teal
+ UPSCALE_MODEL: "#054A29",
+ // dark green
+ VAE: "#facfad"
+ // light pink-orange
+ },
+ litegraph_base: {
+ NODE_TITLE_COLOR: "#fdf6e3",
+ // Base3
+ NODE_SELECTED_TITLE_COLOR: "#A9D400",
+ NODE_TEXT_SIZE: 14,
+ NODE_TEXT_COLOR: "#657b83",
+ // Base00
+ NODE_SUBTEXT_SIZE: 12,
+ NODE_DEFAULT_COLOR: "#094656",
+ NODE_DEFAULT_BGCOLOR: "#073642",
+ // Base02
+ NODE_DEFAULT_BOXCOLOR: "#839496",
+ // Base0
+ NODE_DEFAULT_SHAPE: "box",
+ NODE_BOX_OUTLINE_COLOR: "#fdf6e3",
+ // Base3
+ DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)",
+ DEFAULT_GROUP_FONT: 24,
+ WIDGET_BGCOLOR: "#002b36",
+ // Base03
+ WIDGET_OUTLINE_COLOR: "#839496",
+ // Base0
+ WIDGET_TEXT_COLOR: "#fdf6e3",
+ // Base3
+ WIDGET_SECONDARY_TEXT_COLOR: "#93a1a1",
+ // Base1
+ LINK_COLOR: "#2aa198",
+ // Solarized Cyan
+ EVENT_LINK_COLOR: "#268bd2",
+ // Solarized Blue
+ CONNECTING_LINK_COLOR: "#859900"
+ // Solarized Green
+ },
+ comfy_base: {
+ "fg-color": "#fdf6e3",
+ // Base3
+ "bg-color": "#002b36",
+ // Base03
+ "comfy-menu-bg": "#073642",
+ // Base02
+ "comfy-input-bg": "#002b36",
+ // Base03
+ "input-text": "#93a1a1",
+ // Base1
+ "descrip-text": "#586e75",
+ // Base01
+ "drag-text": "#839496",
+ // Base0
+ "error-text": "#dc322f",
+ // Solarized Red
+ "border-color": "#657b83",
+ // Base00
+ "tr-even-bg-color": "#002b36",
+ "tr-odd-bg-color": "#073642",
+ "content-bg": "#657b83",
+ "content-fg": "#fdf6e3",
+ "content-hover-bg": "#002b36",
+ "content-hover-fg": "#fdf6e3"
+ }
+ }
+ },
+ arc: {
+ id: "arc",
+ name: "Arc",
+ colors: {
+ node_slot: {
+ BOOLEAN: "",
+ CLIP: "#eacb8b",
+ CLIP_VISION: "#A8DADC",
+ CLIP_VISION_OUTPUT: "#ad7452",
+ CONDITIONING: "#cf876f",
+ CONTROL_NET: "#00d78d",
+ CONTROL_NET_WEIGHTS: "",
+ FLOAT: "",
+ GLIGEN: "",
+ IMAGE: "#80a1c0",
+ IMAGEUPLOAD: "",
+ INT: "",
+ LATENT: "#b38ead",
+ LATENT_KEYFRAME: "",
+ MASK: "#a3bd8d",
+ MODEL: "#8978a7",
+ SAMPLER: "",
+ SIGMAS: "",
+ STRING: "",
+ STYLE_MODEL: "#C2FFAE",
+ T2I_ADAPTER_WEIGHTS: "",
+ TAESD: "#DCC274",
+ TIMESTEP_KEYFRAME: "",
+ UPSCALE_MODEL: "",
+ VAE: "#be616b"
+ },
+ litegraph_base: {
+ BACKGROUND_IMAGE: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcklEQVR4nO3YMUoDARgF4RfxBqZI6/0vZqFn0MYtrLIQMFN8U6V4LAtD+Jm9XG/v30OGl2e/AP7yevz4+vx45nvgF/+QGITEICQGITEIiUFIjNNC3q43u3/YnRJyPOzeQ+0e220nhRzReC8e7R7bbdvl+Jal1Bs46jEIiUFIDEJiEBKDkBhKPbZT6qHdptRTu02p53DUYxASg5AYhMQgJAYhMZR6bKfUQ7tNqad2m1LP4ajHICQGITEIiUFIDEJiKPXYTqmHdptST+02pZ7DUY9BSAxCYhASg5AYhMRQ6rGdUg/tNqWe2m1KPYejHoOQGITEICQGITEIiaHUYzulHtptSj2125R6Dkc9BiExCIlBSAxCYhASQ6nHdko9tNuUemq3KfUcjnoMQmIQEoOQGITEICSGUo/tlHpotyn11G5T6jkc9RiExCAkBiExCIlBSAylHtsp9dBuU+qp3abUczjqMQiJQUgMQmIQEoOQGITE+AHFISNQrFTGuwAAAABJRU5ErkJggg==",
+ CLEAR_BACKGROUND_COLOR: "#2b2f38",
+ NODE_TITLE_COLOR: "#b2b7bd",
+ NODE_SELECTED_TITLE_COLOR: "#FFF",
+ NODE_TEXT_SIZE: 14,
+ NODE_TEXT_COLOR: "#AAA",
+ NODE_SUBTEXT_SIZE: 12,
+ NODE_DEFAULT_COLOR: "#2b2f38",
+ NODE_DEFAULT_BGCOLOR: "#242730",
+ NODE_DEFAULT_BOXCOLOR: "#6e7581",
+ NODE_DEFAULT_SHAPE: "box",
+ NODE_BOX_OUTLINE_COLOR: "#FFF",
+ DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)",
+ DEFAULT_GROUP_FONT: 22,
+ WIDGET_BGCOLOR: "#2b2f38",
+ WIDGET_OUTLINE_COLOR: "#6e7581",
+ WIDGET_TEXT_COLOR: "#DDD",
+ WIDGET_SECONDARY_TEXT_COLOR: "#b2b7bd",
+ LINK_COLOR: "#9A9",
+ EVENT_LINK_COLOR: "#A86",
+ CONNECTING_LINK_COLOR: "#AFA"
+ },
+ comfy_base: {
+ "fg-color": "#fff",
+ "bg-color": "#2b2f38",
+ "comfy-menu-bg": "#242730",
+ "comfy-input-bg": "#2b2f38",
+ "input-text": "#ddd",
+ "descrip-text": "#b2b7bd",
+ "drag-text": "#ccc",
+ "error-text": "#ff4444",
+ "border-color": "#6e7581",
+ "tr-even-bg-color": "#2b2f38",
+ "tr-odd-bg-color": "#242730",
+ "content-bg": "#6e7581",
+ "content-fg": "#fff",
+ "content-hover-bg": "#2b2f38",
+ "content-hover-fg": "#fff"
+ }
+ }
+ },
+ nord: {
+ id: "nord",
+ name: "Nord",
+ colors: {
+ node_slot: {
+ BOOLEAN: "",
+ CLIP: "#eacb8b",
+ CLIP_VISION: "#A8DADC",
+ CLIP_VISION_OUTPUT: "#ad7452",
+ CONDITIONING: "#cf876f",
+ CONTROL_NET: "#00d78d",
+ CONTROL_NET_WEIGHTS: "",
+ FLOAT: "",
+ GLIGEN: "",
+ IMAGE: "#80a1c0",
+ IMAGEUPLOAD: "",
+ INT: "",
+ LATENT: "#b38ead",
+ LATENT_KEYFRAME: "",
+ MASK: "#a3bd8d",
+ MODEL: "#8978a7",
+ SAMPLER: "",
+ SIGMAS: "",
+ STRING: "",
+ STYLE_MODEL: "#C2FFAE",
+ T2I_ADAPTER_WEIGHTS: "",
+ TAESD: "#DCC274",
+ TIMESTEP_KEYFRAME: "",
+ UPSCALE_MODEL: "",
+ VAE: "#be616b"
+ },
+ litegraph_base: {
+ BACKGROUND_IMAGE: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFu2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMy0xMS0xM1QwMDoxODowMiswMTowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjMtMTEtMTVUMDE6MjA6NDUrMDE6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjMtMTEtMTVUMDE6MjA6NDUrMDE6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjUwNDFhMmZjLTEzNzQtMTk0ZC1hZWY4LTYxMzM1MTVmNjUwMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyMzFiMTBiMC1iNGZiLTAyNGUtYjEyZS0zMDUzMDNjZDA3YzgiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyMzFiMTBiMC1iNGZiLTAyNGUtYjEyZS0zMDUzMDNjZDA3YzgiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjIzMWIxMGIwLWI0ZmItMDI0ZS1iMTJlLTMwNTMwM2NkMDdjOCIgc3RFdnQ6d2hlbj0iMjAyMy0xMS0xM1QwMDoxODowMiswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo1MDQxYTJmYy0xMzc0LTE5NGQtYWVmOC02MTMzNTE1ZjY1MDAiIHN0RXZ0OndoZW49IjIwMjMtMTEtMTVUMDE6MjA6NDUrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4xIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz73jWg/AAAAyUlEQVR42u3WKwoAIBRFQRdiMb1idv9Lsxn9gEFw4Dbb8JCTojbbXEJwjJVL2HKwYMGCBQuWLbDmjr+9zrBGjHl1WVcvy2DBggULFizTWQpewSt4HzwsgwULFiwFr7MUvMtS8D54WLBgGSxYCl7BK3iXZbBgwYIFC5bpLAWv4BW8Dx6WwYIFC5aC11kK3mUpeB88LFiwDBYsBa/gFbzLMliwYMGCBct0loJX8AreBw/LYMGCBUvB6ywF77IUvA8eFixYBgsWrNfWAZPltufdad+1AAAAAElFTkSuQmCC",
+ CLEAR_BACKGROUND_COLOR: "#212732",
+ NODE_TITLE_COLOR: "#999",
+ NODE_SELECTED_TITLE_COLOR: "#e5eaf0",
+ NODE_TEXT_SIZE: 14,
+ NODE_TEXT_COLOR: "#bcc2c8",
+ NODE_SUBTEXT_SIZE: 12,
+ NODE_DEFAULT_COLOR: "#2e3440",
+ NODE_DEFAULT_BGCOLOR: "#161b22",
+ NODE_DEFAULT_BOXCOLOR: "#545d70",
+ NODE_DEFAULT_SHAPE: "box",
+ NODE_BOX_OUTLINE_COLOR: "#e5eaf0",
+ DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)",
+ DEFAULT_GROUP_FONT: 24,
+ WIDGET_BGCOLOR: "#2e3440",
+ WIDGET_OUTLINE_COLOR: "#545d70",
+ WIDGET_TEXT_COLOR: "#bcc2c8",
+ WIDGET_SECONDARY_TEXT_COLOR: "#999",
+ LINK_COLOR: "#9A9",
+ EVENT_LINK_COLOR: "#A86",
+ CONNECTING_LINK_COLOR: "#AFA"
+ },
+ comfy_base: {
+ "fg-color": "#e5eaf0",
+ "bg-color": "#2e3440",
+ "comfy-menu-bg": "#161b22",
+ "comfy-input-bg": "#2e3440",
+ "input-text": "#bcc2c8",
+ "descrip-text": "#999",
+ "drag-text": "#ccc",
+ "error-text": "#ff4444",
+ "border-color": "#545d70",
+ "tr-even-bg-color": "#2e3440",
+ "tr-odd-bg-color": "#161b22",
+ "content-bg": "#545d70",
+ "content-fg": "#e5eaf0",
+ "content-hover-bg": "#2e3440",
+ "content-hover-fg": "#e5eaf0"
+ }
+ }
+ },
+ github: {
+ id: "github",
+ name: "Github",
+ colors: {
+ node_slot: {
+ BOOLEAN: "",
+ CLIP: "#eacb8b",
+ CLIP_VISION: "#A8DADC",
+ CLIP_VISION_OUTPUT: "#ad7452",
+ CONDITIONING: "#cf876f",
+ CONTROL_NET: "#00d78d",
+ CONTROL_NET_WEIGHTS: "",
+ FLOAT: "",
+ GLIGEN: "",
+ IMAGE: "#80a1c0",
+ IMAGEUPLOAD: "",
+ INT: "",
+ LATENT: "#b38ead",
+ LATENT_KEYFRAME: "",
+ MASK: "#a3bd8d",
+ MODEL: "#8978a7",
+ SAMPLER: "",
+ SIGMAS: "",
+ STRING: "",
+ STYLE_MODEL: "#C2FFAE",
+ T2I_ADAPTER_WEIGHTS: "",
+ TAESD: "#DCC274",
+ TIMESTEP_KEYFRAME: "",
+ UPSCALE_MODEL: "",
+ VAE: "#be616b"
+ },
+ litegraph_base: {
+ BACKGROUND_IMAGE: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMy0xMS0xM1QwMDoxODowMiswMTowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmIyYzRhNjA5LWJmYTctYTg0MC1iOGFlLTk3MzE2ZjM1ZGIyNyIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjk0ZmNlZGU4LTE1MTctZmQ0MC04ZGU3LWYzOTgxM2E3ODk5ZiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjIzMWIxMGIwLWI0ZmItMDI0ZS1iMTJlLTMwNTMwM2NkMDdjOCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MjMxYjEwYjAtYjRmYi0wMjRlLWIxMmUtMzA1MzAzY2QwN2M4IiBzdEV2dDp3aGVuPSIyMDIzLTExLTEzVDAwOjE4OjAyKzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjQ4OWY1NzlmLTJkNjUtZWQ0Zi04OTg0LTA4NGE2MGE1ZTMzNSIgc3RFdnQ6d2hlbj0iMjAyMy0xMS0xNVQwMjowNDo1OSswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpiMmM0YTYwOS1iZmE3LWE4NDAtYjhhZS05NzMxNmYzNWRiMjciIHN0RXZ0OndoZW49IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4xIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4OTe6GAAAAx0lEQVR42u3WMQoAIQxFwRzJys77X8vSLiRgITif7bYbgrwYc/mKXyBoY4VVBgsWLFiwYFmOlTv+9jfDOjHmr8u6eVkGCxYsWLBgmc5S8ApewXvgYRksWLBgKXidpeBdloL3wMOCBctgwVLwCl7BuyyDBQsWLFiwTGcpeAWv4D3wsAwWLFiwFLzOUvAuS8F74GHBgmWwYCl4Ba/gXZbBggULFixYprMUvIJX8B54WAYLFixYCl5nKXiXpeA98LBgwTJYsGC9tg1o8f4TTtqzNQAAAABJRU5ErkJggg==",
+ CLEAR_BACKGROUND_COLOR: "#040506",
+ NODE_TITLE_COLOR: "#999",
+ NODE_SELECTED_TITLE_COLOR: "#e5eaf0",
+ NODE_TEXT_SIZE: 14,
+ NODE_TEXT_COLOR: "#bcc2c8",
+ NODE_SUBTEXT_SIZE: 12,
+ NODE_DEFAULT_COLOR: "#161b22",
+ NODE_DEFAULT_BGCOLOR: "#13171d",
+ NODE_DEFAULT_BOXCOLOR: "#30363d",
+ NODE_DEFAULT_SHAPE: "box",
+ NODE_BOX_OUTLINE_COLOR: "#e5eaf0",
+ DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)",
+ DEFAULT_GROUP_FONT: 24,
+ WIDGET_BGCOLOR: "#161b22",
+ WIDGET_OUTLINE_COLOR: "#30363d",
+ WIDGET_TEXT_COLOR: "#bcc2c8",
+ WIDGET_SECONDARY_TEXT_COLOR: "#999",
+ LINK_COLOR: "#9A9",
+ EVENT_LINK_COLOR: "#A86",
+ CONNECTING_LINK_COLOR: "#AFA"
+ },
+ comfy_base: {
+ "fg-color": "#e5eaf0",
+ "bg-color": "#161b22",
+ "comfy-menu-bg": "#13171d",
+ "comfy-input-bg": "#161b22",
+ "input-text": "#bcc2c8",
+ "descrip-text": "#999",
+ "drag-text": "#ccc",
+ "error-text": "#ff4444",
+ "border-color": "#30363d",
+ "tr-even-bg-color": "#161b22",
+ "tr-odd-bg-color": "#13171d",
+ "content-bg": "#30363d",
+ "content-fg": "#e5eaf0",
+ "content-hover-bg": "#161b22",
+ "content-hover-fg": "#e5eaf0"
+ }
+ }
+ }
+};
+const id$4 = "Comfy.ColorPalette";
+const idCustomColorPalettes = "Comfy.CustomColorPalettes";
+const defaultColorPaletteId = "dark";
+const els = {
+ select: null
+};
+app.registerExtension({
+ name: id$4,
+ init() {
+ LGraphCanvas.prototype.updateBackground = function(image, clearBackgroundColor) {
+ this._bg_img = new Image();
+ this._bg_img.name = image;
+ this._bg_img.src = image;
+ this._bg_img.onload = () => {
+ this.draw(true, true);
+ };
+ this.background_image = image;
+ this.clear_background = true;
+ this.clear_background_color = clearBackgroundColor;
+ this._pattern = null;
+ };
+ },
+ addCustomNodeDefs(node_defs) {
+ const sortObjectKeys = /* @__PURE__ */ __name((unordered) => {
+ return Object.keys(unordered).sort().reduce((obj, key) => {
+ obj[key] = unordered[key];
+ return obj;
+ }, {});
+ }, "sortObjectKeys");
+ function getSlotTypes() {
+ var types = [];
+ const defs = node_defs;
+ for (const nodeId in defs) {
+ const nodeData = defs[nodeId];
+ var inputs = nodeData["input"]["required"];
+ if (nodeData["input"]["optional"] !== void 0) {
+ inputs = Object.assign(
+ {},
+ nodeData["input"]["required"],
+ nodeData["input"]["optional"]
+ );
+ }
+ for (const inputName in inputs) {
+ const inputData = inputs[inputName];
+ const type = inputData[0];
+ if (!Array.isArray(type)) {
+ types.push(type);
+ }
+ }
+ for (const o in nodeData["output"]) {
+ const output = nodeData["output"][o];
+ types.push(output);
+ }
+ }
+ return types;
+ }
+ __name(getSlotTypes, "getSlotTypes");
+ function completeColorPalette(colorPalette) {
+ var types = getSlotTypes();
+ for (const type of types) {
+ if (!colorPalette.colors.node_slot[type]) {
+ colorPalette.colors.node_slot[type] = "";
+ }
+ }
+ colorPalette.colors.node_slot = sortObjectKeys(
+ colorPalette.colors.node_slot
+ );
+ return colorPalette;
+ }
+ __name(completeColorPalette, "completeColorPalette");
+ const getColorPaletteTemplate = /* @__PURE__ */ __name(() => __async(this, null, function* () {
+ let colorPalette = {
+ id: "my_color_palette_unique_id",
+ name: "My Color Palette",
+ colors: {
+ node_slot: {},
+ litegraph_base: {},
+ comfy_base: {}
+ }
+ };
+ const defaultColorPalette = colorPalettes[defaultColorPaletteId];
+ for (const key in defaultColorPalette.colors.litegraph_base) {
+ if (!colorPalette.colors.litegraph_base[key]) {
+ colorPalette.colors.litegraph_base[key] = "";
+ }
+ }
+ for (const key in defaultColorPalette.colors.comfy_base) {
+ if (!colorPalette.colors.comfy_base[key]) {
+ colorPalette.colors.comfy_base[key] = "";
+ }
+ }
+ return completeColorPalette(colorPalette);
+ }), "getColorPaletteTemplate");
+ const getCustomColorPalettes = /* @__PURE__ */ __name(() => {
+ return app.ui.settings.getSettingValue(idCustomColorPalettes, {});
+ }, "getCustomColorPalettes");
+ const setCustomColorPalettes = /* @__PURE__ */ __name((customColorPalettes) => {
+ return app.ui.settings.setSettingValue(
+ idCustomColorPalettes,
+ customColorPalettes
+ );
+ }, "setCustomColorPalettes");
+ const addCustomColorPalette = /* @__PURE__ */ __name((colorPalette) => __async(this, null, function* () {
+ if (typeof colorPalette !== "object") {
+ alert("Invalid color palette.");
+ return;
+ }
+ if (!colorPalette.id) {
+ alert("Color palette missing id.");
+ return;
+ }
+ if (!colorPalette.name) {
+ alert("Color palette missing name.");
+ return;
+ }
+ if (!colorPalette.colors) {
+ alert("Color palette missing colors.");
+ return;
+ }
+ if (colorPalette.colors.node_slot && typeof colorPalette.colors.node_slot !== "object") {
+ alert("Invalid color palette colors.node_slot.");
+ return;
+ }
+ const customColorPalettes = getCustomColorPalettes();
+ customColorPalettes[colorPalette.id] = colorPalette;
+ setCustomColorPalettes(customColorPalettes);
+ for (const option of els.select.childNodes) {
+ if (option.value === "custom_" + colorPalette.id) {
+ els.select.removeChild(option);
+ }
+ }
+ els.select.append(
+ $el("option", {
+ textContent: colorPalette.name + " (custom)",
+ value: "custom_" + colorPalette.id,
+ selected: true
+ })
+ );
+ setColorPalette("custom_" + colorPalette.id);
+ yield loadColorPalette(colorPalette);
+ }), "addCustomColorPalette");
+ const deleteCustomColorPalette = /* @__PURE__ */ __name((colorPaletteId) => __async(this, null, function* () {
+ const customColorPalettes = getCustomColorPalettes();
+ delete customColorPalettes[colorPaletteId];
+ setCustomColorPalettes(customColorPalettes);
+ for (const opt of els.select.childNodes) {
+ const option = opt;
+ if (option.value === defaultColorPaletteId) {
+ option.selected = true;
+ }
+ if (option.value === "custom_" + colorPaletteId) {
+ els.select.removeChild(option);
+ }
+ }
+ setColorPalette(defaultColorPaletteId);
+ yield loadColorPalette(getColorPalette());
+ }), "deleteCustomColorPalette");
+ const loadColorPalette = /* @__PURE__ */ __name((colorPalette) => __async(this, null, function* () {
+ colorPalette = yield completeColorPalette(colorPalette);
+ if (colorPalette.colors) {
+ if (colorPalette.colors.node_slot) {
+ Object.assign(
+ // @ts-expect-error
+ app.canvas.default_connection_color_byType,
+ colorPalette.colors.node_slot
+ );
+ Object.assign(
+ LGraphCanvas.link_type_colors,
+ colorPalette.colors.node_slot
+ );
+ }
+ if (colorPalette.colors.litegraph_base) {
+ app.canvas.node_title_color = colorPalette.colors.litegraph_base.NODE_TITLE_COLOR;
+ app.canvas.default_link_color = colorPalette.colors.litegraph_base.LINK_COLOR;
+ for (const key in colorPalette.colors.litegraph_base) {
+ if (colorPalette.colors.litegraph_base.hasOwnProperty(key) && LiteGraph.hasOwnProperty(key)) {
+ LiteGraph[key] = colorPalette.colors.litegraph_base[key];
+ }
+ }
+ }
+ if (colorPalette.colors.comfy_base) {
+ const rootStyle = document.documentElement.style;
+ for (const key in colorPalette.colors.comfy_base) {
+ rootStyle.setProperty(
+ "--" + key,
+ colorPalette.colors.comfy_base[key]
+ );
+ }
+ }
+ app.canvas.draw(true, true);
+ }
+ }), "loadColorPalette");
+ const getColorPalette = /* @__PURE__ */ __name((colorPaletteId) => {
+ if (!colorPaletteId) {
+ colorPaletteId = app.ui.settings.getSettingValue(
+ id$4,
+ defaultColorPaletteId
+ );
+ }
+ if (colorPaletteId.startsWith("custom_")) {
+ colorPaletteId = colorPaletteId.substr(7);
+ let customColorPalettes = getCustomColorPalettes();
+ if (customColorPalettes[colorPaletteId]) {
+ return customColorPalettes[colorPaletteId];
+ }
+ }
+ return colorPalettes[colorPaletteId];
+ }, "getColorPalette");
+ const setColorPalette = /* @__PURE__ */ __name((colorPaletteId) => {
+ app.ui.settings.setSettingValue(id$4, colorPaletteId);
+ }, "setColorPalette");
+ const fileInput = $el("input", {
+ type: "file",
+ accept: ".json",
+ style: { display: "none" },
+ parent: document.body,
+ onchange: /* @__PURE__ */ __name(() => {
+ const file2 = fileInput.files[0];
+ if (file2.type === "application/json" || file2.name.endsWith(".json")) {
+ const reader = new FileReader();
+ reader.onload = () => __async(this, null, function* () {
+ yield addCustomColorPalette(JSON.parse(reader.result));
+ });
+ reader.readAsText(file2);
+ }
+ }, "onchange")
+ });
+ app.ui.settings.addSetting({
+ id: id$4,
+ name: "Color Palette",
+ type: /* @__PURE__ */ __name((name, setter, value) => {
+ const options = [
+ ...Object.values(colorPalettes).map(
+ (c) => $el("option", {
+ textContent: c.name,
+ value: c.id,
+ selected: c.id === value
+ })
+ ),
+ ...Object.values(getCustomColorPalettes()).map(
+ (c) => $el("option", {
+ textContent: `${c.name} (custom)`,
+ value: `custom_${c.id}`,
+ selected: `custom_${c.id}` === value
+ })
+ )
+ ];
+ els.select = $el(
+ "select",
+ {
+ style: {
+ marginBottom: "0.15rem",
+ width: "100%"
+ },
+ onchange: /* @__PURE__ */ __name((e) => {
+ setter(e.target.value);
+ }, "onchange")
+ },
+ options
+ );
+ return $el("tr", [
+ $el("td", [
+ $el("label", {
+ for: id$4.replaceAll(".", "-"),
+ textContent: "Color palette"
+ })
+ ]),
+ $el("td", [
+ els.select,
+ $el(
+ "div",
+ {
+ style: {
+ display: "grid",
+ gap: "4px",
+ gridAutoFlow: "column"
+ }
+ },
+ [
+ $el("input", {
+ type: "button",
+ value: "Export",
+ onclick: /* @__PURE__ */ __name(() => __async(this, null, function* () {
+ const colorPaletteId = app.ui.settings.getSettingValue(
+ id$4,
+ defaultColorPaletteId
+ );
+ const colorPalette = yield completeColorPalette(
+ getColorPalette(colorPaletteId)
+ );
+ const json = JSON.stringify(colorPalette, null, 2);
+ const blob = new Blob([json], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+ const a = $el("a", {
+ href: url,
+ download: colorPaletteId + ".json",
+ style: { display: "none" },
+ parent: document.body
+ });
+ a.click();
+ setTimeout(function() {
+ a.remove();
+ window.URL.revokeObjectURL(url);
+ }, 0);
+ }), "onclick")
+ }),
+ $el("input", {
+ type: "button",
+ value: "Import",
+ onclick: /* @__PURE__ */ __name(() => {
+ fileInput.click();
+ }, "onclick")
+ }),
+ $el("input", {
+ type: "button",
+ value: "Template",
+ onclick: /* @__PURE__ */ __name(() => __async(this, null, function* () {
+ const colorPalette = yield getColorPaletteTemplate();
+ const json = JSON.stringify(colorPalette, null, 2);
+ const blob = new Blob([json], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+ const a = $el("a", {
+ href: url,
+ download: "color_palette.json",
+ style: { display: "none" },
+ parent: document.body
+ });
+ a.click();
+ setTimeout(function() {
+ a.remove();
+ window.URL.revokeObjectURL(url);
+ }, 0);
+ }), "onclick")
+ }),
+ $el("input", {
+ type: "button",
+ value: "Delete",
+ onclick: /* @__PURE__ */ __name(() => __async(this, null, function* () {
+ let colorPaletteId = app.ui.settings.getSettingValue(
+ id$4,
+ defaultColorPaletteId
+ );
+ if (colorPalettes[colorPaletteId]) {
+ alert("You cannot delete a built-in color palette.");
+ return;
+ }
+ if (colorPaletteId.startsWith("custom_")) {
+ colorPaletteId = colorPaletteId.substr(7);
+ }
+ yield deleteCustomColorPalette(colorPaletteId);
+ }), "onclick")
+ })
+ ]
+ )
+ ])
+ ]);
+ }, "type"),
+ defaultValue: defaultColorPaletteId,
+ onChange(value) {
+ return __async(this, null, function* () {
+ if (!value) {
+ return;
+ }
+ let palette = colorPalettes[value];
+ if (palette) {
+ yield loadColorPalette(palette);
+ } else if (value.startsWith("custom_")) {
+ value = value.substr(7);
+ let customColorPalettes = getCustomColorPalettes();
+ if (customColorPalettes[value]) {
+ palette = customColorPalettes[value];
+ yield loadColorPalette(customColorPalettes[value]);
+ }
+ }
+ let { BACKGROUND_IMAGE, CLEAR_BACKGROUND_COLOR } = palette.colors.litegraph_base;
+ if (BACKGROUND_IMAGE === void 0 || CLEAR_BACKGROUND_COLOR === void 0) {
+ const base = colorPalettes["dark"].colors.litegraph_base;
+ BACKGROUND_IMAGE = base.BACKGROUND_IMAGE;
+ CLEAR_BACKGROUND_COLOR = base.CLEAR_BACKGROUND_COLOR;
+ }
+ app.canvas.updateBackground(BACKGROUND_IMAGE, CLEAR_BACKGROUND_COLOR);
+ });
+ }
+ });
+ }
+});
+const ext$2 = {
+ name: "Comfy.ContextMenuFilter",
+ init() {
+ const ctxMenu = LiteGraph.ContextMenu;
+ LiteGraph.ContextMenu = function(values, options) {
+ const ctx = ctxMenu.call(this, values, options);
+ if ((options == null ? void 0 : options.className) === "dark" && (values == null ? void 0 : values.length) > 10) {
+ const filter = document.createElement("input");
+ filter.classList.add("comfy-context-menu-filter");
+ filter.placeholder = "Filter list";
+ this.root.prepend(filter);
+ const items = Array.from(
+ this.root.querySelectorAll(".litemenu-entry")
+ );
+ let displayedItems = [...items];
+ let itemCount = displayedItems.length;
+ requestAnimationFrame(() => {
+ var _a, _b;
+ const currentNode = LGraphCanvas.active_canvas.current_node;
+ const clickedComboValue = (_b = (_a = currentNode.widgets) == null ? void 0 : _a.filter(
+ (w) => w.type === "combo" && w.options.values.length === values.length
+ ).find(
+ (w) => w.options.values.every((v, i) => v === values[i])
+ )) == null ? void 0 : _b.value;
+ let selectedIndex = clickedComboValue ? values.findIndex((v) => v === clickedComboValue) : 0;
+ if (selectedIndex < 0) {
+ selectedIndex = 0;
+ }
+ let selectedItem = displayedItems[selectedIndex];
+ updateSelected();
+ function updateSelected() {
+ selectedItem == null ? void 0 : selectedItem.style.setProperty("background-color", "");
+ selectedItem == null ? void 0 : selectedItem.style.setProperty("color", "");
+ selectedItem = displayedItems[selectedIndex];
+ selectedItem == null ? void 0 : selectedItem.style.setProperty(
+ "background-color",
+ "#ccc",
+ "important"
+ );
+ selectedItem == null ? void 0 : selectedItem.style.setProperty("color", "#000", "important");
+ }
+ __name(updateSelected, "updateSelected");
+ const positionList = /* @__PURE__ */ __name(() => {
+ const rect = this.root.getBoundingClientRect();
+ if (rect.top < 0) {
+ const scale = 1 - this.root.getBoundingClientRect().height / this.root.clientHeight;
+ const shift = this.root.clientHeight * scale / 2;
+ this.root.style.top = -shift + "px";
+ }
+ }, "positionList");
+ filter.addEventListener("keydown", (event) => {
+ switch (event.key) {
+ case "ArrowUp":
+ event.preventDefault();
+ if (selectedIndex === 0) {
+ selectedIndex = itemCount - 1;
+ } else {
+ selectedIndex--;
+ }
+ updateSelected();
+ break;
+ case "ArrowRight":
+ event.preventDefault();
+ selectedIndex = itemCount - 1;
+ updateSelected();
+ break;
+ case "ArrowDown":
+ event.preventDefault();
+ if (selectedIndex === itemCount - 1) {
+ selectedIndex = 0;
+ } else {
+ selectedIndex++;
+ }
+ updateSelected();
+ break;
+ case "ArrowLeft":
+ event.preventDefault();
+ selectedIndex = 0;
+ updateSelected();
+ break;
+ case "Enter":
+ selectedItem == null ? void 0 : selectedItem.click();
+ break;
+ case "Escape":
+ this.close();
+ break;
+ }
+ });
+ filter.addEventListener("input", () => {
+ const term = filter.value.toLocaleLowerCase();
+ displayedItems = items.filter((item) => {
+ const isVisible = !term || item.textContent.toLocaleLowerCase().includes(term);
+ item.style.display = isVisible ? "block" : "none";
+ return isVisible;
+ });
+ selectedIndex = 0;
+ if (displayedItems.includes(selectedItem)) {
+ selectedIndex = displayedItems.findIndex(
+ (d) => d === selectedItem
+ );
+ }
+ itemCount = displayedItems.length;
+ updateSelected();
+ if (options.event) {
+ let top = options.event.clientY - 10;
+ const bodyRect = document.body.getBoundingClientRect();
+ const rootRect = this.root.getBoundingClientRect();
+ if (bodyRect.height && top > bodyRect.height - rootRect.height - 10) {
+ top = Math.max(0, bodyRect.height - rootRect.height - 10);
+ }
+ this.root.style.top = top + "px";
+ positionList();
+ }
+ });
+ requestAnimationFrame(() => {
+ filter.focus();
+ positionList();
+ });
+ });
+ }
+ return ctx;
+ };
+ LiteGraph.ContextMenu.prototype = ctxMenu.prototype;
+ }
+};
+app.registerExtension(ext$2);
+function stripComments(str) {
+ return str.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, "");
+}
+__name(stripComments, "stripComments");
+app.registerExtension({
+ name: "Comfy.DynamicPrompts",
+ nodeCreated(node) {
+ if (node.widgets) {
+ const widgets = node.widgets.filter((n) => n.dynamicPrompts);
+ for (const widget of widgets) {
+ widget.serializeValue = (workflowNode, widgetIndex) => {
+ let prompt2 = stripComments(widget.value);
+ while (prompt2.replace("\\{", "").includes("{") && prompt2.replace("\\}", "").includes("}")) {
+ const startIndex = prompt2.replace("\\{", "00").indexOf("{");
+ const endIndex = prompt2.replace("\\}", "00").indexOf("}");
+ const optionsString = prompt2.substring(startIndex + 1, endIndex);
+ const options = optionsString.split("|");
+ const randomIndex = Math.floor(Math.random() * options.length);
+ const randomOption = options[randomIndex];
+ prompt2 = prompt2.substring(0, startIndex) + randomOption + prompt2.substring(endIndex + 1);
+ }
+ if (workflowNode == null ? void 0 : workflowNode.widgets_values)
+ workflowNode.widgets_values[widgetIndex] = prompt2;
+ return prompt2;
+ };
+ }
+ }
+ }
+});
+app.registerExtension({
+ name: "Comfy.EditAttention",
+ init() {
+ const editAttentionDelta = app.ui.settings.addSetting({
+ id: "Comfy.EditAttention.Delta",
+ name: "Ctrl+up/down precision",
+ type: "slider",
+ attrs: {
+ min: 0.01,
+ max: 0.5,
+ step: 0.01
+ },
+ defaultValue: 0.05
+ });
+ function incrementWeight(weight, delta) {
+ const floatWeight = parseFloat(weight);
+ if (isNaN(floatWeight)) return weight;
+ const newWeight = floatWeight + delta;
+ if (newWeight < 0) return "0";
+ return String(Number(newWeight.toFixed(10)));
+ }
+ __name(incrementWeight, "incrementWeight");
+ function findNearestEnclosure(text, cursorPos) {
+ let start = cursorPos, end = cursorPos;
+ let openCount = 0, closeCount = 0;
+ while (start >= 0) {
+ start--;
+ if (text[start] === "(" && openCount === closeCount) break;
+ if (text[start] === "(") openCount++;
+ if (text[start] === ")") closeCount++;
+ }
+ if (start < 0) return false;
+ openCount = 0;
+ closeCount = 0;
+ while (end < text.length) {
+ if (text[end] === ")" && openCount === closeCount) break;
+ if (text[end] === "(") openCount++;
+ if (text[end] === ")") closeCount++;
+ end++;
+ }
+ if (end === text.length) return false;
+ return { start: start + 1, end };
+ }
+ __name(findNearestEnclosure, "findNearestEnclosure");
+ function addWeightToParentheses(text) {
+ const parenRegex = /^\((.*)\)$/;
+ const parenMatch = text.match(parenRegex);
+ const floatRegex = /:([+-]?(\d*\.)?\d+([eE][+-]?\d+)?)/;
+ const floatMatch = text.match(floatRegex);
+ if (parenMatch && !floatMatch) {
+ return `(${parenMatch[1]}:1.0)`;
+ } else {
+ return text;
+ }
+ }
+ __name(addWeightToParentheses, "addWeightToParentheses");
+ function editAttention(event) {
+ const inputField = event.composedPath()[0];
+ const delta = parseFloat(editAttentionDelta.value);
+ if (inputField.tagName !== "TEXTAREA") return;
+ if (!(event.key === "ArrowUp" || event.key === "ArrowDown")) return;
+ if (!event.ctrlKey && !event.metaKey) return;
+ event.preventDefault();
+ let start = inputField.selectionStart;
+ let end = inputField.selectionEnd;
+ let selectedText = inputField.value.substring(start, end);
+ if (!selectedText) {
+ const nearestEnclosure = findNearestEnclosure(inputField.value, start);
+ if (nearestEnclosure) {
+ start = nearestEnclosure.start;
+ end = nearestEnclosure.end;
+ selectedText = inputField.value.substring(start, end);
+ } else {
+ const delimiters = " .,\\/!?%^*;:{}=-_`~()\r\n ";
+ while (!delimiters.includes(inputField.value[start - 1]) && start > 0) {
+ start--;
+ }
+ while (!delimiters.includes(inputField.value[end]) && end < inputField.value.length) {
+ end++;
+ }
+ selectedText = inputField.value.substring(start, end);
+ if (!selectedText) return;
+ }
+ }
+ if (selectedText[selectedText.length - 1] === " ") {
+ selectedText = selectedText.substring(0, selectedText.length - 1);
+ end -= 1;
+ }
+ if (inputField.value[start - 1] === "(" && inputField.value[end] === ")") {
+ start -= 1;
+ end += 1;
+ selectedText = inputField.value.substring(start, end);
+ }
+ if (selectedText[0] !== "(" || selectedText[selectedText.length - 1] !== ")") {
+ selectedText = `(${selectedText})`;
+ }
+ selectedText = addWeightToParentheses(selectedText);
+ const weightDelta = event.key === "ArrowUp" ? delta : -delta;
+ const updatedText = selectedText.replace(
+ /\((.*):(\d+(?:\.\d+)?)\)/,
+ (match, text, weight) => {
+ weight = incrementWeight(weight, weightDelta);
+ if (weight == 1) {
+ return text;
+ } else {
+ return `(${text}:${weight})`;
+ }
+ }
+ );
+ inputField.setRangeText(updatedText, start, end, "select");
+ }
+ __name(editAttention, "editAttention");
+ window.addEventListener("keydown", editAttention);
+ }
+});
+const CONVERTED_TYPE = "converted-widget";
+const VALID_TYPES = ["STRING", "combo", "number", "toggle", "BOOLEAN"];
+const CONFIG = Symbol();
+const GET_CONFIG = Symbol();
+const TARGET = Symbol();
+const replacePropertyName = "Run widget replace on values";
+const _PrimitiveNode = class _PrimitiveNode {
+ constructor() {
+ __privateAdd(this, _PrimitiveNode_instances);
+ __publicField(this, "controlValues");
+ __publicField(this, "lastType");
+ this.addOutput("connect to widget input", "*");
+ this.serialize_widgets = true;
+ this.isVirtualNode = true;
+ if (!this.properties || !(replacePropertyName in this.properties)) {
+ this.addProperty(replacePropertyName, false, "boolean");
+ }
+ }
+ applyToGraph(extraLinks = []) {
+ var _a, _b;
+ if (!((_a = this.outputs[0].links) == null ? void 0 : _a.length)) return;
+ function get_links(node) {
+ let links2 = [];
+ for (const l of node.outputs[0].links) {
+ const linkInfo = app.graph.links[l];
+ const n = node.graph.getNodeById(linkInfo.target_id);
+ if (n.type == "Reroute") {
+ links2 = links2.concat(get_links(n));
+ } else {
+ links2.push(l);
+ }
+ }
+ return links2;
+ }
+ __name(get_links, "get_links");
+ let links = [
+ ...get_links(this).map((l) => app.graph.links[l]),
+ ...extraLinks
+ ];
+ let v = (_b = this.widgets) == null ? void 0 : _b[0].value;
+ if (v && this.properties[replacePropertyName]) {
+ v = applyTextReplacements(app, v);
+ }
+ for (const linkInfo of links) {
+ const node = this.graph.getNodeById(linkInfo.target_id);
+ const input = node.inputs[linkInfo.target_slot];
+ let widget;
+ if (input.widget[TARGET]) {
+ widget = input.widget[TARGET];
+ } else {
+ const widgetName = input.widget.name;
+ if (widgetName) {
+ widget = node.widgets.find((w) => w.name === widgetName);
+ }
+ }
+ if (widget) {
+ widget.value = v;
+ if (widget.callback) {
+ widget.callback(
+ widget.value,
+ app.canvas,
+ node,
+ app.canvas.graph_mouse,
+ {}
+ );
+ }
+ }
+ }
+ }
+ refreshComboInNode() {
+ var _a;
+ const widget = (_a = this.widgets) == null ? void 0 : _a[0];
+ if ((widget == null ? void 0 : widget.type) === "combo") {
+ widget.options.values = this.outputs[0].widget[GET_CONFIG]()[0];
+ if (!widget.options.values.includes(widget.value)) {
+ widget.value = widget.options.values[0];
+ widget.callback(widget.value);
+ }
+ }
+ }
+ onAfterGraphConfigured() {
+ var _a, _b;
+ if (((_a = this.outputs[0].links) == null ? void 0 : _a.length) && !((_b = this.widgets) == null ? void 0 : _b.length)) {
+ if (!__privateMethod(this, _PrimitiveNode_instances, onFirstConnection_fn).call(this)) return;
+ if (this.widgets) {
+ for (let i = 0; i < this.widgets_values.length; i++) {
+ const w = this.widgets[i];
+ if (w) {
+ w.value = this.widgets_values[i];
+ }
+ }
+ }
+ __privateMethod(this, _PrimitiveNode_instances, mergeWidgetConfig_fn).call(this);
+ }
+ }
+ onConnectionsChange(_, index, connected) {
+ var _a;
+ if (app.configuringGraph) {
+ return;
+ }
+ const links = this.outputs[0].links;
+ if (connected) {
+ if ((links == null ? void 0 : links.length) && !((_a = this.widgets) == null ? void 0 : _a.length)) {
+ __privateMethod(this, _PrimitiveNode_instances, onFirstConnection_fn).call(this);
+ }
+ } else {
+ __privateMethod(this, _PrimitiveNode_instances, mergeWidgetConfig_fn).call(this);
+ if (!(links == null ? void 0 : links.length)) {
+ this.onLastDisconnect();
+ }
+ }
+ }
+ onConnectOutput(slot, type, input, target_node, target_slot) {
+ var _a;
+ if (!input.widget) {
+ if (!(input.type in ComfyWidgets)) return false;
+ }
+ if ((_a = this.outputs[slot].links) == null ? void 0 : _a.length) {
+ const valid = __privateMethod(this, _PrimitiveNode_instances, isValidConnection_fn).call(this, input);
+ if (valid) {
+ this.applyToGraph([{ target_id: target_node.id, target_slot }]);
+ }
+ return valid;
+ }
+ }
+ recreateWidget() {
+ var _a, _b, _c;
+ const values = (_a = this.widgets) == null ? void 0 : _a.map((w) => w.value);
+ __privateMethod(this, _PrimitiveNode_instances, removeWidgets_fn).call(this);
+ __privateMethod(this, _PrimitiveNode_instances, onFirstConnection_fn).call(this, true);
+ if (values == null ? void 0 : values.length) {
+ for (let i = 0; i < ((_b = this.widgets) == null ? void 0 : _b.length); i++)
+ this.widgets[i].value = values[i];
+ }
+ return (_c = this.widgets) == null ? void 0 : _c[0];
+ }
+ onLastDisconnect() {
+ this.outputs[0].type = "*";
+ this.outputs[0].name = "connect to widget input";
+ delete this.outputs[0].widget;
+ __privateMethod(this, _PrimitiveNode_instances, removeWidgets_fn).call(this);
+ }
+};
+_PrimitiveNode_instances = new WeakSet();
+onFirstConnection_fn = /* @__PURE__ */ __name(function(recreating) {
+ var _a, _b;
+ if (!this.outputs[0].links) {
+ this.onLastDisconnect();
+ return;
+ }
+ const linkId = this.outputs[0].links[0];
+ const link = this.graph.links[linkId];
+ if (!link) return;
+ const theirNode = this.graph.getNodeById(link.target_id);
+ if (!theirNode || !theirNode.inputs) return;
+ const input = theirNode.inputs[link.target_slot];
+ if (!input) return;
+ let widget;
+ if (!input.widget) {
+ if (!(input.type in ComfyWidgets)) return;
+ widget = { name: input.name, [GET_CONFIG]: () => [input.type, {}] };
+ } else {
+ widget = input.widget;
+ }
+ const config = (_a = widget[GET_CONFIG]) == null ? void 0 : _a.call(widget);
+ if (!config) return;
+ const { type } = getWidgetType(config);
+ this.outputs[0].type = type;
+ this.outputs[0].name = type;
+ this.outputs[0].widget = widget;
+ __privateMethod(this, _PrimitiveNode_instances, createWidget_fn).call(this, (_b = widget[CONFIG]) != null ? _b : config, theirNode, widget.name, recreating, widget[TARGET]);
+}, "#onFirstConnection");
+createWidget_fn = /* @__PURE__ */ __name(function(inputData, node, widgetName, recreating, targetWidget) {
+ var _a, _b, _c;
+ let type = inputData[0];
+ if (type instanceof Array) {
+ type = "COMBO";
+ }
+ let widget;
+ if (type in ComfyWidgets) {
+ widget = (ComfyWidgets[type](this, "value", inputData, app) || {}).widget;
+ } else {
+ widget = this.addWidget(type, "value", null, () => {
+ }, {});
+ }
+ if (targetWidget) {
+ widget.value = targetWidget.value;
+ } else if ((node == null ? void 0 : node.widgets) && widget) {
+ const theirWidget = node.widgets.find((w) => w.name === widgetName);
+ if (theirWidget) {
+ widget.value = theirWidget.value;
+ }
+ }
+ if (!((_a = inputData == null ? void 0 : inputData[1]) == null ? void 0 : _a.control_after_generate) && (widget.type === "number" || widget.type === "combo")) {
+ let control_value = (_b = this.widgets_values) == null ? void 0 : _b[1];
+ if (!control_value) {
+ control_value = "fixed";
+ }
+ addValueControlWidgets(
+ this,
+ widget,
+ control_value,
+ void 0,
+ inputData
+ );
+ let filter = (_c = this.widgets_values) == null ? void 0 : _c[2];
+ if (filter && this.widgets.length === 3) {
+ this.widgets[2].value = filter;
+ }
+ }
+ const controlValues = this.controlValues;
+ if (this.lastType === this.widgets[0].type && (controlValues == null ? void 0 : controlValues.length) === this.widgets.length - 1) {
+ for (let i = 0; i < controlValues.length; i++) {
+ this.widgets[i + 1].value = controlValues[i];
+ }
+ }
+ const callback = widget.callback;
+ const self = this;
+ widget.callback = function() {
+ const r = callback ? callback.apply(this, arguments) : void 0;
+ self.applyToGraph();
+ return r;
+ };
+ if (!recreating) {
+ const sz = this.computeSize();
+ if (this.size[0] < sz[0]) {
+ this.size[0] = sz[0];
+ }
+ if (this.size[1] < sz[1]) {
+ this.size[1] = sz[1];
+ }
+ requestAnimationFrame(() => {
+ if (this.onResize) {
+ this.onResize(this.size);
+ }
+ });
+ }
+}, "#createWidget");
+mergeWidgetConfig_fn = /* @__PURE__ */ __name(function() {
+ const output = this.outputs[0];
+ const links = output.links;
+ const hasConfig = !!output.widget[CONFIG];
+ if (hasConfig) {
+ delete output.widget[CONFIG];
+ }
+ if ((links == null ? void 0 : links.length) < 2 && hasConfig) {
+ if (links.length) {
+ this.recreateWidget();
+ }
+ return;
+ }
+ const config1 = output.widget[GET_CONFIG]();
+ const isNumber = config1[0] === "INT" || config1[0] === "FLOAT";
+ if (!isNumber) return;
+ for (const linkId of links) {
+ const link = app.graph.links[linkId];
+ if (!link) continue;
+ const theirNode = app.graph.getNodeById(link.target_id);
+ const theirInput = theirNode.inputs[link.target_slot];
+ __privateMethod(this, _PrimitiveNode_instances, isValidConnection_fn).call(this, theirInput, hasConfig);
+ }
+}, "#mergeWidgetConfig");
+isValidConnection_fn = /* @__PURE__ */ __name(function(input, forceUpdate) {
+ const output = this.outputs[0];
+ const config2 = input.widget[GET_CONFIG]();
+ return !!mergeIfValid.call(
+ this,
+ output,
+ config2,
+ forceUpdate,
+ this.recreateWidget
+ );
+}, "#isValidConnection");
+removeWidgets_fn = /* @__PURE__ */ __name(function() {
+ var _a;
+ if (this.widgets) {
+ for (const w of this.widgets) {
+ if (w.onRemove) {
+ w.onRemove();
+ }
+ }
+ this.controlValues = [];
+ this.lastType = (_a = this.widgets[0]) == null ? void 0 : _a.type;
+ for (let i = 1; i < this.widgets.length; i++) {
+ this.controlValues.push(this.widgets[i].value);
+ }
+ setTimeout(() => {
+ delete this.lastType;
+ delete this.controlValues;
+ }, 15);
+ this.widgets.length = 0;
+ }
+}, "#removeWidgets");
+__name(_PrimitiveNode, "PrimitiveNode");
+__publicField(_PrimitiveNode, "category");
+let PrimitiveNode = _PrimitiveNode;
+function getWidgetConfig(slot) {
+ var _a;
+ return (_a = slot.widget[CONFIG]) != null ? _a : slot.widget[GET_CONFIG]();
+}
+__name(getWidgetConfig, "getWidgetConfig");
+function getConfig(widgetName) {
+ var _a, _b, _c, _d;
+ const { nodeData } = this.constructor;
+ return (_d = (_a = nodeData == null ? void 0 : nodeData.input) == null ? void 0 : _a.required[widgetName]) != null ? _d : (_c = (_b = nodeData == null ? void 0 : nodeData.input) == null ? void 0 : _b.optional) == null ? void 0 : _c[widgetName];
+}
+__name(getConfig, "getConfig");
+function isConvertibleWidget(widget, config) {
+ var _a;
+ return (VALID_TYPES.includes(widget.type) || VALID_TYPES.includes(config[0])) && !((_a = widget.options) == null ? void 0 : _a.forceInput);
+}
+__name(isConvertibleWidget, "isConvertibleWidget");
+function hideWidget(node, widget, suffix = "") {
+ var _a;
+ if ((_a = widget.type) == null ? void 0 : _a.startsWith(CONVERTED_TYPE)) return;
+ widget.origType = widget.type;
+ widget.origComputeSize = widget.computeSize;
+ widget.origSerializeValue = widget.serializeValue;
+ widget.computeSize = () => [0, -4];
+ widget.type = CONVERTED_TYPE + suffix;
+ widget.serializeValue = () => {
+ if (!node.inputs) {
+ return void 0;
+ }
+ let node_input = node.inputs.find((i) => {
+ var _a2;
+ return ((_a2 = i.widget) == null ? void 0 : _a2.name) === widget.name;
+ });
+ if (!node_input || !node_input.link) {
+ return void 0;
+ }
+ return widget.origSerializeValue ? widget.origSerializeValue() : widget.value;
+ };
+ if (widget.linkedWidgets) {
+ for (const w of widget.linkedWidgets) {
+ hideWidget(node, w, ":" + widget.name);
+ }
+ }
+}
+__name(hideWidget, "hideWidget");
+function showWidget(widget) {
+ widget.type = widget.origType;
+ widget.computeSize = widget.origComputeSize;
+ widget.serializeValue = widget.origSerializeValue;
+ delete widget.origType;
+ delete widget.origComputeSize;
+ delete widget.origSerializeValue;
+ if (widget.linkedWidgets) {
+ for (const w of widget.linkedWidgets) {
+ showWidget(w);
+ }
+ }
+}
+__name(showWidget, "showWidget");
+function convertToInput(node, widget, config) {
+ hideWidget(node, widget);
+ const { type } = getWidgetType(config);
+ const sz = node.size;
+ node.addInput(widget.name, type, {
+ widget: { name: widget.name, [GET_CONFIG]: () => config }
+ });
+ for (const widget2 of node.widgets) {
+ widget2.last_y += LiteGraph.NODE_SLOT_HEIGHT;
+ }
+ node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])]);
+}
+__name(convertToInput, "convertToInput");
+function convertToWidget(node, widget) {
+ showWidget(widget);
+ const sz = node.size;
+ node.removeInput(node.inputs.findIndex((i) => {
+ var _a;
+ return ((_a = i.widget) == null ? void 0 : _a.name) === widget.name;
+ }));
+ for (const widget2 of node.widgets) {
+ widget2.last_y -= LiteGraph.NODE_SLOT_HEIGHT;
+ }
+ node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])]);
+}
+__name(convertToWidget, "convertToWidget");
+function getWidgetType(config) {
+ let type = config[0];
+ if (type instanceof Array) {
+ type = "COMBO";
+ }
+ return { type };
+}
+__name(getWidgetType, "getWidgetType");
+function isValidCombo(combo, obj) {
+ if (!(obj instanceof Array)) {
+ console.log(`connection rejected: tried to connect combo to ${obj}`);
+ return false;
+ }
+ if (combo.length !== obj.length) {
+ console.log(`connection rejected: combo lists dont match`);
+ return false;
+ }
+ if (combo.find((v, i) => obj[i] !== v)) {
+ console.log(`connection rejected: combo lists dont match`);
+ return false;
+ }
+ return true;
+}
+__name(isValidCombo, "isValidCombo");
+function isPrimitiveNode(node) {
+ return node.type === "PrimitiveNode";
+}
+__name(isPrimitiveNode, "isPrimitiveNode");
+function setWidgetConfig(slot, config, target) {
+ if (!slot.widget) return;
+ if (config) {
+ slot.widget[GET_CONFIG] = () => config;
+ slot.widget[TARGET] = target;
+ } else {
+ delete slot.widget;
+ }
+ if (slot.link) {
+ const link = app.graph.links[slot.link];
+ if (link) {
+ const originNode = app.graph.getNodeById(link.origin_id);
+ if (isPrimitiveNode(originNode)) {
+ if (config) {
+ originNode.recreateWidget();
+ } else if (!app.configuringGraph) {
+ originNode.disconnectOutput(0);
+ originNode.onLastDisconnect();
+ }
+ }
+ }
+ }
+}
+__name(setWidgetConfig, "setWidgetConfig");
+function mergeIfValid(output, config2, forceUpdate, recreateWidget, config1) {
+ var _a, _b, _c, _d, _e, _f;
+ if (!config1) {
+ config1 = (_a = output.widget[CONFIG]) != null ? _a : output.widget[GET_CONFIG]();
+ }
+ if (config1[0] instanceof Array) {
+ if (!isValidCombo(config1[0], config2[0])) return;
+ } else if (config1[0] !== config2[0]) {
+ console.log(`connection rejected: types dont match`, config1[0], config2[0]);
+ return;
+ }
+ const keys = /* @__PURE__ */ new Set([
+ ...Object.keys((_b = config1[1]) != null ? _b : {}),
+ ...Object.keys((_c = config2[1]) != null ? _c : {})
+ ]);
+ let customConfig;
+ const getCustomConfig = /* @__PURE__ */ __name(() => {
+ var _a2, _b2;
+ if (!customConfig) {
+ if (typeof structuredClone === "undefined") {
+ customConfig = JSON.parse(JSON.stringify((_a2 = config1[1]) != null ? _a2 : {}));
+ } else {
+ customConfig = structuredClone((_b2 = config1[1]) != null ? _b2 : {});
+ }
+ }
+ return customConfig;
+ }, "getCustomConfig");
+ const isNumber = config1[0] === "INT" || config1[0] === "FLOAT";
+ for (const k of keys.values()) {
+ if (k !== "default" && k !== "forceInput" && k !== "defaultInput" && k !== "control_after_generate" && k !== "multiline" && k !== "tooltip") {
+ let v1 = config1[1][k];
+ let v2 = (_d = config2[1]) == null ? void 0 : _d[k];
+ if (v1 === v2 || !v1 && !v2) continue;
+ if (isNumber) {
+ if (k === "min") {
+ const theirMax = (_e = config2[1]) == null ? void 0 : _e["max"];
+ if (theirMax != null && v1 > theirMax) {
+ console.log("connection rejected: min > max", v1, theirMax);
+ return;
+ }
+ getCustomConfig()[k] = v1 == null ? v2 : v2 == null ? v1 : Math.max(v1, v2);
+ continue;
+ } else if (k === "max") {
+ const theirMin = (_f = config2[1]) == null ? void 0 : _f["min"];
+ if (theirMin != null && v1 < theirMin) {
+ console.log("connection rejected: max < min", v1, theirMin);
+ return;
+ }
+ getCustomConfig()[k] = v1 == null ? v2 : v2 == null ? v1 : Math.min(v1, v2);
+ continue;
+ } else if (k === "step") {
+ let step;
+ if (v1 == null) {
+ step = v2;
+ } else if (v2 == null) {
+ step = v1;
+ } else {
+ if (v1 < v2) {
+ const a = v2;
+ v2 = v1;
+ v1 = a;
+ }
+ if (v1 % v2) {
+ console.log(
+ "connection rejected: steps not divisible",
+ "current:",
+ v1,
+ "new:",
+ v2
+ );
+ return;
+ }
+ step = v1;
+ }
+ getCustomConfig()[k] = step;
+ continue;
+ }
+ }
+ console.log(`connection rejected: config ${k} values dont match`, v1, v2);
+ return;
+ }
+ }
+ if (customConfig || forceUpdate) {
+ if (customConfig) {
+ output.widget[CONFIG] = [config1[0], customConfig];
+ }
+ const widget = recreateWidget == null ? void 0 : recreateWidget.call(this);
+ if (widget) {
+ const min = widget.options.min;
+ const max = widget.options.max;
+ if (min != null && widget.value < min) widget.value = min;
+ if (max != null && widget.value > max) widget.value = max;
+ widget.callback(widget.value);
+ }
+ }
+ return { customConfig };
+}
+__name(mergeIfValid, "mergeIfValid");
+let useConversionSubmenusSetting;
+app.registerExtension({
+ name: "Comfy.WidgetInputs",
+ init() {
+ useConversionSubmenusSetting = app.ui.settings.addSetting({
+ id: "Comfy.NodeInputConversionSubmenus",
+ name: "Node widget/input conversion sub-menus",
+ tooltip: "In the node context menu, place the entries that convert between input/widget in sub-menus.",
+ type: "boolean",
+ defaultValue: true
+ });
+ },
+ beforeRegisterNodeDef(nodeType, nodeData, app2) {
+ return __async(this, null, function* () {
+ const origGetExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
+ nodeType.prototype.convertWidgetToInput = function(widget) {
+ var _a;
+ const config = (_a = getConfig.call(this, widget.name)) != null ? _a : [
+ widget.type,
+ widget.options || {}
+ ];
+ if (!isConvertibleWidget(widget, config)) return false;
+ convertToInput(this, widget, config);
+ return true;
+ };
+ nodeType.prototype.getExtraMenuOptions = function(_, options) {
+ var _a, _b;
+ const r = origGetExtraMenuOptions ? origGetExtraMenuOptions.apply(this, arguments) : void 0;
+ if (this.widgets) {
+ let toInput = [];
+ let toWidget = [];
+ for (const w of this.widgets) {
+ if ((_a = w.options) == null ? void 0 : _a.forceInput) {
+ continue;
+ }
+ if (w.type === CONVERTED_TYPE) {
+ toWidget.push({
+ content: `Convert ${w.name} to widget`,
+ callback: /* @__PURE__ */ __name(() => convertToWidget(this, w), "callback")
+ });
+ } else {
+ const config = (_b = getConfig.call(this, w.name)) != null ? _b : [
+ w.type,
+ w.options || {}
+ ];
+ if (isConvertibleWidget(w, config)) {
+ toInput.push({
+ content: `Convert ${w.name} to input`,
+ callback: /* @__PURE__ */ __name(() => convertToInput(this, w, config), "callback")
+ });
+ }
+ }
+ }
+ if (toInput.length) {
+ if (useConversionSubmenusSetting.value) {
+ options.push({
+ content: "Convert Widget to Input",
+ submenu: {
+ options: toInput
+ }
+ });
+ } else {
+ options.push(...toInput, null);
+ }
+ }
+ if (toWidget.length) {
+ if (useConversionSubmenusSetting.value) {
+ options.push({
+ content: "Convert Input to Widget",
+ submenu: {
+ options: toWidget
+ }
+ });
+ } else {
+ options.push(...toWidget, null);
+ }
+ }
+ }
+ return r;
+ };
+ nodeType.prototype.onGraphConfigured = function() {
+ if (!this.inputs) return;
+ for (const input of this.inputs) {
+ if (input.widget) {
+ if (!input.widget[GET_CONFIG]) {
+ input.widget[GET_CONFIG] = () => getConfig.call(this, input.widget.name);
+ }
+ if (input.widget.config) {
+ if (input.widget.config[0] instanceof Array) {
+ input.type = "COMBO";
+ const link = app2.graph.links[input.link];
+ if (link) {
+ link.type = input.type;
+ }
+ }
+ delete input.widget.config;
+ }
+ const w = this.widgets.find((w2) => w2.name === input.widget.name);
+ if (w) {
+ hideWidget(this, w);
+ } else {
+ convertToWidget(this, input);
+ }
+ }
+ }
+ };
+ const origOnNodeCreated = nodeType.prototype.onNodeCreated;
+ nodeType.prototype.onNodeCreated = function() {
+ var _a, _b, _c;
+ const r = origOnNodeCreated ? origOnNodeCreated.apply(this) : void 0;
+ if (!app2.configuringGraph && this.widgets) {
+ for (const w of this.widgets) {
+ if (((_a = w == null ? void 0 : w.options) == null ? void 0 : _a.forceInput) || ((_b = w == null ? void 0 : w.options) == null ? void 0 : _b.defaultInput)) {
+ const config = (_c = getConfig.call(this, w.name)) != null ? _c : [
+ w.type,
+ w.options || {}
+ ];
+ convertToInput(this, w, config);
+ }
+ }
+ }
+ return r;
+ };
+ const origOnConfigure = nodeType.prototype.onConfigure;
+ nodeType.prototype.onConfigure = function() {
+ const r = origOnConfigure ? origOnConfigure.apply(this, arguments) : void 0;
+ if (!app2.configuringGraph && this.inputs) {
+ for (const input of this.inputs) {
+ if (input.widget && !input.widget[GET_CONFIG]) {
+ input.widget[GET_CONFIG] = () => getConfig.call(this, input.widget.name);
+ const w = this.widgets.find((w2) => w2.name === input.widget.name);
+ if (w) {
+ hideWidget(this, w);
+ }
+ }
+ }
+ }
+ return r;
+ };
+ function isNodeAtPos(pos) {
+ for (const n of app2.graph._nodes) {
+ if (n.pos[0] === pos[0] && n.pos[1] === pos[1]) {
+ return true;
+ }
+ }
+ return false;
+ }
+ __name(isNodeAtPos, "isNodeAtPos");
+ const origOnInputDblClick = nodeType.prototype.onInputDblClick;
+ const ignoreDblClick = Symbol();
+ nodeType.prototype.onInputDblClick = function(slot) {
+ var _a, _b, _c;
+ const r = origOnInputDblClick ? origOnInputDblClick.apply(this, arguments) : void 0;
+ const input = this.inputs[slot];
+ if (!input.widget || !input[ignoreDblClick]) {
+ if (!(input.type in ComfyWidgets) && !(((_c = (_b = (_a = input.widget)[GET_CONFIG]) == null ? void 0 : _b.call(_a)) == null ? void 0 : _c[0]) instanceof Array)) {
+ return r;
+ }
+ }
+ const node = LiteGraph.createNode("PrimitiveNode");
+ app2.graph.add(node);
+ const pos = [
+ this.pos[0] - node.size[0] - 30,
+ this.pos[1]
+ ];
+ while (isNodeAtPos(pos)) {
+ pos[1] += LiteGraph.NODE_TITLE_HEIGHT;
+ }
+ node.pos = pos;
+ node.connect(0, this, slot);
+ node.title = input.name;
+ input[ignoreDblClick] = true;
+ setTimeout(() => {
+ delete input[ignoreDblClick];
+ }, 300);
+ return r;
+ };
+ const onConnectInput = nodeType.prototype.onConnectInput;
+ nodeType.prototype.onConnectInput = function(targetSlot, type, output, originNode, originSlot) {
+ var _a, _b, _c, _d, _e, _f;
+ const v = onConnectInput == null ? void 0 : onConnectInput(this, arguments);
+ if (type !== "COMBO") return v;
+ if (originNode.outputs[originSlot].widget) return v;
+ const targetCombo = (_c = (_b = (_a = this.inputs[targetSlot].widget) == null ? void 0 : _a[GET_CONFIG]) == null ? void 0 : _b.call(_a)) == null ? void 0 : _c[0];
+ if (!targetCombo || !(targetCombo instanceof Array)) return v;
+ const originConfig = (_f = (_e = (_d = originNode.constructor) == null ? void 0 : _d.nodeData) == null ? void 0 : _e.output) == null ? void 0 : _f[originSlot];
+ if (!originConfig || !isValidCombo(targetCombo, originConfig)) {
+ return false;
+ }
+ return v;
+ };
+ });
+ },
+ registerCustomNodes() {
+ LiteGraph.registerNodeType(
+ "PrimitiveNode",
+ Object.assign(PrimitiveNode, {
+ title: "Primitive"
+ })
+ );
+ PrimitiveNode.category = "utils";
+ }
+});
+window.comfyAPI = window.comfyAPI || {};
+window.comfyAPI.widgetInputs = window.comfyAPI.widgetInputs || {};
+window.comfyAPI.widgetInputs.getWidgetConfig = getWidgetConfig;
+window.comfyAPI.widgetInputs.setWidgetConfig = setWidgetConfig;
+window.comfyAPI.widgetInputs.mergeIfValid = mergeIfValid;
+const ORDER = Symbol();
+function merge(target, source) {
+ if (typeof target === "object" && typeof source === "object") {
+ for (const key in source) {
+ const sv = source[key];
+ if (typeof sv === "object") {
+ let tv = target[key];
+ if (!tv) tv = target[key] = {};
+ merge(tv, source[key]);
+ } else {
+ target[key] = sv;
+ }
+ }
+ }
+ return target;
+}
+__name(merge, "merge");
+const _ManageGroupDialog = class _ManageGroupDialog extends ComfyDialog {
+ constructor(app2) {
+ super();
+ __publicField(this, "tabs");
+ __publicField(this, "selectedNodeIndex");
+ __publicField(this, "selectedTab", "Inputs");
+ __publicField(this, "selectedGroup");
+ __publicField(this, "modifications", {});
+ __publicField(this, "nodeItems");
+ __publicField(this, "app");
+ __publicField(this, "groupNodeType");
+ __publicField(this, "groupNodeDef");
+ __publicField(this, "groupData");
+ __publicField(this, "innerNodesList");
+ __publicField(this, "widgetsPage");
+ __publicField(this, "inputsPage");
+ __publicField(this, "outputsPage");
+ __publicField(this, "draggable");
+ this.app = app2;
+ this.element = $el("dialog.comfy-group-manage", {
+ parent: document.body
+ });
+ }
+ get selectedNodeInnerIndex() {
+ return +this.nodeItems[this.selectedNodeIndex].dataset.nodeindex;
+ }
+ changeTab(tab) {
+ this.tabs[this.selectedTab].tab.classList.remove("active");
+ this.tabs[this.selectedTab].page.classList.remove("active");
+ this.tabs[tab].tab.classList.add("active");
+ this.tabs[tab].page.classList.add("active");
+ this.selectedTab = tab;
+ }
+ changeNode(index, force) {
+ if (!force && this.selectedNodeIndex === index) return;
+ if (this.selectedNodeIndex != null) {
+ this.nodeItems[this.selectedNodeIndex].classList.remove("selected");
+ }
+ this.nodeItems[index].classList.add("selected");
+ this.selectedNodeIndex = index;
+ if (!this.buildInputsPage() && this.selectedTab === "Inputs") {
+ this.changeTab("Widgets");
+ }
+ if (!this.buildWidgetsPage() && this.selectedTab === "Widgets") {
+ this.changeTab("Outputs");
+ }
+ if (!this.buildOutputsPage() && this.selectedTab === "Outputs") {
+ this.changeTab("Inputs");
+ }
+ this.changeTab(this.selectedTab);
+ }
+ getGroupData() {
+ this.groupNodeType = LiteGraph.registered_node_types["workflow/" + this.selectedGroup];
+ this.groupNodeDef = this.groupNodeType.nodeData;
+ this.groupData = GroupNodeHandler.getGroupData(this.groupNodeType);
+ }
+ changeGroup(group, reset = true) {
+ var _a;
+ this.selectedGroup = group;
+ this.getGroupData();
+ const nodes = this.groupData.nodeData.nodes;
+ this.nodeItems = nodes.map(
+ (n, i) => {
+ var _a2;
+ return $el(
+ "li.draggable-item",
+ {
+ dataset: {
+ nodeindex: n.index + ""
+ },
+ onclick: /* @__PURE__ */ __name(() => {
+ this.changeNode(i);
+ }, "onclick")
+ },
+ [
+ $el("span.drag-handle"),
+ $el(
+ "div",
+ {
+ textContent: (_a2 = n.title) != null ? _a2 : n.type
+ },
+ n.title ? $el("span", {
+ textContent: n.type
+ }) : []
+ )
+ ]
+ );
+ }
+ );
+ this.innerNodesList.replaceChildren(...this.nodeItems);
+ if (reset) {
+ this.selectedNodeIndex = null;
+ this.changeNode(0);
+ } else {
+ const items = this.draggable.getAllItems();
+ let index = items.findIndex((item) => item.classList.contains("selected"));
+ if (index === -1) index = this.selectedNodeIndex;
+ this.changeNode(index, true);
+ }
+ const ordered = [...nodes];
+ (_a = this.draggable) == null ? void 0 : _a.dispose();
+ this.draggable = new DraggableList(this.innerNodesList, "li");
+ this.draggable.addEventListener(
+ "dragend",
+ ({ detail: { oldPosition, newPosition } }) => {
+ if (oldPosition === newPosition) return;
+ ordered.splice(newPosition, 0, ordered.splice(oldPosition, 1)[0]);
+ for (let i = 0; i < ordered.length; i++) {
+ this.storeModification({
+ nodeIndex: ordered[i].index,
+ section: ORDER,
+ prop: "order",
+ value: i
+ });
+ }
+ }
+ );
+ }
+ storeModification(props) {
+ var _a, _b, _c, _d, _e, _f, _g, _h;
+ const { nodeIndex, section, prop, value } = props;
+ const groupMod = (_c = (_a = this.modifications)[_b = this.selectedGroup]) != null ? _c : _a[_b] = {};
+ const nodesMod = (_d = groupMod.nodes) != null ? _d : groupMod.nodes = {};
+ const nodeMod = (_f = nodesMod[_e = nodeIndex != null ? nodeIndex : this.selectedNodeInnerIndex]) != null ? _f : nodesMod[_e] = {};
+ const typeMod = (_g = nodeMod[section]) != null ? _g : nodeMod[section] = {};
+ if (typeof value === "object") {
+ const objMod = (_h = typeMod[prop]) != null ? _h : typeMod[prop] = {};
+ Object.assign(objMod, value);
+ } else {
+ typeMod[prop] = value;
+ }
+ }
+ getEditElement(section, prop, value, placeholder, checked, checkable = true) {
+ var _a, _b, _c, _d;
+ if (value === placeholder) value = "";
+ const mods = (_d = (_c = (_b = (_a = this.modifications[this.selectedGroup]) == null ? void 0 : _a.nodes) == null ? void 0 : _b[this.selectedNodeInnerIndex]) == null ? void 0 : _c[section]) == null ? void 0 : _d[prop];
+ if (mods) {
+ if (mods.name != null) {
+ value = mods.name;
+ }
+ if (mods.visible != null) {
+ checked = mods.visible;
+ }
+ }
+ return $el("div", [
+ $el("input", {
+ value,
+ placeholder,
+ type: "text",
+ onchange: /* @__PURE__ */ __name((e) => {
+ this.storeModification({
+ section,
+ prop,
+ value: { name: e.target.value }
+ });
+ }, "onchange")
+ }),
+ $el("label", { textContent: "Visible" }, [
+ $el("input", {
+ type: "checkbox",
+ checked,
+ disabled: !checkable,
+ onchange: /* @__PURE__ */ __name((e) => {
+ this.storeModification({
+ section,
+ prop,
+ value: { visible: !!e.target.checked }
+ });
+ }, "onchange")
+ })
+ ])
+ ]);
+ }
+ buildWidgetsPage() {
+ var _a, _b;
+ const widgets = this.groupData.oldToNewWidgetMap[this.selectedNodeInnerIndex];
+ const items = Object.keys(widgets != null ? widgets : {});
+ const type = app.graph.extra.groupNodes[this.selectedGroup];
+ const config = (_b = (_a = type.config) == null ? void 0 : _a[this.selectedNodeInnerIndex]) == null ? void 0 : _b.input;
+ this.widgetsPage.replaceChildren(
+ ...items.map((oldName) => {
+ var _a2;
+ return this.getEditElement(
+ "input",
+ oldName,
+ widgets[oldName],
+ oldName,
+ ((_a2 = config == null ? void 0 : config[oldName]) == null ? void 0 : _a2.visible) !== false
+ );
+ })
+ );
+ return !!items.length;
+ }
+ buildInputsPage() {
+ var _a, _b;
+ const inputs = this.groupData.nodeInputs[this.selectedNodeInnerIndex];
+ const items = Object.keys(inputs != null ? inputs : {});
+ const type = app.graph.extra.groupNodes[this.selectedGroup];
+ const config = (_b = (_a = type.config) == null ? void 0 : _a[this.selectedNodeInnerIndex]) == null ? void 0 : _b.input;
+ this.inputsPage.replaceChildren(
+ ...items.map((oldName) => {
+ var _a2;
+ let value = inputs[oldName];
+ if (!value) {
+ return;
+ }
+ return this.getEditElement(
+ "input",
+ oldName,
+ value,
+ oldName,
+ ((_a2 = config == null ? void 0 : config[oldName]) == null ? void 0 : _a2.visible) !== false
+ );
+ }).filter(Boolean)
+ );
+ return !!items.length;
+ }
+ buildOutputsPage() {
+ var _a, _b, _c;
+ const nodes = this.groupData.nodeData.nodes;
+ const innerNodeDef = this.groupData.getNodeDef(
+ nodes[this.selectedNodeInnerIndex]
+ );
+ const outputs = (_a = innerNodeDef == null ? void 0 : innerNodeDef.output) != null ? _a : [];
+ const groupOutputs = this.groupData.oldToNewOutputMap[this.selectedNodeInnerIndex];
+ const type = app.graph.extra.groupNodes[this.selectedGroup];
+ const config = (_c = (_b = type.config) == null ? void 0 : _b[this.selectedNodeInnerIndex]) == null ? void 0 : _c.output;
+ const node = this.groupData.nodeData.nodes[this.selectedNodeInnerIndex];
+ const checkable = node.type !== "PrimitiveNode";
+ this.outputsPage.replaceChildren(
+ ...outputs.map((type2, slot) => {
+ var _a2, _b2, _c2, _d;
+ const groupOutputIndex = groupOutputs == null ? void 0 : groupOutputs[slot];
+ const oldName = (_b2 = (_a2 = innerNodeDef.output_name) == null ? void 0 : _a2[slot]) != null ? _b2 : type2;
+ let value = (_c2 = config == null ? void 0 : config[slot]) == null ? void 0 : _c2.name;
+ const visible = ((_d = config == null ? void 0 : config[slot]) == null ? void 0 : _d.visible) || groupOutputIndex != null;
+ if (!value || value === oldName) {
+ value = "";
+ }
+ return this.getEditElement(
+ "output",
+ slot,
+ value,
+ oldName,
+ visible,
+ checkable
+ );
+ }).filter(Boolean)
+ );
+ return !!outputs.length;
+ }
+ show(type) {
+ var _a, _b;
+ const groupNodes = Object.keys((_b = (_a = app.graph.extra) == null ? void 0 : _a.groupNodes) != null ? _b : {}).sort(
+ (a, b) => a.localeCompare(b)
+ );
+ this.innerNodesList = $el(
+ "ul.comfy-group-manage-list-items"
+ );
+ this.widgetsPage = $el("section.comfy-group-manage-node-page");
+ this.inputsPage = $el("section.comfy-group-manage-node-page");
+ this.outputsPage = $el("section.comfy-group-manage-node-page");
+ const pages = $el("div", [
+ this.widgetsPage,
+ this.inputsPage,
+ this.outputsPage
+ ]);
+ this.tabs = [
+ ["Inputs", this.inputsPage],
+ ["Widgets", this.widgetsPage],
+ ["Outputs", this.outputsPage]
+ ].reduce((p, [name, page]) => {
+ p[name] = {
+ tab: $el("a", {
+ onclick: /* @__PURE__ */ __name(() => {
+ this.changeTab(name);
+ }, "onclick"),
+ textContent: name
+ }),
+ page
+ };
+ return p;
+ }, {});
+ const outer = $el("div.comfy-group-manage-outer", [
+ $el("header", [
+ $el("h2", "Group Nodes"),
+ $el(
+ "select",
+ {
+ onchange: /* @__PURE__ */ __name((e) => {
+ this.changeGroup(e.target.value);
+ }, "onchange")
+ },
+ groupNodes.map(
+ (g) => $el("option", {
+ textContent: g,
+ selected: "workflow/" + g === type,
+ value: g
+ })
+ )
+ )
+ ]),
+ $el("main", [
+ $el("section.comfy-group-manage-list", this.innerNodesList),
+ $el("section.comfy-group-manage-node", [
+ $el(
+ "header",
+ Object.values(this.tabs).map((t) => t.tab)
+ ),
+ pages
+ ])
+ ]),
+ $el("footer", [
+ $el(
+ "button.comfy-btn",
+ {
+ onclick: /* @__PURE__ */ __name((e) => {
+ const node = app.graph._nodes.find(
+ (n) => n.type === "workflow/" + this.selectedGroup
+ );
+ if (node) {
+ alert(
+ "This group node is in use in the current workflow, please first remove these."
+ );
+ return;
+ }
+ if (confirm(
+ `Are you sure you want to remove the node: "${this.selectedGroup}"`
+ )) {
+ delete app.graph.extra.groupNodes[this.selectedGroup];
+ LiteGraph.unregisterNodeType("workflow/" + this.selectedGroup);
+ }
+ this.show();
+ }, "onclick")
+ },
+ "Delete Group Node"
+ ),
+ $el(
+ "button.comfy-btn",
+ {
+ onclick: /* @__PURE__ */ __name(() => __async(this, null, function* () {
+ var _a2, _b2;
+ let nodesByType;
+ let recreateNodes = [];
+ const types = {};
+ for (const g in this.modifications) {
+ const type2 = app.graph.extra.groupNodes[g];
+ let config = (_a2 = type2.config) != null ? _a2 : type2.config = {};
+ let nodeMods = (_b2 = this.modifications[g]) == null ? void 0 : _b2.nodes;
+ if (nodeMods) {
+ const keys = Object.keys(nodeMods);
+ if (nodeMods[keys[0]][ORDER]) {
+ const orderedNodes = [];
+ const orderedMods = {};
+ const orderedConfig = {};
+ for (const n of keys) {
+ const order = nodeMods[n][ORDER].order;
+ orderedNodes[order] = type2.nodes[+n];
+ orderedMods[order] = nodeMods[n];
+ orderedNodes[order].index = order;
+ }
+ for (const l of type2.links) {
+ if (l[0] != null) l[0] = type2.nodes[l[0]].index;
+ if (l[2] != null) l[2] = type2.nodes[l[2]].index;
+ }
+ if (type2.external) {
+ for (const ext2 of type2.external) {
+ ext2[0] = type2.nodes[ext2[0]];
+ }
+ }
+ for (const id2 of keys) {
+ if (config[id2]) {
+ orderedConfig[type2.nodes[id2].index] = config[id2];
+ }
+ delete config[id2];
+ }
+ type2.nodes = orderedNodes;
+ nodeMods = orderedMods;
+ type2.config = config = orderedConfig;
+ }
+ merge(config, nodeMods);
+ }
+ types[g] = type2;
+ if (!nodesByType) {
+ nodesByType = app.graph._nodes.reduce((p, n) => {
+ var _a3, _b3;
+ (_b3 = p[_a3 = n.type]) != null ? _b3 : p[_a3] = [];
+ p[n.type].push(n);
+ return p;
+ }, {});
+ }
+ const nodes = nodesByType["workflow/" + g];
+ if (nodes) recreateNodes.push(...nodes);
+ }
+ yield GroupNodeConfig.registerFromWorkflow(types, {});
+ for (const node of recreateNodes) {
+ node.recreate();
+ }
+ this.modifications = {};
+ this.app.graph.setDirtyCanvas(true, true);
+ this.changeGroup(this.selectedGroup, false);
+ }), "onclick")
+ },
+ "Save"
+ ),
+ $el(
+ "button.comfy-btn",
+ { onclick: /* @__PURE__ */ __name(() => this.element.close(), "onclick") },
+ "Close"
+ )
+ ])
+ ]);
+ this.element.replaceChildren(outer);
+ this.changeGroup(
+ type ? groupNodes.find((g) => "workflow/" + g === type) : groupNodes[0]
+ );
+ this.element.showModal();
+ this.element.addEventListener("close", () => {
+ var _a2;
+ (_a2 = this.draggable) == null ? void 0 : _a2.dispose();
+ });
+ }
+};
+__name(_ManageGroupDialog, "ManageGroupDialog");
+let ManageGroupDialog = _ManageGroupDialog;
+window.comfyAPI = window.comfyAPI || {};
+window.comfyAPI.groupNodeManage = window.comfyAPI.groupNodeManage || {};
+window.comfyAPI.groupNodeManage.ManageGroupDialog = ManageGroupDialog;
+const GROUP = Symbol();
+const Workflow = {
+ InUse: {
+ Free: 0,
+ Registered: 1,
+ InWorkflow: 2
+ },
+ isInUseGroupNode(name) {
+ var _a, _b;
+ const id2 = `workflow/${name}`;
+ if ((_b = (_a = app.graph.extra) == null ? void 0 : _a.groupNodes) == null ? void 0 : _b[name]) {
+ if (app.graph._nodes.find((n) => n.type === id2)) {
+ return Workflow.InUse.InWorkflow;
+ } else {
+ return Workflow.InUse.Registered;
+ }
+ }
+ return Workflow.InUse.Free;
+ },
+ storeGroupNode(name, data) {
+ let extra = app.graph.extra;
+ if (!extra) app.graph.extra = extra = {};
+ let groupNodes = extra.groupNodes;
+ if (!groupNodes) extra.groupNodes = groupNodes = {};
+ groupNodes[name] = data;
+ }
+};
+const _GroupNodeBuilder = class _GroupNodeBuilder {
+ constructor(nodes) {
+ __publicField(this, "nodes");
+ __publicField(this, "nodeData");
+ this.nodes = nodes;
+ }
+ build() {
+ const name = this.getName();
+ if (!name) return;
+ this.sortNodes();
+ this.nodeData = this.getNodeData();
+ Workflow.storeGroupNode(name, this.nodeData);
+ return { name, nodeData: this.nodeData };
+ }
+ getName() {
+ const name = prompt("Enter group name");
+ if (!name) return;
+ const used = Workflow.isInUseGroupNode(name);
+ switch (used) {
+ case Workflow.InUse.InWorkflow:
+ alert(
+ "An in use group node with this name already exists embedded in this workflow, please remove any instances or use a new name."
+ );
+ return;
+ case Workflow.InUse.Registered:
+ if (!confirm(
+ "A group node with this name already exists embedded in this workflow, are you sure you want to overwrite it?"
+ )) {
+ return;
+ }
+ break;
+ }
+ return name;
+ }
+ sortNodes() {
+ const nodesInOrder = app.graph.computeExecutionOrder(false);
+ this.nodes = this.nodes.map((node) => ({ index: nodesInOrder.indexOf(node), node })).sort((a, b) => a.index - b.index || a.node.id - b.node.id).map(({ node }) => node);
+ }
+ getNodeData() {
+ const storeLinkTypes = /* @__PURE__ */ __name((config) => {
+ for (const link of config.links) {
+ const origin = app.graph.getNodeById(link[4]);
+ const type = origin.outputs[link[1]].type;
+ link.push(type);
+ }
+ }, "storeLinkTypes");
+ const storeExternalLinks = /* @__PURE__ */ __name((config) => {
+ var _a, _b;
+ config.external = [];
+ for (let i = 0; i < this.nodes.length; i++) {
+ const node = this.nodes[i];
+ if (!((_a = node.outputs) == null ? void 0 : _a.length)) continue;
+ for (let slot = 0; slot < node.outputs.length; slot++) {
+ let hasExternal = false;
+ const output = node.outputs[slot];
+ let type = output.type;
+ if (!((_b = output.links) == null ? void 0 : _b.length)) continue;
+ for (const l of output.links) {
+ const link = app.graph.links[l];
+ if (!link) continue;
+ if (type === "*") type = link.type;
+ if (!app.canvas.selected_nodes[link.target_id]) {
+ hasExternal = true;
+ break;
+ }
+ }
+ if (hasExternal) {
+ config.external.push([i, slot, type]);
+ }
+ }
+ }
+ }, "storeExternalLinks");
+ const backup = localStorage.getItem("litegrapheditor_clipboard");
+ try {
+ app.canvas.copyToClipboard(this.nodes);
+ const config = JSON.parse(
+ localStorage.getItem("litegrapheditor_clipboard")
+ );
+ storeLinkTypes(config);
+ storeExternalLinks(config);
+ return config;
+ } finally {
+ localStorage.setItem("litegrapheditor_clipboard", backup);
+ }
+ }
+};
+__name(_GroupNodeBuilder, "GroupNodeBuilder");
+let GroupNodeBuilder = _GroupNodeBuilder;
+const _GroupNodeConfig = class _GroupNodeConfig {
+ constructor(name, nodeData) {
+ __publicField(this, "name");
+ __publicField(this, "nodeData");
+ __publicField(this, "inputCount");
+ __publicField(this, "oldToNewOutputMap");
+ __publicField(this, "newToOldOutputMap");
+ __publicField(this, "oldToNewInputMap");
+ __publicField(this, "oldToNewWidgetMap");
+ __publicField(this, "newToOldWidgetMap");
+ __publicField(this, "primitiveDefs");
+ __publicField(this, "widgetToPrimitive");
+ __publicField(this, "primitiveToWidget");
+ __publicField(this, "nodeInputs");
+ __publicField(this, "outputVisibility");
+ __publicField(this, "nodeDef");
+ __publicField(this, "inputs");
+ __publicField(this, "linksFrom");
+ __publicField(this, "linksTo");
+ __publicField(this, "externalFrom");
+ __privateAdd(this, _convertedToProcess, []);
+ this.name = name;
+ this.nodeData = nodeData;
+ this.getLinks();
+ this.inputCount = 0;
+ this.oldToNewOutputMap = {};
+ this.newToOldOutputMap = {};
+ this.oldToNewInputMap = {};
+ this.oldToNewWidgetMap = {};
+ this.newToOldWidgetMap = {};
+ this.primitiveDefs = {};
+ this.widgetToPrimitive = {};
+ this.primitiveToWidget = {};
+ this.nodeInputs = {};
+ this.outputVisibility = [];
+ }
+ registerType(source = "workflow") {
+ return __async(this, null, function* () {
+ this.nodeDef = {
+ output: [],
+ output_name: [],
+ output_is_list: [],
+ output_is_hidden: [],
+ name: source + "/" + this.name,
+ display_name: this.name,
+ category: "group nodes" + ("/" + source),
+ input: { required: {} },
+ [GROUP]: this
+ };
+ this.inputs = [];
+ const seenInputs = {};
+ const seenOutputs = {};
+ for (let i = 0; i < this.nodeData.nodes.length; i++) {
+ const node = this.nodeData.nodes[i];
+ node.index = i;
+ this.processNode(node, seenInputs, seenOutputs);
+ }
+ for (const p of __privateGet(this, _convertedToProcess)) {
+ p();
+ }
+ __privateSet(this, _convertedToProcess, null);
+ yield app.registerNodeDef("workflow/" + this.name, this.nodeDef);
+ });
+ }
+ getLinks() {
+ this.linksFrom = {};
+ this.linksTo = {};
+ this.externalFrom = {};
+ for (const l of this.nodeData.links) {
+ const [sourceNodeId, sourceNodeSlot, targetNodeId, targetNodeSlot] = l;
+ if (sourceNodeId == null) continue;
+ if (!this.linksFrom[sourceNodeId]) {
+ this.linksFrom[sourceNodeId] = {};
+ }
+ if (!this.linksFrom[sourceNodeId][sourceNodeSlot]) {
+ this.linksFrom[sourceNodeId][sourceNodeSlot] = [];
+ }
+ this.linksFrom[sourceNodeId][sourceNodeSlot].push(l);
+ if (!this.linksTo[targetNodeId]) {
+ this.linksTo[targetNodeId] = {};
+ }
+ this.linksTo[targetNodeId][targetNodeSlot] = l;
+ }
+ if (this.nodeData.external) {
+ for (const ext2 of this.nodeData.external) {
+ if (!this.externalFrom[ext2[0]]) {
+ this.externalFrom[ext2[0]] = { [ext2[1]]: ext2[2] };
+ } else {
+ this.externalFrom[ext2[0]][ext2[1]] = ext2[2];
+ }
+ }
+ }
+ }
+ processNode(node, seenInputs, seenOutputs) {
+ var _a, _b, _c;
+ const def = this.getNodeDef(node);
+ if (!def) return;
+ const inputs = __spreadValues(__spreadValues({}, (_a = def.input) == null ? void 0 : _a.required), (_b = def.input) == null ? void 0 : _b.optional);
+ this.inputs.push(this.processNodeInputs(node, seenInputs, inputs));
+ if ((_c = def.output) == null ? void 0 : _c.length) this.processNodeOutputs(node, seenOutputs, def);
+ }
+ getNodeDef(node) {
+ var _a, _b, _c, _d, _e;
+ const def = globalDefs[node.type];
+ if (def) return def;
+ const linksFrom = this.linksFrom[node.index];
+ if (node.type === "PrimitiveNode") {
+ if (!linksFrom) return;
+ let type = linksFrom["0"][0][5];
+ if (type === "COMBO") {
+ const source = node.outputs[0].widget.name;
+ const fromTypeName = this.nodeData.nodes[linksFrom["0"][0][2]].type;
+ const fromType = globalDefs[fromTypeName];
+ const input = (_a = fromType.input.required[source]) != null ? _a : fromType.input.optional[source];
+ type = input[0];
+ }
+ const def2 = this.primitiveDefs[node.index] = {
+ input: {
+ required: {
+ value: [type, {}]
+ }
+ },
+ output: [type],
+ output_name: [],
+ output_is_list: []
+ };
+ return def2;
+ } else if (node.type === "Reroute") {
+ const linksTo = this.linksTo[node.index];
+ if (linksTo && linksFrom && !((_b = this.externalFrom[node.index]) == null ? void 0 : _b[0])) {
+ return null;
+ }
+ let config = {};
+ let rerouteType = "*";
+ if (linksFrom) {
+ for (const [, , id2, slot] of linksFrom["0"]) {
+ const node2 = this.nodeData.nodes[id2];
+ const input = node2.inputs[slot];
+ if (rerouteType === "*") {
+ rerouteType = input.type;
+ }
+ if (input.widget) {
+ const targetDef = globalDefs[node2.type];
+ const targetWidget = (_c = targetDef.input.required[input.widget.name]) != null ? _c : targetDef.input.optional[input.widget.name];
+ const widget = [targetWidget[0], config];
+ const res = mergeIfValid(
+ {
+ widget
+ },
+ targetWidget,
+ false,
+ null,
+ widget
+ );
+ config = (_d = res == null ? void 0 : res.customConfig) != null ? _d : config;
+ }
+ }
+ } else if (linksTo) {
+ const [id2, slot] = linksTo["0"];
+ rerouteType = this.nodeData.nodes[id2].outputs[slot].type;
+ } else {
+ for (const l of this.nodeData.links) {
+ if (l[2] === node.index) {
+ rerouteType = l[5];
+ break;
+ }
+ }
+ if (rerouteType === "*") {
+ const t = (_e = this.externalFrom[node.index]) == null ? void 0 : _e[0];
+ if (t) {
+ rerouteType = t;
+ }
+ }
+ }
+ config.forceInput = true;
+ return {
+ input: {
+ required: {
+ [rerouteType]: [rerouteType, config]
+ }
+ },
+ output: [rerouteType],
+ output_name: [],
+ output_is_list: []
+ };
+ }
+ console.warn(
+ "Skipping virtual node " + node.type + " when building group node " + this.name
+ );
+ }
+ getInputConfig(node, inputName, seenInputs, config, extra) {
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
+ const customConfig = (_c = (_b = (_a = this.nodeData.config) == null ? void 0 : _a[node.index]) == null ? void 0 : _b.input) == null ? void 0 : _c[inputName];
+ let name = (_g = (_f = customConfig == null ? void 0 : customConfig.name) != null ? _f : (_e = (_d = node.inputs) == null ? void 0 : _d.find((inp) => inp.name === inputName)) == null ? void 0 : _e.label) != null ? _g : inputName;
+ let key = name;
+ let prefix = "";
+ if (node.type === "PrimitiveNode" && node.title || name in seenInputs) {
+ prefix = `${(_h = node.title) != null ? _h : node.type} `;
+ key = name = `${prefix}${inputName}`;
+ if (name in seenInputs) {
+ name = `${prefix}${seenInputs[name]} ${inputName}`;
+ }
+ }
+ seenInputs[key] = ((_i = seenInputs[key]) != null ? _i : 1) + 1;
+ if (inputName === "seed" || inputName === "noise_seed") {
+ if (!extra) extra = {};
+ extra.control_after_generate = `${prefix}control_after_generate`;
+ }
+ if (config[0] === "IMAGEUPLOAD") {
+ if (!extra) extra = {};
+ extra.widget = (_m = (_l = this.oldToNewWidgetMap[node.index]) == null ? void 0 : _l[(_k = (_j = config[1]) == null ? void 0 : _j.widget) != null ? _k : "image"]) != null ? _m : "image";
+ }
+ if (extra) {
+ config = [config[0], __spreadValues(__spreadValues({}, config[1]), extra)];
+ }
+ return { name, config, customConfig };
+ }
+ processWidgetInputs(inputs, node, inputNames, seenInputs) {
+ var _a;
+ const slots = [];
+ const converted = /* @__PURE__ */ new Map();
+ const widgetMap = this.oldToNewWidgetMap[node.index] = {};
+ for (const inputName of inputNames) {
+ let widgetType = app.getWidgetType(inputs[inputName], inputName);
+ if (widgetType) {
+ const convertedIndex = (_a = node.inputs) == null ? void 0 : _a.findIndex(
+ (inp) => {
+ var _a2;
+ return inp.name === inputName && ((_a2 = inp.widget) == null ? void 0 : _a2.name) === inputName;
+ }
+ );
+ if (convertedIndex > -1) {
+ converted.set(convertedIndex, inputName);
+ widgetMap[inputName] = null;
+ } else {
+ const { name, config } = this.getInputConfig(
+ node,
+ inputName,
+ seenInputs,
+ inputs[inputName]
+ );
+ this.nodeDef.input.required[name] = config;
+ widgetMap[inputName] = name;
+ this.newToOldWidgetMap[name] = { node, inputName };
+ }
+ } else {
+ slots.push(inputName);
+ }
+ }
+ return { converted, slots };
+ }
+ checkPrimitiveConnection(link, inputName, inputs) {
+ var _a;
+ const sourceNode = this.nodeData.nodes[link[0]];
+ if (sourceNode.type === "PrimitiveNode") {
+ const [sourceNodeId, _, targetNodeId, __] = link;
+ const primitiveDef = this.primitiveDefs[sourceNodeId];
+ const targetWidget = inputs[inputName];
+ const primitiveConfig = primitiveDef.input.required.value;
+ const output = { widget: primitiveConfig };
+ const config = mergeIfValid(
+ output,
+ targetWidget,
+ false,
+ null,
+ primitiveConfig
+ );
+ primitiveConfig[1] = ((_a = config == null ? void 0 : config.customConfig) != null ? _a : inputs[inputName][1]) ? __spreadValues({}, inputs[inputName][1]) : {};
+ let name = this.oldToNewWidgetMap[sourceNodeId]["value"];
+ name = name.substr(0, name.length - 6);
+ primitiveConfig[1].control_after_generate = true;
+ primitiveConfig[1].control_prefix = name;
+ let toPrimitive = this.widgetToPrimitive[targetNodeId];
+ if (!toPrimitive) {
+ toPrimitive = this.widgetToPrimitive[targetNodeId] = {};
+ }
+ if (toPrimitive[inputName]) {
+ toPrimitive[inputName].push(sourceNodeId);
+ }
+ toPrimitive[inputName] = sourceNodeId;
+ let toWidget = this.primitiveToWidget[sourceNodeId];
+ if (!toWidget) {
+ toWidget = this.primitiveToWidget[sourceNodeId] = [];
+ }
+ toWidget.push({ nodeId: targetNodeId, inputName });
+ }
+ }
+ processInputSlots(inputs, node, slots, linksTo, inputMap, seenInputs) {
+ this.nodeInputs[node.index] = {};
+ for (let i = 0; i < slots.length; i++) {
+ const inputName = slots[i];
+ if (linksTo[i]) {
+ this.checkPrimitiveConnection(linksTo[i], inputName, inputs);
+ continue;
+ }
+ const { name, config, customConfig } = this.getInputConfig(
+ node,
+ inputName,
+ seenInputs,
+ inputs[inputName]
+ );
+ this.nodeInputs[node.index][inputName] = name;
+ if ((customConfig == null ? void 0 : customConfig.visible) === false) continue;
+ this.nodeDef.input.required[name] = config;
+ inputMap[i] = this.inputCount++;
+ }
+ }
+ processConvertedWidgets(inputs, node, slots, converted, linksTo, inputMap, seenInputs) {
+ const convertedSlots = [...converted.keys()].sort().map((k) => converted.get(k));
+ for (let i = 0; i < convertedSlots.length; i++) {
+ const inputName = convertedSlots[i];
+ if (linksTo[slots.length + i]) {
+ this.checkPrimitiveConnection(
+ linksTo[slots.length + i],
+ inputName,
+ inputs
+ );
+ continue;
+ }
+ const { name, config } = this.getInputConfig(
+ node,
+ inputName,
+ seenInputs,
+ inputs[inputName],
+ {
+ defaultInput: true
+ }
+ );
+ this.nodeDef.input.required[name] = config;
+ this.newToOldWidgetMap[name] = { node, inputName };
+ if (!this.oldToNewWidgetMap[node.index]) {
+ this.oldToNewWidgetMap[node.index] = {};
+ }
+ this.oldToNewWidgetMap[node.index][inputName] = name;
+ inputMap[slots.length + i] = this.inputCount++;
+ }
+ }
+ processNodeInputs(node, seenInputs, inputs) {
+ var _a;
+ const inputMapping = [];
+ const inputNames = Object.keys(inputs);
+ if (!inputNames.length) return;
+ const { converted, slots } = this.processWidgetInputs(
+ inputs,
+ node,
+ inputNames,
+ seenInputs
+ );
+ const linksTo = (_a = this.linksTo[node.index]) != null ? _a : {};
+ const inputMap = this.oldToNewInputMap[node.index] = {};
+ this.processInputSlots(inputs, node, slots, linksTo, inputMap, seenInputs);
+ __privateGet(this, _convertedToProcess).push(
+ () => this.processConvertedWidgets(
+ inputs,
+ node,
+ slots,
+ converted,
+ linksTo,
+ inputMap,
+ seenInputs
+ )
+ );
+ return inputMapping;
+ }
+ processNodeOutputs(node, seenOutputs, def) {
+ var _a, _b, _c, _d, _e, _f, _g, _h;
+ const oldToNew = this.oldToNewOutputMap[node.index] = {};
+ for (let outputId = 0; outputId < def.output.length; outputId++) {
+ const linksFrom = this.linksFrom[node.index];
+ const hasLink = (linksFrom == null ? void 0 : linksFrom[outputId]) && !((_a = this.externalFrom[node.index]) == null ? void 0 : _a[outputId]);
+ const customConfig = (_d = (_c = (_b = this.nodeData.config) == null ? void 0 : _b[node.index]) == null ? void 0 : _c.output) == null ? void 0 : _d[outputId];
+ const visible = (_e = customConfig == null ? void 0 : customConfig.visible) != null ? _e : !hasLink;
+ this.outputVisibility.push(visible);
+ if (!visible) {
+ continue;
+ }
+ oldToNew[outputId] = this.nodeDef.output.length;
+ this.newToOldOutputMap[this.nodeDef.output.length] = {
+ node,
+ slot: outputId
+ };
+ this.nodeDef.output.push(def.output[outputId]);
+ this.nodeDef.output_is_list.push(def.output_is_list[outputId]);
+ let label = customConfig == null ? void 0 : customConfig.name;
+ if (!label) {
+ label = (_g = (_f = def.output_name) == null ? void 0 : _f[outputId]) != null ? _g : def.output[outputId];
+ const output = node.outputs.find((o) => o.name === label);
+ if (output == null ? void 0 : output.label) {
+ label = output.label;
+ }
+ }
+ let name = label;
+ if (name in seenOutputs) {
+ const prefix = `${(_h = node.title) != null ? _h : node.type} `;
+ name = `${prefix}${label}`;
+ if (name in seenOutputs) {
+ name = `${prefix}${node.index} ${label}`;
+ }
+ }
+ seenOutputs[name] = 1;
+ this.nodeDef.output_name.push(name);
+ }
+ }
+ static registerFromWorkflow(groupNodes, missingNodeTypes) {
+ return __async(this, null, function* () {
+ const clean = app.clean;
+ app.clean = function() {
+ for (const g in groupNodes) {
+ try {
+ LiteGraph.unregisterNodeType("workflow/" + g);
+ } catch (error) {
+ }
+ }
+ app.clean = clean;
+ };
+ for (const g in groupNodes) {
+ const groupData = groupNodes[g];
+ let hasMissing = false;
+ for (const n of groupData.nodes) {
+ if (!(n.type in LiteGraph.registered_node_types)) {
+ missingNodeTypes.push({
+ type: n.type,
+ hint: ` (In group node 'workflow/${g}')`
+ });
+ missingNodeTypes.push({
+ type: "workflow/" + g,
+ action: {
+ text: "Remove from workflow",
+ callback: /* @__PURE__ */ __name((e) => {
+ delete groupNodes[g];
+ e.target.textContent = "Removed";
+ e.target.style.pointerEvents = "none";
+ e.target.style.opacity = 0.7;
+ }, "callback")
+ }
+ });
+ hasMissing = true;
+ }
+ }
+ if (hasMissing) continue;
+ const config = new _GroupNodeConfig(g, groupData);
+ yield config.registerType();
+ }
+ });
+ }
+};
+_convertedToProcess = new WeakMap();
+__name(_GroupNodeConfig, "GroupNodeConfig");
+let GroupNodeConfig = _GroupNodeConfig;
+const _GroupNodeHandler = class _GroupNodeHandler {
+ constructor(node) {
+ __publicField(this, "node");
+ __publicField(this, "groupData");
+ __publicField(this, "innerNodes");
+ var _a, _b;
+ this.node = node;
+ this.groupData = (_b = (_a = node.constructor) == null ? void 0 : _a.nodeData) == null ? void 0 : _b[GROUP];
+ this.node.setInnerNodes = (innerNodes) => {
+ var _a2;
+ this.innerNodes = innerNodes;
+ for (let innerNodeIndex = 0; innerNodeIndex < this.innerNodes.length; innerNodeIndex++) {
+ const innerNode = this.innerNodes[innerNodeIndex];
+ for (const w of (_a2 = innerNode.widgets) != null ? _a2 : []) {
+ if (w.type === "converted-widget") {
+ w.serializeValue = w.origSerializeValue;
+ }
+ }
+ innerNode.index = innerNodeIndex;
+ innerNode.getInputNode = (slot) => {
+ var _a3, _b2;
+ const externalSlot = (_a3 = this.groupData.oldToNewInputMap[innerNode.index]) == null ? void 0 : _a3[slot];
+ if (externalSlot != null) {
+ return this.node.getInputNode(externalSlot);
+ }
+ const innerLink = (_b2 = this.groupData.linksTo[innerNode.index]) == null ? void 0 : _b2[slot];
+ if (!innerLink) return null;
+ const inputNode = innerNodes[innerLink[0]];
+ if (inputNode.type === "PrimitiveNode") return null;
+ return inputNode;
+ };
+ innerNode.getInputLink = (slot) => {
+ var _a3, _b2;
+ const externalSlot = (_a3 = this.groupData.oldToNewInputMap[innerNode.index]) == null ? void 0 : _a3[slot];
+ if (externalSlot != null) {
+ const linkId = this.node.inputs[externalSlot].link;
+ let link2 = app.graph.links[linkId];
+ link2 = __spreadProps(__spreadValues({}, link2), {
+ target_id: innerNode.id,
+ target_slot: +slot
+ });
+ return link2;
+ }
+ let link = (_b2 = this.groupData.linksTo[innerNode.index]) == null ? void 0 : _b2[slot];
+ if (!link) return null;
+ link = {
+ origin_id: innerNodes[link[0]].id,
+ origin_slot: link[1],
+ target_id: innerNode.id,
+ target_slot: +slot
+ };
+ return link;
+ };
+ }
+ };
+ this.node.updateLink = (link) => {
+ var _a2;
+ link = __spreadValues({}, link);
+ const output = this.groupData.newToOldOutputMap[link.origin_slot];
+ let innerNode = this.innerNodes[output.node.index];
+ let l;
+ while ((innerNode == null ? void 0 : innerNode.type) === "Reroute") {
+ l = innerNode.getInputLink(0);
+ innerNode = innerNode.getInputNode(0);
+ }
+ if (!innerNode) {
+ return null;
+ }
+ if (l && _GroupNodeHandler.isGroupNode(innerNode)) {
+ return innerNode.updateLink(l);
+ }
+ link.origin_id = innerNode.id;
+ link.origin_slot = (_a2 = l == null ? void 0 : l.origin_slot) != null ? _a2 : output.slot;
+ return link;
+ };
+ this.node.getInnerNodes = () => {
+ if (!this.innerNodes) {
+ this.node.setInnerNodes(
+ this.groupData.nodeData.nodes.map((n, i) => {
+ const innerNode = LiteGraph.createNode(n.type);
+ innerNode.configure(n);
+ innerNode.id = `${this.node.id}:${i}`;
+ return innerNode;
+ })
+ );
+ }
+ this.updateInnerWidgets();
+ return this.innerNodes;
+ };
+ this.node.recreate = () => __async(this, null, function* () {
+ const id2 = this.node.id;
+ const sz = this.node.size;
+ const nodes = this.node.convertToNodes();
+ const groupNode = LiteGraph.createNode(this.node.type);
+ groupNode.id = id2;
+ groupNode.setInnerNodes(nodes);
+ groupNode[GROUP].populateWidgets();
+ app.graph.add(groupNode);
+ groupNode.size = [
+ Math.max(groupNode.size[0], sz[0]),
+ Math.max(groupNode.size[1], sz[1])
+ ];
+ groupNode[GROUP].replaceNodes(nodes);
+ return groupNode;
+ });
+ this.node.convertToNodes = () => {
+ const addInnerNodes = /* @__PURE__ */ __name(() => {
+ var _a2, _b2;
+ const backup = localStorage.getItem("litegrapheditor_clipboard");
+ const c = __spreadValues({}, this.groupData.nodeData);
+ c.nodes = [...c.nodes];
+ const innerNodes = this.node.getInnerNodes();
+ let ids = [];
+ for (let i = 0; i < c.nodes.length; i++) {
+ let id2 = (_a2 = innerNodes == null ? void 0 : innerNodes[i]) == null ? void 0 : _a2.id;
+ if (id2 == null || isNaN(id2)) {
+ id2 = void 0;
+ } else {
+ ids.push(id2);
+ }
+ c.nodes[i] = __spreadProps(__spreadValues({}, c.nodes[i]), { id: id2 });
+ }
+ localStorage.setItem("litegrapheditor_clipboard", JSON.stringify(c));
+ app.canvas.pasteFromClipboard();
+ localStorage.setItem("litegrapheditor_clipboard", backup);
+ const [x, y] = this.node.pos;
+ let top;
+ let left;
+ const selectedIds2 = ids.length ? ids : Object.keys(app.canvas.selected_nodes);
+ const newNodes2 = [];
+ for (let i = 0; i < selectedIds2.length; i++) {
+ const id2 = selectedIds2[i];
+ const newNode = app.graph.getNodeById(id2);
+ const innerNode = innerNodes[i];
+ newNodes2.push(newNode);
+ if (left == null || newNode.pos[0] < left) {
+ left = newNode.pos[0];
+ }
+ if (top == null || newNode.pos[1] < top) {
+ top = newNode.pos[1];
+ }
+ if (!newNode.widgets) continue;
+ const map = this.groupData.oldToNewWidgetMap[innerNode.index];
+ if (map) {
+ const widgets = Object.keys(map);
+ for (const oldName of widgets) {
+ const newName = map[oldName];
+ if (!newName) continue;
+ const widgetIndex = this.node.widgets.findIndex(
+ (w) => w.name === newName
+ );
+ if (widgetIndex === -1) continue;
+ if (innerNode.type === "PrimitiveNode") {
+ for (let i2 = 0; i2 < newNode.widgets.length; i2++) {
+ newNode.widgets[i2].value = this.node.widgets[widgetIndex + i2].value;
+ }
+ } else {
+ const outerWidget = this.node.widgets[widgetIndex];
+ const newWidget = newNode.widgets.find(
+ (w) => w.name === oldName
+ );
+ if (!newWidget) continue;
+ newWidget.value = outerWidget.value;
+ for (let w = 0; w < ((_b2 = outerWidget.linkedWidgets) == null ? void 0 : _b2.length); w++) {
+ newWidget.linkedWidgets[w].value = outerWidget.linkedWidgets[w].value;
+ }
+ }
+ }
+ }
+ }
+ for (const newNode of newNodes2) {
+ newNode.pos = [
+ newNode.pos[0] - (left - x),
+ newNode.pos[1] - (top - y)
+ ];
+ }
+ return { newNodes: newNodes2, selectedIds: selectedIds2 };
+ }, "addInnerNodes");
+ const reconnectInputs = /* @__PURE__ */ __name((selectedIds2) => {
+ for (const innerNodeIndex in this.groupData.oldToNewInputMap) {
+ const id2 = selectedIds2[innerNodeIndex];
+ const newNode = app.graph.getNodeById(id2);
+ const map = this.groupData.oldToNewInputMap[innerNodeIndex];
+ for (const innerInputId in map) {
+ const groupSlotId = map[innerInputId];
+ if (groupSlotId == null) continue;
+ const slot = node.inputs[groupSlotId];
+ if (slot.link == null) continue;
+ const link = app.graph.links[slot.link];
+ if (!link) continue;
+ const originNode = app.graph.getNodeById(link.origin_id);
+ originNode.connect(link.origin_slot, newNode, +innerInputId);
+ }
+ }
+ }, "reconnectInputs");
+ const reconnectOutputs = /* @__PURE__ */ __name((selectedIds2) => {
+ var _a2;
+ for (let groupOutputId = 0; groupOutputId < ((_a2 = node.outputs) == null ? void 0 : _a2.length); groupOutputId++) {
+ const output = node.outputs[groupOutputId];
+ if (!output.links) continue;
+ const links = [...output.links];
+ for (const l of links) {
+ const slot = this.groupData.newToOldOutputMap[groupOutputId];
+ const link = app.graph.links[l];
+ const targetNode = app.graph.getNodeById(link.target_id);
+ const newNode = app.graph.getNodeById(selectedIds2[slot.node.index]);
+ newNode.connect(slot.slot, targetNode, link.target_slot);
+ }
+ }
+ }, "reconnectOutputs");
+ const { newNodes, selectedIds } = addInnerNodes();
+ reconnectInputs(selectedIds);
+ reconnectOutputs(selectedIds);
+ app.graph.remove(this.node);
+ return newNodes;
+ };
+ const getExtraMenuOptions = this.node.getExtraMenuOptions;
+ this.node.getExtraMenuOptions = function(_, options) {
+ getExtraMenuOptions == null ? void 0 : getExtraMenuOptions.apply(this, arguments);
+ let optionIndex = options.findIndex((o) => o.content === "Outputs");
+ if (optionIndex === -1) optionIndex = options.length;
+ else optionIndex++;
+ options.splice(
+ optionIndex,
+ 0,
+ null,
+ {
+ content: "Convert to nodes",
+ callback: /* @__PURE__ */ __name(() => {
+ return this.convertToNodes();
+ }, "callback")
+ },
+ {
+ content: "Manage Group Node",
+ callback: /* @__PURE__ */ __name(() => {
+ new ManageGroupDialog(app).show(this.type);
+ }, "callback")
+ }
+ );
+ };
+ const onDrawTitleBox = this.node.onDrawTitleBox;
+ this.node.onDrawTitleBox = function(ctx, height, size, scale) {
+ onDrawTitleBox == null ? void 0 : onDrawTitleBox.apply(this, arguments);
+ const fill = ctx.fillStyle;
+ ctx.beginPath();
+ ctx.rect(11, -height + 11, 2, 2);
+ ctx.rect(14, -height + 11, 2, 2);
+ ctx.rect(17, -height + 11, 2, 2);
+ ctx.rect(11, -height + 14, 2, 2);
+ ctx.rect(14, -height + 14, 2, 2);
+ ctx.rect(17, -height + 14, 2, 2);
+ ctx.rect(11, -height + 17, 2, 2);
+ ctx.rect(14, -height + 17, 2, 2);
+ ctx.rect(17, -height + 17, 2, 2);
+ ctx.fillStyle = this.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
+ ctx.fill();
+ ctx.fillStyle = fill;
+ };
+ const onDrawForeground = node.onDrawForeground;
+ const groupData = this.groupData.nodeData;
+ node.onDrawForeground = function(ctx) {
+ var _a2;
+ const r = (_a2 = onDrawForeground == null ? void 0 : onDrawForeground.apply) == null ? void 0 : _a2.call(onDrawForeground, this, arguments);
+ if (+app.runningNodeId === this.id && this.runningInternalNodeId !== null) {
+ const n = groupData.nodes[this.runningInternalNodeId];
+ if (!n) return;
+ const message = `Running ${n.title || n.type} (${this.runningInternalNodeId}/${groupData.nodes.length})`;
+ ctx.save();
+ ctx.font = "12px sans-serif";
+ const sz = ctx.measureText(message);
+ ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
+ ctx.beginPath();
+ ctx.roundRect(
+ 0,
+ -LiteGraph.NODE_TITLE_HEIGHT - 20,
+ sz.width + 12,
+ 20,
+ 5
+ );
+ ctx.fill();
+ ctx.fillStyle = "#fff";
+ ctx.fillText(message, 6, -LiteGraph.NODE_TITLE_HEIGHT - 6);
+ ctx.restore();
+ }
+ };
+ const onExecutionStart = this.node.onExecutionStart;
+ this.node.onExecutionStart = function() {
+ this.resetExecution = true;
+ return onExecutionStart == null ? void 0 : onExecutionStart.apply(this, arguments);
+ };
+ const self = this;
+ const onNodeCreated = this.node.onNodeCreated;
+ this.node.onNodeCreated = function() {
+ var _a2;
+ if (!this.widgets) {
+ return;
+ }
+ const config = self.groupData.nodeData.config;
+ if (config) {
+ for (const n in config) {
+ const inputs = (_a2 = config[n]) == null ? void 0 : _a2.input;
+ for (const w in inputs) {
+ if (inputs[w].visible !== false) continue;
+ const widgetName = self.groupData.oldToNewWidgetMap[n][w];
+ const widget = this.widgets.find((w2) => w2.name === widgetName);
+ if (widget) {
+ widget.type = "hidden";
+ widget.computeSize = () => [0, -4];
+ }
+ }
+ }
+ }
+ return onNodeCreated == null ? void 0 : onNodeCreated.apply(this, arguments);
+ };
+ function handleEvent(type, getId, getEvent) {
+ const handler = /* @__PURE__ */ __name(({ detail }) => {
+ var _a2;
+ const id2 = getId(detail);
+ if (!id2) return;
+ const node2 = app.graph.getNodeById(id2);
+ if (node2) return;
+ const innerNodeIndex = (_a2 = this.innerNodes) == null ? void 0 : _a2.findIndex((n) => n.id == id2);
+ if (innerNodeIndex > -1) {
+ this.node.runningInternalNodeId = innerNodeIndex;
+ api.dispatchEvent(
+ new CustomEvent(type, {
+ detail: getEvent(detail, this.node.id + "", this.node)
+ })
+ );
+ }
+ }, "handler");
+ api.addEventListener(type, handler);
+ return handler;
+ }
+ __name(handleEvent, "handleEvent");
+ const executing = handleEvent.call(
+ this,
+ "executing",
+ (d) => d,
+ (d, id2, node2) => id2
+ );
+ const executed = handleEvent.call(
+ this,
+ "executed",
+ (d) => (d == null ? void 0 : d.display_node) || (d == null ? void 0 : d.node),
+ (d, id2, node2) => __spreadProps(__spreadValues({}, d), {
+ node: id2,
+ display_node: id2,
+ merge: !node2.resetExecution
+ })
+ );
+ const onRemoved = node.onRemoved;
+ this.node.onRemoved = function() {
+ onRemoved == null ? void 0 : onRemoved.apply(this, arguments);
+ api.removeEventListener("executing", executing);
+ api.removeEventListener("executed", executed);
+ };
+ this.node.refreshComboInNode = (defs) => {
+ var _a2, _b2, _c, _d, _e;
+ for (const widgetName in this.groupData.newToOldWidgetMap) {
+ const widget = this.node.widgets.find((w) => w.name === widgetName);
+ if ((widget == null ? void 0 : widget.type) === "combo") {
+ const old = this.groupData.newToOldWidgetMap[widgetName];
+ const def = defs[old.node.type];
+ const input = (_e = (_b2 = (_a2 = def == null ? void 0 : def.input) == null ? void 0 : _a2.required) == null ? void 0 : _b2[old.inputName]) != null ? _e : (_d = (_c = def == null ? void 0 : def.input) == null ? void 0 : _c.optional) == null ? void 0 : _d[old.inputName];
+ if (!input) continue;
+ widget.options.values = input[0];
+ if (old.inputName !== "image" && !widget.options.values.includes(widget.value)) {
+ widget.value = widget.options.values[0];
+ widget.callback(widget.value);
+ }
+ }
+ }
+ };
+ }
+ updateInnerWidgets() {
+ var _a, _b;
+ for (const newWidgetName in this.groupData.newToOldWidgetMap) {
+ const newWidget = this.node.widgets.find((w) => w.name === newWidgetName);
+ if (!newWidget) continue;
+ const newValue = newWidget.value;
+ const old = this.groupData.newToOldWidgetMap[newWidgetName];
+ let innerNode = this.innerNodes[old.node.index];
+ if (innerNode.type === "PrimitiveNode") {
+ innerNode.primitiveValue = newValue;
+ const primitiveLinked = this.groupData.primitiveToWidget[old.node.index];
+ for (const linked of primitiveLinked != null ? primitiveLinked : []) {
+ const node = this.innerNodes[linked.nodeId];
+ const widget2 = node.widgets.find((w) => w.name === linked.inputName);
+ if (widget2) {
+ widget2.value = newValue;
+ }
+ }
+ continue;
+ } else if (innerNode.type === "Reroute") {
+ const rerouteLinks = this.groupData.linksFrom[old.node.index];
+ if (rerouteLinks) {
+ for (const [_, , targetNodeId, targetSlot] of rerouteLinks["0"]) {
+ const node = this.innerNodes[targetNodeId];
+ const input = node.inputs[targetSlot];
+ if (input.widget) {
+ const widget2 = (_a = node.widgets) == null ? void 0 : _a.find(
+ (w) => w.name === input.widget.name
+ );
+ if (widget2) {
+ widget2.value = newValue;
+ }
+ }
+ }
+ }
+ }
+ const widget = (_b = innerNode.widgets) == null ? void 0 : _b.find((w) => w.name === old.inputName);
+ if (widget) {
+ widget.value = newValue;
+ }
+ }
+ }
+ populatePrimitive(node, nodeId, oldName, i, linkedShift) {
+ var _a, _b;
+ const primitiveId = (_a = this.groupData.widgetToPrimitive[nodeId]) == null ? void 0 : _a[oldName];
+ if (primitiveId == null) return;
+ const targetWidgetName = this.groupData.oldToNewWidgetMap[primitiveId]["value"];
+ const targetWidgetIndex = this.node.widgets.findIndex(
+ (w) => w.name === targetWidgetName
+ );
+ if (targetWidgetIndex > -1) {
+ const primitiveNode = this.innerNodes[primitiveId];
+ let len = primitiveNode.widgets.length;
+ if (len - 1 !== ((_b = this.node.widgets[targetWidgetIndex].linkedWidgets) == null ? void 0 : _b.length)) {
+ len = 1;
+ }
+ for (let i2 = 0; i2 < len; i2++) {
+ this.node.widgets[targetWidgetIndex + i2].value = primitiveNode.widgets[i2].value;
+ }
+ }
+ return true;
+ }
+ populateReroute(node, nodeId, map) {
+ var _a, _b, _c, _d, _e, _f;
+ if (node.type !== "Reroute") return;
+ const link = (_b = (_a = this.groupData.linksFrom[nodeId]) == null ? void 0 : _a[0]) == null ? void 0 : _b[0];
+ if (!link) return;
+ const [, , targetNodeId, targetNodeSlot] = link;
+ const targetNode = this.groupData.nodeData.nodes[targetNodeId];
+ const inputs = targetNode.inputs;
+ const targetWidget = (_c = inputs == null ? void 0 : inputs[targetNodeSlot]) == null ? void 0 : _c.widget;
+ if (!targetWidget) return;
+ const offset = inputs.length - ((_e = (_d = targetNode.widgets_values) == null ? void 0 : _d.length) != null ? _e : 0);
+ const v = (_f = targetNode.widgets_values) == null ? void 0 : _f[targetNodeSlot - offset];
+ if (v == null) return;
+ const widgetName = Object.values(map)[0];
+ const widget = this.node.widgets.find((w) => w.name === widgetName);
+ if (widget) {
+ widget.value = v;
+ }
+ }
+ populateWidgets() {
+ var _a, _b, _c, _d, _e, _f;
+ if (!this.node.widgets) return;
+ for (let nodeId = 0; nodeId < this.groupData.nodeData.nodes.length; nodeId++) {
+ const node = this.groupData.nodeData.nodes[nodeId];
+ const map = (_a = this.groupData.oldToNewWidgetMap[nodeId]) != null ? _a : {};
+ const widgets = Object.keys(map);
+ if (!((_b = node.widgets_values) == null ? void 0 : _b.length)) {
+ this.populateReroute(node, nodeId, map);
+ continue;
+ }
+ let linkedShift = 0;
+ for (let i = 0; i < widgets.length; i++) {
+ const oldName = widgets[i];
+ const newName = map[oldName];
+ const widgetIndex = this.node.widgets.findIndex(
+ (w) => w.name === newName
+ );
+ const mainWidget = this.node.widgets[widgetIndex];
+ if (this.populatePrimitive(node, nodeId, oldName, i, linkedShift) || widgetIndex === -1) {
+ const innerWidget = (_c = this.innerNodes[nodeId].widgets) == null ? void 0 : _c.find(
+ (w) => w.name === oldName
+ );
+ linkedShift += (_e = (_d = innerWidget == null ? void 0 : innerWidget.linkedWidgets) == null ? void 0 : _d.length) != null ? _e : 0;
+ }
+ if (widgetIndex === -1) {
+ continue;
+ }
+ mainWidget.value = node.widgets_values[i + linkedShift];
+ for (let w = 0; w < ((_f = mainWidget.linkedWidgets) == null ? void 0 : _f.length); w++) {
+ this.node.widgets[widgetIndex + w + 1].value = node.widgets_values[i + ++linkedShift];
+ }
+ }
+ }
+ }
+ replaceNodes(nodes) {
+ let top;
+ let left;
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i];
+ if (left == null || node.pos[0] < left) {
+ left = node.pos[0];
+ }
+ if (top == null || node.pos[1] < top) {
+ top = node.pos[1];
+ }
+ this.linkOutputs(node, i);
+ app.graph.remove(node);
+ }
+ this.linkInputs();
+ this.node.pos = [left, top];
+ }
+ linkOutputs(originalNode, nodeId) {
+ var _a;
+ if (!originalNode.outputs) return;
+ for (const output of originalNode.outputs) {
+ if (!output.links) continue;
+ const links = [...output.links];
+ for (const l of links) {
+ const link = app.graph.links[l];
+ if (!link) continue;
+ const targetNode = app.graph.getNodeById(link.target_id);
+ const newSlot = (_a = this.groupData.oldToNewOutputMap[nodeId]) == null ? void 0 : _a[link.origin_slot];
+ if (newSlot != null) {
+ this.node.connect(newSlot, targetNode, link.target_slot);
+ }
+ }
+ }
+ }
+ linkInputs() {
+ var _a;
+ for (const link of (_a = this.groupData.nodeData.links) != null ? _a : []) {
+ const [, originSlot, targetId, targetSlot, actualOriginId] = link;
+ const originNode = app.graph.getNodeById(actualOriginId);
+ if (!originNode) continue;
+ originNode.connect(
+ originSlot,
+ this.node.id,
+ this.groupData.oldToNewInputMap[targetId][targetSlot]
+ );
+ }
+ }
+ static getGroupData(node) {
+ var _a, _b, _c;
+ return (_c = (_b = node.nodeData) != null ? _b : (_a = node.constructor) == null ? void 0 : _a.nodeData) == null ? void 0 : _c[GROUP];
+ }
+ static isGroupNode(node) {
+ var _a, _b;
+ return !!((_b = (_a = node.constructor) == null ? void 0 : _a.nodeData) == null ? void 0 : _b[GROUP]);
+ }
+ static fromNodes(nodes) {
+ return __async(this, null, function* () {
+ const builder = new GroupNodeBuilder(nodes);
+ const res = builder.build();
+ if (!res) return;
+ const { name, nodeData } = res;
+ const config = new GroupNodeConfig(name, nodeData);
+ yield config.registerType();
+ const groupNode = LiteGraph.createNode(`workflow/${name}`);
+ groupNode.setInnerNodes(builder.nodes);
+ groupNode[GROUP].populateWidgets();
+ app.graph.add(groupNode);
+ groupNode[GROUP].replaceNodes(builder.nodes);
+ return groupNode;
+ });
+ }
+};
+__name(_GroupNodeHandler, "GroupNodeHandler");
+let GroupNodeHandler = _GroupNodeHandler;
+function addConvertToGroupOptions() {
+ function addConvertOption(options, index) {
+ var _a;
+ const selected = Object.values((_a = app.canvas.selected_nodes) != null ? _a : {});
+ const disabled = selected.length < 2 || selected.find((n) => GroupNodeHandler.isGroupNode(n));
+ options.splice(index + 1, null, {
+ content: `Convert to Group Node`,
+ disabled,
+ callback: /* @__PURE__ */ __name(() => __async(this, null, function* () {
+ return yield GroupNodeHandler.fromNodes(selected);
+ }), "callback")
+ });
+ }
+ __name(addConvertOption, "addConvertOption");
+ function addManageOption(options, index) {
+ var _a;
+ const groups = (_a = app.graph.extra) == null ? void 0 : _a.groupNodes;
+ const disabled = !groups || !Object.keys(groups).length;
+ options.splice(index + 1, null, {
+ content: `Manage Group Nodes`,
+ disabled,
+ callback: /* @__PURE__ */ __name(() => {
+ new ManageGroupDialog(app).show();
+ }, "callback")
+ });
+ }
+ __name(addManageOption, "addManageOption");
+ const getCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions;
+ LGraphCanvas.prototype.getCanvasMenuOptions = function() {
+ const options = getCanvasMenuOptions.apply(this, arguments);
+ const index = options.findIndex((o) => (o == null ? void 0 : o.content) === "Add Group") + 1 || options.length;
+ addConvertOption(options, index);
+ addManageOption(options, index + 1);
+ return options;
+ };
+ const getNodeMenuOptions = LGraphCanvas.prototype.getNodeMenuOptions;
+ LGraphCanvas.prototype.getNodeMenuOptions = function(node) {
+ const options = getNodeMenuOptions.apply(this, arguments);
+ if (!GroupNodeHandler.isGroupNode(node)) {
+ const index = options.findIndex((o) => (o == null ? void 0 : o.content) === "Outputs") + 1 || options.length - 1;
+ addConvertOption(options, index);
+ }
+ return options;
+ };
+}
+__name(addConvertToGroupOptions, "addConvertToGroupOptions");
+const id$3 = "Comfy.GroupNode";
+let globalDefs;
+const ext$1 = {
+ name: id$3,
+ setup() {
+ addConvertToGroupOptions();
+ },
+ beforeConfigureGraph(graphData, missingNodeTypes) {
+ return __async(this, null, function* () {
+ var _a;
+ const nodes = (_a = graphData == null ? void 0 : graphData.extra) == null ? void 0 : _a.groupNodes;
+ if (nodes) {
+ yield GroupNodeConfig.registerFromWorkflow(nodes, missingNodeTypes);
+ }
+ });
+ },
+ addCustomNodeDefs(defs) {
+ globalDefs = defs;
+ },
+ nodeCreated(node) {
+ if (GroupNodeHandler.isGroupNode(node)) {
+ node[GROUP] = new GroupNodeHandler(node);
+ }
+ },
+ refreshComboInNodes(defs) {
+ return __async(this, null, function* () {
+ var _a;
+ Object.assign(globalDefs, defs);
+ const nodes = (_a = app.graph.extra) == null ? void 0 : _a.groupNodes;
+ if (nodes) {
+ yield GroupNodeConfig.registerFromWorkflow(nodes, {});
+ }
+ });
+ }
+};
+app.registerExtension(ext$1);
+window.comfyAPI = window.comfyAPI || {};
+window.comfyAPI.groupNode = window.comfyAPI.groupNode || {};
+window.comfyAPI.groupNode.GroupNodeConfig = GroupNodeConfig;
+window.comfyAPI.groupNode.GroupNodeHandler = GroupNodeHandler;
+function setNodeMode(node, mode) {
+ node.mode = mode;
+ node.graph.change();
+}
+__name(setNodeMode, "setNodeMode");
+function addNodesToGroup(group, nodes = []) {
+ var _a;
+ var x1, y1, x2, y2;
+ var nx1, ny1, nx2, ny2;
+ var node;
+ x1 = y1 = x2 = y2 = -1;
+ nx1 = ny1 = nx2 = ny2 = -1;
+ for (var n of [group._nodes, nodes]) {
+ for (var i in n) {
+ node = n[i];
+ nx1 = node.pos[0];
+ ny1 = node.pos[1];
+ nx2 = node.pos[0] + node.size[0];
+ ny2 = node.pos[1] + node.size[1];
+ if (node.type != "Reroute") {
+ ny1 -= LiteGraph.NODE_TITLE_HEIGHT;
+ }
+ if ((_a = node.flags) == null ? void 0 : _a.collapsed) {
+ ny2 = ny1 + LiteGraph.NODE_TITLE_HEIGHT;
+ if (node == null ? void 0 : node._collapsed_width) {
+ nx2 = nx1 + Math.round(node._collapsed_width);
+ }
+ }
+ if (x1 == -1 || nx1 < x1) {
+ x1 = nx1;
+ }
+ if (y1 == -1 || ny1 < y1) {
+ y1 = ny1;
+ }
+ if (x2 == -1 || nx2 > x2) {
+ x2 = nx2;
+ }
+ if (y2 == -1 || ny2 > y2) {
+ y2 = ny2;
+ }
+ }
+ }
+ var padding = 10;
+ y1 = y1 - Math.round(group.font_size * 1.4);
+ group.pos = [x1 - padding, y1 - padding];
+ group.size = [x2 - x1 + padding * 2, y2 - y1 + padding * 2];
+}
+__name(addNodesToGroup, "addNodesToGroup");
+app.registerExtension({
+ name: "Comfy.GroupOptions",
+ setup() {
+ const orig = LGraphCanvas.prototype.getCanvasMenuOptions;
+ LGraphCanvas.prototype.getCanvasMenuOptions = function() {
+ const options = orig.apply(this, arguments);
+ const group = this.graph.getGroupOnPos(
+ this.graph_mouse[0],
+ this.graph_mouse[1]
+ );
+ if (!group) {
+ options.push({
+ content: "Add Group For Selected Nodes",
+ disabled: !Object.keys(app.canvas.selected_nodes || {}).length,
+ callback: /* @__PURE__ */ __name(() => {
+ var group2 = new LiteGraph.LGraphGroup();
+ addNodesToGroup(group2, this.selected_nodes);
+ app.canvas.graph.add(group2);
+ this.graph.change();
+ }, "callback")
+ });
+ return options;
+ }
+ group.recomputeInsideNodes();
+ const nodesInGroup = group._nodes;
+ options.push({
+ content: "Add Selected Nodes To Group",
+ disabled: !Object.keys(app.canvas.selected_nodes || {}).length,
+ callback: /* @__PURE__ */ __name(() => {
+ addNodesToGroup(group, this.selected_nodes);
+ this.graph.change();
+ }, "callback")
+ });
+ if (nodesInGroup.length === 0) {
+ return options;
+ } else {
+ options.push(null);
+ }
+ let allNodesAreSameMode = true;
+ for (let i = 1; i < nodesInGroup.length; i++) {
+ if (nodesInGroup[i].mode !== nodesInGroup[0].mode) {
+ allNodesAreSameMode = false;
+ break;
+ }
+ }
+ options.push({
+ content: "Fit Group To Nodes",
+ callback: /* @__PURE__ */ __name(() => {
+ addNodesToGroup(group);
+ this.graph.change();
+ }, "callback")
+ });
+ options.push({
+ content: "Select Nodes",
+ callback: /* @__PURE__ */ __name(() => {
+ this.selectNodes(nodesInGroup);
+ this.graph.change();
+ this.canvas.focus();
+ }, "callback")
+ });
+ if (allNodesAreSameMode) {
+ const mode = nodesInGroup[0].mode;
+ switch (mode) {
+ case 0:
+ options.push({
+ content: "Set Group Nodes to Never",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 2);
+ }
+ }, "callback")
+ });
+ options.push({
+ content: "Bypass Group Nodes",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 4);
+ }
+ }, "callback")
+ });
+ break;
+ case 2:
+ options.push({
+ content: "Set Group Nodes to Always",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 0);
+ }
+ }, "callback")
+ });
+ options.push({
+ content: "Bypass Group Nodes",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 4);
+ }
+ }, "callback")
+ });
+ break;
+ case 4:
+ options.push({
+ content: "Set Group Nodes to Always",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 0);
+ }
+ }, "callback")
+ });
+ options.push({
+ content: "Set Group Nodes to Never",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 2);
+ }
+ }, "callback")
+ });
+ break;
+ default:
+ options.push({
+ content: "Set Group Nodes to Always",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 0);
+ }
+ }, "callback")
+ });
+ options.push({
+ content: "Set Group Nodes to Never",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 2);
+ }
+ }, "callback")
+ });
+ options.push({
+ content: "Bypass Group Nodes",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 4);
+ }
+ }, "callback")
+ });
+ break;
+ }
+ } else {
+ options.push({
+ content: "Set Group Nodes to Always",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 0);
+ }
+ }, "callback")
+ });
+ options.push({
+ content: "Set Group Nodes to Never",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 2);
+ }
+ }, "callback")
+ });
+ options.push({
+ content: "Bypass Group Nodes",
+ callback: /* @__PURE__ */ __name(() => {
+ for (const node of nodesInGroup) {
+ setNodeMode(node, 4);
+ }
+ }, "callback")
+ });
+ }
+ return options;
+ };
+ }
+});
+const id$2 = "Comfy.InvertMenuScrolling";
+app.registerExtension({
+ name: id$2,
+ init() {
+ const ctxMenu = LiteGraph.ContextMenu;
+ const replace = /* @__PURE__ */ __name(() => {
+ LiteGraph.ContextMenu = function(values, options) {
+ options = options || {};
+ if (options.scroll_speed) {
+ options.scroll_speed *= -1;
+ } else {
+ options.scroll_speed = -0.1;
+ }
+ return ctxMenu.call(this, values, options);
+ };
+ LiteGraph.ContextMenu.prototype = ctxMenu.prototype;
+ }, "replace");
+ app.ui.settings.addSetting({
+ id: id$2,
+ name: "Invert Menu Scrolling",
+ type: "boolean",
+ defaultValue: false,
+ onChange(value) {
+ if (value) {
+ replace();
+ } else {
+ LiteGraph.ContextMenu = ctxMenu;
+ }
+ }
+ });
+ }
+});
+app.registerExtension({
+ name: "Comfy.Keybinds",
+ init() {
+ const keybindListener = /* @__PURE__ */ __name(function(event) {
+ const modifierPressed = event.ctrlKey || event.metaKey;
+ if (modifierPressed && event.key === "Enter") {
+ if (event.altKey) {
+ api.interrupt();
+ return;
+ }
+ app.queuePrompt(event.shiftKey ? -1 : 0).then();
+ return;
+ }
+ const target = event.composedPath()[0];
+ if (["INPUT", "TEXTAREA"].includes(target.tagName)) {
+ return;
+ }
+ const modifierKeyIdMap = {
+ s: "#comfy-save-button",
+ o: "#comfy-file-input",
+ Backspace: "#comfy-clear-button",
+ d: "#comfy-load-default-button"
+ };
+ const modifierKeybindId = modifierKeyIdMap[event.key];
+ if (modifierPressed && modifierKeybindId) {
+ event.preventDefault();
+ const elem = document.querySelector(modifierKeybindId);
+ elem.click();
+ return;
+ }
+ if (event.ctrlKey || event.altKey || event.metaKey) {
+ return;
+ }
+ if (event.key === "Escape") {
+ const modals = document.querySelectorAll(".comfy-modal");
+ const modal = Array.from(modals).find(
+ (modal2) => window.getComputedStyle(modal2).getPropertyValue("display") !== "none"
+ );
+ if (modal) {
+ modal.style.display = "none";
+ }
+ ;
+ [...document.querySelectorAll("dialog")].forEach((d) => {
+ d.close();
+ });
+ }
+ const keyIdMap = {
+ q: ".queue-tab-button.side-bar-button",
+ h: ".queue-tab-button.side-bar-button",
+ r: "#comfy-refresh-button"
+ };
+ const buttonId = keyIdMap[event.key];
+ if (buttonId) {
+ const button = document.querySelector(buttonId);
+ button.click();
+ }
+ }, "keybindListener");
+ window.addEventListener("keydown", keybindListener, true);
+ }
+});
+const id$1 = "Comfy.LinkRenderMode";
+const ext = {
+ name: id$1,
+ setup(app2) {
+ return __async(this, null, function* () {
+ app2.ui.settings.addSetting({
+ id: id$1,
+ name: "Link Render Mode",
+ defaultValue: 2,
+ type: "combo",
+ // @ts-expect-error
+ options: [...LiteGraph.LINK_RENDER_MODES, "Hidden"].map((m, i) => ({
+ value: i,
+ text: m,
+ selected: i == app2.canvas.links_render_mode
+ })),
+ onChange(value) {
+ app2.canvas.links_render_mode = +value;
+ app2.graph.setDirtyCanvas(true);
+ }
+ });
+ });
+ }
+};
+app.registerExtension(ext);
+function dataURLToBlob(dataURL) {
+ const parts = dataURL.split(";base64,");
+ const contentType = parts[0].split(":")[1];
+ const byteString = atob(parts[1]);
+ const arrayBuffer = new ArrayBuffer(byteString.length);
+ const uint8Array = new Uint8Array(arrayBuffer);
+ for (let i = 0; i < byteString.length; i++) {
+ uint8Array[i] = byteString.charCodeAt(i);
+ }
+ return new Blob([arrayBuffer], { type: contentType });
+}
+__name(dataURLToBlob, "dataURLToBlob");
+function loadedImageToBlob(image) {
+ const canvas = document.createElement("canvas");
+ canvas.width = image.width;
+ canvas.height = image.height;
+ const ctx = canvas.getContext("2d");
+ ctx.drawImage(image, 0, 0);
+ const dataURL = canvas.toDataURL("image/png", 1);
+ const blob = dataURLToBlob(dataURL);
+ return blob;
+}
+__name(loadedImageToBlob, "loadedImageToBlob");
+function loadImage(imagePath) {
+ return new Promise((resolve, reject) => {
+ const image = new Image();
+ image.onload = function() {
+ resolve(image);
+ };
+ image.src = imagePath;
+ });
+}
+__name(loadImage, "loadImage");
+function uploadMask(filepath, formData) {
+ return __async(this, null, function* () {
+ yield api.fetchApi("/upload/mask", {
+ method: "POST",
+ body: formData
+ }).then((response) => {
+ }).catch((error) => {
+ console.error("Error:", error);
+ });
+ ComfyApp.clipspace.imgs[ComfyApp.clipspace["selectedIndex"]] = new Image();
+ ComfyApp.clipspace.imgs[ComfyApp.clipspace["selectedIndex"]].src = api.apiURL(
+ "/view?" + new URLSearchParams(filepath).toString() + app.getPreviewFormatParam() + app.getRandParam()
+ );
+ if (ComfyApp.clipspace.images)
+ ComfyApp.clipspace.images[ComfyApp.clipspace["selectedIndex"]] = filepath;
+ ClipspaceDialog.invalidatePreview();
+ });
+}
+__name(uploadMask, "uploadMask");
+function prepare_mask(image, maskCanvas, maskCtx, maskColor) {
+ maskCtx.drawImage(image, 0, 0, maskCanvas.width, maskCanvas.height);
+ const maskData = maskCtx.getImageData(
+ 0,
+ 0,
+ maskCanvas.width,
+ maskCanvas.height
+ );
+ for (let i = 0; i < maskData.data.length; i += 4) {
+ if (maskData.data[i + 3] == 255) maskData.data[i + 3] = 0;
+ else maskData.data[i + 3] = 255;
+ maskData.data[i] = maskColor.r;
+ maskData.data[i + 1] = maskColor.g;
+ maskData.data[i + 2] = maskColor.b;
+ }
+ maskCtx.globalCompositeOperation = "source-over";
+ maskCtx.putImageData(maskData, 0, 0);
+}
+__name(prepare_mask, "prepare_mask");
+const _MaskEditorDialog = class _MaskEditorDialog extends ComfyDialog {
+ constructor() {
+ super();
+ __publicField(this, "brush");
+ __publicField(this, "maskCtx");
+ __publicField(this, "maskCanvas");
+ __publicField(this, "brush_size_slider");
+ __publicField(this, "brush_opacity_slider");
+ __publicField(this, "colorButton");
+ __publicField(this, "saveButton");
+ __publicField(this, "zoom_ratio");
+ __publicField(this, "pan_x");
+ __publicField(this, "pan_y");
+ __publicField(this, "imgCanvas");
+ __publicField(this, "last_display_style");
+ __publicField(this, "is_visible");
+ __publicField(this, "image");
+ __publicField(this, "handler_registered");
+ __publicField(this, "brush_slider_input");
+ __publicField(this, "cursorX");
+ __publicField(this, "cursorY");
+ __publicField(this, "mousedown_pan_x");
+ __publicField(this, "mousedown_pan_y");
+ __publicField(this, "last_pressure");
+ __publicField(this, "is_layout_created", false);
+ __publicField(this, "brush_opacity", 0.7);
+ __publicField(this, "brush_size", 10);
+ __publicField(this, "brush_color_mode", "black");
+ __publicField(this, "drawing_mode", false);
+ __publicField(this, "lastx", -1);
+ __publicField(this, "lasty", -1);
+ __publicField(this, "lasttime", 0);
+ this.element = $el("div.comfy-modal", { parent: document.body }, [
+ $el("div.comfy-modal-content", [...this.createButtons()])
+ ]);
+ }
+ static getInstance() {
+ if (!_MaskEditorDialog.instance) {
+ _MaskEditorDialog.instance = new _MaskEditorDialog();
+ }
+ return _MaskEditorDialog.instance;
+ }
+ createButtons() {
+ return [];
+ }
+ createButton(name, callback) {
+ var button = document.createElement("button");
+ button.style.pointerEvents = "auto";
+ button.innerText = name;
+ button.addEventListener("click", callback);
+ return button;
+ }
+ createLeftButton(name, callback) {
+ var button = this.createButton(name, callback);
+ button.style.cssFloat = "left";
+ button.style.marginRight = "4px";
+ return button;
+ }
+ createRightButton(name, callback) {
+ var button = this.createButton(name, callback);
+ button.style.cssFloat = "right";
+ button.style.marginLeft = "4px";
+ return button;
+ }
+ createLeftSlider(self, name, callback) {
+ const divElement = document.createElement("div");
+ divElement.id = "maskeditor-slider";
+ divElement.style.cssFloat = "left";
+ divElement.style.fontFamily = "sans-serif";
+ divElement.style.marginRight = "4px";
+ divElement.style.color = "var(--input-text)";
+ divElement.style.backgroundColor = "var(--comfy-input-bg)";
+ divElement.style.borderRadius = "8px";
+ divElement.style.borderColor = "var(--border-color)";
+ divElement.style.borderStyle = "solid";
+ divElement.style.fontSize = "15px";
+ divElement.style.height = "21px";
+ divElement.style.padding = "1px 6px";
+ divElement.style.display = "flex";
+ divElement.style.position = "relative";
+ divElement.style.top = "2px";
+ divElement.style.pointerEvents = "auto";
+ self.brush_slider_input = document.createElement("input");
+ self.brush_slider_input.setAttribute("type", "range");
+ self.brush_slider_input.setAttribute("min", "1");
+ self.brush_slider_input.setAttribute("max", "100");
+ self.brush_slider_input.setAttribute("value", "10");
+ const labelElement = document.createElement("label");
+ labelElement.textContent = name;
+ divElement.appendChild(labelElement);
+ divElement.appendChild(self.brush_slider_input);
+ self.brush_slider_input.addEventListener("change", callback);
+ return divElement;
+ }
+ createOpacitySlider(self, name, callback) {
+ const divElement = document.createElement("div");
+ divElement.id = "maskeditor-opacity-slider";
+ divElement.style.cssFloat = "left";
+ divElement.style.fontFamily = "sans-serif";
+ divElement.style.marginRight = "4px";
+ divElement.style.color = "var(--input-text)";
+ divElement.style.backgroundColor = "var(--comfy-input-bg)";
+ divElement.style.borderRadius = "8px";
+ divElement.style.borderColor = "var(--border-color)";
+ divElement.style.borderStyle = "solid";
+ divElement.style.fontSize = "15px";
+ divElement.style.height = "21px";
+ divElement.style.padding = "1px 6px";
+ divElement.style.display = "flex";
+ divElement.style.position = "relative";
+ divElement.style.top = "2px";
+ divElement.style.pointerEvents = "auto";
+ self.opacity_slider_input = document.createElement("input");
+ self.opacity_slider_input.setAttribute("type", "range");
+ self.opacity_slider_input.setAttribute("min", "0.1");
+ self.opacity_slider_input.setAttribute("max", "1.0");
+ self.opacity_slider_input.setAttribute("step", "0.01");
+ self.opacity_slider_input.setAttribute("value", "0.7");
+ const labelElement = document.createElement("label");
+ labelElement.textContent = name;
+ divElement.appendChild(labelElement);
+ divElement.appendChild(self.opacity_slider_input);
+ self.opacity_slider_input.addEventListener("input", callback);
+ return divElement;
+ }
+ setlayout(imgCanvas, maskCanvas) {
+ const self = this;
+ var bottom_panel = document.createElement("div");
+ bottom_panel.style.position = "absolute";
+ bottom_panel.style.bottom = "0px";
+ bottom_panel.style.left = "20px";
+ bottom_panel.style.right = "20px";
+ bottom_panel.style.height = "50px";
+ bottom_panel.style.pointerEvents = "none";
+ var brush = document.createElement("div");
+ brush.id = "brush";
+ brush.style.backgroundColor = "transparent";
+ brush.style.outline = "1px dashed black";
+ brush.style.boxShadow = "0 0 0 1px white";
+ brush.style.borderRadius = "50%";
+ brush.style.MozBorderRadius = "50%";
+ brush.style.WebkitBorderRadius = "50%";
+ brush.style.position = "absolute";
+ brush.style.zIndex = "8889";
+ brush.style.pointerEvents = "none";
+ this.brush = brush;
+ this.element.appendChild(imgCanvas);
+ this.element.appendChild(maskCanvas);
+ this.element.appendChild(bottom_panel);
+ document.body.appendChild(brush);
+ var clearButton = this.createLeftButton("Clear", () => {
+ self.maskCtx.clearRect(
+ 0,
+ 0,
+ self.maskCanvas.width,
+ self.maskCanvas.height
+ );
+ });
+ this.brush_size_slider = this.createLeftSlider(
+ self,
+ "Thickness",
+ (event) => {
+ self.brush_size = event.target.value;
+ self.updateBrushPreview(self);
+ }
+ );
+ this.brush_opacity_slider = this.createOpacitySlider(
+ self,
+ "Opacity",
+ (event) => {
+ self.brush_opacity = event.target.value;
+ if (self.brush_color_mode !== "negative") {
+ self.maskCanvas.style.opacity = self.brush_opacity.toString();
+ }
+ }
+ );
+ this.colorButton = this.createLeftButton(this.getColorButtonText(), () => {
+ if (self.brush_color_mode === "black") {
+ self.brush_color_mode = "white";
+ } else if (self.brush_color_mode === "white") {
+ self.brush_color_mode = "negative";
+ } else {
+ self.brush_color_mode = "black";
+ }
+ self.updateWhenBrushColorModeChanged();
+ });
+ var cancelButton = this.createRightButton("Cancel", () => {
+ document.removeEventListener("keydown", _MaskEditorDialog.handleKeyDown);
+ self.close();
+ });
+ this.saveButton = this.createRightButton("Save", () => {
+ document.removeEventListener("keydown", _MaskEditorDialog.handleKeyDown);
+ self.save();
+ });
+ this.element.appendChild(imgCanvas);
+ this.element.appendChild(maskCanvas);
+ this.element.appendChild(bottom_panel);
+ bottom_panel.appendChild(clearButton);
+ bottom_panel.appendChild(this.saveButton);
+ bottom_panel.appendChild(cancelButton);
+ bottom_panel.appendChild(this.brush_size_slider);
+ bottom_panel.appendChild(this.brush_opacity_slider);
+ bottom_panel.appendChild(this.colorButton);
+ imgCanvas.style.position = "absolute";
+ maskCanvas.style.position = "absolute";
+ imgCanvas.style.top = "200";
+ imgCanvas.style.left = "0";
+ maskCanvas.style.top = imgCanvas.style.top;
+ maskCanvas.style.left = imgCanvas.style.left;
+ const maskCanvasStyle = this.getMaskCanvasStyle();
+ maskCanvas.style.mixBlendMode = maskCanvasStyle.mixBlendMode;
+ maskCanvas.style.opacity = maskCanvasStyle.opacity.toString();
+ }
+ show() {
+ return __async(this, null, function* () {
+ this.zoom_ratio = 1;
+ this.pan_x = 0;
+ this.pan_y = 0;
+ if (!this.is_layout_created) {
+ const imgCanvas = document.createElement("canvas");
+ const maskCanvas = document.createElement("canvas");
+ imgCanvas.id = "imageCanvas";
+ maskCanvas.id = "maskCanvas";
+ this.setlayout(imgCanvas, maskCanvas);
+ this.imgCanvas = imgCanvas;
+ this.maskCanvas = maskCanvas;
+ this.maskCtx = maskCanvas.getContext("2d", { willReadFrequently: true });
+ this.setEventHandler(maskCanvas);
+ this.is_layout_created = true;
+ const self = this;
+ const observer = new MutationObserver(function(mutations) {
+ mutations.forEach(function(mutation) {
+ if (mutation.type === "attributes" && mutation.attributeName === "style") {
+ if (self.last_display_style && self.last_display_style != "none" && self.element.style.display == "none") {
+ self.brush.style.display = "none";
+ ComfyApp.onClipspaceEditorClosed();
+ }
+ self.last_display_style = self.element.style.display;
+ }
+ });
+ });
+ const config = { attributes: true };
+ observer.observe(this.element, config);
+ }
+ document.addEventListener("keydown", _MaskEditorDialog.handleKeyDown);
+ if (ComfyApp.clipspace_return_node) {
+ this.saveButton.innerText = "Save to node";
+ } else {
+ this.saveButton.innerText = "Save";
+ }
+ this.saveButton.disabled = false;
+ this.element.style.display = "block";
+ this.element.style.width = "85%";
+ this.element.style.margin = "0 7.5%";
+ this.element.style.height = "100vh";
+ this.element.style.top = "50%";
+ this.element.style.left = "42%";
+ this.element.style.zIndex = "8888";
+ yield this.setImages(this.imgCanvas);
+ this.is_visible = true;
+ });
+ }
+ isOpened() {
+ return this.element.style.display == "block";
+ }
+ invalidateCanvas(orig_image, mask_image) {
+ this.imgCanvas.width = orig_image.width;
+ this.imgCanvas.height = orig_image.height;
+ this.maskCanvas.width = orig_image.width;
+ this.maskCanvas.height = orig_image.height;
+ let imgCtx = this.imgCanvas.getContext("2d", { willReadFrequently: true });
+ let maskCtx = this.maskCanvas.getContext("2d", {
+ willReadFrequently: true
+ });
+ imgCtx.drawImage(orig_image, 0, 0, orig_image.width, orig_image.height);
+ prepare_mask(mask_image, this.maskCanvas, maskCtx, this.getMaskColor());
+ }
+ setImages(imgCanvas) {
+ return __async(this, null, function* () {
+ let self = this;
+ const imgCtx = imgCanvas.getContext("2d", { willReadFrequently: true });
+ const maskCtx = this.maskCtx;
+ const maskCanvas = this.maskCanvas;
+ imgCtx.clearRect(0, 0, this.imgCanvas.width, this.imgCanvas.height);
+ maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height);
+ const filepath = ComfyApp.clipspace.images;
+ const alpha_url = new URL(
+ ComfyApp.clipspace.imgs[ComfyApp.clipspace["selectedIndex"]].src
+ );
+ alpha_url.searchParams.delete("channel");
+ alpha_url.searchParams.delete("preview");
+ alpha_url.searchParams.set("channel", "a");
+ let mask_image = yield loadImage(alpha_url);
+ const rgb_url = new URL(
+ ComfyApp.clipspace.imgs[ComfyApp.clipspace["selectedIndex"]].src
+ );
+ rgb_url.searchParams.delete("channel");
+ rgb_url.searchParams.set("channel", "rgb");
+ this.image = new Image();
+ this.image.onload = function() {
+ maskCanvas.width = self.image.width;
+ maskCanvas.height = self.image.height;
+ self.invalidateCanvas(self.image, mask_image);
+ self.initializeCanvasPanZoom();
+ };
+ this.image.src = rgb_url.toString();
+ });
+ }
+ initializeCanvasPanZoom() {
+ let drawWidth = this.image.width;
+ let drawHeight = this.image.height;
+ let width = this.element.clientWidth;
+ let height = this.element.clientHeight;
+ if (this.image.width > width) {
+ drawWidth = width;
+ drawHeight = drawWidth / this.image.width * this.image.height;
+ }
+ if (drawHeight > height) {
+ drawHeight = height;
+ drawWidth = drawHeight / this.image.height * this.image.width;
+ }
+ this.zoom_ratio = drawWidth / this.image.width;
+ const canvasX = (width - drawWidth) / 2;
+ const canvasY = (height - drawHeight) / 2;
+ this.pan_x = canvasX;
+ this.pan_y = canvasY;
+ this.invalidatePanZoom();
+ }
+ invalidatePanZoom() {
+ let raw_width = this.image.width * this.zoom_ratio;
+ let raw_height = this.image.height * this.zoom_ratio;
+ if (this.pan_x + raw_width < 10) {
+ this.pan_x = 10 - raw_width;
+ }
+ if (this.pan_y + raw_height < 10) {
+ this.pan_y = 10 - raw_height;
+ }
+ let width = `${raw_width}px`;
+ let height = `${raw_height}px`;
+ let left = `${this.pan_x}px`;
+ let top = `${this.pan_y}px`;
+ this.maskCanvas.style.width = width;
+ this.maskCanvas.style.height = height;
+ this.maskCanvas.style.left = left;
+ this.maskCanvas.style.top = top;
+ this.imgCanvas.style.width = width;
+ this.imgCanvas.style.height = height;
+ this.imgCanvas.style.left = left;
+ this.imgCanvas.style.top = top;
+ }
+ setEventHandler(maskCanvas) {
+ const self = this;
+ if (!this.handler_registered) {
+ maskCanvas.addEventListener("contextmenu", (event) => {
+ event.preventDefault();
+ });
+ this.element.addEventListener(
+ "wheel",
+ (event) => this.handleWheelEvent(self, event)
+ );
+ this.element.addEventListener(
+ "pointermove",
+ (event) => this.pointMoveEvent(self, event)
+ );
+ this.element.addEventListener(
+ "touchmove",
+ (event) => this.pointMoveEvent(self, event)
+ );
+ this.element.addEventListener("dragstart", (event) => {
+ if (event.ctrlKey) {
+ event.preventDefault();
+ }
+ });
+ maskCanvas.addEventListener(
+ "pointerdown",
+ (event) => this.handlePointerDown(self, event)
+ );
+ maskCanvas.addEventListener(
+ "pointermove",
+ (event) => this.draw_move(self, event)
+ );
+ maskCanvas.addEventListener(
+ "touchmove",
+ (event) => this.draw_move(self, event)
+ );
+ maskCanvas.addEventListener("pointerover", (event) => {
+ this.brush.style.display = "block";
+ });
+ maskCanvas.addEventListener("pointerleave", (event) => {
+ this.brush.style.display = "none";
+ });
+ document.addEventListener("pointerup", _MaskEditorDialog.handlePointerUp);
+ this.handler_registered = true;
+ }
+ }
+ getMaskCanvasStyle() {
+ if (this.brush_color_mode === "negative") {
+ return {
+ mixBlendMode: "difference",
+ opacity: "1"
+ };
+ } else {
+ return {
+ mixBlendMode: "initial",
+ opacity: this.brush_opacity
+ };
+ }
+ }
+ getMaskColor() {
+ if (this.brush_color_mode === "black") {
+ return { r: 0, g: 0, b: 0 };
+ }
+ if (this.brush_color_mode === "white") {
+ return { r: 255, g: 255, b: 255 };
+ }
+ if (this.brush_color_mode === "negative") {
+ return { r: 255, g: 255, b: 255 };
+ }
+ return { r: 0, g: 0, b: 0 };
+ }
+ getMaskFillStyle() {
+ const maskColor = this.getMaskColor();
+ return "rgb(" + maskColor.r + "," + maskColor.g + "," + maskColor.b + ")";
+ }
+ getColorButtonText() {
+ let colorCaption = "unknown";
+ if (this.brush_color_mode === "black") {
+ colorCaption = "black";
+ } else if (this.brush_color_mode === "white") {
+ colorCaption = "white";
+ } else if (this.brush_color_mode === "negative") {
+ colorCaption = "negative";
+ }
+ return "Color: " + colorCaption;
+ }
+ updateWhenBrushColorModeChanged() {
+ this.colorButton.innerText = this.getColorButtonText();
+ const maskCanvasStyle = this.getMaskCanvasStyle();
+ this.maskCanvas.style.mixBlendMode = maskCanvasStyle.mixBlendMode;
+ this.maskCanvas.style.opacity = maskCanvasStyle.opacity.toString();
+ const maskColor = this.getMaskColor();
+ const maskData = this.maskCtx.getImageData(
+ 0,
+ 0,
+ this.maskCanvas.width,
+ this.maskCanvas.height
+ );
+ for (let i = 0; i < maskData.data.length; i += 4) {
+ maskData.data[i] = maskColor.r;
+ maskData.data[i + 1] = maskColor.g;
+ maskData.data[i + 2] = maskColor.b;
+ }
+ this.maskCtx.putImageData(maskData, 0, 0);
+ }
+ static handleKeyDown(event) {
+ const self = _MaskEditorDialog.instance;
+ if (event.key === "]") {
+ self.brush_size = Math.min(self.brush_size + 2, 100);
+ self.brush_slider_input.value = self.brush_size;
+ } else if (event.key === "[") {
+ self.brush_size = Math.max(self.brush_size - 2, 1);
+ self.brush_slider_input.value = self.brush_size;
+ } else if (event.key === "Enter") {
+ self.save();
+ }
+ self.updateBrushPreview(self);
+ }
+ static handlePointerUp(event) {
+ event.preventDefault();
+ this.mousedown_x = null;
+ this.mousedown_y = null;
+ _MaskEditorDialog.instance.drawing_mode = false;
+ }
+ updateBrushPreview(self) {
+ const brush = self.brush;
+ var centerX = self.cursorX;
+ var centerY = self.cursorY;
+ brush.style.width = self.brush_size * 2 * this.zoom_ratio + "px";
+ brush.style.height = self.brush_size * 2 * this.zoom_ratio + "px";
+ brush.style.left = centerX - self.brush_size * this.zoom_ratio + "px";
+ brush.style.top = centerY - self.brush_size * this.zoom_ratio + "px";
+ }
+ handleWheelEvent(self, event) {
+ event.preventDefault();
+ if (event.ctrlKey) {
+ if (event.deltaY < 0) {
+ this.zoom_ratio = Math.min(10, this.zoom_ratio + 0.2);
+ } else {
+ this.zoom_ratio = Math.max(0.2, this.zoom_ratio - 0.2);
+ }
+ this.invalidatePanZoom();
+ } else {
+ if (event.deltaY < 0) this.brush_size = Math.min(this.brush_size + 2, 100);
+ else this.brush_size = Math.max(this.brush_size - 2, 1);
+ this.brush_slider_input.value = this.brush_size.toString();
+ this.updateBrushPreview(this);
+ }
+ }
+ pointMoveEvent(self, event) {
+ this.cursorX = event.pageX;
+ this.cursorY = event.pageY;
+ self.updateBrushPreview(self);
+ if (event.ctrlKey) {
+ event.preventDefault();
+ self.pan_move(self, event);
+ }
+ let left_button_down = window.TouchEvent && event instanceof TouchEvent || event.buttons == 1;
+ if (event.shiftKey && left_button_down) {
+ self.drawing_mode = false;
+ const y = event.clientY;
+ let delta = (self.zoom_lasty - y) * 5e-3;
+ self.zoom_ratio = Math.max(
+ Math.min(10, self.last_zoom_ratio - delta),
+ 0.2
+ );
+ this.invalidatePanZoom();
+ return;
+ }
+ }
+ pan_move(self, event) {
+ if (event.buttons == 1) {
+ if (_MaskEditorDialog.mousedown_x) {
+ let deltaX = _MaskEditorDialog.mousedown_x - event.clientX;
+ let deltaY = _MaskEditorDialog.mousedown_y - event.clientY;
+ self.pan_x = this.mousedown_pan_x - deltaX;
+ self.pan_y = this.mousedown_pan_y - deltaY;
+ self.invalidatePanZoom();
+ }
+ }
+ }
+ draw_move(self, event) {
+ if (event.ctrlKey || event.shiftKey) {
+ return;
+ }
+ event.preventDefault();
+ this.cursorX = event.pageX;
+ this.cursorY = event.pageY;
+ self.updateBrushPreview(self);
+ let left_button_down = window.TouchEvent && event instanceof TouchEvent || event.buttons == 1;
+ let right_button_down = [2, 5, 32].includes(event.buttons);
+ if (!event.altKey && left_button_down) {
+ var diff = performance.now() - self.lasttime;
+ const maskRect = self.maskCanvas.getBoundingClientRect();
+ var x = event.offsetX;
+ var y = event.offsetY;
+ if (event.offsetX == null) {
+ x = event.targetTouches[0].clientX - maskRect.left;
+ }
+ if (event.offsetY == null) {
+ y = event.targetTouches[0].clientY - maskRect.top;
+ }
+ x /= self.zoom_ratio;
+ y /= self.zoom_ratio;
+ var brush_size = this.brush_size;
+ if (event instanceof PointerEvent && event.pointerType == "pen") {
+ brush_size *= event.pressure;
+ this.last_pressure = event.pressure;
+ } else if (window.TouchEvent && event instanceof TouchEvent && diff < 20) {
+ brush_size *= this.last_pressure;
+ } else {
+ brush_size = this.brush_size;
+ }
+ if (diff > 20 && !this.drawing_mode)
+ requestAnimationFrame(() => {
+ self.maskCtx.beginPath();
+ self.maskCtx.fillStyle = this.getMaskFillStyle();
+ self.maskCtx.globalCompositeOperation = "source-over";
+ self.maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false);
+ self.maskCtx.fill();
+ self.lastx = x;
+ self.lasty = y;
+ });
+ else
+ requestAnimationFrame(() => {
+ self.maskCtx.beginPath();
+ self.maskCtx.fillStyle = this.getMaskFillStyle();
+ self.maskCtx.globalCompositeOperation = "source-over";
+ var dx = x - self.lastx;
+ var dy = y - self.lasty;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ var directionX = dx / distance;
+ var directionY = dy / distance;
+ for (var i = 0; i < distance; i += 5) {
+ var px = self.lastx + directionX * i;
+ var py = self.lasty + directionY * i;
+ self.maskCtx.arc(px, py, brush_size, 0, Math.PI * 2, false);
+ self.maskCtx.fill();
+ }
+ self.lastx = x;
+ self.lasty = y;
+ });
+ self.lasttime = performance.now();
+ } else if (event.altKey && left_button_down || right_button_down) {
+ const maskRect = self.maskCanvas.getBoundingClientRect();
+ const x2 = (event.offsetX || event.targetTouches[0].clientX - maskRect.left) / self.zoom_ratio;
+ const y2 = (event.offsetY || event.targetTouches[0].clientY - maskRect.top) / self.zoom_ratio;
+ var brush_size = this.brush_size;
+ if (event instanceof PointerEvent && event.pointerType == "pen") {
+ brush_size *= event.pressure;
+ this.last_pressure = event.pressure;
+ } else if (window.TouchEvent && event instanceof TouchEvent && diff < 20) {
+ brush_size *= this.last_pressure;
+ } else {
+ brush_size = this.brush_size;
+ }
+ if (diff > 20 && !this.drawing_mode)
+ requestAnimationFrame(() => {
+ self.maskCtx.beginPath();
+ self.maskCtx.globalCompositeOperation = "destination-out";
+ self.maskCtx.arc(x2, y2, brush_size, 0, Math.PI * 2, false);
+ self.maskCtx.fill();
+ self.lastx = x2;
+ self.lasty = y2;
+ });
+ else
+ requestAnimationFrame(() => {
+ self.maskCtx.beginPath();
+ self.maskCtx.globalCompositeOperation = "destination-out";
+ var dx = x2 - self.lastx;
+ var dy = y2 - self.lasty;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ var directionX = dx / distance;
+ var directionY = dy / distance;
+ for (var i = 0; i < distance; i += 5) {
+ var px = self.lastx + directionX * i;
+ var py = self.lasty + directionY * i;
+ self.maskCtx.arc(px, py, brush_size, 0, Math.PI * 2, false);
+ self.maskCtx.fill();
+ }
+ self.lastx = x2;
+ self.lasty = y2;
+ });
+ self.lasttime = performance.now();
+ }
+ }
+ handlePointerDown(self, event) {
+ if (event.ctrlKey) {
+ if (event.buttons == 1) {
+ _MaskEditorDialog.mousedown_x = event.clientX;
+ _MaskEditorDialog.mousedown_y = event.clientY;
+ this.mousedown_pan_x = this.pan_x;
+ this.mousedown_pan_y = this.pan_y;
+ }
+ return;
+ }
+ var brush_size = this.brush_size;
+ if (event instanceof PointerEvent && event.pointerType == "pen") {
+ brush_size *= event.pressure;
+ this.last_pressure = event.pressure;
+ }
+ if ([0, 2, 5].includes(event.button)) {
+ self.drawing_mode = true;
+ event.preventDefault();
+ if (event.shiftKey) {
+ self.zoom_lasty = event.clientY;
+ self.last_zoom_ratio = self.zoom_ratio;
+ return;
+ }
+ const maskRect = self.maskCanvas.getBoundingClientRect();
+ const x = (event.offsetX || event.targetTouches[0].clientX - maskRect.left) / self.zoom_ratio;
+ const y = (event.offsetY || event.targetTouches[0].clientY - maskRect.top) / self.zoom_ratio;
+ self.maskCtx.beginPath();
+ if (!event.altKey && event.button == 0) {
+ self.maskCtx.fillStyle = this.getMaskFillStyle();
+ self.maskCtx.globalCompositeOperation = "source-over";
+ } else {
+ self.maskCtx.globalCompositeOperation = "destination-out";
+ }
+ self.maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false);
+ self.maskCtx.fill();
+ self.lastx = x;
+ self.lasty = y;
+ self.lasttime = performance.now();
+ }
+ }
+ save() {
+ return __async(this, null, function* () {
+ const backupCanvas = document.createElement("canvas");
+ const backupCtx = backupCanvas.getContext("2d", {
+ willReadFrequently: true
+ });
+ backupCanvas.width = this.image.width;
+ backupCanvas.height = this.image.height;
+ backupCtx.clearRect(0, 0, backupCanvas.width, backupCanvas.height);
+ backupCtx.drawImage(
+ this.maskCanvas,
+ 0,
+ 0,
+ this.maskCanvas.width,
+ this.maskCanvas.height,
+ 0,
+ 0,
+ backupCanvas.width,
+ backupCanvas.height
+ );
+ const backupData = backupCtx.getImageData(
+ 0,
+ 0,
+ backupCanvas.width,
+ backupCanvas.height
+ );
+ for (let i = 0; i < backupData.data.length; i += 4) {
+ if (backupData.data[i + 3] == 255) backupData.data[i + 3] = 0;
+ else backupData.data[i + 3] = 255;
+ backupData.data[i] = 0;
+ backupData.data[i + 1] = 0;
+ backupData.data[i + 2] = 0;
+ }
+ backupCtx.globalCompositeOperation = "source-over";
+ backupCtx.putImageData(backupData, 0, 0);
+ const formData = new FormData();
+ const filename = "clipspace-mask-" + performance.now() + ".png";
+ const item = {
+ filename,
+ subfolder: "clipspace",
+ type: "input"
+ };
+ if (ComfyApp.clipspace.images) ComfyApp.clipspace.images[0] = item;
+ if (ComfyApp.clipspace.widgets) {
+ const index = ComfyApp.clipspace.widgets.findIndex(
+ (obj) => obj.name === "image"
+ );
+ if (index >= 0) ComfyApp.clipspace.widgets[index].value = item;
+ }
+ const dataURL = backupCanvas.toDataURL();
+ const blob = dataURLToBlob(dataURL);
+ let original_url = new URL(this.image.src);
+ const original_ref = {
+ filename: original_url.searchParams.get("filename")
+ };
+ let original_subfolder = original_url.searchParams.get("subfolder");
+ if (original_subfolder) original_ref.subfolder = original_subfolder;
+ let original_type = original_url.searchParams.get("type");
+ if (original_type) original_ref.type = original_type;
+ formData.append("image", blob, filename);
+ formData.append("original_ref", JSON.stringify(original_ref));
+ formData.append("type", "input");
+ formData.append("subfolder", "clipspace");
+ this.saveButton.innerText = "Saving...";
+ this.saveButton.disabled = true;
+ yield uploadMask(item, formData);
+ ComfyApp.onClipspaceEditorSave();
+ this.close();
+ });
+ }
+};
+__name(_MaskEditorDialog, "MaskEditorDialog");
+__publicField(_MaskEditorDialog, "instance", null);
+__publicField(_MaskEditorDialog, "mousedown_x", null);
+__publicField(_MaskEditorDialog, "mousedown_y", null);
+let MaskEditorDialog = _MaskEditorDialog;
+app.registerExtension({
+ name: "Comfy.MaskEditor",
+ init(app2) {
+ ComfyApp.open_maskeditor = function() {
+ const dlg = MaskEditorDialog.getInstance();
+ if (!dlg.isOpened()) {
+ dlg.show();
+ }
+ };
+ const context_predicate = /* @__PURE__ */ __name(() => ComfyApp.clipspace && ComfyApp.clipspace.imgs && ComfyApp.clipspace.imgs.length > 0, "context_predicate");
+ ClipspaceDialog.registerButton(
+ "MaskEditor",
+ context_predicate,
+ ComfyApp.open_maskeditor
+ );
+ }
+});
+const id = "Comfy.NodeTemplates";
+const file = "comfy.templates.json";
+const _ManageTemplates = class _ManageTemplates extends ComfyDialog {
+ constructor() {
+ super();
+ __publicField(this, "templates");
+ __publicField(this, "draggedEl");
+ __publicField(this, "saveVisualCue");
+ __publicField(this, "emptyImg");
+ __publicField(this, "importInput");
+ this.load().then((v) => {
+ this.templates = v;
+ });
+ this.element.classList.add("comfy-manage-templates");
+ this.draggedEl = null;
+ this.saveVisualCue = null;
+ this.emptyImg = new Image();
+ this.emptyImg.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=";
+ this.importInput = $el("input", {
+ type: "file",
+ accept: ".json",
+ multiple: true,
+ style: { display: "none" },
+ parent: document.body,
+ onchange: /* @__PURE__ */ __name(() => this.importAll(), "onchange")
+ });
+ }
+ createButtons() {
+ const btns = super.createButtons();
+ btns[0].textContent = "Close";
+ btns[0].onclick = (e) => {
+ clearTimeout(this.saveVisualCue);
+ this.close();
+ };
+ btns.unshift(
+ $el("button", {
+ type: "button",
+ textContent: "Export",
+ onclick: /* @__PURE__ */ __name(() => this.exportAll(), "onclick")
+ })
+ );
+ btns.unshift(
+ $el("button", {
+ type: "button",
+ textContent: "Import",
+ onclick: /* @__PURE__ */ __name(() => {
+ this.importInput.click();
+ }, "onclick")
+ })
+ );
+ return btns;
+ }
+ load() {
+ return __async(this, null, function* () {
+ let templates = [];
+ if (app.storageLocation === "server") {
+ if (app.isNewUserSession) {
+ const json = localStorage.getItem(id);
+ if (json) {
+ templates = JSON.parse(json);
+ }
+ yield api.storeUserData(file, json, { stringify: false });
+ } else {
+ const res = yield api.getUserData(file);
+ if (res.status === 200) {
+ try {
+ templates = yield res.json();
+ } catch (error) {
+ }
+ } else if (res.status !== 404) {
+ console.error(res.status + " " + res.statusText);
+ }
+ }
+ } else {
+ const json = localStorage.getItem(id);
+ if (json) {
+ templates = JSON.parse(json);
+ }
+ }
+ return templates != null ? templates : [];
+ });
+ }
+ store() {
+ return __async(this, null, function* () {
+ if (app.storageLocation === "server") {
+ const templates = JSON.stringify(this.templates, void 0, 4);
+ localStorage.setItem(id, templates);
+ try {
+ yield api.storeUserData(file, templates, { stringify: false });
+ } catch (error) {
+ console.error(error);
+ alert(error.message);
+ }
+ } else {
+ localStorage.setItem(id, JSON.stringify(this.templates));
+ }
+ });
+ }
+ importAll() {
+ return __async(this, null, function* () {
+ for (const file2 of this.importInput.files) {
+ if (file2.type === "application/json" || file2.name.endsWith(".json")) {
+ const reader = new FileReader();
+ reader.onload = () => __async(this, null, function* () {
+ const importFile = JSON.parse(reader.result);
+ if (importFile == null ? void 0 : importFile.templates) {
+ for (const template of importFile.templates) {
+ if ((template == null ? void 0 : template.name) && (template == null ? void 0 : template.data)) {
+ this.templates.push(template);
+ }
+ }
+ yield this.store();
+ }
+ });
+ yield reader.readAsText(file2);
+ }
+ }
+ this.importInput.value = null;
+ this.close();
+ });
+ }
+ exportAll() {
+ if (this.templates.length == 0) {
+ alert("No templates to export.");
+ return;
+ }
+ const json = JSON.stringify({ templates: this.templates }, null, 2);
+ const blob = new Blob([json], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+ const a = $el("a", {
+ href: url,
+ download: "node_templates.json",
+ style: { display: "none" },
+ parent: document.body
+ });
+ a.click();
+ setTimeout(function() {
+ a.remove();
+ window.URL.revokeObjectURL(url);
+ }, 0);
+ }
+ show() {
+ super.show(
+ $el(
+ "div",
+ {},
+ this.templates.flatMap((t, i) => {
+ let nameInput;
+ return [
+ $el(
+ "div",
+ {
+ dataset: { id: i.toString() },
+ className: "templateManagerRow",
+ style: {
+ display: "grid",
+ gridTemplateColumns: "1fr auto",
+ border: "1px dashed transparent",
+ gap: "5px",
+ backgroundColor: "var(--comfy-menu-bg)"
+ },
+ ondragstart: /* @__PURE__ */ __name((e) => {
+ this.draggedEl = e.currentTarget;
+ e.currentTarget.style.opacity = "0.6";
+ e.currentTarget.style.border = "1px dashed yellow";
+ e.dataTransfer.effectAllowed = "move";
+ e.dataTransfer.setDragImage(this.emptyImg, 0, 0);
+ }, "ondragstart"),
+ ondragend: /* @__PURE__ */ __name((e) => {
+ e.target.style.opacity = "1";
+ e.currentTarget.style.border = "1px dashed transparent";
+ e.currentTarget.removeAttribute("draggable");
+ this.element.querySelectorAll(".templateManagerRow").forEach((el, i2) => {
+ var prev_i = Number.parseInt(el.dataset.id);
+ if (el == this.draggedEl && prev_i != i2) {
+ this.templates.splice(
+ i2,
+ 0,
+ this.templates.splice(prev_i, 1)[0]
+ );
+ }
+ el.dataset.id = i2.toString();
+ });
+ this.store();
+ }, "ondragend"),
+ ondragover: /* @__PURE__ */ __name((e) => {
+ e.preventDefault();
+ if (e.currentTarget == this.draggedEl) return;
+ let rect = e.currentTarget.getBoundingClientRect();
+ if (e.clientY > rect.top + rect.height / 2) {
+ e.currentTarget.parentNode.insertBefore(
+ this.draggedEl,
+ e.currentTarget.nextSibling
+ );
+ } else {
+ e.currentTarget.parentNode.insertBefore(
+ this.draggedEl,
+ e.currentTarget
+ );
+ }
+ }, "ondragover")
+ },
+ [
+ $el(
+ "label",
+ {
+ textContent: "Name: ",
+ style: {
+ cursor: "grab"
+ },
+ onmousedown: /* @__PURE__ */ __name((e) => {
+ if (e.target.localName == "label")
+ e.currentTarget.parentNode.draggable = "true";
+ }, "onmousedown")
+ },
+ [
+ $el("input", {
+ value: t.name,
+ dataset: { name: t.name },
+ style: {
+ transitionProperty: "background-color",
+ transitionDuration: "0s"
+ },
+ onchange: /* @__PURE__ */ __name((e) => {
+ clearTimeout(this.saveVisualCue);
+ var el = e.target;
+ var row = el.parentNode.parentNode;
+ this.templates[row.dataset.id].name = el.value.trim() || "untitled";
+ this.store();
+ el.style.backgroundColor = "rgb(40, 95, 40)";
+ el.style.transitionDuration = "0s";
+ this.saveVisualCue = setTimeout(function() {
+ el.style.transitionDuration = ".7s";
+ el.style.backgroundColor = "var(--comfy-input-bg)";
+ }, 15);
+ }, "onchange"),
+ onkeypress: /* @__PURE__ */ __name((e) => {
+ var el = e.target;
+ clearTimeout(this.saveVisualCue);
+ el.style.transitionDuration = "0s";
+ el.style.backgroundColor = "var(--comfy-input-bg)";
+ }, "onkeypress"),
+ $: /* @__PURE__ */ __name((el) => nameInput = el, "$")
+ })
+ ]
+ ),
+ $el("div", {}, [
+ $el("button", {
+ textContent: "Export",
+ style: {
+ fontSize: "12px",
+ fontWeight: "normal"
+ },
+ onclick: /* @__PURE__ */ __name((e) => {
+ const json = JSON.stringify({ templates: [t] }, null, 2);
+ const blob = new Blob([json], {
+ type: "application/json"
+ });
+ const url = URL.createObjectURL(blob);
+ const a = $el("a", {
+ href: url,
+ download: (nameInput.value || t.name) + ".json",
+ style: { display: "none" },
+ parent: document.body
+ });
+ a.click();
+ setTimeout(function() {
+ a.remove();
+ window.URL.revokeObjectURL(url);
+ }, 0);
+ }, "onclick")
+ }),
+ $el("button", {
+ textContent: "Delete",
+ style: {
+ fontSize: "12px",
+ color: "red",
+ fontWeight: "normal"
+ },
+ onclick: /* @__PURE__ */ __name((e) => {
+ const item = e.target.parentNode.parentNode;
+ item.parentNode.removeChild(item);
+ this.templates.splice(item.dataset.id * 1, 1);
+ this.store();
+ var that = this;
+ setTimeout(function() {
+ that.element.querySelectorAll(".templateManagerRow").forEach((el, i2) => {
+ el.dataset.id = i2.toString();
+ });
+ }, 0);
+ }, "onclick")
+ })
+ ])
+ ]
+ )
+ ];
+ })
+ )
+ );
+ }
+};
+__name(_ManageTemplates, "ManageTemplates");
+let ManageTemplates = _ManageTemplates;
+app.registerExtension({
+ name: id,
+ setup() {
+ const manage = new ManageTemplates();
+ const clipboardAction = /* @__PURE__ */ __name((cb) => __async(this, null, function* () {
+ const old = localStorage.getItem("litegrapheditor_clipboard");
+ yield cb();
+ localStorage.setItem("litegrapheditor_clipboard", old);
+ }), "clipboardAction");
+ const orig = LGraphCanvas.prototype.getCanvasMenuOptions;
+ LGraphCanvas.prototype.getCanvasMenuOptions = function() {
+ const options = orig.apply(this, arguments);
+ options.push(null);
+ options.push({
+ content: `Save Selected as Template`,
+ disabled: !Object.keys(app.canvas.selected_nodes || {}).length,
+ callback: /* @__PURE__ */ __name(() => {
+ const name = prompt("Enter name");
+ if (!(name == null ? void 0 : name.trim())) return;
+ clipboardAction(() => {
+ app.canvas.copyToClipboard();
+ let data = localStorage.getItem("litegrapheditor_clipboard");
+ data = JSON.parse(data);
+ const nodeIds = Object.keys(app.canvas.selected_nodes);
+ for (let i = 0; i < nodeIds.length; i++) {
+ const node = app.graph.getNodeById(Number.parseInt(nodeIds[i]));
+ const nodeData = node == null ? void 0 : node.constructor.nodeData;
+ let groupData = GroupNodeHandler.getGroupData(node);
+ if (groupData) {
+ groupData = groupData.nodeData;
+ if (!data.groupNodes) {
+ data.groupNodes = {};
+ }
+ data.groupNodes[nodeData.name] = groupData;
+ data.nodes[i].type = nodeData.name;
+ }
+ }
+ manage.templates.push({
+ name,
+ data: JSON.stringify(data)
+ });
+ manage.store();
+ });
+ }, "callback")
+ });
+ const subItems = manage.templates.map((t) => {
+ return {
+ content: t.name,
+ callback: /* @__PURE__ */ __name(() => {
+ clipboardAction(() => __async(this, null, function* () {
+ const data = JSON.parse(t.data);
+ yield GroupNodeConfig.registerFromWorkflow(data.groupNodes, {});
+ localStorage.setItem("litegrapheditor_clipboard", t.data);
+ app.canvas.pasteFromClipboard();
+ }));
+ }, "callback")
+ };
+ });
+ subItems.push(null, {
+ content: "Manage",
+ callback: /* @__PURE__ */ __name(() => manage.show(), "callback")
+ });
+ options.push({
+ content: "Node Templates",
+ submenu: {
+ options: subItems
+ }
+ });
+ return options;
+ };
+ }
+});
+app.registerExtension({
+ name: "Comfy.NoteNode",
+ registerCustomNodes() {
+ const _NoteNode = class _NoteNode {
+ constructor() {
+ __publicField(this, "color", LGraphCanvas.node_colors.yellow.color);
+ __publicField(this, "bgcolor", LGraphCanvas.node_colors.yellow.bgcolor);
+ __publicField(this, "groupcolor", LGraphCanvas.node_colors.yellow.groupcolor);
+ __publicField(this, "properties");
+ __publicField(this, "serialize_widgets");
+ __publicField(this, "isVirtualNode");
+ __publicField(this, "collapsable");
+ __publicField(this, "title_mode");
+ if (!this.properties) {
+ this.properties = { text: "" };
+ }
+ ComfyWidgets.STRING(
+ // @ts-expect-error
+ // Should we extends LGraphNode?
+ this,
+ "",
+ ["", { default: this.properties.text, multiline: true }],
+ app
+ );
+ this.serialize_widgets = true;
+ this.isVirtualNode = true;
+ }
+ };
+ __name(_NoteNode, "NoteNode");
+ __publicField(_NoteNode, "category");
+ let NoteNode = _NoteNode;
+ LiteGraph.registerNodeType(
+ "Note",
+ // @ts-expect-error
+ Object.assign(NoteNode, {
+ title_mode: LiteGraph.NORMAL_TITLE,
+ title: "Note",
+ collapsable: true
+ })
+ );
+ NoteNode.category = "utils";
+ }
+});
+app.registerExtension({
+ name: "Comfy.RerouteNode",
+ registerCustomNodes(app2) {
+ const _RerouteNode = class _RerouteNode {
+ constructor() {
+ if (!this.properties) {
+ this.properties = {};
+ }
+ this.properties.showOutputText = _RerouteNode.defaultVisibility;
+ this.properties.horizontal = false;
+ this.addInput("", "*");
+ this.addOutput(this.properties.showOutputText ? "*" : "", "*");
+ this.onAfterGraphConfigured = function() {
+ requestAnimationFrame(() => {
+ this.onConnectionsChange(LiteGraph.INPUT, null, true, null);
+ });
+ };
+ this.onConnectionsChange = function(type, index, connected, link_info) {
+ var _a, _b, _c, _d, _e;
+ this.applyOrientation();
+ if (connected && type === LiteGraph.OUTPUT) {
+ const types = new Set(
+ this.outputs[0].links.map((l) => app2.graph.links[l].type).filter((t) => t !== "*")
+ );
+ if (types.size > 1) {
+ const linksToDisconnect = [];
+ for (let i = 0; i < this.outputs[0].links.length - 1; i++) {
+ const linkId = this.outputs[0].links[i];
+ const link = app2.graph.links[linkId];
+ linksToDisconnect.push(link);
+ }
+ for (const link of linksToDisconnect) {
+ const node = app2.graph.getNodeById(link.target_id);
+ node.disconnectInput(link.target_slot);
+ }
+ }
+ }
+ let currentNode = this;
+ let updateNodes = [];
+ let inputType = null;
+ let inputNode = null;
+ while (currentNode) {
+ updateNodes.unshift(currentNode);
+ const linkId = currentNode.inputs[0].link;
+ if (linkId !== null) {
+ const link = app2.graph.links[linkId];
+ if (!link) return;
+ const node = app2.graph.getNodeById(link.origin_id);
+ const type2 = node.constructor.type;
+ if (type2 === "Reroute") {
+ if (node === this) {
+ currentNode.disconnectInput(link.target_slot);
+ currentNode = null;
+ } else {
+ currentNode = node;
+ }
+ } else {
+ inputNode = currentNode;
+ inputType = (_b = (_a = node.outputs[link.origin_slot]) == null ? void 0 : _a.type) != null ? _b : null;
+ break;
+ }
+ } else {
+ currentNode = null;
+ break;
+ }
+ }
+ const nodes = [this];
+ let outputType = null;
+ while (nodes.length) {
+ currentNode = nodes.pop();
+ const outputs = (currentNode.outputs ? currentNode.outputs[0].links : []) || [];
+ if (outputs.length) {
+ for (const linkId of outputs) {
+ const link = app2.graph.links[linkId];
+ if (!link) continue;
+ const node = app2.graph.getNodeById(link.target_id);
+ const type2 = node.constructor.type;
+ if (type2 === "Reroute") {
+ nodes.push(node);
+ updateNodes.push(node);
+ } else {
+ const nodeOutType = node.inputs && node.inputs[link == null ? void 0 : link.target_slot] && node.inputs[link.target_slot].type ? node.inputs[link.target_slot].type : null;
+ if (inputType && inputType !== "*" && nodeOutType !== inputType) {
+ node.disconnectInput(link.target_slot);
+ } else {
+ outputType = nodeOutType;
+ }
+ }
+ }
+ } else {
+ }
+ }
+ const displayType = inputType || outputType || "*";
+ const color = LGraphCanvas.link_type_colors[displayType];
+ let widgetConfig;
+ let targetWidget;
+ let widgetType;
+ for (const node of updateNodes) {
+ node.outputs[0].type = inputType || "*";
+ node.__outputType = displayType;
+ node.outputs[0].name = node.properties.showOutputText ? displayType : "";
+ node.size = node.computeSize();
+ node.applyOrientation();
+ for (const l of node.outputs[0].links || []) {
+ const link = app2.graph.links[l];
+ if (link) {
+ link.color = color;
+ if (app2.configuringGraph) continue;
+ const targetNode = app2.graph.getNodeById(link.target_id);
+ const targetInput = (_c = targetNode.inputs) == null ? void 0 : _c[link.target_slot];
+ if (targetInput == null ? void 0 : targetInput.widget) {
+ const config = getWidgetConfig(targetInput);
+ if (!widgetConfig) {
+ widgetConfig = (_d = config[1]) != null ? _d : {};
+ widgetType = config[0];
+ }
+ if (!targetWidget) {
+ targetWidget = (_e = targetNode.widgets) == null ? void 0 : _e.find(
+ (w) => w.name === targetInput.widget.name
+ );
+ }
+ const merged = mergeIfValid(targetInput, [
+ config[0],
+ widgetConfig
+ ]);
+ if (merged.customConfig) {
+ widgetConfig = merged.customConfig;
+ }
+ }
+ }
+ }
+ }
+ for (const node of updateNodes) {
+ if (widgetConfig && outputType) {
+ node.inputs[0].widget = { name: "value" };
+ setWidgetConfig(
+ node.inputs[0],
+ [widgetType != null ? widgetType : displayType, widgetConfig],
+ targetWidget
+ );
+ } else {
+ setWidgetConfig(node.inputs[0], null);
+ }
+ }
+ if (inputNode) {
+ const link = app2.graph.links[inputNode.inputs[0].link];
+ if (link) {
+ link.color = color;
+ }
+ }
+ };
+ this.clone = function() {
+ const cloned = _RerouteNode.prototype.clone.apply(this);
+ cloned.removeOutput(0);
+ cloned.addOutput(this.properties.showOutputText ? "*" : "", "*");
+ cloned.size = cloned.computeSize();
+ return cloned;
+ };
+ this.isVirtualNode = true;
+ }
+ getExtraMenuOptions(_, options) {
+ options.unshift(
+ {
+ content: (this.properties.showOutputText ? "Hide" : "Show") + " Type",
+ callback: /* @__PURE__ */ __name(() => {
+ this.properties.showOutputText = !this.properties.showOutputText;
+ if (this.properties.showOutputText) {
+ this.outputs[0].name = this.__outputType || this.outputs[0].type;
+ } else {
+ this.outputs[0].name = "";
+ }
+ this.size = this.computeSize();
+ this.applyOrientation();
+ app2.graph.setDirtyCanvas(true, true);
+ }, "callback")
+ },
+ {
+ content: (_RerouteNode.defaultVisibility ? "Hide" : "Show") + " Type By Default",
+ callback: /* @__PURE__ */ __name(() => {
+ _RerouteNode.setDefaultTextVisibility(
+ !_RerouteNode.defaultVisibility
+ );
+ }, "callback")
+ },
+ {
+ // naming is inverted with respect to LiteGraphNode.horizontal
+ // LiteGraphNode.horizontal == true means that
+ // each slot in the inputs and outputs are laid out horizontally,
+ // which is the opposite of the visual orientation of the inputs and outputs as a node
+ content: "Set " + (this.properties.horizontal ? "Horizontal" : "Vertical"),
+ callback: /* @__PURE__ */ __name(() => {
+ this.properties.horizontal = !this.properties.horizontal;
+ this.applyOrientation();
+ }, "callback")
+ }
+ );
+ }
+ applyOrientation() {
+ this.horizontal = this.properties.horizontal;
+ if (this.horizontal) {
+ this.inputs[0].pos = [this.size[0] / 2, 0];
+ } else {
+ delete this.inputs[0].pos;
+ }
+ app2.graph.setDirtyCanvas(true, true);
+ }
+ computeSize() {
+ return [
+ this.properties.showOutputText && this.outputs && this.outputs.length ? Math.max(
+ 75,
+ LiteGraph.NODE_TEXT_SIZE * this.outputs[0].name.length * 0.6 + 40
+ ) : 75,
+ 26
+ ];
+ }
+ static setDefaultTextVisibility(visible) {
+ _RerouteNode.defaultVisibility = visible;
+ if (visible) {
+ localStorage["Comfy.RerouteNode.DefaultVisibility"] = "true";
+ } else {
+ delete localStorage["Comfy.RerouteNode.DefaultVisibility"];
+ }
+ }
+ };
+ __name(_RerouteNode, "RerouteNode");
+ __publicField(_RerouteNode, "category");
+ __publicField(_RerouteNode, "defaultVisibility", false);
+ let RerouteNode = _RerouteNode;
+ RerouteNode.setDefaultTextVisibility(
+ !!localStorage["Comfy.RerouteNode.DefaultVisibility"]
+ );
+ LiteGraph.registerNodeType(
+ "Reroute",
+ Object.assign(RerouteNode, {
+ title_mode: LiteGraph.NO_TITLE,
+ title: "Reroute",
+ collapsable: false
+ })
+ );
+ RerouteNode.category = "utils";
+ }
+});
+app.registerExtension({
+ name: "Comfy.SaveImageExtraOutput",
+ beforeRegisterNodeDef(nodeType, nodeData, app2) {
+ return __async(this, null, function* () {
+ if (nodeData.name === "SaveImage" || nodeData.name === "SaveAnimatedWEBP") {
+ const onNodeCreated = nodeType.prototype.onNodeCreated;
+ nodeType.prototype.onNodeCreated = function() {
+ const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : void 0;
+ const widget = this.widgets.find((w) => w.name === "filename_prefix");
+ widget.serializeValue = () => {
+ return applyTextReplacements(app2, widget.value);
+ };
+ return r;
+ };
+ } else {
+ const onNodeCreated = nodeType.prototype.onNodeCreated;
+ nodeType.prototype.onNodeCreated = function() {
+ const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : void 0;
+ if (!this.properties || !("Node name for S&R" in this.properties)) {
+ this.addProperty("Node name for S&R", this.constructor.type, "string");
+ }
+ return r;
+ };
+ }
+ });
+ }
+});
+let touchZooming;
+let touchCount = 0;
+app.registerExtension({
+ name: "Comfy.SimpleTouchSupport",
+ setup() {
+ let zoomPos;
+ let touchTime;
+ let lastTouch;
+ function getMultiTouchPos(e) {
+ return Math.hypot(
+ e.touches[0].clientX - e.touches[1].clientX,
+ e.touches[0].clientY - e.touches[1].clientY
+ );
+ }
+ __name(getMultiTouchPos, "getMultiTouchPos");
+ app.canvasEl.addEventListener(
+ "touchstart",
+ (e) => {
+ var _a, _b;
+ touchCount++;
+ lastTouch = null;
+ if (((_a = e.touches) == null ? void 0 : _a.length) === 1) {
+ touchTime = /* @__PURE__ */ new Date();
+ lastTouch = e.touches[0];
+ } else {
+ touchTime = null;
+ if (((_b = e.touches) == null ? void 0 : _b.length) === 2) {
+ zoomPos = getMultiTouchPos(e);
+ app.canvas.pointer_is_down = false;
+ }
+ }
+ },
+ true
+ );
+ app.canvasEl.addEventListener("touchend", (e) => {
+ var _a, _b, _c;
+ touchZooming = false;
+ touchCount = (_b = (_a = e.touches) == null ? void 0 : _a.length) != null ? _b : touchCount - 1;
+ if (touchTime && !((_c = e.touches) == null ? void 0 : _c.length)) {
+ if ((/* @__PURE__ */ new Date()).getTime() - touchTime > 600) {
+ try {
+ e.constructor = CustomEvent;
+ } catch (error) {
+ }
+ e.clientX = lastTouch.clientX;
+ e.clientY = lastTouch.clientY;
+ app.canvas.pointer_is_down = true;
+ app.canvas._mousedown_callback(e);
+ }
+ touchTime = null;
+ }
+ });
+ app.canvasEl.addEventListener(
+ "touchmove",
+ (e) => {
+ var _a, _b;
+ touchTime = null;
+ if (((_a = e.touches) == null ? void 0 : _a.length) === 2) {
+ app.canvas.pointer_is_down = false;
+ touchZooming = true;
+ LiteGraph.closeAllContextMenus();
+ (_b = app.canvas.search_box) == null ? void 0 : _b.close();
+ const newZoomPos = getMultiTouchPos(e);
+ const midX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
+ const midY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
+ let scale = app.canvas.ds.scale;
+ const diff = zoomPos - newZoomPos;
+ if (diff > 0.5) {
+ scale *= 1 / 1.07;
+ } else if (diff < -0.5) {
+ scale *= 1.07;
+ }
+ app.canvas.ds.changeScale(scale, [midX, midY]);
+ app.canvas.setDirty(true, true);
+ zoomPos = newZoomPos;
+ }
+ },
+ true
+ );
+ }
+});
+const processMouseDown = LGraphCanvas.prototype.processMouseDown;
+LGraphCanvas.prototype.processMouseDown = function(e) {
+ if (touchZooming || touchCount) {
+ return;
+ }
+ return processMouseDown.apply(this, arguments);
+};
+const processMouseMove = LGraphCanvas.prototype.processMouseMove;
+LGraphCanvas.prototype.processMouseMove = function(e) {
+ if (touchZooming || touchCount > 1) {
+ return;
+ }
+ return processMouseMove.apply(this, arguments);
+};
+app.registerExtension({
+ name: "Comfy.SlotDefaults",
+ suggestionsNumber: null,
+ init() {
+ LiteGraph.search_filter_enabled = true;
+ LiteGraph.middle_click_slot_add_default_node = true;
+ this.suggestionsNumber = app.ui.settings.addSetting({
+ id: "Comfy.NodeSuggestions.number",
+ name: "Number of nodes suggestions",
+ type: "slider",
+ attrs: {
+ min: 1,
+ max: 100,
+ step: 1
+ },
+ defaultValue: 5,
+ onChange: /* @__PURE__ */ __name((newVal, oldVal) => {
+ this.setDefaults(newVal);
+ }, "onChange")
+ });
+ },
+ slot_types_default_out: {},
+ slot_types_default_in: {},
+ beforeRegisterNodeDef(nodeType, nodeData, app2) {
+ return __async(this, null, function* () {
+ var nodeId = nodeData.name;
+ var inputs = [];
+ inputs = nodeData["input"]["required"];
+ for (const inputKey in inputs) {
+ var input = inputs[inputKey];
+ if (typeof input[0] !== "string") continue;
+ var type = input[0];
+ if (type in ComfyWidgets) {
+ var customProperties = input[1];
+ if (!(customProperties == null ? void 0 : customProperties.forceInput)) continue;
+ }
+ if (!(type in this.slot_types_default_out)) {
+ this.slot_types_default_out[type] = ["Reroute"];
+ }
+ if (this.slot_types_default_out[type].includes(nodeId)) continue;
+ this.slot_types_default_out[type].push(nodeId);
+ const lowerType = type.toLocaleLowerCase();
+ if (!(lowerType in LiteGraph.registered_slot_in_types)) {
+ LiteGraph.registered_slot_in_types[lowerType] = { nodes: [] };
+ }
+ LiteGraph.registered_slot_in_types[lowerType].nodes.push(
+ nodeType.comfyClass
+ );
+ }
+ var outputs = nodeData["output"];
+ for (const key in outputs) {
+ var type = outputs[key];
+ if (!(type in this.slot_types_default_in)) {
+ this.slot_types_default_in[type] = ["Reroute"];
+ }
+ this.slot_types_default_in[type].push(nodeId);
+ if (!(type in LiteGraph.registered_slot_out_types)) {
+ LiteGraph.registered_slot_out_types[type] = { nodes: [] };
+ }
+ LiteGraph.registered_slot_out_types[type].nodes.push(nodeType.comfyClass);
+ if (!LiteGraph.slot_types_out.includes(type)) {
+ LiteGraph.slot_types_out.push(type);
+ }
+ }
+ var maxNum = this.suggestionsNumber.value;
+ this.setDefaults(maxNum);
+ });
+ },
+ setDefaults(maxNum) {
+ LiteGraph.slot_types_default_out = {};
+ LiteGraph.slot_types_default_in = {};
+ for (const type in this.slot_types_default_out) {
+ LiteGraph.slot_types_default_out[type] = this.slot_types_default_out[type].slice(0, maxNum);
+ }
+ for (const type in this.slot_types_default_in) {
+ LiteGraph.slot_types_default_in[type] = this.slot_types_default_in[type].slice(0, maxNum);
+ }
+ }
+});
+function roundVectorToGrid(vec) {
+ vec[0] = LiteGraph.CANVAS_GRID_SIZE * Math.round(vec[0] / LiteGraph.CANVAS_GRID_SIZE);
+ vec[1] = LiteGraph.CANVAS_GRID_SIZE * Math.round(vec[1] / LiteGraph.CANVAS_GRID_SIZE);
+ return vec;
+}
+__name(roundVectorToGrid, "roundVectorToGrid");
+app.registerExtension({
+ name: "Comfy.SnapToGrid",
+ init() {
+ app.ui.settings.addSetting({
+ id: "Comfy.SnapToGrid.GridSize",
+ name: "Grid Size",
+ type: "slider",
+ attrs: {
+ min: 1,
+ max: 500
+ },
+ tooltip: "When dragging and resizing nodes while holding shift they will be aligned to the grid, this controls the size of that grid.",
+ defaultValue: LiteGraph.CANVAS_GRID_SIZE,
+ onChange(value) {
+ LiteGraph.CANVAS_GRID_SIZE = +value;
+ }
+ });
+ const onNodeMoved = app.canvas.onNodeMoved;
+ app.canvas.onNodeMoved = function(node) {
+ const r = onNodeMoved == null ? void 0 : onNodeMoved.apply(this, arguments);
+ if (app.shiftDown) {
+ for (const id2 in this.selected_nodes) {
+ this.selected_nodes[id2].alignToGrid();
+ }
+ }
+ return r;
+ };
+ const onNodeAdded = app.graph.onNodeAdded;
+ app.graph.onNodeAdded = function(node) {
+ const onResize = node.onResize;
+ node.onResize = function() {
+ if (app.shiftDown) {
+ roundVectorToGrid(node.size);
+ }
+ return onResize == null ? void 0 : onResize.apply(this, arguments);
+ };
+ return onNodeAdded == null ? void 0 : onNodeAdded.apply(this, arguments);
+ };
+ const origDrawNode = LGraphCanvas.prototype.drawNode;
+ LGraphCanvas.prototype.drawNode = function(node, ctx) {
+ if (app.shiftDown && this.node_dragged && node.id in this.selected_nodes) {
+ const [x, y] = roundVectorToGrid([...node.pos]);
+ const shiftX = x - node.pos[0];
+ let shiftY = y - node.pos[1];
+ let w, h;
+ if (node.flags.collapsed) {
+ w = node._collapsed_width;
+ h = LiteGraph.NODE_TITLE_HEIGHT;
+ shiftY -= LiteGraph.NODE_TITLE_HEIGHT;
+ } else {
+ w = node.size[0];
+ h = node.size[1];
+ let titleMode = node.constructor.title_mode;
+ if (titleMode !== LiteGraph.TRANSPARENT_TITLE && titleMode !== LiteGraph.NO_TITLE) {
+ h += LiteGraph.NODE_TITLE_HEIGHT;
+ shiftY -= LiteGraph.NODE_TITLE_HEIGHT;
+ }
+ }
+ const f = ctx.fillStyle;
+ ctx.fillStyle = "rgba(100, 100, 100, 0.5)";
+ ctx.fillRect(shiftX, shiftY, w, h);
+ ctx.fillStyle = f;
+ }
+ return origDrawNode.apply(this, arguments);
+ };
+ let selectedAndMovingGroup = null;
+ const groupMove = LGraphGroup.prototype.move;
+ LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) {
+ const v = groupMove.apply(this, arguments);
+ if (!selectedAndMovingGroup && app.canvas.selected_group === this && (deltax || deltay)) {
+ selectedAndMovingGroup = this;
+ }
+ if (app.canvas.last_mouse_dragging === false && app.shiftDown) {
+ this.recomputeInsideNodes();
+ for (const node of this._nodes) {
+ node.alignToGrid();
+ }
+ LGraphNode.prototype.alignToGrid.apply(this);
+ }
+ return v;
+ };
+ const drawGroups = LGraphCanvas.prototype.drawGroups;
+ LGraphCanvas.prototype.drawGroups = function(canvas, ctx) {
+ if (this.selected_group && app.shiftDown) {
+ if (this.selected_group_resizing) {
+ roundVectorToGrid(this.selected_group.size);
+ } else if (selectedAndMovingGroup) {
+ const [x, y] = roundVectorToGrid([...selectedAndMovingGroup.pos]);
+ const f = ctx.fillStyle;
+ const s = ctx.strokeStyle;
+ ctx.fillStyle = "rgba(100, 100, 100, 0.33)";
+ ctx.strokeStyle = "rgba(100, 100, 100, 0.66)";
+ ctx.rect(x, y, ...selectedAndMovingGroup.size);
+ ctx.fill();
+ ctx.stroke();
+ ctx.fillStyle = f;
+ ctx.strokeStyle = s;
+ }
+ } else if (!this.selected_group) {
+ selectedAndMovingGroup = null;
+ }
+ return drawGroups.apply(this, arguments);
+ };
+ const onGroupAdd = LGraphCanvas.onGroupAdd;
+ LGraphCanvas.onGroupAdd = function() {
+ const v = onGroupAdd.apply(app.canvas, arguments);
+ if (app.shiftDown) {
+ const lastGroup = app.graph._groups[app.graph._groups.length - 1];
+ if (lastGroup) {
+ roundVectorToGrid(lastGroup.pos);
+ roundVectorToGrid(lastGroup.size);
+ }
+ }
+ return v;
+ };
+ }
+});
+app.registerExtension({
+ name: "Comfy.UploadImage",
+ beforeRegisterNodeDef(nodeType, nodeData, app2) {
+ return __async(this, null, function* () {
+ var _a, _b, _c, _d;
+ if (((_d = (_c = (_b = (_a = nodeData == null ? void 0 : nodeData.input) == null ? void 0 : _a.required) == null ? void 0 : _b.image) == null ? void 0 : _c[1]) == null ? void 0 : _d.image_upload) === true) {
+ nodeData.input.required.upload = ["IMAGEUPLOAD"];
+ }
+ });
+ }
+});
+const WEBCAM_READY = Symbol();
+app.registerExtension({
+ name: "Comfy.WebcamCapture",
+ getCustomWidgets(app2) {
+ return {
+ WEBCAM(node, inputName) {
+ let res;
+ node[WEBCAM_READY] = new Promise((resolve) => res = resolve);
+ const container = document.createElement("div");
+ container.style.background = "rgba(0,0,0,0.25)";
+ container.style.textAlign = "center";
+ const video = document.createElement("video");
+ video.style.height = video.style.width = "100%";
+ const loadVideo = /* @__PURE__ */ __name(() => __async(this, null, function* () {
+ try {
+ const stream = yield navigator.mediaDevices.getUserMedia({
+ video: true,
+ audio: false
+ });
+ container.replaceChildren(video);
+ setTimeout(() => res(video), 500);
+ video.addEventListener("loadedmetadata", () => res(video), false);
+ video.srcObject = stream;
+ video.play();
+ } catch (error) {
+ const label = document.createElement("div");
+ label.style.color = "red";
+ label.style.overflow = "auto";
+ label.style.maxHeight = "100%";
+ label.style.whiteSpace = "pre-wrap";
+ if (window.isSecureContext) {
+ label.textContent = "Unable to load webcam, please ensure access is granted:\n" + error.message;
+ } else {
+ label.textContent = "Unable to load webcam. A secure context is required, if you are not accessing ComfyUI on localhost (127.0.0.1) you will have to enable TLS (https)\n\n" + error.message;
+ }
+ container.replaceChildren(label);
+ }
+ }), "loadVideo");
+ loadVideo();
+ return { widget: node.addDOMWidget(inputName, "WEBCAM", container) };
+ }
+ };
+ },
+ nodeCreated(node) {
+ if (node.type, node.constructor.comfyClass !== "WebcamCapture") return;
+ let video;
+ const camera = node.widgets.find((w2) => w2.name === "image");
+ const w = node.widgets.find((w2) => w2.name === "width");
+ const h = node.widgets.find((w2) => w2.name === "height");
+ const captureOnQueue = node.widgets.find(
+ (w2) => w2.name === "capture_on_queue"
+ );
+ const canvas = document.createElement("canvas");
+ const capture = /* @__PURE__ */ __name(() => {
+ canvas.width = w.value;
+ canvas.height = h.value;
+ const ctx = canvas.getContext("2d");
+ ctx.drawImage(video, 0, 0, w.value, h.value);
+ const data = canvas.toDataURL("image/png");
+ const img = new Image();
+ img.onload = () => {
+ node.imgs = [img];
+ app.graph.setDirtyCanvas(true);
+ requestAnimationFrame(() => {
+ var _a;
+ (_a = node.setSizeForImage) == null ? void 0 : _a.call(node);
+ });
+ };
+ img.src = data;
+ }, "capture");
+ const btn = node.addWidget(
+ "button",
+ "waiting for camera...",
+ "capture",
+ capture
+ );
+ btn.disabled = true;
+ btn.serializeValue = () => void 0;
+ camera.serializeValue = () => __async(this, null, function* () {
+ var _a;
+ if (captureOnQueue.value) {
+ capture();
+ } else if (!((_a = node.imgs) == null ? void 0 : _a.length)) {
+ const err = `No webcam image captured`;
+ alert(err);
+ throw new Error(err);
+ }
+ const blob = yield new Promise((r) => canvas.toBlob(r));
+ const name = `${+/* @__PURE__ */ new Date()}.png`;
+ const file2 = new File([blob], name);
+ const body = new FormData();
+ body.append("image", file2);
+ body.append("subfolder", "webcam");
+ body.append("type", "temp");
+ const resp = yield api.fetchApi("/upload/image", {
+ method: "POST",
+ body
+ });
+ if (resp.status !== 200) {
+ const err = `Error uploading camera image: ${resp.status} - ${resp.statusText}`;
+ alert(err);
+ throw new Error(err);
+ }
+ return `webcam/${name} [temp]`;
+ });
+ node[WEBCAM_READY].then((v) => {
+ video = v;
+ if (!w.value) {
+ w.value = video.videoWidth || 640;
+ h.value = video.videoHeight || 480;
+ }
+ btn.disabled = false;
+ btn.label = "capture";
+ });
+ }
+});
+function splitFilePath(path) {
+ const folder_separator = path.lastIndexOf("/");
+ if (folder_separator === -1) {
+ return ["", path];
+ }
+ return [
+ path.substring(0, folder_separator),
+ path.substring(folder_separator + 1)
+ ];
+}
+__name(splitFilePath, "splitFilePath");
+function getResourceURL(subfolder, filename, type = "input") {
+ const params = [
+ "filename=" + encodeURIComponent(filename),
+ "type=" + type,
+ "subfolder=" + subfolder,
+ app.getRandParam().substring(1)
+ ].join("&");
+ return `/view?${params}`;
+}
+__name(getResourceURL, "getResourceURL");
+function uploadFile(audioWidget, audioUIWidget, file2, updateNode, pasted = false) {
+ return __async(this, null, function* () {
+ try {
+ const body = new FormData();
+ body.append("image", file2);
+ if (pasted) body.append("subfolder", "pasted");
+ const resp = yield api.fetchApi("/upload/image", {
+ method: "POST",
+ body
+ });
+ if (resp.status === 200) {
+ const data = yield resp.json();
+ let path = data.name;
+ if (data.subfolder) path = data.subfolder + "/" + path;
+ if (!audioWidget.options.values.includes(path)) {
+ audioWidget.options.values.push(path);
+ }
+ if (updateNode) {
+ audioUIWidget.element.src = api.apiURL(
+ getResourceURL(...splitFilePath(path))
+ );
+ audioWidget.value = path;
+ }
+ } else {
+ alert(resp.status + " - " + resp.statusText);
+ }
+ } catch (error) {
+ alert(error);
+ }
+ });
+}
+__name(uploadFile, "uploadFile");
+app.registerExtension({
+ name: "Comfy.AudioWidget",
+ beforeRegisterNodeDef(nodeType, nodeData) {
+ return __async(this, null, function* () {
+ if (["LoadAudio", "SaveAudio", "PreviewAudio"].includes(nodeType.comfyClass)) {
+ nodeData.input.required.audioUI = ["AUDIO_UI"];
+ }
+ });
+ },
+ getCustomWidgets() {
+ return {
+ AUDIO_UI(node, inputName) {
+ const audio = document.createElement("audio");
+ audio.controls = true;
+ audio.classList.add("comfy-audio");
+ audio.setAttribute("name", "media");
+ const audioUIWidget = node.addDOMWidget(
+ inputName,
+ /* name=*/
+ "audioUI",
+ audio
+ );
+ audioUIWidget.serialize = false;
+ const isOutputNode = node.constructor.nodeData.output_node;
+ if (isOutputNode) {
+ audioUIWidget.element.classList.add("empty-audio-widget");
+ const onExecuted = node.onExecuted;
+ node.onExecuted = function(message) {
+ onExecuted == null ? void 0 : onExecuted.apply(this, arguments);
+ const audios = message.audio;
+ if (!audios) return;
+ const audio2 = audios[0];
+ audioUIWidget.element.src = api.apiURL(
+ getResourceURL(audio2.subfolder, audio2.filename, audio2.type)
+ );
+ audioUIWidget.element.classList.remove("empty-audio-widget");
+ };
+ }
+ return { widget: audioUIWidget };
+ }
+ };
+ },
+ onNodeOutputsUpdated(nodeOutputs) {
+ for (const [nodeId, output] of Object.entries(nodeOutputs)) {
+ const node = app.graph.getNodeById(Number.parseInt(nodeId));
+ if ("audio" in output) {
+ const audioUIWidget = node.widgets.find(
+ (w) => w.name === "audioUI"
+ );
+ const audio = output.audio[0];
+ audioUIWidget.element.src = api.apiURL(
+ getResourceURL(audio.subfolder, audio.filename, audio.type)
+ );
+ audioUIWidget.element.classList.remove("empty-audio-widget");
+ }
+ }
+ }
+});
+app.registerExtension({
+ name: "Comfy.UploadAudio",
+ beforeRegisterNodeDef(nodeType, nodeData) {
+ return __async(this, null, function* () {
+ var _a, _b, _c, _d;
+ if (((_d = (_c = (_b = (_a = nodeData == null ? void 0 : nodeData.input) == null ? void 0 : _a.required) == null ? void 0 : _b.audio) == null ? void 0 : _c[1]) == null ? void 0 : _d.audio_upload) === true) {
+ nodeData.input.required.upload = ["AUDIOUPLOAD"];
+ }
+ });
+ },
+ getCustomWidgets() {
+ return {
+ AUDIOUPLOAD(node, inputName) {
+ const audioWidget = node.widgets.find(
+ (w) => w.name === "audio"
+ );
+ const audioUIWidget = node.widgets.find(
+ (w) => w.name === "audioUI"
+ );
+ const onAudioWidgetUpdate = /* @__PURE__ */ __name(() => {
+ audioUIWidget.element.src = api.apiURL(
+ getResourceURL(...splitFilePath(audioWidget.value))
+ );
+ }, "onAudioWidgetUpdate");
+ if (audioWidget.value) {
+ onAudioWidgetUpdate();
+ }
+ audioWidget.callback = onAudioWidgetUpdate;
+ const onGraphConfigured = node.onGraphConfigured;
+ node.onGraphConfigured = function() {
+ onGraphConfigured == null ? void 0 : onGraphConfigured.apply(this, arguments);
+ if (audioWidget.value) {
+ onAudioWidgetUpdate();
+ }
+ };
+ const fileInput = document.createElement("input");
+ fileInput.type = "file";
+ fileInput.accept = "audio/*";
+ fileInput.style.display = "none";
+ fileInput.onchange = () => {
+ if (fileInput.files.length) {
+ uploadFile(audioWidget, audioUIWidget, fileInput.files[0], true);
+ }
+ };
+ const uploadWidget = node.addWidget(
+ "button",
+ inputName,
+ /* value=*/
+ "",
+ () => {
+ fileInput.click();
+ }
+ );
+ uploadWidget.label = "choose file to upload";
+ uploadWidget.serialize = false;
+ return { widget: uploadWidget };
+ }
+ };
+ }
+});
+//# sourceMappingURL=index--0nRVkuV.js.map
diff --git a/web/assets/index--0nRVkuV.js.map b/web/assets/index--0nRVkuV.js.map
new file mode 100644
index 000000000..bb517b660
--- /dev/null
+++ b/web/assets/index--0nRVkuV.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index--0nRVkuV.js","sources":["../../src/extensions/core/clipspace.ts","../../src/extensions/core/colorPalette.ts","../../src/extensions/core/contextMenuFilter.ts","../../src/extensions/core/dynamicPrompts.ts","../../src/extensions/core/editAttention.ts","../../src/extensions/core/widgetInputs.ts","../../src/extensions/core/groupNodeManage.ts","../../src/extensions/core/groupNode.ts","../../src/extensions/core/groupOptions.ts","../../src/extensions/core/invertMenuScrolling.ts","../../src/extensions/core/keybinds.ts","../../src/extensions/core/linkRenderMode.ts","../../src/extensions/core/maskeditor.ts","../../src/extensions/core/nodeTemplates.ts","../../src/extensions/core/noteNode.ts","../../src/extensions/core/rerouteNode.ts","../../src/extensions/core/saveImageExtraOutput.ts","../../src/extensions/core/simpleTouchSupport.ts","../../src/extensions/core/slotDefaults.ts","../../src/extensions/core/snapToGrid.ts","../../src/extensions/core/uploadImage.ts","../../src/extensions/core/webcamCapture.ts","../../src/extensions/core/uploadAudio.ts"],"sourcesContent":["import { app } from '../../scripts/app'\r\nimport { ComfyDialog, $el } from '../../scripts/ui'\r\nimport { ComfyApp } from '../../scripts/app'\r\n\r\nexport class ClipspaceDialog extends ComfyDialog {\r\n static items = []\r\n static instance = null\r\n\r\n static registerButton(name, contextPredicate, callback) {\r\n const item = $el('button', {\r\n type: 'button',\r\n textContent: name,\r\n contextPredicate: contextPredicate,\r\n onclick: callback\r\n })\r\n\r\n ClipspaceDialog.items.push(item)\r\n }\r\n\r\n static invalidatePreview() {\r\n if (\r\n ComfyApp.clipspace &&\r\n ComfyApp.clipspace.imgs &&\r\n ComfyApp.clipspace.imgs.length > 0\r\n ) {\r\n const img_preview = document.getElementById(\r\n 'clipspace_preview'\r\n ) as HTMLImageElement\r\n if (img_preview) {\r\n img_preview.src =\r\n ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src\r\n img_preview.style.maxHeight = '100%'\r\n img_preview.style.maxWidth = '100%'\r\n }\r\n }\r\n }\r\n\r\n static invalidate() {\r\n if (ClipspaceDialog.instance) {\r\n const self = ClipspaceDialog.instance\r\n // allow reconstruct controls when copying from non-image to image content.\r\n const children = $el('div.comfy-modal-content', [\r\n self.createImgSettings(),\r\n ...self.createButtons()\r\n ])\r\n\r\n if (self.element) {\r\n // update\r\n self.element.removeChild(self.element.firstChild)\r\n self.element.appendChild(children)\r\n } else {\r\n // new\r\n self.element = $el('div.comfy-modal', { parent: document.body }, [\r\n children\r\n ])\r\n }\r\n\r\n if (self.element.children[0].children.length <= 1) {\r\n self.element.children[0].appendChild(\r\n $el('p', {}, [\r\n 'Unable to find the features to edit content of a format stored in the current Clipspace.'\r\n ])\r\n )\r\n }\r\n\r\n ClipspaceDialog.invalidatePreview()\r\n }\r\n }\r\n\r\n constructor() {\r\n super()\r\n }\r\n\r\n createButtons() {\r\n const buttons = []\r\n\r\n for (let idx in ClipspaceDialog.items) {\r\n const item = ClipspaceDialog.items[idx]\r\n if (!item.contextPredicate || item.contextPredicate())\r\n buttons.push(ClipspaceDialog.items[idx])\r\n }\r\n\r\n buttons.push(\r\n $el('button', {\r\n type: 'button',\r\n textContent: 'Close',\r\n onclick: () => {\r\n this.close()\r\n }\r\n })\r\n )\r\n\r\n return buttons\r\n }\r\n\r\n createImgSettings() {\r\n if (ComfyApp.clipspace.imgs) {\r\n const combo_items = []\r\n const imgs = ComfyApp.clipspace.imgs\r\n\r\n for (let i = 0; i < imgs.length; i++) {\r\n combo_items.push($el('option', { value: i }, [`${i}`]))\r\n }\r\n\r\n const combo1 = $el(\r\n 'select',\r\n {\r\n id: 'clipspace_img_selector',\r\n onchange: (event) => {\r\n ComfyApp.clipspace['selectedIndex'] = event.target.selectedIndex\r\n ClipspaceDialog.invalidatePreview()\r\n }\r\n },\r\n combo_items\r\n )\r\n\r\n const row1 = $el('tr', {}, [\r\n $el('td', {}, [$el('font', { color: 'white' }, ['Select Image'])]),\r\n $el('td', {}, [combo1])\r\n ])\r\n\r\n const combo2 = $el(\r\n 'select',\r\n {\r\n id: 'clipspace_img_paste_mode',\r\n onchange: (event) => {\r\n ComfyApp.clipspace['img_paste_mode'] = event.target.value\r\n }\r\n },\r\n [\r\n $el('option', { value: 'selected' }, 'selected'),\r\n $el('option', { value: 'all' }, 'all')\r\n ]\r\n ) as HTMLSelectElement\r\n combo2.value = ComfyApp.clipspace['img_paste_mode']\r\n\r\n const row2 = $el('tr', {}, [\r\n $el('td', {}, [$el('font', { color: 'white' }, ['Paste Mode'])]),\r\n $el('td', {}, [combo2])\r\n ])\r\n\r\n const td = $el(\r\n 'td',\r\n { align: 'center', width: '100px', height: '100px', colSpan: '2' },\r\n [$el('img', { id: 'clipspace_preview', ondragstart: () => false }, [])]\r\n )\r\n\r\n const row3 = $el('tr', {}, [td])\r\n\r\n return $el('table', {}, [row1, row2, row3])\r\n } else {\r\n return []\r\n }\r\n }\r\n\r\n createImgPreview() {\r\n if (ComfyApp.clipspace.imgs) {\r\n return $el('img', { id: 'clipspace_preview', ondragstart: () => false })\r\n } else return []\r\n }\r\n\r\n show() {\r\n const img_preview = document.getElementById('clipspace_preview')\r\n ClipspaceDialog.invalidate()\r\n\r\n this.element.style.display = 'block'\r\n }\r\n}\r\n\r\napp.registerExtension({\r\n name: 'Comfy.Clipspace',\r\n init(app) {\r\n app.openClipspace = function () {\r\n if (!ClipspaceDialog.instance) {\r\n ClipspaceDialog.instance = new ClipspaceDialog()\r\n ComfyApp.clipspace_invalidate_handler = ClipspaceDialog.invalidate\r\n }\r\n\r\n if (ComfyApp.clipspace) {\r\n ClipspaceDialog.instance.show()\r\n } else app.ui.dialog.show('Clipspace is Empty!')\r\n }\r\n }\r\n})\r\n","import { lightenColor } from '@/utils/colorUtil'\r\nimport { app } from '../../scripts/app'\r\nimport { $el } from '../../scripts/ui'\r\nimport type { ColorPalettes, Palette } from '@/types/colorPalette'\r\nimport { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph'\r\n\r\n// Manage color palettes\r\n\r\nconst colorPalettes: ColorPalettes = {\r\n dark: {\r\n id: 'dark',\r\n name: 'Dark (Default)',\r\n colors: {\r\n node_slot: {\r\n CLIP: '#FFD500', // bright yellow\r\n CLIP_VISION: '#A8DADC', // light blue-gray\r\n CLIP_VISION_OUTPUT: '#ad7452', // rusty brown-orange\r\n CONDITIONING: '#FFA931', // vibrant orange-yellow\r\n CONTROL_NET: '#6EE7B7', // soft mint green\r\n IMAGE: '#64B5F6', // bright sky blue\r\n LATENT: '#FF9CF9', // light pink-purple\r\n MASK: '#81C784', // muted green\r\n MODEL: '#B39DDB', // light lavender-purple\r\n STYLE_MODEL: '#C2FFAE', // light green-yellow\r\n VAE: '#FF6E6E', // bright red\r\n NOISE: '#B0B0B0', // gray\r\n GUIDER: '#66FFFF', // cyan\r\n SAMPLER: '#ECB4B4', // very soft red\r\n SIGMAS: '#CDFFCD', // soft lime green\r\n TAESD: '#DCC274' // cheesecake\r\n },\r\n litegraph_base: {\r\n BACKGROUND_IMAGE:\r\n 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII=',\r\n CLEAR_BACKGROUND_COLOR: '#222',\r\n NODE_TITLE_COLOR: '#999',\r\n NODE_SELECTED_TITLE_COLOR: '#FFF',\r\n NODE_TEXT_SIZE: 14,\r\n NODE_TEXT_COLOR: '#AAA',\r\n NODE_SUBTEXT_SIZE: 12,\r\n NODE_DEFAULT_COLOR: '#333',\r\n NODE_DEFAULT_BGCOLOR: '#353535',\r\n NODE_DEFAULT_BOXCOLOR: '#666',\r\n NODE_DEFAULT_SHAPE: 'box',\r\n NODE_BOX_OUTLINE_COLOR: '#FFF',\r\n DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0.5)',\r\n DEFAULT_GROUP_FONT: 24,\r\n\r\n WIDGET_BGCOLOR: '#222',\r\n WIDGET_OUTLINE_COLOR: '#666',\r\n WIDGET_TEXT_COLOR: '#DDD',\r\n WIDGET_SECONDARY_TEXT_COLOR: '#999',\r\n\r\n LINK_COLOR: '#9A9',\r\n EVENT_LINK_COLOR: '#A86',\r\n CONNECTING_LINK_COLOR: '#AFA'\r\n },\r\n comfy_base: {\r\n 'fg-color': '#fff',\r\n 'bg-color': '#202020',\r\n 'comfy-menu-bg': '#353535',\r\n 'comfy-input-bg': '#222',\r\n 'input-text': '#ddd',\r\n 'descrip-text': '#999',\r\n 'drag-text': '#ccc',\r\n 'error-text': '#ff4444',\r\n 'border-color': '#4e4e4e',\r\n 'tr-even-bg-color': '#222',\r\n 'tr-odd-bg-color': '#353535',\r\n 'content-bg': '#4e4e4e',\r\n 'content-fg': '#fff',\r\n 'content-hover-bg': '#222',\r\n 'content-hover-fg': '#fff'\r\n }\r\n }\r\n },\r\n light: {\r\n id: 'light',\r\n name: 'Light',\r\n colors: {\r\n node_slot: {\r\n CLIP: '#FFA726', // orange\r\n CLIP_VISION: '#5C6BC0', // indigo\r\n CLIP_VISION_OUTPUT: '#8D6E63', // brown\r\n CONDITIONING: '#EF5350', // red\r\n CONTROL_NET: '#66BB6A', // green\r\n IMAGE: '#42A5F5', // blue\r\n LATENT: '#AB47BC', // purple\r\n MASK: '#9CCC65', // light green\r\n MODEL: '#7E57C2', // deep purple\r\n STYLE_MODEL: '#D4E157', // lime\r\n VAE: '#FF7043' // deep orange\r\n },\r\n litegraph_base: {\r\n BACKGROUND_IMAGE:\r\n 'data:image/gif;base64,R0lGODlhZABkALMAAAAAAP///+vr6+rq6ujo6Ofn5+bm5uXl5d3d3f///wAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAkALAAAAABkAGQAAAT/UMhJq7046827HkcoHkYxjgZhnGG6si5LqnIM0/fL4qwwIMAg0CAsEovBIxKhRDaNy2GUOX0KfVFrssrNdpdaqTeKBX+dZ+jYvEaTf+y4W66mC8PUdrE879f9d2mBeoNLfH+IhYBbhIx2jkiHiomQlGKPl4uZe3CaeZifnnijgkESBqipqqusra6vsLGys62SlZO4t7qbuby7CLa+wqGWxL3Gv3jByMOkjc2lw8vOoNSi0czAncXW3Njdx9Pf48/Z4Kbbx+fQ5evZ4u3k1fKR6cn03vHlp7T9/v8A/8Gbp4+gwXoFryXMB2qgwoMMHyKEqA5fxX322FG8tzBcRnMW/zlulPbRncmQGidKjMjyYsOSKEF2FBlJQMCbOHP6c9iSZs+UnGYCdbnSo1CZI5F64kn0p1KnTH02nSoV3dGTV7FFHVqVq1dtWcMmVQZTbNGu72zqXMuW7danVL+6e4t1bEy6MeueBYLXrNO5Ze36jQtWsOG97wIj1vt3St/DjTEORss4nNq2mDP3e7w4r1bFkSET5hy6s2TRlD2/mSxXtSHQhCunXo26NevCpmvD/UU6tuullzULH76q92zdZG/Ltv1a+W+osI/nRmyc+fRi1Xdbh+68+0vv10dH3+77KD/i6IdnX669/frn5Zsjh4/2PXju8+8bzc9/6fj27LFnX11/+IUnXWl7BJfegm79FyB9JOl3oHgSklefgxAC+FmFGpqHIYcCfkhgfCohSKKJVo044YUMttggiBkmp6KFXw1oII24oYhjiDByaKOOHcp3Y5BD/njikSkO+eBREQAAOw==',\r\n CLEAR_BACKGROUND_COLOR: 'lightgray',\r\n NODE_TITLE_COLOR: '#222',\r\n NODE_SELECTED_TITLE_COLOR: '#000',\r\n NODE_TEXT_SIZE: 14,\r\n NODE_TEXT_COLOR: '#444',\r\n NODE_SUBTEXT_SIZE: 12,\r\n NODE_DEFAULT_COLOR: '#F7F7F7',\r\n NODE_DEFAULT_BGCOLOR: '#F5F5F5',\r\n NODE_DEFAULT_BOXCOLOR: '#CCC',\r\n NODE_DEFAULT_SHAPE: 'box',\r\n NODE_BOX_OUTLINE_COLOR: '#000',\r\n DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0.1)',\r\n DEFAULT_GROUP_FONT: 24,\r\n\r\n WIDGET_BGCOLOR: '#D4D4D4',\r\n WIDGET_OUTLINE_COLOR: '#999',\r\n WIDGET_TEXT_COLOR: '#222',\r\n WIDGET_SECONDARY_TEXT_COLOR: '#555',\r\n\r\n LINK_COLOR: '#4CAF50',\r\n EVENT_LINK_COLOR: '#FF9800',\r\n CONNECTING_LINK_COLOR: '#2196F3'\r\n },\r\n comfy_base: {\r\n 'fg-color': '#222',\r\n 'bg-color': '#DDD',\r\n 'comfy-menu-bg': '#F5F5F5',\r\n 'comfy-input-bg': '#C9C9C9',\r\n 'input-text': '#222',\r\n 'descrip-text': '#444',\r\n 'drag-text': '#555',\r\n 'error-text': '#F44336',\r\n 'border-color': '#888',\r\n 'tr-even-bg-color': '#f9f9f9',\r\n 'tr-odd-bg-color': '#fff',\r\n 'content-bg': '#e0e0e0',\r\n 'content-fg': '#222',\r\n 'content-hover-bg': '#adadad',\r\n 'content-hover-fg': '#222'\r\n }\r\n }\r\n },\r\n solarized: {\r\n id: 'solarized',\r\n name: 'Solarized',\r\n colors: {\r\n node_slot: {\r\n CLIP: '#2AB7CA', // light blue\r\n CLIP_VISION: '#6c71c4', // blue violet\r\n CLIP_VISION_OUTPUT: '#859900', // olive green\r\n CONDITIONING: '#d33682', // magenta\r\n CONTROL_NET: '#d1ffd7', // light mint green\r\n IMAGE: '#5940bb', // deep blue violet\r\n LATENT: '#268bd2', // blue\r\n MASK: '#CCC9E7', // light purple-gray\r\n MODEL: '#dc322f', // red\r\n STYLE_MODEL: '#1a998a', // teal\r\n UPSCALE_MODEL: '#054A29', // dark green\r\n VAE: '#facfad' // light pink-orange\r\n },\r\n litegraph_base: {\r\n NODE_TITLE_COLOR: '#fdf6e3', // Base3\r\n NODE_SELECTED_TITLE_COLOR: '#A9D400',\r\n NODE_TEXT_SIZE: 14,\r\n NODE_TEXT_COLOR: '#657b83', // Base00\r\n NODE_SUBTEXT_SIZE: 12,\r\n NODE_DEFAULT_COLOR: '#094656',\r\n NODE_DEFAULT_BGCOLOR: '#073642', // Base02\r\n NODE_DEFAULT_BOXCOLOR: '#839496', // Base0\r\n NODE_DEFAULT_SHAPE: 'box',\r\n NODE_BOX_OUTLINE_COLOR: '#fdf6e3', // Base3\r\n DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0.5)',\r\n DEFAULT_GROUP_FONT: 24,\r\n\r\n WIDGET_BGCOLOR: '#002b36', // Base03\r\n WIDGET_OUTLINE_COLOR: '#839496', // Base0\r\n WIDGET_TEXT_COLOR: '#fdf6e3', // Base3\r\n WIDGET_SECONDARY_TEXT_COLOR: '#93a1a1', // Base1\r\n\r\n LINK_COLOR: '#2aa198', // Solarized Cyan\r\n EVENT_LINK_COLOR: '#268bd2', // Solarized Blue\r\n CONNECTING_LINK_COLOR: '#859900' // Solarized Green\r\n },\r\n comfy_base: {\r\n 'fg-color': '#fdf6e3', // Base3\r\n 'bg-color': '#002b36', // Base03\r\n 'comfy-menu-bg': '#073642', // Base02\r\n 'comfy-input-bg': '#002b36', // Base03\r\n 'input-text': '#93a1a1', // Base1\r\n 'descrip-text': '#586e75', // Base01\r\n 'drag-text': '#839496', // Base0\r\n 'error-text': '#dc322f', // Solarized Red\r\n 'border-color': '#657b83', // Base00\r\n 'tr-even-bg-color': '#002b36',\r\n 'tr-odd-bg-color': '#073642',\r\n 'content-bg': '#657b83',\r\n 'content-fg': '#fdf6e3',\r\n 'content-hover-bg': '#002b36',\r\n 'content-hover-fg': '#fdf6e3'\r\n }\r\n }\r\n },\r\n arc: {\r\n id: 'arc',\r\n name: 'Arc',\r\n colors: {\r\n node_slot: {\r\n BOOLEAN: '',\r\n CLIP: '#eacb8b',\r\n CLIP_VISION: '#A8DADC',\r\n CLIP_VISION_OUTPUT: '#ad7452',\r\n CONDITIONING: '#cf876f',\r\n CONTROL_NET: '#00d78d',\r\n CONTROL_NET_WEIGHTS: '',\r\n FLOAT: '',\r\n GLIGEN: '',\r\n IMAGE: '#80a1c0',\r\n IMAGEUPLOAD: '',\r\n INT: '',\r\n LATENT: '#b38ead',\r\n LATENT_KEYFRAME: '',\r\n MASK: '#a3bd8d',\r\n MODEL: '#8978a7',\r\n SAMPLER: '',\r\n SIGMAS: '',\r\n STRING: '',\r\n STYLE_MODEL: '#C2FFAE',\r\n T2I_ADAPTER_WEIGHTS: '',\r\n TAESD: '#DCC274',\r\n TIMESTEP_KEYFRAME: '',\r\n UPSCALE_MODEL: '',\r\n VAE: '#be616b'\r\n },\r\n litegraph_base: {\r\n BACKGROUND_IMAGE:\r\n 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcklEQVR4nO3YMUoDARgF4RfxBqZI6/0vZqFn0MYtrLIQMFN8U6V4LAtD+Jm9XG/v30OGl2e/AP7yevz4+vx45nvgF/+QGITEICQGITEIiUFIjNNC3q43u3/YnRJyPOzeQ+0e220nhRzReC8e7R7bbdvl+Jal1Bs46jEIiUFIDEJiEBKDkBhKPbZT6qHdptRTu02p53DUYxASg5AYhMQgJAYhMZR6bKfUQ7tNqad2m1LP4ajHICQGITEIiUFIDEJiKPXYTqmHdptST+02pZ7DUY9BSAxCYhASg5AYhMRQ6rGdUg/tNqWe2m1KPYejHoOQGITEICQGITEIiaHUYzulHtptSj2125R6Dkc9BiExCIlBSAxCYhASQ6nHdko9tNuUemq3KfUcjnoMQmIQEoOQGITEICSGUo/tlHpotyn11G5T6jkc9RiExCAkBiExCIlBSAylHtsp9dBuU+qp3abUczjqMQiJQUgMQmIQEoOQGITE+AHFISNQrFTGuwAAAABJRU5ErkJggg==',\r\n CLEAR_BACKGROUND_COLOR: '#2b2f38',\r\n NODE_TITLE_COLOR: '#b2b7bd',\r\n NODE_SELECTED_TITLE_COLOR: '#FFF',\r\n NODE_TEXT_SIZE: 14,\r\n NODE_TEXT_COLOR: '#AAA',\r\n NODE_SUBTEXT_SIZE: 12,\r\n NODE_DEFAULT_COLOR: '#2b2f38',\r\n NODE_DEFAULT_BGCOLOR: '#242730',\r\n NODE_DEFAULT_BOXCOLOR: '#6e7581',\r\n NODE_DEFAULT_SHAPE: 'box',\r\n NODE_BOX_OUTLINE_COLOR: '#FFF',\r\n DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0.5)',\r\n DEFAULT_GROUP_FONT: 22,\r\n WIDGET_BGCOLOR: '#2b2f38',\r\n WIDGET_OUTLINE_COLOR: '#6e7581',\r\n WIDGET_TEXT_COLOR: '#DDD',\r\n WIDGET_SECONDARY_TEXT_COLOR: '#b2b7bd',\r\n LINK_COLOR: '#9A9',\r\n EVENT_LINK_COLOR: '#A86',\r\n CONNECTING_LINK_COLOR: '#AFA'\r\n },\r\n comfy_base: {\r\n 'fg-color': '#fff',\r\n 'bg-color': '#2b2f38',\r\n 'comfy-menu-bg': '#242730',\r\n 'comfy-input-bg': '#2b2f38',\r\n 'input-text': '#ddd',\r\n 'descrip-text': '#b2b7bd',\r\n 'drag-text': '#ccc',\r\n 'error-text': '#ff4444',\r\n 'border-color': '#6e7581',\r\n 'tr-even-bg-color': '#2b2f38',\r\n 'tr-odd-bg-color': '#242730',\r\n 'content-bg': '#6e7581',\r\n 'content-fg': '#fff',\r\n 'content-hover-bg': '#2b2f38',\r\n 'content-hover-fg': '#fff'\r\n }\r\n }\r\n },\r\n nord: {\r\n id: 'nord',\r\n name: 'Nord',\r\n colors: {\r\n node_slot: {\r\n BOOLEAN: '',\r\n CLIP: '#eacb8b',\r\n CLIP_VISION: '#A8DADC',\r\n CLIP_VISION_OUTPUT: '#ad7452',\r\n CONDITIONING: '#cf876f',\r\n CONTROL_NET: '#00d78d',\r\n CONTROL_NET_WEIGHTS: '',\r\n FLOAT: '',\r\n GLIGEN: '',\r\n IMAGE: '#80a1c0',\r\n IMAGEUPLOAD: '',\r\n INT: '',\r\n LATENT: '#b38ead',\r\n LATENT_KEYFRAME: '',\r\n MASK: '#a3bd8d',\r\n MODEL: '#8978a7',\r\n SAMPLER: '',\r\n SIGMAS: '',\r\n STRING: '',\r\n STYLE_MODEL: '#C2FFAE',\r\n T2I_ADAPTER_WEIGHTS: '',\r\n TAESD: '#DCC274',\r\n TIMESTEP_KEYFRAME: '',\r\n UPSCALE_MODEL: '',\r\n VAE: '#be616b'\r\n },\r\n litegraph_base: {\r\n BACKGROUND_IMAGE:\r\n 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFu2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMy0xMS0xM1QwMDoxODowMiswMTowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjMtMTEtMTVUMDE6MjA6NDUrMDE6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjMtMTEtMTVUMDE6MjA6NDUrMDE6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjUwNDFhMmZjLTEzNzQtMTk0ZC1hZWY4LTYxMzM1MTVmNjUwMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyMzFiMTBiMC1iNGZiLTAyNGUtYjEyZS0zMDUzMDNjZDA3YzgiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyMzFiMTBiMC1iNGZiLTAyNGUtYjEyZS0zMDUzMDNjZDA3YzgiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjIzMWIxMGIwLWI0ZmItMDI0ZS1iMTJlLTMwNTMwM2NkMDdjOCIgc3RFdnQ6d2hlbj0iMjAyMy0xMS0xM1QwMDoxODowMiswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo1MDQxYTJmYy0xMzc0LTE5NGQtYWVmOC02MTMzNTE1ZjY1MDAiIHN0RXZ0OndoZW49IjIwMjMtMTEtMTVUMDE6MjA6NDUrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4xIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz73jWg/AAAAyUlEQVR42u3WKwoAIBRFQRdiMb1idv9Lsxn9gEFw4Dbb8JCTojbbXEJwjJVL2HKwYMGCBQuWLbDmjr+9zrBGjHl1WVcvy2DBggULFizTWQpewSt4HzwsgwULFiwFr7MUvMtS8D54WLBgGSxYCl7BK3iXZbBgwYIFC5bpLAWv4BW8Dx6WwYIFC5aC11kK3mUpeB88LFiwDBYsBa/gFbzLMliwYMGCBct0loJX8AreBw/LYMGCBUvB6ywF77IUvA8eFixYBgsWrNfWAZPltufdad+1AAAAAElFTkSuQmCC',\r\n CLEAR_BACKGROUND_COLOR: '#212732',\r\n NODE_TITLE_COLOR: '#999',\r\n NODE_SELECTED_TITLE_COLOR: '#e5eaf0',\r\n NODE_TEXT_SIZE: 14,\r\n NODE_TEXT_COLOR: '#bcc2c8',\r\n NODE_SUBTEXT_SIZE: 12,\r\n NODE_DEFAULT_COLOR: '#2e3440',\r\n NODE_DEFAULT_BGCOLOR: '#161b22',\r\n NODE_DEFAULT_BOXCOLOR: '#545d70',\r\n NODE_DEFAULT_SHAPE: 'box',\r\n NODE_BOX_OUTLINE_COLOR: '#e5eaf0',\r\n DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0.5)',\r\n DEFAULT_GROUP_FONT: 24,\r\n WIDGET_BGCOLOR: '#2e3440',\r\n WIDGET_OUTLINE_COLOR: '#545d70',\r\n WIDGET_TEXT_COLOR: '#bcc2c8',\r\n WIDGET_SECONDARY_TEXT_COLOR: '#999',\r\n LINK_COLOR: '#9A9',\r\n EVENT_LINK_COLOR: '#A86',\r\n CONNECTING_LINK_COLOR: '#AFA'\r\n },\r\n comfy_base: {\r\n 'fg-color': '#e5eaf0',\r\n 'bg-color': '#2e3440',\r\n 'comfy-menu-bg': '#161b22',\r\n 'comfy-input-bg': '#2e3440',\r\n 'input-text': '#bcc2c8',\r\n 'descrip-text': '#999',\r\n 'drag-text': '#ccc',\r\n 'error-text': '#ff4444',\r\n 'border-color': '#545d70',\r\n 'tr-even-bg-color': '#2e3440',\r\n 'tr-odd-bg-color': '#161b22',\r\n 'content-bg': '#545d70',\r\n 'content-fg': '#e5eaf0',\r\n 'content-hover-bg': '#2e3440',\r\n 'content-hover-fg': '#e5eaf0'\r\n }\r\n }\r\n },\r\n github: {\r\n id: 'github',\r\n name: 'Github',\r\n colors: {\r\n node_slot: {\r\n BOOLEAN: '',\r\n CLIP: '#eacb8b',\r\n CLIP_VISION: '#A8DADC',\r\n CLIP_VISION_OUTPUT: '#ad7452',\r\n CONDITIONING: '#cf876f',\r\n CONTROL_NET: '#00d78d',\r\n CONTROL_NET_WEIGHTS: '',\r\n FLOAT: '',\r\n GLIGEN: '',\r\n IMAGE: '#80a1c0',\r\n IMAGEUPLOAD: '',\r\n INT: '',\r\n LATENT: '#b38ead',\r\n LATENT_KEYFRAME: '',\r\n MASK: '#a3bd8d',\r\n MODEL: '#8978a7',\r\n SAMPLER: '',\r\n SIGMAS: '',\r\n STRING: '',\r\n STYLE_MODEL: '#C2FFAE',\r\n T2I_ADAPTER_WEIGHTS: '',\r\n TAESD: '#DCC274',\r\n TIMESTEP_KEYFRAME: '',\r\n UPSCALE_MODEL: '',\r\n VAE: '#be616b'\r\n },\r\n litegraph_base: {\r\n BACKGROUND_IMAGE:\r\n 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMy0xMS0xM1QwMDoxODowMiswMTowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmIyYzRhNjA5LWJmYTctYTg0MC1iOGFlLTk3MzE2ZjM1ZGIyNyIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjk0ZmNlZGU4LTE1MTctZmQ0MC04ZGU3LWYzOTgxM2E3ODk5ZiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjIzMWIxMGIwLWI0ZmItMDI0ZS1iMTJlLTMwNTMwM2NkMDdjOCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MjMxYjEwYjAtYjRmYi0wMjRlLWIxMmUtMzA1MzAzY2QwN2M4IiBzdEV2dDp3aGVuPSIyMDIzLTExLTEzVDAwOjE4OjAyKzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjQ4OWY1NzlmLTJkNjUtZWQ0Zi04OTg0LTA4NGE2MGE1ZTMzNSIgc3RFdnQ6d2hlbj0iMjAyMy0xMS0xNVQwMjowNDo1OSswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpiMmM0YTYwOS1iZmE3LWE4NDAtYjhhZS05NzMxNmYzNWRiMjciIHN0RXZ0OndoZW49IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4xIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4OTe6GAAAAx0lEQVR42u3WMQoAIQxFwRzJys77X8vSLiRgITif7bYbgrwYc/mKXyBoY4VVBgsWLFiwYFmOlTv+9jfDOjHmr8u6eVkGCxYsWLBgmc5S8ApewXvgYRksWLBgKXidpeBdloL3wMOCBctgwVLwCl7BuyyDBQsWLFiwTGcpeAWv4D3wsAwWLFiwFLzOUvAuS8F74GHBgmWwYCl4Ba/gXZbBggULFixYprMUvIJX8B54WAYLFixYCl5nKXiXpeA98LBgwTJYsGC9tg1o8f4TTtqzNQAAAABJRU5ErkJggg==',\r\n CLEAR_BACKGROUND_COLOR: '#040506',\r\n NODE_TITLE_COLOR: '#999',\r\n NODE_SELECTED_TITLE_COLOR: '#e5eaf0',\r\n NODE_TEXT_SIZE: 14,\r\n NODE_TEXT_COLOR: '#bcc2c8',\r\n NODE_SUBTEXT_SIZE: 12,\r\n NODE_DEFAULT_COLOR: '#161b22',\r\n NODE_DEFAULT_BGCOLOR: '#13171d',\r\n NODE_DEFAULT_BOXCOLOR: '#30363d',\r\n NODE_DEFAULT_SHAPE: 'box',\r\n NODE_BOX_OUTLINE_COLOR: '#e5eaf0',\r\n DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0.5)',\r\n DEFAULT_GROUP_FONT: 24,\r\n WIDGET_BGCOLOR: '#161b22',\r\n WIDGET_OUTLINE_COLOR: '#30363d',\r\n WIDGET_TEXT_COLOR: '#bcc2c8',\r\n WIDGET_SECONDARY_TEXT_COLOR: '#999',\r\n LINK_COLOR: '#9A9',\r\n EVENT_LINK_COLOR: '#A86',\r\n CONNECTING_LINK_COLOR: '#AFA'\r\n },\r\n comfy_base: {\r\n 'fg-color': '#e5eaf0',\r\n 'bg-color': '#161b22',\r\n 'comfy-menu-bg': '#13171d',\r\n 'comfy-input-bg': '#161b22',\r\n 'input-text': '#bcc2c8',\r\n 'descrip-text': '#999',\r\n 'drag-text': '#ccc',\r\n 'error-text': '#ff4444',\r\n 'border-color': '#30363d',\r\n 'tr-even-bg-color': '#161b22',\r\n 'tr-odd-bg-color': '#13171d',\r\n 'content-bg': '#30363d',\r\n 'content-fg': '#e5eaf0',\r\n 'content-hover-bg': '#161b22',\r\n 'content-hover-fg': '#e5eaf0'\r\n }\r\n }\r\n }\r\n}\r\n\r\nconst id = 'Comfy.ColorPalette'\r\nconst idCustomColorPalettes = 'Comfy.CustomColorPalettes'\r\nconst defaultColorPaletteId = 'dark'\r\nconst els: { select: HTMLSelectElement | null } = {\r\n select: null\r\n}\r\n// const ctxMenu = LiteGraph.ContextMenu;\r\napp.registerExtension({\r\n name: id,\r\n init() {\r\n /**\r\n * Changes the background color of the canvas.\r\n *\r\n * @method updateBackground\r\n * @param {image} String\r\n * @param {clearBackgroundColor} String\r\n */\r\n // @ts-expect-error\r\n LGraphCanvas.prototype.updateBackground = function (\r\n image,\r\n clearBackgroundColor\r\n ) {\r\n this._bg_img = new Image()\r\n this._bg_img.name = image\r\n this._bg_img.src = image\r\n this._bg_img.onload = () => {\r\n this.draw(true, true)\r\n }\r\n this.background_image = image\r\n\r\n this.clear_background = true\r\n this.clear_background_color = clearBackgroundColor\r\n this._pattern = null\r\n }\r\n },\r\n addCustomNodeDefs(node_defs) {\r\n const sortObjectKeys = (unordered) => {\r\n return Object.keys(unordered)\r\n .sort()\r\n .reduce((obj, key) => {\r\n obj[key] = unordered[key]\r\n return obj\r\n }, {})\r\n }\r\n\r\n function getSlotTypes() {\r\n var types = []\r\n\r\n const defs = node_defs\r\n for (const nodeId in defs) {\r\n const nodeData = defs[nodeId]\r\n\r\n var inputs = nodeData['input']['required']\r\n if (nodeData['input']['optional'] !== undefined) {\r\n inputs = Object.assign(\r\n {},\r\n nodeData['input']['required'],\r\n nodeData['input']['optional']\r\n )\r\n }\r\n\r\n for (const inputName in inputs) {\r\n const inputData = inputs[inputName]\r\n const type = inputData[0]\r\n\r\n if (!Array.isArray(type)) {\r\n types.push(type)\r\n }\r\n }\r\n\r\n for (const o in nodeData['output']) {\r\n const output = nodeData['output'][o]\r\n types.push(output)\r\n }\r\n }\r\n\r\n return types\r\n }\r\n\r\n function completeColorPalette(colorPalette) {\r\n var types = getSlotTypes()\r\n\r\n for (const type of types) {\r\n if (!colorPalette.colors.node_slot[type]) {\r\n colorPalette.colors.node_slot[type] = ''\r\n }\r\n }\r\n\r\n colorPalette.colors.node_slot = sortObjectKeys(\r\n colorPalette.colors.node_slot\r\n )\r\n\r\n return colorPalette\r\n }\r\n\r\n const getColorPaletteTemplate = async () => {\r\n let colorPalette = {\r\n id: 'my_color_palette_unique_id',\r\n name: 'My Color Palette',\r\n colors: {\r\n node_slot: {},\r\n litegraph_base: {},\r\n comfy_base: {}\r\n }\r\n }\r\n\r\n // Copy over missing keys from default color palette\r\n const defaultColorPalette = colorPalettes[defaultColorPaletteId]\r\n for (const key in defaultColorPalette.colors.litegraph_base) {\r\n if (!colorPalette.colors.litegraph_base[key]) {\r\n colorPalette.colors.litegraph_base[key] = ''\r\n }\r\n }\r\n for (const key in defaultColorPalette.colors.comfy_base) {\r\n if (!colorPalette.colors.comfy_base[key]) {\r\n colorPalette.colors.comfy_base[key] = ''\r\n }\r\n }\r\n\r\n return completeColorPalette(colorPalette)\r\n }\r\n\r\n const getCustomColorPalettes = (): ColorPalettes => {\r\n return app.ui.settings.getSettingValue(idCustomColorPalettes, {})\r\n }\r\n\r\n const setCustomColorPalettes = (customColorPalettes: ColorPalettes) => {\r\n return app.ui.settings.setSettingValue(\r\n idCustomColorPalettes,\r\n customColorPalettes\r\n )\r\n }\r\n\r\n const addCustomColorPalette = async (colorPalette) => {\r\n if (typeof colorPalette !== 'object') {\r\n alert('Invalid color palette.')\r\n return\r\n }\r\n\r\n if (!colorPalette.id) {\r\n alert('Color palette missing id.')\r\n return\r\n }\r\n\r\n if (!colorPalette.name) {\r\n alert('Color palette missing name.')\r\n return\r\n }\r\n\r\n if (!colorPalette.colors) {\r\n alert('Color palette missing colors.')\r\n return\r\n }\r\n\r\n if (\r\n colorPalette.colors.node_slot &&\r\n typeof colorPalette.colors.node_slot !== 'object'\r\n ) {\r\n alert('Invalid color palette colors.node_slot.')\r\n return\r\n }\r\n\r\n const customColorPalettes = getCustomColorPalettes()\r\n customColorPalettes[colorPalette.id] = colorPalette\r\n setCustomColorPalettes(customColorPalettes)\r\n\r\n for (const option of els.select.childNodes) {\r\n if (\r\n (option as HTMLOptionElement).value ===\r\n 'custom_' + colorPalette.id\r\n ) {\r\n els.select.removeChild(option)\r\n }\r\n }\r\n\r\n els.select.append(\r\n $el('option', {\r\n textContent: colorPalette.name + ' (custom)',\r\n value: 'custom_' + colorPalette.id,\r\n selected: true\r\n })\r\n )\r\n\r\n setColorPalette('custom_' + colorPalette.id)\r\n await loadColorPalette(colorPalette)\r\n }\r\n\r\n const deleteCustomColorPalette = async (colorPaletteId) => {\r\n const customColorPalettes = getCustomColorPalettes()\r\n delete customColorPalettes[colorPaletteId]\r\n setCustomColorPalettes(customColorPalettes)\r\n\r\n for (const opt of els.select.childNodes) {\r\n const option = opt as HTMLOptionElement\r\n if (option.value === defaultColorPaletteId) {\r\n option.selected = true\r\n }\r\n\r\n if (option.value === 'custom_' + colorPaletteId) {\r\n els.select.removeChild(option)\r\n }\r\n }\r\n\r\n setColorPalette(defaultColorPaletteId)\r\n await loadColorPalette(getColorPalette())\r\n }\r\n\r\n const loadColorPalette = async (colorPalette: Palette) => {\r\n colorPalette = await completeColorPalette(colorPalette)\r\n if (colorPalette.colors) {\r\n // Sets the colors of node slots and links\r\n if (colorPalette.colors.node_slot) {\r\n Object.assign(\r\n // @ts-expect-error\r\n app.canvas.default_connection_color_byType,\r\n colorPalette.colors.node_slot\r\n )\r\n Object.assign(\r\n LGraphCanvas.link_type_colors,\r\n colorPalette.colors.node_slot\r\n )\r\n }\r\n // Sets the colors of the LiteGraph objects\r\n if (colorPalette.colors.litegraph_base) {\r\n // Everything updates correctly in the loop, except the Node Title and Link Color for some reason\r\n app.canvas.node_title_color =\r\n colorPalette.colors.litegraph_base.NODE_TITLE_COLOR\r\n app.canvas.default_link_color =\r\n colorPalette.colors.litegraph_base.LINK_COLOR\r\n\r\n for (const key in colorPalette.colors.litegraph_base) {\r\n if (\r\n colorPalette.colors.litegraph_base.hasOwnProperty(key) &&\r\n LiteGraph.hasOwnProperty(key)\r\n ) {\r\n LiteGraph[key] = colorPalette.colors.litegraph_base[key]\r\n }\r\n }\r\n }\r\n // Sets the color of ComfyUI elements\r\n if (colorPalette.colors.comfy_base) {\r\n const rootStyle = document.documentElement.style\r\n for (const key in colorPalette.colors.comfy_base) {\r\n rootStyle.setProperty(\r\n '--' + key,\r\n colorPalette.colors.comfy_base[key]\r\n )\r\n }\r\n }\r\n app.canvas.draw(true, true)\r\n }\r\n }\r\n\r\n const getColorPalette = (colorPaletteId?) => {\r\n if (!colorPaletteId) {\r\n colorPaletteId = app.ui.settings.getSettingValue(\r\n id,\r\n defaultColorPaletteId\r\n )\r\n }\r\n\r\n if (colorPaletteId.startsWith('custom_')) {\r\n colorPaletteId = colorPaletteId.substr(7)\r\n let customColorPalettes = getCustomColorPalettes()\r\n if (customColorPalettes[colorPaletteId]) {\r\n return customColorPalettes[colorPaletteId]\r\n }\r\n }\r\n\r\n return colorPalettes[colorPaletteId]\r\n }\r\n\r\n const setColorPalette = (colorPaletteId) => {\r\n app.ui.settings.setSettingValue(id, colorPaletteId)\r\n }\r\n\r\n const fileInput = $el('input', {\r\n type: 'file',\r\n accept: '.json',\r\n style: { display: 'none' },\r\n parent: document.body,\r\n onchange: () => {\r\n const file = fileInput.files[0]\r\n if (file.type === 'application/json' || file.name.endsWith('.json')) {\r\n const reader = new FileReader()\r\n reader.onload = async () => {\r\n await addCustomColorPalette(JSON.parse(reader.result as string))\r\n }\r\n reader.readAsText(file)\r\n }\r\n }\r\n }) as HTMLInputElement\r\n\r\n app.ui.settings.addSetting({\r\n id,\r\n name: 'Color Palette',\r\n type: (name, setter, value) => {\r\n const options = [\r\n ...Object.values(colorPalettes).map((c) =>\r\n $el('option', {\r\n textContent: c.name,\r\n value: c.id,\r\n selected: c.id === value\r\n })\r\n ),\r\n ...Object.values(getCustomColorPalettes()).map((c) =>\r\n $el('option', {\r\n textContent: `${c.name} (custom)`,\r\n value: `custom_${c.id}`,\r\n selected: `custom_${c.id}` === value\r\n })\r\n )\r\n ]\r\n\r\n els.select = $el(\r\n 'select',\r\n {\r\n style: {\r\n marginBottom: '0.15rem',\r\n width: '100%'\r\n },\r\n onchange: (e) => {\r\n setter(e.target.value)\r\n }\r\n },\r\n options\r\n ) as HTMLSelectElement\r\n\r\n return $el('tr', [\r\n $el('td', [\r\n $el('label', {\r\n for: id.replaceAll('.', '-'),\r\n textContent: 'Color palette'\r\n })\r\n ]),\r\n $el('td', [\r\n els.select,\r\n $el(\r\n 'div',\r\n {\r\n style: {\r\n display: 'grid',\r\n gap: '4px',\r\n gridAutoFlow: 'column'\r\n }\r\n },\r\n [\r\n $el('input', {\r\n type: 'button',\r\n value: 'Export',\r\n onclick: async () => {\r\n const colorPaletteId = app.ui.settings.getSettingValue(\r\n id,\r\n defaultColorPaletteId\r\n )\r\n const colorPalette = await completeColorPalette(\r\n getColorPalette(colorPaletteId)\r\n )\r\n const json = JSON.stringify(colorPalette, null, 2) // convert the data to a JSON string\r\n const blob = new Blob([json], { type: 'application/json' })\r\n const url = URL.createObjectURL(blob)\r\n const a = $el('a', {\r\n href: url,\r\n download: colorPaletteId + '.json',\r\n style: { display: 'none' },\r\n parent: document.body\r\n })\r\n a.click()\r\n setTimeout(function () {\r\n a.remove()\r\n window.URL.revokeObjectURL(url)\r\n }, 0)\r\n }\r\n }),\r\n $el('input', {\r\n type: 'button',\r\n value: 'Import',\r\n onclick: () => {\r\n fileInput.click()\r\n }\r\n }),\r\n $el('input', {\r\n type: 'button',\r\n value: 'Template',\r\n onclick: async () => {\r\n const colorPalette = await getColorPaletteTemplate()\r\n const json = JSON.stringify(colorPalette, null, 2) // convert the data to a JSON string\r\n const blob = new Blob([json], { type: 'application/json' })\r\n const url = URL.createObjectURL(blob)\r\n const a = $el('a', {\r\n href: url,\r\n download: 'color_palette.json',\r\n style: { display: 'none' },\r\n parent: document.body\r\n })\r\n a.click()\r\n setTimeout(function () {\r\n a.remove()\r\n window.URL.revokeObjectURL(url)\r\n }, 0)\r\n }\r\n }),\r\n $el('input', {\r\n type: 'button',\r\n value: 'Delete',\r\n onclick: async () => {\r\n let colorPaletteId = app.ui.settings.getSettingValue(\r\n id,\r\n defaultColorPaletteId\r\n )\r\n\r\n if (colorPalettes[colorPaletteId]) {\r\n alert('You cannot delete a built-in color palette.')\r\n return\r\n }\r\n\r\n if (colorPaletteId.startsWith('custom_')) {\r\n colorPaletteId = colorPaletteId.substr(7)\r\n }\r\n\r\n await deleteCustomColorPalette(colorPaletteId)\r\n }\r\n })\r\n ]\r\n )\r\n ])\r\n ])\r\n },\r\n defaultValue: defaultColorPaletteId,\r\n async onChange(value) {\r\n if (!value) {\r\n return\r\n }\r\n\r\n let palette = colorPalettes[value]\r\n if (palette) {\r\n await loadColorPalette(palette)\r\n } else if (value.startsWith('custom_')) {\r\n value = value.substr(7)\r\n let customColorPalettes = getCustomColorPalettes()\r\n if (customColorPalettes[value]) {\r\n palette = customColorPalettes[value]\r\n await loadColorPalette(customColorPalettes[value])\r\n }\r\n }\r\n\r\n let { BACKGROUND_IMAGE, CLEAR_BACKGROUND_COLOR } =\r\n palette.colors.litegraph_base\r\n if (\r\n BACKGROUND_IMAGE === undefined ||\r\n CLEAR_BACKGROUND_COLOR === undefined\r\n ) {\r\n const base = colorPalettes['dark'].colors.litegraph_base\r\n BACKGROUND_IMAGE = base.BACKGROUND_IMAGE\r\n CLEAR_BACKGROUND_COLOR = base.CLEAR_BACKGROUND_COLOR\r\n }\r\n // @ts-expect-error\r\n // litegraph.extensions.js\r\n app.canvas.updateBackground(BACKGROUND_IMAGE, CLEAR_BACKGROUND_COLOR)\r\n }\r\n })\r\n }\r\n})\r\n","import { LiteGraph, LGraphCanvas } from '@comfyorg/litegraph'\r\nimport { app } from '../../scripts/app'\r\n\r\n// Adds filtering to combo context menus\r\n\r\nconst ext = {\r\n name: 'Comfy.ContextMenuFilter',\r\n init() {\r\n const ctxMenu = LiteGraph.ContextMenu\r\n // @ts-expect-error\r\n // TODO Very hacky way to modify Litegraph behaviour. Fix this later.\r\n LiteGraph.ContextMenu = function (values, options) {\r\n const ctx = ctxMenu.call(this, values, options)\r\n\r\n // If we are a dark menu (only used for combo boxes) then add a filter input\r\n if (options?.className === 'dark' && values?.length > 10) {\r\n const filter = document.createElement('input')\r\n filter.classList.add('comfy-context-menu-filter')\r\n filter.placeholder = 'Filter list'\r\n this.root.prepend(filter)\r\n\r\n const items = Array.from(\r\n this.root.querySelectorAll('.litemenu-entry')\r\n ) as HTMLElement[]\r\n let displayedItems = [...items]\r\n let itemCount = displayedItems.length\r\n\r\n // We must request an animation frame for the current node of the active canvas to update.\r\n requestAnimationFrame(() => {\r\n // @ts-expect-error\r\n const currentNode = LGraphCanvas.active_canvas.current_node\r\n const clickedComboValue = currentNode.widgets\r\n ?.filter(\r\n (w) =>\r\n w.type === 'combo' && w.options.values.length === values.length\r\n )\r\n .find((w) =>\r\n w.options.values.every((v, i) => v === values[i])\r\n )?.value\r\n\r\n let selectedIndex = clickedComboValue\r\n ? values.findIndex((v) => v === clickedComboValue)\r\n : 0\r\n if (selectedIndex < 0) {\r\n selectedIndex = 0\r\n }\r\n let selectedItem = displayedItems[selectedIndex]\r\n updateSelected()\r\n\r\n // Apply highlighting to the selected item\r\n function updateSelected() {\r\n selectedItem?.style.setProperty('background-color', '')\r\n selectedItem?.style.setProperty('color', '')\r\n selectedItem = displayedItems[selectedIndex]\r\n selectedItem?.style.setProperty(\r\n 'background-color',\r\n '#ccc',\r\n 'important'\r\n )\r\n selectedItem?.style.setProperty('color', '#000', 'important')\r\n }\r\n\r\n const positionList = () => {\r\n const rect = this.root.getBoundingClientRect()\r\n\r\n // If the top is off-screen then shift the element with scaling applied\r\n if (rect.top < 0) {\r\n const scale =\r\n 1 -\r\n this.root.getBoundingClientRect().height /\r\n this.root.clientHeight\r\n const shift = (this.root.clientHeight * scale) / 2\r\n this.root.style.top = -shift + 'px'\r\n }\r\n }\r\n\r\n // Arrow up/down to select items\r\n filter.addEventListener('keydown', (event) => {\r\n switch (event.key) {\r\n case 'ArrowUp':\r\n event.preventDefault()\r\n if (selectedIndex === 0) {\r\n selectedIndex = itemCount - 1\r\n } else {\r\n selectedIndex--\r\n }\r\n updateSelected()\r\n break\r\n case 'ArrowRight':\r\n event.preventDefault()\r\n selectedIndex = itemCount - 1\r\n updateSelected()\r\n break\r\n case 'ArrowDown':\r\n event.preventDefault()\r\n if (selectedIndex === itemCount - 1) {\r\n selectedIndex = 0\r\n } else {\r\n selectedIndex++\r\n }\r\n updateSelected()\r\n break\r\n case 'ArrowLeft':\r\n event.preventDefault()\r\n selectedIndex = 0\r\n updateSelected()\r\n break\r\n case 'Enter':\r\n selectedItem?.click()\r\n break\r\n case 'Escape':\r\n this.close()\r\n break\r\n }\r\n })\r\n\r\n filter.addEventListener('input', () => {\r\n // Hide all items that don't match our filter\r\n const term = filter.value.toLocaleLowerCase()\r\n // When filtering, recompute which items are visible for arrow up/down and maintain selection.\r\n displayedItems = items.filter((item) => {\r\n const isVisible =\r\n !term || item.textContent.toLocaleLowerCase().includes(term)\r\n item.style.display = isVisible ? 'block' : 'none'\r\n return isVisible\r\n })\r\n\r\n selectedIndex = 0\r\n if (displayedItems.includes(selectedItem)) {\r\n selectedIndex = displayedItems.findIndex(\r\n (d) => d === selectedItem\r\n )\r\n }\r\n itemCount = displayedItems.length\r\n\r\n updateSelected()\r\n\r\n // If we have an event then we can try and position the list under the source\r\n if (options.event) {\r\n let top = options.event.clientY - 10\r\n\r\n const bodyRect = document.body.getBoundingClientRect()\r\n const rootRect = this.root.getBoundingClientRect()\r\n if (\r\n bodyRect.height &&\r\n top > bodyRect.height - rootRect.height - 10\r\n ) {\r\n top = Math.max(0, bodyRect.height - rootRect.height - 10)\r\n }\r\n\r\n this.root.style.top = top + 'px'\r\n positionList()\r\n }\r\n })\r\n\r\n requestAnimationFrame(() => {\r\n // Focus the filter box when opening\r\n filter.focus()\r\n\r\n positionList()\r\n })\r\n })\r\n }\r\n\r\n return ctx\r\n }\r\n\r\n LiteGraph.ContextMenu.prototype = ctxMenu.prototype\r\n }\r\n}\r\n\r\napp.registerExtension(ext)\r\n","import { app } from '../../scripts/app'\r\n\r\n// Allows for simple dynamic prompt replacement\r\n// Inputs in the format {a|b} will have a random value of a or b chosen when the prompt is queued.\r\n\r\n/*\r\n * Strips C-style line and block comments from a string\r\n */\r\nfunction stripComments(str) {\r\n return str.replace(/\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*/g, '')\r\n}\r\n\r\napp.registerExtension({\r\n name: 'Comfy.DynamicPrompts',\r\n nodeCreated(node) {\r\n if (node.widgets) {\r\n // Locate dynamic prompt text widgets\r\n // Include any widgets with dynamicPrompts set to true, and customtext\r\n const widgets = node.widgets.filter((n) => n.dynamicPrompts)\r\n for (const widget of widgets) {\r\n // Override the serialization of the value to resolve dynamic prompts for all widgets supporting it in this node\r\n widget.serializeValue = (workflowNode, widgetIndex) => {\r\n let prompt = stripComments(widget.value)\r\n while (\r\n prompt.replace('\\\\{', '').includes('{') &&\r\n prompt.replace('\\\\}', '').includes('}')\r\n ) {\r\n const startIndex = prompt.replace('\\\\{', '00').indexOf('{')\r\n const endIndex = prompt.replace('\\\\}', '00').indexOf('}')\r\n\r\n const optionsString = prompt.substring(startIndex + 1, endIndex)\r\n const options = optionsString.split('|')\r\n\r\n const randomIndex = Math.floor(Math.random() * options.length)\r\n const randomOption = options[randomIndex]\r\n\r\n prompt =\r\n prompt.substring(0, startIndex) +\r\n randomOption +\r\n prompt.substring(endIndex + 1)\r\n }\r\n\r\n // Overwrite the value in the serialized workflow pnginfo\r\n if (workflowNode?.widgets_values)\r\n workflowNode.widgets_values[widgetIndex] = prompt\r\n\r\n return prompt\r\n }\r\n }\r\n }\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\n\r\n// Allows you to edit the attention weight by holding ctrl (or cmd) and using the up/down arrow keys\r\n\r\napp.registerExtension({\r\n name: 'Comfy.EditAttention',\r\n init() {\r\n const editAttentionDelta = app.ui.settings.addSetting({\r\n id: 'Comfy.EditAttention.Delta',\r\n name: 'Ctrl+up/down precision',\r\n type: 'slider',\r\n attrs: {\r\n min: 0.01,\r\n max: 0.5,\r\n step: 0.01\r\n },\r\n defaultValue: 0.05\r\n })\r\n\r\n function incrementWeight(weight, delta) {\r\n const floatWeight = parseFloat(weight)\r\n if (isNaN(floatWeight)) return weight\r\n const newWeight = floatWeight + delta\r\n if (newWeight < 0) return '0'\r\n return String(Number(newWeight.toFixed(10)))\r\n }\r\n\r\n function findNearestEnclosure(text, cursorPos) {\r\n let start = cursorPos,\r\n end = cursorPos\r\n let openCount = 0,\r\n closeCount = 0\r\n\r\n // Find opening parenthesis before cursor\r\n while (start >= 0) {\r\n start--\r\n if (text[start] === '(' && openCount === closeCount) break\r\n if (text[start] === '(') openCount++\r\n if (text[start] === ')') closeCount++\r\n }\r\n if (start < 0) return false\r\n\r\n openCount = 0\r\n closeCount = 0\r\n\r\n // Find closing parenthesis after cursor\r\n while (end < text.length) {\r\n if (text[end] === ')' && openCount === closeCount) break\r\n if (text[end] === '(') openCount++\r\n if (text[end] === ')') closeCount++\r\n end++\r\n }\r\n if (end === text.length) return false\r\n\r\n return { start: start + 1, end: end }\r\n }\r\n\r\n function addWeightToParentheses(text) {\r\n const parenRegex = /^\\((.*)\\)$/\r\n const parenMatch = text.match(parenRegex)\r\n\r\n const floatRegex = /:([+-]?(\\d*\\.)?\\d+([eE][+-]?\\d+)?)/\r\n const floatMatch = text.match(floatRegex)\r\n\r\n if (parenMatch && !floatMatch) {\r\n return `(${parenMatch[1]}:1.0)`\r\n } else {\r\n return text\r\n }\r\n }\r\n\r\n function editAttention(event) {\r\n const inputField = event.composedPath()[0]\r\n const delta = parseFloat(editAttentionDelta.value)\r\n\r\n if (inputField.tagName !== 'TEXTAREA') return\r\n if (!(event.key === 'ArrowUp' || event.key === 'ArrowDown')) return\r\n if (!event.ctrlKey && !event.metaKey) return\r\n\r\n event.preventDefault()\r\n\r\n let start = inputField.selectionStart\r\n let end = inputField.selectionEnd\r\n let selectedText = inputField.value.substring(start, end)\r\n\r\n // If there is no selection, attempt to find the nearest enclosure, or select the current word\r\n if (!selectedText) {\r\n const nearestEnclosure = findNearestEnclosure(inputField.value, start)\r\n if (nearestEnclosure) {\r\n start = nearestEnclosure.start\r\n end = nearestEnclosure.end\r\n selectedText = inputField.value.substring(start, end)\r\n } else {\r\n // Select the current word, find the start and end of the word\r\n const delimiters = ' .,\\\\/!?%^*;:{}=-_`~()\\r\\n\\t'\r\n\r\n while (\r\n !delimiters.includes(inputField.value[start - 1]) &&\r\n start > 0\r\n ) {\r\n start--\r\n }\r\n\r\n while (\r\n !delimiters.includes(inputField.value[end]) &&\r\n end < inputField.value.length\r\n ) {\r\n end++\r\n }\r\n\r\n selectedText = inputField.value.substring(start, end)\r\n if (!selectedText) return\r\n }\r\n }\r\n\r\n // If the selection ends with a space, remove it\r\n if (selectedText[selectedText.length - 1] === ' ') {\r\n selectedText = selectedText.substring(0, selectedText.length - 1)\r\n end -= 1\r\n }\r\n\r\n // If there are parentheses left and right of the selection, select them\r\n if (\r\n inputField.value[start - 1] === '(' &&\r\n inputField.value[end] === ')'\r\n ) {\r\n start -= 1\r\n end += 1\r\n selectedText = inputField.value.substring(start, end)\r\n }\r\n\r\n // If the selection is not enclosed in parentheses, add them\r\n if (\r\n selectedText[0] !== '(' ||\r\n selectedText[selectedText.length - 1] !== ')'\r\n ) {\r\n selectedText = `(${selectedText})`\r\n }\r\n\r\n // If the selection does not have a weight, add a weight of 1.0\r\n selectedText = addWeightToParentheses(selectedText)\r\n\r\n // Increment the weight\r\n const weightDelta = event.key === 'ArrowUp' ? delta : -delta\r\n const updatedText = selectedText.replace(\r\n /\\((.*):(\\d+(?:\\.\\d+)?)\\)/,\r\n (match, text, weight) => {\r\n weight = incrementWeight(weight, weightDelta)\r\n if (weight == 1) {\r\n return text\r\n } else {\r\n return `(${text}:${weight})`\r\n }\r\n }\r\n )\r\n\r\n inputField.setRangeText(updatedText, start, end, 'select')\r\n }\r\n window.addEventListener('keydown', editAttention)\r\n }\r\n})\r\n","import { ComfyWidgets, addValueControlWidgets } from '../../scripts/widgets'\r\nimport { app } from '../../scripts/app'\r\nimport { applyTextReplacements } from '../../scripts/utils'\r\nimport { LiteGraph } from '@comfyorg/litegraph'\r\nimport type { LGraphNode, INodeInputSlot, IWidget } from '@comfyorg/litegraph'\r\n\r\nconst CONVERTED_TYPE = 'converted-widget'\r\nconst VALID_TYPES = ['STRING', 'combo', 'number', 'toggle', 'BOOLEAN']\r\nconst CONFIG = Symbol()\r\nconst GET_CONFIG = Symbol()\r\nconst TARGET = Symbol() // Used for reroutes to specify the real target widget\r\n\r\ninterface PrimitiveNode extends LGraphNode {}\r\n\r\nconst replacePropertyName = 'Run widget replace on values'\r\nclass PrimitiveNode {\r\n controlValues: any[]\r\n lastType: string\r\n static category: string\r\n constructor() {\r\n this.addOutput('connect to widget input', '*')\r\n this.serialize_widgets = true\r\n this.isVirtualNode = true\r\n\r\n if (!this.properties || !(replacePropertyName in this.properties)) {\r\n this.addProperty(replacePropertyName, false, 'boolean')\r\n }\r\n }\r\n\r\n applyToGraph(extraLinks = []) {\r\n if (!this.outputs[0].links?.length) return\r\n\r\n function get_links(node) {\r\n let links = []\r\n for (const l of node.outputs[0].links) {\r\n const linkInfo = app.graph.links[l]\r\n const n = node.graph.getNodeById(linkInfo.target_id)\r\n if (n.type == 'Reroute') {\r\n links = links.concat(get_links(n))\r\n } else {\r\n links.push(l)\r\n }\r\n }\r\n return links\r\n }\r\n\r\n let links = [\r\n ...get_links(this).map((l) => app.graph.links[l]),\r\n ...extraLinks\r\n ]\r\n let v = this.widgets?.[0].value\r\n if (v && this.properties[replacePropertyName]) {\r\n v = applyTextReplacements(app, v)\r\n }\r\n\r\n // For each output link copy our value over the original widget value\r\n for (const linkInfo of links) {\r\n const node = this.graph.getNodeById(linkInfo.target_id)\r\n const input = node.inputs[linkInfo.target_slot]\r\n let widget\r\n if (input.widget[TARGET]) {\r\n widget = input.widget[TARGET]\r\n } else {\r\n const widgetName = (input.widget as { name: string }).name\r\n if (widgetName) {\r\n widget = node.widgets.find((w) => w.name === widgetName)\r\n }\r\n }\r\n\r\n if (widget) {\r\n widget.value = v\r\n if (widget.callback) {\r\n widget.callback(\r\n widget.value,\r\n app.canvas,\r\n node,\r\n app.canvas.graph_mouse,\r\n {}\r\n )\r\n }\r\n }\r\n }\r\n }\r\n\r\n refreshComboInNode() {\r\n const widget = this.widgets?.[0]\r\n if (widget?.type === 'combo') {\r\n widget.options.values = this.outputs[0].widget[GET_CONFIG]()[0]\r\n\r\n if (!widget.options.values.includes(widget.value)) {\r\n widget.value = widget.options.values[0]\r\n ;(widget.callback as Function)(widget.value)\r\n }\r\n }\r\n }\r\n\r\n onAfterGraphConfigured() {\r\n if (this.outputs[0].links?.length && !this.widgets?.length) {\r\n // TODO: Review this check\r\n // @ts-expect-error\r\n if (!this.#onFirstConnection()) return\r\n\r\n // Populate widget values from config data\r\n if (this.widgets) {\r\n for (let i = 0; i < this.widgets_values.length; i++) {\r\n const w = this.widgets[i]\r\n if (w) {\r\n w.value = this.widgets_values[i]\r\n }\r\n }\r\n }\r\n\r\n // Merge values if required\r\n this.#mergeWidgetConfig()\r\n }\r\n }\r\n\r\n onConnectionsChange(_, index, connected) {\r\n if (app.configuringGraph) {\r\n // Dont run while the graph is still setting up\r\n return\r\n }\r\n\r\n const links = this.outputs[0].links\r\n if (connected) {\r\n if (links?.length && !this.widgets?.length) {\r\n this.#onFirstConnection()\r\n }\r\n } else {\r\n // We may have removed a link that caused the constraints to change\r\n this.#mergeWidgetConfig()\r\n\r\n if (!links?.length) {\r\n this.onLastDisconnect()\r\n }\r\n }\r\n }\r\n\r\n onConnectOutput(slot, type, input, target_node, target_slot) {\r\n // Fires before the link is made allowing us to reject it if it isn't valid\r\n // No widget, we cant connect\r\n if (!input.widget) {\r\n if (!(input.type in ComfyWidgets)) return false\r\n }\r\n\r\n if (this.outputs[slot].links?.length) {\r\n const valid = this.#isValidConnection(input)\r\n if (valid) {\r\n // On connect of additional outputs, copy our value to their widget\r\n this.applyToGraph([{ target_id: target_node.id, target_slot }])\r\n }\r\n return valid\r\n }\r\n }\r\n\r\n #onFirstConnection(recreating?: boolean) {\r\n // First connection can fire before the graph is ready on initial load so random things can be missing\r\n if (!this.outputs[0].links) {\r\n this.onLastDisconnect()\r\n return\r\n }\r\n const linkId = this.outputs[0].links[0]\r\n const link = this.graph.links[linkId]\r\n if (!link) return\r\n\r\n const theirNode = this.graph.getNodeById(link.target_id)\r\n if (!theirNode || !theirNode.inputs) return\r\n\r\n const input = theirNode.inputs[link.target_slot]\r\n if (!input) return\r\n\r\n let widget\r\n if (!input.widget) {\r\n if (!(input.type in ComfyWidgets)) return\r\n widget = { name: input.name, [GET_CONFIG]: () => [input.type, {}] } //fake widget\r\n } else {\r\n widget = input.widget\r\n }\r\n\r\n const config = widget[GET_CONFIG]?.()\r\n if (!config) return\r\n\r\n const { type } = getWidgetType(config)\r\n // Update our output to restrict to the widget type\r\n this.outputs[0].type = type\r\n this.outputs[0].name = type\r\n this.outputs[0].widget = widget\r\n\r\n this.#createWidget(\r\n widget[CONFIG] ?? config,\r\n theirNode,\r\n widget.name,\r\n recreating,\r\n widget[TARGET]\r\n )\r\n }\r\n\r\n #createWidget(inputData, node, widgetName, recreating, targetWidget) {\r\n let type = inputData[0]\r\n\r\n if (type instanceof Array) {\r\n type = 'COMBO'\r\n }\r\n\r\n let widget\r\n if (type in ComfyWidgets) {\r\n widget = (ComfyWidgets[type](this, 'value', inputData, app) || {}).widget\r\n } else {\r\n widget = this.addWidget(type, 'value', null, () => {}, {})\r\n }\r\n\r\n if (targetWidget) {\r\n widget.value = targetWidget.value\r\n } else if (node?.widgets && widget) {\r\n const theirWidget = node.widgets.find((w) => w.name === widgetName)\r\n if (theirWidget) {\r\n widget.value = theirWidget.value\r\n }\r\n }\r\n\r\n if (\r\n !inputData?.[1]?.control_after_generate &&\r\n (widget.type === 'number' || widget.type === 'combo')\r\n ) {\r\n let control_value = this.widgets_values?.[1]\r\n if (!control_value) {\r\n control_value = 'fixed'\r\n }\r\n addValueControlWidgets(\r\n this,\r\n widget,\r\n control_value as string,\r\n undefined,\r\n inputData\r\n )\r\n let filter = this.widgets_values?.[2]\r\n if (filter && this.widgets.length === 3) {\r\n this.widgets[2].value = filter\r\n }\r\n }\r\n\r\n // Restore any saved control values\r\n const controlValues = this.controlValues\r\n if (\r\n this.lastType === this.widgets[0].type &&\r\n controlValues?.length === this.widgets.length - 1\r\n ) {\r\n for (let i = 0; i < controlValues.length; i++) {\r\n this.widgets[i + 1].value = controlValues[i]\r\n }\r\n }\r\n\r\n // When our value changes, update other widgets to reflect our changes\r\n // e.g. so LoadImage shows correct image\r\n const callback = widget.callback\r\n const self = this\r\n widget.callback = function () {\r\n const r = callback ? callback.apply(this, arguments) : undefined\r\n self.applyToGraph()\r\n return r\r\n }\r\n\r\n if (!recreating) {\r\n // Grow our node if required\r\n const sz = this.computeSize()\r\n if (this.size[0] < sz[0]) {\r\n this.size[0] = sz[0]\r\n }\r\n if (this.size[1] < sz[1]) {\r\n this.size[1] = sz[1]\r\n }\r\n\r\n requestAnimationFrame(() => {\r\n if (this.onResize) {\r\n this.onResize(this.size)\r\n }\r\n })\r\n }\r\n }\r\n\r\n recreateWidget() {\r\n const values = this.widgets?.map((w) => w.value)\r\n this.#removeWidgets()\r\n this.#onFirstConnection(true)\r\n if (values?.length) {\r\n for (let i = 0; i < this.widgets?.length; i++)\r\n this.widgets[i].value = values[i]\r\n }\r\n return this.widgets?.[0]\r\n }\r\n\r\n #mergeWidgetConfig() {\r\n // Merge widget configs if the node has multiple outputs\r\n const output = this.outputs[0]\r\n const links = output.links\r\n\r\n const hasConfig = !!output.widget[CONFIG]\r\n if (hasConfig) {\r\n delete output.widget[CONFIG]\r\n }\r\n\r\n if (links?.length < 2 && hasConfig) {\r\n // Copy the widget options from the source\r\n if (links.length) {\r\n this.recreateWidget()\r\n }\r\n\r\n return\r\n }\r\n\r\n const config1 = output.widget[GET_CONFIG]()\r\n const isNumber = config1[0] === 'INT' || config1[0] === 'FLOAT'\r\n if (!isNumber) return\r\n\r\n for (const linkId of links) {\r\n const link = app.graph.links[linkId]\r\n if (!link) continue // Can be null when removing a node\r\n\r\n const theirNode = app.graph.getNodeById(link.target_id)\r\n const theirInput = theirNode.inputs[link.target_slot]\r\n\r\n // Call is valid connection so it can merge the configs when validating\r\n this.#isValidConnection(theirInput, hasConfig)\r\n }\r\n }\r\n\r\n #isValidConnection(input: INodeInputSlot, forceUpdate?: boolean) {\r\n // Only allow connections where the configs match\r\n const output = this.outputs[0]\r\n const config2 = input.widget[GET_CONFIG]()\r\n return !!mergeIfValid.call(\r\n this,\r\n output,\r\n config2,\r\n forceUpdate,\r\n this.recreateWidget\r\n )\r\n }\r\n\r\n #removeWidgets() {\r\n if (this.widgets) {\r\n // Allow widgets to cleanup\r\n for (const w of this.widgets) {\r\n if (w.onRemove) {\r\n w.onRemove()\r\n }\r\n }\r\n\r\n // Temporarily store the current values in case the node is being recreated\r\n // e.g. by group node conversion\r\n this.controlValues = []\r\n this.lastType = this.widgets[0]?.type\r\n for (let i = 1; i < this.widgets.length; i++) {\r\n this.controlValues.push(this.widgets[i].value)\r\n }\r\n setTimeout(() => {\r\n delete this.lastType\r\n delete this.controlValues\r\n }, 15)\r\n this.widgets.length = 0\r\n }\r\n }\r\n\r\n onLastDisconnect() {\r\n // We cant remove + re-add the output here as if you drag a link over the same link\r\n // it removes, then re-adds, causing it to break\r\n this.outputs[0].type = '*'\r\n this.outputs[0].name = 'connect to widget input'\r\n delete this.outputs[0].widget\r\n\r\n this.#removeWidgets()\r\n }\r\n}\r\n\r\nexport function getWidgetConfig(slot) {\r\n return slot.widget[CONFIG] ?? slot.widget[GET_CONFIG]()\r\n}\r\n\r\nfunction getConfig(widgetName) {\r\n const { nodeData } = this.constructor\r\n return (\r\n nodeData?.input?.required[widgetName] ??\r\n nodeData?.input?.optional?.[widgetName]\r\n )\r\n}\r\n\r\nfunction isConvertibleWidget(widget, config) {\r\n return (\r\n (VALID_TYPES.includes(widget.type) || VALID_TYPES.includes(config[0])) &&\r\n !widget.options?.forceInput\r\n )\r\n}\r\n\r\nfunction hideWidget(node, widget, suffix = '') {\r\n if (widget.type?.startsWith(CONVERTED_TYPE)) return\r\n widget.origType = widget.type\r\n widget.origComputeSize = widget.computeSize\r\n widget.origSerializeValue = widget.serializeValue\r\n widget.computeSize = () => [0, -4] // -4 is due to the gap litegraph adds between widgets automatically\r\n widget.type = CONVERTED_TYPE + suffix\r\n widget.serializeValue = () => {\r\n // Prevent serializing the widget if we have no input linked\r\n if (!node.inputs) {\r\n return undefined\r\n }\r\n let node_input = node.inputs.find((i) => i.widget?.name === widget.name)\r\n\r\n if (!node_input || !node_input.link) {\r\n return undefined\r\n }\r\n return widget.origSerializeValue\r\n ? widget.origSerializeValue()\r\n : widget.value\r\n }\r\n\r\n // Hide any linked widgets, e.g. seed+seedControl\r\n if (widget.linkedWidgets) {\r\n for (const w of widget.linkedWidgets) {\r\n hideWidget(node, w, ':' + widget.name)\r\n }\r\n }\r\n}\r\n\r\nfunction showWidget(widget) {\r\n widget.type = widget.origType\r\n widget.computeSize = widget.origComputeSize\r\n widget.serializeValue = widget.origSerializeValue\r\n\r\n delete widget.origType\r\n delete widget.origComputeSize\r\n delete widget.origSerializeValue\r\n\r\n // Hide any linked widgets, e.g. seed+seedControl\r\n if (widget.linkedWidgets) {\r\n for (const w of widget.linkedWidgets) {\r\n showWidget(w)\r\n }\r\n }\r\n}\r\n\r\nfunction convertToInput(node, widget, config) {\r\n hideWidget(node, widget)\r\n\r\n const { type } = getWidgetType(config)\r\n\r\n // Add input and store widget config for creating on primitive node\r\n const sz = node.size\r\n node.addInput(widget.name, type, {\r\n widget: { name: widget.name, [GET_CONFIG]: () => config }\r\n })\r\n\r\n for (const widget of node.widgets) {\r\n widget.last_y += LiteGraph.NODE_SLOT_HEIGHT\r\n }\r\n\r\n // Restore original size but grow if needed\r\n node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])])\r\n}\r\n\r\nfunction convertToWidget(node, widget) {\r\n showWidget(widget)\r\n const sz = node.size\r\n node.removeInput(node.inputs.findIndex((i) => i.widget?.name === widget.name))\r\n\r\n for (const widget of node.widgets) {\r\n widget.last_y -= LiteGraph.NODE_SLOT_HEIGHT\r\n }\r\n\r\n // Restore original size but grow if needed\r\n node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])])\r\n}\r\n\r\nfunction getWidgetType(config) {\r\n // Special handling for COMBO so we restrict links based on the entries\r\n let type = config[0]\r\n if (type instanceof Array) {\r\n type = 'COMBO'\r\n }\r\n return { type }\r\n}\r\n\r\nfunction isValidCombo(combo, obj) {\r\n // New input isnt a combo\r\n if (!(obj instanceof Array)) {\r\n console.log(`connection rejected: tried to connect combo to ${obj}`)\r\n return false\r\n }\r\n // New input combo has a different size\r\n if (combo.length !== obj.length) {\r\n console.log(`connection rejected: combo lists dont match`)\r\n return false\r\n }\r\n // New input combo has different elements\r\n if (combo.find((v, i) => obj[i] !== v)) {\r\n console.log(`connection rejected: combo lists dont match`)\r\n return false\r\n }\r\n\r\n return true\r\n}\r\n\r\nfunction isPrimitiveNode(node: LGraphNode): node is PrimitiveNode {\r\n return node.type === 'PrimitiveNode'\r\n}\r\n\r\nexport function setWidgetConfig(slot, config, target?: IWidget) {\r\n if (!slot.widget) return\r\n if (config) {\r\n slot.widget[GET_CONFIG] = () => config\r\n slot.widget[TARGET] = target\r\n } else {\r\n delete slot.widget\r\n }\r\n\r\n if (slot.link) {\r\n const link = app.graph.links[slot.link]\r\n if (link) {\r\n const originNode = app.graph.getNodeById(link.origin_id)\r\n if (isPrimitiveNode(originNode)) {\r\n if (config) {\r\n originNode.recreateWidget()\r\n } else if (!app.configuringGraph) {\r\n originNode.disconnectOutput(0)\r\n originNode.onLastDisconnect()\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport function mergeIfValid(\r\n output,\r\n config2,\r\n forceUpdate?: boolean,\r\n recreateWidget?: () => void,\r\n config1?: unknown\r\n) {\r\n if (!config1) {\r\n config1 = output.widget[CONFIG] ?? output.widget[GET_CONFIG]()\r\n }\r\n\r\n if (config1[0] instanceof Array) {\r\n if (!isValidCombo(config1[0], config2[0])) return\r\n } else if (config1[0] !== config2[0]) {\r\n // Types dont match\r\n console.log(`connection rejected: types dont match`, config1[0], config2[0])\r\n return\r\n }\r\n\r\n const keys = new Set([\r\n ...Object.keys(config1[1] ?? {}),\r\n ...Object.keys(config2[1] ?? {})\r\n ])\r\n\r\n let customConfig\r\n const getCustomConfig = () => {\r\n if (!customConfig) {\r\n if (typeof structuredClone === 'undefined') {\r\n customConfig = JSON.parse(JSON.stringify(config1[1] ?? {}))\r\n } else {\r\n customConfig = structuredClone(config1[1] ?? {})\r\n }\r\n }\r\n return customConfig\r\n }\r\n\r\n const isNumber = config1[0] === 'INT' || config1[0] === 'FLOAT'\r\n for (const k of keys.values()) {\r\n if (\r\n k !== 'default' &&\r\n k !== 'forceInput' &&\r\n k !== 'defaultInput' &&\r\n k !== 'control_after_generate' &&\r\n k !== 'multiline' &&\r\n k !== 'tooltip'\r\n ) {\r\n let v1 = config1[1][k]\r\n let v2 = config2[1]?.[k]\r\n\r\n if (v1 === v2 || (!v1 && !v2)) continue\r\n\r\n if (isNumber) {\r\n if (k === 'min') {\r\n const theirMax = config2[1]?.['max']\r\n if (theirMax != null && v1 > theirMax) {\r\n console.log('connection rejected: min > max', v1, theirMax)\r\n return\r\n }\r\n getCustomConfig()[k] =\r\n v1 == null ? v2 : v2 == null ? v1 : Math.max(v1, v2)\r\n continue\r\n } else if (k === 'max') {\r\n const theirMin = config2[1]?.['min']\r\n if (theirMin != null && v1 < theirMin) {\r\n console.log('connection rejected: max < min', v1, theirMin)\r\n return\r\n }\r\n getCustomConfig()[k] =\r\n v1 == null ? v2 : v2 == null ? v1 : Math.min(v1, v2)\r\n continue\r\n } else if (k === 'step') {\r\n let step\r\n if (v1 == null) {\r\n // No current step\r\n step = v2\r\n } else if (v2 == null) {\r\n // No new step\r\n step = v1\r\n } else {\r\n if (v1 < v2) {\r\n // Ensure v1 is larger for the mod\r\n const a = v2\r\n v2 = v1\r\n v1 = a\r\n }\r\n if (v1 % v2) {\r\n console.log(\r\n 'connection rejected: steps not divisible',\r\n 'current:',\r\n v1,\r\n 'new:',\r\n v2\r\n )\r\n return\r\n }\r\n\r\n step = v1\r\n }\r\n\r\n getCustomConfig()[k] = step\r\n continue\r\n }\r\n }\r\n\r\n console.log(`connection rejected: config ${k} values dont match`, v1, v2)\r\n return\r\n }\r\n }\r\n\r\n if (customConfig || forceUpdate) {\r\n if (customConfig) {\r\n output.widget[CONFIG] = [config1[0], customConfig]\r\n }\r\n\r\n const widget = recreateWidget?.call(this)\r\n // When deleting a node this can be null\r\n if (widget) {\r\n const min = widget.options.min\r\n const max = widget.options.max\r\n if (min != null && widget.value < min) widget.value = min\r\n if (max != null && widget.value > max) widget.value = max\r\n widget.callback(widget.value)\r\n }\r\n }\r\n\r\n return { customConfig }\r\n}\r\n\r\nlet useConversionSubmenusSetting\r\napp.registerExtension({\r\n name: 'Comfy.WidgetInputs',\r\n init() {\r\n useConversionSubmenusSetting = app.ui.settings.addSetting({\r\n id: 'Comfy.NodeInputConversionSubmenus',\r\n name: 'Node widget/input conversion sub-menus',\r\n tooltip:\r\n 'In the node context menu, place the entries that convert between input/widget in sub-menus.',\r\n type: 'boolean',\r\n defaultValue: true\r\n })\r\n },\r\n async beforeRegisterNodeDef(nodeType, nodeData, app) {\r\n // Add menu options to convert to/from widgets\r\n const origGetExtraMenuOptions = nodeType.prototype.getExtraMenuOptions\r\n nodeType.prototype.convertWidgetToInput = function (widget) {\r\n const config = getConfig.call(this, widget.name) ?? [\r\n widget.type,\r\n widget.options || {}\r\n ]\r\n if (!isConvertibleWidget(widget, config)) return false\r\n convertToInput(this, widget, config)\r\n return true\r\n }\r\n nodeType.prototype.getExtraMenuOptions = function (_, options) {\r\n const r = origGetExtraMenuOptions\r\n ? origGetExtraMenuOptions.apply(this, arguments)\r\n : undefined\r\n\r\n if (this.widgets) {\r\n let toInput = []\r\n let toWidget = []\r\n for (const w of this.widgets) {\r\n if (w.options?.forceInput) {\r\n continue\r\n }\r\n if (w.type === CONVERTED_TYPE) {\r\n toWidget.push({\r\n content: `Convert ${w.name} to widget`,\r\n callback: () => convertToWidget(this, w)\r\n })\r\n } else {\r\n const config = getConfig.call(this, w.name) ?? [\r\n w.type,\r\n w.options || {}\r\n ]\r\n if (isConvertibleWidget(w, config)) {\r\n toInput.push({\r\n content: `Convert ${w.name} to input`,\r\n callback: () => convertToInput(this, w, config)\r\n })\r\n }\r\n }\r\n }\r\n\r\n //Convert.. main menu\r\n if (toInput.length) {\r\n if (useConversionSubmenusSetting.value) {\r\n options.push({\r\n content: 'Convert Widget to Input',\r\n submenu: {\r\n options: toInput\r\n }\r\n })\r\n } else {\r\n options.push(...toInput, null)\r\n }\r\n }\r\n if (toWidget.length) {\r\n if (useConversionSubmenusSetting.value) {\r\n options.push({\r\n content: 'Convert Input to Widget',\r\n submenu: {\r\n options: toWidget\r\n }\r\n })\r\n } else {\r\n options.push(...toWidget, null)\r\n }\r\n }\r\n }\r\n\r\n return r\r\n }\r\n\r\n nodeType.prototype.onGraphConfigured = function () {\r\n if (!this.inputs) return\r\n\r\n for (const input of this.inputs) {\r\n if (input.widget) {\r\n if (!input.widget[GET_CONFIG]) {\r\n input.widget[GET_CONFIG] = () =>\r\n getConfig.call(this, input.widget.name)\r\n }\r\n\r\n // Cleanup old widget config\r\n if (input.widget.config) {\r\n if (input.widget.config[0] instanceof Array) {\r\n // If we are an old converted combo then replace the input type and the stored link data\r\n input.type = 'COMBO'\r\n\r\n const link = app.graph.links[input.link]\r\n if (link) {\r\n link.type = input.type\r\n }\r\n }\r\n delete input.widget.config\r\n }\r\n\r\n const w = this.widgets.find((w) => w.name === input.widget.name)\r\n if (w) {\r\n hideWidget(this, w)\r\n } else {\r\n convertToWidget(this, input)\r\n }\r\n }\r\n }\r\n }\r\n\r\n const origOnNodeCreated = nodeType.prototype.onNodeCreated\r\n nodeType.prototype.onNodeCreated = function () {\r\n const r = origOnNodeCreated ? origOnNodeCreated.apply(this) : undefined\r\n\r\n // When node is created, convert any force/default inputs\r\n if (!app.configuringGraph && this.widgets) {\r\n for (const w of this.widgets) {\r\n if (w?.options?.forceInput || w?.options?.defaultInput) {\r\n const config = getConfig.call(this, w.name) ?? [\r\n w.type,\r\n w.options || {}\r\n ]\r\n convertToInput(this, w, config)\r\n }\r\n }\r\n }\r\n\r\n return r\r\n }\r\n\r\n const origOnConfigure = nodeType.prototype.onConfigure\r\n nodeType.prototype.onConfigure = function () {\r\n const r = origOnConfigure\r\n ? origOnConfigure.apply(this, arguments)\r\n : undefined\r\n if (!app.configuringGraph && this.inputs) {\r\n // On copy + paste of nodes, ensure that widget configs are set up\r\n for (const input of this.inputs) {\r\n if (input.widget && !input.widget[GET_CONFIG]) {\r\n input.widget[GET_CONFIG] = () =>\r\n getConfig.call(this, input.widget.name)\r\n const w = this.widgets.find((w) => w.name === input.widget.name)\r\n if (w) {\r\n hideWidget(this, w)\r\n }\r\n }\r\n }\r\n }\r\n\r\n return r\r\n }\r\n\r\n function isNodeAtPos(pos) {\r\n for (const n of app.graph._nodes) {\r\n if (n.pos[0] === pos[0] && n.pos[1] === pos[1]) {\r\n return true\r\n }\r\n }\r\n return false\r\n }\r\n\r\n // Double click a widget input to automatically attach a primitive\r\n const origOnInputDblClick = nodeType.prototype.onInputDblClick\r\n const ignoreDblClick = Symbol()\r\n nodeType.prototype.onInputDblClick = function (slot) {\r\n const r = origOnInputDblClick\r\n ? origOnInputDblClick.apply(this, arguments)\r\n : undefined\r\n\r\n const input = this.inputs[slot]\r\n if (!input.widget || !input[ignoreDblClick]) {\r\n // Not a widget input or already handled input\r\n if (\r\n !(input.type in ComfyWidgets) &&\r\n !(input.widget[GET_CONFIG]?.()?.[0] instanceof Array)\r\n ) {\r\n return r //also Not a ComfyWidgets input or combo (do nothing)\r\n }\r\n }\r\n\r\n // Create a primitive node\r\n const node = LiteGraph.createNode('PrimitiveNode')\r\n app.graph.add(node)\r\n\r\n // Calculate a position that wont directly overlap another node\r\n const pos: [number, number] = [\r\n this.pos[0] - node.size[0] - 30,\r\n this.pos[1]\r\n ]\r\n while (isNodeAtPos(pos)) {\r\n pos[1] += LiteGraph.NODE_TITLE_HEIGHT\r\n }\r\n\r\n node.pos = pos\r\n node.connect(0, this, slot)\r\n node.title = input.name\r\n\r\n // Prevent adding duplicates due to triple clicking\r\n input[ignoreDblClick] = true\r\n setTimeout(() => {\r\n delete input[ignoreDblClick]\r\n }, 300)\r\n\r\n return r\r\n }\r\n\r\n // Prevent connecting COMBO lists to converted inputs that dont match types\r\n const onConnectInput = nodeType.prototype.onConnectInput\r\n nodeType.prototype.onConnectInput = function (\r\n targetSlot,\r\n type,\r\n output,\r\n originNode,\r\n originSlot\r\n ) {\r\n const v = onConnectInput?.(this, arguments)\r\n // Not a combo, ignore\r\n if (type !== 'COMBO') return v\r\n // Primitive output, allow that to handle\r\n if (originNode.outputs[originSlot].widget) return v\r\n\r\n // Ensure target is also a combo\r\n const targetCombo = this.inputs[targetSlot].widget?.[GET_CONFIG]?.()?.[0]\r\n if (!targetCombo || !(targetCombo instanceof Array)) return v\r\n\r\n // Check they match\r\n const originConfig =\r\n originNode.constructor?.nodeData?.output?.[originSlot]\r\n if (!originConfig || !isValidCombo(targetCombo, originConfig)) {\r\n return false\r\n }\r\n\r\n return v\r\n }\r\n },\r\n registerCustomNodes() {\r\n LiteGraph.registerNodeType(\r\n 'PrimitiveNode',\r\n Object.assign(PrimitiveNode, {\r\n title: 'Primitive'\r\n })\r\n )\r\n PrimitiveNode.category = 'utils'\r\n }\r\n})\r\n","import { $el, ComfyDialog } from '../../scripts/ui'\r\nimport { DraggableList } from '../../scripts/ui/draggableList'\r\nimport { GroupNodeConfig, GroupNodeHandler } from './groupNode'\r\nimport './groupNodeManage.css'\r\nimport { app, type ComfyApp } from '../../scripts/app'\r\nimport {\r\n LiteGraph,\r\n type LGraphNode,\r\n type LGraphNodeConstructor\r\n} from '@comfyorg/litegraph'\r\n\r\nconst ORDER: symbol = Symbol()\r\n\r\nfunction merge(target, source) {\r\n if (typeof target === 'object' && typeof source === 'object') {\r\n for (const key in source) {\r\n const sv = source[key]\r\n if (typeof sv === 'object') {\r\n let tv = target[key]\r\n if (!tv) tv = target[key] = {}\r\n merge(tv, source[key])\r\n } else {\r\n target[key] = sv\r\n }\r\n }\r\n }\r\n\r\n return target\r\n}\r\n\r\nexport class ManageGroupDialog extends ComfyDialog {\r\n tabs: Record<\r\n 'Inputs' | 'Outputs' | 'Widgets',\r\n { tab: HTMLAnchorElement; page: HTMLElement }\r\n >\r\n selectedNodeIndex: number | null | undefined\r\n selectedTab: keyof ManageGroupDialog['tabs'] = 'Inputs'\r\n selectedGroup: string | undefined\r\n modifications: Record<\r\n string,\r\n Record<\r\n string,\r\n Record<\r\n string,\r\n { name?: string | undefined; visible?: boolean | undefined }\r\n >\r\n >\r\n > = {}\r\n nodeItems: any[]\r\n app: ComfyApp\r\n groupNodeType: LGraphNodeConstructor\r\n groupNodeDef: any\r\n groupData: any\r\n\r\n innerNodesList: HTMLUListElement\r\n widgetsPage: HTMLElement\r\n inputsPage: HTMLElement\r\n outputsPage: HTMLElement\r\n draggable: any\r\n\r\n get selectedNodeInnerIndex() {\r\n return +this.nodeItems[this.selectedNodeIndex].dataset.nodeindex\r\n }\r\n\r\n constructor(app) {\r\n super()\r\n this.app = app\r\n this.element = $el('dialog.comfy-group-manage', {\r\n parent: document.body\r\n }) as HTMLDialogElement\r\n }\r\n\r\n changeTab(tab) {\r\n this.tabs[this.selectedTab].tab.classList.remove('active')\r\n this.tabs[this.selectedTab].page.classList.remove('active')\r\n this.tabs[tab].tab.classList.add('active')\r\n this.tabs[tab].page.classList.add('active')\r\n this.selectedTab = tab\r\n }\r\n\r\n changeNode(index, force?) {\r\n if (!force && this.selectedNodeIndex === index) return\r\n\r\n if (this.selectedNodeIndex != null) {\r\n this.nodeItems[this.selectedNodeIndex].classList.remove('selected')\r\n }\r\n this.nodeItems[index].classList.add('selected')\r\n this.selectedNodeIndex = index\r\n\r\n if (!this.buildInputsPage() && this.selectedTab === 'Inputs') {\r\n this.changeTab('Widgets')\r\n }\r\n if (!this.buildWidgetsPage() && this.selectedTab === 'Widgets') {\r\n this.changeTab('Outputs')\r\n }\r\n if (!this.buildOutputsPage() && this.selectedTab === 'Outputs') {\r\n this.changeTab('Inputs')\r\n }\r\n\r\n this.changeTab(this.selectedTab)\r\n }\r\n\r\n getGroupData() {\r\n this.groupNodeType =\r\n LiteGraph.registered_node_types['workflow/' + this.selectedGroup]\r\n this.groupNodeDef = this.groupNodeType.nodeData\r\n this.groupData = GroupNodeHandler.getGroupData(this.groupNodeType)\r\n }\r\n\r\n changeGroup(group, reset = true) {\r\n this.selectedGroup = group\r\n this.getGroupData()\r\n\r\n const nodes = this.groupData.nodeData.nodes\r\n this.nodeItems = nodes.map((n, i) =>\r\n $el(\r\n 'li.draggable-item',\r\n {\r\n dataset: {\r\n nodeindex: n.index + ''\r\n },\r\n onclick: () => {\r\n this.changeNode(i)\r\n }\r\n },\r\n [\r\n $el('span.drag-handle'),\r\n $el(\r\n 'div',\r\n {\r\n textContent: n.title ?? n.type\r\n },\r\n n.title\r\n ? $el('span', {\r\n textContent: n.type\r\n })\r\n : []\r\n )\r\n ]\r\n )\r\n )\r\n\r\n this.innerNodesList.replaceChildren(...this.nodeItems)\r\n\r\n if (reset) {\r\n this.selectedNodeIndex = null\r\n this.changeNode(0)\r\n } else {\r\n const items = this.draggable.getAllItems()\r\n let index = items.findIndex((item) => item.classList.contains('selected'))\r\n if (index === -1) index = this.selectedNodeIndex\r\n this.changeNode(index, true)\r\n }\r\n\r\n const ordered = [...nodes]\r\n this.draggable?.dispose()\r\n this.draggable = new DraggableList(this.innerNodesList, 'li')\r\n this.draggable.addEventListener(\r\n 'dragend',\r\n ({ detail: { oldPosition, newPosition } }) => {\r\n if (oldPosition === newPosition) return\r\n ordered.splice(newPosition, 0, ordered.splice(oldPosition, 1)[0])\r\n for (let i = 0; i < ordered.length; i++) {\r\n this.storeModification({\r\n nodeIndex: ordered[i].index,\r\n section: ORDER,\r\n prop: 'order',\r\n value: i\r\n })\r\n }\r\n }\r\n )\r\n }\r\n\r\n storeModification(props: {\r\n nodeIndex?: number\r\n section: symbol\r\n prop: string\r\n value: any\r\n }) {\r\n const { nodeIndex, section, prop, value } = props\r\n const groupMod = (this.modifications[this.selectedGroup] ??= {})\r\n const nodesMod = (groupMod.nodes ??= {})\r\n const nodeMod = (nodesMod[nodeIndex ?? this.selectedNodeInnerIndex] ??= {})\r\n const typeMod = (nodeMod[section] ??= {})\r\n if (typeof value === 'object') {\r\n const objMod = (typeMod[prop] ??= {})\r\n Object.assign(objMod, value)\r\n } else {\r\n typeMod[prop] = value\r\n }\r\n }\r\n\r\n getEditElement(section, prop, value, placeholder, checked, checkable = true) {\r\n if (value === placeholder) value = ''\r\n\r\n const mods =\r\n this.modifications[this.selectedGroup]?.nodes?.[\r\n this.selectedNodeInnerIndex\r\n ]?.[section]?.[prop]\r\n if (mods) {\r\n if (mods.name != null) {\r\n value = mods.name\r\n }\r\n if (mods.visible != null) {\r\n checked = mods.visible\r\n }\r\n }\r\n\r\n return $el('div', [\r\n $el('input', {\r\n value,\r\n placeholder,\r\n type: 'text',\r\n onchange: (e) => {\r\n this.storeModification({\r\n section,\r\n prop,\r\n value: { name: e.target.value }\r\n })\r\n }\r\n }),\r\n $el('label', { textContent: 'Visible' }, [\r\n $el('input', {\r\n type: 'checkbox',\r\n checked,\r\n disabled: !checkable,\r\n onchange: (e) => {\r\n this.storeModification({\r\n section,\r\n prop,\r\n value: { visible: !!e.target.checked }\r\n })\r\n }\r\n })\r\n ])\r\n ])\r\n }\r\n\r\n buildWidgetsPage() {\r\n const widgets =\r\n this.groupData.oldToNewWidgetMap[this.selectedNodeInnerIndex]\r\n const items = Object.keys(widgets ?? {})\r\n const type = app.graph.extra.groupNodes[this.selectedGroup]\r\n const config = type.config?.[this.selectedNodeInnerIndex]?.input\r\n this.widgetsPage.replaceChildren(\r\n ...items.map((oldName) => {\r\n return this.getEditElement(\r\n 'input',\r\n oldName,\r\n widgets[oldName],\r\n oldName,\r\n config?.[oldName]?.visible !== false\r\n )\r\n })\r\n )\r\n return !!items.length\r\n }\r\n\r\n buildInputsPage() {\r\n const inputs = this.groupData.nodeInputs[this.selectedNodeInnerIndex]\r\n const items = Object.keys(inputs ?? {})\r\n const type = app.graph.extra.groupNodes[this.selectedGroup]\r\n const config = type.config?.[this.selectedNodeInnerIndex]?.input\r\n this.inputsPage.replaceChildren(\r\n ...items\r\n .map((oldName) => {\r\n let value = inputs[oldName]\r\n if (!value) {\r\n return\r\n }\r\n\r\n return this.getEditElement(\r\n 'input',\r\n oldName,\r\n value,\r\n oldName,\r\n config?.[oldName]?.visible !== false\r\n )\r\n })\r\n .filter(Boolean)\r\n )\r\n return !!items.length\r\n }\r\n\r\n buildOutputsPage() {\r\n const nodes = this.groupData.nodeData.nodes\r\n const innerNodeDef = this.groupData.getNodeDef(\r\n nodes[this.selectedNodeInnerIndex]\r\n )\r\n const outputs = innerNodeDef?.output ?? []\r\n const groupOutputs =\r\n this.groupData.oldToNewOutputMap[this.selectedNodeInnerIndex]\r\n\r\n const type = app.graph.extra.groupNodes[this.selectedGroup]\r\n const config = type.config?.[this.selectedNodeInnerIndex]?.output\r\n const node = this.groupData.nodeData.nodes[this.selectedNodeInnerIndex]\r\n const checkable = node.type !== 'PrimitiveNode'\r\n this.outputsPage.replaceChildren(\r\n ...outputs\r\n .map((type, slot) => {\r\n const groupOutputIndex = groupOutputs?.[slot]\r\n const oldName = innerNodeDef.output_name?.[slot] ?? type\r\n let value = config?.[slot]?.name\r\n const visible = config?.[slot]?.visible || groupOutputIndex != null\r\n if (!value || value === oldName) {\r\n value = ''\r\n }\r\n return this.getEditElement(\r\n 'output',\r\n slot,\r\n value,\r\n oldName,\r\n visible,\r\n checkable\r\n )\r\n })\r\n .filter(Boolean)\r\n )\r\n return !!outputs.length\r\n }\r\n\r\n show(type?) {\r\n const groupNodes = Object.keys(app.graph.extra?.groupNodes ?? {}).sort(\r\n (a, b) => a.localeCompare(b)\r\n )\r\n\r\n this.innerNodesList = $el(\r\n 'ul.comfy-group-manage-list-items'\r\n ) as HTMLUListElement\r\n this.widgetsPage = $el('section.comfy-group-manage-node-page')\r\n this.inputsPage = $el('section.comfy-group-manage-node-page')\r\n this.outputsPage = $el('section.comfy-group-manage-node-page')\r\n const pages = $el('div', [\r\n this.widgetsPage,\r\n this.inputsPage,\r\n this.outputsPage\r\n ])\r\n\r\n this.tabs = [\r\n ['Inputs', this.inputsPage],\r\n ['Widgets', this.widgetsPage],\r\n ['Outputs', this.outputsPage]\r\n ].reduce((p, [name, page]: [string, HTMLElement]) => {\r\n p[name] = {\r\n tab: $el('a', {\r\n onclick: () => {\r\n this.changeTab(name)\r\n },\r\n textContent: name\r\n }),\r\n page\r\n }\r\n return p\r\n }, {}) as any\r\n\r\n const outer = $el('div.comfy-group-manage-outer', [\r\n $el('header', [\r\n $el('h2', 'Group Nodes'),\r\n $el(\r\n 'select',\r\n {\r\n onchange: (e) => {\r\n this.changeGroup(e.target.value)\r\n }\r\n },\r\n groupNodes.map((g) =>\r\n $el('option', {\r\n textContent: g,\r\n selected: 'workflow/' + g === type,\r\n value: g\r\n })\r\n )\r\n )\r\n ]),\r\n $el('main', [\r\n $el('section.comfy-group-manage-list', this.innerNodesList),\r\n $el('section.comfy-group-manage-node', [\r\n $el(\r\n 'header',\r\n Object.values(this.tabs).map((t) => t.tab)\r\n ),\r\n pages\r\n ])\r\n ]),\r\n $el('footer', [\r\n $el(\r\n 'button.comfy-btn',\r\n {\r\n onclick: (e) => {\r\n // @ts-expect-error\r\n const node = app.graph._nodes.find(\r\n (n) => n.type === 'workflow/' + this.selectedGroup\r\n )\r\n if (node) {\r\n alert(\r\n 'This group node is in use in the current workflow, please first remove these.'\r\n )\r\n return\r\n }\r\n if (\r\n confirm(\r\n `Are you sure you want to remove the node: \"${this.selectedGroup}\"`\r\n )\r\n ) {\r\n delete app.graph.extra.groupNodes[this.selectedGroup]\r\n LiteGraph.unregisterNodeType('workflow/' + this.selectedGroup)\r\n }\r\n this.show()\r\n }\r\n },\r\n 'Delete Group Node'\r\n ),\r\n $el(\r\n 'button.comfy-btn',\r\n {\r\n onclick: async () => {\r\n let nodesByType\r\n let recreateNodes = []\r\n const types = {}\r\n for (const g in this.modifications) {\r\n const type = app.graph.extra.groupNodes[g]\r\n let config = (type.config ??= {})\r\n\r\n let nodeMods = this.modifications[g]?.nodes\r\n if (nodeMods) {\r\n const keys = Object.keys(nodeMods)\r\n if (nodeMods[keys[0]][ORDER]) {\r\n // If any node is reordered, they will all need sequencing\r\n const orderedNodes = []\r\n const orderedMods = {}\r\n const orderedConfig = {}\r\n\r\n for (const n of keys) {\r\n const order = nodeMods[n][ORDER].order\r\n orderedNodes[order] = type.nodes[+n]\r\n orderedMods[order] = nodeMods[n]\r\n orderedNodes[order].index = order\r\n }\r\n\r\n // Rewrite links\r\n for (const l of type.links) {\r\n if (l[0] != null) l[0] = type.nodes[l[0]].index\r\n if (l[2] != null) l[2] = type.nodes[l[2]].index\r\n }\r\n\r\n // Rewrite externals\r\n if (type.external) {\r\n for (const ext of type.external) {\r\n ext[0] = type.nodes[ext[0]]\r\n }\r\n }\r\n\r\n // Rewrite modifications\r\n for (const id of keys) {\r\n if (config[id]) {\r\n orderedConfig[type.nodes[id].index] = config[id]\r\n }\r\n delete config[id]\r\n }\r\n\r\n type.nodes = orderedNodes\r\n nodeMods = orderedMods\r\n type.config = config = orderedConfig\r\n }\r\n\r\n merge(config, nodeMods)\r\n }\r\n\r\n types[g] = type\r\n\r\n if (!nodesByType) {\r\n // @ts-expect-error\r\n nodesByType = app.graph._nodes.reduce((p, n) => {\r\n p[n.type] ??= []\r\n p[n.type].push(n)\r\n return p\r\n }, {})\r\n }\r\n\r\n const nodes = nodesByType['workflow/' + g]\r\n if (nodes) recreateNodes.push(...nodes)\r\n }\r\n\r\n await GroupNodeConfig.registerFromWorkflow(types, {})\r\n\r\n for (const node of recreateNodes) {\r\n node.recreate()\r\n }\r\n\r\n this.modifications = {}\r\n this.app.graph.setDirtyCanvas(true, true)\r\n this.changeGroup(this.selectedGroup, false)\r\n }\r\n },\r\n 'Save'\r\n ),\r\n $el(\r\n 'button.comfy-btn',\r\n { onclick: () => this.element.close() },\r\n 'Close'\r\n )\r\n ])\r\n ])\r\n\r\n this.element.replaceChildren(outer)\r\n this.changeGroup(\r\n type ? groupNodes.find((g) => 'workflow/' + g === type) : groupNodes[0]\r\n )\r\n this.element.showModal()\r\n\r\n this.element.addEventListener('close', () => {\r\n this.draggable?.dispose()\r\n })\r\n }\r\n}\r\n","import { app } from '../../scripts/app'\r\nimport { api } from '../../scripts/api'\r\nimport { mergeIfValid } from './widgetInputs'\r\nimport { ManageGroupDialog } from './groupNodeManage'\r\nimport type { LGraphNode } from '@comfyorg/litegraph'\r\nimport { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph'\r\n\r\nconst GROUP = Symbol()\r\n\r\nconst Workflow = {\r\n InUse: {\r\n Free: 0,\r\n Registered: 1,\r\n InWorkflow: 2\r\n },\r\n isInUseGroupNode(name) {\r\n const id = `workflow/${name}`\r\n // Check if lready registered/in use in this workflow\r\n if (app.graph.extra?.groupNodes?.[name]) {\r\n // @ts-expect-error\r\n if (app.graph._nodes.find((n) => n.type === id)) {\r\n return Workflow.InUse.InWorkflow\r\n } else {\r\n return Workflow.InUse.Registered\r\n }\r\n }\r\n return Workflow.InUse.Free\r\n },\r\n storeGroupNode(name, data) {\r\n let extra = app.graph.extra\r\n if (!extra) app.graph.extra = extra = {}\r\n let groupNodes = extra.groupNodes\r\n if (!groupNodes) extra.groupNodes = groupNodes = {}\r\n groupNodes[name] = data\r\n }\r\n}\r\n\r\nclass GroupNodeBuilder {\r\n nodes: LGraphNode[]\r\n nodeData: any\r\n\r\n constructor(nodes) {\r\n this.nodes = nodes\r\n }\r\n\r\n build() {\r\n const name = this.getName()\r\n if (!name) return\r\n\r\n // Sort the nodes so they are in execution order\r\n // this allows for widgets to be in the correct order when reconstructing\r\n this.sortNodes()\r\n\r\n this.nodeData = this.getNodeData()\r\n Workflow.storeGroupNode(name, this.nodeData)\r\n\r\n return { name, nodeData: this.nodeData }\r\n }\r\n\r\n getName() {\r\n const name = prompt('Enter group name')\r\n if (!name) return\r\n const used = Workflow.isInUseGroupNode(name)\r\n switch (used) {\r\n case Workflow.InUse.InWorkflow:\r\n alert(\r\n 'An in use group node with this name already exists embedded in this workflow, please remove any instances or use a new name.'\r\n )\r\n return\r\n case Workflow.InUse.Registered:\r\n if (\r\n !confirm(\r\n 'A group node with this name already exists embedded in this workflow, are you sure you want to overwrite it?'\r\n )\r\n ) {\r\n return\r\n }\r\n break\r\n }\r\n return name\r\n }\r\n\r\n sortNodes() {\r\n // Gets the builders nodes in graph execution order\r\n const nodesInOrder = app.graph.computeExecutionOrder(false)\r\n this.nodes = this.nodes\r\n .map((node) => ({ index: nodesInOrder.indexOf(node), node }))\r\n .sort((a, b) => a.index - b.index || a.node.id - b.node.id)\r\n .map(({ node }) => node)\r\n }\r\n\r\n getNodeData() {\r\n const storeLinkTypes = (config) => {\r\n // Store link types for dynamically typed nodes e.g. reroutes\r\n for (const link of config.links) {\r\n const origin = app.graph.getNodeById(link[4])\r\n const type = origin.outputs[link[1]].type\r\n link.push(type)\r\n }\r\n }\r\n\r\n const storeExternalLinks = (config) => {\r\n // Store any external links to the group in the config so when rebuilding we add extra slots\r\n config.external = []\r\n for (let i = 0; i < this.nodes.length; i++) {\r\n const node = this.nodes[i]\r\n if (!node.outputs?.length) continue\r\n for (let slot = 0; slot < node.outputs.length; slot++) {\r\n let hasExternal = false\r\n const output = node.outputs[slot]\r\n let type = output.type\r\n if (!output.links?.length) continue\r\n for (const l of output.links) {\r\n const link = app.graph.links[l]\r\n if (!link) continue\r\n if (type === '*') type = link.type\r\n\r\n if (!app.canvas.selected_nodes[link.target_id]) {\r\n hasExternal = true\r\n break\r\n }\r\n }\r\n if (hasExternal) {\r\n config.external.push([i, slot, type])\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Use the built in copyToClipboard function to generate the node data we need\r\n const backup = localStorage.getItem('litegrapheditor_clipboard')\r\n try {\r\n // @ts-expect-error\r\n // TODO Figure out if copyToClipboard is really taking this param\r\n app.canvas.copyToClipboard(this.nodes)\r\n const config = JSON.parse(\r\n localStorage.getItem('litegrapheditor_clipboard')\r\n )\r\n\r\n storeLinkTypes(config)\r\n storeExternalLinks(config)\r\n\r\n return config\r\n } finally {\r\n localStorage.setItem('litegrapheditor_clipboard', backup)\r\n }\r\n }\r\n}\r\n\r\nexport class GroupNodeConfig {\r\n name: string\r\n nodeData: any\r\n inputCount: number\r\n oldToNewOutputMap: {}\r\n newToOldOutputMap: {}\r\n oldToNewInputMap: {}\r\n oldToNewWidgetMap: {}\r\n newToOldWidgetMap: {}\r\n primitiveDefs: {}\r\n widgetToPrimitive: {}\r\n primitiveToWidget: {}\r\n nodeInputs: {}\r\n outputVisibility: any[]\r\n nodeDef: any\r\n inputs: any[]\r\n linksFrom: {}\r\n linksTo: {}\r\n externalFrom: {}\r\n\r\n constructor(name, nodeData) {\r\n this.name = name\r\n this.nodeData = nodeData\r\n this.getLinks()\r\n\r\n this.inputCount = 0\r\n this.oldToNewOutputMap = {}\r\n this.newToOldOutputMap = {}\r\n this.oldToNewInputMap = {}\r\n this.oldToNewWidgetMap = {}\r\n this.newToOldWidgetMap = {}\r\n this.primitiveDefs = {}\r\n this.widgetToPrimitive = {}\r\n this.primitiveToWidget = {}\r\n this.nodeInputs = {}\r\n this.outputVisibility = []\r\n }\r\n\r\n async registerType(source = 'workflow') {\r\n this.nodeDef = {\r\n output: [],\r\n output_name: [],\r\n output_is_list: [],\r\n output_is_hidden: [],\r\n name: source + '/' + this.name,\r\n display_name: this.name,\r\n category: 'group nodes' + ('/' + source),\r\n input: { required: {} },\r\n\r\n [GROUP]: this\r\n }\r\n\r\n this.inputs = []\r\n const seenInputs = {}\r\n const seenOutputs = {}\r\n for (let i = 0; i < this.nodeData.nodes.length; i++) {\r\n const node = this.nodeData.nodes[i]\r\n node.index = i\r\n this.processNode(node, seenInputs, seenOutputs)\r\n }\r\n\r\n for (const p of this.#convertedToProcess) {\r\n p()\r\n }\r\n this.#convertedToProcess = null\r\n await app.registerNodeDef('workflow/' + this.name, this.nodeDef)\r\n }\r\n\r\n getLinks() {\r\n this.linksFrom = {}\r\n this.linksTo = {}\r\n this.externalFrom = {}\r\n\r\n // Extract links for easy lookup\r\n for (const l of this.nodeData.links) {\r\n const [sourceNodeId, sourceNodeSlot, targetNodeId, targetNodeSlot] = l\r\n\r\n // Skip links outside the copy config\r\n if (sourceNodeId == null) continue\r\n\r\n if (!this.linksFrom[sourceNodeId]) {\r\n this.linksFrom[sourceNodeId] = {}\r\n }\r\n if (!this.linksFrom[sourceNodeId][sourceNodeSlot]) {\r\n this.linksFrom[sourceNodeId][sourceNodeSlot] = []\r\n }\r\n this.linksFrom[sourceNodeId][sourceNodeSlot].push(l)\r\n\r\n if (!this.linksTo[targetNodeId]) {\r\n this.linksTo[targetNodeId] = {}\r\n }\r\n this.linksTo[targetNodeId][targetNodeSlot] = l\r\n }\r\n\r\n if (this.nodeData.external) {\r\n for (const ext of this.nodeData.external) {\r\n if (!this.externalFrom[ext[0]]) {\r\n this.externalFrom[ext[0]] = { [ext[1]]: ext[2] }\r\n } else {\r\n this.externalFrom[ext[0]][ext[1]] = ext[2]\r\n }\r\n }\r\n }\r\n }\r\n\r\n processNode(node, seenInputs, seenOutputs) {\r\n const def = this.getNodeDef(node)\r\n if (!def) return\r\n\r\n const inputs = { ...def.input?.required, ...def.input?.optional }\r\n\r\n this.inputs.push(this.processNodeInputs(node, seenInputs, inputs))\r\n if (def.output?.length) this.processNodeOutputs(node, seenOutputs, def)\r\n }\r\n\r\n getNodeDef(node) {\r\n const def = globalDefs[node.type]\r\n if (def) return def\r\n\r\n const linksFrom = this.linksFrom[node.index]\r\n if (node.type === 'PrimitiveNode') {\r\n // Skip as its not linked\r\n if (!linksFrom) return\r\n\r\n let type = linksFrom['0'][0][5]\r\n if (type === 'COMBO') {\r\n // Use the array items\r\n const source = node.outputs[0].widget.name\r\n const fromTypeName = this.nodeData.nodes[linksFrom['0'][0][2]].type\r\n const fromType = globalDefs[fromTypeName]\r\n const input =\r\n fromType.input.required[source] ?? fromType.input.optional[source]\r\n type = input[0]\r\n }\r\n\r\n const def = (this.primitiveDefs[node.index] = {\r\n input: {\r\n required: {\r\n value: [type, {}]\r\n }\r\n },\r\n output: [type],\r\n output_name: [],\r\n output_is_list: []\r\n })\r\n return def\r\n } else if (node.type === 'Reroute') {\r\n const linksTo = this.linksTo[node.index]\r\n if (linksTo && linksFrom && !this.externalFrom[node.index]?.[0]) {\r\n // Being used internally\r\n return null\r\n }\r\n\r\n let config = {}\r\n let rerouteType = '*'\r\n if (linksFrom) {\r\n for (const [, , id, slot] of linksFrom['0']) {\r\n const node = this.nodeData.nodes[id]\r\n const input = node.inputs[slot]\r\n if (rerouteType === '*') {\r\n rerouteType = input.type\r\n }\r\n if (input.widget) {\r\n const targetDef = globalDefs[node.type]\r\n const targetWidget =\r\n targetDef.input.required[input.widget.name] ??\r\n targetDef.input.optional[input.widget.name]\r\n\r\n const widget = [targetWidget[0], config]\r\n const res = mergeIfValid(\r\n {\r\n widget\r\n },\r\n targetWidget,\r\n false,\r\n null,\r\n widget\r\n )\r\n config = res?.customConfig ?? config\r\n }\r\n }\r\n } else if (linksTo) {\r\n const [id, slot] = linksTo['0']\r\n rerouteType = this.nodeData.nodes[id].outputs[slot].type\r\n } else {\r\n // Reroute used as a pipe\r\n for (const l of this.nodeData.links) {\r\n if (l[2] === node.index) {\r\n rerouteType = l[5]\r\n break\r\n }\r\n }\r\n if (rerouteType === '*') {\r\n // Check for an external link\r\n const t = this.externalFrom[node.index]?.[0]\r\n if (t) {\r\n rerouteType = t\r\n }\r\n }\r\n }\r\n\r\n // @ts-expect-error\r\n config.forceInput = true\r\n return {\r\n input: {\r\n required: {\r\n [rerouteType]: [rerouteType, config]\r\n }\r\n },\r\n output: [rerouteType],\r\n output_name: [],\r\n output_is_list: []\r\n }\r\n }\r\n\r\n console.warn(\r\n 'Skipping virtual node ' +\r\n node.type +\r\n ' when building group node ' +\r\n this.name\r\n )\r\n }\r\n\r\n getInputConfig(node, inputName, seenInputs, config, extra?) {\r\n const customConfig = this.nodeData.config?.[node.index]?.input?.[inputName]\r\n let name =\r\n customConfig?.name ??\r\n node.inputs?.find((inp) => inp.name === inputName)?.label ??\r\n inputName\r\n let key = name\r\n let prefix = ''\r\n // Special handling for primitive to include the title if it is set rather than just \"value\"\r\n if ((node.type === 'PrimitiveNode' && node.title) || name in seenInputs) {\r\n prefix = `${node.title ?? node.type} `\r\n key = name = `${prefix}${inputName}`\r\n if (name in seenInputs) {\r\n name = `${prefix}${seenInputs[name]} ${inputName}`\r\n }\r\n }\r\n seenInputs[key] = (seenInputs[key] ?? 1) + 1\r\n\r\n if (inputName === 'seed' || inputName === 'noise_seed') {\r\n if (!extra) extra = {}\r\n extra.control_after_generate = `${prefix}control_after_generate`\r\n }\r\n if (config[0] === 'IMAGEUPLOAD') {\r\n if (!extra) extra = {}\r\n extra.widget =\r\n this.oldToNewWidgetMap[node.index]?.[config[1]?.widget ?? 'image'] ??\r\n 'image'\r\n }\r\n\r\n if (extra) {\r\n config = [config[0], { ...config[1], ...extra }]\r\n }\r\n\r\n return { name, config, customConfig }\r\n }\r\n\r\n processWidgetInputs(inputs, node, inputNames, seenInputs) {\r\n const slots = []\r\n const converted = new Map()\r\n const widgetMap = (this.oldToNewWidgetMap[node.index] = {})\r\n for (const inputName of inputNames) {\r\n let widgetType = app.getWidgetType(inputs[inputName], inputName)\r\n if (widgetType) {\r\n const convertedIndex = node.inputs?.findIndex(\r\n (inp) => inp.name === inputName && inp.widget?.name === inputName\r\n )\r\n if (convertedIndex > -1) {\r\n // This widget has been converted to a widget\r\n // We need to store this in the correct position so link ids line up\r\n converted.set(convertedIndex, inputName)\r\n widgetMap[inputName] = null\r\n } else {\r\n // Normal widget\r\n const { name, config } = this.getInputConfig(\r\n node,\r\n inputName,\r\n seenInputs,\r\n inputs[inputName]\r\n )\r\n this.nodeDef.input.required[name] = config\r\n widgetMap[inputName] = name\r\n this.newToOldWidgetMap[name] = { node, inputName }\r\n }\r\n } else {\r\n // Normal input\r\n slots.push(inputName)\r\n }\r\n }\r\n return { converted, slots }\r\n }\r\n\r\n checkPrimitiveConnection(link, inputName, inputs) {\r\n const sourceNode = this.nodeData.nodes[link[0]]\r\n if (sourceNode.type === 'PrimitiveNode') {\r\n // Merge link configurations\r\n const [sourceNodeId, _, targetNodeId, __] = link\r\n const primitiveDef = this.primitiveDefs[sourceNodeId]\r\n const targetWidget = inputs[inputName]\r\n const primitiveConfig = primitiveDef.input.required.value\r\n const output = { widget: primitiveConfig }\r\n const config = mergeIfValid(\r\n output,\r\n targetWidget,\r\n false,\r\n null,\r\n primitiveConfig\r\n )\r\n primitiveConfig[1] =\r\n config?.customConfig ?? inputs[inputName][1]\r\n ? { ...inputs[inputName][1] }\r\n : {}\r\n\r\n let name = this.oldToNewWidgetMap[sourceNodeId]['value']\r\n name = name.substr(0, name.length - 6)\r\n primitiveConfig[1].control_after_generate = true\r\n primitiveConfig[1].control_prefix = name\r\n\r\n let toPrimitive = this.widgetToPrimitive[targetNodeId]\r\n if (!toPrimitive) {\r\n toPrimitive = this.widgetToPrimitive[targetNodeId] = {}\r\n }\r\n if (toPrimitive[inputName]) {\r\n toPrimitive[inputName].push(sourceNodeId)\r\n }\r\n toPrimitive[inputName] = sourceNodeId\r\n\r\n let toWidget = this.primitiveToWidget[sourceNodeId]\r\n if (!toWidget) {\r\n toWidget = this.primitiveToWidget[sourceNodeId] = []\r\n }\r\n toWidget.push({ nodeId: targetNodeId, inputName })\r\n }\r\n }\r\n\r\n processInputSlots(inputs, node, slots, linksTo, inputMap, seenInputs) {\r\n this.nodeInputs[node.index] = {}\r\n for (let i = 0; i < slots.length; i++) {\r\n const inputName = slots[i]\r\n if (linksTo[i]) {\r\n this.checkPrimitiveConnection(linksTo[i], inputName, inputs)\r\n // This input is linked so we can skip it\r\n continue\r\n }\r\n\r\n const { name, config, customConfig } = this.getInputConfig(\r\n node,\r\n inputName,\r\n seenInputs,\r\n inputs[inputName]\r\n )\r\n\r\n this.nodeInputs[node.index][inputName] = name\r\n if (customConfig?.visible === false) continue\r\n\r\n this.nodeDef.input.required[name] = config\r\n inputMap[i] = this.inputCount++\r\n }\r\n }\r\n\r\n processConvertedWidgets(\r\n inputs,\r\n node,\r\n slots,\r\n converted,\r\n linksTo,\r\n inputMap,\r\n seenInputs\r\n ) {\r\n // Add converted widgets sorted into their index order (ordered as they were converted) so link ids match up\r\n const convertedSlots = [...converted.keys()]\r\n .sort()\r\n .map((k) => converted.get(k))\r\n for (let i = 0; i < convertedSlots.length; i++) {\r\n const inputName = convertedSlots[i]\r\n if (linksTo[slots.length + i]) {\r\n this.checkPrimitiveConnection(\r\n linksTo[slots.length + i],\r\n inputName,\r\n inputs\r\n )\r\n // This input is linked so we can skip it\r\n continue\r\n }\r\n\r\n const { name, config } = this.getInputConfig(\r\n node,\r\n inputName,\r\n seenInputs,\r\n inputs[inputName],\r\n {\r\n defaultInput: true\r\n }\r\n )\r\n\r\n this.nodeDef.input.required[name] = config\r\n this.newToOldWidgetMap[name] = { node, inputName }\r\n\r\n if (!this.oldToNewWidgetMap[node.index]) {\r\n this.oldToNewWidgetMap[node.index] = {}\r\n }\r\n this.oldToNewWidgetMap[node.index][inputName] = name\r\n\r\n inputMap[slots.length + i] = this.inputCount++\r\n }\r\n }\r\n\r\n #convertedToProcess = []\r\n processNodeInputs(node, seenInputs, inputs) {\r\n const inputMapping = []\r\n\r\n const inputNames = Object.keys(inputs)\r\n if (!inputNames.length) return\r\n\r\n const { converted, slots } = this.processWidgetInputs(\r\n inputs,\r\n node,\r\n inputNames,\r\n seenInputs\r\n )\r\n const linksTo = this.linksTo[node.index] ?? {}\r\n const inputMap = (this.oldToNewInputMap[node.index] = {})\r\n this.processInputSlots(inputs, node, slots, linksTo, inputMap, seenInputs)\r\n\r\n // Converted inputs have to be processed after all other nodes as they'll be at the end of the list\r\n this.#convertedToProcess.push(() =>\r\n this.processConvertedWidgets(\r\n inputs,\r\n node,\r\n slots,\r\n converted,\r\n linksTo,\r\n inputMap,\r\n seenInputs\r\n )\r\n )\r\n\r\n return inputMapping\r\n }\r\n\r\n processNodeOutputs(node, seenOutputs, def) {\r\n const oldToNew = (this.oldToNewOutputMap[node.index] = {})\r\n\r\n // Add outputs\r\n for (let outputId = 0; outputId < def.output.length; outputId++) {\r\n const linksFrom = this.linksFrom[node.index]\r\n // If this output is linked internally we flag it to hide\r\n const hasLink =\r\n linksFrom?.[outputId] && !this.externalFrom[node.index]?.[outputId]\r\n const customConfig =\r\n this.nodeData.config?.[node.index]?.output?.[outputId]\r\n const visible = customConfig?.visible ?? !hasLink\r\n this.outputVisibility.push(visible)\r\n if (!visible) {\r\n continue\r\n }\r\n\r\n oldToNew[outputId] = this.nodeDef.output.length\r\n this.newToOldOutputMap[this.nodeDef.output.length] = {\r\n node,\r\n slot: outputId\r\n }\r\n this.nodeDef.output.push(def.output[outputId])\r\n this.nodeDef.output_is_list.push(def.output_is_list[outputId])\r\n\r\n let label = customConfig?.name\r\n if (!label) {\r\n label = def.output_name?.[outputId] ?? def.output[outputId]\r\n const output = node.outputs.find((o) => o.name === label)\r\n if (output?.label) {\r\n label = output.label\r\n }\r\n }\r\n\r\n let name = label\r\n if (name in seenOutputs) {\r\n const prefix = `${node.title ?? node.type} `\r\n name = `${prefix}${label}`\r\n if (name in seenOutputs) {\r\n name = `${prefix}${node.index} ${label}`\r\n }\r\n }\r\n seenOutputs[name] = 1\r\n\r\n this.nodeDef.output_name.push(name)\r\n }\r\n }\r\n\r\n static async registerFromWorkflow(groupNodes, missingNodeTypes) {\r\n const clean = app.clean\r\n app.clean = function () {\r\n for (const g in groupNodes) {\r\n try {\r\n LiteGraph.unregisterNodeType('workflow/' + g)\r\n } catch (error) {}\r\n }\r\n app.clean = clean\r\n }\r\n\r\n for (const g in groupNodes) {\r\n const groupData = groupNodes[g]\r\n\r\n let hasMissing = false\r\n for (const n of groupData.nodes) {\r\n // Find missing node types\r\n if (!(n.type in LiteGraph.registered_node_types)) {\r\n missingNodeTypes.push({\r\n type: n.type,\r\n hint: ` (In group node 'workflow/${g}')`\r\n })\r\n\r\n missingNodeTypes.push({\r\n type: 'workflow/' + g,\r\n action: {\r\n text: 'Remove from workflow',\r\n callback: (e) => {\r\n delete groupNodes[g]\r\n e.target.textContent = 'Removed'\r\n e.target.style.pointerEvents = 'none'\r\n e.target.style.opacity = 0.7\r\n }\r\n }\r\n })\r\n\r\n hasMissing = true\r\n }\r\n }\r\n\r\n if (hasMissing) continue\r\n\r\n const config = new GroupNodeConfig(g, groupData)\r\n await config.registerType()\r\n }\r\n }\r\n}\r\n\r\nexport class GroupNodeHandler {\r\n node\r\n groupData\r\n innerNodes: any\r\n\r\n constructor(node) {\r\n this.node = node\r\n this.groupData = node.constructor?.nodeData?.[GROUP]\r\n\r\n this.node.setInnerNodes = (innerNodes) => {\r\n this.innerNodes = innerNodes\r\n\r\n for (\r\n let innerNodeIndex = 0;\r\n innerNodeIndex < this.innerNodes.length;\r\n innerNodeIndex++\r\n ) {\r\n const innerNode = this.innerNodes[innerNodeIndex]\r\n\r\n for (const w of innerNode.widgets ?? []) {\r\n if (w.type === 'converted-widget') {\r\n w.serializeValue = w.origSerializeValue\r\n }\r\n }\r\n\r\n innerNode.index = innerNodeIndex\r\n innerNode.getInputNode = (slot) => {\r\n // Check if this input is internal or external\r\n const externalSlot =\r\n this.groupData.oldToNewInputMap[innerNode.index]?.[slot]\r\n if (externalSlot != null) {\r\n return this.node.getInputNode(externalSlot)\r\n }\r\n\r\n // Internal link\r\n const innerLink = this.groupData.linksTo[innerNode.index]?.[slot]\r\n if (!innerLink) return null\r\n\r\n const inputNode = innerNodes[innerLink[0]]\r\n // Primitives will already apply their values\r\n if (inputNode.type === 'PrimitiveNode') return null\r\n\r\n return inputNode\r\n }\r\n\r\n innerNode.getInputLink = (slot) => {\r\n const externalSlot =\r\n this.groupData.oldToNewInputMap[innerNode.index]?.[slot]\r\n if (externalSlot != null) {\r\n // The inner node is connected via the group node inputs\r\n const linkId = this.node.inputs[externalSlot].link\r\n let link = app.graph.links[linkId]\r\n\r\n // Use the outer link, but update the target to the inner node\r\n // @ts-expect-error\r\n // TODO: Fix this\r\n link = {\r\n ...link,\r\n target_id: innerNode.id,\r\n target_slot: +slot\r\n }\r\n return link\r\n }\r\n\r\n let link = this.groupData.linksTo[innerNode.index]?.[slot]\r\n if (!link) return null\r\n // Use the inner link, but update the origin node to be inner node id\r\n link = {\r\n origin_id: innerNodes[link[0]].id,\r\n origin_slot: link[1],\r\n target_id: innerNode.id,\r\n target_slot: +slot\r\n }\r\n return link\r\n }\r\n }\r\n }\r\n\r\n this.node.updateLink = (link) => {\r\n // Replace the group node reference with the internal node\r\n link = { ...link }\r\n const output = this.groupData.newToOldOutputMap[link.origin_slot]\r\n let innerNode = this.innerNodes[output.node.index]\r\n let l\r\n while (innerNode?.type === 'Reroute') {\r\n l = innerNode.getInputLink(0)\r\n innerNode = innerNode.getInputNode(0)\r\n }\r\n\r\n if (!innerNode) {\r\n return null\r\n }\r\n\r\n if (l && GroupNodeHandler.isGroupNode(innerNode)) {\r\n return innerNode.updateLink(l)\r\n }\r\n\r\n link.origin_id = innerNode.id\r\n link.origin_slot = l?.origin_slot ?? output.slot\r\n return link\r\n }\r\n\r\n this.node.getInnerNodes = () => {\r\n if (!this.innerNodes) {\r\n this.node.setInnerNodes(\r\n this.groupData.nodeData.nodes.map((n, i) => {\r\n const innerNode = LiteGraph.createNode(n.type)\r\n innerNode.configure(n)\r\n // @ts-expect-error\r\n innerNode.id = `${this.node.id}:${i}`\r\n return innerNode\r\n })\r\n )\r\n }\r\n\r\n this.updateInnerWidgets()\r\n\r\n return this.innerNodes\r\n }\r\n\r\n this.node.recreate = async () => {\r\n const id = this.node.id\r\n const sz = this.node.size\r\n const nodes = this.node.convertToNodes()\r\n\r\n const groupNode = LiteGraph.createNode(this.node.type)\r\n groupNode.id = id\r\n\r\n // Reuse the existing nodes for this instance\r\n groupNode.setInnerNodes(nodes)\r\n groupNode[GROUP].populateWidgets()\r\n app.graph.add(groupNode)\r\n groupNode.size = [\r\n Math.max(groupNode.size[0], sz[0]),\r\n Math.max(groupNode.size[1], sz[1])\r\n ]\r\n\r\n // Remove all converted nodes and relink them\r\n groupNode[GROUP].replaceNodes(nodes)\r\n return groupNode\r\n }\r\n\r\n this.node.convertToNodes = () => {\r\n const addInnerNodes = () => {\r\n const backup = localStorage.getItem('litegrapheditor_clipboard')\r\n // Clone the node data so we dont mutate it for other nodes\r\n const c = { ...this.groupData.nodeData }\r\n c.nodes = [...c.nodes]\r\n const innerNodes = this.node.getInnerNodes()\r\n let ids = []\r\n for (let i = 0; i < c.nodes.length; i++) {\r\n let id = innerNodes?.[i]?.id\r\n // Use existing IDs if they are set on the inner nodes\r\n if (id == null || isNaN(id)) {\r\n id = undefined\r\n } else {\r\n ids.push(id)\r\n }\r\n c.nodes[i] = { ...c.nodes[i], id }\r\n }\r\n localStorage.setItem('litegrapheditor_clipboard', JSON.stringify(c))\r\n app.canvas.pasteFromClipboard()\r\n localStorage.setItem('litegrapheditor_clipboard', backup)\r\n\r\n const [x, y] = this.node.pos\r\n let top\r\n let left\r\n // Configure nodes with current widget data\r\n const selectedIds = ids.length\r\n ? ids\r\n : Object.keys(app.canvas.selected_nodes)\r\n const newNodes = []\r\n for (let i = 0; i < selectedIds.length; i++) {\r\n const id = selectedIds[i]\r\n const newNode = app.graph.getNodeById(id)\r\n const innerNode = innerNodes[i]\r\n newNodes.push(newNode)\r\n\r\n if (left == null || newNode.pos[0] < left) {\r\n left = newNode.pos[0]\r\n }\r\n if (top == null || newNode.pos[1] < top) {\r\n top = newNode.pos[1]\r\n }\r\n\r\n if (!newNode.widgets) continue\r\n\r\n const map = this.groupData.oldToNewWidgetMap[innerNode.index]\r\n if (map) {\r\n const widgets = Object.keys(map)\r\n\r\n for (const oldName of widgets) {\r\n const newName = map[oldName]\r\n if (!newName) continue\r\n\r\n const widgetIndex = this.node.widgets.findIndex(\r\n (w) => w.name === newName\r\n )\r\n if (widgetIndex === -1) continue\r\n\r\n // Populate the main and any linked widgets\r\n if (innerNode.type === 'PrimitiveNode') {\r\n for (let i = 0; i < newNode.widgets.length; i++) {\r\n newNode.widgets[i].value =\r\n this.node.widgets[widgetIndex + i].value\r\n }\r\n } else {\r\n const outerWidget = this.node.widgets[widgetIndex]\r\n const newWidget = newNode.widgets.find(\r\n (w) => w.name === oldName\r\n )\r\n if (!newWidget) continue\r\n\r\n newWidget.value = outerWidget.value\r\n for (let w = 0; w < outerWidget.linkedWidgets?.length; w++) {\r\n newWidget.linkedWidgets[w].value =\r\n outerWidget.linkedWidgets[w].value\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Shift each node\r\n for (const newNode of newNodes) {\r\n newNode.pos = [\r\n newNode.pos[0] - (left - x),\r\n newNode.pos[1] - (top - y)\r\n ]\r\n }\r\n\r\n return { newNodes, selectedIds }\r\n }\r\n\r\n const reconnectInputs = (selectedIds) => {\r\n for (const innerNodeIndex in this.groupData.oldToNewInputMap) {\r\n const id = selectedIds[innerNodeIndex]\r\n const newNode = app.graph.getNodeById(id)\r\n const map = this.groupData.oldToNewInputMap[innerNodeIndex]\r\n for (const innerInputId in map) {\r\n const groupSlotId = map[innerInputId]\r\n if (groupSlotId == null) continue\r\n const slot = node.inputs[groupSlotId]\r\n if (slot.link == null) continue\r\n const link = app.graph.links[slot.link]\r\n if (!link) continue\r\n // connect this node output to the input of another node\r\n const originNode = app.graph.getNodeById(link.origin_id)\r\n originNode.connect(link.origin_slot, newNode, +innerInputId)\r\n }\r\n }\r\n }\r\n\r\n const reconnectOutputs = (selectedIds) => {\r\n for (\r\n let groupOutputId = 0;\r\n groupOutputId < node.outputs?.length;\r\n groupOutputId++\r\n ) {\r\n const output = node.outputs[groupOutputId]\r\n if (!output.links) continue\r\n const links = [...output.links]\r\n for (const l of links) {\r\n const slot = this.groupData.newToOldOutputMap[groupOutputId]\r\n const link = app.graph.links[l]\r\n const targetNode = app.graph.getNodeById(link.target_id)\r\n const newNode = app.graph.getNodeById(selectedIds[slot.node.index])\r\n newNode.connect(slot.slot, targetNode, link.target_slot)\r\n }\r\n }\r\n }\r\n\r\n const { newNodes, selectedIds } = addInnerNodes()\r\n reconnectInputs(selectedIds)\r\n reconnectOutputs(selectedIds)\r\n app.graph.remove(this.node)\r\n\r\n return newNodes\r\n }\r\n\r\n const getExtraMenuOptions = this.node.getExtraMenuOptions\r\n this.node.getExtraMenuOptions = function (_, options) {\r\n getExtraMenuOptions?.apply(this, arguments)\r\n\r\n let optionIndex = options.findIndex((o) => o.content === 'Outputs')\r\n if (optionIndex === -1) optionIndex = options.length\r\n else optionIndex++\r\n options.splice(\r\n optionIndex,\r\n 0,\r\n null,\r\n {\r\n content: 'Convert to nodes',\r\n callback: () => {\r\n return this.convertToNodes()\r\n }\r\n },\r\n {\r\n content: 'Manage Group Node',\r\n callback: () => {\r\n new ManageGroupDialog(app).show(this.type)\r\n }\r\n }\r\n )\r\n }\r\n\r\n // Draw custom collapse icon to identity this as a group\r\n const onDrawTitleBox = this.node.onDrawTitleBox\r\n this.node.onDrawTitleBox = function (ctx, height, size, scale) {\r\n onDrawTitleBox?.apply(this, arguments)\r\n\r\n const fill = ctx.fillStyle\r\n ctx.beginPath()\r\n ctx.rect(11, -height + 11, 2, 2)\r\n ctx.rect(14, -height + 11, 2, 2)\r\n ctx.rect(17, -height + 11, 2, 2)\r\n ctx.rect(11, -height + 14, 2, 2)\r\n ctx.rect(14, -height + 14, 2, 2)\r\n ctx.rect(17, -height + 14, 2, 2)\r\n ctx.rect(11, -height + 17, 2, 2)\r\n ctx.rect(14, -height + 17, 2, 2)\r\n ctx.rect(17, -height + 17, 2, 2)\r\n\r\n ctx.fillStyle = this.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR\r\n ctx.fill()\r\n ctx.fillStyle = fill\r\n }\r\n\r\n // Draw progress label\r\n const onDrawForeground = node.onDrawForeground\r\n const groupData = this.groupData.nodeData\r\n node.onDrawForeground = function (ctx) {\r\n const r = onDrawForeground?.apply?.(this, arguments)\r\n if (\r\n +app.runningNodeId === this.id &&\r\n this.runningInternalNodeId !== null\r\n ) {\r\n const n = groupData.nodes[this.runningInternalNodeId]\r\n if (!n) return\r\n const message = `Running ${n.title || n.type} (${this.runningInternalNodeId}/${groupData.nodes.length})`\r\n ctx.save()\r\n ctx.font = '12px sans-serif'\r\n const sz = ctx.measureText(message)\r\n ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR\r\n ctx.beginPath()\r\n ctx.roundRect(\r\n 0,\r\n -LiteGraph.NODE_TITLE_HEIGHT - 20,\r\n sz.width + 12,\r\n 20,\r\n 5\r\n )\r\n ctx.fill()\r\n\r\n ctx.fillStyle = '#fff'\r\n ctx.fillText(message, 6, -LiteGraph.NODE_TITLE_HEIGHT - 6)\r\n ctx.restore()\r\n }\r\n }\r\n\r\n // Flag this node as needing to be reset\r\n const onExecutionStart = this.node.onExecutionStart\r\n this.node.onExecutionStart = function () {\r\n this.resetExecution = true\r\n return onExecutionStart?.apply(this, arguments)\r\n }\r\n\r\n const self = this\r\n const onNodeCreated = this.node.onNodeCreated\r\n this.node.onNodeCreated = function () {\r\n if (!this.widgets) {\r\n return\r\n }\r\n const config = self.groupData.nodeData.config\r\n if (config) {\r\n for (const n in config) {\r\n const inputs = config[n]?.input\r\n for (const w in inputs) {\r\n if (inputs[w].visible !== false) continue\r\n const widgetName = self.groupData.oldToNewWidgetMap[n][w]\r\n const widget = this.widgets.find((w) => w.name === widgetName)\r\n if (widget) {\r\n widget.type = 'hidden'\r\n widget.computeSize = () => [0, -4]\r\n }\r\n }\r\n }\r\n }\r\n\r\n return onNodeCreated?.apply(this, arguments)\r\n }\r\n\r\n function handleEvent(type, getId, getEvent) {\r\n const handler = ({ detail }) => {\r\n const id = getId(detail)\r\n if (!id) return\r\n const node = app.graph.getNodeById(id)\r\n if (node) return\r\n\r\n const innerNodeIndex = this.innerNodes?.findIndex((n) => n.id == id)\r\n if (innerNodeIndex > -1) {\r\n this.node.runningInternalNodeId = innerNodeIndex\r\n api.dispatchEvent(\r\n new CustomEvent(type, {\r\n detail: getEvent(detail, this.node.id + '', this.node)\r\n })\r\n )\r\n }\r\n }\r\n api.addEventListener(type, handler)\r\n return handler\r\n }\r\n\r\n const executing = handleEvent.call(\r\n this,\r\n 'executing',\r\n (d) => d,\r\n (d, id, node) => id\r\n )\r\n\r\n const executed = handleEvent.call(\r\n this,\r\n 'executed',\r\n (d) => d?.display_node || d?.node,\r\n (d, id, node) => ({\r\n ...d,\r\n node: id,\r\n display_node: id,\r\n merge: !node.resetExecution\r\n })\r\n )\r\n\r\n const onRemoved = node.onRemoved\r\n this.node.onRemoved = function () {\r\n onRemoved?.apply(this, arguments)\r\n api.removeEventListener('executing', executing)\r\n api.removeEventListener('executed', executed)\r\n }\r\n\r\n this.node.refreshComboInNode = (defs) => {\r\n // Update combo widget options\r\n for (const widgetName in this.groupData.newToOldWidgetMap) {\r\n const widget = this.node.widgets.find((w) => w.name === widgetName)\r\n if (widget?.type === 'combo') {\r\n const old = this.groupData.newToOldWidgetMap[widgetName]\r\n const def = defs[old.node.type]\r\n const input =\r\n def?.input?.required?.[old.inputName] ??\r\n def?.input?.optional?.[old.inputName]\r\n if (!input) continue\r\n\r\n widget.options.values = input[0]\r\n\r\n if (\r\n old.inputName !== 'image' &&\r\n !widget.options.values.includes(widget.value)\r\n ) {\r\n widget.value = widget.options.values[0]\r\n widget.callback(widget.value)\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n updateInnerWidgets() {\r\n for (const newWidgetName in this.groupData.newToOldWidgetMap) {\r\n const newWidget = this.node.widgets.find((w) => w.name === newWidgetName)\r\n if (!newWidget) continue\r\n\r\n const newValue = newWidget.value\r\n const old = this.groupData.newToOldWidgetMap[newWidgetName]\r\n let innerNode = this.innerNodes[old.node.index]\r\n\r\n if (innerNode.type === 'PrimitiveNode') {\r\n innerNode.primitiveValue = newValue\r\n const primitiveLinked = this.groupData.primitiveToWidget[old.node.index]\r\n for (const linked of primitiveLinked ?? []) {\r\n const node = this.innerNodes[linked.nodeId]\r\n const widget = node.widgets.find((w) => w.name === linked.inputName)\r\n\r\n if (widget) {\r\n widget.value = newValue\r\n }\r\n }\r\n continue\r\n } else if (innerNode.type === 'Reroute') {\r\n const rerouteLinks = this.groupData.linksFrom[old.node.index]\r\n if (rerouteLinks) {\r\n for (const [_, , targetNodeId, targetSlot] of rerouteLinks['0']) {\r\n const node = this.innerNodes[targetNodeId]\r\n const input = node.inputs[targetSlot]\r\n if (input.widget) {\r\n const widget = node.widgets?.find(\r\n (w) => w.name === input.widget.name\r\n )\r\n if (widget) {\r\n widget.value = newValue\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n const widget = innerNode.widgets?.find((w) => w.name === old.inputName)\r\n if (widget) {\r\n widget.value = newValue\r\n }\r\n }\r\n }\r\n\r\n populatePrimitive(node, nodeId, oldName, i, linkedShift) {\r\n // Converted widget, populate primitive if linked\r\n const primitiveId = this.groupData.widgetToPrimitive[nodeId]?.[oldName]\r\n if (primitiveId == null) return\r\n const targetWidgetName =\r\n this.groupData.oldToNewWidgetMap[primitiveId]['value']\r\n const targetWidgetIndex = this.node.widgets.findIndex(\r\n (w) => w.name === targetWidgetName\r\n )\r\n if (targetWidgetIndex > -1) {\r\n const primitiveNode = this.innerNodes[primitiveId]\r\n let len = primitiveNode.widgets.length\r\n if (\r\n len - 1 !==\r\n this.node.widgets[targetWidgetIndex].linkedWidgets?.length\r\n ) {\r\n // Fallback handling for if some reason the primitive has a different number of widgets\r\n // we dont want to overwrite random widgets, better to leave blank\r\n len = 1\r\n }\r\n for (let i = 0; i < len; i++) {\r\n this.node.widgets[targetWidgetIndex + i].value =\r\n primitiveNode.widgets[i].value\r\n }\r\n }\r\n return true\r\n }\r\n\r\n populateReroute(node, nodeId, map) {\r\n if (node.type !== 'Reroute') return\r\n\r\n const link = this.groupData.linksFrom[nodeId]?.[0]?.[0]\r\n if (!link) return\r\n const [, , targetNodeId, targetNodeSlot] = link\r\n const targetNode = this.groupData.nodeData.nodes[targetNodeId]\r\n const inputs = targetNode.inputs\r\n const targetWidget = inputs?.[targetNodeSlot]?.widget\r\n if (!targetWidget) return\r\n\r\n const offset = inputs.length - (targetNode.widgets_values?.length ?? 0)\r\n const v = targetNode.widgets_values?.[targetNodeSlot - offset]\r\n if (v == null) return\r\n\r\n const widgetName = Object.values(map)[0]\r\n const widget = this.node.widgets.find((w) => w.name === widgetName)\r\n if (widget) {\r\n widget.value = v\r\n }\r\n }\r\n\r\n populateWidgets() {\r\n if (!this.node.widgets) return\r\n\r\n for (\r\n let nodeId = 0;\r\n nodeId < this.groupData.nodeData.nodes.length;\r\n nodeId++\r\n ) {\r\n const node = this.groupData.nodeData.nodes[nodeId]\r\n const map = this.groupData.oldToNewWidgetMap[nodeId] ?? {}\r\n const widgets = Object.keys(map)\r\n\r\n if (!node.widgets_values?.length) {\r\n // special handling for populating values into reroutes\r\n // this allows primitives connect to them to pick up the correct value\r\n this.populateReroute(node, nodeId, map)\r\n continue\r\n }\r\n\r\n let linkedShift = 0\r\n for (let i = 0; i < widgets.length; i++) {\r\n const oldName = widgets[i]\r\n const newName = map[oldName]\r\n const widgetIndex = this.node.widgets.findIndex(\r\n (w) => w.name === newName\r\n )\r\n const mainWidget = this.node.widgets[widgetIndex]\r\n if (\r\n this.populatePrimitive(node, nodeId, oldName, i, linkedShift) ||\r\n widgetIndex === -1\r\n ) {\r\n // Find the inner widget and shift by the number of linked widgets as they will have been removed too\r\n const innerWidget = this.innerNodes[nodeId].widgets?.find(\r\n (w) => w.name === oldName\r\n )\r\n linkedShift += innerWidget?.linkedWidgets?.length ?? 0\r\n }\r\n if (widgetIndex === -1) {\r\n continue\r\n }\r\n\r\n // Populate the main and any linked widget\r\n mainWidget.value = node.widgets_values[i + linkedShift]\r\n for (let w = 0; w < mainWidget.linkedWidgets?.length; w++) {\r\n this.node.widgets[widgetIndex + w + 1].value =\r\n node.widgets_values[i + ++linkedShift]\r\n }\r\n }\r\n }\r\n }\r\n\r\n replaceNodes(nodes) {\r\n let top\r\n let left\r\n\r\n for (let i = 0; i < nodes.length; i++) {\r\n const node = nodes[i]\r\n if (left == null || node.pos[0] < left) {\r\n left = node.pos[0]\r\n }\r\n if (top == null || node.pos[1] < top) {\r\n top = node.pos[1]\r\n }\r\n\r\n this.linkOutputs(node, i)\r\n app.graph.remove(node)\r\n }\r\n\r\n this.linkInputs()\r\n this.node.pos = [left, top]\r\n }\r\n\r\n linkOutputs(originalNode, nodeId) {\r\n if (!originalNode.outputs) return\r\n\r\n for (const output of originalNode.outputs) {\r\n if (!output.links) continue\r\n // Clone the links as they'll be changed if we reconnect\r\n const links = [...output.links]\r\n for (const l of links) {\r\n const link = app.graph.links[l]\r\n if (!link) continue\r\n\r\n const targetNode = app.graph.getNodeById(link.target_id)\r\n const newSlot =\r\n this.groupData.oldToNewOutputMap[nodeId]?.[link.origin_slot]\r\n if (newSlot != null) {\r\n this.node.connect(newSlot, targetNode, link.target_slot)\r\n }\r\n }\r\n }\r\n }\r\n\r\n linkInputs() {\r\n for (const link of this.groupData.nodeData.links ?? []) {\r\n const [, originSlot, targetId, targetSlot, actualOriginId] = link\r\n const originNode = app.graph.getNodeById(actualOriginId)\r\n if (!originNode) continue // this node is in the group\r\n originNode.connect(\r\n originSlot,\r\n this.node.id,\r\n this.groupData.oldToNewInputMap[targetId][targetSlot]\r\n )\r\n }\r\n }\r\n\r\n static getGroupData(node) {\r\n return (node.nodeData ?? node.constructor?.nodeData)?.[GROUP]\r\n }\r\n\r\n static isGroupNode(node) {\r\n return !!node.constructor?.nodeData?.[GROUP]\r\n }\r\n\r\n static async fromNodes(nodes) {\r\n // Process the nodes into the stored workflow group node data\r\n const builder = new GroupNodeBuilder(nodes)\r\n const res = builder.build()\r\n if (!res) return\r\n\r\n const { name, nodeData } = res\r\n\r\n // Convert this data into a LG node definition and register it\r\n const config = new GroupNodeConfig(name, nodeData)\r\n await config.registerType()\r\n\r\n const groupNode = LiteGraph.createNode(`workflow/${name}`)\r\n // Reuse the existing nodes for this instance\r\n groupNode.setInnerNodes(builder.nodes)\r\n groupNode[GROUP].populateWidgets()\r\n app.graph.add(groupNode)\r\n\r\n // Remove all converted nodes and relink them\r\n groupNode[GROUP].replaceNodes(builder.nodes)\r\n return groupNode\r\n }\r\n}\r\n\r\nfunction addConvertToGroupOptions() {\r\n function addConvertOption(options, index) {\r\n const selected = Object.values(app.canvas.selected_nodes ?? {})\r\n const disabled =\r\n selected.length < 2 ||\r\n selected.find((n) => GroupNodeHandler.isGroupNode(n))\r\n options.splice(index + 1, null, {\r\n content: `Convert to Group Node`,\r\n disabled,\r\n callback: async () => {\r\n return await GroupNodeHandler.fromNodes(selected)\r\n }\r\n })\r\n }\r\n\r\n function addManageOption(options, index) {\r\n const groups = app.graph.extra?.groupNodes\r\n const disabled = !groups || !Object.keys(groups).length\r\n options.splice(index + 1, null, {\r\n content: `Manage Group Nodes`,\r\n disabled,\r\n callback: () => {\r\n new ManageGroupDialog(app).show()\r\n }\r\n })\r\n }\r\n\r\n // Add to canvas\r\n const getCanvasMenuOptions = LGraphCanvas.prototype.getCanvasMenuOptions\r\n LGraphCanvas.prototype.getCanvasMenuOptions = function () {\r\n const options = getCanvasMenuOptions.apply(this, arguments)\r\n const index =\r\n options.findIndex((o) => o?.content === 'Add Group') + 1 || options.length\r\n addConvertOption(options, index)\r\n addManageOption(options, index + 1)\r\n return options\r\n }\r\n\r\n // Add to nodes\r\n const getNodeMenuOptions = LGraphCanvas.prototype.getNodeMenuOptions\r\n LGraphCanvas.prototype.getNodeMenuOptions = function (node) {\r\n const options = getNodeMenuOptions.apply(this, arguments)\r\n if (!GroupNodeHandler.isGroupNode(node)) {\r\n const index =\r\n options.findIndex((o) => o?.content === 'Outputs') + 1 ||\r\n options.length - 1\r\n addConvertOption(options, index)\r\n }\r\n return options\r\n }\r\n}\r\n\r\nconst id = 'Comfy.GroupNode'\r\nlet globalDefs\r\nconst ext = {\r\n name: id,\r\n setup() {\r\n addConvertToGroupOptions()\r\n },\r\n async beforeConfigureGraph(graphData, missingNodeTypes) {\r\n const nodes = graphData?.extra?.groupNodes\r\n if (nodes) {\r\n await GroupNodeConfig.registerFromWorkflow(nodes, missingNodeTypes)\r\n }\r\n },\r\n addCustomNodeDefs(defs) {\r\n // Store this so we can mutate it later with group nodes\r\n globalDefs = defs\r\n },\r\n nodeCreated(node) {\r\n if (GroupNodeHandler.isGroupNode(node)) {\r\n node[GROUP] = new GroupNodeHandler(node)\r\n }\r\n },\r\n async refreshComboInNodes(defs) {\r\n // Re-register group nodes so new ones are created with the correct options\r\n Object.assign(globalDefs, defs)\r\n const nodes = app.graph.extra?.groupNodes\r\n if (nodes) {\r\n await GroupNodeConfig.registerFromWorkflow(nodes, {})\r\n }\r\n }\r\n}\r\n\r\napp.registerExtension(ext)\r\n","import { app } from '../../scripts/app'\r\nimport { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph'\r\n\r\nfunction setNodeMode(node, mode) {\r\n node.mode = mode\r\n node.graph.change()\r\n}\r\n\r\nfunction addNodesToGroup(group, nodes = []) {\r\n var x1, y1, x2, y2\r\n var nx1, ny1, nx2, ny2\r\n var node\r\n\r\n x1 = y1 = x2 = y2 = -1\r\n nx1 = ny1 = nx2 = ny2 = -1\r\n\r\n for (var n of [group._nodes, nodes]) {\r\n for (var i in n) {\r\n node = n[i]\r\n\r\n nx1 = node.pos[0]\r\n ny1 = node.pos[1]\r\n nx2 = node.pos[0] + node.size[0]\r\n ny2 = node.pos[1] + node.size[1]\r\n\r\n if (node.type != 'Reroute') {\r\n ny1 -= LiteGraph.NODE_TITLE_HEIGHT\r\n }\r\n\r\n if (node.flags?.collapsed) {\r\n ny2 = ny1 + LiteGraph.NODE_TITLE_HEIGHT\r\n\r\n if (node?._collapsed_width) {\r\n nx2 = nx1 + Math.round(node._collapsed_width)\r\n }\r\n }\r\n\r\n if (x1 == -1 || nx1 < x1) {\r\n x1 = nx1\r\n }\r\n\r\n if (y1 == -1 || ny1 < y1) {\r\n y1 = ny1\r\n }\r\n\r\n if (x2 == -1 || nx2 > x2) {\r\n x2 = nx2\r\n }\r\n\r\n if (y2 == -1 || ny2 > y2) {\r\n y2 = ny2\r\n }\r\n }\r\n }\r\n\r\n var padding = 10\r\n\r\n y1 = y1 - Math.round(group.font_size * 1.4)\r\n\r\n group.pos = [x1 - padding, y1 - padding]\r\n group.size = [x2 - x1 + padding * 2, y2 - y1 + padding * 2]\r\n}\r\n\r\napp.registerExtension({\r\n name: 'Comfy.GroupOptions',\r\n setup() {\r\n const orig = LGraphCanvas.prototype.getCanvasMenuOptions\r\n // graph_mouse\r\n LGraphCanvas.prototype.getCanvasMenuOptions = function () {\r\n const options = orig.apply(this, arguments)\r\n const group = this.graph.getGroupOnPos(\r\n this.graph_mouse[0],\r\n this.graph_mouse[1]\r\n )\r\n if (!group) {\r\n options.push({\r\n content: 'Add Group For Selected Nodes',\r\n disabled: !Object.keys(app.canvas.selected_nodes || {}).length,\r\n callback: () => {\r\n // @ts-expect-error\r\n var group = new LiteGraph.LGraphGroup()\r\n addNodesToGroup(group, this.selected_nodes)\r\n app.canvas.graph.add(group)\r\n this.graph.change()\r\n }\r\n })\r\n\r\n return options\r\n }\r\n\r\n // Group nodes aren't recomputed until the group is moved, this ensures the nodes are up-to-date\r\n group.recomputeInsideNodes()\r\n const nodesInGroup = group._nodes\r\n\r\n options.push({\r\n content: 'Add Selected Nodes To Group',\r\n disabled: !Object.keys(app.canvas.selected_nodes || {}).length,\r\n callback: () => {\r\n addNodesToGroup(group, this.selected_nodes)\r\n this.graph.change()\r\n }\r\n })\r\n\r\n // No nodes in group, return default options\r\n if (nodesInGroup.length === 0) {\r\n return options\r\n } else {\r\n // Add a separator between the default options and the group options\r\n options.push(null)\r\n }\r\n\r\n // Check if all nodes are the same mode\r\n let allNodesAreSameMode = true\r\n for (let i = 1; i < nodesInGroup.length; i++) {\r\n if (nodesInGroup[i].mode !== nodesInGroup[0].mode) {\r\n allNodesAreSameMode = false\r\n break\r\n }\r\n }\r\n\r\n options.push({\r\n content: 'Fit Group To Nodes',\r\n callback: () => {\r\n addNodesToGroup(group)\r\n this.graph.change()\r\n }\r\n })\r\n\r\n options.push({\r\n content: 'Select Nodes',\r\n callback: () => {\r\n this.selectNodes(nodesInGroup)\r\n this.graph.change()\r\n this.canvas.focus()\r\n }\r\n })\r\n\r\n // Modes\r\n // 0: Always\r\n // 1: On Event\r\n // 2: Never\r\n // 3: On Trigger\r\n // 4: Bypass\r\n // If all nodes are the same mode, add a menu option to change the mode\r\n if (allNodesAreSameMode) {\r\n const mode = nodesInGroup[0].mode\r\n switch (mode) {\r\n case 0:\r\n // All nodes are always, option to disable, and bypass\r\n options.push({\r\n content: 'Set Group Nodes to Never',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 2)\r\n }\r\n }\r\n })\r\n options.push({\r\n content: 'Bypass Group Nodes',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 4)\r\n }\r\n }\r\n })\r\n break\r\n case 2:\r\n // All nodes are never, option to enable, and bypass\r\n options.push({\r\n content: 'Set Group Nodes to Always',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 0)\r\n }\r\n }\r\n })\r\n options.push({\r\n content: 'Bypass Group Nodes',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 4)\r\n }\r\n }\r\n })\r\n break\r\n case 4:\r\n // All nodes are bypass, option to enable, and disable\r\n options.push({\r\n content: 'Set Group Nodes to Always',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 0)\r\n }\r\n }\r\n })\r\n options.push({\r\n content: 'Set Group Nodes to Never',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 2)\r\n }\r\n }\r\n })\r\n break\r\n default:\r\n // All nodes are On Trigger or On Event(Or other?), option to disable, set to always, or bypass\r\n options.push({\r\n content: 'Set Group Nodes to Always',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 0)\r\n }\r\n }\r\n })\r\n options.push({\r\n content: 'Set Group Nodes to Never',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 2)\r\n }\r\n }\r\n })\r\n options.push({\r\n content: 'Bypass Group Nodes',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 4)\r\n }\r\n }\r\n })\r\n break\r\n }\r\n } else {\r\n // Nodes are not all the same mode, add a menu option to change the mode to always, never, or bypass\r\n options.push({\r\n content: 'Set Group Nodes to Always',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 0)\r\n }\r\n }\r\n })\r\n options.push({\r\n content: 'Set Group Nodes to Never',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 2)\r\n }\r\n }\r\n })\r\n options.push({\r\n content: 'Bypass Group Nodes',\r\n callback: () => {\r\n for (const node of nodesInGroup) {\r\n setNodeMode(node, 4)\r\n }\r\n }\r\n })\r\n }\r\n\r\n return options\r\n }\r\n }\r\n})\r\n","import { LiteGraph } from '@comfyorg/litegraph'\r\nimport { app } from '../../scripts/app'\r\n\r\n// Inverts the scrolling of context menus\r\n\r\nconst id = 'Comfy.InvertMenuScrolling'\r\napp.registerExtension({\r\n name: id,\r\n init() {\r\n const ctxMenu = LiteGraph.ContextMenu\r\n const replace = () => {\r\n // @ts-expect-error\r\n LiteGraph.ContextMenu = function (values, options) {\r\n options = options || {}\r\n if (options.scroll_speed) {\r\n options.scroll_speed *= -1\r\n } else {\r\n options.scroll_speed = -0.1\r\n }\r\n return ctxMenu.call(this, values, options)\r\n }\r\n LiteGraph.ContextMenu.prototype = ctxMenu.prototype\r\n }\r\n app.ui.settings.addSetting({\r\n id,\r\n name: 'Invert Menu Scrolling',\r\n type: 'boolean',\r\n defaultValue: false,\r\n onChange(value) {\r\n if (value) {\r\n replace()\r\n } else {\r\n LiteGraph.ContextMenu = ctxMenu\r\n }\r\n }\r\n })\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport { api } from '../../scripts/api'\r\n\r\napp.registerExtension({\r\n name: 'Comfy.Keybinds',\r\n init() {\r\n const keybindListener = function (event) {\r\n const modifierPressed = event.ctrlKey || event.metaKey\r\n\r\n // Queue prompt using (ctrl or command) + enter\r\n if (modifierPressed && event.key === 'Enter') {\r\n // Cancel current prompt using (ctrl or command) + alt + enter\r\n if (event.altKey) {\r\n api.interrupt()\r\n return\r\n }\r\n // Queue prompt as first for generation using (ctrl or command) + shift + enter\r\n app.queuePrompt(event.shiftKey ? -1 : 0).then()\r\n return\r\n }\r\n\r\n const target = event.composedPath()[0]\r\n if (['INPUT', 'TEXTAREA'].includes(target.tagName)) {\r\n return\r\n }\r\n\r\n const modifierKeyIdMap = {\r\n s: '#comfy-save-button',\r\n o: '#comfy-file-input',\r\n Backspace: '#comfy-clear-button',\r\n d: '#comfy-load-default-button'\r\n }\r\n\r\n const modifierKeybindId = modifierKeyIdMap[event.key]\r\n if (modifierPressed && modifierKeybindId) {\r\n event.preventDefault()\r\n\r\n const elem = document.querySelector(modifierKeybindId)\r\n elem.click()\r\n return\r\n }\r\n\r\n // Finished Handling all modifier keybinds, now handle the rest\r\n if (event.ctrlKey || event.altKey || event.metaKey) {\r\n return\r\n }\r\n\r\n // Close out of modals using escape\r\n if (event.key === 'Escape') {\r\n const modals = document.querySelectorAll('.comfy-modal')\r\n const modal = Array.from(modals).find(\r\n (modal) =>\r\n window.getComputedStyle(modal).getPropertyValue('display') !==\r\n 'none'\r\n )\r\n if (modal) {\r\n modal.style.display = 'none'\r\n }\r\n\r\n ;[...document.querySelectorAll('dialog')].forEach((d) => {\r\n d.close()\r\n })\r\n }\r\n\r\n const keyIdMap = {\r\n q: '.queue-tab-button.side-bar-button',\r\n h: '.queue-tab-button.side-bar-button',\r\n r: '#comfy-refresh-button'\r\n }\r\n\r\n const buttonId = keyIdMap[event.key]\r\n if (buttonId) {\r\n const button = document.querySelector(buttonId)\r\n button.click()\r\n }\r\n }\r\n\r\n window.addEventListener('keydown', keybindListener, true)\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport { LiteGraph } from '@comfyorg/litegraph'\r\nconst id = 'Comfy.LinkRenderMode'\r\nconst ext = {\r\n name: id,\r\n async setup(app) {\r\n app.ui.settings.addSetting({\r\n id,\r\n name: 'Link Render Mode',\r\n defaultValue: 2,\r\n type: 'combo',\r\n // @ts-expect-error\r\n options: [...LiteGraph.LINK_RENDER_MODES, 'Hidden'].map((m, i) => ({\r\n value: i,\r\n text: m,\r\n selected: i == app.canvas.links_render_mode\r\n })),\r\n onChange(value) {\r\n app.canvas.links_render_mode = +value\r\n app.graph.setDirtyCanvas(true)\r\n }\r\n })\r\n }\r\n}\r\n\r\napp.registerExtension(ext)\r\n","import { app } from '../../scripts/app'\r\nimport { ComfyDialog, $el } from '../../scripts/ui'\r\nimport { ComfyApp } from '../../scripts/app'\r\nimport { api } from '../../scripts/api'\r\nimport { ClipspaceDialog } from './clipspace'\r\n\r\n// Helper function to convert a data URL to a Blob object\r\nfunction dataURLToBlob(dataURL) {\r\n const parts = dataURL.split(';base64,')\r\n const contentType = parts[0].split(':')[1]\r\n const byteString = atob(parts[1])\r\n const arrayBuffer = new ArrayBuffer(byteString.length)\r\n const uint8Array = new Uint8Array(arrayBuffer)\r\n for (let i = 0; i < byteString.length; i++) {\r\n uint8Array[i] = byteString.charCodeAt(i)\r\n }\r\n return new Blob([arrayBuffer], { type: contentType })\r\n}\r\n\r\nfunction loadedImageToBlob(image) {\r\n const canvas = document.createElement('canvas')\r\n\r\n canvas.width = image.width\r\n canvas.height = image.height\r\n\r\n const ctx = canvas.getContext('2d')\r\n\r\n ctx.drawImage(image, 0, 0)\r\n\r\n const dataURL = canvas.toDataURL('image/png', 1)\r\n const blob = dataURLToBlob(dataURL)\r\n\r\n return blob\r\n}\r\n\r\nfunction loadImage(imagePath) {\r\n return new Promise((resolve, reject) => {\r\n const image = new Image()\r\n\r\n image.onload = function () {\r\n resolve(image)\r\n }\r\n\r\n image.src = imagePath\r\n })\r\n}\r\n\r\nasync function uploadMask(filepath, formData) {\r\n await api\r\n .fetchApi('/upload/mask', {\r\n method: 'POST',\r\n body: formData\r\n })\r\n .then((response) => {})\r\n .catch((error) => {\r\n console.error('Error:', error)\r\n })\r\n\r\n ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']] = new Image()\r\n ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src = api.apiURL(\r\n '/view?' +\r\n new URLSearchParams(filepath).toString() +\r\n app.getPreviewFormatParam() +\r\n app.getRandParam()\r\n )\r\n\r\n if (ComfyApp.clipspace.images)\r\n ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']] = filepath\r\n\r\n ClipspaceDialog.invalidatePreview()\r\n}\r\n\r\nfunction prepare_mask(image, maskCanvas, maskCtx, maskColor) {\r\n // paste mask data into alpha channel\r\n maskCtx.drawImage(image, 0, 0, maskCanvas.width, maskCanvas.height)\r\n const maskData = maskCtx.getImageData(\r\n 0,\r\n 0,\r\n maskCanvas.width,\r\n maskCanvas.height\r\n )\r\n\r\n // invert mask\r\n for (let i = 0; i < maskData.data.length; i += 4) {\r\n if (maskData.data[i + 3] == 255) maskData.data[i + 3] = 0\r\n else maskData.data[i + 3] = 255\r\n\r\n maskData.data[i] = maskColor.r\r\n maskData.data[i + 1] = maskColor.g\r\n maskData.data[i + 2] = maskColor.b\r\n }\r\n\r\n maskCtx.globalCompositeOperation = 'source-over'\r\n maskCtx.putImageData(maskData, 0, 0)\r\n}\r\n\r\nclass MaskEditorDialog extends ComfyDialog {\r\n static instance = null\r\n static mousedown_x: number | null = null\r\n static mousedown_y: number | null = null\r\n\r\n brush: HTMLDivElement\r\n maskCtx: any\r\n maskCanvas: HTMLCanvasElement\r\n brush_size_slider: HTMLDivElement\r\n brush_opacity_slider: HTMLDivElement\r\n colorButton: HTMLButtonElement\r\n saveButton: HTMLButtonElement\r\n zoom_ratio: number\r\n pan_x: number\r\n pan_y: number\r\n imgCanvas: HTMLCanvasElement\r\n last_display_style: string\r\n is_visible: boolean\r\n image: HTMLImageElement\r\n handler_registered: boolean\r\n brush_slider_input: HTMLInputElement\r\n cursorX: number\r\n cursorY: number\r\n mousedown_pan_x: number\r\n mousedown_pan_y: number\r\n last_pressure: number\r\n\r\n static getInstance() {\r\n if (!MaskEditorDialog.instance) {\r\n MaskEditorDialog.instance = new MaskEditorDialog()\r\n }\r\n\r\n return MaskEditorDialog.instance\r\n }\r\n\r\n is_layout_created = false\r\n\r\n constructor() {\r\n super()\r\n this.element = $el('div.comfy-modal', { parent: document.body }, [\r\n $el('div.comfy-modal-content', [...this.createButtons()])\r\n ])\r\n }\r\n\r\n createButtons() {\r\n return []\r\n }\r\n\r\n createButton(name, callback): HTMLButtonElement {\r\n var button = document.createElement('button')\r\n button.style.pointerEvents = 'auto'\r\n button.innerText = name\r\n button.addEventListener('click', callback)\r\n return button\r\n }\r\n\r\n createLeftButton(name, callback) {\r\n var button = this.createButton(name, callback)\r\n button.style.cssFloat = 'left'\r\n button.style.marginRight = '4px'\r\n return button\r\n }\r\n\r\n createRightButton(name, callback) {\r\n var button = this.createButton(name, callback)\r\n button.style.cssFloat = 'right'\r\n button.style.marginLeft = '4px'\r\n return button\r\n }\r\n\r\n createLeftSlider(self, name, callback): HTMLDivElement {\r\n const divElement = document.createElement('div')\r\n divElement.id = 'maskeditor-slider'\r\n divElement.style.cssFloat = 'left'\r\n divElement.style.fontFamily = 'sans-serif'\r\n divElement.style.marginRight = '4px'\r\n divElement.style.color = 'var(--input-text)'\r\n divElement.style.backgroundColor = 'var(--comfy-input-bg)'\r\n divElement.style.borderRadius = '8px'\r\n divElement.style.borderColor = 'var(--border-color)'\r\n divElement.style.borderStyle = 'solid'\r\n divElement.style.fontSize = '15px'\r\n divElement.style.height = '21px'\r\n divElement.style.padding = '1px 6px'\r\n divElement.style.display = 'flex'\r\n divElement.style.position = 'relative'\r\n divElement.style.top = '2px'\r\n divElement.style.pointerEvents = 'auto'\r\n self.brush_slider_input = document.createElement('input')\r\n self.brush_slider_input.setAttribute('type', 'range')\r\n self.brush_slider_input.setAttribute('min', '1')\r\n self.brush_slider_input.setAttribute('max', '100')\r\n self.brush_slider_input.setAttribute('value', '10')\r\n const labelElement = document.createElement('label')\r\n labelElement.textContent = name\r\n\r\n divElement.appendChild(labelElement)\r\n divElement.appendChild(self.brush_slider_input)\r\n\r\n self.brush_slider_input.addEventListener('change', callback)\r\n\r\n return divElement\r\n }\r\n\r\n createOpacitySlider(self, name, callback): HTMLDivElement {\r\n const divElement = document.createElement('div')\r\n divElement.id = 'maskeditor-opacity-slider'\r\n divElement.style.cssFloat = 'left'\r\n divElement.style.fontFamily = 'sans-serif'\r\n divElement.style.marginRight = '4px'\r\n divElement.style.color = 'var(--input-text)'\r\n divElement.style.backgroundColor = 'var(--comfy-input-bg)'\r\n divElement.style.borderRadius = '8px'\r\n divElement.style.borderColor = 'var(--border-color)'\r\n divElement.style.borderStyle = 'solid'\r\n divElement.style.fontSize = '15px'\r\n divElement.style.height = '21px'\r\n divElement.style.padding = '1px 6px'\r\n divElement.style.display = 'flex'\r\n divElement.style.position = 'relative'\r\n divElement.style.top = '2px'\r\n divElement.style.pointerEvents = 'auto'\r\n self.opacity_slider_input = document.createElement('input')\r\n self.opacity_slider_input.setAttribute('type', 'range')\r\n self.opacity_slider_input.setAttribute('min', '0.1')\r\n self.opacity_slider_input.setAttribute('max', '1.0')\r\n self.opacity_slider_input.setAttribute('step', '0.01')\r\n self.opacity_slider_input.setAttribute('value', '0.7')\r\n const labelElement = document.createElement('label')\r\n labelElement.textContent = name\r\n\r\n divElement.appendChild(labelElement)\r\n divElement.appendChild(self.opacity_slider_input)\r\n\r\n self.opacity_slider_input.addEventListener('input', callback)\r\n\r\n return divElement\r\n }\r\n\r\n setlayout(imgCanvas: HTMLCanvasElement, maskCanvas: HTMLCanvasElement) {\r\n const self = this\r\n\r\n // If it is specified as relative, using it only as a hidden placeholder for padding is recommended\r\n // to prevent anomalies where it exceeds a certain size and goes outside of the window.\r\n var bottom_panel = document.createElement('div')\r\n bottom_panel.style.position = 'absolute'\r\n bottom_panel.style.bottom = '0px'\r\n bottom_panel.style.left = '20px'\r\n bottom_panel.style.right = '20px'\r\n bottom_panel.style.height = '50px'\r\n bottom_panel.style.pointerEvents = 'none'\r\n\r\n var brush = document.createElement('div')\r\n brush.id = 'brush'\r\n brush.style.backgroundColor = 'transparent'\r\n brush.style.outline = '1px dashed black'\r\n brush.style.boxShadow = '0 0 0 1px white'\r\n brush.style.borderRadius = '50%'\r\n // @ts-expect-error\r\n brush.style.MozBorderRadius = '50%'\r\n // @ts-expect-error\r\n brush.style.WebkitBorderRadius = '50%'\r\n brush.style.position = 'absolute'\r\n brush.style.zIndex = '8889'\r\n brush.style.pointerEvents = 'none'\r\n this.brush = brush\r\n this.element.appendChild(imgCanvas)\r\n this.element.appendChild(maskCanvas)\r\n this.element.appendChild(bottom_panel)\r\n document.body.appendChild(brush)\r\n\r\n var clearButton = this.createLeftButton('Clear', () => {\r\n self.maskCtx.clearRect(\r\n 0,\r\n 0,\r\n self.maskCanvas.width,\r\n self.maskCanvas.height\r\n )\r\n })\r\n\r\n this.brush_size_slider = this.createLeftSlider(\r\n self,\r\n 'Thickness',\r\n (event) => {\r\n self.brush_size = event.target.value\r\n self.updateBrushPreview(self)\r\n }\r\n )\r\n\r\n this.brush_opacity_slider = this.createOpacitySlider(\r\n self,\r\n 'Opacity',\r\n (event) => {\r\n self.brush_opacity = event.target.value\r\n if (self.brush_color_mode !== 'negative') {\r\n self.maskCanvas.style.opacity = self.brush_opacity.toString()\r\n }\r\n }\r\n )\r\n\r\n this.colorButton = this.createLeftButton(this.getColorButtonText(), () => {\r\n if (self.brush_color_mode === 'black') {\r\n self.brush_color_mode = 'white'\r\n } else if (self.brush_color_mode === 'white') {\r\n self.brush_color_mode = 'negative'\r\n } else {\r\n self.brush_color_mode = 'black'\r\n }\r\n\r\n self.updateWhenBrushColorModeChanged()\r\n })\r\n\r\n var cancelButton = this.createRightButton('Cancel', () => {\r\n document.removeEventListener('keydown', MaskEditorDialog.handleKeyDown)\r\n self.close()\r\n })\r\n\r\n this.saveButton = this.createRightButton('Save', () => {\r\n document.removeEventListener('keydown', MaskEditorDialog.handleKeyDown)\r\n self.save()\r\n })\r\n\r\n this.element.appendChild(imgCanvas)\r\n this.element.appendChild(maskCanvas)\r\n this.element.appendChild(bottom_panel)\r\n\r\n bottom_panel.appendChild(clearButton)\r\n bottom_panel.appendChild(this.saveButton)\r\n bottom_panel.appendChild(cancelButton)\r\n bottom_panel.appendChild(this.brush_size_slider)\r\n bottom_panel.appendChild(this.brush_opacity_slider)\r\n bottom_panel.appendChild(this.colorButton)\r\n\r\n imgCanvas.style.position = 'absolute'\r\n maskCanvas.style.position = 'absolute'\r\n\r\n imgCanvas.style.top = '200'\r\n imgCanvas.style.left = '0'\r\n\r\n maskCanvas.style.top = imgCanvas.style.top\r\n maskCanvas.style.left = imgCanvas.style.left\r\n\r\n const maskCanvasStyle = this.getMaskCanvasStyle()\r\n maskCanvas.style.mixBlendMode = maskCanvasStyle.mixBlendMode\r\n maskCanvas.style.opacity = maskCanvasStyle.opacity.toString()\r\n }\r\n\r\n async show() {\r\n this.zoom_ratio = 1.0\r\n this.pan_x = 0\r\n this.pan_y = 0\r\n\r\n if (!this.is_layout_created) {\r\n // layout\r\n const imgCanvas = document.createElement('canvas')\r\n const maskCanvas = document.createElement('canvas')\r\n\r\n imgCanvas.id = 'imageCanvas'\r\n maskCanvas.id = 'maskCanvas'\r\n\r\n this.setlayout(imgCanvas, maskCanvas)\r\n\r\n // prepare content\r\n this.imgCanvas = imgCanvas\r\n this.maskCanvas = maskCanvas\r\n this.maskCtx = maskCanvas.getContext('2d', { willReadFrequently: true })\r\n\r\n this.setEventHandler(maskCanvas)\r\n\r\n this.is_layout_created = true\r\n\r\n // replacement of onClose hook since close is not real close\r\n const self = this\r\n const observer = new MutationObserver(function (mutations) {\r\n mutations.forEach(function (mutation) {\r\n if (\r\n mutation.type === 'attributes' &&\r\n mutation.attributeName === 'style'\r\n ) {\r\n if (\r\n self.last_display_style &&\r\n self.last_display_style != 'none' &&\r\n self.element.style.display == 'none'\r\n ) {\r\n self.brush.style.display = 'none'\r\n ComfyApp.onClipspaceEditorClosed()\r\n }\r\n\r\n self.last_display_style = self.element.style.display\r\n }\r\n })\r\n })\r\n\r\n const config = { attributes: true }\r\n observer.observe(this.element, config)\r\n }\r\n\r\n // The keydown event needs to be reconfigured when closing the dialog as it gets removed.\r\n document.addEventListener('keydown', MaskEditorDialog.handleKeyDown)\r\n\r\n if (ComfyApp.clipspace_return_node) {\r\n this.saveButton.innerText = 'Save to node'\r\n } else {\r\n this.saveButton.innerText = 'Save'\r\n }\r\n this.saveButton.disabled = false\r\n\r\n this.element.style.display = 'block'\r\n this.element.style.width = '85%'\r\n this.element.style.margin = '0 7.5%'\r\n this.element.style.height = '100vh'\r\n this.element.style.top = '50%'\r\n this.element.style.left = '42%'\r\n this.element.style.zIndex = '8888' // NOTE: alert dialog must be high priority.\r\n\r\n await this.setImages(this.imgCanvas)\r\n\r\n this.is_visible = true\r\n }\r\n\r\n isOpened() {\r\n return this.element.style.display == 'block'\r\n }\r\n\r\n invalidateCanvas(orig_image, mask_image) {\r\n this.imgCanvas.width = orig_image.width\r\n this.imgCanvas.height = orig_image.height\r\n\r\n this.maskCanvas.width = orig_image.width\r\n this.maskCanvas.height = orig_image.height\r\n\r\n let imgCtx = this.imgCanvas.getContext('2d', { willReadFrequently: true })\r\n let maskCtx = this.maskCanvas.getContext('2d', {\r\n willReadFrequently: true\r\n })\r\n\r\n imgCtx.drawImage(orig_image, 0, 0, orig_image.width, orig_image.height)\r\n prepare_mask(mask_image, this.maskCanvas, maskCtx, this.getMaskColor())\r\n }\r\n\r\n async setImages(imgCanvas) {\r\n let self = this\r\n\r\n const imgCtx = imgCanvas.getContext('2d', { willReadFrequently: true })\r\n const maskCtx = this.maskCtx\r\n const maskCanvas = this.maskCanvas\r\n\r\n imgCtx.clearRect(0, 0, this.imgCanvas.width, this.imgCanvas.height)\r\n maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height)\r\n\r\n // image load\r\n const filepath = ComfyApp.clipspace.images\r\n\r\n const alpha_url = new URL(\r\n ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src\r\n )\r\n alpha_url.searchParams.delete('channel')\r\n alpha_url.searchParams.delete('preview')\r\n alpha_url.searchParams.set('channel', 'a')\r\n let mask_image = await loadImage(alpha_url)\r\n\r\n // original image load\r\n const rgb_url = new URL(\r\n ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src\r\n )\r\n rgb_url.searchParams.delete('channel')\r\n rgb_url.searchParams.set('channel', 'rgb')\r\n this.image = new Image()\r\n this.image.onload = function () {\r\n maskCanvas.width = self.image.width\r\n maskCanvas.height = self.image.height\r\n\r\n self.invalidateCanvas(self.image, mask_image)\r\n self.initializeCanvasPanZoom()\r\n }\r\n this.image.src = rgb_url.toString()\r\n }\r\n\r\n initializeCanvasPanZoom() {\r\n // set initialize\r\n let drawWidth = this.image.width\r\n let drawHeight = this.image.height\r\n\r\n let width = this.element.clientWidth\r\n let height = this.element.clientHeight\r\n\r\n if (this.image.width > width) {\r\n drawWidth = width\r\n drawHeight = (drawWidth / this.image.width) * this.image.height\r\n }\r\n\r\n if (drawHeight > height) {\r\n drawHeight = height\r\n drawWidth = (drawHeight / this.image.height) * this.image.width\r\n }\r\n\r\n this.zoom_ratio = drawWidth / this.image.width\r\n\r\n const canvasX = (width - drawWidth) / 2\r\n const canvasY = (height - drawHeight) / 2\r\n this.pan_x = canvasX\r\n this.pan_y = canvasY\r\n\r\n this.invalidatePanZoom()\r\n }\r\n\r\n invalidatePanZoom() {\r\n let raw_width = this.image.width * this.zoom_ratio\r\n let raw_height = this.image.height * this.zoom_ratio\r\n\r\n if (this.pan_x + raw_width < 10) {\r\n this.pan_x = 10 - raw_width\r\n }\r\n\r\n if (this.pan_y + raw_height < 10) {\r\n this.pan_y = 10 - raw_height\r\n }\r\n\r\n let width = `${raw_width}px`\r\n let height = `${raw_height}px`\r\n\r\n let left = `${this.pan_x}px`\r\n let top = `${this.pan_y}px`\r\n\r\n this.maskCanvas.style.width = width\r\n this.maskCanvas.style.height = height\r\n this.maskCanvas.style.left = left\r\n this.maskCanvas.style.top = top\r\n\r\n this.imgCanvas.style.width = width\r\n this.imgCanvas.style.height = height\r\n this.imgCanvas.style.left = left\r\n this.imgCanvas.style.top = top\r\n }\r\n\r\n setEventHandler(maskCanvas) {\r\n const self = this\r\n\r\n if (!this.handler_registered) {\r\n maskCanvas.addEventListener('contextmenu', (event) => {\r\n event.preventDefault()\r\n })\r\n\r\n this.element.addEventListener('wheel', (event) =>\r\n this.handleWheelEvent(self, event)\r\n )\r\n this.element.addEventListener('pointermove', (event) =>\r\n this.pointMoveEvent(self, event)\r\n )\r\n this.element.addEventListener('touchmove', (event) =>\r\n this.pointMoveEvent(self, event)\r\n )\r\n\r\n this.element.addEventListener('dragstart', (event) => {\r\n if (event.ctrlKey) {\r\n event.preventDefault()\r\n }\r\n })\r\n\r\n maskCanvas.addEventListener('pointerdown', (event) =>\r\n this.handlePointerDown(self, event)\r\n )\r\n maskCanvas.addEventListener('pointermove', (event) =>\r\n this.draw_move(self, event)\r\n )\r\n maskCanvas.addEventListener('touchmove', (event) =>\r\n this.draw_move(self, event)\r\n )\r\n maskCanvas.addEventListener('pointerover', (event) => {\r\n this.brush.style.display = 'block'\r\n })\r\n maskCanvas.addEventListener('pointerleave', (event) => {\r\n this.brush.style.display = 'none'\r\n })\r\n\r\n document.addEventListener('pointerup', MaskEditorDialog.handlePointerUp)\r\n\r\n this.handler_registered = true\r\n }\r\n }\r\n\r\n getMaskCanvasStyle() {\r\n if (this.brush_color_mode === 'negative') {\r\n return {\r\n mixBlendMode: 'difference',\r\n opacity: '1'\r\n }\r\n } else {\r\n return {\r\n mixBlendMode: 'initial',\r\n opacity: this.brush_opacity\r\n }\r\n }\r\n }\r\n\r\n getMaskColor() {\r\n if (this.brush_color_mode === 'black') {\r\n return { r: 0, g: 0, b: 0 }\r\n }\r\n if (this.brush_color_mode === 'white') {\r\n return { r: 255, g: 255, b: 255 }\r\n }\r\n if (this.brush_color_mode === 'negative') {\r\n // negative effect only works with white color\r\n return { r: 255, g: 255, b: 255 }\r\n }\r\n\r\n return { r: 0, g: 0, b: 0 }\r\n }\r\n\r\n getMaskFillStyle() {\r\n const maskColor = this.getMaskColor()\r\n\r\n return 'rgb(' + maskColor.r + ',' + maskColor.g + ',' + maskColor.b + ')'\r\n }\r\n\r\n getColorButtonText() {\r\n let colorCaption = 'unknown'\r\n\r\n if (this.brush_color_mode === 'black') {\r\n colorCaption = 'black'\r\n } else if (this.brush_color_mode === 'white') {\r\n colorCaption = 'white'\r\n } else if (this.brush_color_mode === 'negative') {\r\n colorCaption = 'negative'\r\n }\r\n\r\n return 'Color: ' + colorCaption\r\n }\r\n\r\n updateWhenBrushColorModeChanged() {\r\n this.colorButton.innerText = this.getColorButtonText()\r\n\r\n // update mask canvas css styles\r\n\r\n const maskCanvasStyle = this.getMaskCanvasStyle()\r\n this.maskCanvas.style.mixBlendMode = maskCanvasStyle.mixBlendMode\r\n this.maskCanvas.style.opacity = maskCanvasStyle.opacity.toString()\r\n\r\n // update mask canvas rgb colors\r\n\r\n const maskColor = this.getMaskColor()\r\n\r\n const maskData = this.maskCtx.getImageData(\r\n 0,\r\n 0,\r\n this.maskCanvas.width,\r\n this.maskCanvas.height\r\n )\r\n\r\n for (let i = 0; i < maskData.data.length; i += 4) {\r\n maskData.data[i] = maskColor.r\r\n maskData.data[i + 1] = maskColor.g\r\n maskData.data[i + 2] = maskColor.b\r\n }\r\n\r\n this.maskCtx.putImageData(maskData, 0, 0)\r\n }\r\n\r\n brush_opacity = 0.7\r\n brush_size = 10\r\n brush_color_mode = 'black'\r\n drawing_mode = false\r\n lastx = -1\r\n lasty = -1\r\n lasttime = 0\r\n\r\n static handleKeyDown(event) {\r\n const self = MaskEditorDialog.instance\r\n if (event.key === ']') {\r\n self.brush_size = Math.min(self.brush_size + 2, 100)\r\n self.brush_slider_input.value = self.brush_size\r\n } else if (event.key === '[') {\r\n self.brush_size = Math.max(self.brush_size - 2, 1)\r\n self.brush_slider_input.value = self.brush_size\r\n } else if (event.key === 'Enter') {\r\n self.save()\r\n }\r\n\r\n self.updateBrushPreview(self)\r\n }\r\n\r\n static handlePointerUp(event) {\r\n event.preventDefault()\r\n\r\n this.mousedown_x = null\r\n this.mousedown_y = null\r\n\r\n MaskEditorDialog.instance.drawing_mode = false\r\n }\r\n\r\n updateBrushPreview(self) {\r\n const brush = self.brush\r\n\r\n var centerX = self.cursorX\r\n var centerY = self.cursorY\r\n\r\n brush.style.width = self.brush_size * 2 * this.zoom_ratio + 'px'\r\n brush.style.height = self.brush_size * 2 * this.zoom_ratio + 'px'\r\n brush.style.left = centerX - self.brush_size * this.zoom_ratio + 'px'\r\n brush.style.top = centerY - self.brush_size * this.zoom_ratio + 'px'\r\n }\r\n\r\n handleWheelEvent(self, event) {\r\n event.preventDefault()\r\n\r\n if (event.ctrlKey) {\r\n // zoom canvas\r\n if (event.deltaY < 0) {\r\n this.zoom_ratio = Math.min(10.0, this.zoom_ratio + 0.2)\r\n } else {\r\n this.zoom_ratio = Math.max(0.2, this.zoom_ratio - 0.2)\r\n }\r\n\r\n this.invalidatePanZoom()\r\n } else {\r\n // adjust brush size\r\n if (event.deltaY < 0) this.brush_size = Math.min(this.brush_size + 2, 100)\r\n else this.brush_size = Math.max(this.brush_size - 2, 1)\r\n\r\n this.brush_slider_input.value = this.brush_size.toString()\r\n\r\n this.updateBrushPreview(this)\r\n }\r\n }\r\n\r\n pointMoveEvent(self, event) {\r\n this.cursorX = event.pageX\r\n this.cursorY = event.pageY\r\n\r\n self.updateBrushPreview(self)\r\n\r\n if (event.ctrlKey) {\r\n event.preventDefault()\r\n self.pan_move(self, event)\r\n }\r\n\r\n let left_button_down =\r\n (window.TouchEvent && event instanceof TouchEvent) || event.buttons == 1\r\n\r\n if (event.shiftKey && left_button_down) {\r\n self.drawing_mode = false\r\n\r\n const y = event.clientY\r\n let delta = (self.zoom_lasty - y) * 0.005\r\n self.zoom_ratio = Math.max(\r\n Math.min(10.0, self.last_zoom_ratio - delta),\r\n 0.2\r\n )\r\n\r\n this.invalidatePanZoom()\r\n return\r\n }\r\n }\r\n\r\n pan_move(self, event) {\r\n if (event.buttons == 1) {\r\n if (MaskEditorDialog.mousedown_x) {\r\n let deltaX = MaskEditorDialog.mousedown_x - event.clientX\r\n let deltaY = MaskEditorDialog.mousedown_y - event.clientY\r\n\r\n self.pan_x = this.mousedown_pan_x - deltaX\r\n self.pan_y = this.mousedown_pan_y - deltaY\r\n\r\n self.invalidatePanZoom()\r\n }\r\n }\r\n }\r\n\r\n draw_move(self, event) {\r\n if (event.ctrlKey || event.shiftKey) {\r\n return\r\n }\r\n\r\n event.preventDefault()\r\n\r\n this.cursorX = event.pageX\r\n this.cursorY = event.pageY\r\n\r\n self.updateBrushPreview(self)\r\n\r\n let left_button_down =\r\n (window.TouchEvent && event instanceof TouchEvent) || event.buttons == 1\r\n let right_button_down = [2, 5, 32].includes(event.buttons)\r\n\r\n if (!event.altKey && left_button_down) {\r\n var diff = performance.now() - self.lasttime\r\n\r\n const maskRect = self.maskCanvas.getBoundingClientRect()\r\n\r\n var x = event.offsetX\r\n var y = event.offsetY\r\n\r\n if (event.offsetX == null) {\r\n x = event.targetTouches[0].clientX - maskRect.left\r\n }\r\n\r\n if (event.offsetY == null) {\r\n y = event.targetTouches[0].clientY - maskRect.top\r\n }\r\n\r\n x /= self.zoom_ratio\r\n y /= self.zoom_ratio\r\n\r\n var brush_size = this.brush_size\r\n if (event instanceof PointerEvent && event.pointerType == 'pen') {\r\n brush_size *= event.pressure\r\n this.last_pressure = event.pressure\r\n } else if (\r\n window.TouchEvent &&\r\n event instanceof TouchEvent &&\r\n diff < 20\r\n ) {\r\n // The firing interval of PointerEvents in Pen is unreliable, so it is supplemented by TouchEvents.\r\n brush_size *= this.last_pressure\r\n } else {\r\n brush_size = this.brush_size\r\n }\r\n\r\n if (diff > 20 && !this.drawing_mode)\r\n requestAnimationFrame(() => {\r\n self.maskCtx.beginPath()\r\n self.maskCtx.fillStyle = this.getMaskFillStyle()\r\n self.maskCtx.globalCompositeOperation = 'source-over'\r\n self.maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false)\r\n self.maskCtx.fill()\r\n self.lastx = x\r\n self.lasty = y\r\n })\r\n else\r\n requestAnimationFrame(() => {\r\n self.maskCtx.beginPath()\r\n self.maskCtx.fillStyle = this.getMaskFillStyle()\r\n self.maskCtx.globalCompositeOperation = 'source-over'\r\n\r\n var dx = x - self.lastx\r\n var dy = y - self.lasty\r\n\r\n var distance = Math.sqrt(dx * dx + dy * dy)\r\n var directionX = dx / distance\r\n var directionY = dy / distance\r\n\r\n for (var i = 0; i < distance; i += 5) {\r\n var px = self.lastx + directionX * i\r\n var py = self.lasty + directionY * i\r\n self.maskCtx.arc(px, py, brush_size, 0, Math.PI * 2, false)\r\n self.maskCtx.fill()\r\n }\r\n self.lastx = x\r\n self.lasty = y\r\n })\r\n\r\n self.lasttime = performance.now()\r\n } else if ((event.altKey && left_button_down) || right_button_down) {\r\n const maskRect = self.maskCanvas.getBoundingClientRect()\r\n const x =\r\n (event.offsetX || event.targetTouches[0].clientX - maskRect.left) /\r\n self.zoom_ratio\r\n const y =\r\n (event.offsetY || event.targetTouches[0].clientY - maskRect.top) /\r\n self.zoom_ratio\r\n\r\n var brush_size = this.brush_size\r\n if (event instanceof PointerEvent && event.pointerType == 'pen') {\r\n brush_size *= event.pressure\r\n this.last_pressure = event.pressure\r\n } else if (\r\n window.TouchEvent &&\r\n event instanceof TouchEvent &&\r\n diff < 20\r\n ) {\r\n brush_size *= this.last_pressure\r\n } else {\r\n brush_size = this.brush_size\r\n }\r\n\r\n if (diff > 20 && !this.drawing_mode)\r\n // cannot tracking drawing_mode for touch event\r\n requestAnimationFrame(() => {\r\n self.maskCtx.beginPath()\r\n self.maskCtx.globalCompositeOperation = 'destination-out'\r\n self.maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false)\r\n self.maskCtx.fill()\r\n self.lastx = x\r\n self.lasty = y\r\n })\r\n else\r\n requestAnimationFrame(() => {\r\n self.maskCtx.beginPath()\r\n self.maskCtx.globalCompositeOperation = 'destination-out'\r\n\r\n var dx = x - self.lastx\r\n var dy = y - self.lasty\r\n\r\n var distance = Math.sqrt(dx * dx + dy * dy)\r\n var directionX = dx / distance\r\n var directionY = dy / distance\r\n\r\n for (var i = 0; i < distance; i += 5) {\r\n var px = self.lastx + directionX * i\r\n var py = self.lasty + directionY * i\r\n self.maskCtx.arc(px, py, brush_size, 0, Math.PI * 2, false)\r\n self.maskCtx.fill()\r\n }\r\n self.lastx = x\r\n self.lasty = y\r\n })\r\n\r\n self.lasttime = performance.now()\r\n }\r\n }\r\n\r\n handlePointerDown(self, event) {\r\n if (event.ctrlKey) {\r\n if (event.buttons == 1) {\r\n MaskEditorDialog.mousedown_x = event.clientX\r\n MaskEditorDialog.mousedown_y = event.clientY\r\n\r\n this.mousedown_pan_x = this.pan_x\r\n this.mousedown_pan_y = this.pan_y\r\n }\r\n return\r\n }\r\n\r\n var brush_size = this.brush_size\r\n if (event instanceof PointerEvent && event.pointerType == 'pen') {\r\n brush_size *= event.pressure\r\n this.last_pressure = event.pressure\r\n }\r\n\r\n if ([0, 2, 5].includes(event.button)) {\r\n self.drawing_mode = true\r\n\r\n event.preventDefault()\r\n\r\n if (event.shiftKey) {\r\n self.zoom_lasty = event.clientY\r\n self.last_zoom_ratio = self.zoom_ratio\r\n return\r\n }\r\n\r\n const maskRect = self.maskCanvas.getBoundingClientRect()\r\n const x =\r\n (event.offsetX || event.targetTouches[0].clientX - maskRect.left) /\r\n self.zoom_ratio\r\n const y =\r\n (event.offsetY || event.targetTouches[0].clientY - maskRect.top) /\r\n self.zoom_ratio\r\n\r\n self.maskCtx.beginPath()\r\n if (!event.altKey && event.button == 0) {\r\n self.maskCtx.fillStyle = this.getMaskFillStyle()\r\n self.maskCtx.globalCompositeOperation = 'source-over'\r\n } else {\r\n self.maskCtx.globalCompositeOperation = 'destination-out'\r\n }\r\n self.maskCtx.arc(x, y, brush_size, 0, Math.PI * 2, false)\r\n self.maskCtx.fill()\r\n self.lastx = x\r\n self.lasty = y\r\n self.lasttime = performance.now()\r\n }\r\n }\r\n\r\n async save() {\r\n const backupCanvas = document.createElement('canvas')\r\n const backupCtx = backupCanvas.getContext('2d', {\r\n willReadFrequently: true\r\n })\r\n backupCanvas.width = this.image.width\r\n backupCanvas.height = this.image.height\r\n\r\n backupCtx.clearRect(0, 0, backupCanvas.width, backupCanvas.height)\r\n backupCtx.drawImage(\r\n this.maskCanvas,\r\n 0,\r\n 0,\r\n this.maskCanvas.width,\r\n this.maskCanvas.height,\r\n 0,\r\n 0,\r\n backupCanvas.width,\r\n backupCanvas.height\r\n )\r\n\r\n // paste mask data into alpha channel\r\n const backupData = backupCtx.getImageData(\r\n 0,\r\n 0,\r\n backupCanvas.width,\r\n backupCanvas.height\r\n )\r\n\r\n // refine mask image\r\n for (let i = 0; i < backupData.data.length; i += 4) {\r\n if (backupData.data[i + 3] == 255) backupData.data[i + 3] = 0\r\n else backupData.data[i + 3] = 255\r\n\r\n backupData.data[i] = 0\r\n backupData.data[i + 1] = 0\r\n backupData.data[i + 2] = 0\r\n }\r\n\r\n backupCtx.globalCompositeOperation = 'source-over'\r\n backupCtx.putImageData(backupData, 0, 0)\r\n\r\n const formData = new FormData()\r\n const filename = 'clipspace-mask-' + performance.now() + '.png'\r\n\r\n const item = {\r\n filename: filename,\r\n subfolder: 'clipspace',\r\n type: 'input'\r\n }\r\n\r\n if (ComfyApp.clipspace.images) ComfyApp.clipspace.images[0] = item\r\n\r\n if (ComfyApp.clipspace.widgets) {\r\n const index = ComfyApp.clipspace.widgets.findIndex(\r\n (obj) => obj.name === 'image'\r\n )\r\n\r\n if (index >= 0) ComfyApp.clipspace.widgets[index].value = item\r\n }\r\n\r\n const dataURL = backupCanvas.toDataURL()\r\n const blob = dataURLToBlob(dataURL)\r\n\r\n let original_url = new URL(this.image.src)\r\n\r\n type Ref = { filename: string; subfolder?: string; type?: string }\r\n\r\n const original_ref: Ref = {\r\n filename: original_url.searchParams.get('filename')\r\n }\r\n\r\n let original_subfolder = original_url.searchParams.get('subfolder')\r\n if (original_subfolder) original_ref.subfolder = original_subfolder\r\n\r\n let original_type = original_url.searchParams.get('type')\r\n if (original_type) original_ref.type = original_type\r\n\r\n formData.append('image', blob, filename)\r\n formData.append('original_ref', JSON.stringify(original_ref))\r\n formData.append('type', 'input')\r\n formData.append('subfolder', 'clipspace')\r\n\r\n this.saveButton.innerText = 'Saving...'\r\n this.saveButton.disabled = true\r\n await uploadMask(item, formData)\r\n ComfyApp.onClipspaceEditorSave()\r\n this.close()\r\n }\r\n}\r\n\r\napp.registerExtension({\r\n name: 'Comfy.MaskEditor',\r\n init(app) {\r\n ComfyApp.open_maskeditor = function () {\r\n const dlg = MaskEditorDialog.getInstance()\r\n if (!dlg.isOpened()) {\r\n dlg.show()\r\n }\r\n }\r\n\r\n const context_predicate = () =>\r\n ComfyApp.clipspace &&\r\n ComfyApp.clipspace.imgs &&\r\n ComfyApp.clipspace.imgs.length > 0\r\n ClipspaceDialog.registerButton(\r\n 'MaskEditor',\r\n context_predicate,\r\n ComfyApp.open_maskeditor\r\n )\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport { api } from '../../scripts/api'\r\nimport { ComfyDialog, $el } from '../../scripts/ui'\r\nimport { GroupNodeConfig, GroupNodeHandler } from './groupNode'\r\nimport { LGraphCanvas } from '@comfyorg/litegraph'\r\n\r\n// Adds the ability to save and add multiple nodes as a template\r\n// To save:\r\n// Select multiple nodes (ctrl + drag to select a region or ctrl+click individual nodes)\r\n// Right click the canvas\r\n// Save Node Template -> give it a name\r\n//\r\n// To add:\r\n// Right click the canvas\r\n// Node templates -> click the one to add\r\n//\r\n// To delete/rename:\r\n// Right click the canvas\r\n// Node templates -> Manage\r\n//\r\n// To rearrange:\r\n// Open the manage dialog and Drag and drop elements using the \"Name:\" label as handle\r\n\r\nconst id = 'Comfy.NodeTemplates'\r\nconst file = 'comfy.templates.json'\r\n\r\nclass ManageTemplates extends ComfyDialog {\r\n templates: any[]\r\n draggedEl: HTMLElement | null\r\n saveVisualCue: number | null\r\n emptyImg: HTMLImageElement\r\n importInput: HTMLInputElement\r\n\r\n constructor() {\r\n super()\r\n this.load().then((v) => {\r\n this.templates = v\r\n })\r\n\r\n this.element.classList.add('comfy-manage-templates')\r\n this.draggedEl = null\r\n this.saveVisualCue = null\r\n this.emptyImg = new Image()\r\n this.emptyImg.src =\r\n 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='\r\n\r\n this.importInput = $el('input', {\r\n type: 'file',\r\n accept: '.json',\r\n multiple: true,\r\n style: { display: 'none' },\r\n parent: document.body,\r\n onchange: () => this.importAll()\r\n }) as HTMLInputElement\r\n }\r\n\r\n createButtons() {\r\n const btns = super.createButtons()\r\n btns[0].textContent = 'Close'\r\n btns[0].onclick = (e) => {\r\n clearTimeout(this.saveVisualCue)\r\n this.close()\r\n }\r\n btns.unshift(\r\n $el('button', {\r\n type: 'button',\r\n textContent: 'Export',\r\n onclick: () => this.exportAll()\r\n })\r\n )\r\n btns.unshift(\r\n $el('button', {\r\n type: 'button',\r\n textContent: 'Import',\r\n onclick: () => {\r\n this.importInput.click()\r\n }\r\n })\r\n )\r\n return btns\r\n }\r\n\r\n async load() {\r\n let templates = []\r\n if (app.storageLocation === 'server') {\r\n if (app.isNewUserSession) {\r\n // New user so migrate existing templates\r\n const json = localStorage.getItem(id)\r\n if (json) {\r\n templates = JSON.parse(json)\r\n }\r\n await api.storeUserData(file, json, { stringify: false })\r\n } else {\r\n const res = await api.getUserData(file)\r\n if (res.status === 200) {\r\n try {\r\n templates = await res.json()\r\n } catch (error) {}\r\n } else if (res.status !== 404) {\r\n console.error(res.status + ' ' + res.statusText)\r\n }\r\n }\r\n } else {\r\n const json = localStorage.getItem(id)\r\n if (json) {\r\n templates = JSON.parse(json)\r\n }\r\n }\r\n\r\n return templates ?? []\r\n }\r\n\r\n async store() {\r\n if (app.storageLocation === 'server') {\r\n const templates = JSON.stringify(this.templates, undefined, 4)\r\n localStorage.setItem(id, templates) // Backwards compatibility\r\n try {\r\n await api.storeUserData(file, templates, { stringify: false })\r\n } catch (error) {\r\n console.error(error)\r\n alert(error.message)\r\n }\r\n } else {\r\n localStorage.setItem(id, JSON.stringify(this.templates))\r\n }\r\n }\r\n\r\n async importAll() {\r\n for (const file of this.importInput.files) {\r\n if (file.type === 'application/json' || file.name.endsWith('.json')) {\r\n const reader = new FileReader()\r\n reader.onload = async () => {\r\n const importFile = JSON.parse(reader.result as string)\r\n if (importFile?.templates) {\r\n for (const template of importFile.templates) {\r\n if (template?.name && template?.data) {\r\n this.templates.push(template)\r\n }\r\n }\r\n await this.store()\r\n }\r\n }\r\n await reader.readAsText(file)\r\n }\r\n }\r\n\r\n this.importInput.value = null\r\n\r\n this.close()\r\n }\r\n\r\n exportAll() {\r\n if (this.templates.length == 0) {\r\n alert('No templates to export.')\r\n return\r\n }\r\n\r\n const json = JSON.stringify({ templates: this.templates }, null, 2) // convert the data to a JSON string\r\n const blob = new Blob([json], { type: 'application/json' })\r\n const url = URL.createObjectURL(blob)\r\n const a = $el('a', {\r\n href: url,\r\n download: 'node_templates.json',\r\n style: { display: 'none' },\r\n parent: document.body\r\n })\r\n a.click()\r\n setTimeout(function () {\r\n a.remove()\r\n window.URL.revokeObjectURL(url)\r\n }, 0)\r\n }\r\n\r\n show() {\r\n // Show list of template names + delete button\r\n super.show(\r\n $el(\r\n 'div',\r\n {},\r\n this.templates.flatMap((t, i) => {\r\n let nameInput\r\n return [\r\n $el(\r\n 'div',\r\n {\r\n dataset: { id: i.toString() },\r\n className: 'templateManagerRow',\r\n style: {\r\n display: 'grid',\r\n gridTemplateColumns: '1fr auto',\r\n border: '1px dashed transparent',\r\n gap: '5px',\r\n backgroundColor: 'var(--comfy-menu-bg)'\r\n },\r\n ondragstart: (e) => {\r\n this.draggedEl = e.currentTarget\r\n e.currentTarget.style.opacity = '0.6'\r\n e.currentTarget.style.border = '1px dashed yellow'\r\n e.dataTransfer.effectAllowed = 'move'\r\n e.dataTransfer.setDragImage(this.emptyImg, 0, 0)\r\n },\r\n ondragend: (e) => {\r\n e.target.style.opacity = '1'\r\n e.currentTarget.style.border = '1px dashed transparent'\r\n e.currentTarget.removeAttribute('draggable')\r\n\r\n // rearrange the elements\r\n this.element\r\n .querySelectorAll('.templateManagerRow')\r\n .forEach((el: HTMLElement, i) => {\r\n var prev_i = Number.parseInt(el.dataset.id)\r\n\r\n if (el == this.draggedEl && prev_i != i) {\r\n this.templates.splice(\r\n i,\r\n 0,\r\n this.templates.splice(prev_i, 1)[0]\r\n )\r\n }\r\n el.dataset.id = i.toString()\r\n })\r\n this.store()\r\n },\r\n ondragover: (e) => {\r\n e.preventDefault()\r\n if (e.currentTarget == this.draggedEl) return\r\n\r\n let rect = e.currentTarget.getBoundingClientRect()\r\n if (e.clientY > rect.top + rect.height / 2) {\r\n e.currentTarget.parentNode.insertBefore(\r\n this.draggedEl,\r\n e.currentTarget.nextSibling\r\n )\r\n } else {\r\n e.currentTarget.parentNode.insertBefore(\r\n this.draggedEl,\r\n e.currentTarget\r\n )\r\n }\r\n }\r\n },\r\n [\r\n $el(\r\n 'label',\r\n {\r\n textContent: 'Name: ',\r\n style: {\r\n cursor: 'grab'\r\n },\r\n onmousedown: (e) => {\r\n // enable dragging only from the label\r\n if (e.target.localName == 'label')\r\n e.currentTarget.parentNode.draggable = 'true'\r\n }\r\n },\r\n [\r\n $el('input', {\r\n value: t.name,\r\n dataset: { name: t.name },\r\n style: {\r\n transitionProperty: 'background-color',\r\n transitionDuration: '0s'\r\n },\r\n onchange: (e) => {\r\n clearTimeout(this.saveVisualCue)\r\n var el = e.target\r\n var row = el.parentNode.parentNode\r\n this.templates[row.dataset.id].name =\r\n el.value.trim() || 'untitled'\r\n this.store()\r\n el.style.backgroundColor = 'rgb(40, 95, 40)'\r\n el.style.transitionDuration = '0s'\r\n // @ts-expect-error\r\n // In browser env the return value is number.\r\n this.saveVisualCue = setTimeout(function () {\r\n el.style.transitionDuration = '.7s'\r\n el.style.backgroundColor = 'var(--comfy-input-bg)'\r\n }, 15)\r\n },\r\n onkeypress: (e) => {\r\n var el = e.target\r\n clearTimeout(this.saveVisualCue)\r\n el.style.transitionDuration = '0s'\r\n el.style.backgroundColor = 'var(--comfy-input-bg)'\r\n },\r\n $: (el) => (nameInput = el)\r\n })\r\n ]\r\n ),\r\n $el('div', {}, [\r\n $el('button', {\r\n textContent: 'Export',\r\n style: {\r\n fontSize: '12px',\r\n fontWeight: 'normal'\r\n },\r\n onclick: (e) => {\r\n const json = JSON.stringify({ templates: [t] }, null, 2) // convert the data to a JSON string\r\n const blob = new Blob([json], {\r\n type: 'application/json'\r\n })\r\n const url = URL.createObjectURL(blob)\r\n const a = $el('a', {\r\n href: url,\r\n download: (nameInput.value || t.name) + '.json',\r\n style: { display: 'none' },\r\n parent: document.body\r\n })\r\n a.click()\r\n setTimeout(function () {\r\n a.remove()\r\n window.URL.revokeObjectURL(url)\r\n }, 0)\r\n }\r\n }),\r\n $el('button', {\r\n textContent: 'Delete',\r\n style: {\r\n fontSize: '12px',\r\n color: 'red',\r\n fontWeight: 'normal'\r\n },\r\n onclick: (e) => {\r\n const item = e.target.parentNode.parentNode\r\n item.parentNode.removeChild(item)\r\n this.templates.splice(item.dataset.id * 1, 1)\r\n this.store()\r\n // update the rows index, setTimeout ensures that the list is updated\r\n var that = this\r\n setTimeout(function () {\r\n that.element\r\n .querySelectorAll('.templateManagerRow')\r\n .forEach((el: HTMLElement, i) => {\r\n el.dataset.id = i.toString()\r\n })\r\n }, 0)\r\n }\r\n })\r\n ])\r\n ]\r\n )\r\n ]\r\n })\r\n )\r\n )\r\n }\r\n}\r\n\r\napp.registerExtension({\r\n name: id,\r\n setup() {\r\n const manage = new ManageTemplates()\r\n\r\n const clipboardAction = async (cb) => {\r\n // We use the clipboard functions but dont want to overwrite the current user clipboard\r\n // Restore it after we've run our callback\r\n const old = localStorage.getItem('litegrapheditor_clipboard')\r\n await cb()\r\n localStorage.setItem('litegrapheditor_clipboard', old)\r\n }\r\n\r\n const orig = LGraphCanvas.prototype.getCanvasMenuOptions\r\n LGraphCanvas.prototype.getCanvasMenuOptions = function () {\r\n const options = orig.apply(this, arguments)\r\n\r\n options.push(null)\r\n options.push({\r\n content: `Save Selected as Template`,\r\n disabled: !Object.keys(app.canvas.selected_nodes || {}).length,\r\n callback: () => {\r\n const name = prompt('Enter name')\r\n if (!name?.trim()) return\r\n\r\n clipboardAction(() => {\r\n app.canvas.copyToClipboard()\r\n let data = localStorage.getItem('litegrapheditor_clipboard')\r\n data = JSON.parse(data)\r\n const nodeIds = Object.keys(app.canvas.selected_nodes)\r\n for (let i = 0; i < nodeIds.length; i++) {\r\n const node = app.graph.getNodeById(Number.parseInt(nodeIds[i]))\r\n // @ts-expect-error\r\n const nodeData = node?.constructor.nodeData\r\n\r\n let groupData = GroupNodeHandler.getGroupData(node)\r\n if (groupData) {\r\n groupData = groupData.nodeData\r\n // @ts-expect-error\r\n if (!data.groupNodes) {\r\n // @ts-expect-error\r\n data.groupNodes = {}\r\n }\r\n // @ts-expect-error\r\n data.groupNodes[nodeData.name] = groupData\r\n // @ts-expect-error\r\n data.nodes[i].type = nodeData.name\r\n }\r\n }\r\n\r\n manage.templates.push({\r\n name,\r\n data: JSON.stringify(data)\r\n })\r\n manage.store()\r\n })\r\n }\r\n })\r\n\r\n // Map each template to a menu item\r\n const subItems = manage.templates.map((t) => {\r\n return {\r\n content: t.name,\r\n callback: () => {\r\n clipboardAction(async () => {\r\n const data = JSON.parse(t.data)\r\n await GroupNodeConfig.registerFromWorkflow(data.groupNodes, {})\r\n localStorage.setItem('litegrapheditor_clipboard', t.data)\r\n app.canvas.pasteFromClipboard()\r\n })\r\n }\r\n }\r\n })\r\n\r\n subItems.push(null, {\r\n content: 'Manage',\r\n callback: () => manage.show()\r\n })\r\n\r\n options.push({\r\n content: 'Node Templates',\r\n submenu: {\r\n options: subItems\r\n }\r\n })\r\n\r\n return options\r\n }\r\n }\r\n})\r\n","import { LiteGraph, LGraphCanvas } from '@comfyorg/litegraph'\r\nimport { app } from '../../scripts/app'\r\nimport { ComfyWidgets } from '../../scripts/widgets'\r\n// Node that add notes to your project\r\n\r\napp.registerExtension({\r\n name: 'Comfy.NoteNode',\r\n registerCustomNodes() {\r\n class NoteNode {\r\n static category: string\r\n\r\n color = LGraphCanvas.node_colors.yellow.color\r\n bgcolor = LGraphCanvas.node_colors.yellow.bgcolor\r\n groupcolor = LGraphCanvas.node_colors.yellow.groupcolor\r\n properties: { text: string }\r\n serialize_widgets: boolean\r\n isVirtualNode: boolean\r\n collapsable: boolean\r\n title_mode: number\r\n\r\n constructor() {\r\n if (!this.properties) {\r\n this.properties = { text: '' }\r\n }\r\n ComfyWidgets.STRING(\r\n // @ts-expect-error\r\n // Should we extends LGraphNode?\r\n this,\r\n '',\r\n ['', { default: this.properties.text, multiline: true }],\r\n app\r\n )\r\n\r\n this.serialize_widgets = true\r\n this.isVirtualNode = true\r\n }\r\n }\r\n\r\n // Load default visibility\r\n\r\n LiteGraph.registerNodeType(\r\n 'Note',\r\n // @ts-expect-error\r\n Object.assign(NoteNode, {\r\n title_mode: LiteGraph.NORMAL_TITLE,\r\n title: 'Note',\r\n collapsable: true\r\n })\r\n )\r\n\r\n NoteNode.category = 'utils'\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport { mergeIfValid, getWidgetConfig, setWidgetConfig } from './widgetInputs'\r\nimport { LiteGraph, LGraphCanvas, LGraphNode } from '@comfyorg/litegraph'\r\n\r\n// Node that allows you to redirect connections for cleaner graphs\r\n\r\napp.registerExtension({\r\n name: 'Comfy.RerouteNode',\r\n registerCustomNodes(app) {\r\n interface RerouteNode extends LGraphNode {\r\n __outputType?: string\r\n }\r\n\r\n class RerouteNode {\r\n static category: string | undefined\r\n static defaultVisibility = false\r\n\r\n constructor() {\r\n if (!this.properties) {\r\n this.properties = {}\r\n }\r\n this.properties.showOutputText = RerouteNode.defaultVisibility\r\n this.properties.horizontal = false\r\n\r\n this.addInput('', '*')\r\n this.addOutput(this.properties.showOutputText ? '*' : '', '*')\r\n\r\n this.onAfterGraphConfigured = function () {\r\n requestAnimationFrame(() => {\r\n this.onConnectionsChange(LiteGraph.INPUT, null, true, null)\r\n })\r\n }\r\n\r\n this.onConnectionsChange = function (\r\n type,\r\n index,\r\n connected,\r\n link_info\r\n ) {\r\n this.applyOrientation()\r\n\r\n // Prevent multiple connections to different types when we have no input\r\n if (connected && type === LiteGraph.OUTPUT) {\r\n // Ignore wildcard nodes as these will be updated to real types\r\n const types = new Set(\r\n this.outputs[0].links\r\n .map((l) => app.graph.links[l].type)\r\n .filter((t) => t !== '*')\r\n )\r\n if (types.size > 1) {\r\n const linksToDisconnect = []\r\n for (let i = 0; i < this.outputs[0].links.length - 1; i++) {\r\n const linkId = this.outputs[0].links[i]\r\n const link = app.graph.links[linkId]\r\n linksToDisconnect.push(link)\r\n }\r\n for (const link of linksToDisconnect) {\r\n const node = app.graph.getNodeById(link.target_id)\r\n node.disconnectInput(link.target_slot)\r\n }\r\n }\r\n }\r\n\r\n // Find root input\r\n let currentNode = this\r\n let updateNodes = []\r\n let inputType = null\r\n let inputNode = null\r\n while (currentNode) {\r\n updateNodes.unshift(currentNode)\r\n const linkId = currentNode.inputs[0].link\r\n if (linkId !== null) {\r\n const link = app.graph.links[linkId]\r\n if (!link) return\r\n const node = app.graph.getNodeById(link.origin_id)\r\n const type = node.constructor.type\r\n if (type === 'Reroute') {\r\n if (node === this) {\r\n // We've found a circle\r\n currentNode.disconnectInput(link.target_slot)\r\n currentNode = null\r\n } else {\r\n // Move the previous node\r\n currentNode = node\r\n }\r\n } else {\r\n // We've found the end\r\n inputNode = currentNode\r\n inputType = node.outputs[link.origin_slot]?.type ?? null\r\n break\r\n }\r\n } else {\r\n // This path has no input node\r\n currentNode = null\r\n break\r\n }\r\n }\r\n\r\n // Find all outputs\r\n const nodes = [this]\r\n let outputType = null\r\n while (nodes.length) {\r\n currentNode = nodes.pop()\r\n const outputs =\r\n (currentNode.outputs ? currentNode.outputs[0].links : []) || []\r\n if (outputs.length) {\r\n for (const linkId of outputs) {\r\n const link = app.graph.links[linkId]\r\n\r\n // When disconnecting sometimes the link is still registered\r\n if (!link) continue\r\n\r\n const node = app.graph.getNodeById(link.target_id)\r\n const type = node.constructor.type\r\n\r\n if (type === 'Reroute') {\r\n // Follow reroute nodes\r\n nodes.push(node)\r\n updateNodes.push(node)\r\n } else {\r\n // We've found an output\r\n const nodeOutType =\r\n node.inputs &&\r\n node.inputs[link?.target_slot] &&\r\n node.inputs[link.target_slot].type\r\n ? node.inputs[link.target_slot].type\r\n : null\r\n if (\r\n inputType &&\r\n inputType !== '*' &&\r\n nodeOutType !== inputType\r\n ) {\r\n // The output doesnt match our input so disconnect it\r\n node.disconnectInput(link.target_slot)\r\n } else {\r\n outputType = nodeOutType\r\n }\r\n }\r\n }\r\n } else {\r\n // No more outputs for this path\r\n }\r\n }\r\n\r\n const displayType = inputType || outputType || '*'\r\n const color = LGraphCanvas.link_type_colors[displayType]\r\n\r\n let widgetConfig\r\n let targetWidget\r\n let widgetType\r\n // Update the types of each node\r\n for (const node of updateNodes) {\r\n // If we dont have an input type we are always wildcard but we'll show the output type\r\n // This lets you change the output link to a different type and all nodes will update\r\n node.outputs[0].type = inputType || '*'\r\n node.__outputType = displayType\r\n node.outputs[0].name = node.properties.showOutputText\r\n ? displayType\r\n : ''\r\n node.size = node.computeSize()\r\n node.applyOrientation()\r\n\r\n for (const l of node.outputs[0].links || []) {\r\n const link = app.graph.links[l]\r\n if (link) {\r\n link.color = color\r\n\r\n if (app.configuringGraph) continue\r\n const targetNode = app.graph.getNodeById(link.target_id)\r\n const targetInput = targetNode.inputs?.[link.target_slot]\r\n if (targetInput?.widget) {\r\n const config = getWidgetConfig(targetInput)\r\n if (!widgetConfig) {\r\n widgetConfig = config[1] ?? {}\r\n widgetType = config[0]\r\n }\r\n if (!targetWidget) {\r\n targetWidget = targetNode.widgets?.find(\r\n (w) => w.name === targetInput.widget.name\r\n )\r\n }\r\n\r\n const merged = mergeIfValid(targetInput, [\r\n config[0],\r\n widgetConfig\r\n ])\r\n if (merged.customConfig) {\r\n widgetConfig = merged.customConfig\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n for (const node of updateNodes) {\r\n if (widgetConfig && outputType) {\r\n node.inputs[0].widget = { name: 'value' }\r\n setWidgetConfig(\r\n node.inputs[0],\r\n [widgetType ?? displayType, widgetConfig],\r\n targetWidget\r\n )\r\n } else {\r\n setWidgetConfig(node.inputs[0], null)\r\n }\r\n }\r\n\r\n if (inputNode) {\r\n const link = app.graph.links[inputNode.inputs[0].link]\r\n if (link) {\r\n link.color = color\r\n }\r\n }\r\n }\r\n\r\n this.clone = function () {\r\n const cloned = RerouteNode.prototype.clone.apply(this)\r\n cloned.removeOutput(0)\r\n cloned.addOutput(this.properties.showOutputText ? '*' : '', '*')\r\n cloned.size = cloned.computeSize()\r\n return cloned\r\n }\r\n\r\n // This node is purely frontend and does not impact the resulting prompt so should not be serialized\r\n this.isVirtualNode = true\r\n }\r\n\r\n getExtraMenuOptions(_, options) {\r\n options.unshift(\r\n {\r\n content:\r\n (this.properties.showOutputText ? 'Hide' : 'Show') + ' Type',\r\n callback: () => {\r\n this.properties.showOutputText = !this.properties.showOutputText\r\n if (this.properties.showOutputText) {\r\n this.outputs[0].name =\r\n this.__outputType || (this.outputs[0].type as string)\r\n } else {\r\n this.outputs[0].name = ''\r\n }\r\n this.size = this.computeSize()\r\n this.applyOrientation()\r\n app.graph.setDirtyCanvas(true, true)\r\n }\r\n },\r\n {\r\n content:\r\n (RerouteNode.defaultVisibility ? 'Hide' : 'Show') +\r\n ' Type By Default',\r\n callback: () => {\r\n RerouteNode.setDefaultTextVisibility(\r\n !RerouteNode.defaultVisibility\r\n )\r\n }\r\n },\r\n {\r\n // naming is inverted with respect to LiteGraphNode.horizontal\r\n // LiteGraphNode.horizontal == true means that\r\n // each slot in the inputs and outputs are laid out horizontally,\r\n // which is the opposite of the visual orientation of the inputs and outputs as a node\r\n content:\r\n 'Set ' + (this.properties.horizontal ? 'Horizontal' : 'Vertical'),\r\n callback: () => {\r\n this.properties.horizontal = !this.properties.horizontal\r\n this.applyOrientation()\r\n }\r\n }\r\n )\r\n }\r\n applyOrientation() {\r\n this.horizontal = this.properties.horizontal\r\n if (this.horizontal) {\r\n // we correct the input position, because LiteGraphNode.horizontal\r\n // doesn't account for title presence\r\n // which reroute nodes don't have\r\n this.inputs[0].pos = [this.size[0] / 2, 0]\r\n } else {\r\n delete this.inputs[0].pos\r\n }\r\n app.graph.setDirtyCanvas(true, true)\r\n }\r\n\r\n computeSize(): [number, number] {\r\n return [\r\n this.properties.showOutputText && this.outputs && this.outputs.length\r\n ? Math.max(\r\n 75,\r\n LiteGraph.NODE_TEXT_SIZE * this.outputs[0].name.length * 0.6 +\r\n 40\r\n )\r\n : 75,\r\n 26\r\n ]\r\n }\r\n\r\n static setDefaultTextVisibility(visible) {\r\n RerouteNode.defaultVisibility = visible\r\n if (visible) {\r\n localStorage['Comfy.RerouteNode.DefaultVisibility'] = 'true'\r\n } else {\r\n delete localStorage['Comfy.RerouteNode.DefaultVisibility']\r\n }\r\n }\r\n }\r\n\r\n // Load default visibility\r\n RerouteNode.setDefaultTextVisibility(\r\n !!localStorage['Comfy.RerouteNode.DefaultVisibility']\r\n )\r\n\r\n LiteGraph.registerNodeType(\r\n 'Reroute',\r\n Object.assign(RerouteNode, {\r\n title_mode: LiteGraph.NO_TITLE,\r\n title: 'Reroute',\r\n collapsable: false\r\n })\r\n )\r\n\r\n RerouteNode.category = 'utils'\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport { applyTextReplacements } from '../../scripts/utils'\r\n// Use widget values and dates in output filenames\r\n\r\napp.registerExtension({\r\n name: 'Comfy.SaveImageExtraOutput',\r\n async beforeRegisterNodeDef(nodeType, nodeData, app) {\r\n if (nodeData.name === 'SaveImage' || nodeData.name === 'SaveAnimatedWEBP') {\r\n const onNodeCreated = nodeType.prototype.onNodeCreated\r\n // When the SaveImage node is created we want to override the serialization of the output name widget to run our S&R\r\n nodeType.prototype.onNodeCreated = function () {\r\n const r = onNodeCreated\r\n ? onNodeCreated.apply(this, arguments)\r\n : undefined\r\n\r\n const widget = this.widgets.find((w) => w.name === 'filename_prefix')\r\n widget.serializeValue = () => {\r\n return applyTextReplacements(app, widget.value)\r\n }\r\n\r\n return r\r\n }\r\n } else {\r\n // When any other node is created add a property to alias the node\r\n const onNodeCreated = nodeType.prototype.onNodeCreated\r\n nodeType.prototype.onNodeCreated = function () {\r\n const r = onNodeCreated\r\n ? onNodeCreated.apply(this, arguments)\r\n : undefined\r\n\r\n if (!this.properties || !('Node name for S&R' in this.properties)) {\r\n this.addProperty('Node name for S&R', this.constructor.type, 'string')\r\n }\r\n\r\n return r\r\n }\r\n }\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph'\r\n\r\nlet touchZooming\r\nlet touchCount = 0\r\n\r\napp.registerExtension({\r\n name: 'Comfy.SimpleTouchSupport',\r\n setup() {\r\n let zoomPos\r\n let touchTime\r\n let lastTouch\r\n\r\n function getMultiTouchPos(e) {\r\n return Math.hypot(\r\n e.touches[0].clientX - e.touches[1].clientX,\r\n e.touches[0].clientY - e.touches[1].clientY\r\n )\r\n }\r\n\r\n app.canvasEl.addEventListener(\r\n 'touchstart',\r\n (e) => {\r\n touchCount++\r\n lastTouch = null\r\n if (e.touches?.length === 1) {\r\n // Store start time for press+hold for context menu\r\n touchTime = new Date()\r\n lastTouch = e.touches[0]\r\n } else {\r\n touchTime = null\r\n if (e.touches?.length === 2) {\r\n // Store center pos for zoom\r\n zoomPos = getMultiTouchPos(e)\r\n app.canvas.pointer_is_down = false\r\n }\r\n }\r\n },\r\n true\r\n )\r\n\r\n app.canvasEl.addEventListener('touchend', (e: TouchEvent) => {\r\n touchZooming = false\r\n touchCount = e.touches?.length ?? touchCount - 1\r\n if (touchTime && !e.touches?.length) {\r\n if (new Date().getTime() - touchTime > 600) {\r\n try {\r\n // hack to get litegraph to use this event\r\n e.constructor = CustomEvent\r\n } catch (error) {}\r\n // @ts-expect-error\r\n e.clientX = lastTouch.clientX\r\n // @ts-expect-error\r\n e.clientY = lastTouch.clientY\r\n\r\n app.canvas.pointer_is_down = true\r\n // @ts-expect-error\r\n app.canvas._mousedown_callback(e)\r\n }\r\n touchTime = null\r\n }\r\n })\r\n\r\n app.canvasEl.addEventListener(\r\n 'touchmove',\r\n (e) => {\r\n touchTime = null\r\n if (e.touches?.length === 2) {\r\n app.canvas.pointer_is_down = false\r\n touchZooming = true\r\n // @ts-expect-error\r\n LiteGraph.closeAllContextMenus()\r\n // @ts-expect-error\r\n app.canvas.search_box?.close()\r\n const newZoomPos = getMultiTouchPos(e)\r\n\r\n const midX = (e.touches[0].clientX + e.touches[1].clientX) / 2\r\n const midY = (e.touches[0].clientY + e.touches[1].clientY) / 2\r\n\r\n let scale = app.canvas.ds.scale\r\n const diff = zoomPos - newZoomPos\r\n if (diff > 0.5) {\r\n scale *= 1 / 1.07\r\n } else if (diff < -0.5) {\r\n scale *= 1.07\r\n }\r\n app.canvas.ds.changeScale(scale, [midX, midY])\r\n app.canvas.setDirty(true, true)\r\n zoomPos = newZoomPos\r\n }\r\n },\r\n true\r\n )\r\n }\r\n})\r\n\r\nconst processMouseDown = LGraphCanvas.prototype.processMouseDown\r\nLGraphCanvas.prototype.processMouseDown = function (e) {\r\n if (touchZooming || touchCount) {\r\n return\r\n }\r\n return processMouseDown.apply(this, arguments)\r\n}\r\n\r\nconst processMouseMove = LGraphCanvas.prototype.processMouseMove\r\nLGraphCanvas.prototype.processMouseMove = function (e) {\r\n if (touchZooming || touchCount > 1) {\r\n return\r\n }\r\n return processMouseMove.apply(this, arguments)\r\n}\r\n","import { app } from '../../scripts/app'\r\nimport { ComfyWidgets } from '../../scripts/widgets'\r\nimport { LiteGraph } from '@comfyorg/litegraph'\r\n// Adds defaults for quickly adding nodes with middle click on the input/output\r\n\r\napp.registerExtension({\r\n name: 'Comfy.SlotDefaults',\r\n suggestionsNumber: null,\r\n init() {\r\n LiteGraph.search_filter_enabled = true\r\n LiteGraph.middle_click_slot_add_default_node = true\r\n this.suggestionsNumber = app.ui.settings.addSetting({\r\n id: 'Comfy.NodeSuggestions.number',\r\n name: 'Number of nodes suggestions',\r\n type: 'slider',\r\n attrs: {\r\n min: 1,\r\n max: 100,\r\n step: 1\r\n },\r\n defaultValue: 5,\r\n onChange: (newVal, oldVal) => {\r\n this.setDefaults(newVal)\r\n }\r\n })\r\n },\r\n slot_types_default_out: {},\r\n slot_types_default_in: {},\r\n async beforeRegisterNodeDef(nodeType, nodeData, app) {\r\n var nodeId = nodeData.name\r\n var inputs = []\r\n inputs = nodeData['input']['required'] //only show required inputs to reduce the mess also not logical to create node with optional inputs\r\n for (const inputKey in inputs) {\r\n var input = inputs[inputKey]\r\n if (typeof input[0] !== 'string') continue\r\n\r\n var type = input[0]\r\n if (type in ComfyWidgets) {\r\n var customProperties = input[1]\r\n if (!customProperties?.forceInput) continue //ignore widgets that don't force input\r\n }\r\n\r\n if (!(type in this.slot_types_default_out)) {\r\n this.slot_types_default_out[type] = ['Reroute']\r\n }\r\n if (this.slot_types_default_out[type].includes(nodeId)) continue\r\n this.slot_types_default_out[type].push(nodeId)\r\n\r\n // Input types have to be stored as lower case\r\n // Store each node that can handle this input type\r\n const lowerType = type.toLocaleLowerCase()\r\n if (!(lowerType in LiteGraph.registered_slot_in_types)) {\r\n LiteGraph.registered_slot_in_types[lowerType] = { nodes: [] }\r\n }\r\n LiteGraph.registered_slot_in_types[lowerType].nodes.push(\r\n nodeType.comfyClass\r\n )\r\n }\r\n\r\n var outputs = nodeData['output']\r\n for (const key in outputs) {\r\n var type = outputs[key] as string\r\n if (!(type in this.slot_types_default_in)) {\r\n this.slot_types_default_in[type] = ['Reroute'] // [\"Reroute\", \"Primitive\"]; primitive doesn't always work :'()\r\n }\r\n\r\n this.slot_types_default_in[type].push(nodeId)\r\n\r\n // Store each node that can handle this output type\r\n if (!(type in LiteGraph.registered_slot_out_types)) {\r\n LiteGraph.registered_slot_out_types[type] = { nodes: [] }\r\n }\r\n LiteGraph.registered_slot_out_types[type].nodes.push(nodeType.comfyClass)\r\n\r\n if (!LiteGraph.slot_types_out.includes(type)) {\r\n LiteGraph.slot_types_out.push(type)\r\n }\r\n }\r\n var maxNum = this.suggestionsNumber.value\r\n this.setDefaults(maxNum)\r\n },\r\n setDefaults(maxNum) {\r\n LiteGraph.slot_types_default_out = {}\r\n LiteGraph.slot_types_default_in = {}\r\n\r\n for (const type in this.slot_types_default_out) {\r\n LiteGraph.slot_types_default_out[type] = this.slot_types_default_out[\r\n type\r\n ].slice(0, maxNum)\r\n }\r\n for (const type in this.slot_types_default_in) {\r\n LiteGraph.slot_types_default_in[type] = this.slot_types_default_in[\r\n type\r\n ].slice(0, maxNum)\r\n }\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport {\r\n LGraphCanvas,\r\n LGraphNode,\r\n LGraphGroup,\r\n LiteGraph\r\n} from '@comfyorg/litegraph'\r\n\r\n// Shift + drag/resize to snap to grid\r\n\r\n/** Rounds a Vector2 in-place to the current CANVAS_GRID_SIZE. */\r\nfunction roundVectorToGrid(vec) {\r\n vec[0] =\r\n LiteGraph.CANVAS_GRID_SIZE * Math.round(vec[0] / LiteGraph.CANVAS_GRID_SIZE)\r\n vec[1] =\r\n LiteGraph.CANVAS_GRID_SIZE * Math.round(vec[1] / LiteGraph.CANVAS_GRID_SIZE)\r\n return vec\r\n}\r\n\r\napp.registerExtension({\r\n name: 'Comfy.SnapToGrid',\r\n init() {\r\n // Add setting to control grid size\r\n app.ui.settings.addSetting({\r\n id: 'Comfy.SnapToGrid.GridSize',\r\n name: 'Grid Size',\r\n type: 'slider',\r\n attrs: {\r\n min: 1,\r\n max: 500\r\n },\r\n tooltip:\r\n 'When dragging and resizing nodes while holding shift they will be aligned to the grid, this controls the size of that grid.',\r\n defaultValue: LiteGraph.CANVAS_GRID_SIZE,\r\n onChange(value) {\r\n LiteGraph.CANVAS_GRID_SIZE = +value\r\n }\r\n })\r\n\r\n // After moving a node, if the shift key is down align it to grid\r\n const onNodeMoved = app.canvas.onNodeMoved\r\n app.canvas.onNodeMoved = function (node) {\r\n const r = onNodeMoved?.apply(this, arguments)\r\n\r\n if (app.shiftDown) {\r\n // Ensure all selected nodes are realigned\r\n for (const id in this.selected_nodes) {\r\n this.selected_nodes[id].alignToGrid()\r\n }\r\n }\r\n\r\n return r\r\n }\r\n\r\n // When a node is added, add a resize handler to it so we can fix align the size with the grid\r\n const onNodeAdded = app.graph.onNodeAdded\r\n app.graph.onNodeAdded = function (node) {\r\n const onResize = node.onResize\r\n node.onResize = function () {\r\n if (app.shiftDown) {\r\n roundVectorToGrid(node.size)\r\n }\r\n return onResize?.apply(this, arguments)\r\n }\r\n return onNodeAdded?.apply(this, arguments)\r\n }\r\n\r\n // Draw a preview of where the node will go if holding shift and the node is selected\r\n const origDrawNode = LGraphCanvas.prototype.drawNode\r\n LGraphCanvas.prototype.drawNode = function (node, ctx) {\r\n if (\r\n app.shiftDown &&\r\n this.node_dragged &&\r\n node.id in this.selected_nodes\r\n ) {\r\n const [x, y] = roundVectorToGrid([...node.pos])\r\n const shiftX = x - node.pos[0]\r\n let shiftY = y - node.pos[1]\r\n\r\n let w, h\r\n if (node.flags.collapsed) {\r\n // @ts-expect-error\r\n w = node._collapsed_width\r\n h = LiteGraph.NODE_TITLE_HEIGHT\r\n shiftY -= LiteGraph.NODE_TITLE_HEIGHT\r\n } else {\r\n w = node.size[0]\r\n h = node.size[1]\r\n // @ts-expect-error\r\n let titleMode = node.constructor.title_mode\r\n if (\r\n titleMode !== LiteGraph.TRANSPARENT_TITLE &&\r\n titleMode !== LiteGraph.NO_TITLE\r\n ) {\r\n h += LiteGraph.NODE_TITLE_HEIGHT\r\n shiftY -= LiteGraph.NODE_TITLE_HEIGHT\r\n }\r\n }\r\n const f = ctx.fillStyle\r\n ctx.fillStyle = 'rgba(100, 100, 100, 0.5)'\r\n ctx.fillRect(shiftX, shiftY, w, h)\r\n ctx.fillStyle = f\r\n }\r\n\r\n return origDrawNode.apply(this, arguments)\r\n }\r\n\r\n /**\r\n * The currently moving, selected group only. Set after the `selected_group` has actually started\r\n * moving.\r\n */\r\n let selectedAndMovingGroup: LGraphGroup | null = null\r\n\r\n /**\r\n * Handles moving a group; tracking when a group has been moved (to show the ghost in `drawGroups`\r\n * below) as well as handle the last move call from LiteGraph's `processMouseUp`.\r\n */\r\n const groupMove = LGraphGroup.prototype.move\r\n LGraphGroup.prototype.move = function (deltax, deltay, ignore_nodes) {\r\n const v = groupMove.apply(this, arguments)\r\n // When we've started moving, set `selectedAndMovingGroup` as LiteGraph sets `selected_group`\r\n // too eagerly and we don't want to behave like we're moving until we get a delta.\r\n if (\r\n !selectedAndMovingGroup &&\r\n app.canvas.selected_group === this &&\r\n (deltax || deltay)\r\n ) {\r\n selectedAndMovingGroup = this\r\n }\r\n\r\n // LiteGraph will call group.move both on mouse-move as well as mouse-up though we only want\r\n // to snap on a mouse-up which we can determine by checking if `app.canvas.last_mouse_dragging`\r\n // has been set to `false`. Essentially, this check here is the equivalent to calling an\r\n // `LGraphGroup.prototype.onNodeMoved` if it had existed.\r\n if (app.canvas.last_mouse_dragging === false && app.shiftDown) {\r\n // After moving a group (while app.shiftDown), snap all the child nodes and, finally,\r\n // align the group itself.\r\n this.recomputeInsideNodes()\r\n for (const node of this._nodes) {\r\n node.alignToGrid()\r\n }\r\n LGraphNode.prototype.alignToGrid.apply(this)\r\n }\r\n return v\r\n }\r\n\r\n /**\r\n * Handles drawing a group when, snapping the size when one is actively being resized tracking and/or\r\n * drawing a ghost box when one is actively being moved. This mimics the node snapping behavior for\r\n * both.\r\n */\r\n const drawGroups = LGraphCanvas.prototype.drawGroups\r\n LGraphCanvas.prototype.drawGroups = function (canvas, ctx) {\r\n if (this.selected_group && app.shiftDown) {\r\n if (this.selected_group_resizing) {\r\n roundVectorToGrid(this.selected_group.size)\r\n } else if (selectedAndMovingGroup) {\r\n // @ts-expect-error\r\n const [x, y] = roundVectorToGrid([...selectedAndMovingGroup.pos])\r\n const f = ctx.fillStyle\r\n const s = ctx.strokeStyle\r\n ctx.fillStyle = 'rgba(100, 100, 100, 0.33)'\r\n ctx.strokeStyle = 'rgba(100, 100, 100, 0.66)'\r\n // @ts-expect-error\r\n ctx.rect(x, y, ...selectedAndMovingGroup.size)\r\n ctx.fill()\r\n ctx.stroke()\r\n ctx.fillStyle = f\r\n ctx.strokeStyle = s\r\n }\r\n } else if (!this.selected_group) {\r\n selectedAndMovingGroup = null\r\n }\r\n return drawGroups.apply(this, arguments)\r\n }\r\n\r\n /** Handles adding a group in a snapping-enabled state. */\r\n const onGroupAdd = LGraphCanvas.onGroupAdd\r\n LGraphCanvas.onGroupAdd = function () {\r\n const v = onGroupAdd.apply(app.canvas, arguments)\r\n if (app.shiftDown) {\r\n // @ts-expect-error\r\n const lastGroup = app.graph._groups[app.graph._groups.length - 1]\r\n if (lastGroup) {\r\n // @ts-expect-error\r\n roundVectorToGrid(lastGroup.pos)\r\n // @ts-expect-error\r\n roundVectorToGrid(lastGroup.size)\r\n }\r\n }\r\n return v\r\n }\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport { ComfyNodeDef } from '@/types/apiTypes'\r\n\r\n// Adds an upload button to the nodes\r\n\r\napp.registerExtension({\r\n name: 'Comfy.UploadImage',\r\n async beforeRegisterNodeDef(nodeType, nodeData: ComfyNodeDef, app) {\r\n if (nodeData?.input?.required?.image?.[1]?.image_upload === true) {\r\n nodeData.input.required.upload = ['IMAGEUPLOAD']\r\n }\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport { api } from '../../scripts/api'\r\n\r\nconst WEBCAM_READY = Symbol()\r\n\r\napp.registerExtension({\r\n name: 'Comfy.WebcamCapture',\r\n getCustomWidgets(app) {\r\n return {\r\n WEBCAM(node, inputName) {\r\n let res\r\n node[WEBCAM_READY] = new Promise((resolve) => (res = resolve))\r\n\r\n const container = document.createElement('div')\r\n container.style.background = 'rgba(0,0,0,0.25)'\r\n container.style.textAlign = 'center'\r\n\r\n const video = document.createElement('video')\r\n video.style.height = video.style.width = '100%'\r\n\r\n const loadVideo = async () => {\r\n try {\r\n const stream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: false\r\n })\r\n container.replaceChildren(video)\r\n\r\n setTimeout(() => res(video), 500) // Fallback as loadedmetadata doesnt fire sometimes?\r\n video.addEventListener('loadedmetadata', () => res(video), false)\r\n video.srcObject = stream\r\n video.play()\r\n } catch (error) {\r\n const label = document.createElement('div')\r\n label.style.color = 'red'\r\n label.style.overflow = 'auto'\r\n label.style.maxHeight = '100%'\r\n label.style.whiteSpace = 'pre-wrap'\r\n\r\n if (window.isSecureContext) {\r\n label.textContent =\r\n 'Unable to load webcam, please ensure access is granted:\\n' +\r\n error.message\r\n } else {\r\n label.textContent =\r\n 'Unable to load webcam. A secure context is required, if you are not accessing ComfyUI on localhost (127.0.0.1) you will have to enable TLS (https)\\n\\n' +\r\n error.message\r\n }\r\n\r\n container.replaceChildren(label)\r\n }\r\n }\r\n\r\n loadVideo()\r\n\r\n return { widget: node.addDOMWidget(inputName, 'WEBCAM', container) }\r\n }\r\n }\r\n },\r\n nodeCreated(node) {\r\n if ((node.type, node.constructor.comfyClass !== 'WebcamCapture')) return\r\n\r\n let video\r\n const camera = node.widgets.find((w) => w.name === 'image')\r\n const w = node.widgets.find((w) => w.name === 'width')\r\n const h = node.widgets.find((w) => w.name === 'height')\r\n const captureOnQueue = node.widgets.find(\r\n (w) => w.name === 'capture_on_queue'\r\n )\r\n\r\n const canvas = document.createElement('canvas')\r\n\r\n const capture = () => {\r\n canvas.width = w.value\r\n canvas.height = h.value\r\n const ctx = canvas.getContext('2d')\r\n ctx.drawImage(video, 0, 0, w.value, h.value)\r\n const data = canvas.toDataURL('image/png')\r\n\r\n const img = new Image()\r\n img.onload = () => {\r\n node.imgs = [img]\r\n app.graph.setDirtyCanvas(true)\r\n requestAnimationFrame(() => {\r\n node.setSizeForImage?.()\r\n })\r\n }\r\n img.src = data\r\n }\r\n\r\n const btn = node.addWidget(\r\n 'button',\r\n 'waiting for camera...',\r\n 'capture',\r\n capture\r\n )\r\n btn.disabled = true\r\n btn.serializeValue = () => undefined\r\n\r\n camera.serializeValue = async () => {\r\n if (captureOnQueue.value) {\r\n capture()\r\n } else if (!node.imgs?.length) {\r\n const err = `No webcam image captured`\r\n alert(err)\r\n throw new Error(err)\r\n }\r\n\r\n // Upload image to temp storage\r\n const blob = await new Promise((r) => canvas.toBlob(r))\r\n const name = `${+new Date()}.png`\r\n const file = new File([blob], name)\r\n const body = new FormData()\r\n body.append('image', file)\r\n body.append('subfolder', 'webcam')\r\n body.append('type', 'temp')\r\n const resp = await api.fetchApi('/upload/image', {\r\n method: 'POST',\r\n body\r\n })\r\n if (resp.status !== 200) {\r\n const err = `Error uploading camera image: ${resp.status} - ${resp.statusText}`\r\n alert(err)\r\n throw new Error(err)\r\n }\r\n return `webcam/${name} [temp]`\r\n }\r\n\r\n node[WEBCAM_READY].then((v) => {\r\n video = v\r\n // If width isnt specified then use video output resolution\r\n if (!w.value) {\r\n w.value = video.videoWidth || 640\r\n h.value = video.videoHeight || 480\r\n }\r\n btn.disabled = false\r\n btn.label = 'capture'\r\n })\r\n }\r\n})\r\n","import { app } from '../../scripts/app'\r\nimport { api } from '../../scripts/api'\r\nimport type { IWidget } from '@comfyorg/litegraph'\r\nimport type { DOMWidget } from '@/scripts/domWidget'\r\nimport { ComfyNodeDef } from '@/types/apiTypes'\r\n\r\ntype FolderType = 'input' | 'output' | 'temp'\r\n\r\nfunction splitFilePath(path: string): [string, string] {\r\n const folder_separator = path.lastIndexOf('/')\r\n if (folder_separator === -1) {\r\n return ['', path]\r\n }\r\n return [\r\n path.substring(0, folder_separator),\r\n path.substring(folder_separator + 1)\r\n ]\r\n}\r\n\r\nfunction getResourceURL(\r\n subfolder: string,\r\n filename: string,\r\n type: FolderType = 'input'\r\n): string {\r\n const params = [\r\n 'filename=' + encodeURIComponent(filename),\r\n 'type=' + type,\r\n 'subfolder=' + subfolder,\r\n app.getRandParam().substring(1)\r\n ].join('&')\r\n\r\n return `/view?${params}`\r\n}\r\n\r\nasync function uploadFile(\r\n audioWidget: IWidget,\r\n audioUIWidget: DOMWidget,\r\n file: File,\r\n updateNode: boolean,\r\n pasted: boolean = false\r\n) {\r\n try {\r\n // Wrap file in formdata so it includes filename\r\n const body = new FormData()\r\n body.append('image', file)\r\n if (pasted) body.append('subfolder', 'pasted')\r\n const resp = await api.fetchApi('/upload/image', {\r\n method: 'POST',\r\n body\r\n })\r\n\r\n if (resp.status === 200) {\r\n const data = await resp.json()\r\n // Add the file to the dropdown list and update the widget value\r\n let path = data.name\r\n if (data.subfolder) path = data.subfolder + '/' + path\r\n\r\n if (!audioWidget.options.values.includes(path)) {\r\n audioWidget.options.values.push(path)\r\n }\r\n\r\n if (updateNode) {\r\n audioUIWidget.element.src = api.apiURL(\r\n getResourceURL(...splitFilePath(path))\r\n )\r\n audioWidget.value = path\r\n }\r\n } else {\r\n alert(resp.status + ' - ' + resp.statusText)\r\n }\r\n } catch (error) {\r\n alert(error)\r\n }\r\n}\r\n\r\n// AudioWidget MUST be registered first, as AUDIOUPLOAD depends on AUDIO_UI to be\r\n// present.\r\napp.registerExtension({\r\n name: 'Comfy.AudioWidget',\r\n async beforeRegisterNodeDef(nodeType, nodeData) {\r\n if (\r\n ['LoadAudio', 'SaveAudio', 'PreviewAudio'].includes(nodeType.comfyClass)\r\n ) {\r\n nodeData.input.required.audioUI = ['AUDIO_UI']\r\n }\r\n },\r\n getCustomWidgets() {\r\n return {\r\n AUDIO_UI(node, inputName: string) {\r\n const audio = document.createElement('audio')\r\n audio.controls = true\r\n audio.classList.add('comfy-audio')\r\n audio.setAttribute('name', 'media')\r\n\r\n const audioUIWidget: DOMWidget = node.addDOMWidget(\r\n inputName,\r\n /* name=*/ 'audioUI',\r\n audio\r\n )\r\n // @ts-expect-error\r\n // TODO: Sort out the DOMWidget type.\r\n audioUIWidget.serialize = false\r\n\r\n const isOutputNode = node.constructor.nodeData.output_node\r\n if (isOutputNode) {\r\n // Hide the audio widget when there is no audio initially.\r\n audioUIWidget.element.classList.add('empty-audio-widget')\r\n // Populate the audio widget UI on node execution.\r\n const onExecuted = node.onExecuted\r\n node.onExecuted = function (message) {\r\n onExecuted?.apply(this, arguments)\r\n const audios = message.audio\r\n if (!audios) return\r\n const audio = audios[0]\r\n audioUIWidget.element.src = api.apiURL(\r\n getResourceURL(audio.subfolder, audio.filename, audio.type)\r\n )\r\n audioUIWidget.element.classList.remove('empty-audio-widget')\r\n }\r\n }\r\n return { widget: audioUIWidget }\r\n }\r\n }\r\n },\r\n onNodeOutputsUpdated(nodeOutputs: Record) {\r\n for (const [nodeId, output] of Object.entries(nodeOutputs)) {\r\n const node = app.graph.getNodeById(Number.parseInt(nodeId))\r\n if ('audio' in output) {\r\n const audioUIWidget = node.widgets.find(\r\n (w) => w.name === 'audioUI'\r\n ) as unknown as DOMWidget\r\n const audio = output.audio[0]\r\n audioUIWidget.element.src = api.apiURL(\r\n getResourceURL(audio.subfolder, audio.filename, audio.type)\r\n )\r\n audioUIWidget.element.classList.remove('empty-audio-widget')\r\n }\r\n }\r\n }\r\n})\r\n\r\napp.registerExtension({\r\n name: 'Comfy.UploadAudio',\r\n async beforeRegisterNodeDef(nodeType, nodeData: ComfyNodeDef) {\r\n if (nodeData?.input?.required?.audio?.[1]?.audio_upload === true) {\r\n nodeData.input.required.upload = ['AUDIOUPLOAD']\r\n }\r\n },\r\n getCustomWidgets() {\r\n return {\r\n AUDIOUPLOAD(node, inputName: string) {\r\n // The widget that allows user to select file.\r\n const audioWidget: IWidget = node.widgets.find(\r\n (w: IWidget) => w.name === 'audio'\r\n )\r\n const audioUIWidget: DOMWidget = node.widgets.find(\r\n (w: IWidget) => w.name === 'audioUI'\r\n )\r\n\r\n const onAudioWidgetUpdate = () => {\r\n audioUIWidget.element.src = api.apiURL(\r\n getResourceURL(...splitFilePath(audioWidget.value))\r\n )\r\n }\r\n // Initially load default audio file to audioUIWidget.\r\n if (audioWidget.value) {\r\n onAudioWidgetUpdate()\r\n }\r\n audioWidget.callback = onAudioWidgetUpdate\r\n\r\n // Load saved audio file widget values if restoring from workflow\r\n const onGraphConfigured = node.onGraphConfigured\r\n node.onGraphConfigured = function () {\r\n onGraphConfigured?.apply(this, arguments)\r\n if (audioWidget.value) {\r\n onAudioWidgetUpdate()\r\n }\r\n }\r\n\r\n const fileInput = document.createElement('input')\r\n fileInput.type = 'file'\r\n fileInput.accept = 'audio/*'\r\n fileInput.style.display = 'none'\r\n fileInput.onchange = () => {\r\n if (fileInput.files.length) {\r\n uploadFile(audioWidget, audioUIWidget, fileInput.files[0], true)\r\n }\r\n }\r\n // The widget to pop up the upload dialog.\r\n const uploadWidget = node.addWidget(\r\n 'button',\r\n inputName,\r\n /* value=*/ '',\r\n () => {\r\n fileInput.click()\r\n }\r\n )\r\n uploadWidget.label = 'choose file to upload'\r\n uploadWidget.serialize = false\r\n\r\n return { widget: uploadWidget }\r\n }\r\n }\r\n }\r\n})\r\n"],"names":["app","id","file","ext","prompt","links","_a","widget","_b","w","type","_c","def","node","link","selectedIds","newNodes","i","group","modal","x","y","audio"],"mappings":"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"}
\ No newline at end of file
diff --git a/web/assets/index-BHzRuMlR.css b/web/assets/index-BHzRuMlR.css
new file mode 100644
index 000000000..befb7c6b1
--- /dev/null
+++ b/web/assets/index-BHzRuMlR.css
@@ -0,0 +1,4095 @@
+@font-face {
+ font-family: 'primeicons';
+ font-display: block;
+ src: url('/assets/primeicons-DMOk5skT.eot');
+ src: url('/assets/primeicons-DMOk5skT.eot?#iefix') format('embedded-opentype'), url('/assets/primeicons-C6QP2o4f.woff2') format('woff2'), url('/assets/primeicons-WjwUDZjB.woff') format('woff'), url('/assets/primeicons-MpK4pl85.ttf') format('truetype'), url('/assets/primeicons-Dr5RGzOO.svg?#primeicons') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+
+.pi {
+ font-family: 'primeicons';
+ speak: none;
+ font-style: normal;
+ font-weight: normal;
+ font-variant: normal;
+ text-transform: none;
+ line-height: 1;
+ display: inline-block;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.pi:before {
+ --webkit-backface-visibility:hidden;
+ backface-visibility: hidden;
+}
+
+.pi-fw {
+ width: 1.28571429em;
+ text-align: center;
+}
+
+.pi-spin {
+ animation: fa-spin 2s infinite linear;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .pi-spin {
+ animation-delay: -1ms;
+ animation-duration: 1ms;
+ animation-iteration-count: 1;
+ transition-delay: 0s;
+ transition-duration: 0s;
+ }
+}
+
+@keyframes fa-spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(359deg);
+ }
+}
+
+.pi-folder-plus:before {
+ content: "\ea05";
+}
+
+.pi-receipt:before {
+ content: "\ea06";
+}
+
+.pi-asterisk:before {
+ content: "\ea07";
+}
+
+.pi-face-smile:before {
+ content: "\ea08";
+}
+
+.pi-pinterest:before {
+ content: "\ea09";
+}
+
+.pi-expand:before {
+ content: "\ea0a";
+}
+
+.pi-pen-to-square:before {
+ content: "\ea0b";
+}
+
+.pi-wave-pulse:before {
+ content: "\ea0c";
+}
+
+.pi-turkish-lira:before {
+ content: "\ea0d";
+}
+
+.pi-spinner-dotted:before {
+ content: "\ea0e";
+}
+
+.pi-crown:before {
+ content: "\ea0f";
+}
+
+.pi-pause-circle:before {
+ content: "\ea10";
+}
+
+.pi-warehouse:before {
+ content: "\ea11";
+}
+
+.pi-objects-column:before {
+ content: "\ea12";
+}
+
+.pi-clipboard:before {
+ content: "\ea13";
+}
+
+.pi-play-circle:before {
+ content: "\ea14";
+}
+
+.pi-venus:before {
+ content: "\ea15";
+}
+
+.pi-cart-minus:before {
+ content: "\ea16";
+}
+
+.pi-file-plus:before {
+ content: "\ea17";
+}
+
+.pi-microchip:before {
+ content: "\ea18";
+}
+
+.pi-twitch:before {
+ content: "\ea19";
+}
+
+.pi-building-columns:before {
+ content: "\ea1a";
+}
+
+.pi-file-check:before {
+ content: "\ea1b";
+}
+
+.pi-microchip-ai:before {
+ content: "\ea1c";
+}
+
+.pi-trophy:before {
+ content: "\ea1d";
+}
+
+.pi-barcode:before {
+ content: "\ea1e";
+}
+
+.pi-file-arrow-up:before {
+ content: "\ea1f";
+}
+
+.pi-mars:before {
+ content: "\ea20";
+}
+
+.pi-tiktok:before {
+ content: "\ea21";
+}
+
+.pi-arrow-up-right-and-arrow-down-left-from-center:before {
+ content: "\ea22";
+}
+
+.pi-ethereum:before {
+ content: "\ea23";
+}
+
+.pi-list-check:before {
+ content: "\ea24";
+}
+
+.pi-thumbtack:before {
+ content: "\ea25";
+}
+
+.pi-arrow-down-left-and-arrow-up-right-to-center:before {
+ content: "\ea26";
+}
+
+.pi-equals:before {
+ content: "\ea27";
+}
+
+.pi-lightbulb:before {
+ content: "\ea28";
+}
+
+.pi-star-half:before {
+ content: "\ea29";
+}
+
+.pi-address-book:before {
+ content: "\ea2a";
+}
+
+.pi-chart-scatter:before {
+ content: "\ea2b";
+}
+
+.pi-indian-rupee:before {
+ content: "\ea2c";
+}
+
+.pi-star-half-fill:before {
+ content: "\ea2d";
+}
+
+.pi-cart-arrow-down:before {
+ content: "\ea2e";
+}
+
+.pi-calendar-clock:before {
+ content: "\ea2f";
+}
+
+.pi-sort-up-fill:before {
+ content: "\ea30";
+}
+
+.pi-sparkles:before {
+ content: "\ea31";
+}
+
+.pi-bullseye:before {
+ content: "\ea32";
+}
+
+.pi-sort-down-fill:before {
+ content: "\ea33";
+}
+
+.pi-graduation-cap:before {
+ content: "\ea34";
+}
+
+.pi-hammer:before {
+ content: "\ea35";
+}
+
+.pi-bell-slash:before {
+ content: "\ea36";
+}
+
+.pi-gauge:before {
+ content: "\ea37";
+}
+
+.pi-shop:before {
+ content: "\ea38";
+}
+
+.pi-headphones:before {
+ content: "\ea39";
+}
+
+.pi-eraser:before {
+ content: "\ea04";
+}
+
+.pi-stopwatch:before {
+ content: "\ea01";
+}
+
+.pi-verified:before {
+ content: "\ea02";
+}
+
+.pi-delete-left:before {
+ content: "\ea03";
+}
+
+.pi-hourglass:before {
+ content: "\e9fe";
+}
+
+.pi-truck:before {
+ content: "\ea00";
+}
+
+.pi-wrench:before {
+ content: "\e9ff";
+}
+
+.pi-microphone:before {
+ content: "\e9fa";
+}
+
+.pi-megaphone:before {
+ content: "\e9fb";
+}
+
+.pi-arrow-right-arrow-left:before {
+ content: "\e9fc";
+}
+
+.pi-bitcoin:before {
+ content: "\e9fd";
+}
+
+.pi-file-edit:before {
+ content: "\e9f6";
+}
+
+.pi-language:before {
+ content: "\e9f7";
+}
+
+.pi-file-export:before {
+ content: "\e9f8";
+}
+
+.pi-file-import:before {
+ content: "\e9f9";
+}
+
+.pi-file-word:before {
+ content: "\e9f1";
+}
+
+.pi-gift:before {
+ content: "\e9f2";
+}
+
+.pi-cart-plus:before {
+ content: "\e9f3";
+}
+
+.pi-thumbs-down-fill:before {
+ content: "\e9f4";
+}
+
+.pi-thumbs-up-fill:before {
+ content: "\e9f5";
+}
+
+.pi-arrows-alt:before {
+ content: "\e9f0";
+}
+
+.pi-calculator:before {
+ content: "\e9ef";
+}
+
+.pi-sort-alt-slash:before {
+ content: "\e9ee";
+}
+
+.pi-arrows-h:before {
+ content: "\e9ec";
+}
+
+.pi-arrows-v:before {
+ content: "\e9ed";
+}
+
+.pi-pound:before {
+ content: "\e9eb";
+}
+
+.pi-prime:before {
+ content: "\e9ea";
+}
+
+.pi-chart-pie:before {
+ content: "\e9e9";
+}
+
+.pi-reddit:before {
+ content: "\e9e8";
+}
+
+.pi-code:before {
+ content: "\e9e7";
+}
+
+.pi-sync:before {
+ content: "\e9e6";
+}
+
+.pi-shopping-bag:before {
+ content: "\e9e5";
+}
+
+.pi-server:before {
+ content: "\e9e4";
+}
+
+.pi-database:before {
+ content: "\e9e3";
+}
+
+.pi-hashtag:before {
+ content: "\e9e2";
+}
+
+.pi-bookmark-fill:before {
+ content: "\e9df";
+}
+
+.pi-filter-fill:before {
+ content: "\e9e0";
+}
+
+.pi-heart-fill:before {
+ content: "\e9e1";
+}
+
+.pi-flag-fill:before {
+ content: "\e9de";
+}
+
+.pi-circle:before {
+ content: "\e9dc";
+}
+
+.pi-circle-fill:before {
+ content: "\e9dd";
+}
+
+.pi-bolt:before {
+ content: "\e9db";
+}
+
+.pi-history:before {
+ content: "\e9da";
+}
+
+.pi-box:before {
+ content: "\e9d9";
+}
+
+.pi-at:before {
+ content: "\e9d8";
+}
+
+.pi-arrow-up-right:before {
+ content: "\e9d4";
+}
+
+.pi-arrow-up-left:before {
+ content: "\e9d5";
+}
+
+.pi-arrow-down-left:before {
+ content: "\e9d6";
+}
+
+.pi-arrow-down-right:before {
+ content: "\e9d7";
+}
+
+.pi-telegram:before {
+ content: "\e9d3";
+}
+
+.pi-stop-circle:before {
+ content: "\e9d2";
+}
+
+.pi-stop:before {
+ content: "\e9d1";
+}
+
+.pi-whatsapp:before {
+ content: "\e9d0";
+}
+
+.pi-building:before {
+ content: "\e9cf";
+}
+
+.pi-qrcode:before {
+ content: "\e9ce";
+}
+
+.pi-car:before {
+ content: "\e9cd";
+}
+
+.pi-instagram:before {
+ content: "\e9cc";
+}
+
+.pi-linkedin:before {
+ content: "\e9cb";
+}
+
+.pi-send:before {
+ content: "\e9ca";
+}
+
+.pi-slack:before {
+ content: "\e9c9";
+}
+
+.pi-sun:before {
+ content: "\e9c8";
+}
+
+.pi-moon:before {
+ content: "\e9c7";
+}
+
+.pi-vimeo:before {
+ content: "\e9c6";
+}
+
+.pi-youtube:before {
+ content: "\e9c5";
+}
+
+.pi-flag:before {
+ content: "\e9c4";
+}
+
+.pi-wallet:before {
+ content: "\e9c3";
+}
+
+.pi-map:before {
+ content: "\e9c2";
+}
+
+.pi-link:before {
+ content: "\e9c1";
+}
+
+.pi-credit-card:before {
+ content: "\e9bf";
+}
+
+.pi-discord:before {
+ content: "\e9c0";
+}
+
+.pi-percentage:before {
+ content: "\e9be";
+}
+
+.pi-euro:before {
+ content: "\e9bd";
+}
+
+.pi-book:before {
+ content: "\e9ba";
+}
+
+.pi-shield:before {
+ content: "\e9b9";
+}
+
+.pi-paypal:before {
+ content: "\e9bb";
+}
+
+.pi-amazon:before {
+ content: "\e9bc";
+}
+
+.pi-phone:before {
+ content: "\e9b8";
+}
+
+.pi-filter-slash:before {
+ content: "\e9b7";
+}
+
+.pi-facebook:before {
+ content: "\e9b4";
+}
+
+.pi-github:before {
+ content: "\e9b5";
+}
+
+.pi-twitter:before {
+ content: "\e9b6";
+}
+
+.pi-step-backward-alt:before {
+ content: "\e9ac";
+}
+
+.pi-step-forward-alt:before {
+ content: "\e9ad";
+}
+
+.pi-forward:before {
+ content: "\e9ae";
+}
+
+.pi-backward:before {
+ content: "\e9af";
+}
+
+.pi-fast-backward:before {
+ content: "\e9b0";
+}
+
+.pi-fast-forward:before {
+ content: "\e9b1";
+}
+
+.pi-pause:before {
+ content: "\e9b2";
+}
+
+.pi-play:before {
+ content: "\e9b3";
+}
+
+.pi-compass:before {
+ content: "\e9ab";
+}
+
+.pi-id-card:before {
+ content: "\e9aa";
+}
+
+.pi-ticket:before {
+ content: "\e9a9";
+}
+
+.pi-file-o:before {
+ content: "\e9a8";
+}
+
+.pi-reply:before {
+ content: "\e9a7";
+}
+
+.pi-directions-alt:before {
+ content: "\e9a5";
+}
+
+.pi-directions:before {
+ content: "\e9a6";
+}
+
+.pi-thumbs-up:before {
+ content: "\e9a3";
+}
+
+.pi-thumbs-down:before {
+ content: "\e9a4";
+}
+
+.pi-sort-numeric-down-alt:before {
+ content: "\e996";
+}
+
+.pi-sort-numeric-up-alt:before {
+ content: "\e997";
+}
+
+.pi-sort-alpha-down-alt:before {
+ content: "\e998";
+}
+
+.pi-sort-alpha-up-alt:before {
+ content: "\e999";
+}
+
+.pi-sort-numeric-down:before {
+ content: "\e99a";
+}
+
+.pi-sort-numeric-up:before {
+ content: "\e99b";
+}
+
+.pi-sort-alpha-down:before {
+ content: "\e99c";
+}
+
+.pi-sort-alpha-up:before {
+ content: "\e99d";
+}
+
+.pi-sort-alt:before {
+ content: "\e99e";
+}
+
+.pi-sort-amount-up:before {
+ content: "\e99f";
+}
+
+.pi-sort-amount-down:before {
+ content: "\e9a0";
+}
+
+.pi-sort-amount-down-alt:before {
+ content: "\e9a1";
+}
+
+.pi-sort-amount-up-alt:before {
+ content: "\e9a2";
+}
+
+.pi-palette:before {
+ content: "\e995";
+}
+
+.pi-undo:before {
+ content: "\e994";
+}
+
+.pi-desktop:before {
+ content: "\e993";
+}
+
+.pi-sliders-v:before {
+ content: "\e991";
+}
+
+.pi-sliders-h:before {
+ content: "\e992";
+}
+
+.pi-search-plus:before {
+ content: "\e98f";
+}
+
+.pi-search-minus:before {
+ content: "\e990";
+}
+
+.pi-file-excel:before {
+ content: "\e98e";
+}
+
+.pi-file-pdf:before {
+ content: "\e98d";
+}
+
+.pi-check-square:before {
+ content: "\e98c";
+}
+
+.pi-chart-line:before {
+ content: "\e98b";
+}
+
+.pi-user-edit:before {
+ content: "\e98a";
+}
+
+.pi-exclamation-circle:before {
+ content: "\e989";
+}
+
+.pi-android:before {
+ content: "\e985";
+}
+
+.pi-google:before {
+ content: "\e986";
+}
+
+.pi-apple:before {
+ content: "\e987";
+}
+
+.pi-microsoft:before {
+ content: "\e988";
+}
+
+.pi-heart:before {
+ content: "\e984";
+}
+
+.pi-mobile:before {
+ content: "\e982";
+}
+
+.pi-tablet:before {
+ content: "\e983";
+}
+
+.pi-key:before {
+ content: "\e981";
+}
+
+.pi-shopping-cart:before {
+ content: "\e980";
+}
+
+.pi-comments:before {
+ content: "\e97e";
+}
+
+.pi-comment:before {
+ content: "\e97f";
+}
+
+.pi-briefcase:before {
+ content: "\e97d";
+}
+
+.pi-bell:before {
+ content: "\e97c";
+}
+
+.pi-paperclip:before {
+ content: "\e97b";
+}
+
+.pi-share-alt:before {
+ content: "\e97a";
+}
+
+.pi-envelope:before {
+ content: "\e979";
+}
+
+.pi-volume-down:before {
+ content: "\e976";
+}
+
+.pi-volume-up:before {
+ content: "\e977";
+}
+
+.pi-volume-off:before {
+ content: "\e978";
+}
+
+.pi-eject:before {
+ content: "\e975";
+}
+
+.pi-money-bill:before {
+ content: "\e974";
+}
+
+.pi-images:before {
+ content: "\e973";
+}
+
+.pi-image:before {
+ content: "\e972";
+}
+
+.pi-sign-in:before {
+ content: "\e970";
+}
+
+.pi-sign-out:before {
+ content: "\e971";
+}
+
+.pi-wifi:before {
+ content: "\e96f";
+}
+
+.pi-sitemap:before {
+ content: "\e96e";
+}
+
+.pi-chart-bar:before {
+ content: "\e96d";
+}
+
+.pi-camera:before {
+ content: "\e96c";
+}
+
+.pi-dollar:before {
+ content: "\e96b";
+}
+
+.pi-lock-open:before {
+ content: "\e96a";
+}
+
+.pi-table:before {
+ content: "\e969";
+}
+
+.pi-map-marker:before {
+ content: "\e968";
+}
+
+.pi-list:before {
+ content: "\e967";
+}
+
+.pi-eye-slash:before {
+ content: "\e965";
+}
+
+.pi-eye:before {
+ content: "\e966";
+}
+
+.pi-folder-open:before {
+ content: "\e964";
+}
+
+.pi-folder:before {
+ content: "\e963";
+}
+
+.pi-video:before {
+ content: "\e962";
+}
+
+.pi-inbox:before {
+ content: "\e961";
+}
+
+.pi-lock:before {
+ content: "\e95f";
+}
+
+.pi-unlock:before {
+ content: "\e960";
+}
+
+.pi-tags:before {
+ content: "\e95d";
+}
+
+.pi-tag:before {
+ content: "\e95e";
+}
+
+.pi-power-off:before {
+ content: "\e95c";
+}
+
+.pi-save:before {
+ content: "\e95b";
+}
+
+.pi-question-circle:before {
+ content: "\e959";
+}
+
+.pi-question:before {
+ content: "\e95a";
+}
+
+.pi-copy:before {
+ content: "\e957";
+}
+
+.pi-file:before {
+ content: "\e958";
+}
+
+.pi-clone:before {
+ content: "\e955";
+}
+
+.pi-calendar-times:before {
+ content: "\e952";
+}
+
+.pi-calendar-minus:before {
+ content: "\e953";
+}
+
+.pi-calendar-plus:before {
+ content: "\e954";
+}
+
+.pi-ellipsis-v:before {
+ content: "\e950";
+}
+
+.pi-ellipsis-h:before {
+ content: "\e951";
+}
+
+.pi-bookmark:before {
+ content: "\e94e";
+}
+
+.pi-globe:before {
+ content: "\e94f";
+}
+
+.pi-replay:before {
+ content: "\e94d";
+}
+
+.pi-filter:before {
+ content: "\e94c";
+}
+
+.pi-print:before {
+ content: "\e94b";
+}
+
+.pi-align-right:before {
+ content: "\e946";
+}
+
+.pi-align-left:before {
+ content: "\e947";
+}
+
+.pi-align-center:before {
+ content: "\e948";
+}
+
+.pi-align-justify:before {
+ content: "\e949";
+}
+
+.pi-cog:before {
+ content: "\e94a";
+}
+
+.pi-cloud-download:before {
+ content: "\e943";
+}
+
+.pi-cloud-upload:before {
+ content: "\e944";
+}
+
+.pi-cloud:before {
+ content: "\e945";
+}
+
+.pi-pencil:before {
+ content: "\e942";
+}
+
+.pi-users:before {
+ content: "\e941";
+}
+
+.pi-clock:before {
+ content: "\e940";
+}
+
+.pi-user-minus:before {
+ content: "\e93e";
+}
+
+.pi-user-plus:before {
+ content: "\e93f";
+}
+
+.pi-trash:before {
+ content: "\e93d";
+}
+
+.pi-external-link:before {
+ content: "\e93c";
+}
+
+.pi-window-maximize:before {
+ content: "\e93b";
+}
+
+.pi-window-minimize:before {
+ content: "\e93a";
+}
+
+.pi-refresh:before {
+ content: "\e938";
+}
+
+.pi-user:before {
+ content: "\e939";
+}
+
+.pi-exclamation-triangle:before {
+ content: "\e922";
+}
+
+.pi-calendar:before {
+ content: "\e927";
+}
+
+.pi-chevron-circle-left:before {
+ content: "\e928";
+}
+
+.pi-chevron-circle-down:before {
+ content: "\e929";
+}
+
+.pi-chevron-circle-right:before {
+ content: "\e92a";
+}
+
+.pi-chevron-circle-up:before {
+ content: "\e92b";
+}
+
+.pi-angle-double-down:before {
+ content: "\e92c";
+}
+
+.pi-angle-double-left:before {
+ content: "\e92d";
+}
+
+.pi-angle-double-right:before {
+ content: "\e92e";
+}
+
+.pi-angle-double-up:before {
+ content: "\e92f";
+}
+
+.pi-angle-down:before {
+ content: "\e930";
+}
+
+.pi-angle-left:before {
+ content: "\e931";
+}
+
+.pi-angle-right:before {
+ content: "\e932";
+}
+
+.pi-angle-up:before {
+ content: "\e933";
+}
+
+.pi-upload:before {
+ content: "\e934";
+}
+
+.pi-download:before {
+ content: "\e956";
+}
+
+.pi-ban:before {
+ content: "\e935";
+}
+
+.pi-star-fill:before {
+ content: "\e936";
+}
+
+.pi-star:before {
+ content: "\e937";
+}
+
+.pi-chevron-left:before {
+ content: "\e900";
+}
+
+.pi-chevron-right:before {
+ content: "\e901";
+}
+
+.pi-chevron-down:before {
+ content: "\e902";
+}
+
+.pi-chevron-up:before {
+ content: "\e903";
+}
+
+.pi-caret-left:before {
+ content: "\e904";
+}
+
+.pi-caret-right:before {
+ content: "\e905";
+}
+
+.pi-caret-down:before {
+ content: "\e906";
+}
+
+.pi-caret-up:before {
+ content: "\e907";
+}
+
+.pi-search:before {
+ content: "\e908";
+}
+
+.pi-check:before {
+ content: "\e909";
+}
+
+.pi-check-circle:before {
+ content: "\e90a";
+}
+
+.pi-times:before {
+ content: "\e90b";
+}
+
+.pi-times-circle:before {
+ content: "\e90c";
+}
+
+.pi-plus:before {
+ content: "\e90d";
+}
+
+.pi-plus-circle:before {
+ content: "\e90e";
+}
+
+.pi-minus:before {
+ content: "\e90f";
+}
+
+.pi-minus-circle:before {
+ content: "\e910";
+}
+
+.pi-circle-on:before {
+ content: "\e911";
+}
+
+.pi-circle-off:before {
+ content: "\e912";
+}
+
+.pi-sort-down:before {
+ content: "\e913";
+}
+
+.pi-sort-up:before {
+ content: "\e914";
+}
+
+.pi-sort:before {
+ content: "\e915";
+}
+
+.pi-step-backward:before {
+ content: "\e916";
+}
+
+.pi-step-forward:before {
+ content: "\e917";
+}
+
+.pi-th-large:before {
+ content: "\e918";
+}
+
+.pi-arrow-down:before {
+ content: "\e919";
+}
+
+.pi-arrow-left:before {
+ content: "\e91a";
+}
+
+.pi-arrow-right:before {
+ content: "\e91b";
+}
+
+.pi-arrow-up:before {
+ content: "\e91c";
+}
+
+.pi-bars:before {
+ content: "\e91d";
+}
+
+.pi-arrow-circle-down:before {
+ content: "\e91e";
+}
+
+.pi-arrow-circle-left:before {
+ content: "\e91f";
+}
+
+.pi-arrow-circle-right:before {
+ content: "\e920";
+}
+
+.pi-arrow-circle-up:before {
+ content: "\e921";
+}
+
+.pi-info:before {
+ content: "\e923";
+}
+
+.pi-info-circle:before {
+ content: "\e924";
+}
+
+.pi-home:before {
+ content: "\e925";
+}
+
+.pi-spinner:before {
+ content: "\e926";
+}
+
+.side-bar-button-icon {
+ font-size: var(--sidebar-icon-size) !important;
+}
+.side-bar-button-selected .side-bar-button-icon {
+ font-size: var(--sidebar-icon-size) !important;
+ font-weight: bold;
+}
+
+.side-bar-button[data-v-7a0b94a3] {
+ width: var(--sidebar-width);
+ height: var(--sidebar-width);
+ border-radius: 0;
+}
+.comfyui-body-left .side-bar-button.side-bar-button-selected[data-v-7a0b94a3],
+.comfyui-body-left .side-bar-button.side-bar-button-selected[data-v-7a0b94a3]:hover {
+ border-left: 4px solid var(--p-button-text-primary-color);
+}
+.comfyui-body-right .side-bar-button.side-bar-button-selected[data-v-7a0b94a3],
+.comfyui-body-right .side-bar-button.side-bar-button-selected[data-v-7a0b94a3]:hover {
+ border-right: 4px solid var(--p-button-text-primary-color);
+}
+.lds-ring {
+ display: inline-block;
+ position: relative;
+ width: 1em;
+ height: 1em;
+}
+.lds-ring div {
+ box-sizing: border-box;
+ display: block;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ border: 0.15em solid #fff;
+ border-radius: 50%;
+ animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
+ border-color: #fff transparent transparent transparent;
+}
+.lds-ring div:nth-child(1) {
+ animation-delay: -0.45s;
+}
+.lds-ring div:nth-child(2) {
+ animation-delay: -0.3s;
+}
+.lds-ring div:nth-child(3) {
+ animation-delay: -0.15s;
+}
+@keyframes lds-ring {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+.relative {
+ position: relative;
+}
+.hidden {
+ display: none !important;
+}
+.mdi.rotate270::before {
+ transform: rotate(270deg);
+}
+
+/* Generic */
+.comfyui-button {
+ display: flex;
+ align-items: center;
+ gap: 0.5em;
+ cursor: pointer;
+ border: none;
+ border-radius: 4px;
+ padding: 4px 8px;
+ box-sizing: border-box;
+ margin: 0;
+ transition: box-shadow 0.1s;
+}
+
+.comfyui-button:active {
+ box-shadow: inset 1px 1px 10px rgba(0, 0, 0, 0.5);
+}
+
+.comfyui-button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+.primary .comfyui-button,
+.primary.comfyui-button {
+ background-color: var(--primary-bg) !important;
+ color: var(--primary-fg) !important;
+}
+
+.primary .comfyui-button:not(:disabled):hover,
+.primary.comfyui-button:not(:disabled):hover {
+ background-color: var(--primary-hover-bg) !important;
+ color: var(--primary-hover-fg) !important;
+}
+
+/* Popup */
+.comfyui-popup {
+ position: absolute;
+ left: var(--left);
+ right: var(--right);
+ top: var(--top);
+ bottom: var(--bottom);
+ z-index: 2000;
+ max-height: calc(100vh - var(--limit) - 10px);
+ box-shadow: 3px 3px 5px 0px rgba(0, 0, 0, 0.3);
+}
+
+.comfyui-popup:not(.open) {
+ display: none;
+}
+
+.comfyui-popup.right.open {
+ border-top-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+ overflow: hidden;
+}
+/* Split button */
+.comfyui-split-button {
+ position: relative;
+ display: flex;
+}
+
+.comfyui-split-primary {
+ flex: auto;
+}
+
+.comfyui-split-primary .comfyui-button {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-right: 1px solid var(--comfy-menu-bg);
+ width: 100%;
+}
+
+.comfyui-split-arrow .comfyui-button {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ padding-left: 2px;
+ padding-right: 2px;
+}
+
+.comfyui-split-button-popup {
+ white-space: nowrap;
+ background-color: var(--content-bg);
+ color: var(--content-fg);
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+}
+
+.comfyui-split-button-popup.hover {
+ z-index: 2001;
+}
+.comfyui-split-button-popup > .comfyui-button {
+ border: none;
+ background-color: transparent;
+ color: var(--fg-color);
+ padding: 8px 12px 8px 8px;
+}
+
+.comfyui-split-button-popup > .comfyui-button:not(:disabled):hover {
+ background-color: var(--comfy-input-bg);
+}
+
+/* Button group */
+.comfyui-button-group {
+ display: flex;
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.comfyui-button-group > .comfyui-button,
+.comfyui-button-group > .comfyui-button-wrapper > .comfyui-button {
+ padding: 4px 10px;
+ border-radius: 0;
+}
+
+/* Menu */
+.comfyui-menu {
+ width: 100vw;
+ background: var(--comfy-menu-bg);
+ color: var(--fg-color);
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 0.8em;
+ display: flex;
+ padding: 4px 8px;
+ align-items: center;
+ gap: 8px;
+ box-sizing: border-box;
+ z-index: 1000;
+ order: 0;
+ grid-column: 1/-1;
+ overflow: auto;
+ max-height: 90vh;
+}
+
+.comfyui-menu>* {
+ flex-shrink: 0;
+}
+.comfyui-menu .mdi::before {
+ font-size: 18px;
+}
+
+.comfyui-menu .comfyui-button {
+ background: var(--comfy-input-bg);
+ color: var(--fg-color);
+ white-space: nowrap;
+}
+
+.comfyui-menu .comfyui-button:not(:disabled):hover {
+ background: var(--border-color);
+ color: var(--content-fg);
+}
+
+.comfyui-menu .comfyui-split-button-popup > .comfyui-button {
+ border-radius: 0;
+ background-color: transparent;
+}
+
+.comfyui-menu .comfyui-split-button-popup > .comfyui-button:not(:disabled):hover {
+ background-color: var(--comfy-input-bg);
+}
+
+.comfyui-menu .comfyui-split-button-popup.left {
+ border-top-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+}
+
+.comfyui-menu .comfyui-button.popup-open {
+ background-color: var(--content-bg);
+ color: var(--content-fg);
+}
+
+.comfyui-menu-push {
+ margin-left: -0.8em;
+ flex: auto;
+}
+
+.comfyui-logo {
+ font-size: 1.2em;
+ margin: 0;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ cursor: default;
+}
+
+/* Workflows */
+.comfyui-workflows-button {
+ flex-direction: row-reverse;
+ max-width: 200px;
+ position: relative;
+ z-index: 0;
+}
+
+.comfyui-workflows-button.popup-open {
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.comfyui-workflows-button.unsaved {
+ font-style: italic;
+}
+.comfyui-workflows-button-progress {
+ position: absolute;
+ top: 0;
+ left: 0;
+ background-color: green;
+ height: 100%;
+ border-radius: 4px;
+ z-index: -1;
+}
+
+.comfyui-workflows-button > span {
+ flex: auto;
+ text-align: left;
+ overflow: hidden;
+}
+.comfyui-workflows-button-inner {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ width: 150px;
+}
+.comfyui-workflows-label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ direction: rtl;
+ flex: auto;
+ position: relative;
+}
+
+.comfyui-workflows-button.unsaved .comfyui-workflows-label {
+ padding-left: 8px;
+}
+
+.comfyui-workflows-button.unsaved .comfyui-workflows-label:after {
+ content: "*";
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+.comfyui-workflows-button-inner .mdi-graph::before {
+ transform: rotate(-90deg);
+}
+
+.comfyui-workflows-popup {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 0.8em;
+ padding: 10px;
+ overflow: auto;
+ background-color: var(--content-bg);
+ color: var(--content-fg);
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+ z-index: 1001;
+}
+
+.comfyui-workflows-panel {
+ min-height: 150px;
+}
+
+.comfyui-workflows-panel .lds-ring {
+ transform: translate(-50%);
+ position: absolute;
+ left: 50%;
+ top: 75px;
+}
+
+.comfyui-workflows-panel h3 {
+ margin: 10px 0 10px 0;
+ font-size: 11px;
+ opacity: 0.8;
+}
+
+.comfyui-workflows-panel section header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.comfy-ui-workflows-search .mdi {
+ position: relative;
+ top: 2px;
+ pointer-events: none;
+}
+.comfy-ui-workflows-search input {
+ background-color: var(--comfy-input-bg);
+ color: var(--input-text);
+ border: none;
+ border-radius: 4px;
+ padding: 4px 10px;
+ margin-left: -24px;
+ text-indent: 18px;
+}
+.comfy-ui-workflows-search input:-moz-placeholder-shown {
+ width: 10px;
+}
+.comfy-ui-workflows-search input:placeholder-shown {
+ width: 10px;
+}
+.comfy-ui-workflows-search input:-moz-placeholder-shown:focus {
+ width: auto;
+}
+.comfy-ui-workflows-search input:placeholder-shown:focus {
+ width: auto;
+}
+.comfyui-workflows-actions {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 10px;
+}
+
+.comfyui-workflows-actions .comfyui-button {
+ background: var(--comfy-input-bg);
+ color: var(--input-text);
+}
+
+.comfyui-workflows-actions .comfyui-button:not(:disabled):hover {
+ background: var(--primary-bg);
+ color: var(--primary-fg);
+}
+
+.comfyui-workflows-favorites,
+.comfyui-workflows-open {
+ border-bottom: 1px solid var(--comfy-input-bg);
+ padding-bottom: 5px;
+ margin-bottom: 5px;
+}
+
+.comfyui-workflows-open .active {
+ font-weight: bold;
+ color: var(--primary-fg);
+}
+
+.comfyui-workflows-favorites:empty {
+ display: none;
+}
+
+.comfyui-workflows-tree {
+ padding: 0;
+ margin: 0;
+}
+
+.comfyui-workflows-tree:empty::after {
+ content: "No saved workflows";
+ display: block;
+ text-align: center;
+}
+.comfyui-workflows-tree > ul {
+ padding: 0;
+}
+
+.comfyui-workflows-tree > ul ul {
+ margin: 0;
+ padding: 0 0 0 25px;
+}
+
+.comfyui-workflows-tree:not(.filtered) .closed > ul {
+ display: none;
+}
+
+.comfyui-workflows-tree li,
+.comfyui-workflows-tree-file {
+ --item-height: 32px;
+ list-style-type: none;
+ height: var(--item-height);
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+}
+
+.comfyui-workflows-tree-file.active::before,
+.comfyui-workflows-tree li:hover::before,
+.comfyui-workflows-tree-file:hover::before {
+ content: "";
+ position: absolute;
+ width: 100%;
+ left: 0;
+ height: var(--item-height);
+ background-color: var(--content-hover-bg);
+ color: var(--content-hover-fg);
+ z-index: -1;
+}
+
+.comfyui-workflows-tree-file.active::before {
+ background-color: var(--primary-bg);
+ color: var(--primary-fg);
+}
+
+.comfyui-workflows-tree-file.running:not(:hover)::before {
+ content: "";
+ position: absolute;
+ width: var(--progress, 0);
+ left: 0;
+ height: var(--item-height);
+ background-color: green;
+ z-index: -1;
+}
+
+.comfyui-workflows-tree-file.unsaved span {
+ font-style: italic;
+}
+
+.comfyui-workflows-tree-file span {
+ flex: auto;
+}
+
+.comfyui-workflows-tree-file span + .comfyui-workflows-file-action {
+ margin-left: 10px;
+}
+
+.comfyui-workflows-tree-file .comfyui-workflows-file-action {
+ background-color: transparent;
+ color: var(--fg-color);
+ padding: 2px 4px;
+}
+
+.comfyui-workflows-tree-file.active .comfyui-workflows-file-action {
+ color: var(--primary-fg);
+}
+
+.lg ~ .comfyui-workflows-popup .comfyui-workflows-tree-file:not(:hover) .comfyui-workflows-file-action {
+ opacity: 0;
+}
+
+.comfyui-workflows-tree-file .comfyui-workflows-file-action:hover {
+ background-color: var(--primary-bg);
+ color: var(--primary-fg);
+}
+
+.comfyui-workflows-tree-file .comfyui-workflows-file-action-primary {
+ background-color: transparent;
+ color: var(--fg-color);
+ padding: 2px 4px;
+ margin: 0 -4px;
+}
+
+.comfyui-workflows-file-action-favorite .mdi-star {
+ color: orange;
+}
+
+/* View List */
+.comfyui-view-list-popup {
+ padding: 10px;
+ background-color: var(--content-bg);
+ color: var(--content-fg);
+ min-width: 170px;
+ min-height: 435px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ box-sizing: border-box;
+}
+.comfyui-view-list-popup h3 {
+ margin: 0 0 5px 0;
+}
+.comfyui-view-list-items {
+ width: 100%;
+ background: var(--comfy-menu-bg);
+ border-radius: 5px;
+ display: flex;
+ justify-content: center;
+ flex: auto;
+ align-items: center;
+ flex-direction: column;
+}
+.comfyui-view-list-items section {
+ max-height: 400px;
+ overflow: auto;
+ width: 100%;
+ display: grid;
+ grid-template-columns: auto auto auto;
+ align-items: center;
+ justify-content: center;
+ gap: 5px;
+ padding: 5px 0;
+}
+.comfyui-view-list-items section + section {
+ border-top: 1px solid var(--border-color);
+ margin-top: 10px;
+ padding-top: 5px;
+}
+.comfyui-view-list-items section h5 {
+ grid-column: 1 / 4;
+ text-align: center;
+ margin: 5px;
+}
+.comfyui-view-list-items span {
+ text-align: center;
+ padding: 0 2px;
+}
+.comfyui-view-list-popup header {
+ margin-bottom: 10px;
+ display: flex;
+ gap: 5px;
+}
+.comfyui-view-list-popup header .comfyui-button {
+ border: 1px solid transparent;
+}
+.comfyui-view-list-popup header .comfyui-button:not(:disabled):hover {
+ border: 1px solid var(--comfy-menu-bg);
+}
+/* Queue button */
+.comfyui-queue-button .comfyui-split-primary .comfyui-button {
+ padding-right: 12px;
+}
+.comfyui-queue-count {
+ margin-left: 5px;
+ border-radius: 10px;
+ background-color: rgb(8, 80, 153);
+ padding: 2px 4px;
+ font-size: 10px;
+ min-width: 1em;
+ display: inline-block;
+}
+/* Queue options*/
+.comfyui-queue-options {
+ padding: 10px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 12px;
+ display: flex;
+ gap: 10px;
+}
+
+.comfyui-queue-batch {
+ display: flex;
+ flex-direction: column;
+ border-right: 1px solid var(--comfy-menu-bg);
+ padding-right: 10px;
+ gap: 5px;
+}
+
+.comfyui-queue-batch input {
+ width: 145px;
+}
+
+.comfyui-queue-batch .comfyui-queue-batch-value {
+ width: 70px;
+}
+
+.comfyui-queue-mode {
+ display: flex;
+ flex-direction: column;
+}
+
+.comfyui-queue-mode span {
+ font-weight: bold;
+ margin-bottom: 2px;
+}
+
+.comfyui-queue-mode label {
+ display: flex;
+ flex-direction: row-reverse;
+ justify-content: start;
+ gap: 5px;
+ padding: 2px 0;
+}
+
+.comfyui-queue-mode label input {
+ padding: 0;
+ margin: 0;
+}
+
+/** Send to workflow widget selection dialog */
+.comfy-widget-selection-dialog {
+ border: none;
+}
+
+.comfy-widget-selection-dialog div {
+ color: var(--fg-color);
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+.comfy-widget-selection-dialog h2 {
+ margin-top: 0;
+}
+
+.comfy-widget-selection-dialog section {
+ width: -moz-fit-content;
+ width: fit-content;
+ display: flex;
+ flex-direction: column;
+}
+
+.comfy-widget-selection-item {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+}
+
+.comfy-widget-selection-item span {
+ margin-right: auto;
+}
+
+.comfy-widget-selection-item span::before {
+ content: '#' attr(data-id);
+ opacity: 0.5;
+ margin-right: 5px;
+}
+
+.comfy-modal .comfy-widget-selection-item button {
+ font-size: 1em;
+}
+
+/***** Responsive *****/
+.lg.comfyui-menu .lt-lg-show {
+ display: none !important;
+}
+.comfyui-menu:not(.lg) .nlg-hide {
+ display: none !important;
+}
+/** Large screen */
+.lg.comfyui-menu>.comfyui-menu-mobile-collapse .comfyui-button span,
+.lg.comfyui-menu>.comfyui-menu-mobile-collapse.comfyui-button span {
+ display: none;
+}
+.lg.comfyui-menu>.comfyui-menu-mobile-collapse .comfyui-popup .comfyui-button span {
+ display: unset;
+}
+
+/** Non large screen */
+.lt-lg.comfyui-menu {
+ flex-wrap: wrap;
+}
+
+.lt-lg.comfyui-menu > *:not(.comfyui-menu-mobile-collapse) {
+ order: 1;
+}
+
+.lt-lg.comfyui-menu > .comfyui-menu-mobile-collapse {
+ order: 9999;
+ width: 100%;
+}
+
+.comfyui-body-bottom .lt-lg.comfyui-menu > .comfyui-menu-mobile-collapse {
+ order: -1;
+}
+
+.comfyui-body-bottom .lt-lg.comfyui-menu > .comfyui-menu-button {
+ top: unset;
+ bottom: 4px;
+}
+
+.lt-lg.comfyui-menu > .comfyui-menu-mobile-collapse.comfyui-button-group {
+ flex-wrap: wrap;
+}
+
+.lt-lg.comfyui-menu > .comfyui-menu-mobile-collapse .comfyui-button,
+.lt-lg.comfyui-menu > .comfyui-menu-mobile-collapse.comfyui-button {
+ padding: 10px;
+}
+.lt-lg.comfyui-menu > .comfyui-menu-mobile-collapse .comfyui-button,
+.lt-lg.comfyui-menu > .comfyui-menu-mobile-collapse .comfyui-button-wrapper {
+ width: 100%;
+}
+
+.lt-lg.comfyui-menu > .comfyui-menu-mobile-collapse .comfyui-popup {
+ position: static;
+ background-color: var(--comfy-input-bg);
+ max-width: unset;
+ max-height: 50vh;
+ overflow: auto;
+}
+
+.lt-lg.comfyui-menu:not(.expanded) > .comfyui-menu-mobile-collapse {
+ display: none;
+}
+
+.lt-lg .comfyui-queue-button {
+ margin-right: 44px;
+}
+
+.lt-lg .comfyui-menu-button {
+ position: absolute;
+ top: 4px;
+ right: 8px;
+}
+
+.lt-lg.comfyui-menu > .comfyui-menu-mobile-collapse .comfyui-view-list-popup {
+ border-radius: 0;
+}
+
+.lt-lg.comfyui-menu .comfyui-workflows-popup {
+ width: 100vw;
+}
+
+/** Small */
+.lt-md .comfyui-workflows-button-inner {
+ width: unset !important;
+}
+.lt-md .comfyui-workflows-label {
+ display: none;
+}
+
+/** Extra small */
+.lt-sm .comfyui-queue-button {
+ margin-right: 0;
+ width: 100%;
+}
+.lt-sm .comfyui-queue-button .comfyui-button {
+ justify-content: center;
+}
+.lt-sm .comfyui-interrupt-button {
+ margin-right: 45px;
+}
+.comfyui-body-bottom .lt-sm.comfyui-menu > .comfyui-menu-button{
+ bottom: 41px;
+}/* this CSS contains only the basic CSS needed to run the app and use it */
+
+.lgraphcanvas {
+ /*cursor: crosshair;*/
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ outline: none;
+ font-family: Tahoma, sans-serif;
+}
+
+.lgraphcanvas * {
+ box-sizing: border-box;
+}
+
+.litegraph.litecontextmenu {
+ font-family: Tahoma, sans-serif;
+ position: fixed;
+ top: 100px;
+ left: 100px;
+ min-width: 100px;
+ color: #aaf;
+ padding: 0;
+ box-shadow: 0 0 10px black !important;
+ background-color: #2e2e2e !important;
+ z-index: 10;
+}
+
+.litegraph.litecontextmenu.dark {
+ background-color: #000 !important;
+}
+
+.litegraph.litecontextmenu .litemenu-title img {
+ margin-top: 2px;
+ margin-left: 2px;
+ margin-right: 4px;
+}
+
+.litegraph.litecontextmenu .litemenu-entry {
+ margin: 2px;
+ padding: 2px;
+}
+
+.litegraph.litecontextmenu .litemenu-entry.submenu {
+ background-color: #2e2e2e !important;
+}
+
+.litegraph.litecontextmenu.dark .litemenu-entry.submenu {
+ background-color: #000 !important;
+}
+
+.litegraph .litemenubar ul {
+ font-family: Tahoma, sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+.litegraph .litemenubar li {
+ font-size: 14px;
+ color: #999;
+ display: inline-block;
+ min-width: 50px;
+ padding-left: 10px;
+ padding-right: 10px;
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ cursor: pointer;
+}
+
+.litegraph .litemenubar li:hover {
+ background-color: #777;
+ color: #eee;
+}
+
+.litegraph .litegraph .litemenubar-panel {
+ position: absolute;
+ top: 5px;
+ left: 5px;
+ min-width: 100px;
+ background-color: #444;
+ box-shadow: 0 0 3px black;
+ padding: 4px;
+ border-bottom: 2px solid #aaf;
+ z-index: 10;
+}
+
+.litegraph .litemenu-entry,
+.litemenu-title {
+ font-size: 12px;
+ color: #aaa;
+ padding: 0 0 0 4px;
+ margin: 2px;
+ padding-left: 2px;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+}
+
+.litegraph .litemenu-entry .icon {
+ display: inline-block;
+ width: 12px;
+ height: 12px;
+ margin: 2px;
+ vertical-align: top;
+}
+
+.litegraph .litemenu-entry.checked .icon {
+ background-color: #aaf;
+}
+
+.litegraph .litemenu-entry .more {
+ float: right;
+ padding-right: 5px;
+}
+
+.litegraph .litemenu-entry.disabled {
+ opacity: 0.5;
+ cursor: default;
+}
+
+.litegraph .litemenu-entry.separator {
+ display: block;
+ border-top: 1px solid #333;
+ border-bottom: 1px solid #666;
+ width: 100%;
+ height: 0px;
+ margin: 3px 0 2px 0;
+ background-color: transparent;
+ padding: 0 !important;
+ cursor: default !important;
+}
+
+.litegraph .litemenu-entry.has_submenu {
+ border-right: 2px solid cyan;
+}
+
+.litegraph .litemenu-title {
+ color: #dde;
+ background-color: #111;
+ margin: 0;
+ padding: 2px;
+ cursor: default;
+}
+
+.litegraph .litemenu-entry:hover:not(.disabled):not(.separator) {
+ background-color: #444 !important;
+ color: #eee;
+ transition: all 0.2s;
+}
+
+.litegraph .litemenu-entry .property_name {
+ display: inline-block;
+ text-align: left;
+ min-width: 80px;
+ min-height: 1.2em;
+}
+
+.litegraph .litemenu-entry .property_value {
+ display: inline-block;
+ background-color: rgba(0, 0, 0, 0.5);
+ text-align: right;
+ min-width: 80px;
+ min-height: 1.2em;
+ vertical-align: middle;
+ padding-right: 10px;
+}
+
+.litegraph.litesearchbox {
+ font-family: Tahoma, sans-serif;
+ position: absolute;
+ background-color: rgba(0, 0, 0, 0.5);
+ padding-top: 4px;
+}
+
+.litegraph.litesearchbox input,
+.litegraph.litesearchbox select {
+ margin-top: 3px;
+ min-width: 60px;
+ min-height: 1.5em;
+ background-color: black;
+ border: 0;
+ color: white;
+ padding-left: 10px;
+ margin-right: 5px;
+ max-width: 300px;
+}
+
+.litegraph.litesearchbox .name {
+ display: inline-block;
+ min-width: 60px;
+ min-height: 1.5em;
+ padding-left: 10px;
+}
+
+.litegraph.litesearchbox .helper {
+ overflow: auto;
+ max-height: 200px;
+ margin-top: 2px;
+}
+
+.litegraph.lite-search-item {
+ font-family: Tahoma, sans-serif;
+ background-color: rgba(0, 0, 0, 0.5);
+ color: white;
+ padding-top: 2px;
+}
+
+.litegraph.lite-search-item.not_in_filter{
+ /*background-color: rgba(50, 50, 50, 0.5);*/
+ /*color: #999;*/
+ color: #B99;
+ font-style: italic;
+}
+
+.litegraph.lite-search-item.generic_type{
+ /*background-color: rgba(50, 50, 50, 0.5);*/
+ /*color: #DD9;*/
+ color: #999;
+ font-style: italic;
+}
+
+.litegraph.lite-search-item:hover,
+.litegraph.lite-search-item.selected {
+ cursor: pointer;
+ background-color: white;
+ color: black;
+}
+
+.litegraph.lite-search-item-type {
+ display: inline-block;
+ background: rgba(0,0,0,0.2);
+ margin-left: 5px;
+ font-size: 14px;
+ padding: 2px 5px;
+ position: relative;
+ top: -2px;
+ opacity: 0.8;
+ border-radius: 4px;
+ }
+
+/* DIALOGs ******/
+
+.litegraph .dialog {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin-top: -150px;
+ margin-left: -200px;
+
+ background-color: #2A2A2A;
+
+ min-width: 400px;
+ min-height: 200px;
+ box-shadow: 0 0 4px #111;
+ border-radius: 6px;
+}
+
+.litegraph .dialog.settings {
+ left: 10px;
+ top: 10px;
+ height: calc( 100% - 20px );
+ margin: auto;
+ max-width: 50%;
+}
+
+.litegraph .dialog.centered {
+ top: 50px;
+ left: 50%;
+ position: absolute;
+ transform: translateX(-50%);
+ min-width: 600px;
+ min-height: 300px;
+ height: calc( 100% - 100px );
+ margin: auto;
+}
+
+.litegraph .dialog .close {
+ float: right;
+ margin: 4px;
+ margin-right: 10px;
+ cursor: pointer;
+ font-size: 1.4em;
+}
+
+.litegraph .dialog .close:hover {
+ color: white;
+}
+
+.litegraph .dialog .dialog-header {
+ color: #AAA;
+ border-bottom: 1px solid #161616; height: 40px;
+}
+.litegraph .dialog .dialog-footer { height: 50px; padding: 10px; border-top: 1px solid #1a1a1a;}
+
+.litegraph .dialog .dialog-header .dialog-title {
+ font: 20px "Arial";
+ margin: 4px;
+ padding: 4px 10px;
+ display: inline-block;
+}
+
+.litegraph .dialog .dialog-content, .litegraph .dialog .dialog-alt-content {
+ height: calc(100% - 90px);
+ width: 100%;
+ min-height: 100px;
+ display: inline-block;
+ color: #AAA;
+ /*background-color: black;*/
+ overflow: auto;
+}
+
+.litegraph .dialog .dialog-content h3 {
+ margin: 10px;
+}
+
+.litegraph .dialog .dialog-content .connections {
+ flex-direction: row;
+}
+
+.litegraph .dialog .dialog-content .connections .connections_side {
+ width: calc(50% - 5px);
+ min-height: 100px;
+ background-color: black;
+ display: flex;
+}
+
+.litegraph .dialog .node_type {
+ font-size: 1.2em;
+ display: block;
+ margin: 10px;
+}
+
+.litegraph .dialog .node_desc {
+ opacity: 0.5;
+ display: block;
+ margin: 10px;
+}
+
+.litegraph .dialog .separator {
+ display: block;
+ width: calc( 100% - 4px );
+ height: 1px;
+ border-top: 1px solid #000;
+ border-bottom: 1px solid #333;
+ margin: 10px 2px;
+ padding: 0;
+}
+
+.litegraph .dialog .property {
+ margin-bottom: 2px;
+ padding: 4px;
+}
+
+.litegraph .dialog .property:hover {
+ background: #545454;
+}
+
+.litegraph .dialog .property_name {
+ color: #737373;
+ display: inline-block;
+ text-align: left;
+ vertical-align: top;
+ width: 160px;
+ padding-left: 4px;
+ overflow: hidden;
+ margin-right: 6px;
+}
+
+.litegraph .dialog .property:hover .property_name {
+ color: white;
+}
+
+.litegraph .dialog .property_value {
+ display: inline-block;
+ text-align: right;
+ color: #AAA;
+ background-color: #1A1A1A;
+ /*width: calc( 100% - 122px );*/
+ max-width: calc( 100% - 162px );
+ min-width: 200px;
+ max-height: 300px;
+ min-height: 20px;
+ padding: 4px;
+ padding-right: 12px;
+ overflow: hidden;
+ cursor: pointer;
+ border-radius: 3px;
+}
+
+.litegraph .dialog .property_value:hover {
+ color: white;
+}
+
+.litegraph .dialog .property.boolean .property_value {
+ padding-right: 30px;
+ color: #A88;
+ /*width: auto;
+ float: right;*/
+}
+
+.litegraph .dialog .property.boolean.bool-on .property_name{
+ color: #8A8;
+}
+.litegraph .dialog .property.boolean.bool-on .property_value{
+ color: #8A8;
+}
+
+.litegraph .dialog .btn {
+ border: 0;
+ border-radius: 4px;
+ padding: 4px 20px;
+ margin-left: 0px;
+ background-color: #060606;
+ color: #8e8e8e;
+}
+
+.litegraph .dialog .btn:hover {
+ background-color: #111;
+ color: #FFF;
+}
+
+.litegraph .dialog .btn.delete:hover {
+ background-color: #F33;
+ color: black;
+}
+
+.litegraph .subgraph_property {
+ padding: 4px;
+}
+
+.litegraph .subgraph_property:hover {
+ background-color: #333;
+}
+
+.litegraph .subgraph_property.extra {
+ margin-top: 8px;
+}
+
+.litegraph .subgraph_property span.name {
+ font-size: 1.3em;
+ padding-left: 4px;
+}
+
+.litegraph .subgraph_property span.type {
+ opacity: 0.5;
+ margin-right: 20px;
+ padding-left: 4px;
+}
+
+.litegraph .subgraph_property span.label {
+ display: inline-block;
+ width: 60px;
+ padding: 0px 10px;
+}
+
+.litegraph .subgraph_property input {
+ width: 140px;
+ color: #999;
+ background-color: #1A1A1A;
+ border-radius: 4px;
+ border: 0;
+ margin-right: 10px;
+ padding: 4px;
+ padding-left: 10px;
+}
+
+.litegraph .subgraph_property button {
+ background-color: #1c1c1c;
+ color: #aaa;
+ border: 0;
+ border-radius: 2px;
+ padding: 4px 10px;
+ cursor: pointer;
+}
+
+.litegraph .subgraph_property.extra {
+ color: #ccc;
+}
+
+.litegraph .subgraph_property.extra input {
+ background-color: #111;
+}
+
+.litegraph .bullet_icon {
+ margin-left: 10px;
+ border-radius: 10px;
+ width: 12px;
+ height: 12px;
+ background-color: #666;
+ display: inline-block;
+ margin-top: 2px;
+ margin-right: 4px;
+ transition: background-color 0.1s ease 0s;
+ -moz-transition: background-color 0.1s ease 0s;
+}
+
+.litegraph .bullet_icon:hover {
+ background-color: #698;
+ cursor: pointer;
+}
+
+/* OLD */
+
+.graphcontextmenu {
+ padding: 4px;
+ min-width: 100px;
+}
+
+.graphcontextmenu-title {
+ color: #dde;
+ background-color: #222;
+ margin: 0;
+ padding: 2px;
+ cursor: default;
+}
+
+.graphmenu-entry {
+ box-sizing: border-box;
+ margin: 2px;
+ padding-left: 20px;
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ transition: all linear 0.3s;
+}
+
+.graphmenu-entry.event,
+.litemenu-entry.event {
+ border-left: 8px solid orange;
+ padding-left: 12px;
+}
+
+.graphmenu-entry.disabled {
+ opacity: 0.3;
+}
+
+.graphmenu-entry.submenu {
+ border-right: 2px solid #eee;
+}
+
+.graphmenu-entry:hover {
+ background-color: #555;
+}
+
+.graphmenu-entry.separator {
+ background-color: #111;
+ border-bottom: 1px solid #666;
+ height: 1px;
+ width: calc(100% - 20px);
+ -moz-width: calc(100% - 20px);
+ -webkit-width: calc(100% - 20px);
+}
+
+.graphmenu-entry .property_name {
+ display: inline-block;
+ text-align: left;
+ min-width: 80px;
+ min-height: 1.2em;
+}
+
+.graphmenu-entry .property_value,
+.litemenu-entry .property_value {
+ display: inline-block;
+ background-color: rgba(0, 0, 0, 0.5);
+ text-align: right;
+ min-width: 80px;
+ min-height: 1.2em;
+ vertical-align: middle;
+ padding-right: 10px;
+}
+
+.graphdialog {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ min-height: 2em;
+ background-color: #333;
+ font-size: 1.2em;
+ box-shadow: 0 0 10px black !important;
+ z-index: 10;
+}
+
+.graphdialog.rounded {
+ border-radius: 12px;
+ padding-right: 2px;
+}
+
+.graphdialog .name {
+ display: inline-block;
+ min-width: 60px;
+ min-height: 1.5em;
+ padding-left: 10px;
+}
+
+.graphdialog input,
+.graphdialog textarea,
+.graphdialog select {
+ margin: 3px;
+ min-width: 60px;
+ min-height: 1.5em;
+ background-color: black;
+ border: 0;
+ color: white;
+ padding-left: 10px;
+ outline: none;
+}
+
+.graphdialog textarea {
+ min-height: 150px;
+}
+
+.graphdialog button {
+ margin-top: 3px;
+ vertical-align: top;
+ background-color: #999;
+ border: 0;
+}
+
+.graphdialog button.rounded,
+.graphdialog input.rounded {
+ border-radius: 0 12px 12px 0;
+}
+
+.graphdialog .helper {
+ overflow: auto;
+ max-height: 200px;
+}
+
+.graphdialog .help-item {
+ padding-left: 10px;
+}
+
+.graphdialog .help-item:hover,
+.graphdialog .help-item.selected {
+ cursor: pointer;
+ background-color: white;
+ color: black;
+}
+
+.litegraph .dialog {
+ min-height: 0;
+}
+.litegraph .dialog .dialog-content {
+display: block;
+}
+.litegraph .dialog .dialog-content .subgraph_property {
+padding: 5px;
+}
+.litegraph .dialog .dialog-footer {
+margin: 0;
+}
+.litegraph .dialog .dialog-footer .subgraph_property {
+margin-top: 0;
+display: flex;
+align-items: center;
+padding: 5px;
+}
+.litegraph .dialog .dialog-footer .subgraph_property .name {
+flex: 1;
+}
+.litegraph .graphdialog {
+display: flex;
+align-items: center;
+border-radius: 20px;
+padding: 4px 10px;
+position: fixed;
+}
+.litegraph .graphdialog .name {
+padding: 0;
+min-height: 0;
+font-size: 16px;
+vertical-align: middle;
+}
+.litegraph .graphdialog .value {
+font-size: 16px;
+min-height: 0;
+margin: 0 10px;
+padding: 2px 5px;
+}
+.litegraph .graphdialog input[type="checkbox"] {
+width: 16px;
+height: 16px;
+}
+.litegraph .graphdialog button {
+padding: 4px 18px;
+border-radius: 20px;
+cursor: pointer;
+}
+
+:root {
+ --fg-color: #000;
+ --bg-color: #fff;
+ --comfy-menu-bg: #353535;
+ --comfy-input-bg: #222;
+ --input-text: #ddd;
+ --descrip-text: #999;
+ --drag-text: #ccc;
+ --error-text: #ff4444;
+ --border-color: #4e4e4e;
+ --tr-even-bg-color: #222;
+ --tr-odd-bg-color: #353535;
+ --primary-bg: #236692;
+ --primary-fg: #ffffff;
+ --primary-hover-bg: #3485bb;
+ --primary-hover-fg: #ffffff;
+ --content-bg: #e0e0e0;
+ --content-fg: #000;
+ --content-hover-bg: #adadad;
+ --content-hover-fg: #000;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --fg-color: #fff;
+ --bg-color: #202020;
+ --content-bg: #4e4e4e;
+ --content-fg: #fff;
+ --content-hover-bg: #222;
+ --content-hover-fg: #fff;
+ }
+}
+
+body {
+ width: 100vw;
+ height: 100vh;
+ margin: 0;
+ overflow: hidden;
+ grid-template-columns: auto 1fr auto;
+ grid-template-rows: auto 1fr auto;
+ background-color: var(--bg-color);
+ color: var(--fg-color);
+ min-height: -webkit-fill-available;
+ max-height: -webkit-fill-available;
+ min-width: -webkit-fill-available;
+ max-width: -webkit-fill-available;
+ font-family: Arial, sans-serif;
+}
+
+/**
+ +------------------+------------------+------------------+
+ | |
+ | .comfyui-body- |
+ | top |
+ | (spans all cols) |
+ | |
+ +------------------+------------------+------------------+
+ | | | |
+ | .comfyui-body- | #graph-canvas | .comfyui-body- |
+ | left | | right |
+ | | | |
+ | | | |
+ +------------------+------------------+------------------+
+ | |
+ | .comfyui-body- |
+ | bottom |
+ | (spans all cols) |
+ | |
+ +------------------+------------------+------------------+
+*/
+
+.comfyui-body-top {
+ order: -5;
+ /* Span across all columns */
+ grid-column: 1/-1;
+ /* Position at the first row */
+ grid-row: 1;
+ z-index: 10;
+ display: flex;
+ flex-direction: column;
+}
+
+.comfyui-body-left {
+ order: -4;
+ /* Position in the first column */
+ grid-column: 1;
+ /* Position below the top element */
+ grid-row: 2;
+ z-index: 10;
+ display: flex;
+}
+
+.graph-canvas-container {
+ width: 100%;
+ height: 100%;
+ order: -3;
+ grid-column: 2;
+ grid-row: 2;
+ position: relative;
+ overflow: hidden;
+}
+
+#graph-canvas {
+ width: 100%;
+ height: 100%;
+}
+
+.comfyui-body-right {
+ order: -2;
+ z-index: 10;
+ grid-column: 3;
+ grid-row: 2;
+}
+
+.comfyui-body-bottom {
+ order: 4;
+ /* Span across all columns */
+ grid-column: 1/-1;
+ grid-row: 3;
+ z-index: 10;
+ display: flex;
+ flex-direction: column;
+}
+
+.comfy-multiline-input {
+ background-color: var(--comfy-input-bg);
+ color: var(--input-text);
+ overflow: hidden;
+ overflow-y: auto;
+ padding: 2px;
+ resize: none;
+ border: none;
+ box-sizing: border-box;
+ font-size: var(--comfy-textarea-font-size);
+}
+
+.comfy-modal {
+ display: none; /* Hidden by default */
+ position: fixed; /* Stay in place */
+ z-index: 100; /* Sit on top */
+ padding: 30px 30px 10px 30px;
+ background-color: var(--comfy-menu-bg); /* Modal background */
+ color: var(--error-text);
+ box-shadow: 0 0 20px #888888;
+ border-radius: 10px;
+ top: 50%;
+ left: 50%;
+ max-width: 80vw;
+ max-height: 80vh;
+ transform: translate(-50%, -50%);
+ overflow: hidden;
+ justify-content: center;
+ font-family: monospace;
+ font-size: 15px;
+}
+
+.comfy-modal-content {
+ display: flex;
+ flex-direction: column;
+}
+
+.comfy-modal p {
+ overflow: auto;
+ white-space: pre-line; /* This will respect line breaks */
+ margin-bottom: 20px; /* Add some margin between the text and the close button*/
+}
+
+.comfy-modal select,
+.comfy-modal input[type=button],
+.comfy-modal input[type=checkbox] {
+ margin: 3px 3px 3px 4px;
+}
+
+.comfy-menu-hamburger {
+ position: fixed;
+ top: 10px;
+ z-index: 9999;
+ right: 10px;
+ width: 30px;
+ display: none;
+ gap: 8px;
+ flex-direction: column;
+ cursor: pointer;
+}
+
+.comfy-menu-hamburger div {
+ height: 3px;
+ width: 100%;
+ border-radius: 20px;
+ background-color: white;
+}
+
+.comfy-menu {
+ font-size: 15px;
+ position: absolute;
+ top: 50%;
+ right: 0;
+ text-align: center;
+ z-index: 999;
+ width: 190px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ color: var(--descrip-text);
+ background-color: var(--comfy-menu-bg);
+ font-family: sans-serif;
+ padding: 10px;
+ border-radius: 0 8px 8px 8px;
+ box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.4);
+}
+
+.comfy-menu-header {
+ display: flex;
+}
+
+.comfy-menu-actions {
+ display: flex;
+ gap: 3px;
+ align-items: center;
+ height: 20px;
+ position: relative;
+ top: -1px;
+ font-size: 22px;
+}
+
+.comfy-menu .comfy-menu-actions button {
+ background-color: rgba(0, 0, 0, 0);
+ padding: 0;
+ border: none;
+ cursor: pointer;
+ font-size: inherit;
+}
+
+.comfy-menu .comfy-menu-actions .comfy-settings-btn {
+ font-size: 0.6em;
+}
+
+button.comfy-close-menu-btn {
+ font-size: 1em;
+ line-height: 12px;
+ color: #ccc;
+ position: relative;
+ top: -1px;
+}
+
+.comfy-menu-queue-size {
+ flex: auto;
+}
+
+.comfy-menu button,
+.comfy-modal button {
+ font-size: 20px;
+}
+
+.comfy-menu-btns {
+ margin-bottom: 10px;
+ width: 100%;
+}
+
+.comfy-menu-btns button {
+ font-size: 10px;
+ width: 50%;
+ color: var(--descrip-text) !important;
+}
+
+.comfy-menu > button {
+ width: 100%;
+}
+
+.comfy-btn,
+.comfy-menu > button,
+.comfy-menu-btns button,
+.comfy-menu .comfy-list button,
+.comfy-modal button {
+ color: var(--input-text);
+ background-color: var(--comfy-input-bg);
+ border-radius: 8px;
+ border-color: var(--border-color);
+ border-style: solid;
+ margin-top: 2px;
+}
+
+.comfy-btn:hover:not(:disabled),
+.comfy-menu > button:hover,
+.comfy-menu-btns button:hover,
+.comfy-menu .comfy-list button:hover,
+.comfy-modal button:hover,
+.comfy-menu-actions button:hover {
+ filter: brightness(1.2);
+ will-change: transform;
+ cursor: pointer;
+}
+
+span.drag-handle {
+ width: 10px;
+ height: 20px;
+ display: inline-block;
+ overflow: hidden;
+ line-height: 5px;
+ padding: 3px 4px;
+ cursor: move;
+ vertical-align: middle;
+ margin-top: -.4em;
+ margin-left: -.2em;
+ font-size: 12px;
+ font-family: sans-serif;
+ letter-spacing: 2px;
+ color: var(--drag-text);
+ text-shadow: 1px 0 1px black;
+}
+
+span.drag-handle::after {
+ content: '.. .. ..';
+}
+
+.comfy-queue-btn {
+ width: 100%;
+}
+
+.comfy-list {
+ color: var(--descrip-text);
+ background-color: var(--comfy-menu-bg);
+ margin-bottom: 10px;
+ border-color: var(--border-color);
+ border-style: solid;
+}
+
+.comfy-list-items {
+ overflow-y: scroll;
+ max-height: 100px;
+ min-height: 25px;
+ background-color: var(--comfy-input-bg);
+ padding: 5px;
+}
+
+.comfy-list h4 {
+ min-width: 160px;
+ margin: 0;
+ padding: 3px;
+ font-weight: normal;
+}
+
+.comfy-list-items button {
+ font-size: 10px;
+}
+
+.comfy-list-actions {
+ margin: 5px;
+ display: flex;
+ gap: 5px;
+ justify-content: center;
+}
+
+.comfy-list-actions button {
+ font-size: 12px;
+}
+
+button.comfy-queue-btn {
+ margin: 6px 0 !important;
+}
+
+.comfy-modal.comfy-settings,
+.comfy-modal.comfy-manage-templates {
+ text-align: center;
+ font-family: sans-serif;
+ color: var(--descrip-text);
+ z-index: 99;
+}
+
+.comfy-modal.comfy-settings input[type="range"] {
+ vertical-align: middle;
+}
+
+.comfy-modal.comfy-settings input[type="range"] + input[type="number"] {
+ width: 3.5em;
+}
+
+.comfy-modal input,
+.comfy-modal select {
+ color: var(--input-text);
+ background-color: var(--comfy-input-bg);
+ border-radius: 8px;
+ border-color: var(--border-color);
+ border-style: solid;
+ font-size: inherit;
+}
+
+.comfy-tooltip-indicator {
+ text-decoration: underline;
+ text-decoration-style: dashed;
+}
+
+@media only screen and (max-height: 850px) {
+ .comfy-menu {
+ top: 0 !important;
+ bottom: 0 !important;
+ left: auto !important;
+ right: 0 !important;
+ border-radius: 0;
+ }
+
+ .comfy-menu span.drag-handle {
+ display: none;
+ }
+
+ .comfy-menu-queue-size {
+ flex: unset;
+ }
+
+ .comfy-menu-header {
+ justify-content: space-between;
+ }
+ .comfy-menu-actions {
+ gap: 10px;
+ font-size: 28px;
+ }
+}
+
+/* Input popup */
+
+.graphdialog {
+ min-height: 1em;
+ background-color: var(--comfy-menu-bg);
+}
+
+.graphdialog .name {
+ font-size: 14px;
+ font-family: sans-serif;
+ color: var(--descrip-text);
+}
+
+.graphdialog button {
+ margin-top: unset;
+ vertical-align: unset;
+ height: 1.6em;
+ padding-right: 8px;
+}
+
+.graphdialog input, .graphdialog textarea, .graphdialog select {
+ background-color: var(--comfy-input-bg);
+ border: 2px solid;
+ border-color: var(--border-color);
+ color: var(--input-text);
+ border-radius: 12px 0 0 12px;
+}
+
+/* Dialogs */
+
+dialog {
+ box-shadow: 0 0 20px #888888;
+}
+
+dialog::backdrop {
+ background: rgba(0, 0, 0, 0.5);
+}
+
+.comfy-dialog.comfyui-dialog.comfy-modal {
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ transform: none;
+}
+
+.comfy-dialog.comfy-modal {
+ font-family: Arial, sans-serif;
+ border-color: var(--bg-color);
+ box-shadow: none;
+ border: 2px solid var(--border-color);
+}
+
+.comfy-dialog .comfy-modal-content {
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 10px;
+ color: var(--fg-color);
+}
+
+.comfy-dialog .comfy-modal-content h3 {
+ margin-top: 0;
+}
+
+.comfy-dialog .comfy-modal-content > p {
+ width: 100%;
+}
+
+.comfy-dialog .comfy-modal-content > .comfyui-button {
+ flex: 1;
+ justify-content: center;
+}
+
+#comfy-settings-dialog {
+ padding: 0;
+ width: 41rem;
+}
+
+#comfy-settings-dialog tr > td:first-child {
+ text-align: right;
+}
+
+#comfy-settings-dialog tbody button, #comfy-settings-dialog table > button {
+ background-color: var(--bg-color);
+ border: 1px var(--border-color) solid;
+ border-radius: 0;
+ color: var(--input-text);
+ font-size: 1rem;
+ padding: 0.5rem;
+}
+
+#comfy-settings-dialog button:hover {
+ background-color: var(--tr-odd-bg-color);
+}
+
+/* General CSS for tables */
+
+.comfy-table {
+ border-collapse: collapse;
+ color: var(--input-text);
+ font-family: Arial, sans-serif;
+ width: 100%;
+}
+
+.comfy-table caption {
+ position: sticky;
+ top: 0;
+ background-color: var(--bg-color);
+ color: var(--input-text);
+ font-size: 1rem;
+ font-weight: bold;
+ padding: 8px;
+ text-align: center;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.comfy-table caption .comfy-btn {
+ position: absolute;
+ top: -2px;
+ right: 0;
+ bottom: 0;
+ cursor: pointer;
+ border: none;
+ height: 100%;
+ border-radius: 0;
+ aspect-ratio: 1/1;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ font-size: 20px;
+}
+
+.comfy-table caption .comfy-btn:focus {
+ outline: none;
+}
+
+.comfy-table tr:nth-child(even) {
+ background-color: var(--tr-even-bg-color);
+}
+
+.comfy-table tr:nth-child(odd) {
+ background-color: var(--tr-odd-bg-color);
+}
+
+.comfy-table td,
+.comfy-table th {
+ border: 1px solid var(--border-color);
+ padding: 8px;
+}
+
+/* Context menu */
+
+.litegraph .dialog {
+ z-index: 1;
+ font-family: Arial, sans-serif;
+}
+
+.litegraph .litemenu-entry.has_submenu {
+ position: relative;
+ padding-right: 20px;
+}
+
+.litemenu-entry.has_submenu::after {
+ content: ">";
+ position: absolute;
+ top: 0;
+ right: 2px;
+}
+
+.litegraph.litecontextmenu,
+.litegraph.litecontextmenu.dark {
+ z-index: 9999 !important;
+ background-color: var(--comfy-menu-bg) !important;
+ filter: brightness(95%);
+ will-change: transform;
+}
+
+.litegraph.litecontextmenu .litemenu-entry:hover:not(.disabled):not(.separator) {
+ background-color: var(--comfy-menu-bg) !important;
+ filter: brightness(155%);
+ will-change: transform;
+ color: var(--input-text);
+}
+
+.litegraph.litecontextmenu .litemenu-entry.submenu,
+.litegraph.litecontextmenu.dark .litemenu-entry.submenu {
+ background-color: var(--comfy-menu-bg) !important;
+ color: var(--input-text);
+}
+
+.litegraph.litecontextmenu input {
+ background-color: var(--comfy-input-bg) !important;
+ color: var(--input-text) !important;
+}
+
+.comfy-context-menu-filter {
+ box-sizing: border-box;
+ border: 1px solid #999;
+ margin: 0 0 5px 5px;
+ width: calc(100% - 10px);
+}
+
+.comfy-img-preview {
+ pointer-events: none;
+ overflow: hidden;
+ display: flex;
+ flex-wrap: wrap;
+ align-content: flex-start;
+ justify-content: center;
+}
+
+.comfy-img-preview img {
+ -o-object-fit: contain;
+ object-fit: contain;
+ width: var(--comfy-img-preview-width);
+ height: var(--comfy-img-preview-height);
+}
+
+.comfy-missing-nodes li button {
+ font-size: 12px;
+ margin-left: 5px;
+}
+
+/* Search box */
+
+.litegraph.litesearchbox {
+ z-index: 9999 !important;
+ background-color: var(--comfy-menu-bg) !important;
+ overflow: hidden;
+ display: block;
+}
+
+.litegraph.litesearchbox input,
+.litegraph.litesearchbox select {
+ background-color: var(--comfy-input-bg) !important;
+ color: var(--input-text);
+}
+
+.litegraph.lite-search-item {
+ color: var(--input-text);
+ background-color: var(--comfy-input-bg);
+ filter: brightness(80%);
+ will-change: transform;
+ padding-left: 0.2em;
+}
+
+.litegraph.lite-search-item.generic_type {
+ color: var(--input-text);
+ filter: brightness(50%);
+ will-change: transform;
+}
+
+@media only screen and (max-width: 450px) {
+ #comfy-settings-dialog .comfy-table tbody {
+ display: grid;
+ }
+ #comfy-settings-dialog .comfy-table tr {
+ display: grid;
+ }
+ #comfy-settings-dialog tr > td:first-child {
+ text-align: center;
+ border-bottom: none;
+ padding-bottom: 0;
+ }
+ #comfy-settings-dialog tr > td:not(:first-child) {
+ text-align: center;
+ border-top: none;
+ }
+}
+
+audio.comfy-audio.empty-audio-widget {
+ display: none;
+}
+
+#vue-app {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+}
+
+/* Set auto complete panel's width as it is not accessible within vue-root */
+.p-autocomplete-overlay {
+ max-width: 25vw;
+}
+
+:root {
+ --red-600: #dc3545;
+}
+
+.comfy-missing-nodes[data-v-286402f2] {
+ font-family: monospace;
+ color: var(--red-600);
+ padding: 1.5rem;
+ background-color: var(--surface-ground);
+ border-radius: var(--border-radius);
+ box-shadow: var(--card-shadow);
+}
+.warning-title[data-v-286402f2] {
+ margin-top: 0;
+ margin-bottom: 1rem;
+}
+.warning-description[data-v-286402f2] {
+ margin-bottom: 1rem;
+}
+.missing-nodes-list[data-v-286402f2] {
+ max-height: 300px;
+ overflow-y: auto;
+}
+.missing-nodes-list.maximized[data-v-286402f2] {
+ max-height: unset;
+}
+.missing-node-item[data-v-286402f2] {
+ display: flex;
+ align-items: center;
+ padding: 0.5rem;
+}
+.node-type[data-v-286402f2] {
+ font-weight: 600;
+ color: var(--text-color);
+}
+.node-hint[data-v-286402f2] {
+ margin-left: 0.5rem;
+ font-style: italic;
+ color: var(--text-color-secondary);
+}
+[data-v-286402f2] .p-button {
+ margin-left: auto;
+}
+.added-nodes-warning[data-v-286402f2] {
+ margin-top: 1rem;
+ font-style: italic;
+}
+
+.input-slider[data-v-fbaf7a8c] {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+.slider-part[data-v-fbaf7a8c] {
+ flex-grow: 1;
+}
+.input-part[data-v-fbaf7a8c] {
+ width: 5rem !important;
+}
+
+.info-chip[data-v-4feeb3d2] {
+ background: transparent;
+}
+.setting-item[data-v-4feeb3d2] {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1rem;
+}
+.setting-label[data-v-4feeb3d2] {
+ display: flex;
+ align-items: center;
+ flex: 1;
+}
+.setting-input[data-v-4feeb3d2] {
+ flex: 1;
+ display: flex;
+ justify-content: flex-end;
+ margin-left: 1rem;
+}
+
+/* Ensure PrimeVue components take full width of their container */
+.setting-input[data-v-4feeb3d2] .p-inputtext,
+.setting-input[data-v-4feeb3d2] .input-slider,
+.setting-input[data-v-4feeb3d2] .p-select,
+.setting-input[data-v-4feeb3d2] .p-togglebutton {
+ width: 100%;
+ max-width: 200px;
+}
+.setting-input[data-v-4feeb3d2] .p-inputtext {
+ max-width: unset;
+}
+
+/* Special case for ToggleSwitch to align it to the right */
+.setting-input[data-v-4feeb3d2] .p-toggleswitch {
+ margin-left: auto;
+}
+
+.search-box-input[data-v-3bbe5335] {
+ width: 100%;
+}
+
+.no-results-placeholder[data-v-5a7d148a] {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ padding: 2rem;
+}
+.no-results-placeholder[data-v-5a7d148a] .p-card {
+ background-color: var(--surface-ground);
+ text-align: center;
+}
+.no-results-placeholder h3[data-v-5a7d148a] {
+ color: var(--text-color);
+ margin-bottom: 0.5rem;
+}
+.no-results-placeholder p[data-v-5a7d148a] {
+ color: var(--text-color-secondary);
+ margin-bottom: 1rem;
+}
+
+/* Remove after we have tailwind setup */
+.border-none {
+ border: none !important;
+}
+.settings-tab-panels {
+ padding-top: 0px !important;
+}
+
+.settings-container[data-v-833dbfbb] {
+ display: flex;
+ height: 70vh;
+ width: 60vw;
+ max-width: 1000px;
+ overflow: hidden;
+ /* Prevents container from scrolling */
+}
+.settings-sidebar[data-v-833dbfbb] {
+ width: 250px;
+ flex-shrink: 0;
+ /* Prevents sidebar from shrinking */
+ overflow-y: auto;
+ padding: 10px;
+}
+.settings-search-box[data-v-833dbfbb] {
+ width: 100%;
+ margin-bottom: 10px;
+}
+.settings-content[data-v-833dbfbb] {
+ flex-grow: 1;
+ overflow-y: auto;
+ /* Allows vertical scrolling */
+}
+
+/* Ensure the Listbox takes full width of the sidebar */
+.settings-sidebar[data-v-833dbfbb] .p-listbox {
+ width: 100%;
+}
+
+/* Optional: Style scrollbars for webkit browsers */
+.settings-sidebar[data-v-833dbfbb]::-webkit-scrollbar,
+.settings-content[data-v-833dbfbb]::-webkit-scrollbar {
+ width: 1px;
+}
+.settings-sidebar[data-v-833dbfbb]::-webkit-scrollbar-thumb,
+.settings-content[data-v-833dbfbb]::-webkit-scrollbar-thumb {
+ background-color: transparent;
+}
+
+.pi-cog[data-v-969a1066] {
+ font-size: 1.25rem;
+ margin-right: 0.5rem;
+}
+.version-tag[data-v-969a1066] {
+ margin-left: 0.5rem;
+}
+
+:root {
+ --sidebar-width: 64px;
+ --sidebar-icon-size: 1.5rem;
+}
+:root .small-sidebar {
+ --sidebar-width: 40px;
+ --sidebar-icon-size: 1rem;
+}
+
+.side-tool-bar-container[data-v-f54e0ebc] {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ pointer-events: auto;
+
+ width: var(--sidebar-width);
+ height: 100%;
+
+ background-color: var(--comfy-menu-bg);
+ color: var(--fg-color);
+}
+.side-tool-bar-end[data-v-f54e0ebc] {
+ align-self: flex-end;
+ margin-top: auto;
+}
+.sidebar-content-container[data-v-f54e0ebc] {
+ height: 100%;
+ overflow-y: auto;
+}
+
+.p-splitter-gutter {
+ pointer-events: auto;
+}
+.gutter-hidden {
+ display: none !important;
+}
+
+.side-bar-panel[data-v-1c49e664] {
+ background-color: var(--bg-color);
+ pointer-events: auto;
+}
+.splitter-overlay[data-v-1c49e664] {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ background-color: transparent;
+ pointer-events: none;
+ /* Set it the same as the ComfyUI menu */
+ /* Note: Lite-graph DOM widgets have the same z-index as the node id, so
+ 999 should be sufficient to make sure splitter overlays on node's DOM
+ widgets */
+ z-index: 999;
+ border: none;
+}
+
+._filter-button[data-v-071f55e3] {
+ z-index: 10;
+}
+._dialog[data-v-071f55e3] {
+ min-width: 24rem;
+}
+._dialog-body[data-v-071f55e3] {
+ display: flex;
+ flex-direction: column;
+}
+._dialog-body[data-v-071f55e3] > :not([hidden]) ~ :not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
+}
+
+.comfy-core[data-v-1d7c94e5],
+.comfy-custom-nodes[data-v-1d7c94e5],
+.comfy-unknown[data-v-1d7c94e5] {
+ font-size: small;
+ font-weight: lighter;
+}
+
+.slot_row[data-v-d926c92a] {
+ padding: 2px;
+}
+
+/* Original N-Sidebar styles */
+._sb_dot[data-v-d926c92a] {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background-color: grey;
+}
+.node_header[data-v-d926c92a] {
+ line-height: 1;
+ padding: 8px 13px 7px;
+ background: var(--comfy-input-bg);
+ margin-bottom: 5px;
+ font-size: 15px;
+ text-wrap: nowrap;
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+}
+.headdot[data-v-d926c92a] {
+ width: 10px;
+ height: 10px;
+ float: inline-start;
+ margin-right: 8px;
+}
+.IMAGE[data-v-d926c92a] {
+ background-color: #64b5f6;
+}
+.VAE[data-v-d926c92a] {
+ background-color: #ff6e6e;
+}
+.LATENT[data-v-d926c92a] {
+ background-color: #ff9cf9;
+}
+.MASK[data-v-d926c92a] {
+ background-color: #81c784;
+}
+.CONDITIONING[data-v-d926c92a] {
+ background-color: #ffa931;
+}
+.CLIP[data-v-d926c92a] {
+ background-color: #ffd500;
+}
+.MODEL[data-v-d926c92a] {
+ background-color: #b39ddb;
+}
+.CONTROL_NET[data-v-d926c92a] {
+ background-color: #a5d6a7;
+}
+._sb_node_preview[data-v-d926c92a] {
+ background-color: var(--comfy-menu-bg);
+ font-family: 'Open Sans', sans-serif;
+ font-size: small;
+ color: var(--descrip-text);
+ border: 1px solid var(--descrip-text);
+ min-width: 300px;
+ width: -moz-min-content;
+ width: min-content;
+ height: -moz-fit-content;
+ height: fit-content;
+ z-index: 9999;
+ border-radius: 12px;
+ overflow: hidden;
+ font-size: 12px;
+ padding-bottom: 10px;
+}
+._sb_node_preview ._sb_description[data-v-d926c92a] {
+ margin: 10px;
+ padding: 6px;
+ background: var(--border-color);
+ border-radius: 5px;
+ font-style: italic;
+ font-weight: 500;
+ font-size: 0.9rem;
+ word-break: break-word;
+}
+._sb_table[data-v-d926c92a] {
+ display: grid;
+
+ grid-column-gap: 10px;
+ /* Spazio tra le colonne */
+ width: 100%;
+ /* Imposta la larghezza della tabella al 100% del contenitore */
+}
+._sb_row[data-v-d926c92a] {
+ display: grid;
+ grid-template-columns: 10px 1fr 1fr 1fr 10px;
+ grid-column-gap: 10px;
+ align-items: center;
+ padding-left: 9px;
+ padding-right: 9px;
+}
+._sb_row_string[data-v-d926c92a] {
+ grid-template-columns: 10px 1fr 1fr 10fr 1fr;
+}
+._sb_col[data-v-d926c92a] {
+ border: 0px solid #000;
+ display: flex;
+ align-items: flex-end;
+ flex-direction: row-reverse;
+ flex-wrap: nowrap;
+ align-content: flex-start;
+ justify-content: flex-end;
+}
+._sb_inherit[data-v-d926c92a] {
+ display: inherit;
+}
+._long_field[data-v-d926c92a] {
+ background: var(--bg-color);
+ border: 2px solid var(--border-color);
+ margin: 5px 5px 0 5px;
+ border-radius: 10px;
+ line-height: 1.7;
+ text-wrap: nowrap;
+}
+._sb_arrow[data-v-d926c92a] {
+ color: var(--fg-color);
+}
+._sb_preview_badge[data-v-d926c92a] {
+ text-align: center;
+ background: var(--comfy-input-bg);
+ font-weight: bold;
+ color: var(--error-text);
+}
+
+.comfy-vue-node-search-container[data-v-b8a4ffdc] {
+ display: flex;
+ width: 100%;
+ min-width: 24rem;
+ align-items: center;
+ justify-content: center;
+}
+.comfy-vue-node-search-container[data-v-b8a4ffdc] * {
+ pointer-events: auto;
+}
+.comfy-vue-node-preview-container[data-v-b8a4ffdc] {
+ position: absolute;
+ left: -350px;
+ top: 50px;
+}
+.comfy-vue-node-search-box[data-v-b8a4ffdc] {
+ z-index: 10;
+ flex-grow: 1;
+}
+.option-container[data-v-b8a4ffdc] {
+ display: flex;
+ width: 100%;
+ cursor: pointer;
+ flex-direction: column;
+ overflow: hidden;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+}
+.option-display-name[data-v-b8a4ffdc] {
+ font-weight: 600;
+}
+.option-category[data-v-b8a4ffdc] {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-size: 0.875rem;
+ line-height: 1.25rem;
+ --tw-text-opacity: 1;
+ color: rgb(156 163 175 / var(--tw-text-opacity));
+ /* Keeps the text on a single line by default */
+ white-space: nowrap;
+}
+.i-badge[data-v-b8a4ffdc] {
+ --tw-bg-opacity: 1;
+ background-color: rgb(34 197 94 / var(--tw-bg-opacity));
+ --tw-text-opacity: 1;
+ color: rgb(255 255 255 / var(--tw-text-opacity));
+}
+.o-badge[data-v-b8a4ffdc] {
+ --tw-bg-opacity: 1;
+ background-color: rgb(239 68 68 / var(--tw-bg-opacity));
+ --tw-text-opacity: 1;
+ color: rgb(255 255 255 / var(--tw-text-opacity));
+}
+.c-badge[data-v-b8a4ffdc] {
+ --tw-bg-opacity: 1;
+ background-color: rgb(59 130 246 / var(--tw-bg-opacity));
+ --tw-text-opacity: 1;
+ color: rgb(255 255 255 / var(--tw-text-opacity));
+}
+.s-badge[data-v-b8a4ffdc] {
+ --tw-bg-opacity: 1;
+ background-color: rgb(234 179 8 / var(--tw-bg-opacity));
+}
+[data-v-b8a4ffdc] .highlight {
+ background-color: var(--p-primary-color);
+ color: var(--p-primary-contrast-color);
+ font-weight: bold;
+ border-radius: 0.25rem;
+ padding: 0.125rem 0.25rem;
+ margin: -0.125rem 0.125rem;
+}
+
+.invisible-dialog-root {
+ width: 30%;
+ min-width: 24rem;
+ max-width: 48rem;
+ border: 0 !important;
+ background-color: transparent !important;
+ margin-top: 25vh;
+}
+.node-search-box-dialog-mask {
+ align-items: flex-start !important;
+}
+
+.node-tooltip[data-v-d5e6001c] {
+ background: var(--comfy-input-bg);
+ border-radius: 5px;
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
+ color: var(--input-text);
+ font-family: sans-serif;
+ left: 0;
+ max-width: 30vw;
+ padding: 4px 8px;
+ position: absolute;
+ top: 0;
+ transform: translate(5px, calc(-100% - 5px));
+ white-space: pre-wrap;
+ z-index: 99999;
+}
+
+.result-container[data-v-0fac61d9] {
+ aspect-ratio: 1 / 1;
+ overflow: hidden;
+}
+[data-v-0fac61d9] img {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 100%;
+ height: 100%;
+ -o-object-fit: cover;
+ object-fit: cover;
+}
+.p-image-preview[data-v-0fac61d9] {
+ position: static;
+ display: contents;
+}
+[data-v-0fac61d9] .image-preview-mask {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: auto;
+ height: auto;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ padding: 10px;
+ cursor: pointer;
+ background: rgba(0, 0, 0, 0.5);
+ color: var(--p-image-preview-mask-color);
+ transition:
+ opacity var(--p-image-transition-duration),
+ background var(--p-image-transition-duration);
+ border-radius: 50%;
+}
+
+.task-result-preview[data-v-6cf8179c] {
+ aspect-ratio: 1 / 1;
+ overflow: hidden;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+}
+.task-result-preview i[data-v-6cf8179c],
+.task-result-preview span[data-v-6cf8179c] {
+ font-size: 2rem;
+}
+.task-item[data-v-6cf8179c] {
+ display: flex;
+ flex-direction: column;
+ border-radius: 4px;
+ overflow: hidden;
+ position: relative;
+}
+.task-item-details[data-v-6cf8179c] {
+ position: absolute;
+ bottom: 0;
+ padding: 0.6rem;
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+}
+
+/* In dark mode, transparent background color for tags is not ideal for tags that
+are floating on top of images. */
+.tag-wrapper[data-v-6cf8179c] {
+ background-color: var(--p-primary-contrast-color);
+ border-radius: 6px;
+ display: inline-flex;
+}
+
+.comfy-vue-side-bar-container[data-v-bde767d2] {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ overflow: hidden;
+}
+.comfy-vue-side-bar-header[data-v-bde767d2] {
+ flex-shrink: 0;
+ border-left: none;
+ border-right: none;
+ border-top: none;
+ border-radius: 0;
+ padding: 0.25rem 1rem;
+ min-height: 2.5rem;
+}
+.comfy-vue-side-bar-header-span[data-v-bde767d2] {
+ font-size: small;
+}
+.comfy-vue-side-bar-body[data-v-bde767d2] {
+ flex-grow: 1;
+ overflow: auto;
+ scrollbar-width: thin;
+ scrollbar-color: transparent transparent;
+}
+.comfy-vue-side-bar-body[data-v-bde767d2]::-webkit-scrollbar {
+ width: 1px;
+}
+.comfy-vue-side-bar-body[data-v-bde767d2]::-webkit-scrollbar-thumb {
+ background-color: transparent;
+}
+
+.queue-grid[data-v-7f831ee9] {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ padding: 0.5rem;
+ gap: 0.5rem;
+}
+
+.spinner[data-v-40c18658] {
+ position: absolute;
+ inset: 0px;
+ display: flex;
+ height: 100vh;
+ align-items: center;
+ justify-content: center
+}
diff --git a/web/assets/index-D8Zp4vRl.js b/web/assets/index-D8Zp4vRl.js
new file mode 100644
index 000000000..9e41a540e
--- /dev/null
+++ b/web/assets/index-D8Zp4vRl.js
@@ -0,0 +1,84129 @@
+const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index--0nRVkuV.js","assets/index-DjWyclij.css","assets/userSelection-CH4RQEqW.js","assets/userSelection-BGzn1LuN.css"])))=>i.map(i=>d[i]);
+var __defProp2 = Object.defineProperty;
+var __defProps2 = Object.defineProperties;
+var __getOwnPropDescs2 = Object.getOwnPropertyDescriptors;
+var __getOwnPropSymbols2 = Object.getOwnPropertySymbols;
+var __hasOwnProp2 = Object.prototype.hasOwnProperty;
+var __propIsEnum2 = Object.prototype.propertyIsEnumerable;
+var __typeError = (msg) => {
+ throw TypeError(msg);
+};
+var __defNormalProp2 = (obj, key, value3) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value: value3 }) : obj[key] = value3;
+var __spreadValues2 = (a, b) => {
+ for (var prop2 in b || (b = {}))
+ if (__hasOwnProp2.call(b, prop2))
+ __defNormalProp2(a, prop2, b[prop2]);
+ if (__getOwnPropSymbols2)
+ for (var prop2 of __getOwnPropSymbols2(b)) {
+ if (__propIsEnum2.call(b, prop2))
+ __defNormalProp2(a, prop2, b[prop2]);
+ }
+ return a;
+};
+var __spreadProps2 = (a, b) => __defProps2(a, __getOwnPropDescs2(b));
+var __name = (target, value3) => __defProp2(target, "name", { value: value3, configurable: true });
+var __objRest2 = (source, exclude) => {
+ var target = {};
+ for (var prop2 in source)
+ if (__hasOwnProp2.call(source, prop2) && exclude.indexOf(prop2) < 0)
+ target[prop2] = source[prop2];
+ if (source != null && __getOwnPropSymbols2)
+ for (var prop2 of __getOwnPropSymbols2(source)) {
+ if (exclude.indexOf(prop2) < 0 && __propIsEnum2.call(source, prop2))
+ target[prop2] = source[prop2];
+ }
+ return target;
+};
+var __publicField2 = (obj, key, value3) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value3);
+var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
+var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
+var __privateAdd = (obj, member, value3) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value3);
+var __privateSet = (obj, member, value3, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value3) : member.set(obj, value3), value3);
+var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
+var __privateWrapper = (obj, member, setter, getter) => ({
+ set _(value3) {
+ __privateSet(obj, member, value3, setter);
+ },
+ get _() {
+ return __privateGet(obj, member, getter);
+ }
+});
+var __async = (__this, __arguments, generator) => {
+ return new Promise((resolve8, reject2) => {
+ var fulfilled = (value3) => {
+ try {
+ step(generator.next(value3));
+ } catch (e) {
+ reject2(e);
+ }
+ };
+ var rejected = (value3) => {
+ try {
+ step(generator.throw(value3));
+ } catch (e) {
+ reject2(e);
+ }
+ };
+ var step = (x2) => x2.done ? resolve8(x2.value) : Promise.resolve(x2.value).then(fulfilled, rejected);
+ step((generator = generator.apply(__this, __arguments)).next());
+ });
+};
+var _a, _registered, _ComfyApi_instances, pollQueue_fn, createSocket_fn, postItem_fn, _buttons, _b, _ComfySettingsDialog_instances, dispatchChange_fn, _type, _text, _reverse, _enabled, _console, _over, _popupOpen, _ComfyPopup_instances, hide_fn, show_fn, _escHandler, _clickHandler, _internalQueueSize, _app, _ChangeTracker_instances, setApp_fn, _resolve, _activePromptId, _unsavedCount, _activeWorkflow, _ComfyWorkflowManager_instances, bindExecutionEvents_fn, _name, _path, _pathParts, _isFavorite, _ComfyWorkflow_instances, updatePath_fn, save_fn, _first, _updateProgress, _updateActive, _ComfyWorkflowsMenu_instances, bindEvents_fn, getMenuOptions_fn, getFavoriteMenuOptions_fn, _ComfyWorkflowsContent_instances, expandNode_fn, updateActive_fn, removeActive_fn, getFavoriteIcon_fn, getFavoriteOverIcon_fn, getFavoriteTooltip_fn, getFavoriteButton_fn, getDeleteButton_fn, getInsertButton_fn, getRenameButton_fn, getWorkflowElement_fn, createLeafNode_fn, createNode_fn, _options, _sizeBreak, _lastSizeBreaks, _sizeBreaks, _cachedInnerSize, _cacheTimeout, _c, _d, _queueItems, _processingQueue, _ComfyApp_instances, invokeExtensions_fn, invokeExtensionsAsync_fn, addRestoreWorkflowView_fn, addNodeContextMenuHandler_fn, addNodeKeyHandler_fn, addDrawBackgroundHandler_fn, addDropHandler_fn, addPasteHandler_fn, addCopyHandler_fn, addProcessMouseHandler_fn, addProcessKeyHandler_fn, addDrawGroupsHandler_fn, addDrawNodeHandler_fn, addApiUpdateHandlers_fn, addKeyboardHandler_fn, addConfigureHandler_fn, addAfterConfigureHandler_fn, loadExtensions_fn, migrateSettings_fn, setUser_fn, formatPromptError_fn, formatExecutionError_fn;
+(/* @__PURE__ */ __name(function polyfill() {
+ const relList = document.createElement("link").relList;
+ if (relList && relList.supports && relList.supports("modulepreload")) {
+ return;
+ }
+ for (const link of document.querySelectorAll('link[rel="modulepreload"]')) {
+ processPreload(link);
+ }
+ new MutationObserver((mutations) => {
+ for (const mutation of mutations) {
+ if (mutation.type !== "childList") {
+ continue;
+ }
+ for (const node3 of mutation.addedNodes) {
+ if (node3.tagName === "LINK" && node3.rel === "modulepreload")
+ processPreload(node3);
+ }
+ }
+ }).observe(document, { childList: true, subtree: true });
+ function getFetchOpts(link) {
+ const fetchOpts = {};
+ if (link.integrity) fetchOpts.integrity = link.integrity;
+ if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy;
+ if (link.crossOrigin === "use-credentials")
+ fetchOpts.credentials = "include";
+ else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit";
+ else fetchOpts.credentials = "same-origin";
+ return fetchOpts;
+ }
+ __name(getFetchOpts, "getFetchOpts");
+ function processPreload(link) {
+ if (link.ep)
+ return;
+ link.ep = true;
+ const fetchOpts = getFetchOpts(link);
+ fetch(link.href, fetchOpts);
+ }
+ __name(processPreload, "processPreload");
+}, "polyfill"))();
+var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
+function getDefaultExportFromCjs(x2) {
+ return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
+}
+__name(getDefaultExportFromCjs, "getDefaultExportFromCjs");
+function getDefaultExportFromNamespaceIfPresent(n) {
+ return n && Object.prototype.hasOwnProperty.call(n, "default") ? n["default"] : n;
+}
+__name(getDefaultExportFromNamespaceIfPresent, "getDefaultExportFromNamespaceIfPresent");
+function getDefaultExportFromNamespaceIfNotNamed(n) {
+ return n && Object.prototype.hasOwnProperty.call(n, "default") && Object.keys(n).length === 1 ? n["default"] : n;
+}
+__name(getDefaultExportFromNamespaceIfNotNamed, "getDefaultExportFromNamespaceIfNotNamed");
+function getAugmentedNamespace(n) {
+ if (n.__esModule) return n;
+ var f = n.default;
+ if (typeof f == "function") {
+ var a = /* @__PURE__ */ __name(function a2() {
+ if (this instanceof a2) {
+ return Reflect.construct(f, arguments, this.constructor);
+ }
+ return f.apply(this, arguments);
+ }, "a");
+ a.prototype = f.prototype;
+ } else a = {};
+ Object.defineProperty(a, "__esModule", { value: true });
+ Object.keys(n).forEach(function(k) {
+ var d = Object.getOwnPropertyDescriptor(n, k);
+ Object.defineProperty(a, k, d.get ? d : {
+ enumerable: true,
+ get: /* @__PURE__ */ __name(function() {
+ return n[k];
+ }, "get")
+ });
+ });
+ return a;
+}
+__name(getAugmentedNamespace, "getAugmentedNamespace");
+var _Reflect = {};
+/*! *****************************************************************************
+Copyright (C) Microsoft. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+var Reflect$1;
+(function(Reflect2) {
+ (function(factory) {
+ var root26 = typeof globalThis === "object" ? globalThis : typeof commonjsGlobal === "object" ? commonjsGlobal : typeof self === "object" ? self : typeof this === "object" ? this : sloppyModeThis();
+ var exporter = makeExporter(Reflect2);
+ if (typeof root26.Reflect !== "undefined") {
+ exporter = makeExporter(root26.Reflect, exporter);
+ }
+ factory(exporter, root26);
+ if (typeof root26.Reflect === "undefined") {
+ root26.Reflect = Reflect2;
+ }
+ function makeExporter(target, previous) {
+ return function(key, value3) {
+ Object.defineProperty(target, key, { configurable: true, writable: true, value: value3 });
+ if (previous)
+ previous(key, value3);
+ };
+ }
+ __name(makeExporter, "makeExporter");
+ function functionThis() {
+ try {
+ return Function("return this;")();
+ } catch (_2) {
+ }
+ }
+ __name(functionThis, "functionThis");
+ function indirectEvalThis() {
+ try {
+ return (void 0, eval)("(function() { return this; })()");
+ } catch (_2) {
+ }
+ }
+ __name(indirectEvalThis, "indirectEvalThis");
+ function sloppyModeThis() {
+ return functionThis() || indirectEvalThis();
+ }
+ __name(sloppyModeThis, "sloppyModeThis");
+ })(function(exporter, root26) {
+ var hasOwn2 = Object.prototype.hasOwnProperty;
+ var supportsSymbol = typeof Symbol === "function";
+ var toPrimitiveSymbol = supportsSymbol && typeof Symbol.toPrimitive !== "undefined" ? Symbol.toPrimitive : "@@toPrimitive";
+ var iteratorSymbol = supportsSymbol && typeof Symbol.iterator !== "undefined" ? Symbol.iterator : "@@iterator";
+ var supportsCreate = typeof Object.create === "function";
+ var supportsProto = { __proto__: [] } instanceof Array;
+ var downLevel = !supportsCreate && !supportsProto;
+ var HashMap = {
+ // create an object in dictionary mode (a.k.a. "slow" mode in v8)
+ create: supportsCreate ? function() {
+ return MakeDictionary(/* @__PURE__ */ Object.create(null));
+ } : supportsProto ? function() {
+ return MakeDictionary({ __proto__: null });
+ } : function() {
+ return MakeDictionary({});
+ },
+ has: downLevel ? function(map2, key) {
+ return hasOwn2.call(map2, key);
+ } : function(map2, key) {
+ return key in map2;
+ },
+ get: downLevel ? function(map2, key) {
+ return hasOwn2.call(map2, key) ? map2[key] : void 0;
+ } : function(map2, key) {
+ return map2[key];
+ }
+ };
+ var functionPrototype = Object.getPrototypeOf(Function);
+ var _Map = typeof Map === "function" && typeof Map.prototype.entries === "function" ? Map : CreateMapPolyfill();
+ var _Set = typeof Set === "function" && typeof Set.prototype.entries === "function" ? Set : CreateSetPolyfill();
+ var _WeakMap = typeof WeakMap === "function" ? WeakMap : CreateWeakMapPolyfill();
+ var registrySymbol = supportsSymbol ? Symbol.for("@reflect-metadata:registry") : void 0;
+ var metadataRegistry = GetOrCreateMetadataRegistry();
+ var metadataProvider = CreateMetadataProvider(metadataRegistry);
+ function decorate(decorators, target, propertyKey, attributes) {
+ if (!IsUndefined(propertyKey)) {
+ if (!IsArray(decorators))
+ throw new TypeError();
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsObject(attributes) && !IsUndefined(attributes) && !IsNull(attributes))
+ throw new TypeError();
+ if (IsNull(attributes))
+ attributes = void 0;
+ propertyKey = ToPropertyKey(propertyKey);
+ return DecorateProperty(decorators, target, propertyKey, attributes);
+ } else {
+ if (!IsArray(decorators))
+ throw new TypeError();
+ if (!IsConstructor(target))
+ throw new TypeError();
+ return DecorateConstructor(decorators, target);
+ }
+ }
+ __name(decorate, "decorate");
+ exporter("decorate", decorate);
+ function metadata(metadataKey, metadataValue) {
+ function decorator(target, propertyKey) {
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey) && !IsPropertyKey(propertyKey))
+ throw new TypeError();
+ OrdinaryDefineOwnMetadata(metadataKey, metadataValue, target, propertyKey);
+ }
+ __name(decorator, "decorator");
+ return decorator;
+ }
+ __name(metadata, "metadata");
+ exporter("metadata", metadata);
+ function defineMetadata(metadataKey, metadataValue, target, propertyKey) {
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey))
+ propertyKey = ToPropertyKey(propertyKey);
+ return OrdinaryDefineOwnMetadata(metadataKey, metadataValue, target, propertyKey);
+ }
+ __name(defineMetadata, "defineMetadata");
+ exporter("defineMetadata", defineMetadata);
+ function hasMetadata(metadataKey, target, propertyKey) {
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey))
+ propertyKey = ToPropertyKey(propertyKey);
+ return OrdinaryHasMetadata(metadataKey, target, propertyKey);
+ }
+ __name(hasMetadata, "hasMetadata");
+ exporter("hasMetadata", hasMetadata);
+ function hasOwnMetadata(metadataKey, target, propertyKey) {
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey))
+ propertyKey = ToPropertyKey(propertyKey);
+ return OrdinaryHasOwnMetadata(metadataKey, target, propertyKey);
+ }
+ __name(hasOwnMetadata, "hasOwnMetadata");
+ exporter("hasOwnMetadata", hasOwnMetadata);
+ function getMetadata(metadataKey, target, propertyKey) {
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey))
+ propertyKey = ToPropertyKey(propertyKey);
+ return OrdinaryGetMetadata(metadataKey, target, propertyKey);
+ }
+ __name(getMetadata, "getMetadata");
+ exporter("getMetadata", getMetadata);
+ function getOwnMetadata(metadataKey, target, propertyKey) {
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey))
+ propertyKey = ToPropertyKey(propertyKey);
+ return OrdinaryGetOwnMetadata(metadataKey, target, propertyKey);
+ }
+ __name(getOwnMetadata, "getOwnMetadata");
+ exporter("getOwnMetadata", getOwnMetadata);
+ function getMetadataKeys(target, propertyKey) {
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey))
+ propertyKey = ToPropertyKey(propertyKey);
+ return OrdinaryMetadataKeys(target, propertyKey);
+ }
+ __name(getMetadataKeys, "getMetadataKeys");
+ exporter("getMetadataKeys", getMetadataKeys);
+ function getOwnMetadataKeys(target, propertyKey) {
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey))
+ propertyKey = ToPropertyKey(propertyKey);
+ return OrdinaryOwnMetadataKeys(target, propertyKey);
+ }
+ __name(getOwnMetadataKeys, "getOwnMetadataKeys");
+ exporter("getOwnMetadataKeys", getOwnMetadataKeys);
+ function deleteMetadata(metadataKey, target, propertyKey) {
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey))
+ propertyKey = ToPropertyKey(propertyKey);
+ if (!IsObject(target))
+ throw new TypeError();
+ if (!IsUndefined(propertyKey))
+ propertyKey = ToPropertyKey(propertyKey);
+ var provider = GetMetadataProvider(
+ target,
+ propertyKey,
+ /*Create*/
+ false
+ );
+ if (IsUndefined(provider))
+ return false;
+ return provider.OrdinaryDeleteMetadata(metadataKey, target, propertyKey);
+ }
+ __name(deleteMetadata, "deleteMetadata");
+ exporter("deleteMetadata", deleteMetadata);
+ function DecorateConstructor(decorators, target) {
+ for (var i2 = decorators.length - 1; i2 >= 0; --i2) {
+ var decorator = decorators[i2];
+ var decorated = decorator(target);
+ if (!IsUndefined(decorated) && !IsNull(decorated)) {
+ if (!IsConstructor(decorated))
+ throw new TypeError();
+ target = decorated;
+ }
+ }
+ return target;
+ }
+ __name(DecorateConstructor, "DecorateConstructor");
+ function DecorateProperty(decorators, target, propertyKey, descriptor) {
+ for (var i2 = decorators.length - 1; i2 >= 0; --i2) {
+ var decorator = decorators[i2];
+ var decorated = decorator(target, propertyKey, descriptor);
+ if (!IsUndefined(decorated) && !IsNull(decorated)) {
+ if (!IsObject(decorated))
+ throw new TypeError();
+ descriptor = decorated;
+ }
+ }
+ return descriptor;
+ }
+ __name(DecorateProperty, "DecorateProperty");
+ function OrdinaryHasMetadata(MetadataKey, O, P) {
+ var hasOwn3 = OrdinaryHasOwnMetadata(MetadataKey, O, P);
+ if (hasOwn3)
+ return true;
+ var parent = OrdinaryGetPrototypeOf(O);
+ if (!IsNull(parent))
+ return OrdinaryHasMetadata(MetadataKey, parent, P);
+ return false;
+ }
+ __name(OrdinaryHasMetadata, "OrdinaryHasMetadata");
+ function OrdinaryHasOwnMetadata(MetadataKey, O, P) {
+ var provider = GetMetadataProvider(
+ O,
+ P,
+ /*Create*/
+ false
+ );
+ if (IsUndefined(provider))
+ return false;
+ return ToBoolean(provider.OrdinaryHasOwnMetadata(MetadataKey, O, P));
+ }
+ __name(OrdinaryHasOwnMetadata, "OrdinaryHasOwnMetadata");
+ function OrdinaryGetMetadata(MetadataKey, O, P) {
+ var hasOwn3 = OrdinaryHasOwnMetadata(MetadataKey, O, P);
+ if (hasOwn3)
+ return OrdinaryGetOwnMetadata(MetadataKey, O, P);
+ var parent = OrdinaryGetPrototypeOf(O);
+ if (!IsNull(parent))
+ return OrdinaryGetMetadata(MetadataKey, parent, P);
+ return void 0;
+ }
+ __name(OrdinaryGetMetadata, "OrdinaryGetMetadata");
+ function OrdinaryGetOwnMetadata(MetadataKey, O, P) {
+ var provider = GetMetadataProvider(
+ O,
+ P,
+ /*Create*/
+ false
+ );
+ if (IsUndefined(provider))
+ return;
+ return provider.OrdinaryGetOwnMetadata(MetadataKey, O, P);
+ }
+ __name(OrdinaryGetOwnMetadata, "OrdinaryGetOwnMetadata");
+ function OrdinaryDefineOwnMetadata(MetadataKey, MetadataValue, O, P) {
+ var provider = GetMetadataProvider(
+ O,
+ P,
+ /*Create*/
+ true
+ );
+ provider.OrdinaryDefineOwnMetadata(MetadataKey, MetadataValue, O, P);
+ }
+ __name(OrdinaryDefineOwnMetadata, "OrdinaryDefineOwnMetadata");
+ function OrdinaryMetadataKeys(O, P) {
+ var ownKeys2 = OrdinaryOwnMetadataKeys(O, P);
+ var parent = OrdinaryGetPrototypeOf(O);
+ if (parent === null)
+ return ownKeys2;
+ var parentKeys = OrdinaryMetadataKeys(parent, P);
+ if (parentKeys.length <= 0)
+ return ownKeys2;
+ if (ownKeys2.length <= 0)
+ return parentKeys;
+ var set2 = new _Set();
+ var keys = [];
+ for (var _i = 0, ownKeys_1 = ownKeys2; _i < ownKeys_1.length; _i++) {
+ var key = ownKeys_1[_i];
+ var hasKey = set2.has(key);
+ if (!hasKey) {
+ set2.add(key);
+ keys.push(key);
+ }
+ }
+ for (var _a2 = 0, parentKeys_1 = parentKeys; _a2 < parentKeys_1.length; _a2++) {
+ var key = parentKeys_1[_a2];
+ var hasKey = set2.has(key);
+ if (!hasKey) {
+ set2.add(key);
+ keys.push(key);
+ }
+ }
+ return keys;
+ }
+ __name(OrdinaryMetadataKeys, "OrdinaryMetadataKeys");
+ function OrdinaryOwnMetadataKeys(O, P) {
+ var provider = GetMetadataProvider(
+ O,
+ P,
+ /*create*/
+ false
+ );
+ if (!provider) {
+ return [];
+ }
+ return provider.OrdinaryOwnMetadataKeys(O, P);
+ }
+ __name(OrdinaryOwnMetadataKeys, "OrdinaryOwnMetadataKeys");
+ function Type2(x2) {
+ if (x2 === null)
+ return 1;
+ switch (typeof x2) {
+ case "undefined":
+ return 0;
+ case "boolean":
+ return 2;
+ case "string":
+ return 3;
+ case "symbol":
+ return 4;
+ case "number":
+ return 5;
+ case "object":
+ return x2 === null ? 1 : 6;
+ default:
+ return 6;
+ }
+ }
+ __name(Type2, "Type");
+ function IsUndefined(x2) {
+ return x2 === void 0;
+ }
+ __name(IsUndefined, "IsUndefined");
+ function IsNull(x2) {
+ return x2 === null;
+ }
+ __name(IsNull, "IsNull");
+ function IsSymbol(x2) {
+ return typeof x2 === "symbol";
+ }
+ __name(IsSymbol, "IsSymbol");
+ function IsObject(x2) {
+ return typeof x2 === "object" ? x2 !== null : typeof x2 === "function";
+ }
+ __name(IsObject, "IsObject");
+ function ToPrimitive(input, PreferredType) {
+ switch (Type2(input)) {
+ case 0:
+ return input;
+ case 1:
+ return input;
+ case 2:
+ return input;
+ case 3:
+ return input;
+ case 4:
+ return input;
+ case 5:
+ return input;
+ }
+ var hint = PreferredType === 3 ? "string" : PreferredType === 5 ? "number" : "default";
+ var exoticToPrim = GetMethod(input, toPrimitiveSymbol);
+ if (exoticToPrim !== void 0) {
+ var result = exoticToPrim.call(input, hint);
+ if (IsObject(result))
+ throw new TypeError();
+ return result;
+ }
+ return OrdinaryToPrimitive(input, hint === "default" ? "number" : hint);
+ }
+ __name(ToPrimitive, "ToPrimitive");
+ function OrdinaryToPrimitive(O, hint) {
+ if (hint === "string") {
+ var toString_1 = O.toString;
+ if (IsCallable(toString_1)) {
+ var result = toString_1.call(O);
+ if (!IsObject(result))
+ return result;
+ }
+ var valueOf = O.valueOf;
+ if (IsCallable(valueOf)) {
+ var result = valueOf.call(O);
+ if (!IsObject(result))
+ return result;
+ }
+ } else {
+ var valueOf = O.valueOf;
+ if (IsCallable(valueOf)) {
+ var result = valueOf.call(O);
+ if (!IsObject(result))
+ return result;
+ }
+ var toString_2 = O.toString;
+ if (IsCallable(toString_2)) {
+ var result = toString_2.call(O);
+ if (!IsObject(result))
+ return result;
+ }
+ }
+ throw new TypeError();
+ }
+ __name(OrdinaryToPrimitive, "OrdinaryToPrimitive");
+ function ToBoolean(argument) {
+ return !!argument;
+ }
+ __name(ToBoolean, "ToBoolean");
+ function ToString(argument) {
+ return "" + argument;
+ }
+ __name(ToString, "ToString");
+ function ToPropertyKey(argument) {
+ var key = ToPrimitive(
+ argument,
+ 3
+ /* String */
+ );
+ if (IsSymbol(key))
+ return key;
+ return ToString(key);
+ }
+ __name(ToPropertyKey, "ToPropertyKey");
+ function IsArray(argument) {
+ return Array.isArray ? Array.isArray(argument) : argument instanceof Object ? argument instanceof Array : Object.prototype.toString.call(argument) === "[object Array]";
+ }
+ __name(IsArray, "IsArray");
+ function IsCallable(argument) {
+ return typeof argument === "function";
+ }
+ __name(IsCallable, "IsCallable");
+ function IsConstructor(argument) {
+ return typeof argument === "function";
+ }
+ __name(IsConstructor, "IsConstructor");
+ function IsPropertyKey(argument) {
+ switch (Type2(argument)) {
+ case 3:
+ return true;
+ case 4:
+ return true;
+ default:
+ return false;
+ }
+ }
+ __name(IsPropertyKey, "IsPropertyKey");
+ function SameValueZero(x2, y2) {
+ return x2 === y2 || x2 !== x2 && y2 !== y2;
+ }
+ __name(SameValueZero, "SameValueZero");
+ function GetMethod(V, P) {
+ var func = V[P];
+ if (func === void 0 || func === null)
+ return void 0;
+ if (!IsCallable(func))
+ throw new TypeError();
+ return func;
+ }
+ __name(GetMethod, "GetMethod");
+ function GetIterator(obj) {
+ var method = GetMethod(obj, iteratorSymbol);
+ if (!IsCallable(method))
+ throw new TypeError();
+ var iterator = method.call(obj);
+ if (!IsObject(iterator))
+ throw new TypeError();
+ return iterator;
+ }
+ __name(GetIterator, "GetIterator");
+ function IteratorValue(iterResult) {
+ return iterResult.value;
+ }
+ __name(IteratorValue, "IteratorValue");
+ function IteratorStep(iterator) {
+ var result = iterator.next();
+ return result.done ? false : result;
+ }
+ __name(IteratorStep, "IteratorStep");
+ function IteratorClose(iterator) {
+ var f = iterator["return"];
+ if (f)
+ f.call(iterator);
+ }
+ __name(IteratorClose, "IteratorClose");
+ function OrdinaryGetPrototypeOf(O) {
+ var proto = Object.getPrototypeOf(O);
+ if (typeof O !== "function" || O === functionPrototype)
+ return proto;
+ if (proto !== functionPrototype)
+ return proto;
+ var prototype = O.prototype;
+ var prototypeProto = prototype && Object.getPrototypeOf(prototype);
+ if (prototypeProto == null || prototypeProto === Object.prototype)
+ return proto;
+ var constructor = prototypeProto.constructor;
+ if (typeof constructor !== "function")
+ return proto;
+ if (constructor === O)
+ return proto;
+ return constructor;
+ }
+ __name(OrdinaryGetPrototypeOf, "OrdinaryGetPrototypeOf");
+ function CreateMetadataRegistry() {
+ var fallback;
+ if (!IsUndefined(registrySymbol) && typeof root26.Reflect !== "undefined" && !(registrySymbol in root26.Reflect) && typeof root26.Reflect.defineMetadata === "function") {
+ fallback = CreateFallbackProvider(root26.Reflect);
+ }
+ var first;
+ var second;
+ var rest;
+ var targetProviderMap = new _WeakMap();
+ var registry = {
+ registerProvider,
+ getProvider,
+ setProvider
+ };
+ return registry;
+ function registerProvider(provider) {
+ if (!Object.isExtensible(registry)) {
+ throw new Error("Cannot add provider to a frozen registry.");
+ }
+ switch (true) {
+ case fallback === provider:
+ break;
+ case IsUndefined(first):
+ first = provider;
+ break;
+ case first === provider:
+ break;
+ case IsUndefined(second):
+ second = provider;
+ break;
+ case second === provider:
+ break;
+ default:
+ if (rest === void 0)
+ rest = new _Set();
+ rest.add(provider);
+ break;
+ }
+ }
+ __name(registerProvider, "registerProvider");
+ function getProviderNoCache(O, P) {
+ if (!IsUndefined(first)) {
+ if (first.isProviderFor(O, P))
+ return first;
+ if (!IsUndefined(second)) {
+ if (second.isProviderFor(O, P))
+ return first;
+ if (!IsUndefined(rest)) {
+ var iterator = GetIterator(rest);
+ while (true) {
+ var next = IteratorStep(iterator);
+ if (!next) {
+ return void 0;
+ }
+ var provider = IteratorValue(next);
+ if (provider.isProviderFor(O, P)) {
+ IteratorClose(iterator);
+ return provider;
+ }
+ }
+ }
+ }
+ }
+ if (!IsUndefined(fallback) && fallback.isProviderFor(O, P)) {
+ return fallback;
+ }
+ return void 0;
+ }
+ __name(getProviderNoCache, "getProviderNoCache");
+ function getProvider(O, P) {
+ var providerMap = targetProviderMap.get(O);
+ var provider;
+ if (!IsUndefined(providerMap)) {
+ provider = providerMap.get(P);
+ }
+ if (!IsUndefined(provider)) {
+ return provider;
+ }
+ provider = getProviderNoCache(O, P);
+ if (!IsUndefined(provider)) {
+ if (IsUndefined(providerMap)) {
+ providerMap = new _Map();
+ targetProviderMap.set(O, providerMap);
+ }
+ providerMap.set(P, provider);
+ }
+ return provider;
+ }
+ __name(getProvider, "getProvider");
+ function hasProvider(provider) {
+ if (IsUndefined(provider))
+ throw new TypeError();
+ return first === provider || second === provider || !IsUndefined(rest) && rest.has(provider);
+ }
+ __name(hasProvider, "hasProvider");
+ function setProvider(O, P, provider) {
+ if (!hasProvider(provider)) {
+ throw new Error("Metadata provider not registered.");
+ }
+ var existingProvider = getProvider(O, P);
+ if (existingProvider !== provider) {
+ if (!IsUndefined(existingProvider)) {
+ return false;
+ }
+ var providerMap = targetProviderMap.get(O);
+ if (IsUndefined(providerMap)) {
+ providerMap = new _Map();
+ targetProviderMap.set(O, providerMap);
+ }
+ providerMap.set(P, provider);
+ }
+ return true;
+ }
+ __name(setProvider, "setProvider");
+ }
+ __name(CreateMetadataRegistry, "CreateMetadataRegistry");
+ function GetOrCreateMetadataRegistry() {
+ var metadataRegistry2;
+ if (!IsUndefined(registrySymbol) && IsObject(root26.Reflect) && Object.isExtensible(root26.Reflect)) {
+ metadataRegistry2 = root26.Reflect[registrySymbol];
+ }
+ if (IsUndefined(metadataRegistry2)) {
+ metadataRegistry2 = CreateMetadataRegistry();
+ }
+ if (!IsUndefined(registrySymbol) && IsObject(root26.Reflect) && Object.isExtensible(root26.Reflect)) {
+ Object.defineProperty(root26.Reflect, registrySymbol, {
+ enumerable: false,
+ configurable: false,
+ writable: false,
+ value: metadataRegistry2
+ });
+ }
+ return metadataRegistry2;
+ }
+ __name(GetOrCreateMetadataRegistry, "GetOrCreateMetadataRegistry");
+ function CreateMetadataProvider(registry) {
+ var metadata2 = new _WeakMap();
+ var provider = {
+ isProviderFor: /* @__PURE__ */ __name(function(O, P) {
+ var targetMetadata = metadata2.get(O);
+ if (IsUndefined(targetMetadata))
+ return false;
+ return targetMetadata.has(P);
+ }, "isProviderFor"),
+ OrdinaryDefineOwnMetadata: OrdinaryDefineOwnMetadata2,
+ OrdinaryHasOwnMetadata: OrdinaryHasOwnMetadata2,
+ OrdinaryGetOwnMetadata: OrdinaryGetOwnMetadata2,
+ OrdinaryOwnMetadataKeys: OrdinaryOwnMetadataKeys2,
+ OrdinaryDeleteMetadata
+ };
+ metadataRegistry.registerProvider(provider);
+ return provider;
+ function GetOrCreateMetadataMap(O, P, Create) {
+ var targetMetadata = metadata2.get(O);
+ var createdTargetMetadata = false;
+ if (IsUndefined(targetMetadata)) {
+ if (!Create)
+ return void 0;
+ targetMetadata = new _Map();
+ metadata2.set(O, targetMetadata);
+ createdTargetMetadata = true;
+ }
+ var metadataMap = targetMetadata.get(P);
+ if (IsUndefined(metadataMap)) {
+ if (!Create)
+ return void 0;
+ metadataMap = new _Map();
+ targetMetadata.set(P, metadataMap);
+ if (!registry.setProvider(O, P, provider)) {
+ targetMetadata.delete(P);
+ if (createdTargetMetadata) {
+ metadata2.delete(O);
+ }
+ throw new Error("Wrong provider for target.");
+ }
+ }
+ return metadataMap;
+ }
+ __name(GetOrCreateMetadataMap, "GetOrCreateMetadataMap");
+ function OrdinaryHasOwnMetadata2(MetadataKey, O, P) {
+ var metadataMap = GetOrCreateMetadataMap(
+ O,
+ P,
+ /*Create*/
+ false
+ );
+ if (IsUndefined(metadataMap))
+ return false;
+ return ToBoolean(metadataMap.has(MetadataKey));
+ }
+ __name(OrdinaryHasOwnMetadata2, "OrdinaryHasOwnMetadata");
+ function OrdinaryGetOwnMetadata2(MetadataKey, O, P) {
+ var metadataMap = GetOrCreateMetadataMap(
+ O,
+ P,
+ /*Create*/
+ false
+ );
+ if (IsUndefined(metadataMap))
+ return void 0;
+ return metadataMap.get(MetadataKey);
+ }
+ __name(OrdinaryGetOwnMetadata2, "OrdinaryGetOwnMetadata");
+ function OrdinaryDefineOwnMetadata2(MetadataKey, MetadataValue, O, P) {
+ var metadataMap = GetOrCreateMetadataMap(
+ O,
+ P,
+ /*Create*/
+ true
+ );
+ metadataMap.set(MetadataKey, MetadataValue);
+ }
+ __name(OrdinaryDefineOwnMetadata2, "OrdinaryDefineOwnMetadata");
+ function OrdinaryOwnMetadataKeys2(O, P) {
+ var keys = [];
+ var metadataMap = GetOrCreateMetadataMap(
+ O,
+ P,
+ /*Create*/
+ false
+ );
+ if (IsUndefined(metadataMap))
+ return keys;
+ var keysObj = metadataMap.keys();
+ var iterator = GetIterator(keysObj);
+ var k = 0;
+ while (true) {
+ var next = IteratorStep(iterator);
+ if (!next) {
+ keys.length = k;
+ return keys;
+ }
+ var nextValue = IteratorValue(next);
+ try {
+ keys[k] = nextValue;
+ } catch (e) {
+ try {
+ IteratorClose(iterator);
+ } finally {
+ throw e;
+ }
+ }
+ k++;
+ }
+ }
+ __name(OrdinaryOwnMetadataKeys2, "OrdinaryOwnMetadataKeys");
+ function OrdinaryDeleteMetadata(MetadataKey, O, P) {
+ var metadataMap = GetOrCreateMetadataMap(
+ O,
+ P,
+ /*Create*/
+ false
+ );
+ if (IsUndefined(metadataMap))
+ return false;
+ if (!metadataMap.delete(MetadataKey))
+ return false;
+ if (metadataMap.size === 0) {
+ var targetMetadata = metadata2.get(O);
+ if (!IsUndefined(targetMetadata)) {
+ targetMetadata.delete(P);
+ if (targetMetadata.size === 0) {
+ metadata2.delete(targetMetadata);
+ }
+ }
+ }
+ return true;
+ }
+ __name(OrdinaryDeleteMetadata, "OrdinaryDeleteMetadata");
+ }
+ __name(CreateMetadataProvider, "CreateMetadataProvider");
+ function CreateFallbackProvider(reflect) {
+ var defineMetadata2 = reflect.defineMetadata, hasOwnMetadata2 = reflect.hasOwnMetadata, getOwnMetadata2 = reflect.getOwnMetadata, getOwnMetadataKeys2 = reflect.getOwnMetadataKeys, deleteMetadata2 = reflect.deleteMetadata;
+ var metadataOwner = new _WeakMap();
+ var provider = {
+ isProviderFor: /* @__PURE__ */ __name(function(O, P) {
+ var metadataPropertySet = metadataOwner.get(O);
+ if (!IsUndefined(metadataPropertySet) && metadataPropertySet.has(P)) {
+ return true;
+ }
+ if (getOwnMetadataKeys2(O, P).length) {
+ if (IsUndefined(metadataPropertySet)) {
+ metadataPropertySet = new _Set();
+ metadataOwner.set(O, metadataPropertySet);
+ }
+ metadataPropertySet.add(P);
+ return true;
+ }
+ return false;
+ }, "isProviderFor"),
+ OrdinaryDefineOwnMetadata: defineMetadata2,
+ OrdinaryHasOwnMetadata: hasOwnMetadata2,
+ OrdinaryGetOwnMetadata: getOwnMetadata2,
+ OrdinaryOwnMetadataKeys: getOwnMetadataKeys2,
+ OrdinaryDeleteMetadata: deleteMetadata2
+ };
+ return provider;
+ }
+ __name(CreateFallbackProvider, "CreateFallbackProvider");
+ function GetMetadataProvider(O, P, Create) {
+ var registeredProvider = metadataRegistry.getProvider(O, P);
+ if (!IsUndefined(registeredProvider)) {
+ return registeredProvider;
+ }
+ if (Create) {
+ if (metadataRegistry.setProvider(O, P, metadataProvider)) {
+ return metadataProvider;
+ }
+ throw new Error("Illegal state.");
+ }
+ return void 0;
+ }
+ __name(GetMetadataProvider, "GetMetadataProvider");
+ function CreateMapPolyfill() {
+ var cacheSentinel = {};
+ var arraySentinel = [];
+ var MapIterator = (
+ /** @class */
+ function() {
+ function MapIterator2(keys, values2, selector) {
+ this._index = 0;
+ this._keys = keys;
+ this._values = values2;
+ this._selector = selector;
+ }
+ __name(MapIterator2, "MapIterator");
+ MapIterator2.prototype["@@iterator"] = function() {
+ return this;
+ };
+ MapIterator2.prototype[iteratorSymbol] = function() {
+ return this;
+ };
+ MapIterator2.prototype.next = function() {
+ var index2 = this._index;
+ if (index2 >= 0 && index2 < this._keys.length) {
+ var result = this._selector(this._keys[index2], this._values[index2]);
+ if (index2 + 1 >= this._keys.length) {
+ this._index = -1;
+ this._keys = arraySentinel;
+ this._values = arraySentinel;
+ } else {
+ this._index++;
+ }
+ return { value: result, done: false };
+ }
+ return { value: void 0, done: true };
+ };
+ MapIterator2.prototype.throw = function(error) {
+ if (this._index >= 0) {
+ this._index = -1;
+ this._keys = arraySentinel;
+ this._values = arraySentinel;
+ }
+ throw error;
+ };
+ MapIterator2.prototype.return = function(value3) {
+ if (this._index >= 0) {
+ this._index = -1;
+ this._keys = arraySentinel;
+ this._values = arraySentinel;
+ }
+ return { value: value3, done: true };
+ };
+ return MapIterator2;
+ }()
+ );
+ var Map2 = (
+ /** @class */
+ function() {
+ function Map3() {
+ this._keys = [];
+ this._values = [];
+ this._cacheKey = cacheSentinel;
+ this._cacheIndex = -2;
+ }
+ __name(Map3, "Map");
+ Object.defineProperty(Map3.prototype, "size", {
+ get: /* @__PURE__ */ __name(function() {
+ return this._keys.length;
+ }, "get"),
+ enumerable: true,
+ configurable: true
+ });
+ Map3.prototype.has = function(key) {
+ return this._find(
+ key,
+ /*insert*/
+ false
+ ) >= 0;
+ };
+ Map3.prototype.get = function(key) {
+ var index2 = this._find(
+ key,
+ /*insert*/
+ false
+ );
+ return index2 >= 0 ? this._values[index2] : void 0;
+ };
+ Map3.prototype.set = function(key, value3) {
+ var index2 = this._find(
+ key,
+ /*insert*/
+ true
+ );
+ this._values[index2] = value3;
+ return this;
+ };
+ Map3.prototype.delete = function(key) {
+ var index2 = this._find(
+ key,
+ /*insert*/
+ false
+ );
+ if (index2 >= 0) {
+ var size2 = this._keys.length;
+ for (var i2 = index2 + 1; i2 < size2; i2++) {
+ this._keys[i2 - 1] = this._keys[i2];
+ this._values[i2 - 1] = this._values[i2];
+ }
+ this._keys.length--;
+ this._values.length--;
+ if (SameValueZero(key, this._cacheKey)) {
+ this._cacheKey = cacheSentinel;
+ this._cacheIndex = -2;
+ }
+ return true;
+ }
+ return false;
+ };
+ Map3.prototype.clear = function() {
+ this._keys.length = 0;
+ this._values.length = 0;
+ this._cacheKey = cacheSentinel;
+ this._cacheIndex = -2;
+ };
+ Map3.prototype.keys = function() {
+ return new MapIterator(this._keys, this._values, getKey);
+ };
+ Map3.prototype.values = function() {
+ return new MapIterator(this._keys, this._values, getValue2);
+ };
+ Map3.prototype.entries = function() {
+ return new MapIterator(this._keys, this._values, getEntry);
+ };
+ Map3.prototype["@@iterator"] = function() {
+ return this.entries();
+ };
+ Map3.prototype[iteratorSymbol] = function() {
+ return this.entries();
+ };
+ Map3.prototype._find = function(key, insert2) {
+ if (!SameValueZero(this._cacheKey, key)) {
+ this._cacheIndex = -1;
+ for (var i2 = 0; i2 < this._keys.length; i2++) {
+ if (SameValueZero(this._keys[i2], key)) {
+ this._cacheIndex = i2;
+ break;
+ }
+ }
+ }
+ if (this._cacheIndex < 0 && insert2) {
+ this._cacheIndex = this._keys.length;
+ this._keys.push(key);
+ this._values.push(void 0);
+ }
+ return this._cacheIndex;
+ };
+ return Map3;
+ }()
+ );
+ return Map2;
+ function getKey(key, _2) {
+ return key;
+ }
+ __name(getKey, "getKey");
+ function getValue2(_2, value3) {
+ return value3;
+ }
+ __name(getValue2, "getValue");
+ function getEntry(key, value3) {
+ return [key, value3];
+ }
+ __name(getEntry, "getEntry");
+ }
+ __name(CreateMapPolyfill, "CreateMapPolyfill");
+ function CreateSetPolyfill() {
+ var Set2 = (
+ /** @class */
+ function() {
+ function Set3() {
+ this._map = new _Map();
+ }
+ __name(Set3, "Set");
+ Object.defineProperty(Set3.prototype, "size", {
+ get: /* @__PURE__ */ __name(function() {
+ return this._map.size;
+ }, "get"),
+ enumerable: true,
+ configurable: true
+ });
+ Set3.prototype.has = function(value3) {
+ return this._map.has(value3);
+ };
+ Set3.prototype.add = function(value3) {
+ return this._map.set(value3, value3), this;
+ };
+ Set3.prototype.delete = function(value3) {
+ return this._map.delete(value3);
+ };
+ Set3.prototype.clear = function() {
+ this._map.clear();
+ };
+ Set3.prototype.keys = function() {
+ return this._map.keys();
+ };
+ Set3.prototype.values = function() {
+ return this._map.keys();
+ };
+ Set3.prototype.entries = function() {
+ return this._map.entries();
+ };
+ Set3.prototype["@@iterator"] = function() {
+ return this.keys();
+ };
+ Set3.prototype[iteratorSymbol] = function() {
+ return this.keys();
+ };
+ return Set3;
+ }()
+ );
+ return Set2;
+ }
+ __name(CreateSetPolyfill, "CreateSetPolyfill");
+ function CreateWeakMapPolyfill() {
+ var UUID_SIZE = 16;
+ var keys = HashMap.create();
+ var rootKey = CreateUniqueKey();
+ return (
+ /** @class */
+ function() {
+ function WeakMap2() {
+ this._key = CreateUniqueKey();
+ }
+ __name(WeakMap2, "WeakMap");
+ WeakMap2.prototype.has = function(target) {
+ var table = GetOrCreateWeakMapTable(
+ target,
+ /*create*/
+ false
+ );
+ return table !== void 0 ? HashMap.has(table, this._key) : false;
+ };
+ WeakMap2.prototype.get = function(target) {
+ var table = GetOrCreateWeakMapTable(
+ target,
+ /*create*/
+ false
+ );
+ return table !== void 0 ? HashMap.get(table, this._key) : void 0;
+ };
+ WeakMap2.prototype.set = function(target, value3) {
+ var table = GetOrCreateWeakMapTable(
+ target,
+ /*create*/
+ true
+ );
+ table[this._key] = value3;
+ return this;
+ };
+ WeakMap2.prototype.delete = function(target) {
+ var table = GetOrCreateWeakMapTable(
+ target,
+ /*create*/
+ false
+ );
+ return table !== void 0 ? delete table[this._key] : false;
+ };
+ WeakMap2.prototype.clear = function() {
+ this._key = CreateUniqueKey();
+ };
+ return WeakMap2;
+ }()
+ );
+ function CreateUniqueKey() {
+ var key;
+ do
+ key = "@@WeakMap@@" + CreateUUID();
+ while (HashMap.has(keys, key));
+ keys[key] = true;
+ return key;
+ }
+ __name(CreateUniqueKey, "CreateUniqueKey");
+ function GetOrCreateWeakMapTable(target, create2) {
+ if (!hasOwn2.call(target, rootKey)) {
+ if (!create2)
+ return void 0;
+ Object.defineProperty(target, rootKey, { value: HashMap.create() });
+ }
+ return target[rootKey];
+ }
+ __name(GetOrCreateWeakMapTable, "GetOrCreateWeakMapTable");
+ function FillRandomBytes(buffer2, size2) {
+ for (var i2 = 0; i2 < size2; ++i2)
+ buffer2[i2] = Math.random() * 255 | 0;
+ return buffer2;
+ }
+ __name(FillRandomBytes, "FillRandomBytes");
+ function GenRandomBytes(size2) {
+ if (typeof Uint8Array === "function") {
+ var array = new Uint8Array(size2);
+ if (typeof crypto !== "undefined") {
+ crypto.getRandomValues(array);
+ } else if (typeof msCrypto !== "undefined") {
+ msCrypto.getRandomValues(array);
+ } else {
+ FillRandomBytes(array, size2);
+ }
+ return array;
+ }
+ return FillRandomBytes(new Array(size2), size2);
+ }
+ __name(GenRandomBytes, "GenRandomBytes");
+ function CreateUUID() {
+ var data19 = GenRandomBytes(UUID_SIZE);
+ data19[6] = data19[6] & 79 | 64;
+ data19[8] = data19[8] & 191 | 128;
+ var result = "";
+ for (var offset = 0; offset < UUID_SIZE; ++offset) {
+ var byte = data19[offset];
+ if (offset === 4 || offset === 6 || offset === 8)
+ result += "-";
+ if (byte < 16)
+ result += "0";
+ result += byte.toString(16).toLowerCase();
+ }
+ return result;
+ }
+ __name(CreateUUID, "CreateUUID");
+ }
+ __name(CreateWeakMapPolyfill, "CreateWeakMapPolyfill");
+ function MakeDictionary(obj) {
+ obj.__ = void 0;
+ delete obj.__;
+ return obj;
+ }
+ __name(MakeDictionary, "MakeDictionary");
+ });
+})(Reflect$1 || (Reflect$1 = {}));
+window["__COMFYUI_FRONTEND_VERSION__"] = "1.2.26";
+console.log("ComfyUI Front-end version:", "1.2.26");
+/**
+* @vue/shared v3.4.31
+* (c) 2018-present Yuxi (Evan) You and Vue contributors
+* @license MIT
+**/
+/*! #__NO_SIDE_EFFECTS__ */
+// @__NO_SIDE_EFFECTS__
+function makeMap(str, expectsLowerCase) {
+ const set2 = new Set(str.split(","));
+ return expectsLowerCase ? (val) => set2.has(val.toLowerCase()) : (val) => set2.has(val);
+}
+__name(makeMap, "makeMap");
+const EMPTY_OBJ = false ? Object.freeze({}) : {};
+const EMPTY_ARR = false ? Object.freeze([]) : [];
+const NOOP = /* @__PURE__ */ __name(() => {
+}, "NOOP");
+const NO = /* @__PURE__ */ __name(() => false, "NO");
+const isOn = /* @__PURE__ */ __name((key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && // uppercase letter
+(key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97), "isOn");
+const isModelListener = /* @__PURE__ */ __name((key) => key.startsWith("onUpdate:"), "isModelListener");
+const extend = Object.assign;
+const remove$1 = /* @__PURE__ */ __name((arr, el) => {
+ const i2 = arr.indexOf(el);
+ if (i2 > -1) {
+ arr.splice(i2, 1);
+ }
+}, "remove$1");
+const hasOwnProperty$2 = Object.prototype.hasOwnProperty;
+const hasOwn$2 = /* @__PURE__ */ __name((val, key) => hasOwnProperty$2.call(val, key), "hasOwn$2");
+const isArray$3 = Array.isArray;
+const isMap = /* @__PURE__ */ __name((val) => toTypeString$1(val) === "[object Map]", "isMap");
+const isSet = /* @__PURE__ */ __name((val) => toTypeString$1(val) === "[object Set]", "isSet");
+const isDate$2 = /* @__PURE__ */ __name((val) => toTypeString$1(val) === "[object Date]", "isDate$2");
+const isRegExp$1 = /* @__PURE__ */ __name((val) => toTypeString$1(val) === "[object RegExp]", "isRegExp$1");
+const isFunction$3 = /* @__PURE__ */ __name((val) => typeof val === "function", "isFunction$3");
+const isString$4 = /* @__PURE__ */ __name((val) => typeof val === "string", "isString$4");
+const isSymbol$1 = /* @__PURE__ */ __name((val) => typeof val === "symbol", "isSymbol$1");
+const isObject$4 = /* @__PURE__ */ __name((val) => val !== null && typeof val === "object", "isObject$4");
+const isPromise$2 = /* @__PURE__ */ __name((val) => {
+ return (isObject$4(val) || isFunction$3(val)) && isFunction$3(val.then) && isFunction$3(val.catch);
+}, "isPromise$2");
+const objectToString$1 = Object.prototype.toString;
+const toTypeString$1 = /* @__PURE__ */ __name((value3) => objectToString$1.call(value3), "toTypeString$1");
+const toRawType = /* @__PURE__ */ __name((value3) => {
+ return toTypeString$1(value3).slice(8, -1);
+}, "toRawType");
+const isPlainObject$2 = /* @__PURE__ */ __name((val) => toTypeString$1(val) === "[object Object]", "isPlainObject$2");
+const isIntegerKey = /* @__PURE__ */ __name((key) => isString$4(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key, "isIntegerKey");
+const isReservedProp = /* @__PURE__ */ makeMap(
+ // the leading comma is intentional so empty string "" is also included
+ ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"
+);
+const isBuiltInDirective = /* @__PURE__ */ makeMap(
+ "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo"
+);
+const cacheStringFunction = /* @__PURE__ */ __name((fn) => {
+ const cache2 = /* @__PURE__ */ Object.create(null);
+ return (str) => {
+ const hit = cache2[str];
+ return hit || (cache2[str] = fn(str));
+ };
+}, "cacheStringFunction");
+const camelizeRE = /-(\w)/g;
+const camelize = cacheStringFunction((str) => {
+ return str.replace(camelizeRE, (_2, c) => c ? c.toUpperCase() : "");
+});
+const hyphenateRE = /\B([A-Z])/g;
+const hyphenate = cacheStringFunction(
+ (str) => str.replace(hyphenateRE, "-$1").toLowerCase()
+);
+const capitalize$1 = cacheStringFunction((str) => {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+});
+const toHandlerKey = cacheStringFunction((str) => {
+ const s = str ? `on${capitalize$1(str)}` : ``;
+ return s;
+});
+const hasChanged = /* @__PURE__ */ __name((value3, oldValue) => !Object.is(value3, oldValue), "hasChanged");
+const invokeArrayFns = /* @__PURE__ */ __name((fns, ...arg) => {
+ for (let i2 = 0; i2 < fns.length; i2++) {
+ fns[i2](...arg);
+ }
+}, "invokeArrayFns");
+const def = /* @__PURE__ */ __name((obj, key, value3, writable = false) => {
+ Object.defineProperty(obj, key, {
+ configurable: true,
+ enumerable: false,
+ writable,
+ value: value3
+ });
+}, "def");
+const looseToNumber = /* @__PURE__ */ __name((val) => {
+ const n = parseFloat(val);
+ return isNaN(n) ? val : n;
+}, "looseToNumber");
+const toNumber = /* @__PURE__ */ __name((val) => {
+ const n = isString$4(val) ? Number(val) : NaN;
+ return isNaN(n) ? val : n;
+}, "toNumber");
+let _globalThis$1;
+const getGlobalThis$1 = /* @__PURE__ */ __name(() => {
+ return _globalThis$1 || (_globalThis$1 = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {});
+}, "getGlobalThis$1");
+const identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/;
+function genPropsAccessExp(name) {
+ return identRE.test(name) ? `__props.${name}` : `__props[${JSON.stringify(name)}]`;
+}
+__name(genPropsAccessExp, "genPropsAccessExp");
+const PatchFlags = {
+ "TEXT": 1,
+ "1": "TEXT",
+ "CLASS": 2,
+ "2": "CLASS",
+ "STYLE": 4,
+ "4": "STYLE",
+ "PROPS": 8,
+ "8": "PROPS",
+ "FULL_PROPS": 16,
+ "16": "FULL_PROPS",
+ "NEED_HYDRATION": 32,
+ "32": "NEED_HYDRATION",
+ "STABLE_FRAGMENT": 64,
+ "64": "STABLE_FRAGMENT",
+ "KEYED_FRAGMENT": 128,
+ "128": "KEYED_FRAGMENT",
+ "UNKEYED_FRAGMENT": 256,
+ "256": "UNKEYED_FRAGMENT",
+ "NEED_PATCH": 512,
+ "512": "NEED_PATCH",
+ "DYNAMIC_SLOTS": 1024,
+ "1024": "DYNAMIC_SLOTS",
+ "DEV_ROOT_FRAGMENT": 2048,
+ "2048": "DEV_ROOT_FRAGMENT",
+ "HOISTED": -1,
+ "-1": "HOISTED",
+ "BAIL": -2,
+ "-2": "BAIL"
+};
+const PatchFlagNames = {
+ [1]: `TEXT`,
+ [2]: `CLASS`,
+ [4]: `STYLE`,
+ [8]: `PROPS`,
+ [16]: `FULL_PROPS`,
+ [32]: `NEED_HYDRATION`,
+ [64]: `STABLE_FRAGMENT`,
+ [128]: `KEYED_FRAGMENT`,
+ [256]: `UNKEYED_FRAGMENT`,
+ [512]: `NEED_PATCH`,
+ [1024]: `DYNAMIC_SLOTS`,
+ [2048]: `DEV_ROOT_FRAGMENT`,
+ [-1]: `HOISTED`,
+ [-2]: `BAIL`
+};
+const ShapeFlags = {
+ "ELEMENT": 1,
+ "1": "ELEMENT",
+ "FUNCTIONAL_COMPONENT": 2,
+ "2": "FUNCTIONAL_COMPONENT",
+ "STATEFUL_COMPONENT": 4,
+ "4": "STATEFUL_COMPONENT",
+ "TEXT_CHILDREN": 8,
+ "8": "TEXT_CHILDREN",
+ "ARRAY_CHILDREN": 16,
+ "16": "ARRAY_CHILDREN",
+ "SLOTS_CHILDREN": 32,
+ "32": "SLOTS_CHILDREN",
+ "TELEPORT": 64,
+ "64": "TELEPORT",
+ "SUSPENSE": 128,
+ "128": "SUSPENSE",
+ "COMPONENT_SHOULD_KEEP_ALIVE": 256,
+ "256": "COMPONENT_SHOULD_KEEP_ALIVE",
+ "COMPONENT_KEPT_ALIVE": 512,
+ "512": "COMPONENT_KEPT_ALIVE",
+ "COMPONENT": 6,
+ "6": "COMPONENT"
+};
+const SlotFlags = {
+ "STABLE": 1,
+ "1": "STABLE",
+ "DYNAMIC": 2,
+ "2": "DYNAMIC",
+ "FORWARDED": 3,
+ "3": "FORWARDED"
+};
+const slotFlagsText = {
+ [1]: "STABLE",
+ [2]: "DYNAMIC",
+ [3]: "FORWARDED"
+};
+const GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error";
+const isGloballyAllowed = /* @__PURE__ */ makeMap(GLOBALS_ALLOWED);
+const isGloballyWhitelisted = isGloballyAllowed;
+const range = 2;
+function generateCodeFrame$1(source, start2 = 0, end = source.length) {
+ start2 = Math.max(0, Math.min(start2, source.length));
+ end = Math.max(0, Math.min(end, source.length));
+ if (start2 > end) return "";
+ let lines = source.split(/(\r?\n)/);
+ const newlineSequences = lines.filter((_2, idx) => idx % 2 === 1);
+ lines = lines.filter((_2, idx) => idx % 2 === 0);
+ let count = 0;
+ const res = [];
+ for (let i2 = 0; i2 < lines.length; i2++) {
+ count += lines[i2].length + (newlineSequences[i2] && newlineSequences[i2].length || 0);
+ if (count >= start2) {
+ for (let j = i2 - range; j <= i2 + range || end > count; j++) {
+ if (j < 0 || j >= lines.length) continue;
+ const line = j + 1;
+ res.push(
+ `${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`
+ );
+ const lineLength = lines[j].length;
+ const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0;
+ if (j === i2) {
+ const pad = start2 - (count - (lineLength + newLineSeqLength));
+ const length = Math.max(
+ 1,
+ end > count ? lineLength - pad : end - start2
+ );
+ res.push(` | ` + " ".repeat(pad) + "^".repeat(length));
+ } else if (j > i2) {
+ if (end > count) {
+ const length = Math.max(Math.min(end - count, lineLength), 1);
+ res.push(` | ` + "^".repeat(length));
+ }
+ count += lineLength + newLineSeqLength;
+ }
+ }
+ break;
+ }
+ }
+ return res.join("\n");
+}
+__name(generateCodeFrame$1, "generateCodeFrame$1");
+function normalizeStyle(value3) {
+ if (isArray$3(value3)) {
+ const res = {};
+ for (let i2 = 0; i2 < value3.length; i2++) {
+ const item2 = value3[i2];
+ const normalized = isString$4(item2) ? parseStringStyle(item2) : normalizeStyle(item2);
+ if (normalized) {
+ for (const key in normalized) {
+ res[key] = normalized[key];
+ }
+ }
+ }
+ return res;
+ } else if (isString$4(value3) || isObject$4(value3)) {
+ return value3;
+ }
+}
+__name(normalizeStyle, "normalizeStyle");
+const listDelimiterRE = /;(?![^(]*\))/g;
+const propertyDelimiterRE = /:([^]+)/;
+const styleCommentRE = /\/\*[^]*?\*\//g;
+function parseStringStyle(cssText) {
+ const ret = {};
+ cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item2) => {
+ if (item2) {
+ const tmp = item2.split(propertyDelimiterRE);
+ tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());
+ }
+ });
+ return ret;
+}
+__name(parseStringStyle, "parseStringStyle");
+function stringifyStyle(styles) {
+ let ret = "";
+ if (!styles || isString$4(styles)) {
+ return ret;
+ }
+ for (const key in styles) {
+ const value3 = styles[key];
+ if (isString$4(value3) || typeof value3 === "number") {
+ const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key);
+ ret += `${normalizedKey}:${value3};`;
+ }
+ }
+ return ret;
+}
+__name(stringifyStyle, "stringifyStyle");
+function normalizeClass(value3) {
+ let res = "";
+ if (isString$4(value3)) {
+ res = value3;
+ } else if (isArray$3(value3)) {
+ for (let i2 = 0; i2 < value3.length; i2++) {
+ const normalized = normalizeClass(value3[i2]);
+ if (normalized) {
+ res += normalized + " ";
+ }
+ }
+ } else if (isObject$4(value3)) {
+ for (const name in value3) {
+ if (value3[name]) {
+ res += name + " ";
+ }
+ }
+ }
+ return res.trim();
+}
+__name(normalizeClass, "normalizeClass");
+function normalizeProps(props) {
+ if (!props) return null;
+ let { class: klass, style } = props;
+ if (klass && !isString$4(klass)) {
+ props.class = normalizeClass(klass);
+ }
+ if (style) {
+ props.style = normalizeStyle(style);
+ }
+ return props;
+}
+__name(normalizeProps, "normalizeProps");
+const HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot";
+const SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view";
+const MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics";
+const VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr";
+const isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS);
+const isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS);
+const isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS);
+const isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS);
+const specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`;
+const isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs);
+const isBooleanAttr = /* @__PURE__ */ makeMap(
+ specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected`
+);
+function includeBooleanAttr(value3) {
+ return !!value3 || value3 === "";
+}
+__name(includeBooleanAttr, "includeBooleanAttr");
+const unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/;
+const attrValidationCache = {};
+function isSSRSafeAttrName(name) {
+ if (attrValidationCache.hasOwnProperty(name)) {
+ return attrValidationCache[name];
+ }
+ const isUnsafe = unsafeAttrCharRE.test(name);
+ if (isUnsafe) {
+ console.error(`unsafe attribute name: ${name}`);
+ }
+ return attrValidationCache[name] = !isUnsafe;
+}
+__name(isSSRSafeAttrName, "isSSRSafeAttrName");
+const propsToAttrMap = {
+ acceptCharset: "accept-charset",
+ className: "class",
+ htmlFor: "for",
+ httpEquiv: "http-equiv"
+};
+const isKnownHtmlAttr = /* @__PURE__ */ makeMap(
+ `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap`
+);
+const isKnownSvgAttr = /* @__PURE__ */ makeMap(
+ `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan`
+);
+function isRenderableAttrValue(value3) {
+ if (value3 == null) {
+ return false;
+ }
+ const type = typeof value3;
+ return type === "string" || type === "number" || type === "boolean";
+}
+__name(isRenderableAttrValue, "isRenderableAttrValue");
+const escapeRE = /["'&<>]/;
+function escapeHtml$1(string) {
+ const str = "" + string;
+ const match = escapeRE.exec(str);
+ if (!match) {
+ return str;
+ }
+ let html = "";
+ let escaped;
+ let index2;
+ let lastIndex = 0;
+ for (index2 = match.index; index2 < str.length; index2++) {
+ switch (str.charCodeAt(index2)) {
+ case 34:
+ escaped = """;
+ break;
+ case 38:
+ escaped = "&";
+ break;
+ case 39:
+ escaped = "'";
+ break;
+ case 60:
+ escaped = "<";
+ break;
+ case 62:
+ escaped = ">";
+ break;
+ default:
+ continue;
+ }
+ if (lastIndex !== index2) {
+ html += str.slice(lastIndex, index2);
+ }
+ lastIndex = index2 + 1;
+ html += escaped;
+ }
+ return lastIndex !== index2 ? html + str.slice(lastIndex, index2) : html;
+}
+__name(escapeHtml$1, "escapeHtml$1");
+const commentStripRE = /^-?>||--!>| looseEqual(item2, val));
+}
+__name(looseIndexOf, "looseIndexOf");
+const isRef$1 = /* @__PURE__ */ __name((val) => {
+ return !!(val && val.__v_isRef === true);
+}, "isRef$1");
+const toDisplayString$1 = /* @__PURE__ */ __name((val) => {
+ return isString$4(val) ? val : val == null ? "" : isArray$3(val) || isObject$4(val) && (val.toString === objectToString$1 || !isFunction$3(val.toString)) ? isRef$1(val) ? toDisplayString$1(val.value) : JSON.stringify(val, replacer, 2) : String(val);
+}, "toDisplayString$1");
+const replacer = /* @__PURE__ */ __name((_key, val) => {
+ if (isRef$1(val)) {
+ return replacer(_key, val.value);
+ } else if (isMap(val)) {
+ return {
+ [`Map(${val.size})`]: [...val.entries()].reduce(
+ (entries, [key, val2], i2) => {
+ entries[stringifySymbol(key, i2) + " =>"] = val2;
+ return entries;
+ },
+ {}
+ )
+ };
+ } else if (isSet(val)) {
+ return {
+ [`Set(${val.size})`]: [...val.values()].map((v2) => stringifySymbol(v2))
+ };
+ } else if (isSymbol$1(val)) {
+ return stringifySymbol(val);
+ } else if (isObject$4(val) && !isArray$3(val) && !isPlainObject$2(val)) {
+ return String(val);
+ }
+ return val;
+}, "replacer");
+const stringifySymbol = /* @__PURE__ */ __name((v2, i2 = "") => {
+ var _a2;
+ return (
+ // Symbol.description in es2019+ so we need to cast here to pass
+ // the lib: es2016 check
+ isSymbol$1(v2) ? `Symbol(${(_a2 = v2.description) != null ? _a2 : i2})` : v2
+ );
+}, "stringifySymbol");
+/**
+* @vue/reactivity v3.4.31
+* (c) 2018-present Yuxi (Evan) You and Vue contributors
+* @license MIT
+**/
+function warn$3(msg, ...args) {
+ console.warn(`[Vue warn] ${msg}`, ...args);
+}
+__name(warn$3, "warn$3");
+let activeEffectScope;
+const _EffectScope = class _EffectScope {
+ constructor(detached = false) {
+ this.detached = detached;
+ this._active = true;
+ this.effects = [];
+ this.cleanups = [];
+ this.parent = activeEffectScope;
+ if (!detached && activeEffectScope) {
+ this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(
+ this
+ ) - 1;
+ }
+ }
+ get active() {
+ return this._active;
+ }
+ run(fn) {
+ if (this._active) {
+ const currentEffectScope = activeEffectScope;
+ try {
+ activeEffectScope = this;
+ return fn();
+ } finally {
+ activeEffectScope = currentEffectScope;
+ }
+ } else if (false) {
+ warn$3(`cannot run an inactive effect scope.`);
+ }
+ }
+ /**
+ * This should only be called on non-detached scopes
+ * @internal
+ */
+ on() {
+ activeEffectScope = this;
+ }
+ /**
+ * This should only be called on non-detached scopes
+ * @internal
+ */
+ off() {
+ activeEffectScope = this.parent;
+ }
+ stop(fromParent) {
+ if (this._active) {
+ let i2, l;
+ for (i2 = 0, l = this.effects.length; i2 < l; i2++) {
+ this.effects[i2].stop();
+ }
+ for (i2 = 0, l = this.cleanups.length; i2 < l; i2++) {
+ this.cleanups[i2]();
+ }
+ if (this.scopes) {
+ for (i2 = 0, l = this.scopes.length; i2 < l; i2++) {
+ this.scopes[i2].stop(true);
+ }
+ }
+ if (!this.detached && this.parent && !fromParent) {
+ const last = this.parent.scopes.pop();
+ if (last && last !== this) {
+ this.parent.scopes[this.index] = last;
+ last.index = this.index;
+ }
+ }
+ this.parent = void 0;
+ this._active = false;
+ }
+ }
+};
+__name(_EffectScope, "EffectScope");
+let EffectScope = _EffectScope;
+function effectScope(detached) {
+ return new EffectScope(detached);
+}
+__name(effectScope, "effectScope");
+function recordEffectScope(effect2, scope = activeEffectScope) {
+ if (scope && scope.active) {
+ scope.effects.push(effect2);
+ }
+}
+__name(recordEffectScope, "recordEffectScope");
+function getCurrentScope() {
+ return activeEffectScope;
+}
+__name(getCurrentScope, "getCurrentScope");
+function onScopeDispose(fn) {
+ if (activeEffectScope) {
+ activeEffectScope.cleanups.push(fn);
+ } else if (false) {
+ warn$3(
+ `onScopeDispose() is called when there is no active effect scope to be associated with.`
+ );
+ }
+}
+__name(onScopeDispose, "onScopeDispose");
+let activeEffect;
+const _ReactiveEffect = class _ReactiveEffect {
+ constructor(fn, trigger2, scheduler, scope) {
+ this.fn = fn;
+ this.trigger = trigger2;
+ this.scheduler = scheduler;
+ this.active = true;
+ this.deps = [];
+ this._dirtyLevel = 4;
+ this._trackId = 0;
+ this._runnings = 0;
+ this._shouldSchedule = false;
+ this._depsLength = 0;
+ recordEffectScope(this, scope);
+ }
+ get dirty() {
+ if (this._dirtyLevel === 2 || this._dirtyLevel === 3) {
+ this._dirtyLevel = 1;
+ pauseTracking();
+ for (let i2 = 0; i2 < this._depsLength; i2++) {
+ const dep = this.deps[i2];
+ if (dep.computed) {
+ triggerComputed(dep.computed);
+ if (this._dirtyLevel >= 4) {
+ break;
+ }
+ }
+ }
+ if (this._dirtyLevel === 1) {
+ this._dirtyLevel = 0;
+ }
+ resetTracking();
+ }
+ return this._dirtyLevel >= 4;
+ }
+ set dirty(v2) {
+ this._dirtyLevel = v2 ? 4 : 0;
+ }
+ run() {
+ this._dirtyLevel = 0;
+ if (!this.active) {
+ return this.fn();
+ }
+ let lastShouldTrack = shouldTrack;
+ let lastEffect = activeEffect;
+ try {
+ shouldTrack = true;
+ activeEffect = this;
+ this._runnings++;
+ preCleanupEffect(this);
+ return this.fn();
+ } finally {
+ postCleanupEffect(this);
+ this._runnings--;
+ activeEffect = lastEffect;
+ shouldTrack = lastShouldTrack;
+ }
+ }
+ stop() {
+ if (this.active) {
+ preCleanupEffect(this);
+ postCleanupEffect(this);
+ this.onStop && this.onStop();
+ this.active = false;
+ }
+ }
+};
+__name(_ReactiveEffect, "ReactiveEffect");
+let ReactiveEffect = _ReactiveEffect;
+function triggerComputed(computed2) {
+ return computed2.value;
+}
+__name(triggerComputed, "triggerComputed");
+function preCleanupEffect(effect2) {
+ effect2._trackId++;
+ effect2._depsLength = 0;
+}
+__name(preCleanupEffect, "preCleanupEffect");
+function postCleanupEffect(effect2) {
+ if (effect2.deps.length > effect2._depsLength) {
+ for (let i2 = effect2._depsLength; i2 < effect2.deps.length; i2++) {
+ cleanupDepEffect(effect2.deps[i2], effect2);
+ }
+ effect2.deps.length = effect2._depsLength;
+ }
+}
+__name(postCleanupEffect, "postCleanupEffect");
+function cleanupDepEffect(dep, effect2) {
+ const trackId = dep.get(effect2);
+ if (trackId !== void 0 && effect2._trackId !== trackId) {
+ dep.delete(effect2);
+ if (dep.size === 0) {
+ dep.cleanup();
+ }
+ }
+}
+__name(cleanupDepEffect, "cleanupDepEffect");
+function effect(fn, options3) {
+ if (fn.effect instanceof ReactiveEffect) {
+ fn = fn.effect.fn;
+ }
+ const _effect = new ReactiveEffect(fn, NOOP, () => {
+ if (_effect.dirty) {
+ _effect.run();
+ }
+ });
+ if (options3) {
+ extend(_effect, options3);
+ if (options3.scope) recordEffectScope(_effect, options3.scope);
+ }
+ if (!options3 || !options3.lazy) {
+ _effect.run();
+ }
+ const runner = _effect.run.bind(_effect);
+ runner.effect = _effect;
+ return runner;
+}
+__name(effect, "effect");
+function stop(runner) {
+ runner.effect.stop();
+}
+__name(stop, "stop");
+let shouldTrack = true;
+let pauseScheduleStack = 0;
+const trackStack = [];
+function pauseTracking() {
+ trackStack.push(shouldTrack);
+ shouldTrack = false;
+}
+__name(pauseTracking, "pauseTracking");
+function enableTracking() {
+ trackStack.push(shouldTrack);
+ shouldTrack = true;
+}
+__name(enableTracking, "enableTracking");
+function resetTracking() {
+ const last = trackStack.pop();
+ shouldTrack = last === void 0 ? true : last;
+}
+__name(resetTracking, "resetTracking");
+function pauseScheduling() {
+ pauseScheduleStack++;
+}
+__name(pauseScheduling, "pauseScheduling");
+function resetScheduling() {
+ pauseScheduleStack--;
+ while (!pauseScheduleStack && queueEffectSchedulers.length) {
+ queueEffectSchedulers.shift()();
+ }
+}
+__name(resetScheduling, "resetScheduling");
+function trackEffect(effect2, dep, debuggerEventExtraInfo) {
+ var _a2;
+ if (dep.get(effect2) !== effect2._trackId) {
+ dep.set(effect2, effect2._trackId);
+ const oldDep = effect2.deps[effect2._depsLength];
+ if (oldDep !== dep) {
+ if (oldDep) {
+ cleanupDepEffect(oldDep, effect2);
+ }
+ effect2.deps[effect2._depsLength++] = dep;
+ } else {
+ effect2._depsLength++;
+ }
+ if (false) {
+ (_a2 = effect2.onTrack) == null ? void 0 : _a2.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo));
+ }
+ }
+}
+__name(trackEffect, "trackEffect");
+const queueEffectSchedulers = [];
+function triggerEffects(dep, dirtyLevel, debuggerEventExtraInfo) {
+ var _a2;
+ pauseScheduling();
+ for (const effect2 of dep.keys()) {
+ let tracking;
+ if (effect2._dirtyLevel < dirtyLevel && (tracking != null ? tracking : tracking = dep.get(effect2) === effect2._trackId)) {
+ effect2._shouldSchedule || (effect2._shouldSchedule = effect2._dirtyLevel === 0);
+ effect2._dirtyLevel = dirtyLevel;
+ }
+ if (effect2._shouldSchedule && (tracking != null ? tracking : tracking = dep.get(effect2) === effect2._trackId)) {
+ if (false) {
+ (_a2 = effect2.onTrigger) == null ? void 0 : _a2.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo));
+ }
+ effect2.trigger();
+ if ((!effect2._runnings || effect2.allowRecurse) && effect2._dirtyLevel !== 2) {
+ effect2._shouldSchedule = false;
+ if (effect2.scheduler) {
+ queueEffectSchedulers.push(effect2.scheduler);
+ }
+ }
+ }
+ }
+ resetScheduling();
+}
+__name(triggerEffects, "triggerEffects");
+const createDep = /* @__PURE__ */ __name((cleanup, computed2) => {
+ const dep = /* @__PURE__ */ new Map();
+ dep.cleanup = cleanup;
+ dep.computed = computed2;
+ return dep;
+}, "createDep");
+const targetMap = /* @__PURE__ */ new WeakMap();
+const ITERATE_KEY = Symbol(false ? "iterate" : "");
+const MAP_KEY_ITERATE_KEY = Symbol(false ? "Map key iterate" : "");
+function track(target, type, key) {
+ if (shouldTrack && activeEffect) {
+ let depsMap = targetMap.get(target);
+ if (!depsMap) {
+ targetMap.set(target, depsMap = /* @__PURE__ */ new Map());
+ }
+ let dep = depsMap.get(key);
+ if (!dep) {
+ depsMap.set(key, dep = createDep(() => depsMap.delete(key)));
+ }
+ trackEffect(
+ activeEffect,
+ dep,
+ false ? {
+ target,
+ type,
+ key
+ } : void 0
+ );
+ }
+}
+__name(track, "track");
+function trigger(target, type, key, newValue, oldValue, oldTarget) {
+ const depsMap = targetMap.get(target);
+ if (!depsMap) {
+ return;
+ }
+ let deps = [];
+ if (type === "clear") {
+ deps = [...depsMap.values()];
+ } else if (key === "length" && isArray$3(target)) {
+ const newLength = Number(newValue);
+ depsMap.forEach((dep, key2) => {
+ if (key2 === "length" || !isSymbol$1(key2) && key2 >= newLength) {
+ deps.push(dep);
+ }
+ });
+ } else {
+ if (key !== void 0) {
+ deps.push(depsMap.get(key));
+ }
+ switch (type) {
+ case "add":
+ if (!isArray$3(target)) {
+ deps.push(depsMap.get(ITERATE_KEY));
+ if (isMap(target)) {
+ deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
+ }
+ } else if (isIntegerKey(key)) {
+ deps.push(depsMap.get("length"));
+ }
+ break;
+ case "delete":
+ if (!isArray$3(target)) {
+ deps.push(depsMap.get(ITERATE_KEY));
+ if (isMap(target)) {
+ deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
+ }
+ }
+ break;
+ case "set":
+ if (isMap(target)) {
+ deps.push(depsMap.get(ITERATE_KEY));
+ }
+ break;
+ }
+ }
+ pauseScheduling();
+ for (const dep of deps) {
+ if (dep) {
+ triggerEffects(
+ dep,
+ 4,
+ false ? {
+ target,
+ type,
+ key,
+ newValue,
+ oldValue,
+ oldTarget
+ } : void 0
+ );
+ }
+ }
+ resetScheduling();
+}
+__name(trigger, "trigger");
+function getDepFromReactive(object, key) {
+ const depsMap = targetMap.get(object);
+ return depsMap && depsMap.get(key);
+}
+__name(getDepFromReactive, "getDepFromReactive");
+const isNonTrackableKeys = /* @__PURE__ */ makeMap(`__proto__,__v_isRef,__isVue`);
+const builtInSymbols = new Set(
+ /* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((key) => key !== "arguments" && key !== "caller").map((key) => Symbol[key]).filter(isSymbol$1)
+);
+const arrayInstrumentations = /* @__PURE__ */ createArrayInstrumentations();
+function createArrayInstrumentations() {
+ const instrumentations = {};
+ ["includes", "indexOf", "lastIndexOf"].forEach((key) => {
+ instrumentations[key] = function(...args) {
+ const arr = toRaw(this);
+ for (let i2 = 0, l = this.length; i2 < l; i2++) {
+ track(arr, "get", i2 + "");
+ }
+ const res = arr[key](...args);
+ if (res === -1 || res === false) {
+ return arr[key](...args.map(toRaw));
+ } else {
+ return res;
+ }
+ };
+ });
+ ["push", "pop", "shift", "unshift", "splice"].forEach((key) => {
+ instrumentations[key] = function(...args) {
+ pauseTracking();
+ pauseScheduling();
+ const res = toRaw(this)[key].apply(this, args);
+ resetScheduling();
+ resetTracking();
+ return res;
+ };
+ });
+ return instrumentations;
+}
+__name(createArrayInstrumentations, "createArrayInstrumentations");
+function hasOwnProperty$1(key) {
+ if (!isSymbol$1(key)) key = String(key);
+ const obj = toRaw(this);
+ track(obj, "has", key);
+ return obj.hasOwnProperty(key);
+}
+__name(hasOwnProperty$1, "hasOwnProperty$1");
+const _BaseReactiveHandler = class _BaseReactiveHandler {
+ constructor(_isReadonly = false, _isShallow = false) {
+ this._isReadonly = _isReadonly;
+ this._isShallow = _isShallow;
+ }
+ get(target, key, receiver) {
+ const isReadonly2 = this._isReadonly, isShallow2 = this._isShallow;
+ if (key === "__v_isReactive") {
+ return !isReadonly2;
+ } else if (key === "__v_isReadonly") {
+ return isReadonly2;
+ } else if (key === "__v_isShallow") {
+ return isShallow2;
+ } else if (key === "__v_raw") {
+ if (receiver === (isReadonly2 ? isShallow2 ? shallowReadonlyMap : readonlyMap : isShallow2 ? shallowReactiveMap : reactiveMap).get(target) || // receiver is not the reactive proxy, but has the same prototype
+ // this means the reciever is a user proxy of the reactive proxy
+ Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) {
+ return target;
+ }
+ return;
+ }
+ const targetIsArray = isArray$3(target);
+ if (!isReadonly2) {
+ if (targetIsArray && hasOwn$2(arrayInstrumentations, key)) {
+ return Reflect.get(arrayInstrumentations, key, receiver);
+ }
+ if (key === "hasOwnProperty") {
+ return hasOwnProperty$1;
+ }
+ }
+ const res = Reflect.get(target, key, receiver);
+ if (isSymbol$1(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
+ return res;
+ }
+ if (!isReadonly2) {
+ track(target, "get", key);
+ }
+ if (isShallow2) {
+ return res;
+ }
+ if (isRef(res)) {
+ return targetIsArray && isIntegerKey(key) ? res : res.value;
+ }
+ if (isObject$4(res)) {
+ return isReadonly2 ? readonly(res) : reactive(res);
+ }
+ return res;
+ }
+};
+__name(_BaseReactiveHandler, "BaseReactiveHandler");
+let BaseReactiveHandler = _BaseReactiveHandler;
+const _MutableReactiveHandler = class _MutableReactiveHandler extends BaseReactiveHandler {
+ constructor(isShallow2 = false) {
+ super(false, isShallow2);
+ }
+ set(target, key, value3, receiver) {
+ let oldValue = target[key];
+ if (!this._isShallow) {
+ const isOldValueReadonly = isReadonly(oldValue);
+ if (!isShallow(value3) && !isReadonly(value3)) {
+ oldValue = toRaw(oldValue);
+ value3 = toRaw(value3);
+ }
+ if (!isArray$3(target) && isRef(oldValue) && !isRef(value3)) {
+ if (isOldValueReadonly) {
+ return false;
+ } else {
+ oldValue.value = value3;
+ return true;
+ }
+ }
+ }
+ const hadKey = isArray$3(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn$2(target, key);
+ const result = Reflect.set(target, key, value3, receiver);
+ if (target === toRaw(receiver)) {
+ if (!hadKey) {
+ trigger(target, "add", key, value3);
+ } else if (hasChanged(value3, oldValue)) {
+ trigger(target, "set", key, value3, oldValue);
+ }
+ }
+ return result;
+ }
+ deleteProperty(target, key) {
+ const hadKey = hasOwn$2(target, key);
+ const oldValue = target[key];
+ const result = Reflect.deleteProperty(target, key);
+ if (result && hadKey) {
+ trigger(target, "delete", key, void 0, oldValue);
+ }
+ return result;
+ }
+ has(target, key) {
+ const result = Reflect.has(target, key);
+ if (!isSymbol$1(key) || !builtInSymbols.has(key)) {
+ track(target, "has", key);
+ }
+ return result;
+ }
+ ownKeys(target) {
+ track(
+ target,
+ "iterate",
+ isArray$3(target) ? "length" : ITERATE_KEY
+ );
+ return Reflect.ownKeys(target);
+ }
+};
+__name(_MutableReactiveHandler, "MutableReactiveHandler");
+let MutableReactiveHandler = _MutableReactiveHandler;
+const _ReadonlyReactiveHandler = class _ReadonlyReactiveHandler extends BaseReactiveHandler {
+ constructor(isShallow2 = false) {
+ super(true, isShallow2);
+ }
+ set(target, key) {
+ if (false) {
+ warn$3(
+ `Set operation on key "${String(key)}" failed: target is readonly.`,
+ target
+ );
+ }
+ return true;
+ }
+ deleteProperty(target, key) {
+ if (false) {
+ warn$3(
+ `Delete operation on key "${String(key)}" failed: target is readonly.`,
+ target
+ );
+ }
+ return true;
+ }
+};
+__name(_ReadonlyReactiveHandler, "ReadonlyReactiveHandler");
+let ReadonlyReactiveHandler = _ReadonlyReactiveHandler;
+const mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler();
+const readonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler();
+const shallowReactiveHandlers = /* @__PURE__ */ new MutableReactiveHandler(
+ true
+);
+const shallowReadonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler(true);
+const toShallow = /* @__PURE__ */ __name((value3) => value3, "toShallow");
+const getProto = /* @__PURE__ */ __name((v2) => Reflect.getPrototypeOf(v2), "getProto");
+function get$1(target, key, isReadonly2 = false, isShallow2 = false) {
+ target = target["__v_raw"];
+ const rawTarget = toRaw(target);
+ const rawKey = toRaw(key);
+ if (!isReadonly2) {
+ if (hasChanged(key, rawKey)) {
+ track(rawTarget, "get", key);
+ }
+ track(rawTarget, "get", rawKey);
+ }
+ const { has: has2 } = getProto(rawTarget);
+ const wrap = isShallow2 ? toShallow : isReadonly2 ? toReadonly : toReactive;
+ if (has2.call(rawTarget, key)) {
+ return wrap(target.get(key));
+ } else if (has2.call(rawTarget, rawKey)) {
+ return wrap(target.get(rawKey));
+ } else if (target !== rawTarget) {
+ target.get(key);
+ }
+}
+__name(get$1, "get$1");
+function has(key, isReadonly2 = false) {
+ const target = this["__v_raw"];
+ const rawTarget = toRaw(target);
+ const rawKey = toRaw(key);
+ if (!isReadonly2) {
+ if (hasChanged(key, rawKey)) {
+ track(rawTarget, "has", key);
+ }
+ track(rawTarget, "has", rawKey);
+ }
+ return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey);
+}
+__name(has, "has");
+function size(target, isReadonly2 = false) {
+ target = target["__v_raw"];
+ !isReadonly2 && track(toRaw(target), "iterate", ITERATE_KEY);
+ return Reflect.get(target, "size", target);
+}
+__name(size, "size");
+function add(value3) {
+ value3 = toRaw(value3);
+ const target = toRaw(this);
+ const proto = getProto(target);
+ const hadKey = proto.has.call(target, value3);
+ if (!hadKey) {
+ target.add(value3);
+ trigger(target, "add", value3, value3);
+ }
+ return this;
+}
+__name(add, "add");
+function set$1(key, value3) {
+ value3 = toRaw(value3);
+ const target = toRaw(this);
+ const { has: has2, get: get2 } = getProto(target);
+ let hadKey = has2.call(target, key);
+ if (!hadKey) {
+ key = toRaw(key);
+ hadKey = has2.call(target, key);
+ } else if (false) {
+ checkIdentityKeys(target, has2, key);
+ }
+ const oldValue = get2.call(target, key);
+ target.set(key, value3);
+ if (!hadKey) {
+ trigger(target, "add", key, value3);
+ } else if (hasChanged(value3, oldValue)) {
+ trigger(target, "set", key, value3, oldValue);
+ }
+ return this;
+}
+__name(set$1, "set$1");
+function deleteEntry(key) {
+ const target = toRaw(this);
+ const { has: has2, get: get2 } = getProto(target);
+ let hadKey = has2.call(target, key);
+ if (!hadKey) {
+ key = toRaw(key);
+ hadKey = has2.call(target, key);
+ } else if (false) {
+ checkIdentityKeys(target, has2, key);
+ }
+ const oldValue = get2 ? get2.call(target, key) : void 0;
+ const result = target.delete(key);
+ if (hadKey) {
+ trigger(target, "delete", key, void 0, oldValue);
+ }
+ return result;
+}
+__name(deleteEntry, "deleteEntry");
+function clear() {
+ const target = toRaw(this);
+ const hadItems = target.size !== 0;
+ const oldTarget = false ? isMap(target) ? new Map(target) : new Set(target) : void 0;
+ const result = target.clear();
+ if (hadItems) {
+ trigger(target, "clear", void 0, void 0, oldTarget);
+ }
+ return result;
+}
+__name(clear, "clear");
+function createForEach(isReadonly2, isShallow2) {
+ return /* @__PURE__ */ __name(function forEach(callback, thisArg) {
+ const observed = this;
+ const target = observed["__v_raw"];
+ const rawTarget = toRaw(target);
+ const wrap = isShallow2 ? toShallow : isReadonly2 ? toReadonly : toReactive;
+ !isReadonly2 && track(rawTarget, "iterate", ITERATE_KEY);
+ return target.forEach((value3, key) => {
+ return callback.call(thisArg, wrap(value3), wrap(key), observed);
+ });
+ }, "forEach");
+}
+__name(createForEach, "createForEach");
+function createIterableMethod(method, isReadonly2, isShallow2) {
+ return function(...args) {
+ const target = this["__v_raw"];
+ const rawTarget = toRaw(target);
+ const targetIsMap = isMap(rawTarget);
+ const isPair = method === "entries" || method === Symbol.iterator && targetIsMap;
+ const isKeyOnly = method === "keys" && targetIsMap;
+ const innerIterator = target[method](...args);
+ const wrap = isShallow2 ? toShallow : isReadonly2 ? toReadonly : toReactive;
+ !isReadonly2 && track(
+ rawTarget,
+ "iterate",
+ isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY
+ );
+ return {
+ // iterator protocol
+ next() {
+ const { value: value3, done } = innerIterator.next();
+ return done ? { value: value3, done } : {
+ value: isPair ? [wrap(value3[0]), wrap(value3[1])] : wrap(value3),
+ done
+ };
+ },
+ // iterable protocol
+ [Symbol.iterator]() {
+ return this;
+ }
+ };
+ };
+}
+__name(createIterableMethod, "createIterableMethod");
+function createReadonlyMethod(type) {
+ return function(...args) {
+ if (false) {
+ const key = args[0] ? `on key "${args[0]}" ` : ``;
+ warn$3(
+ `${capitalize$1(type)} operation ${key}failed: target is readonly.`,
+ toRaw(this)
+ );
+ }
+ return type === "delete" ? false : type === "clear" ? void 0 : this;
+ };
+}
+__name(createReadonlyMethod, "createReadonlyMethod");
+function createInstrumentations() {
+ const mutableInstrumentations2 = {
+ get(key) {
+ return get$1(this, key);
+ },
+ get size() {
+ return size(this);
+ },
+ has,
+ add,
+ set: set$1,
+ delete: deleteEntry,
+ clear,
+ forEach: createForEach(false, false)
+ };
+ const shallowInstrumentations2 = {
+ get(key) {
+ return get$1(this, key, false, true);
+ },
+ get size() {
+ return size(this);
+ },
+ has,
+ add,
+ set: set$1,
+ delete: deleteEntry,
+ clear,
+ forEach: createForEach(false, true)
+ };
+ const readonlyInstrumentations2 = {
+ get(key) {
+ return get$1(this, key, true);
+ },
+ get size() {
+ return size(this, true);
+ },
+ has(key) {
+ return has.call(this, key, true);
+ },
+ add: createReadonlyMethod("add"),
+ set: createReadonlyMethod("set"),
+ delete: createReadonlyMethod("delete"),
+ clear: createReadonlyMethod("clear"),
+ forEach: createForEach(true, false)
+ };
+ const shallowReadonlyInstrumentations2 = {
+ get(key) {
+ return get$1(this, key, true, true);
+ },
+ get size() {
+ return size(this, true);
+ },
+ has(key) {
+ return has.call(this, key, true);
+ },
+ add: createReadonlyMethod("add"),
+ set: createReadonlyMethod("set"),
+ delete: createReadonlyMethod("delete"),
+ clear: createReadonlyMethod("clear"),
+ forEach: createForEach(true, true)
+ };
+ const iteratorMethods = [
+ "keys",
+ "values",
+ "entries",
+ Symbol.iterator
+ ];
+ iteratorMethods.forEach((method) => {
+ mutableInstrumentations2[method] = createIterableMethod(method, false, false);
+ readonlyInstrumentations2[method] = createIterableMethod(method, true, false);
+ shallowInstrumentations2[method] = createIterableMethod(method, false, true);
+ shallowReadonlyInstrumentations2[method] = createIterableMethod(
+ method,
+ true,
+ true
+ );
+ });
+ return [
+ mutableInstrumentations2,
+ readonlyInstrumentations2,
+ shallowInstrumentations2,
+ shallowReadonlyInstrumentations2
+ ];
+}
+__name(createInstrumentations, "createInstrumentations");
+const [
+ mutableInstrumentations,
+ readonlyInstrumentations,
+ shallowInstrumentations,
+ shallowReadonlyInstrumentations
+] = /* @__PURE__ */ createInstrumentations();
+function createInstrumentationGetter(isReadonly2, shallow) {
+ const instrumentations = shallow ? isReadonly2 ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly2 ? readonlyInstrumentations : mutableInstrumentations;
+ return (target, key, receiver) => {
+ if (key === "__v_isReactive") {
+ return !isReadonly2;
+ } else if (key === "__v_isReadonly") {
+ return isReadonly2;
+ } else if (key === "__v_raw") {
+ return target;
+ }
+ return Reflect.get(
+ hasOwn$2(instrumentations, key) && key in target ? instrumentations : target,
+ key,
+ receiver
+ );
+ };
+}
+__name(createInstrumentationGetter, "createInstrumentationGetter");
+const mutableCollectionHandlers = {
+ get: /* @__PURE__ */ createInstrumentationGetter(false, false)
+};
+const shallowCollectionHandlers = {
+ get: /* @__PURE__ */ createInstrumentationGetter(false, true)
+};
+const readonlyCollectionHandlers = {
+ get: /* @__PURE__ */ createInstrumentationGetter(true, false)
+};
+const shallowReadonlyCollectionHandlers = {
+ get: /* @__PURE__ */ createInstrumentationGetter(true, true)
+};
+function checkIdentityKeys(target, has2, key) {
+ const rawKey = toRaw(key);
+ if (rawKey !== key && has2.call(target, rawKey)) {
+ const type = toRawType(target);
+ warn$3(
+ `Reactive ${type} contains both the raw and reactive versions of the same object${type === `Map` ? ` as keys` : ``}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.`
+ );
+ }
+}
+__name(checkIdentityKeys, "checkIdentityKeys");
+const reactiveMap = /* @__PURE__ */ new WeakMap();
+const shallowReactiveMap = /* @__PURE__ */ new WeakMap();
+const readonlyMap = /* @__PURE__ */ new WeakMap();
+const shallowReadonlyMap = /* @__PURE__ */ new WeakMap();
+function targetTypeMap(rawType) {
+ switch (rawType) {
+ case "Object":
+ case "Array":
+ return 1;
+ case "Map":
+ case "Set":
+ case "WeakMap":
+ case "WeakSet":
+ return 2;
+ default:
+ return 0;
+ }
+}
+__name(targetTypeMap, "targetTypeMap");
+function getTargetType(value3) {
+ return value3["__v_skip"] || !Object.isExtensible(value3) ? 0 : targetTypeMap(toRawType(value3));
+}
+__name(getTargetType, "getTargetType");
+function reactive(target) {
+ if (isReadonly(target)) {
+ return target;
+ }
+ return createReactiveObject(
+ target,
+ false,
+ mutableHandlers,
+ mutableCollectionHandlers,
+ reactiveMap
+ );
+}
+__name(reactive, "reactive");
+function shallowReactive(target) {
+ return createReactiveObject(
+ target,
+ false,
+ shallowReactiveHandlers,
+ shallowCollectionHandlers,
+ shallowReactiveMap
+ );
+}
+__name(shallowReactive, "shallowReactive");
+function readonly(target) {
+ return createReactiveObject(
+ target,
+ true,
+ readonlyHandlers,
+ readonlyCollectionHandlers,
+ readonlyMap
+ );
+}
+__name(readonly, "readonly");
+function shallowReadonly(target) {
+ return createReactiveObject(
+ target,
+ true,
+ shallowReadonlyHandlers,
+ shallowReadonlyCollectionHandlers,
+ shallowReadonlyMap
+ );
+}
+__name(shallowReadonly, "shallowReadonly");
+function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {
+ if (!isObject$4(target)) {
+ if (false) {
+ warn$3(
+ `value cannot be made ${isReadonly2 ? "readonly" : "reactive"}: ${String(
+ target
+ )}`
+ );
+ }
+ return target;
+ }
+ if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) {
+ return target;
+ }
+ const existingProxy = proxyMap.get(target);
+ if (existingProxy) {
+ return existingProxy;
+ }
+ const targetType = getTargetType(target);
+ if (targetType === 0) {
+ return target;
+ }
+ const proxy = new Proxy(
+ target,
+ targetType === 2 ? collectionHandlers : baseHandlers
+ );
+ proxyMap.set(target, proxy);
+ return proxy;
+}
+__name(createReactiveObject, "createReactiveObject");
+function isReactive(value3) {
+ if (isReadonly(value3)) {
+ return isReactive(value3["__v_raw"]);
+ }
+ return !!(value3 && value3["__v_isReactive"]);
+}
+__name(isReactive, "isReactive");
+function isReadonly(value3) {
+ return !!(value3 && value3["__v_isReadonly"]);
+}
+__name(isReadonly, "isReadonly");
+function isShallow(value3) {
+ return !!(value3 && value3["__v_isShallow"]);
+}
+__name(isShallow, "isShallow");
+function isProxy(value3) {
+ return value3 ? !!value3["__v_raw"] : false;
+}
+__name(isProxy, "isProxy");
+function toRaw(observed) {
+ const raw = observed && observed["__v_raw"];
+ return raw ? toRaw(raw) : observed;
+}
+__name(toRaw, "toRaw");
+function markRaw(value3) {
+ if (Object.isExtensible(value3)) {
+ def(value3, "__v_skip", true);
+ }
+ return value3;
+}
+__name(markRaw, "markRaw");
+const toReactive = /* @__PURE__ */ __name((value3) => isObject$4(value3) ? reactive(value3) : value3, "toReactive");
+const toReadonly = /* @__PURE__ */ __name((value3) => isObject$4(value3) ? readonly(value3) : value3, "toReadonly");
+const COMPUTED_SIDE_EFFECT_WARN = `Computed is still dirty after getter evaluation, likely because a computed is mutating its own dependency in its getter. State mutations in computed getters should be avoided. Check the docs for more details: https://vuejs.org/guide/essentials/computed.html#getters-should-be-side-effect-free`;
+const _ComputedRefImpl = class _ComputedRefImpl {
+ constructor(getter, _setter, isReadonly2, isSSR) {
+ this.getter = getter;
+ this._setter = _setter;
+ this.dep = void 0;
+ this.__v_isRef = true;
+ this["__v_isReadonly"] = false;
+ this.effect = new ReactiveEffect(
+ () => getter(this._value),
+ () => triggerRefValue(
+ this,
+ this.effect._dirtyLevel === 2 ? 2 : 3
+ )
+ );
+ this.effect.computed = this;
+ this.effect.active = this._cacheable = !isSSR;
+ this["__v_isReadonly"] = isReadonly2;
+ }
+ get value() {
+ const self2 = toRaw(this);
+ if ((!self2._cacheable || self2.effect.dirty) && hasChanged(self2._value, self2._value = self2.effect.run())) {
+ triggerRefValue(self2, 4);
+ }
+ trackRefValue(self2);
+ if (self2.effect._dirtyLevel >= 2) {
+ if (false) {
+ warn$3(COMPUTED_SIDE_EFFECT_WARN, `
+
+getter: `, this.getter);
+ }
+ triggerRefValue(self2, 2);
+ }
+ return self2._value;
+ }
+ set value(newValue) {
+ this._setter(newValue);
+ }
+ // #region polyfill _dirty for backward compatibility third party code for Vue <= 3.3.x
+ get _dirty() {
+ return this.effect.dirty;
+ }
+ set _dirty(v2) {
+ this.effect.dirty = v2;
+ }
+ // #endregion
+};
+__name(_ComputedRefImpl, "ComputedRefImpl");
+let ComputedRefImpl = _ComputedRefImpl;
+function computed$1(getterOrOptions, debugOptions, isSSR = false) {
+ let getter;
+ let setter;
+ const onlyGetter = isFunction$3(getterOrOptions);
+ if (onlyGetter) {
+ getter = getterOrOptions;
+ setter = false ? () => {
+ warn$3("Write operation failed: computed value is readonly");
+ } : NOOP;
+ } else {
+ getter = getterOrOptions.get;
+ setter = getterOrOptions.set;
+ }
+ const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
+ if (false) {
+ cRef.effect.onTrack = debugOptions.onTrack;
+ cRef.effect.onTrigger = debugOptions.onTrigger;
+ }
+ return cRef;
+}
+__name(computed$1, "computed$1");
+function trackRefValue(ref2) {
+ var _a2;
+ if (shouldTrack && activeEffect) {
+ ref2 = toRaw(ref2);
+ trackEffect(
+ activeEffect,
+ (_a2 = ref2.dep) != null ? _a2 : ref2.dep = createDep(
+ () => ref2.dep = void 0,
+ ref2 instanceof ComputedRefImpl ? ref2 : void 0
+ ),
+ false ? {
+ target: ref2,
+ type: "get",
+ key: "value"
+ } : void 0
+ );
+ }
+}
+__name(trackRefValue, "trackRefValue");
+function triggerRefValue(ref2, dirtyLevel = 4, newVal, oldVal) {
+ ref2 = toRaw(ref2);
+ const dep = ref2.dep;
+ if (dep) {
+ triggerEffects(
+ dep,
+ dirtyLevel,
+ false ? {
+ target: ref2,
+ type: "set",
+ key: "value",
+ newValue: newVal,
+ oldValue: oldVal
+ } : void 0
+ );
+ }
+}
+__name(triggerRefValue, "triggerRefValue");
+function isRef(r) {
+ return !!(r && r.__v_isRef === true);
+}
+__name(isRef, "isRef");
+function ref(value3) {
+ return createRef(value3, false);
+}
+__name(ref, "ref");
+function shallowRef(value3) {
+ return createRef(value3, true);
+}
+__name(shallowRef, "shallowRef");
+function createRef(rawValue, shallow) {
+ if (isRef(rawValue)) {
+ return rawValue;
+ }
+ return new RefImpl(rawValue, shallow);
+}
+__name(createRef, "createRef");
+const _RefImpl = class _RefImpl {
+ constructor(value3, __v_isShallow) {
+ this.__v_isShallow = __v_isShallow;
+ this.dep = void 0;
+ this.__v_isRef = true;
+ this._rawValue = __v_isShallow ? value3 : toRaw(value3);
+ this._value = __v_isShallow ? value3 : toReactive(value3);
+ }
+ get value() {
+ trackRefValue(this);
+ return this._value;
+ }
+ set value(newVal) {
+ const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal);
+ newVal = useDirectValue ? newVal : toRaw(newVal);
+ if (hasChanged(newVal, this._rawValue)) {
+ const oldVal = this._rawValue;
+ this._rawValue = newVal;
+ this._value = useDirectValue ? newVal : toReactive(newVal);
+ triggerRefValue(this, 4, newVal, oldVal);
+ }
+ }
+};
+__name(_RefImpl, "RefImpl");
+let RefImpl = _RefImpl;
+function triggerRef(ref2) {
+ triggerRefValue(ref2, 4, false ? ref2.value : void 0);
+}
+__name(triggerRef, "triggerRef");
+function unref(ref2) {
+ return isRef(ref2) ? ref2.value : ref2;
+}
+__name(unref, "unref");
+function toValue$1(source) {
+ return isFunction$3(source) ? source() : unref(source);
+}
+__name(toValue$1, "toValue$1");
+const shallowUnwrapHandlers = {
+ get: /* @__PURE__ */ __name((target, key, receiver) => unref(Reflect.get(target, key, receiver)), "get"),
+ set: /* @__PURE__ */ __name((target, key, value3, receiver) => {
+ const oldValue = target[key];
+ if (isRef(oldValue) && !isRef(value3)) {
+ oldValue.value = value3;
+ return true;
+ } else {
+ return Reflect.set(target, key, value3, receiver);
+ }
+ }, "set")
+};
+function proxyRefs(objectWithRefs) {
+ return isReactive(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers);
+}
+__name(proxyRefs, "proxyRefs");
+const _CustomRefImpl = class _CustomRefImpl {
+ constructor(factory) {
+ this.dep = void 0;
+ this.__v_isRef = true;
+ const { get: get2, set: set2 } = factory(
+ () => trackRefValue(this),
+ () => triggerRefValue(this)
+ );
+ this._get = get2;
+ this._set = set2;
+ }
+ get value() {
+ return this._get();
+ }
+ set value(newVal) {
+ this._set(newVal);
+ }
+};
+__name(_CustomRefImpl, "CustomRefImpl");
+let CustomRefImpl = _CustomRefImpl;
+function customRef(factory) {
+ return new CustomRefImpl(factory);
+}
+__name(customRef, "customRef");
+function toRefs(object) {
+ if (false) {
+ warn$3(`toRefs() expects a reactive object but received a plain one.`);
+ }
+ const ret = isArray$3(object) ? new Array(object.length) : {};
+ for (const key in object) {
+ ret[key] = propertyToRef(object, key);
+ }
+ return ret;
+}
+__name(toRefs, "toRefs");
+const _ObjectRefImpl = class _ObjectRefImpl {
+ constructor(_object, _key, _defaultValue) {
+ this._object = _object;
+ this._key = _key;
+ this._defaultValue = _defaultValue;
+ this.__v_isRef = true;
+ }
+ get value() {
+ const val = this._object[this._key];
+ return val === void 0 ? this._defaultValue : val;
+ }
+ set value(newVal) {
+ this._object[this._key] = newVal;
+ }
+ get dep() {
+ return getDepFromReactive(toRaw(this._object), this._key);
+ }
+};
+__name(_ObjectRefImpl, "ObjectRefImpl");
+let ObjectRefImpl = _ObjectRefImpl;
+const _GetterRefImpl = class _GetterRefImpl {
+ constructor(_getter) {
+ this._getter = _getter;
+ this.__v_isRef = true;
+ this.__v_isReadonly = true;
+ }
+ get value() {
+ return this._getter();
+ }
+};
+__name(_GetterRefImpl, "GetterRefImpl");
+let GetterRefImpl = _GetterRefImpl;
+function toRef(source, key, defaultValue) {
+ if (isRef(source)) {
+ return source;
+ } else if (isFunction$3(source)) {
+ return new GetterRefImpl(source);
+ } else if (isObject$4(source) && arguments.length > 1) {
+ return propertyToRef(source, key, defaultValue);
+ } else {
+ return ref(source);
+ }
+}
+__name(toRef, "toRef");
+function propertyToRef(source, key, defaultValue) {
+ const val = source[key];
+ return isRef(val) ? val : new ObjectRefImpl(source, key, defaultValue);
+}
+__name(propertyToRef, "propertyToRef");
+const deferredComputed = computed$1;
+const TrackOpTypes = {
+ "GET": "get",
+ "HAS": "has",
+ "ITERATE": "iterate"
+};
+const TriggerOpTypes = {
+ "SET": "set",
+ "ADD": "add",
+ "DELETE": "delete",
+ "CLEAR": "clear"
+};
+const ReactiveFlags = {
+ "SKIP": "__v_skip",
+ "IS_REACTIVE": "__v_isReactive",
+ "IS_READONLY": "__v_isReadonly",
+ "IS_SHALLOW": "__v_isShallow",
+ "RAW": "__v_raw"
+};
+/**
+* @vue/runtime-core v3.4.31
+* (c) 2018-present Yuxi (Evan) You and Vue contributors
+* @license MIT
+**/
+const stack = [];
+function pushWarningContext(vnode) {
+ stack.push(vnode);
+}
+__name(pushWarningContext, "pushWarningContext");
+function popWarningContext() {
+ stack.pop();
+}
+__name(popWarningContext, "popWarningContext");
+function warn$1(msg, ...args) {
+ pauseTracking();
+ const instance = stack.length ? stack[stack.length - 1].component : null;
+ const appWarnHandler = instance && instance.appContext.config.warnHandler;
+ const trace = getComponentTrace();
+ if (appWarnHandler) {
+ callWithErrorHandling(
+ appWarnHandler,
+ instance,
+ 11,
+ [
+ // eslint-disable-next-line no-restricted-syntax
+ msg + args.map((a) => {
+ var _a2, _b2;
+ return (_b2 = (_a2 = a.toString) == null ? void 0 : _a2.call(a)) != null ? _b2 : JSON.stringify(a);
+ }).join(""),
+ instance && instance.proxy,
+ trace.map(
+ ({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>`
+ ).join("\n"),
+ trace
+ ]
+ );
+ } else {
+ const warnArgs = [`[Vue warn]: ${msg}`, ...args];
+ if (trace.length && // avoid spamming console during tests
+ true) {
+ warnArgs.push(`
+`, ...formatTrace(trace));
+ }
+ console.warn(...warnArgs);
+ }
+ resetTracking();
+}
+__name(warn$1, "warn$1");
+function getComponentTrace() {
+ let currentVNode = stack[stack.length - 1];
+ if (!currentVNode) {
+ return [];
+ }
+ const normalizedStack = [];
+ while (currentVNode) {
+ const last = normalizedStack[0];
+ if (last && last.vnode === currentVNode) {
+ last.recurseCount++;
+ } else {
+ normalizedStack.push({
+ vnode: currentVNode,
+ recurseCount: 0
+ });
+ }
+ const parentInstance = currentVNode.component && currentVNode.component.parent;
+ currentVNode = parentInstance && parentInstance.vnode;
+ }
+ return normalizedStack;
+}
+__name(getComponentTrace, "getComponentTrace");
+function formatTrace(trace) {
+ const logs = [];
+ trace.forEach((entry, i2) => {
+ logs.push(...i2 === 0 ? [] : [`
+`], ...formatTraceEntry(entry));
+ });
+ return logs;
+}
+__name(formatTrace, "formatTrace");
+function formatTraceEntry({ vnode, recurseCount }) {
+ const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``;
+ const isRoot = vnode.component ? vnode.component.parent == null : false;
+ const open2 = ` at <${formatComponentName(
+ vnode.component,
+ vnode.type,
+ isRoot
+ )}`;
+ const close4 = `>` + postfix;
+ return vnode.props ? [open2, ...formatProps(vnode.props), close4] : [open2 + close4];
+}
+__name(formatTraceEntry, "formatTraceEntry");
+function formatProps(props) {
+ const res = [];
+ const keys = Object.keys(props);
+ keys.slice(0, 3).forEach((key) => {
+ res.push(...formatProp(key, props[key]));
+ });
+ if (keys.length > 3) {
+ res.push(` ...`);
+ }
+ return res;
+}
+__name(formatProps, "formatProps");
+function formatProp(key, value3, raw) {
+ if (isString$4(value3)) {
+ value3 = JSON.stringify(value3);
+ return raw ? value3 : [`${key}=${value3}`];
+ } else if (typeof value3 === "number" || typeof value3 === "boolean" || value3 == null) {
+ return raw ? value3 : [`${key}=${value3}`];
+ } else if (isRef(value3)) {
+ value3 = formatProp(key, toRaw(value3.value), true);
+ return raw ? value3 : [`${key}=Ref<`, value3, `>`];
+ } else if (isFunction$3(value3)) {
+ return [`${key}=fn${value3.name ? `<${value3.name}>` : ``}`];
+ } else {
+ value3 = toRaw(value3);
+ return raw ? value3 : [`${key}=`, value3];
+ }
+}
+__name(formatProp, "formatProp");
+function assertNumber(val, type) {
+ if (true) return;
+ if (val === void 0) {
+ return;
+ } else if (typeof val !== "number") {
+ warn$1(`${type} is not a valid number - got ${JSON.stringify(val)}.`);
+ } else if (isNaN(val)) {
+ warn$1(`${type} is NaN - the duration expression might be incorrect.`);
+ }
+}
+__name(assertNumber, "assertNumber");
+const ErrorCodes = {
+ "SETUP_FUNCTION": 0,
+ "0": "SETUP_FUNCTION",
+ "RENDER_FUNCTION": 1,
+ "1": "RENDER_FUNCTION",
+ "WATCH_GETTER": 2,
+ "2": "WATCH_GETTER",
+ "WATCH_CALLBACK": 3,
+ "3": "WATCH_CALLBACK",
+ "WATCH_CLEANUP": 4,
+ "4": "WATCH_CLEANUP",
+ "NATIVE_EVENT_HANDLER": 5,
+ "5": "NATIVE_EVENT_HANDLER",
+ "COMPONENT_EVENT_HANDLER": 6,
+ "6": "COMPONENT_EVENT_HANDLER",
+ "VNODE_HOOK": 7,
+ "7": "VNODE_HOOK",
+ "DIRECTIVE_HOOK": 8,
+ "8": "DIRECTIVE_HOOK",
+ "TRANSITION_HOOK": 9,
+ "9": "TRANSITION_HOOK",
+ "APP_ERROR_HANDLER": 10,
+ "10": "APP_ERROR_HANDLER",
+ "APP_WARN_HANDLER": 11,
+ "11": "APP_WARN_HANDLER",
+ "FUNCTION_REF": 12,
+ "12": "FUNCTION_REF",
+ "ASYNC_COMPONENT_LOADER": 13,
+ "13": "ASYNC_COMPONENT_LOADER",
+ "SCHEDULER": 14,
+ "14": "SCHEDULER"
+};
+const ErrorTypeStrings$1 = {
+ ["sp"]: "serverPrefetch hook",
+ ["bc"]: "beforeCreate hook",
+ ["c"]: "created hook",
+ ["bm"]: "beforeMount hook",
+ ["m"]: "mounted hook",
+ ["bu"]: "beforeUpdate hook",
+ ["u"]: "updated",
+ ["bum"]: "beforeUnmount hook",
+ ["um"]: "unmounted hook",
+ ["a"]: "activated hook",
+ ["da"]: "deactivated hook",
+ ["ec"]: "errorCaptured hook",
+ ["rtc"]: "renderTracked hook",
+ ["rtg"]: "renderTriggered hook",
+ [0]: "setup function",
+ [1]: "render function",
+ [2]: "watcher getter",
+ [3]: "watcher callback",
+ [4]: "watcher cleanup function",
+ [5]: "native event handler",
+ [6]: "component event handler",
+ [7]: "vnode hook",
+ [8]: "directive hook",
+ [9]: "transition hook",
+ [10]: "app errorHandler",
+ [11]: "app warnHandler",
+ [12]: "ref function",
+ [13]: "async component loader",
+ [14]: "scheduler flush. This is likely a Vue internals bug. Please open an issue at https://github.com/vuejs/core ."
+};
+function callWithErrorHandling(fn, instance, type, args) {
+ try {
+ return args ? fn(...args) : fn();
+ } catch (err) {
+ handleError(err, instance, type);
+ }
+}
+__name(callWithErrorHandling, "callWithErrorHandling");
+function callWithAsyncErrorHandling(fn, instance, type, args) {
+ if (isFunction$3(fn)) {
+ const res = callWithErrorHandling(fn, instance, type, args);
+ if (res && isPromise$2(res)) {
+ res.catch((err) => {
+ handleError(err, instance, type);
+ });
+ }
+ return res;
+ }
+ if (isArray$3(fn)) {
+ const values2 = [];
+ for (let i2 = 0; i2 < fn.length; i2++) {
+ values2.push(callWithAsyncErrorHandling(fn[i2], instance, type, args));
+ }
+ return values2;
+ } else if (false) {
+ warn$1(
+ `Invalid value type passed to callWithAsyncErrorHandling(): ${typeof fn}`
+ );
+ }
+}
+__name(callWithAsyncErrorHandling, "callWithAsyncErrorHandling");
+function handleError(err, instance, type, throwInDev = true) {
+ const contextVNode = instance ? instance.vnode : null;
+ if (instance) {
+ let cur = instance.parent;
+ const exposedInstance = instance.proxy;
+ const errorInfo = false ? ErrorTypeStrings$1[type] : `https://vuejs.org/error-reference/#runtime-${type}`;
+ while (cur) {
+ const errorCapturedHooks = cur.ec;
+ if (errorCapturedHooks) {
+ for (let i2 = 0; i2 < errorCapturedHooks.length; i2++) {
+ if (errorCapturedHooks[i2](err, exposedInstance, errorInfo) === false) {
+ return;
+ }
+ }
+ }
+ cur = cur.parent;
+ }
+ const appErrorHandler = instance.appContext.config.errorHandler;
+ if (appErrorHandler) {
+ pauseTracking();
+ callWithErrorHandling(
+ appErrorHandler,
+ null,
+ 10,
+ [err, exposedInstance, errorInfo]
+ );
+ resetTracking();
+ return;
+ }
+ }
+ logError(err, type, contextVNode, throwInDev);
+}
+__name(handleError, "handleError");
+function logError(err, type, contextVNode, throwInDev = true) {
+ if (false) {
+ const info = ErrorTypeStrings$1[type];
+ if (contextVNode) {
+ pushWarningContext(contextVNode);
+ }
+ warn$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`);
+ if (contextVNode) {
+ popWarningContext();
+ }
+ if (throwInDev) {
+ throw err;
+ } else {
+ console.error(err);
+ }
+ } else {
+ console.error(err);
+ }
+}
+__name(logError, "logError");
+let isFlushing = false;
+let isFlushPending = false;
+const queue = [];
+let flushIndex = 0;
+const pendingPostFlushCbs = [];
+let activePostFlushCbs = null;
+let postFlushIndex = 0;
+const resolvedPromise = /* @__PURE__ */ Promise.resolve();
+let currentFlushPromise = null;
+const RECURSION_LIMIT = 100;
+function nextTick(fn) {
+ const p2 = currentFlushPromise || resolvedPromise;
+ return fn ? p2.then(this ? fn.bind(this) : fn) : p2;
+}
+__name(nextTick, "nextTick");
+function findInsertionIndex(id2) {
+ let start2 = flushIndex + 1;
+ let end = queue.length;
+ while (start2 < end) {
+ const middle = start2 + end >>> 1;
+ const middleJob = queue[middle];
+ const middleJobId = getId(middleJob);
+ if (middleJobId < id2 || middleJobId === id2 && middleJob.pre) {
+ start2 = middle + 1;
+ } else {
+ end = middle;
+ }
+ }
+ return start2;
+}
+__name(findInsertionIndex, "findInsertionIndex");
+function queueJob(job) {
+ if (!queue.length || !queue.includes(
+ job,
+ isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex
+ )) {
+ if (job.id == null) {
+ queue.push(job);
+ } else {
+ queue.splice(findInsertionIndex(job.id), 0, job);
+ }
+ queueFlush();
+ }
+}
+__name(queueJob, "queueJob");
+function queueFlush() {
+ if (!isFlushing && !isFlushPending) {
+ isFlushPending = true;
+ currentFlushPromise = resolvedPromise.then(flushJobs);
+ }
+}
+__name(queueFlush, "queueFlush");
+function invalidateJob(job) {
+ const i2 = queue.indexOf(job);
+ if (i2 > flushIndex) {
+ queue.splice(i2, 1);
+ }
+}
+__name(invalidateJob, "invalidateJob");
+function queuePostFlushCb(cb) {
+ if (!isArray$3(cb)) {
+ if (!activePostFlushCbs || !activePostFlushCbs.includes(
+ cb,
+ cb.allowRecurse ? postFlushIndex + 1 : postFlushIndex
+ )) {
+ pendingPostFlushCbs.push(cb);
+ }
+ } else {
+ pendingPostFlushCbs.push(...cb);
+ }
+ queueFlush();
+}
+__name(queuePostFlushCb, "queuePostFlushCb");
+function flushPreFlushCbs(instance, seen2, i2 = isFlushing ? flushIndex + 1 : 0) {
+ if (false) {
+ seen2 = seen2 || /* @__PURE__ */ new Map();
+ }
+ for (; i2 < queue.length; i2++) {
+ const cb = queue[i2];
+ if (cb && cb.pre) {
+ if (instance && cb.id !== instance.uid) {
+ continue;
+ }
+ if (false) {
+ continue;
+ }
+ queue.splice(i2, 1);
+ i2--;
+ cb();
+ }
+ }
+}
+__name(flushPreFlushCbs, "flushPreFlushCbs");
+function flushPostFlushCbs(seen2) {
+ if (pendingPostFlushCbs.length) {
+ const deduped = [...new Set(pendingPostFlushCbs)].sort(
+ (a, b) => getId(a) - getId(b)
+ );
+ pendingPostFlushCbs.length = 0;
+ if (activePostFlushCbs) {
+ activePostFlushCbs.push(...deduped);
+ return;
+ }
+ activePostFlushCbs = deduped;
+ if (false) {
+ seen2 = seen2 || /* @__PURE__ */ new Map();
+ }
+ for (postFlushIndex = 0; postFlushIndex < activePostFlushCbs.length; postFlushIndex++) {
+ const cb = activePostFlushCbs[postFlushIndex];
+ if (false) {
+ continue;
+ }
+ if (cb.active !== false) cb();
+ }
+ activePostFlushCbs = null;
+ postFlushIndex = 0;
+ }
+}
+__name(flushPostFlushCbs, "flushPostFlushCbs");
+const getId = /* @__PURE__ */ __name((job) => job.id == null ? Infinity : job.id, "getId");
+const comparator = /* @__PURE__ */ __name((a, b) => {
+ const diff = getId(a) - getId(b);
+ if (diff === 0) {
+ if (a.pre && !b.pre) return -1;
+ if (b.pre && !a.pre) return 1;
+ }
+ return diff;
+}, "comparator");
+function flushJobs(seen2) {
+ isFlushPending = false;
+ isFlushing = true;
+ if (false) {
+ seen2 = seen2 || /* @__PURE__ */ new Map();
+ }
+ queue.sort(comparator);
+ const check = false ? (job) => checkRecursiveUpdates(seen2, job) : NOOP;
+ try {
+ for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
+ const job = queue[flushIndex];
+ if (job && job.active !== false) {
+ if (false) {
+ continue;
+ }
+ callWithErrorHandling(job, null, 14);
+ }
+ }
+ } finally {
+ flushIndex = 0;
+ queue.length = 0;
+ flushPostFlushCbs(seen2);
+ isFlushing = false;
+ currentFlushPromise = null;
+ if (queue.length || pendingPostFlushCbs.length) {
+ flushJobs(seen2);
+ }
+ }
+}
+__name(flushJobs, "flushJobs");
+function checkRecursiveUpdates(seen2, fn) {
+ if (!seen2.has(fn)) {
+ seen2.set(fn, 1);
+ } else {
+ const count = seen2.get(fn);
+ if (count > RECURSION_LIMIT) {
+ const instance = fn.ownerInstance;
+ const componentName = instance && getComponentName(instance.type);
+ handleError(
+ `Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.`,
+ null,
+ 10
+ );
+ return true;
+ } else {
+ seen2.set(fn, count + 1);
+ }
+ }
+}
+__name(checkRecursiveUpdates, "checkRecursiveUpdates");
+let isHmrUpdating = false;
+const hmrDirtyComponents = /* @__PURE__ */ new Set();
+if (false) {
+ getGlobalThis$1().__VUE_HMR_RUNTIME__ = {
+ createRecord: tryWrap(createRecord),
+ rerender: tryWrap(rerender),
+ reload: tryWrap(reload)
+ };
+}
+const map = /* @__PURE__ */ new Map();
+function registerHMR(instance) {
+ const id2 = instance.type.__hmrId;
+ let record = map.get(id2);
+ if (!record) {
+ createRecord(id2, instance.type);
+ record = map.get(id2);
+ }
+ record.instances.add(instance);
+}
+__name(registerHMR, "registerHMR");
+function unregisterHMR(instance) {
+ map.get(instance.type.__hmrId).instances.delete(instance);
+}
+__name(unregisterHMR, "unregisterHMR");
+function createRecord(id2, initialDef) {
+ if (map.has(id2)) {
+ return false;
+ }
+ map.set(id2, {
+ initialDef: normalizeClassComponent(initialDef),
+ instances: /* @__PURE__ */ new Set()
+ });
+ return true;
+}
+__name(createRecord, "createRecord");
+function normalizeClassComponent(component) {
+ return isClassComponent(component) ? component.__vccOpts : component;
+}
+__name(normalizeClassComponent, "normalizeClassComponent");
+function rerender(id2, newRender) {
+ const record = map.get(id2);
+ if (!record) {
+ return;
+ }
+ record.initialDef.render = newRender;
+ [...record.instances].forEach((instance) => {
+ if (newRender) {
+ instance.render = newRender;
+ normalizeClassComponent(instance.type).render = newRender;
+ }
+ instance.renderCache = [];
+ isHmrUpdating = true;
+ instance.effect.dirty = true;
+ instance.update();
+ isHmrUpdating = false;
+ });
+}
+__name(rerender, "rerender");
+function reload(id2, newComp) {
+ const record = map.get(id2);
+ if (!record) return;
+ newComp = normalizeClassComponent(newComp);
+ updateComponentDef(record.initialDef, newComp);
+ const instances = [...record.instances];
+ for (const instance of instances) {
+ const oldComp = normalizeClassComponent(instance.type);
+ if (!hmrDirtyComponents.has(oldComp)) {
+ if (oldComp !== record.initialDef) {
+ updateComponentDef(oldComp, newComp);
+ }
+ hmrDirtyComponents.add(oldComp);
+ }
+ instance.appContext.propsCache.delete(instance.type);
+ instance.appContext.emitsCache.delete(instance.type);
+ instance.appContext.optionsCache.delete(instance.type);
+ if (instance.ceReload) {
+ hmrDirtyComponents.add(oldComp);
+ instance.ceReload(newComp.styles);
+ hmrDirtyComponents.delete(oldComp);
+ } else if (instance.parent) {
+ instance.parent.effect.dirty = true;
+ queueJob(() => {
+ instance.parent.update();
+ hmrDirtyComponents.delete(oldComp);
+ });
+ } else if (instance.appContext.reload) {
+ instance.appContext.reload();
+ } else if (typeof window !== "undefined") {
+ window.location.reload();
+ } else {
+ console.warn(
+ "[HMR] Root or manually mounted instance modified. Full reload required."
+ );
+ }
+ }
+ queuePostFlushCb(() => {
+ for (const instance of instances) {
+ hmrDirtyComponents.delete(
+ normalizeClassComponent(instance.type)
+ );
+ }
+ });
+}
+__name(reload, "reload");
+function updateComponentDef(oldComp, newComp) {
+ extend(oldComp, newComp);
+ for (const key in oldComp) {
+ if (key !== "__file" && !(key in newComp)) {
+ delete oldComp[key];
+ }
+ }
+}
+__name(updateComponentDef, "updateComponentDef");
+function tryWrap(fn) {
+ return (id2, arg) => {
+ try {
+ return fn(id2, arg);
+ } catch (e) {
+ console.error(e);
+ console.warn(
+ `[HMR] Something went wrong during Vue component hot-reload. Full reload required.`
+ );
+ }
+ };
+}
+__name(tryWrap, "tryWrap");
+let devtools$1;
+let buffer = [];
+let devtoolsNotInstalled = false;
+function emit$1(event2, ...args) {
+ if (devtools$1) {
+ devtools$1.emit(event2, ...args);
+ } else if (!devtoolsNotInstalled) {
+ buffer.push({ event: event2, args });
+ }
+}
+__name(emit$1, "emit$1");
+function setDevtoolsHook$1(hook, target) {
+ var _a2, _b2;
+ devtools$1 = hook;
+ if (devtools$1) {
+ devtools$1.enabled = true;
+ buffer.forEach(({ event: event2, args }) => devtools$1.emit(event2, ...args));
+ buffer = [];
+ } else if (
+ // handle late devtools injection - only do this if we are in an actual
+ // browser environment to avoid the timer handle stalling test runner exit
+ // (#4815)
+ typeof window !== "undefined" && // some envs mock window but not fully
+ window.HTMLElement && // also exclude jsdom
+ // eslint-disable-next-line no-restricted-syntax
+ !((_b2 = (_a2 = window.navigator) == null ? void 0 : _a2.userAgent) == null ? void 0 : _b2.includes("jsdom"))
+ ) {
+ const replay = target.__VUE_DEVTOOLS_HOOK_REPLAY__ = target.__VUE_DEVTOOLS_HOOK_REPLAY__ || [];
+ replay.push((newHook) => {
+ setDevtoolsHook$1(newHook, target);
+ });
+ setTimeout(() => {
+ if (!devtools$1) {
+ target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null;
+ devtoolsNotInstalled = true;
+ buffer = [];
+ }
+ }, 3e3);
+ } else {
+ devtoolsNotInstalled = true;
+ buffer = [];
+ }
+}
+__name(setDevtoolsHook$1, "setDevtoolsHook$1");
+function devtoolsInitApp(app2, version2) {
+ emit$1("app:init", app2, version2, {
+ Fragment,
+ Text,
+ Comment,
+ Static
+ });
+}
+__name(devtoolsInitApp, "devtoolsInitApp");
+function devtoolsUnmountApp(app2) {
+ emit$1("app:unmount", app2);
+}
+__name(devtoolsUnmountApp, "devtoolsUnmountApp");
+const devtoolsComponentAdded = /* @__PURE__ */ createDevtoolsComponentHook(
+ "component:added"
+ /* COMPONENT_ADDED */
+);
+const devtoolsComponentUpdated = /* @__PURE__ */ createDevtoolsComponentHook(
+ "component:updated"
+ /* COMPONENT_UPDATED */
+);
+const _devtoolsComponentRemoved = /* @__PURE__ */ createDevtoolsComponentHook(
+ "component:removed"
+ /* COMPONENT_REMOVED */
+);
+const devtoolsComponentRemoved = /* @__PURE__ */ __name((component) => {
+ if (devtools$1 && typeof devtools$1.cleanupBuffer === "function" && // remove the component if it wasn't buffered
+ !devtools$1.cleanupBuffer(component)) {
+ _devtoolsComponentRemoved(component);
+ }
+}, "devtoolsComponentRemoved");
+/*! #__NO_SIDE_EFFECTS__ */
+// @__NO_SIDE_EFFECTS__
+function createDevtoolsComponentHook(hook) {
+ return (component) => {
+ emit$1(
+ hook,
+ component.appContext.app,
+ component.uid,
+ component.parent ? component.parent.uid : void 0,
+ component
+ );
+ };
+}
+__name(createDevtoolsComponentHook, "createDevtoolsComponentHook");
+const devtoolsPerfStart = /* @__PURE__ */ createDevtoolsPerformanceHook(
+ "perf:start"
+ /* PERFORMANCE_START */
+);
+const devtoolsPerfEnd = /* @__PURE__ */ createDevtoolsPerformanceHook(
+ "perf:end"
+ /* PERFORMANCE_END */
+);
+function createDevtoolsPerformanceHook(hook) {
+ return (component, type, time) => {
+ emit$1(hook, component.appContext.app, component.uid, component, type, time);
+ };
+}
+__name(createDevtoolsPerformanceHook, "createDevtoolsPerformanceHook");
+function devtoolsComponentEmit(component, event2, params) {
+ emit$1(
+ "component:emit",
+ component.appContext.app,
+ component,
+ event2,
+ params
+ );
+}
+__name(devtoolsComponentEmit, "devtoolsComponentEmit");
+function emit(instance, event2, ...rawArgs) {
+ if (instance.isUnmounted) return;
+ const props = instance.vnode.props || EMPTY_OBJ;
+ if (false) {
+ const {
+ emitsOptions,
+ propsOptions: [propsOptions]
+ } = instance;
+ if (emitsOptions) {
+ if (!(event2 in emitsOptions) && true) {
+ if (!propsOptions || !(toHandlerKey(event2) in propsOptions)) {
+ warn$1(
+ `Component emitted event "${event2}" but it is neither declared in the emits option nor as an "${toHandlerKey(event2)}" prop.`
+ );
+ }
+ } else {
+ const validator2 = emitsOptions[event2];
+ if (isFunction$3(validator2)) {
+ const isValid2 = validator2(...rawArgs);
+ if (!isValid2) {
+ warn$1(
+ `Invalid event arguments: event validation failed for event "${event2}".`
+ );
+ }
+ }
+ }
+ }
+ }
+ let args = rawArgs;
+ const isModelListener2 = event2.startsWith("update:");
+ const modelArg = isModelListener2 && event2.slice(7);
+ if (modelArg && modelArg in props) {
+ const modifiersKey = `${modelArg === "modelValue" ? "model" : modelArg}Modifiers`;
+ const { number: number2, trim } = props[modifiersKey] || EMPTY_OBJ;
+ if (trim) {
+ args = rawArgs.map((a) => isString$4(a) ? a.trim() : a);
+ }
+ if (number2) {
+ args = rawArgs.map(looseToNumber);
+ }
+ }
+ if (false) {
+ devtoolsComponentEmit(instance, event2, args);
+ }
+ if (false) {
+ const lowerCaseEvent = event2.toLowerCase();
+ if (lowerCaseEvent !== event2 && props[toHandlerKey(lowerCaseEvent)]) {
+ warn$1(
+ `Event "${lowerCaseEvent}" is emitted in component ${formatComponentName(
+ instance,
+ instance.type
+ )} but the handler is registered for "${event2}". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "${hyphenate(
+ event2
+ )}" instead of "${event2}".`
+ );
+ }
+ }
+ let handlerName;
+ let handler4 = props[handlerName = toHandlerKey(event2)] || // also try camelCase event handler (#2249)
+ props[handlerName = toHandlerKey(camelize(event2))];
+ if (!handler4 && isModelListener2) {
+ handler4 = props[handlerName = toHandlerKey(hyphenate(event2))];
+ }
+ if (handler4) {
+ callWithAsyncErrorHandling(
+ handler4,
+ instance,
+ 6,
+ args
+ );
+ }
+ const onceHandler = props[handlerName + `Once`];
+ if (onceHandler) {
+ if (!instance.emitted) {
+ instance.emitted = {};
+ } else if (instance.emitted[handlerName]) {
+ return;
+ }
+ instance.emitted[handlerName] = true;
+ callWithAsyncErrorHandling(
+ onceHandler,
+ instance,
+ 6,
+ args
+ );
+ }
+}
+__name(emit, "emit");
+function normalizeEmitsOptions(comp, appContext, asMixin = false) {
+ const cache2 = appContext.emitsCache;
+ const cached = cache2.get(comp);
+ if (cached !== void 0) {
+ return cached;
+ }
+ const raw = comp.emits;
+ let normalized = {};
+ let hasExtends = false;
+ if (!isFunction$3(comp)) {
+ const extendEmits = /* @__PURE__ */ __name((raw2) => {
+ const normalizedFromExtend = normalizeEmitsOptions(raw2, appContext, true);
+ if (normalizedFromExtend) {
+ hasExtends = true;
+ extend(normalized, normalizedFromExtend);
+ }
+ }, "extendEmits");
+ if (!asMixin && appContext.mixins.length) {
+ appContext.mixins.forEach(extendEmits);
+ }
+ if (comp.extends) {
+ extendEmits(comp.extends);
+ }
+ if (comp.mixins) {
+ comp.mixins.forEach(extendEmits);
+ }
+ }
+ if (!raw && !hasExtends) {
+ if (isObject$4(comp)) {
+ cache2.set(comp, null);
+ }
+ return null;
+ }
+ if (isArray$3(raw)) {
+ raw.forEach((key) => normalized[key] = null);
+ } else {
+ extend(normalized, raw);
+ }
+ if (isObject$4(comp)) {
+ cache2.set(comp, normalized);
+ }
+ return normalized;
+}
+__name(normalizeEmitsOptions, "normalizeEmitsOptions");
+function isEmitListener(options3, key) {
+ if (!options3 || !isOn(key)) {
+ return false;
+ }
+ key = key.slice(2).replace(/Once$/, "");
+ return hasOwn$2(options3, key[0].toLowerCase() + key.slice(1)) || hasOwn$2(options3, hyphenate(key)) || hasOwn$2(options3, key);
+}
+__name(isEmitListener, "isEmitListener");
+let currentRenderingInstance = null;
+let currentScopeId = null;
+function setCurrentRenderingInstance(instance) {
+ const prev = currentRenderingInstance;
+ currentRenderingInstance = instance;
+ currentScopeId = instance && instance.type.__scopeId || null;
+ return prev;
+}
+__name(setCurrentRenderingInstance, "setCurrentRenderingInstance");
+function pushScopeId(id2) {
+ currentScopeId = id2;
+}
+__name(pushScopeId, "pushScopeId");
+function popScopeId() {
+ currentScopeId = null;
+}
+__name(popScopeId, "popScopeId");
+const withScopeId = /* @__PURE__ */ __name((_id2) => withCtx, "withScopeId");
+function withCtx(fn, ctx = currentRenderingInstance, isNonScopedSlot) {
+ if (!ctx) return fn;
+ if (fn._n) {
+ return fn;
+ }
+ const renderFnWithContext = /* @__PURE__ */ __name((...args) => {
+ if (renderFnWithContext._d) {
+ setBlockTracking(-1);
+ }
+ const prevInstance = setCurrentRenderingInstance(ctx);
+ let res;
+ try {
+ res = fn(...args);
+ } finally {
+ setCurrentRenderingInstance(prevInstance);
+ if (renderFnWithContext._d) {
+ setBlockTracking(1);
+ }
+ }
+ if (false) {
+ devtoolsComponentUpdated(ctx);
+ }
+ return res;
+ }, "renderFnWithContext");
+ renderFnWithContext._n = true;
+ renderFnWithContext._c = true;
+ renderFnWithContext._d = true;
+ return renderFnWithContext;
+}
+__name(withCtx, "withCtx");
+let accessedAttrs = false;
+function markAttrsAccessed() {
+ accessedAttrs = true;
+}
+__name(markAttrsAccessed, "markAttrsAccessed");
+function renderComponentRoot(instance) {
+ const {
+ type: Component,
+ vnode,
+ proxy,
+ withProxy,
+ propsOptions: [propsOptions],
+ slots,
+ attrs: attrs3,
+ emit: emit2,
+ render: render2,
+ renderCache,
+ props,
+ data: data19,
+ setupState,
+ ctx,
+ inheritAttrs
+ } = instance;
+ const prev = setCurrentRenderingInstance(instance);
+ let result;
+ let fallthroughAttrs;
+ if (false) {
+ accessedAttrs = false;
+ }
+ try {
+ if (vnode.shapeFlag & 4) {
+ const proxyToUse = withProxy || proxy;
+ const thisProxy = false ? new Proxy(proxyToUse, {
+ get(target, key, receiver) {
+ warn$1(
+ `Property '${String(
+ key
+ )}' was accessed via 'this'. Avoid using 'this' in templates.`
+ );
+ return Reflect.get(target, key, receiver);
+ }
+ }) : proxyToUse;
+ result = normalizeVNode(
+ render2.call(
+ thisProxy,
+ proxyToUse,
+ renderCache,
+ false ? shallowReadonly(props) : props,
+ setupState,
+ data19,
+ ctx
+ )
+ );
+ fallthroughAttrs = attrs3;
+ } else {
+ const render22 = Component;
+ if (false) {
+ markAttrsAccessed();
+ }
+ result = normalizeVNode(
+ render22.length > 1 ? render22(
+ false ? shallowReadonly(props) : props,
+ false ? {
+ get attrs() {
+ markAttrsAccessed();
+ return shallowReadonly(attrs3);
+ },
+ slots,
+ emit: emit2
+ } : { attrs: attrs3, slots, emit: emit2 }
+ ) : render22(
+ false ? shallowReadonly(props) : props,
+ null
+ )
+ );
+ fallthroughAttrs = Component.props ? attrs3 : getFunctionalFallthrough(attrs3);
+ }
+ } catch (err) {
+ blockStack.length = 0;
+ handleError(err, instance, 1);
+ result = createVNode(Comment);
+ }
+ let root26 = result;
+ let setRoot = void 0;
+ if (false) {
+ [root26, setRoot] = getChildRoot(result);
+ }
+ if (fallthroughAttrs && inheritAttrs !== false) {
+ const keys = Object.keys(fallthroughAttrs);
+ const { shapeFlag } = root26;
+ if (keys.length) {
+ if (shapeFlag & (1 | 6)) {
+ if (propsOptions && keys.some(isModelListener)) {
+ fallthroughAttrs = filterModelListeners(
+ fallthroughAttrs,
+ propsOptions
+ );
+ }
+ root26 = cloneVNode(root26, fallthroughAttrs, false, true);
+ } else if (false) {
+ const allAttrs = Object.keys(attrs3);
+ const eventAttrs = [];
+ const extraAttrs = [];
+ for (let i2 = 0, l = allAttrs.length; i2 < l; i2++) {
+ const key = allAttrs[i2];
+ if (isOn(key)) {
+ if (!isModelListener(key)) {
+ eventAttrs.push(key[2].toLowerCase() + key.slice(3));
+ }
+ } else {
+ extraAttrs.push(key);
+ }
+ }
+ if (extraAttrs.length) {
+ warn$1(
+ `Extraneous non-props attributes (${extraAttrs.join(", ")}) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.`
+ );
+ }
+ if (eventAttrs.length) {
+ warn$1(
+ `Extraneous non-emits event listeners (${eventAttrs.join(", ")}) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.`
+ );
+ }
+ }
+ }
+ }
+ if (vnode.dirs) {
+ if (false) {
+ warn$1(
+ `Runtime directive used on component with non-element root node. The directives will not function as intended.`
+ );
+ }
+ root26 = cloneVNode(root26, null, false, true);
+ root26.dirs = root26.dirs ? root26.dirs.concat(vnode.dirs) : vnode.dirs;
+ }
+ if (vnode.transition) {
+ if (false) {
+ warn$1(
+ `Component inside renders non-element root node that cannot be animated.`
+ );
+ }
+ root26.transition = vnode.transition;
+ }
+ if (false) {
+ setRoot(root26);
+ } else {
+ result = root26;
+ }
+ setCurrentRenderingInstance(prev);
+ return result;
+}
+__name(renderComponentRoot, "renderComponentRoot");
+const getChildRoot = /* @__PURE__ */ __name((vnode) => {
+ const rawChildren = vnode.children;
+ const dynamicChildren = vnode.dynamicChildren;
+ const childRoot = filterSingleRoot(rawChildren, false);
+ if (!childRoot) {
+ return [vnode, void 0];
+ } else if (false) {
+ return getChildRoot(childRoot);
+ }
+ const index2 = rawChildren.indexOf(childRoot);
+ const dynamicIndex = dynamicChildren ? dynamicChildren.indexOf(childRoot) : -1;
+ const setRoot = /* @__PURE__ */ __name((updatedRoot) => {
+ rawChildren[index2] = updatedRoot;
+ if (dynamicChildren) {
+ if (dynamicIndex > -1) {
+ dynamicChildren[dynamicIndex] = updatedRoot;
+ } else if (updatedRoot.patchFlag > 0) {
+ vnode.dynamicChildren = [...dynamicChildren, updatedRoot];
+ }
+ }
+ }, "setRoot");
+ return [normalizeVNode(childRoot), setRoot];
+}, "getChildRoot");
+function filterSingleRoot(children, recurse = true) {
+ let singleRoot;
+ for (let i2 = 0; i2 < children.length; i2++) {
+ const child = children[i2];
+ if (isVNode$1(child)) {
+ if (child.type !== Comment || child.children === "v-if") {
+ if (singleRoot) {
+ return;
+ } else {
+ singleRoot = child;
+ if (false) {
+ return filterSingleRoot(singleRoot.children);
+ }
+ }
+ }
+ } else {
+ return;
+ }
+ }
+ return singleRoot;
+}
+__name(filterSingleRoot, "filterSingleRoot");
+const getFunctionalFallthrough = /* @__PURE__ */ __name((attrs3) => {
+ let res;
+ for (const key in attrs3) {
+ if (key === "class" || key === "style" || isOn(key)) {
+ (res || (res = {}))[key] = attrs3[key];
+ }
+ }
+ return res;
+}, "getFunctionalFallthrough");
+const filterModelListeners = /* @__PURE__ */ __name((attrs3, props) => {
+ const res = {};
+ for (const key in attrs3) {
+ if (!isModelListener(key) || !(key.slice(9) in props)) {
+ res[key] = attrs3[key];
+ }
+ }
+ return res;
+}, "filterModelListeners");
+const isElementRoot = /* @__PURE__ */ __name((vnode) => {
+ return vnode.shapeFlag & (6 | 1) || vnode.type === Comment;
+}, "isElementRoot");
+function shouldUpdateComponent(prevVNode, nextVNode, optimized) {
+ const { props: prevProps, children: prevChildren, component } = prevVNode;
+ const { props: nextProps, children: nextChildren, patchFlag } = nextVNode;
+ const emits = component.emitsOptions;
+ if (false) {
+ return true;
+ }
+ if (nextVNode.dirs || nextVNode.transition) {
+ return true;
+ }
+ if (optimized && patchFlag >= 0) {
+ if (patchFlag & 1024) {
+ return true;
+ }
+ if (patchFlag & 16) {
+ if (!prevProps) {
+ return !!nextProps;
+ }
+ return hasPropsChanged(prevProps, nextProps, emits);
+ } else if (patchFlag & 8) {
+ const dynamicProps = nextVNode.dynamicProps;
+ for (let i2 = 0; i2 < dynamicProps.length; i2++) {
+ const key = dynamicProps[i2];
+ if (nextProps[key] !== prevProps[key] && !isEmitListener(emits, key)) {
+ return true;
+ }
+ }
+ }
+ } else {
+ if (prevChildren || nextChildren) {
+ if (!nextChildren || !nextChildren.$stable) {
+ return true;
+ }
+ }
+ if (prevProps === nextProps) {
+ return false;
+ }
+ if (!prevProps) {
+ return !!nextProps;
+ }
+ if (!nextProps) {
+ return true;
+ }
+ return hasPropsChanged(prevProps, nextProps, emits);
+ }
+ return false;
+}
+__name(shouldUpdateComponent, "shouldUpdateComponent");
+function hasPropsChanged(prevProps, nextProps, emitsOptions) {
+ const nextKeys = Object.keys(nextProps);
+ if (nextKeys.length !== Object.keys(prevProps).length) {
+ return true;
+ }
+ for (let i2 = 0; i2 < nextKeys.length; i2++) {
+ const key = nextKeys[i2];
+ if (nextProps[key] !== prevProps[key] && !isEmitListener(emitsOptions, key)) {
+ return true;
+ }
+ }
+ return false;
+}
+__name(hasPropsChanged, "hasPropsChanged");
+function updateHOCHostEl({ vnode, parent }, el) {
+ while (parent) {
+ const root26 = parent.subTree;
+ if (root26.suspense && root26.suspense.activeBranch === vnode) {
+ root26.el = vnode.el;
+ }
+ if (root26 === vnode) {
+ (vnode = parent.vnode).el = el;
+ parent = parent.parent;
+ } else {
+ break;
+ }
+ }
+}
+__name(updateHOCHostEl, "updateHOCHostEl");
+const COMPONENTS = "components";
+const DIRECTIVES = "directives";
+function resolveComponent(name, maybeSelfReference) {
+ return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name;
+}
+__name(resolveComponent, "resolveComponent");
+const NULL_DYNAMIC_COMPONENT = Symbol.for("v-ndc");
+function resolveDynamicComponent(component) {
+ if (isString$4(component)) {
+ return resolveAsset(COMPONENTS, component, false) || component;
+ } else {
+ return component || NULL_DYNAMIC_COMPONENT;
+ }
+}
+__name(resolveDynamicComponent, "resolveDynamicComponent");
+function resolveDirective(name) {
+ return resolveAsset(DIRECTIVES, name);
+}
+__name(resolveDirective, "resolveDirective");
+function resolveAsset(type, name, warnMissing = true, maybeSelfReference = false) {
+ const instance = currentRenderingInstance || currentInstance;
+ if (instance) {
+ const Component = instance.type;
+ if (type === COMPONENTS) {
+ const selfName = getComponentName(
+ Component,
+ false
+ );
+ if (selfName && (selfName === name || selfName === camelize(name) || selfName === capitalize$1(camelize(name)))) {
+ return Component;
+ }
+ }
+ const res = (
+ // local registration
+ // check instance[type] first which is resolved for options API
+ resolve$1(instance[type] || Component[type], name) || // global registration
+ resolve$1(instance.appContext[type], name)
+ );
+ if (!res && maybeSelfReference) {
+ return Component;
+ }
+ if (false) {
+ const extra = type === COMPONENTS ? `
+If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.` : ``;
+ warn$1(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`);
+ }
+ return res;
+ } else if (false) {
+ warn$1(
+ `resolve${capitalize$1(type.slice(0, -1))} can only be used in render() or setup().`
+ );
+ }
+}
+__name(resolveAsset, "resolveAsset");
+function resolve$1(registry, name) {
+ return registry && (registry[name] || registry[camelize(name)] || registry[capitalize$1(camelize(name))]);
+}
+__name(resolve$1, "resolve$1");
+const isSuspense = /* @__PURE__ */ __name((type) => type.__isSuspense, "isSuspense");
+let suspenseId = 0;
+const SuspenseImpl = {
+ name: "Suspense",
+ // In order to make Suspense tree-shakable, we need to avoid importing it
+ // directly in the renderer. The renderer checks for the __isSuspense flag
+ // on a vnode's type and calls the `process` method, passing in renderer
+ // internals.
+ __isSuspense: true,
+ process(n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals) {
+ if (n1 == null) {
+ mountSuspense(
+ n2,
+ container,
+ anchor,
+ parentComponent,
+ parentSuspense,
+ namespace,
+ slotScopeIds,
+ optimized,
+ rendererInternals
+ );
+ } else {
+ if (parentSuspense && parentSuspense.deps > 0 && !n1.suspense.isInFallback) {
+ n2.suspense = n1.suspense;
+ n2.suspense.vnode = n2;
+ n2.el = n1.el;
+ return;
+ }
+ patchSuspense(
+ n1,
+ n2,
+ container,
+ anchor,
+ parentComponent,
+ namespace,
+ slotScopeIds,
+ optimized,
+ rendererInternals
+ );
+ }
+ },
+ hydrate: hydrateSuspense,
+ normalize: normalizeSuspenseChildren
+};
+const Suspense = SuspenseImpl;
+function triggerEvent(vnode, name) {
+ const eventListener = vnode.props && vnode.props[name];
+ if (isFunction$3(eventListener)) {
+ eventListener();
+ }
+}
+__name(triggerEvent, "triggerEvent");
+function mountSuspense(vnode, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals) {
+ const {
+ p: patch,
+ o: { createElement: createElement2 }
+ } = rendererInternals;
+ const hiddenContainer = createElement2("div");
+ const suspense = vnode.suspense = createSuspenseBoundary(
+ vnode,
+ parentSuspense,
+ parentComponent,
+ container,
+ hiddenContainer,
+ anchor,
+ namespace,
+ slotScopeIds,
+ optimized,
+ rendererInternals
+ );
+ patch(
+ null,
+ suspense.pendingBranch = vnode.ssContent,
+ hiddenContainer,
+ null,
+ parentComponent,
+ suspense,
+ namespace,
+ slotScopeIds
+ );
+ if (suspense.deps > 0) {
+ triggerEvent(vnode, "onPending");
+ triggerEvent(vnode, "onFallback");
+ patch(
+ null,
+ vnode.ssFallback,
+ container,
+ anchor,
+ parentComponent,
+ null,
+ // fallback tree will not have suspense context
+ namespace,
+ slotScopeIds
+ );
+ setActiveBranch(suspense, vnode.ssFallback);
+ } else {
+ suspense.resolve(false, true);
+ }
+}
+__name(mountSuspense, "mountSuspense");
+function patchSuspense(n1, n2, container, anchor, parentComponent, namespace, slotScopeIds, optimized, { p: patch, um: unmount, o: { createElement: createElement2 } }) {
+ const suspense = n2.suspense = n1.suspense;
+ suspense.vnode = n2;
+ n2.el = n1.el;
+ const newBranch = n2.ssContent;
+ const newFallback = n2.ssFallback;
+ const { activeBranch, pendingBranch, isInFallback, isHydrating } = suspense;
+ if (pendingBranch) {
+ suspense.pendingBranch = newBranch;
+ if (isSameVNodeType(newBranch, pendingBranch)) {
+ patch(
+ pendingBranch,
+ newBranch,
+ suspense.hiddenContainer,
+ null,
+ parentComponent,
+ suspense,
+ namespace,
+ slotScopeIds,
+ optimized
+ );
+ if (suspense.deps <= 0) {
+ suspense.resolve();
+ } else if (isInFallback) {
+ if (!isHydrating) {
+ patch(
+ activeBranch,
+ newFallback,
+ container,
+ anchor,
+ parentComponent,
+ null,
+ // fallback tree will not have suspense context
+ namespace,
+ slotScopeIds,
+ optimized
+ );
+ setActiveBranch(suspense, newFallback);
+ }
+ }
+ } else {
+ suspense.pendingId = suspenseId++;
+ if (isHydrating) {
+ suspense.isHydrating = false;
+ suspense.activeBranch = pendingBranch;
+ } else {
+ unmount(pendingBranch, parentComponent, suspense);
+ }
+ suspense.deps = 0;
+ suspense.effects.length = 0;
+ suspense.hiddenContainer = createElement2("div");
+ if (isInFallback) {
+ patch(
+ null,
+ newBranch,
+ suspense.hiddenContainer,
+ null,
+ parentComponent,
+ suspense,
+ namespace,
+ slotScopeIds,
+ optimized
+ );
+ if (suspense.deps <= 0) {
+ suspense.resolve();
+ } else {
+ patch(
+ activeBranch,
+ newFallback,
+ container,
+ anchor,
+ parentComponent,
+ null,
+ // fallback tree will not have suspense context
+ namespace,
+ slotScopeIds,
+ optimized
+ );
+ setActiveBranch(suspense, newFallback);
+ }
+ } else if (activeBranch && isSameVNodeType(newBranch, activeBranch)) {
+ patch(
+ activeBranch,
+ newBranch,
+ container,
+ anchor,
+ parentComponent,
+ suspense,
+ namespace,
+ slotScopeIds,
+ optimized
+ );
+ suspense.resolve(true);
+ } else {
+ patch(
+ null,
+ newBranch,
+ suspense.hiddenContainer,
+ null,
+ parentComponent,
+ suspense,
+ namespace,
+ slotScopeIds,
+ optimized
+ );
+ if (suspense.deps <= 0) {
+ suspense.resolve();
+ }
+ }
+ }
+ } else {
+ if (activeBranch && isSameVNodeType(newBranch, activeBranch)) {
+ patch(
+ activeBranch,
+ newBranch,
+ container,
+ anchor,
+ parentComponent,
+ suspense,
+ namespace,
+ slotScopeIds,
+ optimized
+ );
+ setActiveBranch(suspense, newBranch);
+ } else {
+ triggerEvent(n2, "onPending");
+ suspense.pendingBranch = newBranch;
+ if (newBranch.shapeFlag & 512) {
+ suspense.pendingId = newBranch.component.suspenseId;
+ } else {
+ suspense.pendingId = suspenseId++;
+ }
+ patch(
+ null,
+ newBranch,
+ suspense.hiddenContainer,
+ null,
+ parentComponent,
+ suspense,
+ namespace,
+ slotScopeIds,
+ optimized
+ );
+ if (suspense.deps <= 0) {
+ suspense.resolve();
+ } else {
+ const { timeout, pendingId } = suspense;
+ if (timeout > 0) {
+ setTimeout(() => {
+ if (suspense.pendingId === pendingId) {
+ suspense.fallback(newFallback);
+ }
+ }, timeout);
+ } else if (timeout === 0) {
+ suspense.fallback(newFallback);
+ }
+ }
+ }
+ }
+}
+__name(patchSuspense, "patchSuspense");
+let hasWarned$1 = false;
+function createSuspenseBoundary(vnode, parentSuspense, parentComponent, container, hiddenContainer, anchor, namespace, slotScopeIds, optimized, rendererInternals, isHydrating = false) {
+ if (false) {
+ hasWarned$1 = true;
+ console[console.info ? "info" : "log"](
+ ` is an experimental feature and its API will likely change.`
+ );
+ }
+ const {
+ p: patch,
+ m: move,
+ um: unmount,
+ n: next,
+ o: { parentNode, remove: remove22 }
+ } = rendererInternals;
+ let parentSuspenseId;
+ const isSuspensible = isVNodeSuspensible(vnode);
+ if (isSuspensible) {
+ if (parentSuspense && parentSuspense.pendingBranch) {
+ parentSuspenseId = parentSuspense.pendingId;
+ parentSuspense.deps++;
+ }
+ }
+ const timeout = vnode.props ? toNumber(vnode.props.timeout) : void 0;
+ if (false) {
+ assertNumber(timeout, `Suspense timeout`);
+ }
+ const initialAnchor = anchor;
+ const suspense = {
+ vnode,
+ parent: parentSuspense,
+ parentComponent,
+ namespace,
+ container,
+ hiddenContainer,
+ deps: 0,
+ pendingId: suspenseId++,
+ timeout: typeof timeout === "number" ? timeout : -1,
+ activeBranch: null,
+ pendingBranch: null,
+ isInFallback: !isHydrating,
+ isHydrating,
+ isUnmounted: false,
+ effects: [],
+ resolve(resume = false, sync = false) {
+ if (false) {
+ if (!resume && !suspense.pendingBranch) {
+ throw new Error(
+ `suspense.resolve() is called without a pending branch.`
+ );
+ }
+ if (suspense.isUnmounted) {
+ throw new Error(
+ `suspense.resolve() is called on an already unmounted suspense boundary.`
+ );
+ }
+ }
+ const {
+ vnode: vnode2,
+ activeBranch,
+ pendingBranch,
+ pendingId,
+ effects,
+ parentComponent: parentComponent2,
+ container: container2
+ } = suspense;
+ let delayEnter = false;
+ if (suspense.isHydrating) {
+ suspense.isHydrating = false;
+ } else if (!resume) {
+ delayEnter = activeBranch && pendingBranch.transition && pendingBranch.transition.mode === "out-in";
+ if (delayEnter) {
+ activeBranch.transition.afterLeave = () => {
+ if (pendingId === suspense.pendingId) {
+ move(
+ pendingBranch,
+ container2,
+ anchor === initialAnchor ? next(activeBranch) : anchor,
+ 0
+ );
+ queuePostFlushCb(effects);
+ }
+ };
+ }
+ if (activeBranch) {
+ if (parentNode(activeBranch.el) !== suspense.hiddenContainer) {
+ anchor = next(activeBranch);
+ }
+ unmount(activeBranch, parentComponent2, suspense, true);
+ }
+ if (!delayEnter) {
+ move(pendingBranch, container2, anchor, 0);
+ }
+ }
+ setActiveBranch(suspense, pendingBranch);
+ suspense.pendingBranch = null;
+ suspense.isInFallback = false;
+ let parent = suspense.parent;
+ let hasUnresolvedAncestor = false;
+ while (parent) {
+ if (parent.pendingBranch) {
+ parent.effects.push(...effects);
+ hasUnresolvedAncestor = true;
+ break;
+ }
+ parent = parent.parent;
+ }
+ if (!hasUnresolvedAncestor && !delayEnter) {
+ queuePostFlushCb(effects);
+ }
+ suspense.effects = [];
+ if (isSuspensible) {
+ if (parentSuspense && parentSuspense.pendingBranch && parentSuspenseId === parentSuspense.pendingId) {
+ parentSuspense.deps--;
+ if (parentSuspense.deps === 0 && !sync) {
+ parentSuspense.resolve();
+ }
+ }
+ }
+ triggerEvent(vnode2, "onResolve");
+ },
+ fallback(fallbackVNode) {
+ if (!suspense.pendingBranch) {
+ return;
+ }
+ const { vnode: vnode2, activeBranch, parentComponent: parentComponent2, container: container2, namespace: namespace2 } = suspense;
+ triggerEvent(vnode2, "onFallback");
+ const anchor2 = next(activeBranch);
+ const mountFallback = /* @__PURE__ */ __name(() => {
+ if (!suspense.isInFallback) {
+ return;
+ }
+ patch(
+ null,
+ fallbackVNode,
+ container2,
+ anchor2,
+ parentComponent2,
+ null,
+ // fallback tree will not have suspense context
+ namespace2,
+ slotScopeIds,
+ optimized
+ );
+ setActiveBranch(suspense, fallbackVNode);
+ }, "mountFallback");
+ const delayEnter = fallbackVNode.transition && fallbackVNode.transition.mode === "out-in";
+ if (delayEnter) {
+ activeBranch.transition.afterLeave = mountFallback;
+ }
+ suspense.isInFallback = true;
+ unmount(
+ activeBranch,
+ parentComponent2,
+ null,
+ // no suspense so unmount hooks fire now
+ true
+ // shouldRemove
+ );
+ if (!delayEnter) {
+ mountFallback();
+ }
+ },
+ move(container2, anchor2, type) {
+ suspense.activeBranch && move(suspense.activeBranch, container2, anchor2, type);
+ suspense.container = container2;
+ },
+ next() {
+ return suspense.activeBranch && next(suspense.activeBranch);
+ },
+ registerDep(instance, setupRenderEffect, optimized2) {
+ const isInPendingSuspense = !!suspense.pendingBranch;
+ if (isInPendingSuspense) {
+ suspense.deps++;
+ }
+ const hydratedEl = instance.vnode.el;
+ instance.asyncDep.catch((err) => {
+ handleError(err, instance, 0);
+ }).then((asyncSetupResult) => {
+ if (instance.isUnmounted || suspense.isUnmounted || suspense.pendingId !== instance.suspenseId) {
+ return;
+ }
+ instance.asyncResolved = true;
+ const { vnode: vnode2 } = instance;
+ if (false) {
+ pushWarningContext(vnode2);
+ }
+ handleSetupResult(instance, asyncSetupResult, false);
+ if (hydratedEl) {
+ vnode2.el = hydratedEl;
+ }
+ const placeholder = !hydratedEl && instance.subTree.el;
+ setupRenderEffect(
+ instance,
+ vnode2,
+ // component may have been moved before resolve.
+ // if this is not a hydration, instance.subTree will be the comment
+ // placeholder.
+ parentNode(hydratedEl || instance.subTree.el),
+ // anchor will not be used if this is hydration, so only need to
+ // consider the comment placeholder case.
+ hydratedEl ? null : next(instance.subTree),
+ suspense,
+ namespace,
+ optimized2
+ );
+ if (placeholder) {
+ remove22(placeholder);
+ }
+ updateHOCHostEl(instance, vnode2.el);
+ if (false) {
+ popWarningContext();
+ }
+ if (isInPendingSuspense && --suspense.deps === 0) {
+ suspense.resolve();
+ }
+ });
+ },
+ unmount(parentSuspense2, doRemove) {
+ suspense.isUnmounted = true;
+ if (suspense.activeBranch) {
+ unmount(
+ suspense.activeBranch,
+ parentComponent,
+ parentSuspense2,
+ doRemove
+ );
+ }
+ if (suspense.pendingBranch) {
+ unmount(
+ suspense.pendingBranch,
+ parentComponent,
+ parentSuspense2,
+ doRemove
+ );
+ }
+ }
+ };
+ return suspense;
+}
+__name(createSuspenseBoundary, "createSuspenseBoundary");
+function hydrateSuspense(node3, vnode, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals, hydrateNode) {
+ const suspense = vnode.suspense = createSuspenseBoundary(
+ vnode,
+ parentSuspense,
+ parentComponent,
+ node3.parentNode,
+ // eslint-disable-next-line no-restricted-globals
+ document.createElement("div"),
+ null,
+ namespace,
+ slotScopeIds,
+ optimized,
+ rendererInternals,
+ true
+ );
+ const result = hydrateNode(
+ node3,
+ suspense.pendingBranch = vnode.ssContent,
+ parentComponent,
+ suspense,
+ slotScopeIds,
+ optimized
+ );
+ if (suspense.deps === 0) {
+ suspense.resolve(false, true);
+ }
+ return result;
+}
+__name(hydrateSuspense, "hydrateSuspense");
+function normalizeSuspenseChildren(vnode) {
+ const { shapeFlag, children } = vnode;
+ const isSlotChildren = shapeFlag & 32;
+ vnode.ssContent = normalizeSuspenseSlot(
+ isSlotChildren ? children.default : children
+ );
+ vnode.ssFallback = isSlotChildren ? normalizeSuspenseSlot(children.fallback) : createVNode(Comment);
+}
+__name(normalizeSuspenseChildren, "normalizeSuspenseChildren");
+function normalizeSuspenseSlot(s) {
+ let block2;
+ if (isFunction$3(s)) {
+ const trackBlock = isBlockTreeEnabled && s._c;
+ if (trackBlock) {
+ s._d = false;
+ openBlock();
+ }
+ s = s();
+ if (trackBlock) {
+ s._d = true;
+ block2 = currentBlock;
+ closeBlock();
+ }
+ }
+ if (isArray$3(s)) {
+ const singleChild = filterSingleRoot(s);
+ if (false) {
+ warn$1(` slots expect a single root node.`);
+ }
+ s = singleChild;
+ }
+ s = normalizeVNode(s);
+ if (block2 && !s.dynamicChildren) {
+ s.dynamicChildren = block2.filter((c) => c !== s);
+ }
+ return s;
+}
+__name(normalizeSuspenseSlot, "normalizeSuspenseSlot");
+function queueEffectWithSuspense(fn, suspense) {
+ if (suspense && suspense.pendingBranch) {
+ if (isArray$3(fn)) {
+ suspense.effects.push(...fn);
+ } else {
+ suspense.effects.push(fn);
+ }
+ } else {
+ queuePostFlushCb(fn);
+ }
+}
+__name(queueEffectWithSuspense, "queueEffectWithSuspense");
+function setActiveBranch(suspense, branch) {
+ suspense.activeBranch = branch;
+ const { vnode, parentComponent } = suspense;
+ let el = branch.el;
+ while (!el && branch.component) {
+ branch = branch.component.subTree;
+ el = branch.el;
+ }
+ vnode.el = el;
+ if (parentComponent && parentComponent.subTree === vnode) {
+ parentComponent.vnode.el = el;
+ updateHOCHostEl(parentComponent, el);
+ }
+}
+__name(setActiveBranch, "setActiveBranch");
+function isVNodeSuspensible(vnode) {
+ const suspensible = vnode.props && vnode.props.suspensible;
+ return suspensible != null && suspensible !== false;
+}
+__name(isVNodeSuspensible, "isVNodeSuspensible");
+function injectHook(type, hook, target = currentInstance, prepend = false) {
+ if (target) {
+ const hooks = target[type] || (target[type] = []);
+ const wrappedHook = hook.__weh || (hook.__weh = (...args) => {
+ pauseTracking();
+ const reset = setCurrentInstance(target);
+ const res = callWithAsyncErrorHandling(hook, target, type, args);
+ reset();
+ resetTracking();
+ return res;
+ });
+ if (prepend) {
+ hooks.unshift(wrappedHook);
+ } else {
+ hooks.push(wrappedHook);
+ }
+ return wrappedHook;
+ } else if (false) {
+ const apiName = toHandlerKey(ErrorTypeStrings$1[type].replace(/ hook$/, ""));
+ warn$1(
+ `${apiName} is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup(). If you are using async setup(), make sure to register lifecycle hooks before the first await statement.`
+ );
+ }
+}
+__name(injectHook, "injectHook");
+const createHook = /* @__PURE__ */ __name((lifecycle2) => (hook, target = currentInstance) => {
+ if (!isInSSRComponentSetup || lifecycle2 === "sp") {
+ injectHook(lifecycle2, (...args) => hook(...args), target);
+ }
+}, "createHook");
+const onBeforeMount = createHook("bm");
+const onMounted = createHook("m");
+const onBeforeUpdate = createHook("bu");
+const onUpdated = createHook("u");
+const onBeforeUnmount = createHook("bum");
+const onUnmounted = createHook("um");
+const onServerPrefetch = createHook("sp");
+const onRenderTriggered = createHook(
+ "rtg"
+);
+const onRenderTracked = createHook(
+ "rtc"
+);
+function onErrorCaptured(hook, target = currentInstance) {
+ injectHook("ec", hook, target);
+}
+__name(onErrorCaptured, "onErrorCaptured");
+function validateDirectiveName(name) {
+ if (isBuiltInDirective(name)) {
+ warn$1("Do not use built-in directive ids as custom directive id: " + name);
+ }
+}
+__name(validateDirectiveName, "validateDirectiveName");
+function withDirectives(vnode, directives) {
+ if (currentRenderingInstance === null) {
+ return vnode;
+ }
+ const instance = getComponentPublicInstance(currentRenderingInstance);
+ const bindings = vnode.dirs || (vnode.dirs = []);
+ for (let i2 = 0; i2 < directives.length; i2++) {
+ let [dir, value3, arg, modifiers = EMPTY_OBJ] = directives[i2];
+ if (dir) {
+ if (isFunction$3(dir)) {
+ dir = {
+ mounted: dir,
+ updated: dir
+ };
+ }
+ if (dir.deep) {
+ traverse(value3);
+ }
+ bindings.push({
+ dir,
+ instance,
+ value: value3,
+ oldValue: void 0,
+ arg,
+ modifiers
+ });
+ }
+ }
+ return vnode;
+}
+__name(withDirectives, "withDirectives");
+function invokeDirectiveHook(vnode, prevVNode, instance, name) {
+ const bindings = vnode.dirs;
+ const oldBindings = prevVNode && prevVNode.dirs;
+ for (let i2 = 0; i2 < bindings.length; i2++) {
+ const binding = bindings[i2];
+ if (oldBindings) {
+ binding.oldValue = oldBindings[i2].value;
+ }
+ let hook = binding.dir[name];
+ if (hook) {
+ pauseTracking();
+ callWithAsyncErrorHandling(hook, instance, 8, [
+ vnode.el,
+ binding,
+ vnode,
+ prevVNode
+ ]);
+ resetTracking();
+ }
+ }
+}
+__name(invokeDirectiveHook, "invokeDirectiveHook");
+function renderList(source, renderItem, cache2, index2) {
+ let ret;
+ const cached = cache2 && cache2[index2];
+ if (isArray$3(source) || isString$4(source)) {
+ ret = new Array(source.length);
+ for (let i2 = 0, l = source.length; i2 < l; i2++) {
+ ret[i2] = renderItem(source[i2], i2, void 0, cached && cached[i2]);
+ }
+ } else if (typeof source === "number") {
+ if (false) {
+ warn$1(`The v-for range expect an integer value but got ${source}.`);
+ }
+ ret = new Array(source);
+ for (let i2 = 0; i2 < source; i2++) {
+ ret[i2] = renderItem(i2 + 1, i2, void 0, cached && cached[i2]);
+ }
+ } else if (isObject$4(source)) {
+ if (source[Symbol.iterator]) {
+ ret = Array.from(
+ source,
+ (item2, i2) => renderItem(item2, i2, void 0, cached && cached[i2])
+ );
+ } else {
+ const keys = Object.keys(source);
+ ret = new Array(keys.length);
+ for (let i2 = 0, l = keys.length; i2 < l; i2++) {
+ const key = keys[i2];
+ ret[i2] = renderItem(source[key], key, i2, cached && cached[i2]);
+ }
+ }
+ } else {
+ ret = [];
+ }
+ if (cache2) {
+ cache2[index2] = ret;
+ }
+ return ret;
+}
+__name(renderList, "renderList");
+function createSlots(slots, dynamicSlots) {
+ for (let i2 = 0; i2 < dynamicSlots.length; i2++) {
+ const slot = dynamicSlots[i2];
+ if (isArray$3(slot)) {
+ for (let j = 0; j < slot.length; j++) {
+ slots[slot[j].name] = slot[j].fn;
+ }
+ } else if (slot) {
+ slots[slot.name] = slot.key ? (...args) => {
+ const res = slot.fn(...args);
+ if (res) res.key = slot.key;
+ return res;
+ } : slot.fn;
+ }
+ }
+ return slots;
+}
+__name(createSlots, "createSlots");
+/*! #__NO_SIDE_EFFECTS__ */
+// @__NO_SIDE_EFFECTS__
+function defineComponent(options3, extraOptions) {
+ return isFunction$3(options3) ? (
+ // #8326: extend call and options.name access are considered side-effects
+ // by Rollup, so we have to wrap it in a pure-annotated IIFE.
+ /* @__PURE__ */ (() => extend({ name: options3.name }, extraOptions, { setup: options3 }))()
+ ) : options3;
+}
+__name(defineComponent, "defineComponent");
+const isAsyncWrapper = /* @__PURE__ */ __name((i2) => !!i2.type.__asyncLoader, "isAsyncWrapper");
+/*! #__NO_SIDE_EFFECTS__ */
+// @__NO_SIDE_EFFECTS__
+function defineAsyncComponent(source) {
+ if (isFunction$3(source)) {
+ source = { loader: source };
+ }
+ const {
+ loader,
+ loadingComponent,
+ errorComponent,
+ delay = 200,
+ timeout,
+ // undefined = never times out
+ suspensible = true,
+ onError: userOnError
+ } = source;
+ let pendingRequest = null;
+ let resolvedComp;
+ let retries = 0;
+ const retry = /* @__PURE__ */ __name(() => {
+ retries++;
+ pendingRequest = null;
+ return load2();
+ }, "retry");
+ const load2 = /* @__PURE__ */ __name(() => {
+ let thisRequest;
+ return pendingRequest || (thisRequest = pendingRequest = loader().catch((err) => {
+ err = err instanceof Error ? err : new Error(String(err));
+ if (userOnError) {
+ return new Promise((resolve22, reject2) => {
+ const userRetry = /* @__PURE__ */ __name(() => resolve22(retry()), "userRetry");
+ const userFail = /* @__PURE__ */ __name(() => reject2(err), "userFail");
+ userOnError(err, userRetry, userFail, retries + 1);
+ });
+ } else {
+ throw err;
+ }
+ }).then((comp) => {
+ if (thisRequest !== pendingRequest && pendingRequest) {
+ return pendingRequest;
+ }
+ if (false) {
+ warn$1(
+ `Async component loader resolved to undefined. If you are using retry(), make sure to return its return value.`
+ );
+ }
+ if (comp && (comp.__esModule || comp[Symbol.toStringTag] === "Module")) {
+ comp = comp.default;
+ }
+ if (false) {
+ throw new Error(`Invalid async component load result: ${comp}`);
+ }
+ resolvedComp = comp;
+ return comp;
+ }));
+ }, "load");
+ return /* @__PURE__ */ defineComponent({
+ name: "AsyncComponentWrapper",
+ __asyncLoader: load2,
+ get __asyncResolved() {
+ return resolvedComp;
+ },
+ setup() {
+ const instance = currentInstance;
+ if (resolvedComp) {
+ return () => createInnerComp(resolvedComp, instance);
+ }
+ const onError2 = /* @__PURE__ */ __name((err) => {
+ pendingRequest = null;
+ handleError(
+ err,
+ instance,
+ 13,
+ !errorComponent
+ );
+ }, "onError");
+ if (suspensible && instance.suspense || isInSSRComponentSetup) {
+ return load2().then((comp) => {
+ return () => createInnerComp(comp, instance);
+ }).catch((err) => {
+ onError2(err);
+ return () => errorComponent ? createVNode(errorComponent, {
+ error: err
+ }) : null;
+ });
+ }
+ const loaded = ref(false);
+ const error = ref();
+ const delayed = ref(!!delay);
+ if (delay) {
+ setTimeout(() => {
+ delayed.value = false;
+ }, delay);
+ }
+ if (timeout != null) {
+ setTimeout(() => {
+ if (!loaded.value && !error.value) {
+ const err = new Error(
+ `Async component timed out after ${timeout}ms.`
+ );
+ onError2(err);
+ error.value = err;
+ }
+ }, timeout);
+ }
+ load2().then(() => {
+ loaded.value = true;
+ if (instance.parent && isKeepAlive(instance.parent.vnode)) {
+ instance.parent.effect.dirty = true;
+ queueJob(instance.parent.update);
+ }
+ }).catch((err) => {
+ onError2(err);
+ error.value = err;
+ });
+ return () => {
+ if (loaded.value && resolvedComp) {
+ return createInnerComp(resolvedComp, instance);
+ } else if (error.value && errorComponent) {
+ return createVNode(errorComponent, {
+ error: error.value
+ });
+ } else if (loadingComponent && !delayed.value) {
+ return createVNode(loadingComponent);
+ }
+ };
+ }
+ });
+}
+__name(defineAsyncComponent, "defineAsyncComponent");
+function createInnerComp(comp, parent) {
+ const { ref: ref22, props, children, ce } = parent.vnode;
+ const vnode = createVNode(comp, props, children);
+ vnode.ref = ref22;
+ vnode.ce = ce;
+ delete parent.vnode.ce;
+ return vnode;
+}
+__name(createInnerComp, "createInnerComp");
+function renderSlot(slots, name, props = {}, fallback, noSlotted) {
+ if (currentRenderingInstance.isCE || currentRenderingInstance.parent && isAsyncWrapper(currentRenderingInstance.parent) && currentRenderingInstance.parent.isCE) {
+ if (name !== "default") props.name = name;
+ return createVNode("slot", props, fallback && fallback());
+ }
+ let slot = slots[name];
+ if (false) {
+ warn$1(
+ `SSR-optimized slot function detected in a non-SSR-optimized render function. You need to mark this component with $dynamic-slots in the parent template.`
+ );
+ slot = /* @__PURE__ */ __name(() => [], "slot");
+ }
+ if (slot && slot._c) {
+ slot._d = false;
+ }
+ openBlock();
+ const validSlotContent = slot && ensureValidVNode(slot(props));
+ const rendered = createBlock(
+ Fragment,
+ {
+ key: props.key || // slot content array of a dynamic conditional slot may have a branch
+ // key attached in the `createSlots` helper, respect that
+ validSlotContent && validSlotContent.key || `_${name}`
+ },
+ validSlotContent || (fallback ? fallback() : []),
+ validSlotContent && slots._ === 1 ? 64 : -2
+ );
+ if (!noSlotted && rendered.scopeId) {
+ rendered.slotScopeIds = [rendered.scopeId + "-s"];
+ }
+ if (slot && slot._c) {
+ slot._d = true;
+ }
+ return rendered;
+}
+__name(renderSlot, "renderSlot");
+function ensureValidVNode(vnodes) {
+ return vnodes.some((child) => {
+ if (!isVNode$1(child)) return true;
+ if (child.type === Comment) return false;
+ if (child.type === Fragment && !ensureValidVNode(child.children))
+ return false;
+ return true;
+ }) ? vnodes : null;
+}
+__name(ensureValidVNode, "ensureValidVNode");
+function toHandlers(obj, preserveCaseIfNecessary) {
+ const ret = {};
+ if (false) {
+ warn$1(`v-on with no argument expects an object value.`);
+ return ret;
+ }
+ for (const key in obj) {
+ ret[preserveCaseIfNecessary && /[A-Z]/.test(key) ? `on:${key}` : toHandlerKey(key)] = obj[key];
+ }
+ return ret;
+}
+__name(toHandlers, "toHandlers");
+const getPublicInstance = /* @__PURE__ */ __name((i2) => {
+ if (!i2) return null;
+ if (isStatefulComponent(i2)) return getComponentPublicInstance(i2);
+ return getPublicInstance(i2.parent);
+}, "getPublicInstance");
+const publicPropertiesMap = (
+ // Move PURE marker to new line to workaround compiler discarding it
+ // due to type annotation
+ /* @__PURE__ */ extend(/* @__PURE__ */ Object.create(null), {
+ $: /* @__PURE__ */ __name((i2) => i2, "$"),
+ $el: /* @__PURE__ */ __name((i2) => i2.vnode.el, "$el"),
+ $data: /* @__PURE__ */ __name((i2) => i2.data, "$data"),
+ $props: /* @__PURE__ */ __name((i2) => false ? shallowReadonly(i2.props) : i2.props, "$props"),
+ $attrs: /* @__PURE__ */ __name((i2) => false ? shallowReadonly(i2.attrs) : i2.attrs, "$attrs"),
+ $slots: /* @__PURE__ */ __name((i2) => false ? shallowReadonly(i2.slots) : i2.slots, "$slots"),
+ $refs: /* @__PURE__ */ __name((i2) => false ? shallowReadonly(i2.refs) : i2.refs, "$refs"),
+ $parent: /* @__PURE__ */ __name((i2) => getPublicInstance(i2.parent), "$parent"),
+ $root: /* @__PURE__ */ __name((i2) => getPublicInstance(i2.root), "$root"),
+ $emit: /* @__PURE__ */ __name((i2) => i2.emit, "$emit"),
+ $options: /* @__PURE__ */ __name((i2) => true ? resolveMergedOptions(i2) : i2.type, "$options"),
+ $forceUpdate: /* @__PURE__ */ __name((i2) => i2.f || (i2.f = () => {
+ i2.effect.dirty = true;
+ queueJob(i2.update);
+ }), "$forceUpdate"),
+ $nextTick: /* @__PURE__ */ __name((i2) => i2.n || (i2.n = nextTick.bind(i2.proxy)), "$nextTick"),
+ $watch: /* @__PURE__ */ __name((i2) => true ? instanceWatch.bind(i2) : NOOP, "$watch")
+ })
+);
+const isReservedPrefix = /* @__PURE__ */ __name((key) => key === "_" || key === "$", "isReservedPrefix");
+const hasSetupBinding = /* @__PURE__ */ __name((state, key) => state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn$2(state, key), "hasSetupBinding");
+const PublicInstanceProxyHandlers = {
+ get({ _: instance }, key) {
+ if (key === "__v_skip") {
+ return true;
+ }
+ const { ctx, setupState, data: data19, props, accessCache, type, appContext } = instance;
+ if (false) {
+ return true;
+ }
+ let normalizedProps;
+ if (key[0] !== "$") {
+ const n = accessCache[key];
+ if (n !== void 0) {
+ switch (n) {
+ case 1:
+ return setupState[key];
+ case 2:
+ return data19[key];
+ case 4:
+ return ctx[key];
+ case 3:
+ return props[key];
+ }
+ } else if (hasSetupBinding(setupState, key)) {
+ accessCache[key] = 1;
+ return setupState[key];
+ } else if (data19 !== EMPTY_OBJ && hasOwn$2(data19, key)) {
+ accessCache[key] = 2;
+ return data19[key];
+ } else if (
+ // only cache other properties when instance has declared (thus stable)
+ // props
+ (normalizedProps = instance.propsOptions[0]) && hasOwn$2(normalizedProps, key)
+ ) {
+ accessCache[key] = 3;
+ return props[key];
+ } else if (ctx !== EMPTY_OBJ && hasOwn$2(ctx, key)) {
+ accessCache[key] = 4;
+ return ctx[key];
+ } else if (shouldCacheAccess) {
+ accessCache[key] = 0;
+ }
+ }
+ const publicGetter = publicPropertiesMap[key];
+ let cssModule, globalProperties;
+ if (publicGetter) {
+ if (key === "$attrs") {
+ track(instance.attrs, "get", "");
+ } else if (false) {
+ track(instance, "get", key);
+ }
+ return publicGetter(instance);
+ } else if (
+ // css module (injected by vue-loader)
+ (cssModule = type.__cssModules) && (cssModule = cssModule[key])
+ ) {
+ return cssModule;
+ } else if (ctx !== EMPTY_OBJ && hasOwn$2(ctx, key)) {
+ accessCache[key] = 4;
+ return ctx[key];
+ } else if (
+ // global properties
+ globalProperties = appContext.config.globalProperties, hasOwn$2(globalProperties, key)
+ ) {
+ {
+ return globalProperties[key];
+ }
+ } else if (false) {
+ if (data19 !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn$2(data19, key)) {
+ warn$1(
+ `Property ${JSON.stringify(
+ key
+ )} must be accessed via $data because it starts with a reserved character ("$" or "_") and is not proxied on the render context.`
+ );
+ } else if (instance === currentRenderingInstance) {
+ warn$1(
+ `Property ${JSON.stringify(key)} was accessed during render but is not defined on instance.`
+ );
+ }
+ }
+ },
+ set({ _: instance }, key, value3) {
+ const { data: data19, setupState, ctx } = instance;
+ if (hasSetupBinding(setupState, key)) {
+ setupState[key] = value3;
+ return true;
+ } else if (false) {
+ warn$1(`Cannot mutate \r\n\r\n\r\n\r\n\r\n","var util;\n(function (util) {\n util.assertEqual = (val) => val;\n function assertIs(_arg) { }\n util.assertIs = assertIs;\n function assertNever(_x) {\n throw new Error();\n }\n util.assertNever = assertNever;\n util.arrayToEnum = (items) => {\n const obj = {};\n for (const item of items) {\n obj[item] = item;\n }\n return obj;\n };\n util.getValidEnumValues = (obj) => {\n const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== \"number\");\n const filtered = {};\n for (const k of validKeys) {\n filtered[k] = obj[k];\n }\n return util.objectValues(filtered);\n };\n util.objectValues = (obj) => {\n return util.objectKeys(obj).map(function (e) {\n return obj[e];\n });\n };\n util.objectKeys = typeof Object.keys === \"function\" // eslint-disable-line ban/ban\n ? (obj) => Object.keys(obj) // eslint-disable-line ban/ban\n : (object) => {\n const keys = [];\n for (const key in object) {\n if (Object.prototype.hasOwnProperty.call(object, key)) {\n keys.push(key);\n }\n }\n return keys;\n };\n util.find = (arr, checker) => {\n for (const item of arr) {\n if (checker(item))\n return item;\n }\n return undefined;\n };\n util.isInteger = typeof Number.isInteger === \"function\"\n ? (val) => Number.isInteger(val) // eslint-disable-line ban/ban\n : (val) => typeof val === \"number\" && isFinite(val) && Math.floor(val) === val;\n function joinValues(array, separator = \" | \") {\n return array\n .map((val) => (typeof val === \"string\" ? `'${val}'` : val))\n .join(separator);\n }\n util.joinValues = joinValues;\n util.jsonStringifyReplacer = (_, value) => {\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n return value;\n };\n})(util || (util = {}));\nvar objectUtil;\n(function (objectUtil) {\n objectUtil.mergeShapes = (first, second) => {\n return {\n ...first,\n ...second, // second overwrites first\n };\n };\n})(objectUtil || (objectUtil = {}));\nconst ZodParsedType = util.arrayToEnum([\n \"string\",\n \"nan\",\n \"number\",\n \"integer\",\n \"float\",\n \"boolean\",\n \"date\",\n \"bigint\",\n \"symbol\",\n \"function\",\n \"undefined\",\n \"null\",\n \"array\",\n \"object\",\n \"unknown\",\n \"promise\",\n \"void\",\n \"never\",\n \"map\",\n \"set\",\n]);\nconst getParsedType = (data) => {\n const t = typeof data;\n switch (t) {\n case \"undefined\":\n return ZodParsedType.undefined;\n case \"string\":\n return ZodParsedType.string;\n case \"number\":\n return isNaN(data) ? ZodParsedType.nan : ZodParsedType.number;\n case \"boolean\":\n return ZodParsedType.boolean;\n case \"function\":\n return ZodParsedType.function;\n case \"bigint\":\n return ZodParsedType.bigint;\n case \"symbol\":\n return ZodParsedType.symbol;\n case \"object\":\n if (Array.isArray(data)) {\n return ZodParsedType.array;\n }\n if (data === null) {\n return ZodParsedType.null;\n }\n if (data.then &&\n typeof data.then === \"function\" &&\n data.catch &&\n typeof data.catch === \"function\") {\n return ZodParsedType.promise;\n }\n if (typeof Map !== \"undefined\" && data instanceof Map) {\n return ZodParsedType.map;\n }\n if (typeof Set !== \"undefined\" && data instanceof Set) {\n return ZodParsedType.set;\n }\n if (typeof Date !== \"undefined\" && data instanceof Date) {\n return ZodParsedType.date;\n }\n return ZodParsedType.object;\n default:\n return ZodParsedType.unknown;\n }\n};\n\nconst ZodIssueCode = util.arrayToEnum([\n \"invalid_type\",\n \"invalid_literal\",\n \"custom\",\n \"invalid_union\",\n \"invalid_union_discriminator\",\n \"invalid_enum_value\",\n \"unrecognized_keys\",\n \"invalid_arguments\",\n \"invalid_return_type\",\n \"invalid_date\",\n \"invalid_string\",\n \"too_small\",\n \"too_big\",\n \"invalid_intersection_types\",\n \"not_multiple_of\",\n \"not_finite\",\n]);\nconst quotelessJson = (obj) => {\n const json = JSON.stringify(obj, null, 2);\n return json.replace(/\"([^\"]+)\":/g, \"$1:\");\n};\nclass ZodError extends Error {\n constructor(issues) {\n super();\n this.issues = [];\n this.addIssue = (sub) => {\n this.issues = [...this.issues, sub];\n };\n this.addIssues = (subs = []) => {\n this.issues = [...this.issues, ...subs];\n };\n const actualProto = new.target.prototype;\n if (Object.setPrototypeOf) {\n // eslint-disable-next-line ban/ban\n Object.setPrototypeOf(this, actualProto);\n }\n else {\n this.__proto__ = actualProto;\n }\n this.name = \"ZodError\";\n this.issues = issues;\n }\n get errors() {\n return this.issues;\n }\n format(_mapper) {\n const mapper = _mapper ||\n function (issue) {\n return issue.message;\n };\n const fieldErrors = { _errors: [] };\n const processError = (error) => {\n for (const issue of error.issues) {\n if (issue.code === \"invalid_union\") {\n issue.unionErrors.map(processError);\n }\n else if (issue.code === \"invalid_return_type\") {\n processError(issue.returnTypeError);\n }\n else if (issue.code === \"invalid_arguments\") {\n processError(issue.argumentsError);\n }\n else if (issue.path.length === 0) {\n fieldErrors._errors.push(mapper(issue));\n }\n else {\n let curr = fieldErrors;\n let i = 0;\n while (i < issue.path.length) {\n const el = issue.path[i];\n const terminal = i === issue.path.length - 1;\n if (!terminal) {\n curr[el] = curr[el] || { _errors: [] };\n // if (typeof el === \"string\") {\n // curr[el] = curr[el] || { _errors: [] };\n // } else if (typeof el === \"number\") {\n // const errorArray: any = [];\n // errorArray._errors = [];\n // curr[el] = curr[el] || errorArray;\n // }\n }\n else {\n curr[el] = curr[el] || { _errors: [] };\n curr[el]._errors.push(mapper(issue));\n }\n curr = curr[el];\n i++;\n }\n }\n }\n };\n processError(this);\n return fieldErrors;\n }\n static assert(value) {\n if (!(value instanceof ZodError)) {\n throw new Error(`Not a ZodError: ${value}`);\n }\n }\n toString() {\n return this.message;\n }\n get message() {\n return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2);\n }\n get isEmpty() {\n return this.issues.length === 0;\n }\n flatten(mapper = (issue) => issue.message) {\n const fieldErrors = {};\n const formErrors = [];\n for (const sub of this.issues) {\n if (sub.path.length > 0) {\n fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];\n fieldErrors[sub.path[0]].push(mapper(sub));\n }\n else {\n formErrors.push(mapper(sub));\n }\n }\n return { formErrors, fieldErrors };\n }\n get formErrors() {\n return this.flatten();\n }\n}\nZodError.create = (issues) => {\n const error = new ZodError(issues);\n return error;\n};\n\nconst errorMap = (issue, _ctx) => {\n let message;\n switch (issue.code) {\n case ZodIssueCode.invalid_type:\n if (issue.received === ZodParsedType.undefined) {\n message = \"Required\";\n }\n else {\n message = `Expected ${issue.expected}, received ${issue.received}`;\n }\n break;\n case ZodIssueCode.invalid_literal:\n message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`;\n break;\n case ZodIssueCode.unrecognized_keys:\n message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, \", \")}`;\n break;\n case ZodIssueCode.invalid_union:\n message = `Invalid input`;\n break;\n case ZodIssueCode.invalid_union_discriminator:\n message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`;\n break;\n case ZodIssueCode.invalid_enum_value:\n message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`;\n break;\n case ZodIssueCode.invalid_arguments:\n message = `Invalid function arguments`;\n break;\n case ZodIssueCode.invalid_return_type:\n message = `Invalid function return type`;\n break;\n case ZodIssueCode.invalid_date:\n message = `Invalid date`;\n break;\n case ZodIssueCode.invalid_string:\n if (typeof issue.validation === \"object\") {\n if (\"includes\" in issue.validation) {\n message = `Invalid input: must include \"${issue.validation.includes}\"`;\n if (typeof issue.validation.position === \"number\") {\n message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`;\n }\n }\n else if (\"startsWith\" in issue.validation) {\n message = `Invalid input: must start with \"${issue.validation.startsWith}\"`;\n }\n else if (\"endsWith\" in issue.validation) {\n message = `Invalid input: must end with \"${issue.validation.endsWith}\"`;\n }\n else {\n util.assertNever(issue.validation);\n }\n }\n else if (issue.validation !== \"regex\") {\n message = `Invalid ${issue.validation}`;\n }\n else {\n message = \"Invalid\";\n }\n break;\n case ZodIssueCode.too_small:\n if (issue.type === \"array\")\n message = `Array must contain ${issue.exact ? \"exactly\" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;\n else if (issue.type === \"string\")\n message = `String must contain ${issue.exact ? \"exactly\" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;\n else if (issue.type === \"number\")\n message = `Number must be ${issue.exact\n ? `exactly equal to `\n : issue.inclusive\n ? `greater than or equal to `\n : `greater than `}${issue.minimum}`;\n else if (issue.type === \"date\")\n message = `Date must be ${issue.exact\n ? `exactly equal to `\n : issue.inclusive\n ? `greater than or equal to `\n : `greater than `}${new Date(Number(issue.minimum))}`;\n else\n message = \"Invalid input\";\n break;\n case ZodIssueCode.too_big:\n if (issue.type === \"array\")\n message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;\n else if (issue.type === \"string\")\n message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;\n else if (issue.type === \"number\")\n message = `Number must be ${issue.exact\n ? `exactly`\n : issue.inclusive\n ? `less than or equal to`\n : `less than`} ${issue.maximum}`;\n else if (issue.type === \"bigint\")\n message = `BigInt must be ${issue.exact\n ? `exactly`\n : issue.inclusive\n ? `less than or equal to`\n : `less than`} ${issue.maximum}`;\n else if (issue.type === \"date\")\n message = `Date must be ${issue.exact\n ? `exactly`\n : issue.inclusive\n ? `smaller than or equal to`\n : `smaller than`} ${new Date(Number(issue.maximum))}`;\n else\n message = \"Invalid input\";\n break;\n case ZodIssueCode.custom:\n message = `Invalid input`;\n break;\n case ZodIssueCode.invalid_intersection_types:\n message = `Intersection results could not be merged`;\n break;\n case ZodIssueCode.not_multiple_of:\n message = `Number must be a multiple of ${issue.multipleOf}`;\n break;\n case ZodIssueCode.not_finite:\n message = \"Number must be finite\";\n break;\n default:\n message = _ctx.defaultError;\n util.assertNever(issue);\n }\n return { message };\n};\n\nlet overrideErrorMap = errorMap;\nfunction setErrorMap(map) {\n overrideErrorMap = map;\n}\nfunction getErrorMap() {\n return overrideErrorMap;\n}\n\nconst makeIssue = (params) => {\n const { data, path, errorMaps, issueData } = params;\n const fullPath = [...path, ...(issueData.path || [])];\n const fullIssue = {\n ...issueData,\n path: fullPath,\n };\n if (issueData.message !== undefined) {\n return {\n ...issueData,\n path: fullPath,\n message: issueData.message,\n };\n }\n let errorMessage = \"\";\n const maps = errorMaps\n .filter((m) => !!m)\n .slice()\n .reverse();\n for (const map of maps) {\n errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message;\n }\n return {\n ...issueData,\n path: fullPath,\n message: errorMessage,\n };\n};\nconst EMPTY_PATH = [];\nfunction addIssueToContext(ctx, issueData) {\n const overrideMap = getErrorMap();\n const issue = makeIssue({\n issueData: issueData,\n data: ctx.data,\n path: ctx.path,\n errorMaps: [\n ctx.common.contextualErrorMap,\n ctx.schemaErrorMap,\n overrideMap,\n overrideMap === errorMap ? undefined : errorMap, // then global default map\n ].filter((x) => !!x),\n });\n ctx.common.issues.push(issue);\n}\nclass ParseStatus {\n constructor() {\n this.value = \"valid\";\n }\n dirty() {\n if (this.value === \"valid\")\n this.value = \"dirty\";\n }\n abort() {\n if (this.value !== \"aborted\")\n this.value = \"aborted\";\n }\n static mergeArray(status, results) {\n const arrayValue = [];\n for (const s of results) {\n if (s.status === \"aborted\")\n return INVALID;\n if (s.status === \"dirty\")\n status.dirty();\n arrayValue.push(s.value);\n }\n return { status: status.value, value: arrayValue };\n }\n static async mergeObjectAsync(status, pairs) {\n const syncPairs = [];\n for (const pair of pairs) {\n const key = await pair.key;\n const value = await pair.value;\n syncPairs.push({\n key,\n value,\n });\n }\n return ParseStatus.mergeObjectSync(status, syncPairs);\n }\n static mergeObjectSync(status, pairs) {\n const finalObject = {};\n for (const pair of pairs) {\n const { key, value } = pair;\n if (key.status === \"aborted\")\n return INVALID;\n if (value.status === \"aborted\")\n return INVALID;\n if (key.status === \"dirty\")\n status.dirty();\n if (value.status === \"dirty\")\n status.dirty();\n if (key.value !== \"__proto__\" &&\n (typeof value.value !== \"undefined\" || pair.alwaysSet)) {\n finalObject[key.value] = value.value;\n }\n }\n return { status: status.value, value: finalObject };\n }\n}\nconst INVALID = Object.freeze({\n status: \"aborted\",\n});\nconst DIRTY = (value) => ({ status: \"dirty\", value });\nconst OK = (value) => ({ status: \"valid\", value });\nconst isAborted = (x) => x.status === \"aborted\";\nconst isDirty = (x) => x.status === \"dirty\";\nconst isValid = (x) => x.status === \"valid\";\nconst isAsync = (x) => typeof Promise !== \"undefined\" && x instanceof Promise;\n\n/******************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n\r\nfunction __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nfunction __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n\r\ntypeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\r\n var e = new Error(message);\r\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\r\n};\n\nvar errorUtil;\n(function (errorUtil) {\n errorUtil.errToObj = (message) => typeof message === \"string\" ? { message } : message || {};\n errorUtil.toString = (message) => typeof message === \"string\" ? message : message === null || message === void 0 ? void 0 : message.message;\n})(errorUtil || (errorUtil = {}));\n\nvar _ZodEnum_cache, _ZodNativeEnum_cache;\nclass ParseInputLazyPath {\n constructor(parent, value, path, key) {\n this._cachedPath = [];\n this.parent = parent;\n this.data = value;\n this._path = path;\n this._key = key;\n }\n get path() {\n if (!this._cachedPath.length) {\n if (this._key instanceof Array) {\n this._cachedPath.push(...this._path, ...this._key);\n }\n else {\n this._cachedPath.push(...this._path, this._key);\n }\n }\n return this._cachedPath;\n }\n}\nconst handleResult = (ctx, result) => {\n if (isValid(result)) {\n return { success: true, data: result.value };\n }\n else {\n if (!ctx.common.issues.length) {\n throw new Error(\"Validation failed but no issues detected.\");\n }\n return {\n success: false,\n get error() {\n if (this._error)\n return this._error;\n const error = new ZodError(ctx.common.issues);\n this._error = error;\n return this._error;\n },\n };\n }\n};\nfunction processCreateParams(params) {\n if (!params)\n return {};\n const { errorMap, invalid_type_error, required_error, description } = params;\n if (errorMap && (invalid_type_error || required_error)) {\n throw new Error(`Can't use \"invalid_type_error\" or \"required_error\" in conjunction with custom error map.`);\n }\n if (errorMap)\n return { errorMap: errorMap, description };\n const customMap = (iss, ctx) => {\n var _a, _b;\n const { message } = params;\n if (iss.code === \"invalid_enum_value\") {\n return { message: message !== null && message !== void 0 ? message : ctx.defaultError };\n }\n if (typeof ctx.data === \"undefined\") {\n return { message: (_a = message !== null && message !== void 0 ? message : required_error) !== null && _a !== void 0 ? _a : ctx.defaultError };\n }\n if (iss.code !== \"invalid_type\")\n return { message: ctx.defaultError };\n return { message: (_b = message !== null && message !== void 0 ? message : invalid_type_error) !== null && _b !== void 0 ? _b : ctx.defaultError };\n };\n return { errorMap: customMap, description };\n}\nclass ZodType {\n constructor(def) {\n /** Alias of safeParseAsync */\n this.spa = this.safeParseAsync;\n this._def = def;\n this.parse = this.parse.bind(this);\n this.safeParse = this.safeParse.bind(this);\n this.parseAsync = this.parseAsync.bind(this);\n this.safeParseAsync = this.safeParseAsync.bind(this);\n this.spa = this.spa.bind(this);\n this.refine = this.refine.bind(this);\n this.refinement = this.refinement.bind(this);\n this.superRefine = this.superRefine.bind(this);\n this.optional = this.optional.bind(this);\n this.nullable = this.nullable.bind(this);\n this.nullish = this.nullish.bind(this);\n this.array = this.array.bind(this);\n this.promise = this.promise.bind(this);\n this.or = this.or.bind(this);\n this.and = this.and.bind(this);\n this.transform = this.transform.bind(this);\n this.brand = this.brand.bind(this);\n this.default = this.default.bind(this);\n this.catch = this.catch.bind(this);\n this.describe = this.describe.bind(this);\n this.pipe = this.pipe.bind(this);\n this.readonly = this.readonly.bind(this);\n this.isNullable = this.isNullable.bind(this);\n this.isOptional = this.isOptional.bind(this);\n }\n get description() {\n return this._def.description;\n }\n _getType(input) {\n return getParsedType(input.data);\n }\n _getOrReturnCtx(input, ctx) {\n return (ctx || {\n common: input.parent.common,\n data: input.data,\n parsedType: getParsedType(input.data),\n schemaErrorMap: this._def.errorMap,\n path: input.path,\n parent: input.parent,\n });\n }\n _processInputParams(input) {\n return {\n status: new ParseStatus(),\n ctx: {\n common: input.parent.common,\n data: input.data,\n parsedType: getParsedType(input.data),\n schemaErrorMap: this._def.errorMap,\n path: input.path,\n parent: input.parent,\n },\n };\n }\n _parseSync(input) {\n const result = this._parse(input);\n if (isAsync(result)) {\n throw new Error(\"Synchronous parse encountered promise.\");\n }\n return result;\n }\n _parseAsync(input) {\n const result = this._parse(input);\n return Promise.resolve(result);\n }\n parse(data, params) {\n const result = this.safeParse(data, params);\n if (result.success)\n return result.data;\n throw result.error;\n }\n safeParse(data, params) {\n var _a;\n const ctx = {\n common: {\n issues: [],\n async: (_a = params === null || params === void 0 ? void 0 : params.async) !== null && _a !== void 0 ? _a : false,\n contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap,\n },\n path: (params === null || params === void 0 ? void 0 : params.path) || [],\n schemaErrorMap: this._def.errorMap,\n parent: null,\n data,\n parsedType: getParsedType(data),\n };\n const result = this._parseSync({ data, path: ctx.path, parent: ctx });\n return handleResult(ctx, result);\n }\n async parseAsync(data, params) {\n const result = await this.safeParseAsync(data, params);\n if (result.success)\n return result.data;\n throw result.error;\n }\n async safeParseAsync(data, params) {\n const ctx = {\n common: {\n issues: [],\n contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap,\n async: true,\n },\n path: (params === null || params === void 0 ? void 0 : params.path) || [],\n schemaErrorMap: this._def.errorMap,\n parent: null,\n data,\n parsedType: getParsedType(data),\n };\n const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx });\n const result = await (isAsync(maybeAsyncResult)\n ? maybeAsyncResult\n : Promise.resolve(maybeAsyncResult));\n return handleResult(ctx, result);\n }\n refine(check, message) {\n const getIssueProperties = (val) => {\n if (typeof message === \"string\" || typeof message === \"undefined\") {\n return { message };\n }\n else if (typeof message === \"function\") {\n return message(val);\n }\n else {\n return message;\n }\n };\n return this._refinement((val, ctx) => {\n const result = check(val);\n const setError = () => ctx.addIssue({\n code: ZodIssueCode.custom,\n ...getIssueProperties(val),\n });\n if (typeof Promise !== \"undefined\" && result instanceof Promise) {\n return result.then((data) => {\n if (!data) {\n setError();\n return false;\n }\n else {\n return true;\n }\n });\n }\n if (!result) {\n setError();\n return false;\n }\n else {\n return true;\n }\n });\n }\n refinement(check, refinementData) {\n return this._refinement((val, ctx) => {\n if (!check(val)) {\n ctx.addIssue(typeof refinementData === \"function\"\n ? refinementData(val, ctx)\n : refinementData);\n return false;\n }\n else {\n return true;\n }\n });\n }\n _refinement(refinement) {\n return new ZodEffects({\n schema: this,\n typeName: ZodFirstPartyTypeKind.ZodEffects,\n effect: { type: \"refinement\", refinement },\n });\n }\n superRefine(refinement) {\n return this._refinement(refinement);\n }\n optional() {\n return ZodOptional.create(this, this._def);\n }\n nullable() {\n return ZodNullable.create(this, this._def);\n }\n nullish() {\n return this.nullable().optional();\n }\n array() {\n return ZodArray.create(this, this._def);\n }\n promise() {\n return ZodPromise.create(this, this._def);\n }\n or(option) {\n return ZodUnion.create([this, option], this._def);\n }\n and(incoming) {\n return ZodIntersection.create(this, incoming, this._def);\n }\n transform(transform) {\n return new ZodEffects({\n ...processCreateParams(this._def),\n schema: this,\n typeName: ZodFirstPartyTypeKind.ZodEffects,\n effect: { type: \"transform\", transform },\n });\n }\n default(def) {\n const defaultValueFunc = typeof def === \"function\" ? def : () => def;\n return new ZodDefault({\n ...processCreateParams(this._def),\n innerType: this,\n defaultValue: defaultValueFunc,\n typeName: ZodFirstPartyTypeKind.ZodDefault,\n });\n }\n brand() {\n return new ZodBranded({\n typeName: ZodFirstPartyTypeKind.ZodBranded,\n type: this,\n ...processCreateParams(this._def),\n });\n }\n catch(def) {\n const catchValueFunc = typeof def === \"function\" ? def : () => def;\n return new ZodCatch({\n ...processCreateParams(this._def),\n innerType: this,\n catchValue: catchValueFunc,\n typeName: ZodFirstPartyTypeKind.ZodCatch,\n });\n }\n describe(description) {\n const This = this.constructor;\n return new This({\n ...this._def,\n description,\n });\n }\n pipe(target) {\n return ZodPipeline.create(this, target);\n }\n readonly() {\n return ZodReadonly.create(this);\n }\n isOptional() {\n return this.safeParse(undefined).success;\n }\n isNullable() {\n return this.safeParse(null).success;\n }\n}\nconst cuidRegex = /^c[^\\s-]{8,}$/i;\nconst cuid2Regex = /^[0-9a-z]+$/;\nconst ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/;\n// const uuidRegex =\n// /^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i;\nconst uuidRegex = /^[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12}$/i;\nconst nanoidRegex = /^[a-z0-9_-]{21}$/i;\nconst durationRegex = /^[-+]?P(?!$)(?:(?:[-+]?\\d+Y)|(?:[-+]?\\d+[.,]\\d+Y$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:(?:[-+]?\\d+W)|(?:[-+]?\\d+[.,]\\d+W$))?(?:(?:[-+]?\\d+D)|(?:[-+]?\\d+[.,]\\d+D$))?(?:T(?=[\\d+-])(?:(?:[-+]?\\d+H)|(?:[-+]?\\d+[.,]\\d+H$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:[-+]?\\d+(?:[.,]\\d+)?S)?)??$/;\n// from https://stackoverflow.com/a/46181/1550155\n// old version: too slow, didn't support unicode\n// const emailRegex = /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$/i;\n//old email regex\n// const emailRegex = /^(([^<>()[\\].,;:\\s@\"]+(\\.[^<>()[\\].,;:\\s@\"]+)*)|(\".+\"))@((?!-)([^<>()[\\].,;:\\s@\"]+\\.)+[^<>()[\\].,;:\\s@\"]{1,})[^-<>()[\\].,;:\\s@\"]$/i;\n// eslint-disable-next-line\n// const emailRegex =\n// /^(([^<>()[\\]\\\\.,;:\\s@\\\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@((\\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\\])|(\\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\\.[A-Za-z]{2,})+))$/;\n// const emailRegex =\n// /^[a-zA-Z0-9\\.\\!\\#\\$\\%\\&\\'\\*\\+\\/\\=\\?\\^\\_\\`\\{\\|\\}\\~\\-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n// const emailRegex =\n// /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$/i;\nconst emailRegex = /^(?!\\.)(?!.*\\.\\.)([A-Z0-9_'+\\-\\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\\-]*\\.)+[A-Z]{2,}$/i;\n// const emailRegex =\n// /^[a-z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-z0-9-]+(?:\\.[a-z0-9\\-]+)*$/i;\n// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression\nconst _emojiRegex = `^(\\\\p{Extended_Pictographic}|\\\\p{Emoji_Component})+$`;\nlet emojiRegex;\n// faster, simpler, safer\nconst ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;\nconst ipv6Regex = /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;\n// https://stackoverflow.com/questions/7860392/determine-if-string-is-in-base64-using-javascript\nconst base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;\n// simple\n// const dateRegexSource = `\\\\d{4}-\\\\d{2}-\\\\d{2}`;\n// no leap year validation\n// const dateRegexSource = `\\\\d{4}-((0[13578]|10|12)-31|(0[13-9]|1[0-2])-30|(0[1-9]|1[0-2])-(0[1-9]|1\\\\d|2\\\\d))`;\n// with leap year validation\nconst dateRegexSource = `((\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\\\d|30)|(02)-(0[1-9]|1\\\\d|2[0-8])))`;\nconst dateRegex = new RegExp(`^${dateRegexSource}$`);\nfunction timeRegexSource(args) {\n // let regex = `\\\\d{2}:\\\\d{2}:\\\\d{2}`;\n let regex = `([01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d`;\n if (args.precision) {\n regex = `${regex}\\\\.\\\\d{${args.precision}}`;\n }\n else if (args.precision == null) {\n regex = `${regex}(\\\\.\\\\d+)?`;\n }\n return regex;\n}\nfunction timeRegex(args) {\n return new RegExp(`^${timeRegexSource(args)}$`);\n}\n// Adapted from https://stackoverflow.com/a/3143231\nfunction datetimeRegex(args) {\n let regex = `${dateRegexSource}T${timeRegexSource(args)}`;\n const opts = [];\n opts.push(args.local ? `Z?` : `Z`);\n if (args.offset)\n opts.push(`([+-]\\\\d{2}:?\\\\d{2})`);\n regex = `${regex}(${opts.join(\"|\")})`;\n return new RegExp(`^${regex}$`);\n}\nfunction isValidIP(ip, version) {\n if ((version === \"v4\" || !version) && ipv4Regex.test(ip)) {\n return true;\n }\n if ((version === \"v6\" || !version) && ipv6Regex.test(ip)) {\n return true;\n }\n return false;\n}\nclass ZodString extends ZodType {\n _parse(input) {\n if (this._def.coerce) {\n input.data = String(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.string) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.string,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const status = new ParseStatus();\n let ctx = undefined;\n for (const check of this._def.checks) {\n if (check.kind === \"min\") {\n if (input.data.length < check.value) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: check.value,\n type: \"string\",\n inclusive: true,\n exact: false,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"max\") {\n if (input.data.length > check.value) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: check.value,\n type: \"string\",\n inclusive: true,\n exact: false,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"length\") {\n const tooBig = input.data.length > check.value;\n const tooSmall = input.data.length < check.value;\n if (tooBig || tooSmall) {\n ctx = this._getOrReturnCtx(input, ctx);\n if (tooBig) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: check.value,\n type: \"string\",\n inclusive: true,\n exact: true,\n message: check.message,\n });\n }\n else if (tooSmall) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: check.value,\n type: \"string\",\n inclusive: true,\n exact: true,\n message: check.message,\n });\n }\n status.dirty();\n }\n }\n else if (check.kind === \"email\") {\n if (!emailRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"email\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"emoji\") {\n if (!emojiRegex) {\n emojiRegex = new RegExp(_emojiRegex, \"u\");\n }\n if (!emojiRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"emoji\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"uuid\") {\n if (!uuidRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"uuid\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"nanoid\") {\n if (!nanoidRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"nanoid\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"cuid\") {\n if (!cuidRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"cuid\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"cuid2\") {\n if (!cuid2Regex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"cuid2\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"ulid\") {\n if (!ulidRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"ulid\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"url\") {\n try {\n new URL(input.data);\n }\n catch (_a) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"url\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"regex\") {\n check.regex.lastIndex = 0;\n const testResult = check.regex.test(input.data);\n if (!testResult) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"regex\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"trim\") {\n input.data = input.data.trim();\n }\n else if (check.kind === \"includes\") {\n if (!input.data.includes(check.value, check.position)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: { includes: check.value, position: check.position },\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"toLowerCase\") {\n input.data = input.data.toLowerCase();\n }\n else if (check.kind === \"toUpperCase\") {\n input.data = input.data.toUpperCase();\n }\n else if (check.kind === \"startsWith\") {\n if (!input.data.startsWith(check.value)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: { startsWith: check.value },\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"endsWith\") {\n if (!input.data.endsWith(check.value)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: { endsWith: check.value },\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"datetime\") {\n const regex = datetimeRegex(check);\n if (!regex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: \"datetime\",\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"date\") {\n const regex = dateRegex;\n if (!regex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: \"date\",\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"time\") {\n const regex = timeRegex(check);\n if (!regex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_string,\n validation: \"time\",\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"duration\") {\n if (!durationRegex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"duration\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"ip\") {\n if (!isValidIP(input.data, check.version)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"ip\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"base64\") {\n if (!base64Regex.test(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n validation: \"base64\",\n code: ZodIssueCode.invalid_string,\n message: check.message,\n });\n status.dirty();\n }\n }\n else {\n util.assertNever(check);\n }\n }\n return { status: status.value, value: input.data };\n }\n _regex(regex, validation, message) {\n return this.refinement((data) => regex.test(data), {\n validation,\n code: ZodIssueCode.invalid_string,\n ...errorUtil.errToObj(message),\n });\n }\n _addCheck(check) {\n return new ZodString({\n ...this._def,\n checks: [...this._def.checks, check],\n });\n }\n email(message) {\n return this._addCheck({ kind: \"email\", ...errorUtil.errToObj(message) });\n }\n url(message) {\n return this._addCheck({ kind: \"url\", ...errorUtil.errToObj(message) });\n }\n emoji(message) {\n return this._addCheck({ kind: \"emoji\", ...errorUtil.errToObj(message) });\n }\n uuid(message) {\n return this._addCheck({ kind: \"uuid\", ...errorUtil.errToObj(message) });\n }\n nanoid(message) {\n return this._addCheck({ kind: \"nanoid\", ...errorUtil.errToObj(message) });\n }\n cuid(message) {\n return this._addCheck({ kind: \"cuid\", ...errorUtil.errToObj(message) });\n }\n cuid2(message) {\n return this._addCheck({ kind: \"cuid2\", ...errorUtil.errToObj(message) });\n }\n ulid(message) {\n return this._addCheck({ kind: \"ulid\", ...errorUtil.errToObj(message) });\n }\n base64(message) {\n return this._addCheck({ kind: \"base64\", ...errorUtil.errToObj(message) });\n }\n ip(options) {\n return this._addCheck({ kind: \"ip\", ...errorUtil.errToObj(options) });\n }\n datetime(options) {\n var _a, _b;\n if (typeof options === \"string\") {\n return this._addCheck({\n kind: \"datetime\",\n precision: null,\n offset: false,\n local: false,\n message: options,\n });\n }\n return this._addCheck({\n kind: \"datetime\",\n precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === \"undefined\" ? null : options === null || options === void 0 ? void 0 : options.precision,\n offset: (_a = options === null || options === void 0 ? void 0 : options.offset) !== null && _a !== void 0 ? _a : false,\n local: (_b = options === null || options === void 0 ? void 0 : options.local) !== null && _b !== void 0 ? _b : false,\n ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message),\n });\n }\n date(message) {\n return this._addCheck({ kind: \"date\", message });\n }\n time(options) {\n if (typeof options === \"string\") {\n return this._addCheck({\n kind: \"time\",\n precision: null,\n message: options,\n });\n }\n return this._addCheck({\n kind: \"time\",\n precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === \"undefined\" ? null : options === null || options === void 0 ? void 0 : options.precision,\n ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message),\n });\n }\n duration(message) {\n return this._addCheck({ kind: \"duration\", ...errorUtil.errToObj(message) });\n }\n regex(regex, message) {\n return this._addCheck({\n kind: \"regex\",\n regex: regex,\n ...errorUtil.errToObj(message),\n });\n }\n includes(value, options) {\n return this._addCheck({\n kind: \"includes\",\n value: value,\n position: options === null || options === void 0 ? void 0 : options.position,\n ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message),\n });\n }\n startsWith(value, message) {\n return this._addCheck({\n kind: \"startsWith\",\n value: value,\n ...errorUtil.errToObj(message),\n });\n }\n endsWith(value, message) {\n return this._addCheck({\n kind: \"endsWith\",\n value: value,\n ...errorUtil.errToObj(message),\n });\n }\n min(minLength, message) {\n return this._addCheck({\n kind: \"min\",\n value: minLength,\n ...errorUtil.errToObj(message),\n });\n }\n max(maxLength, message) {\n return this._addCheck({\n kind: \"max\",\n value: maxLength,\n ...errorUtil.errToObj(message),\n });\n }\n length(len, message) {\n return this._addCheck({\n kind: \"length\",\n value: len,\n ...errorUtil.errToObj(message),\n });\n }\n /**\n * @deprecated Use z.string().min(1) instead.\n * @see {@link ZodString.min}\n */\n nonempty(message) {\n return this.min(1, errorUtil.errToObj(message));\n }\n trim() {\n return new ZodString({\n ...this._def,\n checks: [...this._def.checks, { kind: \"trim\" }],\n });\n }\n toLowerCase() {\n return new ZodString({\n ...this._def,\n checks: [...this._def.checks, { kind: \"toLowerCase\" }],\n });\n }\n toUpperCase() {\n return new ZodString({\n ...this._def,\n checks: [...this._def.checks, { kind: \"toUpperCase\" }],\n });\n }\n get isDatetime() {\n return !!this._def.checks.find((ch) => ch.kind === \"datetime\");\n }\n get isDate() {\n return !!this._def.checks.find((ch) => ch.kind === \"date\");\n }\n get isTime() {\n return !!this._def.checks.find((ch) => ch.kind === \"time\");\n }\n get isDuration() {\n return !!this._def.checks.find((ch) => ch.kind === \"duration\");\n }\n get isEmail() {\n return !!this._def.checks.find((ch) => ch.kind === \"email\");\n }\n get isURL() {\n return !!this._def.checks.find((ch) => ch.kind === \"url\");\n }\n get isEmoji() {\n return !!this._def.checks.find((ch) => ch.kind === \"emoji\");\n }\n get isUUID() {\n return !!this._def.checks.find((ch) => ch.kind === \"uuid\");\n }\n get isNANOID() {\n return !!this._def.checks.find((ch) => ch.kind === \"nanoid\");\n }\n get isCUID() {\n return !!this._def.checks.find((ch) => ch.kind === \"cuid\");\n }\n get isCUID2() {\n return !!this._def.checks.find((ch) => ch.kind === \"cuid2\");\n }\n get isULID() {\n return !!this._def.checks.find((ch) => ch.kind === \"ulid\");\n }\n get isIP() {\n return !!this._def.checks.find((ch) => ch.kind === \"ip\");\n }\n get isBase64() {\n return !!this._def.checks.find((ch) => ch.kind === \"base64\");\n }\n get minLength() {\n let min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n }\n return min;\n }\n get maxLength() {\n let max = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return max;\n }\n}\nZodString.create = (params) => {\n var _a;\n return new ZodString({\n checks: [],\n typeName: ZodFirstPartyTypeKind.ZodString,\n coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false,\n ...processCreateParams(params),\n });\n};\n// https://stackoverflow.com/questions/3966484/why-does-modulus-operator-return-fractional-number-in-javascript/31711034#31711034\nfunction floatSafeRemainder(val, step) {\n const valDecCount = (val.toString().split(\".\")[1] || \"\").length;\n const stepDecCount = (step.toString().split(\".\")[1] || \"\").length;\n const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;\n const valInt = parseInt(val.toFixed(decCount).replace(\".\", \"\"));\n const stepInt = parseInt(step.toFixed(decCount).replace(\".\", \"\"));\n return (valInt % stepInt) / Math.pow(10, decCount);\n}\nclass ZodNumber extends ZodType {\n constructor() {\n super(...arguments);\n this.min = this.gte;\n this.max = this.lte;\n this.step = this.multipleOf;\n }\n _parse(input) {\n if (this._def.coerce) {\n input.data = Number(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.number) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.number,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n let ctx = undefined;\n const status = new ParseStatus();\n for (const check of this._def.checks) {\n if (check.kind === \"int\") {\n if (!util.isInteger(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: \"integer\",\n received: \"float\",\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"min\") {\n const tooSmall = check.inclusive\n ? input.data < check.value\n : input.data <= check.value;\n if (tooSmall) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: check.value,\n type: \"number\",\n inclusive: check.inclusive,\n exact: false,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"max\") {\n const tooBig = check.inclusive\n ? input.data > check.value\n : input.data >= check.value;\n if (tooBig) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: check.value,\n type: \"number\",\n inclusive: check.inclusive,\n exact: false,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"multipleOf\") {\n if (floatSafeRemainder(input.data, check.value) !== 0) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.not_multiple_of,\n multipleOf: check.value,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"finite\") {\n if (!Number.isFinite(input.data)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.not_finite,\n message: check.message,\n });\n status.dirty();\n }\n }\n else {\n util.assertNever(check);\n }\n }\n return { status: status.value, value: input.data };\n }\n gte(value, message) {\n return this.setLimit(\"min\", value, true, errorUtil.toString(message));\n }\n gt(value, message) {\n return this.setLimit(\"min\", value, false, errorUtil.toString(message));\n }\n lte(value, message) {\n return this.setLimit(\"max\", value, true, errorUtil.toString(message));\n }\n lt(value, message) {\n return this.setLimit(\"max\", value, false, errorUtil.toString(message));\n }\n setLimit(kind, value, inclusive, message) {\n return new ZodNumber({\n ...this._def,\n checks: [\n ...this._def.checks,\n {\n kind,\n value,\n inclusive,\n message: errorUtil.toString(message),\n },\n ],\n });\n }\n _addCheck(check) {\n return new ZodNumber({\n ...this._def,\n checks: [...this._def.checks, check],\n });\n }\n int(message) {\n return this._addCheck({\n kind: \"int\",\n message: errorUtil.toString(message),\n });\n }\n positive(message) {\n return this._addCheck({\n kind: \"min\",\n value: 0,\n inclusive: false,\n message: errorUtil.toString(message),\n });\n }\n negative(message) {\n return this._addCheck({\n kind: \"max\",\n value: 0,\n inclusive: false,\n message: errorUtil.toString(message),\n });\n }\n nonpositive(message) {\n return this._addCheck({\n kind: \"max\",\n value: 0,\n inclusive: true,\n message: errorUtil.toString(message),\n });\n }\n nonnegative(message) {\n return this._addCheck({\n kind: \"min\",\n value: 0,\n inclusive: true,\n message: errorUtil.toString(message),\n });\n }\n multipleOf(value, message) {\n return this._addCheck({\n kind: \"multipleOf\",\n value: value,\n message: errorUtil.toString(message),\n });\n }\n finite(message) {\n return this._addCheck({\n kind: \"finite\",\n message: errorUtil.toString(message),\n });\n }\n safe(message) {\n return this._addCheck({\n kind: \"min\",\n inclusive: true,\n value: Number.MIN_SAFE_INTEGER,\n message: errorUtil.toString(message),\n })._addCheck({\n kind: \"max\",\n inclusive: true,\n value: Number.MAX_SAFE_INTEGER,\n message: errorUtil.toString(message),\n });\n }\n get minValue() {\n let min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n }\n return min;\n }\n get maxValue() {\n let max = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return max;\n }\n get isInt() {\n return !!this._def.checks.find((ch) => ch.kind === \"int\" ||\n (ch.kind === \"multipleOf\" && util.isInteger(ch.value)));\n }\n get isFinite() {\n let max = null, min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"finite\" ||\n ch.kind === \"int\" ||\n ch.kind === \"multipleOf\") {\n return true;\n }\n else if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n else if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return Number.isFinite(min) && Number.isFinite(max);\n }\n}\nZodNumber.create = (params) => {\n return new ZodNumber({\n checks: [],\n typeName: ZodFirstPartyTypeKind.ZodNumber,\n coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,\n ...processCreateParams(params),\n });\n};\nclass ZodBigInt extends ZodType {\n constructor() {\n super(...arguments);\n this.min = this.gte;\n this.max = this.lte;\n }\n _parse(input) {\n if (this._def.coerce) {\n input.data = BigInt(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.bigint) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.bigint,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n let ctx = undefined;\n const status = new ParseStatus();\n for (const check of this._def.checks) {\n if (check.kind === \"min\") {\n const tooSmall = check.inclusive\n ? input.data < check.value\n : input.data <= check.value;\n if (tooSmall) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n type: \"bigint\",\n minimum: check.value,\n inclusive: check.inclusive,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"max\") {\n const tooBig = check.inclusive\n ? input.data > check.value\n : input.data >= check.value;\n if (tooBig) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n type: \"bigint\",\n maximum: check.value,\n inclusive: check.inclusive,\n message: check.message,\n });\n status.dirty();\n }\n }\n else if (check.kind === \"multipleOf\") {\n if (input.data % check.value !== BigInt(0)) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.not_multiple_of,\n multipleOf: check.value,\n message: check.message,\n });\n status.dirty();\n }\n }\n else {\n util.assertNever(check);\n }\n }\n return { status: status.value, value: input.data };\n }\n gte(value, message) {\n return this.setLimit(\"min\", value, true, errorUtil.toString(message));\n }\n gt(value, message) {\n return this.setLimit(\"min\", value, false, errorUtil.toString(message));\n }\n lte(value, message) {\n return this.setLimit(\"max\", value, true, errorUtil.toString(message));\n }\n lt(value, message) {\n return this.setLimit(\"max\", value, false, errorUtil.toString(message));\n }\n setLimit(kind, value, inclusive, message) {\n return new ZodBigInt({\n ...this._def,\n checks: [\n ...this._def.checks,\n {\n kind,\n value,\n inclusive,\n message: errorUtil.toString(message),\n },\n ],\n });\n }\n _addCheck(check) {\n return new ZodBigInt({\n ...this._def,\n checks: [...this._def.checks, check],\n });\n }\n positive(message) {\n return this._addCheck({\n kind: \"min\",\n value: BigInt(0),\n inclusive: false,\n message: errorUtil.toString(message),\n });\n }\n negative(message) {\n return this._addCheck({\n kind: \"max\",\n value: BigInt(0),\n inclusive: false,\n message: errorUtil.toString(message),\n });\n }\n nonpositive(message) {\n return this._addCheck({\n kind: \"max\",\n value: BigInt(0),\n inclusive: true,\n message: errorUtil.toString(message),\n });\n }\n nonnegative(message) {\n return this._addCheck({\n kind: \"min\",\n value: BigInt(0),\n inclusive: true,\n message: errorUtil.toString(message),\n });\n }\n multipleOf(value, message) {\n return this._addCheck({\n kind: \"multipleOf\",\n value,\n message: errorUtil.toString(message),\n });\n }\n get minValue() {\n let min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n }\n return min;\n }\n get maxValue() {\n let max = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return max;\n }\n}\nZodBigInt.create = (params) => {\n var _a;\n return new ZodBigInt({\n checks: [],\n typeName: ZodFirstPartyTypeKind.ZodBigInt,\n coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false,\n ...processCreateParams(params),\n });\n};\nclass ZodBoolean extends ZodType {\n _parse(input) {\n if (this._def.coerce) {\n input.data = Boolean(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.boolean) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.boolean,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodBoolean.create = (params) => {\n return new ZodBoolean({\n typeName: ZodFirstPartyTypeKind.ZodBoolean,\n coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,\n ...processCreateParams(params),\n });\n};\nclass ZodDate extends ZodType {\n _parse(input) {\n if (this._def.coerce) {\n input.data = new Date(input.data);\n }\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.date) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.date,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n if (isNaN(input.data.getTime())) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_date,\n });\n return INVALID;\n }\n const status = new ParseStatus();\n let ctx = undefined;\n for (const check of this._def.checks) {\n if (check.kind === \"min\") {\n if (input.data.getTime() < check.value) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n message: check.message,\n inclusive: true,\n exact: false,\n minimum: check.value,\n type: \"date\",\n });\n status.dirty();\n }\n }\n else if (check.kind === \"max\") {\n if (input.data.getTime() > check.value) {\n ctx = this._getOrReturnCtx(input, ctx);\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n message: check.message,\n inclusive: true,\n exact: false,\n maximum: check.value,\n type: \"date\",\n });\n status.dirty();\n }\n }\n else {\n util.assertNever(check);\n }\n }\n return {\n status: status.value,\n value: new Date(input.data.getTime()),\n };\n }\n _addCheck(check) {\n return new ZodDate({\n ...this._def,\n checks: [...this._def.checks, check],\n });\n }\n min(minDate, message) {\n return this._addCheck({\n kind: \"min\",\n value: minDate.getTime(),\n message: errorUtil.toString(message),\n });\n }\n max(maxDate, message) {\n return this._addCheck({\n kind: \"max\",\n value: maxDate.getTime(),\n message: errorUtil.toString(message),\n });\n }\n get minDate() {\n let min = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"min\") {\n if (min === null || ch.value > min)\n min = ch.value;\n }\n }\n return min != null ? new Date(min) : null;\n }\n get maxDate() {\n let max = null;\n for (const ch of this._def.checks) {\n if (ch.kind === \"max\") {\n if (max === null || ch.value < max)\n max = ch.value;\n }\n }\n return max != null ? new Date(max) : null;\n }\n}\nZodDate.create = (params) => {\n return new ZodDate({\n checks: [],\n coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false,\n typeName: ZodFirstPartyTypeKind.ZodDate,\n ...processCreateParams(params),\n });\n};\nclass ZodSymbol extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.symbol) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.symbol,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodSymbol.create = (params) => {\n return new ZodSymbol({\n typeName: ZodFirstPartyTypeKind.ZodSymbol,\n ...processCreateParams(params),\n });\n};\nclass ZodUndefined extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.undefined) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.undefined,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodUndefined.create = (params) => {\n return new ZodUndefined({\n typeName: ZodFirstPartyTypeKind.ZodUndefined,\n ...processCreateParams(params),\n });\n};\nclass ZodNull extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.null) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.null,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodNull.create = (params) => {\n return new ZodNull({\n typeName: ZodFirstPartyTypeKind.ZodNull,\n ...processCreateParams(params),\n });\n};\nclass ZodAny extends ZodType {\n constructor() {\n super(...arguments);\n // to prevent instances of other classes from extending ZodAny. this causes issues with catchall in ZodObject.\n this._any = true;\n }\n _parse(input) {\n return OK(input.data);\n }\n}\nZodAny.create = (params) => {\n return new ZodAny({\n typeName: ZodFirstPartyTypeKind.ZodAny,\n ...processCreateParams(params),\n });\n};\nclass ZodUnknown extends ZodType {\n constructor() {\n super(...arguments);\n // required\n this._unknown = true;\n }\n _parse(input) {\n return OK(input.data);\n }\n}\nZodUnknown.create = (params) => {\n return new ZodUnknown({\n typeName: ZodFirstPartyTypeKind.ZodUnknown,\n ...processCreateParams(params),\n });\n};\nclass ZodNever extends ZodType {\n _parse(input) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.never,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n}\nZodNever.create = (params) => {\n return new ZodNever({\n typeName: ZodFirstPartyTypeKind.ZodNever,\n ...processCreateParams(params),\n });\n};\nclass ZodVoid extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.undefined) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.void,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n}\nZodVoid.create = (params) => {\n return new ZodVoid({\n typeName: ZodFirstPartyTypeKind.ZodVoid,\n ...processCreateParams(params),\n });\n};\nclass ZodArray extends ZodType {\n _parse(input) {\n const { ctx, status } = this._processInputParams(input);\n const def = this._def;\n if (ctx.parsedType !== ZodParsedType.array) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.array,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n if (def.exactLength !== null) {\n const tooBig = ctx.data.length > def.exactLength.value;\n const tooSmall = ctx.data.length < def.exactLength.value;\n if (tooBig || tooSmall) {\n addIssueToContext(ctx, {\n code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small,\n minimum: (tooSmall ? def.exactLength.value : undefined),\n maximum: (tooBig ? def.exactLength.value : undefined),\n type: \"array\",\n inclusive: true,\n exact: true,\n message: def.exactLength.message,\n });\n status.dirty();\n }\n }\n if (def.minLength !== null) {\n if (ctx.data.length < def.minLength.value) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: def.minLength.value,\n type: \"array\",\n inclusive: true,\n exact: false,\n message: def.minLength.message,\n });\n status.dirty();\n }\n }\n if (def.maxLength !== null) {\n if (ctx.data.length > def.maxLength.value) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: def.maxLength.value,\n type: \"array\",\n inclusive: true,\n exact: false,\n message: def.maxLength.message,\n });\n status.dirty();\n }\n }\n if (ctx.common.async) {\n return Promise.all([...ctx.data].map((item, i) => {\n return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i));\n })).then((result) => {\n return ParseStatus.mergeArray(status, result);\n });\n }\n const result = [...ctx.data].map((item, i) => {\n return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i));\n });\n return ParseStatus.mergeArray(status, result);\n }\n get element() {\n return this._def.type;\n }\n min(minLength, message) {\n return new ZodArray({\n ...this._def,\n minLength: { value: minLength, message: errorUtil.toString(message) },\n });\n }\n max(maxLength, message) {\n return new ZodArray({\n ...this._def,\n maxLength: { value: maxLength, message: errorUtil.toString(message) },\n });\n }\n length(len, message) {\n return new ZodArray({\n ...this._def,\n exactLength: { value: len, message: errorUtil.toString(message) },\n });\n }\n nonempty(message) {\n return this.min(1, message);\n }\n}\nZodArray.create = (schema, params) => {\n return new ZodArray({\n type: schema,\n minLength: null,\n maxLength: null,\n exactLength: null,\n typeName: ZodFirstPartyTypeKind.ZodArray,\n ...processCreateParams(params),\n });\n};\nfunction deepPartialify(schema) {\n if (schema instanceof ZodObject) {\n const newShape = {};\n for (const key in schema.shape) {\n const fieldSchema = schema.shape[key];\n newShape[key] = ZodOptional.create(deepPartialify(fieldSchema));\n }\n return new ZodObject({\n ...schema._def,\n shape: () => newShape,\n });\n }\n else if (schema instanceof ZodArray) {\n return new ZodArray({\n ...schema._def,\n type: deepPartialify(schema.element),\n });\n }\n else if (schema instanceof ZodOptional) {\n return ZodOptional.create(deepPartialify(schema.unwrap()));\n }\n else if (schema instanceof ZodNullable) {\n return ZodNullable.create(deepPartialify(schema.unwrap()));\n }\n else if (schema instanceof ZodTuple) {\n return ZodTuple.create(schema.items.map((item) => deepPartialify(item)));\n }\n else {\n return schema;\n }\n}\nclass ZodObject extends ZodType {\n constructor() {\n super(...arguments);\n this._cached = null;\n /**\n * @deprecated In most cases, this is no longer needed - unknown properties are now silently stripped.\n * If you want to pass through unknown properties, use `.passthrough()` instead.\n */\n this.nonstrict = this.passthrough;\n // extend<\n // Augmentation extends ZodRawShape,\n // NewOutput extends util.flatten<{\n // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation\n // ? Augmentation[k][\"_output\"]\n // : k extends keyof Output\n // ? Output[k]\n // : never;\n // }>,\n // NewInput extends util.flatten<{\n // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation\n // ? Augmentation[k][\"_input\"]\n // : k extends keyof Input\n // ? Input[k]\n // : never;\n // }>\n // >(\n // augmentation: Augmentation\n // ): ZodObject<\n // extendShape,\n // UnknownKeys,\n // Catchall,\n // NewOutput,\n // NewInput\n // > {\n // return new ZodObject({\n // ...this._def,\n // shape: () => ({\n // ...this._def.shape(),\n // ...augmentation,\n // }),\n // }) as any;\n // }\n /**\n * @deprecated Use `.extend` instead\n * */\n this.augment = this.extend;\n }\n _getCached() {\n if (this._cached !== null)\n return this._cached;\n const shape = this._def.shape();\n const keys = util.objectKeys(shape);\n return (this._cached = { shape, keys });\n }\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.object) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.object,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const { status, ctx } = this._processInputParams(input);\n const { shape, keys: shapeKeys } = this._getCached();\n const extraKeys = [];\n if (!(this._def.catchall instanceof ZodNever &&\n this._def.unknownKeys === \"strip\")) {\n for (const key in ctx.data) {\n if (!shapeKeys.includes(key)) {\n extraKeys.push(key);\n }\n }\n }\n const pairs = [];\n for (const key of shapeKeys) {\n const keyValidator = shape[key];\n const value = ctx.data[key];\n pairs.push({\n key: { status: \"valid\", value: key },\n value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)),\n alwaysSet: key in ctx.data,\n });\n }\n if (this._def.catchall instanceof ZodNever) {\n const unknownKeys = this._def.unknownKeys;\n if (unknownKeys === \"passthrough\") {\n for (const key of extraKeys) {\n pairs.push({\n key: { status: \"valid\", value: key },\n value: { status: \"valid\", value: ctx.data[key] },\n });\n }\n }\n else if (unknownKeys === \"strict\") {\n if (extraKeys.length > 0) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.unrecognized_keys,\n keys: extraKeys,\n });\n status.dirty();\n }\n }\n else if (unknownKeys === \"strip\") ;\n else {\n throw new Error(`Internal ZodObject error: invalid unknownKeys value.`);\n }\n }\n else {\n // run catchall validation\n const catchall = this._def.catchall;\n for (const key of extraKeys) {\n const value = ctx.data[key];\n pairs.push({\n key: { status: \"valid\", value: key },\n value: catchall._parse(new ParseInputLazyPath(ctx, value, ctx.path, key) //, ctx.child(key), value, getParsedType(value)\n ),\n alwaysSet: key in ctx.data,\n });\n }\n }\n if (ctx.common.async) {\n return Promise.resolve()\n .then(async () => {\n const syncPairs = [];\n for (const pair of pairs) {\n const key = await pair.key;\n const value = await pair.value;\n syncPairs.push({\n key,\n value,\n alwaysSet: pair.alwaysSet,\n });\n }\n return syncPairs;\n })\n .then((syncPairs) => {\n return ParseStatus.mergeObjectSync(status, syncPairs);\n });\n }\n else {\n return ParseStatus.mergeObjectSync(status, pairs);\n }\n }\n get shape() {\n return this._def.shape();\n }\n strict(message) {\n errorUtil.errToObj;\n return new ZodObject({\n ...this._def,\n unknownKeys: \"strict\",\n ...(message !== undefined\n ? {\n errorMap: (issue, ctx) => {\n var _a, _b, _c, _d;\n const defaultError = (_c = (_b = (_a = this._def).errorMap) === null || _b === void 0 ? void 0 : _b.call(_a, issue, ctx).message) !== null && _c !== void 0 ? _c : ctx.defaultError;\n if (issue.code === \"unrecognized_keys\")\n return {\n message: (_d = errorUtil.errToObj(message).message) !== null && _d !== void 0 ? _d : defaultError,\n };\n return {\n message: defaultError,\n };\n },\n }\n : {}),\n });\n }\n strip() {\n return new ZodObject({\n ...this._def,\n unknownKeys: \"strip\",\n });\n }\n passthrough() {\n return new ZodObject({\n ...this._def,\n unknownKeys: \"passthrough\",\n });\n }\n // const AugmentFactory =\n // (def: Def) =>\n // (\n // augmentation: Augmentation\n // ): ZodObject<\n // extendShape, Augmentation>,\n // Def[\"unknownKeys\"],\n // Def[\"catchall\"]\n // > => {\n // return new ZodObject({\n // ...def,\n // shape: () => ({\n // ...def.shape(),\n // ...augmentation,\n // }),\n // }) as any;\n // };\n extend(augmentation) {\n return new ZodObject({\n ...this._def,\n shape: () => ({\n ...this._def.shape(),\n ...augmentation,\n }),\n });\n }\n /**\n * Prior to zod@1.0.12 there was a bug in the\n * inferred type of merged objects. Please\n * upgrade if you are experiencing issues.\n */\n merge(merging) {\n const merged = new ZodObject({\n unknownKeys: merging._def.unknownKeys,\n catchall: merging._def.catchall,\n shape: () => ({\n ...this._def.shape(),\n ...merging._def.shape(),\n }),\n typeName: ZodFirstPartyTypeKind.ZodObject,\n });\n return merged;\n }\n // merge<\n // Incoming extends AnyZodObject,\n // Augmentation extends Incoming[\"shape\"],\n // NewOutput extends {\n // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation\n // ? Augmentation[k][\"_output\"]\n // : k extends keyof Output\n // ? Output[k]\n // : never;\n // },\n // NewInput extends {\n // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation\n // ? Augmentation[k][\"_input\"]\n // : k extends keyof Input\n // ? Input[k]\n // : never;\n // }\n // >(\n // merging: Incoming\n // ): ZodObject<\n // extendShape>,\n // Incoming[\"_def\"][\"unknownKeys\"],\n // Incoming[\"_def\"][\"catchall\"],\n // NewOutput,\n // NewInput\n // > {\n // const merged: any = new ZodObject({\n // unknownKeys: merging._def.unknownKeys,\n // catchall: merging._def.catchall,\n // shape: () =>\n // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()),\n // typeName: ZodFirstPartyTypeKind.ZodObject,\n // }) as any;\n // return merged;\n // }\n setKey(key, schema) {\n return this.augment({ [key]: schema });\n }\n // merge(\n // merging: Incoming\n // ): //ZodObject = (merging) => {\n // ZodObject<\n // extendShape>,\n // Incoming[\"_def\"][\"unknownKeys\"],\n // Incoming[\"_def\"][\"catchall\"]\n // > {\n // // const mergedShape = objectUtil.mergeShapes(\n // // this._def.shape(),\n // // merging._def.shape()\n // // );\n // const merged: any = new ZodObject({\n // unknownKeys: merging._def.unknownKeys,\n // catchall: merging._def.catchall,\n // shape: () =>\n // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()),\n // typeName: ZodFirstPartyTypeKind.ZodObject,\n // }) as any;\n // return merged;\n // }\n catchall(index) {\n return new ZodObject({\n ...this._def,\n catchall: index,\n });\n }\n pick(mask) {\n const shape = {};\n util.objectKeys(mask).forEach((key) => {\n if (mask[key] && this.shape[key]) {\n shape[key] = this.shape[key];\n }\n });\n return new ZodObject({\n ...this._def,\n shape: () => shape,\n });\n }\n omit(mask) {\n const shape = {};\n util.objectKeys(this.shape).forEach((key) => {\n if (!mask[key]) {\n shape[key] = this.shape[key];\n }\n });\n return new ZodObject({\n ...this._def,\n shape: () => shape,\n });\n }\n /**\n * @deprecated\n */\n deepPartial() {\n return deepPartialify(this);\n }\n partial(mask) {\n const newShape = {};\n util.objectKeys(this.shape).forEach((key) => {\n const fieldSchema = this.shape[key];\n if (mask && !mask[key]) {\n newShape[key] = fieldSchema;\n }\n else {\n newShape[key] = fieldSchema.optional();\n }\n });\n return new ZodObject({\n ...this._def,\n shape: () => newShape,\n });\n }\n required(mask) {\n const newShape = {};\n util.objectKeys(this.shape).forEach((key) => {\n if (mask && !mask[key]) {\n newShape[key] = this.shape[key];\n }\n else {\n const fieldSchema = this.shape[key];\n let newField = fieldSchema;\n while (newField instanceof ZodOptional) {\n newField = newField._def.innerType;\n }\n newShape[key] = newField;\n }\n });\n return new ZodObject({\n ...this._def,\n shape: () => newShape,\n });\n }\n keyof() {\n return createZodEnum(util.objectKeys(this.shape));\n }\n}\nZodObject.create = (shape, params) => {\n return new ZodObject({\n shape: () => shape,\n unknownKeys: \"strip\",\n catchall: ZodNever.create(),\n typeName: ZodFirstPartyTypeKind.ZodObject,\n ...processCreateParams(params),\n });\n};\nZodObject.strictCreate = (shape, params) => {\n return new ZodObject({\n shape: () => shape,\n unknownKeys: \"strict\",\n catchall: ZodNever.create(),\n typeName: ZodFirstPartyTypeKind.ZodObject,\n ...processCreateParams(params),\n });\n};\nZodObject.lazycreate = (shape, params) => {\n return new ZodObject({\n shape,\n unknownKeys: \"strip\",\n catchall: ZodNever.create(),\n typeName: ZodFirstPartyTypeKind.ZodObject,\n ...processCreateParams(params),\n });\n};\nclass ZodUnion extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n const options = this._def.options;\n function handleResults(results) {\n // return first issue-free validation if it exists\n for (const result of results) {\n if (result.result.status === \"valid\") {\n return result.result;\n }\n }\n for (const result of results) {\n if (result.result.status === \"dirty\") {\n // add issues from dirty option\n ctx.common.issues.push(...result.ctx.common.issues);\n return result.result;\n }\n }\n // return invalid\n const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues));\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_union,\n unionErrors,\n });\n return INVALID;\n }\n if (ctx.common.async) {\n return Promise.all(options.map(async (option) => {\n const childCtx = {\n ...ctx,\n common: {\n ...ctx.common,\n issues: [],\n },\n parent: null,\n };\n return {\n result: await option._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: childCtx,\n }),\n ctx: childCtx,\n };\n })).then(handleResults);\n }\n else {\n let dirty = undefined;\n const issues = [];\n for (const option of options) {\n const childCtx = {\n ...ctx,\n common: {\n ...ctx.common,\n issues: [],\n },\n parent: null,\n };\n const result = option._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: childCtx,\n });\n if (result.status === \"valid\") {\n return result;\n }\n else if (result.status === \"dirty\" && !dirty) {\n dirty = { result, ctx: childCtx };\n }\n if (childCtx.common.issues.length) {\n issues.push(childCtx.common.issues);\n }\n }\n if (dirty) {\n ctx.common.issues.push(...dirty.ctx.common.issues);\n return dirty.result;\n }\n const unionErrors = issues.map((issues) => new ZodError(issues));\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_union,\n unionErrors,\n });\n return INVALID;\n }\n }\n get options() {\n return this._def.options;\n }\n}\nZodUnion.create = (types, params) => {\n return new ZodUnion({\n options: types,\n typeName: ZodFirstPartyTypeKind.ZodUnion,\n ...processCreateParams(params),\n });\n};\n/////////////////////////////////////////////////////\n/////////////////////////////////////////////////////\n////////// //////////\n////////// ZodDiscriminatedUnion //////////\n////////// //////////\n/////////////////////////////////////////////////////\n/////////////////////////////////////////////////////\nconst getDiscriminator = (type) => {\n if (type instanceof ZodLazy) {\n return getDiscriminator(type.schema);\n }\n else if (type instanceof ZodEffects) {\n return getDiscriminator(type.innerType());\n }\n else if (type instanceof ZodLiteral) {\n return [type.value];\n }\n else if (type instanceof ZodEnum) {\n return type.options;\n }\n else if (type instanceof ZodNativeEnum) {\n // eslint-disable-next-line ban/ban\n return util.objectValues(type.enum);\n }\n else if (type instanceof ZodDefault) {\n return getDiscriminator(type._def.innerType);\n }\n else if (type instanceof ZodUndefined) {\n return [undefined];\n }\n else if (type instanceof ZodNull) {\n return [null];\n }\n else if (type instanceof ZodOptional) {\n return [undefined, ...getDiscriminator(type.unwrap())];\n }\n else if (type instanceof ZodNullable) {\n return [null, ...getDiscriminator(type.unwrap())];\n }\n else if (type instanceof ZodBranded) {\n return getDiscriminator(type.unwrap());\n }\n else if (type instanceof ZodReadonly) {\n return getDiscriminator(type.unwrap());\n }\n else if (type instanceof ZodCatch) {\n return getDiscriminator(type._def.innerType);\n }\n else {\n return [];\n }\n};\nclass ZodDiscriminatedUnion extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.object) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.object,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const discriminator = this.discriminator;\n const discriminatorValue = ctx.data[discriminator];\n const option = this.optionsMap.get(discriminatorValue);\n if (!option) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_union_discriminator,\n options: Array.from(this.optionsMap.keys()),\n path: [discriminator],\n });\n return INVALID;\n }\n if (ctx.common.async) {\n return option._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n }\n else {\n return option._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n }\n }\n get discriminator() {\n return this._def.discriminator;\n }\n get options() {\n return this._def.options;\n }\n get optionsMap() {\n return this._def.optionsMap;\n }\n /**\n * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.\n * However, it only allows a union of objects, all of which need to share a discriminator property. This property must\n * have a different value for each object in the union.\n * @param discriminator the name of the discriminator property\n * @param types an array of object schemas\n * @param params\n */\n static create(discriminator, options, params) {\n // Get all the valid discriminator values\n const optionsMap = new Map();\n // try {\n for (const type of options) {\n const discriminatorValues = getDiscriminator(type.shape[discriminator]);\n if (!discriminatorValues.length) {\n throw new Error(`A discriminator value for key \\`${discriminator}\\` could not be extracted from all schema options`);\n }\n for (const value of discriminatorValues) {\n if (optionsMap.has(value)) {\n throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);\n }\n optionsMap.set(value, type);\n }\n }\n return new ZodDiscriminatedUnion({\n typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,\n discriminator,\n options,\n optionsMap,\n ...processCreateParams(params),\n });\n }\n}\nfunction mergeValues(a, b) {\n const aType = getParsedType(a);\n const bType = getParsedType(b);\n if (a === b) {\n return { valid: true, data: a };\n }\n else if (aType === ZodParsedType.object && bType === ZodParsedType.object) {\n const bKeys = util.objectKeys(b);\n const sharedKeys = util\n .objectKeys(a)\n .filter((key) => bKeys.indexOf(key) !== -1);\n const newObj = { ...a, ...b };\n for (const key of sharedKeys) {\n const sharedValue = mergeValues(a[key], b[key]);\n if (!sharedValue.valid) {\n return { valid: false };\n }\n newObj[key] = sharedValue.data;\n }\n return { valid: true, data: newObj };\n }\n else if (aType === ZodParsedType.array && bType === ZodParsedType.array) {\n if (a.length !== b.length) {\n return { valid: false };\n }\n const newArray = [];\n for (let index = 0; index < a.length; index++) {\n const itemA = a[index];\n const itemB = b[index];\n const sharedValue = mergeValues(itemA, itemB);\n if (!sharedValue.valid) {\n return { valid: false };\n }\n newArray.push(sharedValue.data);\n }\n return { valid: true, data: newArray };\n }\n else if (aType === ZodParsedType.date &&\n bType === ZodParsedType.date &&\n +a === +b) {\n return { valid: true, data: a };\n }\n else {\n return { valid: false };\n }\n}\nclass ZodIntersection extends ZodType {\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n const handleParsed = (parsedLeft, parsedRight) => {\n if (isAborted(parsedLeft) || isAborted(parsedRight)) {\n return INVALID;\n }\n const merged = mergeValues(parsedLeft.value, parsedRight.value);\n if (!merged.valid) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_intersection_types,\n });\n return INVALID;\n }\n if (isDirty(parsedLeft) || isDirty(parsedRight)) {\n status.dirty();\n }\n return { status: status.value, value: merged.data };\n };\n if (ctx.common.async) {\n return Promise.all([\n this._def.left._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n }),\n this._def.right._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n }),\n ]).then(([left, right]) => handleParsed(left, right));\n }\n else {\n return handleParsed(this._def.left._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n }), this._def.right._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n }));\n }\n }\n}\nZodIntersection.create = (left, right, params) => {\n return new ZodIntersection({\n left: left,\n right: right,\n typeName: ZodFirstPartyTypeKind.ZodIntersection,\n ...processCreateParams(params),\n });\n};\nclass ZodTuple extends ZodType {\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.array) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.array,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n if (ctx.data.length < this._def.items.length) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: this._def.items.length,\n inclusive: true,\n exact: false,\n type: \"array\",\n });\n return INVALID;\n }\n const rest = this._def.rest;\n if (!rest && ctx.data.length > this._def.items.length) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: this._def.items.length,\n inclusive: true,\n exact: false,\n type: \"array\",\n });\n status.dirty();\n }\n const items = [...ctx.data]\n .map((item, itemIndex) => {\n const schema = this._def.items[itemIndex] || this._def.rest;\n if (!schema)\n return null;\n return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex));\n })\n .filter((x) => !!x); // filter nulls\n if (ctx.common.async) {\n return Promise.all(items).then((results) => {\n return ParseStatus.mergeArray(status, results);\n });\n }\n else {\n return ParseStatus.mergeArray(status, items);\n }\n }\n get items() {\n return this._def.items;\n }\n rest(rest) {\n return new ZodTuple({\n ...this._def,\n rest,\n });\n }\n}\nZodTuple.create = (schemas, params) => {\n if (!Array.isArray(schemas)) {\n throw new Error(\"You must pass an array of schemas to z.tuple([ ... ])\");\n }\n return new ZodTuple({\n items: schemas,\n typeName: ZodFirstPartyTypeKind.ZodTuple,\n rest: null,\n ...processCreateParams(params),\n });\n};\nclass ZodRecord extends ZodType {\n get keySchema() {\n return this._def.keyType;\n }\n get valueSchema() {\n return this._def.valueType;\n }\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.object) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.object,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const pairs = [];\n const keyType = this._def.keyType;\n const valueType = this._def.valueType;\n for (const key in ctx.data) {\n pairs.push({\n key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),\n value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),\n alwaysSet: key in ctx.data,\n });\n }\n if (ctx.common.async) {\n return ParseStatus.mergeObjectAsync(status, pairs);\n }\n else {\n return ParseStatus.mergeObjectSync(status, pairs);\n }\n }\n get element() {\n return this._def.valueType;\n }\n static create(first, second, third) {\n if (second instanceof ZodType) {\n return new ZodRecord({\n keyType: first,\n valueType: second,\n typeName: ZodFirstPartyTypeKind.ZodRecord,\n ...processCreateParams(third),\n });\n }\n return new ZodRecord({\n keyType: ZodString.create(),\n valueType: first,\n typeName: ZodFirstPartyTypeKind.ZodRecord,\n ...processCreateParams(second),\n });\n }\n}\nclass ZodMap extends ZodType {\n get keySchema() {\n return this._def.keyType;\n }\n get valueSchema() {\n return this._def.valueType;\n }\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.map) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.map,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const keyType = this._def.keyType;\n const valueType = this._def.valueType;\n const pairs = [...ctx.data.entries()].map(([key, value], index) => {\n return {\n key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, \"key\"])),\n value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, \"value\"])),\n };\n });\n if (ctx.common.async) {\n const finalMap = new Map();\n return Promise.resolve().then(async () => {\n for (const pair of pairs) {\n const key = await pair.key;\n const value = await pair.value;\n if (key.status === \"aborted\" || value.status === \"aborted\") {\n return INVALID;\n }\n if (key.status === \"dirty\" || value.status === \"dirty\") {\n status.dirty();\n }\n finalMap.set(key.value, value.value);\n }\n return { status: status.value, value: finalMap };\n });\n }\n else {\n const finalMap = new Map();\n for (const pair of pairs) {\n const key = pair.key;\n const value = pair.value;\n if (key.status === \"aborted\" || value.status === \"aborted\") {\n return INVALID;\n }\n if (key.status === \"dirty\" || value.status === \"dirty\") {\n status.dirty();\n }\n finalMap.set(key.value, value.value);\n }\n return { status: status.value, value: finalMap };\n }\n }\n}\nZodMap.create = (keyType, valueType, params) => {\n return new ZodMap({\n valueType,\n keyType,\n typeName: ZodFirstPartyTypeKind.ZodMap,\n ...processCreateParams(params),\n });\n};\nclass ZodSet extends ZodType {\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.set) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.set,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const def = this._def;\n if (def.minSize !== null) {\n if (ctx.data.size < def.minSize.value) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_small,\n minimum: def.minSize.value,\n type: \"set\",\n inclusive: true,\n exact: false,\n message: def.minSize.message,\n });\n status.dirty();\n }\n }\n if (def.maxSize !== null) {\n if (ctx.data.size > def.maxSize.value) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.too_big,\n maximum: def.maxSize.value,\n type: \"set\",\n inclusive: true,\n exact: false,\n message: def.maxSize.message,\n });\n status.dirty();\n }\n }\n const valueType = this._def.valueType;\n function finalizeSet(elements) {\n const parsedSet = new Set();\n for (const element of elements) {\n if (element.status === \"aborted\")\n return INVALID;\n if (element.status === \"dirty\")\n status.dirty();\n parsedSet.add(element.value);\n }\n return { status: status.value, value: parsedSet };\n }\n const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i)));\n if (ctx.common.async) {\n return Promise.all(elements).then((elements) => finalizeSet(elements));\n }\n else {\n return finalizeSet(elements);\n }\n }\n min(minSize, message) {\n return new ZodSet({\n ...this._def,\n minSize: { value: minSize, message: errorUtil.toString(message) },\n });\n }\n max(maxSize, message) {\n return new ZodSet({\n ...this._def,\n maxSize: { value: maxSize, message: errorUtil.toString(message) },\n });\n }\n size(size, message) {\n return this.min(size, message).max(size, message);\n }\n nonempty(message) {\n return this.min(1, message);\n }\n}\nZodSet.create = (valueType, params) => {\n return new ZodSet({\n valueType,\n minSize: null,\n maxSize: null,\n typeName: ZodFirstPartyTypeKind.ZodSet,\n ...processCreateParams(params),\n });\n};\nclass ZodFunction extends ZodType {\n constructor() {\n super(...arguments);\n this.validate = this.implement;\n }\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.function) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.function,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n function makeArgsIssue(args, error) {\n return makeIssue({\n data: args,\n path: ctx.path,\n errorMaps: [\n ctx.common.contextualErrorMap,\n ctx.schemaErrorMap,\n getErrorMap(),\n errorMap,\n ].filter((x) => !!x),\n issueData: {\n code: ZodIssueCode.invalid_arguments,\n argumentsError: error,\n },\n });\n }\n function makeReturnsIssue(returns, error) {\n return makeIssue({\n data: returns,\n path: ctx.path,\n errorMaps: [\n ctx.common.contextualErrorMap,\n ctx.schemaErrorMap,\n getErrorMap(),\n errorMap,\n ].filter((x) => !!x),\n issueData: {\n code: ZodIssueCode.invalid_return_type,\n returnTypeError: error,\n },\n });\n }\n const params = { errorMap: ctx.common.contextualErrorMap };\n const fn = ctx.data;\n if (this._def.returns instanceof ZodPromise) {\n // Would love a way to avoid disabling this rule, but we need\n // an alias (using an arrow function was what caused 2651).\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const me = this;\n return OK(async function (...args) {\n const error = new ZodError([]);\n const parsedArgs = await me._def.args\n .parseAsync(args, params)\n .catch((e) => {\n error.addIssue(makeArgsIssue(args, e));\n throw error;\n });\n const result = await Reflect.apply(fn, this, parsedArgs);\n const parsedReturns = await me._def.returns._def.type\n .parseAsync(result, params)\n .catch((e) => {\n error.addIssue(makeReturnsIssue(result, e));\n throw error;\n });\n return parsedReturns;\n });\n }\n else {\n // Would love a way to avoid disabling this rule, but we need\n // an alias (using an arrow function was what caused 2651).\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const me = this;\n return OK(function (...args) {\n const parsedArgs = me._def.args.safeParse(args, params);\n if (!parsedArgs.success) {\n throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);\n }\n const result = Reflect.apply(fn, this, parsedArgs.data);\n const parsedReturns = me._def.returns.safeParse(result, params);\n if (!parsedReturns.success) {\n throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);\n }\n return parsedReturns.data;\n });\n }\n }\n parameters() {\n return this._def.args;\n }\n returnType() {\n return this._def.returns;\n }\n args(...items) {\n return new ZodFunction({\n ...this._def,\n args: ZodTuple.create(items).rest(ZodUnknown.create()),\n });\n }\n returns(returnType) {\n return new ZodFunction({\n ...this._def,\n returns: returnType,\n });\n }\n implement(func) {\n const validatedFunc = this.parse(func);\n return validatedFunc;\n }\n strictImplement(func) {\n const validatedFunc = this.parse(func);\n return validatedFunc;\n }\n static create(args, returns, params) {\n return new ZodFunction({\n args: (args\n ? args\n : ZodTuple.create([]).rest(ZodUnknown.create())),\n returns: returns || ZodUnknown.create(),\n typeName: ZodFirstPartyTypeKind.ZodFunction,\n ...processCreateParams(params),\n });\n }\n}\nclass ZodLazy extends ZodType {\n get schema() {\n return this._def.getter();\n }\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n const lazySchema = this._def.getter();\n return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx });\n }\n}\nZodLazy.create = (getter, params) => {\n return new ZodLazy({\n getter: getter,\n typeName: ZodFirstPartyTypeKind.ZodLazy,\n ...processCreateParams(params),\n });\n};\nclass ZodLiteral extends ZodType {\n _parse(input) {\n if (input.data !== this._def.value) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n received: ctx.data,\n code: ZodIssueCode.invalid_literal,\n expected: this._def.value,\n });\n return INVALID;\n }\n return { status: \"valid\", value: input.data };\n }\n get value() {\n return this._def.value;\n }\n}\nZodLiteral.create = (value, params) => {\n return new ZodLiteral({\n value: value,\n typeName: ZodFirstPartyTypeKind.ZodLiteral,\n ...processCreateParams(params),\n });\n};\nfunction createZodEnum(values, params) {\n return new ZodEnum({\n values,\n typeName: ZodFirstPartyTypeKind.ZodEnum,\n ...processCreateParams(params),\n });\n}\nclass ZodEnum extends ZodType {\n constructor() {\n super(...arguments);\n _ZodEnum_cache.set(this, void 0);\n }\n _parse(input) {\n if (typeof input.data !== \"string\") {\n const ctx = this._getOrReturnCtx(input);\n const expectedValues = this._def.values;\n addIssueToContext(ctx, {\n expected: util.joinValues(expectedValues),\n received: ctx.parsedType,\n code: ZodIssueCode.invalid_type,\n });\n return INVALID;\n }\n if (!__classPrivateFieldGet(this, _ZodEnum_cache, \"f\")) {\n __classPrivateFieldSet(this, _ZodEnum_cache, new Set(this._def.values), \"f\");\n }\n if (!__classPrivateFieldGet(this, _ZodEnum_cache, \"f\").has(input.data)) {\n const ctx = this._getOrReturnCtx(input);\n const expectedValues = this._def.values;\n addIssueToContext(ctx, {\n received: ctx.data,\n code: ZodIssueCode.invalid_enum_value,\n options: expectedValues,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n get options() {\n return this._def.values;\n }\n get enum() {\n const enumValues = {};\n for (const val of this._def.values) {\n enumValues[val] = val;\n }\n return enumValues;\n }\n get Values() {\n const enumValues = {};\n for (const val of this._def.values) {\n enumValues[val] = val;\n }\n return enumValues;\n }\n get Enum() {\n const enumValues = {};\n for (const val of this._def.values) {\n enumValues[val] = val;\n }\n return enumValues;\n }\n extract(values, newDef = this._def) {\n return ZodEnum.create(values, {\n ...this._def,\n ...newDef,\n });\n }\n exclude(values, newDef = this._def) {\n return ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), {\n ...this._def,\n ...newDef,\n });\n }\n}\n_ZodEnum_cache = new WeakMap();\nZodEnum.create = createZodEnum;\nclass ZodNativeEnum extends ZodType {\n constructor() {\n super(...arguments);\n _ZodNativeEnum_cache.set(this, void 0);\n }\n _parse(input) {\n const nativeEnumValues = util.getValidEnumValues(this._def.values);\n const ctx = this._getOrReturnCtx(input);\n if (ctx.parsedType !== ZodParsedType.string &&\n ctx.parsedType !== ZodParsedType.number) {\n const expectedValues = util.objectValues(nativeEnumValues);\n addIssueToContext(ctx, {\n expected: util.joinValues(expectedValues),\n received: ctx.parsedType,\n code: ZodIssueCode.invalid_type,\n });\n return INVALID;\n }\n if (!__classPrivateFieldGet(this, _ZodNativeEnum_cache, \"f\")) {\n __classPrivateFieldSet(this, _ZodNativeEnum_cache, new Set(util.getValidEnumValues(this._def.values)), \"f\");\n }\n if (!__classPrivateFieldGet(this, _ZodNativeEnum_cache, \"f\").has(input.data)) {\n const expectedValues = util.objectValues(nativeEnumValues);\n addIssueToContext(ctx, {\n received: ctx.data,\n code: ZodIssueCode.invalid_enum_value,\n options: expectedValues,\n });\n return INVALID;\n }\n return OK(input.data);\n }\n get enum() {\n return this._def.values;\n }\n}\n_ZodNativeEnum_cache = new WeakMap();\nZodNativeEnum.create = (values, params) => {\n return new ZodNativeEnum({\n values: values,\n typeName: ZodFirstPartyTypeKind.ZodNativeEnum,\n ...processCreateParams(params),\n });\n};\nclass ZodPromise extends ZodType {\n unwrap() {\n return this._def.type;\n }\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n if (ctx.parsedType !== ZodParsedType.promise &&\n ctx.common.async === false) {\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.promise,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n const promisified = ctx.parsedType === ZodParsedType.promise\n ? ctx.data\n : Promise.resolve(ctx.data);\n return OK(promisified.then((data) => {\n return this._def.type.parseAsync(data, {\n path: ctx.path,\n errorMap: ctx.common.contextualErrorMap,\n });\n }));\n }\n}\nZodPromise.create = (schema, params) => {\n return new ZodPromise({\n type: schema,\n typeName: ZodFirstPartyTypeKind.ZodPromise,\n ...processCreateParams(params),\n });\n};\nclass ZodEffects extends ZodType {\n innerType() {\n return this._def.schema;\n }\n sourceType() {\n return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects\n ? this._def.schema.sourceType()\n : this._def.schema;\n }\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n const effect = this._def.effect || null;\n const checkCtx = {\n addIssue: (arg) => {\n addIssueToContext(ctx, arg);\n if (arg.fatal) {\n status.abort();\n }\n else {\n status.dirty();\n }\n },\n get path() {\n return ctx.path;\n },\n };\n checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx);\n if (effect.type === \"preprocess\") {\n const processed = effect.transform(ctx.data, checkCtx);\n if (ctx.common.async) {\n return Promise.resolve(processed).then(async (processed) => {\n if (status.value === \"aborted\")\n return INVALID;\n const result = await this._def.schema._parseAsync({\n data: processed,\n path: ctx.path,\n parent: ctx,\n });\n if (result.status === \"aborted\")\n return INVALID;\n if (result.status === \"dirty\")\n return DIRTY(result.value);\n if (status.value === \"dirty\")\n return DIRTY(result.value);\n return result;\n });\n }\n else {\n if (status.value === \"aborted\")\n return INVALID;\n const result = this._def.schema._parseSync({\n data: processed,\n path: ctx.path,\n parent: ctx,\n });\n if (result.status === \"aborted\")\n return INVALID;\n if (result.status === \"dirty\")\n return DIRTY(result.value);\n if (status.value === \"dirty\")\n return DIRTY(result.value);\n return result;\n }\n }\n if (effect.type === \"refinement\") {\n const executeRefinement = (acc) => {\n const result = effect.refinement(acc, checkCtx);\n if (ctx.common.async) {\n return Promise.resolve(result);\n }\n if (result instanceof Promise) {\n throw new Error(\"Async refinement encountered during synchronous parse operation. Use .parseAsync instead.\");\n }\n return acc;\n };\n if (ctx.common.async === false) {\n const inner = this._def.schema._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n if (inner.status === \"aborted\")\n return INVALID;\n if (inner.status === \"dirty\")\n status.dirty();\n // return value is ignored\n executeRefinement(inner.value);\n return { status: status.value, value: inner.value };\n }\n else {\n return this._def.schema\n ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx })\n .then((inner) => {\n if (inner.status === \"aborted\")\n return INVALID;\n if (inner.status === \"dirty\")\n status.dirty();\n return executeRefinement(inner.value).then(() => {\n return { status: status.value, value: inner.value };\n });\n });\n }\n }\n if (effect.type === \"transform\") {\n if (ctx.common.async === false) {\n const base = this._def.schema._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n if (!isValid(base))\n return base;\n const result = effect.transform(base.value, checkCtx);\n if (result instanceof Promise) {\n throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);\n }\n return { status: status.value, value: result };\n }\n else {\n return this._def.schema\n ._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx })\n .then((base) => {\n if (!isValid(base))\n return base;\n return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ status: status.value, value: result }));\n });\n }\n }\n util.assertNever(effect);\n }\n}\nZodEffects.create = (schema, effect, params) => {\n return new ZodEffects({\n schema,\n typeName: ZodFirstPartyTypeKind.ZodEffects,\n effect,\n ...processCreateParams(params),\n });\n};\nZodEffects.createWithPreprocess = (preprocess, schema, params) => {\n return new ZodEffects({\n schema,\n effect: { type: \"preprocess\", transform: preprocess },\n typeName: ZodFirstPartyTypeKind.ZodEffects,\n ...processCreateParams(params),\n });\n};\nclass ZodOptional extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType === ZodParsedType.undefined) {\n return OK(undefined);\n }\n return this._def.innerType._parse(input);\n }\n unwrap() {\n return this._def.innerType;\n }\n}\nZodOptional.create = (type, params) => {\n return new ZodOptional({\n innerType: type,\n typeName: ZodFirstPartyTypeKind.ZodOptional,\n ...processCreateParams(params),\n });\n};\nclass ZodNullable extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType === ZodParsedType.null) {\n return OK(null);\n }\n return this._def.innerType._parse(input);\n }\n unwrap() {\n return this._def.innerType;\n }\n}\nZodNullable.create = (type, params) => {\n return new ZodNullable({\n innerType: type,\n typeName: ZodFirstPartyTypeKind.ZodNullable,\n ...processCreateParams(params),\n });\n};\nclass ZodDefault extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n let data = ctx.data;\n if (ctx.parsedType === ZodParsedType.undefined) {\n data = this._def.defaultValue();\n }\n return this._def.innerType._parse({\n data,\n path: ctx.path,\n parent: ctx,\n });\n }\n removeDefault() {\n return this._def.innerType;\n }\n}\nZodDefault.create = (type, params) => {\n return new ZodDefault({\n innerType: type,\n typeName: ZodFirstPartyTypeKind.ZodDefault,\n defaultValue: typeof params.default === \"function\"\n ? params.default\n : () => params.default,\n ...processCreateParams(params),\n });\n};\nclass ZodCatch extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n // newCtx is used to not collect issues from inner types in ctx\n const newCtx = {\n ...ctx,\n common: {\n ...ctx.common,\n issues: [],\n },\n };\n const result = this._def.innerType._parse({\n data: newCtx.data,\n path: newCtx.path,\n parent: {\n ...newCtx,\n },\n });\n if (isAsync(result)) {\n return result.then((result) => {\n return {\n status: \"valid\",\n value: result.status === \"valid\"\n ? result.value\n : this._def.catchValue({\n get error() {\n return new ZodError(newCtx.common.issues);\n },\n input: newCtx.data,\n }),\n };\n });\n }\n else {\n return {\n status: \"valid\",\n value: result.status === \"valid\"\n ? result.value\n : this._def.catchValue({\n get error() {\n return new ZodError(newCtx.common.issues);\n },\n input: newCtx.data,\n }),\n };\n }\n }\n removeCatch() {\n return this._def.innerType;\n }\n}\nZodCatch.create = (type, params) => {\n return new ZodCatch({\n innerType: type,\n typeName: ZodFirstPartyTypeKind.ZodCatch,\n catchValue: typeof params.catch === \"function\" ? params.catch : () => params.catch,\n ...processCreateParams(params),\n });\n};\nclass ZodNaN extends ZodType {\n _parse(input) {\n const parsedType = this._getType(input);\n if (parsedType !== ZodParsedType.nan) {\n const ctx = this._getOrReturnCtx(input);\n addIssueToContext(ctx, {\n code: ZodIssueCode.invalid_type,\n expected: ZodParsedType.nan,\n received: ctx.parsedType,\n });\n return INVALID;\n }\n return { status: \"valid\", value: input.data };\n }\n}\nZodNaN.create = (params) => {\n return new ZodNaN({\n typeName: ZodFirstPartyTypeKind.ZodNaN,\n ...processCreateParams(params),\n });\n};\nconst BRAND = Symbol(\"zod_brand\");\nclass ZodBranded extends ZodType {\n _parse(input) {\n const { ctx } = this._processInputParams(input);\n const data = ctx.data;\n return this._def.type._parse({\n data,\n path: ctx.path,\n parent: ctx,\n });\n }\n unwrap() {\n return this._def.type;\n }\n}\nclass ZodPipeline extends ZodType {\n _parse(input) {\n const { status, ctx } = this._processInputParams(input);\n if (ctx.common.async) {\n const handleAsync = async () => {\n const inResult = await this._def.in._parseAsync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n if (inResult.status === \"aborted\")\n return INVALID;\n if (inResult.status === \"dirty\") {\n status.dirty();\n return DIRTY(inResult.value);\n }\n else {\n return this._def.out._parseAsync({\n data: inResult.value,\n path: ctx.path,\n parent: ctx,\n });\n }\n };\n return handleAsync();\n }\n else {\n const inResult = this._def.in._parseSync({\n data: ctx.data,\n path: ctx.path,\n parent: ctx,\n });\n if (inResult.status === \"aborted\")\n return INVALID;\n if (inResult.status === \"dirty\") {\n status.dirty();\n return {\n status: \"dirty\",\n value: inResult.value,\n };\n }\n else {\n return this._def.out._parseSync({\n data: inResult.value,\n path: ctx.path,\n parent: ctx,\n });\n }\n }\n }\n static create(a, b) {\n return new ZodPipeline({\n in: a,\n out: b,\n typeName: ZodFirstPartyTypeKind.ZodPipeline,\n });\n }\n}\nclass ZodReadonly extends ZodType {\n _parse(input) {\n const result = this._def.innerType._parse(input);\n const freeze = (data) => {\n if (isValid(data)) {\n data.value = Object.freeze(data.value);\n }\n return data;\n };\n return isAsync(result)\n ? result.then((data) => freeze(data))\n : freeze(result);\n }\n unwrap() {\n return this._def.innerType;\n }\n}\nZodReadonly.create = (type, params) => {\n return new ZodReadonly({\n innerType: type,\n typeName: ZodFirstPartyTypeKind.ZodReadonly,\n ...processCreateParams(params),\n });\n};\nfunction custom(check, params = {}, \n/**\n * @deprecated\n *\n * Pass `fatal` into the params object instead:\n *\n * ```ts\n * z.string().custom((val) => val.length > 5, { fatal: false })\n * ```\n *\n */\nfatal) {\n if (check)\n return ZodAny.create().superRefine((data, ctx) => {\n var _a, _b;\n if (!check(data)) {\n const p = typeof params === \"function\"\n ? params(data)\n : typeof params === \"string\"\n ? { message: params }\n : params;\n const _fatal = (_b = (_a = p.fatal) !== null && _a !== void 0 ? _a : fatal) !== null && _b !== void 0 ? _b : true;\n const p2 = typeof p === \"string\" ? { message: p } : p;\n ctx.addIssue({ code: \"custom\", ...p2, fatal: _fatal });\n }\n });\n return ZodAny.create();\n}\nconst late = {\n object: ZodObject.lazycreate,\n};\nvar ZodFirstPartyTypeKind;\n(function (ZodFirstPartyTypeKind) {\n ZodFirstPartyTypeKind[\"ZodString\"] = \"ZodString\";\n ZodFirstPartyTypeKind[\"ZodNumber\"] = \"ZodNumber\";\n ZodFirstPartyTypeKind[\"ZodNaN\"] = \"ZodNaN\";\n ZodFirstPartyTypeKind[\"ZodBigInt\"] = \"ZodBigInt\";\n ZodFirstPartyTypeKind[\"ZodBoolean\"] = \"ZodBoolean\";\n ZodFirstPartyTypeKind[\"ZodDate\"] = \"ZodDate\";\n ZodFirstPartyTypeKind[\"ZodSymbol\"] = \"ZodSymbol\";\n ZodFirstPartyTypeKind[\"ZodUndefined\"] = \"ZodUndefined\";\n ZodFirstPartyTypeKind[\"ZodNull\"] = \"ZodNull\";\n ZodFirstPartyTypeKind[\"ZodAny\"] = \"ZodAny\";\n ZodFirstPartyTypeKind[\"ZodUnknown\"] = \"ZodUnknown\";\n ZodFirstPartyTypeKind[\"ZodNever\"] = \"ZodNever\";\n ZodFirstPartyTypeKind[\"ZodVoid\"] = \"ZodVoid\";\n ZodFirstPartyTypeKind[\"ZodArray\"] = \"ZodArray\";\n ZodFirstPartyTypeKind[\"ZodObject\"] = \"ZodObject\";\n ZodFirstPartyTypeKind[\"ZodUnion\"] = \"ZodUnion\";\n ZodFirstPartyTypeKind[\"ZodDiscriminatedUnion\"] = \"ZodDiscriminatedUnion\";\n ZodFirstPartyTypeKind[\"ZodIntersection\"] = \"ZodIntersection\";\n ZodFirstPartyTypeKind[\"ZodTuple\"] = \"ZodTuple\";\n ZodFirstPartyTypeKind[\"ZodRecord\"] = \"ZodRecord\";\n ZodFirstPartyTypeKind[\"ZodMap\"] = \"ZodMap\";\n ZodFirstPartyTypeKind[\"ZodSet\"] = \"ZodSet\";\n ZodFirstPartyTypeKind[\"ZodFunction\"] = \"ZodFunction\";\n ZodFirstPartyTypeKind[\"ZodLazy\"] = \"ZodLazy\";\n ZodFirstPartyTypeKind[\"ZodLiteral\"] = \"ZodLiteral\";\n ZodFirstPartyTypeKind[\"ZodEnum\"] = \"ZodEnum\";\n ZodFirstPartyTypeKind[\"ZodEffects\"] = \"ZodEffects\";\n ZodFirstPartyTypeKind[\"ZodNativeEnum\"] = \"ZodNativeEnum\";\n ZodFirstPartyTypeKind[\"ZodOptional\"] = \"ZodOptional\";\n ZodFirstPartyTypeKind[\"ZodNullable\"] = \"ZodNullable\";\n ZodFirstPartyTypeKind[\"ZodDefault\"] = \"ZodDefault\";\n ZodFirstPartyTypeKind[\"ZodCatch\"] = \"ZodCatch\";\n ZodFirstPartyTypeKind[\"ZodPromise\"] = \"ZodPromise\";\n ZodFirstPartyTypeKind[\"ZodBranded\"] = \"ZodBranded\";\n ZodFirstPartyTypeKind[\"ZodPipeline\"] = \"ZodPipeline\";\n ZodFirstPartyTypeKind[\"ZodReadonly\"] = \"ZodReadonly\";\n})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));\nconst instanceOfType = (\n// const instanceOfType = any>(\ncls, params = {\n message: `Input not instance of ${cls.name}`,\n}) => custom((data) => data instanceof cls, params);\nconst stringType = ZodString.create;\nconst numberType = ZodNumber.create;\nconst nanType = ZodNaN.create;\nconst bigIntType = ZodBigInt.create;\nconst booleanType = ZodBoolean.create;\nconst dateType = ZodDate.create;\nconst symbolType = ZodSymbol.create;\nconst undefinedType = ZodUndefined.create;\nconst nullType = ZodNull.create;\nconst anyType = ZodAny.create;\nconst unknownType = ZodUnknown.create;\nconst neverType = ZodNever.create;\nconst voidType = ZodVoid.create;\nconst arrayType = ZodArray.create;\nconst objectType = ZodObject.create;\nconst strictObjectType = ZodObject.strictCreate;\nconst unionType = ZodUnion.create;\nconst discriminatedUnionType = ZodDiscriminatedUnion.create;\nconst intersectionType = ZodIntersection.create;\nconst tupleType = ZodTuple.create;\nconst recordType = ZodRecord.create;\nconst mapType = ZodMap.create;\nconst setType = ZodSet.create;\nconst functionType = ZodFunction.create;\nconst lazyType = ZodLazy.create;\nconst literalType = ZodLiteral.create;\nconst enumType = ZodEnum.create;\nconst nativeEnumType = ZodNativeEnum.create;\nconst promiseType = ZodPromise.create;\nconst effectsType = ZodEffects.create;\nconst optionalType = ZodOptional.create;\nconst nullableType = ZodNullable.create;\nconst preprocessType = ZodEffects.createWithPreprocess;\nconst pipelineType = ZodPipeline.create;\nconst ostring = () => stringType().optional();\nconst onumber = () => numberType().optional();\nconst oboolean = () => booleanType().optional();\nconst coerce = {\n string: ((arg) => ZodString.create({ ...arg, coerce: true })),\n number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),\n boolean: ((arg) => ZodBoolean.create({\n ...arg,\n coerce: true,\n })),\n bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),\n date: ((arg) => ZodDate.create({ ...arg, coerce: true })),\n};\nconst NEVER = INVALID;\n\nvar z = /*#__PURE__*/Object.freeze({\n __proto__: null,\n defaultErrorMap: errorMap,\n setErrorMap: setErrorMap,\n getErrorMap: getErrorMap,\n makeIssue: makeIssue,\n EMPTY_PATH: EMPTY_PATH,\n addIssueToContext: addIssueToContext,\n ParseStatus: ParseStatus,\n INVALID: INVALID,\n DIRTY: DIRTY,\n OK: OK,\n isAborted: isAborted,\n isDirty: isDirty,\n isValid: isValid,\n isAsync: isAsync,\n get util () { return util; },\n get objectUtil () { return objectUtil; },\n ZodParsedType: ZodParsedType,\n getParsedType: getParsedType,\n ZodType: ZodType,\n datetimeRegex: datetimeRegex,\n ZodString: ZodString,\n ZodNumber: ZodNumber,\n ZodBigInt: ZodBigInt,\n ZodBoolean: ZodBoolean,\n ZodDate: ZodDate,\n ZodSymbol: ZodSymbol,\n ZodUndefined: ZodUndefined,\n ZodNull: ZodNull,\n ZodAny: ZodAny,\n ZodUnknown: ZodUnknown,\n ZodNever: ZodNever,\n ZodVoid: ZodVoid,\n ZodArray: ZodArray,\n ZodObject: ZodObject,\n ZodUnion: ZodUnion,\n ZodDiscriminatedUnion: ZodDiscriminatedUnion,\n ZodIntersection: ZodIntersection,\n ZodTuple: ZodTuple,\n ZodRecord: ZodRecord,\n ZodMap: ZodMap,\n ZodSet: ZodSet,\n ZodFunction: ZodFunction,\n ZodLazy: ZodLazy,\n ZodLiteral: ZodLiteral,\n ZodEnum: ZodEnum,\n ZodNativeEnum: ZodNativeEnum,\n ZodPromise: ZodPromise,\n ZodEffects: ZodEffects,\n ZodTransformer: ZodEffects,\n ZodOptional: ZodOptional,\n ZodNullable: ZodNullable,\n ZodDefault: ZodDefault,\n ZodCatch: ZodCatch,\n ZodNaN: ZodNaN,\n BRAND: BRAND,\n ZodBranded: ZodBranded,\n ZodPipeline: ZodPipeline,\n ZodReadonly: ZodReadonly,\n custom: custom,\n Schema: ZodType,\n ZodSchema: ZodType,\n late: late,\n get ZodFirstPartyTypeKind () { return ZodFirstPartyTypeKind; },\n coerce: coerce,\n any: anyType,\n array: arrayType,\n bigint: bigIntType,\n boolean: booleanType,\n date: dateType,\n discriminatedUnion: discriminatedUnionType,\n effect: effectsType,\n 'enum': enumType,\n 'function': functionType,\n 'instanceof': instanceOfType,\n intersection: intersectionType,\n lazy: lazyType,\n literal: literalType,\n map: mapType,\n nan: nanType,\n nativeEnum: nativeEnumType,\n never: neverType,\n 'null': nullType,\n nullable: nullableType,\n number: numberType,\n object: objectType,\n oboolean: oboolean,\n onumber: onumber,\n optional: optionalType,\n ostring: ostring,\n pipeline: pipelineType,\n preprocess: preprocessType,\n promise: promiseType,\n record: recordType,\n set: setType,\n strictObject: strictObjectType,\n string: stringType,\n symbol: symbolType,\n transformer: effectsType,\n tuple: tupleType,\n 'undefined': undefinedType,\n union: unionType,\n unknown: unknownType,\n 'void': voidType,\n NEVER: NEVER,\n ZodIssueCode: ZodIssueCode,\n quotelessJson: quotelessJson,\n ZodError: ZodError\n});\n\nexport { BRAND, DIRTY, EMPTY_PATH, INVALID, NEVER, OK, ParseStatus, ZodType as Schema, ZodAny, ZodArray, ZodBigInt, ZodBoolean, ZodBranded, ZodCatch, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodEffects, ZodEnum, ZodError, ZodFirstPartyTypeKind, ZodFunction, ZodIntersection, ZodIssueCode, ZodLazy, ZodLiteral, ZodMap, ZodNaN, ZodNativeEnum, ZodNever, ZodNull, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodParsedType, ZodPipeline, ZodPromise, ZodReadonly, ZodRecord, ZodType as ZodSchema, ZodSet, ZodString, ZodSymbol, ZodEffects as ZodTransformer, ZodTuple, ZodType, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, addIssueToContext, anyType as any, arrayType as array, bigIntType as bigint, booleanType as boolean, coerce, custom, dateType as date, datetimeRegex, z as default, errorMap as defaultErrorMap, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, getErrorMap, getParsedType, instanceOfType as instanceof, intersectionType as intersection, isAborted, isAsync, isDirty, isValid, late, lazyType as lazy, literalType as literal, makeIssue, mapType as map, nanType as nan, nativeEnumType as nativeEnum, neverType as never, nullType as null, nullableType as nullable, numberType as number, objectType as object, objectUtil, oboolean, onumber, optionalType as optional, ostring, pipelineType as pipeline, preprocessType as preprocess, promiseType as promise, quotelessJson, recordType as record, setType as set, setErrorMap, strictObjectType as strictObject, stringType as string, symbolType as symbol, effectsType as transformer, tupleType as tuple, undefinedType as undefined, unionType as union, unknownType as unknown, util, voidType as void, z };\n","// lib/isZodErrorLike.ts\nfunction isZodErrorLike(err) {\n return err instanceof Error && err.name === \"ZodError\" && \"issues\" in err && Array.isArray(err.issues);\n}\n\n// lib/ValidationError.ts\nvar ValidationError = class extends Error {\n name;\n details;\n constructor(message, options) {\n super(message, options);\n this.name = \"ZodValidationError\";\n this.details = getIssuesFromErrorOptions(options);\n }\n toString() {\n return this.message;\n }\n};\nfunction getIssuesFromErrorOptions(options) {\n if (options) {\n const cause = options.cause;\n if (isZodErrorLike(cause)) {\n return cause.issues;\n }\n }\n return [];\n}\n\n// lib/isValidationError.ts\nfunction isValidationError(err) {\n return err instanceof ValidationError;\n}\n\n// lib/isValidationErrorLike.ts\nfunction isValidationErrorLike(err) {\n return err instanceof Error && err.name === \"ZodValidationError\";\n}\n\n// lib/fromZodIssue.ts\nimport * as zod from \"zod\";\n\n// lib/config.ts\nvar ISSUE_SEPARATOR = \"; \";\nvar MAX_ISSUES_IN_MESSAGE = 99;\nvar PREFIX = \"Validation error\";\nvar PREFIX_SEPARATOR = \": \";\nvar UNION_SEPARATOR = \", or \";\n\n// lib/prefixMessage.ts\nfunction prefixMessage(message, prefix, prefixSeparator) {\n if (prefix !== null) {\n if (message.length > 0) {\n return [prefix, message].join(prefixSeparator);\n }\n return prefix;\n }\n if (message.length > 0) {\n return message;\n }\n return PREFIX;\n}\n\n// lib/utils/joinPath.ts\nvar identifierRegex = /[$_\\p{ID_Start}][$\\u200c\\u200d\\p{ID_Continue}]*/u;\nfunction joinPath(path) {\n if (path.length === 1) {\n return path[0].toString();\n }\n return path.reduce((acc, item) => {\n if (typeof item === \"number\") {\n return acc + \"[\" + item.toString() + \"]\";\n }\n if (item.includes('\"')) {\n return acc + '[\"' + escapeQuotes(item) + '\"]';\n }\n if (!identifierRegex.test(item)) {\n return acc + '[\"' + item + '\"]';\n }\n const separator = acc.length === 0 ? \"\" : \".\";\n return acc + separator + item;\n }, \"\");\n}\nfunction escapeQuotes(str) {\n return str.replace(/\"/g, '\\\\\"');\n}\n\n// lib/utils/NonEmptyArray.ts\nfunction isNonEmptyArray(value) {\n return value.length !== 0;\n}\n\n// lib/fromZodIssue.ts\nfunction getMessageFromZodIssue(props) {\n const { issue, issueSeparator, unionSeparator, includePath } = props;\n if (issue.code === \"invalid_union\") {\n return issue.unionErrors.reduce((acc, zodError) => {\n const newIssues = zodError.issues.map(\n (issue2) => getMessageFromZodIssue({\n issue: issue2,\n issueSeparator,\n unionSeparator,\n includePath\n })\n ).join(issueSeparator);\n if (!acc.includes(newIssues)) {\n acc.push(newIssues);\n }\n return acc;\n }, []).join(unionSeparator);\n }\n if (issue.code === \"invalid_arguments\") {\n return [\n issue.message,\n ...issue.argumentsError.issues.map(\n (issue2) => getMessageFromZodIssue({\n issue: issue2,\n issueSeparator,\n unionSeparator,\n includePath\n })\n )\n ].join(issueSeparator);\n }\n if (issue.code === \"invalid_return_type\") {\n return [\n issue.message,\n ...issue.returnTypeError.issues.map(\n (issue2) => getMessageFromZodIssue({\n issue: issue2,\n issueSeparator,\n unionSeparator,\n includePath\n })\n )\n ].join(issueSeparator);\n }\n if (includePath && isNonEmptyArray(issue.path)) {\n if (issue.path.length === 1) {\n const identifier = issue.path[0];\n if (typeof identifier === \"number\") {\n return `${issue.message} at index ${identifier}`;\n }\n }\n return `${issue.message} at \"${joinPath(issue.path)}\"`;\n }\n return issue.message;\n}\nfunction fromZodIssue(issue, options = {}) {\n const {\n issueSeparator = ISSUE_SEPARATOR,\n unionSeparator = UNION_SEPARATOR,\n prefixSeparator = PREFIX_SEPARATOR,\n prefix = PREFIX,\n includePath = true\n } = options;\n const reason = getMessageFromZodIssue({\n issue,\n issueSeparator,\n unionSeparator,\n includePath\n });\n const message = prefixMessage(reason, prefix, prefixSeparator);\n return new ValidationError(message, { cause: new zod.ZodError([issue]) });\n}\n\n// lib/errorMap.ts\nvar errorMap = (issue, ctx) => {\n const error = fromZodIssue({\n ...issue,\n // fallback to the default error message\n // when issue does not have a message\n message: issue.message ?? ctx.defaultError\n });\n return {\n message: error.message\n };\n};\n\n// lib/fromZodError.ts\nfunction fromZodError(zodError, options = {}) {\n if (!isZodErrorLike(zodError)) {\n throw new TypeError(\n `Invalid zodError param; expected instance of ZodError. Did you mean to use the \"${fromError.name}\" method instead?`\n );\n }\n return fromZodErrorWithoutRuntimeCheck(zodError, options);\n}\nfunction fromZodErrorWithoutRuntimeCheck(zodError, options = {}) {\n const {\n maxIssuesInMessage = MAX_ISSUES_IN_MESSAGE,\n issueSeparator = ISSUE_SEPARATOR,\n unionSeparator = UNION_SEPARATOR,\n prefixSeparator = PREFIX_SEPARATOR,\n prefix = PREFIX,\n includePath = true\n } = options;\n const zodIssues = zodError.errors;\n const reason = zodIssues.length === 0 ? zodError.message : zodIssues.slice(0, maxIssuesInMessage).map(\n (issue) => getMessageFromZodIssue({\n issue,\n issueSeparator,\n unionSeparator,\n includePath\n })\n ).join(issueSeparator);\n const message = prefixMessage(reason, prefix, prefixSeparator);\n return new ValidationError(message, { cause: zodError });\n}\n\n// lib/toValidationError.ts\nvar toValidationError = (options = {}) => (err) => {\n if (isZodErrorLike(err)) {\n return fromZodErrorWithoutRuntimeCheck(err, options);\n }\n if (err instanceof Error) {\n return new ValidationError(err.message, { cause: err });\n }\n return new ValidationError(\"Unknown error\");\n};\n\n// lib/fromError.ts\nfunction fromError(err, options = {}) {\n return toValidationError(options)(err);\n}\nexport {\n ValidationError,\n errorMap,\n fromError,\n fromZodError,\n fromZodIssue,\n isValidationError,\n isValidationErrorLike,\n isZodErrorLike,\n toValidationError\n};\n//# sourceMappingURL=index.mjs.map","import { z } from 'zod'\r\nimport { fromZodError } from 'zod-validation-error'\r\n\r\n// GroupNode is hacking node id to be a string, so we need to allow that.\r\n// innerNode.id = `${this.node.id}:${i}`\r\n// Remove it after GroupNode is redesigned.\r\nexport const zNodeId = z.union([z.number().int(), z.string()])\r\nexport type NodeId = z.infer\r\nexport const zSlotIndex = z.union([\r\n z.number().int(),\r\n z\r\n .string()\r\n .transform((val) => parseInt(val))\r\n .refine((val) => !isNaN(val), {\r\n message: 'Invalid number'\r\n })\r\n])\r\n\r\n// TODO: Investigate usage of array and number as data type usage in custom nodes.\r\n// Known usage:\r\n// - https://github.com/rgthree/rgthree-comfy Context Big node is using array as type.\r\nexport const zDataType = z.union([z.string(), z.array(z.string()), z.number()])\r\n\r\n// Definition of an AI model file used in the workflow.\r\nconst zModelFile = z.object({\r\n name: z.string(),\r\n url: z.string().url(),\r\n hash: z.string(),\r\n hash_type: z.string(),\r\n directory: z.string()\r\n})\r\n\r\nconst zComfyLink = z.tuple([\r\n z.number(), // Link id\r\n zNodeId, // Node id of source node\r\n zSlotIndex, // Output slot# of source node\r\n zNodeId, // Node id of destination node\r\n zSlotIndex, // Input slot# of destination node\r\n zDataType // Data type\r\n])\r\n\r\nconst zNodeOutput = z\r\n .object({\r\n name: z.string(),\r\n type: zDataType,\r\n links: z.array(z.number()).nullable().optional(),\r\n slot_index: zSlotIndex.optional()\r\n })\r\n .passthrough()\r\n\r\nconst zNodeInput = z\r\n .object({\r\n name: z.string(),\r\n type: zDataType,\r\n link: z.number().nullable().optional(),\r\n slot_index: zSlotIndex.optional()\r\n })\r\n .passthrough()\r\n\r\nconst zFlags = z\r\n .object({\r\n collapsed: z.boolean().optional(),\r\n pinned: z.boolean().optional(),\r\n allow_interaction: z.boolean().optional(),\r\n horizontal: z.boolean().optional(),\r\n skip_repeated_outputs: z.boolean().optional()\r\n })\r\n .passthrough()\r\n\r\nconst zProperties = z\r\n .object({\r\n ['Node name for S&R']: z.string().optional()\r\n })\r\n .passthrough()\r\n\r\nconst zVector2 = z.union([\r\n z.object({ 0: z.number(), 1: z.number() }).transform((v) => [v[0], v[1]]),\r\n z.tuple([z.number(), z.number()])\r\n])\r\n\r\nconst zWidgetValues = z.union([z.array(z.any()), z.record(z.any())])\r\n\r\nconst zComfyNode = z\r\n .object({\r\n id: zNodeId,\r\n type: z.string(),\r\n pos: zVector2,\r\n size: zVector2,\r\n flags: zFlags,\r\n order: z.number(),\r\n mode: z.number(),\r\n inputs: z.array(zNodeInput).optional(),\r\n outputs: z.array(zNodeOutput).optional(),\r\n properties: zProperties,\r\n widgets_values: zWidgetValues.optional(),\r\n color: z.string().optional(),\r\n bgcolor: z.string().optional()\r\n })\r\n .passthrough()\r\n\r\nconst zGroup = z\r\n .object({\r\n title: z.string(),\r\n bounding: z.tuple([z.number(), z.number(), z.number(), z.number()]),\r\n color: z.string().optional(),\r\n font_size: z.number().optional(),\r\n locked: z.boolean().optional()\r\n })\r\n .passthrough()\r\n\r\nconst zInfo = z\r\n .object({\r\n name: z.string(),\r\n author: z.string(),\r\n description: z.string(),\r\n version: z.string(),\r\n created: z.string(),\r\n modified: z.string(),\r\n software: z.string()\r\n })\r\n .passthrough()\r\n\r\nconst zDS = z\r\n .object({\r\n scale: z.number(),\r\n offset: zVector2\r\n })\r\n .passthrough()\r\n\r\nconst zConfig = z\r\n .object({\r\n links_ontop: z.boolean().optional(),\r\n align_to_grid: z.boolean().optional()\r\n })\r\n .passthrough()\r\n\r\nconst zExtra = z\r\n .object({\r\n ds: zDS.optional(),\r\n info: zInfo.optional()\r\n })\r\n .passthrough()\r\n\r\nexport const zComfyWorkflow = z\r\n .object({\r\n last_node_id: zNodeId,\r\n last_link_id: z.number(),\r\n nodes: z.array(zComfyNode),\r\n links: z.array(zComfyLink),\r\n groups: z.array(zGroup).optional(),\r\n config: zConfig.optional().nullable(),\r\n extra: zExtra.optional().nullable(),\r\n version: z.number(),\r\n models: z.array(zModelFile).optional()\r\n })\r\n .passthrough()\r\n\r\nexport type NodeInput = z.infer\r\nexport type NodeOutput = z.infer\r\nexport type ComfyLink = z.infer\r\nexport type ComfyNode = z.infer\r\nexport type ComfyWorkflowJSON = z.infer\r\n\r\nexport async function validateComfyWorkflow(\r\n data: any,\r\n onError: (error: string) => void = console.warn\r\n): Promise {\r\n const result = await zComfyWorkflow.safeParseAsync(data)\r\n if (!result.success) {\r\n const error = fromZodError(result.error)\r\n onError(`Invalid workflow against zod schema:\\n${error}`)\r\n return null\r\n }\r\n return result.data\r\n}\r\n","import { z } from 'zod'\r\n\r\nconst nodeSlotSchema = z\r\n .object({\r\n BOOLEAN: z.string().optional(),\r\n CLIP: z.string(),\r\n CLIP_VISION: z.string(),\r\n CLIP_VISION_OUTPUT: z.string(),\r\n CONDITIONING: z.string(),\r\n CONTROL_NET: z.string(),\r\n CONTROL_NET_WEIGHTS: z.string().optional(),\r\n FLOAT: z.string().optional(),\r\n GLIGEN: z.string().optional(),\r\n IMAGE: z.string(),\r\n IMAGEUPLOAD: z.string().optional(),\r\n INT: z.string().optional(),\r\n LATENT: z.string(),\r\n LATENT_KEYFRAME: z.string().optional(),\r\n MASK: z.string(),\r\n MODEL: z.string(),\r\n SAMPLER: z.string().optional(),\r\n SIGMAS: z.string().optional(),\r\n STRING: z.string().optional(),\r\n STYLE_MODEL: z.string(),\r\n T2I_ADAPTER_WEIGHTS: z.string().optional(),\r\n TAESD: z.string(),\r\n TIMESTEP_KEYFRAME: z.string().optional(),\r\n UPSCALE_MODEL: z.string().optional(),\r\n VAE: z.string()\r\n })\r\n .passthrough()\r\n\r\nconst litegraphBaseSchema = z\r\n .object({\r\n BACKGROUND_IMAGE: z.string(),\r\n CLEAR_BACKGROUND_COLOR: z.string(),\r\n NODE_TITLE_COLOR: z.string(),\r\n NODE_SELECTED_TITLE_COLOR: z.string(),\r\n NODE_TEXT_SIZE: z.number(),\r\n NODE_TEXT_COLOR: z.string(),\r\n NODE_SUBTEXT_SIZE: z.number(),\r\n NODE_DEFAULT_COLOR: z.string(),\r\n NODE_DEFAULT_BGCOLOR: z.string(),\r\n NODE_DEFAULT_BOXCOLOR: z.string(),\r\n NODE_DEFAULT_SHAPE: z.string(),\r\n NODE_BOX_OUTLINE_COLOR: z.string(),\r\n DEFAULT_SHADOW_COLOR: z.string(),\r\n DEFAULT_GROUP_FONT: z.number(),\r\n WIDGET_BGCOLOR: z.string(),\r\n WIDGET_OUTLINE_COLOR: z.string(),\r\n WIDGET_TEXT_COLOR: z.string(),\r\n WIDGET_SECONDARY_TEXT_COLOR: z.string(),\r\n LINK_COLOR: z.string(),\r\n EVENT_LINK_COLOR: z.string(),\r\n CONNECTING_LINK_COLOR: z.string()\r\n })\r\n .passthrough()\r\n\r\nconst comfyBaseSchema = z.object({\r\n ['fg-color']: z.string(),\r\n ['bg-color']: z.string(),\r\n ['comfy-menu-bg']: z.string(),\r\n ['comfy-input-bg']: z.string(),\r\n ['input-text']: z.string(),\r\n ['descrip-text']: z.string(),\r\n ['drag-text']: z.string(),\r\n ['error-text']: z.string(),\r\n ['border-color']: z.string(),\r\n ['tr-even-bg-color']: z.string(),\r\n ['tr-odd-bg-color']: z.string(),\r\n ['content-bg']: z.string(),\r\n ['content-fg']: z.string(),\r\n ['content-hover-bg']: z.string(),\r\n ['content-hover-fg']: z.string()\r\n})\r\n\r\nconst colorsSchema = z\r\n .object({\r\n node_slot: nodeSlotSchema,\r\n litegraph_base: litegraphBaseSchema,\r\n comfy_base: comfyBaseSchema\r\n })\r\n .passthrough()\r\n\r\nconst paletteSchema = z.object({\r\n id: z.string(),\r\n name: z.string(),\r\n colors: colorsSchema\r\n})\r\n\r\nexport const colorPalettesSchema = z.record(paletteSchema)\r\n\r\nexport type Colors = z.infer\r\nexport type Palette = z.infer\r\nexport type ColorPalettes = z.infer\r\n","import { ZodType, z } from 'zod'\r\nimport { zComfyWorkflow, zNodeId } from './comfyWorkflow'\r\nimport { fromZodError } from 'zod-validation-error'\r\nimport { colorPalettesSchema } from './colorPalette'\r\n\r\nconst zNodeType = z.string()\r\nconst zQueueIndex = z.number()\r\nconst zPromptId = z.string()\r\nconst zResultItem = z.object({\r\n filename: z.string(),\r\n subfolder: z.string().optional(),\r\n type: z.string()\r\n})\r\nexport type ResultItem = z.infer\r\nconst zOutputs = z\r\n .object({\r\n audio: z.array(zResultItem).optional(),\r\n images: z.array(zResultItem).optional()\r\n })\r\n .passthrough()\r\n\r\n// WS messages\r\nconst zStatusWsMessageStatus = z.object({\r\n exec_info: z.object({\r\n queue_remaining: z.number().int()\r\n })\r\n})\r\n\r\nconst zStatusWsMessage = z.object({\r\n status: zStatusWsMessageStatus.nullable().optional()\r\n})\r\n\r\nconst zProgressWsMessage = z.object({\r\n value: z.number().int(),\r\n max: z.number().int(),\r\n prompt_id: zPromptId,\r\n node: zNodeId\r\n})\r\n\r\nconst zExecutingWsMessage = z.object({\r\n node: zNodeId,\r\n display_node: zNodeId,\r\n prompt_id: zPromptId\r\n})\r\n\r\nconst zExecutedWsMessage = zExecutingWsMessage.extend({\r\n outputs: zOutputs\r\n})\r\n\r\nconst zExecutionWsMessageBase = z.object({\r\n prompt_id: zPromptId,\r\n timestamp: z.number().int()\r\n})\r\n\r\nconst zExecutionStartWsMessage = zExecutionWsMessageBase\r\nconst zExecutionSuccessWsMessage = zExecutionWsMessageBase\r\nconst zExecutionCachedWsMessage = zExecutionWsMessageBase.extend({\r\n nodes: z.array(zNodeId)\r\n})\r\nconst zExecutionInterruptedWsMessage = zExecutionWsMessageBase.extend({\r\n node_id: zNodeId,\r\n node_type: zNodeType,\r\n executed: z.array(zNodeId)\r\n})\r\nconst zExecutionErrorWsMessage = zExecutionWsMessageBase.extend({\r\n node_id: zNodeId,\r\n node_type: zNodeType,\r\n executed: z.array(zNodeId),\r\n exception_message: z.string(),\r\n exception_type: z.string(),\r\n traceback: z.string(),\r\n current_inputs: z.any(),\r\n current_outputs: z.any()\r\n})\r\n\r\nexport type StatusWsMessageStatus = z.infer\r\nexport type StatusWsMessage = z.infer\r\nexport type ProgressWsMessage = z.infer\r\nexport type ExecutingWsMessage = z.infer\r\nexport type ExecutedWsMessage = z.infer\r\nexport type ExecutionStartWsMessage = z.infer\r\nexport type ExecutionSuccessWsMessage = z.infer<\r\n typeof zExecutionSuccessWsMessage\r\n>\r\nexport type ExecutionCachedWsMessage = z.infer\r\nexport type ExecutionInterruptedWsMessage = z.infer<\r\n typeof zExecutionInterruptedWsMessage\r\n>\r\nexport type ExecutionErrorWsMessage = z.infer\r\n// End of ws messages\r\n\r\nconst zPromptInputItem = z.object({\r\n inputs: z.record(z.string(), z.any()),\r\n class_type: zNodeType\r\n})\r\n\r\nconst zPromptInputs = z.record(zPromptInputItem)\r\n\r\nconst zExtraPngInfo = z\r\n .object({\r\n workflow: zComfyWorkflow\r\n })\r\n .passthrough()\r\n\r\nconst zExtraData = z.object({\r\n extra_pnginfo: zExtraPngInfo,\r\n client_id: z.string()\r\n})\r\nconst zOutputsToExecute = z.array(zNodeId)\r\n\r\nconst zExecutionStartMessage = z.tuple([\r\n z.literal('execution_start'),\r\n zExecutionStartWsMessage\r\n])\r\n\r\nconst zExecutionSuccessMessage = z.tuple([\r\n z.literal('execution_success'),\r\n zExecutionSuccessWsMessage\r\n])\r\n\r\nconst zExecutionCachedMessage = z.tuple([\r\n z.literal('execution_cached'),\r\n zExecutionCachedWsMessage\r\n])\r\n\r\nconst zExecutionInterruptedMessage = z.tuple([\r\n z.literal('execution_interrupted'),\r\n zExecutionInterruptedWsMessage\r\n])\r\n\r\nconst zExecutionErrorMessage = z.tuple([\r\n z.literal('execution_error'),\r\n zExecutionErrorWsMessage\r\n])\r\n\r\nconst zStatusMessage = z.union([\r\n zExecutionStartMessage,\r\n zExecutionSuccessMessage,\r\n zExecutionCachedMessage,\r\n zExecutionInterruptedMessage,\r\n zExecutionErrorMessage\r\n])\r\n\r\nconst zStatus = z.object({\r\n status_str: z.enum(['success', 'error']),\r\n completed: z.boolean(),\r\n messages: z.array(zStatusMessage)\r\n})\r\n\r\nconst zTaskPrompt = z.tuple([\r\n zQueueIndex,\r\n zPromptId,\r\n zPromptInputs,\r\n zExtraData,\r\n zOutputsToExecute\r\n])\r\n\r\nconst zRunningTaskItem = z.object({\r\n taskType: z.literal('Running'),\r\n prompt: zTaskPrompt,\r\n // @Deprecated\r\n remove: z.object({\r\n name: z.literal('Cancel'),\r\n cb: z.function()\r\n })\r\n})\r\n\r\nconst zPendingTaskItem = z.object({\r\n taskType: z.literal('Pending'),\r\n prompt: zTaskPrompt\r\n})\r\n\r\nconst zTaskOutput = z.record(zNodeId, zOutputs)\r\n\r\nconst zHistoryTaskItem = z.object({\r\n taskType: z.literal('History'),\r\n prompt: zTaskPrompt,\r\n status: zStatus.optional(),\r\n outputs: zTaskOutput\r\n})\r\n\r\nconst zTaskItem = z.union([\r\n zRunningTaskItem,\r\n zPendingTaskItem,\r\n zHistoryTaskItem\r\n])\r\n\r\nconst zTaskType = z.union([\r\n z.literal('Running'),\r\n z.literal('Pending'),\r\n z.literal('History')\r\n])\r\n\r\nexport type TaskType = z.infer\r\nexport type TaskPrompt = z.infer\r\nexport type TaskStatus = z.infer\r\nexport type TaskOutput = z.infer\r\n\r\n// `/queue`\r\nexport type RunningTaskItem = z.infer\r\nexport type PendingTaskItem = z.infer\r\n// `/history`\r\nexport type HistoryTaskItem = z.infer\r\nexport type TaskItem = z.infer\r\n\r\nexport function validateTaskItem(taskItem: unknown) {\r\n const result = zTaskItem.safeParse(taskItem)\r\n if (!result.success) {\r\n const zodError = fromZodError(result.error)\r\n // TODO accept a callback to report error.\r\n console.warn(\r\n `Invalid TaskItem: ${JSON.stringify(taskItem)}\\n${zodError.message}`\r\n )\r\n }\r\n return result\r\n}\r\n\r\nfunction inputSpec(\r\n spec: [ZodType, ZodType],\r\n allowUpcast: boolean = true\r\n): ZodType {\r\n const [inputType, inputSpec] = spec\r\n // e.g. \"INT\" => [\"INT\", {}]\r\n const upcastTypes: ZodType[] = allowUpcast\r\n ? [inputType.transform((type) => [type, {}])]\r\n : []\r\n\r\n return z.union([\r\n z.tuple([inputType, inputSpec]),\r\n z.tuple([inputType]).transform(([type]) => [type, {}]),\r\n ...upcastTypes\r\n ])\r\n}\r\n\r\nconst zBaseInputSpecValue = z\r\n .object({\r\n default: z.any().optional(),\r\n forceInput: z.boolean().optional()\r\n })\r\n .passthrough()\r\n\r\nconst zIntInputSpec = inputSpec([\r\n z.literal('INT'),\r\n zBaseInputSpecValue.extend({\r\n min: z.number().optional(),\r\n max: z.number().optional(),\r\n step: z.number().optional(),\r\n // Note: Many node authors are using INT to pass list of INT.\r\n // TODO: Add list of ints type.\r\n default: z.union([z.number(), z.array(z.number())]).optional()\r\n })\r\n])\r\n\r\nconst zFloatInputSpec = inputSpec([\r\n z.literal('FLOAT'),\r\n zBaseInputSpecValue.extend({\r\n min: z.number().optional(),\r\n max: z.number().optional(),\r\n step: z.number().optional(),\r\n round: z.union([z.number(), z.literal(false)]).optional(),\r\n // Note: Many node authors are using FLOAT to pass list of FLOAT.\r\n // TODO: Add list of floats type.\r\n default: z.union([z.number(), z.array(z.number())]).optional()\r\n })\r\n])\r\n\r\nconst zBooleanInputSpec = inputSpec([\r\n z.literal('BOOLEAN'),\r\n zBaseInputSpecValue.extend({\r\n label_on: z.string().optional(),\r\n label_off: z.string().optional(),\r\n default: z.boolean().optional()\r\n })\r\n])\r\n\r\nconst zStringInputSpec = inputSpec([\r\n z.literal('STRING'),\r\n zBaseInputSpecValue.extend({\r\n default: z.string().optional(),\r\n multiline: z.boolean().optional(),\r\n dynamicPrompts: z.boolean().optional()\r\n })\r\n])\r\n\r\n// Dropdown Selection.\r\nconst zComboInputSpec = inputSpec(\r\n [\r\n z.array(z.any()),\r\n zBaseInputSpecValue.extend({\r\n control_after_generate: z.boolean().optional(),\r\n image_upload: z.boolean().optional()\r\n })\r\n ],\r\n /* allowUpcast=*/ false\r\n)\r\n\r\nconst excludedLiterals = new Set(['INT', 'FLOAT', 'BOOLEAN', 'STRING', 'COMBO'])\r\n\r\nconst zCustomInputSpec = inputSpec([\r\n z.string().refine((value) => !excludedLiterals.has(value)),\r\n zBaseInputSpecValue\r\n])\r\n\r\nconst zInputSpec = z.union([\r\n zIntInputSpec,\r\n zFloatInputSpec,\r\n zBooleanInputSpec,\r\n zStringInputSpec,\r\n zComboInputSpec,\r\n zCustomInputSpec\r\n])\r\n\r\nconst zComfyInputsSpec = z.object({\r\n required: z.record(zInputSpec).optional(),\r\n optional: z.record(zInputSpec).optional(),\r\n // Frontend repo is not using it, but some custom nodes are using the\r\n // hidden field to pass various values.\r\n hidden: z.record(z.any()).optional()\r\n})\r\n\r\nconst zComfyNodeDataType = z.string()\r\nconst zComfyComboOutput = z.array(z.any())\r\nconst zComfyOutputTypesSpec = z.array(\r\n z.union([zComfyNodeDataType, zComfyComboOutput])\r\n)\r\n\r\nconst zComfyNodeDef = z.object({\r\n input: zComfyInputsSpec,\r\n output: zComfyOutputTypesSpec,\r\n output_is_list: z.array(z.boolean()),\r\n output_name: z.array(z.string()),\r\n output_tooltips: z.array(z.string()).optional(),\r\n name: z.string(),\r\n display_name: z.string(),\r\n description: z.string(),\r\n category: z.string(),\r\n output_node: z.boolean(),\r\n python_module: z.string()\r\n})\r\n\r\n// `/object_info`\r\nexport type ComfyInputsSpec = z.infer\r\nexport type ComfyOutputTypesSpec = z.infer\r\nexport type ComfyNodeDef = z.infer\r\n\r\nexport function validateComfyNodeDef(\r\n data: any,\r\n onError: (error: string) => void = console.warn\r\n): ComfyNodeDef | null {\r\n const result = zComfyNodeDef.safeParse(data)\r\n if (!result.success) {\r\n const zodError = fromZodError(result.error)\r\n onError(\r\n `Invalid ComfyNodeDef: ${JSON.stringify(data)}\\n${zodError.message}`\r\n )\r\n return null\r\n }\r\n return result.data\r\n}\r\n\r\nconst zEmbeddingsResponse = z.array(z.string())\r\nconst zExtensionsResponse = z.array(z.string())\r\nconst zPromptResponse = z.object({\r\n node_errors: z.array(z.string()).optional(),\r\n prompt_id: z.string().optional(),\r\n exec_info: z\r\n .object({\r\n queue_remaining: z.number().optional()\r\n })\r\n .optional()\r\n})\r\nexport const zSystemStats = z.object({\r\n system: z.object({\r\n os: z.string(),\r\n python_version: z.string(),\r\n embedded_python: z.boolean()\r\n }),\r\n devices: z.array(\r\n z.object({\r\n name: z.string(),\r\n type: z.string(),\r\n index: z.number().optional(),\r\n vram_total: z.number(),\r\n vram_free: z.number(),\r\n torch_vram_total: z.number(),\r\n torch_vram_free: z.number()\r\n })\r\n )\r\n})\r\nconst zUser = z.object({\r\n storage: z.enum(['server', 'browser']),\r\n migrated: z.boolean(),\r\n users: z.record(z.string(), z.unknown())\r\n})\r\nconst zUserData = z.array(z.array(z.string(), z.string()))\r\nconst zSettings = z.record(z.any()).and(\r\n z\r\n .object({\r\n 'Comfy.ColorPalette': z.string(),\r\n 'Comfy.CustomColorPalettes': colorPalettesSchema,\r\n 'Comfy.ConfirmClear': z.boolean(),\r\n 'Comfy.DevMode': z.boolean(),\r\n 'Comfy.DisableFloatRounding': z.boolean(),\r\n 'Comfy.DisableSliders': z.boolean(),\r\n 'Comfy.DOMClippingEnabled': z.boolean(),\r\n 'Comfy.EditAttention.Delta': z.number(),\r\n 'Comfy.EnableTooltips': z.boolean(),\r\n 'Comfy.EnableWorkflowViewRestore': z.boolean(),\r\n 'Comfy.FloatRoundingPrecision': z.number(),\r\n 'Comfy.InvertMenuScrolling': z.boolean(),\r\n 'Comfy.Logging.Enabled': z.boolean(),\r\n 'Comfy.NodeInputConversionSubmenus': z.boolean(),\r\n 'Comfy.NodeSearchBoxImpl.LinkReleaseTrigger': z.enum([\r\n 'always',\r\n 'hold shift',\r\n 'NOT hold shift'\r\n ]),\r\n 'Comfy.NodeSearchBoxImpl.NodePreview': z.boolean(),\r\n 'Comfy.NodeSearchBoxImpl': z.enum(['default', 'simple']),\r\n 'Comfy.NodeSuggestions.number': z.number(),\r\n 'Comfy.PreviewFormat': z.string(),\r\n 'Comfy.PromptFilename': z.boolean(),\r\n 'Comfy.Sidebar.Location': z.enum(['left', 'right']),\r\n 'Comfy.Sidebar.Size': z.number(),\r\n 'Comfy.SwitchUser': z.any(),\r\n 'Comfy.SnapToGrid.GridSize': z.number(),\r\n 'Comfy.TextareaWidget.FontSize': z.number(),\r\n 'Comfy.UseNewMenu': z.any(),\r\n 'Comfy.Validation.Workflows': z.boolean()\r\n })\r\n .optional()\r\n)\r\n\r\nexport type EmbeddingsResponse = z.infer\r\nexport type ExtensionsResponse = z.infer\r\nexport type PromptResponse = z.infer\r\nexport type Settings = z.infer\r\nexport type SystemStats = z.infer\r\nexport type User = z.infer\r\nexport type UserData = z.infer\r\n","import { ComfyWorkflowJSON } from '@/types/comfyWorkflow'\r\nimport {\r\n HistoryTaskItem,\r\n PendingTaskItem,\r\n RunningTaskItem,\r\n ComfyNodeDef,\r\n validateComfyNodeDef,\r\n EmbeddingsResponse,\r\n ExtensionsResponse,\r\n PromptResponse,\r\n SystemStats,\r\n User,\r\n Settings\r\n} from '@/types/apiTypes'\r\n\r\ninterface QueuePromptRequestBody {\r\n client_id: string\r\n // Mapping from node id to node info + input values\r\n // TODO: Type this.\r\n prompt: Record\r\n extra_data: {\r\n extra_pnginfo: {\r\n workflow: ComfyWorkflowJSON\r\n }\r\n }\r\n front?: boolean\r\n number?: number\r\n}\r\n\r\nclass ComfyApi extends EventTarget {\r\n #registered = new Set()\r\n api_host: string\r\n api_base: string\r\n initialClientId: string\r\n user: string\r\n socket?: WebSocket\r\n clientId?: string\r\n\r\n reportedUnknownMessageTypes = new Set()\r\n\r\n constructor() {\r\n super()\r\n this.api_host = location.host\r\n this.api_base = location.pathname.split('/').slice(0, -1).join('/')\r\n this.initialClientId = sessionStorage.getItem('clientId')\r\n }\r\n\r\n apiURL(route: string): string {\r\n return this.api_base + '/api' + route\r\n }\r\n\r\n fileURL(route: string): string {\r\n return this.api_base + route\r\n }\r\n\r\n fetchApi(route: string, options?: RequestInit) {\r\n if (!options) {\r\n options = {}\r\n }\r\n if (!options.headers) {\r\n options.headers = {}\r\n }\r\n if (!options.cache) {\r\n options.cache = 'no-cache'\r\n }\r\n options.headers['Comfy-User'] = this.user\r\n return fetch(this.apiURL(route), options)\r\n }\r\n\r\n addEventListener(\r\n type: string,\r\n callback: any,\r\n options?: AddEventListenerOptions\r\n ) {\r\n super.addEventListener(type, callback, options)\r\n this.#registered.add(type)\r\n }\r\n\r\n /**\r\n * Poll status for colab and other things that don't support websockets.\r\n */\r\n #pollQueue() {\r\n setInterval(async () => {\r\n try {\r\n const resp = await this.fetchApi('/prompt')\r\n const status = await resp.json()\r\n this.dispatchEvent(new CustomEvent('status', { detail: status }))\r\n } catch (error) {\r\n this.dispatchEvent(new CustomEvent('status', { detail: null }))\r\n }\r\n }, 1000)\r\n }\r\n\r\n /**\r\n * Creates and connects a WebSocket for realtime updates\r\n * @param {boolean} isReconnect If the socket is connection is a reconnect attempt\r\n */\r\n #createSocket(isReconnect?: boolean) {\r\n if (this.socket) {\r\n return\r\n }\r\n\r\n let opened = false\r\n let existingSession = window.name\r\n if (existingSession) {\r\n existingSession = '?clientId=' + existingSession\r\n }\r\n this.socket = new WebSocket(\r\n `ws${window.location.protocol === 'https:' ? 's' : ''}://${this.api_host}${this.api_base}/ws${existingSession}`\r\n )\r\n this.socket.binaryType = 'arraybuffer'\r\n\r\n this.socket.addEventListener('open', () => {\r\n opened = true\r\n if (isReconnect) {\r\n this.dispatchEvent(new CustomEvent('reconnected'))\r\n }\r\n })\r\n\r\n this.socket.addEventListener('error', () => {\r\n if (this.socket) this.socket.close()\r\n if (!isReconnect && !opened) {\r\n this.#pollQueue()\r\n }\r\n })\r\n\r\n this.socket.addEventListener('close', () => {\r\n setTimeout(() => {\r\n this.socket = null\r\n this.#createSocket(true)\r\n }, 300)\r\n if (opened) {\r\n this.dispatchEvent(new CustomEvent('status', { detail: null }))\r\n this.dispatchEvent(new CustomEvent('reconnecting'))\r\n }\r\n })\r\n\r\n this.socket.addEventListener('message', (event) => {\r\n try {\r\n if (event.data instanceof ArrayBuffer) {\r\n const view = new DataView(event.data)\r\n const eventType = view.getUint32(0)\r\n const buffer = event.data.slice(4)\r\n switch (eventType) {\r\n case 1:\r\n const view2 = new DataView(event.data)\r\n const imageType = view2.getUint32(0)\r\n let imageMime\r\n switch (imageType) {\r\n case 1:\r\n default:\r\n imageMime = 'image/jpeg'\r\n break\r\n case 2:\r\n imageMime = 'image/png'\r\n }\r\n const imageBlob = new Blob([buffer.slice(4)], {\r\n type: imageMime\r\n })\r\n this.dispatchEvent(\r\n new CustomEvent('b_preview', { detail: imageBlob })\r\n )\r\n break\r\n default:\r\n throw new Error(\r\n `Unknown binary websocket message of type ${eventType}`\r\n )\r\n }\r\n } else {\r\n const msg = JSON.parse(event.data)\r\n switch (msg.type) {\r\n case 'status':\r\n if (msg.data.sid) {\r\n this.clientId = msg.data.sid\r\n window.name = this.clientId // use window name so it isnt reused when duplicating tabs\r\n sessionStorage.setItem('clientId', this.clientId) // store in session storage so duplicate tab can load correct workflow\r\n }\r\n this.dispatchEvent(\r\n new CustomEvent('status', { detail: msg.data.status })\r\n )\r\n break\r\n case 'progress':\r\n this.dispatchEvent(\r\n new CustomEvent('progress', { detail: msg.data })\r\n )\r\n break\r\n case 'executing':\r\n this.dispatchEvent(\r\n new CustomEvent('executing', {\r\n detail: msg.data.display_node || msg.data.node\r\n })\r\n )\r\n break\r\n case 'executed':\r\n this.dispatchEvent(\r\n new CustomEvent('executed', { detail: msg.data })\r\n )\r\n break\r\n case 'execution_start':\r\n this.dispatchEvent(\r\n new CustomEvent('execution_start', { detail: msg.data })\r\n )\r\n break\r\n case 'execution_success':\r\n this.dispatchEvent(\r\n new CustomEvent('execution_success', { detail: msg.data })\r\n )\r\n break\r\n case 'execution_error':\r\n this.dispatchEvent(\r\n new CustomEvent('execution_error', { detail: msg.data })\r\n )\r\n break\r\n case 'execution_cached':\r\n this.dispatchEvent(\r\n new CustomEvent('execution_cached', { detail: msg.data })\r\n )\r\n break\r\n default:\r\n if (this.#registered.has(msg.type)) {\r\n this.dispatchEvent(\r\n new CustomEvent(msg.type, { detail: msg.data })\r\n )\r\n } else if (!this.reportedUnknownMessageTypes.has(msg.type)) {\r\n this.reportedUnknownMessageTypes.add(msg.type)\r\n throw new Error(`Unknown message type ${msg.type}`)\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.warn('Unhandled message:', event.data, error)\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * Initialises sockets and realtime updates\r\n */\r\n init() {\r\n this.#createSocket()\r\n }\r\n\r\n /**\r\n * Gets a list of extension urls\r\n */\r\n async getExtensions(): Promise {\r\n const resp = await this.fetchApi('/extensions', { cache: 'no-store' })\r\n return await resp.json()\r\n }\r\n\r\n /**\r\n * Gets a list of embedding names\r\n */\r\n async getEmbeddings(): Promise {\r\n const resp = await this.fetchApi('/embeddings', { cache: 'no-store' })\r\n return await resp.json()\r\n }\r\n\r\n /**\r\n * Loads node object definitions for the graph\r\n * @returns The node definitions\r\n */\r\n async getNodeDefs(): Promise> {\r\n const resp = await this.fetchApi('/object_info', { cache: 'no-store' })\r\n const objectInfoUnsafe = await resp.json()\r\n const objectInfo: Record = {}\r\n for (const key in objectInfoUnsafe) {\r\n const validatedDef = validateComfyNodeDef(\r\n objectInfoUnsafe[key],\r\n /* onError=*/ (errorMessage: string) => {\r\n console.warn(\r\n `Skipping invalid node definition: ${key}. See debug log for more information.`\r\n )\r\n console.debug(errorMessage)\r\n }\r\n )\r\n if (validatedDef !== null) {\r\n objectInfo[key] = validatedDef\r\n }\r\n }\r\n return objectInfo\r\n }\r\n\r\n /**\r\n *\r\n * @param {number} number The index at which to queue the prompt, passing -1 will insert the prompt at the front of the queue\r\n * @param {object} prompt The prompt data to queue\r\n */\r\n async queuePrompt(\r\n number: number,\r\n { output, workflow }\r\n ): Promise {\r\n const body: QueuePromptRequestBody = {\r\n client_id: this.clientId,\r\n prompt: output,\r\n extra_data: { extra_pnginfo: { workflow } }\r\n }\r\n\r\n if (number === -1) {\r\n body.front = true\r\n } else if (number != 0) {\r\n body.number = number\r\n }\r\n\r\n const res = await this.fetchApi('/prompt', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json'\r\n },\r\n body: JSON.stringify(body)\r\n })\r\n\r\n if (res.status !== 200) {\r\n throw {\r\n response: await res.json()\r\n }\r\n }\r\n\r\n return await res.json()\r\n }\r\n\r\n /**\r\n * Loads a list of items (queue or history)\r\n * @param {string} type The type of items to load, queue or history\r\n * @returns The items of the specified type grouped by their status\r\n */\r\n async getItems(type: 'queue' | 'history') {\r\n if (type === 'queue') {\r\n return this.getQueue()\r\n }\r\n return this.getHistory()\r\n }\r\n\r\n /**\r\n * Gets the current state of the queue\r\n * @returns The currently running and queued items\r\n */\r\n async getQueue(): Promise<{\r\n Running: RunningTaskItem[]\r\n Pending: PendingTaskItem[]\r\n }> {\r\n try {\r\n const res = await this.fetchApi('/queue')\r\n const data = await res.json()\r\n return {\r\n // Running action uses a different endpoint for cancelling\r\n Running: data.queue_running.map((prompt) => ({\r\n taskType: 'Running',\r\n prompt,\r\n remove: { name: 'Cancel', cb: () => api.interrupt() }\r\n })),\r\n Pending: data.queue_pending.map((prompt) => ({\r\n taskType: 'Pending',\r\n prompt\r\n }))\r\n }\r\n } catch (error) {\r\n console.error(error)\r\n return { Running: [], Pending: [] }\r\n }\r\n }\r\n\r\n /**\r\n * Gets the prompt execution history\r\n * @returns Prompt history including node outputs\r\n */\r\n async getHistory(\r\n max_items: number = 200\r\n ): Promise<{ History: HistoryTaskItem[] }> {\r\n try {\r\n const res = await this.fetchApi(`/history?max_items=${max_items}`)\r\n const json: Promise = await res.json()\r\n return {\r\n History: Object.values(json).map((item) => ({\r\n ...item,\r\n taskType: 'History'\r\n }))\r\n }\r\n } catch (error) {\r\n console.error(error)\r\n return { History: [] }\r\n }\r\n }\r\n\r\n /**\r\n * Gets system & device stats\r\n * @returns System stats such as python version, OS, per device info\r\n */\r\n async getSystemStats(): Promise {\r\n const res = await this.fetchApi('/system_stats')\r\n return await res.json()\r\n }\r\n\r\n /**\r\n * Sends a POST request to the API\r\n * @param {*} type The endpoint to post to\r\n * @param {*} body Optional POST data\r\n */\r\n async #postItem(type: string, body: any) {\r\n try {\r\n await this.fetchApi('/' + type, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json'\r\n },\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n } catch (error) {\r\n console.error(error)\r\n }\r\n }\r\n\r\n /**\r\n * Deletes an item from the specified list\r\n * @param {string} type The type of item to delete, queue or history\r\n * @param {number} id The id of the item to delete\r\n */\r\n async deleteItem(type: string, id: string) {\r\n await this.#postItem(type, { delete: [id] })\r\n }\r\n\r\n /**\r\n * Clears the specified list\r\n * @param {string} type The type of list to clear, queue or history\r\n */\r\n async clearItems(type: string) {\r\n await this.#postItem(type, { clear: true })\r\n }\r\n\r\n /**\r\n * Interrupts the execution of the running prompt\r\n */\r\n async interrupt() {\r\n await this.#postItem('interrupt', null)\r\n }\r\n\r\n /**\r\n * Gets user configuration data and where data should be stored\r\n */\r\n async getUserConfig(): Promise {\r\n return (await this.fetchApi('/users')).json()\r\n }\r\n\r\n /**\r\n * Creates a new user\r\n * @param { string } username\r\n * @returns The fetch response\r\n */\r\n createUser(username: string) {\r\n return this.fetchApi('/users', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json'\r\n },\r\n body: JSON.stringify({ username })\r\n })\r\n }\r\n\r\n /**\r\n * Gets all setting values for the current user\r\n * @returns { Promise } A dictionary of id -> value\r\n */\r\n async getSettings(): Promise {\r\n return (await this.fetchApi('/settings')).json()\r\n }\r\n\r\n /**\r\n * Gets a setting for the current user\r\n * @param { string } id The id of the setting to fetch\r\n * @returns { Promise } The setting value\r\n */\r\n async getSetting(id: keyof Settings): Promise {\r\n return (await this.fetchApi(`/settings/${encodeURIComponent(id)}`)).json()\r\n }\r\n\r\n /**\r\n * Stores a dictionary of settings for the current user\r\n */\r\n async storeSettings(settings: Settings) {\r\n return this.fetchApi(`/settings`, {\r\n method: 'POST',\r\n body: JSON.stringify(settings)\r\n })\r\n }\r\n\r\n /**\r\n * Stores a setting for the current user\r\n */\r\n async storeSetting(id: keyof Settings, value: Settings[keyof Settings]) {\r\n return this.fetchApi(`/settings/${encodeURIComponent(id)}`, {\r\n method: 'POST',\r\n body: JSON.stringify(value)\r\n })\r\n }\r\n\r\n /**\r\n * Gets a user data file for the current user\r\n */\r\n async getUserData(file: string, options?: RequestInit) {\r\n return this.fetchApi(`/userdata/${encodeURIComponent(file)}`, options)\r\n }\r\n\r\n /**\r\n * Stores a user data file for the current user\r\n * @param { string } file The name of the userdata file to save\r\n * @param { unknown } data The data to save to the file\r\n * @param { RequestInit & { stringify?: boolean, throwOnError?: boolean } } [options]\r\n * @returns { Promise }\r\n */\r\n async storeUserData(\r\n file: string,\r\n data: any,\r\n options: RequestInit & {\r\n overwrite?: boolean\r\n stringify?: boolean\r\n throwOnError?: boolean\r\n } = { overwrite: true, stringify: true, throwOnError: true }\r\n ): Promise {\r\n const resp = await this.fetchApi(\r\n `/userdata/${encodeURIComponent(file)}?overwrite=${options.overwrite}`,\r\n {\r\n method: 'POST',\r\n body: options?.stringify ? JSON.stringify(data) : data,\r\n ...options\r\n }\r\n )\r\n if (resp.status !== 200 && options.throwOnError !== false) {\r\n throw new Error(\r\n `Error storing user data file '${file}': ${resp.status} ${(await resp).statusText}`\r\n )\r\n }\r\n\r\n return resp\r\n }\r\n\r\n /**\r\n * Deletes a user data file for the current user\r\n * @param { string } file The name of the userdata file to delete\r\n */\r\n async deleteUserData(file: string) {\r\n const resp = await this.fetchApi(`/userdata/${encodeURIComponent(file)}`, {\r\n method: 'DELETE'\r\n })\r\n if (resp.status !== 204) {\r\n throw new Error(\r\n `Error removing user data file '${file}': ${resp.status} ${resp.statusText}`\r\n )\r\n }\r\n }\r\n\r\n /**\r\n * Move a user data file for the current user\r\n * @param { string } source The userdata file to move\r\n * @param { string } dest The destination for the file\r\n */\r\n async moveUserData(\r\n source: string,\r\n dest: string,\r\n options = { overwrite: false }\r\n ) {\r\n const resp = await this.fetchApi(\r\n `/userdata/${encodeURIComponent(source)}/move/${encodeURIComponent(dest)}?overwrite=${options?.overwrite}`,\r\n {\r\n method: 'POST'\r\n }\r\n )\r\n return resp\r\n }\r\n\r\n /**\r\n * @overload\r\n * Lists user data files for the current user\r\n * @param { string } dir The directory in which to list files\r\n * @param { boolean } [recurse] If the listing should be recursive\r\n * @param { true } [split] If the paths should be split based on the os path separator\r\n * @returns { Promise } The list of split file paths in the format [fullPath, ...splitPath]\r\n */\r\n /**\r\n * @overload\r\n * Lists user data files for the current user\r\n * @param { string } dir The directory in which to list files\r\n * @param { boolean } [recurse] If the listing should be recursive\r\n * @param { false | undefined } [split] If the paths should be split based on the os path separator\r\n * @returns { Promise } The list of files\r\n */\r\n async listUserData(\r\n dir: string,\r\n recurse: true,\r\n split?: boolean\r\n ): Promise\r\n async listUserData(\r\n dir: string,\r\n recurse: false,\r\n split?: boolean\r\n ): Promise\r\n async listUserData(dir, recurse, split) {\r\n const resp = await this.fetchApi(\r\n `/userdata?${new URLSearchParams({\r\n recurse,\r\n dir,\r\n split\r\n })}`\r\n )\r\n if (resp.status === 404) return []\r\n if (resp.status !== 200) {\r\n throw new Error(\r\n `Error getting user data list '${dir}': ${resp.status} ${resp.statusText}`\r\n )\r\n }\r\n return resp.json()\r\n }\r\n}\r\n\r\nexport const api = new ComfyApi()\r\n","import { useDialogStore } from '@/stores/dialogStore'\r\nimport { $el } from '../ui'\r\n\r\nexport class ComfyDialog<\r\n T extends HTMLElement = HTMLElement\r\n> extends EventTarget {\r\n element: T\r\n textElement: HTMLElement\r\n #buttons: HTMLButtonElement[] | null\r\n\r\n constructor(type = 'div', buttons = null) {\r\n super()\r\n this.#buttons = buttons\r\n this.element = $el(type + '.comfy-modal', { parent: document.body }, [\r\n $el('div.comfy-modal-content', [\r\n $el('p', { $: (p) => (this.textElement = p) }),\r\n ...this.createButtons()\r\n ])\r\n ]) as T\r\n }\r\n\r\n createButtons() {\r\n return (\r\n this.#buttons ?? [\r\n $el('button', {\r\n type: 'button',\r\n textContent: 'Close',\r\n onclick: () => this.close()\r\n })\r\n ]\r\n )\r\n }\r\n\r\n close() {\r\n this.element.style.display = 'none'\r\n }\r\n\r\n show(html) {\r\n if (typeof html === 'string') {\r\n this.textElement.innerHTML = html\r\n } else {\r\n this.textElement.replaceChildren(\r\n ...(html instanceof Array ? html : [html])\r\n )\r\n }\r\n this.element.style.display = 'flex'\r\n }\r\n}\r\n","import { $el } from '../ui'\r\n\r\n/**\r\n * @typedef { { text: string, value?: string, tooltip?: string } } ToggleSwitchItem\r\n */\r\n/**\r\n * Creates a toggle switch element\r\n * @param { string } name\r\n * @param { Array | ToggleSwitchItem } items\r\n * @param { Object } [opts]\r\n * @param { (e: { item: ToggleSwitchItem, prev?: ToggleSwitchItem }) => void } [opts.onChange]\r\n */\r\nexport function toggleSwitch(name, items, e?) {\r\n const onChange = e?.onChange\r\n\r\n let selectedIndex\r\n let elements\r\n\r\n function updateSelected(index) {\r\n if (selectedIndex != null) {\r\n elements[selectedIndex].classList.remove('comfy-toggle-selected')\r\n }\r\n onChange?.({\r\n item: items[index],\r\n prev: selectedIndex == null ? undefined : items[selectedIndex]\r\n })\r\n selectedIndex = index\r\n elements[selectedIndex].classList.add('comfy-toggle-selected')\r\n }\r\n\r\n elements = items.map((item, i) => {\r\n if (typeof item === 'string') item = { text: item }\r\n if (!item.value) item.value = item.text\r\n\r\n const toggle = $el(\r\n 'label',\r\n {\r\n textContent: item.text,\r\n title: item.tooltip ?? ''\r\n },\r\n $el('input', {\r\n name,\r\n type: 'radio',\r\n value: item.value ?? item.text,\r\n checked: item.selected,\r\n onchange: () => {\r\n updateSelected(i)\r\n }\r\n })\r\n )\r\n if (item.selected) {\r\n updateSelected(i)\r\n }\r\n return toggle\r\n })\r\n\r\n const container = $el('div.comfy-toggle-switch', elements)\r\n\r\n if (selectedIndex == null) {\r\n elements[0].children[0].checked = true\r\n updateSelected(0)\r\n }\r\n\r\n return container\r\n}\r\n","import { $el } from '../ui'\r\nimport { api } from '../api'\r\nimport { ComfyDialog } from './dialog'\r\nimport type { ComfyApp } from '../app'\r\nimport type { Setting, SettingParams } from '@/types/settingTypes'\r\nimport { useSettingStore } from '@/stores/settingStore'\r\nimport { Settings } from '@/types/apiTypes'\r\n\r\nexport class ComfySettingsDialog extends ComfyDialog {\r\n app: ComfyApp\r\n settingsValues: any\r\n settingsLookup: Record\r\n settingsParamLookup: Record\r\n\r\n constructor(app: ComfyApp) {\r\n super()\r\n const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__']\r\n this.app = app\r\n this.settingsValues = {}\r\n this.settingsLookup = {}\r\n this.settingsParamLookup = {}\r\n this.element = $el(\r\n 'dialog',\r\n {\r\n id: 'comfy-settings-dialog',\r\n parent: document.body\r\n },\r\n [\r\n $el('table.comfy-modal-content.comfy-table', [\r\n $el(\r\n 'caption',\r\n { textContent: `Settings (v${frontendVersion})` },\r\n $el('button.comfy-btn', {\r\n type: 'button',\r\n textContent: '\\u00d7',\r\n onclick: () => {\r\n this.element.close()\r\n }\r\n })\r\n ),\r\n $el('tbody', { $: (tbody) => (this.textElement = tbody) }),\r\n $el('button', {\r\n type: 'button',\r\n textContent: 'Close',\r\n style: {\r\n cursor: 'pointer'\r\n },\r\n onclick: () => {\r\n this.element.close()\r\n }\r\n })\r\n ])\r\n ]\r\n ) as HTMLDialogElement\r\n }\r\n\r\n get settings() {\r\n return Object.values(this.settingsLookup)\r\n }\r\n\r\n #dispatchChange(id: string, value: T, oldValue?: T) {\r\n // Keep the settingStore updated. Not using `store.set` as it would trigger\r\n // setSettingValue again.\r\n // `load` re-dispatch the change for any settings added before load so\r\n // settingStore is always up to date.\r\n if (this.app.vueAppReady) {\r\n useSettingStore().settingValues[id] = value\r\n }\r\n\r\n this.dispatchEvent(\r\n new CustomEvent(id + '.change', {\r\n detail: {\r\n value,\r\n oldValue\r\n }\r\n })\r\n )\r\n }\r\n\r\n async load() {\r\n if (this.app.storageLocation === 'browser') {\r\n this.settingsValues = localStorage\r\n } else {\r\n this.settingsValues = await api.getSettings()\r\n }\r\n\r\n // Trigger onChange for any settings added before load\r\n for (const id in this.settingsLookup) {\r\n const value = this.settingsValues[this.getId(id)]\r\n this.settingsLookup[id].onChange?.(value)\r\n this.#dispatchChange(id, value)\r\n }\r\n }\r\n\r\n getId(id: string) {\r\n if (this.app.storageLocation === 'browser') {\r\n id = 'Comfy.Settings.' + id\r\n }\r\n return id\r\n }\r\n\r\n getSettingValue(\r\n id: K,\r\n defaultValue?: Settings[K]\r\n ): Settings[K] {\r\n let value = this.settingsValues[this.getId(id)]\r\n if (value != null) {\r\n if (this.app.storageLocation === 'browser') {\r\n try {\r\n value = JSON.parse(value)\r\n } catch (error) {}\r\n }\r\n }\r\n return (value ?? defaultValue) as Settings[K]\r\n }\r\n\r\n getSettingDefaultValue(id: string) {\r\n const param = this.settingsParamLookup[id]\r\n return param?.defaultValue\r\n }\r\n\r\n async setSettingValueAsync(\r\n id: K,\r\n value: Settings[K]\r\n ) {\r\n const json = JSON.stringify(value)\r\n localStorage['Comfy.Settings.' + id] = json // backwards compatibility for extensions keep setting in storage\r\n\r\n let oldValue = this.getSettingValue(id, undefined)\r\n this.settingsValues[this.getId(id)] = value\r\n\r\n if (id in this.settingsLookup) {\r\n this.settingsLookup[id].onChange?.(value, oldValue)\r\n }\r\n this.#dispatchChange(id, value, oldValue)\r\n\r\n await api.storeSetting(id, value)\r\n }\r\n\r\n setSettingValue(id: K, value: Settings[K]) {\r\n this.setSettingValueAsync(id, value).catch((err) => {\r\n alert(`Error saving setting '${id}'`)\r\n console.error(err)\r\n })\r\n }\r\n\r\n refreshSetting(id: keyof Settings) {\r\n const value = this.getSettingValue(id)\r\n this.settingsLookup[id].onChange?.(value)\r\n this.#dispatchChange(id, value)\r\n }\r\n\r\n addSetting(params: SettingParams) {\r\n const {\r\n id,\r\n name,\r\n type,\r\n defaultValue,\r\n onChange,\r\n attrs = {},\r\n tooltip = '',\r\n options = undefined\r\n } = params\r\n if (!id) {\r\n throw new Error('Settings must have an ID')\r\n }\r\n\r\n if (id in this.settingsLookup) {\r\n throw new Error(`Setting ${id} of type ${type} must have a unique ID.`)\r\n }\r\n\r\n let skipOnChange = false\r\n let value = this.getSettingValue(id)\r\n if (value == null) {\r\n if (this.app.isNewUserSession) {\r\n // Check if we have a localStorage value but not a setting value and we are a new user\r\n const localValue = localStorage['Comfy.Settings.' + id]\r\n if (localValue) {\r\n value = JSON.parse(localValue)\r\n this.setSettingValue(id, value) // Store on the server\r\n }\r\n }\r\n if (value == null) {\r\n value = defaultValue\r\n }\r\n }\r\n\r\n // Trigger initial setting of value\r\n if (!skipOnChange) {\r\n onChange?.(value, undefined)\r\n this.#dispatchChange(id, value)\r\n }\r\n\r\n this.settingsParamLookup[id] = params\r\n if (this.app.vueAppReady) {\r\n useSettingStore().settings[id] = params\r\n }\r\n this.settingsLookup[id] = {\r\n id,\r\n onChange,\r\n name,\r\n render: () => {\r\n if (type === 'hidden') return\r\n\r\n const setter = (v) => {\r\n if (onChange) {\r\n onChange(v, value)\r\n }\r\n\r\n this.setSettingValue(id, v)\r\n value = v\r\n }\r\n value = this.getSettingValue(id, defaultValue)\r\n\r\n let element\r\n const htmlID = id.replaceAll('.', '-')\r\n\r\n const labelCell = $el('td', [\r\n $el('label', {\r\n for: htmlID,\r\n classList: [tooltip !== '' ? 'comfy-tooltip-indicator' : ''],\r\n textContent: name\r\n })\r\n ])\r\n\r\n if (typeof type === 'function') {\r\n element = type(name, setter, value, attrs)\r\n } else {\r\n switch (type) {\r\n case 'boolean':\r\n element = $el('tr', [\r\n labelCell,\r\n $el('td', [\r\n $el('input', {\r\n id: htmlID,\r\n type: 'checkbox',\r\n checked: value,\r\n onchange: (event) => {\r\n const isChecked = event.target.checked\r\n if (onChange !== undefined) {\r\n onChange(isChecked)\r\n }\r\n this.setSettingValue(id, isChecked)\r\n }\r\n })\r\n ])\r\n ])\r\n break\r\n case 'number':\r\n element = $el('tr', [\r\n labelCell,\r\n $el('td', [\r\n $el('input', {\r\n type,\r\n value,\r\n id: htmlID,\r\n oninput: (e) => {\r\n setter(e.target.value)\r\n },\r\n ...attrs\r\n })\r\n ])\r\n ])\r\n break\r\n case 'slider':\r\n element = $el('tr', [\r\n labelCell,\r\n $el('td', [\r\n $el(\r\n 'div',\r\n {\r\n style: {\r\n display: 'grid',\r\n gridAutoFlow: 'column'\r\n }\r\n },\r\n [\r\n $el('input', {\r\n ...attrs,\r\n value,\r\n type: 'range',\r\n oninput: (e) => {\r\n setter(e.target.value)\r\n e.target.nextElementSibling.value = e.target.value\r\n }\r\n }),\r\n $el('input', {\r\n ...attrs,\r\n value,\r\n id: htmlID,\r\n type: 'number',\r\n style: { maxWidth: '4rem' },\r\n oninput: (e) => {\r\n setter(e.target.value)\r\n e.target.previousElementSibling.value = e.target.value\r\n }\r\n })\r\n ]\r\n )\r\n ])\r\n ])\r\n break\r\n case 'combo':\r\n element = $el('tr', [\r\n labelCell,\r\n $el('td', [\r\n $el(\r\n 'select',\r\n {\r\n oninput: (e) => {\r\n setter(e.target.value)\r\n }\r\n },\r\n (typeof options === 'function'\r\n ? options(value)\r\n : options || []\r\n ).map((opt) => {\r\n if (typeof opt === 'string') {\r\n opt = { text: opt }\r\n }\r\n const v = opt.value ?? opt.text\r\n return $el('option', {\r\n value: v,\r\n textContent: opt.text,\r\n selected: value + '' === v + ''\r\n })\r\n })\r\n )\r\n ])\r\n ])\r\n break\r\n case 'text':\r\n default:\r\n if (type !== 'text') {\r\n console.warn(\r\n `Unsupported setting type '${type}, defaulting to text`\r\n )\r\n }\r\n\r\n element = $el('tr', [\r\n labelCell,\r\n $el('td', [\r\n $el('input', {\r\n value,\r\n id: htmlID,\r\n oninput: (e) => {\r\n setter(e.target.value)\r\n },\r\n ...attrs\r\n })\r\n ])\r\n ])\r\n break\r\n }\r\n }\r\n if (tooltip) {\r\n element.title = tooltip\r\n }\r\n\r\n return element\r\n }\r\n } as Setting\r\n\r\n const self = this\r\n return {\r\n get value() {\r\n return self.getSettingValue(id, defaultValue)\r\n },\r\n set value(v) {\r\n self.setSettingValue(id, v)\r\n }\r\n }\r\n }\r\n\r\n show() {\r\n this.textElement.replaceChildren(\r\n $el(\r\n 'tr',\r\n {\r\n style: { display: 'none' }\r\n },\r\n [$el('th'), $el('th', { style: { width: '33%' } })]\r\n ),\r\n ...this.settings\r\n .sort((a, b) => a.name.localeCompare(b.name))\r\n .map((s) => s.render())\r\n .filter(Boolean)\r\n )\r\n this.element.showModal()\r\n }\r\n}\r\n","import { api } from './api'\r\nimport { ComfyDialog as _ComfyDialog } from './ui/dialog'\r\nimport { toggleSwitch } from './ui/toggleSwitch'\r\nimport { ComfySettingsDialog } from './ui/settings'\r\nimport { ComfyApp, app } from './app'\r\nimport { StatusWsMessageStatus, TaskItem } from '@/types/apiTypes'\r\n\r\nexport const ComfyDialog = _ComfyDialog\r\n\r\ntype Position2D = {\r\n x: number\r\n y: number\r\n}\r\n\r\ntype Props = {\r\n parent?: HTMLElement\r\n $?: (el: HTMLElement) => void\r\n dataset?: DOMStringMap\r\n style?: Partial\r\n for?: string\r\n textContent?: string\r\n [key: string]: any\r\n}\r\n\r\ntype Children = Element[] | Element | string | string[]\r\n\r\ntype ElementType = K extends keyof HTMLElementTagNameMap\r\n ? HTMLElementTagNameMap[K]\r\n : HTMLElement\r\n\r\nexport function $el(\r\n tag: TTag,\r\n propsOrChildren?: Children | Props,\r\n children?: Children\r\n): ElementType {\r\n const split = tag.split('.')\r\n const element = document.createElement(split.shift() as string)\r\n if (split.length > 0) {\r\n element.classList.add(...split)\r\n }\r\n\r\n if (propsOrChildren) {\r\n if (typeof propsOrChildren === 'string') {\r\n propsOrChildren = { textContent: propsOrChildren }\r\n } else if (propsOrChildren instanceof Element) {\r\n propsOrChildren = [propsOrChildren]\r\n }\r\n if (Array.isArray(propsOrChildren)) {\r\n element.append(...propsOrChildren)\r\n } else {\r\n const {\r\n parent,\r\n $: cb,\r\n dataset,\r\n style,\r\n ...rest\r\n } = propsOrChildren as Props\r\n\r\n if (rest.for) {\r\n element.setAttribute('for', rest.for)\r\n }\r\n\r\n if (style) {\r\n Object.assign(element.style, style)\r\n }\r\n\r\n if (dataset) {\r\n Object.assign(element.dataset, dataset)\r\n }\r\n\r\n Object.assign(element, rest)\r\n if (children) {\r\n element.append(...(Array.isArray(children) ? children : [children]))\r\n }\r\n\r\n if (parent) {\r\n parent.append(element)\r\n }\r\n\r\n if (cb) {\r\n cb(element)\r\n }\r\n }\r\n }\r\n return element as ElementType\r\n}\r\n\r\nfunction dragElement(dragEl, settings): () => void {\r\n var posDiffX = 0,\r\n posDiffY = 0,\r\n posStartX = 0,\r\n posStartY = 0,\r\n newPosX = 0,\r\n newPosY = 0\r\n if (dragEl.getElementsByClassName('drag-handle')[0]) {\r\n // if present, the handle is where you move the DIV from:\r\n dragEl.getElementsByClassName('drag-handle')[0].onmousedown = dragMouseDown\r\n } else {\r\n // otherwise, move the DIV from anywhere inside the DIV:\r\n dragEl.onmousedown = dragMouseDown\r\n }\r\n\r\n // When the element resizes (e.g. view queue) ensure it is still in the windows bounds\r\n const resizeObserver = new ResizeObserver(() => {\r\n ensureInBounds()\r\n }).observe(dragEl)\r\n\r\n function ensureInBounds() {\r\n try {\r\n newPosX = Math.min(\r\n document.body.clientWidth - dragEl.clientWidth,\r\n Math.max(0, dragEl.offsetLeft)\r\n )\r\n newPosY = Math.min(\r\n document.body.clientHeight - dragEl.clientHeight,\r\n Math.max(0, dragEl.offsetTop)\r\n )\r\n\r\n positionElement()\r\n } catch (exception) {\r\n // robust\r\n }\r\n }\r\n\r\n function positionElement() {\r\n if (dragEl.style.display === 'none') return\r\n\r\n const halfWidth = document.body.clientWidth / 2\r\n const anchorRight = newPosX + dragEl.clientWidth / 2 > halfWidth\r\n\r\n // set the element's new position:\r\n if (anchorRight) {\r\n dragEl.style.left = 'unset'\r\n dragEl.style.right =\r\n document.body.clientWidth - newPosX - dragEl.clientWidth + 'px'\r\n } else {\r\n dragEl.style.left = newPosX + 'px'\r\n dragEl.style.right = 'unset'\r\n }\r\n\r\n dragEl.style.top = newPosY + 'px'\r\n dragEl.style.bottom = 'unset'\r\n\r\n if (savePos) {\r\n localStorage.setItem(\r\n 'Comfy.MenuPosition',\r\n JSON.stringify({\r\n x: dragEl.offsetLeft,\r\n y: dragEl.offsetTop\r\n })\r\n )\r\n }\r\n }\r\n\r\n function restorePos() {\r\n let posString = localStorage.getItem('Comfy.MenuPosition')\r\n if (posString) {\r\n const pos = JSON.parse(posString) as Position2D\r\n newPosX = pos.x\r\n newPosY = pos.y\r\n positionElement()\r\n ensureInBounds()\r\n }\r\n }\r\n\r\n let savePos = undefined\r\n settings.addSetting({\r\n id: 'Comfy.MenuPosition',\r\n name: 'Save menu position',\r\n type: 'boolean',\r\n defaultValue: savePos,\r\n onChange(value) {\r\n if (savePos === undefined && value) {\r\n restorePos()\r\n }\r\n savePos = value\r\n }\r\n })\r\n\r\n function dragMouseDown(e) {\r\n e = e || window.event\r\n e.preventDefault()\r\n // get the mouse cursor position at startup:\r\n posStartX = e.clientX\r\n posStartY = e.clientY\r\n document.onmouseup = closeDragElement\r\n // call a function whenever the cursor moves:\r\n document.onmousemove = elementDrag\r\n }\r\n\r\n function elementDrag(e) {\r\n e = e || window.event\r\n e.preventDefault()\r\n\r\n dragEl.classList.add('comfy-menu-manual-pos')\r\n\r\n // calculate the new cursor position:\r\n posDiffX = e.clientX - posStartX\r\n posDiffY = e.clientY - posStartY\r\n posStartX = e.clientX\r\n posStartY = e.clientY\r\n\r\n newPosX = Math.min(\r\n document.body.clientWidth - dragEl.clientWidth,\r\n Math.max(0, dragEl.offsetLeft + posDiffX)\r\n )\r\n newPosY = Math.min(\r\n document.body.clientHeight - dragEl.clientHeight,\r\n Math.max(0, dragEl.offsetTop + posDiffY)\r\n )\r\n\r\n positionElement()\r\n }\r\n\r\n window.addEventListener('resize', () => {\r\n ensureInBounds()\r\n })\r\n\r\n function closeDragElement() {\r\n // stop moving when mouse button is released:\r\n document.onmouseup = null\r\n document.onmousemove = null\r\n }\r\n\r\n return restorePos\r\n}\r\n\r\nclass ComfyList {\r\n #type\r\n #text\r\n #reverse\r\n element: HTMLDivElement\r\n button?: HTMLButtonElement\r\n\r\n constructor(text, type?, reverse?) {\r\n this.#text = text\r\n this.#type = type || text.toLowerCase()\r\n this.#reverse = reverse || false\r\n this.element = $el('div.comfy-list') as HTMLDivElement\r\n this.element.style.display = 'none'\r\n }\r\n\r\n get visible() {\r\n return this.element.style.display !== 'none'\r\n }\r\n\r\n async load() {\r\n const items = await api.getItems(this.#type)\r\n this.element.replaceChildren(\r\n ...Object.keys(items).flatMap((section) => [\r\n $el('h4', {\r\n textContent: section\r\n }),\r\n $el('div.comfy-list-items', [\r\n ...(this.#reverse ? items[section].reverse() : items[section]).map(\r\n (item: TaskItem) => {\r\n // Allow items to specify a custom remove action (e.g. for interrupt current prompt)\r\n const removeAction =\r\n 'remove' in item\r\n ? item.remove\r\n : {\r\n name: 'Delete',\r\n cb: () => api.deleteItem(this.#type, item.prompt[1])\r\n }\r\n return $el('div', { textContent: item.prompt[0] + ': ' }, [\r\n $el('button', {\r\n textContent: 'Load',\r\n onclick: async () => {\r\n await app.loadGraphData(\r\n item.prompt[3].extra_pnginfo.workflow,\r\n true,\r\n false\r\n )\r\n if ('outputs' in item) {\r\n app.nodeOutputs = {}\r\n for (const [key, value] of Object.entries(item.outputs)) {\r\n const realKey = item['meta']?.[key]?.display_node ?? key\r\n app.nodeOutputs[realKey] = value\r\n }\r\n }\r\n }\r\n }),\r\n $el('button', {\r\n textContent: removeAction.name,\r\n onclick: async () => {\r\n await removeAction.cb()\r\n await this.update()\r\n }\r\n })\r\n ])\r\n }\r\n )\r\n ])\r\n ]),\r\n $el('div.comfy-list-actions', [\r\n $el('button', {\r\n textContent: 'Clear ' + this.#text,\r\n onclick: async () => {\r\n await api.clearItems(this.#type)\r\n await this.load()\r\n }\r\n }),\r\n $el('button', { textContent: 'Refresh', onclick: () => this.load() })\r\n ])\r\n )\r\n }\r\n\r\n async update() {\r\n if (this.visible) {\r\n await this.load()\r\n }\r\n }\r\n\r\n async show() {\r\n this.element.style.display = 'block'\r\n this.button.textContent = 'Close'\r\n\r\n await this.load()\r\n }\r\n\r\n hide() {\r\n this.element.style.display = 'none'\r\n this.button.textContent = 'View ' + this.#text\r\n }\r\n\r\n toggle() {\r\n if (this.visible) {\r\n this.hide()\r\n return false\r\n } else {\r\n this.show()\r\n return true\r\n }\r\n }\r\n}\r\n\r\nexport class ComfyUI {\r\n app: ComfyApp\r\n dialog: _ComfyDialog\r\n settings: ComfySettingsDialog\r\n batchCount: number\r\n lastQueueSize: number\r\n queue: ComfyList\r\n history: ComfyList\r\n autoQueueMode: string\r\n graphHasChanged: boolean\r\n autoQueueEnabled: boolean\r\n menuHamburger: HTMLDivElement\r\n menuContainer: HTMLDivElement\r\n queueSize: Element\r\n restoreMenuPosition: () => void\r\n loadFile: () => void\r\n\r\n constructor(app) {\r\n this.app = app\r\n this.dialog = new ComfyDialog()\r\n this.settings = new ComfySettingsDialog(app)\r\n\r\n this.batchCount = 1\r\n this.lastQueueSize = 0\r\n this.queue = new ComfyList('Queue')\r\n this.history = new ComfyList('History', 'history', true)\r\n\r\n api.addEventListener('status', () => {\r\n this.queue.update()\r\n this.history.update()\r\n })\r\n\r\n const confirmClear = this.settings.addSetting({\r\n id: 'Comfy.ConfirmClear',\r\n name: 'Require confirmation when clearing workflow',\r\n type: 'boolean',\r\n defaultValue: true\r\n })\r\n\r\n const promptFilename = this.settings.addSetting({\r\n id: 'Comfy.PromptFilename',\r\n name: 'Prompt for filename when saving workflow',\r\n type: 'boolean',\r\n defaultValue: true\r\n })\r\n\r\n /**\r\n * file format for preview\r\n *\r\n * format;quality\r\n *\r\n * ex)\r\n * webp;50 -> webp, quality 50\r\n * jpeg;80 -> rgb, jpeg, quality 80\r\n *\r\n * @type {string}\r\n */\r\n const previewImage = this.settings.addSetting({\r\n id: 'Comfy.PreviewFormat',\r\n name: 'When displaying a preview in the image widget, convert it to a lightweight image, e.g. webp, jpeg, webp;50, etc.',\r\n type: 'text',\r\n defaultValue: ''\r\n })\r\n\r\n this.settings.addSetting({\r\n id: 'Comfy.DisableSliders',\r\n name: 'Disable sliders.',\r\n type: 'boolean',\r\n defaultValue: false\r\n })\r\n\r\n this.settings.addSetting({\r\n id: 'Comfy.DisableFloatRounding',\r\n name: 'Disable rounding floats (requires page reload).',\r\n type: 'boolean',\r\n defaultValue: false\r\n })\r\n\r\n this.settings.addSetting({\r\n id: 'Comfy.FloatRoundingPrecision',\r\n name: 'Decimal places [0 = auto] (requires page reload).',\r\n type: 'slider',\r\n attrs: {\r\n min: 0,\r\n max: 6,\r\n step: 1\r\n },\r\n defaultValue: 0\r\n })\r\n\r\n this.settings.addSetting({\r\n id: 'Comfy.EnableTooltips',\r\n name: 'Enable Tooltips',\r\n type: 'boolean',\r\n defaultValue: true\r\n })\r\n\r\n const fileInput = $el('input', {\r\n id: 'comfy-file-input',\r\n type: 'file',\r\n accept: '.json,image/png,.latent,.safetensors,image/webp,audio/flac',\r\n style: { display: 'none' },\r\n parent: document.body,\r\n onchange: () => {\r\n app.handleFile(fileInput.files[0])\r\n }\r\n })\r\n\r\n this.loadFile = () => fileInput.click()\r\n\r\n const autoQueueModeEl = toggleSwitch(\r\n 'autoQueueMode',\r\n [\r\n {\r\n text: 'instant',\r\n tooltip: 'A new prompt will be queued as soon as the queue reaches 0'\r\n },\r\n {\r\n text: 'change',\r\n tooltip:\r\n 'A new prompt will be queued when the queue is at 0 and the graph is/has changed'\r\n }\r\n ],\r\n {\r\n onChange: (value) => {\r\n this.autoQueueMode = value.item.value\r\n }\r\n }\r\n )\r\n autoQueueModeEl.style.display = 'none'\r\n\r\n api.addEventListener('graphChanged', () => {\r\n if (this.autoQueueMode === 'change' && this.autoQueueEnabled === true) {\r\n if (this.lastQueueSize === 0) {\r\n this.graphHasChanged = false\r\n app.queuePrompt(0, this.batchCount)\r\n } else {\r\n this.graphHasChanged = true\r\n }\r\n }\r\n })\r\n\r\n this.menuHamburger = $el(\r\n 'div.comfy-menu-hamburger',\r\n {\r\n parent: document.body,\r\n onclick: () => {\r\n this.menuContainer.style.display = 'block'\r\n this.menuHamburger.style.display = 'none'\r\n }\r\n },\r\n [$el('div'), $el('div'), $el('div')]\r\n ) as HTMLDivElement\r\n\r\n this.menuContainer = $el('div.comfy-menu', { parent: document.body }, [\r\n $el(\r\n 'div.drag-handle.comfy-menu-header',\r\n {\r\n style: {\r\n overflow: 'hidden',\r\n position: 'relative',\r\n width: '100%',\r\n cursor: 'default'\r\n }\r\n },\r\n [\r\n $el('span.drag-handle'),\r\n $el('span.comfy-menu-queue-size', { $: (q) => (this.queueSize = q) }),\r\n $el('div.comfy-menu-actions', [\r\n $el('button.comfy-settings-btn', {\r\n textContent: '⚙️',\r\n onclick: () => this.settings.show()\r\n }),\r\n $el('button.comfy-close-menu-btn', {\r\n textContent: '\\u00d7',\r\n onclick: () => {\r\n this.menuContainer.style.display = 'none'\r\n this.menuHamburger.style.display = 'flex'\r\n }\r\n })\r\n ])\r\n ]\r\n ),\r\n $el('button.comfy-queue-btn', {\r\n id: 'queue-button',\r\n textContent: 'Queue Prompt',\r\n onclick: () => app.queuePrompt(0, this.batchCount)\r\n }),\r\n $el('div', {}, [\r\n $el('label', { innerHTML: 'Extra options' }, [\r\n $el('input', {\r\n type: 'checkbox',\r\n onchange: (i) => {\r\n document.getElementById('extraOptions').style.display = i\r\n .srcElement.checked\r\n ? 'block'\r\n : 'none'\r\n this.batchCount = i.srcElement.checked\r\n ? Number.parseInt(\r\n (\r\n document.getElementById(\r\n 'batchCountInputRange'\r\n ) as HTMLInputElement\r\n ).value\r\n )\r\n : 1\r\n ;(\r\n document.getElementById('autoQueueCheckbox') as HTMLInputElement\r\n ).checked = false\r\n this.autoQueueEnabled = false\r\n }\r\n })\r\n ])\r\n ]),\r\n $el(\r\n 'div',\r\n { id: 'extraOptions', style: { width: '100%', display: 'none' } },\r\n [\r\n $el('div', [\r\n $el('label', { innerHTML: 'Batch count' }),\r\n $el('input', {\r\n id: 'batchCountInputNumber',\r\n type: 'number',\r\n value: this.batchCount,\r\n min: '1',\r\n style: { width: '35%', marginLeft: '0.4em' },\r\n oninput: (i) => {\r\n this.batchCount = i.target.value\r\n /* Even though an element with a type of range logically represents a number (since\r\n it's used for numeric input), the value it holds is still treated as a string in HTML and\r\n JavaScript. This behavior is consistent across all elements regardless of their type\r\n (like text, number, or range), where the .value property is always a string. */\r\n ;(\r\n document.getElementById(\r\n 'batchCountInputRange'\r\n ) as HTMLInputElement\r\n ).value = this.batchCount.toString()\r\n }\r\n }),\r\n $el('input', {\r\n id: 'batchCountInputRange',\r\n type: 'range',\r\n min: '1',\r\n max: '100',\r\n value: this.batchCount,\r\n oninput: (i) => {\r\n this.batchCount = i.srcElement.value\r\n // Note\r\n ;(\r\n document.getElementById(\r\n 'batchCountInputNumber'\r\n ) as HTMLInputElement\r\n ).value = i.srcElement.value\r\n }\r\n })\r\n ]),\r\n $el('div', [\r\n $el('label', {\r\n for: 'autoQueueCheckbox',\r\n innerHTML: 'Auto Queue'\r\n }),\r\n $el('input', {\r\n id: 'autoQueueCheckbox',\r\n type: 'checkbox',\r\n checked: false,\r\n title: 'Automatically queue prompt when the queue size hits 0',\r\n onchange: (e) => {\r\n this.autoQueueEnabled = e.target.checked\r\n autoQueueModeEl.style.display = this.autoQueueEnabled\r\n ? ''\r\n : 'none'\r\n }\r\n }),\r\n autoQueueModeEl\r\n ])\r\n ]\r\n ),\r\n $el('div.comfy-menu-btns', [\r\n $el('button', {\r\n id: 'queue-front-button',\r\n textContent: 'Queue Front',\r\n onclick: () => app.queuePrompt(-1, this.batchCount)\r\n }),\r\n $el('button', {\r\n $: (b) => (this.queue.button = b as HTMLButtonElement),\r\n id: 'comfy-view-queue-button',\r\n textContent: 'View Queue',\r\n onclick: () => {\r\n this.history.hide()\r\n this.queue.toggle()\r\n }\r\n }),\r\n $el('button', {\r\n $: (b) => (this.history.button = b as HTMLButtonElement),\r\n id: 'comfy-view-history-button',\r\n textContent: 'View History',\r\n onclick: () => {\r\n this.queue.hide()\r\n this.history.toggle()\r\n }\r\n })\r\n ]),\r\n this.queue.element,\r\n this.history.element,\r\n $el('button', {\r\n id: 'comfy-save-button',\r\n textContent: 'Save',\r\n onclick: () => {\r\n let filename = 'workflow.json'\r\n if (promptFilename.value) {\r\n filename = prompt('Save workflow as:', filename)\r\n if (!filename) return\r\n if (!filename.toLowerCase().endsWith('.json')) {\r\n filename += '.json'\r\n }\r\n }\r\n app.graphToPrompt().then((p) => {\r\n const json = JSON.stringify(p.workflow, null, 2) // convert the data to a JSON string\r\n const blob = new Blob([json], { type: 'application/json' })\r\n const url = URL.createObjectURL(blob)\r\n const a = $el('a', {\r\n href: url,\r\n download: filename,\r\n style: { display: 'none' },\r\n parent: document.body\r\n })\r\n a.click()\r\n setTimeout(function () {\r\n a.remove()\r\n window.URL.revokeObjectURL(url)\r\n }, 0)\r\n })\r\n }\r\n }),\r\n $el('button', {\r\n id: 'comfy-dev-save-api-button',\r\n textContent: 'Save (API Format)',\r\n style: { width: '100%', display: 'none' },\r\n onclick: () => {\r\n let filename = 'workflow_api.json'\r\n if (promptFilename.value) {\r\n filename = prompt('Save workflow (API) as:', filename)\r\n if (!filename) return\r\n if (!filename.toLowerCase().endsWith('.json')) {\r\n filename += '.json'\r\n }\r\n }\r\n app.graphToPrompt().then((p) => {\r\n const json = JSON.stringify(p.output, null, 2) // convert the data to a JSON string\r\n const blob = new Blob([json], { type: 'application/json' })\r\n const url = URL.createObjectURL(blob)\r\n const a = $el('a', {\r\n href: url,\r\n download: filename,\r\n style: { display: 'none' },\r\n parent: document.body\r\n })\r\n a.click()\r\n setTimeout(function () {\r\n a.remove()\r\n window.URL.revokeObjectURL(url)\r\n }, 0)\r\n })\r\n }\r\n }),\r\n $el('button', {\r\n id: 'comfy-load-button',\r\n textContent: 'Load',\r\n onclick: () => fileInput.click()\r\n }),\r\n $el('button', {\r\n id: 'comfy-refresh-button',\r\n textContent: 'Refresh',\r\n onclick: () => app.refreshComboInNodes()\r\n }),\r\n $el('button', {\r\n id: 'comfy-clipspace-button',\r\n textContent: 'Clipspace',\r\n onclick: () => app.openClipspace()\r\n }),\r\n $el('button', {\r\n id: 'comfy-clear-button',\r\n textContent: 'Clear',\r\n onclick: () => {\r\n if (!confirmClear.value || confirm('Clear workflow?')) {\r\n app.clean()\r\n app.graph.clear()\r\n app.resetView()\r\n api.dispatchEvent(new CustomEvent('graphCleared'))\r\n }\r\n }\r\n }),\r\n $el('button', {\r\n id: 'comfy-load-default-button',\r\n textContent: 'Load Default',\r\n onclick: async () => {\r\n if (!confirmClear.value || confirm('Load default workflow?')) {\r\n app.resetView()\r\n await app.loadGraphData()\r\n }\r\n }\r\n }),\r\n $el('button', {\r\n id: 'comfy-reset-view-button',\r\n textContent: 'Reset View',\r\n onclick: async () => {\r\n app.resetView()\r\n }\r\n })\r\n ]) as HTMLDivElement\r\n\r\n const devMode = this.settings.addSetting({\r\n id: 'Comfy.DevMode',\r\n name: 'Enable Dev mode Options',\r\n type: 'boolean',\r\n defaultValue: false,\r\n onChange: function (value) {\r\n document.getElementById('comfy-dev-save-api-button').style.display =\r\n value ? 'flex' : 'none'\r\n }\r\n })\r\n\r\n this.restoreMenuPosition = dragElement(this.menuContainer, this.settings)\r\n\r\n this.setStatus({ exec_info: { queue_remaining: 'X' } })\r\n }\r\n\r\n setStatus(status) {\r\n this.queueSize.textContent =\r\n 'Queue size: ' + (status ? status.exec_info.queue_remaining : 'ERR')\r\n if (status) {\r\n if (\r\n this.lastQueueSize != 0 &&\r\n status.exec_info.queue_remaining == 0 &&\r\n this.autoQueueEnabled &&\r\n (this.autoQueueMode === 'instant' || this.graphHasChanged) &&\r\n !app.lastExecutionError\r\n ) {\r\n app.queuePrompt(0, this.batchCount)\r\n status.exec_info.queue_remaining += this.batchCount\r\n this.graphHasChanged = false\r\n }\r\n this.lastQueueSize = status.exec_info.queue_remaining\r\n }\r\n }\r\n}\r\n","import { $el, ComfyDialog } from './ui'\r\nimport { api } from './api'\r\nimport type { ComfyApp } from './app'\r\n\r\n$el('style', {\r\n textContent: `\r\n .comfy-logging-logs {\r\n display: grid;\r\n color: var(--fg-color);\r\n white-space: pre-wrap;\r\n }\r\n .comfy-logging-log {\r\n display: contents;\r\n }\r\n .comfy-logging-title {\r\n background: var(--tr-even-bg-color);\r\n font-weight: bold;\r\n margin-bottom: 5px;\r\n text-align: center;\r\n }\r\n .comfy-logging-log div {\r\n background: var(--row-bg);\r\n padding: 5px;\r\n }\r\n `,\r\n parent: document.body\r\n})\r\n\r\n// Stringify function supporting max depth and removal of circular references\r\n// https://stackoverflow.com/a/57193345\r\nfunction stringify(val, depth, replacer, space, onGetObjID?) {\r\n depth = isNaN(+depth) ? 1 : depth\r\n var recursMap = new WeakMap()\r\n function _build(val, depth, o?, a?, r?) {\r\n // (JSON.stringify() has it's own rules, which we respect here by using it for property iteration)\r\n return !val || typeof val != 'object'\r\n ? val\r\n : ((r = recursMap.has(val)),\r\n recursMap.set(val, true),\r\n (a = Array.isArray(val)),\r\n r\r\n ? (o = (onGetObjID && onGetObjID(val)) || null)\r\n : JSON.stringify(val, function (k, v) {\r\n if (a || depth > 0) {\r\n if (replacer) v = replacer(k, v)\r\n if (!k) return (a = Array.isArray(v)), (val = v)\r\n !o && (o = a ? [] : {})\r\n o[k] = _build(v, a ? depth : depth - 1)\r\n }\r\n }),\r\n o === void 0 ? (a ? [] : {}) : o)\r\n }\r\n return JSON.stringify(_build(val, depth), null, space)\r\n}\r\n\r\nconst jsonReplacer = (k, v, ui) => {\r\n if (v instanceof Array && v.length === 1) {\r\n v = v[0]\r\n }\r\n if (v instanceof Date) {\r\n v = v.toISOString()\r\n if (ui) {\r\n v = v.split('T')[1]\r\n }\r\n }\r\n if (v instanceof Error) {\r\n let err = ''\r\n if (v.name) err += v.name + '\\n'\r\n if (v.message) err += v.message + '\\n'\r\n if (v.stack) err += v.stack + '\\n'\r\n if (!err) {\r\n err = v.toString()\r\n }\r\n v = err\r\n }\r\n return v\r\n}\r\n\r\nconst fileInput: HTMLInputElement = $el('input', {\r\n type: 'file',\r\n accept: '.json',\r\n style: { display: 'none' },\r\n parent: document.body\r\n}) as HTMLInputElement\r\n\r\nclass ComfyLoggingDialog extends ComfyDialog {\r\n logging: any\r\n\r\n constructor(logging) {\r\n super()\r\n this.logging = logging\r\n }\r\n\r\n clear() {\r\n this.logging.clear()\r\n this.show()\r\n }\r\n\r\n export() {\r\n const blob = new Blob(\r\n [stringify([...this.logging.entries], 20, jsonReplacer, '\\t')],\r\n {\r\n type: 'application/json'\r\n }\r\n )\r\n const url = URL.createObjectURL(blob)\r\n const a = $el('a', {\r\n href: url,\r\n download: `comfyui-logs-${Date.now()}.json`,\r\n style: { display: 'none' },\r\n parent: document.body\r\n })\r\n a.click()\r\n setTimeout(function () {\r\n a.remove()\r\n window.URL.revokeObjectURL(url)\r\n }, 0)\r\n }\r\n\r\n import() {\r\n fileInput.onchange = () => {\r\n const reader = new FileReader()\r\n reader.onload = () => {\r\n fileInput.remove()\r\n try {\r\n const obj = JSON.parse(reader.result as string)\r\n if (obj instanceof Array) {\r\n this.show(obj)\r\n } else {\r\n throw new Error('Invalid file selected.')\r\n }\r\n } catch (error) {\r\n alert('Unable to load logs: ' + error.message)\r\n }\r\n }\r\n reader.readAsText(fileInput.files[0])\r\n }\r\n fileInput.click()\r\n }\r\n\r\n createButtons() {\r\n return [\r\n $el('button', {\r\n type: 'button',\r\n textContent: 'Clear',\r\n onclick: () => this.clear()\r\n }),\r\n $el('button', {\r\n type: 'button',\r\n textContent: 'Export logs...',\r\n onclick: () => this.export()\r\n }),\r\n $el('button', {\r\n type: 'button',\r\n textContent: 'View exported logs...',\r\n onclick: () => this.import()\r\n }),\r\n ...super.createButtons()\r\n ]\r\n }\r\n\r\n getTypeColor(type) {\r\n switch (type) {\r\n case 'error':\r\n return 'red'\r\n case 'warn':\r\n return 'orange'\r\n case 'debug':\r\n return 'dodgerblue'\r\n }\r\n }\r\n\r\n show(entries?: any[]) {\r\n if (!entries) entries = this.logging.entries\r\n this.element.style.width = '100%'\r\n const cols = {\r\n source: 'Source',\r\n type: 'Type',\r\n timestamp: 'Timestamp',\r\n message: 'Message'\r\n }\r\n const keys = Object.keys(cols)\r\n const headers = Object.values(cols).map((title) =>\r\n $el('div.comfy-logging-title', {\r\n textContent: title\r\n })\r\n )\r\n const rows = entries.map((entry, i) => {\r\n return $el(\r\n 'div.comfy-logging-log',\r\n {\r\n $: (el) =>\r\n el.style.setProperty(\r\n '--row-bg',\r\n `var(--tr-${i % 2 ? 'even' : 'odd'}-bg-color)`\r\n )\r\n },\r\n keys.map((key) => {\r\n let v = entry[key]\r\n let color\r\n if (key === 'type') {\r\n color = this.getTypeColor(v)\r\n } else {\r\n v = jsonReplacer(key, v, true)\r\n\r\n if (typeof v === 'object') {\r\n v = stringify(v, 5, jsonReplacer, ' ')\r\n }\r\n }\r\n\r\n return $el('div', {\r\n style: {\r\n color\r\n },\r\n textContent: v\r\n })\r\n })\r\n )\r\n })\r\n\r\n const grid = $el(\r\n 'div.comfy-logging-logs',\r\n {\r\n style: {\r\n gridTemplateColumns: `repeat(${headers.length}, 1fr)`\r\n }\r\n },\r\n [...headers, ...rows]\r\n )\r\n const els = [grid]\r\n if (!this.logging.enabled) {\r\n els.unshift(\r\n $el('h3', {\r\n style: { textAlign: 'center' },\r\n textContent: 'Logging is disabled'\r\n })\r\n )\r\n }\r\n super.show($el('div', els))\r\n }\r\n}\r\n\r\nexport class ComfyLogging {\r\n /**\r\n * @type Array<{ source: string, type: string, timestamp: Date, message: any }>\r\n */\r\n entries = []\r\n\r\n #enabled\r\n #console = {}\r\n\r\n app: ComfyApp\r\n dialog: ComfyLoggingDialog\r\n\r\n get enabled() {\r\n return this.#enabled\r\n }\r\n\r\n set enabled(value) {\r\n if (value === this.#enabled) return\r\n if (value) {\r\n this.patchConsole()\r\n } else {\r\n this.unpatchConsole()\r\n }\r\n this.#enabled = value\r\n }\r\n\r\n constructor(app) {\r\n this.app = app\r\n\r\n this.dialog = new ComfyLoggingDialog(this)\r\n this.addSetting()\r\n this.catchUnhandled()\r\n this.addInitData()\r\n }\r\n\r\n addSetting() {\r\n const settingId: string = 'Comfy.Logging.Enabled'\r\n const htmlSettingId = settingId.replaceAll('.', '-')\r\n const setting = this.app.ui.settings.addSetting({\r\n id: settingId,\r\n name: settingId,\r\n defaultValue: true,\r\n onChange: (value) => {\r\n this.enabled = value\r\n },\r\n type: (name, setter, value) => {\r\n return $el('tr', [\r\n $el('td', [\r\n $el('label', {\r\n textContent: 'Logging',\r\n for: htmlSettingId\r\n })\r\n ]),\r\n $el('td', [\r\n $el('input', {\r\n id: htmlSettingId,\r\n type: 'checkbox',\r\n checked: value,\r\n onchange: (event) => {\r\n setter(event.target.checked)\r\n }\r\n }),\r\n $el('button', {\r\n textContent: 'View Logs',\r\n onclick: () => {\r\n this.app.ui.settings.element.close()\r\n this.dialog.show()\r\n },\r\n style: {\r\n fontSize: '14px',\r\n display: 'block',\r\n marginTop: '5px'\r\n }\r\n })\r\n ])\r\n ])\r\n }\r\n })\r\n this.enabled = setting.value\r\n }\r\n\r\n patchConsole() {\r\n // Capture common console outputs\r\n const self = this\r\n for (const type of ['log', 'warn', 'error', 'debug']) {\r\n const orig = console[type]\r\n this.#console[type] = orig\r\n console[type] = function () {\r\n orig.apply(console, arguments)\r\n self.addEntry('console', type, ...arguments)\r\n }\r\n }\r\n }\r\n\r\n unpatchConsole() {\r\n // Restore original console functions\r\n for (const type of Object.keys(this.#console)) {\r\n console[type] = this.#console[type]\r\n }\r\n this.#console = {}\r\n }\r\n\r\n catchUnhandled() {\r\n // Capture uncaught errors\r\n window.addEventListener('error', (e) => {\r\n this.addEntry('window', 'error', e.error ?? 'Unknown error')\r\n return false\r\n })\r\n\r\n window.addEventListener('unhandledrejection', (e) => {\r\n this.addEntry('unhandledrejection', 'error', e.reason ?? 'Unknown error')\r\n })\r\n }\r\n\r\n clear() {\r\n this.entries = []\r\n }\r\n\r\n addEntry(source, type, ...args) {\r\n if (this.enabled) {\r\n this.entries.push({\r\n source,\r\n type,\r\n timestamp: new Date(),\r\n message: args\r\n })\r\n }\r\n }\r\n\r\n log(source, ...args) {\r\n this.addEntry(source, 'log', ...args)\r\n }\r\n\r\n async addInitData() {\r\n if (!this.enabled) return\r\n const source = 'ComfyUI.Logging'\r\n this.addEntry(source, 'debug', { UserAgent: navigator.userAgent })\r\n const systemStats = await api.getSystemStats()\r\n this.addEntry(source, 'debug', systemStats)\r\n }\r\n}\r\n","var __defProp = Object.defineProperty;\nvar __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;\nvar __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== \"symbol\" ? key + \"\" : key, value);\nconst globalExport = {};\n(function(globalThis) {\n var LiteGraph = globalThis.LiteGraph = {\n VERSION: 0.4,\n CANVAS_GRID_SIZE: 10,\n NODE_TITLE_HEIGHT: 30,\n NODE_TITLE_TEXT_Y: 20,\n NODE_SLOT_HEIGHT: 20,\n NODE_WIDGET_HEIGHT: 20,\n NODE_WIDTH: 140,\n NODE_MIN_WIDTH: 50,\n NODE_COLLAPSED_RADIUS: 10,\n NODE_COLLAPSED_WIDTH: 80,\n NODE_TITLE_COLOR: \"#999\",\n NODE_SELECTED_TITLE_COLOR: \"#FFF\",\n NODE_TEXT_SIZE: 14,\n NODE_TEXT_COLOR: \"#AAA\",\n NODE_SUBTEXT_SIZE: 12,\n NODE_DEFAULT_COLOR: \"#333\",\n NODE_DEFAULT_BGCOLOR: \"#353535\",\n NODE_DEFAULT_BOXCOLOR: \"#666\",\n NODE_DEFAULT_SHAPE: \"box\",\n NODE_BOX_OUTLINE_COLOR: \"#FFF\",\n DEFAULT_SHADOW_COLOR: \"rgba(0,0,0,0.5)\",\n DEFAULT_GROUP_FONT: 24,\n WIDGET_BGCOLOR: \"#222\",\n WIDGET_OUTLINE_COLOR: \"#666\",\n WIDGET_TEXT_COLOR: \"#DDD\",\n WIDGET_SECONDARY_TEXT_COLOR: \"#999\",\n LINK_COLOR: \"#9A9\",\n EVENT_LINK_COLOR: \"#A86\",\n CONNECTING_LINK_COLOR: \"#AFA\",\n MAX_NUMBER_OF_NODES: 1e4,\n //avoid infinite loops\n DEFAULT_POSITION: [100, 100],\n //default node position\n VALID_SHAPES: [\"default\", \"box\", \"round\", \"card\"],\n //,\"circle\"\n //shapes are used for nodes but also for slots\n BOX_SHAPE: 1,\n ROUND_SHAPE: 2,\n CIRCLE_SHAPE: 3,\n CARD_SHAPE: 4,\n ARROW_SHAPE: 5,\n GRID_SHAPE: 6,\n // intended for slot arrays\n //enums\n INPUT: 1,\n OUTPUT: 2,\n EVENT: -1,\n //for outputs\n ACTION: -1,\n //for inputs\n NODE_MODES: [\"Always\", \"On Event\", \"Never\", \"On Trigger\"],\n // helper, will add \"On Request\" and more in the future\n NODE_MODES_COLORS: [\"#666\", \"#422\", \"#333\", \"#224\", \"#626\"],\n // use with node_box_coloured_by_mode\n ALWAYS: 0,\n ON_EVENT: 1,\n NEVER: 2,\n ON_TRIGGER: 3,\n UP: 1,\n DOWN: 2,\n LEFT: 3,\n RIGHT: 4,\n CENTER: 5,\n LINK_RENDER_MODES: [\"Straight\", \"Linear\", \"Spline\"],\n // helper\n STRAIGHT_LINK: 0,\n LINEAR_LINK: 1,\n SPLINE_LINK: 2,\n NORMAL_TITLE: 0,\n NO_TITLE: 1,\n TRANSPARENT_TITLE: 2,\n AUTOHIDE_TITLE: 3,\n VERTICAL_LAYOUT: \"vertical\",\n // arrange nodes vertically\n proxy: null,\n //used to redirect calls\n node_images_path: \"\",\n debug: false,\n catch_exceptions: true,\n throw_errors: true,\n allow_scripts: false,\n //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits\n registered_node_types: {},\n //nodetypes by string\n node_types_by_file_extension: {},\n //used for dropping files in the canvas\n Nodes: {},\n //node types by classname\n Globals: {},\n //used to store vars between graphs\n searchbox_extras: {},\n //used to add extra features to the search box\n auto_sort_node_types: false,\n // [true!] If set to true, will automatically sort node types / categories in the context menus\n node_box_coloured_when_on: false,\n // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action), visual feedback\n node_box_coloured_by_mode: false,\n // [true!] nodebox based on node mode, visual feedback\n dialog_close_on_mouse_leave: false,\n // [false on mobile] better true if not touch device, TODO add an helper/listener to close if false\n dialog_close_on_mouse_leave_delay: 500,\n shift_click_do_break_link_from: false,\n // [false!] prefer false if results too easy to break links - implement with ALT or TODO custom keys\n click_do_break_link_to: false,\n // [false!]prefer false, way too easy to break links\n ctrl_alt_click_do_break_link: true,\n // [true!] who accidentally ctrl-alt-clicks on an in/output? nobody! that's who!\n search_hide_on_mouse_leave: true,\n // [false on mobile] better true if not touch device, TODO add an helper/listener to close if false\n search_filter_enabled: false,\n // [true!] enable filtering slots type in the search widget, !requires auto_load_slot_types or manual set registered_slot_[in/out]_types and slot_types_[in/out]\n search_show_all_on_open: true,\n // [true!] opens the results list when opening the search widget\n auto_load_slot_types: false,\n // [if want false, use true, run, get vars values to be statically set, than disable] nodes types and nodeclass association with node types need to be calculated, if dont want this, calculate once and set registered_slot_[in/out]_types and slot_types_[in/out]\n // set these values if not using auto_load_slot_types\n registered_slot_in_types: {},\n // slot types for nodeclass\n registered_slot_out_types: {},\n // slot types for nodeclass\n slot_types_in: [],\n // slot types IN\n slot_types_out: [],\n // slot types OUT\n slot_types_default_in: [],\n // specify for each IN slot type a(/many) default node(s), use single string, array, or object (with node, title, parameters, ..) like for search\n slot_types_default_out: [],\n // specify for each OUT slot type a(/many) default node(s), use single string, array, or object (with node, title, parameters, ..) like for search\n alt_drag_do_clone_nodes: false,\n // [true!] very handy, ALT click to clone and drag the new node\n do_add_triggers_slots: false,\n // [true!] will create and connect event slots when using action/events connections, !WILL CHANGE node mode when using onTrigger (enable mode colors), onExecuted does not need this\n allow_multi_output_for_events: true,\n // [false!] being events, it is strongly reccomended to use them sequentially, one by one\n middle_click_slot_add_default_node: false,\n //[true!] allows to create and connect a ndoe clicking with the third button (wheel)\n release_link_on_empty_shows_menu: false,\n //[true!] dragging a link to empty space will open a menu, add from list, search or defaults\n pointerevents_method: \"pointer\",\n // \"mouse\"|\"pointer\" use mouse for retrocompatibility issues? (none found @ now)\n // TODO implement pointercancel, gotpointercapture, lostpointercapture, (pointerover, pointerout if necessary)\n ctrl_shift_v_paste_connect_unselected_outputs: true,\n //[true!] allows ctrl + shift + v to paste nodes with the outputs of the unselected nodes connected with the inputs of the newly pasted nodes\n // if true, all newly created nodes/links will use string UUIDs for their id fields instead of integers.\n // use this if you must have node IDs that are unique across all graphs and subgraphs.\n use_uuids: false,\n /**\n * Register a node class so it can be listed when the user wants to create a new one\n * @method registerNodeType\n * @param {String} type name of the node and path\n * @param {Class} base_class class containing the structure of a node\n */\n registerNodeType: function(type, base_class) {\n if (!base_class.prototype) {\n throw \"Cannot register a simple object, it must be a class with a prototype\";\n }\n base_class.type = type;\n if (LiteGraph.debug) {\n console.log(\"Node registered: \" + type);\n }\n const classname = base_class.name;\n const pos2 = type.lastIndexOf(\"/\");\n base_class.category = type.substring(0, pos2);\n if (!base_class.title) {\n base_class.title = classname;\n }\n for (var i2 in LGraphNode.prototype) {\n if (!base_class.prototype[i2]) {\n base_class.prototype[i2] = LGraphNode.prototype[i2];\n }\n }\n const prev = this.registered_node_types[type];\n if (prev) {\n console.log(\"replacing node type: \" + type);\n }\n if (!Object.prototype.hasOwnProperty.call(base_class.prototype, \"shape\")) {\n Object.defineProperty(base_class.prototype, \"shape\", {\n set: function(v2) {\n switch (v2) {\n case \"default\":\n delete this._shape;\n break;\n case \"box\":\n this._shape = LiteGraph.BOX_SHAPE;\n break;\n case \"round\":\n this._shape = LiteGraph.ROUND_SHAPE;\n break;\n case \"circle\":\n this._shape = LiteGraph.CIRCLE_SHAPE;\n break;\n case \"card\":\n this._shape = LiteGraph.CARD_SHAPE;\n break;\n default:\n this._shape = v2;\n }\n },\n get: function() {\n return this._shape;\n },\n enumerable: true,\n configurable: true\n });\n if (base_class.supported_extensions) {\n for (let i3 in base_class.supported_extensions) {\n const ext = base_class.supported_extensions[i3];\n if (ext && ext.constructor === String) {\n this.node_types_by_file_extension[ext.toLowerCase()] = base_class;\n }\n }\n }\n }\n this.registered_node_types[type] = base_class;\n if (base_class.constructor.name) {\n this.Nodes[classname] = base_class;\n }\n if (LiteGraph.onNodeTypeRegistered) {\n LiteGraph.onNodeTypeRegistered(type, base_class);\n }\n if (prev && LiteGraph.onNodeTypeReplaced) {\n LiteGraph.onNodeTypeReplaced(type, base_class, prev);\n }\n if (base_class.prototype.onPropertyChange) {\n console.warn(\n \"LiteGraph node class \" + type + \" has onPropertyChange method, it must be called onPropertyChanged with d at the end\"\n );\n }\n if (this.auto_load_slot_types) {\n new base_class(base_class.title || \"tmpnode\");\n }\n },\n /**\n * removes a node type from the system\n * @method unregisterNodeType\n * @param {String|Object} type name of the node or the node constructor itself\n */\n unregisterNodeType: function(type) {\n const base_class = type.constructor === String ? this.registered_node_types[type] : type;\n if (!base_class) {\n throw \"node type not found: \" + type;\n }\n delete this.registered_node_types[base_class.type];\n if (base_class.constructor.name) {\n delete this.Nodes[base_class.constructor.name];\n }\n },\n /**\n * Save a slot type and his node\n * @method registerSlotType\n * @param {String|Object} type name of the node or the node constructor itself\n * @param {String} slot_type name of the slot type (variable type), eg. string, number, array, boolean, ..\n */\n registerNodeAndSlotType: function(type, slot_type, out) {\n out = out || false;\n const base_class = type.constructor === String && this.registered_node_types[type] !== \"anonymous\" ? this.registered_node_types[type] : type;\n const class_type = base_class.constructor.type;\n let allTypes = [];\n if (typeof slot_type === \"string\") {\n allTypes = slot_type.split(\",\");\n } else if (slot_type == this.EVENT || slot_type == this.ACTION) {\n allTypes = [\"_event_\"];\n } else {\n allTypes = [\"*\"];\n }\n for (let i2 = 0; i2 < allTypes.length; ++i2) {\n let slotType = allTypes[i2];\n if (slotType === \"\") {\n slotType = \"*\";\n }\n const registerTo = out ? \"registered_slot_out_types\" : \"registered_slot_in_types\";\n if (this[registerTo][slotType] === void 0) {\n this[registerTo][slotType] = { nodes: [] };\n }\n if (!this[registerTo][slotType].nodes.includes(class_type)) {\n this[registerTo][slotType].nodes.push(class_type);\n }\n if (!out) {\n if (!this.slot_types_in.includes(slotType.toLowerCase())) {\n this.slot_types_in.push(slotType.toLowerCase());\n this.slot_types_in.sort();\n }\n } else {\n if (!this.slot_types_out.includes(slotType.toLowerCase())) {\n this.slot_types_out.push(slotType.toLowerCase());\n this.slot_types_out.sort();\n }\n }\n }\n },\n /**\n * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function.\n * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output.\n * @method wrapFunctionAsNode\n * @param {String} name node name with namespace (p.e.: 'math/sum')\n * @param {Function} func\n * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type\n * @param {String} return_type [optional] string with the return type, otherwise it will be generic\n * @param {Object} properties [optional] properties to be configurable\n */\n wrapFunctionAsNode: function(name, func, param_types, return_type, properties) {\n var params = Array(func.length);\n var code = \"\";\n var names = LiteGraph.getParameterNames(func);\n for (var i2 = 0; i2 < names.length; ++i2) {\n code += \"this.addInput('\" + names[i2] + \"',\" + (param_types && param_types[i2] ? \"'\" + param_types[i2] + \"'\" : \"0\") + \");\\n\";\n }\n code += \"this.addOutput('out',\" + (return_type ? \"'\" + return_type + \"'\" : 0) + \");\\n\";\n if (properties) {\n code += \"this.properties = \" + JSON.stringify(properties) + \";\\n\";\n }\n var classobj = Function(code);\n classobj.title = name.split(\"/\").pop();\n classobj.desc = \"Generated from \" + func.name;\n classobj.prototype.onExecute = function onExecute() {\n for (var i3 = 0; i3 < params.length; ++i3) {\n params[i3] = this.getInputData(i3);\n }\n var r = func.apply(this, params);\n this.setOutputData(0, r);\n };\n this.registerNodeType(name, classobj);\n },\n /**\n * Removes all previously registered node's types\n */\n clearRegisteredTypes: function() {\n this.registered_node_types = {};\n this.node_types_by_file_extension = {};\n this.Nodes = {};\n this.searchbox_extras = {};\n },\n /**\n * Adds this method to all nodetypes, existing and to be created\n * (You can add it to LGraphNode.prototype but then existing node types wont have it)\n * @method addNodeMethod\n * @param {Function} func\n */\n addNodeMethod: function(name, func) {\n LGraphNode.prototype[name] = func;\n for (var i2 in this.registered_node_types) {\n var type = this.registered_node_types[i2];\n if (type.prototype[name]) {\n type.prototype[\"_\" + name] = type.prototype[name];\n }\n type.prototype[name] = func;\n }\n },\n /**\n * Create a node of a given type with a name. The node is not attached to any graph yet.\n * @method createNode\n * @param {String} type full name of the node class. p.e. \"math/sin\"\n * @param {String} name a name to distinguish from other nodes\n * @param {Object} options to set options\n */\n createNode: function(type, title, options) {\n var base_class = this.registered_node_types[type];\n if (!base_class) {\n if (LiteGraph.debug) {\n console.log(\n 'GraphNode type \"' + type + '\" not registered.'\n );\n }\n return null;\n }\n base_class.prototype || base_class;\n title = title || base_class.title || type;\n var node2 = null;\n if (LiteGraph.catch_exceptions) {\n try {\n node2 = new base_class(title);\n } catch (err) {\n console.error(err);\n return null;\n }\n } else {\n node2 = new base_class(title);\n }\n node2.type = type;\n if (!node2.title && title) {\n node2.title = title;\n }\n if (!node2.properties) {\n node2.properties = {};\n }\n if (!node2.properties_info) {\n node2.properties_info = [];\n }\n if (!node2.flags) {\n node2.flags = {};\n }\n if (!node2.size) {\n node2.size = node2.computeSize();\n }\n if (!node2.pos) {\n node2.pos = LiteGraph.DEFAULT_POSITION.concat();\n }\n if (!node2.mode) {\n node2.mode = LiteGraph.ALWAYS;\n }\n if (options) {\n for (var i2 in options) {\n node2[i2] = options[i2];\n }\n }\n if (node2.onNodeCreated) {\n node2.onNodeCreated();\n }\n return node2;\n },\n /**\n * Returns a registered node type with a given name\n * @method getNodeType\n * @param {String} type full name of the node class. p.e. \"math/sin\"\n * @return {Class} the node class\n */\n getNodeType: function(type) {\n return this.registered_node_types[type];\n },\n /**\n * Returns a list of node types matching one category\n * @method getNodeType\n * @param {String} category category name\n * @return {Array} array with all the node classes\n */\n getNodeTypesInCategory: function(category, filter) {\n var r = [];\n for (var i2 in this.registered_node_types) {\n var type = this.registered_node_types[i2];\n if (type.filter != filter) {\n continue;\n }\n if (category == \"\") {\n if (type.category == null) {\n r.push(type);\n }\n } else if (type.category == category) {\n r.push(type);\n }\n }\n if (this.auto_sort_node_types) {\n r.sort(function(a, b) {\n return a.title.localeCompare(b.title);\n });\n }\n return r;\n },\n /**\n * Returns a list with all the node type categories\n * @method getNodeTypesCategories\n * @param {String} filter only nodes with ctor.filter equal can be shown\n * @return {Array} array with all the names of the categories\n */\n getNodeTypesCategories: function(filter) {\n var categories = { \"\": 1 };\n for (var i2 in this.registered_node_types) {\n var type = this.registered_node_types[i2];\n if (type.category && !type.skip_list) {\n if (type.filter != filter)\n continue;\n categories[type.category] = 1;\n }\n }\n var result = [];\n for (var i2 in categories) {\n result.push(i2);\n }\n return this.auto_sort_node_types ? result.sort() : result;\n },\n //debug purposes: reloads all the js scripts that matches a wildcard\n reloadNodes: function(folder_wildcard) {\n var tmp = document.getElementsByTagName(\"script\");\n var script_files = [];\n for (var i2 = 0; i2 < tmp.length; i2++) {\n script_files.push(tmp[i2]);\n }\n var docHeadObj = document.getElementsByTagName(\"head\")[0];\n folder_wildcard = document.location.href + folder_wildcard;\n for (var i2 = 0; i2 < script_files.length; i2++) {\n var src = script_files[i2].src;\n if (!src || src.substr(0, folder_wildcard.length) != folder_wildcard) {\n continue;\n }\n try {\n if (LiteGraph.debug) {\n console.log(\"Reloading: \" + src);\n }\n var dynamicScript = document.createElement(\"script\");\n dynamicScript.type = \"text/javascript\";\n dynamicScript.src = src;\n docHeadObj.appendChild(dynamicScript);\n docHeadObj.removeChild(script_files[i2]);\n } catch (err) {\n if (LiteGraph.throw_errors) {\n throw err;\n }\n if (LiteGraph.debug) {\n console.log(\"Error while reloading \" + src);\n }\n }\n }\n if (LiteGraph.debug) {\n console.log(\"Nodes reloaded\");\n }\n },\n //separated just to improve if it doesn't work\n cloneObject: function(obj, target) {\n if (obj == null) {\n return null;\n }\n var r = JSON.parse(JSON.stringify(obj));\n if (!target) {\n return r;\n }\n for (var i2 in r) {\n target[i2] = r[i2];\n }\n return target;\n },\n /*\n * https://gist.github.com/jed/982883?permalink_comment_id=852670#gistcomment-852670\n */\n uuidv4: function() {\n return (\"10000000-1000-4000-8000\" + -1e11).replace(/[018]/g, (a) => (a ^ Math.random() * 16 >> a / 4).toString(16));\n },\n /**\n * Returns if the types of two slots are compatible (taking into account wildcards, etc)\n * @method isValidConnection\n * @param {String} type_a\n * @param {String} type_b\n * @return {Boolean} true if they can be connected\n */\n isValidConnection: function(type_a, type_b) {\n if (type_a == \"\" || type_a === \"*\") type_a = 0;\n if (type_b == \"\" || type_b === \"*\") type_b = 0;\n if (!type_a || !type_b || type_a == type_b || type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) {\n return true;\n }\n type_a = String(type_a);\n type_b = String(type_b);\n type_a = type_a.toLowerCase();\n type_b = type_b.toLowerCase();\n if (type_a.indexOf(\",\") == -1 && type_b.indexOf(\",\") == -1) {\n return type_a == type_b;\n }\n var supported_types_a = type_a.split(\",\");\n var supported_types_b = type_b.split(\",\");\n for (var i2 = 0; i2 < supported_types_a.length; ++i2) {\n for (var j = 0; j < supported_types_b.length; ++j) {\n if (this.isValidConnection(supported_types_a[i2], supported_types_b[j])) {\n return true;\n }\n }\n }\n return false;\n },\n /**\n * Register a string in the search box so when the user types it it will recommend this node\n * @method registerSearchboxExtra\n * @param {String} node_type the node recommended\n * @param {String} description text to show next to it\n * @param {Object} data it could contain info of how the node should be configured\n * @return {Boolean} true if they can be connected\n */\n registerSearchboxExtra: function(node_type, description, data) {\n this.searchbox_extras[description.toLowerCase()] = {\n type: node_type,\n desc: description,\n data\n };\n },\n /**\n * Wrapper to load files (from url using fetch or from file using FileReader)\n * @method fetchFile\n * @param {String|File|Blob} url the url of the file (or the file itself)\n * @param {String} type an string to know how to fetch it: \"text\",\"arraybuffer\",\"json\",\"blob\"\n * @param {Function} on_complete callback(data)\n * @param {Function} on_error in case of an error\n * @return {FileReader|Promise} returns the object used to \n */\n fetchFile: function(url, type, on_complete, on_error) {\n if (!url)\n return null;\n type = type || \"text\";\n if (url.constructor === String) {\n if (url.substr(0, 4) == \"http\" && LiteGraph.proxy) {\n url = LiteGraph.proxy + url.substr(url.indexOf(\":\") + 3);\n }\n return fetch(url).then(function(response) {\n if (!response.ok)\n throw new Error(\"File not found\");\n if (type == \"arraybuffer\")\n return response.arrayBuffer();\n else if (type == \"text\" || type == \"string\")\n return response.text();\n else if (type == \"json\")\n return response.json();\n else if (type == \"blob\")\n return response.blob();\n }).then(function(data) {\n if (on_complete)\n on_complete(data);\n }).catch(function(error) {\n console.error(\"error fetching file:\", url);\n if (on_error)\n on_error(error);\n });\n } else if (url.constructor === File || url.constructor === Blob) {\n var reader = new FileReader();\n reader.onload = function(e) {\n var v2 = e.target.result;\n if (type == \"json\")\n v2 = JSON.parse(v2);\n if (on_complete)\n on_complete(v2);\n };\n if (type == \"arraybuffer\")\n return reader.readAsArrayBuffer(url);\n else if (type == \"text\" || type == \"json\")\n return reader.readAsText(url);\n else if (type == \"blob\")\n return reader.readAsBinaryString(url);\n }\n return null;\n }\n };\n if (typeof performance != \"undefined\") {\n LiteGraph.getTime = performance.now.bind(performance);\n } else if (typeof Date != \"undefined\" && Date.now) {\n LiteGraph.getTime = Date.now.bind(Date);\n } else if (typeof process != \"undefined\") {\n LiteGraph.getTime = function() {\n var t = process.hrtime();\n return t[0] * 1e-3 + t[1] * 1e-6;\n };\n } else {\n LiteGraph.getTime = function getTime() {\n return (/* @__PURE__ */ new Date()).getTime();\n };\n }\n const _LGraph = class _LGraph {\n constructor(o) {\n if (LiteGraph.debug) {\n console.log(\"Graph created\");\n }\n this.list_of_graphcanvas = null;\n this.clear();\n if (o) {\n this.configure(o);\n }\n }\n //used to know which types of connections support this graph (some graphs do not allow certain types)\n getSupportedTypes() {\n return this.supported_types || _LGraph.supported_types;\n }\n /**\n * Removes all nodes from this graph\n * @method clear\n */\n clear() {\n this.stop();\n this.status = _LGraph.STATUS_STOPPED;\n this.last_node_id = 0;\n this.last_link_id = 0;\n this._version = -1;\n if (this._nodes) {\n for (var i2 = 0; i2 < this._nodes.length; ++i2) {\n var node2 = this._nodes[i2];\n if (node2.onRemoved) {\n node2.onRemoved();\n }\n }\n }\n this._nodes = [];\n this._nodes_by_id = {};\n this._nodes_in_order = [];\n this._nodes_executable = null;\n this._groups = [];\n this.links = {};\n this.iteration = 0;\n this.config = {};\n this.vars = {};\n this.extra = {};\n this.globaltime = 0;\n this.runningtime = 0;\n this.fixedtime = 0;\n this.fixedtime_lapse = 0.01;\n this.elapsed_time = 0.01;\n this.last_update_time = 0;\n this.starttime = 0;\n this.catch_errors = true;\n this.nodes_executing = [];\n this.nodes_actioning = [];\n this.nodes_executedAction = [];\n this.inputs = {};\n this.outputs = {};\n this.change();\n this.sendActionToCanvas(\"clear\");\n }\n /**\n * Attach Canvas to this graph\n * @method attachCanvas\n * @param {GraphCanvas} graph_canvas\n */\n attachCanvas(graphcanvas) {\n if (graphcanvas.constructor != LGraphCanvas) {\n throw \"attachCanvas expects a LGraphCanvas instance\";\n }\n if (graphcanvas.graph && graphcanvas.graph != this) {\n graphcanvas.graph.detachCanvas(graphcanvas);\n }\n graphcanvas.graph = this;\n if (!this.list_of_graphcanvas) {\n this.list_of_graphcanvas = [];\n }\n this.list_of_graphcanvas.push(graphcanvas);\n }\n /**\n * Detach Canvas from this graph\n * @method detachCanvas\n * @param {GraphCanvas} graph_canvas\n */\n detachCanvas(graphcanvas) {\n if (!this.list_of_graphcanvas) {\n return;\n }\n var pos2 = this.list_of_graphcanvas.indexOf(graphcanvas);\n if (pos2 == -1) {\n return;\n }\n graphcanvas.graph = null;\n this.list_of_graphcanvas.splice(pos2, 1);\n }\n /**\n * Starts running this graph every interval milliseconds.\n * @method start\n * @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate\n */\n start(interval) {\n if (this.status == _LGraph.STATUS_RUNNING) {\n return;\n }\n this.status = _LGraph.STATUS_RUNNING;\n if (this.onPlayEvent) {\n this.onPlayEvent();\n }\n this.sendEventToAllNodes(\"onStart\");\n this.starttime = LiteGraph.getTime();\n this.last_update_time = this.starttime;\n interval = interval || 0;\n var that2 = this;\n if (interval == 0 && typeof window != \"undefined\" && window.requestAnimationFrame) {\n let on_frame = function() {\n if (that2.execution_timer_id != -1) {\n return;\n }\n window.requestAnimationFrame(on_frame);\n if (that2.onBeforeStep)\n that2.onBeforeStep();\n that2.runStep(1, !that2.catch_errors);\n if (that2.onAfterStep)\n that2.onAfterStep();\n };\n this.execution_timer_id = -1;\n on_frame();\n } else {\n this.execution_timer_id = setInterval(function() {\n if (that2.onBeforeStep)\n that2.onBeforeStep();\n that2.runStep(1, !that2.catch_errors);\n if (that2.onAfterStep)\n that2.onAfterStep();\n }, interval);\n }\n }\n /**\n * Stops the execution loop of the graph\n * @method stop execution\n */\n stop() {\n if (this.status == _LGraph.STATUS_STOPPED) {\n return;\n }\n this.status = _LGraph.STATUS_STOPPED;\n if (this.onStopEvent) {\n this.onStopEvent();\n }\n if (this.execution_timer_id != null) {\n if (this.execution_timer_id != -1) {\n clearInterval(this.execution_timer_id);\n }\n this.execution_timer_id = null;\n }\n this.sendEventToAllNodes(\"onStop\");\n }\n /**\n * Run N steps (cycles) of the graph\n * @method runStep\n * @param {number} num number of steps to run, default is 1\n * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors\n * @param {number} limit max number of nodes to execute (used to execute from start to a node)\n */\n runStep(num, do_not_catch_errors, limit) {\n num = num || 1;\n var start = LiteGraph.getTime();\n this.globaltime = 1e-3 * (start - this.starttime);\n var nodes = this._nodes_executable ? this._nodes_executable : this._nodes;\n if (!nodes) {\n return;\n }\n limit = limit || nodes.length;\n if (do_not_catch_errors) {\n for (var i2 = 0; i2 < num; i2++) {\n for (var j = 0; j < limit; ++j) {\n var node2 = nodes[j];\n if (node2.mode == LiteGraph.ALWAYS && node2.onExecute) {\n node2.doExecute();\n }\n }\n this.fixedtime += this.fixedtime_lapse;\n if (this.onExecuteStep) {\n this.onExecuteStep();\n }\n }\n if (this.onAfterExecute) {\n this.onAfterExecute();\n }\n } else {\n try {\n for (var i2 = 0; i2 < num; i2++) {\n for (var j = 0; j < limit; ++j) {\n var node2 = nodes[j];\n if (node2.mode == LiteGraph.ALWAYS && node2.onExecute) {\n node2.onExecute();\n }\n }\n this.fixedtime += this.fixedtime_lapse;\n if (this.onExecuteStep) {\n this.onExecuteStep();\n }\n }\n if (this.onAfterExecute) {\n this.onAfterExecute();\n }\n this.errors_in_execution = false;\n } catch (err) {\n this.errors_in_execution = true;\n if (LiteGraph.throw_errors) {\n throw err;\n }\n if (LiteGraph.debug) {\n console.log(\"Error during execution: \" + err);\n }\n this.stop();\n }\n }\n var now = LiteGraph.getTime();\n var elapsed = now - start;\n if (elapsed == 0) {\n elapsed = 1;\n }\n this.execution_time = 1e-3 * elapsed;\n this.globaltime += 1e-3 * elapsed;\n this.iteration += 1;\n this.elapsed_time = (now - this.last_update_time) * 1e-3;\n this.last_update_time = now;\n this.nodes_executing = [];\n this.nodes_actioning = [];\n this.nodes_executedAction = [];\n }\n /**\n * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than\n * nodes with only inputs.\n * @method updateExecutionOrder\n */\n updateExecutionOrder() {\n this._nodes_in_order = this.computeExecutionOrder(false);\n this._nodes_executable = [];\n for (var i2 = 0; i2 < this._nodes_in_order.length; ++i2) {\n if (this._nodes_in_order[i2].onExecute) {\n this._nodes_executable.push(this._nodes_in_order[i2]);\n }\n }\n }\n //This is more internal, it computes the executable nodes in order and returns it\n computeExecutionOrder(only_onExecute, set_level) {\n var L = [];\n var S = [];\n var M = {};\n var visited_links = {};\n var remaining_links = {};\n for (var i2 = 0, l = this._nodes.length; i2 < l; ++i2) {\n var node2 = this._nodes[i2];\n if (only_onExecute && !node2.onExecute) {\n continue;\n }\n M[node2.id] = node2;\n var num = 0;\n if (node2.inputs) {\n for (var j = 0, l2 = node2.inputs.length; j < l2; j++) {\n if (node2.inputs[j] && node2.inputs[j].link != null) {\n num += 1;\n }\n }\n }\n if (num == 0) {\n S.push(node2);\n if (set_level) {\n node2._level = 1;\n }\n } else {\n if (set_level) {\n node2._level = 0;\n }\n remaining_links[node2.id] = num;\n }\n }\n while (true) {\n if (S.length == 0) {\n break;\n }\n var node2 = S.shift();\n L.push(node2);\n delete M[node2.id];\n if (!node2.outputs) {\n continue;\n }\n for (var i2 = 0; i2 < node2.outputs.length; i2++) {\n var output = node2.outputs[i2];\n if (output == null || output.links == null || output.links.length == 0) {\n continue;\n }\n for (var j = 0; j < output.links.length; j++) {\n var link_id = output.links[j];\n var link = this.links[link_id];\n if (!link) {\n continue;\n }\n if (visited_links[link.id]) {\n continue;\n }\n var target_node = this.getNodeById(link.target_id);\n if (target_node == null) {\n visited_links[link.id] = true;\n continue;\n }\n if (set_level && (!target_node._level || target_node._level <= node2._level)) {\n target_node._level = node2._level + 1;\n }\n visited_links[link.id] = true;\n remaining_links[target_node.id] -= 1;\n if (remaining_links[target_node.id] == 0) {\n S.push(target_node);\n }\n }\n }\n }\n for (var i2 in M) {\n L.push(M[i2]);\n }\n if (L.length != this._nodes.length && LiteGraph.debug) {\n console.warn(\"something went wrong, nodes missing\");\n }\n var l = L.length;\n for (var i2 = 0; i2 < l; ++i2) {\n L[i2].order = i2;\n }\n L = L.sort(function(A, B) {\n var Ap = A.constructor.priority || A.priority || 0;\n var Bp = B.constructor.priority || B.priority || 0;\n if (Ap == Bp) {\n return A.order - B.order;\n }\n return Ap - Bp;\n });\n for (var i2 = 0; i2 < l; ++i2) {\n L[i2].order = i2;\n }\n return L;\n }\n /**\n * Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively.\n * It doesn't include the node itself\n * @method getAncestors\n * @return {Array} an array with all the LGraphNodes that affect this node, in order of execution\n */\n getAncestors(node2) {\n var ancestors = [];\n var pending = [node2];\n var visited = {};\n while (pending.length) {\n var current = pending.shift();\n if (!current.inputs) {\n continue;\n }\n if (!visited[current.id] && current != node2) {\n visited[current.id] = true;\n ancestors.push(current);\n }\n for (var i2 = 0; i2 < current.inputs.length; ++i2) {\n var input = current.getInputNode(i2);\n if (input && ancestors.indexOf(input) == -1) {\n pending.push(input);\n }\n }\n }\n ancestors.sort(function(a, b) {\n return a.order - b.order;\n });\n return ancestors;\n }\n /**\n * Positions every node in a more readable manner\n * @method arrange\n */\n arrange(margin, layout) {\n margin = margin || 100;\n const nodes = this.computeExecutionOrder(false, true);\n const columns = [];\n for (let i2 = 0; i2 < nodes.length; ++i2) {\n const node2 = nodes[i2];\n const col = node2._level || 1;\n if (!columns[col]) {\n columns[col] = [];\n }\n columns[col].push(node2);\n }\n let x2 = margin;\n for (let i2 = 0; i2 < columns.length; ++i2) {\n const column = columns[i2];\n if (!column) {\n continue;\n }\n let max_size = 100;\n let y2 = margin + LiteGraph.NODE_TITLE_HEIGHT;\n for (let j = 0; j < column.length; ++j) {\n const node2 = column[j];\n node2.pos[0] = layout == LiteGraph.VERTICAL_LAYOUT ? y2 : x2;\n node2.pos[1] = layout == LiteGraph.VERTICAL_LAYOUT ? x2 : y2;\n const max_size_index = layout == LiteGraph.VERTICAL_LAYOUT ? 1 : 0;\n if (node2.size[max_size_index] > max_size) {\n max_size = node2.size[max_size_index];\n }\n const node_size_index = layout == LiteGraph.VERTICAL_LAYOUT ? 0 : 1;\n y2 += node2.size[node_size_index] + margin + LiteGraph.NODE_TITLE_HEIGHT;\n }\n x2 += max_size + margin;\n }\n this.setDirtyCanvas(true, true);\n }\n /**\n * Returns the amount of time the graph has been running in milliseconds\n * @method getTime\n * @return {number} number of milliseconds the graph has been running\n */\n getTime() {\n return this.globaltime;\n }\n /**\n * Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant\n * @method getFixedTime\n * @return {number} number of milliseconds the graph has been running\n */\n getFixedTime() {\n return this.fixedtime;\n }\n /**\n * Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct\n * if the nodes are using graphical actions\n * @method getElapsedTime\n * @return {number} number of milliseconds it took the last cycle\n */\n getElapsedTime() {\n return this.elapsed_time;\n }\n /**\n * Sends an event to all the nodes, useful to trigger stuff\n * @method sendEventToAllNodes\n * @param {String} eventname the name of the event (function to be called)\n * @param {Array} params parameters in array format\n */\n sendEventToAllNodes(eventname, params, mode) {\n mode = mode || LiteGraph.ALWAYS;\n var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes;\n if (!nodes) {\n return;\n }\n for (var j = 0, l = nodes.length; j < l; ++j) {\n var node2 = nodes[j];\n if (node2.constructor === LiteGraph.Subgraph && eventname != \"onExecute\") {\n if (node2.mode == mode) {\n node2.sendEventToAllNodes(eventname, params, mode);\n }\n continue;\n }\n if (!node2[eventname] || node2.mode != mode) {\n continue;\n }\n if (params === void 0) {\n node2[eventname]();\n } else if (params && params.constructor === Array) {\n node2[eventname].apply(node2, params);\n } else {\n node2[eventname](params);\n }\n }\n }\n sendActionToCanvas(action, params) {\n if (!this.list_of_graphcanvas) {\n return;\n }\n for (var i2 = 0; i2 < this.list_of_graphcanvas.length; ++i2) {\n var c = this.list_of_graphcanvas[i2];\n if (c[action]) {\n c[action].apply(c, params);\n }\n }\n }\n /**\n * Adds a new node instance to this graph\n * @method add\n * @param {LGraphNode} node the instance of the node\n */\n add(node2, skip_compute_order) {\n if (!node2) {\n return;\n }\n if (node2.constructor === LGraphGroup) {\n this._groups.push(node2);\n this.setDirtyCanvas(true);\n this.change();\n node2.graph = this;\n this._version++;\n return;\n }\n if (node2.id != -1 && this._nodes_by_id[node2.id] != null) {\n console.warn(\n \"LiteGraph: there is already a node with this ID, changing it\"\n );\n if (LiteGraph.use_uuids) {\n node2.id = LiteGraph.uuidv4();\n } else {\n node2.id = ++this.last_node_id;\n }\n }\n if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) {\n throw \"LiteGraph: max number of nodes in a graph reached\";\n }\n if (LiteGraph.use_uuids) {\n if (node2.id == null || node2.id == -1)\n node2.id = LiteGraph.uuidv4();\n } else {\n if (node2.id == null || node2.id == -1) {\n node2.id = ++this.last_node_id;\n } else if (this.last_node_id < node2.id) {\n this.last_node_id = node2.id;\n }\n }\n node2.graph = this;\n this._version++;\n this._nodes.push(node2);\n this._nodes_by_id[node2.id] = node2;\n if (node2.onAdded) {\n node2.onAdded(this);\n }\n if (this.config.align_to_grid) {\n node2.alignToGrid();\n }\n if (!skip_compute_order) {\n this.updateExecutionOrder();\n }\n if (this.onNodeAdded) {\n this.onNodeAdded(node2);\n }\n this.setDirtyCanvas(true);\n this.change();\n return node2;\n }\n /**\n * Removes a node from the graph\n * @method remove\n * @param {LGraphNode} node the instance of the node\n */\n remove(node2) {\n if (node2.constructor === LiteGraph.LGraphGroup) {\n var index2 = this._groups.indexOf(node2);\n if (index2 != -1) {\n this._groups.splice(index2, 1);\n }\n node2.graph = null;\n this._version++;\n this.setDirtyCanvas(true, true);\n this.change();\n return;\n }\n if (this._nodes_by_id[node2.id] == null) {\n return;\n }\n if (node2.ignore_remove) {\n return;\n }\n this.beforeChange();\n if (node2.inputs) {\n for (var i2 = 0; i2 < node2.inputs.length; i2++) {\n var slot = node2.inputs[i2];\n if (slot.link != null) {\n node2.disconnectInput(i2);\n }\n }\n }\n if (node2.outputs) {\n for (var i2 = 0; i2 < node2.outputs.length; i2++) {\n var slot = node2.outputs[i2];\n if (slot.links != null && slot.links.length) {\n node2.disconnectOutput(i2);\n }\n }\n }\n if (node2.onRemoved) {\n node2.onRemoved();\n }\n node2.graph = null;\n this._version++;\n if (this.list_of_graphcanvas) {\n for (var i2 = 0; i2 < this.list_of_graphcanvas.length; ++i2) {\n var canvas = this.list_of_graphcanvas[i2];\n if (canvas.selected_nodes[node2.id]) {\n delete canvas.selected_nodes[node2.id];\n }\n if (canvas.node_dragged == node2) {\n canvas.node_dragged = null;\n }\n }\n }\n var pos2 = this._nodes.indexOf(node2);\n if (pos2 != -1) {\n this._nodes.splice(pos2, 1);\n }\n delete this._nodes_by_id[node2.id];\n if (this.onNodeRemoved) {\n this.onNodeRemoved(node2);\n }\n this.sendActionToCanvas(\"checkPanels\");\n this.setDirtyCanvas(true, true);\n this.afterChange();\n this.change();\n this.updateExecutionOrder();\n }\n /**\n * Returns a node by its id.\n * @method getNodeById\n * @param {Number} id\n */\n getNodeById(id) {\n if (id == null) {\n return null;\n }\n return this._nodes_by_id[id];\n }\n /**\n * Returns a list of nodes that matches a class\n * @method findNodesByClass\n * @param {Class} classObject the class itself (not an string)\n * @return {Array} a list with all the nodes of this type\n */\n findNodesByClass(classObject, result) {\n result = result || [];\n result.length = 0;\n for (var i2 = 0, l = this._nodes.length; i2 < l; ++i2) {\n if (this._nodes[i2].constructor === classObject) {\n result.push(this._nodes[i2]);\n }\n }\n return result;\n }\n /**\n * Returns a list of nodes that matches a type\n * @method findNodesByType\n * @param {String} type the name of the node type\n * @return {Array} a list with all the nodes of this type\n */\n findNodesByType(type, result) {\n var type = type.toLowerCase();\n result = result || [];\n result.length = 0;\n for (var i2 = 0, l = this._nodes.length; i2 < l; ++i2) {\n if (this._nodes[i2].type.toLowerCase() == type) {\n result.push(this._nodes[i2]);\n }\n }\n return result;\n }\n /**\n * Returns the first node that matches a name in its title\n * @method findNodeByTitle\n * @param {String} name the name of the node to search\n * @return {Node} the node or null\n */\n findNodeByTitle(title) {\n for (var i2 = 0, l = this._nodes.length; i2 < l; ++i2) {\n if (this._nodes[i2].title == title) {\n return this._nodes[i2];\n }\n }\n return null;\n }\n /**\n * Returns a list of nodes that matches a name\n * @method findNodesByTitle\n * @param {String} name the name of the node to search\n * @return {Array} a list with all the nodes with this name\n */\n findNodesByTitle(title) {\n var result = [];\n for (var i2 = 0, l = this._nodes.length; i2 < l; ++i2) {\n if (this._nodes[i2].title == title) {\n result.push(this._nodes[i2]);\n }\n }\n return result;\n }\n /**\n * Returns the top-most node in this position of the canvas\n * @method getNodeOnPos\n * @param {number} x the x coordinate in canvas space\n * @param {number} y the y coordinate in canvas space\n * @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph\n * @return {LGraphNode} the node at this position or null\n */\n getNodeOnPos(x2, y2, nodes_list, margin) {\n nodes_list = nodes_list || this._nodes;\n var nRet = null;\n for (var i2 = nodes_list.length - 1; i2 >= 0; i2--) {\n var n = nodes_list[i2];\n var skip_title = n.constructor.title_mode == LiteGraph.NO_TITLE;\n if (n.isPointInside(x2, y2, margin, skip_title)) {\n return n;\n }\n }\n return nRet;\n }\n /**\n * Returns the top-most group in that position\n * @method getGroupOnPos\n * @param {number} x the x coordinate in canvas space\n * @param {number} y the y coordinate in canvas space\n * @return {LGraphGroup} the group or null\n */\n getGroupOnPos(x2, y2) {\n for (var i2 = this._groups.length - 1; i2 >= 0; i2--) {\n var g = this._groups[i2];\n if (g.isPointInside(x2, y2, 2, true)) {\n return g;\n }\n }\n return null;\n }\n /**\n * Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution\n * this replaces the ones using the old version with the new version\n * @method checkNodeTypes\n */\n checkNodeTypes() {\n for (var i2 = 0; i2 < this._nodes.length; i2++) {\n var node2 = this._nodes[i2];\n var ctor = LiteGraph.registered_node_types[node2.type];\n if (node2.constructor == ctor) {\n continue;\n }\n console.log(\"node being replaced by newer version: \" + node2.type);\n var newnode = LiteGraph.createNode(node2.type);\n this._nodes[i2] = newnode;\n newnode.configure(node2.serialize());\n newnode.graph = this;\n this._nodes_by_id[newnode.id] = newnode;\n if (node2.inputs) {\n newnode.inputs = node2.inputs.concat();\n }\n if (node2.outputs) {\n newnode.outputs = node2.outputs.concat();\n }\n }\n this.updateExecutionOrder();\n }\n // ********** GLOBALS *****************\n onAction(action, param, options) {\n this._input_nodes = this.findNodesByClass(\n LiteGraph.GraphInput,\n this._input_nodes\n );\n for (var i2 = 0; i2 < this._input_nodes.length; ++i2) {\n var node2 = this._input_nodes[i2];\n if (node2.properties.name != action) {\n continue;\n }\n node2.actionDo(action, param, options);\n break;\n }\n }\n trigger(action, param) {\n if (this.onTrigger) {\n this.onTrigger(action, param);\n }\n }\n /**\n * Tell this graph it has a global graph input of this type\n * @method addGlobalInput\n * @param {String} name\n * @param {String} type\n * @param {*} value [optional]\n */\n addInput(name, type, value) {\n var input = this.inputs[name];\n if (input) {\n return;\n }\n this.beforeChange();\n this.inputs[name] = { name, type, value };\n this._version++;\n this.afterChange();\n if (this.onInputAdded) {\n this.onInputAdded(name, type);\n }\n if (this.onInputsOutputsChange) {\n this.onInputsOutputsChange();\n }\n }\n /**\n * Assign a data to the global graph input\n * @method setGlobalInputData\n * @param {String} name\n * @param {*} data\n */\n setInputData(name, data) {\n var input = this.inputs[name];\n if (!input) {\n return;\n }\n input.value = data;\n }\n /**\n * Returns the current value of a global graph input\n * @method getInputData\n * @param {String} name\n * @return {*} the data\n */\n getInputData(name) {\n var input = this.inputs[name];\n if (!input) {\n return null;\n }\n return input.value;\n }\n /**\n * Changes the name of a global graph input\n * @method renameInput\n * @param {String} old_name\n * @param {String} new_name\n */\n renameInput(old_name, name) {\n if (name == old_name) {\n return;\n }\n if (!this.inputs[old_name]) {\n return false;\n }\n if (this.inputs[name]) {\n console.error(\"there is already one input with that name\");\n return false;\n }\n this.inputs[name] = this.inputs[old_name];\n delete this.inputs[old_name];\n this._version++;\n if (this.onInputRenamed) {\n this.onInputRenamed(old_name, name);\n }\n if (this.onInputsOutputsChange) {\n this.onInputsOutputsChange();\n }\n }\n /**\n * Changes the type of a global graph input\n * @method changeInputType\n * @param {String} name\n * @param {String} type\n */\n changeInputType(name, type) {\n if (!this.inputs[name]) {\n return false;\n }\n if (this.inputs[name].type && String(this.inputs[name].type).toLowerCase() == String(type).toLowerCase()) {\n return;\n }\n this.inputs[name].type = type;\n this._version++;\n if (this.onInputTypeChanged) {\n this.onInputTypeChanged(name, type);\n }\n }\n /**\n * Removes a global graph input\n * @method removeInput\n * @param {String} name\n * @param {String} type\n */\n removeInput(name) {\n if (!this.inputs[name]) {\n return false;\n }\n delete this.inputs[name];\n this._version++;\n if (this.onInputRemoved) {\n this.onInputRemoved(name);\n }\n if (this.onInputsOutputsChange) {\n this.onInputsOutputsChange();\n }\n return true;\n }\n /**\n * Creates a global graph output\n * @method addOutput\n * @param {String} name\n * @param {String} type\n * @param {*} value\n */\n addOutput(name, type, value) {\n this.outputs[name] = { name, type, value };\n this._version++;\n if (this.onOutputAdded) {\n this.onOutputAdded(name, type);\n }\n if (this.onInputsOutputsChange) {\n this.onInputsOutputsChange();\n }\n }\n /**\n * Assign a data to the global output\n * @method setOutputData\n * @param {String} name\n * @param {String} value\n */\n setOutputData(name, value) {\n var output = this.outputs[name];\n if (!output) {\n return;\n }\n output.value = value;\n }\n /**\n * Returns the current value of a global graph output\n * @method getOutputData\n * @param {String} name\n * @return {*} the data\n */\n getOutputData(name) {\n var output = this.outputs[name];\n if (!output) {\n return null;\n }\n return output.value;\n }\n /**\n * Renames a global graph output\n * @method renameOutput\n * @param {String} old_name\n * @param {String} new_name\n */\n renameOutput(old_name, name) {\n if (!this.outputs[old_name]) {\n return false;\n }\n if (this.outputs[name]) {\n console.error(\"there is already one output with that name\");\n return false;\n }\n this.outputs[name] = this.outputs[old_name];\n delete this.outputs[old_name];\n this._version++;\n if (this.onOutputRenamed) {\n this.onOutputRenamed(old_name, name);\n }\n if (this.onInputsOutputsChange) {\n this.onInputsOutputsChange();\n }\n }\n /**\n * Changes the type of a global graph output\n * @method changeOutputType\n * @param {String} name\n * @param {String} type\n */\n changeOutputType(name, type) {\n if (!this.outputs[name]) {\n return false;\n }\n if (this.outputs[name].type && String(this.outputs[name].type).toLowerCase() == String(type).toLowerCase()) {\n return;\n }\n this.outputs[name].type = type;\n this._version++;\n if (this.onOutputTypeChanged) {\n this.onOutputTypeChanged(name, type);\n }\n }\n /**\n * Removes a global graph output\n * @method removeOutput\n * @param {String} name\n */\n removeOutput(name) {\n if (!this.outputs[name]) {\n return false;\n }\n delete this.outputs[name];\n this._version++;\n if (this.onOutputRemoved) {\n this.onOutputRemoved(name);\n }\n if (this.onInputsOutputsChange) {\n this.onInputsOutputsChange();\n }\n return true;\n }\n triggerInput(name, value) {\n var nodes = this.findNodesByTitle(name);\n for (var i2 = 0; i2 < nodes.length; ++i2) {\n nodes[i2].onTrigger(value);\n }\n }\n setCallback(name, func) {\n var nodes = this.findNodesByTitle(name);\n for (var i2 = 0; i2 < nodes.length; ++i2) {\n nodes[i2].setTrigger(func);\n }\n }\n //used for undo, called before any change is made to the graph\n beforeChange(info) {\n if (this.onBeforeChange) {\n this.onBeforeChange(this, info);\n }\n this.sendActionToCanvas(\"onBeforeChange\", this);\n }\n //used to resend actions, called after any change is made to the graph\n afterChange(info) {\n if (this.onAfterChange) {\n this.onAfterChange(this, info);\n }\n this.sendActionToCanvas(\"onAfterChange\", this);\n }\n connectionChange(node2, link_info) {\n this.updateExecutionOrder();\n if (this.onConnectionChange) {\n this.onConnectionChange(node2);\n }\n this._version++;\n this.sendActionToCanvas(\"onConnectionChange\");\n }\n /**\n * returns if the graph is in live mode\n * @method isLive\n */\n isLive() {\n if (!this.list_of_graphcanvas) {\n return false;\n }\n for (var i2 = 0; i2 < this.list_of_graphcanvas.length; ++i2) {\n var c = this.list_of_graphcanvas[i2];\n if (c.live_mode) {\n return true;\n }\n }\n return false;\n }\n /**\n * clears the triggered slot animation in all links (stop visual animation)\n * @method clearTriggeredSlots\n */\n clearTriggeredSlots() {\n for (var i2 in this.links) {\n var link_info = this.links[i2];\n if (!link_info) {\n continue;\n }\n if (link_info._last_time) {\n link_info._last_time = 0;\n }\n }\n }\n /* Called when something visually changed (not the graph!) */\n change() {\n if (LiteGraph.debug) {\n console.log(\"Graph changed\");\n }\n this.sendActionToCanvas(\"setDirty\", [true, true]);\n if (this.on_change) {\n this.on_change(this);\n }\n }\n setDirtyCanvas(fg, bg) {\n this.sendActionToCanvas(\"setDirty\", [fg, bg]);\n }\n /**\n * Destroys a link\n * @method removeLink\n * @param {Number} link_id\n */\n removeLink(link_id) {\n var link = this.links[link_id];\n if (!link) {\n return;\n }\n var node2 = this.getNodeById(link.target_id);\n if (node2) {\n node2.disconnectInput(link.target_slot);\n }\n }\n //save and recover app state ***************************************\n /**\n * Creates a Object containing all the info about this graph, it can be serialized\n * @method serialize\n * @return {Object} value of the node\n */\n serialize() {\n var nodes_info = [];\n nodes_info = [...this._nodes].sort((a, b) => a.id - b.id).map((node2) => node2.serialize());\n var links = [];\n for (var i2 in this.links) {\n var link = this.links[i2];\n if (!link.serialize) {\n console.warn(\n \"weird LLink bug, link info is not a LLink but a regular object\"\n );\n var link2 = new LLink();\n for (var j in link) {\n link2[j] = link[j];\n }\n this.links[i2] = link2;\n link = link2;\n }\n links.push(link.serialize());\n }\n var groups_info = [];\n for (var i2 = 0; i2 < this._groups.length; ++i2) {\n groups_info.push(this._groups[i2].serialize());\n }\n var data = {\n last_node_id: this.last_node_id,\n last_link_id: this.last_link_id,\n nodes: nodes_info,\n links,\n groups: groups_info,\n config: this.config,\n extra: this.extra,\n version: LiteGraph.VERSION\n };\n if (this.onSerialize)\n this.onSerialize(data);\n return data;\n }\n /**\n * Configure a graph from a JSON string\n * @method configure\n * @param {String} str configure a graph from a JSON string\n * @param {Boolean} returns if there was any error parsing\n */\n configure(data, keep_old) {\n if (!data) {\n return;\n }\n if (!keep_old) {\n this.clear();\n }\n var nodes = data.nodes;\n if (data.links && data.links.constructor === Array) {\n var links = [];\n for (var i2 = 0; i2 < data.links.length; ++i2) {\n var link_data = data.links[i2];\n if (!link_data) {\n console.warn(\"serialized graph link data contains errors, skipping.\");\n continue;\n }\n var link = new LLink();\n link.configure(link_data);\n links[link.id] = link;\n }\n data.links = links;\n }\n for (var i2 in data) {\n if (i2 == \"nodes\" || i2 == \"groups\")\n continue;\n this[i2] = data[i2];\n }\n var error = false;\n this._nodes = [];\n if (nodes) {\n for (var i2 = 0, l = nodes.length; i2 < l; ++i2) {\n var n_info = nodes[i2];\n var node2 = LiteGraph.createNode(n_info.type, n_info.title);\n if (!node2) {\n if (LiteGraph.debug) {\n console.log(\n \"Node not found or has errors: \" + n_info.type\n );\n }\n node2 = new LGraphNode();\n node2.last_serialization = n_info;\n node2.has_errors = true;\n error = true;\n }\n node2.id = n_info.id;\n this.add(node2, true);\n }\n for (var i2 = 0, l = nodes.length; i2 < l; ++i2) {\n var n_info = nodes[i2];\n var node2 = this.getNodeById(n_info.id);\n if (node2) {\n node2.configure(n_info);\n }\n }\n }\n this._groups.length = 0;\n if (data.groups) {\n for (var i2 = 0; i2 < data.groups.length; ++i2) {\n var group = new LiteGraph.LGraphGroup();\n group.configure(data.groups[i2]);\n this.add(group);\n }\n }\n this.updateExecutionOrder();\n this.extra = data.extra || {};\n if (this.onConfigure)\n this.onConfigure(data);\n this._version++;\n this.setDirtyCanvas(true, true);\n return error;\n }\n load(url, callback) {\n var that2 = this;\n if (url.constructor === File || url.constructor === Blob) {\n var reader = new FileReader();\n reader.addEventListener(\"load\", function(event2) {\n var data = JSON.parse(event2.target.result);\n that2.configure(data);\n if (callback)\n callback();\n });\n reader.readAsText(url);\n return;\n }\n var req = new XMLHttpRequest();\n req.open(\"GET\", url, true);\n req.send(null);\n req.onload = function(oEvent) {\n if (req.status !== 200) {\n console.error(\"Error loading graph:\", req.status, req.response);\n return;\n }\n var data = JSON.parse(req.response);\n that2.configure(data);\n if (callback)\n callback();\n };\n req.onerror = function(err) {\n console.error(\"Error loading graph:\", err);\n };\n }\n onNodeTrace(node2, msg, color) {\n }\n };\n //default supported types\n __publicField(_LGraph, \"supported_types\", [\"number\", \"string\", \"boolean\"]);\n __publicField(_LGraph, \"STATUS_STOPPED\", 1);\n __publicField(_LGraph, \"STATUS_RUNNING\", 2);\n let LGraph = _LGraph;\n globalThis.LGraph = LiteGraph.LGraph = LGraph;\n class LLink {\n constructor(id, type, origin_id, origin_slot, target_id, target_slot) {\n this.id = id;\n this.type = type;\n this.origin_id = origin_id;\n this.origin_slot = origin_slot;\n this.target_id = target_id;\n this.target_slot = target_slot;\n this._data = null;\n this._pos = new Float32Array(2);\n }\n configure(o) {\n if (o.constructor === Array) {\n this.id = o[0];\n this.origin_id = o[1];\n this.origin_slot = o[2];\n this.target_id = o[3];\n this.target_slot = o[4];\n this.type = o[5];\n } else {\n this.id = o.id;\n this.type = o.type;\n this.origin_id = o.origin_id;\n this.origin_slot = o.origin_slot;\n this.target_id = o.target_id;\n this.target_slot = o.target_slot;\n }\n }\n serialize() {\n return [\n this.id,\n this.origin_id,\n this.origin_slot,\n this.target_id,\n this.target_slot,\n this.type\n ];\n }\n }\n LiteGraph.LLink = LLink;\n function LGraphNode(title) {\n this._ctor(title);\n }\n globalThis.LGraphNode = LiteGraph.LGraphNode = LGraphNode;\n LGraphNode.prototype._ctor = function(title) {\n this.title = title || \"Unnamed\";\n this.size = [LiteGraph.NODE_WIDTH, 60];\n this.graph = null;\n this._pos = new Float32Array(10, 10);\n Object.defineProperty(this, \"pos\", {\n set: function(v2) {\n if (!v2 || v2.length < 2) {\n return;\n }\n this._pos[0] = v2[0];\n this._pos[1] = v2[1];\n },\n get: function() {\n return this._pos;\n },\n enumerable: true\n });\n if (LiteGraph.use_uuids) {\n this.id = LiteGraph.uuidv4();\n } else {\n this.id = -1;\n }\n this.type = null;\n this.inputs = [];\n this.outputs = [];\n this.connections = [];\n this.properties = {};\n this.properties_info = [];\n this.flags = {};\n };\n LGraphNode.prototype.configure = function(info) {\n if (this.graph) {\n this.graph._version++;\n }\n for (var j in info) {\n if (j == \"properties\") {\n for (var k in info.properties) {\n this.properties[k] = info.properties[k];\n if (this.onPropertyChanged) {\n this.onPropertyChanged(k, info.properties[k]);\n }\n }\n continue;\n }\n if (info[j] == null) {\n continue;\n } else if (typeof info[j] == \"object\") {\n if (this[j] && this[j].configure) {\n this[j].configure(info[j]);\n } else {\n this[j] = LiteGraph.cloneObject(info[j], this[j]);\n }\n } else {\n this[j] = info[j];\n }\n }\n if (!info.title) {\n this.title = this.constructor.title;\n }\n if (this.inputs) {\n for (var i2 = 0; i2 < this.inputs.length; ++i2) {\n var input = this.inputs[i2];\n var link_info = this.graph ? this.graph.links[input.link] : null;\n if (this.onConnectionsChange)\n this.onConnectionsChange(LiteGraph.INPUT, i2, true, link_info, input);\n if (this.onInputAdded)\n this.onInputAdded(input);\n }\n }\n if (this.outputs) {\n for (var i2 = 0; i2 < this.outputs.length; ++i2) {\n var output = this.outputs[i2];\n if (!output.links) {\n continue;\n }\n for (var j = 0; j < output.links.length; ++j) {\n var link_info = this.graph ? this.graph.links[output.links[j]] : null;\n if (this.onConnectionsChange)\n this.onConnectionsChange(LiteGraph.OUTPUT, i2, true, link_info, output);\n }\n if (this.onOutputAdded)\n this.onOutputAdded(output);\n }\n }\n if (this.widgets) {\n for (var i2 = 0; i2 < this.widgets.length; ++i2) {\n var w2 = this.widgets[i2];\n if (!w2)\n continue;\n if (w2.options && w2.options.property && this.properties[w2.options.property] != void 0)\n w2.value = JSON.parse(JSON.stringify(this.properties[w2.options.property]));\n }\n if (info.widgets_values) {\n for (var i2 = 0; i2 < info.widgets_values.length; ++i2) {\n if (this.widgets[i2]) {\n this.widgets[i2].value = info.widgets_values[i2];\n }\n }\n }\n }\n if (this.onConfigure) {\n this.onConfigure(info);\n }\n };\n LGraphNode.prototype.serialize = function() {\n var o = {\n id: this.id,\n type: this.type,\n pos: this.pos,\n size: this.size,\n flags: LiteGraph.cloneObject(this.flags),\n order: this.order,\n mode: this.mode\n };\n if (this.constructor === LGraphNode && this.last_serialization) {\n return this.last_serialization;\n }\n if (this.inputs) {\n o.inputs = this.inputs;\n }\n if (this.outputs) {\n for (var i2 = 0; i2 < this.outputs.length; i2++) {\n delete this.outputs[i2]._data;\n }\n o.outputs = this.outputs;\n }\n if (this.title && this.title != this.constructor.title) {\n o.title = this.title;\n }\n if (this.properties) {\n o.properties = LiteGraph.cloneObject(this.properties);\n }\n if (this.widgets && this.serialize_widgets) {\n o.widgets_values = [];\n for (var i2 = 0; i2 < this.widgets.length; ++i2) {\n if (this.widgets[i2])\n o.widgets_values[i2] = this.widgets[i2].value;\n else\n o.widgets_values[i2] = null;\n }\n }\n if (!o.type) {\n o.type = this.constructor.type;\n }\n if (this.color) {\n o.color = this.color;\n }\n if (this.bgcolor) {\n o.bgcolor = this.bgcolor;\n }\n if (this.boxcolor) {\n o.boxcolor = this.boxcolor;\n }\n if (this.shape) {\n o.shape = this.shape;\n }\n if (this.onSerialize) {\n if (this.onSerialize(o)) {\n console.warn(\n \"node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter\"\n );\n }\n }\n return o;\n };\n LGraphNode.prototype.clone = function() {\n var node2 = LiteGraph.createNode(this.type);\n if (!node2) {\n return null;\n }\n var data = LiteGraph.cloneObject(this.serialize());\n if (data.inputs) {\n for (var i2 = 0; i2 < data.inputs.length; ++i2) {\n data.inputs[i2].link = null;\n }\n }\n if (data.outputs) {\n for (var i2 = 0; i2 < data.outputs.length; ++i2) {\n if (data.outputs[i2].links) {\n data.outputs[i2].links.length = 0;\n }\n }\n }\n delete data[\"id\"];\n if (LiteGraph.use_uuids) {\n data[\"id\"] = LiteGraph.uuidv4();\n }\n node2.configure(data);\n return node2;\n };\n LGraphNode.prototype.toString = function() {\n return JSON.stringify(this.serialize());\n };\n LGraphNode.prototype.getTitle = function() {\n return this.title || this.constructor.title;\n };\n LGraphNode.prototype.setProperty = function(name, value) {\n if (!this.properties) {\n this.properties = {};\n }\n if (value === this.properties[name])\n return;\n var prev_value = this.properties[name];\n this.properties[name] = value;\n if (this.onPropertyChanged) {\n if (this.onPropertyChanged(name, value, prev_value) === false)\n this.properties[name] = prev_value;\n }\n if (this.widgets)\n for (var i2 = 0; i2 < this.widgets.length; ++i2) {\n var w2 = this.widgets[i2];\n if (!w2)\n continue;\n if (w2.options.property == name) {\n w2.value = value;\n break;\n }\n }\n };\n LGraphNode.prototype.setOutputData = function(slot, data) {\n if (!this.outputs) {\n return;\n }\n if (slot == -1 || slot >= this.outputs.length) {\n return;\n }\n var output_info = this.outputs[slot];\n if (!output_info) {\n return;\n }\n output_info._data = data;\n if (this.outputs[slot].links) {\n for (var i2 = 0; i2 < this.outputs[slot].links.length; i2++) {\n var link_id = this.outputs[slot].links[i2];\n var link = this.graph.links[link_id];\n if (link)\n link.data = data;\n }\n }\n };\n LGraphNode.prototype.setOutputDataType = function(slot, type) {\n if (!this.outputs) {\n return;\n }\n if (slot == -1 || slot >= this.outputs.length) {\n return;\n }\n var output_info = this.outputs[slot];\n if (!output_info) {\n return;\n }\n output_info.type = type;\n if (this.outputs[slot].links) {\n for (var i2 = 0; i2 < this.outputs[slot].links.length; i2++) {\n var link_id = this.outputs[slot].links[i2];\n this.graph.links[link_id].type = type;\n }\n }\n };\n LGraphNode.prototype.getInputData = function(slot, force_update) {\n if (!this.inputs) {\n return;\n }\n if (slot >= this.inputs.length || this.inputs[slot].link == null) {\n return;\n }\n var link_id = this.inputs[slot].link;\n var link = this.graph.links[link_id];\n if (!link) {\n return null;\n }\n if (!force_update) {\n return link.data;\n }\n var node2 = this.graph.getNodeById(link.origin_id);\n if (!node2) {\n return link.data;\n }\n if (node2.updateOutputData) {\n node2.updateOutputData(link.origin_slot);\n } else if (node2.onExecute) {\n node2.onExecute();\n }\n return link.data;\n };\n LGraphNode.prototype.getInputDataType = function(slot) {\n if (!this.inputs) {\n return null;\n }\n if (slot >= this.inputs.length || this.inputs[slot].link == null) {\n return null;\n }\n var link_id = this.inputs[slot].link;\n var link = this.graph.links[link_id];\n if (!link) {\n return null;\n }\n var node2 = this.graph.getNodeById(link.origin_id);\n if (!node2) {\n return link.type;\n }\n var output_info = node2.outputs[link.origin_slot];\n if (output_info) {\n return output_info.type;\n }\n return null;\n };\n LGraphNode.prototype.getInputDataByName = function(slot_name, force_update) {\n var slot = this.findInputSlot(slot_name);\n if (slot == -1) {\n return null;\n }\n return this.getInputData(slot, force_update);\n };\n LGraphNode.prototype.isInputConnected = function(slot) {\n if (!this.inputs) {\n return false;\n }\n return slot < this.inputs.length && this.inputs[slot].link != null;\n };\n LGraphNode.prototype.getInputInfo = function(slot) {\n if (!this.inputs) {\n return null;\n }\n if (slot < this.inputs.length) {\n return this.inputs[slot];\n }\n return null;\n };\n LGraphNode.prototype.getInputLink = function(slot) {\n if (!this.inputs) {\n return null;\n }\n if (slot < this.inputs.length) {\n var slot_info = this.inputs[slot];\n return this.graph.links[slot_info.link];\n }\n return null;\n };\n LGraphNode.prototype.getInputNode = function(slot) {\n if (!this.inputs) {\n return null;\n }\n if (slot >= this.inputs.length) {\n return null;\n }\n var input = this.inputs[slot];\n if (!input || input.link === null) {\n return null;\n }\n var link_info = this.graph.links[input.link];\n if (!link_info) {\n return null;\n }\n return this.graph.getNodeById(link_info.origin_id);\n };\n LGraphNode.prototype.getInputOrProperty = function(name) {\n if (!this.inputs || !this.inputs.length) {\n return this.properties ? this.properties[name] : null;\n }\n for (var i2 = 0, l = this.inputs.length; i2 < l; ++i2) {\n var input_info = this.inputs[i2];\n if (name == input_info.name && input_info.link != null) {\n var link = this.graph.links[input_info.link];\n if (link) {\n return link.data;\n }\n }\n }\n return this.properties[name];\n };\n LGraphNode.prototype.getOutputData = function(slot) {\n if (!this.outputs) {\n return null;\n }\n if (slot >= this.outputs.length) {\n return null;\n }\n var info = this.outputs[slot];\n return info._data;\n };\n LGraphNode.prototype.getOutputInfo = function(slot) {\n if (!this.outputs) {\n return null;\n }\n if (slot < this.outputs.length) {\n return this.outputs[slot];\n }\n return null;\n };\n LGraphNode.prototype.isOutputConnected = function(slot) {\n if (!this.outputs) {\n return false;\n }\n return slot < this.outputs.length && this.outputs[slot].links && this.outputs[slot].links.length;\n };\n LGraphNode.prototype.isAnyOutputConnected = function() {\n if (!this.outputs) {\n return false;\n }\n for (var i2 = 0; i2 < this.outputs.length; ++i2) {\n if (this.outputs[i2].links && this.outputs[i2].links.length) {\n return true;\n }\n }\n return false;\n };\n LGraphNode.prototype.getOutputNodes = function(slot) {\n if (!this.outputs || this.outputs.length == 0) {\n return null;\n }\n if (slot >= this.outputs.length) {\n return null;\n }\n var output = this.outputs[slot];\n if (!output.links || output.links.length == 0) {\n return null;\n }\n var r = [];\n for (var i2 = 0; i2 < output.links.length; i2++) {\n var link_id = output.links[i2];\n var link = this.graph.links[link_id];\n if (link) {\n var target_node = this.graph.getNodeById(link.target_id);\n if (target_node) {\n r.push(target_node);\n }\n }\n }\n return r;\n };\n LGraphNode.prototype.addOnTriggerInput = function() {\n var trigS = this.findInputSlot(\"onTrigger\");\n if (trigS == -1) {\n //!trigS || \n this.addInput(\"onTrigger\", LiteGraph.EVENT, { optional: true, nameLocked: true });\n return this.findInputSlot(\"onTrigger\");\n }\n return trigS;\n };\n LGraphNode.prototype.addOnExecutedOutput = function() {\n var trigS = this.findOutputSlot(\"onExecuted\");\n if (trigS == -1) {\n //!trigS || \n this.addOutput(\"onExecuted\", LiteGraph.ACTION, { optional: true, nameLocked: true });\n return this.findOutputSlot(\"onExecuted\");\n }\n return trigS;\n };\n LGraphNode.prototype.onAfterExecuteNode = function(param, options) {\n var trigS = this.findOutputSlot(\"onExecuted\");\n if (trigS != -1) {\n this.triggerSlot(trigS, param, null, options);\n }\n };\n LGraphNode.prototype.changeMode = function(modeTo) {\n switch (modeTo) {\n case LiteGraph.ON_EVENT:\n break;\n case LiteGraph.ON_TRIGGER:\n this.addOnTriggerInput();\n this.addOnExecutedOutput();\n break;\n case LiteGraph.NEVER:\n break;\n case LiteGraph.ALWAYS:\n break;\n case LiteGraph.ON_REQUEST:\n break;\n default:\n return false;\n }\n this.mode = modeTo;\n return true;\n };\n LGraphNode.prototype.doExecute = function(param, options) {\n options = options || {};\n if (this.onExecute) {\n if (!options.action_call) options.action_call = this.id + \"_exec_\" + Math.floor(Math.random() * 9999);\n this.graph.nodes_executing[this.id] = true;\n this.onExecute(param, options);\n this.graph.nodes_executing[this.id] = false;\n this.exec_version = this.graph.iteration;\n if (options && options.action_call) {\n this.action_call = options.action_call;\n this.graph.nodes_executedAction[this.id] = options.action_call;\n }\n }\n this.execute_triggered = 2;\n if (this.onAfterExecuteNode) this.onAfterExecuteNode(param, options);\n };\n LGraphNode.prototype.actionDo = function(action, param, options) {\n options = options || {};\n if (this.onAction) {\n if (!options.action_call) options.action_call = this.id + \"_\" + (action ? action : \"action\") + \"_\" + Math.floor(Math.random() * 9999);\n this.graph.nodes_actioning[this.id] = action ? action : \"actioning\";\n this.onAction(action, param, options);\n this.graph.nodes_actioning[this.id] = false;\n if (options && options.action_call) {\n this.action_call = options.action_call;\n this.graph.nodes_executedAction[this.id] = options.action_call;\n }\n }\n this.action_triggered = 2;\n if (this.onAfterExecuteNode) this.onAfterExecuteNode(param, options);\n };\n LGraphNode.prototype.trigger = function(action, param, options) {\n if (!this.outputs || !this.outputs.length) {\n return;\n }\n if (this.graph)\n this.graph._last_trigger_time = LiteGraph.getTime();\n for (var i2 = 0; i2 < this.outputs.length; ++i2) {\n var output = this.outputs[i2];\n if (!output || output.type !== LiteGraph.EVENT || action && output.name != action)\n continue;\n this.triggerSlot(i2, param, null, options);\n }\n };\n LGraphNode.prototype.triggerSlot = function(slot, param, link_id, options) {\n options = options || {};\n if (!this.outputs) {\n return;\n }\n if (slot == null) {\n console.error(\"slot must be a number\");\n return;\n }\n if (slot.constructor !== Number)\n console.warn(\"slot must be a number, use node.trigger('name') if you want to use a string\");\n var output = this.outputs[slot];\n if (!output) {\n return;\n }\n var links = output.links;\n if (!links || !links.length) {\n return;\n }\n if (this.graph) {\n this.graph._last_trigger_time = LiteGraph.getTime();\n }\n for (var k = 0; k < links.length; ++k) {\n var id = links[k];\n if (link_id != null && link_id != id) {\n continue;\n }\n var link_info = this.graph.links[links[k]];\n if (!link_info) {\n continue;\n }\n link_info._last_time = LiteGraph.getTime();\n var node2 = this.graph.getNodeById(link_info.target_id);\n if (!node2) {\n continue;\n }\n var target_connection = node2.inputs[link_info.target_slot];\n if (node2.mode === LiteGraph.ON_TRIGGER) {\n if (!options.action_call) options.action_call = this.id + \"_trigg_\" + Math.floor(Math.random() * 9999);\n if (node2.onExecute) {\n node2.doExecute(param, options);\n }\n } else if (node2.onAction) {\n if (!options.action_call) options.action_call = this.id + \"_act_\" + Math.floor(Math.random() * 9999);\n var target_connection = node2.inputs[link_info.target_slot];\n node2.actionDo(target_connection.name, param, options);\n }\n }\n };\n LGraphNode.prototype.clearTriggeredSlot = function(slot, link_id) {\n if (!this.outputs) {\n return;\n }\n var output = this.outputs[slot];\n if (!output) {\n return;\n }\n var links = output.links;\n if (!links || !links.length) {\n return;\n }\n for (var k = 0; k < links.length; ++k) {\n var id = links[k];\n if (link_id != null && link_id != id) {\n continue;\n }\n var link_info = this.graph.links[links[k]];\n if (!link_info) {\n continue;\n }\n link_info._last_time = 0;\n }\n };\n LGraphNode.prototype.setSize = function(size) {\n this.size = size;\n if (this.onResize)\n this.onResize(this.size);\n };\n LGraphNode.prototype.addProperty = function(name, default_value, type, extra_info) {\n var o = { name, type, default_value };\n if (extra_info) {\n for (var i2 in extra_info) {\n o[i2] = extra_info[i2];\n }\n }\n if (!this.properties_info) {\n this.properties_info = [];\n }\n this.properties_info.push(o);\n if (!this.properties) {\n this.properties = {};\n }\n this.properties[name] = default_value;\n return o;\n };\n LGraphNode.prototype.addOutput = function(name, type, extra_info) {\n var output = { name, type, links: null };\n if (extra_info) {\n for (var i2 in extra_info) {\n output[i2] = extra_info[i2];\n }\n }\n if (!this.outputs) {\n this.outputs = [];\n }\n this.outputs.push(output);\n if (this.onOutputAdded) {\n this.onOutputAdded(output);\n }\n if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this, type, true);\n this.setSize(this.computeSize());\n this.setDirtyCanvas(true, true);\n return output;\n };\n LGraphNode.prototype.addOutputs = function(array) {\n for (var i2 = 0; i2 < array.length; ++i2) {\n var info = array[i2];\n var o = { name: info[0], type: info[1], link: null };\n if (array[2]) {\n for (var j in info[2]) {\n o[j] = info[2][j];\n }\n }\n if (!this.outputs) {\n this.outputs = [];\n }\n this.outputs.push(o);\n if (this.onOutputAdded) {\n this.onOutputAdded(o);\n }\n if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this, info[1], true);\n }\n this.setSize(this.computeSize());\n this.setDirtyCanvas(true, true);\n };\n LGraphNode.prototype.removeOutput = function(slot) {\n this.disconnectOutput(slot);\n this.outputs.splice(slot, 1);\n for (var i2 = slot; i2 < this.outputs.length; ++i2) {\n if (!this.outputs[i2] || !this.outputs[i2].links) {\n continue;\n }\n var links = this.outputs[i2].links;\n for (var j = 0; j < links.length; ++j) {\n var link = this.graph.links[links[j]];\n if (!link) {\n continue;\n }\n link.origin_slot -= 1;\n }\n }\n this.setSize(this.computeSize());\n if (this.onOutputRemoved) {\n this.onOutputRemoved(slot);\n }\n this.setDirtyCanvas(true, true);\n };\n LGraphNode.prototype.addInput = function(name, type, extra_info) {\n type = type || 0;\n var input = { name, type, link: null };\n if (extra_info) {\n for (var i2 in extra_info) {\n input[i2] = extra_info[i2];\n }\n }\n if (!this.inputs) {\n this.inputs = [];\n }\n this.inputs.push(input);\n this.setSize(this.computeSize());\n if (this.onInputAdded) {\n this.onInputAdded(input);\n }\n LiteGraph.registerNodeAndSlotType(this, type);\n this.setDirtyCanvas(true, true);\n return input;\n };\n LGraphNode.prototype.addInputs = function(array) {\n for (var i2 = 0; i2 < array.length; ++i2) {\n var info = array[i2];\n var o = { name: info[0], type: info[1], link: null };\n if (array[2]) {\n for (var j in info[2]) {\n o[j] = info[2][j];\n }\n }\n if (!this.inputs) {\n this.inputs = [];\n }\n this.inputs.push(o);\n if (this.onInputAdded) {\n this.onInputAdded(o);\n }\n LiteGraph.registerNodeAndSlotType(this, info[1]);\n }\n this.setSize(this.computeSize());\n this.setDirtyCanvas(true, true);\n };\n LGraphNode.prototype.removeInput = function(slot) {\n this.disconnectInput(slot);\n var slot_info = this.inputs.splice(slot, 1);\n for (var i2 = slot; i2 < this.inputs.length; ++i2) {\n if (!this.inputs[i2]) {\n continue;\n }\n var link = this.graph.links[this.inputs[i2].link];\n if (!link) {\n continue;\n }\n link.target_slot -= 1;\n }\n this.setSize(this.computeSize());\n if (this.onInputRemoved) {\n this.onInputRemoved(slot, slot_info[0]);\n }\n this.setDirtyCanvas(true, true);\n };\n LGraphNode.prototype.addConnection = function(name, type, pos2, direction) {\n var o = {\n name,\n type,\n pos: pos2,\n direction,\n links: null\n };\n this.connections.push(o);\n return o;\n };\n LGraphNode.prototype.computeSize = function(out) {\n if (this.constructor.size) {\n return this.constructor.size.concat();\n }\n var rows = Math.max(\n this.inputs ? this.inputs.length : 1,\n this.outputs ? this.outputs.length : 1\n );\n var size = out || new Float32Array([0, 0]);\n rows = Math.max(rows, 1);\n var font_size = LiteGraph.NODE_TEXT_SIZE;\n var title_width = compute_text_size(this.title);\n var input_width = 0;\n var output_width = 0;\n if (this.inputs) {\n for (var i2 = 0, l = this.inputs.length; i2 < l; ++i2) {\n var input = this.inputs[i2];\n var text = input.label || input.name || \"\";\n var text_width = compute_text_size(text);\n if (input_width < text_width) {\n input_width = text_width;\n }\n }\n }\n if (this.outputs) {\n for (var i2 = 0, l = this.outputs.length; i2 < l; ++i2) {\n var output = this.outputs[i2];\n var text = output.label || output.name || \"\";\n var text_width = compute_text_size(text);\n if (output_width < text_width) {\n output_width = text_width;\n }\n }\n }\n size[0] = Math.max(input_width + output_width + 10, title_width);\n size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH);\n if (this.widgets && this.widgets.length) {\n size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5);\n }\n size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT;\n var widgets_height = 0;\n if (this.widgets && this.widgets.length) {\n for (var i2 = 0, l = this.widgets.length; i2 < l; ++i2) {\n if (this.widgets[i2].computeSize)\n widgets_height += this.widgets[i2].computeSize(size[0])[1] + 4;\n else\n widgets_height += LiteGraph.NODE_WIDGET_HEIGHT + 4;\n }\n widgets_height += 8;\n }\n if (this.widgets_up)\n size[1] = Math.max(size[1], widgets_height);\n else if (this.widgets_start_y != null)\n size[1] = Math.max(size[1], widgets_height + this.widgets_start_y);\n else\n size[1] += widgets_height;\n function compute_text_size(text2) {\n if (!text2) {\n return 0;\n }\n return font_size * text2.length * 0.6;\n }\n if (this.constructor.min_height && size[1] < this.constructor.min_height) {\n size[1] = this.constructor.min_height;\n }\n size[1] += 6;\n return size;\n };\n LGraphNode.prototype.inResizeCorner = function(canvasX, canvasY) {\n var rows = this.outputs ? this.outputs.length : 1;\n var outputs_offset = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT;\n return isInsideRectangle(\n canvasX,\n canvasY,\n this.pos[0] + this.size[0] - 15,\n this.pos[1] + Math.max(this.size[1] - 15, outputs_offset),\n 20,\n 20\n );\n };\n LGraphNode.prototype.getPropertyInfo = function(property) {\n var info = null;\n if (this.properties_info) {\n for (var i2 = 0; i2 < this.properties_info.length; ++i2) {\n if (this.properties_info[i2].name == property) {\n info = this.properties_info[i2];\n break;\n }\n }\n }\n if (this.constructor[\"@\" + property])\n info = this.constructor[\"@\" + property];\n if (this.constructor.widgets_info && this.constructor.widgets_info[property])\n info = this.constructor.widgets_info[property];\n if (!info && this.onGetPropertyInfo) {\n info = this.onGetPropertyInfo(property);\n }\n if (!info)\n info = {};\n if (!info.type)\n info.type = typeof this.properties[property];\n if (info.widget == \"combo\")\n info.type = \"enum\";\n return info;\n };\n LGraphNode.prototype.addWidget = function(type, name, value, callback, options) {\n if (!this.widgets) {\n this.widgets = [];\n }\n if (!options && callback && callback.constructor === Object) {\n options = callback;\n callback = null;\n }\n if (options && options.constructor === String)\n options = { property: options };\n if (callback && callback.constructor === String) {\n if (!options)\n options = {};\n options.property = callback;\n callback = null;\n }\n if (callback && callback.constructor !== Function) {\n console.warn(\"addWidget: callback must be a function\");\n callback = null;\n }\n var w2 = {\n type: type.toLowerCase(),\n name,\n value,\n callback,\n options: options || {}\n };\n if (w2.options.y !== void 0) {\n w2.y = w2.options.y;\n }\n if (!callback && !w2.options.callback && !w2.options.property) {\n console.warn(\"LiteGraph addWidget(...) without a callback or property assigned\");\n }\n if (type == \"combo\" && !w2.options.values) {\n throw \"LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }\";\n }\n this.widgets.push(w2);\n this.setSize(this.computeSize());\n return w2;\n };\n LGraphNode.prototype.addCustomWidget = function(custom_widget) {\n if (!this.widgets) {\n this.widgets = [];\n }\n this.widgets.push(custom_widget);\n return custom_widget;\n };\n LGraphNode.prototype.getBounding = function(out, compute_outer) {\n out = out || new Float32Array(4);\n const nodePos = this.pos;\n const isCollapsed = this.flags.collapsed;\n const nodeSize = this.size;\n let left_offset = 0;\n let right_offset = 1;\n let top_offset = 0;\n let bottom_offset = 0;\n if (compute_outer) {\n left_offset = 4;\n right_offset = 6 + left_offset;\n top_offset = 4;\n bottom_offset = 5 + top_offset;\n }\n out[0] = nodePos[0] - left_offset;\n out[1] = nodePos[1] - LiteGraph.NODE_TITLE_HEIGHT - top_offset;\n out[2] = isCollapsed ? (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + right_offset : nodeSize[0] + right_offset;\n out[3] = isCollapsed ? LiteGraph.NODE_TITLE_HEIGHT + bottom_offset : nodeSize[1] + LiteGraph.NODE_TITLE_HEIGHT + bottom_offset;\n if (this.onBounding) {\n this.onBounding(out);\n }\n return out;\n };\n LGraphNode.prototype.isPointInside = function(x2, y2, margin, skip_title) {\n margin = margin || 0;\n var margin_top = this.graph && this.graph.isLive() ? 0 : LiteGraph.NODE_TITLE_HEIGHT;\n if (skip_title) {\n margin_top = 0;\n }\n if (this.flags && this.flags.collapsed) {\n if (isInsideRectangle(\n x2,\n y2,\n this.pos[0] - margin,\n this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin,\n (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + 2 * margin,\n LiteGraph.NODE_TITLE_HEIGHT + 2 * margin\n )) {\n return true;\n }\n } else if (this.pos[0] - 4 - margin < x2 && this.pos[0] + this.size[0] + 4 + margin > x2 && this.pos[1] - margin_top - margin < y2 && this.pos[1] + this.size[1] + margin > y2) {\n return true;\n }\n return false;\n };\n LGraphNode.prototype.getSlotInPosition = function(x2, y2) {\n var link_pos = new Float32Array(2);\n if (this.inputs) {\n for (var i2 = 0, l = this.inputs.length; i2 < l; ++i2) {\n var input = this.inputs[i2];\n this.getConnectionPos(true, i2, link_pos);\n if (isInsideRectangle(\n x2,\n y2,\n link_pos[0] - 10,\n link_pos[1] - 5,\n 20,\n 10\n )) {\n return { input, slot: i2, link_pos };\n }\n }\n }\n if (this.outputs) {\n for (var i2 = 0, l = this.outputs.length; i2 < l; ++i2) {\n var output = this.outputs[i2];\n this.getConnectionPos(false, i2, link_pos);\n if (isInsideRectangle(\n x2,\n y2,\n link_pos[0] - 10,\n link_pos[1] - 5,\n 20,\n 10\n )) {\n return { output, slot: i2, link_pos };\n }\n }\n }\n return null;\n };\n LGraphNode.prototype.findInputSlot = function(name, returnObj) {\n if (!this.inputs) {\n return -1;\n }\n for (var i2 = 0, l = this.inputs.length; i2 < l; ++i2) {\n if (name == this.inputs[i2].name) {\n return !returnObj ? i2 : this.inputs[i2];\n }\n }\n return -1;\n };\n LGraphNode.prototype.findOutputSlot = function(name, returnObj) {\n returnObj = returnObj || false;\n if (!this.outputs) {\n return -1;\n }\n for (var i2 = 0, l = this.outputs.length; i2 < l; ++i2) {\n if (name == this.outputs[i2].name) {\n return !returnObj ? i2 : this.outputs[i2];\n }\n }\n return -1;\n };\n LGraphNode.prototype.findInputSlotFree = function(optsIn) {\n var optsIn = optsIn || {};\n var optsDef = {\n returnObj: false,\n typesNotAccepted: []\n };\n var opts = Object.assign(optsDef, optsIn);\n if (!this.inputs) {\n return -1;\n }\n for (var i2 = 0, l = this.inputs.length; i2 < l; ++i2) {\n if (this.inputs[i2].link && this.inputs[i2].link != null) {\n continue;\n }\n if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.inputs[i2].type)) {\n continue;\n }\n return !opts.returnObj ? i2 : this.inputs[i2];\n }\n return -1;\n };\n LGraphNode.prototype.findOutputSlotFree = function(optsIn) {\n var optsIn = optsIn || {};\n var optsDef = {\n returnObj: false,\n typesNotAccepted: []\n };\n var opts = Object.assign(optsDef, optsIn);\n if (!this.outputs) {\n return -1;\n }\n for (var i2 = 0, l = this.outputs.length; i2 < l; ++i2) {\n if (this.outputs[i2].links && this.outputs[i2].links != null) {\n continue;\n }\n if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.outputs[i2].type)) {\n continue;\n }\n return !opts.returnObj ? i2 : this.outputs[i2];\n }\n return -1;\n };\n LGraphNode.prototype.findInputSlotByType = function(type, returnObj, preferFreeSlot, doNotUseOccupied) {\n return this.findSlotByType(true, type, returnObj, preferFreeSlot, doNotUseOccupied);\n };\n LGraphNode.prototype.findOutputSlotByType = function(type, returnObj, preferFreeSlot, doNotUseOccupied) {\n return this.findSlotByType(false, type, returnObj, preferFreeSlot, doNotUseOccupied);\n };\n LGraphNode.prototype.findSlotByType = function(input, type, returnObj, preferFreeSlot, doNotUseOccupied) {\n input = input || false;\n returnObj = returnObj || false;\n preferFreeSlot = preferFreeSlot || false;\n doNotUseOccupied = doNotUseOccupied || false;\n var aSlots = input ? this.inputs : this.outputs;\n if (!aSlots) {\n return -1;\n }\n if (type == \"\" || type == \"*\") type = 0;\n for (var i2 = 0, l = aSlots.length; i2 < l; ++i2) {\n var aSource = (type + \"\").toLowerCase().split(\",\");\n var aDest = aSlots[i2].type == \"0\" || aSlots[i2].type == \"*\" ? \"0\" : aSlots[i2].type;\n aDest = (aDest + \"\").toLowerCase().split(\",\");\n for (var sI = 0; sI < aSource.length; sI++) {\n for (var dI = 0; dI < aDest.length; dI++) {\n if (aSource[sI] == \"_event_\") aSource[sI] = LiteGraph.EVENT;\n if (aDest[sI] == \"_event_\") aDest[sI] = LiteGraph.EVENT;\n if (aSource[sI] == \"*\") aSource[sI] = 0;\n if (aDest[sI] == \"*\") aDest[sI] = 0;\n if (aSource[sI] == aDest[dI]) {\n if (preferFreeSlot && (aSlots[i2].links && aSlots[i2].links !== null) || aSlots[i2].link && aSlots[i2].link !== null) continue;\n return !returnObj ? i2 : aSlots[i2];\n }\n }\n }\n }\n if (preferFreeSlot && !doNotUseOccupied) {\n for (var i2 = 0, l = aSlots.length; i2 < l; ++i2) {\n var aSource = (type + \"\").toLowerCase().split(\",\");\n var aDest = aSlots[i2].type == \"0\" || aSlots[i2].type == \"*\" ? \"0\" : aSlots[i2].type;\n aDest = (aDest + \"\").toLowerCase().split(\",\");\n for (var sI = 0; sI < aSource.length; sI++) {\n for (var dI = 0; dI < aDest.length; dI++) {\n if (aSource[sI] == \"*\") aSource[sI] = 0;\n if (aDest[sI] == \"*\") aDest[sI] = 0;\n if (aSource[sI] == aDest[dI]) {\n return !returnObj ? i2 : aSlots[i2];\n }\n }\n }\n }\n }\n return -1;\n };\n LGraphNode.prototype.connectByType = function(slot, target_node, target_slotType, optsIn) {\n var optsIn = optsIn || {};\n var optsDef = {\n createEventInCase: true,\n firstFreeIfOutputGeneralInCase: true,\n generalTypeInCase: true\n };\n var opts = Object.assign(optsDef, optsIn);\n if (target_node && target_node.constructor === Number) {\n target_node = this.graph.getNodeById(target_node);\n }\n var target_slot = target_node.findInputSlotByType(target_slotType, false, true);\n if (target_slot >= 0 && target_slot !== null) {\n return this.connect(slot, target_node, target_slot);\n } else {\n if (opts.createEventInCase && target_slotType == LiteGraph.EVENT) {\n return this.connect(slot, target_node, -1);\n }\n if (opts.generalTypeInCase) {\n var target_slot = target_node.findInputSlotByType(0, false, true, true);\n if (target_slot >= 0) {\n return this.connect(slot, target_node, target_slot);\n }\n }\n if (opts.firstFreeIfOutputGeneralInCase && (target_slotType == 0 || target_slotType == \"*\" || target_slotType == \"\")) {\n var target_slot = target_node.findInputSlotFree({ typesNotAccepted: [LiteGraph.EVENT] });\n if (target_slot >= 0) {\n return this.connect(slot, target_node, target_slot);\n }\n }\n console.debug(\"no way to connect type: \", target_slotType, \" to targetNODE \", target_node);\n return null;\n }\n };\n LGraphNode.prototype.connectByTypeOutput = function(slot, source_node, source_slotType, optsIn) {\n var optsIn = optsIn || {};\n var optsDef = {\n createEventInCase: true,\n firstFreeIfInputGeneralInCase: true,\n generalTypeInCase: true\n };\n var opts = Object.assign(optsDef, optsIn);\n if (source_node && source_node.constructor === Number) {\n source_node = this.graph.getNodeById(source_node);\n }\n var source_slot = source_node.findOutputSlotByType(source_slotType, false, true);\n if (source_slot >= 0 && source_slot !== null) {\n return source_node.connect(source_slot, this, slot);\n } else {\n if (opts.generalTypeInCase) {\n var source_slot = source_node.findOutputSlotByType(0, false, true, true);\n if (source_slot >= 0) {\n return source_node.connect(source_slot, this, slot);\n }\n }\n if (opts.createEventInCase && source_slotType == LiteGraph.EVENT) {\n if (LiteGraph.do_add_triggers_slots) {\n var source_slot = source_node.addOnExecutedOutput();\n return source_node.connect(source_slot, this, slot);\n }\n }\n if (opts.firstFreeIfInputGeneralInCase && (source_slotType == 0 || source_slotType == \"*\" || source_slotType == \"\")) {\n var source_slot = source_node.findOutputSlotFree({ typesNotAccepted: [LiteGraph.EVENT] });\n if (source_slot >= 0) {\n return source_node.connect(source_slot, this, slot);\n }\n }\n console.debug(\"no way to connect byOUT type: \", source_slotType, \" to sourceNODE \", source_node);\n return null;\n }\n };\n LGraphNode.prototype.connect = function(slot, target_node, target_slot) {\n target_slot = target_slot || 0;\n if (!this.graph) {\n console.log(\n \"Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them.\"\n );\n return null;\n }\n if (slot.constructor === String) {\n slot = this.findOutputSlot(slot);\n if (slot == -1) {\n if (LiteGraph.debug) {\n console.log(\"Connect: Error, no slot of name \" + slot);\n }\n return null;\n }\n } else if (!this.outputs || slot >= this.outputs.length) {\n if (LiteGraph.debug) {\n console.log(\"Connect: Error, slot number not found\");\n }\n return null;\n }\n if (target_node && target_node.constructor === Number) {\n target_node = this.graph.getNodeById(target_node);\n }\n if (!target_node) {\n throw \"target node is null\";\n }\n if (target_node == this) {\n return null;\n }\n if (target_slot.constructor === String) {\n target_slot = target_node.findInputSlot(target_slot);\n if (target_slot == -1) {\n if (LiteGraph.debug) {\n console.log(\n \"Connect: Error, no slot of name \" + target_slot\n );\n }\n return null;\n }\n } else if (target_slot === LiteGraph.EVENT) {\n if (LiteGraph.do_add_triggers_slots) {\n target_node.changeMode(LiteGraph.ON_TRIGGER);\n target_slot = target_node.findInputSlot(\"onTrigger\");\n } else {\n return null;\n }\n } else if (!target_node.inputs || target_slot >= target_node.inputs.length) {\n if (LiteGraph.debug) {\n console.log(\"Connect: Error, slot number not found\");\n }\n return null;\n }\n var changed = false;\n var input = target_node.inputs[target_slot];\n var link_info = null;\n var output = this.outputs[slot];\n if (!this.outputs[slot]) {\n return null;\n }\n if (target_node.onBeforeConnectInput) {\n target_slot = target_node.onBeforeConnectInput(target_slot);\n }\n if (target_slot === false || target_slot === null || !LiteGraph.isValidConnection(output.type, input.type)) {\n this.setDirtyCanvas(false, true);\n if (changed)\n this.graph.connectionChange(this, link_info);\n return null;\n }\n if (target_node.onConnectInput) {\n if (target_node.onConnectInput(target_slot, output.type, output, this, slot) === false) {\n return null;\n }\n }\n if (this.onConnectOutput) {\n if (this.onConnectOutput(slot, input.type, input, target_node, target_slot) === false) {\n return null;\n }\n }\n if (target_node.inputs[target_slot] && target_node.inputs[target_slot].link != null) {\n this.graph.beforeChange();\n target_node.disconnectInput(target_slot, { doProcessChange: false });\n changed = true;\n }\n if (output.links !== null && output.links.length) {\n switch (output.type) {\n case LiteGraph.EVENT:\n if (!LiteGraph.allow_multi_output_for_events) {\n this.graph.beforeChange();\n this.disconnectOutput(slot, false, { doProcessChange: false });\n changed = true;\n }\n break;\n }\n }\n var nextId;\n if (LiteGraph.use_uuids)\n nextId = LiteGraph.uuidv4();\n else\n nextId = ++this.graph.last_link_id;\n link_info = new LLink(\n nextId,\n input.type || output.type,\n this.id,\n slot,\n target_node.id,\n target_slot\n );\n this.graph.links[link_info.id] = link_info;\n if (output.links == null) {\n output.links = [];\n }\n output.links.push(link_info.id);\n target_node.inputs[target_slot].link = link_info.id;\n if (this.graph) {\n this.graph._version++;\n }\n if (this.onConnectionsChange) {\n this.onConnectionsChange(\n LiteGraph.OUTPUT,\n slot,\n true,\n link_info,\n output\n );\n }\n if (target_node.onConnectionsChange) {\n target_node.onConnectionsChange(\n LiteGraph.INPUT,\n target_slot,\n true,\n link_info,\n input\n );\n }\n if (this.graph && this.graph.onNodeConnectionChange) {\n this.graph.onNodeConnectionChange(\n LiteGraph.INPUT,\n target_node,\n target_slot,\n this,\n slot\n );\n this.graph.onNodeConnectionChange(\n LiteGraph.OUTPUT,\n this,\n slot,\n target_node,\n target_slot\n );\n }\n this.setDirtyCanvas(false, true);\n this.graph.afterChange();\n this.graph.connectionChange(this, link_info);\n return link_info;\n };\n LGraphNode.prototype.disconnectOutput = function(slot, target_node) {\n if (slot.constructor === String) {\n slot = this.findOutputSlot(slot);\n if (slot == -1) {\n if (LiteGraph.debug) {\n console.log(\"Connect: Error, no slot of name \" + slot);\n }\n return false;\n }\n } else if (!this.outputs || slot >= this.outputs.length) {\n if (LiteGraph.debug) {\n console.log(\"Connect: Error, slot number not found\");\n }\n return false;\n }\n var output = this.outputs[slot];\n if (!output || !output.links || output.links.length == 0) {\n return false;\n }\n if (target_node) {\n if (target_node.constructor === Number) {\n target_node = this.graph.getNodeById(target_node);\n }\n if (!target_node) {\n throw \"Target Node not found\";\n }\n for (var i2 = 0, l = output.links.length; i2 < l; i2++) {\n var link_id = output.links[i2];\n var link_info = this.graph.links[link_id];\n if (link_info.target_id == target_node.id) {\n output.links.splice(i2, 1);\n var input = target_node.inputs[link_info.target_slot];\n input.link = null;\n delete this.graph.links[link_id];\n if (this.graph) {\n this.graph._version++;\n }\n if (target_node.onConnectionsChange) {\n target_node.onConnectionsChange(\n LiteGraph.INPUT,\n link_info.target_slot,\n false,\n link_info,\n input\n );\n }\n if (this.onConnectionsChange) {\n this.onConnectionsChange(\n LiteGraph.OUTPUT,\n slot,\n false,\n link_info,\n output\n );\n }\n if (this.graph && this.graph.onNodeConnectionChange) {\n this.graph.onNodeConnectionChange(\n LiteGraph.OUTPUT,\n this,\n slot\n );\n }\n if (this.graph && this.graph.onNodeConnectionChange) {\n this.graph.onNodeConnectionChange(\n LiteGraph.OUTPUT,\n this,\n slot\n );\n this.graph.onNodeConnectionChange(\n LiteGraph.INPUT,\n target_node,\n link_info.target_slot\n );\n }\n break;\n }\n }\n } else {\n for (var i2 = 0, l = output.links.length; i2 < l; i2++) {\n var link_id = output.links[i2];\n var link_info = this.graph.links[link_id];\n if (!link_info) {\n continue;\n }\n var target_node = this.graph.getNodeById(link_info.target_id);\n var input = null;\n if (this.graph) {\n this.graph._version++;\n }\n if (target_node) {\n input = target_node.inputs[link_info.target_slot];\n input.link = null;\n if (target_node.onConnectionsChange) {\n target_node.onConnectionsChange(\n LiteGraph.INPUT,\n link_info.target_slot,\n false,\n link_info,\n input\n );\n }\n if (this.graph && this.graph.onNodeConnectionChange) {\n this.graph.onNodeConnectionChange(\n LiteGraph.INPUT,\n target_node,\n link_info.target_slot\n );\n }\n }\n delete this.graph.links[link_id];\n if (this.onConnectionsChange) {\n this.onConnectionsChange(\n LiteGraph.OUTPUT,\n slot,\n false,\n link_info,\n output\n );\n }\n if (this.graph && this.graph.onNodeConnectionChange) {\n this.graph.onNodeConnectionChange(\n LiteGraph.OUTPUT,\n this,\n slot\n );\n this.graph.onNodeConnectionChange(\n LiteGraph.INPUT,\n target_node,\n link_info.target_slot\n );\n }\n }\n output.links = null;\n }\n this.setDirtyCanvas(false, true);\n this.graph.connectionChange(this);\n return true;\n };\n LGraphNode.prototype.disconnectInput = function(slot) {\n if (slot.constructor === String) {\n slot = this.findInputSlot(slot);\n if (slot == -1) {\n if (LiteGraph.debug) {\n console.log(\"Connect: Error, no slot of name \" + slot);\n }\n return false;\n }\n } else if (!this.inputs || slot >= this.inputs.length) {\n if (LiteGraph.debug) {\n console.log(\"Connect: Error, slot number not found\");\n }\n return false;\n }\n var input = this.inputs[slot];\n if (!input) {\n return false;\n }\n var link_id = this.inputs[slot].link;\n if (link_id != null) {\n this.inputs[slot].link = null;\n var link_info = this.graph.links[link_id];\n if (link_info) {\n var target_node = this.graph.getNodeById(link_info.origin_id);\n if (!target_node) {\n return false;\n }\n var output = target_node.outputs[link_info.origin_slot];\n if (!output || !output.links || output.links.length == 0) {\n return false;\n }\n for (var i2 = 0, l = output.links.length; i2 < l; i2++) {\n if (output.links[i2] == link_id) {\n output.links.splice(i2, 1);\n break;\n }\n }\n delete this.graph.links[link_id];\n if (this.graph) {\n this.graph._version++;\n }\n if (this.onConnectionsChange) {\n this.onConnectionsChange(\n LiteGraph.INPUT,\n slot,\n false,\n link_info,\n input\n );\n }\n if (target_node.onConnectionsChange) {\n target_node.onConnectionsChange(\n LiteGraph.OUTPUT,\n i2,\n false,\n link_info,\n output\n );\n }\n if (this.graph && this.graph.onNodeConnectionChange) {\n this.graph.onNodeConnectionChange(\n LiteGraph.OUTPUT,\n target_node,\n i2\n );\n this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot);\n }\n }\n }\n this.setDirtyCanvas(false, true);\n if (this.graph)\n this.graph.connectionChange(this);\n return true;\n };\n LGraphNode.prototype.getConnectionPos = function(is_input, slot_number, out) {\n out = out || new Float32Array(2);\n var num_slots = 0;\n if (is_input && this.inputs) {\n num_slots = this.inputs.length;\n }\n if (!is_input && this.outputs) {\n num_slots = this.outputs.length;\n }\n var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5;\n if (this.flags.collapsed) {\n var w2 = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH;\n if (this.horizontal) {\n out[0] = this.pos[0] + w2 * 0.5;\n if (is_input) {\n out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT;\n } else {\n out[1] = this.pos[1];\n }\n } else {\n if (is_input) {\n out[0] = this.pos[0];\n } else {\n out[0] = this.pos[0] + w2;\n }\n out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5;\n }\n return out;\n }\n if (is_input && slot_number == -1) {\n out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5;\n out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5;\n return out;\n }\n if (is_input && num_slots > slot_number && this.inputs[slot_number].pos) {\n out[0] = this.pos[0] + this.inputs[slot_number].pos[0];\n out[1] = this.pos[1] + this.inputs[slot_number].pos[1];\n return out;\n } else if (!is_input && num_slots > slot_number && this.outputs[slot_number].pos) {\n out[0] = this.pos[0] + this.outputs[slot_number].pos[0];\n out[1] = this.pos[1] + this.outputs[slot_number].pos[1];\n return out;\n }\n if (this.horizontal) {\n out[0] = this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots);\n if (is_input) {\n out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT;\n } else {\n out[1] = this.pos[1] + this.size[1];\n }\n return out;\n }\n if (is_input) {\n out[0] = this.pos[0] + offset;\n } else {\n out[0] = this.pos[0] + this.size[0] + 1 - offset;\n }\n out[1] = this.pos[1] + (slot_number + 0.7) * LiteGraph.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0);\n return out;\n };\n LGraphNode.prototype.alignToGrid = function() {\n this.pos[0] = LiteGraph.CANVAS_GRID_SIZE * Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE);\n this.pos[1] = LiteGraph.CANVAS_GRID_SIZE * Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE);\n };\n LGraphNode.prototype.trace = function(msg) {\n if (!this.console) {\n this.console = [];\n }\n this.console.push(msg);\n if (this.console.length > LGraphNode.MAX_CONSOLE) {\n this.console.shift();\n }\n if (this.graph.onNodeTrace)\n this.graph.onNodeTrace(this, msg);\n };\n LGraphNode.prototype.setDirtyCanvas = function(dirty_foreground, dirty_background) {\n if (!this.graph) {\n return;\n }\n this.graph.sendActionToCanvas(\"setDirty\", [\n dirty_foreground,\n dirty_background\n ]);\n };\n LGraphNode.prototype.loadImage = function(url) {\n var img = new Image();\n img.src = LiteGraph.node_images_path + url;\n img.ready = false;\n var that2 = this;\n img.onload = function() {\n this.ready = true;\n that2.setDirtyCanvas(true);\n };\n return img;\n };\n LGraphNode.prototype.captureInput = function(v2) {\n if (!this.graph || !this.graph.list_of_graphcanvas) {\n return;\n }\n var list = this.graph.list_of_graphcanvas;\n for (var i2 = 0; i2 < list.length; ++i2) {\n var c = list[i2];\n if (!v2 && c.node_capturing_input != this) {\n continue;\n }\n c.node_capturing_input = v2 ? this : null;\n }\n };\n LGraphNode.prototype.collapse = function(force) {\n this.graph._version++;\n if (this.constructor.collapsable === false && !force) {\n return;\n }\n if (!this.flags.collapsed) {\n this.flags.collapsed = true;\n } else {\n this.flags.collapsed = false;\n }\n this.setDirtyCanvas(true, true);\n };\n LGraphNode.prototype.pin = function(v2) {\n this.graph._version++;\n if (v2 === void 0) {\n this.flags.pinned = !this.flags.pinned;\n } else {\n this.flags.pinned = v2;\n }\n };\n LGraphNode.prototype.localToScreen = function(x2, y2, graphcanvas) {\n return [\n (x2 + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0],\n (y2 + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1]\n ];\n };\n function LGraphGroup(title) {\n this._ctor(title);\n }\n globalThis.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup;\n LGraphGroup.prototype._ctor = function(title) {\n this.title = title || \"Group\";\n this.font_size = LiteGraph.DEFAULT_GROUP_FONT || 24;\n this.color = LGraphCanvas.node_colors.pale_blue ? LGraphCanvas.node_colors.pale_blue.groupcolor : \"#AAA\";\n this._bounding = new Float32Array([10, 10, 140, 80]);\n this._pos = this._bounding.subarray(0, 2);\n this._size = this._bounding.subarray(2, 4);\n this._nodes = [];\n this.graph = null;\n Object.defineProperty(this, \"pos\", {\n set: function(v2) {\n if (!v2 || v2.length < 2) {\n return;\n }\n this._pos[0] = v2[0];\n this._pos[1] = v2[1];\n },\n get: function() {\n return this._pos;\n },\n enumerable: true\n });\n Object.defineProperty(this, \"size\", {\n set: function(v2) {\n if (!v2 || v2.length < 2) {\n return;\n }\n this._size[0] = Math.max(140, v2[0]);\n this._size[1] = Math.max(80, v2[1]);\n },\n get: function() {\n return this._size;\n },\n enumerable: true\n });\n };\n LGraphGroup.prototype.configure = function(o) {\n this.title = o.title;\n this._bounding.set(o.bounding);\n this.color = o.color;\n if (o.font_size) {\n this.font_size = o.font_size;\n }\n };\n LGraphGroup.prototype.serialize = function() {\n var b = this._bounding;\n return {\n title: this.title,\n bounding: [\n Math.round(b[0]),\n Math.round(b[1]),\n Math.round(b[2]),\n Math.round(b[3])\n ],\n color: this.color,\n font_size: this.font_size\n };\n };\n LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) {\n this._pos[0] += deltax;\n this._pos[1] += deltay;\n if (ignore_nodes) {\n return;\n }\n for (var i2 = 0; i2 < this._nodes.length; ++i2) {\n var node2 = this._nodes[i2];\n node2.pos[0] += deltax;\n node2.pos[1] += deltay;\n }\n };\n LGraphGroup.prototype.recomputeInsideNodes = function() {\n this._nodes.length = 0;\n var nodes = this.graph._nodes;\n var node_bounding = new Float32Array(4);\n for (var i2 = 0; i2 < nodes.length; ++i2) {\n var node2 = nodes[i2];\n node2.getBounding(node_bounding);\n if (!overlapBounding(this._bounding, node_bounding)) {\n continue;\n }\n this._nodes.push(node2);\n }\n };\n LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside;\n LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas;\n function DragAndScale(element, skip_events) {\n this.offset = new Float32Array([0, 0]);\n this.scale = 1;\n this.max_scale = 10;\n this.min_scale = 0.1;\n this.onredraw = null;\n this.enabled = true;\n this.last_mouse = [0, 0];\n this.element = null;\n this.visible_area = new Float32Array(4);\n if (element) {\n this.element = element;\n if (!skip_events) {\n this.bindEvents(element);\n }\n }\n }\n LiteGraph.DragAndScale = DragAndScale;\n DragAndScale.prototype.bindEvents = function(element) {\n this.last_mouse = new Float32Array(2);\n this._binded_mouse_callback = this.onMouse.bind(this);\n LiteGraph.pointerListenerAdd(element, \"down\", this._binded_mouse_callback);\n LiteGraph.pointerListenerAdd(element, \"move\", this._binded_mouse_callback);\n LiteGraph.pointerListenerAdd(element, \"up\", this._binded_mouse_callback);\n element.addEventListener(\n \"mousewheel\",\n this._binded_mouse_callback,\n false\n );\n element.addEventListener(\"wheel\", this._binded_mouse_callback, false);\n };\n DragAndScale.prototype.computeVisibleArea = function(viewport) {\n if (!this.element) {\n this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0;\n return;\n }\n var width2 = this.element.width;\n var height = this.element.height;\n var startx = -this.offset[0];\n var starty = -this.offset[1];\n if (viewport) {\n startx += viewport[0] / this.scale;\n starty += viewport[1] / this.scale;\n width2 = viewport[2];\n height = viewport[3];\n }\n var endx = startx + width2 / this.scale;\n var endy = starty + height / this.scale;\n this.visible_area[0] = startx;\n this.visible_area[1] = starty;\n this.visible_area[2] = endx - startx;\n this.visible_area[3] = endy - starty;\n };\n DragAndScale.prototype.onMouse = function(e) {\n if (!this.enabled) {\n return;\n }\n var canvas = this.element;\n var rect = canvas.getBoundingClientRect();\n var x2 = e.clientX - rect.left;\n var y2 = e.clientY - rect.top;\n e.canvasx = x2;\n e.canvasy = y2;\n e.dragging = this.dragging;\n var is_inside = !this.viewport || this.viewport && x2 >= this.viewport[0] && x2 < this.viewport[0] + this.viewport[2] && y2 >= this.viewport[1] && y2 < this.viewport[1] + this.viewport[3];\n var ignore = false;\n if (this.onmouse) {\n ignore = this.onmouse(e);\n }\n if (e.type == LiteGraph.pointerevents_method + \"down\" && is_inside) {\n this.dragging = true;\n LiteGraph.pointerListenerRemove(canvas, \"move\", this._binded_mouse_callback);\n LiteGraph.pointerListenerAdd(document, \"move\", this._binded_mouse_callback);\n LiteGraph.pointerListenerAdd(document, \"up\", this._binded_mouse_callback);\n } else if (e.type == LiteGraph.pointerevents_method + \"move\") {\n if (!ignore) {\n var deltax = x2 - this.last_mouse[0];\n var deltay = y2 - this.last_mouse[1];\n if (this.dragging) {\n this.mouseDrag(deltax, deltay);\n }\n }\n } else if (e.type == LiteGraph.pointerevents_method + \"up\") {\n this.dragging = false;\n LiteGraph.pointerListenerRemove(document, \"move\", this._binded_mouse_callback);\n LiteGraph.pointerListenerRemove(document, \"up\", this._binded_mouse_callback);\n LiteGraph.pointerListenerAdd(canvas, \"move\", this._binded_mouse_callback);\n } else if (is_inside && (e.type == \"mousewheel\" || e.type == \"wheel\" || e.type == \"DOMMouseScroll\")) {\n e.eventType = \"mousewheel\";\n if (e.type == \"wheel\") {\n e.wheel = -e.deltaY;\n } else {\n e.wheel = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60;\n }\n e.delta = e.wheelDelta ? e.wheelDelta / 40 : e.deltaY ? -e.deltaY / 3 : 0;\n this.changeDeltaScale(1 + e.delta * 0.05);\n }\n this.last_mouse[0] = x2;\n this.last_mouse[1] = y2;\n if (is_inside) {\n e.preventDefault();\n e.stopPropagation();\n return false;\n }\n };\n DragAndScale.prototype.toCanvasContext = function(ctx) {\n ctx.scale(this.scale, this.scale);\n ctx.translate(this.offset[0], this.offset[1]);\n };\n DragAndScale.prototype.convertOffsetToCanvas = function(pos2) {\n return [\n (pos2[0] + this.offset[0]) * this.scale,\n (pos2[1] + this.offset[1]) * this.scale\n ];\n };\n DragAndScale.prototype.convertCanvasToOffset = function(pos2, out) {\n out = out || [0, 0];\n out[0] = pos2[0] / this.scale - this.offset[0];\n out[1] = pos2[1] / this.scale - this.offset[1];\n return out;\n };\n DragAndScale.prototype.mouseDrag = function(x2, y2) {\n this.offset[0] += x2 / this.scale;\n this.offset[1] += y2 / this.scale;\n if (this.onredraw) {\n this.onredraw(this);\n }\n };\n DragAndScale.prototype.changeScale = function(value, zooming_center) {\n if (value < this.min_scale) {\n value = this.min_scale;\n } else if (value > this.max_scale) {\n value = this.max_scale;\n }\n if (value == this.scale) {\n return;\n }\n if (!this.element) {\n return;\n }\n var rect = this.element.getBoundingClientRect();\n if (!rect) {\n return;\n }\n zooming_center = zooming_center || [\n rect.width * 0.5,\n rect.height * 0.5\n ];\n var center = this.convertCanvasToOffset(zooming_center);\n this.scale = value;\n if (Math.abs(this.scale - 1) < 0.01) {\n this.scale = 1;\n }\n var new_center = this.convertCanvasToOffset(zooming_center);\n var delta_offset = [\n new_center[0] - center[0],\n new_center[1] - center[1]\n ];\n this.offset[0] += delta_offset[0];\n this.offset[1] += delta_offset[1];\n if (this.onredraw) {\n this.onredraw(this);\n }\n };\n DragAndScale.prototype.changeDeltaScale = function(value, zooming_center) {\n this.changeScale(this.scale * value, zooming_center);\n };\n DragAndScale.prototype.reset = function() {\n this.scale = 1;\n this.offset[0] = 0;\n this.offset[1] = 0;\n };\n function LGraphCanvas(canvas, graph, options) {\n this.options = options = options || {};\n this.background_image = LGraphCanvas.DEFAULT_BACKGROUND_IMAGE;\n if (canvas && canvas.constructor === String) {\n canvas = document.querySelector(canvas);\n }\n this.ds = new DragAndScale();\n this.zoom_modify_alpha = true;\n this.title_text_font = \"\" + LiteGraph.NODE_TEXT_SIZE + \"px Arial\";\n this.inner_text_font = \"normal \" + LiteGraph.NODE_SUBTEXT_SIZE + \"px Arial\";\n this.node_title_color = LiteGraph.NODE_TITLE_COLOR;\n this.default_link_color = LiteGraph.LINK_COLOR;\n this.default_connection_color = {\n input_off: \"#778\",\n input_on: \"#7F7\",\n //\"#BBD\"\n output_off: \"#778\",\n output_on: \"#7F7\"\n //\"#BBD\"\n };\n this.default_connection_color_byType = {\n /*number: \"#7F7\",\n string: \"#77F\",\n boolean: \"#F77\",*/\n };\n this.default_connection_color_byTypeOff = {\n /*number: \"#474\",\n string: \"#447\",\n boolean: \"#744\",*/\n };\n this.highquality_render = true;\n this.use_gradients = false;\n this.editor_alpha = 1;\n this.pause_rendering = false;\n this.clear_background = true;\n this.clear_background_color = \"#222\";\n this.read_only = false;\n this.render_only_selected = true;\n this.live_mode = false;\n this.show_info = true;\n this.allow_dragcanvas = true;\n this.allow_dragnodes = true;\n this.allow_interaction = true;\n this.multi_select = false;\n this.allow_searchbox = true;\n this.allow_reconnect_links = true;\n this.align_to_grid = false;\n this.drag_mode = false;\n this.dragging_rectangle = null;\n this.filter = null;\n this.set_canvas_dirty_on_mouse_event = true;\n this.always_render_background = false;\n this.render_shadows = true;\n this.render_canvas_border = true;\n this.render_connections_shadows = false;\n this.render_connections_border = true;\n this.render_curved_connections = false;\n this.render_connection_arrows = false;\n this.render_collapsed_slots = true;\n this.render_execution_order = false;\n this.render_title_colored = true;\n this.render_link_tooltip = true;\n this.links_render_mode = LiteGraph.SPLINE_LINK;\n this.mouse = [0, 0];\n this.graph_mouse = [0, 0];\n this.canvas_mouse = this.graph_mouse;\n this.onSearchBox = null;\n this.onSearchBoxSelection = null;\n this.onMouse = null;\n this.onDrawBackground = null;\n this.onDrawForeground = null;\n this.onDrawOverlay = null;\n this.onDrawLinkTooltip = null;\n this.onNodeMoved = null;\n this.onSelectionChange = null;\n this.onConnectingChange = null;\n this.onBeforeChange = null;\n this.onAfterChange = null;\n this.connections_width = 3;\n this.round_radius = 8;\n this.current_node = null;\n this.node_widget = null;\n this.over_link_center = null;\n this.last_mouse_position = [0, 0];\n this.visible_area = this.ds.visible_area;\n this.visible_links = [];\n this.connecting_links = null;\n this.viewport = options.viewport || null;\n if (graph) {\n graph.attachCanvas(this);\n }\n this.setCanvas(canvas, options.skip_events);\n this.clear();\n if (!options.skip_render) {\n this.startRendering();\n }\n this.autoresize = options.autoresize;\n }\n globalThis.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas;\n LGraphCanvas.DEFAULT_BACKGROUND_IMAGE = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII=\";\n LGraphCanvas.link_type_colors = {\n \"-1\": LiteGraph.EVENT_LINK_COLOR,\n number: \"#AAA\",\n node: \"#DCA\"\n };\n LGraphCanvas.gradients = {};\n LGraphCanvas.prototype.clear = function() {\n this.frame = 0;\n this.last_draw_time = 0;\n this.render_time = 0;\n this.fps = 0;\n this.dragging_rectangle = null;\n this.selected_nodes = {};\n this.selected_group = null;\n this.visible_nodes = [];\n this.node_dragged = null;\n this.node_over = null;\n this.node_capturing_input = null;\n this.connecting_links = null;\n this.highlighted_links = {};\n this.dragging_canvas = false;\n this.dirty_canvas = true;\n this.dirty_bgcanvas = true;\n this.dirty_area = null;\n this.node_in_panel = null;\n this.node_widget = null;\n this.last_mouse = [0, 0];\n this.last_mouseclick = 0;\n this.pointer_is_down = false;\n this.pointer_is_double = false;\n this.visible_area.set([0, 0, 0, 0]);\n if (this.onClear) {\n this.onClear();\n }\n };\n LGraphCanvas.prototype.setGraph = function(graph, skip_clear) {\n if (this.graph == graph) {\n return;\n }\n if (!skip_clear) {\n this.clear();\n }\n if (!graph && this.graph) {\n this.graph.detachCanvas(this);\n return;\n }\n graph.attachCanvas(this);\n if (this._graph_stack)\n this._graph_stack = null;\n this.setDirty(true, true);\n };\n LGraphCanvas.prototype.getTopGraph = function() {\n if (this._graph_stack.length)\n return this._graph_stack[0];\n return this.graph;\n };\n LGraphCanvas.prototype.openSubgraph = function(graph) {\n if (!graph) {\n throw \"graph cannot be null\";\n }\n if (this.graph == graph) {\n throw \"graph cannot be the same\";\n }\n this.clear();\n if (this.graph) {\n if (!this._graph_stack) {\n this._graph_stack = [];\n }\n this._graph_stack.push(this.graph);\n }\n graph.attachCanvas(this);\n this.checkPanels();\n this.setDirty(true, true);\n };\n LGraphCanvas.prototype.closeSubgraph = function() {\n if (!this._graph_stack || this._graph_stack.length == 0) {\n return;\n }\n var subgraph_node = this.graph._subgraph_node;\n var graph = this._graph_stack.pop();\n this.selected_nodes = {};\n this.highlighted_links = {};\n graph.attachCanvas(this);\n this.setDirty(true, true);\n if (subgraph_node) {\n this.centerOnNode(subgraph_node);\n this.selectNodes([subgraph_node]);\n }\n this.ds.offset = [0, 0];\n this.ds.scale = 1;\n };\n LGraphCanvas.prototype.getCurrentGraph = function() {\n return this.graph;\n };\n LGraphCanvas.prototype.setCanvas = function(canvas, skip_events) {\n if (canvas) {\n if (canvas.constructor === String) {\n canvas = document.getElementById(canvas);\n if (!canvas) {\n throw \"Error creating LiteGraph canvas: Canvas not found\";\n }\n }\n }\n if (canvas === this.canvas) {\n return;\n }\n if (!canvas && this.canvas) {\n if (!skip_events) {\n this.unbindEvents();\n }\n }\n this.canvas = canvas;\n this.ds.element = canvas;\n if (!canvas) {\n return;\n }\n canvas.className += \" lgraphcanvas\";\n canvas.data = this;\n canvas.tabindex = \"1\";\n this.bgcanvas = null;\n if (!this.bgcanvas) {\n this.bgcanvas = document.createElement(\"canvas\");\n this.bgcanvas.width = this.canvas.width;\n this.bgcanvas.height = this.canvas.height;\n }\n if (canvas.getContext == null) {\n if (canvas.localName != \"canvas\") {\n throw \"Element supplied for LGraphCanvas must be a